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));
79 CVodesIntegrator::CVodesIntegrator()
85 CVodesIntegrator::~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;
162 throw CanteraError(
"CVodesIntegrator::setMethod",
"unknown method");
170 CVodeSetMaxStep(m_cvode_mem, hmax);
178 CVodeSetMinStep(m_cvode_mem, hmin);
186 CVodeSetMaxNumSteps(m_cvode_mem, m_maxsteps);
197 m_maxErrTestFails = n;
199 CVodeSetMaxErrTestFails(m_cvode_mem, n);
203 void CVodesIntegrator::sensInit(
double t0,
FuncEval& func)
209 #if CT_SUNDIALS_VERSION >= 60
210 m_yS = N_VCloneVectorArray(
static_cast<int>(m_np), y);
212 m_yS = N_VCloneVectorArray_Serial(
static_cast<int>(m_np), y);
214 for (
size_t n = 0; n < m_np; n++) {
215 N_VConst(0.0, m_yS[n]);
217 N_VDestroy_Serial(y);
219 int flag = CVodeSensInit(m_cvode_mem,
static_cast<int>(m_np),
220 CV_STAGGERED, CVSensRhsFn(0), m_yS);
221 checkError(flag,
"sensInit",
"CVodeSensInit");
223 vector<double> atol(m_np);
224 for (
size_t n = 0; n < m_np; n++) {
229 flag = CVodeSensSStolerances(m_cvode_mem, m_reltolsens, atol.data());
230 checkError(flag,
"sensInit",
"CVodeSensSStolerances");
242 if (
m_prec_side != PreconditionerSide::NO_PRECONDITION) {
246 N_VDestroy_Serial(
m_y);
251 N_VDestroy_Serial(m_dky);
254 N_VConst(0.0, m_dky);
256 if (m_itol == CV_SV && m_nabs < m_neq) {
258 "not enough absolute tolerance values specified.");
264 CVodeFree(&m_cvode_mem);
270 #if CT_SUNDIALS_VERSION < 40
271 m_cvode_mem = CVodeCreate(m_method, CV_NEWTON);
272 #elif CT_SUNDIALS_VERSION < 60
273 m_cvode_mem = CVodeCreate(m_method);
279 "CVodeCreate failed.");
283 if (flag != CV_SUCCESS) {
284 if (flag == CV_MEM_FAIL) {
286 "Memory allocation failed.");
287 }
else if (flag == CV_ILL_INPUT) {
289 "Illegal value for CVodeInit input argument.");
292 "CVodeInit failed.");
295 flag = CVodeSetErrHandlerFn(m_cvode_mem, &
cvodes_err,
this);
297 if (m_itol == CV_SV) {
298 flag = CVodeSVtolerances(m_cvode_mem, m_reltol, m_abstol);
299 checkError(flag,
"initialize",
"CVodeSVtolerances");
301 flag = CVodeSStolerances(m_cvode_mem, m_reltol, m_abstols);
302 checkError(flag,
"initialize",
"CVodeSStolerances");
305 flag = CVodeSetUserData(m_cvode_mem, &func);
306 checkError(flag,
"initialize",
"CVodeSetUserData");
310 flag = CVodeSetSensParams(m_cvode_mem, func.
m_sens_params.data(),
312 checkError(flag,
"initialize",
"CVodeSetSensParams");
317 void CVodesIntegrator::reinitialize(
double t0,
FuncEval& func)
326 if (
m_prec_side != PreconditionerSide::NO_PRECONDITION) {
329 int result = CVodeReInit(m_cvode_mem, m_t0,
m_y);
330 checkError(result,
"reinitialize",
"CVodeReInit");
336 if (m_type ==
"DENSE") {
337 sd_size_t N =
static_cast<sd_size_t
>(m_neq);
338 SUNLinSolFree((SUNLinearSolver)
m_linsol);
340 #if CT_SUNDIALS_VERSION >= 60
347 "Unable to create SUNDenseMatrix of size {0} x {0}", N);
350 #if CT_SUNDIALS_VERSION >= 60
351 #if CT_SUNDIALS_USE_LAPACK
358 flag = CVodeSetLinearSolver(m_cvode_mem, (SUNLinearSolver)
m_linsol,
361 #if CT_SUNDIALS_USE_LAPACK
366 flag = CVDlsSetLinearSolver(m_cvode_mem, (SUNLinearSolver)
m_linsol,
371 "Error creating Sundials dense linear solver object");
372 }
else if (flag != CV_SUCCESS) {
374 "Error connecting linear solver to CVODES. "
375 "Sundials error code: {}", flag);
379 if (
m_prec_side != PreconditionerSide::NO_PRECONDITION) {
381 "Preconditioning is not available with the specified problem type.");
383 }
else if (m_type ==
"DIAG") {
386 if (
m_prec_side != PreconditionerSide::NO_PRECONDITION) {
388 "Preconditioning is not available with the specified problem type.");
390 }
else if (m_type ==
"GMRES") {
391 #if CT_SUNDIALS_VERSION >= 60
393 CVodeSetLinearSolver(m_cvode_mem, (SUNLinearSolver)
m_linsol,
nullptr);
394 #elif CT_SUNDIALS_VERSION >= 40
396 CVSpilsSetLinearSolver(m_cvode_mem, (SUNLinearSolver)
m_linsol);
399 CVSpilsSetLinearSolver(m_cvode_mem, (SUNLinearSolver)
m_linsol);
402 #if CT_SUNDIALS_VERSION >= 40
403 if (
m_prec_side != PreconditionerSide::NO_PRECONDITION) {
404 SUNLinSol_SPGMRSetPrecType((SUNLinearSolver)
m_linsol,
406 CVodeSetPreconditioner(m_cvode_mem, cvodes_prec_setup,
410 if (
m_prec_side != PreconditionerSide::NO_PRECONDITION) {
411 SUNSPGMRSetPrecType((SUNLinearSolver)
m_linsol,
413 CVSpilsSetPreconditioner(m_cvode_mem, cvodes_prec_setup,
417 }
else if (m_type ==
"BAND") {
418 sd_size_t N =
static_cast<sd_size_t
>(m_neq);
419 sd_size_t nu = m_mupper;
420 sd_size_t nl = m_mlower;
421 SUNLinSolFree((SUNLinearSolver)
m_linsol);
423 #if CT_SUNDIALS_VERSION >= 60
425 #elif CT_SUNDIALS_VERSION >= 40
432 "Unable to create SUNBandMatrix of size {} with bandwidths "
433 "{} and {}", N, nu, nl);
435 #if CT_SUNDIALS_VERSION >= 60
436 #if CT_SUNDIALS_USE_LAPACK
443 CVodeSetLinearSolver(m_cvode_mem, (SUNLinearSolver)
m_linsol,
446 #if CT_SUNDIALS_USE_LAPACK
451 CVDlsSetLinearSolver(m_cvode_mem, (SUNLinearSolver)
m_linsol,
456 "unsupported linear solver flag '{}'", m_type);
460 int flag = CVodeSetMaxOrd(m_cvode_mem, m_maxord);
461 checkError(flag,
"applyOptions",
"CVodeSetMaxOrd");
463 if (m_maxsteps > 0) {
464 CVodeSetMaxNumSteps(m_cvode_mem, m_maxsteps);
467 CVodeSetMaxStep(m_cvode_mem, m_hmax);
470 CVodeSetMinStep(m_cvode_mem, m_hmin);
472 if (m_maxErrTestFails > 0) {
473 CVodeSetMaxErrTestFails(m_cvode_mem, m_maxErrTestFails);
481 }
else if (tout <
m_time) {
483 "Cannot integrate backwards in time.\n"
484 "Requested time {} < current time {}",
489 if (nsteps >= m_maxsteps) {
491 "Maximum number of timesteps ({}) taken without reaching output "
492 "time ({}).\nCurrent integrator time: {}",
495 int flag = CVode(m_cvode_mem, tout,
m_y, &
m_tInteg, CV_ONE_STEP);
496 if (flag != CV_SUCCESS) {
498 if (!f_errs.empty()) {
499 f_errs =
"Exceptions caught during RHS evaluation:\n" + f_errs;
502 "CVodes error encountered. Error code: {}\n{}\n"
504 "Components with largest weighted error estimates:\n{}",
509 int flag = CVodeGetDky(m_cvode_mem, tout, 0,
m_y);
517 int flag = CVode(m_cvode_mem, tout,
m_y, &
m_tInteg, CV_ONE_STEP);
518 if (flag != CV_SUCCESS) {
520 if (!f_errs.empty()) {
521 f_errs =
"Exceptions caught during RHS evaluation:\n" + f_errs;
524 "CVodes error encountered. Error code: {}\n{}\n"
526 "Components with largest weighted error estimates:\n{}",
537 int flag = CVodeGetDky(m_cvode_mem, tout, n, m_dky);
538 checkError(flag,
"derivative",
"CVodeGetDky");
539 return NV_DATA_S(m_dky);
545 CVodeGetLastOrder(m_cvode_mem, &ord);
552 CVodeGetNumRhsEvals(m_cvode_mem, &ne);
562 long int steps = 0, rhsEvals = 0, errTestFails = 0, jacEvals = 0, linSetup = 0,
563 linRhsEvals = 0, linIters = 0, linConvFails = 0, precEvals = 0,
564 precSolves = 0, jtSetupEvals = 0, jTimesEvals = 0, nonlinIters = 0,
565 nonlinConvFails = 0, orderReductions = 0;
568 #if CT_SUNDIALS_VERSION >= 40
569 CVodeGetNumSteps(m_cvode_mem, &steps);
570 CVodeGetNumRhsEvals(m_cvode_mem, &rhsEvals);
571 CVodeGetNonlinSolvStats(m_cvode_mem, &nonlinIters, &nonlinConvFails);
572 CVodeGetNumErrTestFails(m_cvode_mem, &errTestFails);
573 CVodeGetLastOrder(m_cvode_mem, &
lastOrder);
574 CVodeGetNumStabLimOrderReds(m_cvode_mem, &orderReductions);
575 CVodeGetNumJacEvals(m_cvode_mem, &jacEvals);
576 CVodeGetNumLinRhsEvals(m_cvode_mem, &linRhsEvals);
577 CVodeGetNumLinSolvSetups(m_cvode_mem, &linSetup);
578 CVodeGetNumLinIters(m_cvode_mem, &linIters);
579 CVodeGetNumLinConvFails(m_cvode_mem, &linConvFails);
580 CVodeGetNumPrecEvals(m_cvode_mem, &precEvals);
581 CVodeGetNumPrecSolves(m_cvode_mem, &precSolves);
582 CVodeGetNumJTSetupEvals(m_cvode_mem, &jtSetupEvals);
583 CVodeGetNumJtimesEvals(m_cvode_mem, &jTimesEvals);
585 warn_user(
"CVodesIntegrator::solverStats",
"Function not"
586 "supported with sundials versions less than 4.");
589 #if CT_SUNDIALS_VERION >= 60
590 long int stepSolveFails = 0;
591 CVodeGetNumStepSolveFails(m_cvode_mem, &stepSolveFails);
592 stats[
"step_solve_fails"] = stepSolveFails;
595 stats[
"steps"] = steps;
596 stats[
"rhs_evals"] = rhsEvals;
597 stats[
"nonlinear_iters"] = nonlinIters;
598 stats[
"nonlinear_conv_fails"] = nonlinConvFails;
599 stats[
"err_test_fails"] = errTestFails;
601 stats[
"stab_order_reductions"] = orderReductions;
603 stats[
"jac_evals"] = jacEvals;
604 stats[
"lin_solve_setups"] = linSetup;
605 stats[
"lin_rhs_evals"] = linRhsEvals;
606 stats[
"lin_iters"] = linIters;
607 stats[
"lin_conv_fails"] = linConvFails;
608 stats[
"prec_evals"] = precEvals;
609 stats[
"prec_solves"] = precSolves;
610 stats[
"jt_vec_setup_evals"] = jtSetupEvals;
611 stats[
"jt_vec_prod_evals"] = jTimesEvals;
615 double CVodesIntegrator::sensitivity(
size_t k,
size_t p)
622 int flag = CVodeGetSensDky(m_cvode_mem,
m_time, 0, m_yS);
623 checkError(flag,
"sensitivity",
"CVodeGetSens");
628 throw CanteraError(
"CVodesIntegrator::sensitivity",
629 "sensitivity: k out of range ({})", k);
632 throw CanteraError(
"CVodesIntegrator::sensitivity",
633 "sensitivity: p out of range ({})", p);
635 return NV_Ith_S(m_yS[p],k);
642 CVodeGetErrWeights(m_cvode_mem, errw);
643 CVodeGetEstLocalErrors(m_cvode_mem, errs);
645 vector<tuple<double, double, size_t>> weightedErrors;
646 for (
size_t i=0; i<m_neq; i++) {
647 double err = NV_Ith_S(errs, i) * NV_Ith_S(errw, i);
648 weightedErrors.emplace_back(-abs(err), err, i);
653 N = std::min(N,
static_cast<int>(m_neq));
654 sort(weightedErrors.begin(), weightedErrors.end());
655 fmt::memory_buffer s;
656 for (
int i=0; i<N; i++) {
658 get<2>(weightedErrors[i]), get<1>(weightedErrors[i]));
664 const string& cvodesMethod)
const
666 if (flag == CV_SUCCESS) {
668 }
else if (flag == CV_MEM_NULL) {
670 "CVODES integrator is not initialized");
672 const char* flagname = CVodeGetReturnFlagName(flag);
674 "{} 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.
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 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.
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.