/[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 903 - (show annotations) (download) (as text)
Wed Oct 25 13:07:12 2006 UTC (17 years, 11 months ago) by johnpye
File MIME type: text/x-csrc
File size: 29682 byte(s)
Some success with IDA: fixed up the indexing dilemma and was able to
integrate 'johnpye/thermalequilibrium.a4c' for a short time span (but
through to 3000 s as with LSODE). I would blame lack of jacobian routine
in the first instance.

Added 'more properties' button in Properties dialog for a variable, to allow
values of ode_id, ode_type etc to be queried (but not changed).

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

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