Devolución de matriz al llamar a Fortran desde Python usando Ctypes
arrays (1)
Un par de cosas:
- Fortran utiliza la llamada por referencia, es decir, se pasan los punteros por defecto. Sin embargo, al especificar
VALUE, cambia a llamar por valor en su lugar. Esto lo haces solo porlenInOut, entonces losargtypescorrectos son
mylib.mySub.argtypes = [ POINTER(c_double), c_int, POINTER(c_double) ]
- Aquí tienes una subrutina, no una función. Entonces no obtienes una salida. En cambio, su código llena la matriz
outArray. Laoutputmatriz en su código de Python nunca se toca e imprimirá valores indefinidos. Como señala @eryksun, es posible que desee indicar explícitamente que para evitar devolver basura del registro de pila / valor de retorno:
mylib.mySub.restype = None
Además, aunque especifique
bind(c), el nombre de la función no se especifica exactamente. Si no se especifica ningúnnameen el atributo de vinculación, se indica una minúscula, cf. Fortran 2008 Cláusula 15.5.2 p2 (gracias @francescalus). Para evitar este problema, proporcionebind(c, name=''mySub'').Aunque especifique
IMPLICIT NONE,ino está declarado.
Futuras mejoras:
No necesita una
returnal final de la subrutina.Como el convertidor ctypes puede manejar valores
intPython, puede usarinput2 = 5directamente (gracias a @eryksun para la sugerencia)Todos los búfers de ctypes inicialmente se ponen a cero, por lo que puede simplificar la inicialización de la
inputoutputdeinputoutputainputoutput = ArrayType()(gracias a @eryksun para la pista)
El código completo se ve así:
test.f90:
SUBROUTINE mySub(inArray, lenInOut, outArray) BIND(C, NAME=''mySub'')
USE ISO_C_BINDING
IMPLICIT NONE
INTEGER(C_INT), INTENT(IN), VALUE :: lenInOut
REAL(C_DOUBLE), DIMENSION(lenInOut), INTENT(IN) :: inArray
REAL(C_DOUBLE), DIMENSION(lenInOut), INTENT(OUT) :: outArray
integer :: i
print *, "outArray from within Fortran"
do i = 1, lenInOut
outArray(i) = inArray(i)
print *, outArray(i)
end do
end subroutine mySub
test.py:
from ctypes import *
mylib = CDLL(''./mylib.so'')
mylib.mySub.argtypes = [ POINTER(c_double), c_int, POINTER(c_double) ]
mylib.mySub.restype = None
ArrayType = c_double*5
IntType = c_int
input1 = ArrayType(1.1,2.2,3.3,4.4,5.5)
input2 = 5
inputoutput = ArrayType()
mylib.mySub( input1, input2, inputoutput )
print ''------------------------------------------------------''
print ''output within Python''
a = [0,1,2,3,4]
for ii in a: print inputoutput[ii]
¿Cómo se devuelven las matrices de Fortran a Python usando ctypes?
Como ejemplo, paso una matriz (longitud 5) de Python a Fortran. Se crea una matriz de salida con los mismos valores. Luego, se devuelve a Python. Dentro de Fortran, los valores son correctos, pero después de ser enviados de vuelta a Python, no lo son. ¿Qué pasa con mi configuración no permite que la matriz pase correctamente?
Mi código Fortran (por ejemplo, test.f) contiene lo siguiente:
SUBROUTINE mySub(inArray, lenInOut, outArray) BIND(C)
USE ISO_C_BINDING
IMPLICIT NONE
INTEGER(C_INT), INTENT(IN), VALUE :: lenInOut
REAL(C_DOUBLE), DIMENSION(lenInOut), INTENT(IN) :: inArray
REAL(C_DOUBLE), DIMENSION(lenInOut), INTENT(OUT) :: outArray
print *, "outArray from within Fortran"
do i = 1, lenInOut
outArray(i) = inArray(i)
print *, outArray(i)
end do
return
end subroutine mySub
Esto se compila como .so:
ifort -g -O0 -fpic -traceback -c -o "test.o" "../test.f"
ifort -shared -o "mylib.so" ./test.o
El código de Python es el siguiente:
from ctypes import *
mylib = CDLL(''mylib.so'')
ArrayType = c_double*5
IntType = c_int
input1 = ArrayType(1.1,2.2,3.3,4.4,5.5)
input2 = IntType(5)
inputoutput = ArrayType(0,0,0,0,0)
mylib.mySub.argtypes = [ArrayType,IntType,ArrayType]
mylib.mySub.restype = ArrayType
output = mylib.mySub(input1,input2,inputoutput)
print ''------------------------------------------------------''
print ''output within Python''
print output
a = [0,1,2,3,4]
for ii in a: print output[ii]
El resultado da lo siguiente:
#outArray from within Fortran
#1.10000000000000
#2.20000000000000
#3.30000000000000
#4.40000000000000
#5.50000000000000
#------------------------------------------------------
#output within Python
#<__main__.c_double_Array_5 object at 0x10aa830>
#2.96439387505e-323
#6.91177308417e-310
#1.48219693752e-323
#6.91177308238e-310
#6.91177319086e-310