3

I am trying to compile a piece of old Fortran code with f2py so that it can be called within Python. However, the part with external function wouldn’t work. Here is an MWE, first I have the test.f:

      function f(x)
      implicit double precision (a-z)

      f = x * x

      return
      end function f

      subroutine gauss(fun)
      implicit double precision (a-h, j-z)
      ! external fun

      x = 1.5
      write(*,*) fun(x)

      return
      end subroutine gauss

which is later compiled with makefile:

f2py -c --quiet --fcompiler=gnu95 \
        --f90flags=“-Wtabs” \
        -m test \
        test.f

Lastly it is called from Python:

import test
f = lambda x: x
test.gauss(test.f)

and gives the error TypeError: test.gauss() 1st argument (fun) can’t be converted to double.

In a second attempt, I uncomment the line external fun in the subroutine gauss and get the following error message during compilation:

/tmp/tmpet9sk3e9/src.linux-x86_64-3.7/testmodule.c: In function ‘cb_fun_in_gauss__user__routines’:
/tmp/tmpet9sk3e9/src.linux-x86_64-3.7/testmodule.c:313:8: error: variable or field ‘return_value’ declared void

I am running out of ideas, any help will be greatly appreciated!

zyy
  • 1,271
  • 15
  • 25
  • 1
    Uncommenting the line `external fun` and adding another line `double precision fun` worked for me, so f2py might need explicit type declaration of the function argument (though not very sure...) https://numpy.org/devdocs/f2py/python-usage.html#call-back-arguments – roygvib May 27 '22 at 22:24

1 Answers1

0

I have (partly) figured it out: f2py needs you to explicitly specify the use of the function. Here is how:

      function f(x)
      implicit double precision (a-z)

      f = x * x

      return
      end function f

      subroutine gauss(fun)
      implicit double precision (a-h, j-z)
      !! <-------- updates starts HERE
      external fun
!f2py real*8 y
!f2py y = fun(y)
      double precision fun
      !! <-------- updates ends HERE

      x = 1.5
      write(*,*) fun(x)

      return
      end subroutine gauss

then compile it using

main:
    f2py -c --quiet --fcompiler=gnu95 \
        --f90flags=“-Wtabs” \
        -m test \
        test.f

You can test with

import test
f = lambda x: x
test.gauss(test.f)
test.gauss(f)

and see that the external function works for both Fortran and Python functions.


Two side notes:

  1. Although the documentation says !f2py intent(callback) fun is necessary, I find the code can work without it.
  2. You can specify multiple external functions using the same syntax, you can even use the same dummy input variable y for all of them (maybe not a good practice though).
zyy
  • 1,271
  • 15
  • 25