2016-12-14 75 views
1

我的問題幾乎與this one相同。但是,我正在尋找一種使用Cython而不是ctypes的解決方案。從Cython訪問Fortran常用變量

我正在包裝一些遺留的F77代碼,以便在Python中使用。我已經使用模塊和iso_c_bindings編寫了子例程的包裝器,然後我可以使用Cython。這適用於調用子例程,將數據作爲參數傳遞等。但是,現在我想直接從Cython訪問庫中的公共塊數據。

所以我的問題是兩個部分:

A)我可以用用Cython訪問公共數據塊直接在上面的例子的ctypes?我怎麼樣?我收集我應該使用cdef extern引用公共塊作爲結構,但我不知道如何指向庫數據。

B)通過在包裝模塊中編寫setter/getter函數,我會更好嗎,而不是犧牲性能?這在上面引用的ctypes問題的答覆中被提出。

+1

與使用Cython進行封裝相比,使用'f2py'封裝可能更容易。在[scipy f2py使用頁面](https://docs.scipy.org/doc/numpy-dev/f2py/python-usage.html)上有一些示例用法。 – mgilson

+0

我已經將F2PY用於其他庫。它運行良好,並且提供對公共塊數據的訪問。但是,在其他項目中,我鏈接到使用fortran派生類型的代碼,這是F2PY無法處理的,但很容易在Cython中使用。所以我一直在尋找一個Cython解決方案。 – tharen

回答

1

A)這種方法試驗&錯誤後,似乎下面的代碼工作與python3.5/gfortran4.8/cython0.25在Linux x86_64的,所以你可以試試看它是否有效......?

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 

在這裏,請注意,我並沒有包括在ztest.pyx檢查我們是否可以在公共塊聲明唯一入選的變量。此外,有些編譯器選項可能需要使C和Fortran之間的公共變量對齊一致(this YoLinux page可能有用)。


B)我想這將取決於由Fortran程序peformed計算量...如果例程重(至少需要幾分鐘),在吸氣複製操作/ setter方法可能沒有問題。另一方面,如果程序快速完成而被稱爲大量次數,則開銷可能不可忽略...

爲了提高效率,將指針變量從Cython傳遞到Fortran可能很有用,get所選的common變量的地址由c_loc()以某種方式被選中,並通過Cython端的指針直接訪問它們(雖然不能確定它是否工作......)但是如果沒有內存對齊的問題(對於使用的編譯器),它可能比以上更直接地使用結構。

+0

優秀的答案!我沒有直接實現你的答案,但使用你的例子我能夠正確地在Cython中設置我的外部引用。我被卡住的地方在於如何引用公共塊「結構」。這是失蹤的作品,「Mycom mycom_」。 – tharen

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

+1

這並不真正告訴用戶如何訪問/修改Cython中的值,除非您建議OP爲公共塊中的每個值寫入getters/setters ... – mgilson

+0

我的印象是OP已經擁有訪問模塊變量和過程(函數/子程序)的例程。 – jlokimlin

+0

謝謝@jlokimlin,這正是我所做的。我沒有提到它,因爲它看起來不相關。我需要的是一種使用Cython訪問公共塊變量的機制。我認爲有一些關於從Cython訪問外部數據的東西,我不明白。 – tharen