仅在内部使用派生类型的 F2PY 包装过程

F2PY wrap procedure that uses derived types only internally

很多地方都说F2PY不“支持派生类型”,但是我不清楚这是否意味着

对于我的用例来说,第一点是不便,第二点是破坏交易的因素。

例如,考虑这个计算向量和的模块:

module derived_types

implicit none
public :: point, add

type :: point
   real :: x
   real :: y
end type point

contains

type(point) function add(p1, p2)

type(point), intent(in) :: p1
type(point), intent(in) :: p2

add%x = p1%x + p2%x
add%y = p1%y + p2%y

end function add

end module derived_types


module expose

use derived_types, only: point, add
implicit none

contains

subroutine vector_sum(x1, y1, x2, y2, x3, y3)

real, intent(in) ::  x1, y1, x2, y2
real, intent(out) :: x3, y3

type(point) :: p1, p2, p3

p1 = point(x1, y1)
p2 = point(x2, y2)

p3 = add(p1, p2)

x3 = p3%x
y3 = p3%y

end subroutine vector_sum

end module expose

子例程 vector_sum 应暴露给 Python。派生类型 point 不能在 Python 和 Fortran 之间传递。

这作为普通 Fortran 程序(添加了适当的程序块)但 F2PY 失败:

f2py -c ff.f90 only: vector_sum

running build
running config_cc
unifing config_cc, config, build_clib, build_ext, build commands --compiler options
running config_fc
unifing config_fc, config, build_clib, build_ext, build commands --fcompiler options
running build_src
build_src
building extension "untitled" sources
f2py options: ['only:', 'vector_sum', ':']
f2py:> /tmp/tmpjdq8b9dq/src.linux-x86_64-3.8/untitledmodule.c
creating /tmp/tmpjdq8b9dq/src.linux-x86_64-3.8
Reading fortran codes...
    Reading file 'ff.f90' (format:free)
Line #7 in ff.f90:"type :: point "
    analyzeline: No name/args pattern found for line.
Post-processing...
    Block: untitled
            Block: derived_types
                Block: unknown_type
            Block: expose
                Block: vector_sum
            Block: run
Post-processing (stage 2)...
    Block: untitled
        Block: unknown_interface
            Block: derived_types
                Block: unknown_type
            Block: expose
                Block: vector_sum
            Block: run
Building modules...
    Building module "untitled"...
        Constructing F90 module support for "derived_types"...
          Variables: point add
getctype: No C-type found in "{'attrspec': ['public']}", assuming void.
Traceback (most recent call last):
File "/home/me/.pyenv/versions/anaconda3-2020.11/lib/python3.8/site-packages/numpy/f2py/f90mod_rules.py", line 143, in buildhooks
    at = capi_maps.c2capi_map[ct]
KeyError: 'void'

这样的事情完全可以使用 F2PY 来完成吗?

您可以使用单个过程,在其中定义派生类型,也可以使用包含的过程。 这是一种快速而肮脏的方法,仅适用于较小的问题。

示例实现如下

! file: a.f90
subroutine vector_sum(x1, y1, x2, y2, x3, y3)
  real, intent(in)  :: x1, y1, x2, y2
  real, intent(out) :: x3, y3

  type point
    real :: x(2)
  end type

  type(point) :: p1, p2, p3

  p1%x = [x1, y1]
  p2%x = [x2, y2]

  p3 = add(p1, p2)

  x3 = p3%x(1)
  y3 = p3%x(2)

contains

  type(point) function add(p1, p2)
    type(point), intent(in) :: p1
    type(point), intent(in) :: p2

    add%x = p1%x + p2%x
  end function

end subroutine

编译

$ f2py -c a.f90 -m amod

在python

中的用法
>>> import amod
>>> amod.vector_sum(1, 2, 3, 4)
(4.0, 6.0)

经过一些修改,我认为首先将定义所有派生类型的代码编译到静态库中可能是最简单的,即:

libff.f90 包含:

module derived_types

implicit none

public :: point, add

type :: point
   real :: x
   real :: y
end type point

contains

type(point) function add(p1, p2)

type(point), intent(in) :: p1
type(point), intent(in) :: p2

add%x = p1%x + p2%x
add%y = p1%y + p2%y

end function add

end module derived_types

gfortran -free -c libff.f90 编译生成 libff.o

然后使用预编译模块的派生类型的代码可以在链接库时使用F2PY编译,即:

ff.f90 包含:

module expose

use derived_types, only: point, add
implicit none

contains

subroutine vector_sum(x1, y1, x2, y2, x3, y3)

real, intent(in) ::  x1, y1, x2, y2
real, intent(out) :: x3, y3

type(point) :: p1, p2, p3

p1 = point(x1, y1)
p2 = point(x2, y2)

p3 = add(p1, p2)

x3 = p3%x
y3 = p3%y

end subroutine vector_sum

end module expose

在使用 F2PY 与 f2py -c ff.f90 libff.o -m ff 构建 Python 扩展后,我们可以将其导入 Python 与 from ff.expose import vector_sum

经验教训:将派生类型的定义放入预编译库中,然后使用 F2PY 仅编译使用这些类型的代码。