【问题标题】:Python/Cython/Fortran module - undefined symbol: _gfortran_runtime_errorPython/Cython/Fortran 模块 - 未定义符号:_gfortran_runtime_error
【发布时间】:2015-07-10 08:01:07
【问题描述】:

我想创建一个使用 Cython 调用 Fortran 函数的 Python 模块。我在全球范围内工作得很好,除了下面的示例,当我尝试导入我的模块时,我从 Python 收到消息错误:

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

最小的工作示例是:

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);

文件 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)

然后我用

编译所有东西
python setup.py build_ext --inplace

错误消息清楚地表明包含库时出现问题(我猜是在链接编辑期间)。我尝试为 gfortran 和 gcc (-lm, lgfortran, ...) 添加几个选项,但没有成功。我很困惑,因为错误来自可分配数组的使用

f_fortfunction

在文件fortfunction.f90

确实,如果我用静态数组替换可分配数组,一切正常。然而,这对我来说不是一个可接受的解决方案,因为该函数可能会返回不同大小的数组,我真的需要它的动态分配

修改(工作)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

在那种情况下,我得到了我想要的:

>>> import m
>>> m.f([1,3])
[2.0, 4.0]

【问题讨论】:

  • 只是一种解决方法,但你不能用 numpy 分配数组然后将它们传递给f_fortfunction 函数吗?我的意思是,你已经这样做了,为什么还要在 Fortran 函数中分配一个形状相同的数组呢?
  • 你是对的。我可以做到(而且我做到了),将f_fortfunction 写为子例程而不是函数。它运作良好。问题是我还希望fortfunction.f90 中的代码可以直接从 fortran 主程序调用(最后,我想在 Fortran 库中开发一些代码,可以使用相同的语法从 Python 和 Fortran 调用,这种语法是返回值的数组(或列表))。根据我的一位同事的说法,我应该避免使用由 Fortran 分配的 Python 数组。这是非常危险的。我可能必须更改我的代码架构才能得到我想要的。
  • 是的,使用 Numpy(或者在您的主​​ Fortran 代码中)分配数组可能是最好的。错误消息仍然很奇怪。我尝试将-lgfortranbegin-static-libgfortran 添加到链接标志中,但没有取得多大成功(使用gcc 4.9.2 和cython 0.22 导入时出现同样的错误)。否则你可以尝试使用 f2py、ctypes 或 cffi。
  • 不要使用没有标签fortran的标签fortran90。并且仅在必要时使用fortran90 以与fortran95 等其他版本区分开来。您实际上使用的是 Fortran 2003!
  • @rth libfortranbegin 在 GCC 4.9.2 中已过时,它什么也不做。它正在为 GCC 6 完全删除。

标签: python c fortran cython


【解决方案1】:

解决方案是在 Fortran 中使用自动数组而不是可分配数组。这是一个有效的fortfunction.f90 示例,

function f_fortfunction(var, n)
implicit none
real, intent(in)   :: var(:)
integer, intent(in)   :: n
real:: f_fortfunction(size(var))

f_fortfunction(:) = var(:)+1.0

end function f_fortfunction

【讨论】:

  • 工作得很好!我认为使用自动数组会导致与使用可分配数组相同的问题,但事实并非如此。非常感谢。
【解决方案2】:

我能够通过在setup.py 中的Extension 对象中链接gfortran 的库来纠正此问题,而不是尝试手动链接库。

修改setup.py

使用原始的fortfunction.f90 并给出预期的结果(在 Python3 上测试)。唯一的变化是在ext_modules 中包含libraries

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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-09-21
    • 2012-10-13
    • 2012-07-23
    • 2014-08-14
    • 1970-01-01
    • 2015-09-24
    • 2014-06-16
    • 1970-01-01
    相关资源
    最近更新 更多