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 losargtypes
correctos 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
. Laoutput
matriz 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únname
en 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
,i
no está declarado.
Futuras mejoras:
No necesita una
return
al final de la subrutina.Como el convertidor ctypes puede manejar valores
int
Python, puede usarinput2 = 5
directamente (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
inputoutput
deinputoutput
ainputoutput = 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