diff options
Diffstat (limited to 'numpy/f2py/doc/notes.tex')
-rw-r--r-- | numpy/f2py/doc/notes.tex | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/numpy/f2py/doc/notes.tex b/numpy/f2py/doc/notes.tex new file mode 100644 index 000000000..2746b049d --- /dev/null +++ b/numpy/f2py/doc/notes.tex @@ -0,0 +1,310 @@ + +\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/}. + + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: "f2py2e" +%%% End: |