1 |
/* ASCEND modelling environment |
/* ASCEND modelling environment |
2 |
Copyright (C) 2006-2011 Carnegie Mellon University |
Copyright (C) 2011 Carnegie Mellon University |
3 |
|
|
4 |
This program is free software; you can redistribute it and/or modify |
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) |
it under the terms of the GNU General Public License as published by |
7 |
any later version. |
the Free Software Foundation; either version 2, or (at your option) |
8 |
|
any later version. |
9 |
This program is distributed in the hope that it will be useful, |
|
10 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
This program is distributed in the hope that it will be useful, |
11 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 |
GNU General Public License for more details. |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 |
|
|
14 |
You should have received a copy of the GNU General Public License |
GNU General Public License for more details. |
15 |
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 |
*//** |
You should have received a copy of the GNU General Public License |
17 |
@file |
along with this program; if not, write to the Free Software |
18 |
Access to the IDA integrator for ASCEND. IDA is a DAE solver that comes |
Foundation, Inc., 59 Temple Place - Suite 330, |
19 |
as part of the GPL-licensed SUNDIALS solver package from LLNL. |
Boston, MA 02111-1307, USA. |
20 |
|
|
21 |
IDA provides the non-linear parts, as well as a number of pluggable linear |
*//* |
22 |
solvers: dense, banded and krylov types. |
by Harry, 4th June 2015 |
23 |
|
This file contains the ida_main_solve function. |
24 |
We also implement here an EXPERIMENTAL direct sparse linear solver for IDA |
This is the main solver function that calls other |
25 |
using the ASCEND linsolqr routines. |
sub-functions to complete integration. |
26 |
|
The current outline of this function is as below: |
27 |
@see http://www.llnl.gov/casc/sundials/ |
Boundary flags initialisation: all the condition equations are evaluated |
28 |
*//* |
and the values of corresponding boundary flags are set. |
29 |
by John Pye, May 2006 |
Boolean variables initialisation: the system is solved using the logical |
30 |
*/ |
equations solver in order to get the correct values of Boolean variables. |
31 |
|
Using the logical values found at step 2 the configuration of the system is chosen. |
32 |
#define _GNU_SOURCE |
Solver initialisation: setting solver parameters, allocating memory, et cetera. |
33 |
|
Loop through all time steps: |
34 |
#include "ida.h" |
If a root has not been found at the previous iteration then then reset all |
35 |
#include "idalinear.h" |
the root direction settings. It means that IDA will be searching for |
36 |
#include "idaanalyse.h" |
roots of condition equations in both directions. |
37 |
#include "idatypes.h" |
If a root has been found and we are far from desired output then make a very |
38 |
#include "idaprec.h" |
small time step and solve the system with IDA. Else make a usual time |
39 |
#include "idacalc.h" |
step which is set by the user. |
40 |
#include "idaio.h" |
If we have made a small time step then call a logical solver in order to update |
41 |
#include "idaboundary.h" |
the values of logical variables. |
42 |
|
Check if a root has been found at this step. If yes: |
43 |
#include <signal.h> |
Call a function for processing this boundary. |
44 |
#include <setjmp.h> |
If there have been any changes in the system, then reinitialise the solver. |
45 |
#include <fenv.h> |
Set the direction in which the roots of condition equations will be detected: |
46 |
#include <math.h> |
if a boundary has been crossed, then at the next step (which is an |
47 |
|
auxiliary small step) it will not be crossed in the same direction. |
48 |
#ifdef ASC_WITH_MMIO |
If we are still far from desired output then repeat steps. |
49 |
# include <mmio.h> |
|
50 |
#endif |
So basically we have two modes here: the first is when we make an ordinary time step, check all |
51 |
|
roots and if a boundary is found then call the function for processing it. The second mode is entered. |
52 |
#include <ascend/general/platform.h> |
*/ |
53 |
#include <ascend/utilities/error.h> |
#define _GNU_SOURCE |
54 |
#include <ascend/utilities/ascSignal.h> |
#include "ida.h" |
55 |
#include <ascend/general/panic.h> |
#include "idalinear.h" |
56 |
#include <ascend/compiler/instance_enum.h> |
#include "idaanalyse.h" |
57 |
|
#include "idatypes.h" |
58 |
|
#include "prepare_integrator.h" /* The list of includes needs cleaning up! Once basic files are written, |
59 |
#include <ascend/system/slv_client.h> |
#include "idaprec.h" all commonly called functions can be added in just one header file. |
60 |
#include <ascend/system/relman.h> |
#include "idacalc.h" Once that's done, these includes must be revisited*/ |
61 |
#include <ascend/system/block.h> |
#include "idaio.h" |
62 |
#include <ascend/system/slv_stdcalls.h> |
#include "idaboundary.h" |
63 |
#include <ascend/system/jacobian.h> |
#include <signal.h> /*Check if all these includes are necessary*/ |
64 |
#include <ascend/system/bndman.h> |
#include <setjmp.h> |
65 |
|
#include <fenv.h> |
66 |
#include <ascend/utilities/config.h> |
#include <math.h> |
67 |
#include <ascend/integrator/integrator.h> |
#ifdef ASC_WITH_MMIO |
68 |
|
# include <mmio.h> |
69 |
/* #define FEX_DEBUG */ |
#endif |
70 |
/* #define SOLVE_DEBUG */ |
#include <ascend/general/platform.h> |
71 |
#define IDA_BND_DEBUG |
#include <ascend/utilities/error.h> |
72 |
/* #define STATS_DEBUG */ |
#include <ascend/utilities/ascSignal.h> |
73 |
/* #define DESTROY_DEBUG */ |
#include <ascend/general/panic.h> |
74 |
|
#include <ascend/compiler/instance_enum.h> |
75 |
/*------------------------------------------------------------- |
#include <ascend/system/slv_client.h> |
76 |
SOLVER REGISTRATION |
#include <ascend/system/relman.h> |
77 |
*/ |
#include <ascend/system/block.h> |
78 |
|
#include <ascend/system/slv_stdcalls.h> |
79 |
/* |
#include <ascend/system/jacobian.h> |
80 |
The following functions are declared static because we don't want them |
#include <ascend/system/bndman.h> |
81 |
ever to be called directly from outside code. Instead, they are call via |
#include <ascend/utilities/config.h> |
82 |
function pointers passed through the ida_register function. |
#include <ascend/integrator/integrator.h> |
83 |
*/ |
|
84 |
static IntegratorCreateFn integrator_ida_create; |
|
85 |
static IntegratorParamsDefaultFn integrator_ida_params_default; |
int integrate_ida_solve(IntegratorSystem *integ, unsigned long start_index, unsigned long finish_index){ |
86 |
static IntegratorSolveFn integrator_ida_solve; |
void *ida_mem; |
87 |
static IntegratorFreeFn integrator_ida_free; |
int t_index; |
88 |
|
realtype t0, tout, tret, tol; |
89 |
/** |
realtype tmin; /** < The length of a small additional step made after an event is triggered */ |
90 |
This data structure contains pointers to the various functions that are |
N_Vector ypret, yret; |
91 |
exposed to libascend in order for ASCEND to drive this solver. |
IntegratorIdaData *enginedata; |
92 |
*/ |
int i, flag; |
93 |
static const IntegratorInternals integrator_ida_internals = { |
int *rootsfound; /** < IDA rootfinder reports root index in here */ |
94 |
integrator_ida_create, integrator_ida_params_default, |
int *rootdir = NULL; /** < Used to tell IDA to ignore doulve crossings */ |
95 |
integrator_ida_analyse, integrator_ida_solve, |
int *bnd_cond_states; /** < Record of boundary states so that IDA can tell LRSlv how to evaluate a boundary crossing */ |
96 |
integrator_ida_write_matrix, integrator_ida_debug, integrator_ida_free, |
int *bnd_not_set; |
97 |
INTEG_IDA, "IDA" }; |
int all_bnds_set = 1; |
98 |
|
int need_to_reconfigure; /** < Flag to indicate system rebuild after crossing */ |
99 |
/** |
int need_to_reinteg = 0; /** < Flag for when crossings happen on or very close to timesteps */ |
100 |
This function is accessed by libascend when loading this solver. The |
int skipping_output; /** < Flag to skip output to reporter */ |
101 |
function will register the integrator such that it can then be applied |
int qrslv_ind, lrslv_ind; |
102 |
to solving problems. |
int after_root = 0; |
103 |
*/ |
int peaw = 0; /*Flag returned by process_events_and_whens*/ |
104 |
extern ASC_EXPORT int ida_register(void) { |
int subpeaw = 0; /*Sub-flag used to control auxiliay integration steps*/ |
105 |
//CONSOLE_DEBUG("Registering IDA..."); |
int auxcount = 1; |
106 |
return integrator_register(&integrator_ida_internals); |
int first_run = 1; |
107 |
} |
#if defined(SOLVE_DEBUG) || defined(IDA_BND_DEBUG) |
108 |
|
char *relname; |
109 |
/*------------------------------------------------------------- |
//CONSOLE_DEBUG("STARTING IDA..."); |
110 |
FORWARD DECLS |
#endif |
111 |
*/ |
#ifdef SOLVE_DEBUG |
112 |
|
integrator_ida_debug(integ, stderr); |
113 |
typedef void ( IntegratorVarVisitorFn)(IntegratorSystem *integ, |
#endif |
114 |
struct var_variable *var, const int *varindx); |
/* create IDA object */ |
115 |
|
ida_mem = IDACreate(); |
116 |
/*static IntegratorVarVisitorFn integrator_dae_classify_var; |
/* solve the initial conditions, allocate memory, other stuff... */ |
117 |
static void integrator_visit_system_vars(IntegratorSystem *integ,IntegratorVarVisitorFn *visitor); |
ida_prepare_integrator(integ); /*Change call to new function*/ |
118 |
static void integrator_dae_show_var(IntegratorSystem *integ, struct var_variable *var, const int *varindx); */ |
/* store reference to list of relations (in enginedata) */ |
119 |
|
ida_load_rellist(integ); |
120 |
static int integrator_ida_stats(void *ida_mem, IntegratorIdaStats *s); |
|
121 |
|
tol = 0.0001*(samplelist_get(integ->samples, finish_index) |
122 |
/*------------------------------------------------------------- |
- samplelist_get(integ->samples, start_index)) |
123 |
SETUP/TEARDOWN ROUTINES |
/samplelist_length(integ->samples); |
124 |
*/ |
tmin = tol; |
125 |
|
rootdir = ASC_NEW_ARRAY_CLEAR(int,enginedata->nbnds); |
126 |
/** |
for(i = 0; i < enginedata->nbnds; i++){ |
127 |
Allocate memory inside the IntegratorIdaData structure at |
rootdir[i] = 0; |
128 |
the time when IDA is assigned to a particular system as its integrator. |
#ifdef IDA_BND_DEBUG |
129 |
*/ |
char *n = bnd_make_name(integ->system,enginedata->bndlist[i]); |
130 |
static void integrator_ida_create(IntegratorSystem *integ) { |
CONSOLE_DEBUG("Boundary '%s': bnd_cond_states[%d]=%d, bndman_calc_satisfied=%d; (trigger dirn=%s)" |
131 |
//CONSOLE_DEBUG("ALLOCATING IDA ENGINE DATA"); |
,n, i, bnd_cond_states[i], bndman_calc_satisfied(enginedata->bndlist[i]) |
132 |
IntegratorIdaData *enginedata; |
,rootdir[i]==1?"UP":(rootdir[i]==-1?"DOWN":"both") |
133 |
enginedata = ASC_NEW(IntegratorIdaData); |
); |
134 |
//CONSOLE_DEBUG("enginedata = %p",enginedata); |
ASC_FREE(n); |
135 |
enginedata->rellist = NULL; |
#endif |
136 |
enginedata->safeeval = 0; |
} |
137 |
enginedata->vfilter.matchbits = VAR_SVAR | VAR_INCIDENT | VAR_ACTIVE |
|
138 |
| VAR_FIXED; |
|
139 |
enginedata->vfilter.matchvalue = VAR_SVAR | VAR_INCIDENT | VAR_ACTIVE | 0; |
for(t_index = start_index + 1; t_index <= finish_index; ++t_index, ++integ->currentstep){ |
140 |
enginedata->pfree = NULL; |
tout = samplelist_get(integ->samples, t_index); |
141 |
|
t0 = integrator_get_t(integ); |
142 |
enginedata->rfilter.matchbits = REL_EQUALITY | REL_INCLUDED | REL_ACTIVE; |
asc_assert(tout > t0); |
143 |
enginedata->rfilter.matchvalue = REL_EQUALITY | REL_INCLUDED | REL_ACTIVE; |
|
144 |
|
#ifdef SOLVE_DEBUG |
145 |
enginedata->flagfn = NULL; |
CONSOLE_DEBUG("Integrating from t0 = %f to t = %f", t0, tout); |
146 |
enginedata->flagfntype = NULL; |
#endif |
147 |
enginedata->flagnamefn = NULL; |
if(enginedata->nbnds || first_run){ |
148 |
|
peaw = process_events_and_whens(integ, ida_mem, t0, tout, rootdir, first_run); |
149 |
integ->enginedata = (void *) enginedata; |
if(first_run == 1){ |
150 |
|
first_run = 0; |
151 |
integrator_ida_params_default(integ); |
} |
152 |
} |
} |
153 |
|
|
154 |
/** |
|
155 |
Free any memory specifically allocated for use by our ASCEND wrapper for |
if(peaw==143){ /*Flag for rootsfound - system is reconfigured already!*/ |
156 |
the IDA integrator, other than that which the IDA code itself may be |
|
157 |
managing. |
do{ |
158 |
*/ |
subpeaw = 0; /*Flag for taking auxiliary small steps after root*/ |
159 |
static void integrator_ida_free(void *enginedata) { |
flag = IDASolve(ida_mem, t0 + auxcount*tmin, &t0, yret, ypret, IDA_NORMAL); |
160 |
#ifdef DESTROY_DEBUG |
/*Todo: Error care for integrator*/ |
161 |
CONSOLE_DEBUG("DESTROYING IDA engine data at %p",enginedata); |
/*Now, take small timesteps and check for further roots*/ |
162 |
#endif |
for(i = 0; i < enginedata->nbnds; i++) { |
163 |
IntegratorIdaData *d = (IntegratorIdaData *) enginedata; |
rootdir[i] = -1*rootsfound[i]; |
164 |
asc_assert(d); |
#ifdef IDA_BND_DEBUG |
165 |
if (d->pfree) { |
char *n = bnd_make_name(integ->system,enginedata->bndlist[i]); |
166 |
CONSOLE_DEBUG("DESTROYING preconditioner data using fn at %p",d->pfree); |
CONSOLE_DEBUG("Set direction=%d for boundary '%s'",rootdir[i],n); |
167 |
/* free the preconditioner data, whatever it happens to be */ |
ASC_FREE(n); |
168 |
(d->pfree)(enginedata); |
#endif |
169 |
} |
} |
170 |
|
subpeaw = process_events_and_whens(integ, ida_mem, t0, t0 + auxcount*tmin, rootdir,first_run); |
171 |
ASC_FREE(d->rellist); |
}while(subpeaw == 143 && auxcount < 20); /*Arbitrary limit. Still better limit: auxcount < (tout-t0)/tmin ?*/ |
172 |
|
|
173 |
#ifdef DESTROY_DEBUG |
/*Important Todo ---- Time needs to be reset*/ |
174 |
CONSOLE_DEBUG("Now destroying the enginedata"); |
|
175 |
#endif |
|
176 |
ASC_FREE(d); |
} |
177 |
#ifdef DESTROY_DEBUG |
|
178 |
CONSOLE_DEBUG("enginedata freed"); |
|
179 |
#endif |
if(peaw==657){ |
180 |
} |
flag = IDASolve(ida_mem, tout, &t0, yret, ypret, IDA_NORMAL); |
181 |
|
} |
182 |
IntegratorIdaData *integrator_ida_enginedata(IntegratorSystem *integ) { |
|
183 |
IntegratorIdaData *d; |
|
184 |
assert(integ!=NULL); |
}/*End of Main integration For Loop*/ |
185 |
assert(integ->enginedata!=NULL); |
|
186 |
assert(integ->engine==INTEG_IDA); |
/* -- set up the IntegratorReporter */ |
187 |
d = ((IntegratorIdaData *) (integ->enginedata)); |
integrator_output_init(integ); |
188 |
return d; |
/* -- store the initial values of all the stuff */ |
189 |
} |
integrator_output_write(integ); |
190 |
|
integrator_output_write_obs(integ); |
191 |
/*------------------------------------------------------------- |
/* specify where the returned values should be stored */ |
192 |
PARAMETERS FOR IDA |
yret = ida_bnd_new_zero_NV(integ->n_y); |
193 |
*/ |
ypret = ida_bnd_new_zero_NV(integ->n_y); |
194 |
|
/* advance solution in time, return values as yret and derivatives as ypret */ |
195 |
enum ida_parameters { |
integ->currentstep = 1; |
196 |
IDA_PARAM_LINSOLVER, |
return 0; |
197 |
IDA_PARAM_MAXL, |
|
198 |
IDA_PARAM_MAXORD, |
} |
|
IDA_PARAM_AUTODIFF, |
|
|
IDA_PARAM_CALCIC, |
|
|
IDA_PARAM_SAFEEVAL, |
|
|
IDA_PARAM_RTOL, |
|
|
IDA_PARAM_ATOL, |
|
|
IDA_PARAM_ATOLVECT, |
|
|
IDA_PARAM_GSMODIFIED, |
|
|
IDA_PARAM_MAXNCF, |
|
|
IDA_PARAM_PREC, |
|
|
IDA_PARAMS_SIZE |
|
|
}; |
|
|
|
|
|
/** |
|
|
Here the full set of parameters is defined, along with upper/lower bounds, |
|
|
etc. The values are stuck into the integ->params structure. |
|
|
|
|
|
To add a new parameter, first give it a name IDA_PARAM_* in thge above enum ida_parameters |
|
|
list. Then add a slv_param_*(...) statement below to define the type, description and range |
|
|
for the new parameter. |
|
|
|
|
|
@return 0 on success |
|
|
*/ |
|
|
static int integrator_ida_params_default(IntegratorSystem *integ) { |
|
|
asc_assert(integ!=NULL); |
|
|
asc_assert(integ->engine==INTEG_IDA); |
|
|
slv_parameters_t *p; |
|
|
p = &(integ->params); |
|
|
|
|
|
slv_destroy_parms(p); |
|
|
|
|
|
if (p->parms == NULL) { |
|
|
//CONSOLE_DEBUG("params NULL"); |
|
|
p->parms = ASC_NEW_ARRAY(struct slv_parameter, IDA_PARAMS_SIZE); |
|
|
if (p->parms == NULL) |
|
|
return -1; |
|
|
p->dynamic_parms = 1; |
|
|
} else { |
|
|
//CONSOLE_DEBUG("params not NULL"); |
|
|
} |
|
|
|
|
|
/* reset the number of parameters to zero so that we can check it at the end */ |
|
|
p->num_parms = 0; |
|
|
|
|
|
slv_param_bool( |
|
|
p, |
|
|
IDA_PARAM_AUTODIFF, |
|
|
(SlvParameterInitBool) { {"autodiff" |
|
|
,"Use auto-diff?",1 |
|
|
,"Use automatic differentiation of expressions (1) or use numerical derivatives (0)" |
|
|
}, TRUE} |
|
|
); |
|
|
|
|
|
slv_param_char(p,IDA_PARAM_CALCIC |
|
|
,(SlvParameterInitChar) { {"calcic" |
|
|
,"Initial conditions calcuation",1 |
|
|
,"Use specified values of ydot to solve for inital y (Y)," |
|
|
" or use the the values of the differential variables (yd) to solve" |
|
|
" for the pure algebraic variables (ya) along with the derivatives" |
|
|
" of the differential variables (yddot) (YA_YDP), or else don't solve" |
|
|
" the intial conditions at all (NONE). See IDA manual p 41 (IDASetId)" |
|
|
}, "YA_YDP"}, (char *[]) {"Y", "YA_YDP", "NONE",NULL} |
|
|
); |
|
|
|
|
|
slv_param_bool(p,IDA_PARAM_SAFEEVAL |
|
|
,(SlvParameterInitBool) { {"safeeval" |
|
|
,"Use safe evaluation?",1 |
|
|
,"Use 'safe' function evaluation routines (TRUE) or allow ASCEND to " |
|
|
"throw SIGFPE errors which will then halt integration." |
|
|
}, FALSE} |
|
|
); |
|
|
|
|
|
slv_param_bool(p,IDA_PARAM_ATOLVECT |
|
|
,(SlvParameterInitBool) { {"atolvect" |
|
|
,"Use 'ode_atol' values as specified?",1 |
|
|
,"If TRUE, values of 'ode_atol' are taken from your model and used " |
|
|
" in the integration. If FALSE, a scalar absolute tolerance value" |
|
|
" is shared by all variables. See IDA manual, section 5.5.1" |
|
|
}, TRUE} |
|
|
); |
|
|
|
|
|
slv_param_real(p,IDA_PARAM_ATOL |
|
|
,(SlvParameterInitReal) { {"atol" |
|
|
,"Scalar absolute error tolerance",1 |
|
|
,"Value of the scalar absolute error tolerance. See also 'atolvect'." |
|
|
" See IDA manual, sections 5.5.1 and 5.5.2 'Advice on choice and use of tolerances'" |
|
|
}, 1e-5, 0, 1e10} |
|
|
); |
|
|
|
|
|
slv_param_real(p,IDA_PARAM_RTOL |
|
|
,(SlvParameterInitReal) { {"rtol" |
|
|
,"Scalar relative error tolerance",1 |
|
|
,"Value of the scalar relative error tolerance. (Note that for IDA," |
|
|
" it's not possible to set per-variable relative tolerances as it is" |
|
|
" with LSODE)." |
|
|
" See IDA manual, section 5.5.2 'Advice on choice and use of tolerances'" |
|
|
}, 1e-4, 0, 1} |
|
|
); |
|
|
|
|
|
slv_param_char(p,IDA_PARAM_LINSOLVER |
|
|
,(SlvParameterInitChar) { {"linsolver" |
|
|
,"Linear solver",1 |
|
|
,"See IDA manual, section 5.5.3. Choose 'ASCEND' to use the linsolqr" |
|
|
" direct linear solver bundled with ASCEND, 'DENSE' to use the dense" |
|
|
" solver bundled with IDA, or one of the Krylov solvers SPGMR, SPBCG" |
|
|
" or SPTFQMR (which still need preconditioners to be implemented" |
|
|
" before they can be very useful." |
|
|
}, "DENSE"}, (char *[]) {"ASCEND","DENSE","BAND","SPGMR","SPBCG","SPTFQMR",NULL} |
|
|
); |
|
|
|
|
|
slv_param_int(p,IDA_PARAM_MAXL |
|
|
,(SlvParameterInitInt) { {"maxl" |
|
|
,"Maximum Krylov dimension",0 |
|
|
,"The maximum dimension of Krylov space used by the linear solver" |
|
|
" (for SPGMR, SPBCG, SPTFQMR) with IDA. See IDA manual section 5.5." |
|
|
" The default of 0 results in IDA using its internal default, which" |
|
|
" is currently a value of 5." |
|
|
}, 0, 0, 20} |
|
|
); |
|
|
|
|
|
slv_param_int(p,IDA_PARAM_MAXORD |
|
|
,(SlvParameterInitInt) { {"maxord" |
|
|
,"Maximum order of linear multistep method",0 |
|
|
,"The maximum order of the linear multistep method with IDA. See" |
|
|
" IDA manual p 38." |
|
|
}, 5, 1, 5} |
|
|
); |
|
|
|
|
|
slv_param_bool(p,IDA_PARAM_GSMODIFIED |
|
|
,(SlvParameterInitBool) { {"gsmodified" |
|
|
,"Gram-Schmidt Orthogonalisation Scheme", 2 |
|
|
,"TRUE = GS_MODIFIED, FALSE = GS_CLASSICAL. See IDA manual section" |
|
|
" 5.5.6.6. Only applies when linsolve=SPGMR is selected." |
|
|
}, TRUE} |
|
|
); |
|
|
|
|
|
slv_param_int(p,IDA_PARAM_MAXNCF |
|
|
,(SlvParameterInitInt) { {"maxncf" |
|
|
,"Max nonlinear solver convergence failures per step", 2 |
|
|
,"Maximum number of allowable nonlinear solver convergence failures" |
|
|
" on one step. See IDA manual section 5.5.6.1." |
|
|
}, 10,0,1000} |
|
|
); |
|
|
|
|
|
slv_param_char(p,IDA_PARAM_PREC |
|
|
,(SlvParameterInitChar) { {"prec" |
|
|
,"Preconditioner",1 |
|
|
,"See IDA manual, section section 5.6.8." |
|
|
},"NONE"}, (char *[]) {"NONE","DIAG",NULL} |
|
|
); |
|
|
|
|
|
asc_assert(p->num_parms == IDA_PARAMS_SIZE); |
|
|
|
|
|
//CONSOLE_DEBUG("Created %d params", p->num_parms); |
|
|
|
|
|
return 0; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/******************************************* |
|
|
* SOLVE SETUP FUNCTIONS |
|
|
*******************************************/ |
|
|
|
|
|
int ida_load_rellist(IntegratorSystem *integ) { |
|
|
IntegratorIdaData *enginedata; |
|
|
struct rel_relation **rels; |
|
|
int i, j, n_solverrels, n_active_rels; |
|
|
|
|
|
enginedata = integrator_ida_enginedata(integ); |
|
|
|
|
|
n_solverrels = slv_get_num_solvers_rels(integ->system); |
|
|
n_active_rels = slv_count_solvers_rels(integ->system, &integrator_ida_rel); |
|
|
rels = slv_get_solvers_rel_list(integ->system); |
|
|
|
|
|
if (enginedata->rellist != NULL) { |
|
|
ASC_FREE(enginedata->rellist); |
|
|
enginedata->rellist = NULL; |
|
|
} |
|
|
|
|
|
enginedata->rellist |
|
|
= ASC_NEW_ARRAY(struct rel_relation *, n_active_rels); |
|
|
|
|
|
#ifdef SOLVE_DEBUG |
|
|
CONSOLE_DEBUG("rels matchbits: 0x%x",integrator_ida_rel.matchbits); |
|
|
CONSOLE_DEBUG("rels matchvalue: 0x%x",integrator_ida_rel.matchvalue); |
|
|
|
|
|
CONSOLE_DEBUG("Number of relations: %d",n_solverrels); |
|
|
CONSOLE_DEBUG("Number of active relations: %d",n_active_rels); |
|
|
CONSOLE_DEBUG("Number of dependent vars: %d",integ->n_y); |
|
|
CONSOLE_DEBUG("Number of boundaries: %d",enginedata->nbnds); |
|
|
#endif |
|
|
|
|
|
|
|
|
j = 0; |
|
|
for (i = 0; i < n_solverrels; ++i) { |
|
|
if (rel_apply_filter(rels[i], &integrator_ida_rel)) { |
|
|
#ifdef SOLVE_DEBUG |
|
|
{ |
|
|
char *relname = rel_make_name(integ->system, rels[i]); |
|
|
CONSOLE_DEBUG("rel '%s': 0x%x", relname, rel_flags(rels[i])); |
|
|
ASC_FREE(relname); |
|
|
} |
|
|
#endif |
|
|
enginedata->rellist[j++] = rels[i]; |
|
|
} |
|
|
} |
|
|
|
|
|
asc_assert(j == n_active_rels); |
|
|
enginedata->nrels = n_active_rels; |
|
|
|
|
|
if (enginedata->nrels != integ->n_y) { |
|
|
ERROR_REPORTER_HERE(ASC_USER_ERROR |
|
|
,"Integration problem is not square (%d active rels, %d vars)" |
|
|
,n_active_rels, integ->n_y |
|
|
); |
|
|
return 1; /* failure */ |
|
|
} |
|
|
|
|
|
return 0; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Retrieve initial values from the system |
|
|
*/ |
|
|
int ida_retrieve_IVs(IntegratorSystem *integ, realtype t0, N_Vector y0, |
|
|
N_Vector yp0) { |
|
|
|
|
|
#ifdef SOLVE_DEBUG |
|
|
char *varname; |
|
|
char diffname[30]; |
|
|
int i; |
|
|
CONSOLE_DEBUG("RETRIEVING INITIAL VALUES:"); |
|
|
CONSOLE_DEBUG("t0 = %f",t0); |
|
|
#endif |
|
|
|
|
|
integrator_get_y(integ, NV_DATA_S(y0)); |
|
|
integrator_get_ydot(integ, NV_DATA_S(yp0)); |
|
|
|
|
|
#ifdef SOLVE_DEBUG |
|
|
fprintf(stderr, "index\t%25s\t%25s\n", "y", "ydot"); |
|
|
for (i = 0; i < integ->n_y; ++i) { |
|
|
varname = var_make_name(integ->system, integ->y[i]); |
|
|
fprintf(stderr, "%d\t%15s=%10f\t", i, varname, NV_Ith_S(y0,i)); |
|
|
|
|
|
if (integ->ydot[i]) { |
|
|
ASC_FREE(varname); |
|
|
varname = var_make_name(integ->system, integ->ydot[i]); |
|
|
fprintf(stderr, "%15s=%10f\t\n", varname, NV_Ith_S(yp0,i)); |
|
|
} else { |
|
|
snprintf(diffname,99,"diff(%s)",varname); |
|
|
fprintf(stderr,"%15s=%10f\t\n",diffname,NV_Ith_S(yp0,i)); |
|
|
} |
|
|
ASC_FREE(varname); |
|
|
} |
|
|
#endif |
|
|
|
|
|
return 0; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Assign internal memory for the appropriate sundials version and error |
|
|
* tolerance parameter settings. |
|
|
*/ |
|
|
int ida_malloc(IntegratorSystem *integ, void *ida_mem, realtype t0, |
|
|
N_Vector y0, N_Vector yp0) { |
|
|
int flag; |
|
|
N_Vector abstolvect; |
|
|
realtype reltol, abstol; |
|
|
|
|
|
/* relative error tolerance */ |
|
|
reltol = SLV_PARAM_REAL(&(integ->params),IDA_PARAM_RTOL); |
|
|
//CONSOLE_DEBUG("rtol = %8.2e",reltol); |
|
|
|
|
|
|
|
|
#if SUNDIALS_VERSION_MAJOR==2 && SUNDIALS_VERSION_MINOR>=4 |
|
|
flag = IDAInit(ida_mem, &integrator_ida_fex, t0, y0 ,yp0); |
|
|
#else |
|
|
if(SLV_PARAM_BOOL(&(integ->params),IDA_PARAM_ATOLVECT)) { |
|
|
/* vector of absolute tolerances */ |
|
|
CONSOLE_DEBUG("USING VECTOR OF ATOL VALUES"); |
|
|
abstolvect = N_VNew_Serial(integ->n_y); |
|
|
integrator_get_atol(integ, NV_DATA_S(abstolvect)); |
|
|
|
|
|
flag = IDAMalloc(ida_mem, &integrator_ida_fex, t0, y0, yp0, IDA_SV, |
|
|
reltol, abstolvect); |
|
|
|
|
|
N_VDestroy_Serial(abstolvect); |
|
|
}else{ |
|
|
/* scalar absolute tolerance (one value for all) */ |
|
|
abstol = SLV_PARAM_REAL(&(integ->params),IDA_PARAM_ATOL); |
|
|
CONSOLE_DEBUG("USING SCALAR ATOL VALUE = %8.2e",abstol); |
|
|
flag = IDAMalloc(ida_mem, &integrator_ida_fex, t0, y0, yp0, IDA_SS, |
|
|
reltol, &abstol); |
|
|
} |
|
|
#endif |
|
|
|
|
|
if(flag == IDA_MEM_NULL) { |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"ida_mem is NULL"); |
|
|
return 2; |
|
|
}else if(flag == IDA_MEM_FAIL) { |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to allocate memory (IDAMalloc)"); |
|
|
return 3; |
|
|
}else if(flag == IDA_ILL_INPUT) { |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Invalid input to IDAMalloc"); |
|
|
return 4; |
|
|
} |
|
|
|
|
|
#if SUNDIALS_VERSION_MAJOR==2 && SUNDIALS_VERSION_MINOR>=4 |
|
|
//CONSOLE_DEBUG("Assigning tolerances..."); |
|
|
/* assign tolerances */ |
|
|
if(SLV_PARAM_BOOL(&(integ->params),IDA_PARAM_ATOLVECT)) { |
|
|
//CONSOLE_DEBUG("using vector of atol values"); |
|
|
abstolvect = N_VNew_Serial(integ->n_y); |
|
|
integrator_get_atol(integ,NV_DATA_S(abstolvect)); |
|
|
IDASVtolerances(ida_mem, reltol, abstolvect); |
|
|
N_VDestroy_Serial(abstolvect); |
|
|
}else{ |
|
|
/* scalar tolerances */ |
|
|
abstol = SLV_PARAM_REAL(&(integ->params),IDA_PARAM_ATOL); |
|
|
CONSOLE_DEBUG("using scalar atol value = %8.2e",abstol); |
|
|
IDASStolerances(ida_mem, reltol, abstol); |
|
|
} |
|
|
#endif |
|
|
|
|
|
/* success */ |
|
|
return 0; |
|
|
} |
|
|
|
|
|
/* |
|
|
* Set parameter inputs for step size, the linear solver module and preconditioner |
|
|
*/ |
|
|
int ida_set_optional_inputs(IntegratorSystem *integ, void *ida_mem) { |
|
|
int flag; |
|
|
char *linsolver; |
|
|
char *pname = NULL; |
|
|
int maxl; |
|
|
const IntegratorIdaPrec *prec = NULL; |
|
|
|
|
|
IntegratorIdaData *enginedata = integ->enginedata; |
|
|
|
|
|
IDASetErrHandlerFn(ida_mem, &integrator_ida_error, (void *) integ); |
|
|
#if SUNDIALS_VERSION_MAJOR==2 && SUNDIALS_VERSION_MINOR>=4 |
|
|
IDASetUserData(ida_mem, (void *)integ); |
|
|
#else |
|
|
IDASetRdata(ida_mem, (void *) integ); |
|
|
#endif |
|
|
IDASetMaxStep(ida_mem, integrator_get_maxstep(integ)); |
|
|
IDASetInitStep(ida_mem, integrator_get_stepzero(integ)); |
|
|
IDASetMaxNumSteps(ida_mem, integrator_get_maxsubsteps(integ)); |
|
|
if (integrator_get_minstep(integ) > 0) { |
|
|
ERROR_REPORTER_HERE(ASC_PROG_NOTE,"IDA does not support minstep (ignored)\n"); |
|
|
} |
|
|
|
|
|
//CONSOLE_DEBUG("MAXNCF = %d",SLV_PARAM_INT(&integ->params,IDA_PARAM_MAXNCF)); |
|
|
IDASetMaxConvFails(ida_mem, SLV_PARAM_INT(&integ->params,IDA_PARAM_MAXNCF)); |
|
|
|
|
|
//CONSOLE_DEBUG("MAXORD = %d",SLV_PARAM_INT(&integ->params,IDA_PARAM_MAXORD)); |
|
|
IDASetMaxOrd(ida_mem, SLV_PARAM_INT(&integ->params,IDA_PARAM_MAXORD)); |
|
|
|
|
|
/* there's no capability for setting *minimum* step size in IDA */ |
|
|
|
|
|
/* attach linear solver module, using the default value of maxl */ |
|
|
linsolver = SLV_PARAM_CHAR(&(integ->params),IDA_PARAM_LINSOLVER); |
|
|
//CONSOLE_DEBUG("ASSIGNING LINEAR SOLVER '%s'",linsolver); |
|
|
if(strcmp(linsolver, "ASCEND") == 0) { |
|
|
CONSOLE_DEBUG("ASCEND DIRECT SOLVER, size = %d",integ->n_y); |
|
|
IDAASCEND(ida_mem, integ->n_y); |
|
|
IDAASCENDSetJacFn(ida_mem, &integrator_ida_sjex, (void *) integ); |
|
|
|
|
|
enginedata->flagfntype = "IDAASCEND"; |
|
|
enginedata->flagfn = &IDAASCENDGetLastFlag; |
|
|
enginedata->flagnamefn = &IDAASCENDGetReturnFlagName; |
|
|
|
|
|
}else if (strcmp(linsolver, "DENSE") == 0) { |
|
|
//CONSOLE_DEBUG("DENSE DIRECT SOLVER, size = %d",integ->n_y); |
|
|
flag = IDADense(ida_mem, integ->n_y); |
|
|
switch(flag){ |
|
|
case IDADENSE_SUCCESS: |
|
|
break; |
|
|
case IDADENSE_MEM_NULL: |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"ida_mem is NULL"); |
|
|
return 5; |
|
|
case IDADENSE_ILL_INPUT: |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"IDADENSE is not compatible with current nvector module"); |
|
|
return 5; |
|
|
case IDADENSE_MEM_FAIL: |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Memory allocation failed for IDADENSE"); |
|
|
return 5; |
|
|
default: |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"bad return"); |
|
|
return 5; |
|
|
} |
|
|
|
|
|
if(SLV_PARAM_BOOL(&(integ->params),IDA_PARAM_AUTODIFF)) { |
|
|
//CONSOLE_DEBUG("USING AUTODIFF"); |
|
|
#if SUNDIALS_VERSION_MAJOR==2 && SUNDIALS_VERSION_MINOR>=4 |
|
|
flag = IDADlsSetDenseJacFn(ida_mem, &integrator_ida_djex); |
|
|
#else |
|
|
flag = IDADenseSetJacFn(ida_mem, &integrator_ida_djex, |
|
|
(void *) integ); |
|
|
#endif |
|
|
switch (flag) { |
|
|
case IDADENSE_SUCCESS: |
|
|
break; |
|
|
default: |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed IDADenseSetJacFn"); |
|
|
return 6; |
|
|
} |
|
|
}else{ |
|
|
CONSOLE_DEBUG("USING NUMERICAL DIFF"); |
|
|
} |
|
|
|
|
|
enginedata->flagfntype = "IDADENSE"; |
|
|
#if SUNDIALS_VERSION_MAJOR==2 && SUNDIALS_VERSION_MINOR>=4 |
|
|
enginedata->flagfn = &IDADlsGetLastFlag; |
|
|
enginedata->flagnamefn = &IDADlsGetReturnFlagName; |
|
|
#else |
|
|
enginedata->flagfn = &IDADenseGetLastFlag; |
|
|
enginedata->flagnamefn = &IDADenseGetReturnFlagName; |
|
|
#endif |
|
|
}else{ |
|
|
/* remaining methods are all SPILS */ |
|
|
CONSOLE_DEBUG("IDA SPILS"); |
|
|
|
|
|
maxl = SLV_PARAM_INT(&(integ->params),IDA_PARAM_MAXL); |
|
|
CONSOLE_DEBUG("maxl = %d",maxl); |
|
|
|
|
|
/* what preconditioner? */ |
|
|
pname = SLV_PARAM_CHAR(&(integ->params),IDA_PARAM_PREC); |
|
|
if(strcmp(pname, "NONE") == 0){ |
|
|
prec = NULL; |
|
|
}else if(strcmp(pname, "JACOBI") == 0){ |
|
|
prec = &prec_jacobi; |
|
|
}else{ |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Invalid preconditioner choice '%s'",pname); |
|
|
return 7; |
|
|
} |
|
|
|
|
|
/* which SPILS linear solver? */ |
|
|
if(strcmp(linsolver, "SPGMR") == 0){ |
|
|
CONSOLE_DEBUG("IDA SPGMR"); |
|
|
flag = IDASpgmr(ida_mem, maxl); /* 0 means use the default max Krylov dimension of 5 */ |
|
|
}else if(strcmp(linsolver, "SPBCG") == 0){ |
|
|
CONSOLE_DEBUG("IDA SPBCG"); |
|
|
flag = IDASpbcg(ida_mem, maxl); |
|
|
}else if(strcmp(linsolver, "SPTFQMR") == 0){ |
|
|
CONSOLE_DEBUG("IDA SPTFQMR"); |
|
|
flag = IDASptfqmr(ida_mem, maxl); |
|
|
}else{ |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unknown IDA linear solver choice '%s'",linsolver); |
|
|
return 8; |
|
|
} |
|
|
|
|
|
if(prec){ |
|
|
/* assign the preconditioner to the linear solver */ |
|
|
(prec->pcreate)(integ); |
|
|
#if SUNDIALS_VERSION_MAJOR==2 && SUNDIALS_VERSION_MINOR>=4 |
|
|
IDASpilsSetPreconditioner(ida_mem,prec->psetup,prec->psolve); |
|
|
#else |
|
|
IDASpilsSetPreconditioner(ida_mem, prec->psetup, prec->psolve, |
|
|
(void *) integ); |
|
|
#endif |
|
|
CONSOLE_DEBUG("PRECONDITIONER = %s",pname); |
|
|
}else{ |
|
|
CONSOLE_DEBUG("No preconditioner"); |
|
|
} |
|
|
|
|
|
enginedata->flagfntype = "IDASPILS"; |
|
|
enginedata->flagfn = &IDASpilsGetLastFlag; |
|
|
enginedata->flagnamefn = &IDASpilsGetReturnFlagName; |
|
|
|
|
|
if(flag == IDASPILS_MEM_NULL){ |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"ida_mem is NULL"); |
|
|
return 9; |
|
|
}else if (flag == IDASPILS_MEM_FAIL) { |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to allocate memory (IDASpgmr)"); |
|
|
return 9; |
|
|
}/* else success */ |
|
|
|
|
|
|
|
|
/* assign the J*v function */ |
|
|
if(SLV_PARAM_BOOL(&(integ->params),IDA_PARAM_AUTODIFF)) { |
|
|
CONSOLE_DEBUG("USING AUTODIFF"); |
|
|
#if SUNDIALS_VERSION_MAJOR==2 && SUNDIALS_VERSION_MINOR>=4 |
|
|
flag = IDASpilsSetJacTimesVecFn(ida_mem, &integrator_ida_jvex); |
|
|
#else |
|
|
flag = IDASpilsSetJacTimesVecFn(ida_mem, &integrator_ida_jvex, |
|
|
(void *) integ); |
|
|
#endif |
|
|
if(flag == IDASPILS_MEM_NULL) { |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"ida_mem is NULL"); |
|
|
return 10; |
|
|
}else if (flag == IDASPILS_LMEM_NULL) { |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"IDASPILS linear solver has not been initialized"); |
|
|
return 10; |
|
|
}/* else success */ |
|
|
}else{ |
|
|
CONSOLE_DEBUG("USING NUMERICAL DIFF"); |
|
|
} |
|
|
|
|
|
if(strcmp(linsolver, "SPGMR") == 0) { |
|
|
/* select Gram-Schmidt orthogonalisation */ |
|
|
if(SLV_PARAM_BOOL(&(integ->params),IDA_PARAM_GSMODIFIED)) { |
|
|
CONSOLE_DEBUG("USING MODIFIED GS"); |
|
|
flag = IDASpilsSetGSType(ida_mem, MODIFIED_GS); |
|
|
if(flag != IDASPILS_SUCCESS) { |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed to set GS_MODIFIED"); |
|
|
return 11; |
|
|
} |
|
|
}else{ |
|
|
CONSOLE_DEBUG("USING CLASSICAL GS"); |
|
|
flag = IDASpilsSetGSType(ida_mem, CLASSICAL_GS); |
|
|
if(flag != IDASPILS_SUCCESS) { |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed to set GS_MODIFIED"); |
|
|
return 11; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
/* set linear solver optional inputs... |
|
|
...nothing here at the moment... |
|
|
*/ |
|
|
|
|
|
return 0; |
|
|
} /* ida_set_optional_inputs */ |
|
|
|
|
|
/** |
|
|
* Calculate initial conditions using IDACalcIC |
|
|
*/ |
|
|
|
|
|
int ida_setup_IC(IntegratorSystem *integ, void *ida_mem, |
|
|
realtype tout1, realtype t0, N_Vector y0, N_Vector yp0) { |
|
|
int i, flag, flag1; |
|
|
int icopt; /* initial conditions strategy */ |
|
|
N_Vector id; |
|
|
|
|
|
IntegratorIdaData *enginedata = integ->enginedata; |
|
|
|
|
|
#ifdef SOLVE_DEBUG |
|
|
char *varname; |
|
|
#endif |
|
|
|
|
|
#ifdef IDA_BND_DEBUG |
|
|
CONSOLE_DEBUG("Solving initial conditions..."); |
|
|
#endif |
|
|
|
|
|
icopt = 0; |
|
|
if(strcmp(SLV_PARAM_CHAR(&integ->params,IDA_PARAM_CALCIC), "Y") == 0) { |
|
|
#ifdef SOLVE_DEBUG |
|
|
CONSOLE_DEBUG("Solving initial conditions using values of yddot"); |
|
|
#endif |
|
|
icopt = IDA_Y_INIT; |
|
|
asc_assert(icopt!=0); |
|
|
}else if(0==strcmp(SLV_PARAM_CHAR(&integ->params,IDA_PARAM_CALCIC), "YA_YDP")){ |
|
|
#ifdef SOLVE_DEBUG |
|
|
CONSOLE_DEBUG("Solving initial conditions using values of yd"); |
|
|
#endif |
|
|
icopt = IDA_YA_YDP_INIT; |
|
|
asc_assert(icopt!=0); |
|
|
id = N_VNew_Serial(integ->n_y); |
|
|
for(i = 0; i < integ->n_y; ++i) { |
|
|
if(integ->ydot[i] == NULL) { |
|
|
NV_Ith_S(id, i) = 0.0; |
|
|
#ifdef SOLVE_DEBUG |
|
|
varname = var_make_name(integ->system, integ->y[i]); |
|
|
CONSOLE_DEBUG("y[%d] = '%s' is pure algebraic",i,varname); |
|
|
ASC_FREE(varname); |
|
|
#endif |
|
|
}else{ |
|
|
#ifdef SOLVE_DEBUG |
|
|
CONSOLE_DEBUG("y[%d] is differential",i); |
|
|
#endif |
|
|
NV_Ith_S(id, i) = 1.0; |
|
|
} |
|
|
} |
|
|
IDASetId(ida_mem, id); |
|
|
N_VDestroy_Serial(id); |
|
|
}else if (strcmp(SLV_PARAM_CHAR(&integ->params,IDA_PARAM_CALCIC), "NONE") |
|
|
== 0) { |
|
|
ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Not solving initial conditions: check current residuals"); |
|
|
} else { |
|
|
ERROR_REPORTER_HERE(ASC_USER_ERROR,"Invalid 'iccalc' value: check solver parameters."); |
|
|
} |
|
|
|
|
|
if (icopt) { |
|
|
#ifdef SOLVE_DEBUG |
|
|
CONSOLE_DEBUG("SOLVING INITIAL CONDITIONS IDACalcIC (tout1 = %f)", tout1); |
|
|
#endif |
|
|
|
|
|
#ifdef ASC_SIGNAL_TRAPS |
|
|
/* catch SIGFPE if desired to */ |
|
|
if(enginedata->safeeval) { |
|
|
CONSOLE_DEBUG("SETTING TO IGNORE SIGFPE..."); |
|
|
Asc_SignalHandlerPush(SIGFPE, SIG_DFL); |
|
|
}else { |
|
|
# ifdef FEX_DEBUG |
|
|
CONSOLE_DEBUG("SETTING TO CATCH SIGFPE..."); |
|
|
# endif |
|
|
Asc_SignalHandlerPushDefault(SIGFPE); |
|
|
} |
|
|
if(setjmp(g_fpe_env) == 0) { |
|
|
#endif |
|
|
|
|
|
# if SUNDIALS_VERSION_MAJOR==2 && SUNDIALS_VERSION_MINOR>=3 |
|
|
flag = IDACalcIC(ida_mem, icopt, tout1);/* new API from v2.3 */ |
|
|
# else |
|
|
flag = IDACalcIC(ida_mem, t0, y0, yp0, icopt, tout1); |
|
|
# endif |
|
|
/* check flags and output status */ |
|
|
switch(flag){ |
|
|
case IDA_SUCCESS: |
|
|
#ifdef SOLVE_DEBUG |
|
|
CONSOLE_DEBUG("Initial conditions solved OK"); |
|
|
#endif |
|
|
break; |
|
|
|
|
|
case IDA_LSETUP_FAIL: |
|
|
case IDA_LINIT_FAIL: |
|
|
case IDA_LSOLVE_FAIL: |
|
|
case IDA_NO_RECOVERY: |
|
|
flag1 = -999; |
|
|
flag = (enginedata->flagfn)(ida_mem, &flag1); |
|
|
if (flag) { |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR |
|
|
,"Unable to retrieve error code from %s (err %d)" |
|
|
,enginedata->flagfntype,flag |
|
|
); |
|
|
return 12; |
|
|
} |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR |
|
|
,"%s returned flag '%s' (value = %d)" |
|
|
,enginedata->flagfntype,(enginedata->flagnamefn)(flag1),flag1 |
|
|
); |
|
|
return 12; |
|
|
|
|
|
default: |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed to solve initial condition (IDACalcIC)"); |
|
|
return 12; |
|
|
} |
|
|
#ifdef ASC_SIGNAL_TRAPS |
|
|
}else{ |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Floating point error while solving initial conditions"); |
|
|
return 13; |
|
|
} |
|
|
|
|
|
if(enginedata->safeeval){ |
|
|
Asc_SignalHandlerPop(SIGFPE, SIG_DFL); |
|
|
}else{ |
|
|
/* CONSOLE_DEBUG("pop..."); */ |
|
|
Asc_SignalHandlerPopDefault(SIGFPE); |
|
|
/* CONSOLE_DEBUG("...pop"); */ |
|
|
} |
|
|
#endif |
|
|
}/* icopt */ |
|
|
|
|
|
return 0; |
|
|
} /* ida_setup_IC */ |
|
|
|
|
|
|
|
|
int ida_root_init(IntegratorSystem *integ, void *ida_mem) { |
|
|
IntegratorIdaData *enginedata = integ->enginedata; |
|
|
|
|
|
if(enginedata->nbnds) { |
|
|
#if SUNDIALS_VERSION_MAJOR==2 && SUNDIALS_VERSION_MINOR>=4 |
|
|
IDARootInit(ida_mem, enginedata->nbnds, &integrator_ida_rootfn); |
|
|
#else |
|
|
IDARootInit(ida_mem, enginedata->nbnds, &integrator_ida_rootfn, |
|
|
(void *) integ); |
|
|
#endif |
|
|
} |
|
|
|
|
|
return 0; |
|
|
|
|
|
} |
|
|
|
|
|
/** |
|
|
* Allocate memory and prepare for integration. |
|
|
* |
|
|
* @param t the first point at which a solution is desired. Required by IdaCalcIC |
|
|
* |
|
|
* @return 0 on success |
|
|
*/ |
|
|
int ida_prepare_integrator(IntegratorSystem *integ, void *ida_mem, |
|
|
realtype tout1) { |
|
|
realtype t0; |
|
|
N_Vector y0, yp0; |
|
|
|
|
|
y0 = ida_bnd_new_zero_NV(integ->n_y); |
|
|
yp0 = ida_bnd_new_zero_NV(integ->n_y); |
|
|
|
|
|
#if 0 |
|
|
int i; |
|
|
double val; |
|
|
CONSOLE_DEBUG("Values of the derivatives present in the model"); |
|
|
for(i=0; i < integ->n_y; i++) { |
|
|
if(integ->ydot[i]){ |
|
|
val = var_value(integ->ydot[i]); |
|
|
CONSOLE_DEBUG("ydot[%d]= %g", i, val); |
|
|
} |
|
|
} |
|
|
#endif |
|
|
|
|
|
t0 = integrator_get_t(integ); |
|
|
ida_retrieve_IVs(integ, t0, y0, yp0); |
|
|
|
|
|
#ifdef IDA_BND_DEBUG |
|
|
CONSOLE_DEBUG("Initial values BEFORE IDACalcIC: y0 ="); |
|
|
N_VPrint_Serial(y0); |
|
|
CONSOLE_DEBUG("yp0 = "); |
|
|
N_VPrint_Serial(yp0); |
|
|
#endif |
|
|
|
|
|
/* allocate internal memory */ |
|
|
ida_malloc(integ, ida_mem, t0, y0, yp0); |
|
|
|
|
|
/* set optional inputs... */ |
|
|
ida_set_optional_inputs(integ, ida_mem); |
|
|
|
|
|
/* calculate initial conditions */ |
|
|
ida_setup_IC(integ, ida_mem, tout1, t0, y0, yp0); |
|
|
|
|
|
/* specify ROOT-FINDING problem (if necessary) */ |
|
|
ida_root_init(integ, ida_mem); |
|
|
|
|
|
/* Clean up */ |
|
|
N_VDestroy_Serial(y0); |
|
|
N_VDestroy_Serial(yp0); |
|
|
return 0; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Reinitialise IDA after boundary-crossing. We assume that IDAInit has been called. |
|
|
* Works with Sundials 2.4.0 and later versions. |
|
|
* |
|
|
* @param t the first point at which a solution is desired. Required by IdaCalcIC |
|
|
* |
|
|
* @return 0 on success |
|
|
*/ |
|
|
int ida_reinit_integrator(IntegratorSystem *integ, void *ida_mem, |
|
|
realtype tout1) { |
|
|
realtype t0; |
|
|
N_Vector y0, yp0; |
|
|
|
|
|
y0 = ida_bnd_new_zero_NV(integ->n_y); |
|
|
yp0 = ida_bnd_new_zero_NV(integ->n_y); |
|
|
|
|
|
t0 = integrator_get_t(integ); |
|
|
ida_retrieve_IVs(integ, t0, y0, yp0); |
|
|
|
|
|
int flag; |
|
|
|
|
|
flag = IDAReInit(ida_mem, t0, y0, yp0); |
|
|
if(flag!=IDA_SUCCESS){ |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR, "Reinitialisation failed."); |
|
|
} |
|
|
|
|
|
/* calculate initial conditions */ |
|
|
ida_setup_IC(integ, ida_mem, tout1, t0, y0, yp0); |
|
|
|
|
|
/* Clean up */ |
|
|
N_VDestroy_Serial(y0); |
|
|
N_VDestroy_Serial(yp0); |
|
|
return 0; |
|
|
} |
|
|
|
|
|
/*------------------------------------------------------------- |
|
|
MAIN IDA SOLVER ROUTINE, see IDA manual, sec 5.4, p. 27 ff. |
|
|
*/ |
|
|
|
|
|
/** |
|
|
Main IDA solver routine. We can assume that the integrator_ida_analyse |
|
|
function will already have been run. |
|
|
|
|
|
The presence of 'start_index' and 'finish_index' is a hangover from the |
|
|
Tcl/Tk GUI. We would like to get rid of them eventually. |
|
|
|
|
|
@return 0 on success */ |
|
|
static int integrator_ida_solve(IntegratorSystem *integ, |
|
|
unsigned long start_index, unsigned long finish_index) { |
|
|
void *ida_mem; |
|
|
int t_index; |
|
|
realtype t0, tout, tret, tol; |
|
|
realtype tmin; /** < The length of a small additional step made after an event is triggered */ |
|
|
N_Vector ypret, yret; |
|
|
IntegratorIdaData *enginedata; |
|
|
int i, flag; |
|
|
|
|
|
int *rootsfound; /** < IDA rootfinder reports root index in here */ |
|
|
int *rootdir = NULL; /** < Used to tell IDA to ignore doulve crossings */ |
|
|
int *bnd_cond_states; /** < Record of boundary states so that IDA can tell LRSlv |
|
|
how to evaluate a boundary crossing */ |
|
|
int *bnd_not_set; |
|
|
int all_bnds_set = 1; |
|
|
|
|
|
int need_to_reconfigure; /** < Flag to indicate system rebuild after crossing */ |
|
|
int need_to_reinteg = 0; /** < Flag for when crossings happen on or very close to timesteps */ |
|
|
int skipping_output; /** < Flag to skip output to reporter */ |
|
|
int qrslv_ind, lrslv_ind; |
|
|
|
|
|
int after_root = 0; |
|
|
|
|
|
#if defined(SOLVE_DEBUG) || defined(IDA_BND_DEBUG) |
|
|
char *relname; |
|
|
#endif |
|
|
|
|
|
//CONSOLE_DEBUG("STARTING IDA..."); |
|
|
/* Setup boundary list */ |
|
|
enginedata = integrator_ida_enginedata(integ); |
|
|
enginedata->bndlist = slv_get_solvers_bnd_list(integ->system); |
|
|
enginedata->nbnds = slv_get_num_solvers_bnds(integ->system); |
|
|
enginedata->safeeval = SLV_PARAM_BOOL(&(integ->params),IDA_PARAM_SAFEEVAL); |
|
|
//CONSOLE_DEBUG("safeeval = %d",enginedata->safeeval); |
|
|
|
|
|
qrslv_ind = slv_lookup_client("QRSlv"); |
|
|
lrslv_ind = slv_lookup_client("LRSlv"); |
|
|
|
|
|
bnd_not_set = ASC_NEW_ARRAY(int,enginedata->nbnds); |
|
|
|
|
|
#ifdef SOLVE_DEBUG |
|
|
integrator_ida_debug(integ, stderr); |
|
|
#endif |
|
|
|
|
|
/* store reference to list of relations (in enginedata) */ |
|
|
ida_load_rellist(integ); |
|
|
|
|
|
/* create IDA object */ |
|
|
ida_mem = IDACreate(); |
|
|
|
|
|
/* Setup parameter inputs and initial conditions for IDA. */ |
|
|
tout = samplelist_get(integ->samples, start_index + 1); |
|
|
/* solve the initial conditions, allocate memory, other stuff... */ |
|
|
ida_prepare_integrator(integ, ida_mem, tout); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Initialise boundary condition states if appropriate. Reconfigure if necessary */ |
|
|
if(enginedata->nbnds){ |
|
|
CONSOLE_DEBUG("Initialising boundary states"); |
|
|
#if SUNDIALS_VERSION_MAJOR==2 && SUNDIALS_VERSION_MINOR<4 |
|
|
ERROR_REPORTER_HERE(ASC_PROG_WARNING, "Warning: boundary detection is" |
|
|
"unreliable with SUNDIALS pre version 2.4.0. Please update if you" |
|
|
"wish to use IDA for conditional integration"); |
|
|
#endif |
|
|
bnd_cond_states = ASC_NEW_ARRAY_CLEAR(int,enginedata->nbnds); |
|
|
|
|
|
/* identify if we're exactly *on* any boundaries currently */ |
|
|
for(i = 0; i < enginedata->nbnds; i++) { |
|
|
#ifdef IDA_BND_DEBUG |
|
|
relname = bnd_make_name(integ->system,enginedata->bndlist[i]); |
|
|
#endif |
|
|
bnd_cond_states[i] = bndman_calc_satisfied(enginedata->bndlist[i]); |
|
|
bnd_set_ida_first_cross(enginedata->bndlist[i],1); |
|
|
if(bndman_real_eval(enginedata->bndlist[i]) == 0) { |
|
|
/* if the residual for the boundary is zero (ie looks like we are *on* the boundary?) JP */ |
|
|
#ifdef IDA_BND_DEBUG |
|
|
CONSOLE_DEBUG("Boundary '%s': not set",relname); |
|
|
#endif |
|
|
bnd_not_set[i] = 1; |
|
|
all_bnds_set = 0; |
|
|
}else{ |
|
|
bnd_not_set[i] = 0; |
|
|
} |
|
|
#ifdef IDA_BND_DEBUG |
|
|
CONSOLE_DEBUG("Boundary '%s' is %d",relname,bnd_cond_states[i]); |
|
|
ASC_FREE(relname); |
|
|
#endif |
|
|
} |
|
|
CONSOLE_DEBUG("Setting up LRSlv..."); |
|
|
if(ida_setup_lrslv(integ,qrslv_ind,lrslv_ind)){ |
|
|
ERROR_REPORTER_HERE(ASC_USER_ERROR, "Idaanalyse failed."); |
|
|
return 1; |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tol = 0.0001*(samplelist_get(integ->samples, finish_index) |
|
|
- samplelist_get(integ->samples, start_index)) |
|
|
/samplelist_length(integ->samples); |
|
|
tmin = tol; |
|
|
|
|
|
/* -- set up the IntegratorReporter */ |
|
|
integrator_output_init(integ); |
|
|
|
|
|
/* -- store the initial values of all the stuff */ |
|
|
integrator_output_write(integ); |
|
|
integrator_output_write_obs(integ); |
|
|
|
|
|
/* specify where the returned values should be stored */ |
|
|
yret = ida_bnd_new_zero_NV(integ->n_y); |
|
|
ypret = ida_bnd_new_zero_NV(integ->n_y); |
|
|
|
|
|
/* advance solution in time, return values as yret and derivatives as ypret */ |
|
|
integ->currentstep = 1; |
|
|
|
|
|
rootdir = ASC_NEW_ARRAY_CLEAR(int,enginedata->nbnds); |
|
|
|
|
|
for(t_index = start_index + 1; t_index <= finish_index; ++t_index, ++integ->currentstep) { |
|
|
tout = samplelist_get(integ->samples, t_index); |
|
|
t0 = integrator_get_t(integ); |
|
|
|
|
|
asc_assert(tout > t0); |
|
|
|
|
|
#ifdef SOLVE_DEBUG |
|
|
CONSOLE_DEBUG("Integrating from t0 = %f to t = %f", t0, tout); |
|
|
#endif |
|
|
|
|
|
if(!after_root){ |
|
|
/* reset the root-crossing-direction flags */ |
|
|
for(i = 0; i < enginedata->nbnds; i++){ |
|
|
rootdir[i] = 0; |
|
|
#ifdef IDA_BND_DEBUG |
|
|
char *n = bnd_make_name(integ->system,enginedata->bndlist[i]); |
|
|
CONSOLE_DEBUG("Boundary '%s': bnd_cond_states[%d]=%d, bndman_calc_satisfied=%d; (trigger dirn=%s)" |
|
|
,n, i, bnd_cond_states[i], bndman_calc_satisfied(enginedata->bndlist[i]) |
|
|
,rootdir[i]==1?"UP":(rootdir[i]==-1?"DOWN":"both") |
|
|
); |
|
|
ASC_FREE(n); |
|
|
#endif |
|
|
} |
|
|
|
|
|
if(enginedata->nbnds) IDASetRootDirection(ida_mem, rootdir); |
|
|
} |
|
|
|
|
|
/** |
|
|
* do { solve routine } while(need_to_reintegrate) |
|
|
* If IDA detects a boundary sufficiently far from the desired output |
|
|
* time tout, then the system is reconfigured and IDASolve is recalled |
|
|
* up to the same tindex. |
|
|
* |
|
|
* @TODO "Sufficiently far" is currently tol = 0.0001, i.e complete arbitrary |
|
|
* Should reflect IDACalcIC's requirements? |
|
|
*/ |
|
|
do{ |
|
|
if(need_to_reinteg && !after_root){ |
|
|
#ifdef IDA_BND_DEBUG |
|
|
CONSOLE_DEBUG("Resuming integration from %f to %f", integrator_get_t(integ), tout); |
|
|
#endif |
|
|
integrator_output_write(integ); |
|
|
} |
|
|
|
|
|
#ifdef IDA_BND_DEBUG |
|
|
if(after_root && (tout - tret) > tol){ |
|
|
CONSOLE_DEBUG("Resuming integration from %f to %f", integrator_get_t(integ), tret + tmin); |
|
|
} |
|
|
#endif |
|
|
|
|
|
/* Control flags for boundary crossings */ |
|
|
need_to_reinteg = 0; |
|
|
skipping_output = 0; |
|
|
|
|
|
#ifdef ASC_SIGNAL_TRAPS |
|
|
Asc_SignalHandlerPushDefault(SIGINT); |
|
|
if(setjmp(g_int_env) == 0) { |
|
|
#endif |
|
|
if(after_root && (tout - tret) > tmin){ |
|
|
CONSOLE_DEBUG("Continuing (slightly after root)..."); |
|
|
/* after a root, we advance time by a tiny amount to ensure |
|
|
we traverse past the switching poing -- FIXME a bit hacky? */ |
|
|
flag = IDASolve(ida_mem, tret + tmin, &tret, yret, ypret, IDA_NORMAL); |
|
|
}else{ |
|
|
CONSOLE_DEBUG("Continuing immediately..."); |
|
|
/* if we didn't pass a root, continue immediately from prev end time */ |
|
|
flag = IDASolve(ida_mem, tout, &tret, yret, ypret, IDA_NORMAL); |
|
|
} |
|
|
if(after_root){ |
|
|
CONSOLE_DEBUG("Running logic solver after root..."); |
|
|
ida_log_solve(integ, lrslv_ind); |
|
|
after_root = 0; |
|
|
} |
|
|
#ifdef ASC_SIGNAL_TRAPS |
|
|
}else{ |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Caught interrupt"); |
|
|
flag = -555; |
|
|
} |
|
|
Asc_SignalHandlerPopDefault(SIGINT); |
|
|
#endif |
|
|
|
|
|
if(enginedata->nbnds) { |
|
|
if(flag == IDA_ROOT_RETURN){ |
|
|
#ifdef IDA_BND_DEBUG |
|
|
CONSOLE_DEBUG("IDA reports root found!"); |
|
|
#endif |
|
|
/* Store the root index */ |
|
|
rootsfound = ASC_NEW_ARRAY_CLEAR(int,enginedata->nbnds); |
|
|
|
|
|
if(IDA_SUCCESS != IDAGetRootInfo(ida_mem, rootsfound)) { |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to fetch boundary-crossing info"); |
|
|
return 14; |
|
|
} |
|
|
|
|
|
#ifdef IDA_BND_DEBUG |
|
|
/* write out the boundaries that were crossed */ |
|
|
for(i = 0; i < enginedata->nbnds; i++) { |
|
|
if(rootsfound[i]) { |
|
|
relname = bnd_make_name(integ->system, enginedata->bndlist[i]); |
|
|
CONSOLE_DEBUG("Boundary '%s' crossed at time x = %f, direction %s" |
|
|
,relname,tret,(rootsfound>0?"UP":"DOWN") |
|
|
); |
|
|
ASC_FREE(relname); |
|
|
} |
|
|
} |
|
|
#endif |
|
|
|
|
|
|
|
|
if(all_bnds_set == 0){ |
|
|
#ifdef IDA_BND_DEBUG |
|
|
CONSOLE_DEBUG("Unset bounds exist; evaluate them explicitly..."); |
|
|
#endif |
|
|
all_bnds_set = 1; |
|
|
for(i = 0; i < enginedata->nbnds; i++){ |
|
|
if(bnd_not_set[i]){ |
|
|
if(!rootsfound[i]){ |
|
|
bnd_cond_states[i] = bndman_calc_satisfied(enginedata->bndlist[i]); |
|
|
#ifdef IDA_BND_DEBUG |
|
|
relname = bnd_make_name(integ->system, enginedata->bndlist[i]); |
|
|
CONSOLE_DEBUG("Boundary '%s': bnd_cond_states[%d] = %d" |
|
|
,relname,i,bnd_cond_states[i] |
|
|
); |
|
|
ASC_FREE(relname); |
|
|
#endif |
|
|
}else all_bnds_set = 0; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
if(!after_root){ |
|
|
#ifdef IDA_BND_DEBUG |
|
|
CONSOLE_DEBUG("Just 'after_root'..."); |
|
|
for(i=0;i<enginedata->nbnds;++i){ |
|
|
relname = bnd_make_name(integ->system, enginedata->bndlist[i]); |
|
|
CONSOLE_DEBUG("Boundary '%s': bnd_cond_states[%d] = %d" |
|
|
,relname,i,bnd_cond_states[i] |
|
|
); |
|
|
ASC_FREE(relname); |
|
|
} |
|
|
#endif |
|
|
need_to_reconfigure = ida_cross_boundary(integ, rootsfound, |
|
|
bnd_cond_states, qrslv_ind, lrslv_ind); |
|
|
} |
|
|
if(need_to_reconfigure == 2) { |
|
|
ERROR_REPORTER_HERE(ASC_USER_ERROR,"Analysis after the boundary failed."); |
|
|
return 1; |
|
|
} |
|
|
if(need_to_reconfigure){ |
|
|
after_root = 1; |
|
|
if (ida_bnd_update_relist(integ) != 0) { |
|
|
/* system not square, failure */ |
|
|
return 1; |
|
|
} |
|
|
#ifdef IDA_BND_DEBUG |
|
|
CONSOLE_DEBUG("Boundary(ies) crossed at x=%f: need to reinitialize solver",tret); |
|
|
#endif |
|
|
/* so, now we need to restart the integration. we will assume that |
|
|
everything changes: number of variables, etc, etc, etc. */ |
|
|
|
|
|
/* Need to destroy and rebuild system */ |
|
|
/* IDAFree(ida_mem); */ |
|
|
/* ida_mem = IDACreate(); */ |
|
|
/* ida_reinit_integrator(integ, ida_mem, tout); */ |
|
|
/* n_y may have changed */ |
|
|
N_VDestroy_Serial(yret); |
|
|
N_VDestroy_Serial(ypret); |
|
|
|
|
|
yret = N_VNew_Serial(integ->n_y); |
|
|
ypret = N_VNew_Serial(integ->n_y); |
|
|
} /* need to reconfigure */ |
|
|
|
|
|
#if SUNDIALS_VERSION_MAJOR==2 && SUNDIALS_VERSION_MINOR>=4 |
|
|
/* set rootdir to -1*rootsfound to set IDA |
|
|
* to ignore double crossings */ |
|
|
for(i = 0; i < enginedata->nbnds; i++) { |
|
|
rootdir[i] = -1*rootsfound[i]; |
|
|
#ifdef IDA_BND_DEBUG |
|
|
char *n = bnd_make_name(integ->system,enginedata->bndlist[i]); |
|
|
CONSOLE_DEBUG("Set direction=%d for boundary '%s'",rootdir[i],n); |
|
|
ASC_FREE(n); |
|
|
#endif |
|
|
} |
|
|
IDASetRootDirection(ida_mem, rootdir);\ |
|
|
/*^^^ FIXME what about setting root direction for 'normal' roots? */ |
|
|
#endif |
|
|
ASC_FREE(rootsfound); |
|
|
ida_reinit_integrator(integ, ida_mem, tout); |
|
|
} /* IDA_ROOT_RETURN */ |
|
|
|
|
|
} /* nbnds */ |
|
|
|
|
|
/* Are we sufficiently far from tout to continue |
|
|
* integrating on this timestep? */ |
|
|
if(fabs(tret - tout) > tol) need_to_reinteg = 1; |
|
|
else if(after_root){ |
|
|
/* Advance timestep, skip writing output once*/ |
|
|
tout = samplelist_get(integ->samples, t_index + 1); |
|
|
skipping_output = 1; |
|
|
} |
|
|
|
|
|
}while (need_to_reinteg); /* end of solve time step */ |
|
|
|
|
|
if(flag != IDA_ROOT_RETURN && all_bnds_set == 0) { |
|
|
all_bnds_set = 1; |
|
|
for(i = 0; i < enginedata->nbnds; i++) { |
|
|
if(bnd_not_set[i]) { |
|
|
bnd_cond_states[i] = bndman_calc_satisfied(enginedata->bndlist[i]); |
|
|
#ifdef IDA_BND_DEBUG |
|
|
char *n = bnd_make_name(integ->system,enginedata->bndlist[i]); |
|
|
CONSOLE_DEBUG("Boundary '%s' not set; satisfied=%d",n,bnd_cond_states[i]); |
|
|
ASC_FREE(n); |
|
|
#endif |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
if(!skipping_output){ |
|
|
/* pass the values of everything back to the compiler */ |
|
|
integrator_set_t(integ, (double) tret); |
|
|
integrator_set_y(integ, NV_DATA_S(yret)); |
|
|
integrator_set_ydot(integ, NV_DATA_S(ypret)); |
|
|
|
|
|
/* -- store the current values of all the stuff */ |
|
|
if(integrator_output_write(integ) == 0) { |
|
|
ERROR_REPORTER_HERE(ASC_USER_WARNING,"Interrupted at t = %f", tout); |
|
|
break; |
|
|
} |
|
|
integrator_output_write_obs(integ); |
|
|
} |
|
|
|
|
|
if(flag < 0) { |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Failed to solve t = %f (IDASolve), error %d", tout, flag); |
|
|
break; |
|
|
} |
|
|
}/* loop through next sample timestep */ |
|
|
|
|
|
ASC_FREE(rootdir); |
|
|
|
|
|
/* -- close the IntegratorReporter */ |
|
|
integrator_output_close(integ); |
|
|
|
|
|
/* get optional outputs */ |
|
|
#ifdef STATS_DEBUG |
|
|
IntegratorIdaStats stats; |
|
|
if(IDA_SUCCESS == integrator_ida_stats(ida_mem, &stats)) { |
|
|
integrator_ida_write_stats(&stats); |
|
|
}else{ |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Unable to fetch stats!?!?"); |
|
|
} |
|
|
#endif |
|
|
|
|
|
/* free solution memory */ |
|
|
N_VDestroy_Serial(yret); |
|
|
N_VDestroy_Serial(ypret); |
|
|
|
|
|
/* free bnd states if appropriate */ |
|
|
if(enginedata->nbnds) { |
|
|
ASC_FREE(bnd_cond_states); |
|
|
} |
|
|
|
|
|
/* free solver memory */ |
|
|
IDAFree(&ida_mem); |
|
|
|
|
|
if(flag < -500) { |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Interrupted while attempting t = %f", tout); |
|
|
return -flag; |
|
|
} |
|
|
|
|
|
if(flag < 0) { |
|
|
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Solving aborted while attempting t = %f", tout); |
|
|
return 14; |
|
|
} |
|
|
|
|
|
/* all done, success */ |
|
|
return 0; |
|
|
} |
|
|
|
|
|
/*---------------------------------------------- |
|
|
STATS |
|
|
*/ |
|
|
|
|
|
/** |
|
|
A simple wrapper to the IDAGetIntegratorStats function. Returns all the |
|
|
status in a struct instead of separately. |
|
|
|
|
|
@return IDA_SUCCESS on success. |
|
|
*/ |
|
|
static int integrator_ida_stats(void *ida_mem, IntegratorIdaStats *s) { |
|
|
|
|
|
#if SUNDIALS_VERSION_MAJOR==2 && SUNDIALS_VERSION_MINOR==2 |
|
|
|
|
|
int res; |
|
|
|
|
|
/* |
|
|
There is an error in the documentation for this function in Sundials 2.2. |
|
|
According the the header file, the hinused stat is not provided. |
|
|
*/ |
|
|
res = IDAGetIntegratorStats(ida_mem, &s->nsteps, &s->nrevals, |
|
|
&s->nlinsetups, &s->netfails, &s->qlast, &s->qcur, &s->hlast, |
|
|
&s->hcur, &s->tcur); |
|
|
|
|
|
/* get the missing statistic */ |
|
|
IDAGetActualInitStep(ida_mem, &s->hinused); |
|
|
|
|
|
return res; |
|
|
#else |
|
|
|
|
|
return IDAGetIntegratorStats(ida_mem, &s->nsteps, &s->nrevals, &s->nlinsetups |
|
|
,&s->netfails, &s->qlast, &s->qcur, &s->hinused |
|
|
,&s->hlast, &s->hcur, &s->tcur |
|
|
); |
|
|
|
|
|
#endif |
|
|
} |
|
|
|
|
|
/* vim: set ts=4: */ |
|