Reputation: 493
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 (i.e. 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?
Upvotes: 1
Views: 172
Reputation: 1263
The problem appears to be caused by a mismatch between the expected and actual data types of the external function FUN
:
CALLBACK.F
, the EXTERNAL
function FUN
has an implicit type REAL
(since there is no explicit type, or IMPLICIT NONE
statement). 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
Upvotes: 2