Me gustaría usar f2py
con Fortran moderno. En particular, estoy tratando de que el siguiente ejemplo básico funcione. Este es el ejemplo útil más pequeño que pude generar.
! 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
Tenga en cuenta que n
se infiere de la forma del parámetro de entrada x
. Tenga en cuenta que y
está asignado y desasignado dentro del cuerpo de la subrutina.
Cuando compilo esto con f2py
f2py -c alloc_test.f90 -m alloc
Y luego correr en Python
from alloc import f
from numpy import ones
x = ones(5)
print f(x)
Obtuve el siguiente error
ValueError: failed to create intent(cache|hide)|optional array-- must have defined dimensions but got (-1,)
Así que voy y creo y edito el pyf
archivo manualmente
f2py -h alloc_test.pyf -m alloc alloc_test.f90
Original
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
Modificado
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
Ahora se ejecuta pero los valores de la salida z
son siempre 0
. Algunas impresiones de depuración revelan que n
tiene el valor 0
dentro de la subrutina f
. Supongo que me falta algo de f2py
magia de encabezado para manejar esta situación correctamente.
En general, ¿cuál es la mejor manera de vincular la subrutina anterior a Python? Preferiría no tener que modificar la subrutina misma.
Respuestas:
No estoy muy familiarizado con las partes internas f2py, pero estoy muy familiarizado con envolver Fortran. F2py solo automatiza algunas o todas las cosas a continuación.
Primero debe exportar a C utilizando el módulo iso_c_binding, como se describe, por ejemplo, aquí:
http://fortran90.org/src/best-practices.html#interfacing-with-c
Descargo de responsabilidad: soy el autor principal de las páginas fortran90.org. Esta es la única forma independiente de plataforma y compilador de llamar a Fortran desde C. Este es F2003, por lo que en estos días no hay razón para usarlo de otra manera.
Solo puede exportar / llamar matrices con longitud completa especificada (forma explícita), es decir:
pero no asumir forma:
Esto se debe a que el lenguaje C no admite tales matrices en sí. Se habla de incluir dicho soporte en F2008 o posterior (no estoy seguro), y la forma en que funcionaría es a través de algunas estructuras de datos C de soporte, ya que necesita llevar información de forma sobre la matriz.
En Fortran, debe usar principalmente la forma de asumir, solo en casos especiales debe usar la forma explícita, como se describe aquí:
http://fortran90.org/src/best-practices.html#arrays
Eso significa que debe escribir un contenedor simple alrededor de su subrutina de forma de suposición, que envolverá las cosas en matrices de formas explícitas, según mi primer enlace anterior.
Una vez que tenga una firma C, simplemente llámela desde Python de la manera que desee, uso Cython, pero puede usar ctype o C / API a mano.
La
deallocate(y)
declaración no es necesaria, Fortran desasigna automáticamente.http://fortran90.org/src/best-practices.html#allocatable-arrays
real*8
no debe usarse, sino más bienreal(dp)
:http://fortran90.org/src/best-practices.html#floating-point-numbers
La declaración
y(:) = 1.0
asigna 1.0 en precisión simple, por lo que el resto de los dígitos serán aleatorios. Esta es una trampa común:http://fortran90.org/src/gotchas.html#floating-point-numbers
Necesitas usar
y(:) = 1.0_dp
.En lugar de escribir
y(:) = 1.0_dp
, puedes escribiry = 1
, eso es todo. Puede asignar un número entero a un número de coma flotante sin perder precisión, y no necesita colocar el redundante(:)
allí. Mucho mas simple.En lugar de
Solo usa
y no te molestes con la
y
matriz en absoluto.No necesita la declaración "return" al final de la subrutina.
Finalmente, probablemente deberías estar usando módulos, y simplemente colocar
implicit none
el nivel del módulo y no necesitas repetirlo en cada subrutina.De lo contrario, me parece bien. Aquí está el código que sigue las sugerencias 1-10 anteriores ::
Muestra la subrutina simplificada, así como una envoltura en C.
En cuanto a f2py, probablemente intente escribir este contenedor para usted y falla. Tampoco estoy seguro de si está utilizando el
iso_c_binding
módulo. Entonces, por todas estas razones, prefiero envolver las cosas a mano. Entonces está exactamente claro lo que está sucediendo.fuente
y
pero quería hacer que algo fuera asignado (mi código real tiene asignaciones no triviales). Sin embargo, no conocía muchos de los otros puntos. Parece que debería buscar algún tipo de guía de mejores prácticas de Fortran90 ... ¡Gracias por la respuesta completa!Todo lo que tienes que hacer es lo siguiente:
Aunque el tamaño de la matriz xy z ahora se pasa como un argumento explícito, f2py hace que el argumento n sea opcional. A continuación se muestra la cadena de documentación de la función tal como aparece en python:
Importando y llamándolo desde python:
da el siguiente resultado:
fuente
n
y quiero obtener una variedad de tamaños2 ** n
. Hasta ahora tengo que pasar también 2 ** n como argumento separado.