Search code examples
pythoncythonf2py

Wrapping a f2py-compiled Fortran .so with Cython (without passing by python)


Looking at some other similar questions (Cython and fortran - how to compile together without f2py), I may be going about this the wrong way, but the workflow I'd like to put in place seems so cythonic/pythonic that I would be surprised if the following isn't possible:

1) Compile a fortran_func.so with f2py from the fortran_func.f source file that contains the foo subroutine I need. Seeing as I have the .f source, I do know the correct function call syntax.

2) Magic step perhaps involving a "cdef extern" or something similar.

3) Directly call the function without having to pass by:

import fortran_func

out = fortran_func.foo(x, y)

The idea is that I have three for loops around this function call and calling the fortran function through a python import is really slow. Currently I have this:

import fortran_func

cdef np.ndarray[double, ndim=3] cy_func(double[::1] x,
                                        double[::1] y,
                                        double[::1] z,
                                        int N):
    cdef:
        int i, j, k
        np.ndarray[double, ndim=3] out

    out = np.zeros((N, N, N), dtype=np.float64)

    for i in range(N): # fast C loops
        for j in range(N):
            for k in range(N):
                # extremely slow python call
                out[i,j,k] = fortran_func.foo(x[i], y[j], z[k])

                # would prefer a fast, direct function call
                # out[i,j,k] = foo(x[i], y[j], z[k])

I would very much appreciate any help in finding out what this magic step should be!


Solution

  • You can call a shared fortran object as a c-function with ctypes without converting to python types. You should be able to do so with cython as well. I wrote it down here:

    http://pythology.blogspot.fr/2014/03/wrapping-fortran-and-python-without-f2py.html

    This is the summary:

    fortran file add.f95:

    subroutine add(a,b,c)    
        implicit none
        integer a,b,c
        c = a + b 
    end
    

    compile with:

    gfortran -shared -fPIC add.f95 -o add.so
    

    the python code to call it (careful, 'add' becomes 'add_') is:

    #!/usr/bin/env python
    
    from ctypes import c_int, byref, cdll
    
    a = c_int(3)
    b = c_int(4)
    c = c_int()
    
    addlib = cdll.LoadLibrary('./add.so')
    addlib.add_(byref(a),byref(b),byref(c))
    print c