fortran for about 3 months
python - intermediate : never used the ctypes module in python before this
I was looking for a way to use the fortran code for my doctoral work in python - subsequently using the computation for visualizations on-the-fly for visualizations using matplotlib.
THIS POST helped (this tells that fortran code can be used/invoked in python using the ctypes module - and given that the fortran functions have alternate names bound to them - this much makes sense to me logically although i do not know how this works in detail. But we DO choose our battles wisely!).
Then this SO post deals with calling fortran functions from python as well.
The next logical step was to look up the documentation for the python module ctypes. This talks about how the shared library can be accessed using python at an API level.
I had all the pieces to make a minimal working example, which another answer has already done. But i wanted to look at the output mechanism and mathematical operations involving real floats. Here is the test case i made.
logical :: prnt
prnt = .true.
end function prnt
sin_2 = sin(r)**2
end function sin_2
$gfortran -shared -g -o test.so test.f90
$ nm test.so | tail -3
0000067f T prnt_
0000065c T sin_2_
>>> from ctypes import byref, cdll, c_float,c_char_p
>>> t = cdll.LoadLibrary('./test.so')
>>> c = c_char_p("Mary had a little lamb")
>>> t.prnt_('Mary had a little lamb')
Mary had a little lambe
>>> t.prnt_("Mary had a little lamb")
Mary had a little lambe
Mary had a little lambe[� .prnt_(c)
>>> f = c_float(4.56)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ctypes.ArgumentError: argument 1: <type 'exceptions.TypeError'>: Don't know how to convert parameter 1
Segmentation fault (core dumped)
In Fortran, arguments are passed by reference. Fortran character arrays aren't null terminated; the length is passed by value as an implicit
long int argument. Also, Python's
float type is a
double, so you may want to use Fortran
real(8) for consistency.
function prnt(s) ! byref(s), byval(length) [long int, implicit] character(len=*):: s ! variable length input logical :: prnt write(*, "(A)") s ! formatted, to remove initial space prnt = .true. end function prnt function sin_2(r) ! byref(r) real:: r, sin_2 ! float; use real(8) for double sin_2 = sin(r)**2 end function sin_2
Remember to set ctypes
argtypes for functions, and
restype where appropriate. In this case
sin_2 takes a float pointer and returns a float.
>>> from ctypes import * >>> test = CDLL('./test.so') >>> test.prnt_.argtypes = [c_char_p, c_long] >>> test.sin_2_.argtypes = [POINTER(c_float)] >>> test.sin_2_.restype = c_float >>> s = 'Mary had a little lamb' >>> test.prnt_(s, len(s)) Mary had a little lamb 1 >>> x = c_float(4.56) >>> test.sin_2_(byref(x)) 0.9769567847251892