Using F2PY bindings in Python#
In this page, you can find a full description and a few examples of common usage patterns for F2PY with Python and different argument types. For more examples and use cases, see F2PY examples.
Fortran type objects#
All wrappers for Fortran/C routines, common blocks, or for Fortran 90 module
data generated by F2PY are exposed to Python as fortran
type objects.
Routine wrappers are callable fortran
type objects while wrappers to Fortran
data have attributes referring to data objects.
All fortran
type objects have an attribute _cpointer
that contains a
CObject
referring to the C pointer of the corresponding Fortran/C function
or variable at the C level. Such CObjects
can be used as callback arguments
for F2PY generated functions to bypass the Python C/API layer for calling Python
functions from Fortran or C. This can be useful when the computational aspects
of such functions are implemented in C or Fortran and wrapped with F2PY (or any
other tool capable of providing the CObject
of a function).
Consider a Fortran 77 file `ftype.f
:
C FILE: FTYPE.F
SUBROUTINE FOO(N)
INTEGER N
Cf2py integer optional,intent(in) :: n = 13
REAL A,X
COMMON /DATA/ A,X(3)
PRINT*, "IN FOO: N=",N," A=",A," X=[",X(1),X(2),X(3),"]"
END
C END OF FTYPE.F
and a wrapper built using f2py -c ftype.f -m ftype
.
In Python, you can observe the types of foo
and data
, and how to access
individual objects of the wrapped Fortran code.
>>> import ftype
>>> print(ftype.__doc__)
This module 'ftype' is auto-generated with f2py (version:2).
Functions:
foo(n=13)
COMMON blocks:
/data/ a,x(3)
.
>>> type(ftype.foo), type(ftype.data)
(<class 'fortran'>, <class 'fortran'>)
>>> ftype.foo()
IN FOO: N= 13 A= 0. X=[ 0. 0. 0.]
>>> ftype.data.a = 3
>>> ftype.data.x = [1,2,3]
>>> ftype.foo()
IN FOO: N= 13 A= 3. X=[ 1. 2. 3.]
>>> ftype.data.x[1] = 45
>>> ftype.foo(24)
IN FOO: N= 24 A= 3. X=[ 1. 45. 3.]
>>> ftype.data.x
array([ 1., 45., 3.], dtype=float32)
Scalar arguments#
In general, a scalar argument for a F2PY generated wrapper function can be an ordinary Python scalar (integer, float, complex number) as well as an arbitrary sequence object (list, tuple, array, string) of scalars. In the latter case, the first element of the sequence object is passed to the Fortran routine as a scalar argument.
Note
When type-casting is required and there is possible loss of information via narrowing e.g. when type-casting float to integer or complex to float, F2PY does not raise an exception.
For complex to real type-casting only the real part of a complex number is used.
intent(inout)
scalar arguments are assumed to be array objects in order to have in situ changes be effective. It is recommended to use arrays with proper type but also other types work. Read more about the intent attribute.
Consider the following Fortran 77 code:
C FILE: SCALAR.F
SUBROUTINE FOO(A,B)
REAL*8 A, B
Cf2py intent(in) a
Cf2py intent(inout) b
PRINT*, " A=",A," B=",B
PRINT*, "INCREMENT A AND B"
A = A + 1D0
B = B + 1D0
PRINT*, "NEW A=",A," B=",B
END
C END OF FILE SCALAR.F
and wrap it using f2py -c -m scalar scalar.f
.
In Python:
>>> import scalar
>>> print(scalar.foo.__doc__)
foo(a,b)
Wrapper for ``foo``.
Parameters
----------
a : input float
b : in/output rank-0 array(float,'d')
>>> scalar.foo(2, 3)
A= 2. B= 3.
INCREMENT A AND B
NEW A= 3. B= 4.
>>> import numpy
>>> a = numpy.array(2) # these are integer rank-0 arrays
>>> b = numpy.array(3)
>>> scalar.foo(a, b)
A= 2. B= 3.
INCREMENT A AND B
NEW A= 3. B= 4.
>>> print(a, b) # note that only b is changed in situ
2 4
String arguments#
F2PY generated wrapper functions accept almost any Python object as a string
argument, since str
is applied for non-string objects. Exceptions are NumPy
arrays that must have type code 'S1'
or 'b'
(corresponding to the
outdated 'c'
or '1'
typecodes, respectively) when used as string
arguments. See Scalars for more information on these typecodes.
A string can have an arbitrary length when used as a string argument for an F2PY
generated wrapper function. If the length is greater than expected, the string
is truncated silently. If the length is smaller than expected, additional memory
is allocated and filled with \0
.
Because Python strings are immutable, an intent(inout)
argument expects an
array version of a string in order to have in situ changes be effective.
Consider the following Fortran 77 code:
C FILE: STRING.F
SUBROUTINE FOO(A,B,C,D)
CHARACTER*5 A, B
CHARACTER*(*) C,D
Cf2py intent(in) a,c
Cf2py intent(inout) b,d
PRINT*, "A=",A
PRINT*, "B=",B
PRINT*, "C=",C
PRINT*, "D=",D
PRINT*, "CHANGE A,B,C,D"
A(1:1) = 'A'
B(1:1) = 'B'
C(1:1) = 'C'
D(1:1) = 'D'
PRINT*, "A=",A
PRINT*, "B=",B
PRINT*, "C=",C
PRINT*, "D=",D
END
C END OF FILE STRING.F
and wrap it using f2py -c -m mystring string.f
.
Python session:
>>> import mystring
>>> print(mystring.foo.__doc__)
foo(a,b,c,d)
Wrapper for ``foo``.
Parameters
----------
a : input string(len=5)
b : in/output rank-0 array(string(len=5),'c')
c : input string(len=-1)
d : in/output rank-0 array(string(len=-1),'c')
>>> from numpy import array
>>> a = array(b'123\0\0')
>>> b = array(b'123\0\0')
>>> c = array(b'123')
>>> d = array(b'123')
>>> mystring.foo(a, b, c, d)
A=123
B=123
C=123
D=123
CHANGE A,B,C,D
A=A23
B=B23
C=C23
D=D23
>>> a[()], b[()], c[()], d[()]
(b'123', b'B23', b'123', b'D2')
Array arguments#
In general, array arguments for F2PY generated wrapper functions accept arbitrary sequences that can be transformed to NumPy array objects. There are two notable exceptions:
intent(inout)
array arguments must always be proper-contiguous and have a compatibledtype
, otherwise an exception is raised.intent(inplace)
array arguments will be changed in situ if the argument has a different type than expected (see theintent(inplace)
attribute for more information).
In general, if a NumPy array is proper-contiguous and has a proper type then it is directly passed to the wrapped Fortran/C function. Otherwise, an element-wise copy of the input array is made and the copy, being proper-contiguous and with proper type, is used as the array argument.
Usually there is no need to worry about how the arrays are stored in memory and whether the wrapped functions, being either Fortran or C functions, assume one or another storage order. F2PY automatically ensures that wrapped functions get arguments with the proper storage order; the underlying algorithm is designed to make copies of arrays only when absolutely necessary. However, when dealing with very large multidimensional input arrays with sizes close to the size of the physical memory in your computer, then care must be taken to ensure the usage of proper-contiguous and proper type arguments.
To transform input arrays to column major storage order before passing
them to Fortran routines, use the function numpy.asfortranarray
.
Consider the following Fortran 77 code:
C FILE: ARRAY.F
SUBROUTINE FOO(A,N,M)
C
C INCREMENT THE FIRST ROW AND DECREMENT THE FIRST COLUMN OF A
C
INTEGER N,M,I,J
REAL*8 A(N,M)
Cf2py intent(in,out,copy) a
Cf2py integer intent(hide),depend(a) :: n=shape(a,0), m=shape(a,1)
DO J=1,M
A(1,J) = A(1,J) + 1D0
ENDDO
DO I=1,N
A(I,1) = A(I,1) - 1D0
ENDDO
END
C END OF FILE ARRAY.F
and wrap it using f2py -c -m arr array.f -DF2PY_REPORT_ON_ARRAY_COPY=1
.
In Python:
>>> import arr
>>> from numpy import asfortranarray
>>> print(arr.foo.__doc__)
a = foo(a,[overwrite_a])
Wrapper for ``foo``.
Parameters
----------
a : input rank-2 array('d') with bounds (n,m)
Other Parameters
----------------
overwrite_a : input int, optional
Default: 0
Returns
-------
a : rank-2 array('d') with bounds (n,m)
>>> a = arr.foo([[1, 2, 3],
... [4, 5, 6]])
created an array from object
>>> print(a)
[[ 1. 3. 4.]
[ 3. 5. 6.]]
>>> a.flags.c_contiguous
False
>>> a.flags.f_contiguous
True
# even if a is proper-contiguous and has proper type,
# a copy is made forced by intent(copy) attribute
# to preserve its original contents
>>> b = arr.foo(a)
copied an array: size=6, elsize=8
>>> print(a)
[[ 1. 3. 4.]
[ 3. 5. 6.]]
>>> print(b)
[[ 1. 4. 5.]
[ 2. 5. 6.]]
>>> b = arr.foo(a, overwrite_a = 1) # a is passed directly to Fortran
... # routine and its contents is discarded
...
>>> print(a)
[[ 1. 4. 5.]
[ 2. 5. 6.]]
>>> print(b)
[[ 1. 4. 5.]
[ 2. 5. 6.]]
>>> a is b # a and b are actually the same objects
True
>>> print(arr.foo([1, 2, 3])) # different rank arrays are allowed
created an array from object
[ 1. 1. 2.]
>>> print(arr.foo([[[1], [2], [3]]]))
created an array from object
[[[ 1.]
[ 1.]
[ 2.]]]
>>>
>>> # Creating arrays with column major data storage order:
...
>>> s = asfortranarray([[1, 2, 3], [4, 5, 6]])
>>> s.flags.f_contiguous
True
>>> print(s)
[[1 2 3]
[4 5 6]]
>>> print(arr.foo(s))
>>> s2 = asfortranarray(s)
>>> s2 is s # an array with column major storage order
# is returned immediately
True
>>> # Note that arr.foo returns a column major data storage order array:
...
>>> s3 = ascontiguousarray(s)
>>> s3.flags.f_contiguous
False
>>> s3.flags.c_contiguous
True
>>> s3 = arr.foo(s3)
copied an array: size=6, elsize=8
>>> s3.flags.f_contiguous
True
>>> s3.flags.c_contiguous
False
Call-back arguments#
F2PY supports calling Python functions from Fortran or C codes.
Consider the following Fortran 77 code:
C FILE: CALLBACK.F
SUBROUTINE FOO(FUN,R)
EXTERNAL FUN
INTEGER I
REAL*8 R, FUN
Cf2py intent(out) r
R = 0D0
DO I=-5,5
R = R + FUN(I)
ENDDO
END
C END OF FILE CALLBACK.F
and wrap it using f2py -c -m callback callback.f
.
In Python:
>>> import callback
>>> print(callback.foo.__doc__)
r = foo(fun,[fun_extra_args])
Wrapper for ``foo``.
Parameters
----------
fun : call-back function
Other Parameters
----------------
fun_extra_args : input tuple, optional
Default: ()
Returns
-------
r : float
Notes
-----
Call-back functions::
def fun(i): return r
Required arguments:
i : input int
Return objects:
r : float
>>> def f(i): return i*i
...
>>> print(callback.foo(f))
110.0
>>> print(callback.foo(lambda i:1))
11.0
In the above example F2PY was able to guess accurately the signature of the call-back function. However, sometimes F2PY cannot establish the appropriate signature; in these cases the signature of the call-back function must be explicitly defined in the signature file.
To facilitate this, signature files may contain special modules (the names of
these modules contain the special __user__
sub-string) that define the
various signatures for call-back functions. Callback arguments in routine
signatures have the external
attribute (see also the intent(callback)
attribute). To relate a callback argument with its
signature in a __user__
module block, a use
statement can be utilized as
illustrated below. The same signature for a callback argument can be referred to
in different routine signatures.
We use the same Fortran 77 code as in the previous example but now
we will pretend that F2PY was not able to guess the signatures of
call-back arguments correctly. First, we create an initial signature
file callback2.pyf
using F2PY:
f2py -m callback2 -h callback2.pyf callback.f
Then modify it as follows
! -*- f90 -*-
python module __user__routines
interface
function fun(i) result (r)
integer :: i
real*8 :: r
end function fun
end interface
end python module __user__routines
python module callback2
interface
subroutine foo(f,r)
use __user__routines, f=>fun
external f
real*8 intent(out) :: r
end subroutine foo
end interface
end python module callback2
Finally, we build the extension module using
f2py -c callback2.pyf callback.f
.
An example Python session for this snippet would be identical to the previous example except that the argument names would differ.
Sometimes a Fortran package may require that users provide routines that the package will use. F2PY can construct an interface to such routines so that Python functions can be called from Fortran.
Consider the following Fortran 77 subroutine that takes an array as its input
and applies a function func
to its elements.
subroutine calculate(x,n)
cf2py intent(callback) func
external func
c The following lines define the signature of func for F2PY:
cf2py real*8 y
cf2py y = func(y)
c
cf2py intent(in,out,copy) x
integer n,i
real*8 x(n), func
do i=1,n
x(i) = func(x(i))
end do
end
The Fortran code expects that the function func
has been defined externally.
In order to use a Python function for func
, it must have an attribute
intent(callback)
and it must be specified before the external
statement.
Finally, build an extension module using f2py -c -m foo calculate.f
In Python:
>>> import foo
>>> foo.calculate(range(5), lambda x: x*x)
array([ 0., 1., 4., 9., 16.])
>>> import math
>>> foo.calculate(range(5), math.exp)
array([ 1. , 2.71828183, 7.3890561, 20.08553692, 54.59815003])
The function is included as an argument to the python function call to the Fortran subroutine even though it was not in the Fortran subroutine argument list. The “external” keyword refers to the C function generated by f2py, not the Python function itself. The python function is essentially being supplied to the C function.
The callback function may also be explicitly set in the module. Then it is not necessary to pass the function in the argument list to the Fortran function. This may be desired if the Fortran function calling the Python callback function is itself called by another Fortran function.
Consider the following Fortran 77 subroutine:
subroutine f1()
print *, "in f1, calling f2 twice.."
call f2()
call f2()
return
end
subroutine f2()
cf2py intent(callback, hide) fpy
external fpy
print *, "in f2, calling f2py.."
call fpy()
return
end
and wrap it using f2py -c -m pfromf extcallback.f
.
In Python:
>>> import pfromf
>>> pfromf.f2()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
pfromf.error: Callback fpy not defined (as an argument or module pfromf attribute).
>>> def f(): print("python f")
...
>>> pfromf.fpy = f
>>> pfromf.f2()
in f2, calling f2py..
python f
>>> pfromf.f1()
in f1, calling f2 twice..
in f2, calling f2py..
python f
in f2, calling f2py..
python f
>>>
Resolving arguments to call-back functions#
F2PY generated interfaces are very flexible with respect to call-back arguments. For each call-back argument an additional optional
argument <name>_extra_args
is introduced by F2PY. This argument can be used
to pass extra arguments to user provided call-back functions.
If a F2PY generated wrapper function expects the following call-back argument:
def fun(a_1,...,a_n):
...
return x_1,...,x_k
but the following Python function
def gun(b_1,...,b_m):
...
return y_1,...,y_l
is provided by a user, and in addition,
fun_extra_args = (e_1,...,e_p)
is used, then the following rules are applied when a Fortran or C function
evaluates the call-back argument gun
:
If
p == 0
thengun(a_1, ..., a_q)
is called, hereq = min(m, n)
.If
n + p <= m
thengun(a_1, ..., a_n, e_1, ..., e_p)
is called.If
p <= m < n + p
thengun(a_1, ..., a_q, e_1, ..., e_p)
is called, and hereq=m-p
.If
p > m
thengun(e_1, ..., e_m)
is called.If
n + p
is less than the number of required arguments togun
then an exception is raised.
If the function gun
may return any number of objects as a tuple; then the
following rules are applied:
If
k < l
, theny_{k + 1}, ..., y_l
are ignored.If
k > l
, then onlyx_1, ..., x_l
are set.
Common blocks#
F2PY generates wrappers to common
blocks defined in a routine signature
block. Common blocks are visible to all Fortran codes linked to the current
extension module, but not to other extension modules (this restriction is due to
the way Python imports shared libraries). In Python, the F2PY wrappers to
common
blocks are fortran
type objects that have (dynamic) attributes
related to the data members of the common blocks. When accessed, these
attributes return as NumPy array objects (multidimensional arrays are
Fortran-contiguous) which directly link to data members in common blocks. Data
members can be changed by direct assignment or by in-place changes to the
corresponding array objects.
Consider the following Fortran 77 code:
C FILE: COMMON.F
SUBROUTINE FOO
INTEGER I,X
REAL A
COMMON /DATA/ I,X(4),A(2,3)
PRINT*, "I=",I
PRINT*, "X=[",X,"]"
PRINT*, "A=["
PRINT*, "[",A(1,1),",",A(1,2),",",A(1,3),"]"
PRINT*, "[",A(2,1),",",A(2,2),",",A(2,3),"]"
PRINT*, "]"
END
C END OF COMMON.F
and wrap it using f2py -c -m common common.f
.
In Python:
>>> import common
>>> print(common.data.__doc__)
i : 'i'-scalar
x : 'i'-array(4)
a : 'f'-array(2,3)
>>> common.data.i = 5
>>> common.data.x[1] = 2
>>> common.data.a = [[1,2,3],[4,5,6]]
>>> common.foo()
>>> common.foo()
I= 5
X=[ 0 2 0 0 ]
A=[
[ 1.00000000 , 2.00000000 , 3.00000000 ]
[ 4.00000000 , 5.00000000 , 6.00000000 ]
]
>>> common.data.a[1] = 45
>>> common.foo()
I= 5
X=[ 0 2 0 0 ]
A=[
[ 1.00000000 , 2.00000000 , 3.00000000 ]
[ 45.0000000 , 45.0000000 , 45.0000000 ]
]
>>> common.data.a # a is Fortran-contiguous
array([[ 1., 2., 3.],
[ 45., 45., 45.]], dtype=float32)
>>> common.data.a.flags.f_contiguous
True
Fortran 90 module data#
The F2PY interface to Fortran 90 module data is similar to the handling of Fortran 77 common blocks.
Consider the following Fortran 90 code:
module mod
integer i
integer :: x(4)
real, dimension(2,3) :: a
real, allocatable, dimension(:,:) :: b
contains
subroutine foo
integer k
print*, "i=",i
print*, "x=[",x,"]"
print*, "a=["
print*, "[",a(1,1),",",a(1,2),",",a(1,3),"]"
print*, "[",a(2,1),",",a(2,2),",",a(2,3),"]"
print*, "]"
print*, "Setting a(1,2)=a(1,2)+3"
a(1,2) = a(1,2)+3
end subroutine foo
end module mod
and wrap it using f2py -c -m moddata moddata.f90
.
In Python:
>>> import moddata
>>> print(moddata.mod.__doc__)
i : 'i'-scalar
x : 'i'-array(4)
a : 'f'-array(2,3)
b : 'f'-array(-1,-1), not allocated
foo()
Wrapper for ``foo``.
>>> moddata.mod.i = 5
>>> moddata.mod.x[:2] = [1,2]
>>> moddata.mod.a = [[1,2,3],[4,5,6]]
>>> moddata.mod.foo()
i= 5
x=[ 1 2 0 0 ]
a=[
[ 1.000000 , 2.000000 , 3.000000 ]
[ 4.000000 , 5.000000 , 6.000000 ]
]
Setting a(1,2)=a(1,2)+3
>>> moddata.mod.a # a is Fortran-contiguous
array([[ 1., 5., 3.],
[ 4., 5., 6.]], dtype=float32)
>>> moddata.mod.a.flags.f_contiguous
True
Allocatable arrays#
F2PY has basic support for Fortran 90 module allocatable arrays.
Consider the following Fortran 90 code:
module mod
real, allocatable, dimension(:,:) :: b
contains
subroutine foo
integer k
if (allocated(b)) then
print*, "b=["
do k = 1,size(b,1)
print*, b(k,1:size(b,2))
enddo
print*, "]"
else
print*, "b is not allocated"
endif
end subroutine foo
end module mod
and wrap it using f2py -c -m allocarr allocarr.f90
.
In Python:
>>> import allocarr
>>> print(allocarr.mod.__doc__)
b : 'f'-array(-1,-1), not allocated
foo()
Wrapper for ``foo``.
>>> allocarr.mod.foo()
b is not allocated
>>> allocarr.mod.b = [[1, 2, 3], [4, 5, 6]] # allocate/initialize b
>>> allocarr.mod.foo()
b=[
1.000000 2.000000 3.000000
4.000000 5.000000 6.000000
]
>>> allocarr.mod.b # b is Fortran-contiguous
array([[ 1., 2., 3.],
[ 4., 5., 6.]], dtype=float32)
>>> allocarr.mod.b.flags.f_contiguous
True
>>> allocarr.mod.b = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] # reallocate/initialize b
>>> allocarr.mod.foo()
b=[
1.000000 2.000000 3.000000
4.000000 5.000000 6.000000
7.000000 8.000000 9.000000
]
>>> allocarr.mod.b = None # deallocate array
>>> allocarr.mod.foo()
b is not allocated