I want to create a Python module that calls Fortran functions using Cython. I globally works wells, except for the example below where I get a message error from Python when I try to import my module:
Python 2.7.5 (default, Feb 8 2014, 08:16:49)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-3)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import m
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ImportError: ./m.so: undefined symbol: _gfortran_runtime_error
The minimum working example is:
m.pyx
cdef extern from "fortinterface.h":
void f_fortinterface(int* n, float* var, float* resu)
import numpy as pnp
cimport numpy as cnp
def f(list a):
cdef cnp.ndarray var = pnp.array(a,dtype='f',order='F') #from list to numpy array
cdef cnp.ndarray resu = pnp.ones(len(a),dtype='f',order='F')
cdef int n = len(var)
f_fortinterface(&n, <float*> var.data, <float*> resu.data)
return resu.tolist() #back to list from numpy array
fortinterface.f90
module fortinterface
use iso_c_binding
use fortfunction
implicit none
contains
subroutine f_fortinterface(n,var,resu) bind(c)
implicit none
integer(c_int), intent(in) :: n
real(c_float), intent(in) :: var(n)
real(c_float), intent(out) :: resu(n)
resu(:) = f_fortfunction(var)
end subroutine f_fortinterface
end module fortinterface
fortinterface.h
extern void f_fortinterface(int* n, float* var, float* resu);
file fortfunction.f90
module fortfunction
implicit none
contains
function f_fortfunction(var)
implicit none
real, intent(in) :: var(:)
real, allocatable :: f_fortfunction(:)
allocate(f_fortfunction(size(var)))
f_fortfunction(:) = var(:)+1.0
end function f_fortfunction
end module fortfunction
setup.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
from numpy import get_include
from os import system
# compile the fortran modules without linking
fortran_mod_comp = 'gfortran fortfunction.f90 -c -o fortfunction.o -fPIC'
print fortran_mod_comp
system(fortran_mod_comp)
shared_obj_comp = 'gfortran fortinterface.f90 -c -o fortinterface.o -fPIC'
print shared_obj_comp
system(shared_obj_comp)
# needed if building with NumPy : this includes the NumPy headers when compiling.
path_includes = [get_include()]
ext_modules = [Extension('m', # module name:
['m.pyx'], # source file:
extra_link_args=['fortfunction.o', 'fortinterface.o'])] # other files to link to
setup(name = 'm',
cmdclass = {'build_ext': build_ext},
include_dirs = path_includes,
ext_modules = ext_modules)
and I compile everything with
python setup.py build_ext --inplace
The error message clearly indicates a problem in the inclusion of libraries (during link edition, I guess). I tried to add several options for gfortran and gcc (-lm, lgfortran, ...) with no success. I am very puzzled because the error comes from the use of the allocatable array
f_fortfunction
in the file fortfunction.f90
Indeed, if I replace the allocatable array with a static array, everything works fine. It is however not an acceptable solution for me because the function may return arrays of varying sizes and I really need its dynamic allocation
Modified (working) fortfunction.f90
module fortfunction
implicit none
contains
function f_fortfunction(var)
implicit none
real, intent(in) :: var(:)
real :: f_fortfunction(2)
f_fortfunction(:) = var(:)+1.0
end function f_fortfunction
end module fortfunction
In that case, I get what I want:
>>> import m
>>> m.f([1,3])
[2.0, 4.0]
I was able to correct this issue by linking the library for gfortran in the Extension object in setup.py, rather than trying to manually link the library.
setup.pyWorks with the original fortfunction.f90 and gives the expected result (tested on Python3). Only change is the inclusion of libraries in ext_modules.
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
from numpy import get_include
from os import system
# compile the fortran modules without linking
fortran_mod_comp = 'gfortran fortfunction.f90 -c -o fortfunction.o -fPIC'
print fortran_mod_comp
system(fortran_mod_comp)
shared_obj_comp = 'gfortran fortinterface.f90 -c -o fortinterface.o -fPIC'
print shared_obj_comp
system(shared_obj_comp)
# needed if building with NumPy : this includes the NumPy headers when compiling.
path_includes = [get_include()]
ext_modules = [Extension('m', # module name:
['m.pyx'], # source file:
libraries = ['gfortran'], # need to include gfortran as a library
extra_link_args=['fortfunction.o', 'fortinterface.o'])] # other files to link to
setup(name = 'm',
cmdclass = {'build_ext': build_ext},
include_dirs = path_includes,
ext_modules = ext_modules)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With