Building a python frontend for HPC codes

PyCon UK 2017

Alice Harpole

soton logo soton logo

subroutine ca_initdata(level,time,lo,hi,nscal, &
                       state,state_l1,state_l2,&
                       state_l3,state_h1,state_h2,&
                       state_h3, &
                       delta,xlo,xhi)

  use probdata_module
  use bl_constants_module, only: M_PI, FOUR3RD, &
  ZERO, ONE
  use meth_params_module, only: NVAR, NQ, QRHO, &
  QU, QV, QW, QREINT, QPRES, URHO, UMX, UMY, UMZ, &
  UEDEN, UEINT, UFS, UTEMP
  use prob_params_module, only : center
  use amrex_fort_module, only : rt => amrex_real
  use network, only : nspec
  use eos_module, only : eos
  use eos_type_module, only : eos_t, eos_input_rp, &
  eos_input_re
  use riemann_util_module, only: gr_cons_state

  implicit none

  integer, intent(in) :: level, nscal
  integer, intent(in) :: lo(3), hi(3)
  integer, intent(in) :: state_l1,state_l2,state_l3,&
                         state_h1,state_h2,state_h3
  real(rt), intent(in) :: xlo(3), xhi(3), time, delta(3)
  real(rt), intent(out) :: state(state_l1:state_h1, &
                           state_l2:state_h2, &
                           state_l3:state_h3,NVAR)
  real(rt), intent(out) :: q(state_l1:state_h1, &
                           state_l2:state_h2, &
                           state_l3:state_h3,NQ)

  real(rt) :: xmin,ymin,zmin
  real(rt) :: xx, yy, zz
  real(rt) :: dist
  real(rt) :: eint, p_zone
  real(rt) :: vctr, p_exp

  integer :: i,j,k, ii, jj, kk
  integer :: npert, nambient
  real(rt) :: e_zone, gamma_up(9)
  type(eos_t) :: eos_state

  gamma_up(:) = 0.0d0
  gamma_up(1) = 1.0d0
  gamma_up(5) = 1.0d0
  gamma_up(9) = 1.0d0

  vctr  = FOUR3RD*M_PI*r_init**3


import numpy as np
from eos import eos_type_module, eos_module
from riemann_util import riemann_util_module as riemann
from probdata import probdata_module as probdata
from prob_params import prob_params_module as prob_params
from meth_params import meth_params_module as meth_params

def ca_initdata(lo, hi, slo, shi, delta,
                xlo, xhi, probin="probin.3d.sph"):

	gamma_up = np.zeros(9)
	gamma_up[0] = 1.0
	gamma_up[4] = 1.0
	gamma_up[8] = 1.0

	vctr  = 4.0/3.0 * np.pi * r_init**3

Simple fortran example


module f_module

    implicit none

    type :: f_type

        integer :: exponent
        double precision :: mantissa

    end type f_type

contains

    subroutine f_pow(a, b)

        implicit none

        type (f_type), intent(in) :: a
        double precision, intent(out) :: b

        b = (a % mantissa)**(a % exponent)

    end subroutine f_pow

end module f_module

gfortran -c f_module.f90
f90wrap -c -m f_module f_module.f90
f2py-f90wrap -c -m _f_module f90wrap_f_module.f90 -L. f_module.o

import _f_module
import f90wrap.runtime
import logging

class F_Module(f90wrap.runtime.FortranModule):
    class F_Type(f90wrap.runtime.FortranDerivedType):

        def __init__(self, handle=None):
            f90wrap.runtime.FortranDerivedType.__init__(self)
            self._handle = _f_module.f90wrap_f_type_initialise()

        def __del__(self):
            if self._alloc:
                _f_module.f90wrap_f_type_finalise(this=self._handle)

        @property
        def exponent(self):
            return _f_module.f90wrap_f_type__get__exponent(self._handle)

        @exponent.setter
        def exponent(self, exponent):
            _f_module.f90wrap_f_type__set__exponent(self._handle, exponent)

        @property
        def mantissa(self):
            return _f_module.f90wrap_f_type__get__mantissa(self._handle)

        @mantissa.setter
        def mantissa(self, mantissa):
            _f_module.f90wrap_f_type__set__mantissa(self._handle, mantissa)

    @staticmethod
    def f_pow(a):
        b = _f_module.f90wrap_f_pow(a=a._handle)
        return b

