12#include "cantera/numerics/sundials_headers.h"
18#if CT_SUNDIALS_VERSION >= 60
19 return N_VNew_Serial(
static_cast<sd_size_t
>(N), context.get());
21 return N_VNew_Serial(
static_cast<sd_size_t
>(N));
39 static int cvodes_rhs(realtype t, N_Vector y, N_Vector ydot,
void* f_data)
42 return f->
evalNoThrow(t, NV_DATA_S(y), NV_DATA_S(ydot));
48 static void cvodes_err(
int error_code,
const char* module,
49 const char* function,
char* msg,
void* eh_data)
56 static int cvodes_prec_setup(realtype t, N_Vector y, N_Vector ydot, booleantype jok,
57 booleantype *jcurPtr, realtype gamma,
void *f_data)
59 FuncEval* f = (FuncEval*) f_data;
62 return f->preconditioner_setup_nothrow(t, NV_DATA_S(y), gamma);
64 f->updatePreconditioner(gamma);
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,
74 FuncEval* f = (FuncEval*) f_data;
75 return f->preconditioner_solve_nothrow(NV_DATA_S(r),NV_DATA_S(z));
85CVodesIntegrator::~CVodesIntegrator()
89 CVodeSensFree(m_cvode_mem);
91 CVodeFree(&m_cvode_mem);
94 SUNLinSolFree((SUNLinearSolver)
m_linsol);
98 N_VDestroy_Serial(
m_y);
101 N_VDestroy_Serial(m_abstol);
104 N_VDestroy_Serial(m_dky);
107 #if CT_SUNDIALS_VERSION >= 60
108 N_VDestroyVectorArray(m_yS,
static_cast<int>(m_np));
110 N_VDestroyVectorArray_Serial(m_yS,
static_cast<int>(m_np));
118 return NV_Ith_S(
m_y, k);
123 return NV_DATA_S(
m_y);
132 N_VDestroy_Serial(m_abstol);
136 for (
size_t i=0; i<n; i++) {
137 NV_Ith_S(m_abstol, i) = abstol[i];
151 m_reltolsens = reltol;
152 m_abstolsens = abstol;
158 "To be removed. Set linear solver type with setLinearSolverType");
159 if (probtype == DIAG)
162 }
else if (probtype == DENSE + NOJAC) {
164 }
else if (probtype == BAND + NOJAC) {
166 }
else if (probtype == GMRES) {
180 throw CanteraError(
"CVodesIntegrator::setMethod",
"unknown method");
188 CVodeSetMaxStep(m_cvode_mem, hmax);
196 CVodeSetMinStep(m_cvode_mem, hmin);
204 CVodeSetMaxNumSteps(m_cvode_mem, m_maxsteps);
215 m_maxErrTestFails = n;
217 CVodeSetMaxErrTestFails(m_cvode_mem, n);
221void CVodesIntegrator::sensInit(
double t0,
FuncEval& func)
227 #if CT_SUNDIALS_VERSION >= 60
228 m_yS = N_VCloneVectorArray(
static_cast<int>(m_np), y);
230 m_yS = N_VCloneVectorArray_Serial(
static_cast<int>(m_np), y);
232 for (
size_t n = 0; n < m_np; n++) {
233 N_VConst(0.0, m_yS[n]);
235 N_VDestroy_Serial(y);
237 int flag = CVodeSensInit(m_cvode_mem,
static_cast<int>(m_np),
238 CV_STAGGERED, CVSensRhsFn(0), m_yS);
239 checkError(flag,
"sensInit",
"CVodeSensInit");
241 vector<double> atol(m_np);
242 for (
size_t n = 0; n < m_np; n++) {
247 flag = CVodeSensSStolerances(m_cvode_mem, m_reltolsens, atol.data());
248 checkError(flag,
"sensInit",
"CVodeSensSStolerances");
260 if (
m_prec_side != PreconditionerSide::NO_PRECONDITION) {
264 N_VDestroy_Serial(
m_y);
269 N_VDestroy_Serial(m_dky);
272 N_VConst(0.0, m_dky);
274 if (m_itol == CV_SV && m_nabs < m_neq) {
276 "not enough absolute tolerance values specified.");
282 CVodeFree(&m_cvode_mem);
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);
297 "CVodeCreate failed.");
301 if (flag != CV_SUCCESS) {
302 if (flag == CV_MEM_FAIL) {
304 "Memory allocation failed.");
305 }
else if (flag == CV_ILL_INPUT) {
307 "Illegal value for CVodeInit input argument.");
310 "CVodeInit failed.");
313 flag = CVodeSetErrHandlerFn(m_cvode_mem, &
cvodes_err,
this);
315 if (m_itol == CV_SV) {
316 flag = CVodeSVtolerances(m_cvode_mem, m_reltol, m_abstol);
317 checkError(flag,
"initialize",
"CVodeSVtolerances");
319 flag = CVodeSStolerances(m_cvode_mem, m_reltol, m_abstols);
320 checkError(flag,
"initialize",
"CVodeSStolerances");
323 flag = CVodeSetUserData(m_cvode_mem, &func);
324 checkError(flag,
"initialize",
"CVodeSetUserData");
328 flag = CVodeSetSensParams(m_cvode_mem, func.
m_sens_params.data(),
330 checkError(flag,
"initialize",
"CVodeSetSensParams");
335void CVodesIntegrator::reinitialize(
double t0,
FuncEval& func)
344 if (
m_prec_side != PreconditionerSide::NO_PRECONDITION) {
347 int result = CVodeReInit(m_cvode_mem, m_t0,
m_y);
348 checkError(result,
"reinitialize",
"CVodeReInit");
354 if (m_type ==
"DENSE") {
355 sd_size_t N =
static_cast<sd_size_t
>(m_neq);
356 SUNLinSolFree((SUNLinearSolver)
m_linsol);
358 #if CT_SUNDIALS_VERSION >= 60
365 "Unable to create SUNDenseMatrix of size {0} x {0}", N);
368 #if CT_SUNDIALS_VERSION >= 60
369 #if CT_SUNDIALS_USE_LAPACK
376 flag = CVodeSetLinearSolver(m_cvode_mem, (SUNLinearSolver)
m_linsol,
379 #if CT_SUNDIALS_USE_LAPACK
384 flag = CVDlsSetLinearSolver(m_cvode_mem, (SUNLinearSolver)
m_linsol,
389 "Error creating Sundials dense linear solver object");
390 }
else if (flag != CV_SUCCESS) {
392 "Error connecting linear solver to CVODES. "
393 "Sundials error code: {}", flag);
397 if (
m_prec_side != PreconditionerSide::NO_PRECONDITION) {
399 "Preconditioning is not available with the specified problem type.");
401 }
else if (m_type ==
"DIAG") {
404 if (
m_prec_side != PreconditionerSide::NO_PRECONDITION) {
406 "Preconditioning is not available with the specified problem type.");
408 }
else if (m_type ==
"GMRES") {
409 #if CT_SUNDIALS_VERSION >= 60
411 CVodeSetLinearSolver(m_cvode_mem, (SUNLinearSolver)
m_linsol,
nullptr);
412 #elif CT_SUNDIALS_VERSION >= 40
414 CVSpilsSetLinearSolver(m_cvode_mem, (SUNLinearSolver)
m_linsol);
417 CVSpilsSetLinearSolver(m_cvode_mem, (SUNLinearSolver)
m_linsol);
420 #if CT_SUNDIALS_VERSION >= 40
421 if (
m_prec_side != PreconditionerSide::NO_PRECONDITION) {
422 SUNLinSol_SPGMRSetPrecType((SUNLinearSolver)
m_linsol,
424 CVodeSetPreconditioner(m_cvode_mem, cvodes_prec_setup,
428 if (
m_prec_side != PreconditionerSide::NO_PRECONDITION) {
429 SUNSPGMRSetPrecType((SUNLinearSolver)
m_linsol,
431 CVSpilsSetPreconditioner(m_cvode_mem, cvodes_prec_setup,
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);
441 #if CT_SUNDIALS_VERSION >= 60
443 #elif CT_SUNDIALS_VERSION >= 40
450 "Unable to create SUNBandMatrix of size {} with bandwidths "
451 "{} and {}", N, nu, nl);
453 #if CT_SUNDIALS_VERSION >= 60
454 #if CT_SUNDIALS_USE_LAPACK
461 CVodeSetLinearSolver(m_cvode_mem, (SUNLinearSolver)
m_linsol,
464 #if CT_SUNDIALS_USE_LAPACK
469 CVDlsSetLinearSolver(m_cvode_mem, (SUNLinearSolver)
m_linsol,
474 "unsupported linear solver flag '{}'", m_type);
478 int flag = CVodeSetMaxOrd(m_cvode_mem, m_maxord);
479 checkError(flag,
"applyOptions",
"CVodeSetMaxOrd");
481 if (m_maxsteps > 0) {
482 CVodeSetMaxNumSteps(m_cvode_mem, m_maxsteps);
485 CVodeSetMaxStep(m_cvode_mem, m_hmax);
488 CVodeSetMinStep(m_cvode_mem, m_hmin);
490 if (m_maxErrTestFails > 0) {
491 CVodeSetMaxErrTestFails(m_cvode_mem, m_maxErrTestFails);
499 }
else if (tout <
m_time) {
501 "Cannot integrate backwards in time.\n"
502 "Requested time {} < current time {}",
507 if (nsteps >= m_maxsteps) {
509 "Maximum number of timesteps ({}) taken without reaching output "
510 "time ({}).\nCurrent integrator time: {}",
513 int flag = CVode(m_cvode_mem, tout,
m_y, &
m_tInteg, CV_ONE_STEP);
514 if (flag != CV_SUCCESS) {
516 if (!f_errs.empty()) {
517 f_errs =
"Exceptions caught during RHS evaluation:\n" + f_errs;
520 "CVodes error encountered. Error code: {}\n{}\n"
522 "Components with largest weighted error estimates:\n{}",
527 int flag = CVodeGetDky(m_cvode_mem, tout, 0,
m_y);
535 int flag = CVode(m_cvode_mem, tout,
m_y, &
m_tInteg, CV_ONE_STEP);
536 if (flag != CV_SUCCESS) {
538 if (!f_errs.empty()) {
539 f_errs =
"Exceptions caught during RHS evaluation:\n" + f_errs;
542 "CVodes error encountered. Error code: {}\n{}\n"
544 "Components with largest weighted error estimates:\n{}",
555 int flag = CVodeGetDky(m_cvode_mem, tout, n, m_dky);
556 checkError(flag,
"derivative",
"CVodeGetDky");
557 return NV_DATA_S(m_dky);
563 CVodeGetLastOrder(m_cvode_mem, &ord);
570 CVodeGetNumRhsEvals(m_cvode_mem, &ne);
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;
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);
603 warn_user(
"CVodesIntegrator::solverStats",
"Function not"
604 "supported with sundials versions less than 4.");
607 #if CT_SUNDIALS_VERION >= 60
608 long int stepSolveFails = 0;
609 CVodeGetNumStepSolveFails(m_cvode_mem, &stepSolveFails);
610 stats[
"step_solve_fails"] = stepSolveFails;
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;
619 stats[
"stab_order_reductions"] = orderReductions;
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;
633double CVodesIntegrator::sensitivity(
size_t k,
size_t p)
640 int flag = CVodeGetSensDky(m_cvode_mem,
m_time, 0, m_yS);
641 checkError(flag,
"sensitivity",
"CVodeGetSens");
646 throw CanteraError(
"CVodesIntegrator::sensitivity",
647 "sensitivity: k out of range ({})", k);
650 throw CanteraError(
"CVodesIntegrator::sensitivity",
651 "sensitivity: p out of range ({})", p);
653 return NV_Ith_S(m_yS[p],k);
660 CVodeGetErrWeights(m_cvode_mem, errw);
661 CVodeGetEstLocalErrors(m_cvode_mem, errs);
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);
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++) {
676 get<2>(weightedErrors[i]), get<1>(weightedErrors[i]));
682 const string& cvodesMethod)
const
684 if (flag == CV_SUCCESS) {
686 }
else if (flag == CV_MEM_NULL) {
688 "CVODES integrator is not initialized");
690 const char* flagname = CVodeGetReturnFlagName(flag);
692 "{} returned error code {} ({}):\n{}",
A map of string keys to values whose type can vary at runtime.
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.
CVodesIntegrator()
Constructor.
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.
vector< double > m_paramScales
Scaling factors for each sensitivity parameter.
void clearErrors()
Clear any previously-stored suppressed errors.
int evalNoThrow(double t, double *y, double *ydot)
Evaluate the right-hand side using return code to indicate status.
virtual size_t neq() const =0
Number of equations.
virtual size_t nparams() const
Number of sensitivity parameters.
string getErrors() const
Return a string containing the text of any suppressed errors.
vector< double > m_sens_params
Values for the problem parameters for which sensitivities are computed This is the array which is per...
virtual void getState(double *y)
Fill in the vector y with the current state of the system.
shared_ptr< PreconditionerBase > m_preconditioner
Pointer to preconditioner object used in integration which is set by setPreconditioner and initialize...
PreconditionerSide m_prec_side
Type of preconditioning used in applyOptions.
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....
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.
Namespace for the Cantera kernel.
MethodType
Specifies the method used to integrate the system of equations.
@ BDF_Method
Backward Differentiation.
void warn_deprecated(const string &source, const AnyBase &node, const string &message)
A deprecation warning for syntax in an input file.
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.