1 前言
网上关于Python调用Fortran程序的方法通常分为三种:1)基于f2py;2)生成动态链接库;3)生成可执行文件。
其中第1种方法在涉及到“祖传”代码时,通常会出现各种报错;第3种方法在进行数据传递时基本只能通过操作文件的方式,很不方便。
由于涉及Fortran程序时,一般都逃不开“祖传”代码,因此本文将介绍最为稳定可靠的第2种方法。
2 方法详情
2.1 示例代码
- 新建
test01.f90
文件,创建子例程sub_test01
以及函数func_test01
,详细内容如下
subroutine sub_test01(x,y,z) bind(C,name="sub_test01")
use iso_c_binding
real(c_double), intent(in), value :: x,y
real(c_double), intent(out) :: z(2)
z(1) = x + x
z(2) = y*y
end subroutine sub_test01
function func_test01(x,y) result(z) bind(c,name="func_test01")
use iso_c_binding
real(c_double), intent(in), value :: x,y
real(c_double) :: z
z = x + y
end function func_test01
bind
:用于声明外部调用时子例程/函数名称iso_c_binding
:Fortran自带的模组,必须引用intent
:声明变量属性,输入为in
,输出为out
,即是输入也是输出为inout
c_double
:变量类型,real
对应c_double
,integer
对应c_int
value
:输入变量为单个值时,需添加此标记
2.2 生成动态链接库
- 与正常编译相比增加
-shared
,生成后缀为.so
的文件,如下
gfortran -shared test01.f90 -o test01.so
如果此步骤报错
recompile with -fPIC
,则在-shared
后加上-fPIC
。
2.3 使用Python调用
- 调用
sub_test01
子例程,需要引用ctypes
和numpy
,示例代码如下
import ctypes as ct
import numpy as np
# 加载动态链接库
fortlib = ct.CDLL('test01.so')
# 引用sub_test01子例程
f_sub = fortlib.sub_test01
# 声明变量类型
f_sub.argtypes = [ct.c_double, ct.c_double, ct.POINTER(ct.c_double)]
# 输入变量赋值
x = ct.c_double(3)
y = ct.c_double(4)
# 输出变量初始化
z = np.ones(2)
z_p = z.ctypes.data_as(ct.POINTER(ct.c_double))
# 调用sub_test01子例程
f_sub(x,y,z_p)
print(z)
- 使用
ctypes.CDLL(<so name>)
加载动态链接库,其中<so name>
为上一节生成的动态链接库名称- 子例程的引用名称为上一节
bind
中name
定义的名称argtypes
用于声明变量类型,其中ctypes.POINTER
表示指针。当变量为单个值(Fortran代码中value
)时,声明为相应类型;当变量为数组(或输出变量,即intent(out)
)时,声明为指针ctypes.c_double(<value>)
,其中<value>
为变量的值- 打印结果为
[6. 16.]
- 调用
func_test01
函数,与子例程调用方式基本相同,示例代码如下
import ctypes as ct
# 加载动态链接库
fortlib = ct.CDLL('test01.so')
# 引用sub_test01子例程
f_sub = fortlib.func_test01
# 声明变量类型
f_sub.argtypes = [ct.c_double, ct.c_double]
# 声明结果类型
f_sub.restype = ct.c_double
# 输入变量赋值
x = ct.c_double(3)
y = ct.c_double(4)
# 调用sub_test01子例程
z = f_sub(x,y)
print(z)
restype
声明返回值的类型- 打印结果为
7.0
评论 (0)