I came across a rather peculiar behavior when was passing arrays to LSODA
procedure of ODEPACK
library.
The scene: Intel Fortran compiler, one of the recent ones, OS Windows. I have a project that uses features of Fortran 2003, but dirty computational work is passed to LSODA
, which is Fortran 77.
LSODA
is declared as
SUBROUTINE DLSODA (F, NEQ, Y, T, TOUT, ITOL, RTOL, ATOL, ITASK,
1 ISTATE, IOPT, RWORK, LRW, IWORK, LIW, JAC, JT)
EXTERNAL F, JAC
INTEGER NEQ, ITOL, ITASK, ISTATE, IOPT, LRW, IWORK, LIW, JT
DOUBLE PRECISION Y, T, TOUT, RTOL, ATOL, RWORK
DIMENSION NEQ(*), Y(*), RTOL(*), ATOL(*), RWORK(LRW), IWORK(LIW)
note the arrays. To call all that I do:
CALL DLSODA(FDARCY, NEQ, SATCCUR, TCUR, TNEXT, ITOL, RTOL, ATOL, ITASK, &
ISTATE, IOPT, RWORK, LRW, IWORK, LIW, JDUM, JT)
Let's focus on SATCCUR
(in Y
position of LSODA
). It is declared:
REAL(8), DIMENSION(:), ALLOCATABLE :: SATCCUR
Just in case, in the debugger I checked that DOUBLE PRECISION
is REAL(8)
. At some point SATCCUR
is also allocated. The problem is, that LSODA
with this call sees complete garbage. The pointer Y
sometimes points to something that I cannot either read or write, leading to the program crash. The only way to fix the problem was to change the call to:
CALL DLSODA(FDARCY, NEQ, SATCCUR(1), TCUR, TNEXT, ITOL, RTOL, ATOL, ITASK, &
ISTATE, IOPT, RWORK(1), LRW, IWORK(1), LIW, JDUM, JT)
Notice that I am not passing the array, but its first element --- the pass by reference of Fortran helps here, as LSODA
would effectively get the address of the first element. And I had to do it with all arrays in the call.
If I use the example that is supplied by the LSODA
's documentation, everything works fine with passing whole arrays. The differences that I could spot:
- The arrays in
LSODA
's example are statically allocated, whereas I do dynamic allocation. - In place of function
F
I pass the function internal to the subroutine. It makes my life much easier, as it captures parameters necessary to run it. This, I believe, is Fortran 2003 feature. But I can't see how this can create the problem as the problem occurs much earlier thanF
call.
So, the question is why does this happen? Is it an expected behavior?
PS. Also, for LSODA
to compile I had to switch off the function interface check as it's internally passing DOUBLE PRECISION
array where INTEGER
array is expected --- shameless hack on the authors side!
UPDATE
I've managed to trace down the error. The problem was that in my interface declaration for DLSODA
I have put
INTERFACE
SUBROUTINE DLSODA(F, NEQ, Y, T, TOUT, ITOL, RTOL, ATOL, ITASK, &
ISTATE, IOPT, RWORK, LRW, IWORK, LIW, JAC, JT)
USE GENERAL
EXTERNAL :: F, JAC
INTEGER :: NEQ, ITOL, ITASK, ISTATE, IOPT, LRW, LIW, JT, IWORK
REAL(DP) :: T, TOUT, RTOL, ATOL, RWORK
DIMENSION RWORK(LRW), IWORK(LIW)
REAL(DP), DIMENSION(:) :: Y
END SUBROUTINE DLSODA
END INTERFACE
Note the declaration for Y
and how it is different from RWORK
. Apparently, there is a difference between
REAL(8), DIMENSION(:) :: X
and
REAL(8) X
DIMENSION X(*)
I thought this was just a matter of style. Can someone explain why there is a difference?