Cython, C and Fortran

4
我想请您帮忙翻译一下,关于通过C函数调用Fortran函数的问题。这些C函数将通过Cython在Python代码中使用。整个流程如下:
Cython模块 -> C函数 -> Fortran函数,其中“->”表示“调用”。
目前我已经成功从Cython调用了C函数,但是我在调用Fortran函数方面遇到了困难。您能帮助我吗?(提供一个简单的示例会很好。)
谢谢您提前的帮助。
编辑: 我正在使用gcc 4.1.2和gfortran。

一个更精确的答案可能需要知道您所询问的具体C和Fortran编译器。 - SingleNegationElimination
抱歉,我忘记编辑了 :) - nunolourenco
你是否了解fwrap - ephemient
3个回答

7
第一个答案中的链接描述了过时的方法。随着将ISO C绑定添加到Fortran中,从C调用Fortran或从Fortran调用C变得更加容易。使用这个Fortran语言特性指示Fortran编译器生成与C二进制兼容的可执行代码。程序员不必“hack”连接,并且由于它是语言的一部分,它是编译器和平台无关的。从技术上讲,ISO C Binding是Fortran 2003的一部分,但自几年前以来已经在许多编译器中提供,例如gfortran自4.3版本以来和Intel ifort。
要从C调用Fortran子例程或函数,您需要使用绑定C选项声明Fortran子例程或函数,并使用提供的绑定中的C兼容类型来声明参数。在gfortran手册的“混合语言编程”下有示例。由于ISO C Binding是语言的一部分,因此该手册的这一部分基本上独立于编译器。在Stack Overflow和其他网站的先前答案中也有其他示例。
以下是一个快速的代码片段(未经测试),用于从C调用Fortran子例程的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

bind C名称"MyTest"覆盖了Fortran名称——它区分大小写,与Fortran不同。不需要担心下划线!变量类型应该很明显...请参阅gfortran手册或其他文档以获取可用内容。


嗯,我本来以为我可以做到它,而不必修改FORTRAN代码,因为它是给我的一个黑盒。现在我终于想出一种方法了,多亏了你的建议,非常感谢 :) - nunolourenco

2

由于这是调试HPC代码的有用方法,因此这里提供了一个简单的Fortran“hello world”程序,可以从Python中调用。

使用GNU Fortran (GCC) 9.3.0Python 3.7.3

您需要至少:

  1. 一个Fortran代码,具有C绑定(即每个子例程/函数都有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 并使用 export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:$(pwd) 将当前工作目录添加到 PATH 中。

2. 一个 Cython 模块,允许从 Python 调用 C 函数(或者,您可以使用 CFFI,方法类似)。对于我们的示例,让我们称其为“hello_cython.pyx”。

cdef extern:
    void hello_fortran()

def hello_cython():
    print("Called hello_cython")
    hello_fortran()

重要的是,对于每个要调用的Fortran函数,您需要在 cdef extern 块中声明C接口。请记住, Fortran不区分大小写:此块中的所有内容都应为小写。
  1. 一个 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 必须有共享库的名称(即对于 libhello_fortran.so,写入 hello_fortran)。
使用命令 python setup.py build_ext --inplace 进行编译。
一个标准的 Python 脚本 hello_python.py 调用 hello_cython_FI。
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链接方法的信息,请在评论中提问。


1

有一个名为"fwrap"的自动化工具,可以生成Fortran例程的C、Cython和Python绑定。我认为它仍处于测试阶段,但您可能会发现它很有用,链接在这里


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接