2

Python の関数が非常に遅いgenfromtxtことがわかりました。numpy

したがって、モジュールをラップし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

f2py が動的サイズを処理できない場合、ファイル名の長さは 100 に固定されます。コードは 100 未満のサイズで機能しますが、それ以外の場合は Python のコードがクラッシュします。

これは Python では次のように呼び出されます。

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

パラメータとしてを渡すことなく、また長さを 100 に固定することなくnx、これを動的に行うにはどうすればよいですか?nyfilename

4

2 に答える 2

3

ファイル名の長さの問題は簡単に対処できます: filename: を作成しcharacter(len_filename):: filenamelen_filenamefortran 関数のパラメーターとして使用し、 を使用しf2py intent(hide)て Python インターフェイスから非表示にします (以下のコードを参照)。

渡す必要がなく、より複雑でnxありny、本質的には f2py から割り当て可能な配列を返す方法の問題です。2 つの方法がありますが、どちらも (小さくて非常に軽い) Python ラッパーを使用して適切なインターフェイスを提供する必要があります。

csv ファイルの読み取り方法の問題に実際には対処していません。ダミー データを生成して返しただけです。そのための適切な実装があると思います。

方法 1: モジュール レベルの割付け配列

これはかなり標準的な方法のようです - これを推奨しているニュースグループの投稿を少なくとも 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

これに関する主な問題は、スレッド セーフではないことです。2 つの 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

次に、「署名ファイル」を生成する必要があります (が Fortran コードを含むファイルであるとf2py -m _genfromtxt -h _genfromtxt.pyf genfromtxt.f90仮定します)。genfromtxt.f90次に、「ユーザー ルーチン ブロック」を変更して、コールバックの署名を明確にします。

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 コードを実行することはできません。ただし、このアプリケーションのスレッドセーフについて気にかけているとは思えません...

于 2015-06-25T07:21:14.547 に答える