从 Cython 访问 Fortran 公共变量

Access Fortran Common Variables from Cython

我的问题几乎与 相同。但是,我正在寻找一种使用 Cython 而不是 ctypes 的解决方案。

我正在包装一些遗留的 F77 代码以便在 Python 中使用。我已经使用模块和 iso_c_bindings 为子例程编写了包装器,然后我可以从 Cython 使用它。这适用于调用子程序、将数据作为参数传递等。但是,我现在想直接从 Cython 访问库中的公共块数据。

所以我的问题分为两部分:

A) 我可以像上面的 ctypes 示例那样使用 Cython 直接访问公共块数据吗?我怎么这样我想我应该使用 cdef extern 将公共块作为结构引用,但我不确定如何指向库数据。

B) 通过简单地在我的包装器模块中编写 setter/getter 函数,我会更好,并且不会牺牲性能吗?这是在对上面提到的 ctypes 问题的答复中提出的。

既然你已经熟悉模块化编程,我建议你将common块放在一个模块中,并在需要访问时导入变量:

module common_block_mod
    common /myCommonBlock/ var, var2, var3
    save ! This is not necessary in Fortran 2008+
end module common_block_mod

您现在可以在需要访问时导入变量。

subroutine foo()
    use common_block_mod
    !.. do stuff
end subroutine foo

您可以在 http://iprc.soest.hawaii.edu/users/furue/improve-fortran.html

阅读有关此方法的更多信息

A) 经过反复试验,下面的代码似乎可以在 Linux x86_64 上与 python3.5/gfortran4.8/cython0.25 一起使用,你也可以吗试试看是否有效...?

fort.f90:

module mymod
    use iso_c_binding
    implicit none
contains

subroutine fortsub() bind(c)
    double precision x( 2 )
    real             y( 3 )
    real             z( 4 )
    integer          n( 5 )
    common /mycom/ x, y, z, n

    data z / 100.0, 200.0, 300.0, 400.0 /  !! initialize only z(:) (for check)

    print *, "(fort) x(:) = ", x(:)
    print *, "(fort) y(:) = ", y(:)
    print *, "(fort) z(:) = ", z(:)
    print *, "(fort) n(:) = ", n(:)
end subroutine

end module

fort.h:

extern void fortsub( void );   /* or fortsub_() if bind(c) is not used */

extern struct Mycom {
    double x[ 2 ];
    float  y[ 3 ];
    float  z[ 4 ];
    int    n[ 5 ];
} mycom_;

test.pyx:

cdef extern from "fort.h":
    void fortsub()

    struct Mycom:
        double x[ 2 ]
        float  y[ 3 ]
        int    n[ 5 ]

    Mycom mycom_;

def go():

    mycom_.x[:] = [ 1.0, 2.0 ]
    mycom_.y[:] = [ 11.0, 12.0, 13.0 ]
    mycom_.n[:] = [ 1, 2, 3, 4, 5 ]

    fortsub()

setup.py:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
from os import system

system( 'gfortran -c fort.f90 -o fort.o -fPIC' )

ext_modules = [Extension( 'test', ['test.pyx'],
                          extra_compile_args = ['-fPIC'],
                          extra_link_args = ['fort.o', '-lgfortran'] )]

setup( name = 'test',
       cmdclass = {'build_ext': build_ext},
       ext_modules = ext_modules )

编译:

$ python setup.py build_ext --inplace

测试:

$ python
>>> import test
>>> test.go()
 (fort) x(:) =    1.0000000000000000        2.0000000000000000     
 (fort) y(:) =    11.0000000       12.0000000       13.0000000    
 (fort) z(:) =    100.000000       200.000000       300.000000       400.000000    
 (fort) n(:) =            1           2           3           4           5

这里请注意,我没有在test.pyx中包含z来检查我们是否可以在公共块中只声明选定的变量。此外,可能需要一些编译器选项才能使 C 和 Fortran 之间的公共变量对齐一致(this YoLinux page 可能有用)。


B) 我想这取决于 Fortran 例程执行的计算量...如果例程很重(至少需要几分钟),getter/setter 中的复制操作可能不会问题。另一方面,如果例程在调用大量次数的同时快速完成,那么开销可能是不可忽略的...

为了提高效率,将指针变量从 Cython 传递到 Fortran,通过 c_loc() 以某种方式获取所选 common 变量的地址,并通过 Cython 端的指针直接访问它们可能很有用(虽然仍然不确定它是否有效......)但是如果没有内存对齐问题(对于使用的编译器),使用上面的结构可能更直接。