F2PY examples#
Below are some examples of F2PY usage. This list is not comprehensive, but canbe used as a starting point when wrapping your own code.
Note
The best place to look for examples is theNumPy issue tracker, or thetest cases forf2py. Some more use cases are inBoilerplate reduction and templating.
F2PY walkthrough: a basic extension module#
Creating source for a basic extension module#
Consider the following subroutine, contained in a file namedadd.f
CSUBROUTINEZADD(A,B,C,N)CDOUBLE COMPLEXA(*)DOUBLE COMPLEXB(*)DOUBLE COMPLEXC(*)INTEGERNDO20J=1,NC(J)=A(J)+B(J)20CONTINUE END
This routine simply adds the elements in two contiguous arrays and places theresult in a third. The memory for all three arrays must be provided by thecalling routine. A very basic interface to this routine can be automaticallygenerated by f2py:
python-mnumpy.f2py-maddadd.f
This command will produce an extension module namedaddmodule.c in thecurrent directory. This extension module can now be compiled and used fromPython just like any other extension module.
Creating a compiled extension module#
You can also get f2py to both compileadd.f along with the producedextension module leaving only a shared-library extension file that canbe imported from Python:
python-mnumpy.f2py-c-maddadd.f
This command produces a Python extension module compatible with your platform.This module may then be imported from Python. It will contain a method for eachsubroutine inadd. The docstring of each method contains information abouthow the module method may be called:
>>>importadd>>>print(add.zadd.__doc__)zadd(a,b,c,n)Wrapper for ``zadd``.Parameters----------a : input rank-1 array('D') with bounds (*)b : input rank-1 array('D') with bounds (*)c : input rank-1 array('D') with bounds (*)n : input int
Improving the basic interface#
The default interface is a very literal translation of the Fortran code intoPython. The Fortran array arguments are converted to NumPy arrays and theinteger argument should be mapped to aC integer. The interface will attemptto convert all arguments to their required types (and shapes) and issue an errorif unsuccessful. However, becausef2py knows nothing about the semantics ofthe arguments (such thatC is an output andn should really match thearray sizes), it is possible to abuse this function in ways that can causePython to crash. For example:
>>>add.zadd([1,2,3],[1,2],[3,4],1000)
will cause a program crash on most systems. Under the hood, the lists are beingconverted to arrays but then the underlyingadd function is told to cycleway beyond the borders of the allocated memory.
In order to improve the interface,f2py supports directives. This isaccomplished by constructing a signature file. It is usually best to start fromthe interfaces thatf2py produces in that file, which correspond to thedefault behavior. To getf2py to generate the interface file use the-hoption:
python-mnumpy.f2py-hadd.pyf-maddadd.f
This command creates theadd.pyf file in the current directory. The sectionof this file corresponding tozadd is:
subroutinezadd(a,b,c,n)! in :add:add.fdouble complexdimension(*)::adouble complexdimension(*)::bdouble complexdimension(*)::cinteger::nend subroutinezadd
By placing intent directives and checking code, the interface can be cleaned upquite a bit so the Python module method is both easier to use and more robust tomalformed inputs.
subroutinezadd(a,b,c,n)! in :add:add.fdouble complexdimension(n)::adouble complexdimension(n)::bdouble complexintent(out),dimension(n)::cintegerintent(hide),depend(a)::n=len(a)end subroutinezadd
The intent directive, intent(out) is used to tell f2py thatc isan output variable and should be created by the interface before beingpassed to the underlying code. The intent(hide) directive tells f2pyto not allow the user to specify the variable,n, but instead toget it from the size ofa. The depend(a ) directive isnecessary to tell f2py that the value of n depends on the input a (sothat it won’t try to create the variable n until the variable a iscreated).
After modifyingadd.pyf, the new Python module file can be generatedby compiling bothadd.f andadd.pyf:
python-mnumpy.f2py-cadd.pyfadd.f
The new interface’s docstring is:
>>>importadd>>>print(add.zadd.__doc__)c = zadd(a,b)Wrapper for ``zadd``.Parameters----------a : input rank-1 array('D') with bounds (n)b : input rank-1 array('D') with bounds (n)Returns-------c : rank-1 array('D') with bounds (n)
Now, the function can be called in a much more robust way:
>>>add.zadd([1,2,3],[4,5,6])array([5.+0.j, 7.+0.j, 9.+0.j])
Notice the automatic conversion to the correct format that occurred.
Inserting directives in Fortran source#
The robust interface of the previous section can also be generated automaticallyby placing the variable directives as special comments in the original Fortrancode.
Note
For projects where the Fortran code is being actively developed, this may bepreferred.
Thus, if the source code is modified to contain:
CSUBROUTINEZADD(A,B,C,N)CCF2PYINTENT(OUT)::CCF2PYINTENT(HIDE)::NCF2PYDOUBLE COMPLEX::A(N)CF2PYDOUBLE COMPLEX::B(N)CF2PYDOUBLE COMPLEX::C(N)DOUBLE COMPLEXA(*)DOUBLE COMPLEXB(*)DOUBLE COMPLEXC(*)INTEGERNDO20J=1,NC(J)=A(J)+B(J)20CONTINUE END
Then, one can compile the extension module using:
python-mnumpy.f2py-c-maddadd.f
The resulting signature for the function add.zadd is exactly the sameone that was created previously. If the original source code hadcontainedA(N) instead ofA(*) and so forth withB andC,then nearly the same interface can be obtained by placing theINTENT(OUT)::C comment line in the source code. The only differenceis thatN would be an optional input that would default to the lengthofA.
A filtering example#
This example shows a function that filters a two-dimensional array of doubleprecision floating-point numbers using a fixed averaging filter. The advantageof using Fortran to index into multi-dimensional arrays should be clear fromthis example.
CSUBROUTINEDFILTER2D(A,B,M,N)CDOUBLE PRECISIONA(M,N)DOUBLE PRECISIONB(M,N)INTEGERN,MCF2PYINTENT(OUT)::BCF2PYINTENT(HIDE)::NCF2PYINTENT(HIDE)::MDO20I=2,M-1DO40J=2,N-1B(I,J)=A(I,J)+&(A(I-1,J)+A(I+1,J)+&A(I,J-1)+A(I,J+1))*0.5D0+&(A(I-1,J-1)+A(I-1,J+1)+&A(I+1,J-1)+A(I+1,J+1))*0.25D040CONTINUE20CONTINUE END
This code can be compiled and linked into an extension module namedfilter using:
python-mnumpy.f2py-c-mfilterfilter.f
This will produce an extension module in the current directory with a methodnameddfilter2d that returns a filtered version of the input.
depends keyword example#
Consider the following code, saved in the filemyroutine.f90:
subroutines(n,m,c,x)implicit noneinteger,intent(in)::n,mreal(kind=8),intent(out),dimension(n,m)::xreal(kind=8),intent(in)::c(:)x=0.0d0x(1,1)=c(1)end subroutines
Wrapping this withpython-mnumpy.f2py-cmyroutine.f90-mmyroutine, wecan do the following in Python:
>>>importnumpyasnp>>>importmyroutine>>>x=myroutine.s(2,3,np.array([5,6,7]))>>>xarray([[5., 0., 0.], [0., 0., 0.]])
Now, instead of generating the extension module directly, we will create asignature file for this subroutine first. This is a common pattern formulti-step extension module generation. In this case, after running
python-mnumpy.f2pymyroutine.f90-mmyroutine-hmyroutine.pyf
the following signature file is generated:
! -*- f90 -*-! Note: the context of this file is case sensitive.pythonmodulemyroutine! ininterface! in :myroutinesubroutines(n,m,c,x)! in :myroutine:myroutine.f90integerintent(in)::nintegerintent(in)::mreal(kind=8)dimension(:),intent(in)::creal(kind=8)dimension(n,m),intent(out),depend(m,n)::xend subroutinesend interfaceendpythonmodulemyroutine! This file was auto-generated with f2py (version:1.23.0.dev0+120.g4da01f42d).! See:! https://web.archive.org/web/20140822061353/http://cens.ioc.ee/projects/f2py2e
Now, if we runpython-mnumpy.f2py-cmyroutine.pyfmyroutine.f90 we see anerror; note that the signature file included adepend(m,n) statement forx which is not necessary. Indeed, editing the file above to read
! -*- f90 -*-! Note: the context of this file is case sensitive.pythonmodulemyroutine! ininterface! in :myroutinesubroutines(n,m,c,x)! in :myroutine:myroutine.f90integerintent(in)::nintegerintent(in)::mreal(kind=8)dimension(:),intent(in)::creal(kind=8)dimension(n,m),intent(out)::xend subroutinesend interfaceendpythonmodulemyroutine! This file was auto-generated with f2py (version:1.23.0.dev0+120.g4da01f42d).! See:! https://web.archive.org/web/20140822061353/http://cens.ioc.ee/projects/f2py2e
and runningf2py-cmyroutine.pyfmyroutine.f90 yields correct results.