2016-09-16 2 views
1

Я пытаюсь получить доступ к данным, хранящимся в общем блоке Fortran 77, из сценария Python. Вопрос в том, что я не знаю, где хранятся эти данные. Приложение Python, которое я разрабатываю, использует разные библиотеки. Эти библиотеки содержат функции со следующими директивами:Доступ к общим переменным блока из ctypes

#include <tcsisc_common.inc> 

Общий блок содержит:

C 
     INTEGER*4 IDEBUG 
C 
C.... ARRAY DIMENSIONS 
     DIMENSION IDEBUG(10) 
C 
C.... COMMON BLOCK 
     COMMON /TCSD/ IDEBUG 
C 

Со стороны Python (на примере я использовал IPython), я загрузить библиотеку:

In [1]: import ctypes 
In [2]: _libtcsisc= /home/jfreixa/project/bin/libtcsisc.so 
In [3]: _tcsisc = ctypes.CDLL(_libtcsisc, ctypes.RTLD_GLOBAL) 

Проблема в том, что я не знаю, как получить IDEBUG. Я попытался следующие, но я просто получить tcsd как c_long инициализируется 0.

In [4]: tcsd = ctypes.c_int.in_dll(_tcsisc, "TCSD_") 
In [5]: tcsd 
Out[5]: c_long(0) 
In [6]: idebug = ctypes.c_int.in_dll(_tcsisc, "IDEBUG_") 
--------------------------------------------------------------------------- 
ValueError        Traceback (most recent call last) 
<ipython-input-6-ee5018286275> in <module>() 

----> 1 idebug = ctypes.c_int.in_dll(_tcsisc,'IDEBUG_') 

ValueError: ld.so.1: python2.7: fatal: IDEBUG_: can't find symbol 

Любая идея, чтобы правильно получить переменную?

+2

Почему бы вам не написать функции fortran, которые python может вызвать для доступа к этой переменной. – innoSPG

+0

Поскольку библиотеки были разработаны давно, и я хочу избежать попытки перевести проверенную инфраструктуру. Доступ к переменным в общих блоках позволит мне использовать их в Python (часть, которая нуждается в обновлении) и оставить сотни функций на Fortran, которые не нуждаются в обновлении. – Jfreixa

+0

Написание обертки не означает ничего обновить! Вы просто напишете Fortran setter и getter, совершенно новые процедуры. Ничего не изменишь! Ваши сотни функций могут оставаться нетронутыми. –

ответ

3

В соответствии с this page (в частности, как получить доступ к общим блокам Fortran от C) и некоторым Q/A page о том, как получить доступ к C struct из Python, кажется, что мы можем получить доступ к общим блокам следующим образом (хотя это может быть не очень портативным, см. ниже):

mylib.f90

subroutine fortsub() 
    implicit none 
    integer n 
    common /mycom/ n 
    print *, "fortsub> current /mycom/ n = ", n 
end 

компиляции:

$ gfortran -shared -fPIC -o mylib.so mylib.f90 

test.py

from __future__ import print_function 
import ctypes 

class Mycom(ctypes.Structure): 
    _fields_ = [ ("n", ctypes.c_int) ] 

mylib = ctypes.CDLL("./mylib.so") 

mycom = Mycom.in_dll(mylib, "mycom_") 

print(" python> modifying /mycom/ n to 777") 

mycom.n = 777 

fortsub = mylib.fortsub_ 
fortsub() 

Тест:

$ python test.py 
python> modifying /mycom/ n to 777 
fortsub> current /mycom/ n =   777 

Вот, пожалуйста, обратите внимание, что имя общего блока (здесь, mycom) производится в нижнем регистре и прикреплен один подчеркивание (по предполагающей gfortran). Поскольку это соглашение является зависимым от компилятора, оно может быть более надежным и переносимым для записи новых подпрограмм Fortran для установки/получения значений в общих блоках (в частности, с помощью iso_c_binding) и вызова этих подпрограмм из Python (как было предложено @innoSPG в первый комментарий).


Другой пример включая различные типы и массивы могут выглядеть следующим образом:

mylib.f90

subroutine initcom() 
    implicit none 
    integer   n(2), w !! assumed to be compatible with c_int 
    real    f(2)  !!      ... with c_float 
    double precision d(2)  !!      ... with c_double 
    common /mycom/ n, f, d, w 

    print *, "(fort) initializing /mycom/" 
    n(:) = [ 1, 2 ] 
    f(:) = [ 3.0, 4.0 ] 
    d(:) = [ 5.0d0, 6.0d0 ] 
    w = 7 
    call printcom() 
end 

subroutine printcom() 
    implicit none 
    integer   n(2), w 
    real    f(2) 
    double precision d(2) 
    common /mycom/ n, f, d, w 

    print *, "(fort) current /mycom/" 
    print *, "  n = ", n 
    print *, "  f = ", f 
    print *, "  d = ", d 
    print *, "  w = ", w 
end 

test.py

from __future__ import print_function 
import ctypes 

N = 2 

class Mycom(ctypes.Structure): 
    _fields_ = [ ("x", ctypes.c_int * N), 
       ("y", ctypes.c_float * N), 
       ("z", ctypes.c_double * N), 
       ("w", ctypes.c_int  ) ] 

mylib = ctypes.CDLL("./mylib.so") 

mycom = Mycom.in_dll(mylib, "mycom_") 

initcom = mylib.initcom_ 
initcom() 

print(" (python) current /mycom/") 
print("   x = ", mycom.x[:]) 
print("   y = ", mycom.y[:]) 
print("   z = ", mycom.z[:]) 
print("   w = ", mycom.w ) 

print(" (python) modifying /mycom/ ...") 
for i in range(N): 
    mycom.x[ i ] = (i + 1) * 10 
    mycom.y[ i ] = (i + 1) * 100 
    mycom.z[ i ] = (i + 1) * 0.1 
mycom.w = 777 

printcom = mylib.printcom_ 
printcom() 

Тест:

$ python test.py 

(fort) initializing /mycom/ 
(fort) current /mycom/ 
     n =   1   2 
     f = 3.0000000  4.0000000  
     d = 5.0000000000000000  6.0000000000000000  
     w =   7 
(python) current /mycom/ 
      x = [1, 2] 
      y = [3.0, 4.0] 
      z = [5.0, 6.0] 
      w = 7 
(python) modifying /mycom/ ... 
(fort) current /mycom/ 
     n =   10   20 
     f = 100.00000  200.00000  
     d = 0.10000000000000001  0.20000000000000001  
     w =   777 
+0

Когда появляется примечание или предупреждение о выравнивании общих переменных, может потребоваться добавить некоторую опцию (например, -align для ifort) при компиляции источника fortran. Это также зависит от компилятора, поэтому, чтобы избежать этого, может быть удобнее (в конечном итоге) писать процедуры getter/setter для необходимых общих переменных, а не напрямую обращаться к ним с помощью python. – roygvib

Смежные вопросы