diff options
Diffstat (limited to 'numpy/f2py/doc/fortranobject.tex')
-rw-r--r-- | numpy/f2py/doc/fortranobject.tex | 574 |
1 files changed, 574 insertions, 0 deletions
diff --git a/numpy/f2py/doc/fortranobject.tex b/numpy/f2py/doc/fortranobject.tex new file mode 100644 index 000000000..dbb244cdd --- /dev/null +++ b/numpy/f2py/doc/fortranobject.tex @@ -0,0 +1,574 @@ +\documentclass{article} + +\headsep=0pt +\topmargin=0pt +\headheight=0pt +\oddsidemargin=0pt +\textwidth=6.5in +\textheight=9in + +\usepackage{xspace} +\usepackage{verbatim} +\newcommand{\fpy}{\texttt{f2py}\xspace} +\newcommand{\bs}{\symbol{`\\}} +\newcommand{\email}[1]{\special{html:<A href="mailto:#1">}\texttt{<#1>}\special{html:</A>}} +\title{\texttt{PyFortranObject} --- example usages} +\author{ +\large Pearu Peterson\\ +\small \email{pearu@cens.ioc.ee} +} + +\begin{document} + +\maketitle + +\special{html: Other formats of this document: +<A href=pyfobj.ps.gz>Gzipped PS</A>, +<A href=pyfobj.pdf>PDF</A> +} + +\tableofcontents + +\section{Introduction} +\label{sec:intro} + +Fortran language defines the following concepts that we would like to +access from Python: functions, subroutines, data in \texttt{COMMON} blocks, +F90 module functions and subroutines, F90 module data (both static and +allocatable arrays). + +In the following we shall assume that we know the signatures (full +specifications of routine arguments and variables) of these concepts +from their Fortran source codes. Now, in order to call or use them +from C, one needs to have pointers to the corresponding objects. The +pointers to Fortran 77 objects (routines, data in \texttt{COMMON} +blocks) are readily available to C codes (there are various sources +available about mixing Fortran 77 and C codes). On the other hand, F90 +module specifications are highly compiler dependent and sometimes it +is not even possible to access F90 module objects from C (at least, +not directly, see remark about MIPSPro 7 Compilers). But using some +tricks (described below), the pointers to F90 module objects can be +determined in runtime providing a compiler independent solution. + +To use Fortran objects from Python in unified manner, \fpy introduces +\texttt{PyFortranObject} to hold pointers of the Fortran objects and +the corresponing wrapper functions. In fact, \texttt{PyFortranObject} +does much more: it generates documentation strings in run-time (for +items in \texttt{COMMON} blocks and data in F90 modules), provides +methods for accessing Fortran data and for calling Fortran routines, +etc. + +\section{\texttt{PyFortranObject}} +\label{sec:pyfortobj} + +\texttt{PyFortranObject} is defined as follows +\begin{verbatim} +typedef struct { + PyObject_HEAD + int len; /* Number of attributes */ + FortranDataDef *defs; /* An array of FortranDataDef's */ + PyObject *dict; /* Fortran object attribute dictionary */ +} PyFortranObject; +\end{verbatim} +where \texttt{FortranDataDef} is +\begin{verbatim} +typedef struct { + char *name; /* attribute (array||routine) name */ + int rank; /* array rank, 0 for scalar, max is F2PY_MAX_DIMS, + || rank=-1 for Fortran routine */ + struct {int d[F2PY_MAX_DIMS];} dims; /* dimensions of the array, || not used */ + int type; /* PyArray_<type> || not used */ + char *data; /* pointer to array || Fortran routine */ + void (*func)(); /* initialization function for + allocatable arrays: + func(&rank,dims,set_ptr_func,name,len(name)) + || C/API wrapper for Fortran routine */ + char *doc; /* documentation string; only recommended + for routines. */ +} FortranDataDef; +\end{verbatim} +In the following we demonstrate typical usages of +\texttt{PyFortranObject}. Just relevant code fragments will be given. + + +\section{Fortran 77 subroutine} +\label{sec:f77subrout} + +Consider Fortran 77 subroutine +\begin{verbatim} +subroutine bar() +end +\end{verbatim} +The corresponding \texttt{PyFortranObject} is defined in C as follows: +\begin{verbatim} +static char doc_bar[] = "bar()"; +static PyObject *c_bar(PyObject *self, PyObject *args, + PyObject *keywds, void (*f2py_func)()) { + static char *capi_kwlist[] = {NULL}; + if (!PyArg_ParseTupleAndKeywords(args,keywds,"|:bar",capi_kwlist)) + return NULL; + (*f2py_func)(); + return Py_BuildValue(""); +} +extern void F_FUNC(bar,BAR)(); +static FortranDataDef f2py_routines_def[] = { + {"bar",-1, {-1}, 0, (char *)F_FUNC(bar,BAR),(void*)c_bar,doc_bar}, + {NULL} +}; +void initfoo() { + <snip> + d = PyModule_GetDict(m); + PyDict_SetItemString(d, f2py_routines_def[0].name, + PyFortranObject_NewAsAttr(&f2py_routines_def[0])); +} +\end{verbatim} +where CPP macro \texttt{F\_FUNC} defines how Fortran 77 routines are +seen in C. +In Python, Fortran subroutine \texttt{bar} is called as follows +\begin{verbatim} +>>> import foo +>>> foo.bar() +\end{verbatim} + +\section{Fortran 77 function} +\label{sec:f77func} +Consider Fortran 77 function +\begin{verbatim} +function bar() +complex bar +end +\end{verbatim} +The corresponding \texttt{PyFortranObject} is defined in C as in +previous example but with the following changes: +\begin{verbatim} +static char doc_bar[] = "bar = bar()"; +static PyObject *c_bar(PyObject *self, PyObject *args, + PyObject *keywds, void (*f2py_func)()) { + complex_float bar; + static char *capi_kwlist[] = {NULL}; + if (!PyArg_ParseTupleAndKeywords(args,keywds,"|:bar",capi_kwlist)) + return NULL; + (*f2py_func)(&bar); + return Py_BuildValue("O",pyobj_from_complex_float1(bar)); +} +extern void F_WRAPPEDFUNC(bar,BAR)(); +static FortranDataDef f2py_routines_def[] = { + {"bar",-1,{-1},0,(char *)F_WRAPPEDFUNC(bar,BAR),(void *)c_bar,doc_bar}, + {NULL} +}; +\end{verbatim} +where CPP macro \texttt{F\_WRAPPEDFUNC} gives the pointer to the following +Fortran 77 subroutine: +\begin{verbatim} +subroutine f2pywrapbar (barf2pywrap) +external bar +complex bar, barf2pywrap +barf2pywrap = bar() +end +\end{verbatim} +With these hooks, calling Fortran functions returning composed types +becomes platform/compiler independent. + + +\section{\texttt{COMMON} block data} +\label{sec:commondata} + +Consider Fortran 77 \texttt{COMMON} block +\begin{verbatim} +integer i +COMMON /bar/ i +\end{verbatim} +In order to access the variable \texttt{i} from Python, +\texttt{PyFortranObject} is defined as follows: +\begin{verbatim} +static FortranDataDef f2py_bar_def[] = { + {"i",0,{-1},PyArray_INT}, + {NULL} +}; +static void f2py_setup_bar(char *i) { + f2py_bar_def[0].data = i; +} +extern void F_FUNC(f2pyinitbar,F2PYINITBAR)(); +static void f2py_init_bar() { + F_FUNC(f2pyinitbar,F2PYINITBAR)(f2py_setup_bar); +} +void initfoo() { + <snip> + PyDict_SetItemString(d, "bar", PyFortranObject_New(f2py_bar_def,f2py_init_bar)); +} +\end{verbatim} +where auxiliary Fortran function \texttt{f2pyinitbar} is defined as follows +\begin{verbatim} +subroutine f2pyinitbar(setupfunc) +external setupfunc +integer i +common /bar/ i +call setupfunc(i) +end +\end{verbatim} +and it is called in \texttt{PyFortranObject\_New}. + + +\section{Fortran 90 module subroutine} +\label{sec:f90modsubrout} + +Consider +\begin{verbatim} +module fun + subroutine bar() + end subroutine bar +end module fun +\end{verbatim} +\texttt{PyFortranObject} is defined as follows +\begin{verbatim} +static char doc_fun_bar[] = "fun.bar()"; +static PyObject *c_fun_bar(PyObject *self, PyObject *args, + PyObject *keywds, void (*f2py_func)()) { + static char *kwlist[] = {NULL}; + if (!PyArg_ParseTupleAndKeywords(args,keywds,"",kwlist)) + return NULL; + (*f2py_func)(); + return Py_BuildValue(""); +} +static FortranDataDef f2py_fun_def[] = { + {"bar",-1,{-1},0,NULL,(void *)c_fun_bar,doc_fun_bar}, + {NULL} +}; +static void f2py_setup_fun(char *bar) { + f2py_fun_def[0].data = bar; +} +extern void F_FUNC(f2pyinitfun,F2PYINITFUN)(); +static void f2py_init_fun() { + F_FUNC(f2pyinitfun,F2PYINITFUN)(f2py_setup_fun); +} +void initfoo () { + <snip> + PyDict_SetItemString(d, "fun", PyFortranObject_New(f2py_fun_def,f2py_init_fun)); +} +\end{verbatim} +where auxiliary Fortran function \texttt{f2pyinitfun} is defined as +follows +\begin{verbatim} +subroutine f2pyinitfun(f2pysetupfunc) +use fun +external f2pysetupfunc +call f2pysetupfunc(bar) +end subroutine f2pyinitfun +\end{verbatim} +The following Python session demonstrates how to call Fortran 90 +module function \texttt{bar}: +\begin{verbatim} +>>> import foo +>>> foo.fun.bar() +\end{verbatim} + +\section{Fortran 90 module function} +\label{sec:f90modfunc} + +Consider +\begin{verbatim} +module fun + function bar() + complex bar + end subroutine bar +end module fun +\end{verbatim} +\texttt{PyFortranObject} is defined as follows +\begin{verbatim} +static char doc_fun_bar[] = "bar = fun.bar()"; +static PyObject *c_fun_bar(PyObject *self, PyObject *args, + PyObject *keywds, void (*f2py_func)()) { + complex_float bar; + static char *kwlist[] = {NULL}; + if (!PyArg_ParseTupleAndKeywords(args,keywds,"",kwlist)) + return NULL; + (*f2py_func)(&bar); + return Py_BuildValue("O",pyobj_from_complex_float1(bar)); +} +static FortranDataDef f2py_fun_def[] = { + {"bar",-1,{-1},0,NULL,(void *)c_fun_bar,doc_fun_bar}, + {NULL} +}; +static void f2py_setup_fun(char *bar) { + f2py_fun_def[0].data = bar; +} +extern void F_FUNC(f2pyinitfun,F2PYINITFUN)(); +static void f2py_init_fun() { + F_FUNC(f2pyinitfun,F2PYINITFUN)(f2py_setup_fun); +} +void initfoo() { + <snip> + PyDict_SetItemString(d, "fun", PyFortranObject_New(f2py_fun_def,f2py_init_fun)); +} +\end{verbatim} +where +\begin{verbatim} +subroutine f2pywrap_fun_bar (barf2pywrap) +use fun +complex barf2pywrap +barf2pywrap = bar() +end + +subroutine f2pyinitfun(f2pysetupfunc) +external f2pysetupfunc,f2pywrap_fun_bar +call f2pysetupfunc(f2pywrap_fun_bar) +end +\end{verbatim} + + +\section{Fortran 90 module data} +\label{sec:f90moddata} + +Consider +\begin{verbatim} +module fun + integer i +end module fun +\end{verbatim} +Then +\begin{verbatim} +static FortranDataDef f2py_fun_def[] = { + {"i",0,{-1},PyArray_INT}, + {NULL} +}; +static void f2py_setup_fun(char *i) { + f2py_fun_def[0].data = i; +} +extern void F_FUNC(f2pyinitfun,F2PYINITFUN)(); +static void f2py_init_fun() { + F_FUNC(f2pyinitfun,F2PYINITFUN)(f2py_setup_fun); +} +void initfoo () { + <snip> + PyDict_SetItemString(d, "fun", + PyFortranObject_New(f2py_fun_def,f2py_init_fun)); +} +\end{verbatim} +where +\begin{verbatim} +subroutine f2pyinitfun(f2pysetupfunc) +use fun +external f2pysetupfunc +call f2pysetupfunc(i) +end subroutine f2pyinitfun +\end{verbatim} +Example usage in Python: +\begin{verbatim} +>>> import foo +>>> foo.fun.i = 4 +\end{verbatim} + +\section{Fortran 90 module allocatable array} +\label{sec:f90modallocarr} + +Consider +\begin{verbatim} +module fun + real, allocatable :: r(:) +end module fun +\end{verbatim} +Then +\begin{verbatim} +static FortranDataDef f2py_fun_def[] = { + {"r",1,{-1},PyArray_FLOAT}, + {NULL} +}; +static void f2py_setup_fun(void (*r)()) { + f2py_fun_def[0].func = r; +} +extern void F_FUNC(f2pyinitfun,F2PYINITFUN)(); +static void f2py_init_fun() { + F_FUNC(f2pyinitfun,F2PYINITFUN)(f2py_setup_fun); +} +void initfoo () { + <snip> + PyDict_SetItemString(d, "fun", PyFortranObject_New(f2py_fun_def,f2py_init_fun)); +} +\end{verbatim} +where +\begin{verbatim} +subroutine f2py_fun_getdims_r(r,s,f2pysetdata) +use fun, only: d => r +external f2pysetdata +logical ns +integer s(*),r,i,j +ns = .FALSE. +if (allocated(d)) then + do i=1,r + if ((size(d,r-i+1).ne.s(i)).and.(s(i).ge.0)) then + ns = .TRUE. + end if + end do + if (ns) then + deallocate(d) + end if +end if +if ((.not.allocated(d)).and.(s(1).ge.1)) then + allocate(d(s(1))) +end if +if (allocated(d)) then + do i=1,r + s(i) = size(d,r-i+1) + end do +end if +call f2pysetdata(d,allocated(d)) +end subroutine f2py_fun_getdims_r + +subroutine f2pyinitfun(f2pysetupfunc) +use fun +external f2pysetupfunc,f2py_fun_getdims_r +call f2pysetupfunc(f2py_fun_getdims_r) +end subroutine f2pyinitfun +\end{verbatim} +Usage in Python: +\begin{verbatim} +>>> import foo +>>> foo.fun.r = [1,2,3,4] +\end{verbatim} + +\section{Callback subroutine} +\label{sec:cbsubr} + +Thanks to Travis Oliphant for working out the basic idea of the +following callback mechanism. + +Consider +\begin{verbatim} +subroutine fun(bar) +external bar +call bar(1) +end +\end{verbatim} +Then +\begin{verbatim} +static char doc_foo8_fun[] = " +Function signature: + fun(bar,[bar_extra_args]) +Required arguments: + bar : call-back function +Optional arguments: + bar_extra_args := () input tuple +Call-back functions: + def bar(e_1_e): return + Required arguments: + e_1_e : input int"; +static PyObject *foo8_fun(PyObject *capi_self, PyObject *capi_args, + PyObject *capi_keywds, void (*f2py_func)()) { + PyObject *capi_buildvalue = NULL; + PyObject *bar_capi = Py_None; + PyTupleObject *bar_xa_capi = NULL; + PyTupleObject *bar_args_capi = NULL; + jmp_buf bar_jmpbuf; + int bar_jmpbuf_flag = 0; + int bar_nofargs_capi = 0; + static char *capi_kwlist[] = {"bar","bar_extra_args",NULL}; + + if (!PyArg_ParseTupleAndKeywords(capi_args,capi_keywds,\ + "O!|O!:foo8.fun",\ + capi_kwlist,&PyFunction_Type,&bar_capi,&PyTuple_Type,&bar_xa_capi)) + goto capi_fail; + + bar_nofargs_capi = cb_bar_in_fun__user__routines_nofargs; + if (create_cb_arglist(bar_capi,bar_xa_capi,1,0, + &cb_bar_in_fun__user__routines_nofargs,&bar_args_capi)) { + if ((PyErr_Occurred())==NULL) + PyErr_SetString(foo8_error,"failed in processing argument list for call-back bar." ); + goto capi_fail; + } + + SWAP(bar_capi,cb_bar_in_fun__user__routines_capi,PyObject); + SWAP(bar_args_capi,cb_bar_in_fun__user__routines_args_capi,PyTupleObject); + memcpy(&bar_jmpbuf,&cb_bar_in_fun__user__routines_jmpbuf,sizeof(jmp_buf)); + bar_jmpbuf_flag = 1; + + if ((setjmp(cb_bar_in_fun__user__routines_jmpbuf))) { + if ((PyErr_Occurred())==NULL) + PyErr_SetString(foo8_error,"Failure of a callback function"); + goto capi_fail; + } else + (*f2py_func)(cb_bar_in_fun__user__routines); + + capi_buildvalue = Py_BuildValue(""); +capi_fail: + + if (bar_jmpbuf_flag) { + cb_bar_in_fun__user__routines_capi = bar_capi; + Py_DECREF(cb_bar_in_fun__user__routines_args_capi); + cb_bar_in_fun__user__routines_args_capi = bar_args_capi; + cb_bar_in_fun__user__routines_nofargs = bar_nofargs_capi; + memcpy(&cb_bar_in_fun__user__routines_jmpbuf,&bar_jmpbuf,sizeof(jmp_buf)); + bar_jmpbuf_flag = 0; + } + return capi_buildvalue; +} +extern void F_FUNC(fun,FUN)(); +static FortranDataDef f2py_routine_defs[] = { + {"fun",-1,{-1},0,(char *)F_FUNC(fun,FUN),(void *)foo8_fun,doc_foo8_fun}, + {NULL} +}; +void initfoo8 () { + <snip> + PyDict_SetItemString(d, f2py_routine_defs[0].name, + PyFortranObject_NewAsAttr(&f2py_routine_defs[0])); +} +\end{verbatim} +where +\begin{verbatim} +PyObject *cb_bar_in_fun__user__routines_capi = Py_None; +PyTupleObject *cb_bar_in_fun__user__routines_args_capi = NULL; +int cb_bar_in_fun__user__routines_nofargs = 0; +jmp_buf cb_bar_in_fun__user__routines_jmpbuf; +static void cb_bar_in_fun__user__routines (int *e_1_e_cb_capi) { + PyTupleObject *capi_arglist = cb_bar_in_fun__user__routines_args_capi; + PyObject *capi_return = NULL; + PyObject *capi_tmp = NULL; + int capi_j,capi_i = 0; + + int e_1_e=(*e_1_e_cb_capi); + if (capi_arglist == NULL) + goto capi_fail; + if (cb_bar_in_fun__user__routines_nofargs>capi_i) + if (PyTuple_SetItem((PyObject *)capi_arglist,capi_i++,pyobj_from_int1(e_1_e))) + goto capi_fail; + + capi_return = PyEval_CallObject(cb_bar_in_fun__user__routines_capi, + (PyObject *)capi_arglist); + + if (capi_return == NULL) + goto capi_fail; + if (capi_return == Py_None) { + Py_DECREF(capi_return); + capi_return = Py_BuildValue("()"); + } + else if (!PyTuple_Check(capi_return)) { + capi_tmp = capi_return; + capi_return = Py_BuildValue("(O)",capi_tmp); + Py_DECREF(capi_tmp); + } + capi_j = PyTuple_Size(capi_return); + capi_i = 0; + goto capi_return_pt; +capi_fail: + fprintf(stderr,"Call-back cb_bar_in_fun__user__routines failed.\n"); + Py_XDECREF(capi_return); + longjmp(cb_bar_in_fun__user__routines_jmpbuf,-1); +capi_return_pt: + ; +} +\end{verbatim} +Usage in Python: +\begin{verbatim} +>>> import foo8 as foo +>>> def bar(i): print 'In bar i=',i +... +>>> foo.fun(bar) +In bar i= 1 +\end{verbatim} + +\end{document} + + +%%% Local Variables: +%%% mode: latex +%%% TeX-master: t +%%% End: |