为了账号安全,请及时绑定邮箱和手机立即绑定

通过 ctypes 在 Fortran 中的 python 回调

通过 ctypes 在 Fortran 中的 python 回调

三国纷争 2022-05-19 18:35:09
考虑这个 C 互操作的 Fortran 子例程,它从 Python 调用并将 Python 回调函数作为输入参数,然后调用它,module FortranFunc_mod    ! C-interoperable interface for the python callback    abstract interface        function getSquare_proc( x ) result(xSquared) bind(C)            use, intrinsic :: iso_c_binding, only: c_double            real(c_double), intent(in)              :: x            real(c_double)                          :: xSquared        end function getSquare_proc    end interfacecontains    subroutine fortranFunc( getSquareFromPython ) bind(C, name="fortranFunc")        !DEC$ ATTRIBUTES DLLEXPORT :: fortranFunc        use, intrinsic :: iso_c_binding, only: c_funptr, c_f_procpointer, c_double        implicit none        type(c_funptr), intent(in)          :: getSquareFromPython        procedure(getSquare_proc), pointer  :: getSquare        real(c_double)                      :: x = 2._c_double, xSquared        ! associate the input C procedure pointer to a Fortran procedure pointer        call c_f_procpointer(cptr=getSquareFromPython, fptr=getSquare)        xSquared = getSquare(x)        write(*,*) "xSquared = ", xSquared    end subroutine fortranFuncend module FortranFunc_modPython 函数可能如下所示,import numpy as npimport ctypes as ct# import dll and define result typeff = ct.CDLL('FortranFunc_mod')ff.fortranFunc.restype = None# define and decorate Python callback with propoer ctypes@ct.CFUNCTYPE( ct.c_double, ct.c_double ) # result type, argument typedef getSquareFromPython(x): return np.double(x**2)# call Fortran functionff.fortranFunc( getSquareFromPython )但是,使用 ifort 编译此代码(已成功完成)然后运行 Python 代码会导致以下错误,在这个简单的例子中我错过了什么?Fortran 和 python 代码之间是否需要额外的 C-wrapper 来定义回调原型?如果您还可以提供 C 等效代码来调用 Python 函数,那也会有所帮助。
查看完整描述

1 回答

?
慕妹3146593

TA贡献1820条经验 获得超9个赞

您的示例的主要问题是ff.fortranFunc只指定了它的返回类型,而不是它的参数类型。Fortran 子例程fortranFunc有一个输入参数,type(c_funptr)这也应该反映在 Python 端。

究竟如何实现解决方案,取决于您是否只想在 Python 中进行更改,或者也愿意在 Fortran 源代码中进行更改。我将概述这两种解决方案:

仅在 Python 中进行更改

以下是 Python 测试例程的更新版本(我称之为test.py),具有以下特定更改:

  • 指定ff.fortranFunc.argtypes

  • thearg_type被指定为指向 a 的指针c_double- 如何在 C 中传递标量参数

  • 回调函数getSquareFromPython也被修改以反映这一点x[0]

(有关最后两点的详细信息,请参阅ctypes 文档- 2.7 版本可能更清楚地解释了这一点)

import ctypes as ct


# callback function ctypes specification

return_type = ct.c_double

arg_type = ct.POINTER(ct.c_double)

func_spec = ct.CFUNCTYPE(return_type, arg_type)


# import dll and define result AND argument type

ff = ct.CDLL('FortranFunc_mod')

ff.fortranFunc.restype = None

ff.fortranFunc.argtypes = [ct.POINTER(func_spec),]


# decorate Python callback

@func_spec

def getSquareFromPython(x):

    return x[0]**2


# call Fortran function

ff.fortranFunc( getSquareFromPython )

在 Python 和 Fortran 中进行更改

如果您希望更接近原始 Python 实现,也可以通过对以下内容进行以下更改来实现test.py:


changefortranFunc的参数类型:arg_type = ct.c_double

改变getSquareFromPython的返回值:x**2

但是,由于回调函数现在需要一个作为输入参数(而不是指向一个的指针),因此您必须通过将属性添加到虚拟参数c_double来更改 Fortran 抽象接口以反映这一点:valuex


abstract interface

    function getSquare_proc( x ) result(xSquared) bind(C)

        use, intrinsic :: iso_c_binding, only: c_double

        real(c_double), intent(in), value       :: x

        real(c_double)                          :: xSquared

    end function getSquare_proc

end interface

编译运行

编译并运行代码的任一修改版本,在 Windows 上使用 ifort 给我以下结果(适当更改编译命令和库名称,它也适用于 Linux 和 OS X 上的 gfortran):


> ifort /DLL FortranFunc_mod.f90 /o FortranFunc_mod.dll

...

> python test.py

 xSquared =    4.00000000000000


注意两种情况的区别

getSquareFromPython通过查看' 参数的动态类型可以清楚地反映两种实现之间的差异x(它还解释了两种替代方案所需的符号更改)。对于提出的第一个替代方案,您可以将左侧的语句添加到getSquareFromPython,以获得右侧显示的结果:


print(type(x).__name__)                 :  LP_c_double

print(type(x.contents).__name__)        :  c_double

print(type(x.contents.value).__name__)  :  float

print(type(x[0]).__name__)              :  float

print(x.contents.value == x[0])         :  True

而对于第二种选择:


print(type(x).__name__)                 :  float


查看完整回答
反对 回复 2022-05-19
  • 1 回答
  • 0 关注
  • 169 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信