简体   繁体   中英

How to correctly run f2py example from numpy docs?

I'm having trouble following one of the examples shown in the numpy docs concerning f2py and callback functions. I performed the exact same steps as in the first example (ie f2py -c -m callback callback.f ) to wrap 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

However, testing the result as in the example, gives me:

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

Thus, it returns 0.0 instead of 110.0 , where 0.0 is the initial value of r in the Fortran code. No matter which callback function I use, the result remains the same (unchanged R ). I'm using a recent version of python 3.7 and numpy obtained from conda .

Can you reproduce that problem, or am I doing something wrong?

The problem appears to be caused by a mismatch between the expected and actual data types of the external function FUN :

  • According to the implicit data typing rules of Fortran , in CALLBACK.F , the EXTERNAL function FUN has an implicit type REAL (since there is no explicit type, or IMPLICIT NONE statement).
  • However, also looking at the details of the second example in the documentation , where the F2PY wrapper is created explicitly using f2py -m callback2 -h callback2.pyf callback.f , you will note that the result r of the external function fun is defined as having type real*8 :: r (this is also true for the unmodified callback2.pyf signature file, so this is the default F2PY behaviour).

In short the problem is that FOO expects FUN to return a REAL result, while from the Python and F2PY wrapper's side the external function is defined to return a REAL*8 result. A solution is therefore to ensure that FUN has the same return type in Fortran and the Python/F2PY wrapper. This can be achieved in several ways, for example by adding a data type specification REAL*8 FUN in 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

Wrapping this modified CALLBACK.F with python -m numpy.f2py -c -m callback callback.f as in the example, now gives the desired output:

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

For interest sake, the same behaviour that you saw in Python/F2PY can be produced in pure Fortran using two files prog.f and 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

and

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

Compiled using gfortran -fcheck=all -Wall -g func.f prog.f , it gives the following output (in effect the same as your problematic Python example):

./a.out
 BEFORE:    0.0000000000000000
 AFTER:    0.0000000000000000

Uncommenting both instances of REAL*8 FUN in prog.f and recompiling solves the problem:

./a.out
 BEFORE:    0.0000000000000000
 AFTER:    110.00000000000000

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM