减少样板代码和模板化#
使用 FYPP 绑定通用接口#
f2py
目前不支持绑定接口块。但是,有一些解决方法可以使用。也许最著名的用法是使用 tempita
来使用 .pyf.src
文件,就像在 scipy 中的绑定 中所做的那样。tempita 支持已被移除,并且在任何情况下都不再推荐。
注意
接口无法在 f2py
本身中支持的原因是它们不对应于已编译库中导出的符号。
❯ nm gen.o
0000000000000078 T __add_mod_MOD_add_complex
0000000000000000 T __add_mod_MOD_add_complex_dp
0000000000000150 T __add_mod_MOD_add_integer
0000000000000124 T __add_mod_MOD_add_real
00000000000000ee T __add_mod_MOD_add_real_dp
在这里,我们将讨论一些利用 f2py
与 fypp 相结合的技术,以模拟通用接口并简化多个(类似)函数的绑定。
基本示例:加法模块#
让我们从用户指南(F2PY 示例)中接收两个数组并返回其和的子例程的示例构建。
C
SUBROUTINE ZADD(A,B,C,N)
C
DOUBLE COMPLEX A(*)
DOUBLE COMPLEX B(*)
DOUBLE COMPLEX C(*)
INTEGER N
DO 20 J = 1, N
C(J) = A(J)+B(J)
20 CONTINUE
END
我们将将其重铸为现代 fortran
module adder
implicit none
contains
subroutine zadd(a, b, c, n)
integer, intent(in) :: n
double complex, intent(in) :: a(n), b(n)
double complex, intent(out) :: c(n)
integer :: j
do j = 1, n
c(j) = a(j) + b(j)
end do
end subroutine zadd
end module adder
我们可以像在原始示例中那样继续,手动添加意图等等,但是在生产中通常还有其他问题。一方面,我们可以通过 FYPP 对类似函数的构造进行模板化
module adder
implicit none
contains
#:def add_subroutine(dtype_prefix, dtype)
subroutine ${dtype_prefix}$add(a, b, c, n)
integer, intent(in) :: n
${dtype}$, intent(in) :: a(n), b(n)
${dtype}$ :: c(n)
integer :: j
do j = 1, n
c(j) = a(j) + b(j)
end do
end subroutine ${dtype_prefix}$add
#:enddef
#:for dtype_prefix, dtype in [('i', 'integer'), ('s', 'real'), ('d', 'real(kind=8)'), ('c', 'complex'), ('z', 'double complex')]
@:add_subroutine(${dtype_prefix}$, ${dtype}$)
#:endfor
end module adder
这可以预处理以生成完整的 fortran 代码
❯ fypp gen_adder.f90.fypp > adder.f90
正如预期的那样,这随后可以被 f2py
包装。
现在,我们将考虑在单独的文件中维护绑定。请注意以下基本 .pyf
,它可以通过 f2py -m adder adder_base.f90 -h adder.pyf
为单个子例程生成
! -*- f90 -*-
! Note: the context of this file is case sensitive.
python module adder ! in
interface ! in :adder
module adder ! in :adder:adder_base.f90
subroutine zadd(a,b,c,n) ! in :adder:adder_base.f90:adder
double complex dimension(n),intent(in) :: a
double complex dimension(n),intent(in),depend(n) :: b
double complex dimension(n),intent(out),depend(n) :: c
integer, optional,intent(in),check(shape(a, 0) == n),depend(a) :: n=shape(a, 0)
end subroutine zadd
end module adder
end interface
end python module adder
! This file was auto-generated with f2py (version:2.0.0.dev0+git20240101.bab7280).
! See:
! https://web.archive.org/web/20140822061353/http://cens.ioc.ee/projects/f2py2e
带有文档字符串
c = zadd(a,b,[n])
Wrapper for ``zadd``.
Parameters
----------
a : input rank-1 array('D') with bounds (n)
b : input rank-1 array('D') with bounds (n)
Other Parameters
----------------
n : input int, optional
Default: shape(a, 0)
Returns
-------
c : rank-1 array('D') with bounds (n)
这已经很好了。但是,n
永远不应该传递,所以我们将做一些小的调整。
! -*- f90 -*-
! Note: the context of this file is case sensitive.
python module adder ! in
interface ! in :adder
module adder ! in :adder:adder_base.f90
subroutine zadd(a,b,c,n) ! in :adder:adder_base.f90:adder
integer intent(hide),depend(a) :: n=len(a)
double complex dimension(n),intent(in) :: a
double complex dimension(n),intent(in),depend(n) :: b
double complex dimension(n),intent(out),depend(n) :: c
end subroutine zadd
end module adder
end interface
end python module adder
! This file was auto-generated with f2py (version:2.0.0.dev0+git20240101.bab7280).
! See:
! https://numpy.com.cn/doc/stable/f2py/
这对应于
In [3]: ?adder.adder.zadd
Call signature: adder.adder.zadd(*args, **kwargs)
Type: fortran
String form: <fortran function zadd>
Docstring:
c = zadd(a,b)
Wrapper for ``zadd``.
Parameters
----------
a : input rank-1 array('D') with bounds (n)
b : input rank-1 array('D') with bounds (n)
Returns
-------
c : rank-1 array('D') with bounds (n)
最后,我们可以以类似的方式对它进行模板化,以实现最初的目标,即拥有使用 f2py
指令并具有最少冗余的绑定。
! -*- f90 -*-
! Note: the context of this file is case sensitive.
python module adder ! in
interface ! in :adder
module adder ! in :adder:adder_base.f90
#:def add_subroutine(dtype_prefix, dtype)
subroutine ${dtype_prefix}$add(a,b,c,n) ! in :adder:adder_base.f90:adder
integer intent(hide),depend(a) :: n=len(a)
${dtype}$ dimension(n),intent(in) :: a
${dtype}$ dimension(n),intent(in),depend(n) :: b
${dtype}$ dimension(n),intent(out),depend(n) :: c
end subroutine ${dtype_prefix}$add
#:enddef
#:for dtype_prefix, dtype in [('i', 'integer'), ('s', 'real'), ('d', 'real(kind=8)'), ('c', 'complex'), ('z', 'complex(kind=8)')]
@:add_subroutine(${dtype_prefix}$, ${dtype}$)
#:endfor
end module adder
end interface
end python module adder
! This file was auto-generated with f2py (version:2.0.0.dev0+git20240101.bab7280).
! See:
! https://numpy.com.cn/doc/stable/f2py/
用法归结为
fypp gen_adder.f90.fypp > adder.f90
fypp adder.pyf.fypp > adder.pyf
f2py -m adder -c adder.pyf adder.f90 --backend meson