/[ascend]/trunk/base/generic/solver/lsode.c
ViewVC logotype

Contents of /trunk/base/generic/solver/lsode.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1055 - (show annotations) (download) (as text)
Sat Jan 6 14:47:13 2007 UTC (16 years, 8 months ago) by johnpye
File MIME type: text/x-csrc
File size: 33985 byte(s)
simplified slv_check_bounds call.
1 /* ASCEND modelling environment
2 Copyright 1997, Carnegie Mellon University
3 Copyright (C) 2006 Carnegie Mellon University
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 *//**
20 @file
21 LSODE integrator.
22
23 (old implementation notes:)
24
25 As fortran io is unreliably portable (vc5+digital fortran)
26 we have converted xerrwv to xascwv provided here.
27
28 The lsode interface variable t is actually an array of
29 2 doubles rather than just 1. The first is the the one
30 used by lsode. The second is used by LSODE_FEX to tell
31 what the last time it was called was. This is so the
32 C driver can tell if it needs to resolve d to compute
33 observation variables. If x[0]==x[1] we save ourselves
34 a solve.
35
36 @NOTE The above doesn't work since lsode doesn't use the same t internally
37 that we hand it.
38
39 *//*
40 by Kirk Abbott and Ben Allan
41 Created: 1/94
42 Version: $Revision: 1.29 $
43 Version control file: $RCSfile: Lsode.c,v $
44 Date last modified: $Date: 2000/01/25 02:26:31 $
45 Last modified by: $Author: ballan $
46 */
47
48 #ifndef NO_SIGNAL_TRAPS
49 #include <signal.h>
50 #include <setjmp.h>
51 #endif /* NO_SIGNAL_TRAPS */
52
53 #include <utilities/ascConfig.h>
54 #include <utilities/error.h>
55 #include <compiler/instance_enum.h>
56 #include <utilities/ascSignal.h>
57 #include <utilities/ascMalloc.h>
58 #include <utilities/ascPanic.h>
59
60 #include "slv_types.h"
61 #include "mtx.h"
62 #include "rel.h"
63 #include "var.h"
64 #include "discrete.h"
65 #include "conditional.h"
66 #include "bnd.h"
67 #include "logrel.h"
68 #include "slv_common.h"
69 #include "linsol.h"
70 #include "linsolqr.h"
71 #include "slv_client.h"
72 #include "slv_stdcalls.h"
73
74 #include "integrator.h"
75 #include "lsode.h"
76
77 const IntegratorInternals integrator_lsode_internals = {
78 integrator_lsode_create
79 ,integrator_lsode_params_default
80 ,integrator_analyse_ode /* note, this routine is back in integrator.c */
81 ,integrator_lsode_solve
82 ,integrator_lsode_free
83 ,INTEG_LSODE
84 ,"LSODE"
85 };
86
87 /*
88 #include "Sensitivity.h"
89 *//* see the packages dir */
90
91 /*
92 * NOUNDERBARS --> FORTRAN compiler naming convention for subroutine
93 * is wierd. WIN32/CRAY is treated as special case
94 */
95 #ifdef APOLLO
96 #define NOUNDERBARS TRUE
97 #endif
98 #ifdef _HPUX_SOURCE
99 #define NOUNDERBARS TRUE
100 #endif
101 /* AIX xlf will not suffix an underbar on a symbol
102 * unless xlf is given the ``-qextname'' option
103 */
104 #ifdef _AIX
105 #define NOUNDERBARS TRUE
106 #endif
107
108 #ifdef NOUNDERBARS
109 #define LSODE lsode
110 #define LSODE_JEX jex
111 #define LSODE_FEX fex
112 #define GETCOMMON get_lsode_common
113 #define XASCWV xascwv
114 #else
115 /* sun, __alpha, __sgi, ... */
116 #define LSODE lsode_
117 #define LSODE_JEX jex_
118 #define LSODE_FEX fex_
119 #define GETCOMMON get_lsode_common_
120 #define XASCWV xascwv_
121 #endif
122
123 #if defined(CRAY) || (defined(__WIN32__) && !defined(__MINGW32_VERSION))
124 #undef LSODE
125 #undef LSODE_JEX
126 #undef LSODE_FEX
127 #undef GETCOMMON
128 #undef XASCWV
129 #define XASCWV XASCWV
130 #define LSODE LSODE
131 #define LSODE_JEX JEX
132 #define LSODE_FEX FEX
133 #define GETCOMMON GET_LSODE_COMMON
134 #endif
135
136 #define DOTIME FALSE
137
138 /* definitions of lsode supported children of atoms, etc */
139 /********************************************************************/
140 /* solver_var children expected for state variables */
141 static symchar *g_symbols[2];
142 #define STATERTOL g_symbols[0]
143 #define STATEATOL g_symbols[1]
144 static
145 void InitTolNames(void)
146 {
147 STATERTOL = AddSymbol("ode_rtol");
148 STATEATOL = AddSymbol("ode_atol");
149 }
150
151 /**
152 Because LSODE doesn't seem to make an allowance for 'client data' we
153 have to store this as a 'local global' and fish it out when we're in the
154 callbacks.
155 */
156 IntegratorSystem *l_lsode_blsys;
157
158 enum Lsode_enum {
159 lsode_none, /* true on first call */
160 lsode_function, lsode_derivative, /* functions or gradients done */
161 lsode_sparse, lsode_dense, /* what type of backend should we */
162 lsode_band, /* use for the integrator */
163 lsode_ok, lsode_nok /* bad return from func or grad */
164 };
165
166 static struct Lsode_Data {
167 enum Lsode_enum lastcall; /* type of last call; func or grad */
168 enum Lsode_enum status; /* solve status */
169 int partitioned; /* partioned func evals or not */
170 } lsodesys = {lsode_none, lsode_ok, 1};
171
172
173 /*--------------------------
174 Data space for use by LSODE
175 */
176 typedef struct IntegratorLsodeDataStruct{
177 long n_eqns; /**< dimension of state vector */
178 int *input_indices; /**< vector of state vars indexes */
179 int *output_indices; /**< vector of derivative var indexes */
180 struct var_variable **y_vars; /**< NULL terminated list of states vars */
181 struct var_variable **ydot_vars; /**< NULL terminated list of derivative vars*/
182 struct rel_relation **rlist; /**< NULL terminated list of relevant rels
183 to be differentiated */
184 double **dydx_dx; /**< change in derivatives wrt states
185 I prefer to call this: d(ydot)/dy */
186 } IntegratorLsodeData;
187
188
189 /*----------------------------
190 Function types that LSODE wants to use
191 */
192
193 /**
194 Type of function used to evaluate derivative system.
195 */
196 typedef void LsodeEvalFn(int *, double *, double *, double *);
197
198 /**
199 Type of function used to evaluate jacobian system.
200 */
201 typedef void LsodeJacobianFn(int *, double *, double *, int *, int *, double *, int *);
202
203 /*----------------------------
204 forward declarations
205 */
206
207 int integrator_lsode_setup_diffs(IntegratorSystem *blsys);
208 static double **lsode_densematrix_create(int nrows, int ncols);
209 static void lsode_densematrix_destroy(double **matrix,int nrows);
210
211 /**
212 void LSODE(&fex, &neq, y, &x, &xend, &itol, reltol, abtol, &itask,
213 &istate, &iopt ,rwork, &lrw, iwork, &liw, &jex, &mf);
214
215 This is a prototype for the *fortran* LSODE function.
216
217 No 'extern' here, so we want linker to complain if no static linkage.
218 */
219 void LSODE(LsodeEvalFn*,int *neq ,double *y ,double *x
220 ,double *xend
221 ,int *itol ,double *reltol ,double *abtol
222 ,int *itask ,int *istate ,int *iopt
223 ,double *rwork ,int *lrw
224 ,int *iwork ,int *liw
225 ,LsodeJacobianFn *jex ,int *mf
226 );
227
228 /*------------------------------------------------------
229 Memory allocation/free
230 */
231
232 void integrator_lsode_create(IntegratorSystem *blsys){
233 IntegratorLsodeData *d;
234 d = ASC_NEW_CLEAR(IntegratorLsodeData);
235 d->n_eqns=0;
236 d->input_indices=NULL;
237 d->output_indices=NULL;
238 d->y_vars=NULL;
239 d->ydot_vars=NULL;
240 d->rlist=NULL;
241 d->dydx_dx=NULL;
242 blsys->enginedata=(void*)d;
243 integrator_lsode_params_default(blsys);
244
245 }
246
247 /**
248 Cleanup the data struct that belongs to LSODE
249 */
250 void integrator_lsode_free(void *enginedata){
251 IntegratorLsodeData d;
252 d = *((IntegratorLsodeData *)enginedata);
253
254 if(d.input_indices)ASC_FREE(d.input_indices);
255 d.input_indices = NULL;
256
257 if(d.output_indices)ASC_FREE(d.output_indices);
258 d.output_indices = NULL;
259
260 if(d.y_vars)ASC_FREE(d.y_vars);
261 d.y_vars = NULL;
262
263 if(d.ydot_vars)ASC_FREE(d.ydot_vars);
264 d.ydot_vars = NULL;
265
266 if(d.rlist)ASC_FREE(d.rlist);
267 d.rlist = NULL;
268
269 if(d.dydx_dx)lsode_densematrix_destroy(d.dydx_dx, d.n_eqns);
270 d.dydx_dx = NULL;
271
272 d.n_eqns = 0L;
273 }
274
275 /*------------------------------------------------------------------------------
276 PARAMETERS
277 */
278
279 enum ida_parameters{
280 LSODE_PARAM_TIMING
281 ,LSODE_PARAM_RTOLVECT
282 ,LSODE_PARAM_RTOL
283 ,LSODE_PARAM_ATOLVECT
284 ,LSODE_PARAM_ATOL
285 ,LSODE_PARAMS_SIZE
286 };
287
288 /**
289 Here the full set of parameters is defined, along with upper/lower bounds,
290 etc. The values are stuck into the blsys->params structure.
291
292 @return 0 on success
293 */
294 int integrator_lsode_params_default(IntegratorSystem *blsys){
295
296 asc_assert(blsys!=NULL);
297 asc_assert(blsys->engine==INTEG_LSODE);
298 slv_parameters_t *p;
299 p = &(blsys->params);
300
301 slv_destroy_parms(p);
302
303 if(p->parms==NULL){
304 CONSOLE_DEBUG("params NULL");
305 p->parms = ASC_NEW_ARRAY(struct slv_parameter, LSODE_PARAMS_SIZE);
306 if(p->parms==NULL)return -1;
307 p->dynamic_parms = 1;
308 }else{
309 asc_assert(p->num_parms == LSODE_PARAMS_SIZE);
310 CONSOLE_DEBUG("reusing parm memory");
311 }
312
313 /* reset the number of parameters to zero so that we can check it at the end */
314 p->num_parms = 0;
315
316 slv_param_bool(p,LSODE_PARAM_TIMING
317 ,(SlvParameterInitBool){{"timing"
318 ,"Output timing statistics?",1,NULL
319 }, TRUE}
320 );
321
322 slv_param_bool(p,LSODE_PARAM_ATOLVECT
323 ,(SlvParameterInitBool){{"atolvect"
324 ,"Use 'ode_atol' values as specified for each var?",1
325 ,"If TRUE, values of 'ode_atol' are taken from your model and used "
326 " in the integration. If FALSE, a scalar absolute tolerance (atol)"
327 " is shared by all variables."
328 }, TRUE }
329 );
330
331 slv_param_real(p,LSODE_PARAM_ATOL
332 ,(SlvParameterInitReal){{"atol"
333 ,"Scalar absolute error tolerance",1
334 ,"Default value of the scalar absolute error tolerance (for cases"
335 " where not specified in oda_atol var property. See 'lsode.f' for"
336 " details"
337 }, 1e-6, DBL_MIN, DBL_MAX }
338 );
339
340 slv_param_bool(p,LSODE_PARAM_RTOLVECT
341 ,(SlvParameterInitBool){{"rtolvect"
342 ,"Use 'ode_rtol' values as specified for each var?",1
343 ,"If TRUE, values of 'ode_atol' are taken from your model and used "
344 " in the integration. If FALSE, a scalar absolute tolerance (rtol)"
345 " is shared by all variables."
346 }, TRUE }
347 );
348
349 slv_param_real(p,LSODE_PARAM_RTOL
350 ,(SlvParameterInitReal){{"rtol"
351 ,"Scalar relative error tolerance",1
352 ,"Default value of the scalar relative error tolerance (for cases"
353 " where not specified in oda_rtol var property. See 'lsode.f' for"
354 " details"
355 }, 1e-6, DBL_MIN, DBL_MAX }
356 );
357
358 asc_assert(p->num_parms == LSODE_PARAMS_SIZE);
359
360 CONSOLE_DEBUG("Created %d params", p->num_parms);
361
362 return 0;
363 }
364
365 /*---------------------------------------------------------
366 Couple of matrix methods...?
367 */
368
369 static double **lsode_densematrix_create(int nrows, int ncols){
370 int c;
371 double **result;
372 assert(nrows>0);
373 assert(ncols>0);
374 result = ASC_NEW_ARRAY(double *, nrows);
375 for (c=0;c<nrows;c++) {
376 result[c] = ASC_NEW_ARRAY_CLEAR(double, ncols);
377 }
378 return result;
379 }
380
381 static void lsode_densematrix_destroy(double **matrix,int nrows){
382 int c;
383 if (matrix) {
384 for (c=0;c<nrows;c++) {
385 if (matrix[c]) {
386 ascfree((char *)matrix[c]);
387 }
388 }
389 ascfree((char *)matrix);
390 }
391 }
392
393 /*------------------------------------------------------------------------------
394 PROBLEM ANALYSIS
395 */
396
397 /**
398 @TODO needs work. Assumes struct Instance* and struct var_variable*
399 are synonymous, which demonstrates the need for a method to take
400 an instance and ask the solvers for its global or local index
401 if var and inst are decoupled.
402 */
403 int integrator_lsode_setup_diffs(IntegratorSystem *blsys) {
404 /* long n_eqns; */
405 unsigned long nch,i;
406
407 struct var_variable **vp;
408 int *ip;
409
410 IntegratorLsodeData *enginedata;
411 enginedata = (IntegratorLsodeData *)blsys->enginedata;
412 assert(enginedata!=NULL);
413
414 assert(enginedata->n_eqns==blsys->n_y);
415
416 /*
417 Put the
418 Let us now process what we consider *inputs* to the problem as
419 far as ASCEND is concerned; i.e. the state vars or the y_vars's
420 if you prefer.
421 */
422 nch = enginedata->n_eqns;
423
424
425 vp = enginedata->y_vars;
426 ip = enginedata->input_indices;
427 for (i=0;i<nch;i++) {
428 *vp = (struct var_variable *)blsys->y[i];
429 *ip = var_sindex(*vp);
430 vp++;
431 ip++;
432 }
433 *vp = NULL; /* terminate */
434
435 /*
436 Let us now go for the outputs, ie the derivative terms.
437 */
438 vp = enginedata->ydot_vars;
439 ip = enginedata->output_indices;
440 for (i=0;i<nch;i++) {
441 *vp = (struct var_variable *)blsys->ydot[i];
442 *ip = var_sindex(*vp);
443 vp++; /* dont assume that a var is synonymous with */
444 ip++; /* an Instance; that might/will change soon */
445 }
446 *vp = NULL; /* terminate */
447
448 return 0;
449 }
450
451 /**
452 allocates, fills, and returns the atol vector based on LSODE
453
454 State variables missing child ode_rtol will be defaulted to ATOL
455 */
456 static double *lsode_get_atol( IntegratorSystem *blsys) {
457
458 struct Instance *tol;
459 double *atoli;
460 int i,len;
461 double atol;
462
463 len = blsys->n_y;
464 atoli = ASC_NEW_ARRAY(double, blsys->n_y); /* changed, this was n_y+1 before, dunnowi -- JP */
465 if (atoli == NULL) {
466 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Insufficient memory");
467 return atoli;
468 }
469
470 if(!SLV_PARAM_BOOL(&(blsys->params),LSODE_PARAM_ATOLVECT)){
471 atol = SLV_PARAM_REAL(&(blsys->params),LSODE_PARAM_ATOL);
472 CONSOLE_DEBUG("Using ATOL = %f for all vars", atol);
473 for(i=0; i<len; ++i){
474 atoli[i] = atol;
475 }
476 }else{
477 InitTolNames();
478 for (i=0; i<len; i++) {
479
480 tol = ChildByChar(var_instance(blsys->y[i]),STATEATOL);
481 if (tol == NULL || !AtomAssigned(tol) ) {
482 atoli[i] = SLV_PARAM_REAL(&(blsys->params),LSODE_PARAM_ATOL);
483 ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Assuming atol = %3g"
484 "for ode_atol child undefined for state variable %ld."
485 ,atoli[i], blsys->y_id[i]
486 );
487 } else {
488 atoli[i] = RealAtomValue(tol);
489 CONSOLE_DEBUG("Using atol %3g for state variable %ld.",atoli[i], blsys->y_id[i]);
490 }
491 }
492 }
493 return atoli;
494 }
495
496 /**
497 Allocates, fills, and returns the rtol vector based on LSODE
498
499 State variables missing child ode_rtol will be defaulted to RTOL
500 */
501 static double *lsode_get_rtol( IntegratorSystem *blsys) {
502
503 struct Instance *tol;
504 double rtol, *rtoli;
505 int i,len;
506
507 len = blsys->n_y;
508 rtoli = ASC_NEW_ARRAY(double, blsys->n_y+1);
509 if (rtoli == NULL) {
510 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Insufficient memory");
511 return rtoli;
512 }
513 if(!SLV_PARAM_BOOL(&(blsys->params),LSODE_PARAM_RTOLVECT)){
514 rtol = SLV_PARAM_REAL(&(blsys->params),LSODE_PARAM_RTOL);
515 CONSOLE_DEBUG("Using RTOL = %f for all vars", rtol);
516 for(i=0; i<len; ++i){
517 rtoli[i] = rtol;
518 }
519 }else{
520 InitTolNames();
521 for (i=0; i<len; i++) {
522 tol = ChildByChar(var_instance(blsys->y[i]),STATERTOL);
523 if (tol == NULL || !AtomAssigned(tol) ) {
524 rtoli[i] = SLV_PARAM_REAL(&(blsys->params),LSODE_PARAM_RTOL);
525
526 ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Assuming rtol = %3g"
527 "for ode_rtol child undefined for state variable %ld."
528 ,rtoli[i], blsys->y_id[i]
529 );
530
531 } else {
532 rtoli[i] = RealAtomValue(tol);
533 }
534 }
535 }
536 rtoli[len] = SLV_PARAM_REAL(&(blsys->params),LSODE_PARAM_RTOL);
537 return rtoli;
538 }
539
540 /*
541 Write out a a status message based on the istate parameter.
542 */
543 static void lsode_write_istate( int istate) {
544 switch (istate) {
545 case -1:
546 FPRINTF(ASCERR,"Excess steps taken on this call (perhaps wrong MF).");
547 break;
548 case -2:
549 FPRINTF(ASCERR,"Excess accuracy requested (tolerances too small).");
550 break;
551 case -3:
552 FPRINTF(ASCERR,"Illegal input detected (see console).");
553 break;
554 case -4:
555 FPRINTF(ASCERR,"Repeated error test failures (check all inputs).");
556 break;
557 case -5:
558 FPRINTF(ASCERR,"Repeated convergence failures (perhaps bad Jacobian supplied, or wrong choice of MF or tolerances).");
559 break;
560 case -6:
561 FPRINTF(ASCERR,"Error weight became zero during problem (solution component i vanished, and atol or atol(i) = 0).");
562 break;
563 case -7:
564 FPRINTF(ASCERR,"Interrupted? User cancelled operation?");
565 break;
566 default:
567 FPRINTF(ASCERR,"Unknown 'istate' error code %d from LSODE.",istate);
568 break;
569 }
570 }
571
572 /**
573 Free memory allocated for the LSODE, but first check.
574 */
575 static void lsode_free_mem(double *y, double *reltol, double *abtol, double *rwork,
576 int *iwork, double *obs, double *dydx)
577 {
578 if (y != NULL) {
579 ascfree((double *)y);
580 }
581 if (reltol != NULL) {
582 ascfree((double *)reltol);
583 }
584 if (abtol != NULL) {
585 ascfree((double *)abtol);
586 }
587 if (rwork != NULL) {
588 ascfree((double *)rwork);
589 }
590 if (iwork != NULL) {
591 ascfree((int *)iwork);
592 }
593 if (obs != NULL) {
594 ascfree((double *)obs);
595 }
596 if (dydx != NULL) {
597 ascfree((double *)dydx);
598 }
599 }
600
601 /*
602 *********************************************************************
603 * This code is provided for the benefit of a temporary
604 * fix for the derivative problem in Lsode.
605 * The proper permanent fix for lsode is to dump it in favor of
606 * cvode or dassl.
607 * Extended 7/95 baa to deal with linsolqr and lsode.
608 * It is assumed the system has been solved at the current point.
609 *********************************************************************
610 */
611 int lsode_derivatives(slv_system_t sys, double **dy_dx,
612 int *inputs_ndx_list, int ninputs,
613 int *outputs_ndx_list, int noutputs)
614 {
615 static int n_calls = 0;
616 linsolqr_system_t lqr_sys; /* stuff for the linear system & matrix */
617 mtx_matrix_t mtx;
618 int32 capacity;
619 real64 *scratch_vector = NULL;
620 int result=0;
621
622 (void)NumberFreeVars(NULL); /* used to re-init the system */
623 (void)NumberIncludedRels(NULL); /* used to re-init the system */
624 if (!sys) {
625 FPRINTF(stderr,"The solve system does not exist !\n");
626 return 1;
627 }
628
629 result = Compute_J(sys);
630 if (result) {
631 FPRINTF(stderr,"Early termination due to failure in calc Jacobian\n");
632 return 1;
633 }
634
635 lqr_sys = slv_get_linsolqr_sys(sys); /* get the linear system */
636 if (lqr_sys==NULL) {
637 FPRINTF(stderr,"Early termination due to missing linsolqr system.\n");
638 return 1;
639 }
640 mtx = slv_get_sys_mtx(sys); /* get the matrix */
641 if (mtx==NULL) {
642 FPRINTF(stderr,"Early termination due to missing mtx in linsolqr.\n");
643 return 1;
644 }
645 capacity = mtx_capacity(mtx);
646 scratch_vector = ASC_NEW_ARRAY_CLEAR(real64,capacity);
647 linsolqr_add_rhs(lqr_sys,scratch_vector,FALSE);
648
649 result = LUFactorJacobian(sys);
650 if (result) {
651 FPRINTF(stderr,"Early termination due to failure in LUFactorJacobian\n");
652 goto error;
653 }
654 result = Compute_dy_dx_smart(sys, scratch_vector, dy_dx,
655 inputs_ndx_list, ninputs,
656 outputs_ndx_list, noutputs);
657
658 linsolqr_remove_rhs(lqr_sys,scratch_vector);
659 if (result) {
660 FPRINTF(stderr,"Early termination due to failure in Compute_dy_dx\n");
661 goto error;
662 }
663
664 error:
665 n_calls++;
666 if (scratch_vector) {
667 ascfree((char *)scratch_vector);
668 }
669 return result;
670 }
671
672 /**
673 The current way that we are getting the derivatives (if the problem
674 was solved partitioned) messes up the slv_system so that we *have*
675 to do a *presolve* rather than a simply a *resolve* before doing
676 function calls. This code below attempts to handle these cases.
677 */
678 static void LSODE_FEX( int *n_eq ,double *t ,double *y ,double *ydot)
679 {
680 slv_status_t status;
681
682 /* slv_parameters_t parameters; pity lsode doesn't allow error returns */
683 /* int i; */
684 unsigned long res;
685
686 #if DOTIME
687 double time1,time2;
688 #endif
689
690 /* CONSOLE_DEBUG("Calling for a function evaluation"); */
691
692 #if DOTIME
693 CONSOLE_DEBUG("Calling for a function evaluation");
694 time1 = tm_cpu_time();
695 #endif
696
697 /*
698 t[1]=t[0]; can't do this. lsode calls us with a different t than the t we sent in.
699 */
700 integrator_set_t(l_lsode_blsys, t[0]);
701 integrator_set_y(l_lsode_blsys, y);
702
703 #if DOTIME
704 time2 = tm_cpu_time();
705 #endif
706
707 switch(lsodesys.lastcall) {
708 case lsode_none: /* first call */
709 CONSOLE_DEBUG("FIRST CALL...");
710
711 case lsode_derivative:
712 if (lsodesys.partitioned) {
713 /* CONSOLE_DEBUG("PRE-SOLVE"); */
714 slv_presolve(l_lsode_blsys->system);
715 } else {
716 /** @TODO this doesn't ever seem to be called */
717 CONSOLE_DEBUG("RE-SOLVE");
718 slv_resolve(l_lsode_blsys->system);
719 }
720 break;
721 default:
722 case lsode_function:
723 slv_resolve(l_lsode_blsys->system);
724 break;
725 }
726
727 slv_solve(l_lsode_blsys->system);
728 slv_get_status(l_lsode_blsys->system, &status);
729 if(slv_check_bounds(l_lsode_blsys->system,0,-1,"")){
730 lsodesys.status = lsode_nok;
731 }
732
733 /* pass the solver status to the integrator */
734 res = integrator_checkstatus(status);
735
736 #if DOTIME
737 time2 = tm_cpu_time() - time2;
738 #endif
739
740 if(res){
741 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed to solve for derivatives (%d)",res);
742 #if 0
743 ERROR_REPORTER_START_HERE(ASC_PROG_ERR);
744 FPRINTF(ASCERR,"Unable to compute the vector of derivatives with the following values for the state variables:\n");
745 for (i = 0; i< *n_eq; i++) {
746 FPRINTF(ASCERR,"y[%4d] = %f\n",i, y[i]);
747 }
748 error_reporter_end_flush();
749 #endif
750 lsodesys.status = lsode_nok;
751 }else{
752 lsodesys.status = lsode_ok;
753 }
754 integrator_get_ydot(l_lsode_blsys, ydot);
755
756 lsodesys.lastcall = lsode_function;
757 #if DOTIME
758 time1 = tm_cpu_time() - time1;
759 CONSOLE_DEBUG("Function evalulation has been completed in time %g. True function call time = %g",time1,time2);
760 #endif
761 }
762
763 /**
764 Evaluate the jacobian
765 */
766 static void LSODE_JEX(int *neq ,double *t, double *y,
767 int *ml ,int *mu ,double *pd, int *nrpd)
768 {
769 int nok = 0;
770 int i,j;
771
772 IntegratorLsodeData enginedata=*((IntegratorLsodeData *)l_lsode_blsys->enginedata);
773
774 UNUSED_PARAMETER(t);
775 UNUSED_PARAMETER(y);
776 UNUSED_PARAMETER(ml);
777 UNUSED_PARAMETER(mu);
778
779 /* CONSOLE_DEBUG("Calling for a gradient evaluation"); */
780 #if DOTIME
781 double time1;
782
783 CONSOLE_DEBUG("Calling for a gradient evaluation");
784 time1 = tm_cpu_time();
785 #endif
786 /*
787 * Make the real call.
788 */
789 nok = lsode_derivatives(l_lsode_blsys->system
790 , enginedata.dydx_dx
791 , enginedata.input_indices
792 , *neq
793 , enginedata.output_indices
794 , *nrpd
795 );
796
797 if (nok) {
798 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Error in computing the derivatives for the system. Failing...");
799 lsodesys.status = lsode_nok;
800 lsodesys.lastcall = lsode_derivative;
801 return;
802 } else {
803 lsodesys.status = lsode_ok;
804 lsodesys.lastcall = lsode_derivative;
805 }
806 /*
807 Map data from C based matrix to Fortan matrix.
808 We will send in a column major ordering vector for pd.
809 */
810 for (j=0;j<*neq;j++) { /* loop through columnns */
811 for (i=0;i<*nrpd;i++){ /* loop through rows */
812 /* CONSOLE_DEBUG("JAC[r=%d,c=%d]=%f",i,j,enginedata.dydx_dx[i][j]); */
813 *pd++ = enginedata.dydx_dx[i][j];
814 }
815 }
816
817 #if DOTIME
818 time1 = tm_cpu_time() - time1;
819 CONSOLE_DEBUG("Time to do gradient evaluation %g",time1);
820 #endif
821
822 return;
823 }
824
825 /**
826 The public function: here we do the actual integration, I guess.
827
828 Return 0 on success
829 */
830 int integrator_lsode_solve(IntegratorSystem *blsys
831 , unsigned long start_index, unsigned long finish_index
832 ){
833 slv_status_t status;
834 slv_parameters_t params;
835 IntegratorLsodeData *d;
836
837 double x[2];
838 double xend,xprev;
839 unsigned long nsamples, neq;
840 long nobs;
841 int itol, itask, mf, lrw, liw;
842 unsigned long index;
843 int istate, iopt;
844 double * rwork;
845 int * iwork;
846 double *y, *abtol, *reltol, *obs, *dydx;
847 int my_neq;
848 FILE *y_out =NULL;
849 FILE *obs_out =NULL;
850 int reporterstatus;
851
852 /* store the local variable so that we can get at stuff from inside LSODE_FEX. */
853 l_lsode_blsys = blsys;
854
855 d = (IntegratorLsodeData *)(blsys->enginedata);
856
857 /* the numer of equations must be equal to blsys->n_y, the number of states */
858 d->n_eqns = blsys->n_y;
859 assert(d->n_eqns>0);
860
861 d->input_indices = ASC_NEW_ARRAY_CLEAR(int, d->n_eqns);
862 d->output_indices = ASC_NEW_ARRAY_CLEAR(int, d->n_eqns);
863 d->dydx_dx = lsode_densematrix_create(d->n_eqns,d->n_eqns);
864
865 d->y_vars = ASC_NEW_ARRAY(struct var_variable *,d->n_eqns+1);
866 d->ydot_vars = ASC_NEW_ARRAY(struct var_variable *, d->n_eqns+1);
867
868 integrator_lsode_setup_diffs(blsys);
869
870 /* this is a lie, but we will keep it.
871 We handle any linsol/linsolqr based solver. */
872 if (strcmp(slv_solver_name(slv_get_selected_solver(blsys->system)),"QRSlv") != 0) {
873 ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"QRSlv must be selected before integration.");
874 return 1;
875 }
876
877 slv_get_status(l_lsode_blsys->system, &status);
878
879 if (status.struct_singular) {
880 ERROR_REPORTER_HERE(ASC_USER_ERROR,"Integration will not be performed. The system is structurally singular.");
881 lsodesys.status = lsode_nok;
882 return 2;
883 }
884
885 #if defined(STATIC_LSOD) || defined (DYNAMIC_LSOD)
886
887 /* here we assume integrators.c is in charge of dynamic loading */
888
889 slv_get_parameters(blsys->system,&params);
890 lsodesys.partitioned = 1;
891
892 nsamples = integrator_getnsamples(blsys);
893 if (nsamples <2) {
894 ERROR_REPORTER_HERE(ASC_USER_ERROR,"Integration will not be performed. The system has no end sample time defined.");
895 lsodesys.status = lsode_nok;
896 return 3;
897 }
898 neq = blsys->n_y;
899 nobs = blsys->n_obs;
900
901 /* samplelist_debug(blsys->samples); */
902
903 /* x[0] = integrator_get_t(blsys); */
904 x[0] = integrator_getsample(blsys, 0);
905 x[1] = x[0]-1; /* make sure we don't start with wierd x[1] */
906 lrw = 22 + 9*neq + neq*neq;
907 rwork = ASC_NEW_ARRAY_CLEAR(double, lrw+1);
908 liw = 20 + neq;
909 iwork = ASC_NEW_ARRAY_CLEAR(int, liw+1);
910 y = integrator_get_y(blsys, NULL);
911 reltol = lsode_get_rtol(blsys);
912 abtol = lsode_get_atol(blsys);
913 obs = integrator_get_observations(blsys, NULL);
914 dydx = ASC_NEW_ARRAY_CLEAR(double, neq+1);
915 if (!y || !obs || !abtol || !reltol || !rwork || !iwork || !dydx) {
916 lsode_free_mem(y,reltol,abtol,rwork,iwork,obs,dydx);
917 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Insufficient memory for lsode.");
918 lsodesys.status = lsode_nok;
919 return 4;
920 }
921
922 /*
923 Prepare args and call lsode.
924 */
925 itol = 4;
926 itask = 1;
927 istate = 1;
928 iopt = 1;
929 rwork[4] = integrator_get_stepzero(blsys);
930 rwork[5] = integrator_get_maxstep(blsys);
931 rwork[6] = integrator_get_minstep(blsys);
932 iwork[5] = integrator_get_maxsubsteps(blsys);
933 mf = 21; /* 21 = BDF with exact jacobian. 22 = BDF with finite diff Jacobian */
934
935 if(x[0] > integrator_getsample(blsys, 2)){
936 ERROR_REPORTER_HERE(ASC_USER_ERROR,"Invalid initialisation time: exceeds second timestep value");
937 return 5;
938 }
939
940 /* put the values from derivative system into the record */
941 integrator_setsample(blsys, start_index, x[0]);
942
943 integrator_output_init(blsys);
944
945 /* -- store the initial values of all the stuff */
946 integrator_output_write(blsys);
947 integrator_output_write_obs(blsys);
948
949 my_neq = (int)neq;
950
951 /*
952 First time entering lsode, x is input. After that,
953 lsode uses x as output (y output is y(x)). To drive
954 the loop ahead in time, all we need to do is keep upping
955 xend.
956 */
957
958 blsys->currentstep = 0;
959 for (index = start_index; index < finish_index; index++, blsys->currentstep++) {
960 xend = integrator_getsample(blsys, index+1);
961 xprev = x[0];
962 asc_assert(xend > xprev);
963 /* CONSOLE_DEBUG("LSODE call #%lu: x = [%f,%f]", index,xprev,xend); */
964
965 # ifndef NO_SIGNAL_TRAPS
966 if (SETJMP(g_fpe_env)==0) {
967 # endif /* NO_SIGNAL_TRAPS */
968
969 /* CONSOLE_DEBUG("Calling LSODE with end-time = %f",xend); */
970 /*
971 switch(mf){
972 case 10:
973 CONSOLE_DEBUG("Non-stiff (Adams) method; no Jacobian will be used"); break;
974 case 21:
975 CONSOLE_DEBUG("Stiff (BDF) method, user-supplied full Jacobian"); break;
976 case 22:
977 CONSOLE_DEBUG("Stiff (BDF) method, internally generated full Jacobian"); break;
978 case 24:
979 CONSOLE_DEBUG("Stiff (BDF) method, user-supplied banded jacobian"); break;
980 case 25:
981 CONSOLE_DEBUG("Stiff (BDF) method, internally generated banded jacobian"); break;
982 default:
983 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Invalid method id %d for LSODE",mf);
984 return 0; * failure *
985 }
986 */
987
988 LSODE(&(LSODE_FEX), &my_neq, y, x, &xend,
989 &itol, reltol, abtol, &itask, &istate,
990 &iopt ,rwork, &lrw, iwork, &liw, &(LSODE_JEX), &mf);
991
992
993 # ifndef NO_SIGNAL_TRAPS
994 } else {
995 FPRINTF(stderr,
996 "Integration terminated due to float error in LSODE call.\n");
997 lsode_free_mem(y,reltol,abtol,rwork,iwork,obs,dydx);
998 lsodesys.status = lsode_ok; /* clean up before we go */
999 lsodesys.lastcall = lsode_none;
1000 if (y_out!=NULL) {
1001 fclose(y_out);
1002 }
1003 if (obs_out!=NULL) {
1004 fclose(obs_out);
1005 }
1006 return 6;
1007 }
1008 # endif /* NO_SIGNAL_TRAPS */
1009
1010 /* CONSOLE_DEBUG("AFTER %lu LSODE CALL\n", index); */
1011 /* this check is better done in fex,jex, but lsode takes no status */
1012 if (Solv_C_CheckHalt()) {
1013 if (istate >= 0) {
1014 istate=-7;
1015 }
1016 }
1017
1018 if (istate < 0 ) {
1019 /* some kind of error occurred... */
1020 ERROR_REPORTER_START_HERE(ASC_PROG_ERR);
1021 lsode_write_istate(istate);
1022 FPRINTF(ASCERR, "\nFurthest point reached was t = %g.\n",x[0]);
1023 error_reporter_end_flush();
1024
1025 lsode_free_mem(y,reltol,abtol,rwork,iwork,obs,dydx);
1026 integrator_output_close(blsys);
1027 return 7;
1028 }
1029
1030 if (lsodesys.status==lsode_nok) {
1031 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Integration terminated due to an error in derivative computations.");
1032 lsode_free_mem(y,reltol,abtol,rwork,iwork,obs,dydx);
1033 lsodesys.status = lsode_ok; /* clean up before we go */
1034 lsodesys.lastcall = lsode_none;
1035 integrator_output_close(blsys);
1036 return 8;
1037 }
1038
1039 integrator_setsample(blsys, index+1, x[0]);
1040 /* record when lsode actually came back */
1041 integrator_set_t(blsys, x[0]);
1042 integrator_set_y(blsys, y);
1043 /* put x,y in d in case lsode got x,y by interpolation, as it does */
1044
1045 reporterstatus = integrator_output_write(blsys);
1046
1047 if(reporterstatus==0){
1048 ERROR_REPORTER_HERE(ASC_USER_ERROR,"Integration cancelled");
1049 lsode_free_mem(y,reltol,abtol,rwork,iwork,obs,dydx);
1050 lsodesys.status = lsode_ok;
1051 lsodesys.lastcall = lsode_none;
1052 integrator_output_close(blsys);
1053 return 9;
1054 }
1055
1056 if (nobs > 0) {
1057 # ifndef NO_SIGNAL_TRAPS
1058 if (SETJMP(g_fpe_env)==0) {
1059 # endif /* NO_SIGNAL_TRAPS */
1060
1061 /* solve for obs since d isn't necessarily already
1062 computed there though lsode's x and y may be.
1063 Note that since lsode usually steps beyond xend
1064 x1 usually wouldn't be x0 precisely if the x1/x0
1065 scheme worked, which it doesn't anyway. */
1066
1067 LSODE_FEX(&my_neq, x, y, dydx);
1068
1069 /* calculate observations, if any, at returned x and y. */
1070 obs = integrator_get_observations(blsys, obs);
1071
1072 integrator_output_write_obs(blsys);
1073
1074 # ifndef NO_SIGNAL_TRAPS
1075 } else {
1076 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Integration terminated due to float error in LSODE FEX call.");
1077 lsode_free_mem(y,reltol,abtol,rwork,iwork,obs,dydx);
1078 lsodesys.status = lsode_ok; /* clean up before we go */
1079 lsodesys.lastcall = lsode_none;
1080 integrator_output_close(blsys);
1081 return 10;
1082 }
1083 # endif /* NO_SIGNAL_TRAPS */
1084 }
1085 /* CONSOLE_DEBUG("Integration completed from %3g to %3g.",xprev,x[0]); */
1086 }
1087
1088 CONSOLE_DEBUG("...");
1089 CONSOLE_DEBUG("Number of steps taken: %1d.", iwork[10]);
1090 CONSOLE_DEBUG("Number of function evaluations: %1d.", iwork[11]);
1091 CONSOLE_DEBUG("Number of Jacobian evaluations: %1d.", iwork[12]);
1092 CONSOLE_DEBUG("...");
1093
1094
1095 lsode_free_mem(y,reltol,abtol,rwork,iwork,obs,dydx);
1096
1097 /*
1098 * return the system to its original state.
1099 */
1100
1101 lsodesys.status = lsode_ok;
1102 lsodesys.lastcall = lsode_none;
1103
1104 integrator_output_close(blsys);
1105
1106 CONSOLE_DEBUG("--- LSODE done ---");
1107 return 0; /* success */
1108
1109 #else /* STATIC_LSOD || DYNAMIC_LSOD */
1110
1111 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Integration will not be performed. LSODE binary not available.");
1112 lsodesys.status = lsode_nok;
1113 return 11;
1114
1115 #endif
1116 }
1117
1118 /**
1119 Function XASCWV is an error reporting function replacing the XERRWV
1120 routine in lsode.f. The call signature is the same with the original Fortran
1121 function.
1122
1123 @see the comments for 'xerrwv' from lsode.f, with which XASCWV is compatible...
1124
1125 @param msg = the message (hollerith literal or integer array).
1126 @param nmes = the length of msg (number of characters).
1127 @param nerr = the error number (not used).
1128 @param level = the error level..
1129 0 or 1 means recoverable (control returns to caller).
1130 2 means fatal (run is aborted--see note below).
1131 @param ni = number of integers (0, 1, or 2) to be printed with message.
1132 @param i1,i2 = integers to be printed, depending on ni.
1133 @param nr = number of reals (0, 1, or 2) to be printed with message.
1134 @param r1,r2 = reals to be printed, depending on nr.
1135 */
1136 void XASCWV( char *msg, /* pointer to start of message */
1137 int *nmes, /* the length of msg (number of characters) */
1138 int *nerr, /* the error number (not used). */
1139 int *level,
1140 int *ni,
1141 int *i1,
1142 int *i2,
1143 int *nr,
1144 double *r1,
1145 double *r2
1146 ){
1147 static double r1last;
1148
1149 asc_assert(*level!=2); // LSODE doesn't give level 2 in our version.
1150
1151 switch(*nerr){
1152 case 52:
1153 if(*nr==2){
1154 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Illegal t = %f, not in range (t - hu,t) = (%f,%f)", r1last, *r1, *r2);
1155 return;
1156 }else if(*nr==1){
1157 r1last = *r1;
1158 return;
1159 } break;
1160 case 204:
1161 if(*nr==0 && *ni==0)return;
1162 if(*nr==2){
1163 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Error test failed repeatedly or with abs(h)=hmin.\nt=%f and step size h=%f",*r1,*r2);
1164 return;
1165 } break;
1166 case 205:
1167 if(*nr==2){
1168 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Corrector convergence test failed repeatedly or with abs(h)=hmin.\nt=%f and step size h=%f",*r1,*r2);
1169 return;
1170 } break;
1171 case 27:
1172 if(*nr==1 && *ni==1){
1173 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Trouble with INTDY: itask = %d, tout = %f", *i1, *r1);
1174 return;
1175 } break;
1176 }
1177
1178 ERROR_REPORTER_START_NOLINE(ASC_PROG_ERR);
1179
1180 /* note that %.*s means that a string length (integer) and string pointer are being required */
1181 FPRINTF(stderr,"LSODE error: (%d) %.*s",*nerr,*nmes,msg);
1182 if (*ni == 1) {
1183 FPRINTF(stderr,"\nwhere i1 = %d",*i1);
1184 }
1185 if (*ni == 2) {
1186 FPRINTF(stderr,"\nwhere i1 = %d, i2 = %d",*i1,*i2);
1187 }
1188 if (*nr == 1) {
1189 FPRINTF(stderr,"\nwhere r1 = %.13g", *r1);
1190 }
1191 if (*nr == 2) {
1192 FPRINTF(stderr,"\nwhere r1 = %.13g, r2 = %.13g", *r1,*r2);
1193 }
1194 error_reporter_end_flush();
1195 }

john.pye@anu.edu.au
ViewVC Help
Powered by ViewVC 1.1.22