使用python-ctypes将fortran与python连接起来

Deb*_*asu 8 python fortran ctypes gfortran fortran90

经验:
fortran大约3个月
python - 中间:在此之前从未在python中使用过ctypes模块

我正在寻找一种方法来在python中使用fortran代码进行我的博士工作 - 随后使用matplotlib即时使用可视化计算进行可视化.

这个帖子有帮助(这告诉我可以使用ctypes模块在python中使用/调用fortran代码 - 并且鉴于fortran函数具有绑定到它们的备用名称 - 这在逻辑上对我有意义,尽管我不知道它是如何工作的细节,但我们不要选择我们的战斗明智的!).
然后这个SO帖子也处理从python调用fortran函数.

下一个合乎逻辑的步骤是查找python模块ctypes文档.这里讨论了如何在API级别使用python访问共享库.

我把所有的部分都做成了一个最小的工作示例,另一个答案已经完成了.但我想看看涉及真实浮点数的输出机制和数学运算.这是我做的测试用例.

test.f90

function prnt(s)
    character(80):: s
    logical :: prnt
    print*, s
    prnt = .true.
end function prnt

function sin_2(r)
    real:: r,sin_2
    sin_2 = sin(r)**2
end function sin_2
Run Code Online (Sandbox Code Playgroud)

制作共享对象

$gfortran -shared  -g -o test.so test.f90
Run Code Online (Sandbox Code Playgroud)

编辑:由于某种原因,我的工作计算机需要-fPIC选项进行编译

为了确保我的两个功能prnt,sin_2并在那里,我检查nm:

$ nm test.so | tail -3  
0000067f T prnt_
0000065c T sin_2_
         U sinf@@GLIBC_2.0
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.我的功能prntsin_2已映射到prnt_sin_2_存储库中.

从python解释器调用fortran函数

这是所有这一切都有点潮湿的地方.使用python-ctypes文档中的表,我输入以下内容 -

>>> from ctypes import byref, cdll, c_float,c_char_p
>>> t = cdll.LoadLibrary('./test.so')
>>> c = c_char_p("Mary had a little lamb")
>>> t.prnt_('Mary had a little lamb')
 Mary had a little lambe
1
>>> t.prnt_("Mary had a little lamb")
 Mary had a little lambe
1
>>> t.prnt_(c)
 Mary had a little lambe[?  .prnt_(c)

1
Run Code Online (Sandbox Code Playgroud)

我想在每个输出结尾打印的1是python让我知道布尔输出为的t.prnt_方式.true..当我切换到字符串的正确数据类型时,我
有点担心情况会变得更糟t.prnt_.文字打印好,只有e最后一个.这是EOL角色吗?

然后是t.sin_2_功能.我决定用它来计算罪(4.56)**2.这是怎么回事 -

>>> f = c_float(4.56)
>>> t.sin_2_(4.56)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
 ctypes.ArgumentError: argument 1: <type 'exceptions.TypeError'>: Don't know how to convert parameter 1
>>> t.sin_2_(f)
Segmentation fault (core dumped)
Run Code Online (Sandbox Code Playgroud)

我在哪里错了?我试图解释我是如何处理这个问题的,这样如果我在某个地方做出明显的失误就会很明显.
与其他SO帖子的大量链接是为了帮助其他人提出与我现在相同的问题.

Ery*_*Sun 8

在Fortran中,参数通过引用传递.Fortran字符数组不以null结尾; 长度作为隐式long int参数通过值传递.此外,Python的float类型是a double,因此您可能希望使用Fortran real(8)来保持一致性.

test.f90:

function prnt(s)          ! byref(s), byval(length) [long int, implicit]
    character(len=*):: s  ! variable length input
    logical :: prnt
    write(*, "(A)") s     ! formatted, to remove initial space
    prnt = .true.
end function prnt

function sin_2(r)         ! byref(r)
    real:: r, sin_2       ! float; use real(8) for double
    sin_2 = sin(r)**2
end function sin_2
Run Code Online (Sandbox Code Playgroud)

请记住argtypes为函数设置ctypes ,并restype在适当的位置.在这种情况下,sin_2采用浮点指针并返回一个浮点数.

ctypes示例:

>>> from ctypes import *
>>> test = CDLL('./test.so')

>>> test.prnt_.argtypes = [c_char_p, c_long]
>>> test.sin_2_.argtypes = [POINTER(c_float)]
>>> test.sin_2_.restype = c_float

>>> s = 'Mary had a little lamb'
>>> test.prnt_(s, len(s))
Mary had a little lamb
1

>>> x = c_float(4.56)
>>> test.sin_2_(byref(x))
0.9769567847251892
Run Code Online (Sandbox Code Playgroud)

  • 使用-v选项编译以查看`gfortran`正在做什么.它调用编译器`f951`来创建.s汇编程序文件.接下来,它调用[assembler`as`](http://sourceware.org/binutils/docs-2.23.1/as)来创建.o对象文件.最后它调用[`collect2`](http://gcc.gnu.org/onlinedocs/gccint/Collect2.html),它调用[linker`ld`](http://sourceware.org/binutils/docs- 2.23.1/ld)创建一个ELF .so共享对象.查看`readelf -d test.so`的输出以查看链接库,例如libgfortran. (2认同)

小智 5

我经常使用f2py,它很简单。如果您的代码符合 Fortran 90 声明(例如double precision, intent(in) :: myvar),f2py 将自动将您的代码包装为 C 并编译为可通过 Python 直接调用的共享对象。您可以像导入任何其他 python 模块一样导入 f2py 创建的模块。C 包装器是透明的并处理 Python 和 Fortran 之间的类型接口,您根本不必与 c_types 混淆。我肯定会推荐这条路线。


归档时间:

查看次数:

4791 次

最近记录:

6 年,10 月 前