2015-09-22 3 views
3

Я пытаюсь вызвать функцию Fortran из Python с использованием ctypes. Я попытался получить результат из подпрограммы и из функции (оба с одинаковой функциональностью), но я не могу получить ожидаемый результат от функции, тогда как подпрограмма работает хорошо. Проблема в том, что у меня есть много библиотек с функциями Fortran вместо подпрограмм. Есть ли проблемы с функциями Fortran и ctypes?Невозможно получить вывод функции fortran из ctypes

Кусок Fortran кода:

MODULE Vector 
! Public types 
TYPE VectorType 
    PRIVATE 
    DOUBLE PRECISION, DIMENSION(3):: components = 0.0d0 
END TYPE VectorType 
!---------------------------------------------------------------------  
CONTAINS 
!--------------------------------------------------------------------- 
SUBROUTINE newVect(this,vectorIn) 
TYPE (VectorType),  INTENT(OUT):: this 
DOUBLE PRECISION, DIMENSION(3), INTENT(IN)::vectorIn 

    this%components = (/vectorIn(1), vectorIn(2), vectorIn(3)/) 

END SUBROUTINE newVect 
!--------------------------------------------------------------------- 
SUBROUTINE subVect(this,vectorOut) 

TYPE(VectorType), INTENT (OUT):: vectorOut 
TYPE(VectorType), INTENT (IN) :: this 

    vectorOut%components = this%components 

END SUBROUTINE subVect 
!---------------------------------------------------------------------- 
TYPE(VectorType) FUNCTION getVect(this) RESULT(vectorOut) 

TYPE(VectorType), INTENT (IN) :: this 

    vectorOut%components = this%components 

    END FUNCTION getVect 
!-------------------------------------------------------------------- 
END MODULE Vector 

Питон код я использую:

import ctypes 
import numpy as np 

class _VectorType(ctypes.Structure): 
    _fields_ = [('components', ctypes.c_double*3)] 


lib_gen_ctypes = '/local/scratch/jfreixa/lib/lib_ctypes_vector.so' 

try_ctypes = ctypes.CDLL(lib_gen_ctypes,ctypes.RTLD_GLOBAL) 

class vector(object): 

    _ctypes_newVect = try_ctypes['Vector.newVect_'] 
    _ctypes_subVect = try_ctypes['Vector._subVect_'] 
    _ctypes_getVect = try_ctypes['Vector.getVect_'] 

    vector_pointer = ctypes.POINTER(_VectorType) 

    _ctypes_getVect.argtypes = [vector_pointer,] 
    _ctypes_getVect.restype = _VectorType 


    def __init__(self,*args): 
     self._vector = _VectorType() 
     self._newVect(*args) 

    def _newVect(self,vectIn): 
     pdb.set_trace() 
     c_vect = (ctypes.c_double*3)(*vectIn) 
     self._ctypes_newVect(self._vector,c_vect) 

    def subVect(self): 
     pdb.set_trace() 
     c_vect = _VectorType() 
     self._ctypes_subVect(ctypes.byref(self._vector),ctypes.byref(c_vect)) 
     print c_vect.components[:] 
     return np.array(c_vect.components[:]) 

    def getVect(self): 
     pdb.set_trace() 
     c_vect = self._ctypes_getVect(ctypes.byref(self._vector)) 
     vect = self._ctypes_getVect(self.vector_pointer.from_address(ctypes.addressof(c_vect))) 
     print vect.components[:] 
     return np.array(vect.components[:]) 

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

import pyctp.vector 
newVect = pyctp.vector.vector((1.0,2.0,3.0)) 
newVect.subVect() 
newVect.getVect() 

Вызов подпрограммы возвращает ожидаемый вектор в то время как вызов функции возвращает нулевой вектор или вектор, полный мусора.

+0

Почему бы не использовать f2py? Или даже NumPy, если это то, что вы собираетесь делать? –

+1

Или, если вы хотите использовать ctypes вместо f2py, используйте внутренний модуль iso_c_bindig в fortran, чтобы открыть простой C-подобный интерфейс для подпрограмм. –

+2

