Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python/Cython/Fortran module - undefined symbol: _gfortran_runtime_error

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]
like image 308
Mazkime Avatar asked Nov 20 '25 16:11

Mazkime


1 Answers

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.

Modified setup.py

Works 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)
like image 200
Tom Body Avatar answered Nov 22 '25 06:11

Tom Body



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!