python包-f2py使用流程

个人使用 f2py 的流程整理。

修改 fortran 脚本

  1. 需要直接调用的子例程中的参数中,如果存在数组,那么同时需要将数组的维度作为参数添加进来

  2. 需要直接调用的子例程中的参数中,如果存在数组,那么定义数组参数时,需要准确定义数组的维度(因为有些子例程中数组维度会定义为1)。

  3. 定义变量后,通过注释字符+f2py 定义参数的 intent 属性,总共有三种属性,解释如下:

    • in :输入变量,只读

    • out: 输出变量,输出变量不会出现在模块的输入参数中,如果有多个输出变量,会以元组的形式同时输出。

    • in, out : 输入输出变量,会对输入变量进行修改,再输出,如果有多个输出变量,会以元组的形式同时输出。

举个例子,根据下面的脚本,假设这个子例程原本就只有两个数组参数 a, b ,那么首先第一步我们要将 nzen 加入到子例程的参数中,第二步因为定义 a, b 时其维度已经定义好了,不需要动,第三步就是像最后两句一样定义 a, bintent ,这里就是需要同时输入 a, b ,然后输出 a

1
2
3
      real*8 a(nze),b(n)
cf2py intent(in,out) a
cf2py intent(in) b

检查 fortran 脚本

在 linux 中逐个编译需要用到的 fortran 脚本,看看是否有语法问题。

1
gfortran -c *.f

调用 F2PY

两种方式:既可以使用命令行工具f2py ,也可以使用 python 模块 numpy.f2py

如果使用第一种方式,则直接运行

1
f2py

如果使用第二种方式,则运行命令为

1
python -m numpy.f2py

按照下面的命令进行打包,-c 后面接 fortran 脚本名称,可以包含多个脚本;-m 后面接输出的模块名称。

运行日志中如果最后有 Removing build directory *** 字样并且没有报错信息,则说明运行正常。并且在当前目录中会生成 modulename.cpython-39-x86_64-linux-gnu.so(在windows 中后缀为 .pyd ) 。

1
f2py -c *.f  -m modulename 

然后在 ipython 页面中,导入生成的模块,查看其文档 (__doc__)。

首先模块的文档包含了所有可用的函数。

1
2
3
4
5
6
7
In [2]: import fib1

In [3]: print(fib1.__doc__)
This module 'fib1' is auto-generated with f2py (version:1.21.5).
Functions:
fib(a,n=len(a))
.

然后查看需要调用的函数的文档,可以看到所有输入函数的类型。

这里有两个作用,第一是检查一下是否和之前设置的参数的 intent 属性保持一致(如果不一致,重新调用一遍 F2PY 覆盖一下),第二就是看如何在 python 中运行这个模块 (看第一行)。这个模块在 python 中的调用方式就是 ia,ja,a,b = test(option,ia,ja,a,b,[n,nze])

这里也可以看到,虽然我们需要在 fortran 脚本将数组参数的维度也作为参数加入进来(nnze),但是经过 F2PY 打包之后,我们在 python 中运行其实是不需要输入数组维度的(可选参数),因为会自动从输入数组维度中得到 nnze

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
In [2]: print(test.test.__doc__)
ia,ja,a,b = test(option,ia,ja,a,b,[n,nze])

Wrapper for ``test``.

Parameters
----------
option : input int
ia : input rank-1 array('i') with bounds (n + 1)
ja : input rank-1 array('i') with bounds (nze)
a : input rank-1 array('d') with bounds (nze)
b : input rank-1 array('d') with bounds (n)


Other Parameters
----------------
n : input int, optional
Default: (len(ia)-1)
nze : input int, optional
Default: len(ja)

Returns
-------
ia : rank-1 array('i') with bounds (n + 1)
ja : rank-1 array('i') with bounds (nze)
a : rank-1 array('d') with bounds (nze)
b : rank-1 array('d') with bounds (n)

除了在 ipython 界面运行,你也可以按照下面的命令生成一个签名文件 (*.pyf) ,查看子例程参数的属性。

1
f2py *.f  -m * -h *.pyf

在 python 中运行

唯一需要注意的地方就是输入数组的数据类型必须正确,不然不会对输入数组进行修改,也不会报错。

否则 F2PY会生成输入数组的连续副本(具有正确的dtype),并将副本的C指针传递给Fortran子例程。因此,对输入数组(副本)的任何可能更改都不会影响原始参数。

比如说 fortran 中 REAL*8 对应的就是 python 中的 float 及数组中的 np.float64

注意 array 的顺序

注意 numpy 数组在内存中数据顺序一直使用 C 的惯例(称为行优先顺序),而 Fortran 使用列优先顺序。举个例例子,矩阵[ [1, 2], [3, 4] ] 在内存中按行优先顺序排列成[1, 2, 3, 4],按列优先顺序排列成
[1, 3, 2, 4]。

因此对于2维数组,在声明时需要指定其 order=’F’ ,即按照列优先顺序排列,从而与 Fortran 保持一致。

参考文献

  1. https://numpy123.com/f2py/

  2. https://blog.finaltheory.me/research/Introduction-to-F2PY.html

  3. https://sites.engineering.ucsb.edu/~shell/che210d/f2py.pdf

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2019-2024 Vincere Zhou
  • 访问人数: | 浏览次数:

请我喝杯茶吧~

支付宝
微信