Обычный C-подобный интерфейс выставляется 'bind (c)', а не модулем. Модуль просто определяет некоторые типы и вспомогательные процедуры для преобразования указателей. Использование модуля НЕ изменяет соглашение о вызове. –

ответ

5

Прежде всего, вы должны поместить атрибуты bind (C) ко всем процедурам и типам, которые вы хотите видеть из Python. Все Fortran типа должны быть взяты из iso_c_binding, например, с использованием real(c_double) вместо double precision вы будете уверены, что это типа совместим с C.

MODULE Vector 
use iso_c_binding 
! Public types 
TYPE,bind(C) :: VectorType 
    real(c_double), DIMENSION(3):: components = 0.0d0 
END TYPE VectorType 

CONTAINS 

!--------------------------------------------------------------------- 
SUBROUTINE newVect(this,vectorIn) bind(c,name="newVect") 
    TYPE (VectorType),  INTENT(OUT):: this 
    real(c_double), DIMENSION(3), INTENT(IN)::vectorIn 

    this%components = (/vectorIn(1), vectorIn(2), vectorIn(3)/) 

END SUBROUTINE newVect 
!--------------------------------------------------------------------- 
SUBROUTINE subVect(this,vectorOut) bind(c,name="subVect") 
    TYPE(VectorType), INTENT (OUT):: vectorOut 
    TYPE(VectorType), INTENT (IN) :: this 

    vectorOut%components = this%components 

END SUBROUTINE subVect 
!---------------------------------------------------------------------- 
TYPE(VectorType) FUNCTION getVect(this) RESULT(vectorOut) bind(c,name="getVect") 

TYPE(VectorType), INTENT (IN) :: this 

    vectorOut%components = this%components 

END FUNCTION getVect 
!-------------------------------------------------------------------- 
END MODULE Vector 

Затем в питона передать все аргументы в качестве ссылки:

import ctypes 
import numpy as np 

class _VectorType(ctypes.Structure): 
    _fields_ = [('components', ctypes.c_double*3)] 

lib_gen_ctypes = 'lib_ctypes_vector.so' 
try_ctypes = ctypes.CDLL(lib_gen_ctypes,ctypes.RTLD_GLOBAL) 

class vector(object): 

    _ctypes_newVect = try_ctypes['newVect'] 
    _ctypes_subVect = try_ctypes['subVect'] 
    _ctypes_getVect = try_ctypes['getVect'] 

    vector_pointer = ctypes.POINTER(_VectorType) 

    _ctypes_getVect.argtypes = [vector_pointer,] 
    _ctypes_getVect.restype = _VectorType 

    def __init__(self,*args): 
     self._vector = _VectorType() 
     self._newVect(*args) 

    def _newVect(self,vectIn): 
     c_vect = (ctypes.c_double*3)(*vectIn) 
     self._ctypes_newVect(ctypes.byref(self._vector),ctypes.byref(c_vect)) 

    def subVect(self): 
     c_vect = _VectorType() 
     self._ctypes_subVect(ctypes.byref(self._vector),ctypes.byref(c_vect)) 
     return np.array(c_vect.components[:]) 

    def getVect(self): 
     c_vect = self._ctypes_getVect(ctypes.byref(self._vector)) 
     return np.array(vect.components[:]) 

Результаты getVect уже из VectorType, поэтому вы можете напрямую обращаться к его компонентам.

+0

Спасибо Edmondo !!! Дело в том, что я хочу избежать модификации исходного кода Fortran (я понимаю, что выход Fortran, совместимый с типами C, является лучшим способом использования ctypes). Есть ли способ получить это с помощью только трюков python ?? (дело в том, что у меня есть тонны модулей fortran, и я думаю, что изменить их невозможно, подтвердите их ....) – Jfreixa

+0

Как и предполагалось другими людьми, вы можете использовать f2py. Возможно, вы не сможете получить доступ к определенному пользователю компоненту (я не проверял, возможно ли это), но нет проблем с массивами и переменными модуля. По крайней мере, вам, возможно, придется написать несколько дополнительных процедур для доступа к компонентам. Я использовал его много раз, он довольно прост в использовании. Посмотрите и посмотрите, может ли это вам помочь. –