Я хотел бы использовать f2py
с современным Fortran. В частности, я пытаюсь заставить работать следующий базовый пример. Это самый маленький полезный пример, который я мог привести.
! 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
Обратите внимание, что n
выводится из формы входного параметра x
. Обратите внимание, что y
это распределяется и освобождается в теле подпрограммы.
Когда я собираю это с f2py
f2py -c alloc_test.f90 -m alloc
А затем запустить в Python
from alloc import f
from numpy import ones
x = ones(5)
print f(x)
Я получаю следующую ошибку
ValueError: failed to create intent(cache|hide)|optional array-- must have defined dimensions but got (-1,)
Поэтому я иду и создаю и редактирую pyf
файл вручную
f2py -h alloc_test.pyf -m alloc alloc_test.f90
оригинал
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
модифицированный
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
Теперь он работает, но значения вывода z
всегда 0
. Некоторая отладочная печать показывает, что n
имеет значение 0
в подпрограмме f
. Я предполагаю, что мне не хватает некоторой f2py
магии заголовка, чтобы правильно управлять этой ситуацией.
В более общем смысле, как лучше связать вышеуказанную подпрограмму с Python? Я бы предпочел не изменять саму подпрограмму.
Ответы:
Я не очень знаком с внутренностями f2py, но я очень хорошо знаком с упаковкой Фортрана. F2py просто автоматизирует некоторые или все вещи ниже.
Сначала вам нужно экспортировать в C, используя модуль iso_c_binding, как описано, например, здесь:
http://fortran90.org/src/best-practices.html#interfacing-with-c
Отказ от ответственности: я являюсь основным автором страниц fortran90.org. Это единственный независимый от платформы и компилятора способ вызова Fortran из C. Это F2003, поэтому в наши дни нет никаких причин использовать какой-либо другой способ.
Вы можете экспортировать / вызывать только массивы с полной длиной (явная форма), то есть:
но не принимать форму:
Это потому, что язык C не поддерживает такие массивы сам по себе. Говорят о том, чтобы включить такую поддержку в F2008 или более позднюю версию (я не уверен), и способ, которым она будет работать, заключается в поддержке структур данных C, так как вам нужно нести информацию о форме массива.
В Фортране вы должны в основном использовать предположительную форму, только в особых случаях вы должны использовать явную форму, как описано здесь:
http://fortran90.org/src/best-practices.html#arrays
Это означает, что вам нужно написать простую обертку вокруг подпрограммы предполагаемой формы, которая обернет вещи в явные массивы форм, согласно моей первой ссылке выше.
Если у вас есть подпись C, просто вызывайте ее из Python любым удобным для вас способом, я использую Cython, но вы можете использовать ctype или C / API вручную.
deallocate(y)
Заявление не требуется, Fortran автоматически освобождает.http://fortran90.org/src/best-practices.html#allocatable-arrays
real*8
не следует использовать, аreal(dp)
:http://fortran90.org/src/best-practices.html#floating-point-numbers
Оператор
y(:) = 1.0
присваивает 1.0 с одинарной точностью, поэтому остальные цифры будут случайными! Это распространенная ошибка:http://fortran90.org/src/gotchas.html#floating-point-numbers
Вам нужно использовать
y(:) = 1.0_dp
.Вместо того, чтобы писать
y(:) = 1.0_dp
, вы можете просто написатьy = 1
, вот и все. Вы можете назначить целое число на число с плавающей запятой без потери точности, и вам не нужно помещать(:)
туда избыточное число . Гораздо прощеВместо того
просто используйте
и не беспокойтесь с
y
массивом вообще.Вам не нужно выражение "return" в конце подпрограммы.
Наконец, вы, вероятно, должны использовать модули и просто поставить
implicit none
на уровень модулей, и вам не нужно повторять это в каждой подпрограмме.В противном случае это выглядит хорошо для меня. Вот код, следующий за предложениями 1-10 выше:
Он показывает упрощенную подпрограмму, а также оболочку на языке C.
Что касается f2py, он, вероятно, пытается написать эту обертку для вас и терпит неудачу. Я также не уверен, использует ли он
iso_c_binding
модуль. Поэтому по всем этим причинам я предпочитаю оборачивать вещи вручную. Тогда точно понятно, что происходит.источник
y
но я хотел, чтобы что-то было выделено (мой реальный код имеет нетривиальное распределение). Я не знал о многих других моментах, хотя. Похоже, мне стоит заглянуть в какое-нибудь руководство по лучшим практикам Fortran90 .... Спасибо за подробный ответ!Все, что вам нужно сделать, это следующее:
Хотя размер массива x и z теперь передается в качестве явного аргумента, f2py делает аргумент n необязательным. Ниже приведена строка документации функции в том виде, как она выглядит в python:
Импортировать и вызывать его из python:
дает следующий вывод:
источник
n
и хочу получить массив размера2 ** n
. До сих пор я должен также передать 2 ** n в качестве отдельного аргумента.