diff options
Diffstat (limited to 'doc/source/f2py/f2py.getting-started.rst')
-rw-r--r-- | doc/source/f2py/f2py.getting-started.rst | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/doc/source/f2py/f2py.getting-started.rst b/doc/source/f2py/f2py.getting-started.rst new file mode 100644 index 000000000..27ddbb005 --- /dev/null +++ b/doc/source/f2py/f2py.getting-started.rst @@ -0,0 +1,272 @@ +====================================== + 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.] |