Chciałbym używać f2py
z nowoczesnym Fortranem. W szczególności próbuję uzyskać następujący podstawowy przykład do działania. To jest najmniejszy użyteczny przykład, jaki mogłem wygenerować.
! alloc_test.f90
subroutine f(x, z)
implicit none
! Argument Declarations !
real*8, intent(in) :: x(:)
real*8, intent(out) :: z(:)
! Variable Declarations !
real*8, allocatable :: y(:)
integer :: n
! Variable Initializations !
n = size(x)
allocate(y(n))
! Statements !
y(:) = 1.0
z = x + y
deallocate(y)
return
end subroutine f
Zauważ, że n
wynika to z kształtu parametru wejściowego x
. Należy pamiętać, że y
jest on przydzielany i zwalniany w treści podprogramu.
Kiedy to skompiluję f2py
f2py -c alloc_test.f90 -m alloc
A następnie uruchom w Pythonie
from alloc import f
from numpy import ones
x = ones(5)
print f(x)
Pojawia się następujący błąd
ValueError: failed to create intent(cache|hide)|optional array-- must have defined dimensions but got (-1,)
Więc idę pyf
ręcznie tworzyć i edytować plik
f2py -h alloc_test.pyf -m alloc alloc_test.f90
Oryginalny
python module alloc ! in
interface ! in :alloc
subroutine f(x,z) ! in :alloc:alloc_test.f90
real*8 dimension(:),intent(in) :: x
real*8 dimension(:),intent(out) :: z
end subroutine f
end interface
end python module alloc
Zmodyfikowano
python module alloc ! in
interface ! in :alloc
subroutine f(x,z,n) ! in :alloc:alloc_test.f90
integer, intent(in) :: n
real*8 dimension(n),intent(in) :: x
real*8 dimension(n),intent(out) :: z
end subroutine f
end interface
end python module alloc
Teraz działa, ale wartości wyjściowe z
są zawsze 0
. Niektóre drukowanie debugowania ujawnia, że n
ma wartość 0
w podprogramie f
. Zakładam, że brakuje mi f2py
magii nagłówka, aby właściwie zarządzać tą sytuacją.
Mówiąc bardziej ogólnie, jaki jest najlepszy sposób połączenia powyższego podprogramu z Pythonem? Zdecydowanie wolałbym nie modyfikować samego podprogramu.
Odpowiedzi:
Nie jestem zbyt obeznany z wewnętrznymi elementami f2py, ale doskonale znam się na pakowaniu Fortrana. F2py po prostu automatyzuje niektóre lub wszystkie rzeczy poniżej.
Najpierw musisz wyeksportować do C za pomocą modułu iso_c_binding, jak opisano na przykład tutaj:
http://fortran90.org/src/best-practices.html#interfacing-with-c
Oświadczenie: Jestem głównym autorem stron fortran90.org. Jest to jedyny niezależny od platformy i kompilatora sposób wywoływania Fortran z C. To jest F2003, więc w dzisiejszych czasach nie ma powodu, aby używać innego sposobu.
Możesz eksportować / wywoływać tylko tablice o pełnej długości (wyraźny kształt), to znaczy:
ale nie przyjmują kształtu:
Wynika to z faktu, że język C nie obsługuje takich tablic. Mówi się o włączeniu takiego wsparcia w F2008 lub nowszym (nie jestem pewien), a sposób, w jaki działałby, to poprzez niektóre wspierające struktury danych C, ponieważ musisz przenosić informacje o kształcie o tablicy.
W Fortranie powinieneś używać głównie zakładania kształtu, tylko w szczególnych przypadkach powinieneś używać wyraźnego kształtu, jak opisano tutaj:
http://fortran90.org/src/best-practices.html#arrays
Oznacza to, że musisz napisać prostą owijkę wokół podprogramu „Przyjmij kształt”, który zawinie rzeczy w jawne tablice kształtów, za pomocą mojego pierwszego linku powyżej.
Gdy masz już podpis C, po prostu wywołaj go z Pythona w dowolny sposób, używam Cython, ale możesz ręcznie używać ctype lub C / API.
deallocate(y)
Instrukcja nie jest potrzebna, Fortran dealokuje automatycznie.http://fortran90.org/src/best-practices.html#allocatable-arrays
real*8
nie należy go używać, alereal(dp)
:http://fortran90.org/src/best-practices.html#floating-point-numbers
Instrukcja
y(:) = 1.0
przypisuje 1.0 z pojedynczą precyzją, więc reszta cyfr będzie losowa! Jest to częsta pułapka:http://fortran90.org/src/gotchas.html#floating-point-numbers
Musisz użyć
y(:) = 1.0_dp
.Zamiast pisać
y(:) = 1.0_dp
, możesz po prostu pisaćy = 1
, to wszystko. Możesz przypisać liczbę całkowitą do liczby zmiennoprzecinkowej bez utraty dokładności i nie musisz umieszczać tam nadmiaru(:)
. O wiele prostsze.Zamiast
po prostu użyj
i nie zawracaj sobie głowy
y
tablicą.Nie potrzebujesz instrukcji „return” na końcu podprogramu.
Wreszcie, prawdopodobnie powinieneś używać modułów i po prostu umieścić
implicit none
na poziomie modułu i nie musisz powtarzać go w każdym podprogramie.W przeciwnym razie wygląda mi to dobrze. Oto kod zgodny z powyższymi sugestiami 1-10:
Pokazuje uproszczony podprogram, a także opakowanie typu C.
Jeśli chodzi o f2py, prawdopodobnie próbuje napisać to opakowanie dla ciebie i kończy się niepowodzeniem. Nie jestem też pewien, czy używa
iso_c_binding
modułu. Z tych wszystkich powodów wolę owijać rzeczy ręcznie. Wtedy jest dokładnie jasne, co się dzieje.źródło
y
ale chciałem, aby coś zostało przydzielone (mój rzeczywisty kod ma przydziały niebanalne). Nie wiedziałem jednak o wielu innych kwestiach. Wygląda na to, że powinienem zajrzeć do przewodnika po najlepszych praktykach Fortran90 .... Dziękuję za dokładną odpowiedź!Wszystko, co musisz zrobić, to:
Chociaż rozmiar tablicy xiz jest teraz przekazywany jako jawny argument, f2py sprawia, że argument n jest opcjonalny. Poniżej znajduje się dokumentacja funkcji, tak jak wygląda na python:
Importowanie i wywoływanie go z Pythona:
daje następujący wynik:
źródło
n
i chcę uzyskać tablicę rozmiarów2 ** n
. Do tej pory muszę podać także 2 ** n jako osobny argument.