1 |
/* ASCEND modelling environment |
2 |
Copyright (C) 2006-2011 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 |
Calculation of residuals, jacobian, etc, for the ASCEND wrapping of IDA. |
21 |
|
22 |
@see http://www.llnl.gov/casc/sundials/ |
23 |
*//* |
24 |
by John Pye, May 2006 |
25 |
*/ |
26 |
|
27 |
#define _GNU_SOURCE |
28 |
|
29 |
#include <signal.h> |
30 |
#include <setjmp.h> |
31 |
#include <fenv.h> |
32 |
#include <math.h> |
33 |
|
34 |
/* SUNDIALS includes */ |
35 |
#ifdef ASC_WITH_IDA |
36 |
|
37 |
#if SUNDIALS_VERSION_MAJOR==2 && SUNDIALS_VERSION_MINOR==2 |
38 |
# include <sundials/sundials_config.h> |
39 |
# include <sundials/sundials_nvector.h> |
40 |
# include <ida/ida_spgmr.h> |
41 |
# include <ida.h> |
42 |
# include <nvector_serial.h> |
43 |
#else |
44 |
# include <sundials/sundials_config.h> |
45 |
# include <nvector/nvector_serial.h> |
46 |
# include <ida/ida.h> |
47 |
#endif |
48 |
|
49 |
# include <sundials/sundials_dense.h> |
50 |
# include <ida/ida_spgmr.h> |
51 |
# include <ida/ida_spbcgs.h> |
52 |
# include <ida/ida_sptfqmr.h> |
53 |
# include <ida/ida_dense.h> |
54 |
|
55 |
# ifndef IDA_SUCCESS |
56 |
# error "Failed to include SUNDIALS IDA header file" |
57 |
# endif |
58 |
#else |
59 |
# error "If you're building this file, you should have ASC_WITH_IDA" |
60 |
#endif |
61 |
|
62 |
#ifdef ASC_WITH_MMIO |
63 |
# include <mmio.h> |
64 |
#endif |
65 |
|
66 |
#include <ascend/general/platform.h> |
67 |
#include <ascend/utilities/error.h> |
68 |
#include <ascend/utilities/ascSignal.h> |
69 |
#include <ascend/general/panic.h> |
70 |
#include <ascend/compiler/instance_enum.h> |
71 |
|
72 |
#include <ascend/system/slv_client.h> |
73 |
#include <ascend/system/relman.h> |
74 |
#include <ascend/system/block.h> |
75 |
#include <ascend/system/slv_stdcalls.h> |
76 |
#include <ascend/system/jacobian.h> |
77 |
#include <ascend/system/bndman.h> |
78 |
|
79 |
#include <ascend/utilities/config.h> |
80 |
#include <ascend/integrator/integrator.h> |
81 |
|
82 |
#include "idalinear.h" |
83 |
#include "idaanalyse.h" |
84 |
#include "ida_impl.h" |
85 |
|
86 |
/* |
87 |
for cases where we don't have SUNDIALS_VERSION_MINOR defined, guess version 2.2 |
88 |
*/ |
89 |
#ifndef SUNDIALS_VERSION_MINOR |
90 |
# ifdef __GNUC__ |
91 |
# warning "GUESSING SUNDIALS VERSION 2.2" |
92 |
# endif |
93 |
# define SUNDIALS_VERSION_MINOR 2 |
94 |
#endif |
95 |
#ifndef SUNDIALS_VERSION_MAJOR |
96 |
# define SUNDIALS_VERSION_MAJOR 2 |
97 |
#endif |
98 |
|
99 |
/* SUNDIALS 2.4.0 introduces new DlsMat in place of DenseMat */ |
100 |
#if SUNDIALS_VERSION_MAJOR==2 && SUNDIALS_VERSION_MINOR==4 |
101 |
# define IDA_MTX_T DlsMat |
102 |
# define IDADENSE_SUCCESS IDADLS_SUCCESS |
103 |
# define IDADENSE_MEM_NULL IDADLS_MEM_NULL |
104 |
# define IDADENSE_ILL_INPUT IDADLS_ILL_INPUT |
105 |
# define IDADENSE_MEM_FAIL IDADLS_MEM_FAIL |
106 |
#else |
107 |
# define IDA_MTX_T DenseMat |
108 |
#endif |
109 |
|
110 |
/* #define FEX_DEBUG */ |
111 |
#define JEX_DEBUG |
112 |
/* #define DJEX_DEBUG */ |
113 |
#define SOLVE_DEBUG |
114 |
#define STATS_DEBUG |
115 |
#define PREC_DEBUG |
116 |
/* #define ROOT_DEBUG */ |
117 |
|
118 |
/* #define DIFFINDEX_DEBUG */ |
119 |
/* #define ANALYSE_DEBUG */ |
120 |
/* #define DESTROY_DEBUG */ |
121 |
/* #define MATRIX_DEBUG */ |
122 |
|
123 |
/*-------------------------------------------------- |
124 |
RESIDUALS AND JACOBIAN AND IDAROOTFN |
125 |
*/ |
126 |
|
127 |
#if 0 |
128 |
typedef void (SignalHandlerFn)(int); |
129 |
SignalHandlerFn integrator_ida_sig; |
130 |
SignalHandlerFn *integrator_ida_sig_old; |
131 |
jmp_buf integrator_ida_jmp_buf; |
132 |
fenv_t integrator_ida_fenv_old; |
133 |
|
134 |
|
135 |
void integrator_ida_write_feinfo(){ |
136 |
int f; |
137 |
f = fegetexcept(); |
138 |
CONSOLE_DEBUG("Locating nature of exception..."); |
139 |
if(f & FE_DIVBYZERO)ERROR_REPORTER_HERE(ASC_PROG_ERR,"DIV BY ZERO"); |
140 |
if(f & FE_INEXACT)ERROR_REPORTER_HERE(ASC_PROG_ERR,"INEXACT"); |
141 |
if(f & FE_INVALID)ERROR_REPORTER_HERE(ASC_PROG_ERR,"INVALID"); |
142 |
if(f & FE_OVERFLOW)ERROR_REPORTER_HERE(ASC_PROG_ERR,"OVERFLOW"); |
143 |
if(f & FE_UNDERFLOW)ERROR_REPORTER_HERE(ASC_PROG_ERR,"UNDERFLOW"); |
144 |
if(f==0)ERROR_REPORTER_HERE(ASC_PROG_ERR,"FLAGS ARE CLEAR?!?"); |
145 |
} |
146 |
|
147 |
void integrator_ida_sig(int sig){ |
148 |
/* the wrong signal: rethrow to the default handler */ |
149 |
if(sig!=SIGFPE){ |
150 |
signal(SIGFPE,SIG_DFL); |
151 |
raise(sig); |
152 |
} |
153 |
integrator_ida_write_feinfo(); |
154 |
CONSOLE_DEBUG("Caught SIGFPE=%d (in signal handler). Jumping to...",sig); |
155 |
longjmp(integrator_ida_jmp_buf,sig); |
156 |
} |
157 |
#endif |
158 |
|
159 |
/** |
160 |
Function to evaluate system residuals, in the form required for IDA. |
161 |
|
162 |
Given tt, yy and yp, we need to evaluate and return rr. |
163 |
|
164 |
@param tt current value of indep variable (time) |
165 |
@param yy current values of dependent variable vector |
166 |
@param yp current values of derivatives of dependent variables |
167 |
@param rr the output residual vector (is we're returning data to) |
168 |
@param res_data pointer to our stuff (integ in this case). |
169 |
|
170 |
@return 0 on success, positive on recoverable error, and |
171 |
negative on unrecoverable error. |
172 |
*/ |
173 |
int integrator_ida_fex(realtype tt, N_Vector yy, N_Vector yp, N_Vector rr, void *res_data){ |
174 |
IntegratorSystem *integ; |
175 |
IntegratorIdaData *enginedata; |
176 |
int i, calc_ok, is_error; |
177 |
struct rel_relation** relptr; |
178 |
double resid; |
179 |
char *relname; |
180 |
#ifdef FEX_DEBUG |
181 |
char *varname; |
182 |
char diffname[30]; |
183 |
#endif |
184 |
|
185 |
integ = (IntegratorSystem *)res_data; |
186 |
enginedata = integrator_ida_enginedata(integ); |
187 |
|
188 |
#ifdef FEX_DEBUG |
189 |
/* fprintf(stderr,"\n\n"); */ |
190 |
CONSOLE_DEBUG("EVALUTE RESIDUALS..."); |
191 |
#endif |
192 |
|
193 |
if(NV_LENGTH_S(rr)!=enginedata->nrels){ |
194 |
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Invalid residuals nrels!=length(rr)"); |
195 |
return -1; /* unrecoverable */ |
196 |
} |
197 |
|
198 |
/* pass the values of everything back to the compiler */ |
199 |
integrator_set_t(integ, (double)tt); |
200 |
integrator_set_y(integ, NV_DATA_S(yy)); |
201 |
integrator_set_ydot(integ, NV_DATA_S(yp)); |
202 |
|
203 |
/* perform bounds checking on all variables */ |
204 |
if(slv_check_bounds(integ->system, 0, -1, NULL)){ |
205 |
/* ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Variable(s) out of bounds"); */ |
206 |
return 1; |
207 |
} |
208 |
|
209 |
/* evaluate each residual in the rellist */ |
210 |
is_error = 0; |
211 |
relptr = enginedata->rellist; |
212 |
|
213 |
|
214 |
#ifdef ASC_SIGNAL_TRAPS |
215 |
if(enginedata->safeeval){ |
216 |
Asc_SignalHandlerPush(SIGFPE,SIG_IGN); |
217 |
}else{ |
218 |
# ifdef FEX_DEBUG |
219 |
ERROR_REPORTER_HERE(ASC_PROG_ERR,"SETTING TO CATCH SIGFPE..."); |
220 |
# endif |
221 |
Asc_SignalHandlerPushDefault(SIGFPE); |
222 |
} |
223 |
|
224 |
if (SETJMP(g_fpe_env)==0) { |
225 |
#endif |
226 |
|
227 |
|
228 |
for(i=0, relptr = enginedata->rellist; |
229 |
i< enginedata->nrels && relptr != NULL; |
230 |
++i, ++relptr |
231 |
){ |
232 |
resid = relman_eval(*relptr, &calc_ok, enginedata->safeeval); |
233 |
|
234 |
NV_Ith_S(rr,i) = resid; |
235 |
if(!calc_ok){ |
236 |
relname = rel_make_name(integ->system, *relptr); |
237 |
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Calculation error in rel '%s'",relname); |
238 |
ASC_FREE(relname); |
239 |
/* presumable some output already made? */ |
240 |
is_error = 1; |
241 |
}/*else{ |
242 |
CONSOLE_DEBUG("Calc OK"); |
243 |
}*/ |
244 |
} |
245 |
|
246 |
if(!is_error){ |
247 |
for(i=0;i< enginedata->nrels; ++i){ |
248 |
if(isnan(NV_Ith_S(rr,i))){ |
249 |
ERROR_REPORTER_HERE(ASC_PROG_ERR,"NAN detected in residual %d",i); |
250 |
is_error=1; |
251 |
} |
252 |
} |
253 |
#ifdef FEX_DEBUG |
254 |
if(!is_error){ |
255 |
CONSOLE_DEBUG("No NAN detected"); |
256 |
} |
257 |
#endif |
258 |
} |
259 |
|
260 |
#ifdef ASC_SIGNAL_TRAPS |
261 |
}else{ |
262 |
relname = rel_make_name(integ->system, *relptr); |
263 |
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Floating point error (SIGFPE) in rel '%s'",relname); |
264 |
ASC_FREE(relname); |
265 |
is_error = 1; |
266 |
} |
267 |
|
268 |
if(enginedata->safeeval){ |
269 |
Asc_SignalHandlerPop(SIGFPE,SIG_IGN); |
270 |
}else{ |
271 |
Asc_SignalHandlerPopDefault(SIGFPE); |
272 |
} |
273 |
#endif |
274 |
|
275 |
|
276 |
#ifdef FEX_DEBUG |
277 |
/* output residuals to console */ |
278 |
CONSOLE_DEBUG("RESIDUAL OUTPUT"); |
279 |
fprintf(stderr,"index\t%25s\t%25s\t%s\n","y","ydot","resid"); |
280 |
for(i=0; i<integ->n_y; ++i){ |
281 |
varname = var_make_name(integ->system,integ->y[i]); |
282 |
fprintf(stderr,"%d\t%15s=%10f\t",i,varname,NV_Ith_S(yy,i)); |
283 |
if(integ->ydot[i]){ |
284 |
varname = var_make_name(integ->system,integ->ydot[i]); |
285 |
fprintf(stderr,"%15s=%10f\t",varname,NV_Ith_S(yp,i)); |
286 |
}else{ |
287 |
snprintf(diffname,99,"diff(%s)",varname); |
288 |
fprintf(stderr,"%15s=%10f\t",diffname,NV_Ith_S(yp,i)); |
289 |
} |
290 |
ASC_FREE(varname); |
291 |
relname = rel_make_name(integ->system,enginedata->rellist[i]); |
292 |
fprintf(stderr,"'%s'=%f (%p)\n",relname,NV_Ith_S(rr,i),enginedata->rellist[i]); |
293 |
} |
294 |
#endif |
295 |
|
296 |
if(is_error){ |
297 |
return 1; |
298 |
} |
299 |
|
300 |
#ifdef FEX_DEBUG |
301 |
CONSOLE_DEBUG("RESIDUAL OK"); |
302 |
#endif |
303 |
return 0; |
304 |
} |
305 |
|
306 |
/** |
307 |
Dense Jacobian evaluation. Only suitable for small problems! |
308 |
Has been seen working for problems up to around 2000 vars, FWIW. |
309 |
*/ |
310 |
#if SUNDIALS_VERSION_MAJOR==2 && SUNDIALS_VERSION_MINOR>=4 |
311 |
int integrator_ida_djex(int Neq, realtype tt, realtype c_j |
312 |
, N_Vector yy, N_Vector yp, N_Vector rr |
313 |
, IDA_MTX_T Jac, void *jac_data |
314 |
, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3 |
315 |
){ |
316 |
#else |
317 |
int integrator_ida_djex(long int Neq, realtype tt |
318 |
, N_Vector yy, N_Vector yp, N_Vector rr |
319 |
, realtype c_j, void *jac_data, IDA_MTX_T Jac |
320 |
, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3 |
321 |
){ |
322 |
#endif |
323 |
IntegratorSystem *integ; |
324 |
IntegratorIdaData *enginedata; |
325 |
char *relname; |
326 |
#ifdef DJEX_DEBUG |
327 |
struct var_variable **varlist; |
328 |
char *varname; |
329 |
#endif |
330 |
struct rel_relation **relptr; |
331 |
int i; |
332 |
double *derivatives; |
333 |
struct var_variable **variables; |
334 |
int count, j; |
335 |
int status, is_error = 0; |
336 |
|
337 |
integ = (IntegratorSystem *)jac_data; |
338 |
enginedata = integrator_ida_enginedata(integ); |
339 |
|
340 |
/* allocate space for returns from relman_diff3 */ |
341 |
/** @TODO instead, we should use 'tmp1' and 'tmp2' here... */ |
342 |
variables = ASC_NEW_ARRAY(struct var_variable*, NV_LENGTH_S(yy) * 2); |
343 |
derivatives = ASC_NEW_ARRAY(double, NV_LENGTH_S(yy) * 2); |
344 |
|
345 |
/* pass the values of everything back to the compiler */ |
346 |
integrator_set_t(integ, (double)tt); |
347 |
integrator_set_y(integ, NV_DATA_S(yy)); |
348 |
integrator_set_ydot(integ, NV_DATA_S(yp)); |
349 |
|
350 |
/* perform bounds checking on all variables */ |
351 |
if(slv_check_bounds(integ->system, 0, -1, NULL)){ |
352 |
/* ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Variable(s) out of bounds"); */ |
353 |
return 1; |
354 |
} |
355 |
|
356 |
#ifdef DJEX_DEBUG |
357 |
varlist = slv_get_solvers_var_list(integ->system); |
358 |
|
359 |
/* print vars */ |
360 |
for(i=0; i < integ->n_y; ++i){ |
361 |
varname = var_make_name(integ->system, integ->y[i]); |
362 |
CONSOLE_DEBUG("%s = %f",varname,NV_Ith_S(yy,i)); |
363 |
asc_assert(NV_Ith_S(yy,i) == var_value(integ->y[i])); |
364 |
ASC_FREE(varname); |
365 |
} |
366 |
|
367 |
/* print derivatives */ |
368 |
for(i=0; i < integ->n_y; ++i){ |
369 |
if(integ->ydot[i]){ |
370 |
varname = var_make_name(integ->system, integ->ydot[i]); |
371 |
CONSOLE_DEBUG("%s = %f =%g",varname,NV_Ith_S(yp,i),var_value(integ->ydot[i])); |
372 |
ASC_FREE(varname); |
373 |
}else{ |
374 |
varname = var_make_name(integ->system, integ->y[i]); |
375 |
CONSOLE_DEBUG("diff(%s) = %g",varname,NV_Ith_S(yp,i)); |
376 |
ASC_FREE(varname); |
377 |
} |
378 |
} |
379 |
|
380 |
/* print step size */ |
381 |
CONSOLE_DEBUG("<c_j> = %g",c_j); |
382 |
#endif |
383 |
|
384 |
/* build up the dense jacobian matrix... */ |
385 |
status = 0; |
386 |
for(i=0, relptr = enginedata->rellist; |
387 |
i< enginedata->nrels && relptr != NULL; |
388 |
++i, ++relptr |
389 |
){ |
390 |
/* get derivatives for this particular relation */ |
391 |
status = relman_diff3(*relptr, &enginedata->vfilter, derivatives, variables, &count, enginedata->safeeval); |
392 |
|
393 |
if(status){ |
394 |
relname = rel_make_name(integ->system, *relptr); |
395 |
CONSOLE_DEBUG("ERROR calculating derivatives for relation '%s'",relname); |
396 |
ASC_FREE(relname); |
397 |
is_error = 1; |
398 |
break; |
399 |
} |
400 |
|
401 |
/* output what's going on here ... */ |
402 |
#ifdef DJEX_DEBUG |
403 |
relname = rel_make_name(integ->system, *relptr); |
404 |
fprintf(stderr,"%d: '%s': ",i,relname); |
405 |
for(j=0;j<count;++j){ |
406 |
varname = var_make_name(integ->system, variables[j]); |
407 |
if(var_deriv(variables[j])){ |
408 |
fprintf(stderr," '%s'=",varname); |
409 |
fprintf(stderr,"ydot[%d]",integrator_ida_diffindex(integ,variables[j])); |
410 |
}else{ |
411 |
fprintf(stderr," '%s'=y[%d]",varname,var_sindex(variables[j])); |
412 |
} |
413 |
ASC_FREE(varname); |
414 |
} |
415 |
/* relname is freed further down */ |
416 |
fprintf(stderr,"\n"); |
417 |
#endif |
418 |
|
419 |
/* insert values into the Jacobian row in appropriate spots (can assume Jac starts with zeros -- IDA manual) */ |
420 |
for(j=0; j < count; ++j){ |
421 |
#ifdef DJEX_DEBUG |
422 |
varname = var_make_name(integ->system,variables[j]); |
423 |
fprintf(stderr,"d(%s)/d(%s) = %g",relname,varname,derivatives[j]); |
424 |
ASC_FREE(varname); |
425 |
#endif |
426 |
if(!var_deriv(variables[j])){ |
427 |
#ifdef DJEX_DEBUG |
428 |
fprintf(stderr," --> J[%d,%d] += %g\n", i,j,derivatives[j]); |
429 |
asc_assert(var_sindex(variables[j]) >= 0); |
430 |
ASC_ASSERT_LT(var_sindex(variables[j]) , Neq); |
431 |
#endif |
432 |
DENSE_ELEM(Jac,i,var_sindex(variables[j])) += derivatives[j]; |
433 |
}else{ |
434 |
DENSE_ELEM(Jac,i,integrator_ida_diffindex(integ,variables[j])) += derivatives[j] * c_j; |
435 |
#ifdef DJEX_DEBUG |
436 |
fprintf(stderr," --> * c_j --> J[%d,%d] += %g\n", i,j,derivatives[j] * c_j); |
437 |
#endif |
438 |
} |
439 |
} |
440 |
} |
441 |
|
442 |
#ifdef DJEX_DEBUG |
443 |
ASC_FREE(relname); |
444 |
CONSOLE_DEBUG("PRINTING JAC"); |
445 |
fprintf(stderr,"\t"); |
446 |
for(j=0; j < integ->n_y; ++j){ |
447 |
if(j)fprintf(stderr,"\t"); |
448 |
varname = var_make_name(integ->system,integ->y[j]); |
449 |
fprintf(stderr,"%11s",varname); |
450 |
ASC_FREE(varname); |
451 |
} |
452 |
fprintf(stderr,"\n"); |
453 |
for(i=0; i < enginedata->nrels; ++i){ |
454 |
relname = rel_make_name(integ->system, enginedata->rellist[i]); |
455 |
fprintf(stderr,"%s\t",relname); |
456 |
ASC_FREE(relname); |
457 |
|
458 |
for(j=0; j < integ->n_y; ++j){ |
459 |
if(j)fprintf(stderr,"\t"); |
460 |
fprintf(stderr,"%11.2e",DENSE_ELEM(Jac,i,j)); |
461 |
} |
462 |
fprintf(stderr,"\n"); |
463 |
} |
464 |
#endif |
465 |
|
466 |
/* test for NANs */ |
467 |
if(!is_error){ |
468 |
for(i=0;i< enginedata->nrels; ++i){ |
469 |
for(j=0;j<integ->n_y;++j){ |
470 |
if(isnan(DENSE_ELEM(Jac,i,j))){ |
471 |
ERROR_REPORTER_HERE(ASC_PROG_ERR,"NAN detected in jacobian J[%d,%d]",i,j); |
472 |
is_error=1; |
473 |
} |
474 |
} |
475 |
} |
476 |
#ifdef DJEX_DEBUG |
477 |
if(!is_error){ |
478 |
CONSOLE_DEBUG("No NAN detected"); |
479 |
} |
480 |
#endif |
481 |
} |
482 |
|
483 |
/* if(integrator_ida_check_diffindex(integ)){ |
484 |
is_error = 1; |
485 |
}*/ |
486 |
|
487 |
if(is_error){ |
488 |
ERROR_REPORTER_HERE(ASC_PROG_ERR,"There were derivative evaluation errors in the dense jacobian"); |
489 |
return 1; |
490 |
} |
491 |
|
492 |
#ifdef DJEX_DEBUG |
493 |
CONSOLE_DEBUG("DJEX RETURNING 0"); |
494 |
/* ASC_PANIC("Quitting"); */ |
495 |
#endif |
496 |
return 0; |
497 |
} |
498 |
|
499 |
/** |
500 |
Function to evaluate the product J*v, in the form required for IDA (see IDASpilsSetJacTimesVecFn) |
501 |
|
502 |
Given tt, yy, yp, rr and v, we need to evaluate and return Jv. |
503 |
|
504 |
@param tt current value of the independent variable (time, t) |
505 |
@param yy current value of the dependent variable vector, y(t). |
506 |
@param yp current value of y'(t). |
507 |
@param rr current value of the residual vector F(t, y, y'). |
508 |
@param v the vector by which the Jacobian must be multiplied to the right. |
509 |
@param Jv the output vector computed |
510 |
@param c_j the scalar in the system Jacobian, proportional to the inverse of the step size ($ \alpha$ in Eq. (3.5) ). |
511 |
@param jac_data pointer to our stuff (integ in this case, passed into IDA via IDASp*SetJacTimesVecFn.) |
512 |
@param tmp1 @see tmp2 |
513 |
@param tmp2 (as well as tmp1) pointers to memory allocated for variables of type N_Vector for use here as temporary storage or work space. |
514 |
@return 0 on success |
515 |
*/ |
516 |
int integrator_ida_jvex(realtype tt, N_Vector yy, N_Vector yp, N_Vector rr |
517 |
, N_Vector v, N_Vector Jv, realtype c_j |
518 |
, void *jac_data, N_Vector tmp1, N_Vector tmp2 |
519 |
){ |
520 |
IntegratorSystem *integ; |
521 |
IntegratorIdaData *enginedata; |
522 |
int i, j, is_error=0; |
523 |
struct rel_relation** relptr = 0; |
524 |
char *relname; |
525 |
int status; |
526 |
double Jv_i; |
527 |
|
528 |
struct var_variable **variables; |
529 |
double *derivatives; |
530 |
int count; |
531 |
struct var_variable **varlist; |
532 |
#ifdef JEX_DEBUG |
533 |
|
534 |
CONSOLE_DEBUG("EVALUATING JACOBIAN..."); |
535 |
#endif |
536 |
|
537 |
integ = (IntegratorSystem *)jac_data; |
538 |
enginedata = integrator_ida_enginedata(integ); |
539 |
varlist = slv_get_solvers_var_list(integ->system); |
540 |
|
541 |
/* pass the values of everything back to the compiler */ |
542 |
integrator_set_t(integ, (double)tt); |
543 |
integrator_set_y(integ, NV_DATA_S(yy)); |
544 |
integrator_set_ydot(integ, NV_DATA_S(yp)); |
545 |
/* no real use for residuals (rr) here, I don't think? */ |
546 |
|
547 |
/* allocate space for returns from relman_diff2: we *should* be able to use 'tmp1' and 'tmp2' here... */ |
548 |
|
549 |
i = NV_LENGTH_S(yy) * 2; |
550 |
#ifdef JEX_DEBUG |
551 |
CONSOLE_DEBUG("Allocating 'variables' with length %d",i); |
552 |
#endif |
553 |
variables = ASC_NEW_ARRAY(struct var_variable*, i); |
554 |
derivatives = ASC_NEW_ARRAY(double, i); |
555 |
|
556 |
/* evaluate the derivatives... */ |
557 |
/* J = dG_dy = dF_dy + alpha * dF_dyp */ |
558 |
|
559 |
#ifdef ASC_SIGNAL_TRAPS |
560 |
Asc_SignalHandlerPushDefault(SIGFPE); |
561 |
if (SETJMP(g_fpe_env)==0) { |
562 |
#endif |
563 |
for(i=0, relptr = enginedata->rellist; |
564 |
i< enginedata->nrels && relptr != NULL; |
565 |
++i, ++relptr |
566 |
){ |
567 |
/* get derivatives for this particular relation */ |
568 |
status = relman_diff3(*relptr, &enginedata->vfilter, derivatives, variables, &count, enginedata->safeeval); |
569 |
#ifdef JEX_DEBUG |
570 |
CONSOLE_DEBUG("Got derivatives against %d matching variables, status = %d", count,status); |
571 |
#endif |
572 |
|
573 |
if(status){ |
574 |
relname = rel_make_name(integ->system, *relptr); |
575 |
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Calculation error in rel '%s'",relname); |
576 |
ASC_FREE(relname); |
577 |
is_error = 1; |
578 |
break; |
579 |
} |
580 |
|
581 |
/* |
582 |
Now we have the derivatives wrt each alg/diff variable in the |
583 |
present equation. variables[] points into the varlist. need |
584 |
a mapping from the varlist to the y and ydot lists. |
585 |
*/ |
586 |
|
587 |
Jv_i = 0; |
588 |
for(j=0; j < count; ++j){ |
589 |
/* CONSOLE_DEBUG("j = %d, variables[j] = %d, n_y = %ld", j, variables[j], integ->n_y); |
590 |
varname = var_make_name(integ->system, enginedata->varlist[variables[j]]); |
591 |
if(varname){ |
592 |
CONSOLE_DEBUG("Variable %d '%s' derivative = %f", variables[j],varname,derivatives[j]); |
593 |
ASC_FREE(varname); |
594 |
}else{ |
595 |
CONSOLE_DEBUG("Variable %d (UNKNOWN!): derivative = %f",variables[j],derivatives[j]); |
596 |
} |
597 |
*/ |
598 |
|
599 |
/* we don't calculate derivatives wrt indep var */ |
600 |
asc_assert(variables[j]>=0); |
601 |
if(variables[j] == integ->x) continue; |
602 |
#ifdef JEX_DEBUG |
603 |
CONSOLE_DEBUG("j = %d: variables[j] = %d",j,var_sindex(variables[j])); |
604 |
#endif |
605 |
if(var_deriv(variables[j])){ |
606 |
#define DIFFINDEX integrator_ida_diffindex(integ,variables[j]) |
607 |
#ifdef JEX_DEBUG |
608 |
fprintf(stderr,"Jv[%d] += %f (dF[%d]/dydot[%d] = %f, v[%d] = %f)\n", i |
609 |
, derivatives[j] * NV_Ith_S(v,DIFFINDEX) |
610 |
, i, DIFFINDEX, derivatives[j] |
611 |
, DIFFINDEX, NV_Ith_S(v,DIFFINDEX) |
612 |
); |
613 |
#endif |
614 |
asc_assert(integ->ydot[DIFFINDEX]==variables[j]); |
615 |
Jv_i += derivatives[j] * NV_Ith_S(v,DIFFINDEX) * c_j; |
616 |
#undef DIFFINDEX |
617 |
}else{ |
618 |
#define VARINDEX var_sindex(variables[j]) |
619 |
#ifdef JEX_DEBUG |
620 |
asc_assert(integ->y[VARINDEX]==variables[j]); |
621 |
fprintf(stderr,"Jv[%d] += %f (dF[%d]/dy[%d] = %f, v[%d] = %f)\n" |
622 |
, i |
623 |
, derivatives[j] * NV_Ith_S(v,VARINDEX) |
624 |
, i, VARINDEX, derivatives[j] |
625 |
, VARINDEX, NV_Ith_S(v,VARINDEX) |
626 |
); |
627 |
#endif |
628 |
Jv_i += derivatives[j] * NV_Ith_S(v,VARINDEX); |
629 |
#undef VARINDEX |
630 |
} |
631 |
} |
632 |
|
633 |
NV_Ith_S(Jv,i) = Jv_i; |
634 |
#ifdef JEX_DEBUG |
635 |
CONSOLE_DEBUG("rel = %p",*relptr); |
636 |
relname = rel_make_name(integ->system, *relptr); |
637 |
CONSOLE_DEBUG("'%s': Jv[%d] = %f", relname, i, NV_Ith_S(Jv,i)); |
638 |
ASC_FREE(relname); |
639 |
return 1; |
640 |
#endif |
641 |
} |
642 |
#ifdef ASC_SIGNAL_TRAPS |
643 |
}else{ |
644 |
relname = rel_make_name(integ->system, *relptr); |
645 |
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Floating point error (SIGFPE) in rel '%s'",relname); |
646 |
ASC_FREE(relname); |
647 |
is_error = 1; |
648 |
} |
649 |
Asc_SignalHandlerPopDefault(SIGFPE); |
650 |
#endif |
651 |
|
652 |
if(is_error){ |
653 |
CONSOLE_DEBUG("SOME ERRORS FOUND IN EVALUATION"); |
654 |
return 1; |
655 |
} |
656 |
return 0; |
657 |
} |
658 |
|
659 |
/* sparse jacobian evaluation for IDAASCEND sparse direct linear solver */ |
660 |
int integrator_ida_sjex(long int Neq, realtype tt |
661 |
, N_Vector yy, N_Vector yp, N_Vector rr |
662 |
, realtype c_j, void *jac_data, mtx_matrix_t Jac |
663 |
, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3 |
664 |
){ |
665 |
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Not implemented"); |
666 |
return -1; |
667 |
} |
668 |
|
669 |
/* root finding function */ |
670 |
|
671 |
int integrator_ida_rootfn(realtype tt, N_Vector yy, N_Vector yp, realtype *gout, void *g_data){ |
672 |
IntegratorSystem *integ; |
673 |
IntegratorIdaData *enginedata; |
674 |
int i; |
675 |
#ifdef ROOT_DEBUG |
676 |
char *relname; |
677 |
#endif |
678 |
|
679 |
asc_assert(g_data!=NULL); |
680 |
integ = (IntegratorSystem *)g_data; |
681 |
enginedata = integrator_ida_enginedata(integ); |
682 |
|
683 |
/* pass the values of everything back to the compiler */ |
684 |
integrator_set_t(integ, (double)tt); |
685 |
integrator_set_y(integ, NV_DATA_S(yy)); |
686 |
integrator_set_ydot(integ, NV_DATA_S(yp)); |
687 |
|
688 |
asc_assert(gout!=NULL); |
689 |
|
690 |
#ifdef ROOT_DEBUG |
691 |
CONSOLE_DEBUG("t = %f",tt); |
692 |
#endif |
693 |
|
694 |
/* evaluate the residuals for each of the boundaries */ |
695 |
for(i=0; i < enginedata->nbnds; ++i){ |
696 |
switch(bnd_kind(enginedata->bndlist[i])){ |
697 |
case e_bnd_rel: /* real-valued boundary relation */ |
698 |
gout[i] = bndman_real_eval(enginedata->bndlist[i]); |
699 |
#ifdef ROOT_DEBUG |
700 |
relname = bnd_make_name(integ->system,enginedata->bndlist[i]); |
701 |
CONSOLE_DEBUG("gout[%d] = %f (boundary '%s')", i, gout[i], relname); |
702 |
ASC_FREE(relname); |
703 |
#endif |
704 |
break; |
705 |
case e_bnd_logrel: |
706 |
if(bndman_log_eval(enginedata->bndlist[i])){ |
707 |
CONSOLE_DEBUG("bnd[%d] = TRUE",i); |
708 |
#ifdef ROOT_DEBUG |
709 |
relname = bnd_make_name(integ->system,enginedata->bndlist[i]); |
710 |
CONSOLE_DEBUG("gout[%d] = %f (boundary '%s')", i, gout[i], relname); |
711 |
ASC_FREE(relname); |
712 |
#endif |
713 |
gout[i] = +1.0; |
714 |
}else{ |
715 |
CONSOLE_DEBUG("bnd[%d] = FALSE",i); |
716 |
gout[i] = -1.0; |
717 |
} |
718 |
break; |
719 |
case e_bnd_undefined: |
720 |
ERROR_REPORTER_HERE(ASC_PROG_ERR,"Invalid boundary type e_bnd_undefined"); |
721 |
return 1; |
722 |
} |
723 |
} |
724 |
|
725 |
return 0; /* no way to detect errors in bndman_*_eval at this stage */ |
726 |
} |
727 |
|