我的问题几乎与this one相同。但是,我正在寻找一种使用Cython而不是ctypes的解决方案。
我正在包装一些传统的F77代码,以便在Python中使用。我已经使用模块和iso_c_bindings为子程序编写了包装器,然后我可以从Cython中使用它。这适用于调用子例程,将数据作为参数传递等。但是,我现在想直接从Cython访问库中的公共块数据。
所以我的问题分为两部分:
A)我可以直接使用Cython访问公共块数据,就像上面的ctypes示例一样吗?我怎么样?我收集我应该使用cdef extern
将公共块作为结构引用,但我不确定如何指向库数据。
B)通过在我的包装模块中编写setter / getter函数,我会更好,而不是牺牲性能吗?在上面引用的ctypes问题的答复中提出了这一点。
答案 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
了解有关此方法的更多信息