可分配数组的f2py错误

时间:2016-01-03 18:03:58

标签: python fortran f2py

我有一个Fortran子程序,我想在Python中使用。

subroutine create_hist(a, n, dr, bins, hist)
    integer, intent(in) :: n
    real(8), intent(in) :: a(n)
    real(8), intent(in) :: dr
    integer, intent(out), allocatable :: hist(:)
    real(8), intent(out), allocatable :: bins(:)

    n_b = n_bins(a, n, dr)  ! a function calculating the number of bins
    allocate(bins(n_b+1))
    allocate(hist(n_b))
    hist = 0

    !... Create the histogram hist by putting elems of a into bins
end subroutine

这是一个简单的程序,用于获取数字a数组并根据给定的bin大小dr创建直方图。首先,它使用函数n_bins获取bin的数量,然后相应地为数组binshist分配空间。

虽然gfortran可以很好地编译此代码,但f2py会引发错误:

/tmp/tmpY5badz/src.linux-x86_64-2.6/mat_ops-f2pywrappers2.f90:32.37:
      call create_hist_gnu(a, n, dr, bins, hist)
Error: Actual argument for 'bins' must be ALLOCATABLE at (1)

据我了解,f2py不允许在运行时为数组分配空间(不知道为什么,因为这似乎是一种自然的科学需求)。

有人可以建议一种在运行时分配Fortran数组的方法,以便f2py对此感到满意吗?

2 个答案:

答案 0 :(得分:7)

您可以使用一些有效的选项/工作轮次。

1。可能最简单的方法是安排python调用函数n_bins,然后使用其中的结果调用{{略微修改后的版本){{ 1}}具有正确大小的不可分配输出数组的函数。

即。在Python中:

create_hist

n_b = n_bins(a,dr) # don't need to pass n - inferred from a bins,hist = create_hist(a,dr,n_b) # bins and hist are auto-allocated 的Fortran接口现在定义为

create_hist

这仅适用于您可以廉价地从subroutine create_hist(a, n, dr, n_b, bins, hist) integer, intent(in) :: n real(8), intent(in) :: a(n) real(8), intent(in) :: dr integer, intent(in) :: n_b integer, intent(out) :: hist(n_b) real(8), intent(out) :: bins(n_b+1) ! code follows ! you also need to specify the function n_bins... 以外拨打n_bins的情况。我知道有两种模拟可分配数组的方法,适用于那些不适用的情况(即计算数组大小的代码很昂贵且无法轻易分离)。

2. 第一种是使用模块级可分配数组(在文档here中描述)。这本质上是一个可分配的全局变量 - 你调用你的函数,它将数据保存到全局变量中,然后从Python访问它。缺点是它不是线程安全的(即如果同时并行调用create_hist则不好)

create_hist

然后Python调用看起来像

module something
    real(8), allocatable :: bins(:)
    integer, allocatable :: hist(:)
contains
    subroutine create_hist(a,n,dr)
        integer, intent(in) :: n
        real(8), intent(in) :: a(n)
        real(8), intent(in) :: dr
        integer :: n_b

        n_b = n_bins(a,n,dr)

        allocate(bins(n_b+1))
        allocate(hist(n_b))
        ! code follows
    end subroutine
end module

3. 我非常喜欢的另一种方法是仅在函数内分配数组(即不将它们作为参数传入/传出)。但是,你传递的是一个Python回调函数,它在最后被调用并保存数组。这是线程安全的(我相信)。

fortran代码看起来像

something.create_hist(a,n,dr)
bins = something.bins # or possible bins = copy.copy(something.bins)
hist = something.hist # or possible hist = copy.copy(something.hist)
不幸的是,它稍微涉及了一些。您需要使用命令subroutine create_hist(a,n,dr,callback) integer, intent(in) :: n real(8), intent(in) :: a(n) real(8), intent(in) :: dr external callable ! note: better to specify the type with an interface block (see http://www.fortran90.org/src/best-practices.html#callbacks) integer :: n_b real(8), allocatable :: bins(:) integer, allocatable :: hist(:) n_b = n_bins(a,n,dr) allocate(bins(n_b+1)) allocate(hist(n_b)) ! code follows call callable(bins,hist,n_b) end subroutine 创建一个签名文件(这是为了构建一个名为f2py -m fortran_module -h fortran_module.pyf my_fortran_file.f90的Python模块 - 根据需要更改名称),然后修改它的相关行以阐明维度回调函数:

fortran_module

使用python module create_hist__user__routines interface create_hist_user_interface subroutine callable(bins,hist,n_b) ! in :f:my_fortran_file.f90:stuff:create_hist:unknown_interface real(kind=8), dimension(n_b+1) :: bins integer, dimension(n_b) :: hist integer :: n_b end subroutine callable end interface create_hist_user_interface end python module create_hist__user__routines

编译

然后是一个简短的Python包装器(给你一个简单的界面),看起来像

f2py -c fortran_module.pyf my_fortran_file.f90

<强>摘要

对于许多情况,选项1可能是最好的。否则选项2或选项3.我更喜欢选项3,因为没有多线程陷阱要避免(但实际上这是一个你可能永远看不到的角落情况)。选项2更容易实现。

答案 1 :(得分:2)

据我所知,f2py不支持具有属性ALLOCATABLE的伪参数(有时称为函数参数)。另一种方法是使它成为一个足够大的显式形状数组。然后,您将只使用数组的某个部分。

subroutine create_hist(a, n, dr, n_b_max, bins, hist)
    integer, intent(in) :: n
    real(8), intent(in) :: a(n)
    real(8), intent(in) :: dr
    integer, intent(in) :: n_b_max
    integer, intent(out) :: hist(n_b_max)
    real(8), intent(out) :: bins(n_b_max+1)

顺便说一句,使用real(8)不是一种可移植的方法,如何指定64位精度,并且某些编译器会失败。即使是好的旧double precision也会更好,因为它总是会编译成某种东西,而且通常是64位。