doc/source/f2py/f2py-examples.rst
.. _f2py-examples:
Below are some examples of F2PY usage. This list is not comprehensive, but can be used as a starting point when wrapping your own code.
.. note::
The best place to look for examples is the NumPy issue tracker_, or the
test cases for f2py. Some more use cases are in
:ref:f2py-boilerplating.
Creating source for a basic extension module
Consider the following subroutine, contained in a file named :file:`add.f`
.. literalinclude:: ./code/add.f
:language: fortran
This routine simply adds the elements in two contiguous arrays and places the
result in a third. The memory for all three arrays must be provided by the
calling routine. A very basic interface to this routine can be automatically
generated by f2py::
python -m numpy.f2py -m add add.f
This command will produce an extension module named :file:`addmodule.c` in the
current directory. This extension module can now be compiled and used from
Python just like any other extension module.
Creating a compiled extension module
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can also get f2py to both compile :file:`add.f` along with the produced
extension module leaving only a shared-library extension file that can
be imported from Python::
python -m numpy.f2py -c -m add add.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 each
subroutine in ``add``. The docstring of each method contains information about
how the module method may be called:
.. code-block:: python
>>> import add
>>> 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 into
Python. The Fortran array arguments are converted to NumPy arrays and the
integer argument should be mapped to a ``C`` integer. The interface will attempt
to convert all arguments to their required types (and shapes) and issue an error
if unsuccessful. However, because ``f2py`` knows nothing about the semantics of
the arguments (such that ``C`` is an output and ``n`` should really match the
array sizes), it is possible to abuse this function in ways that can cause
Python to crash. For example:
.. code-block:: python
>>> add.zadd([1, 2, 3], [1, 2], [3, 4], 1000)
will cause a program crash on most systems. Under the hood, the lists are being
converted to arrays but then the underlying ``add`` function is told to cycle
way beyond the borders of the allocated memory.
In order to improve the interface, ``f2py`` supports directives. This is
accomplished by constructing a signature file. It is usually best to start from
the interfaces that ``f2py`` produces in that file, which correspond to the
default behavior. To get ``f2py`` to generate the interface file use the ``-h``
option::
python -m numpy.f2py -h add.pyf -m add add.f
This command creates the ``add.pyf`` file in the current directory. The section
of this file corresponding to ``zadd`` is:
.. literalinclude:: ./code/add.pyf
:language: fortran
By placing intent directives and checking code, the interface can be cleaned up
quite a bit so the Python module method is both easier to use and more robust to
malformed inputs.
.. literalinclude:: ./code/add-edited.pyf
:language: fortran
The intent directive, intent(out) is used to tell f2py that ``c`` is
an output variable and should be created by the interface before being
passed to the underlying code. The intent(hide) directive tells f2py
to not allow the user to specify the variable, ``n``, but instead to
get it from the size of ``a``. The depend( ``a`` ) directive is
necessary to tell f2py that the value of n depends on the input a (so
that it won't try to create the variable n until the variable a is
created).
After modifying ``add.pyf``, the new Python module file can be generated
by compiling both ``add.f`` and ``add.pyf``::
python -m numpy.f2py -c add.pyf add.f
The new interface's docstring is:
.. code-block:: python
>>> import add
>>> 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:
.. code-block::
>>> 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 automatically
by placing the variable directives as special comments in the original Fortran
code.
.. note::
For projects where the Fortran code is being actively developed, this may be
preferred.
Thus, if the source code is modified to contain:
.. literalinclude:: ./code/add-improved.f
:language: fortran
Then, one can compile the extension module using::
python -m numpy.f2py -c -m add add.f
The resulting signature for the function add.zadd is exactly the same
one that was created previously. If the original source code had
contained ``A(N)`` instead of ``A(*)`` and so forth with ``B`` and ``C``,
then nearly the same interface can be obtained by placing the
``INTENT(OUT) :: C`` comment line in the source code. The only difference
is that ``N`` would be an optional input that would default to the length
of ``A``.
A filtering example
-------------------
This example shows a function that filters a two-dimensional array of double
precision floating-point numbers using a fixed averaging filter. The advantage
of using Fortran to index into multi-dimensional arrays should be clear from
this example.
.. literalinclude:: ./code/filter.f
:language: fortran
This code can be compiled and linked into an extension module named
filter using::
python -m numpy.f2py -c -m filter filter.f
This will produce an extension module in the current directory with a method
named ``dfilter2d`` that returns a filtered version of the input.
``depends`` keyword example
---------------------------
Consider the following code, saved in the file ``myroutine.f90``:
.. literalinclude:: ./code/myroutine.f90
:language: fortran
Wrapping this with ``python -m numpy.f2py -c myroutine.f90 -m myroutine``, we
can do the following in Python::
>>> import numpy as np
>>> import myroutine
>>> x = myroutine.s(2, 3, np.array([5, 6, 7]))
>>> x
array([[5., 0., 0.],
[0., 0., 0.]])
Now, instead of generating the extension module directly, we will create a
signature file for this subroutine first. This is a common pattern for
multi-step extension module generation. In this case, after running
.. code-block:: python
python -m numpy.f2py myroutine.f90 -m myroutine -h myroutine.pyf
the following signature file is generated:
.. literalinclude:: ./code/myroutine.pyf
:language: fortran
Now, if we run ``python -m numpy.f2py -c myroutine.pyf myroutine.f90`` we see an
error; note that the signature file included a ``depend(m,n)`` statement for
``x`` which is not necessary. Indeed, editing the file above to read
.. literalinclude:: ./code/myroutine-edited.pyf
:language: fortran
and running ``f2py -c myroutine.pyf myroutine.f90`` yields correct results.
Read more
---------
* `Wrapping C codes using f2py <https://scipy.github.io/old-wiki/pages/Cookbook/f2py_and_NumPy.html>`_
* `F2py section on the SciPy Cookbook <https://scipy-cookbook.readthedocs.io/items/F2Py.html>`_
* `"Interfacing With Other Languages" section on the SciPy Cookbook.
<https://scipy-cookbook.readthedocs.io/items/idx_interfacing_with_other_languages.html>`_
.. _`NumPy issue tracker`: https://github.com/numpy/numpy/issues?q=is%3Aissue+label%3A%22component%3A+numpy.f2py%22+is%3Aclosed
.. _`test cases`: https://github.com/numpy/numpy/tree/main/doc/source/f2py/code