Cantera  3.0.0
Loading...
Searching...
No Matches
CVodesIntegrator.cpp
Go to the documentation of this file.
1//! @file CVodesIntegrator.cpp
2
3// This file is part of Cantera. See License.txt in the top-level directory or
4// at https://cantera.org/license.txt for license and copyright information.
5
8
9#include <iostream>
10using namespace std;
11
12#include "cantera/numerics/sundials_headers.h"
13
14namespace {
15
16N_Vector newNVector(size_t N, Cantera::SundialsContext& context)
17{
18#if CT_SUNDIALS_VERSION >= 60
19 return N_VNew_Serial(static_cast<sd_size_t>(N), context.get());
20#else
21 return N_VNew_Serial(static_cast<sd_size_t>(N));
22#endif
23}
24
25} // end anonymous namespace
26
27namespace Cantera
28{
29
30extern "C" {
31 /**
32 * Function called by cvodes to evaluate ydot given y. The CVODE integrator
33 * allows passing in a void* pointer to access external data. This pointer
34 * is cast to a pointer to a instance of class FuncEval. The equations to be
35 * integrated should be specified by deriving a class from FuncEval that
36 * evaluates the desired equations.
37 * @ingroup odeGroup
38 */
39 static int cvodes_rhs(realtype t, N_Vector y, N_Vector ydot, void* f_data)
40 {
41 FuncEval* f = (FuncEval*) f_data;
42 return f->evalNoThrow(t, NV_DATA_S(y), NV_DATA_S(ydot));
43 }
44
45 //! Function called by CVodes when an error is encountered instead of
46 //! writing to stdout. Here, save the error message provided by CVodes so
47 //! that it can be included in the subsequently raised CanteraError.
48 static void cvodes_err(int error_code, const char* module,
49 const char* function, char* msg, void* eh_data)
50 {
51 CVodesIntegrator* integrator = (CVodesIntegrator*) eh_data;
52 integrator->m_error_message = msg;
53 integrator->m_error_message += "\n";
54 }
55
56 static int cvodes_prec_setup(realtype t, N_Vector y, N_Vector ydot, booleantype jok,
57 booleantype *jcurPtr, realtype gamma, void *f_data)
58 {
59 FuncEval* f = (FuncEval*) f_data;
60 if (!jok) {
61 *jcurPtr = true; // jacobian data was recomputed
62 return f->preconditioner_setup_nothrow(t, NV_DATA_S(y), gamma);
63 } else {
64 f->updatePreconditioner(gamma); // updates preconditioner with new gamma
65 *jcurPtr = false; // indicates that Jacobian data was not recomputed
66 return 0; // no error because not recomputed
67 }
68 }
69
70 static int cvodes_prec_solve(realtype t, N_Vector y, N_Vector ydot, N_Vector r,
71 N_Vector z, realtype gamma, realtype delta, int lr,
72 void* f_data)
73 {
74 FuncEval* f = (FuncEval*) f_data;
75 return f->preconditioner_solve_nothrow(NV_DATA_S(r),NV_DATA_S(z));
76 }
77}
78
80 : m_itol(CV_SS)
81 , m_method(CV_BDF)
82{
83}
84
85CVodesIntegrator::~CVodesIntegrator()
86{
87 if (m_cvode_mem) {
88 if (m_np > 0) {
89 CVodeSensFree(m_cvode_mem);
90 }
91 CVodeFree(&m_cvode_mem);
92 }
93
94 SUNLinSolFree((SUNLinearSolver) m_linsol);
95 SUNMatDestroy((SUNMatrix) m_linsol_matrix);
96
97 if (m_y) {
98 N_VDestroy_Serial(m_y);
99 }
100 if (m_abstol) {
101 N_VDestroy_Serial(m_abstol);
102 }
103 if (m_dky) {
104 N_VDestroy_Serial(m_dky);
105 }
106 if (m_yS) {
107 #if CT_SUNDIALS_VERSION >= 60
108 N_VDestroyVectorArray(m_yS, static_cast<int>(m_np));
109 #else
110 N_VDestroyVectorArray_Serial(m_yS, static_cast<int>(m_np));
111 #endif
112 }
113}
114
115
117{
118 return NV_Ith_S(m_y, k);
119}
120
122{
123 return NV_DATA_S(m_y);
124}
125
126void CVodesIntegrator::setTolerances(double reltol, size_t n, double* abstol)
127{
128 m_itol = CV_SV;
129 m_nabs = n;
130 if (n != m_neq) {
131 if (m_abstol) {
132 N_VDestroy_Serial(m_abstol);
133 }
134 m_abstol = newNVector(n, m_sundials_ctx);
135 }
136 for (size_t i=0; i<n; i++) {
137 NV_Ith_S(m_abstol, i) = abstol[i];
138 }
139 m_reltol = reltol;
140}
141
142void CVodesIntegrator::setTolerances(double reltol, double abstol)
143{
144 m_itol = CV_SS;
145 m_reltol = reltol;
146 m_abstols = abstol;
147}
148
149void CVodesIntegrator::setSensitivityTolerances(double reltol, double abstol)
150{
151 m_reltolsens = reltol;
152 m_abstolsens = abstol;
153}
154
156{
157 warn_deprecated("CVodesIntegrator::setProblemType()",
158 "To be removed. Set linear solver type with setLinearSolverType");
159 if (probtype == DIAG)
160 {
161 setLinearSolverType("DIAG");
162 } else if (probtype == DENSE + NOJAC) {
163 setLinearSolverType("DENSE");
164 } else if (probtype == BAND + NOJAC) {
165 setLinearSolverType("BAND");
166 } else if (probtype == GMRES) {
167 setLinearSolverType("GMRES");
168 } else {
169 setLinearSolverType("Invalid Option");
170 }
171}
172
174{
175 if (t == BDF_Method) {
176 m_method = CV_BDF;
177 } else if (t == Adams_Method) {
178 m_method = CV_ADAMS;
179 } else {
180 throw CanteraError("CVodesIntegrator::setMethod", "unknown method");
181 }
182}
183
185{
186 m_hmax = hmax;
187 if (m_cvode_mem) {
188 CVodeSetMaxStep(m_cvode_mem, hmax);
189 }
190}
191
193{
194 m_hmin = hmin;
195 if (m_cvode_mem) {
196 CVodeSetMinStep(m_cvode_mem, hmin);
197 }
198}
199
201{
202 m_maxsteps = nmax;
203 if (m_cvode_mem) {
204 CVodeSetMaxNumSteps(m_cvode_mem, m_maxsteps);
205 }
206}
207
209{
210 return m_maxsteps;
211}
212
214{
215 m_maxErrTestFails = n;
216 if (m_cvode_mem) {
217 CVodeSetMaxErrTestFails(m_cvode_mem, n);
218 }
219}
220
221void CVodesIntegrator::sensInit(double t0, FuncEval& func)
222{
223 m_np = func.nparams();
224 m_sens_ok = false;
225
226 N_Vector y = newNVector(func.neq(), m_sundials_ctx);
227 #if CT_SUNDIALS_VERSION >= 60
228 m_yS = N_VCloneVectorArray(static_cast<int>(m_np), y);
229 #else
230 m_yS = N_VCloneVectorArray_Serial(static_cast<int>(m_np), y);
231 #endif
232 for (size_t n = 0; n < m_np; n++) {
233 N_VConst(0.0, m_yS[n]);
234 }
235 N_VDestroy_Serial(y);
236
237 int flag = CVodeSensInit(m_cvode_mem, static_cast<int>(m_np),
238 CV_STAGGERED, CVSensRhsFn(0), m_yS);
239 checkError(flag, "sensInit", "CVodeSensInit");
240
241 vector<double> atol(m_np);
242 for (size_t n = 0; n < m_np; n++) {
243 // This scaling factor is tuned so that reaction and species enthalpy
244 // sensitivities can be computed simultaneously with the same abstol.
245 atol[n] = m_abstolsens / func.m_paramScales[n];
246 }
247 flag = CVodeSensSStolerances(m_cvode_mem, m_reltolsens, atol.data());
248 checkError(flag, "sensInit", "CVodeSensSStolerances");
249}
250
252{
253 m_neq = func.neq();
254 m_t0 = t0;
255 m_time = t0;
256 m_tInteg = t0;
257 m_func = &func;
258 func.clearErrors();
259 // Initialize preconditioner if applied
260 if (m_prec_side != PreconditionerSide::NO_PRECONDITION) {
261 m_preconditioner->initialize(m_neq);
262 }
263 if (m_y) {
264 N_VDestroy_Serial(m_y); // free solution vector if already allocated
265 }
266 m_y = newNVector(m_neq, m_sundials_ctx); // allocate solution vector
267 N_VConst(0.0, m_y);
268 if (m_dky) {
269 N_VDestroy_Serial(m_dky); // free derivative vector if already allocated
270 }
271 m_dky = newNVector(m_neq, m_sundials_ctx); // allocate derivative vector
272 N_VConst(0.0, m_dky);
273 // check abs tolerance array size
274 if (m_itol == CV_SV && m_nabs < m_neq) {
275 throw CanteraError("CVodesIntegrator::initialize",
276 "not enough absolute tolerance values specified.");
277 }
278
279 func.getState(NV_DATA_S(m_y));
280
281 if (m_cvode_mem) {
282 CVodeFree(&m_cvode_mem);
283 }
284
285 //! Specify the method and the iteration type. Cantera Defaults:
286 //! CV_BDF - Use BDF methods
287 //! CV_NEWTON - use Newton's method
288 #if CT_SUNDIALS_VERSION < 40
289 m_cvode_mem = CVodeCreate(m_method, CV_NEWTON);
290 #elif CT_SUNDIALS_VERSION < 60
291 m_cvode_mem = CVodeCreate(m_method);
292 #else
293 m_cvode_mem = CVodeCreate(m_method, m_sundials_ctx.get());
294 #endif
295 if (!m_cvode_mem) {
296 throw CanteraError("CVodesIntegrator::initialize",
297 "CVodeCreate failed.");
298 }
299
300 int flag = CVodeInit(m_cvode_mem, cvodes_rhs, m_t0, m_y);
301 if (flag != CV_SUCCESS) {
302 if (flag == CV_MEM_FAIL) {
303 throw CanteraError("CVodesIntegrator::initialize",
304 "Memory allocation failed.");
305 } else if (flag == CV_ILL_INPUT) {
306 throw CanteraError("CVodesIntegrator::initialize",
307 "Illegal value for CVodeInit input argument.");
308 } else {
309 throw CanteraError("CVodesIntegrator::initialize",
310 "CVodeInit failed.");
311 }
312 }
313 flag = CVodeSetErrHandlerFn(m_cvode_mem, &cvodes_err, this);
314
315 if (m_itol == CV_SV) {
316 flag = CVodeSVtolerances(m_cvode_mem, m_reltol, m_abstol);
317 checkError(flag, "initialize", "CVodeSVtolerances");
318 } else {
319 flag = CVodeSStolerances(m_cvode_mem, m_reltol, m_abstols);
320 checkError(flag, "initialize", "CVodeSStolerances");
321 }
322
323 flag = CVodeSetUserData(m_cvode_mem, &func);
324 checkError(flag, "initialize", "CVodeSetUserData");
325
326 if (func.nparams() > 0) {
327 sensInit(t0, func);
328 flag = CVodeSetSensParams(m_cvode_mem, func.m_sens_params.data(),
329 func.m_paramScales.data(), NULL);
330 checkError(flag, "initialize", "CVodeSetSensParams");
331 }
332 applyOptions();
333}
334
335void CVodesIntegrator::reinitialize(double t0, FuncEval& func)
336{
337 m_t0 = t0;
338 m_time = t0;
339 m_tInteg = t0;
340 func.getState(NV_DATA_S(m_y));
341 m_func = &func;
342 func.clearErrors();
343 // reinitialize preconditioner if applied
344 if (m_prec_side != PreconditionerSide::NO_PRECONDITION) {
345 m_preconditioner->initialize(m_neq);
346 }
347 int result = CVodeReInit(m_cvode_mem, m_t0, m_y);
348 checkError(result, "reinitialize", "CVodeReInit");
349 applyOptions();
350}
351
353{
354 if (m_type == "DENSE") {
355 sd_size_t N = static_cast<sd_size_t>(m_neq);
356 SUNLinSolFree((SUNLinearSolver) m_linsol);
357 SUNMatDestroy((SUNMatrix) m_linsol_matrix);
358 #if CT_SUNDIALS_VERSION >= 60
359 m_linsol_matrix = SUNDenseMatrix(N, N, m_sundials_ctx.get());
360 #else
361 m_linsol_matrix = SUNDenseMatrix(N, N);
362 #endif
363 if (m_linsol_matrix == nullptr) {
364 throw CanteraError("CVodesIntegrator::applyOptions",
365 "Unable to create SUNDenseMatrix of size {0} x {0}", N);
366 }
367 int flag;
368 #if CT_SUNDIALS_VERSION >= 60
369 #if CT_SUNDIALS_USE_LAPACK
370 m_linsol = SUNLinSol_LapackDense(m_y, (SUNMatrix) m_linsol_matrix,
371 m_sundials_ctx.get());
372 #else
373 m_linsol = SUNLinSol_Dense(m_y, (SUNMatrix) m_linsol_matrix,
374 m_sundials_ctx.get());
375 #endif
376 flag = CVodeSetLinearSolver(m_cvode_mem, (SUNLinearSolver) m_linsol,
377 (SUNMatrix) m_linsol_matrix);
378 #else
379 #if CT_SUNDIALS_USE_LAPACK
380 m_linsol = SUNLapackDense(m_y, (SUNMatrix) m_linsol_matrix);
381 #else
382 m_linsol = SUNDenseLinearSolver(m_y, (SUNMatrix) m_linsol_matrix);
383 #endif
384 flag = CVDlsSetLinearSolver(m_cvode_mem, (SUNLinearSolver) m_linsol,
385 (SUNMatrix) m_linsol_matrix);
386 #endif
387 if (m_linsol == nullptr) {
388 throw CanteraError("CVodesIntegrator::applyOptions",
389 "Error creating Sundials dense linear solver object");
390 } else if (flag != CV_SUCCESS) {
391 throw CanteraError("CVodesIntegrator::applyOptions",
392 "Error connecting linear solver to CVODES. "
393 "Sundials error code: {}", flag);
394 }
395
396 // throw preconditioner error for DENSE + NOJAC
397 if (m_prec_side != PreconditionerSide::NO_PRECONDITION) {
398 throw CanteraError("CVodesIntegrator::applyOptions",
399 "Preconditioning is not available with the specified problem type.");
400 }
401 } else if (m_type == "DIAG") {
402 CVDiag(m_cvode_mem);
403 // throw preconditioner error for DIAG
404 if (m_prec_side != PreconditionerSide::NO_PRECONDITION) {
405 throw CanteraError("CVodesIntegrator::applyOptions",
406 "Preconditioning is not available with the specified problem type.");
407 }
408 } else if (m_type == "GMRES") {
409 #if CT_SUNDIALS_VERSION >= 60
410 m_linsol = SUNLinSol_SPGMR(m_y, PREC_NONE, 0, m_sundials_ctx.get());
411 CVodeSetLinearSolver(m_cvode_mem, (SUNLinearSolver) m_linsol, nullptr);
412 #elif CT_SUNDIALS_VERSION >= 40
413 m_linsol = SUNLinSol_SPGMR(m_y, PREC_NONE, 0);
414 CVSpilsSetLinearSolver(m_cvode_mem, (SUNLinearSolver) m_linsol);
415 # else
416 m_linsol = SUNSPGMR(m_y, PREC_NONE, 0);
417 CVSpilsSetLinearSolver(m_cvode_mem, (SUNLinearSolver) m_linsol);
418 #endif
419 // set preconditioner if used
420 #if CT_SUNDIALS_VERSION >= 40
421 if (m_prec_side != PreconditionerSide::NO_PRECONDITION) {
422 SUNLinSol_SPGMRSetPrecType((SUNLinearSolver) m_linsol,
423 static_cast<int>(m_prec_side));
424 CVodeSetPreconditioner(m_cvode_mem, cvodes_prec_setup,
425 cvodes_prec_solve);
426 }
427 #else
428 if (m_prec_side != PreconditionerSide::NO_PRECONDITION) {
429 SUNSPGMRSetPrecType((SUNLinearSolver) m_linsol,
430 static_cast<int>(m_prec_side));
431 CVSpilsSetPreconditioner(m_cvode_mem, cvodes_prec_setup,
432 cvodes_prec_solve);
433 }
434 #endif
435 } else if (m_type == "BAND") {
436 sd_size_t N = static_cast<sd_size_t>(m_neq);
437 sd_size_t nu = m_mupper;
438 sd_size_t nl = m_mlower;
439 SUNLinSolFree((SUNLinearSolver) m_linsol);
440 SUNMatDestroy((SUNMatrix) m_linsol_matrix);
441 #if CT_SUNDIALS_VERSION >= 60
442 m_linsol_matrix = SUNBandMatrix(N, nu, nl, m_sundials_ctx.get());
443 #elif CT_SUNDIALS_VERSION >= 40
444 m_linsol_matrix = SUNBandMatrix(N, nu, nl);
445 #else
446 m_linsol_matrix = SUNBandMatrix(N, nu, nl, nu+nl);
447 #endif
448 if (m_linsol_matrix == nullptr) {
449 throw CanteraError("CVodesIntegrator::applyOptions",
450 "Unable to create SUNBandMatrix of size {} with bandwidths "
451 "{} and {}", N, nu, nl);
452 }
453 #if CT_SUNDIALS_VERSION >= 60
454 #if CT_SUNDIALS_USE_LAPACK
455 m_linsol = SUNLinSol_LapackBand(m_y, (SUNMatrix) m_linsol_matrix,
456 m_sundials_ctx.get());
457 #else
458 m_linsol = SUNLinSol_Band(m_y, (SUNMatrix) m_linsol_matrix,
459 m_sundials_ctx.get());
460 #endif
461 CVodeSetLinearSolver(m_cvode_mem, (SUNLinearSolver) m_linsol,
462 (SUNMatrix) m_linsol_matrix);
463 #else
464 #if CT_SUNDIALS_USE_LAPACK
465 m_linsol = SUNLapackBand(m_y, (SUNMatrix) m_linsol_matrix);
466 #else
467 m_linsol = SUNBandLinearSolver(m_y, (SUNMatrix) m_linsol_matrix);
468 #endif
469 CVDlsSetLinearSolver(m_cvode_mem, (SUNLinearSolver) m_linsol,
470 (SUNMatrix) m_linsol_matrix);
471 #endif
472 } else {
473 throw CanteraError("CVodesIntegrator::applyOptions",
474 "unsupported linear solver flag '{}'", m_type);
475 }
476
477 if (m_maxord > 0) {
478 int flag = CVodeSetMaxOrd(m_cvode_mem, m_maxord);
479 checkError(flag, "applyOptions", "CVodeSetMaxOrd");
480 }
481 if (m_maxsteps > 0) {
482 CVodeSetMaxNumSteps(m_cvode_mem, m_maxsteps);
483 }
484 if (m_hmax > 0) {
485 CVodeSetMaxStep(m_cvode_mem, m_hmax);
486 }
487 if (m_hmin > 0) {
488 CVodeSetMinStep(m_cvode_mem, m_hmin);
489 }
490 if (m_maxErrTestFails > 0) {
491 CVodeSetMaxErrTestFails(m_cvode_mem, m_maxErrTestFails);
492 }
493}
494
496{
497 if (tout == m_time) {
498 return;
499 } else if (tout < m_time) {
500 throw CanteraError("CVodesIntegrator::integrate",
501 "Cannot integrate backwards in time.\n"
502 "Requested time {} < current time {}",
503 tout, m_time);
504 }
505 int nsteps = 0;
506 while (m_tInteg < tout) {
507 if (nsteps >= m_maxsteps) {
508 throw CanteraError("CVodesIntegrator::integrate",
509 "Maximum number of timesteps ({}) taken without reaching output "
510 "time ({}).\nCurrent integrator time: {}",
511 nsteps, tout, m_tInteg);
512 }
513 int flag = CVode(m_cvode_mem, tout, m_y, &m_tInteg, CV_ONE_STEP);
514 if (flag != CV_SUCCESS) {
515 string f_errs = m_func->getErrors();
516 if (!f_errs.empty()) {
517 f_errs = "Exceptions caught during RHS evaluation:\n" + f_errs;
518 }
519 throw CanteraError("CVodesIntegrator::integrate",
520 "CVodes error encountered. Error code: {}\n{}\n"
521 "{}"
522 "Components with largest weighted error estimates:\n{}",
523 flag, m_error_message, f_errs, getErrorInfo(10));
524 }
525 nsteps++;
526 }
527 int flag = CVodeGetDky(m_cvode_mem, tout, 0, m_y);
528 checkError(flag, "integrate", "CVodeGetDky");
529 m_time = tout;
530 m_sens_ok = false;
531}
532
533double CVodesIntegrator::step(double tout)
534{
535 int flag = CVode(m_cvode_mem, tout, m_y, &m_tInteg, CV_ONE_STEP);
536 if (flag != CV_SUCCESS) {
537 string f_errs = m_func->getErrors();
538 if (!f_errs.empty()) {
539 f_errs = "Exceptions caught during RHS evaluation:\n" + f_errs;
540 }
541 throw CanteraError("CVodesIntegrator::step",
542 "CVodes error encountered. Error code: {}\n{}\n"
543 "{}"
544 "Components with largest weighted error estimates:\n{}",
545 flag, f_errs, m_error_message, getErrorInfo(10));
546
547 }
548 m_sens_ok = false;
550 return m_time;
551}
552
553double* CVodesIntegrator::derivative(double tout, int n)
554{
555 int flag = CVodeGetDky(m_cvode_mem, tout, n, m_dky);
556 checkError(flag, "derivative", "CVodeGetDky");
557 return NV_DATA_S(m_dky);
558}
559
561{
562 int ord;
563 CVodeGetLastOrder(m_cvode_mem, &ord);
564 return ord;
565}
566
568{
569 long int ne;
570 CVodeGetNumRhsEvals(m_cvode_mem, &ne);
571 return ne;
572}
573
575{
576 // AnyMap to return stats
577 AnyMap stats;
578
579 // long int linear solver stats provided by CVodes
580 long int steps = 0, rhsEvals = 0, errTestFails = 0, jacEvals = 0, linSetup = 0,
581 linRhsEvals = 0, linIters = 0, linConvFails = 0, precEvals = 0,
582 precSolves = 0, jtSetupEvals = 0, jTimesEvals = 0, nonlinIters = 0,
583 nonlinConvFails = 0, orderReductions = 0;
584 int lastOrder = 0;
585;
586 #if CT_SUNDIALS_VERSION >= 40
587 CVodeGetNumSteps(m_cvode_mem, &steps);
588 CVodeGetNumRhsEvals(m_cvode_mem, &rhsEvals);
589 CVodeGetNonlinSolvStats(m_cvode_mem, &nonlinIters, &nonlinConvFails);
590 CVodeGetNumErrTestFails(m_cvode_mem, &errTestFails);
591 CVodeGetLastOrder(m_cvode_mem, &lastOrder);
592 CVodeGetNumStabLimOrderReds(m_cvode_mem, &orderReductions);
593 CVodeGetNumJacEvals(m_cvode_mem, &jacEvals);
594 CVodeGetNumLinRhsEvals(m_cvode_mem, &linRhsEvals);
595 CVodeGetNumLinSolvSetups(m_cvode_mem, &linSetup);
596 CVodeGetNumLinIters(m_cvode_mem, &linIters);
597 CVodeGetNumLinConvFails(m_cvode_mem, &linConvFails);
598 CVodeGetNumPrecEvals(m_cvode_mem, &precEvals);
599 CVodeGetNumPrecSolves(m_cvode_mem, &precSolves);
600 CVodeGetNumJTSetupEvals(m_cvode_mem, &jtSetupEvals);
601 CVodeGetNumJtimesEvals(m_cvode_mem, &jTimesEvals);
602 #else
603 warn_user("CVodesIntegrator::solverStats", "Function not"
604 "supported with sundials versions less than 4.");
605 #endif
606
607 #if CT_SUNDIALS_VERION >= 60
608 long int stepSolveFails = 0;
609 CVodeGetNumStepSolveFails(m_cvode_mem, &stepSolveFails);
610 stats["step_solve_fails"] = stepSolveFails;
611 #endif
612
613 stats["steps"] = steps;
614 stats["rhs_evals"] = rhsEvals;
615 stats["nonlinear_iters"] = nonlinIters;
616 stats["nonlinear_conv_fails"] = nonlinConvFails;
617 stats["err_test_fails"] = errTestFails;
618 stats["last_order"] = lastOrder;
619 stats["stab_order_reductions"] = orderReductions;
620
621 stats["jac_evals"] = jacEvals;
622 stats["lin_solve_setups"] = linSetup;
623 stats["lin_rhs_evals"] = linRhsEvals;
624 stats["lin_iters"] = linIters;
625 stats["lin_conv_fails"] = linConvFails;
626 stats["prec_evals"] = precEvals;
627 stats["prec_solves"] = precSolves;
628 stats["jt_vec_setup_evals"] = jtSetupEvals;
629 stats["jt_vec_prod_evals"] = jTimesEvals;
630 return stats;
631}
632
633double CVodesIntegrator::sensitivity(size_t k, size_t p)
634{
635 if (m_time == m_t0) {
636 // calls to CVodeGetSens are only allowed after a successful time step.
637 return 0.0;
638 }
639 if (!m_sens_ok && m_np) {
640 int flag = CVodeGetSensDky(m_cvode_mem, m_time, 0, m_yS);
641 checkError(flag, "sensitivity", "CVodeGetSens");
642 m_sens_ok = true;
643 }
644
645 if (k >= m_neq) {
646 throw CanteraError("CVodesIntegrator::sensitivity",
647 "sensitivity: k out of range ({})", k);
648 }
649 if (p >= m_np) {
650 throw CanteraError("CVodesIntegrator::sensitivity",
651 "sensitivity: p out of range ({})", p);
652 }
653 return NV_Ith_S(m_yS[p],k);
654}
655
657{
658 N_Vector errs = newNVector(m_neq, m_sundials_ctx);
659 N_Vector errw = newNVector(m_neq, m_sundials_ctx);
660 CVodeGetErrWeights(m_cvode_mem, errw);
661 CVodeGetEstLocalErrors(m_cvode_mem, errs);
662
663 vector<tuple<double, double, size_t>> weightedErrors;
664 for (size_t i=0; i<m_neq; i++) {
665 double err = NV_Ith_S(errs, i) * NV_Ith_S(errw, i);
666 weightedErrors.emplace_back(-abs(err), err, i);
667 }
668 N_VDestroy(errs);
669 N_VDestroy(errw);
670
671 N = std::min(N, static_cast<int>(m_neq));
672 sort(weightedErrors.begin(), weightedErrors.end());
673 fmt::memory_buffer s;
674 for (int i=0; i<N; i++) {
675 fmt_append(s, "{}: {}\n",
676 get<2>(weightedErrors[i]), get<1>(weightedErrors[i]));
677 }
678 return to_string(s);
679}
680
681void CVodesIntegrator::checkError(long flag, const string& ctMethod,
682 const string& cvodesMethod) const
683{
684 if (flag == CV_SUCCESS) {
685 return;
686 } else if (flag == CV_MEM_NULL) {
687 throw CanteraError("CVodesIntegrator::" + ctMethod,
688 "CVODES integrator is not initialized");
689 } else {
690 const char* flagname = CVodeGetReturnFlagName(flag);
691 throw CanteraError("CVodesIntegrator::" + ctMethod,
692 "{} returned error code {} ({}):\n{}",
693 cvodesMethod, flag, flagname, m_error_message);
694 }
695}
696
697}
A map of string keys to values whose type can vary at runtime.
Definition AnyMap.h:427
Wrapper class for 'cvodes' integrator from LLNL.
double step(double tout) override
Integrate the system of equations.
void setMaxStepSize(double hmax) override
Set the maximum step size.
double * solution() override
The current value of the solution of the system of equations.
void setProblemType(int probtype) override
Set the problem type.
N_Vector m_y
The system state at m_time.
void * m_linsol
Sundials linear solver object.
int nEvals() const override
The number of function evaluations.
double m_time
The current system time, corresponding to m_y.
bool m_sens_ok
Indicates whether the sensitivities stored in m_yS have been updated for at the current integrator ti...
int maxSteps() override
Returns the maximum number of time-steps the integrator can take before reaching the next output time...
SundialsContext m_sundials_ctx
SUNContext object for Sundials>=6.0.
void setLinearSolverType(const string &linSolverType) override
Set the linear solver type.
void checkError(long flag, const string &ctMethod, const string &cvodesMethod) const
Check whether a CVODES method indicated an error.
void setSensitivityTolerances(double reltol, double abstol) override
Set the sensitivity error tolerances.
void applyOptions()
Applies user-specified options to the underlying CVODES solver.
int lastOrder() const override
Order used during the last solution step.
void setMinStepSize(double hmin) override
Set the minimum step size.
void integrate(double tout) override
Integrate the system of equations.
void setTolerances(double reltol, size_t n, double *abstol) override
Set error tolerances.
double * derivative(double tout, int n) override
n-th derivative of the output function at time tout.
string m_error_message
Error message information provide by CVodes.
void setMaxErrTestFails(int n) override
Set the maximum permissible number of error test failures.
void setMaxSteps(int nmax) override
Set the maximum number of time-steps the integrator can take before reaching the next output time.
AnyMap solverStats() const override
Get solver stats from integrator.
void * m_linsol_matrix
matrix used by Sundials
double m_tInteg
The latest time reached by the integrator. May be greater than m_time.
void initialize(double t0, FuncEval &func) override
Initialize the integrator for a new problem.
string getErrorInfo(int N)
Returns a string listing the weighted error estimates associated with each solution component.
void setMethod(MethodType t) override
Set the solution method.
Base class for exceptions thrown by Cantera classes.
Virtual base class for ODE/DAE right-hand-side function evaluators.
Definition FuncEval.h:32
vector< double > m_paramScales
Scaling factors for each sensitivity parameter.
Definition FuncEval.h:184
void clearErrors()
Clear any previously-stored suppressed errors.
Definition FuncEval.h:174
int evalNoThrow(double t, double *y, double *ydot)
Evaluate the right-hand side using return code to indicate status.
Definition FuncEval.cpp:7
virtual size_t neq() const =0
Number of equations.
virtual size_t nparams() const
Number of sensitivity parameters.
Definition FuncEval.h:156
string getErrors() const
Return a string containing the text of any suppressed errors.
Definition FuncEval.cpp:71
vector< double > m_sens_params
Values for the problem parameters for which sensitivities are computed This is the array which is per...
Definition FuncEval.h:181
virtual void getState(double *y)
Fill in the vector y with the current state of the system.
Definition FuncEval.h:142
shared_ptr< PreconditionerBase > m_preconditioner
Pointer to preconditioner object used in integration which is set by setPreconditioner and initialize...
Definition Integrator.h:320
PreconditionerSide m_prec_side
Type of preconditioning used in applyOptions.
Definition Integrator.h:322
A wrapper for managing a SUNContext object, need for Sundials >= 6.0.
void fmt_append(fmt::memory_buffer &b, Args... args)
Versions 6.2.0 and 6.2.1 of fmtlib do not include this define before they include windows....
Definition fmt.h:29
static int cvodes_rhs(realtype t, N_Vector y, N_Vector ydot, void *f_data)
Function called by cvodes to evaluate ydot given y.
void warn_user(const string &method, const string &msg, const Args &... args)
Print a user warning raised from method as CanteraWarning.
Definition global.h:267
Namespace for the Cantera kernel.
Definition AnyMap.cpp:564
MethodType
Specifies the method used to integrate the system of equations.
Definition Integrator.h:30
@ BDF_Method
Backward Differentiation.
Definition Integrator.h:31
@ Adams_Method
Adams.
Definition Integrator.h:32
void warn_deprecated(const string &source, const AnyBase &node, const string &message)
A deprecation warning for syntax in an input file.
Definition AnyMap.cpp:1926
static void cvodes_err(int error_code, const char *module, const char *function, char *msg, void *eh_data)
Function called by CVodes when an error is encountered instead of writing to stdout.
Contains declarations for string manipulation functions within Cantera.