Как правильно запустить пример f2py из numy документов? - PullRequest
0 голосов
/ 01 февраля 2019

У меня проблемы с выполнением одного из примеров, показанных в кратких документах , касающихся f2py и функций обратного вызова.Я выполнил те же самые шаги, что и в первом примере (например, f2py -c -m callback callback.f), чтобы обернуть callback.f:

C FILE: CALLBACK.F
      SUBROUTINE FOO(FUN,R)
      EXTERNAL FUN
      INTEGER I
      REAL*8 R
Cf2py intent(out) r
      R = 0D0
      DO I=-5,5
         R = R + FUN(I)
      ENDDO
      END
C END OF FILE CALLBACK.F

Однако тестирование результата, как в примере, дает мне:

python
>>> import callback
>>> def f(i): return i*i
... 
>>> print callback.foo(f)
0.0

Таким образом, он возвращает 0.0 вместо 110.0, где 0.0 - начальное значение r в коде Фортрана.Независимо от того, какую функцию обратного вызова я использую, результат остается неизменным (без изменений R).Я использую последние версии python 3.7 и numpy, полученные из conda.

Можете ли вы воспроизвести эту проблему, или я делаю что-то не так?

1 Ответ

0 голосов
/ 04 февраля 2019

Возможно, проблема вызвана несоответствием между ожидаемыми и фактическими типами данных внешней функции FUN:

  • Согласно правила неявной типизации данных в Fortran в CALLBACK.F, функция EXTERNAL FUN имеет неявный тип REAL (поскольку нет явного типа или оператора IMPLICIT NONE).
  • Однако, рассматривая подробности примера second в документации , где обертка F2PY создается явно с использованием f2py -m callback2 -h callback2.pyf callback.f, вы заметите, чторезультат r внешней функции fun определяется как имеющий тип real*8 :: r (это также верно для неизмененного * * файла сигнатуры callback2.pyf, так что это поведение по умолчанию F2PY).

Короче говоря, проблема в том, что FOO ожидает, что FUN вернет результат REAL, в то время как со стороны оболочки Python и F2PY внешняя функция определена для возврата результата REAL*8.Поэтому решение состоит в том, чтобы гарантировать, что FUN имеет одинаковый тип возврата в Fortran и оболочке Python / F2PY.Это может быть достигнуто несколькими способами, например, путем добавления спецификации типа данных REAL*8 FUN в CALLBACK.F:

C FILE: CALLBACK.F
      SUBROUTINE FOO(FUN,R)
      REAL*8 FUN
      EXTERNAL FUN
      INTEGER I
      REAL*8 R
Cf2py intent(out) r
      R = 0D0
      DO I=-5,5
         R = R + FUN(I)
      ENDDO
      END
C END OF FILE CALLBACK.F

Обтекание этого модифицированного CALLBACK.F с python -m numpy.f2py -c -m callback callback.f, как в примере, теперь даетжелаемый результат:

python
>>> import callback
>>> def f(i): return i*i
...
>>> print(callback.foo(f))
110.0

Ради интереса, то же поведение, которое вы видели в Python / F2PY, может быть реализовано в чистом Fortran с использованием двух файлов prog.f и fun.f:

C FILE: PROG.F, including subroutine FOO previously in CALLBACK.F
      PROGRAM MAIN
C     REAL*8 FUN
      EXTERNAL FUN
      REAL*8 R

      R = 0
      PRINT *, "BEFORE: ", R
      CALL FOO(FUN, R)
      PRINT *, "AFTER: ", R

      END

C This is copied from CALLBACK.F 
      SUBROUTINE FOO(FUN,R)
C     REAL*8 FUN
      EXTERNAL FUN
      INTEGER I
      REAL*8 R
Cf2py intent(out) r
      R = 0D0
      DO I=-5,5
         R = R + FUN(I)
      ENDDO
      END
C END OF FILE CALLBACK.F

и

C FILE: FUN.F containing the function to be called by FOO
      REAL*8 FUNCTION FUN(I)
      INTEGER I
      FUN = I*I
      END

Скомпилировано с использованием gfortran -fcheck=all -Wall -g func.f prog.f, оно выдает следующий результат (по сути тот же, что и ваш проблемный пример Python):

./a.out
 BEFORE:    0.0000000000000000
 AFTER:    0.0000000000000000

Раскомментирование обоих экземпляров REAL*8 FUN в prog.f и перекомпиляция решает проблему:

./a.out
 BEFORE:    0.0000000000000000
 AFTER:    110.00000000000000
...