| \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: |