3 ;; Initialize for DLSODE. This must be called before running DLSODE_STEP.
6 ;; f - list of differential equations to be solved
7 ;; vars - list of the independent variable and the dependent
8 ;; variables. The order of the dependent variables must be
9 ;; in the same order as the equations in f.
11 ;; mf - method to be used. It should be one of the following
14 ;; 10 Nonstiff (Adams) method, no Jacobian used.
15 ;; 21 Stiff (BDF) method, user-supplied full Jacobian.
16 ;; 22 Stiff method, internally generated full
18 ;; The Fortran version of DLSODE supports additional
19 ;; methods, but these are not supported here.
21 ;; A state object is created that should be used as the state
22 ;; parameter in DLSODE_STEP. The user must not modify these. The
23 ;; output includes the equations, the set of variables, the mf
24 ;; parameter, and various work arrays that must be modified between
25 ;; calls to dlsode_step.
26 (defmfun $dlsode_init
(f vars mf
)
27 ;; Verify values of mf.
28 (unless (member mf
'(10 21 22) :test
#'eql
)
29 (merror "MF must be 10, 21, or 22"))
30 (let* ((neq (length (cdr f
)))
35 (+ 22 (* 9 neq
) (* neq neq
)))))
36 (rwork (make-array lrw
:element-type
'flonum
))
42 (iwork (make-array liw
:element-type
'f2cl-lib
:integer4
))
44 ;; Jacobian is needed only for mf = 21, so compute only then.
47 (meval `(($jacobian
) ,f
,(list* '(mlist) (cddr vars
))))
49 ;; Make sure neq is consistent with the number of elements in f
51 (unless (= (1+ neq
) ($length vars
))
52 (merror "Expected ~M variables but got ~M: ~M"
53 (1+ neq
) ($length vars
) vars
))
55 (make-mlist '$f
(compile nil
(coerce-float-fun f vars
)))
56 (make-mlist '$vars vars
)
58 (make-mlist '$neq neq
)
59 (make-mlist '$lrw lrw
)
60 (make-mlist '$liw liw
)
61 (make-mlist '$rwork rwork
)
62 (make-mlist '$iwork iwork
)
63 (make-mlist '$fjac fjac
))))
65 ;; Main DLSODE routine to compute each output point. Must be called
68 ;; For full details see comments in fortran/dlsode.f
72 ;; init-y - For the first call (when istate = 1), the initial values
73 ;; tt - Value of the independent value
74 ;; tout - Next point where output is desired (/= tt)
75 ;; rtol - relative tolerance parameter
77 ;; atol - Absolute tolerance parameter, scalar of vector. If
78 ;; scalar, it applies to all dependent variables.
79 ;; Otherwise it must be the tolerance for each dependent
82 ;; Use rtol = 0 for pure absolute error and use atol = 0
83 ;; for pure relative error.
85 ;; istate - 1 for the first call to dlsode, 2 for subsequent calls.
86 ;; state - state returned by dlsode-init.
89 ;; A list consisting of the following items:
90 ;; t - independent variable value
91 ;; y - list of values of the dependent variables at time t.
92 ;; istate - Integration status:
93 ;; 1 - no work because tout = tt
94 ;; 2 - successful result
95 ;; -1 - Excess work done on this call
96 ;; -2 - Excess accuracy requested
97 ;; -3 - Illegal input detected
98 ;; -4 - Repeated error test failures
99 ;; -5 - Repeated convergence failures (perhaps bad
100 ;; Jacobian or wrong choice of mf or tolerances)
101 ;; -6 - Error weight because zero during problem
102 ;; (solution component i vanishded and atol(i) = 0.
103 ;; info - association list of various bits of information:
104 ;; n_steps - total steps taken thus far
105 ;; n_f_eval - total number of function evals
106 ;; n_j_eval - total number of Jacobian evals
107 ;; method_order - method order
108 ;; len_rwork - Actual length used for real work array
109 ;; len_iwork - Actual length used for integer work array
111 (defmfun $dlsode_step
(init-y tt tout rtol atol istate state
)
112 (let ((f ($assoc
'$f state
))
113 (vars ($assoc
'$vars state
))
114 (mf ($assoc
'$mf state
))
115 (neq ($assoc
'$neq state
))
116 (lrw ($assoc
'$lrw state
))
117 (liw ($assoc
'$liw state
))
118 (rwork ($assoc
'$rwork state
))
119 (iwork ($assoc
'$iwork state
))
120 (fjac ($assoc
'$fjac state
)))
121 ;; Verify that we got something from state. (Do we need more validation?)
122 (unless (and f vars mf neq lrw liw rwork iwork
)
123 (merror "State appears to be invalid"))
125 (itol (if (listp atol
)
128 (y-array (make-array (1- (length init-y
))
129 :element-type
'double-float
130 :initial-contents
(cdr ($float init-y
)))))
133 (format t
"vars = ~S~%" vars
)
134 (format t
"ff = ~S~%" ff
)
135 (format t
"fjac = ~S~%" fjac
))
136 (flet ((fex (neq tt y ydot
)
137 (declare (type double-float tt
)
138 (type (cl:array double-float
(*)) y ydot
)
140 (let* ((y-list (coerce y
'list
))
141 (yval (cl:apply ff tt y-list
)))
144 (format t
"fex: t = ~S; y = ~S~%" tt y
)
145 (format t
" yval = ~S~%" yval
)
146 (format t
" ydot = ~S~%" ydot
))
147 (replace ydot
(cdr yval
))))
148 (jex (neq tt y ml mu pd nrpd
)
149 (declare (type f2cl-lib
:integer4 ml mu nrpd
)
150 (type double-float tt
)
151 (type (cl:array double-float
(*)) y
)
152 (type (cl:array double-float
*) pd
)
156 (format t
"jex: tt = ~S; y = ~S~%" tt y
)
157 (format t
" ml, mu = ~S ~S~%" ml mu
)
158 (format t
" pd = ~S~%" pd
)
159 (format t
" nrpd = ~S~%" nrpd
)
160 (format t
" fjac = ~S~%" fjac
))
161 (let* ((y-list (coerce y
'list
))
162 (j (cl:apply fjac tt y-list
))
166 (format t
" y-list = ~S~%" y-list
)
167 (format t
" j = ~S~%" j
))
171 (setf (f2cl-lib:fref pd
(row col
) ((1 nrpd
) (1)))
176 (format t
"pd = ~S~%" pd
))))
178 (multiple-value-bind (ign-0 ign-1 ign-2
180 ign-5 ign-6 ign-7 ign-8 ign-9
182 (odepack:dlsode
#'fex
183 (make-array 1 :element-type
'f2cl-lib
:integer4
184 :initial-element neq
)
189 (make-array 1 :element-type
'double-float
190 :initial-element
($float rtol
))
192 (make-array (1- (length atol
))
193 :element-type
'double-float
194 :initial-contents
(rest ($float atol
)))
195 (make-array 1 :element-type
'double-float
196 :initial-element
($float atol
)))
206 (declare (ignore ign-0 ign-1 ign-2 ign-5 ign-6 ign-7 ign-8 ign-9
))
211 (coerce y-array
'list
))
214 (make-mlist '$n_steps
(aref iwork
10))
215 (make-mlist '$n_f_eval
(aref iwork
11))
216 (make-mlist '$n_j_eval
(aref iwork
12))
217 (make-mlist '$method_order
(aref iwork
13))
218 (make-mlist '$len_rwork
(aref iwork
16))
219 (make-mlist '$len_iwork
(aref iwork
17)))))))))
221 (defmfun $dlsode
(f yvars inity trange rtol atol mf
)
222 (let* ((tvar (elt trange
1))
223 (tstart ($float
(elt trange
2)))
224 (tend ($float
(elt trange
3)))
225 (tstep ($float
(elt trange
4)))
226 (vars ($cons tvar yvars
))
227 (state ($dlsode_init f vars mf
))
229 (loop for tout from tstart upto tend by tstep
231 (let ((r ($dlsode_step inity tstart tout rtol atol
1 state
)))
232 (when (minusp (elt r
3))
233 ;; See dlsode.f for the definitions for these values.
234 (merror "Error ~M: ~M"
238 "Excess work done on this call (perhaps wrong MF")
240 "Excess accuracy requested (tolerances too small)")
242 "Illegal input detected (see printed message)")
244 "Repeated error test failures (check all inputs)")
246 "Repeated convergence failures (perhaps bad Jacobian supplied or wrong choice of MF or tolerances)")
248 "Error weight became zero during problem (solution component i vanished, and ATOL or ATOL(i) = 0.)"))))
249 (push ($cons
(elt r
1) (elt r
2))