【问题标题】:call functions from a shared fortran library in python从python中的共享fortran库调用函数
【发布时间】:2023-03-11 10:51:01
【问题描述】:

我想从 Python 中的 Fortran 共享库中调用一些函数。我在网上找到了一些链接并阅读了它们,根据我的发现,我应该这样做

libadd = cdll.LoadLibrary('./libbin.so') 

加载共享对象。但是,此共享对象包含来自另一个共享库的一些符号。我阅读了 cdll 的帮助,但是似乎无法同时加载多个共享对象文件。我如何调用这个 Fortran 库中的函数,它很可能是由英特尔 Fortran 编译器编译的?

【问题讨论】:

  • 您可能还想查看f2py。它是 NumPy 的一部分。

标签: python fortran fortran-iso-c-binding


【解决方案1】:

您需要知道共享对象中函数的签名。您是否有源代码或解释函数名称和参数类型的参考资料?

例如,我有这个源代码(mult.f90):

integer function multiply(a, b)
    integer, intent(in) :: a, b
    multiply = a * b
end function multiply

.. 为了演示如何一次加载和使用多个共享对象,我还有 (add.f90):

integer function addtwo(a, b)
    integer, intent(in) :: a, b
    addtwo = a + b
end function addtwo

编译,检查符号:

% gfortran-4.4 -shared -fPIC -g -o mult.so mult.f90
% gfortran-4.4 -shared -fPIC -g -o add.so add.f90
% nm -ao mult.so | grep multiply
mult.so:00000000000005cc T multiply_

请注意,共享对象中的符号名称附加了一个下划线。既然有源码,就知道签名是multiply_(int *a, int *b),所以很容易从ctypes调用那个函数:

from ctypes import byref, cdll, c_int

mult = cdll.LoadLibrary('./mult.so')
add = cdll.LoadLibrary('./add.so')
a = c_int(2)
b = c_int(4)
print mult.multiply_(byref(a), byref(b))
print add.addtwo_(byref(a), byref(b))

输出:

8
6

【讨论】:

  • 感谢您的精彩回答。我有一个快速跟进的问题。当我检查库的名称列表 (nm mult.so) 时,它显示 _multiply_ 而不是 multiply_,尽管后者仍然适用于 C 和 python。你知道为什么会这样吗?
  • 不客气。不同的编译器可能会在符号名称前添加/附加下划线。我知道在 gfortran 的情况下,您可以提供 -fno-underscoring 来关闭它;导致该函数的符号multiply。你的编译器+平台可能有这个选项。如果您正在处理未知来源的共享对象,我将使用另一种可能的解决方案更新答案。
  • 实际上,我将暂缓进一步评论我听到的关于编译器选项的评论,因为这可能会为您解决问题。底线是你需要知道你正在调用的函数的签名,所以你想通过纠正上游的事情来最小化猜测。
  • @samplebias 我应该提到我使用的是 OS X。前导下划线不会出现在 Linux 库中,所以它可能是 OS X(或 BSD)的东西。由于这超出了问题的范围,我可能应该把它留在那里。感谢您的调查。
  • @samplebias:在共享对象文件之一中引用的一些符号仍未定义。这些论文位于另一个对象文件中,但是尝试加载该共享对象时会出现无法识别对象文件的错误,您知道吗?
【解决方案2】:

我会在@sameplebias 的答案中添加,可以使用iso_c_binding 模块强制(任何)fortran 编译器生成正确的C 签名。用法示例:

module fmesh_wrapper

use iso_c_binding, only: c_double, c_int
use fmesh, only: mesh_exp

implicit none

contains

subroutine c_mesh_exp(r_min, r_max, a, N, mesh) bind(c)
real(c_double), intent(in) :: r_min
real(c_double), intent(in) :: r_max
real(c_double), intent(in) :: a
integer(c_int), intent(in) :: N
real(c_double), intent(out) :: mesh(N)
call mesh_exp(r_min, r_max, a, N, mesh)
end subroutine

! wrap more functions here
! ...

end module

这将具有以下 C 签名:

void c_mesh_exp(double *r_min, double *r_max, double *a, int *N,
        double *mesh);

然后你可以像往常一样从 Python 调用它。这种方法的优点是它适用于所有平台(无需使用任何特殊的编译器选项)。

【讨论】:

    【解决方案3】:

    要使 f2py(来自 NumPy)工作,请从 @samplebias 借用 mult.f90add.f90 示例。从 shell 编译 Python 可导入共享库:

    f2py -c -m mult mult.f90
    f2py -c -m add add.f90
    

    现在在 Python 中使用它们:

    >>> import add
    >>> import mult
    >>> add.addtwo(4, 5)
    9
    >>> mult.multiply(4, 5)
    20
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-22
      • 2021-12-28
      • 2011-02-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-25
      • 1970-01-01
      相关资源
      最近更新 更多