我想请教一下关于通过 C 函数调用 fortran 函数的问题。这些 C 函数将通过 cython 在 python 代码中使用。把事情放在一起,我有这个方案:
Cython 模块 -> C 函数 -> Fortran,其中 -> 表示“调用”。
目前我设法从 cython 调用 C 函数,但我很难调用 fortran 函数。你能帮我吗?(一个简单的例子会很棒)。
提前致谢。编辑:我正在使用 gcc 4.1.2。和 gfortran
第一个答案中的链接描述了过时的方法。通过向 Fortran 添加 ISO C 绑定,从 C 调用 Fortran 或从 Fortran 调用 C 变得更加容易。使用此 Fortran 语言功能可指示 Fortran 编译器生成与 C 二进制兼容的可执行代码。程序员不必“破解”连接,因为它是语言的一部分,所以它与编译器和平台无关。从技术上讲,ISO C 绑定是 Fortran 2003 的一部分,但它已经在许多编译器中使用了好几年,例如,自 4.3 版以来的 gfortran 和 Intel ifort。
要从 C 中调用 Fortran 子例程或函数,请使用 bind C 选项声明 Fortran 子例程或函数,并使用绑定中为参数声明提供的 C 兼容类型。在“混合语言编程”下的 gfortran 手册中有示例。由于 ISO C 绑定是该语言的一部分,因此手册的这一部分很大程度上与编译器无关。Stack Overflow 和网络上其他地方的先前答案中还有其他示例。
这是要从 C 调用的子例程的 Fortran 子例程声明的快速代码片段(未经测试):
subroutine test ( varint1, varflt2 ) bind ( C, name="MyTest" )
use iso_c_binding
integer (kind=c_int32_t), intent (in) :: varint1
real (kind=c_float), intent (out) :: varflt2
绑定 C 名称“MyTest”覆盖 Fortran 名称——它区分大小写,与 Fortran 不同。无需担心下划线!变量类型应该很明显......请参阅 gfortran 手册或其他地方了解可用的内容。
由于这是调试 HPC 代码的有用方法,因此这里有一个简单的 Fortran 中的“hello world”程序,从 Python 调用。
使用GNU Fortran (GCC) 9.3.0
和Python 3.7.3
你至少需要
bind(C)
,对于每个子例程/函数,以及iso_c_binding
对于写入/输出的每个变量)。让我们称之为hello_fortran.f90
subroutine hello_fortran() bind(c)
print *, "Hello world from fortran"
end subroutine hello_fortran
如果您不想使用 iso_c_bindings 修改原始 Fortran 代码,您还可以在 Fortran 中编写一个简单的包装函数,直接调用原始代码。
使用 编译此代码以在-c
不链接的情况下进行编译,并-fPIC
生成与位置无关的代码。
gfortran hello_fortran.f90 -c -fPIC -o hello_fortran.o
您可以直接链接 setup.py 中的目标文件,但我发现将所有内容捆绑到共享库
gfortran *.o -shared -o libhello_fortran.so
中并将当前工作目录添加到 PATH更容易export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:$(pwd)
2. 允许从 Python 调用 C 函数的 Cython 模块(或者,您可以使用 CFFI——方法类似)。对于我们的示例,我们称之为“hello_cython.pyx”
cdef extern:
void hello_fortran()
def hello_cython():
print("Called hello_cython")
hello_fortran()
至关重要的是,对于您要调用的每个 Fortran 函数,您需要在cdef extern
块中声明 C 接口。请记住,fortran 不区分大小写:此块中的所有内容都应为小写。
setup.py
用于“编译” Cython 模块的文件。不幸的是,这些很快就会变得非常复杂,所以下面给出的是一个最小的工作示例from distutils.core import setup, Extension
from Cython.Build import cythonize
import numpy
files = ['hello_cython.pyx']
ext_module = Extension(
name = "hello_cython_FI",
sources = files,
include_dirs = ['.'],
library_dirs=['.'],
libraries=["hello_fortran"]
)
setup(
name = "hello_cython_FI",
ext_modules = cythonize(ext_module)
)
需要注意的重要事项是name
Python 中将给出模块的名称,files
必须包含.pyx
文件,并且libraries
必须具有共享库的名称(即 for libhello_fortran.so
, write hello_fortran
)
编译这个python setup.py build_ext --inplace
hello_python.py
调用 hello_cython_FI的标准 Python 脚本import hello_cython_FI
print("Called hello_python")
hello_cython_FI.hello_cython()
那么你应该得到
>>> python hello_python.py
Called hello_python
Called hello_cython
Hello world from fortran
在评论中询问 Intel、OpenMP 和 OpenMP 链接方法。
有一个名为 fwrap 的自动化工具,它生成 C、cython 和 python 到 fortran 例程的绑定。我认为它仍处于 beta 状态,但您可能会发现它很有帮助,链接在此处。