|
\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{[email protected]} |
|
} |
|
|
|
\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; /* NPY_<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},NPY_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},NPY_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},NPY_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} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|