273 lines
8.9 KiB
ReStructuredText
273 lines
8.9 KiB
ReStructuredText
======================================
|
|
Three ways to wrap - getting started
|
|
======================================
|
|
|
|
Wrapping Fortran or C functions to Python using F2PY consists of the
|
|
following steps:
|
|
|
|
* Creating the so-called signature file that contains descriptions of
|
|
wrappers to Fortran or C functions, also called as signatures of the
|
|
functions. In the case of Fortran routines, F2PY can create initial
|
|
signature file by scanning Fortran source codes and
|
|
catching all relevant information needed to create wrapper
|
|
functions.
|
|
|
|
* Optionally, F2PY created signature files can be edited to optimize
|
|
wrappers functions, make them "smarter" and more "Pythonic".
|
|
|
|
* F2PY reads a signature file and writes a Python C/API module containing
|
|
Fortran/C/Python bindings.
|
|
|
|
* F2PY compiles all sources and builds an extension module containing
|
|
the wrappers. In building extension modules, F2PY uses
|
|
``numpy_distutils`` that supports a number of Fortran 77/90/95
|
|
compilers, including Gnu, Intel,
|
|
Sun Fortre, SGI MIPSpro, Absoft, NAG, Compaq etc. compilers.
|
|
|
|
Depending on a particular situation, these steps can be carried out
|
|
either by just in one command or step-by-step, some steps can be
|
|
omitted or combined with others.
|
|
|
|
Below I'll describe three typical approaches of using F2PY.
|
|
The following example Fortran 77 code will be used for
|
|
illustration, save it as fib1.f:
|
|
|
|
.. include:: fib1.f
|
|
:literal:
|
|
|
|
|
|
The quick way
|
|
==============
|
|
|
|
The quickest way to wrap the Fortran subroutine ``FIB`` to Python is
|
|
to run
|
|
|
|
::
|
|
|
|
python -m numpy.f2py -c fib1.f -m fib1
|
|
|
|
This command builds (see ``-c`` flag, execute ``python -m numpy.f2py`` without
|
|
arguments to see the explanation of command line options) an extension
|
|
module ``fib1.so`` (see ``-m`` flag) to the current directory. Now, in
|
|
Python the Fortran subroutine ``FIB`` is accessible via ``fib1.fib``::
|
|
|
|
>>> import numpy
|
|
>>> import fib1
|
|
>>> print(fib1.fib.__doc__)
|
|
fib(a,[n])
|
|
|
|
Wrapper for ``fib``.
|
|
|
|
Parameters
|
|
----------
|
|
a : input rank-1 array('d') with bounds (n)
|
|
|
|
Other Parameters
|
|
----------------
|
|
n : input int, optional
|
|
Default: len(a)
|
|
|
|
>>> a = numpy.zeros(8, 'd')
|
|
>>> fib1.fib(a)
|
|
>>> print(a)
|
|
[ 0. 1. 1. 2. 3. 5. 8. 13.]
|
|
|
|
.. note::
|
|
|
|
* Note that F2PY found that the second argument ``n`` is the
|
|
dimension of the first array argument ``a``. Since by default all
|
|
arguments are input-only arguments, F2PY concludes that ``n`` can
|
|
be optional with the default value ``len(a)``.
|
|
|
|
* One can use different values for optional ``n``::
|
|
|
|
>>> a1 = numpy.zeros(8, 'd')
|
|
>>> fib1.fib(a1, 6)
|
|
>>> print(a1)
|
|
[ 0. 1. 1. 2. 3. 5. 0. 0.]
|
|
|
|
but an exception is raised when it is incompatible with the input
|
|
array ``a``::
|
|
|
|
>>> fib1.fib(a, 10)
|
|
Traceback (most recent call last):
|
|
File "<stdin>", line 1, in <module>
|
|
fib.error: (len(a)>=n) failed for 1st keyword n: fib:n=10
|
|
>>>
|
|
|
|
F2PY implements basic compatibility checks between related
|
|
arguments in order to avoid any unexpected crashes.
|
|
|
|
* When a NumPy array, that is Fortran contiguous and has a dtype
|
|
corresponding to presumed Fortran type, is used as an input array
|
|
argument, then its C pointer is directly passed to Fortran.
|
|
|
|
Otherwise F2PY makes a contiguous copy (with a proper dtype) of
|
|
the input array and passes C pointer of the copy to Fortran
|
|
subroutine. As a result, any possible changes to the (copy of)
|
|
input array have no effect to the original argument, as
|
|
demonstrated below::
|
|
|
|
>>> a = numpy.ones(8, 'i')
|
|
>>> fib1.fib(a)
|
|
>>> print(a)
|
|
[1 1 1 1 1 1 1 1]
|
|
|
|
Clearly, this is not an expected behaviour. The fact that the
|
|
above example worked with ``dtype=float`` is considered
|
|
accidental.
|
|
|
|
F2PY provides ``intent(inplace)`` attribute that would modify
|
|
the attributes of an input array so that any changes made by
|
|
Fortran routine will be effective also in input argument. For example,
|
|
if one specifies ``intent(inplace) a`` (see below, how), then
|
|
the example above would read::
|
|
|
|
>>> a = numpy.ones(8, 'i')
|
|
>>> fib1.fib(a)
|
|
>>> print(a)
|
|
[ 0. 1. 1. 2. 3. 5. 8. 13.]
|
|
|
|
However, the recommended way to get changes made by Fortran
|
|
subroutine back to Python is to use ``intent(out)`` attribute. It
|
|
is more efficient and a cleaner solution.
|
|
|
|
* The usage of ``fib1.fib`` in Python is very similar to using
|
|
``FIB`` in Fortran. However, using *in situ* output arguments in
|
|
Python indicates a poor style as there is no safety mechanism
|
|
in Python with respect to wrong argument types. When using Fortran
|
|
or C, compilers naturally discover any type mismatches during
|
|
compile time but in Python the types must be checked in
|
|
runtime. So, using *in situ* output arguments in Python may cause
|
|
difficult to find bugs, not to mention that the codes will be less
|
|
readable when all required type checks are implemented.
|
|
|
|
Though the demonstrated way of wrapping Fortran routines to Python
|
|
is very straightforward, it has several drawbacks (see the comments
|
|
above). These drawbacks are due to the fact that there is no way
|
|
that F2PY can determine what is the actual intention of one or the
|
|
other argument, is it input or output argument, or both, or
|
|
something else. So, F2PY conservatively assumes that all arguments
|
|
are input arguments by default.
|
|
|
|
However, there are ways (see below) how to "teach" F2PY about the
|
|
true intentions (among other things) of function arguments; and then
|
|
F2PY is able to generate more Pythonic (more explicit, easier to
|
|
use, and less error prone) wrappers to Fortran functions.
|
|
|
|
The smart way
|
|
==============
|
|
|
|
Let's apply the steps of wrapping Fortran functions to Python one by
|
|
one.
|
|
|
|
* First, we create a signature file from ``fib1.f`` by running
|
|
|
|
::
|
|
|
|
python -m numpy.f2py fib1.f -m fib2 -h fib1.pyf
|
|
|
|
The signature file is saved to ``fib1.pyf`` (see ``-h`` flag) and
|
|
its contents is shown below.
|
|
|
|
.. include:: fib1.pyf
|
|
:literal:
|
|
|
|
* Next, we'll teach F2PY that the argument ``n`` is an input argument
|
|
(use ``intent(in)`` attribute) and that the result, i.e. the
|
|
contents of ``a`` after calling Fortran function ``FIB``, should be
|
|
returned to Python (use ``intent(out)`` attribute). In addition, an
|
|
array ``a`` should be created dynamically using the size given by
|
|
the input argument ``n`` (use ``depend(n)`` attribute to indicate
|
|
dependence relation).
|
|
|
|
The content of a modified version of ``fib1.pyf`` (saved as
|
|
``fib2.pyf``) is as follows:
|
|
|
|
.. include:: fib2.pyf
|
|
:literal:
|
|
|
|
* And finally, we build the extension module by running
|
|
|
|
::
|
|
|
|
python -m numpy.f2py -c fib2.pyf fib1.f
|
|
|
|
In Python::
|
|
|
|
>>> import fib2
|
|
>>> print(fib2.fib.__doc__)
|
|
a = fib(n)
|
|
|
|
Wrapper for ``fib``.
|
|
|
|
Parameters
|
|
----------
|
|
n : input int
|
|
|
|
Returns
|
|
-------
|
|
a : rank-1 array('d') with bounds (n)
|
|
|
|
>>> print(fib2.fib(8))
|
|
[ 0. 1. 1. 2. 3. 5. 8. 13.]
|
|
|
|
.. note::
|
|
|
|
* Clearly, the signature of ``fib2.fib`` now corresponds to the
|
|
intention of Fortran subroutine ``FIB`` more closely: given the
|
|
number ``n``, ``fib2.fib`` returns the first ``n`` Fibonacci numbers
|
|
as a NumPy array. Also, the new Python signature ``fib2.fib``
|
|
rules out any surprises that we experienced with ``fib1.fib``.
|
|
|
|
* Note that by default using single ``intent(out)`` also implies
|
|
``intent(hide)``. Arguments that have the ``intent(hide)`` attribute
|
|
specified will not be listed in the argument list of a wrapper
|
|
function.
|
|
|
|
The quick and smart way
|
|
========================
|
|
|
|
The "smart way" of wrapping Fortran functions, as explained above, is
|
|
suitable for wrapping (e.g. third party) Fortran codes for which
|
|
modifications to their source codes are not desirable nor even
|
|
possible.
|
|
|
|
However, if editing Fortran codes is acceptable, then the generation
|
|
of an intermediate signature file can be skipped in most
|
|
cases. Namely, F2PY specific attributes can be inserted directly to
|
|
Fortran source codes using the so-called F2PY directive. A F2PY
|
|
directive defines special comment lines (starting with ``Cf2py``, for
|
|
example) which are ignored by Fortran compilers but F2PY interprets
|
|
them as normal lines.
|
|
|
|
Here is shown a modified version of the previous Fortran code, save it
|
|
as ``fib3.f``:
|
|
|
|
.. include:: fib3.f
|
|
:literal:
|
|
|
|
Building the extension module can be now carried out in one command::
|
|
|
|
python -m numpy.f2py -c -m fib3 fib3.f
|
|
|
|
Notice that the resulting wrapper to ``FIB`` is as "smart" as in
|
|
previous case::
|
|
|
|
>>> import fib3
|
|
>>> print(fib3.fib.__doc__)
|
|
a = fib(n)
|
|
|
|
Wrapper for ``fib``.
|
|
|
|
Parameters
|
|
----------
|
|
n : input int
|
|
|
|
Returns
|
|
-------
|
|
a : rank-1 array('d') with bounds (n)
|
|
|
|
>>> print(fib3.fib(8))
|
|
[ 0. 1. 1. 2. 3. 5. 8. 13.]
|