2 votes

Retourner des tableaux de longueur 0 de fortran vers python en utilisant f2py

J'utilise f2py pour générer un wrapper pour une bibliothèque MPI écrite en fortran. En raison du schéma de partitionnement des tableaux que j'utilise, il est possible pour un processus d'avoir un tableau local avec une longueur de 0, s'il y a suffisamment de processus MPI. Cela déclenche l'erreur suivante sur le système Cray auquel j'ai accès :

ValueError: failed to create intent(cache|hide)|optional array-- 
must have defined dimensions but got (0,)

Je ne reçois pas la même erreur sur mon bureau. C'est probablement lié aux versions de python et de numpy que j'ai installées. Sur mon bureau, il s'agit de la version 1.16.4 de numpy et de la version 2.7.15+ de python, et sur le cluster, il s'agit de la version 1.13.3 de numpy et de la version 2.7.14 de python. Comme je ne peux pas mettre à jour les paquets sur le cluster, je me demande s'il existe une solution de contournement simple. Le code suivant reproduit l'erreur :

Fichier 'fortran_sub.f90' :

subroutine sub(a_size, a)                                                       

    integer, intent(in) :: a_size                                               
    real, dimension(a_size), intent(out) :: a                                   

    if (size(a) > 0) then                                                       
        a = size(a)                                                             
    endif                                                                       

end subroutine sub  

Enveloppé et compilé à l'aide de f2py comme suit :

python2 -m numpy.f2py -h --overwrite-signature fortran_sub.pyf -m 
fortran_sub fortran_sub.f90

python2 -m numpy.f2py --f90exec="ftn" -c fortran_sub.pyf -m 
fortran_sub fortran_sub.f90

Le fichier .pyf généré est le suivant :

!    -*- f90 -*-
! Note: the context of this file is case sensitive.

python module fortran_sub ! in 
    interface  ! in :fortran_sub
        subroutine sub(a_size,a) ! in :fortran_sub:fortran_sub.f90
            integer intent(in) :: a_size
            real dimension(a_size),intent(out),depend(a_size) :: a
        end subroutine sub
     end interface 
end python module fortran_sub

! This file was auto-generated with f2py (version:2).
! See http://cens.ioc.ee/projects/f2py2e/

Exécution du programme python suivant 'pytest.py' avec python2 pytest.py :

import fortran_sub

a = fortran_sub.sub(2)
print(a)

a = fortran_sub.sub(1)
print(a)

a = fortran_sub.sub(0)
print(a)

J'obtiens le résultat suivant :

[ 2.  2.]
[ 1.]
Traceback (most recent call last):
  File "pytest.py", line 11, in <module>
   a = fortran_sub.sub(0)
ValueError: failed to create intent(cache|hide)|optional array-- must have defined dimensions but got (0,)

2voto

King Points 682

Je n'ai pas utilisé f2py depuis longtemps, depuis que j'ai appris à connaître f2py. ctypes pour l'interface Python-Fortran. Voici une solution basée sur ctypes qui peut également gérer des tableaux de taille zéro (ce qui devrait techniquement fonctionner avec MPI),

module sub_mod
contains
    subroutine sub(a_size, a) bind(C,name="sub")
        !DEC$ ATTRIBUTES DLLEXPORT :: sub
        use iso_c_binding, only: c_size_t, c_double
        integer(c_size_t), intent(in) :: a_size
        real(c_double), intent(inout) :: a(a_size)
        if (size(a) > 0) then
            a = size(a)
        endif
    end subroutine sub
end module sub_mod

le compiler avec Intel ou (d'autres compilateurs) pour créer une bibliothèque DLL,

ifort /dll main.f90

avec un message de sortie comme celui qui suit de la part d'ifort,

Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 19.0.4.245 Build 20190417
Copyright (C) 1985-2019 Intel Corporation.  All rights reserved.

Microsoft (R) Incremental Linker Version 14.22.27905.0
Copyright (C) Microsoft Corporation.  All rights reserved.

-out:main.dll
-dll
-implib:main.lib
main.obj
   Creating library main.lib and object main.exp

qui peut ensuite être appelée depuis Python via des ctypes comme suit,

#!/usr/bin/env python

import ctypes as ct
import numpy as np

# import Fortran DLL
lib = ct.CDLL("main.dll")
lib.sub.restype = None    # define subroutine result type

# define subroutine argument type
lib.sub.argtypes =  [ ct.POINTER(ct.c_size_t)        # a_size
                    , ct.POINTER(ct.c_double)       # a array
                    ]
# all Fortran DLL
for i in [2,1,0]:
    a_size = i
    a = np.zeros(shape=[a_size])
    lib.sub ( ct.byref( ct.c_size_t(a_size) ) # Fortran passes everything around by reference
            , np.ctypeslib.as_ctypes( a ) # pointer to numpy array
            )
    print("a_size = {}, a = {}".format(a_size, a))

ce qui donne ce qui suit,

a_size = 2, a = [2. 2.]
a_size = 1, a = [1.]
a_size = 0, a = []

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X