Cantera  4.0.0a1
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 return N_VNew_Serial(static_cast<sd_size_t>(N), context.get());
19}
20
21} // end anonymous namespace
22
23namespace Cantera
24{
25
26extern "C" {
27 /**
28 * Function called by cvodes to evaluate ydot given y. The CVODE integrator
29 * allows passing in a void* pointer to access external data. This pointer
30 * is cast to a pointer to a instance of class FuncEval. The equations to be
31 * integrated should be specified by deriving a class from FuncEval that
32 * evaluates the desired equations.
33 * @ingroup odeGroup
34 */
35 static int cvodes_rhs(sunrealtype t, N_Vector y, N_Vector ydot, void* f_data)
36 {
37 FuncEval* f = (FuncEval*) f_data;
38 return f->evalNoThrow(t, asSpan(y), asSpan(ydot));
39 }
40
41 #if SUNDIALS_VERSION_MAJOR >= 7
42 //! Function called by CVodes when an error is encountered instead of
43 //! writing to stdout. Here, save the error message provided by CVodes so
44 //! that it can be included in the subsequently raised CanteraError. Used by
45 //! SUNDIALS 7.0 and newer.
46 static void sundials_err(int line, const char *func, const char *file,
47 const char *msg, SUNErrCode err_code,
48 void *err_user_data, SUNContext sunctx)
49 {
50 CVodesIntegrator* integrator = (CVodesIntegrator*) err_user_data;
51 integrator->m_error_message = fmt::format("{}: {}\n", func, msg);
52 }
53 #else
54 //! Function called by CVodes when an error is encountered instead of
55 //! writing to stdout. Here, save the error message provided by CVodes so
56 //! that it can be included in the subsequently raised CanteraError. Used by
57 //! SUNDIALS 6.x and older.
58 static void cvodes_err(int error_code, const char* module,
59 const char* function, char* msg, void* eh_data)
60 {
61 CVodesIntegrator* integrator = (CVodesIntegrator*) eh_data;
62 integrator->m_error_message = msg;
63 integrator->m_error_message += "\n";
64 }
65 #endif
66
67 static int cvodes_prec_setup(sunrealtype t, N_Vector y, N_Vector ydot,
68 sunbooleantype jok, sunbooleantype *jcurPtr,
69 sunrealtype gamma, void *f_data)
70 {
71 FuncEval* f = (FuncEval*) f_data;
72 if (!jok) {
73 *jcurPtr = true; // jacobian data was recomputed
74 return f->preconditioner_setup_nothrow(t, asSpan(y), gamma);
75 } else {
76 f->updatePreconditioner(gamma); // updates preconditioner with new gamma
77 *jcurPtr = false; // indicates that Jacobian data was not recomputed
78 return 0; // no error because not recomputed
79 }
80 }
81
82 static int cvodes_prec_solve(sunrealtype t, N_Vector y, N_Vector ydot, N_Vector r,
83 N_Vector z, sunrealtype gamma, sunrealtype delta,
84 int lr, void* f_data)
85 {
86 FuncEval* f = (FuncEval*) f_data;
87 return f->preconditioner_solve_nothrow(asSpan(r), asSpan(z));
88 }
89
90 /**
91 * SUNDIALS callback that forwards root evaluations to the FuncEval.
92 * @param[in] t Current integration time at which roots are requested
93 * @param[in] y Solution vector provided by CVODE (length FuncEval::neq())
94 * @param[out] gout Output array that must be filled with FuncEval::nRootFunctions()
95 * values
96 * @param[in] user_data Opaque pointer (FuncEval*) supplied during integrator setup
97 * @returns The result of FuncEval::evalRootFunctions (0 on success)
98 */
99 static int cvodes_root(sunrealtype t, N_Vector y, sunrealtype* gout, void* user_data)
100 {
101 auto* f = static_cast<FuncEval*>(user_data);
102 return f->evalRootFunctionsNoThrow(t, asSpan(y),
103 span<double>(gout, f->nRootFunctions()));
104 }
105}
106
108 : m_itol(CV_SS)
109 , m_method(CV_BDF)
110{
111}
112
113CVodesIntegrator::~CVodesIntegrator()
114{
115 if (m_cvode_mem) {
116 if (m_np > 0) {
117 CVodeSensFree(m_cvode_mem);
118 }
119 CVodeFree(&m_cvode_mem);
120 }
121
122 SUNLinSolFree((SUNLinearSolver) m_linsol);
123 SUNMatDestroy((SUNMatrix) m_linsol_matrix);
124
125 if (m_y) {
126 N_VDestroy_Serial(m_y);
127 }
128 if (m_abstol) {
129 N_VDestroy_Serial(m_abstol);
130 }
131 if (m_dky) {
132 N_VDestroy_Serial(m_dky);
133 }
134 if (m_yS) {
135 N_VDestroyVectorArray(m_yS, static_cast<int>(m_np));
136 }
137}
138
139
141{
142 return NV_Ith_S(m_y, k);
143}
144
146{
147 return asSpan(m_y);
148}
149
150void CVodesIntegrator::setTolerances(double reltol, span<const double> abstol)
151{
152 size_t n = abstol.size();
153 m_itol = CV_SV;
154 m_nabs = n;
155 if (n != m_neq) {
156 if (m_abstol) {
157 N_VDestroy_Serial(m_abstol);
158 }
159 m_abstol = newNVector(n, m_sundials_ctx);
160 }
161 for (size_t i=0; i<n; i++) {
162 NV_Ith_S(m_abstol, i) = abstol[i];
163 }
164 m_reltol = reltol;
165 if (m_cvode_mem) {
166 int flag = CVodeSVtolerances(m_cvode_mem, m_reltol, m_abstol);
167 checkError(flag, "setTolerances", "CVodeSVtolerances");
168 }
169}
170
171void CVodesIntegrator::setTolerances(double reltol, double abstol)
172{
173 m_itol = CV_SS;
174 m_reltol = reltol;
175 m_abstols = abstol;
176 if (m_cvode_mem) {
177 int flag = CVodeSStolerances(m_cvode_mem, m_reltol, m_abstols);
178 checkError(flag, "setTolerances", "CVodeSStolerances");
179 }
180}
181
182void CVodesIntegrator::setSensitivityTolerances(double reltol, double abstol)
183{
184 m_reltolsens = reltol;
185 m_abstolsens = abstol;
186 if (m_cvode_mem && m_yS) {
187 vector<double> atol(m_np);
188 for (size_t n = 0; n < m_np; n++) {
189 // This scaling factor is tuned so that reaction and species enthalpy
190 // sensitivities can be computed simultaneously with the same abstol.
191 atol[n] = m_abstolsens / m_func->m_paramScales[n];
192 }
193 int flag = CVodeSensSStolerances(m_cvode_mem, m_reltolsens, atol.data());
194 checkError(flag, "setSensitivityTolerances", "CVodeSensSStolerances");
195 }
196}
197
199{
200 if (t == BDF_Method) {
201 m_method = CV_BDF;
202 } else if (t == Adams_Method) {
203 m_method = CV_ADAMS;
204 } else {
205 throw CanteraError("CVodesIntegrator::setMethod", "unknown method");
206 }
207}
208
210{
211 m_hmax = hmax;
212 if (m_cvode_mem) {
213 CVodeSetMaxStep(m_cvode_mem, hmax);
214 }
215}
216
218{
219 m_hmin = hmin;
220 if (m_cvode_mem) {
221 CVodeSetMinStep(m_cvode_mem, hmin);
222 }
223}
224
226{
227 m_maxsteps = nmax;
228 if (m_cvode_mem) {
229 CVodeSetMaxNumSteps(m_cvode_mem, m_maxsteps);
230 }
231}
232
234{
235 return m_maxsteps;
236}
237
239{
240 m_maxErrTestFails = n;
241 if (m_cvode_mem) {
242 CVodeSetMaxErrTestFails(m_cvode_mem, n);
243 }
244}
245
247{
248 // Skip the Sundials call when the requested count matches what CVODE already has
249 if (m_cvode_mem && m_nRootFunctions == nroots) {
250 return;
251 }
252 m_nRootFunctions = nroots;
253 // When initialize() hasn’t created CVODE memory yet, just cache the count
254 if (!m_cvode_mem) {
255 return;
256 }
257 // Register (or remove) the root callback; passing nullptr disables root finding
258 CVRootFn root_cb = nroots ? &cvodes_root : nullptr;
259 int flag = CVodeRootInit(m_cvode_mem, static_cast<int>(nroots), root_cb);
260 checkError(flag, "setRootFunctionCount", "CVodeRootInit");
261}
262
263void CVodesIntegrator::sensInit(double t0, FuncEval& func)
264{
265 m_np = func.nparams();
266 m_sens_ok = false;
267
268 N_Vector y = newNVector(func.neq(), m_sundials_ctx);
269 m_yS = N_VCloneVectorArray(static_cast<int>(m_np), y);
270 for (size_t n = 0; n < m_np; n++) {
271 N_VConst(0.0, m_yS[n]);
272 }
273 N_VDestroy_Serial(y);
274
275 int flag = CVodeSensInit(m_cvode_mem, static_cast<int>(m_np),
276 CV_STAGGERED, CVSensRhsFn(0), m_yS);
277 checkError(flag, "sensInit", "CVodeSensInit");
278 setSensitivityTolerances(m_reltolsens, m_abstolsens);
279}
280
282{
283 m_neq = func.neq();
284 m_t0 = t0;
285 m_time = t0;
286 m_tInteg = t0;
287 m_func = &func;
288 func.clearErrors();
289 // Initialize preconditioner if applied
290 if (m_prec_side != PreconditionerSide::NO_PRECONDITION) {
291 m_preconditioner->initialize(m_neq);
292 }
293 if (m_y) {
294 N_VDestroy_Serial(m_y); // free solution vector if already allocated
295 }
296 m_y = newNVector(m_neq, m_sundials_ctx); // allocate solution vector
297 N_VConst(0.0, m_y);
298 if (m_dky) {
299 N_VDestroy_Serial(m_dky); // free derivative vector if already allocated
300 }
301 m_dky = newNVector(m_neq, m_sundials_ctx); // allocate derivative vector
302 N_VConst(0.0, m_dky);
303 // check abs tolerance array size
304 if (m_itol == CV_SV && m_nabs < m_neq) {
305 throw CanteraError("CVodesIntegrator::initialize",
306 "not enough absolute tolerance values specified.");
307 }
308
309 func.getState(asSpan(m_y));
310
311 if (m_cvode_mem) {
312 CVodeFree(&m_cvode_mem);
313 }
314
315 m_cvode_mem = CVodeCreate(m_method, m_sundials_ctx.get());
316 if (!m_cvode_mem) {
317 throw CanteraError("CVodesIntegrator::initialize",
318 "CVodeCreate failed.");
319 }
320
321 int flag = CVodeInit(m_cvode_mem, cvodes_rhs, m_t0, m_y);
322 if (flag != CV_SUCCESS) {
323 if (flag == CV_MEM_FAIL) {
324 throw CanteraError("CVodesIntegrator::initialize",
325 "Memory allocation failed.");
326 } else if (flag == CV_ILL_INPUT) {
327 throw CanteraError("CVodesIntegrator::initialize",
328 "Illegal value for CVodeInit input argument.");
329 } else {
330 throw CanteraError("CVodesIntegrator::initialize",
331 "CVodeInit failed.");
332 }
333 }
334 #if SUNDIALS_VERSION_MAJOR >= 7
335 flag = SUNContext_PushErrHandler(m_sundials_ctx.get(), &sundials_err, this);
336 #else
337 flag = CVodeSetErrHandlerFn(m_cvode_mem, &cvodes_err, this);
338 #endif
339
340 if (m_itol == CV_SV) {
341 flag = CVodeSVtolerances(m_cvode_mem, m_reltol, m_abstol);
342 checkError(flag, "initialize", "CVodeSVtolerances");
343 } else {
344 flag = CVodeSStolerances(m_cvode_mem, m_reltol, m_abstols);
345 checkError(flag, "initialize", "CVodeSStolerances");
346 }
347
348 flag = CVodeSetUserData(m_cvode_mem, &func);
349 checkError(flag, "initialize", "CVodeSetUserData");
350
351 m_nRootFunctions = npos;
353
354 if (func.nparams() > 0) {
355 sensInit(t0, func);
356 flag = CVodeSetSensParams(m_cvode_mem, func.m_sens_params.data(),
357 func.m_paramScales.data(), NULL);
358 checkError(flag, "initialize", "CVodeSetSensParams");
359 }
360 applyOptions();
361}
362
363void CVodesIntegrator::reinitialize(double t0, FuncEval& func)
364{
365 m_t0 = t0;
366 m_time = t0;
367 m_tInteg = t0;
368 func.getState(asSpan(m_y));
369 m_func = &func;
370 func.clearErrors();
371 // reinitialize preconditioner if applied
372 if (m_prec_side != PreconditionerSide::NO_PRECONDITION) {
373 m_preconditioner->initialize(m_neq);
374 }
375 int result = CVodeReInit(m_cvode_mem, m_t0, m_y);
376 checkError(result, "reinitialize", "CVodeReInit");
377 m_nRootFunctions = npos;
379 applyOptions();
380}
381
383{
384 if (m_type == "DENSE") {
385 sd_size_t N = static_cast<sd_size_t>(m_neq);
386 SUNLinSolFree((SUNLinearSolver) m_linsol);
387 SUNMatDestroy((SUNMatrix) m_linsol_matrix);
388 m_linsol_matrix = SUNDenseMatrix(N, N, m_sundials_ctx.get());
389 if (m_linsol_matrix == nullptr) {
390 throw CanteraError("CVodesIntegrator::applyOptions",
391 "Unable to create SUNDenseMatrix of size {0} x {0}", N);
392 }
393 int flag;
394 #if CT_SUNDIALS_USE_LAPACK
395 m_linsol = SUNLinSol_LapackDense(m_y, (SUNMatrix) m_linsol_matrix,
396 m_sundials_ctx.get());
397 #else
398 m_linsol = SUNLinSol_Dense(m_y, (SUNMatrix) m_linsol_matrix,
399 m_sundials_ctx.get());
400 #endif
401 flag = CVodeSetLinearSolver(m_cvode_mem, (SUNLinearSolver) m_linsol,
402 (SUNMatrix) m_linsol_matrix);
403 if (m_linsol == nullptr) {
404 throw CanteraError("CVodesIntegrator::applyOptions",
405 "Error creating Sundials dense linear solver object");
406 } else if (flag != CV_SUCCESS) {
407 throw CanteraError("CVodesIntegrator::applyOptions",
408 "Error connecting linear solver to CVODES. "
409 "Sundials error code: {}", flag);
410 }
411
412 // throw preconditioner error for DENSE + NOJAC
413 if (m_prec_side != PreconditionerSide::NO_PRECONDITION) {
414 throw CanteraError("CVodesIntegrator::applyOptions",
415 "Preconditioning is not available with the specified problem type.");
416 }
417 } else if (m_type == "DIAG") {
418 CVDiag(m_cvode_mem);
419 // throw preconditioner error for DIAG
420 if (m_prec_side != PreconditionerSide::NO_PRECONDITION) {
421 throw CanteraError("CVodesIntegrator::applyOptions",
422 "Preconditioning is not available with the specified problem type.");
423 }
424 } else if (m_type == "GMRES") {
425 SUNLinSolFree((SUNLinearSolver) m_linsol);
426 m_linsol = SUNLinSol_SPGMR(m_y, SUN_PREC_NONE, 0, m_sundials_ctx.get());
427 CVodeSetLinearSolver(m_cvode_mem, (SUNLinearSolver) m_linsol, nullptr);
428 // set preconditioner if used
429 if (m_prec_side != PreconditionerSide::NO_PRECONDITION) {
430 SUNLinSol_SPGMRSetPrecType((SUNLinearSolver) m_linsol,
431 static_cast<int>(m_prec_side));
432 CVodeSetPreconditioner(m_cvode_mem, cvodes_prec_setup,
433 cvodes_prec_solve);
434 }
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 m_linsol_matrix = SUNBandMatrix(N, nu, nl, m_sundials_ctx.get());
442 if (m_linsol_matrix == nullptr) {
443 throw CanteraError("CVodesIntegrator::applyOptions",
444 "Unable to create SUNBandMatrix of size {} with bandwidths "
445 "{} and {}", N, nu, nl);
446 }
447 #if CT_SUNDIALS_USE_LAPACK
448 m_linsol = SUNLinSol_LapackBand(m_y, (SUNMatrix) m_linsol_matrix,
449 m_sundials_ctx.get());
450 #else
451 m_linsol = SUNLinSol_Band(m_y, (SUNMatrix) m_linsol_matrix,
452 m_sundials_ctx.get());
453 #endif
454 CVodeSetLinearSolver(m_cvode_mem, (SUNLinearSolver) m_linsol,
455 (SUNMatrix) m_linsol_matrix);
456 } else {
457 throw CanteraError("CVodesIntegrator::applyOptions",
458 "unsupported linear solver flag '{}'", m_type);
459 }
460
461 if (m_maxord > 0) {
462 int flag = CVodeSetMaxOrd(m_cvode_mem, m_maxord);
463 checkError(flag, "applyOptions", "CVodeSetMaxOrd");
464 }
465 if (m_maxsteps > 0) {
466 CVodeSetMaxNumSteps(m_cvode_mem, m_maxsteps);
467 }
468 if (m_hmax > 0) {
469 CVodeSetMaxStep(m_cvode_mem, m_hmax);
470 }
471 if (m_hmin > 0) {
472 CVodeSetMinStep(m_cvode_mem, m_hmin);
473 }
474 if (m_maxErrTestFails > 0) {
475 CVodeSetMaxErrTestFails(m_cvode_mem, m_maxErrTestFails);
476 }
477}
478
480{
481 if (tout == m_time) {
482 return;
483 } else if (tout < m_time) {
484 throw CanteraError("CVodesIntegrator::integrate",
485 "Cannot integrate backwards in time.\n"
486 "Requested time {} < current time {}",
487 tout, m_time);
488 }
489 int nsteps = 0;
490 while (m_tInteg < tout) {
491 if (nsteps >= m_maxsteps) {
492 string f_errs = m_func->getErrors();
493 if (!f_errs.empty()) {
494 f_errs = "\nExceptions caught during RHS evaluation:\n" + f_errs;
495 }
496 throw CanteraError("CVodesIntegrator::integrate",
497 "Maximum number of timesteps ({}) taken without reaching output "
498 "time ({}).\nCurrent integrator time: {}{}",
499 nsteps, tout, m_tInteg, f_errs);
500 }
501 int flag = CVode(m_cvode_mem, tout, m_y, &m_tInteg, CV_ONE_STEP);
502 if (flag != CV_SUCCESS && flag != CV_ROOT_RETURN) {
503 string f_errs = m_func->getErrors();
504 if (!f_errs.empty()) {
505 f_errs = "Exceptions caught during RHS evaluation:\n" + f_errs;
506 }
507 throw CanteraError("CVodesIntegrator::integrate",
508 "CVodes error encountered. Error code: {}\n{}\n"
509 "{}"
510 "Components with largest weighted error estimates:\n{}",
511 flag, m_error_message, f_errs, getErrorInfo(10));
512 }
513 if (flag == CV_ROOT_RETURN) {
514 // Stop early at root (e.g., advance limit reached); align tout to the
515 // root time
516 tout = m_tInteg;
517 break;
518 }
519 nsteps++;
520 }
521
522 // Interpolate the solution to either the user-specified output time or
523 // the time at which a root event occurred.
524 double t_eval = tout;
525 int flag = CVodeGetDky(m_cvode_mem, t_eval, 0, m_y);
526 checkError(flag, "integrate", "CVodeGetDky");
527 m_time = t_eval;
528 m_sens_ok = false;
529}
530
531double CVodesIntegrator::step(double tout)
532{
533 int flag = CVode(m_cvode_mem, tout, m_y, &m_tInteg, CV_ONE_STEP);
534 if (flag != CV_SUCCESS && flag != CV_ROOT_RETURN) {
535 string f_errs = m_func->getErrors();
536 if (!f_errs.empty()) {
537 f_errs = "Exceptions caught during RHS evaluation:\n" + f_errs;
538 }
539 throw CanteraError("CVodesIntegrator::step",
540 "CVodes error encountered. Error code: {}\n{}\n"
541 "{}"
542 "Components with largest weighted error estimates:\n{}",
543 flag, f_errs, m_error_message, getErrorInfo(10));
544
545 }
546 m_sens_ok = false;
548 return m_time;
549}
550
551span<double> CVodesIntegrator::derivative(double tout, int n)
552{
553 int flag = CVodeGetDky(m_cvode_mem, tout, n, m_dky);
554 checkError(flag, "derivative", "CVodeGetDky");
555 return asSpan(m_dky);
556}
557
559{
560 int ord;
561 CVodeGetLastOrder(m_cvode_mem, &ord);
562 return ord;
563}
564
566{
567 long int ne;
568 CVodeGetNumRhsEvals(m_cvode_mem, &ne);
569 return ne;
570}
571
573{
574 // AnyMap to return stats
575 AnyMap stats;
576
577 // long int linear solver stats provided by CVodes
578 long int steps = 0, rhsEvals = 0, errTestFails = 0, jacEvals = 0, linSetup = 0,
579 linRhsEvals = 0, linIters = 0, linConvFails = 0, precEvals = 0,
580 precSolves = 0, jtSetupEvals = 0, jTimesEvals = 0, nonlinIters = 0,
581 nonlinConvFails = 0, orderReductions = 0, stepSolveFails = 0;
582 int lastOrder = 0;
583;
584
585 CVodeGetNumSteps(m_cvode_mem, &steps);
586 CVodeGetNumRhsEvals(m_cvode_mem, &rhsEvals);
587 CVodeGetNonlinSolvStats(m_cvode_mem, &nonlinIters, &nonlinConvFails);
588 CVodeGetNumErrTestFails(m_cvode_mem, &errTestFails);
589 CVodeGetLastOrder(m_cvode_mem, &lastOrder);
590 CVodeGetNumStabLimOrderReds(m_cvode_mem, &orderReductions);
591 CVodeGetNumJacEvals(m_cvode_mem, &jacEvals);
592 CVodeGetNumLinRhsEvals(m_cvode_mem, &linRhsEvals);
593 CVodeGetNumLinSolvSetups(m_cvode_mem, &linSetup);
594 CVodeGetNumLinIters(m_cvode_mem, &linIters);
595 CVodeGetNumLinConvFails(m_cvode_mem, &linConvFails);
596 CVodeGetNumPrecEvals(m_cvode_mem, &precEvals);
597 CVodeGetNumPrecSolves(m_cvode_mem, &precSolves);
598 CVodeGetNumJTSetupEvals(m_cvode_mem, &jtSetupEvals);
599 CVodeGetNumJtimesEvals(m_cvode_mem, &jTimesEvals);
600 CVodeGetNumStepSolveFails(m_cvode_mem, &stepSolveFails);
601
602 stats["steps"] = steps;
603 stats["step_solve_fails"] = stepSolveFails;
604 stats["rhs_evals"] = rhsEvals;
605 stats["nonlinear_iters"] = nonlinIters;
606 stats["nonlinear_conv_fails"] = nonlinConvFails;
607 stats["err_test_fails"] = errTestFails;
608 stats["last_order"] = lastOrder;
609 stats["stab_order_reductions"] = orderReductions;
610
611 stats["jac_evals"] = jacEvals;
612 stats["lin_solve_setups"] = linSetup;
613 stats["lin_rhs_evals"] = linRhsEvals;
614 stats["lin_iters"] = linIters;
615 stats["lin_conv_fails"] = linConvFails;
616 stats["prec_evals"] = precEvals;
617 stats["prec_solves"] = precSolves;
618 stats["jt_vec_setup_evals"] = jtSetupEvals;
619 stats["jt_vec_prod_evals"] = jTimesEvals;
620 return stats;
621}
622
623double CVodesIntegrator::sensitivity(size_t k, size_t p)
624{
625 if (m_time == m_t0) {
626 // calls to CVodeGetSens are only allowed after a successful time step.
627 return 0.0;
628 }
629 if (!m_sens_ok && m_np) {
630 int flag = CVodeGetSensDky(m_cvode_mem, m_time, 0, m_yS);
631 checkError(flag, "sensitivity", "CVodeGetSens");
632 m_sens_ok = true;
633 }
634
635 if (k >= m_neq) {
636 throw CanteraError("CVodesIntegrator::sensitivity",
637 "sensitivity: k out of range ({})", k);
638 }
639 if (p >= m_np) {
640 throw CanteraError("CVodesIntegrator::sensitivity",
641 "sensitivity: p out of range ({})", p);
642 }
643 return NV_Ith_S(m_yS[p],k);
644}
645
647{
648 N_Vector errs = newNVector(m_neq, m_sundials_ctx);
649 N_Vector errw = newNVector(m_neq, m_sundials_ctx);
650 CVodeGetErrWeights(m_cvode_mem, errw);
651 CVodeGetEstLocalErrors(m_cvode_mem, errs);
652
653 vector<tuple<double, double, size_t>> weightedErrors;
654 for (size_t i=0; i<m_neq; i++) {
655 double err = NV_Ith_S(errs, i) * NV_Ith_S(errw, i);
656 weightedErrors.emplace_back(-abs(err), err, i);
657 }
658 N_VDestroy(errs);
659 N_VDestroy(errw);
660
661 N = std::min(N, static_cast<int>(m_neq));
662 sort(weightedErrors.begin(), weightedErrors.end());
663 fmt::memory_buffer s;
664 for (int i=0; i<N; i++) {
665 fmt_append(s, "{}: {}\n",
666 get<2>(weightedErrors[i]), get<1>(weightedErrors[i]));
667 }
668 return to_string(s);
669}
670
671void CVodesIntegrator::checkError(long flag, const string& ctMethod,
672 const string& cvodesMethod) const
673{
674 if (flag == CV_SUCCESS) {
675 return;
676 } else if (flag == CV_MEM_NULL) {
677 throw CanteraError("CVodesIntegrator::" + ctMethod,
678 "CVODES integrator is not initialized");
679 } else {
680 const char* flagname = CVodeGetReturnFlagName(flag);
681 throw CanteraError("CVodesIntegrator::" + ctMethod,
682 "{} returned error code {} ({}):\n{}",
683 cvodesMethod, flag, flagname, m_error_message);
684 }
685}
686
687}
A map of string keys to values whose type can vary at runtime.
Definition AnyMap.h:431
Wrapper class for 'cvodes' integrator from LLNL.
span< double > solution() override
The current value of the solution of the system of equations.
double step(double tout) override
Integrate the system of equations.
void setMaxStepSize(double hmax) override
Set the maximum step size.
N_Vector m_y
The system state at m_time.
void * m_cvode_mem
CVODES internal memory object.
void setRootFunctionCount(size_t nroots) override
Configure how many event/root functions the integrator should monitor.
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 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.
string m_error_message
Error message information provide by CVodes.
void setTolerances(double reltol, span< const double > abstol) override
Set error tolerances.
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.
span< double > derivative(double tout, int n) override
n-th derivative of the output function at time tout.
Base class for exceptions thrown by Cantera classes.
Virtual base class for ODE/DAE right-hand-side function evaluators.
Definition FuncEval.h:32
int evalRootFunctionsNoThrow(double t, span< const double > y, span< double > gout)
Wrapper for evalRootFunctions that converts exceptions to return codes.
Definition FuncEval.cpp:147
vector< double > m_paramScales
Scaling factors for each sensitivity parameter.
Definition FuncEval.h:208
void clearErrors()
Clear any previously-stored suppressed errors.
Definition FuncEval.h:198
virtual void getState(span< double > y)
Fill in the vector y with the current state of the system.
Definition FuncEval.h:166
virtual size_t neq() const =0
Number of equations.
int evalNoThrow(double t, span< const double > y, span< double > ydot)
Evaluate the right-hand side using return code to indicate status.
Definition FuncEval.cpp:7
virtual size_t nRootFunctions() const
Number of event/root functions exposed to the integrator.
Definition FuncEval.h:144
virtual size_t nparams() const
Number of sensitivity parameters.
Definition FuncEval.h:180
string getErrors() const
Return a string containing the text of any suppressed errors.
Definition FuncEval.cpp:72
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:205
shared_ptr< SystemJacobian > m_preconditioner
Pointer to preconditioner object used in integration which is set by setPreconditioner and initialize...
Definition Integrator.h:307
PreconditionerSide m_prec_side
Type of preconditioning used in applyOptions.
Definition Integrator.h:309
A wrapper for managing a SUNContext object.
static int cvodes_rhs(sunrealtype t, N_Vector y, N_Vector ydot, void *f_data)
Function called by cvodes to evaluate ydot given y.
Namespace for the Cantera kernel.
Definition AnyMap.cpp:595
const size_t npos
index returned by functions to indicate "no position"
Definition ct_defs.h:183
MethodType
Specifies the method used to integrate the system of equations.
Definition Integrator.h:23
@ BDF_Method
Backward Differentiation.
Definition Integrator.h:24
@ Adams_Method
Adams.
Definition Integrator.h:25
span< double > asSpan(Eigen::DenseBase< Derived > &v)
Convenience wrapper for accessing Eigen vector/array/map data as a span.
Definition eigen_dense.h:46
static int cvodes_root(sunrealtype t, N_Vector y, sunrealtype *gout, void *user_data)
SUNDIALS callback that forwards root evaluations to the FuncEval.
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.