Chaste Commit::29c3c4ab2bee00ddd311a035a1e522c03cbe928a
AbstractCvodeSystem.cpp
1/*
2
3Copyright (c) 2005-2026, University of Oxford.
4All rights reserved.
5
6University of Oxford means the Chancellor, Masters and Scholars of the
7University of Oxford, having an administrative office at Wellington
8Square, Oxford OX1 2JD, UK.
9
10This file is part of Chaste.
11
12Redistribution and use in source and binary forms, with or without
13modification, are permitted provided that the following conditions are met:
14 * Redistributions of source code must retain the above copyright notice,
15 this list of conditions and the following disclaimer.
16 * Redistributions in binary form must reproduce the above copyright notice,
17 this list of conditions and the following disclaimer in the documentation
18 and/or other materials provided with the distribution.
19 * Neither the name of the University of Oxford nor the names of its
20 contributors may be used to endorse or promote products derived from this
21 software without specific prior written permission.
22
23THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33
34*/
35
36#include <cassert>
37#include <sstream>
38
39#include "AbstractCvodeSystem.hpp"
40#include "CvodeAdaptor.hpp" // For CvodeErrorHandler
41#include "Exception.hpp"
42#include "MathsCustomFunctions.hpp" // For tolerance comparison
43#include "TimeStepper.hpp"
45
46// CVODE headers
47#include <cvode/cvode.h>
48#include <sundials/sundials_nvector.h>
49
50#if CHASTE_SUNDIALS_VERSION < 70000
51#include <cvode/cvode_direct.h> /* access to CVDls interface */
52#endif
53#include <sundials/sundials_types.h> /* defs. of realtype, sunindextype */
54#include <sunlinsol/sunlinsol_dense.h> /* access to dense SUNLinearSolver */
55#include <sunmatrix/sunmatrix_dense.h> /* access to dense SUNMatrix */
56
57#if CHASTE_SUNDIALS_VERSION >= 60000
58#include "CvodeContextManager.hpp" // access to shared SUNContext object required by Sundials 6.0+
59#endif
60
61//#include "Debug.hpp"
62//void DebugSteps(void* pCvodeMem, AbstractCvodeSystem* pSys)
63//{
64// long int num_jac_evals, nniters, num_steps;
65// CVDenseGetNumJacEvals(pCvodeMem, &num_jac_evals);
67// CVodeGetNumNonlinSolvIters(pCvodeMem, &nniters);
68// CVodeGetNumSteps(pCvodeMem, &num_steps);
69// double num_newton_iters = nniters;
70// PRINT_3_VARIABLES(pSys->GetSystemName(), num_newton_iters/num_steps, num_jac_evals/num_newton_iters);
71//}
81int AbstractCvodeSystemRhsAdaptor(realtype t, N_Vector y, N_Vector ydot, void* pData)
82{
83 assert(pData != nullptr);
84 AbstractCvodeSystem* p_ode_system = (AbstractCvodeSystem*)pData;
85 try
86 {
87 p_ode_system->EvaluateYDerivatives(t, y, ydot);
88 }
89 catch (const Exception& e)
90 {
91 std::cerr << "CVODE RHS Exception: " << e.GetMessage()
92 << std::endl
93 << std::flush;
94 return -1;
95 }
96
97 // Something like this might help CVODE when things are a bit unstable...
98 // We tried this again in Jan 2023 but now with VerifyStateVariables() returning a recoverable error when values look off. However this didn't improve the situation.
99 // See: https://github.com/Chaste/Chaste/issues/46
100 // try
101 // {
102 // p_ode_system->VerifyStateVariables();
103 // }
104 // catch (const Exception &e)
105 // {
106 // std::cout << "t = " << t << ":\t" << e.GetMessage() << std::endl << std::flush;
107 // return 1; // A positive return flag to CVODE tells it there's been an error but it might be recoverable.
108 // }
109
110 return 0;
111}
112
113/*
114 * The dense Jacobian function in the form CVODE expects.
115 */
116int AbstractCvodeSystemJacAdaptor(realtype t, N_Vector y, N_Vector ydot, CHASTE_CVODE_DENSE_MATRIX jacobian,
117 void* pData, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3)
118{
119 assert(pData != nullptr);
120 AbstractCvodeSystem* p_ode_system = (AbstractCvodeSystem*)pData;
121 try
122 {
123 p_ode_system->EvaluateAnalyticJacobian(t, y, ydot, jacobian, tmp1, tmp2, tmp3);
124 }
125 catch (const Exception& e)
126 {
127 std::cerr << "CVODE Jacobian Exception: " << e.GetMessage() << std::endl
128 << std::flush;
129 return -1;
130 }
131 return 0;
132}
133
134AbstractCvodeSystem::AbstractCvodeSystem(unsigned numberOfStateVariables)
135 : AbstractParameterisedSystem<N_Vector>(numberOfStateVariables),
136 mLastSolutionState(nullptr),
137 mLastSolutionTime(0.0),
138 mForceReset(false),
139 mForceMinimalReset(false),
140 mpSundialsDenseMatrix(nullptr),
141 mpSundialsLinearSolver(nullptr),
142 mHasAnalyticJacobian(false),
143 mUseAnalyticJacobian(false),
144 mpCvodeMem(nullptr),
145#if CHASTE_SUNDIALS_VERSION >= 60000
146 mpSundialsContextManager(CvodeContextManager::Instance()),
147#endif
148 mMaxSteps(0),
149 mLastInternalStepSize(0)
150{
151 SetTolerances(); // Set the tolerances to the defaults.
152}
153
155{
159#if CHASTE_SUNDIALS_VERSION >= 60000
160 mParameters = N_VNew_Serial(rGetParameterNames().size(), CvodeContextManager::Instance()->GetSundialsContext());
161#else
162 mParameters = N_VNew_Serial(rGetParameterNames().size());
163#endif
164 for (int i = 0; i < NV_LENGTH_S(mParameters); i++)
165 {
166 NV_Ith_S(mParameters, i) = 0.0;
167 }
168}
169
177
178//
179//double AbstractCvodeSystem::CalculateRootFunction(double time, const std::vector<double>& rY)
180//{
181// bool stop = CalculateStoppingEvent(time, rY);
182// return stop ? 0.0 : 1.0;
183//}
184
186 realtype tEnd,
187 realtype maxDt,
188 realtype tSamp)
189{
190 assert(tEnd >= tStart);
191 assert(tSamp > 0.0);
192
193 SetupCvode(mStateVariables, tStart, maxDt);
194
195 TimeStepper stepper(tStart, tEnd, tSamp);
196
197 // Set up ODE solution
198 OdeSolution solutions;
199 solutions.SetNumberOfTimeSteps(stepper.EstimateTimeSteps());
200 solutions.rGetSolutions().push_back(MakeStdVec(mStateVariables));
201 solutions.rGetTimes().push_back(tStart);
203
204 // Main time sampling loop
205 while (!stepper.IsTimeAtEnd())
206 {
207 // This should stop CVODE going past the end of where we wanted and interpolating back.
208 int ierr = CVodeSetStopTime(mpCvodeMem, stepper.GetNextTime());
209 assert(ierr == CV_SUCCESS);
210 UNUSED_OPT(ierr); // avoid unused var warning
211
212 // // This parameter governs how many times we allow a recoverable right hand side failure
213 // int ierr = CVodeSetMaxConvFails(mpCvodeMem, 1000);
214 // assert(ierr == CV_SUCCESS); UNUSED_OPT(ierr); // avoid unused var warning
215
216 double cvode_stopped_at = stepper.GetTime();
217 ierr = CVode(mpCvodeMem, stepper.GetNextTime(), mStateVariables,
218 &cvode_stopped_at, CV_NORMAL);
219 if (ierr < 0)
220 {
221 // DebugSteps(mpCvodeMem, this);
222 CvodeError(ierr, "CVODE failed to solve system", cvode_stopped_at, stepper.GetTime(), stepper.GetNextTime());
223 }
224 // Not root finding, so should have reached requested time
225 assert(fabs(cvode_stopped_at - stepper.GetNextTime()) < DBL_EPSILON);
226#ifndef NDEBUG
228#endif
229 // Store solution
230 solutions.rGetSolutions().push_back(MakeStdVec(mStateVariables));
231 solutions.rGetTimes().push_back(cvode_stopped_at);
232 stepper.AdvanceOneTimeStep();
233 }
234
235 // stepper.EstimateTimeSteps may have been an overestimate...
236 solutions.SetNumberOfTimeSteps(stepper.GetTotalTimeStepsTaken());
237
238 int ierr = CVodeGetLastStep(mpCvodeMem, &mLastInternalStepSize);
239 assert(ierr == CV_SUCCESS);
240 UNUSED_OPT(ierr); // avoid unused var warning
241
243
244 return solutions;
245}
246
247void AbstractCvodeSystem::Solve(realtype tStart,
248 realtype tEnd,
249 realtype maxDt)
250{
251 assert(tEnd >= tStart);
252
253 SetupCvode(mStateVariables, tStart, maxDt);
254
255 // This should stop CVODE going past the end of where we wanted and interpolating back.
256 int ierr = CVodeSetStopTime(mpCvodeMem, tEnd);
257 assert(ierr == CV_SUCCESS);
258 UNUSED_OPT(ierr); // avoid unused var warning
259
260 double cvode_stopped_at = tStart;
261 ierr = CVode(mpCvodeMem, tEnd, mStateVariables, &cvode_stopped_at, CV_NORMAL);
262 if (ierr < 0)
263 {
264 // DebugSteps(mpCvodeMem, this);
265 CvodeError(ierr, "CVODE failed to solve system", cvode_stopped_at, tStart, tEnd);
266 }
267 // Not root finding, so should have reached requested time
268 assert(fabs(cvode_stopped_at - tEnd) < DBL_EPSILON);
269
270 ierr = CVodeGetLastStep(mpCvodeMem, &mLastInternalStepSize);
271 assert(ierr == CV_SUCCESS);
272 UNUSED_OPT(ierr); // avoid unused var warning
273
274 RecordStoppingPoint(cvode_stopped_at);
275
276//
277// long int nst, nfe, nsetups, nje, nfeLS, nni, ncfn, netf, nge;
278//
279//
280// CVodeGetNumSteps(mpCvodeMem, &nst);
281// CVodeGetNumRhsEvals(mpCvodeMem, &nfe);
282// CVodeGetNumLinSolvSetups(mpCvodeMem, &nsetups);
283// CVodeGetNumErrTestFails(mpCvodeMem, &netf);
284// CVodeGetNumNonlinSolvIters(mpCvodeMem, &nni);
285// CVodeGetNumNonlinSolvConvFails(mpCvodeMem, &ncfn);
286// CVDlsGetNumJacEvals(mpCvodeMem, &nje);
287// CVDlsGetNumRhsEvals(mpCvodeMem, &nfeLS);
288// CVodeGetNumGEvals(mpCvodeMem, &nge);
289//
290// printf("\nFinal Statistics:\n");
291// printf("nst = %-6ld nfe = %-6ld nsetups = %-6ld nfeLS = %-6ld nje = %ld\n",
292// nst, nfe, nsetups, nfeLS, nje);
293// printf("nni = %-6ld ncfn = %-6ld netf = %-6ld nge = %ld\n \n",
294// nni, ncfn, netf, nge);
295// std::cout << std::flush;
296#ifndef NDEBUG
298#endif
299}
300
301void AbstractCvodeSystem::SetMaxSteps(long int numSteps)
302{
303 mMaxSteps = numSteps;
304}
305
307{
308 return mMaxSteps;
309}
310
311void AbstractCvodeSystem::SetTolerances(double relTol, double absTol)
312{
313 mRelTol = relTol;
314 mAbsTol = absTol;
315 ResetSolver();
316}
317
322
327
332
334{
335 mForceReset = autoReset;
336 if (mForceReset)
337 {
338 ResetSolver();
339 }
340}
341
346
351
353{
354 mForceMinimalReset = minimalReset;
356 {
357 SetForceReset(false);
358 }
359}
360
365
367 realtype tStart,
368 realtype maxDt)
369{
370 assert((unsigned)NV_LENGTH_S(initialConditions) == GetNumberOfStateVariables());
371 assert(maxDt >= 0.0);
372
373 // Find out if we need to (re-)initialise
374 //std::cout << "!mpCvodeMem = " << !mpCvodeMem << ", mForceReset = " << mForceReset << ", !mLastSolutionState = " << !mLastSolutionState << ", comp doubles = " << !CompareDoubles::WithinAnyTolerance(tStart, mLastSolutionTime) << "\n";
376 if (!reinit && !mForceMinimalReset)
377 {
378 const unsigned size = GetNumberOfStateVariables();
379 for (unsigned i = 0; i < size; i++)
380 {
382 {
383 reinit = true;
384 break;
385 }
386 }
387 }
388
389 if (!mpCvodeMem)
390 {
391 //std::cout << "New CVODE solver\n";
392#if CHASTE_SUNDIALS_VERSION >= 60000
393 mpCvodeMem = CVodeCreate(CV_BDF, CvodeContextManager::Instance()->GetSundialsContext());
394#else
395 // v4.0.0 release notes: instead of specifying the nonlinear iteration type when creating the CVODE(S) memory structure,
396 // CVODE(S) uses the SUNNONLINSOL_NEWTON module implementation of a Newton iteration by default.
397 mpCvodeMem = CVodeCreate(CV_BDF);
398#endif
399 if (mpCvodeMem == nullptr)
400 EXCEPTION("Failed to SetupCvode CVODE"); // LCOV_EXCL_LINE
401
402 // Set error handler
403#if CHASTE_SUNDIALS_VERSION >= 70000
404 SUNContext_PushErrHandler(CvodeContextManager::Instance()->GetSundialsContext(), CvodeErrorHandler, nullptr);
405#else
406 CVodeSetErrHandlerFn(mpCvodeMem, CvodeErrorHandler, nullptr);
407#endif
408// Set the user data
409 CVodeSetUserData(mpCvodeMem, (void*)(this));
410// Setup CVODE
411 CVodeInit(mpCvodeMem, AbstractCvodeSystemRhsAdaptor, tStart, initialConditions);
412 CVodeSStolerances(mpCvodeMem, mRelTol, mAbsTol);
413
414#if CHASTE_SUNDIALS_VERSION >= 60000
415 /* Create dense matrix SUNDenseMatrix for use in linear solves */
416 mpSundialsDenseMatrix = SUNDenseMatrix(NV_LENGTH_S(initialConditions), NV_LENGTH_S(initialConditions), CvodeContextManager::Instance()->GetSundialsContext());
417#else
418 /* Create dense matrix SUNDenseMatrix for use in linear solves */
419 mpSundialsDenseMatrix = SUNDenseMatrix(NV_LENGTH_S(initialConditions), NV_LENGTH_S(initialConditions));
420#endif
421
422#if CHASTE_SUNDIALS_VERSION >= 60000
423 /* Create dense SUNLinSol_Dense object for use by CVode */
424 mpSundialsLinearSolver = SUNLinSol_Dense(initialConditions, mpSundialsDenseMatrix, CvodeContextManager::Instance()->GetSundialsContext());
425
426 /* Call CVodeSetLinearSolver to attach the matrix and linear solver to CVode */
428#else
429 /* Create dense SUNLinSol_Dense object for use by CVode */
430 mpSundialsLinearSolver = SUNLinSol_Dense(initialConditions, mpSundialsDenseMatrix);
431
432 /* Call CVodeSetLinearSolver to attach the matrix and linear solver to CVode */
434#endif
435
437 {
438 CVodeSetJacFn(mpCvodeMem, AbstractCvodeSystemJacAdaptor);
439 }
440 }
441 else if (reinit)
442 {
443//std::cout << "Resetting CVODE solver\n";
444 CVodeReInit(mpCvodeMem, tStart, initialConditions);
445 //CVodeSStolerances(mpCvodeMem, mRelTol, mAbsTol); - "all solver inputs remain in effect" so we don't need this.
446 }
447
448 // Set max dt and change max steps if wanted
449 if (maxDt > 0)
450 {
451 CVodeSetMaxStep(mpCvodeMem, maxDt);
452 }
453
454 if (mMaxSteps > 0)
455 {
456 CVodeSetMaxNumSteps(mpCvodeMem, mMaxSteps);
457 CVodeSetMaxErrTestFails(mpCvodeMem, 15);
458 }
459}
460
462{
463 // DebugSteps(mpCvodeMem, this);
464
465 // If we're forcing a reset then we don't record the stopping time
466 // as a result it won't match and we will force a reset in SetupCvode() on
467 // the next solve call.
468 if (mForceReset)
469 return;
470
471 // Otherwise we will store the state variables and time for comparison on the
472 // next solve call, to work out whether we need to reset.
473 const unsigned size = GetNumberOfStateVariables();
475 for (unsigned i = 0; i < size; i++)
476 {
478 }
479 mLastSolutionTime = stopTime;
480}
481
483{
484 if (mpCvodeMem)
485 {
486 CVodeFree(&mpCvodeMem);
487 }
488 mpCvodeMem = nullptr;
489
491 {
492 /* Free the linear solver memory */
493 SUNLinSolFree(mpSundialsLinearSolver);
494 }
495 mpSundialsLinearSolver = nullptr;
496
498 {
499 /* Free the matrix memory */
500 SUNMatDestroy(mpSundialsDenseMatrix);
501 }
502 mpSundialsDenseMatrix = nullptr;
503}
504
505void AbstractCvodeSystem::CvodeError(int flag, const char* msg,
506 const double& rTime, const double& rStartTime, const double& rEndTime)
507{
508 std::stringstream err;
509 char* p_flag_name = CVodeGetReturnFlagName(flag);
510 err << msg << ": " << p_flag_name;
511 free(p_flag_name);
512 if (flag == CV_LSETUP_FAIL)
513 {
514 long int ls_flag;
515 char* p_ls_flag_name;
516
517 CVodeGetLastLinFlag(mpCvodeMem, &ls_flag);
518 p_ls_flag_name = CVodeGetLinReturnFlagName(ls_flag);
519 err << " (LS flag=" << ls_flag << ":" << p_ls_flag_name << ")";
520 free(p_ls_flag_name);
521 }
522
523 err << "\nGot from time " << rStartTime << " to time " << rTime << ", was supposed to finish at time " << rEndTime << "\n";
524 err << "\nState variables are now:\n";
525 std::vector<double> state_vars = MakeStdVec(mStateVariables);
526 std::vector<std::string> state_var_names = rGetStateVariableNames();
527 for (unsigned i = 0; i < state_vars.size(); i++)
528 {
529 err << "\t" << state_var_names[i] << "\t:\t" << state_vars[i] << std::endl;
530 }
531
533 std::cerr << err.str() << std::endl
534 << std::flush;
535 EXCEPTION(err.str());
536}
537
542
547
549{
550 if (!useNumericalJacobian && !mHasAnalyticJacobian)
551 {
552 EXCEPTION("Analytic Jacobian requested, but this ODE system doesn't have one. You can check this with HasAnalyticJacobian().");
553 }
554
555 if (mUseAnalyticJacobian == useNumericalJacobian)
556 {
557 mUseAnalyticJacobian = !useNumericalJacobian;
558 // We need to re-initialise the solver completely to change this.
559 this->FreeCvodeMemory();
560 }
561}
562
563//#include "MathsCustomFunctions.hpp"
564//#include <algorithm>
565//void AbstractCvodeSystem::CheckAnalyticJacobian(realtype time, N_Vector y, N_Vector ydot,
566// CHASTE_CVODE_DENSE_MATRIX jacobian,
567// N_Vector tmp1, N_Vector tmp2, N_Vector tmp3)
568//{
569// N_Vector nudge_ydot = tmp1;
570// N_Vector numeric_jth_col = tmp2;
571// N_Vector ewt = tmp3;
572// const unsigned size = GetNumberOfStateVariables();
573// const double rel_tol = 1e-1;
574// const double abs_tol = 1e-6;
575// realtype* p_y = N_VGetArrayPointer(y);
576// realtype* p_numeric_jth_col = N_VGetArrayPointer(numeric_jth_col);
577//
578// // CVODE internal data for computing the numeric J
579// realtype h;
580// CVodeGetLastStep(mpCvodeMem, &h);
581// CVodeGetErrWeights(mpCvodeMem, ewt);
582// realtype* p_ewt = N_VGetArrayPointer(ewt);
583// // Compute minimum nudge
584// realtype srur = sqrt(DBL_EPSILON);
585// realtype fnorm = N_VWrmsNorm(ydot, ewt);
586// realtype min_nudge = (fnorm != 0.0) ?
587// (1000.0 * fabs(h) * DBL_EPSILON * size * fnorm) : 1.0;
588//
589// for (unsigned j=0; j<size; j++)
590// {
591// // Check the j'th column of the Jacobian
592// realtype yjsaved = p_y[j];
593// realtype nudge = std::max(srur*fabs(yjsaved), min_nudge/p_ewt[j]);
594// p_y[j] += nudge;
595// EvaluateYDerivatives(time, y, nudge_ydot);
596// p_y[j] = yjsaved;
597// realtype nudge_inv = 1.0 / nudge;
598// N_VLinearSum(nudge_inv, nudge_ydot, -nudge_inv, ydot, numeric_jth_col);
599// realtype* p_analytic_jth_col = DENSE_COL(jacobian, j);
600//
601// for (unsigned i=0; i<size; i++)
602// {
603// if (!CompareDoubles::WithinAnyTolerance(p_numeric_jth_col[i], p_analytic_jth_col[i], rel_tol, abs_tol))
604// {
605// EXCEPTION("Analytic Jacobian appears dodgy at time " << time << " entry (" << i << "," << j << ").\n"
606// << "Analytic=" << p_analytic_jth_col[i] << "; numeric=" << p_numeric_jth_col[i] << "."
607// << DumpState("", y, time));
608// }
609// }
610// }
611//}
#define EXCEPTION(message)
#define UNUSED_OPT(var)
void DeleteVector(VECTOR &rVec)
void CreateVectorIfEmpty(VECTOR &rVec, unsigned size)
void SetVectorComponent(VECTOR &rVec, unsigned index, double value)
double GetVectorComponent(const VECTOR &rVec, unsigned index)
std::vector< double > MakeStdVec(N_Vector v)
void SetTolerances(double relTol=1e-5, double absTol=1e-7)
bool GetMinimalReset()
Get whether we want to run with minimal reset or not (no reinitialisation of the solver if variables ...
void SetForceReset(bool autoReset)
void SetupCvode(N_Vector initialConditions, realtype tStart, realtype maxDt)
void CvodeError(int flag, const char *msg, const double &rTime, const double &rStartTime, const double &rEndTime)
void RecordStoppingPoint(double stopTime)
void SetMaxSteps(long int numSteps)
AbstractCvodeSystem(unsigned numberOfStateVariables)
OdeSolution Solve(realtype tStart, realtype tEnd, realtype maxDt, realtype tSamp)
void ForceUseOfNumericalJacobian(bool useNumericalJacobian=true)
virtual void EvaluateYDerivatives(realtype time, const N_Vector y, N_Vector ydot)=0
SUNLinearSolver mpSundialsLinearSolver
virtual void EvaluateAnalyticJacobian(realtype time, N_Vector y, N_Vector ydot, CHASTE_CVODE_DENSE_MATRIX jacobian, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3)
void SetMinimalReset(bool minimalReset)
bool GetForceReset()
Get whether we will force a solver reset on every call to Solve()
const std::vector< std::string > & rGetParameterNames() const
boost::shared_ptr< AbstractOdeSystemInformation > mpSystemInfo
const std::vector< std::string > & rGetStateVariableNames() const
static bool WithinAnyTolerance(double number1, double number2, double relTol=DBL_EPSILON, double absTol=DBL_EPSILON, bool printError=false)
void SetNumberOfTimeSteps(unsigned numTimeSteps)
std::vector< std::vector< double > > & rGetSolutions()
std::vector< double > & rGetTimes()
void SetOdeSystemInformation(boost::shared_ptr< const AbstractOdeSystemInformation > pOdeSystemInfo)
bool IsTimeAtEnd() const
unsigned GetTotalTimeStepsTaken() const
double GetTime() const
void AdvanceOneTimeStep()
double GetNextTime() const
unsigned EstimateTimeSteps() const