python - 使用 python-ctypes 将 fortran 与 python 接口(interface)

标签 python fortran ctypes fortran90 gfortran

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

我一直在寻找一种方法来使用 fortran 代码来完成我在 python 方面的博士工作——随后使用 matplotlib 进行可视化的动态计算。

THIS POST帮助(这表明可以使用 ctypes 模块在 python 中使用/调用 fortran 代码 - 并且考虑到 fortran 函数具有绑定(bind)到它们的备用名称 - 这在逻辑上对我来说很有意义,尽管我不知道它是如何工作的细节。但我们确实明智地选择了我们的战斗!)。
那么this SO post也处理从 python 调用 fortran 函数。

下一个合乎逻辑的步骤是查找 documentation for the python module ctypes .这讨论了如何在 API 级别使用 python 访问共享库。

我有所有的东西来制作一个最小的工作示例,another answer has already done .但我想看看涉及真实 float 的输出机制和数学运算。这是我做的测试用例。

测试.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

创建共享对象

$gfortran -shared  -g -o test.so test.f90

编辑:出于某种原因,我的工作计算机需要 -fPIC 选项才能编译

为了确保我的两个函数 prntsin_2 在某处,我检查了 nm:

$ nm test.so | tail -3  
0000067f T prnt_
0000065c T sin_2_
         U sinf@@GLIBC_2.0

到目前为止一切顺利。我的函数 prntsin_2 已经映射到库中的 prnt_sin_2_ 上。

从 python 解释器调用 fortran 函数

这就是所有这一切变得有点湿透的地方。使用the table in python-ctypes documentation ,我输入了以下内容 -

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

我想每个输出末尾打印的 1 是 python 让我知道 t.prnt_ 的 bool 输出是 .true. 的方式。< br/> 当我切换到字符串的正确数据类型时,我有点担心 t.prnt_ 的情况会变得更糟。文字打印没问题,只是结尾有一个 e。那是 EOL 角色吗?

然后是t.sin_2_函数。我决定用它来计算 sin(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)

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

最佳答案

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

测试.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

请记住为函数设置 ctypes argtypes,并在适当的地方设置 restype。在这种情况下,sin_2 接受一个浮点指针并返回一个 float 。

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

关于python - 使用 python-ctypes 将 fortran 与 python 接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15875533/

相关文章:

python - 使用 python 获取 Oracle 数据库的模式

python - 从上下文管理器中产生是好的做法吗?

matlab - 如何在 Fortran mex 文件中实现 A = sparse(I, J, K) (来自三元组的稀疏矩阵)?

python - 将带有数组的嵌套 ctypes 结构传递给 C 库函数不符合预期

python : can I get a memoryview or a bytearray mapping to a mmap

python - 在 Python 3.x 下将字符串传递给 ctypes 函数

python - python3 之后的 ionic "Error with start undefined"

Python:波的可视化

fortran - 覆盖不同模块中的私有(private)函数

fortran - 将 Fortran 2D 数组打印为矩阵