I have some old fortran code which I want to wrap with f2py and which I am not allowed to change.
The core of the program is some subroutine that depends on user defined subroutines. All of these subroutines share common parameter arrays for exchange of user supplied data between the routines. These parameter arrays are one-dimensional and as such don't expect any predefined input size in the main subroutine headers.
However, it seems, like the python callback function headers need precise information about the actual size of the arrays.
Let's look at an example:
This is my fortran code:
Subroutine myscript(x,fun,o,n,rpar, ipar)
external fun
integer n
double precision x(n,*)
double precision o(n)
double precision rpar(*)
integer ipar(*)
write(*,*) n
!write(*,*) x(1,:)
call fun(n,x,o,rpar,ipar)
write(*,*) o
write(*,*) rpar(1)
end
And this is my python portion:
import numpy as np
import routine
import ipdb
def fun(X, rpar):
print("In Callback function")
ipdb.set_trace()
print(X)
O = X[1,:]
print(O)
return O
O = np.array([1.,0.])
X = np.identity(2)
rpar = np.array([1.,1.])
routine.myscript(X, fun, rpar)
I compiled with f2py -c routine.pyf routine.f
and the following header file
! -*- f90 -*-
! Note: the context of this file is case sensitive.
python module myscript__user__routines
interface myscript_user_interface
subroutine fun(n,x,o,rpar,ipar) ! in :routine:routine.f:myscript:unknown_interface
integer, optional,intent(hide),check(shape(x,0)==n),depend(x) :: n=shape(x,0)
double precision dimension(n,n),intent(in) :: x
double precision dimension(n),intent(out),depend(n) :: o
double precision dimension(100),intent(in) :: rpar
integer, optional, dimension(*) :: ipar
end subroutine fun
end interface myscript_user_interface
end python module myscript__user__routines
python module routine ! in
interface ! in :routine
subroutine myscript(x,fun,o,n,rpar,ipar) ! in :routine:routine.f
use myscript__user__routines
double precision dimension(n,*),intent(in,copy) :: x
external fun
double precision dimension(n),intent(out),depend(n) :: o
integer, optional,intent(hide),check(shape(x,0)==n),depend(x) :: n=shape(x,0)
double precision dimension(shape(rpar,0)) :: rpar
integer, optional, dimension(2) :: ipar
end subroutine myscript
end interface
end python module routine
! This file was auto-generated with f2py (version:2).
! See http://cens.ioc.ee/projects/f2py2e/
The important line in the header file is
double precision dimension(100),intent(in) :: rpar
when in the python debugger, rpar
now is a 100-dimensional array, where the last 98 values are garbage (not surprising).
changing dimension to *
just returns an empty array
ipdb> rpar
array([], dtype=float64)
changing dimensions to shape(rpar,0)
as in the main routine produces the error
ValueError: negative dimensions are not allowed
Is there an elegant way of letting the python user decide the dimension of the parameter array rpar
dynamically?
The expected value in the example is 2
, because its the dimension of the input array.
I guess I could change the interface and add another integer variable declaring the size of this array. However, this means changing all calls to this subroutine at every occurrence in the code; something I want to avoid