pointer Retour tableau lorsque vous appelez Fortran à partir de Python en utilisant Ctypes



python ctypes example (1)

Un certain nombre de choses:

  • Fortran utilise l'appel par référence, c'est-à-dire que les pointeurs sont passés par défaut. Toutefois, en spécifiant VALUE , vous passez à l'appel par valeur à la place. C'est ce que vous faites uniquement pour lenInOut , donc les argtypes corrects sont
mylib.mySub.argtypes = [ POINTER(c_double), c_int, POINTER(c_double) ]
  • Vous avez un sous-programme ici, pas une fonction. Donc vous n'obtenez pas de résultat. Au lieu de cela, votre code remplit le tableau outArray . La output tableau dans votre code Python n'est jamais touchée et imprimera des valeurs indéfinies. Comme le souligne @eryksun, vous pouvez indiquer explicitement que pour éviter de renvoyer des déchets du registre de pile / valeur de retour:
mylib.mySub.restype = None
  • De plus, bien que vous spécifiiez bind(c) , le nom de la fonction n'est pas exactement spécifié. Si aucun name n'est spécifié dans l'attribut bind les minuscules sont obligatoires, cf. Fortran 2008 Clause 15.5.2 p2 (merci @francescalus). Pour éviter ce problème, fournissez bind(c, name='mySub') .

  • Bien que vous spécifiez IMPLICIT NONE , i n'est pas déclaré.

D'autres améliorations:

  • Vous n'avez pas besoin d'un return à la fin du sous-programme.

  • Comme le convertisseur ctypes peut gérer les valeurs int Python, vous pouvez utiliser input2 = 5 directement (grâce à @eryksun pour l'indice)

  • Tous les buffers ctypes sont initialement mis à zéro, donc vous pouvez simplifier l'initialisation de inputoutput à inputoutput = ArrayType() (merci à @eryksun pour l'indice)

Le code complet ressemble alors à:

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]

Comment retourner des tableaux de Fortran à Python en utilisant ctypes?

A titre d'exemple, je passe dans un tableau (longueur 5) de Python à Fortran. Un tableau de sortie est créé avec les mêmes valeurs. Ensuite, il est renvoyé à Python. Dans Fortran, les valeurs sont correctes, mais après avoir été renvoyées à Python, elles ne le sont pas. Qu'en est-il de ma configuration ne permet pas le tableau de passer correctement?

Mon code Fortran (par exemple, test.f) contient les éléments suivants:

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

Ceci est compilé en tant que .so:

ifort -g -O0 -fpic -traceback -c -o "test.o" "../test.f"
ifort -shared -o "mylib.so"  ./test.o

Le code Python est le suivant:

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]

La sortie donne ce qui suit:

#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




ctypes