从Cython访问Fortran公共变量

时间:2016-12-14 22:04:57

标签: python fortran cython fortran-iso-c-binding

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

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

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

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

B)通过在我的包装模块中编写setter / getter函数,我会更好,而不是牺牲性能吗?在上面引用的ctypes问题的答复中提出了这一点。

2 个答案:

答案 0 :(得分:1)

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

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

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

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

答案 1 :(得分:0)

由于您已熟悉模块化编程,我建议您将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

了解有关此方法的更多信息