如何用f2py编写genfromttxt



我发现python中numpy的函数genfromtxt非常慢。

因此,我决定用f2py包装一个模块来读取我的数据。数据是一个矩阵。

subroutine genfromtxt(filename, nx, ny, a)
implicit none
    character(100):: filename
    real, dimension(ny,nx) :: a 
    integer :: row, col, ny, nx
    !f2py character(100), intent(in) ::filename
    !f2py integer, intent(in) :: nx
    !f2py integer, intent(in) :: ny
    !f2py real, intent(out), dimension(nx,ny) :: a
    !Opening file
    open(5, file=filename)
    !read data again
    do row = 1, ny
        read(5,*) (a(row,col), col =1,nx) !reading line by line 
    end do
    close (5)
end subroutine genfromtxt

文件名的长度固定为100,因为如果f2py不能处理动态大小。该代码适用于小于100的大小,否则python中的代码将崩溃。

这在python中称为:

import Fmodules as modules
w_map=modules.genfromtxt(filename,100, 50)

在不传递nxny作为参数,也不将filename长度固定为100的情况下,如何动态地执行此操作?

文件名长度问题很容易解决:您制作filename:character(len_filename):: filename,将len_filename作为fortran函数的参数,并使用f2py intent(hide)将其隐藏在Python接口中(请参阅下面的代码)。

不希望传递nxny的问题更为复杂,本质上是如何从f2py返回可分配数组的问题。我可以看到两种方法,这两种方法都需要一个(小而轻)Python包装器来为您提供一个良好的接口。

我还没有真正解决如何读取csv文件的问题——我只是生成并返回了一些伪数据。我假设你有一个很好的实现。

方法1:模块级可分配阵列

这似乎是一种相当标准的方法——我发现至少有一个新闻组的帖子推荐了这种方法。您基本上有一个可分配的全局变量,将其初始化为合适的大小,然后写入

module mod
    real, allocatable, dimension(:,:) :: genfromtxt_output
contains
    subroutine genfromtxt_v1(filename, len_filename)
    implicit none
        character(len_filename), intent(in):: filename
        integer, intent(in) :: len_filename
        !f2py intent(hide) :: len_filename
        integer :: row, col
        ! for the sake of a quick demo, assume 5*6
        ! and make it all 2
        if (allocated(genfromtxt_output)) deallocate(genfromtxt_output)
        allocate(genfromtxt_output(1:5,1:6))
        do row = 1,5
           do col = 1,6
             genfromtxt_output(row,col) = 2
           end do
        end do        
    end subroutine genfromtxt_v1
end module mod

Python的短包装看起来是这样的:

import _genfromtxt
def genfromtxt_v1(filename):
    _genfromtxt.mod.genfromtxt_v1(filename)
    return _genfromtxt.mod.genfromtxt_output.copy()
    # copy is needed, otherwise subsequent calls overwrite the data

这样做的主要问题是它不会是线程安全的:如果两个Python线程在非常相似的时间调用genfromtxt_v1,那么在您进行更改以将其复制出去之前,数据可能会被覆盖。

方法2:保存数据的Python回调函数

这稍微涉及更多,是我自己发明的一个可怕的破解。您将数组传递给回调函数,回调函数会保存它。Fortran代码如下所示:

subroutine genfromtxt_v2(filename,len_filename,callable)
implicit none
    character(len_filename), intent(in):: filename
    integer, intent(in) :: len_filename
    !f2py intent(hide) :: len_filename
    external callable
    real, allocatable, dimension(:,:) :: result
    integer :: row, col
    integer :: rows,cols
    ! for the sake of a quick demo, assume 5*6
    ! and make it all 2
    rows = 5
    cols = 6
    allocate(result(1:rows,1:cols))
    do row = 1,rows
        do col = 1,cols
            result(row,col) = 2
        end do
    end do        
    call callable(result,rows,cols)
    deallocate(result)
end subroutine genfromtxt_v2

然后,您需要使用f2py -m _genfromtxt -h _genfromtxt.pyf genfromtxt.f90生成"签名文件"(假设genfromtxt.f90是包含Fortran代码的文件)。然后修改"用户例程块"以澄清回调的签名:

python module genfromtxt_v2__user__routines 
    interface genfromtxt_v2_user_interface 
        subroutine callable(result,rows,cols) 
            real, dimension(rows,cols) :: result
            integer :: rows
            integer :: cols
        end subroutine callable
    end interface genfromtxt_v2_user_interface
end python module genfromtxt_v2__user__routines

(即指定尺寸)。文件的其余部分保持不变。用f2py -c genfromtxt.f90 _genfromtxt.pyf 编译

小型Python包装器是

import _genfromtxt
def genfromtxt_v2(filename):
    class SaveArrayCallable(object):
        def __call__(self,array):
            self.array = array.copy() # needed to avoid data corruption
    f = SaveArrayCallable()
    _genfromtxt.genfromtxt_v2(filename,f)
    return f.array

我认为这应该是线程安全的:虽然我认为Python回调函数是作为全局变量实现的,但默认的f2py不会释放GIL,因此在设置全局和运行回调之间不能运行Python代码。不过,我怀疑您是否关心此应用程序的线程安全性。。。

我认为您可以使用:

open(5, file=trim(filename))

以处理比CCD_ 16的长度短的文件名。(也就是说,你可以让filename比它需要的更长,只需在这里修剪它)

我不知道有什么好的干净方法可以消除将nxny传递到fortran子例程的需要。如果您可以用程序确定数据文件的大小和形状(例如,读取第一行以找到nx,调用某个函数或对文件进行第一次遍历以确定文件中的行数),那么在找到这些值后,您可以allocate您的a数组。不过,这会减慢一切速度,因此可能会适得其反

相关内容

  • 没有找到相关文章

最新更新