123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- c banded5x5.f
- c
- c This Fortran library contains implementations of the
- c differential equation
- c dy/dt = A*y
- c where A is a 5x5 banded matrix (see below for the actual
- c values). These functions will be used to test
- c scipy.integrate.odeint.
- c
- c The idea is to solve the system two ways: pure Fortran, and
- c using odeint. The "pure Fortran" solver is implemented in
- c the subroutine banded5x5_solve below. It calls LSODA to
- c solve the system.
- c
- c To solve the same system using odeint, the functions in this
- c file are given a python wrapper using f2py. Then the code
- c in test_odeint_jac.py uses the wrapper to implement the
- c equation and Jacobian functions required by odeint. Because
- c those functions ultimately call the Fortran routines defined
- c in this file, the two method (pure Fortran and odeint) should
- c produce exactly the same results. (That's assuming floating
- c point calculations are deterministic, which can be an
- c incorrect assumption.) If we simply re-implemented the
- c equation and Jacobian functions using just python and numpy,
- c the floating point calculations would not be performed in
- c the same sequence as in the Fortran code, and we would obtain
- c different answers. The answer for either method would be
- c numerically "correct", but the errors would be different,
- c and the counts of function and Jacobian evaluations would
- c likely be different.
- c
- block data jacobian
- implicit none
- double precision bands
- dimension bands(4,5)
- common /jac/ bands
- c The data for a banded Jacobian stored in packed banded
- c format. The full Jacobian is
- c
- c -1, 0.25, 0, 0, 0
- c 0.25, -5, 0.25, 0, 0
- c 0.10, 0.25, -25, 0.25, 0
- c 0, 0.10, 0.25, -125, 0.25
- c 0, 0, 0.10, 0.25, -625
- c
- c The columns in the following layout of numbers are
- c the upper diagonal, main diagonal and two lower diagonals
- c (i.e. each row in the layout is a column of the packed
- c banded Jacobian). The values 0.00D0 are in the "don't
- c care" positions.
- data bands/
- + 0.00D0, -1.0D0, 0.25D0, 0.10D0,
- + 0.25D0, -5.0D0, 0.25D0, 0.10D0,
- + 0.25D0, -25.0D0, 0.25D0, 0.10D0,
- + 0.25D0, -125.0D0, 0.25D0, 0.00D0,
- + 0.25D0, -625.0D0, 0.00D0, 0.00D0
- + /
- end
- subroutine getbands(jac)
- double precision jac
- dimension jac(4, 5)
- cf2py intent(out) jac
- double precision bands
- dimension bands(4,5)
- common /jac/ bands
- integer i, j
- do 5 i = 1, 4
- do 5 j = 1, 5
- jac(i, j) = bands(i, j)
- 5 continue
- return
- end
- c
- c Differential equations, right-hand-side
- c
- subroutine banded5x5(n, t, y, f)
- implicit none
- integer n
- double precision t, y, f
- dimension y(n), f(n)
- double precision bands
- dimension bands(4,5)
- common /jac/ bands
- f(1) = bands(2,1)*y(1) + bands(1,2)*y(2)
- f(2) = bands(3,1)*y(1) + bands(2,2)*y(2) + bands(1,3)*y(3)
- f(3) = bands(4,1)*y(1) + bands(3,2)*y(2) + bands(2,3)*y(3)
- + + bands(1,4)*y(4)
- f(4) = bands(4,2)*y(2) + bands(3,3)*y(3) + bands(2,4)*y(4)
- + + bands(1,5)*y(5)
- f(5) = bands(4,3)*y(3) + bands(3,4)*y(4) + bands(2,5)*y(5)
- return
- end
- c
- c Jacobian
- c
- c The subroutine assumes that the full Jacobian is to be computed.
- c ml and mu are ignored, and nrowpd is assumed to be n.
- c
- subroutine banded5x5_jac(n, t, y, ml, mu, jac, nrowpd)
- implicit none
- integer n, ml, mu, nrowpd
- double precision t, y, jac
- dimension y(n), jac(nrowpd, n)
- integer i, j
- double precision bands
- dimension bands(4,5)
- common /jac/ bands
- do 15 i = 1, 4
- do 15 j = 1, 5
- if ((i - j) .gt. 0) then
- jac(i - j, j) = bands(i, j)
- end if
- 15 continue
- return
- end
- c
- c Banded Jacobian
- c
- c ml = 2, mu = 1
- c
- subroutine banded5x5_bjac(n, t, y, ml, mu, bjac, nrowpd)
- implicit none
- integer n, ml, mu, nrowpd
- double precision t, y, bjac
- dimension y(5), bjac(nrowpd, n)
- integer i, j
- double precision bands
- dimension bands(4,5)
- common /jac/ bands
- do 20 i = 1, 4
- do 20 j = 1, 5
- bjac(i, j) = bands(i, j)
- 20 continue
- return
- end
- subroutine banded5x5_solve(y, nsteps, dt, jt, nst, nfe, nje)
- c jt is the Jacobian type:
- c jt = 1 Use the full Jacobian.
- c jt = 4 Use the banded Jacobian.
- c nst, nfe and nje are outputs:
- c nst: Total number of internal steps
- c nfe: Total number of function (i.e. right-hand-side)
- c evaluations
- c nje: Total number of Jacobian evaluations
- implicit none
- external banded5x5
- external banded5x5_jac
- external banded5x5_bjac
- external LSODA
- c Arguments...
- double precision y, dt
- integer nsteps, jt, nst, nfe, nje
- cf2py intent(inout) y
- cf2py intent(in) nsteps, dt, jt
- cf2py intent(out) nst, nfe, nje
- c Local variables...
- double precision atol, rtol, t, tout, rwork
- integer iwork
- dimension y(5), rwork(500), iwork(500)
- integer neq, i
- integer itol, iopt, itask, istate, lrw, liw
- c Common block...
- double precision jacband
- dimension jacband(4,5)
- common /jac/ jacband
- c --- t range ---
- t = 0.0D0
- c --- Solver tolerances ---
- rtol = 1.0D-11
- atol = 1.0D-13
- itol = 1
- c --- Other LSODA parameters ---
- neq = 5
- itask = 1
- istate = 1
- iopt = 0
- iwork(1) = 2
- iwork(2) = 1
- lrw = 500
- liw = 500
- c --- Call LSODA in a loop to compute the solution ---
- do 40 i = 1, nsteps
- tout = i*dt
- if (jt .eq. 1) then
- call LSODA(banded5x5, neq, y, t, tout,
- & itol, rtol, atol, itask, istate, iopt,
- & rwork, lrw, iwork, liw,
- & banded5x5_jac, jt)
- else
- call LSODA(banded5x5, neq, y, t, tout,
- & itol, rtol, atol, itask, istate, iopt,
- & rwork, lrw, iwork, liw,
- & banded5x5_bjac, jt)
- end if
- 40 if (istate .lt. 0) goto 80
- nst = iwork(11)
- nfe = iwork(12)
- nje = iwork(13)
- return
- 80 write (6,89) istate
- 89 format(1X,"Error: istate=",I3)
- return
- end
|