Developer guide

Debugging

h2lib.H2Lib is wrapper function that returns a H2LibThread instance that either runs in current thread H2Lib(subprocess=False) or in a separate process (default).

Debugging e.g. in visual studio is possible when if subprocess=False

Add method

Add function or subroutine in fortran

Add function to one of the existing fortran files or to a new one e.g. in the lib folder. New files must be added to the src variable in the lib/cmakelist.txt file (lib/*.f90 files are all included).

Note, these example functions are already defined in lib/test_module.f90

function myfunction(int, dbl) bind(C, name='myfunction')
  !DEC$ ATTRIBUTES DLLEXPORT :: myfunction
  integer*8, intent(in) :: int
  real(c_double), intent(in) :: dbl
  real(c_double) :: myfunction
  myfunction = int + dbl
end function


subroutine mysubroutine(str_arr, dbl_array) bind(C, name='mysubroutine')
  !DEC$ ATTRIBUTES DLLEXPORT :: mysubroutine
  character(kind=c_char, len=1), intent(in)       :: str_arr(1024)
  character(len=256) :: str
  real(c_double), intent(inout), dimension(2) :: dbl_array
  call cstring2fortran(str_arr, str)
  print *, str
  dbl_array = dbl_array + 1
end subroutine

Build the library and copy the dll/so to src-python/h2lib/HAWC2Lib.dll (automatically done in the h2lib setup process).

Moreover the function/subroutine names must be appended to the lib/export.txt file.

Add python interface to function/subroutine

After adding the method name to lib/export.txt you can run python make_lib_signatures.py from the lib folder

This will update src-python/h2lib/h2lib_signatures.py with the following methods, which makes autocomplete work in some editors:

def myfunction(self, int, dbl, restype):
    '''function myfunction(int, dbl) bind(C, name='myfunction')
  !DEC$ ATTRIBUTES DLLEXPORT :: myfunction
  integer*8, intent(in) :: int
  real(c_double), intent(in) :: dbl
  real(c_double) :: myfunction
end function'''
    return self.get_lib_function('myfunction')(int, dbl, restype=restype)

def mysubroutine(self, str_arr, dbl_arr):
    '''subroutine mysubroutine(str_arr, dbl_arr) bind(C, name='mysubroutine')
  character(kind=c_char, len=1), intent(inout)       :: str_arr(20)
  real(c_double), intent(inout), dimension(2) :: dbl_arr
end subroutine'''
    return self.get_lib_function('mysubroutine')(str_arr, dbl_arr)

You can now call the methods directly after converting the inputs to ctypes. The library functions, however, is wrapped with features that changes current working directory, suppresses output (if specified during instantiation, i.e. H2Lib(suppress_output=True)) and tries to convert python types into ctypes. The type conversion works for most standard python and numpy types (if properly specified).

[1]:
from h2lib import H2Lib
import numpy as np
with H2Lib(subprocess=False, suppress_output=False) as h2:
    print(h2.myfunction(1, 2., restype=np.float64))  # return (updated) input arguements and return value
    s = "hello" + " " * 20
    print(h2.mysubroutine(s, np.array([3.,4]))) # return (updated) input arguements and subroutine return value
([1, 2.0], 3.0)
(['world                    ', array([4., 5.])], 152)

In the code above the call to h2.myfunction only works because the 1 is an interger and 2. is a float as the wrapper as default converts integers to 64-bit integer and floats to 64 bit floats. Similarly, the array becomes a np.float64 because 3. is a float.

To please the user you can override the method, typically by adding methods to the h2lib.H2Lib class. In this example we extend H2LibThread:

[2]:
from h2lib._h2lib import H2LibThread

class MyLib(H2LibThread):
    def myfunction(self, i, d):
        return H2LibThread.myfunction(self, int=int(i), dbl=float(d),  # convert to int and float
                                      restype=np.float64  # set return type
                                      )[1]  # return output only

    def mysubroutine(self, s, dbl_arr):
        s, dbl_arr = H2LibThread.mysubroutine(self, s.ljust(20),  # space pad string to have length 32
                                        np.asarray(dbl_arr).astype(np.float64)  # convert to np.float64 array
                                        )[0]  # return arguments only
        return s.strip(), dbl_arr


with MyLib(suppress_output=False) as h2:
    print (h2.myfunction(1,2))
    print (h2.mysubroutine("hello", [3,4]))
3.0
('world', array([4., 5.]))
[ ]: