f2py: указание реальной точности в fortran при взаимодействии с python?

Играю с f2py. Я немного запутался в numpy внутренних типах по сравнению с типами fortran 90. Похоже, я могу использовать только вещественные числа одинарной точности в fortran 90 при взаимодействии с python. Позвольте мне проиллюстрировать это на примере:

Скажем, у меня есть этот модуль fortran 90, test.f90, который нужно скомпилировать с помощью f2py и импортировать в python:

module test
implicit none

integer, parameter :: sp = selected_real_kind(6,37) ! single precision
integer, parameter :: dp = selected_real_kind(15,307) ! double precision

real(sp) :: r_sp = 1.0
real(dp) :: r_dp = 1.0_dp
end module

и я компилирую вот так:

f2py -c -m test test.f90

Затем в питоне:

>>> import test
>>> test.test.r_sp
array(1.0, dtype=float32)
>>> test.test.r_dp
array(1.0)

IOW, похоже, что f2py не принимает двойную точность. Это становится еще более проблематичным при передаче входных данных подпрограмме fortran 90 из python. Скажем, я расширил свой модуль до:

module test

implicit none
integer, parameter :: sp = selected_real_kind(6,37) ! single precision
integer, parameter :: dp = selected_real_kind(15,307) ! double precision

real(sp) :: r_sp = 1.0
real(dp) :: r_dp = 1.0_dp

contains 

subroutine input_sp(val)
  real(sp), intent(in) :: val
  real(sp) :: x
  x = val
  write(*,*) x
end subroutine

subroutine input_dp(val)
  real(dp), intent(in) :: val
  real(dp) :: x
  x = val
  write(*,*) x
end subroutine
end module

f2py -c -m test test.f90

питон

>>> import test
>>> test.test.input_sp(array(1.0,dtype=float32))
1.0000000    
>>> test.test.input_sp(array(1.0,dtype=float64))
1.0000000    
>>> test.test.input_dp(array(1.0,dtype=float32))
-1.15948430791165406E+155
>>> test.test.input_dp(array(1.0,dtype=float64))

-1.15948430791165406E+155

Итак, похоже, что любая входная переменная, отправляемая из python, должна быть объявлена ​​с одинарной точностью. Это известная проблема с f2py?

Кроме того, в качестве дополнительного вопроса: преобразование из sp в dp работает в следующем смысле:

subroutine input_sp_to_dp(val)
  real(sp), intent(in) :: val(2)
  real(dp) :: x(2)
  x = val
  write(*,*) x
end subroutine

Но мне интересно, специфично ли это вообще для компилятора? Могу ли я ожидать, что вышеуказанная подпрограмма будет работать правильно с любым компилятором любой архитектуры? При тестировании для всех вышеперечисленных примеров я использовал gfortran.


person arne    schedule 21.09.2012    source источник


Ответы (1)


В вашем первом примере я не знаю, почему вы говорите, что f2py не принимает двойную точность, когда test.test.r_dp - двойная точность. Массив numpy, который показывает значение с десятичной точкой и без явного dtype, является массивом двойной точности.

Во втором примере показано ограничение в обработке F2PY определений типов с помощью kind=<kind>. См. FAQ: https://numpy.org/doc/stable/f2py/advanced.html#dealing-with-kind-specifiers

Чтобы увидеть, что происходит, запустите f2py test.f90 -m test. Я получаю это:

Reading fortran codes...
    Reading file 'test.f90' (format:free)
Post-processing...
    Block: test
            Block: test
                Block: input_sp
                Block: input_dp
Post-processing (stage 2)...
    Block: test
        Block: unknown_interface
            Block: test
                Block: input_sp
                Block: input_dp
Building modules...
    Building module "test"...
        Constructing F90 module support for "test"...
          Variables: r_dp sp r_sp dp
            Constructing wrapper function "test.input_sp"...
getctype: "real(kind=sp)" is mapped to C "float" (to override define dict(real = dict(sp="<C typespec>")) in /Users/warren/tmp/.f2py_f2cmap file).
getctype: "real(kind=sp)" is mapped to C "float" (to override define dict(real = dict(sp="<C typespec>")) in /Users/warren/tmp/.f2py_f2cmap file).
getctype: "real(kind=sp)" is mapped to C "float" (to override define dict(real = dict(sp="<C typespec>")) in /Users/warren/tmp/.f2py_f2cmap file).
              input_sp(val)
            Constructing wrapper function "test.input_dp"...
getctype: "real(kind=dp)" is mapped to C "float" (to override define dict(real = dict(dp="<C typespec>")) in /Users/warren/tmp/.f2py_f2cmap file).
getctype: "real(kind=dp)" is mapped to C "float" (to override define dict(real = dict(dp="<C typespec>")) in /Users/warren/tmp/.f2py_f2cmap file).
getctype: "real(kind=dp)" is mapped to C "float" (to override define dict(real = dict(dp="<C typespec>")) in /Users/warren/tmp/.f2py_f2cmap file).
              input_dp(val)
    Wrote C/API module "test" to file "./testmodule.c"
    Fortran 90 wrappers are saved to "./test-f2pywrappers2.f90"

Обратите внимание, что он отображает как реальный (kind = sp), так и реальный (kind = dp) на C float, который имеет одинарную точность.

Есть несколько способов исправить это.

Метод 1

Измените объявления типа на real (kind = 4) и real (kind = 8) (или real 4 и real 8) соответственно.

Конечно, это противоречит цели использования selected_real_kind, и для некоторых компиляторов 4 и 8 не являются правильными значениями KIND для одинарной и двойной точности. В этом случае с gfortran sp равно 4, а dp равно 8, так что это работает.

Способ 2

Скажите f2py, как обрабатывать эти объявления. Это объясняется в FAQ по f2py, и этот подход предлагается в сообщениях getctype: ... в выводе f2py, показанном выше.

В этом случае вы должны создать файл с именем .f2py_f2cmap (в каталоге, где вы запускаете f2py), который содержит строку

dict(real=dict(sp='float', dp='double'))

Тогда f2py поступит правильно с этими объявлениями real(sp) и real(dp).

Способ 3

Это также помогает немного изменить ваш код:

module types

implicit none
integer, parameter :: sp = selected_real_kind(6,37) ! single precision
integer, parameter :: dp = selected_real_kind(15,307) ! double precision

real(sp) :: r_sp = 1.0
real(dp) :: r_dp = 1.0_dp

end module


module input

contains 

subroutine input_sp(val)
  use types
  real(sp), intent(in) :: val
  real(sp) :: x
  x = val
  write(*,*) x
end subroutine

subroutine input_dp(val)
  use types
  real(dp), intent(in) :: val
  real(dp) :: x
  x = val
  write(*,*) dp, val, x
end subroutine

end module

См. Аргумент подпрограммы неправильно передан из Python в Fortran для аналогичного предложения.

person Warren Weckesser    schedule 21.09.2012
comment
Спасибо за ответ. Сначала я применил метод 2, и он отлично сработал. Но затем я попытался применить это к реальной программе, которую я пишу, которая использует distutils для компиляции через файл setup.py и так далее. Затем я обнаружил, что просто наличия файла .f2py_cmap недостаточно, хотя он выплюнул, что успешно применил изменения из .f2py_cmap во время сборки. Поэтому мне пришлось использовать метод 3. На самом деле, я уже использовал отдельный модуль для определений переменных точности, но у меня был оператор «типы использования» в верхней части модуля, который его использовал, а не внутри отдельных подпрограмм. - person arne; 04.10.2012