Chaste Commit::fa89f2b838c1edb21a1eaec92ee3a2eacc9255dd
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#ifdef CHASTE_CVODE
37
38#include <cassert>
39#include <sstream>
40
41#include "AbstractCvodeSystem.hpp"
42#include "CvodeAdaptor.hpp" // For CvodeErrorHandler
43#include "Exception.hpp"
44#include "MathsCustomFunctions.hpp" // For tolerance comparison
45#include "TimeStepper.hpp"
47
48// CVODE headers
49#include <cvode/cvode.h>
50#include <sundials/sundials_nvector.h>
51
52#if CHASTE_SUNDIALS_VERSION >= 30000
53#if CHASTE_SUNDIALS_VERSION < 70000
54#include <cvode/cvode_direct.h> /* access to CVDls interface */
55#endif
56#include <sundials/sundials_types.h> /* defs. of realtype, sunindextype */
57#include <sunlinsol/sunlinsol_dense.h> /* access to dense SUNLinearSolver */
58#include <sunmatrix/sunmatrix_dense.h> /* access to dense SUNMatrix */
59#else
60#include <cvode/cvode_dense.h>
61#endif
62
63#if CHASTE_SUNDIALS_VERSION >= 60000
64#include "CvodeContextManager.hpp" // access to shared SUNContext object required by Sundials 6.0+
65#endif
66
67//#include "Debug.hpp"
68//void DebugSteps(void* pCvodeMem, AbstractCvodeSystem* pSys)
69//{
70// long int num_jac_evals, nniters, num_steps;
71// CVDenseGetNumJacEvals(pCvodeMem, &num_jac_evals);
73// CVodeGetNumNonlinSolvIters(pCvodeMem, &nniters);
74// CVodeGetNumSteps(pCvodeMem, &num_steps);
75// double num_newton_iters = nniters;
76// PRINT_3_VARIABLES(pSys->GetSystemName(), num_newton_iters/num_steps, num_jac_evals/num_newton_iters);
77//}
87int AbstractCvodeSystemRhsAdaptor(realtype t, N_Vector y, N_Vector ydot, void* pData)
88{
89 assert(pData != nullptr);
90 AbstractCvodeSystem* p_ode_system = (AbstractCvodeSystem*)pData;
91 try
92 {
93 p_ode_system->EvaluateYDerivatives(t, y, ydot);
94 }
95 catch (const Exception& e)
96 {
97#if CHASTE_SUNDIALS_VERSION <= 20300
98 // Really old CVODE used to solve past the requested time points and could trigger this exception unnecessarily...
99 if (e.CheckShortMessageContains("is outside the times stored in the data clamp") == "")
100 {
101 return 1; // This may be a recoverable error!
102 }
103#endif
104
105 std::cerr << "CVODE RHS Exception: " << e.GetMessage()
106 << std::endl
107 << std::flush;
108 return -1;
109 }
110
111 // Something like this might help CVODE when things are a bit unstable...
112 // 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.
113 // See: https://github.com/Chaste/Chaste/issues/46
114 // try
115 // {
116 // p_ode_system->VerifyStateVariables();
117 // }
118 // catch (const Exception &e)
119 // {
120 // std::cout << "t = " << t << ":\t" << e.GetMessage() << std::endl << std::flush;
121 // return 1; // A positive return flag to CVODE tells it there's been an error but it might be recoverable.
122 // }
123
124 return 0;
125}
126
127/*
128 * Absolute chaos here with four different possible interfaces to the jacobian.
129 */
130#if CHASTE_SUNDIALS_VERSION >= 30000
131// Sundials 3.0 - has taken away the argument N at the top...
132int AbstractCvodeSystemJacAdaptor(realtype t, N_Vector y, N_Vector ydot, CHASTE_CVODE_DENSE_MATRIX jacobian,
133#elif CHASTE_SUNDIALS_VERSION >= 20500
134// Sundials 2.5
135int AbstractCvodeSystemJacAdaptor(long int N, realtype t, N_Vector y, N_Vector ydot, CHASTE_CVODE_DENSE_MATRIX jacobian,
136#elif CHASTE_SUNDIALS_VERSION >= 20400
137// Sundials 2.4
138int AbstractCvodeSystemJacAdaptor(int N, realtype t, N_Vector y, N_Vector ydot, DlsMat jacobian,
139#else
140// Sundials 2.3 and below (not sure how far below, but this is 2006 so old enough).
141int AbstractCvodeSystemJacAdaptor(long int N, DenseMat jacobian, realtype t, N_Vector y, N_Vector ydot,
142#endif
143 void* pData, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3)
144{
145 assert(pData != nullptr);
146 AbstractCvodeSystem* p_ode_system = (AbstractCvodeSystem*)pData;
147 try
148 {
149 p_ode_system->EvaluateAnalyticJacobian(t, y, ydot, jacobian, tmp1, tmp2, tmp3);
150 }
151 catch (const Exception& e)
152 {
153 std::cerr << "CVODE Jacobian Exception: " << e.GetMessage() << std::endl
154 << std::flush;
155 return -1;
156 }
157 return 0;
158}
159
160AbstractCvodeSystem::AbstractCvodeSystem(unsigned numberOfStateVariables)
161 : AbstractParameterisedSystem<N_Vector>(numberOfStateVariables),
162 mLastSolutionState(nullptr),
163 mLastSolutionTime(0.0),
164#if CHASTE_SUNDIALS_VERSION >= 20400
165 mForceReset(false),
166#else
167 // Old Sundials don't seem to 'go back' when something has changed
168 // properly, and give more inaccurate answers.
169 mForceReset(true),
170#endif
171 mForceMinimalReset(false),
172#if CHASTE_SUNDIALS_VERSION >= 30000
173 mpSundialsDenseMatrix(nullptr),
174 mpSundialsLinearSolver(nullptr),
175#endif
176 mHasAnalyticJacobian(false),
177 mUseAnalyticJacobian(false),
178 mpCvodeMem(nullptr),
179#if CHASTE_SUNDIALS_VERSION >= 60000
180 mpSundialsContextManager(CvodeContextManager::Instance()),
181#endif
182 mMaxSteps(0),
183 mLastInternalStepSize(0)
184{
185 SetTolerances(); // Set the tolerances to the defaults.
186}
187
189{
193#if CHASTE_SUNDIALS_VERSION >= 60000
194 mParameters = N_VNew_Serial(rGetParameterNames().size(), CvodeContextManager::Instance()->GetSundialsContext());
195#else
196 mParameters = N_VNew_Serial(rGetParameterNames().size());
197#endif
198 for (int i = 0; i < NV_LENGTH_S(mParameters); i++)
199 {
200 NV_Ith_S(mParameters, i) = 0.0;
201 }
202}
203
211
212//
213//double AbstractCvodeSystem::CalculateRootFunction(double time, const std::vector<double>& rY)
214//{
215// bool stop = CalculateStoppingEvent(time, rY);
216// return stop ? 0.0 : 1.0;
217//}
218
220 realtype tEnd,
221 realtype maxDt,
222 realtype tSamp)
223{
224 assert(tEnd >= tStart);
225 assert(tSamp > 0.0);
226
227 SetupCvode(mStateVariables, tStart, maxDt);
228
229 TimeStepper stepper(tStart, tEnd, tSamp);
230
231 // Set up ODE solution
232 OdeSolution solutions;
233 solutions.SetNumberOfTimeSteps(stepper.EstimateTimeSteps());
234 solutions.rGetSolutions().push_back(MakeStdVec(mStateVariables));
235 solutions.rGetTimes().push_back(tStart);
237
238 // Main time sampling loop
239 while (!stepper.IsTimeAtEnd())
240 {
241 // This should stop CVODE going past the end of where we wanted and interpolating back.
242 int ierr = CVodeSetStopTime(mpCvodeMem, stepper.GetNextTime());
243 assert(ierr == CV_SUCCESS);
244 UNUSED_OPT(ierr); // avoid unused var warning
245
246 // // This parameter governs how many times we allow a recoverable right hand side failure
247 // int ierr = CVodeSetMaxConvFails(mpCvodeMem, 1000);
248 // assert(ierr == CV_SUCCESS); UNUSED_OPT(ierr); // avoid unused var warning
249
250 double cvode_stopped_at = stepper.GetTime();
251 ierr = CVode(mpCvodeMem, stepper.GetNextTime(), mStateVariables,
252 &cvode_stopped_at, CV_NORMAL);
253 if (ierr < 0)
254 {
255 // DebugSteps(mpCvodeMem, this);
256 CvodeError(ierr, "CVODE failed to solve system", cvode_stopped_at, stepper.GetTime(), stepper.GetNextTime());
257 }
258 // Not root finding, so should have reached requested time
259 assert(fabs(cvode_stopped_at - stepper.GetNextTime()) < DBL_EPSILON);
260#ifndef NDEBUG
262#endif
263 // Store solution
264 solutions.rGetSolutions().push_back(MakeStdVec(mStateVariables));
265 solutions.rGetTimes().push_back(cvode_stopped_at);
266 stepper.AdvanceOneTimeStep();
267 }
268
269 // stepper.EstimateTimeSteps may have been an overestimate...
270 solutions.SetNumberOfTimeSteps(stepper.GetTotalTimeStepsTaken());
271
272 int ierr = CVodeGetLastStep(mpCvodeMem, &mLastInternalStepSize);
273 assert(ierr == CV_SUCCESS);
274 UNUSED_OPT(ierr); // avoid unused var warning
275
277
278 return solutions;
279}
280
281void AbstractCvodeSystem::Solve(realtype tStart,
282 realtype tEnd,
283 realtype maxDt)
284{
285 assert(tEnd >= tStart);
286
287 SetupCvode(mStateVariables, tStart, maxDt);
288
289 // This should stop CVODE going past the end of where we wanted and interpolating back.
290 int ierr = CVodeSetStopTime(mpCvodeMem, tEnd);
291 assert(ierr == CV_SUCCESS);
292 UNUSED_OPT(ierr); // avoid unused var warning
293
294 double cvode_stopped_at = tStart;
295 ierr = CVode(mpCvodeMem, tEnd, mStateVariables, &cvode_stopped_at, CV_NORMAL);
296 if (ierr < 0)
297 {
298 // DebugSteps(mpCvodeMem, this);
299 CvodeError(ierr, "CVODE failed to solve system", cvode_stopped_at, tStart, tEnd);
300 }
301 // Not root finding, so should have reached requested time
302 assert(fabs(cvode_stopped_at - tEnd) < DBL_EPSILON);
303
304 ierr = CVodeGetLastStep(mpCvodeMem, &mLastInternalStepSize);
305 assert(ierr == CV_SUCCESS);
306 UNUSED_OPT(ierr); // avoid unused var warning
307
308 RecordStoppingPoint(cvode_stopped_at);
309
310//
311// long int nst, nfe, nsetups, nje, nfeLS, nni, ncfn, netf, nge;
312//
313//
314// CVodeGetNumSteps(mpCvodeMem, &nst);
315// CVodeGetNumRhsEvals(mpCvodeMem, &nfe);
316// CVodeGetNumLinSolvSetups(mpCvodeMem, &nsetups);
317// CVodeGetNumErrTestFails(mpCvodeMem, &netf);
318// CVodeGetNumNonlinSolvIters(mpCvodeMem, &nni);
319// CVodeGetNumNonlinSolvConvFails(mpCvodeMem, &ncfn);
320// CVDlsGetNumJacEvals(mpCvodeMem, &nje);
321// CVDlsGetNumRhsEvals(mpCvodeMem, &nfeLS);
322// CVodeGetNumGEvals(mpCvodeMem, &nge);
323//
324// printf("\nFinal Statistics:\n");
325// printf("nst = %-6ld nfe = %-6ld nsetups = %-6ld nfeLS = %-6ld nje = %ld\n",
326// nst, nfe, nsetups, nfeLS, nje);
327// printf("nni = %-6ld ncfn = %-6ld netf = %-6ld nge = %ld\n \n",
328// nni, ncfn, netf, nge);
329// std::cout << std::flush;
330#ifndef NDEBUG
332#endif
333}
334
335void AbstractCvodeSystem::SetMaxSteps(long int numSteps)
336{
337 mMaxSteps = numSteps;
338}
339
341{
342 return mMaxSteps;
343}
344
345void AbstractCvodeSystem::SetTolerances(double relTol, double absTol)
346{
347 mRelTol = relTol;
348 mAbsTol = absTol;
349 ResetSolver();
350}
351
356
361
366
368{
369 mForceReset = autoReset;
370 if (mForceReset)
371 {
372 ResetSolver();
373 }
374}
375
380
385
387{
388 mForceMinimalReset = minimalReset;
390 {
391 SetForceReset(false);
392 }
393}
394
399
401 realtype tStart,
402 realtype maxDt)
403{
404 assert((unsigned)NV_LENGTH_S(initialConditions) == GetNumberOfStateVariables());
405 assert(maxDt >= 0.0);
406
407 // Find out if we need to (re-)initialise
408 //std::cout << "!mpCvodeMem = " << !mpCvodeMem << ", mForceReset = " << mForceReset << ", !mLastSolutionState = " << !mLastSolutionState << ", comp doubles = " << !CompareDoubles::WithinAnyTolerance(tStart, mLastSolutionTime) << "\n";
410 if (!reinit && !mForceMinimalReset)
411 {
412 const unsigned size = GetNumberOfStateVariables();
413 for (unsigned i = 0; i < size; i++)
414 {
416 {
417 reinit = true;
418 break;
419 }
420 }
421 }
422
423 if (!mpCvodeMem)
424 {
425 //std::cout << "New CVODE solver\n";
426#if CHASTE_SUNDIALS_VERSION >= 60000
427 mpCvodeMem = CVodeCreate(CV_BDF, CvodeContextManager::Instance()->GetSundialsContext());
428#elif CHASTE_SUNDIALS_VERSION >= 40000
429 // v4.0.0 release notes: instead of specifying the nonlinear iteration type when creating the CVODE(S) memory structure,
430 // CVODE(S) uses the SUNNONLINSOL_NEWTON module implementation of a Newton iteration by default.
431 mpCvodeMem = CVodeCreate(CV_BDF);
432#else
433 mpCvodeMem = CVodeCreate(CV_BDF, CV_NEWTON);
434#endif
435 if (mpCvodeMem == nullptr)
436 EXCEPTION("Failed to SetupCvode CVODE"); // LCOV_EXCL_LINE
437
438 // Set error handler
439#if CHASTE_SUNDIALS_VERSION >= 70000
440 SUNContext_PushErrHandler(CvodeContextManager::Instance()->GetSundialsContext(), CvodeErrorHandler, nullptr);
441#else
442 CVodeSetErrHandlerFn(mpCvodeMem, CvodeErrorHandler, nullptr);
443#endif
444// Set the user data
445#if CHASTE_SUNDIALS_VERSION >= 20400
446 CVodeSetUserData(mpCvodeMem, (void*)(this));
447#else
448 CVodeSetFdata(mpCvodeMem, (void*)(this));
449#endif
450// Setup CVODE
451#if CHASTE_SUNDIALS_VERSION >= 20400
452 CVodeInit(mpCvodeMem, AbstractCvodeSystemRhsAdaptor, tStart, initialConditions);
453 CVodeSStolerances(mpCvodeMem, mRelTol, mAbsTol);
454#else
455 CVodeMalloc(mpCvodeMem, AbstractCvodeSystemRhsAdaptor, tStart, initialConditions,
456 CV_SS, mRelTol, &mAbsTol);
457#endif
458
459#if CHASTE_SUNDIALS_VERSION >= 60000
460 /* Create dense matrix SUNDenseMatrix for use in linear solves */
461 mpSundialsDenseMatrix = SUNDenseMatrix(NV_LENGTH_S(initialConditions), NV_LENGTH_S(initialConditions), CvodeContextManager::Instance()->GetSundialsContext());
462#elif CHASTE_SUNDIALS_VERSION >= 30000
463 /* Create dense matrix SUNDenseMatrix for use in linear solves */
464 mpSundialsDenseMatrix = SUNDenseMatrix(NV_LENGTH_S(initialConditions), NV_LENGTH_S(initialConditions));
465#endif
466
467#if CHASTE_SUNDIALS_VERSION >= 60000
468 /* Create dense SUNLinSol_Dense object for use by CVode */
469 mpSundialsLinearSolver = SUNLinSol_Dense(initialConditions, mpSundialsDenseMatrix, CvodeContextManager::Instance()->GetSundialsContext());
470
471 /* Call CVodeSetLinearSolver to attach the matrix and linear solver to CVode */
472 CVodeSetLinearSolver(mpCvodeMem, mpSundialsLinearSolver, mpSundialsDenseMatrix);
473#elif CHASTE_SUNDIALS_VERSION >= 40000
474 /* Create dense SUNLinSol_Dense object for use by CVode */
475 mpSundialsLinearSolver = SUNLinSol_Dense(initialConditions, mpSundialsDenseMatrix);
476
477 /* Call CVodeSetLinearSolver to attach the matrix and linear solver to CVode */
478 CVodeSetLinearSolver(mpCvodeMem, mpSundialsLinearSolver, mpSundialsDenseMatrix);
479#elif CHASTE_SUNDIALS_VERSION >= 30000
480 /* Create dense SUNDenseLinearSolver object for use by CVode */
481 mpSundialsLinearSolver = SUNDenseLinearSolver(initialConditions, mpSundialsDenseMatrix);
482
483 /* Call CVDlsSetLinearSolver to attach the matrix and linear solver to CVode */
484 CVDlsSetLinearSolver(mpCvodeMem, mpSundialsLinearSolver, mpSundialsDenseMatrix);
485#else
486 // CVODE < v3.0.0
487 // Attach a linear solver for Newton iteration
488 CVDense(mpCvodeMem, NV_LENGTH_S(initialConditions));
489#endif
490
492 {
493#if CHASTE_SUNDIALS_VERSION >= 40000
494 CVodeSetJacFn(mpCvodeMem, AbstractCvodeSystemJacAdaptor);
495#elif CHASTE_SUNDIALS_VERSION >= 30000
496 CVDlsSetJacFn(mpCvodeMem, AbstractCvodeSystemJacAdaptor);
497#elif CHASTE_SUNDIALS_VERSION >= 20400
498 CVDlsSetDenseJacFn(mpCvodeMem, AbstractCvodeSystemJacAdaptor);
499#else
500 CVDenseSetJacFn(mpCvodeMem, AbstractCvodeSystemJacAdaptor, (void*)(this));
501#endif
502 }
503 }
504 else if (reinit)
505 {
506//std::cout << "Resetting CVODE solver\n";
507#if CHASTE_SUNDIALS_VERSION >= 20400
508 CVodeReInit(mpCvodeMem, tStart, initialConditions);
509 //CVodeSStolerances(mpCvodeMem, mRelTol, mAbsTol); - "all solver inputs remain in effect" so we don't need this.
510#else
511 CVodeReInit(mpCvodeMem, AbstractCvodeSystemRhsAdaptor, tStart, initialConditions,
512 CV_SS, mRelTol, &mAbsTol);
513#endif
514 }
515
516 // Set max dt and change max steps if wanted
517 if (maxDt > 0)
518 {
519 CVodeSetMaxStep(mpCvodeMem, maxDt);
520 }
521
522 if (mMaxSteps > 0)
523 {
524 CVodeSetMaxNumSteps(mpCvodeMem, mMaxSteps);
525 CVodeSetMaxErrTestFails(mpCvodeMem, 15);
526 }
527}
528
530{
531 // DebugSteps(mpCvodeMem, this);
532
533 // If we're forcing a reset then we don't record the stopping time
534 // as a result it won't match and we will force a reset in SetupCvode() on
535 // the next solve call.
536 if (mForceReset)
537 return;
538
539 // Otherwise we will store the state variables and time for comparison on the
540 // next solve call, to work out whether we need to reset.
541 const unsigned size = GetNumberOfStateVariables();
543 for (unsigned i = 0; i < size; i++)
544 {
546 }
547 mLastSolutionTime = stopTime;
548}
549
551{
552 if (mpCvodeMem)
553 {
554 CVodeFree(&mpCvodeMem);
555 }
556 mpCvodeMem = nullptr;
557
558#if CHASTE_SUNDIALS_VERSION >= 30000
559 if (mpSundialsLinearSolver)
560 {
561 /* Free the linear solver memory */
562 SUNLinSolFree(mpSundialsLinearSolver);
563 }
564 mpSundialsLinearSolver = nullptr;
565
566 if (mpSundialsDenseMatrix)
567 {
568 /* Free the matrix memory */
569 SUNMatDestroy(mpSundialsDenseMatrix);
570 }
571 mpSundialsDenseMatrix = nullptr;
572#endif
573}
574
575void AbstractCvodeSystem::CvodeError(int flag, const char* msg,
576 const double& rTime, const double& rStartTime, const double& rEndTime)
577{
578 std::stringstream err;
579 char* p_flag_name = CVodeGetReturnFlagName(flag);
580 err << msg << ": " << p_flag_name;
581 free(p_flag_name);
582 if (flag == CV_LSETUP_FAIL)
583 {
584#if CHASTE_SUNDIALS_VERSION >= 20500
585 long int ls_flag;
586#else
587 int ls_flag;
588#endif
589 char* p_ls_flag_name;
590
591#if CHASTE_SUNDIALS_VERSION >= 40000
592 CVodeGetLastLinFlag(mpCvodeMem, &ls_flag);
593 p_ls_flag_name = CVodeGetLinReturnFlagName(ls_flag);
594#elif CHASTE_SUNDIALS_VERSION >= 20400
595 CVDlsGetLastFlag(mpCvodeMem, &ls_flag);
596 p_ls_flag_name = CVDlsGetReturnFlagName(ls_flag);
597#else
598 CVDenseGetLastFlag(mpCvodeMem, &ls_flag);
599 p_ls_flag_name = CVDenseGetReturnFlagName(ls_flag);
600#endif
601 err << " (LS flag=" << ls_flag << ":" << p_ls_flag_name << ")";
602 free(p_ls_flag_name);
603 }
604
605 err << "\nGot from time " << rStartTime << " to time " << rTime << ", was supposed to finish at time " << rEndTime << "\n";
606 err << "\nState variables are now:\n";
607 std::vector<double> state_vars = MakeStdVec(mStateVariables);
608 std::vector<std::string> state_var_names = rGetStateVariableNames();
609 for (unsigned i = 0; i < state_vars.size(); i++)
610 {
611 err << "\t" << state_var_names[i] << "\t:\t" << state_vars[i] << std::endl;
612 }
613
615 std::cerr << err.str() << std::endl
616 << std::flush;
617 EXCEPTION(err.str());
618}
619
624
629
631{
632 if (!useNumericalJacobian && !mHasAnalyticJacobian)
633 {
634 EXCEPTION("Analytic Jacobian requested, but this ODE system doesn't have one. You can check this with HasAnalyticJacobian().");
635 }
636
637 if (mUseAnalyticJacobian == useNumericalJacobian)
638 {
639 mUseAnalyticJacobian = !useNumericalJacobian;
640 // We need to re-initialise the solver completely to change this.
641 this->FreeCvodeMemory();
642 }
643}
644
645//#include "MathsCustomFunctions.hpp"
646//#include <algorithm>
647//void AbstractCvodeSystem::CheckAnalyticJacobian(realtype time, N_Vector y, N_Vector ydot,
648// CHASTE_CVODE_DENSE_MATRIX jacobian,
649// N_Vector tmp1, N_Vector tmp2, N_Vector tmp3)
650//{
651// N_Vector nudge_ydot = tmp1;
652// N_Vector numeric_jth_col = tmp2;
653// N_Vector ewt = tmp3;
654// const unsigned size = GetNumberOfStateVariables();
655// const double rel_tol = 1e-1;
656// const double abs_tol = 1e-6;
657// realtype* p_y = N_VGetArrayPointer(y);
658// realtype* p_numeric_jth_col = N_VGetArrayPointer(numeric_jth_col);
659//
660// // CVODE internal data for computing the numeric J
661// realtype h;
662// CVodeGetLastStep(mpCvodeMem, &h);
663// CVodeGetErrWeights(mpCvodeMem, ewt);
664// realtype* p_ewt = N_VGetArrayPointer(ewt);
665// // Compute minimum nudge
666// realtype srur = sqrt(DBL_EPSILON);
667// realtype fnorm = N_VWrmsNorm(ydot, ewt);
668// realtype min_nudge = (fnorm != 0.0) ?
669// (1000.0 * fabs(h) * DBL_EPSILON * size * fnorm) : 1.0;
670//
671// for (unsigned j=0; j<size; j++)
672// {
673// // Check the j'th column of the Jacobian
674// realtype yjsaved = p_y[j];
675// realtype nudge = std::max(srur*fabs(yjsaved), min_nudge/p_ewt[j]);
676// p_y[j] += nudge;
677// EvaluateYDerivatives(time, y, nudge_ydot);
678// p_y[j] = yjsaved;
679// realtype nudge_inv = 1.0 / nudge;
680// N_VLinearSum(nudge_inv, nudge_ydot, -nudge_inv, ydot, numeric_jth_col);
681// realtype* p_analytic_jth_col = DENSE_COL(jacobian, j);
682//
683// for (unsigned i=0; i<size; i++)
684// {
685// if (!CompareDoubles::WithinAnyTolerance(p_numeric_jth_col[i], p_analytic_jth_col[i], rel_tol, abs_tol))
686// {
687// EXCEPTION("Analytic Jacobian appears dodgy at time " << time << " entry (" << i << "," << j << ").\n"
688// << "Analytic=" << p_analytic_jth_col[i] << "; numeric=" << p_numeric_jth_col[i] << "."
689// << DumpState("", y, time));
690// }
691// }
692// }
693//}
694
695#endif // CHASTE_CVODE
#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
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