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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 977 - (show annotations) (download) (as text)
Wed Dec 20 00:39:52 2006 UTC (17 years, 8 months ago) by johnpye
File MIME type: text/x-csrc
File size: 45132 byte(s)
Abstracted the internal integrator calls into a struct IntegratorInternals.
Fixed up compile-time list of integrators.
If IDA is not available, then 'INTEG_IDA' will not be defined.
Added ASC_ASSERT_RANGE for assertions x in [low,high).
Changed calling convention for integrator_get_engines().
1 /* ASCEND modelling environment
2 Copyright (C) 2006 Carnegie Mellon University
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA.
18 *//**
19 @file
20 Integrator API for ASCEND, for solving systems of ODEs and/or DAEs.
21 *//*
22 by John Pye, May 2006
23 based on parts of Integrators.c in the Tcl/Tk interface directory, heavily
24 modified to provide a non-GUI-specific API and modularised for multiple
25 integration engines.
26 */
27 #include <time.h>
28 #include <string.h>
29 #include "integrator.h"
30 #include "lsode.h"
31 #include "aww.h"
32 #include "ida.h"
33 #include "slv_common.h"
34 #include "slv_client.h"
35 #include <utilities/ascPanic.h>
36
37 #include "samplelist.h"
38
39 /**
40 Define as TRUE to enable debug message printing.
41 @TODO this needs to go away.
42 */
43 #define INTEG_DEBUG TRUE
44 #define ANALYSE_DEBUG
45
46 /**
47 Print a debug message with value if INTEG_DEBUG is TRUE.
48 */
49 #define print_debug(msg, value) \
50 if(INTEG_DEBUG){ CONSOLE_DEBUG(msg, value); }
51
52 /**
53 Print a debug message string if INTEG_DEBUG is TRUE.
54 */
55 #define print_debugstring(msg) \
56 if(INTEG_DEBUG){ CONSOLE_DEBUG(msg); }
57
58 /*------------------------------------------------------------------------------
59 The following names are of solver_var children or attributes
60 * we support (at least temporarily) to determine who is a state and
61 * who matching derivative.
62 * These should be supported directly in a future solveratominst.
63 */
64
65 static symchar *g_symbols[3];
66
67 #define STATEFLAG g_symbols[0]
68 /*
69 Integer child. 0= algebraic, 1 = state, 2 = derivative, 3 = 2nd deriv etc
70 independent variable is -1.
71 */
72 #define INTEG_OTHER_VAR -1L
73 #define INTEG_ALGEBRAIC_VAR 0L
74 #define INTEG_STATE_VAR 1L
75
76 #define STATEINDEX g_symbols[1]
77 /* Integer child. all variables with the same STATEINDEX value are taken to
78 * be derivatives of the same state variable. We really need a compiler
79 * that maintains this info by backpointers, but oh well until then.
80 */
81 #define OBSINDEX g_symbols[2]
82 /* Integer child. All variables with OBSINDEX !=0 will be sent to the
83 IntegratorOutputWriteObsFn allowing output to a file, graph, console, etc.
84 */
85
86
87 /** Temporary catcher of dynamic variable and observation variable data */
88 struct Integ_var_t {
89 long index;
90 long type;
91 struct Integ_var_t *derivative;
92 struct Integ_var_t *derivative_of;
93 struct var_variable *i;
94 int varindx; /**< index into slv_get_master_vars_list, or -1 if not there */
95 int isstate;
96 };
97
98 /*------------------------------------------------------------------------------
99 forward declarations
100 */
101
102 /* abstractions of setup/teardown procedures for the specific solvers */
103 void integrator_create_engine(IntegratorSystem *sys);
104 void integrator_free_engine(IntegratorSystem *sys);
105
106 IntegratorAnalyseFn integrator_analyse_ode;
107 IntegratorAnalyseFn integrator_analyse_dae;
108
109 typedef void (IntegratorVarVisitorFn)(IntegratorSystem *sys, struct var_variable *var, const int *varindx);
110 static void integrator_visit_system_vars(IntegratorSystem *sys,IntegratorVarVisitorFn *visitor);
111 IntegratorVarVisitorFn integrator_ode_classify_var;
112 IntegratorVarVisitorFn integrator_dae_classify_var;
113 IntegratorVarVisitorFn integrator_classify_indep_var;
114 IntegratorVarVisitorFn integrator_dae_show_var;
115
116 static int integrator_sort_obs_vars(IntegratorSystem *sys);
117 static void integrator_print_var_stats(IntegratorSystem *sys);
118 static int integrator_check_indep_var(IntegratorSystem *sys);
119
120 static int Integ_CmpDynVars(struct Integ_var_t *v1, struct Integ_var_t *v2);
121 static int Integ_CmpObs(struct Integ_var_t *v1, struct Integ_var_t *v2);
122 static void Integ_SetObsId(struct var_variable *v, long oindex);
123
124 static long DynamicVarInfo(struct var_variable *v,long *vindex);
125 static struct var_variable *ObservationVar(struct var_variable *v, long *oindex);
126 static void IntegInitSymbols(void);
127
128 /*------------------------------------------------------------------------------
129 INSTANTIATION AND DESTRUCTION
130 */
131
132 /**
133 Create a new IntegratorSystem and assign a slv_system_t to it.
134 */
135 IntegratorSystem *integrator_new(slv_system_t slvsys, struct Instance *inst){
136 IntegratorSystem *sys;
137
138 if (slvsys == NULL) {
139 ERROR_REPORTER_HERE(ASC_PROG_ERR,"slvsys is NULL!");
140 return NULL;
141 }
142
143 sys = ASC_NEW_CLEAR(IntegratorSystem);
144 sys->system = slvsys;
145 sys->instance = inst;
146
147 sys->engine = INTEG_UNKNOWN;
148 sys->internals = NULL;
149
150 sys->states = NULL; sys->derivs = NULL;
151 sys->dynvars = NULL; sys->obslist = NULL; sys->indepvars = NULL;
152
153 sys->y_id = NULL;
154 sys->obs_id = NULL;
155 sys->y = NULL;
156 sys->ydot = NULL;
157 sys->obs = NULL;
158 return sys;
159 }
160
161 /**
162 Carefully trash any data in the IntegratorSystem that we own,
163 then destroy the IntegratorSystem struct.
164
165 Note that the integrator doesn't own the samplelist.
166
167 @param sys will be destroyed and set to NULL.
168 */
169 void integrator_free(IntegratorSystem *sys){
170 if(sys==NULL)return;
171
172 integrator_free_engine(sys);
173
174 if(sys->states != NULL)gl_destroy(sys->states);
175 if(sys->derivs != NULL)gl_destroy(sys->derivs);
176
177 if(sys->dynvars != NULL)gl_free_and_destroy(sys->dynvars); /* we own the objects in dynvars */
178 if(sys->obslist != NULL)gl_free_and_destroy(sys->obslist); /* and obslist */
179 if (sys->indepvars != NULL)gl_free_and_destroy(sys->indepvars); /* and indepvars */
180
181 /* if(sys->y_id != NULL)ASC_FREE(sys->y_id); */
182 if(sys->obs_id != NULL)ASC_FREE(sys->obs_id);
183
184 if(sys->y != NULL && !sys->ycount)ASC_FREE(sys->y);
185 if(sys->ydot != NULL && !sys->ydotcount)ASC_FREE(sys->ydot);
186 if(sys->obs != NULL && !sys->obscount)ASC_FREE(sys->obs);
187
188 slv_destroy_parms(&(sys->params));
189
190 ASC_FREE(sys);
191 CONSOLE_DEBUG("Destroyed IntegratorSystem");
192 sys=NULL;
193 }
194
195 /**
196 Utility function to retreive pointers to the symbols we'll be looking for
197 in the instance hierarchy.
198 */
199 static void IntegInitSymbols(void){
200 STATEFLAG = AddSymbol("ode_type");
201 STATEINDEX = AddSymbol("ode_id");
202 OBSINDEX = AddSymbol("obs_id");
203 }
204
205 /*------------------------------------------------------------------------------
206 INTEGRATOR ENGINE
207 */
208
209 /**
210 At present, integrators are all present at compile-time so this is a static
211 list; we just return the list.
212 */
213 const IntegratorLookup *integrator_get_engines(){
214 #define S ,
215 #define I(N,P) {INTEG_##N, P.name}
216 static const IntegratorLookup list[] = {
217 INTEG_LIST
218 ,{INTEG_UNKNOWN,NULL}
219 };
220 #undef S
221 #undef I
222 return list;
223 }
224
225 /* return 0 on success */
226 int integrator_set_engine(IntegratorSystem *sys, IntegratorEngine engine){
227
228 CONSOLE_DEBUG("Setting engine...");
229
230 /* verify integrator type ok. always passes for nonNULL inst. */
231 if(engine==INTEG_UNKNOWN){
232 ERROR_REPORTER_NOLINE(ASC_USER_ERROR
233 ,"Integrator has not been specified (or is unknown)."
234 );
235 return 1;
236 }else{
237 /** @TODO other engine-specific tests */
238 }
239
240 if(engine==sys->engine){
241 return 0;
242 }
243 if(sys->engine!=INTEG_UNKNOWN){
244 integrator_free_engine(sys);
245 }
246 sys->engine = engine;
247 switch(sys->engine){
248 #ifdef ASC_WITH_IDA
249 case INTEG_IDA: sys->internals = &integrator_ida_internals; break;
250 #endif
251 case INTEG_LSODE: sys->internals = &integrator_lsode_internals; break;
252 case INTEG_AWW: sys->internals = &integrator_aww_internals; break;
253 default:
254 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unknown integrator engine");
255 sys->internals = NULL;
256 return 1;
257 };
258
259 asc_assert(sys->internals);
260 integrator_create_engine(sys);
261 return 0;
262 }
263
264 IntegratorEngine integrator_get_engine(const IntegratorSystem *sys){
265 return sys->engine;
266 }
267
268 /**
269 Free any engine-specific data that was required for the solution of
270 this system. Note that this data is pointed to by sys->enginedata.
271 */
272 void integrator_free_engine(IntegratorSystem *sys){
273 if(sys->engine==INTEG_UNKNOWN)return;
274 if(sys->enginedata){
275 if(sys->internals){
276 (sys->internals->freefn)(sys->enginedata);
277 sys->enginedata=NULL;
278 }else{
279 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to free engine data: no sys->internals");
280 }
281 }
282 }
283
284 /**
285 Create enginedata memory if required for this solver. This doesn't include
286 allocating computation space, since we assume that this stage all we know
287 is that we want to use a specified integrator engine, not the full details
288 of the problem at hand. Allocating space inside enginedata should be done
289 during the solve stage (and freed inside integrator_free_engine)
290 */
291 void integrator_create_engine(IntegratorSystem *sys){
292 asc_assert(sys->engine!=INTEG_UNKNOWN);
293 asc_assert(sys->internals);
294 asc_assert(sys->enginedata==NULL);
295 (sys->internals->createfn)(sys);
296 }
297
298 /*------------------------------------------------------------------------------
299 PARAMETERS
300 */
301
302 /**
303 Reset the parameters in this IntegratorSystem to the default ones for this
304 Integrator.
305
306 @return 0 on success, 1 on error
307 */
308 int integrator_params_default(IntegratorSystem *sys){
309 asc_assert(sys->engine!=INTEG_UNKNOWN);
310 asc_assert(sys->internals);
311 return (sys->internals->paramsdefaultfn)(sys);
312 }
313
314 int integrator_params_get(const IntegratorSystem *sys, slv_parameters_t *parameters){
315 asc_assert(sys!=NULL);
316 asc_assert(sys->params.num_parms > 0);
317 memcpy(parameters,&(sys->params),sizeof(slv_parameters_t));
318 return 0;
319 }
320
321 int integrator_params_set(IntegratorSystem *sys, const slv_parameters_t *parameters){
322 asc_assert(sys!=NULL);
323 asc_assert(parameters!=NULL);
324 memcpy(&(sys->params),parameters,sizeof(slv_parameters_t));
325 return 0;
326 }
327
328 /*------------------------------------------------------------------------------
329 ANALYSIS
330
331 Provide two modes in order to provide analysis suitable for solution of both
332 ODEs (the previous technique) and DAEs (new code). These share a common
333 "visit" method that needs to eventually be integrated with the code in
334 <solver/analyze.c>. For the moment, we're just hacking in to the compiler.
335 */
336
337 /**
338 Locate the independent variable. For the purpose of GUI design, this needs
339 to work independent of the integration engine being used.
340 */
341 int integrator_find_indep_var(IntegratorSystem *sys){
342 int result = 0;
343
344 if(sys->x != NULL){
345 CONSOLE_DEBUG("sys->x already set");
346 return 1;
347 }
348 assert(sys->indepvars==NULL);
349 sys->indepvars = gl_create(10L);
350
351 IntegInitSymbols();
352
353 /* CONSOLE_DEBUG("About to visit..."); */
354 integrator_visit_system_vars(sys,&integrator_classify_indep_var);
355
356 /* CONSOLE_DEBUG("..."); */
357
358 result = integrator_check_indep_var(sys);
359 gl_free_and_destroy(sys->indepvars);
360 sys->indepvars = NULL;
361
362 /* ERROR_REPORTER_HERE(ASC_PROG_NOTE,"Returning result %d",result); */
363
364 return result;
365 }
366
367 /**
368 Analyse the system, either as DAE or as an ODE system, depending on the
369 solver engine selected.
370
371 @return 1 on success
372 */
373 int integrator_analyse(IntegratorSystem *sys){
374 CONSOLE_DEBUG("Analysing integration system...");
375 asc_assert(sys);
376 if(sys->engine==INTEG_UNKNOWN){
377 ERROR_REPORTER_HERE(ASC_PROG_ERR,"No engine selected: can't analyse");
378 return 0;
379 }
380 asc_assert(sys->engine!=INTEG_UNKNOWN);
381 asc_assert(sys->internals);
382 return (sys->internals->analysefn)(sys);
383 }
384
385 /**
386 To analyse a DAE we need to identify *ALL* variables in the system
387 Except for the highest-level derivatives of any present?
388 We also need to identify the independent variable (just one).
389
390 @TODO implement Pantelides algorithm in here?
391 @TODO prevent re-analysis without clearing out the data structures?
392 @return 1 on success
393 */
394 int integrator_analyse_dae(IntegratorSystem *sys){
395 struct Integ_var_t *info, *prev;
396 #ifdef ANALYSE_DEBUG
397 char *varname, *derivname;
398 #endif
399 int i;
400 int numstates;
401 int numy, nrels;
402 int yindex;
403 int maxderiv;
404
405 CONSOLE_DEBUG("Starting DAE analysis");
406 IntegInitSymbols();
407
408 assert(sys->indepvars==NULL);
409
410 sys->indepvars = gl_create(10L); /* t var info */
411 sys->dynvars = gl_create(200L); /* y and ydot var info */
412 sys->obslist = gl_create(100L); /* obs info */
413
414 if(sys->indepvars==NULL
415 || sys->dynvars==NULL
416 || sys->obslist==NULL
417 ){
418 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Insufficient memory");
419 return 0;
420 }
421
422 integrator_visit_system_vars(sys,&integrator_dae_classify_var);
423
424 #ifdef ANALYSE_DEBUG
425 CONSOLE_DEBUG("Found %lu observation variables:",gl_length(sys->obslist));
426 for(i=1; i<=gl_length(sys->obslist); ++i){
427 info = (struct Integ_var_t *)gl_fetch(sys->obslist, i);
428 varname = var_make_name(sys->system,info->i);
429 CONSOLE_DEBUG("observation[%d] = \"%s\"",i,varname);
430 ASC_FREE(varname);
431 }
432 #endif
433
434 /* CONSOLE_DEBUG("Checking found vars..."); */
435 if(gl_length(sys->dynvars)==0){
436 ERROR_REPORTER_HERE(ASC_USER_ERROR,"No solver_var vars found to integrate (check 'ode_type'?).");
437 return 0;
438 }
439
440 CONSOLE_DEBUG("Found %lu vars.", gl_length(sys->dynvars));
441
442 maxderiv = 0;
443 numstates = 0;
444 for(i=1; i<=gl_length(sys->dynvars); ++i){
445 info = (struct Integ_var_t *)gl_fetch(sys->dynvars, i);
446 if(info->type==1 || info->type==0)numstates++;
447 if(maxderiv < info->type - 1)maxderiv = info->type - 1;
448 /* varname = var_make_name(sys->system,info->i);
449 CONSOLE_DEBUG("var[%d] = \"%s\": ode_index = %ld",i,varname,info->type);
450 ASC_FREE(varname); */
451 }
452 if(maxderiv == 0){
453 ERROR_REPORTER_HERE(ASC_USER_ERROR,"No derivatives found (check 'ode_type' values for your vars).");
454 return 0;
455 }
456 if(maxderiv > 1){
457 ERROR_REPORTER_HERE(ASC_USER_ERROR,"Higher-order derivatives found. You must provide a reduced order formulation for your model.");
458 return 0;
459 }
460
461 if(!integrator_check_indep_var(sys))return 0;
462
463 gl_sort(sys->dynvars,(CmpFunc)Integ_CmpDynVars);
464 /* !!! NOTE THAT dynvars IS NOW REARRANGED !!! *sigh* bug hunting(!) */
465
466 #ifdef ANALYSE_DEBUG
467 CONSOLE_DEBUG("Variables rearranged to increasing ODE ID & TYPE (varindx = matrix order)");
468 for(i=1; i<=gl_length(sys->dynvars); ++i){
469 info = (struct Integ_var_t *)gl_fetch(sys->dynvars, i);
470 varname = var_make_name(sys->system,info->i);
471 CONSOLE_DEBUG("var[%d] = \"%s\": ode_type = %ld (varindx = %d)",i-1,varname,info->type,info->varindx);
472 ASC_FREE(varname);
473 }
474 #endif
475
476 /* link up variables with their derivatives */
477 prev = NULL;
478 for(i=1; i<=gl_length(sys->dynvars); ++i){ /* why does gl_list index with base 1??? */
479 info = (struct Integ_var_t *)gl_fetch(sys->dynvars, i);
480
481 if(info->type == INTEG_STATE_VAR || info->type == INTEG_ALGEBRAIC_VAR){
482 #ifdef ANALYSE_DEBUG
483 varname = var_make_name(sys->system,info->i);
484 CONSOLE_DEBUG("Var \"%s\" is an algebraic variable",varname);
485 ASC_FREE(varname);
486 #endif
487 info->type = INTEG_STATE_VAR;
488 info->derivative_of = NULL;
489 }else{
490 if(prev==NULL || info->index != prev->index){
491 /* derivative, but without undifferentiated var present in model */
492 #ifdef ANALYSE_DEBUG
493 varname = var_make_name(sys->system,info->i);
494 ERROR_REPORTER_HERE(ASC_USER_ERROR,"Derivative %d of \"%s\" is present without its un-differentiated equivalent"
495 , info->type-1
496 , varname
497 );
498 ASC_FREE(varname);
499 #endif
500 return 0;
501 }else if(info->type != prev->type + 1){
502 /* derivative, but missing the next-lower-order derivative */
503 #ifdef ANALYSE_DEBUG
504 derivname = var_make_name(sys->system,info->i);
505 varname = var_make_name(sys->system,prev->i);
506 ERROR_REPORTER_HERE(ASC_USER_ERROR
507 ,"Looking at \"%s\", expected a derivative (order %d) of \"%s\"."
508 ,varname
509 ,prev->type+1
510 ,derivname
511 );
512 ASC_FREE(varname);
513 ASC_FREE(derivname);
514 #endif
515 return 0;
516 }else{
517 /* variable with derivative */
518 #ifdef ANALYSE_DEBUG
519 varname = var_make_name(sys->system,prev->i);
520 derivname = var_make_name(sys->system,info->i);
521 CONSOLE_DEBUG("Var \"%s\" is the derivative of \"%s\"",derivname,varname);
522 ASC_FREE(varname);
523 ASC_FREE(derivname);
524 #endif
525 info->derivative_of = prev;
526 }
527 }
528 prev = info;
529 }
530
531 /* record which vars have derivatives and which don't, and count 'states' */
532 numy = 0;
533 for(i=1; i<=gl_length(sys->dynvars); ++i){
534 info = (struct Integ_var_t *)gl_fetch(sys->dynvars, i);
535 if(info->derivative_of){
536 info->derivative_of->derivative = info;
537 }else{
538 numy++;
539 }
540 }
541
542 /* allocate storage for the 'y' and 'ydot' arrays */
543 sys->y = ASC_NEW_ARRAY(struct var_variable *,numy);
544 sys->ydot = ASC_NEW_ARRAY_CLEAR(struct var_variable *,numy);
545 sys->y_id = ASC_NEW_ARRAY(long, slv_get_num_master_vars(sys->system));
546
547 for(i=0; i<numy; ++i){
548 asc_assert(sys->ydot[i]==NULL);
549 }
550
551 for(i=0; i<numy; ++i){
552 sys->y_id[i] = 9999999L;
553 }
554
555 /* now add variables and their derivatives to 'ydot' and 'y' */
556 yindex = 0;
557
558 #ifdef ANALYSE_DEBUG
559 CONSOLE_DEBUG("VARS IN THEIR MATRIX ORDER");
560 #endif
561 for(i=1; i<=gl_length(sys->dynvars); ++i){
562 info = (struct Integ_var_t *)gl_fetch(sys->dynvars, i);
563 if(info->derivative_of)continue;
564 if(info->derivative){
565 sys->y[yindex] = info->i;
566 assert(info->derivative);
567 sys->ydot[yindex] = info->derivative->i;
568 if(info->varindx >= 0){
569 ASC_ASSERT_RANGE(yindex, -1e7L, 1e7L);
570 sys->y_id[info->varindx] = yindex;
571 #ifdef ANALYSE_DEBUG
572 CONSOLE_DEBUG("y_id[%d] = %d",info->varindx,yindex);
573 #endif
574 }
575 if(info->derivative->varindx >= 0){
576 sys->y_id[info->derivative->varindx] = -1-yindex;
577 ASC_ASSERT_RANGE(-1-yindex,-numy,0);
578 #ifdef ANALYSE_DEBUG
579 CONSOLE_DEBUG("y_id[%d] = %d",info->derivative->varindx,-1-yindex);
580 #endif
581 }
582 }else{
583 sys->y[yindex] = info ->i;
584 sys->ydot[yindex] = NULL;
585 if(info->varindx >= 0){
586 sys->y_id[info->varindx] = yindex;
587 ASC_ASSERT_RANGE(yindex,0,numy);
588 #ifdef ANALYSE_DEBUG
589 CONSOLE_DEBUG("y_id[%d] = %d",info->varindx,yindex);
590 #endif
591 }
592 }
593 yindex++;
594 }
595
596 nrels = slv_get_num_solvers_rels(sys->system);
597 if(numy != nrels){
598 ERROR_REPORTER_HERE(ASC_USER_ERROR
599 ,"System is not square: solver has %d rels, found %d system states"
600 ,nrels, numy
601 );
602 return 0;
603 }
604
605 CONSOLE_DEBUG("THERE ARE %d VARIABLES IN THE INTEGRATION SYSTEM",numy);
606
607 sys->n_y = numy;
608
609 if(!integrator_sort_obs_vars(sys))return 0;
610
611 #ifdef ANALYSE_DEBUG
612 CONSOLE_DEBUG("RESULTS OF ANALYSIS");
613 fprintf(stderr,"index\ty\tydot\n");
614 fprintf(stderr,"-----\t-----\t-----\n");
615 for(i=0;i<numy;++i){
616 varname = var_make_name(sys->system, sys->y[i]);
617 fprintf(stderr,"%d\t%s\t",i,varname);
618 if(sys->ydot[i]){
619 ASC_FREE(varname);
620 varname = var_make_name(sys->system, sys->ydot[i]);
621 fprintf(stderr,"%s\n",varname);
622 ASC_FREE(varname);
623 }else{
624 fprintf(stderr,"diff(%s)\n",varname);
625 ASC_FREE(varname);
626 }
627 }
628
629 CONSOLE_DEBUG("CORRESPONDENCE OF SOLVER VARS TO INTEGRATOR VARS");
630 fprintf(stderr,"index\tname\ty_id\ty\tydot\n");
631 fprintf(stderr,"-----\t-----\t-----\t-----\t-----\n");
632 integrator_visit_system_vars(sys,integrator_dae_show_var);
633 #endif
634
635 return 1;
636 }
637
638 void integrator_dae_show_var(IntegratorSystem *sys
639 , struct var_variable *var, const int *varindx
640 ){
641 char *varname;
642 long y_id;
643 varname = var_make_name(sys->system, var);
644 if(varindx==NULL){
645 fprintf(stderr,".\t%s\n",varname);
646 ASC_FREE(varname);
647 return;
648 }
649 y_id = sys->y_id[*varindx];
650
651 fprintf(stderr,"%d\t%s\t%ld", *varindx, varname,y_id);
652 ASC_FREE(varname);
653 if(y_id >= 0){
654 fprintf(stderr,"\ty[%ld]\t.\n",y_id);
655 }else{
656 fprintf(stderr,"\t.\tydot[%ld]\n",-y_id-1);
657 }
658
659 ASC_ASSERT_LT(*varindx,1e7L);
660 ASC_ASSERT_LT(y_id, 9999999L);
661 ASC_ASSERT_LT(-9999999L, y_id);
662 }
663
664 void integrator_visit_system_vars(IntegratorSystem *sys,IntegratorVarVisitorFn *visitfn){
665 struct var_variable **vlist;
666 int i, vlen;
667
668 /* visit all the slv_system_t master var lists to collect vars */
669 /* find the vars mostly in this one */
670 vlist = slv_get_solvers_var_list(sys->system);
671 vlen = slv_get_num_solvers_vars(sys->system);
672 for (i=0;i<vlen;i++) {
673 (*visitfn)(sys, vlist[i], &i);
674 }
675
676 /*
677 CONSOLE_DEBUG("Checked %d vars",vlen);
678 integrator_print_var_stats(sys);
679 */
680
681 /* probably nothing here, but gotta check. */
682 vlist = slv_get_master_par_list(sys->system);
683 vlen = slv_get_num_master_pars(sys->system);
684 for (i=0;i<vlen;i++) {
685 (*visitfn)(sys, vlist[i], NULL);
686 }
687
688 /*
689 CONSOLE_DEBUG("Checked %d pars",vlen);
690 integrator_print_var_stats(sys);
691 */
692
693 /* might find t here */
694 vlist = slv_get_master_unattached_list(sys->system);
695 vlen = slv_get_num_master_unattached(sys->system);
696 for (i=0;i<vlen;i++) {
697 (*visitfn)(sys, vlist[i], NULL);
698 }
699
700 /* CONSOLE_DEBUG("Checked %d unattached",vlen); */
701 }
702 /**
703 @return 1 on success
704 */
705 int integrator_analyse_ode(IntegratorSystem *sys){
706 struct Integ_var_t *v1,*v2;
707 long half,i,len;
708 int happy=1;
709 char *varname1, *varname2;
710
711 CONSOLE_DEBUG("Starting ODE analysis");
712 IntegInitSymbols();
713
714 /* collect potential states and derivatives */
715 sys->indepvars = gl_create(10L); /* t var info */
716 sys->dynvars = gl_create(200L); /* y ydot var info */
717 sys->obslist = gl_create(100L); /* obs info */
718 if (sys->dynvars == NULL
719 || sys->obslist == NULL
720 || sys->indepvars == NULL
721 ){
722 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Insufficient memory.");
723 return 0;
724 }
725
726 sys->nstates = sys->nderivs = 0;
727
728 integrator_visit_system_vars(sys,&integrator_ode_classify_var);
729
730 integrator_print_var_stats(sys);
731
732 if(!integrator_check_indep_var(sys))return 0;
733
734 /* check sanity of state and var lists */
735
736 len = gl_length(sys->dynvars);
737 half = len/2;
738 CONSOLE_DEBUG("NUMBER OF DYNAMIC VARIABLES = %ld",half);
739
740 if (len % 2 || len == 0L || sys->nstates != sys->nderivs ) {
741 /* list length must be even for vars to pair off */
742 ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"n_y != n_ydot, or no dynamic vars found. Fix your indexing.");
743 return 0;
744 }
745 gl_sort(sys->dynvars,(CmpFunc)Integ_CmpDynVars);
746 if (gl_fetch(sys->dynvars,len)==NULL) {
747 ERROR_REPORTER_NOLINE(ASC_PROG_ERR,"Mysterious NULL found!");
748 return 0;
749 }
750 sys->states = gl_create(half); /* state vars Integ_var_t references */
751 sys->derivs = gl_create(half); /* derivative var atoms */
752 for (i=1;i < len; i+=2) {
753 v1 = (struct Integ_var_t *)gl_fetch(sys->dynvars,i);
754 v2 = (struct Integ_var_t *)gl_fetch(sys->dynvars,i+1);
755 if (v1->type!=1 || v2 ->type !=2 || v1->index != v2->index) {
756 varname1 = var_make_name(sys->system,v1->i);
757 varname2 = var_make_name(sys->system,v2->i);
758
759 ERROR_REPORTER_HERE(ASC_USER_ERROR,"Mistyped or misindexed dynamic variables: %s (%s = %ld,%s = %ld) and %s (%s = %ld,%s = %ld).",
760 varname1, SCP(STATEFLAG),v1->type,SCP(STATEINDEX),v1->index,
761 varname2, SCP(STATEFLAG),v2->type,SCP(STATEINDEX),v2->index
762 );
763 ASC_FREE(varname1);
764 ASC_FREE(varname2);
765 happy=0;
766 break;
767 } else {
768 gl_append_ptr(sys->states,(POINTER)v1);
769 gl_append_ptr(sys->derivs,(POINTER)v2->i);
770 }
771 }
772 if (!happy) {
773 return 0;
774 }
775 sys->n_y = half;
776 sys->y = ASC_NEW_ARRAY(struct var_variable *, half);
777 sys->y_id = ASC_NEW_ARRAY(long, half);
778 sys->ydot = ASC_NEW_ARRAY(struct var_variable *, half);
779 if (sys->y==NULL || sys->ydot==NULL || sys->y_id==NULL) {
780 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Insufficient memory.");
781 return 0;
782 }
783 for (i = 1; i <= half; i++) {
784 v1 = (struct Integ_var_t *)gl_fetch(sys->states,i);
785 sys->y[i-1] = v1->i;
786 sys->y_id[i-1] = v1->index;
787 sys->ydot[i-1] = (struct var_variable *)gl_fetch(sys->derivs,i);
788 }
789
790 if(!integrator_sort_obs_vars(sys))return 0;
791
792 /* don't need the gl_lists now that we have arrays for everyone */
793 gl_destroy(sys->states);
794 gl_destroy(sys->derivs);
795 gl_free_and_destroy(sys->indepvars); /* we own the objects in indepvars */
796 gl_free_and_destroy(sys->dynvars); /* we own the objects in dynvars */
797 gl_free_and_destroy(sys->obslist); /* and obslist */
798 sys->states = NULL;
799 sys->derivs = NULL;
800 sys->indepvars = NULL;
801 sys->dynvars = NULL;
802 sys->obslist = NULL;
803
804 /* analysis completed OK */
805 return 1;
806 }
807
808 /**
809 Reindex observations. Sort if the user mostly numbered. Take natural order
810 if user just booleaned.
811
812 @return 1 on success
813 */
814 static int integrator_sort_obs_vars(IntegratorSystem *sys){
815 int half, i, len = 0;
816 struct Integ_var_t *v2;
817
818 half = sys->n_y;
819 len = gl_length(sys->obslist);
820 /* we shouldn't be seeing NULL here ever except if malloc fail. */
821 if (len > 1L) {
822 half = ((struct Integ_var_t *)gl_fetch(sys->obslist,1))->index;
823 /* half != 0 now because we didn't collect 0 indexed vars */
824 for (i=2; i <= len; i++) {
825 if (half != ((struct Integ_var_t *)gl_fetch(sys->obslist,i))->index) {
826 /* change seen. sort and go on */
827 gl_sort(sys->obslist,(CmpFunc)Integ_CmpObs);
828 break;
829 }
830 }
831 }
832 for (i = half = 1; i <= len; i++) {
833 v2 = (struct Integ_var_t *)gl_fetch(sys->obslist,i);
834 if (v2==NULL) {
835 /* we shouldn't be seeing NULL here ever except if malloc fail. */
836 gl_delete(sys->obslist,i,0); /* should not be gl_delete(so,i,1) */
837 } else {
838 Integ_SetObsId(v2->i,half);
839 v2->index = half++;
840 }
841 }
842
843 /* obslist now uniquely indexed, no nulls */
844 /* make into arrays */
845 half = gl_length(sys->obslist);
846 sys->obs = ASC_NEW_ARRAY(struct var_variable *,half);
847 sys->obs_id = ASC_NEW_ARRAY(long, half);
848 if ( sys->obs==NULL || sys->obs_id==NULL) {
849 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Insufficient memory.");
850 return 0;
851 }
852 sys->n_obs = half;
853 for (i = 1; i <= half; i++) {
854 v2 = (struct Integ_var_t *)gl_fetch(sys->obslist,i);
855 sys->obs[i-1] = v2->i;
856 sys->obs_id[i-1] = v2->index;
857 }
858
859 return 1;
860 }
861
862 static void integrator_print_var_stats(IntegratorSystem *sys){
863 int v = gl_length(sys->dynvars);
864 int i = gl_length(sys->indepvars);
865 CONSOLE_DEBUG("Currently %d vars, %d indep",v,i);
866 }
867
868 /**
869 Check sanity of the independent variable.
870
871 @return 1 on success
872 */
873 static int integrator_check_indep_var(IntegratorSystem *sys){
874 int len, i;
875 struct Integ_var_t *info;
876 char *varname;
877
878 /* check the sanity of the independent variable */
879 len = gl_length(sys->indepvars);
880 if (!len) {
881 ERROR_REPORTER_HERE(ASC_PROG_ERR,"No independent variable found.");
882 return 0;
883 }
884 if (len > 1) {
885 ERROR_REPORTER_START_HERE(ASC_USER_ERROR);
886 FPRINTF(ASCERR,"Excess %ld independent variables found:",
887 len);
888 for(i=1; i <=len;i++) {
889 info = (struct Integ_var_t *)gl_fetch(sys->indepvars,i);
890 if(info==NULL)continue;
891
892 varname = var_make_name(sys->system,info->i);
893 FPRINTF(ASCERR," %s",varname);
894 ASC_FREE(varname);
895 }
896 FPRINTF(ASCERR , "\nSet the \"%s\" flag on all but one of these to %s >= 0.\n"
897 , SCP(STATEFLAG),SCP(STATEFLAG)
898 );
899 error_reporter_end_flush();
900 return 0;
901 }else{
902 info = (struct Integ_var_t *)gl_fetch(sys->indepvars,1);
903 sys->x = info->i;
904 }
905 return 1;
906 }
907
908 /*------------------------------------------------------------------------------
909 CLASSIFICATION OF VARIABLES (for ANALYSIS step)
910 */
911
912 #define INTEG_ADD_TO_LIST(info,TYPE,INDEX,VAR,VARINDX,LIST) \
913 info = ASC_NEW(struct Integ_var_t); \
914 if(info==NULL){ \
915 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Insufficient memory (INTEG_VAR_NEW)"); \
916 return; \
917 } \
918 info->type=TYPE; \
919 info->index=INDEX; \
920 info->i=VAR; \
921 info->derivative=NULL; \
922 info->derivative_of=NULL; \
923 if(VARINDX==NULL){ \
924 info->varindx = -1; \
925 }else{ \
926 info->varindx = *VARINDX; \
927 } \
928 gl_append_ptr(LIST,(void *)info); \
929 info = NULL
930
931 /**
932 In a DAE, it's either the (single) independent variable, or it's a
933 variable in the model.
934
935 I'm not sure what we should be doing with variables that are already
936 present as derivatives of other variables, I guess those ones need to be
937 removed from the list in a second pass?
938 */
939 void integrator_dae_classify_var(IntegratorSystem *sys
940 , struct var_variable *var, const int *varindx
941 ){
942 struct Integ_var_t *info;
943 long type,index;
944
945 /* filter for recognition of solver_vars */
946 var_filter_t vfilt;
947 vfilt.matchbits = VAR_SVAR;
948 vfilt.matchvalue = VAR_SVAR;
949
950 assert(var != NULL && var_instance(var)!=NULL );
951
952 if( var_apply_filter(var,&vfilt) ) {
953 if(!var_active(var)){
954 CONSOLE_DEBUG("VARIABLE IS NOT ACTIVE");
955 return;
956 }
957
958 /* only non-fixed variables are accepted */
959 if(!var_fixed(var)){
960 /* get the ode_type and ode_id of this solver_var */
961 type = DynamicVarInfo(var,&index);
962
963 if(type==INTEG_OTHER_VAR){
964 /* if the var's type is -1, it's independent */
965 INTEG_ADD_TO_LIST(info,INTEG_OTHER_VAR,0,var,varindx,sys->indepvars);
966 }else{
967 if(type < 0)type=0;
968 /* any other type of var is in the DAE system, at least for now */
969 INTEG_ADD_TO_LIST(info,type,index,var,varindx,sys->dynvars);
970 }
971 }
972 #if 0
973 else{
974 /* fixed variable, only include it if ode_type == 1 */
975 type = DynamicVarInfo(var,&index);
976 if(type==INTEG_STATE_VAR){
977 INTEG_ADD_TO_LIST(info,type,index,var,varindx,sys->dynvars);
978 }
979 }
980 #endif
981
982 /* if the var's obs_id > 0, add it to the observation list */
983 if(ObservationVar(var,&index) != NULL && index > 0L) {
984 INTEG_ADD_TO_LIST(info,type,index,var,varindx,sys->obslist);
985 }
986 }
987 }
988
989 /**
990 Inspect a specific variable and work out what type it is (what 'ode_type' it
991 has) and what other variable(s) it corresponds to (ie dydt corresponds to
992 y as a derivative).
993
994 @TODO add ability to create new variables for 'missing' derivative vars?
995 */
996 void integrator_ode_classify_var(IntegratorSystem *sys, struct var_variable *var
997 , const int *varindx
998 ){
999 struct Integ_var_t *info;
1000 long type,index;
1001
1002 var_filter_t vfilt;
1003 vfilt.matchbits = VAR_SVAR;
1004 vfilt.matchvalue = VAR_SVAR;
1005
1006 assert(var != NULL && var_instance(var)!=NULL );
1007
1008 if( var_apply_filter(var,&vfilt) ) {
1009 /* it's a solver var: what type of variable? */
1010 type = DynamicVarInfo(var,&index);
1011
1012 if(type==INTEG_ALGEBRAIC_VAR){
1013 /* no action required */
1014 }else if(type==INTEG_OTHER_VAR){
1015 /* i.e. independent var */
1016 INTEG_ADD_TO_LIST(info,type,index,var,varindx,sys->indepvars);
1017 }else if(type>=INTEG_STATE_VAR){
1018 INTEG_ADD_TO_LIST(info,type,index,var,varindx,sys->dynvars);
1019 if(type == 1){
1020 sys->nstates++;
1021 }else if(type == 2){ /* what about higher-order derivatives? -- JP */
1022 sys->nderivs++;
1023 }else{
1024 ERROR_REPORTER_HERE(ASC_USER_WARNING,"Higher-order (>=2) derivatives are not supported in ODEs.");
1025 } }
1026
1027 if(ObservationVar(var,&index) != NULL && index > 0L) {
1028 INTEG_ADD_TO_LIST(info,0L,index,var,varindx,sys->obslist);
1029 }
1030 }
1031 }
1032
1033 /**
1034 Look at a variable and determine if it's the independent variable or not.
1035 This is just for the purpose of the integrator_find_indep_var function,
1036 which is a utility function provided for use by the GUI.
1037 */
1038 void integrator_classify_indep_var(IntegratorSystem *sys
1039 , struct var_variable *var, const int *varindx
1040 ){
1041 struct Integ_var_t *info;
1042 long type,index;
1043
1044 var_filter_t vfilt;
1045 vfilt.matchbits = VAR_SVAR;
1046 vfilt.matchvalue = VAR_SVAR;
1047
1048 /* CONSOLE_DEBUG("..."); */
1049
1050 assert(var != NULL && var_instance(var)!=NULL );
1051
1052 if( var_apply_filter(var,&vfilt) ) {
1053 type = DynamicVarInfo(var,&index);
1054
1055 if(type==INTEG_OTHER_VAR){
1056 /* i.e. independent var */
1057 INTEG_ADD_TO_LIST(info,type,index,var,varindx,sys->indepvars);
1058 }
1059 }
1060 }
1061
1062 /**
1063 Look at a variable, and if it is an 'ODE variable' (it has a child instance
1064 named 'ode_type') return its type, which will be either:
1065 - INTEG_OTHER_VAR (if 'ode_type' is -1)
1066 - INTEG_ALGEBRAIC_VAR (if 'ode_type' is zero or any negative value < -1)
1067 - INTEG_STATE_VAR (if 'ode_type' is 1)
1068 - values 2, 3 or up, indicating derivatives (1st deriv=2, 2nd deriv=3, etc)
1069
1070 If the parameter 'index' is not null, the value of 'ode_id' will be stuffed
1071 there.
1072 */
1073 static long DynamicVarInfo(struct var_variable *v,long *index){
1074 struct Instance *c, *d, *i;
1075 i = var_instance(v);
1076 assert(i!=NULL);
1077 assert(STATEFLAG!=NULL);
1078 assert(STATEINDEX!=NULL);
1079 c = ChildByChar(i,STATEFLAG);
1080 d = ChildByChar(i,STATEINDEX);
1081 /* lazy evaluation is important in the following if */
1082 if(c == NULL
1083 || d == NULL
1084 || InstanceKind(c) != INTEGER_INST
1085 || InstanceKind(d) != INTEGER_INST
1086 || !AtomAssigned(c)
1087 || (!AtomAssigned(d) && GetIntegerAtomValue(c) != INTEG_OTHER_VAR)
1088 ){
1089 return INTEG_ALGEBRAIC_VAR;
1090 }
1091 if (index != NULL) {
1092 *index = GetIntegerAtomValue(d);
1093 }
1094 return GetIntegerAtomValue(c);
1095 }
1096
1097 /**
1098 Looks at the given variable checks if it is an 'observation variable'. This
1099 means that it has its 'obs_id' child instance set to a non-zero value.
1100
1101 If the variable is an observation variable, its index value ('obs_id') is
1102 stuff into *index (provided index!=NULL), and the pointer to the original
1103 instance is rtruend.
1104
1105 If it's not an observation variable, we return NULL and *index is untouched.
1106 */
1107 static struct var_variable *ObservationVar(struct var_variable *v, long *index){
1108 struct Instance *c,*i;
1109 i = var_instance(v);
1110 assert(i!=NULL);
1111 c = ChildByChar(i,OBSINDEX);
1112 if( c == NULL || InstanceKind(c) != INTEGER_INST || !AtomAssigned(c)) {
1113 return NULL;
1114 }
1115 if (index != NULL) {
1116 *index = GetIntegerAtomValue(c);
1117 }
1118 return v;
1119 }
1120
1121 /*------------------------------------------------------------------------------
1122 RUNNING THE SOLVER
1123 */
1124
1125 /*
1126 Make the call to the actual integrator we've selected, for the range of
1127 time values specified. The sys contains all the specifics.
1128
1129 Return 1 on success
1130 */
1131 int integrator_solve(IntegratorSystem *sys, long i0, long i1){
1132
1133 long nstep;
1134 unsigned long start_index=0, finish_index=0;
1135 assert(sys!=NULL);
1136
1137 assert(sys->internals);
1138 assert(sys->engine!=INTEG_UNKNOWN);
1139
1140 nstep = integrator_getnsamples(sys)-1;
1141 /* check for at least 2 steps and dimensionality of x vs steps here */
1142
1143 if (i0<0 || i1 <0) {
1144 /* dude, there's no way we're writing interactive stuff here... */
1145 ERROR_REPORTER_HERE(ASC_PROG_ERROR,"Console input of integration limits has been disabled!");
1146 return 0;
1147 } else {
1148 start_index=i0;
1149 finish_index =i1;
1150 if (start_index >= (unsigned long)nstep) {
1151 ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"Start point (=%lu) must be an index in the range [0,%li]."
1152 ,start_index,nstep
1153 );
1154 return 0;
1155 }
1156 if (finish_index > (unsigned long)nstep) {
1157 ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"End point (=%lu) must be an index in the range [0,%li]."
1158 ,finish_index,nstep
1159 );
1160 return 0;
1161 }
1162 }
1163
1164 if(finish_index <= start_index) {
1165 ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"End point comes before start point! (start=%lu, end=%lu)"
1166 ,start_index,finish_index
1167 );
1168 return 0;
1169 }
1170
1171 CONSOLE_DEBUG("RUNNING INTEGRATION...");
1172
1173 return (sys->internals->solvefn)(sys,start_index,finish_index);
1174 }
1175
1176 /*---------------------------------------------------------------
1177 HANDLING THE LIST OF TIMESTEMPS
1178 */
1179
1180 #define GETTER_AND_SETTER(TYPE,NAME) \
1181 void integrator_set_##NAME(IntegratorSystem *sys, TYPE val){ \
1182 sys->NAME=val; \
1183 } \
1184 TYPE integrator_get_##NAME(IntegratorSystem *sys){ \
1185 return sys->NAME; \
1186 }
1187
1188 GETTER_AND_SETTER(SampleList *,samples) /*;*/
1189 GETTER_AND_SETTER(double,maxstep) /*;*/
1190 GETTER_AND_SETTER(double,minstep) /*;*/
1191 GETTER_AND_SETTER(double,stepzero) /*;*/
1192 GETTER_AND_SETTER(int,maxsubsteps) /*;*/
1193 #undef GETTER_AND_SETTER
1194
1195 long integrator_getnsamples(IntegratorSystem *sys){
1196 assert(sys!=NULL);
1197 assert(sys->samples!=NULL);
1198 return samplelist_length(sys->samples);
1199 }
1200
1201 double integrator_getsample(IntegratorSystem *sys, long i){
1202 assert(sys!=NULL);
1203 assert(sys->samples!=NULL);
1204 return samplelist_get(sys->samples,i);
1205 }
1206
1207 void integrator_setsample(IntegratorSystem *sys, long i,double xi){
1208 assert(sys!=NULL);
1209 assert(sys->samples!=NULL);
1210 samplelist_set(sys->samples,i,xi);
1211 }
1212
1213 const dim_type *integrator_getsampledim(IntegratorSystem *sys){
1214 assert(sys!=NULL);
1215 assert(sys->samples!=NULL);
1216 return samplelist_dim(sys->samples);
1217 }
1218
1219 ASC_DLLSPEC(long) integrator_getcurrentstep(IntegratorSystem *sys){
1220 return sys->currentstep;
1221 }
1222
1223 /*------------------------------------------------------------------------------
1224 GET/SET VALUE OF THE INDEP VARIABLE
1225 */
1226
1227 /**
1228 Retrieve the value of the independent variable (time) from ASCEND
1229 and return it as a double.
1230 */
1231 double integrator_get_t(IntegratorSystem *sys){
1232 assert(sys->x!=NULL);
1233 return var_value(sys->x);
1234 }
1235
1236 /**
1237 Set the value of the independent variable (time) in ASCEND.
1238 */
1239 void integrator_set_t(IntegratorSystem *sys, double value){
1240 var_set_value(sys->x, value);
1241 /* CONSOLE_DEBUG("set_t = %g", value); */
1242 }
1243
1244 /*------------------------------------------------------------------------------
1245 PASSING DIFFERENTIAL VARIABLES AND THEIR DERIVATIVES TO/FROM THE SOLVER
1246 */
1247 /**
1248 Retrieve the current values of the derivatives of the y-variables
1249 and stick them in the/an array that the integrator will use.
1250
1251 If the pointer 'y' is NULL, the necessary space is allocated (and
1252 must be freed somewhere else).
1253 */
1254 double *integrator_get_y(IntegratorSystem *sys, double *y) {
1255 long i;
1256
1257 if (y==NULL) {
1258 y = ASC_NEW_ARRAY_CLEAR(double, sys->n_y+1);
1259 /* C y[0] <==> ascend d.y[1] <==> f77 y(1) */
1260 }
1261
1262 for (i=0; i< sys->n_y; i++) {
1263 assert(sys->y[i]!=NULL);
1264 y[i] = var_value(sys->y[i]);
1265 /* CONSOLE_DEBUG("ASCEND --> y[%ld] = %g", i+1, y[i]); */
1266 }
1267 return y;
1268 }
1269
1270 /**
1271 Take the values of the derivatives from the array that the integrator
1272 uses, and use them to update the values of the corresponding variables
1273 in ASCEND.
1274 */
1275 void integrator_set_y(IntegratorSystem *sys, double *y) {
1276 long i;
1277 #ifndef NDEBUG
1278 char *varname;
1279 #endif
1280
1281 for (i=0; i < sys->n_y; i++) {
1282 assert(sys->y[i]!=NULL);
1283 var_set_value(sys->y[i],y[i]);
1284 #ifndef NDEBUG
1285 varname = var_make_name(sys->system, sys->y[i]);
1286 /* CONSOLE_DEBUG("y[%ld] = \"%s\" = %g --> ASCEND", i+1, varname, y[i]); */
1287 ASC_FREE(varname);
1288 #endif
1289 }
1290 }
1291
1292 /**
1293 Send the values of the derivatives of the 'y' variables to the solver.
1294 Allocate space for an array if necessary.
1295
1296 Any element in sys->ydot that is NULL will be passed over (the value
1297 won't be modified in dydx).
1298 */
1299 double *integrator_get_ydot(IntegratorSystem *sys, double *dydx) {
1300 long i;
1301
1302 if (dydx==NULL) {
1303 dydx = ASC_NEW_ARRAY_CLEAR(double, sys->n_y+1);
1304 /* C dydx[0] <==> ascend d.dydx[1] <==> f77 ydot(1) */
1305 }
1306
1307 for (i=0; i < sys->n_y; i++) {
1308 if(sys->ydot[i]!=NULL){
1309 dydx[i] = var_value(sys->ydot[i]);
1310 }
1311 /* CONSOLE_DEBUG("ASCEND --> ydot[%ld] = %g", i+1, dydx[i]); */
1312 }
1313 return dydx;
1314 }
1315
1316 void integrator_set_ydot(IntegratorSystem *sys, double *dydx) {
1317 long i;
1318 #ifndef NDEBUG
1319 /* char *varname; */
1320 #endif
1321 for (i=0; i < sys->n_y; i++) {
1322 if(sys->ydot[i]!=NULL){
1323 var_set_value(sys->ydot[i],dydx[i]);
1324 #ifndef NDEBUG
1325 /* varname = var_make_name(sys->system, sys->ydot[i]);
1326 CONSOLE_DEBUG("ydot[%ld] = \"%s\" = %g --> ASCEND", i+1, varname, dydx[i]);
1327 ASC_FREE(varname); */
1328 #endif
1329 }
1330 #ifndef NDEBUG
1331 /*else{
1332 CONSOLE_DEBUG("ydot[%ld] = %g (internal)", i+1, dydx[i]);
1333 }*/
1334 #endif
1335 }
1336 }
1337
1338 /**
1339 Retrieve the values of 'ode_atol' properties of each of y-variables,
1340 for use in setting absolute error tolerances for the Integrator.
1341
1342 If the pointer 'atol' is NULL, the necessary space is allocated (and
1343 must be freed somewhere else).
1344 */
1345 double *integrator_get_atol(IntegratorSystem *sys, double *atol){
1346 long i;
1347 char *varname;
1348
1349 if (atol==NULL) {
1350 atol = ASC_NEW_ARRAY_CLEAR(double, sys->n_y);
1351 }
1352
1353 for (i=0; i< sys->n_y; i++) {
1354 assert(sys->y[i]!=NULL);
1355 atol[i] = var_odeatol(sys->y[i]);
1356 assert(atol[i]!=-1);
1357 varname = var_make_name(sys->system,sys->y[i]);
1358 CONSOLE_DEBUG("%s.ode_atol = %8.2e",varname,atol[i]);
1359 ASC_FREE(varname);
1360 }
1361 return atol;
1362 }
1363
1364 /*-------------------------------------------------------------
1365 RETRIEVING OBSERVATION DATA
1366 */
1367
1368 /**
1369 This function takes the inst in the solver and returns the vector of
1370 observation variables that are located in the submodel d.obs array.
1371 */
1372 double *integrator_get_observations(IntegratorSystem *sys, double *obsi) {
1373 long i;
1374
1375 if (obsi==NULL) {
1376 obsi = ASC_NEW_ARRAY_CLEAR(double, sys->n_obs+1);
1377 }
1378
1379 /* C obsi[0] <==> ascend d.obs[1] */
1380
1381 for (i=0; i < sys->n_obs; i++) {
1382 obsi[i] = var_value(sys->obs[i]);
1383 /* CONSOLE_DEBUG("*get_d_obs[%ld] = %g\n", i+1, obsi[i]); */
1384 }
1385 return obsi;
1386 }
1387
1388 struct var_variable *integrator_get_observed_var(IntegratorSystem *sys, const long i){
1389 assert(i>=0);
1390 assert(i<sys->n_obs);
1391 return sys->obs[i];
1392 }
1393
1394 /**
1395 @NOTE Although this shouldn't be required for implementation of solver
1396 engines, this is useful for GUI reporting of integration results.
1397 */
1398 struct var_variable *integrator_get_independent_var(IntegratorSystem *sys){
1399 return sys->x;
1400 }
1401
1402
1403 /*----------------------------------------------------
1404 Build an analytic jacobian for solving the state system
1405
1406 This necessarily ugly piece of code attempts to create a unique
1407 list of relations that explicitly contain the variables in the
1408 given input list. The utility of this information is that we know
1409 exactly which relations must be differentiated, to fill in the
1410 df/dy matrix. If the problem has very few derivative terms, this will
1411 be of great savings. If the problem arose from the discretization of
1412 a pde, then this will be not so useful. The decision wether to use
1413 this function or to simply differentiate the entire relations list
1414 must be done before calling this function.
1415
1416 Final Note: the callee owns the array, but not the array elements.
1417 */
1418 #define AVG_NUM_INCIDENT 4
1419
1420
1421 /**
1422 This function helps to arrange the observation variables in a sensible order.
1423 The 'obs_id' child instance of v, if present, is assigned the value of the
1424 given parameter 'index'.
1425 */
1426 static void Integ_SetObsId(struct var_variable *v, long index){
1427 struct Instance *c, *i;
1428 i = var_instance(v);
1429 assert(i!=NULL);
1430 c = ChildByChar(i,OBSINDEX);
1431 if( c == NULL || InstanceKind(c) != INTEGER_INST || !AtomAssigned(c)) {
1432 return;
1433 }
1434 SetIntegerAtomValue(c,index,0);
1435 }
1436
1437 /**
1438 Compares observation structs. NULLs should end up at far end.
1439 */
1440 static int Integ_CmpObs(struct Integ_var_t *v1, struct Integ_var_t *v2){
1441 if(v1 == NULL)return 1;
1442 if(v2 == NULL)return -1;
1443 if(v1->index > v2->index)return 1;
1444 if(v1->index == v2->index)return 0;
1445 return -1;
1446 }
1447
1448 /**
1449 Compares dynamic vars structs. NULLs should end up at far end.
1450 List should be sorted primarily by index and then by type, in order
1451 of increasing value of both.
1452 */
1453 static int Integ_CmpDynVars(struct Integ_var_t *v1, struct Integ_var_t *v2){
1454 if(v1 == NULL)return 1;
1455 if(v2 == NULL)return -1;
1456 if(v1->index > v2->index)return 1;
1457 if(v1->index != v2->index)return -1;
1458 if(v1->type > v2->type)return 1;
1459 return -1;
1460 }
1461 /*----------------------------
1462 Output handling to the GUI/interface.
1463 */
1464
1465 int integrator_set_reporter(IntegratorSystem *sys
1466 , IntegratorReporter *reporter
1467 ){
1468 assert(sys!=NULL);
1469 sys->reporter = reporter;
1470 /* ERROR_REPORTER_HERE(ASC_PROG_NOTE,"INTEGRATOR REPORTER HOOKS HAVE BEEN SET\n"); */
1471 return 1;
1472 }
1473
1474 int integrator_output_init(IntegratorSystem *sys){
1475 assert(sys!=NULL);
1476 assert(sys->reporter!=NULL);
1477 if(sys->reporter->init!=NULL){
1478 /* call the specified output function */
1479 return (*(sys->reporter->init))(sys);
1480 }
1481 ERROR_REPORTER_HERE(ASC_PROG_ERR,"No integrator reporter init method");
1482 return 1;
1483 }
1484
1485 int integrator_output_write(IntegratorSystem *sys){
1486 static int reported_already=0;
1487 assert(sys!=NULL);
1488 if(sys->reporter->write!=NULL){
1489 return (*(sys->reporter->write))(sys);
1490 }
1491 if(!reported_already){
1492 ERROR_REPORTER_HERE(ASC_PROG_ERR,"No integrator reporter write method (this message only shown once)");
1493 reported_already=1;
1494 }
1495 return 1;
1496 }
1497
1498 int integrator_output_write_obs(IntegratorSystem *sys){
1499 static int reported_already=0;
1500 assert(sys!=NULL);
1501 if(sys->reporter->write_obs!=NULL){
1502 return (*(sys->reporter->write_obs))(sys);
1503 }
1504 if(!reported_already){
1505 ERROR_REPORTER_HERE(ASC_PROG_ERR,"No integrator reporter write_obs method (this message only shown once)");
1506 reported_already=1;
1507 }
1508 return 1;
1509 }
1510
1511 int integrator_output_close(IntegratorSystem *sys){
1512 assert(sys!=NULL);
1513 if(sys->reporter->close!=NULL){
1514 return (*(sys->reporter->close))(sys);
1515 }
1516 ERROR_REPORTER_HERE(ASC_PROG_ERR,"No integrator reporter close method");
1517 return 1;
1518 }
1519
1520 /**
1521 Decode status codes from the integrator, and output them via FPRINTF.
1522 */
1523 int integrator_checkstatus(slv_status_t status) {
1524 if (status.converged) {
1525 return 1;
1526 }
1527 if (status.diverged) {
1528 FPRINTF(stderr, "The derivative system did not converge. Integration will terminate.");
1529 return 0;
1530 }
1531 if (status.inconsistent) {
1532 FPRINTF(stderr, "A numerically inconsistent state was discovered while "
1533 "calculating derivatives. Integration will terminate.");
1534 return 0;
1535 }
1536 if (status.time_limit_exceeded) {
1537 FPRINTF(stderr, "The time limit was exceeded while calculating "
1538 "derivatives. Integration will terminate.");
1539 return 0;
1540 }
1541 if (status.iteration_limit_exceeded) {
1542 FPRINTF(stderr, "The iteration limit was exceeded while calculating "
1543 "derivatives. Integration will terminate.");
1544 return 0;
1545 }
1546 if (status.panic) {
1547 FPRINTF(stderr, "The user patience limit was exceeded while "
1548 "calculating derivatives. Integration will terminate.");
1549 return 0;
1550 }
1551 return 0;
1552 }

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