如何正确 运行 来自 numpy 文档的 f2py 示例?

How to correctly run f2py example from numpy docs?

我无法理解关于 f2py 和回调函数的 examples shown in the numpy docs 之一。 我执行了与第一个示例(即 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

因此,它returns 0.0而不是110.0,其中0.0是Fortran代码中r的初始值。 无论我使用哪个回调函数,结果都是一样的(不变R)。 我正在使用从 conda.

获得的 python 3.7numpy 的最新版本

你能重现那个问题吗,还是我做错了什么?

问题似乎是由外部函数 预期实际 数据类型不匹配引起的 FUN :

  • 根据implicit data typing rules of Fortran,在CALLBACK.F中,EXTERNAL函数FUN有一个隐式类型REAL(因为没有显式类型,或 IMPLICIT NONE 语句)。
  • 但是,还要查看 second example in the documentation 的详细信息,其中 F2PY 包装器是使用 f2py -m callback2 -h callback2.pyf callback.f 显式创建的,您会注意到外部函数 r 的结果 fun 被定义为 real*8 :: r 类型(对于未修改的 callback2.pyf 签名文件也是如此,因此这是默认的 F2PY 行为)。

简而言之,问题是 FOO 期望 FUN 到 return 一个 REAL 结果,而从 Python 和 F2PY 包装器的外部函数被定义为 return 一个 REAL*8 结果。因此,一个解决方案是确保 FUN 在 Fortran 和 Python/F2PY 包装器中具有相同的 return 类型。这可以通过多种方式实现,例如通过在 CALLBACK.F:

中添加数据类型规范 REAL*8 FUN
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

如示例中那样用 python -m numpy.f2py -c -m callback callback.f 包装这个修改后的 CALLBACK.F,现在给出了所需的输出:

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

出于兴趣,您可以使用两个文件 prog.ffun.f:

在纯 Fortran 中生成您在 Python/F2PY 中看到的相同行为
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

prog.f 中取消注释 REAL*8 FUN 的两个实例并重新编译解决了问题:

./a.out
 BEFORE:    0.0000000000000000
 AFTER:    110.00000000000000