|
|
|
\section{Calling wrapper functions from Python} |
|
\label{sec:notes} |
|
|
|
\subsection{Scalar arguments} |
|
\label{sec:scalars} |
|
|
|
In general, for scalar argument you can pass in in |
|
addition to ordinary Python scalars (like integers, floats, complex |
|
values) also arbitrary sequence objects (lists, arrays, strings) --- |
|
then the first element of a sequence is passed in to the Fortran routine. |
|
|
|
It is recommended that you always pass in scalars of required type. This |
|
ensures the correctness as no type-casting is needed. |
|
However, no exception is raised if type-casting would produce |
|
inaccurate or incorrect results! For example, in place of an expected |
|
complex value you can give an integer, or vice-versa (in the latter case only |
|
a rounded real part of the complex value will be used). |
|
|
|
If the argument is \texttt{intent(inout)} then Fortran routine can change the |
|
value ``in place'' only if you pass in a sequence object, for |
|
instance, rank-0 array. Also make sure that the type of an array is of |
|
correct type. Otherwise type-casting will be performed and you may |
|
get inaccurate or incorrect results. The following example illustrates this |
|
\begin{verbatim} |
|
>>> a = array(0) |
|
>>> calculate_pi(a) |
|
>>> print a |
|
3 |
|
\end{verbatim} |
|
|
|
If you pass in an ordinary Python scalar in place of |
|
\texttt{intent(inout)} variable, it will be used as an input argument |
|
since |
|
Python |
|
scalars cannot not be changed ``in place'' (all Python scalars |
|
are immutable objects). |
|
|
|
\subsection{String arguments} |
|
\label{sec:strings} |
|
|
|
You can pass in strings of arbitrary length. If the length is greater than |
|
required, only a required part of the string is used. If the length |
|
is smaller than required, additional memory is allocated and fulfilled |
|
with `\texttt{\bs0}'s. |
|
|
|
Because Python strings are immutable, \texttt{intent(inout)} argument |
|
expects an array version of a string --- an array of chars: |
|
\texttt{array("<string>")}. |
|
Otherwise, the change ``in place'' has no effect. |
|
|
|
|
|
\subsection{Array arguments} |
|
\label{sec:arrays} |
|
|
|
If the size of an array is relatively large, it is \emph{highly |
|
recommended} that you pass in arrays of required type. Otherwise, |
|
type-casting will be performed which includes the creation of new |
|
arrays and their copying. If the argument is also |
|
\texttt{intent(inout)}, the wasted time is doubled. So, pass in arrays |
|
of required type! |
|
|
|
On the other hand, there are situations where it is perfectly all |
|
right to ignore this recommendation: if the size of an array is |
|
relatively small or the actual time spent in Fortran routine takes |
|
much longer than copying an array. Anyway, if you want to optimize |
|
your Python code, start using arrays of required types. |
|
|
|
Another source of performance hit is when you use non-contiguous |
|
arrays. The performance hit will be exactly the same as when using |
|
incorrect array types. This is because a contiguous copy is created |
|
to be passed in to the Fortran routine. |
|
|
|
\fpy provides a feature such that the ranks of array arguments need |
|
not to match --- only the correct total size matters. For example, if |
|
the wrapper function expects a rank-1 array \texttt{array([...])}, |
|
then it is correct to pass in rank-2 (or higher) arrays |
|
\texttt{array([[...],...,[...]])} assuming that the sizes will match. |
|
This is especially useful when the arrays should contain only one |
|
element (size is 1). Then you can pass in arrays \texttt{array(0)}, |
|
\texttt{array([0])}, \texttt{array([[0]])}, etc and all cases are |
|
handled correctly. In this case it is correct to pass in a Python |
|
scalar in place of an array (but then ``change in place'' is ignored, |
|
of course). |
|
|
|
\subsubsection{Multidimensional arrays} |
|
|
|
If you are using rank-2 or higher rank arrays, you must always |
|
remember that indexing in Fortran starts from the lowest dimension |
|
while in Python (and in C) the indexing starts from the highest |
|
dimension (though some compilers have switches to change this). As a |
|
result, if you pass in a 2-dimensional array then the Fortran routine |
|
sees it as the transposed version of the array (in multi-dimensional |
|
case the indexes are reversed). |
|
|
|
You must take this matter into account also when modifying the |
|
signature file and interpreting the generated Python signatures: |
|
|
|
\begin{itemize} |
|
\item First, when initializing an array using \texttt{init\_expr}, the index |
|
vector \texttt{\_i[]} changes accordingly to Fortran convention. |
|
\item Second, the result of CPP-macro \texttt{shape(<array>,0)} |
|
corresponds to the last dimension of the Fortran array, etc. |
|
\end{itemize} |
|
Let me illustrate this with the following example:\\ |
|
\begin{verbatim} |
|
! Fortran file: arr.f |
|
subroutine arr(l,m,n,a) |
|
integer l,m,n |
|
real*8 a(l,m,n) |
|
... |
|
end |
|
\end{verbatim} |
|
\fpy will generate the following signature file:\\ |
|
\begin{verbatim} |
|
!%f90 |
|
! Signature file: arr.f90 |
|
python module arr ! in |
|
interface ! in :arr |
|
subroutine arr(l,m,n,a) ! in :arr:arr.f |
|
integer optional,check(shape(a,2)==l),depend(a) :: l=shape(a,2) |
|
integer optional,check(shape(a,1)==m),depend(a) :: m=shape(a,1) |
|
integer optional,check(shape(a,0)==n),depend(a) :: n=shape(a,0) |
|
real*8 dimension(l,m,n) :: a |
|
end subroutine arr |
|
end interface |
|
end python module arr |
|
\end{verbatim} |
|
and the following wrapper function will be produced |
|
\begin{verbatim} |
|
None = arr(a,l=shape(a,2),m=shape(a,1),n=shape(a,0)) |
|
\end{verbatim} |
|
|
|
In general, I would suggest not to specify the given optional |
|
variables \texttt{l,m,n} when calling the wrapper function --- let the |
|
interface find the values of the variables \texttt{l,m,n}. But there |
|
are occasions when you need to specify the dimensions in Python. |
|
|
|
So, in Python a proper way to create an array from the given |
|
dimensions is |
|
\begin{verbatim} |
|
>>> a = zeros(n,m,l,'d') |
|
\end{verbatim} |
|
(note that the dimensions are reversed and correct type is specified), |
|
and then a complete call to \texttt{arr} is |
|
\begin{verbatim} |
|
>>> arr(a,l,m,n) |
|
\end{verbatim} |
|
|
|
From the performance point of view, always be consistent with Fortran |
|
indexing convention, that is, use transposed arrays. But if you do the |
|
following |
|
\begin{verbatim} |
|
>>> a = transpose(zeros(l,m,n,'d')) |
|
>>> arr(a) |
|
\end{verbatim} |
|
then you will get a performance hit! The reason is that here the |
|
transposition is not actually performed. Instead, the array \texttt{a} |
|
will be non-contiguous which means that before calling a Fortran |
|
routine, internally a contiguous array is created which |
|
includes memory allocation and copying. In addition, if |
|
the argument array is also \texttt{intent(inout)}, the results are |
|
copied back to the initial array which doubles the |
|
performance hit! |
|
|
|
So, to improve the performance: always pass in |
|
arrays that are contiguous. |
|
|
|
\subsubsection{Work arrays} |
|
|
|
Often Fortran routines use the so-called work arrays. The |
|
corresponding arguments can be declared as optional arguments, but be |
|
sure that all dimensions are specified (bounded) and defined before |
|
the initialization (dependence relations). |
|
|
|
On the other hand, if you call the Fortran routine many times then you |
|
don't want to allocate/deallocate the memory of the work arrays on |
|
every call. In this case it is recommended that you create temporary |
|
arrays with proper sizes in Python and use them as work arrays. But be |
|
careful when specifying the required type and be sure that the |
|
temporary arrays are contiguous. Otherwise the performance hit would |
|
be even harder than the hit when not using the temporary arrays from |
|
Python! |
|
|
|
|
|
|
|
\subsection{Call-back arguments} |
|
\label{sec:cbargs} |
|
|
|
\fpy builds a very flexible call-back mechanisms for call-back |
|
arguments. If the wrapper function expects a call-back function \texttt{fun} |
|
with the following Python signature to be passed in |
|
\begin{verbatim} |
|
def fun(a_1,...,a_n): |
|
... |
|
return x_1,...,x_k |
|
\end{verbatim} |
|
but the user passes in a function \texttt{gun} with the signature |
|
\begin{verbatim} |
|
def gun(b_1,...,b_m): |
|
... |
|
return y_1,...,y_l |
|
\end{verbatim} |
|
and the following extra arguments (specified as additional optional |
|
argument for the wrapper function): |
|
\begin{verbatim} |
|
fun_extra_args = (e_1,...,e_p) |
|
\end{verbatim} |
|
then the actual call-back is constructed accordingly to the following rules: |
|
\begin{itemize} |
|
\item if \texttt{p==0} then \texttt{gun(a\_1,...,a\_q)}, where |
|
\texttt{q=min(m,n)}; |
|
\item if \texttt{n+p<=m} then \texttt{gun(a\_1,...,a\_n,e\_1,...,e\_p)}; |
|
\item if \texttt{p<=m<n+p} then \texttt{gun(a\_1,...,a\_q,e\_1,...,e\_p)}, |
|
where \texttt{q=m-p}; |
|
\item if \texttt{p>m} then \texttt{gun(e\_1,...,e\_m)}; |
|
\item if \texttt{n+p} is less than the number of required arguments |
|
of the function \texttt{gun}, an exception is raised. |
|
\end{itemize} |
|
|
|
A call-back function \texttt{gun} may return any number of objects as a tuple: |
|
if \texttt{k<l}, then objects \texttt{y\_k+1,...,y\_l} are ignored; |
|
if \texttt{k>l}, then only objects \texttt{x\_1,...,x\_l} are set. |
|
|
|
|
|
\subsection{Obtaining information on wrapper functions} |
|
\label{sec:info} |
|
|
|
From the previous sections we learned that it is useful for the |
|
performance to pass in arguments of expected type, if possible. To |
|
know what are the expected types, \fpy generates a complete |
|
documentation strings for all wrapper functions. You can read them |
|
from Python by printing out \texttt{\_\_doc\_\_} attributes of the |
|
wrapper functions. For the example in Sec.~\ref{sec:intro}: |
|
\begin{verbatim} |
|
>>> print foobar.foo.__doc__ |
|
Function signature: |
|
foo(a) |
|
Required arguments: |
|
a : in/output rank-0 array(int,'i') |
|
>>> print foobar.bar.__doc__ |
|
Function signature: |
|
bar = bar(a,b) |
|
Required arguments: |
|
a : input int |
|
b : input int |
|
Return objects: |
|
bar : int |
|
\end{verbatim} |
|
|
|
In addition, \fpy generates a LaTeX document |
|
(\texttt{<modulename>module.tex}) containing a bit more information on |
|
the wrapper functions. See for example Appendix that contains a result |
|
of the documentation generation for the example module |
|
\texttt{foobar}. Here the file \texttt{foobar-smart.f90} (modified |
|
version of \texttt{foobar.f90}) is used --- it contains |
|
\texttt{note(<LaTeX text>)} attributes for specifying some additional |
|
information. |
|
|
|
\subsection{Wrappers for common blocks} |
|
\label{sec:wrapcomblock} |
|
|
|
[See examples \texttt{test-site/e/runme*}] |
|
|
|
What follows is obsolute for \fpy version higher that 2.264. |
|
|
|
\fpy generates wrapper functions for common blocks. For every common |
|
block with a name \texttt{<commonname>} a function |
|
\texttt{get\_<commonname>()} is constructed that takes no arguments |
|
and returns a dictionary. The dictionary represents maps between the |
|
names of common block fields and the arrays containing the common |
|
block fields (multi-dimensional arrays are transposed). So, in order |
|
to access to the common block fields, you must first obtain the |
|
references |
|
\begin{verbatim} |
|
commonblock = get_<commonname>() |
|
\end{verbatim} |
|
and then the fields are available through the arrays |
|
\texttt{commonblock["<fieldname>"]}. |
|
To change the values of common block fields, you can use for scalars |
|
\begin{verbatim} |
|
commonblock["<fieldname>"][0] = <new value> |
|
\end{verbatim} |
|
and for arrays |
|
\begin{verbatim} |
|
commonblock["<fieldname>"][:] = <new array> |
|
\end{verbatim} |
|
for example. |
|
|
|
For more information on the particular common block wrapping, see |
|
\texttt{get\_<commonname>.\_\_doc\_\_}. |
|
|
|
\subsection{Wrappers for F90/95 module data and routines} |
|
\label{sec:wrapf90modules} |
|
|
|
[See example \texttt{test-site/mod/runme\_mod}] |
|
|
|
\subsection{Examples} |
|
\label{sec:examples} |
|
|
|
Examples on various aspects of wrapping Fortran routines to Python can |
|
be found in directories \texttt{test-site/d/} and |
|
\texttt{test-site/e/}: study the shell scripts \texttt{runme\_*}. See |
|
also files in \texttt{doc/ex1/}. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|