I have written a small Fortran function and pass arguments to it in Python using f2py. Somehow the order of arguments is messed up during the transfer and I can't figure out why.
Relevant parts of the Fortran function (which is in a file called calc_density.f95):
subroutine calc_density(position, nparticles, ncells, L, density)
implicit none
integer, intent(in) :: nparticles
integer, intent(in) :: ncells
double precision, intent(in) :: L
double precision, dimension(nparticles), intent(in) :: position
double precision, dimension(ncells), intent(out) :: density
double precision :: sumBuf, offSum
integer :: pLower, pUpper, pBuf, numBuf, last, idx
double precision, dimension(nparticles) :: sorted
print *, 'Fortran ', 'position length ', size(position), &
'density length ', size(density), 'nparticles ', nparticles, &
'ncells ', ncells, 'L ', L
end subroutine calc_density
f2py compile command:
f2py -c --fcompiler=gnu95 -m fortran_calc_density calc_density.f95
Relevant parts of the Python code:
from fortran_calc_density import calc_density as densityCalc
from numpy import array, float64
def calc_density(position, ncells, L):
arg = array(position, dtype = float64, order = 'F')
nparticles = len(position)
density = densityCalc(position, nparticles, ncells, L)
print 'Python ', 'position length ', len(position), 'density length', len(density), 'nparticles ', nparticles, 'ncells ', ncells, 'L ', L
return density
Sample of screen output showing the mismatch in all transferred variables:
Fortran position length 12 density length 100 nparticles 12 ncells 100 L 20.000000000000000
Python position length 100 density length 100 nparticles 100 ncells 20 L 12.5663706144
The print out from Python shows the values, except for the length of the density array which should be equal to ncells and therefore 20 by the design of the Fortran function , exactly as they should be. The Fortran values however are totally off so something must have happened during the transfer which scrambled the arguments around.
What am I doing wrong here?
Looking at the doc created by f2py (compiled with gfortran-5.3.0):
>>> print calc_density.__doc__
Wrapper for ``calc_density``.
Parameters
----------
position : input rank-1 array('d') with bounds (nparticles)
ncells : input int
l : input float
Other Parameters
----------------
nparticles : input int, optional
Default: len(position)
Returns
-------
density : rank-1 array('d') with bounds (cells)
you can see that nparticles
is optional (this is automatically done by f2py) and that the default value is len(position)
. By default, optional arguments are moved to the end of the argument list. Consequently, within your call, the last argument is interpreted as nparticles
.
You can either leave nparticles
out of the function call or move it to the last argument. Both:
density = densityCalc(position, ncells, L)
density = densityCalc(position, ncells, L, nparticles)
should result in the right results. If you want to keep the order of the fortran subroutine argument list, you also can use keywords:
density = densityCalc(position=position, nparticles=nparticles, ncells=ncells, l=L)
Note that fortran is not case sensitive so the keyword has to be lowercase l = L
.