【发布时间】: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。 -
@rth
libfortranbegin在 GCC 4.9.2 中已过时,它什么也不做。它正在为 GCC 6 完全删除。