|
From [email protected] Thu Dec 30 09:58:01 1999 |
|
Date: Fri, 26 Nov 1999 12:02:42 +0200 (EET) |
|
From: Pearu Peterson <[email protected]> |
|
To: Users of f2py2e -- Curtis Jensen <[email protected]>, |
|
Vladimir Janku <[email protected]>, |
|
Travis Oliphant <[email protected]> |
|
Subject: Multidimensional arrays in f2py2e |
|
|
|
|
|
Hi! |
|
|
|
Below I will describe how f2py2e wraps Fortran multidimensional arrays as |
|
it constantly causes confusion. As for example, consider Fortran code |
|
|
|
subroutine foo(l,m,n,a) |
|
integer l,m,n |
|
real*8 a(l,m,n) |
|
.. |
|
end |
|
Running f2py2e with -h flag, it generates the following signature |
|
|
|
subroutine foo(l,m,n,a) |
|
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),check(rank(a)==3) :: a |
|
end subroutine foo |
|
|
|
where parameters l,m,n are considered optional and they are initialized in |
|
Python C/API code using the array a. Note that a can be also a proper |
|
list, that is, asarray(a) should result in a rank-3 array. But then there |
|
is an automatic restriction that elements of a (in Python) are not |
|
changeable (in place) even if Fortran subroutine changes the array a (in |
|
C,Fortran). |
|
|
|
Hint: you can attribute the array a with 'intent(out)' which causes foo to |
|
return the array a (in Python) if you are to lazy to define a=asarray(a) |
|
before the call to foo (in Python). |
|
|
|
Calling f2py2e without the switch -h, a Python C/API module will be |
|
generated. After compiling it and importing it to Python |
|
>>> print foo.__doc__ |
|
shows |
|
None = foo(a,l=shape(a,2),m=shape(a,1),n=shape(a,0)) |
|
|
|
You will notice that f2py2e has changed the order of arguments putting the |
|
optional ones at the end of the argument list. |
|
Now, you have to be careful when specifying the parameters l,m,n (though |
|
situations where you need this should be rare). A proper definition |
|
of the array a should be, say |
|
|
|
a = zeros(n,m,l) |
|
|
|
Note that the dimensions l,m,n are in reverse, that is, the array a should |
|
be transposed when feeding it to the wrapper. |
|
|
|
Hint (and a performance hit): To be always consistent with fortran |
|
arrays, you can define, for example |
|
a = zeros(l,m,n) |
|
and call from Python |
|
foo(transpose(a),l,m,n) |
|
which is equivalent with the given Fortran call |
|
call foo(l,m,n,a) |
|
|
|
Another hint (not recommended, though): If you don't like optional |
|
arguments feature at all and want to be strictly consistent with Fortran |
|
signature, that is, you want to call foo from Python as |
|
foo(l,m,n,a) |
|
then you should edit the signature to |
|
subroutine foo(l,m,n,a) |
|
integer :: l |
|
integer :: m |
|
integer :: n |
|
real*8 dimension(l,m,n),check(rank(a)==3),depend(l,m,n), & |
|
check(shape(a,2)==l,shape(a,1)==m,shape(a,0)==n):: a |
|
end |
|
Important! Note that now the array a should depend on l,m,n |
|
so that the checks can be performed in the proper order. |
|
(you cannot check, say, shape(a,2)==l before initializing a or l) |
|
(There are other ways to edit the signature in order to get the same |
|
effect but they are not so safe and I will not discuss about them here). |
|
|
|
Hint: If the array a should be a work array (as used frequently in |
|
Fortran) and you a too lazy (its good lazyness;) to provide it (in Python) |
|
then you can define it as optional by ediding the signature: |
|
subroutine foo(l,m,n,a) |
|
integer :: l |
|
integer :: m |
|
integer :: n |
|
real*8 dimension(l,m,n),check(rank(a)==3),depend(l,m,n), & |
|
check(shape(a,2)==l,shape(a,1)==m,shape(a,0)==n):: a |
|
optional a |
|
end |
|
Note again that the array a must depend on l,m,n. Then the array a will be |
|
allocated in the Python C/API module. Not also that |
|
>>> print foo.__doc__ |
|
shows then |
|
None = foo(l,m,n,a=) |
|
Performance hint: If you call the given foo lots of times from Python then |
|
you don't want to allocate/deallocate the memory in each call. So, it is |
|
then recommended to define a temporary array in Python, for instance |
|
>>> tmp = zeros(n,m,l) |
|
>>> for i in ...: |
|
>>> foo(l,m,n,a=tmp) |
|
|
|
Important! It is not good at all to define |
|
>>> tmp = transpose(zeros(l,m,n)) |
|
because tmp will be then a noncontiguous array and there will be a |
|
huge performance hit as in Python C/API a new array will be allocated and |
|
also a copying of arrays will be performed elementwise! |
|
But |
|
>>> tmp = asarray(transpose(zeros(l,m,n))) |
|
is still ok. |
|
|
|
I hope that the above answers lots of your (possible) questions about |
|
wrapping Fortran multidimensional arrays with f2py2e. |
|
|
|
Regards, |
|
Pearu |
|
|