python arrays fortran ctypes

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 por lenInOut , entonces los argtypes 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 . La output 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ún name 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, proporcione bind(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 usar input2 = 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 de inputoutput a inputoutput = 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