I have recently started using f2py to call Fortran from Python. I have found this useful for two reasons: speeding up Python scripts by calling compiled Fortran code, and using Python as a unit testing framework for Fortran modules. Unfortunately, the documentation for f2py is rather sparse, and may not be completely up to date. In this note, I will hopefully prevent you from wasting a lot of time figuring out how to pass array arguments, and return array results.
Passing array arguments is a critical when working with numerical algorithms. F2py handles this well, but it is difficult to figure it out how to do it correctly. Here is a simple example with some Fortran 90 routines:
module test contains subroutine foo (a) implicit none integer, intent(in) :: a print*, "Hello from Fortran!" print*, "a=",a end subroutine foo function bar (len_a, a) implicit none integer, intent(in) :: len_a real, dimension(len_a), intent(in) :: a real, dimension(len_a) :: bar !f2py depend(len_a) a, bar integer :: i real, dimension(len_a) :: b do i=1,len_a b(i) = 2.0*a(i) end do bar = b end function bar subroutine sub (a, len_a, a_out) implicit none real, dimension(len_a), intent(in) :: a integer, intent(in) :: len_a real, dimension(len_a), intent(out) :: a_out integer :: i do i=1,len_a a_out(i) = 2.0*a(i) end do end subroutine sub end module test
Function “foo” is quite straightforward. Function “bar” is a little more complex, because it accepts an array argument and returns an array. If you’re new to Fortran, it will seem strange to pass the length of the array along with the array, but you need that information to declare the output array. f2py needs to know that the input array a and the output array bar both depend on the argument len_a. The special comment line
!f2py depend(len_a) a, bar
is mandatory! It tells f2py that a depends on len_a. If you omit this comment or the corresponding one for, the function will not work correctly. You will get strange errors like
ValueError: failed to create intent(cache|hide)|optional array-- must have defined dimensions but got (0,)
Now look at the subroutine called sub. On the Fortran side, it has three arguments: two inputs and an output. However, Python only has functions, and all non-array arguments are passed by value. How do you reconcile this? When the Fortran subroutine is called from Python, the intent(out) variables are returned as a function result, or a tuple of results if there are more than one. Compare the Fortran code above with the Python call below to see what I mean.
This code can be compiled using the “fast and smart” method described in the docs:
f2py -c -m hello hello.f90
Here is the Python code that calls the Fortran routines:
#!/usr/bin/env python import hello from numpy import * a = arange(0.0, 10.0, 1.0) len_a = len(a) print "foo:" hello.test.foo(len_a) print "bar:" a_out = hello.test.bar(len_a, a) print a_out print "sub:" a_out = hello.test.sub(a, len_a) print a_out
You might also want to check out this rather complicated f2py example.