f_module = F_Module()

from f_module import f_module

a = f_module.F_Type()
a.mantissa = 2.5
a.exponent = 2

f_module.f_pow(a)

>> 6.25

from unittest.mock import patch
import unittest
import numpy as np

class TestCase(unittest.TestCase):

    class mocked_F_type():
        def __init__(self):
            print("\nUsing mocked F_type")
            self.exponent = 0
            self.mantissa = 0

    def mocked_f_pow(a):
        print("\nUsing mocked f_pow")
        return a.mantissa ** a.exponent

    @patch('f_module.f_module.f_pow', new=mocked_f_pow)
    @patch('f_module.f_module.F_Type', new=mocked_F_type)
    def test_py_pow(self):
        from run_f_module import py_pow

        np.testing.assert_equal(py_pow(2.5, 2), 6.25)

    def test_py_pow2(self):
        from run_f_module import py_pow

        np.testing.assert_equal(py_pow(2.5, 2), 6.25)

Simple python example


def add_me(x, y):
    return x + y

#include "Python.h"

int add(int x, int y)
{
    PyObject *pName, *pModule, *pDict, *pFunc, *pArgs, *pValue;
    int i;
    const char* pymodule = "python_ex";
    const char* pyfunc = "add_me";

    // initialise python interpreter
    Py_Initialize();

    // add current directory to python path
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append(\".\")");

    // build the name object
    pName = PyUnicode_FromString(pymodule);

    // build the module object
    pModule = PyImport_Import(pName);
    Py_DECREF(pName);

    if (pModule != NULL) {
        pDict = PyModule_GetDict(pModule);
        pFunc = PyDict_GetItemString(pDict, pyfunc);

        if (pFunc && PyCallable_Check(pFunc)) {
            pArgs = Py_BuildValue("(ii)", x, y);
            pValue = PyObject_CallObject(pFunc, pArgs);
            Py_DECREF(pArgs);
            if (pValue != NULL) {
                printf("Result of call: %ld\n", PyLong_AsLong(pValue));
                Py_DECREF(pValue);
            }
            else { /* Call failed */ }
        }
        else { /* Cannot find function */ }
        Py_XDECREF(pFunc);
        Py_DECREF(pModule);
    }
    else { /* Cannot load module */ }

    i = PyLong_AsLong(pValue);
    Py_Finalize();
    return i;
}
	

yt hello world


import yt

# load data into DataSet
ds = yt.load('plt13740')

# create Slice Plot of prim_density
yt.SlicePlot(ds, 'z', 'prim_density').save('13740')

ds.create_field_info()
ds.field_info[('boxlib', 'prim_density')].display_name = r'$\rho$'

centre = 0.5 * (ds.domain_left_edge + ds.domain_right_edge) *
         np.sign(ds.domain_width)

p = yt.SlicePlot(ds, 'z', 'prim_density', origin='native', center=centre)

v, c = ds.find_max('prim_density')
p.annotate_sphere([c[0],c[1]], radius=0.015, coord_system='axis',
                   text=f'Max density = {float(v.d):.2f}',
                   circle_args={'color':'black', 'linewidth':4},
                   text_args={'color':'black', 'size':30})

p.annotate_quiver('prim_u', 'prim_v', 70)

axis_widths = tuple([(ds.domain_width.value[i], 'unitary') for i in range(3)])
p.set_width(axis_widths)

p.annotate_timestamp(draw_inset_box=True)