Chaste Release::3.1
AbstractCvodeSystem.cpp
00001 /*
00002 
00003 Copyright (c) 2005-2012, University of Oxford.
00004 All rights reserved.
00005 
00006 University of Oxford means the Chancellor, Masters and Scholars of the
00007 University of Oxford, having an administrative office at Wellington
00008 Square, Oxford OX1 2JD, UK.
00009 
00010 This file is part of Chaste.
00011 
00012 Redistribution and use in source and binary forms, with or without
00013 modification, are permitted provided that the following conditions are met:
00014  * Redistributions of source code must retain the above copyright notice,
00015    this list of conditions and the following disclaimer.
00016  * Redistributions in binary form must reproduce the above copyright notice,
00017    this list of conditions and the following disclaimer in the documentation
00018    and/or other materials provided with the distribution.
00019  * Neither the name of the University of Oxford nor the names of its
00020    contributors may be used to endorse or promote products derived from this
00021    software without specific prior written permission.
00022 
00023 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00024 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00025 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00026 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
00027 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00028 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00029 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00030 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
00032 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00033 
00034 */
00035 
00036 #ifdef CHASTE_CVODE
00037 
00038 #include <sstream>
00039 #include <cassert>
00040 
00041 #include "AbstractCvodeSystem.hpp"
00042 #include "Exception.hpp"
00043 #include "VectorHelperFunctions.hpp"
00044 #include "MathsCustomFunctions.hpp" // For tolerance comparison
00045 #include "TimeStepper.hpp"
00046 #include "CvodeAdaptor.hpp" // For CvodeErrorHandler
00047 
00048 // CVODE headers
00049 #include <cvode/cvode.h>
00050 #include <sundials/sundials_nvector.h>
00051 #include <cvode/cvode_dense.h>
00052 
00053 
00063 int AbstractCvodeSystemRhsAdaptor(realtype t, N_Vector y, N_Vector ydot, void *pData)
00064 {
00065     assert(pData != NULL);
00066     AbstractCvodeSystem* p_ode_system = (AbstractCvodeSystem*) pData;
00067     try
00068     {
00069         p_ode_system->EvaluateYDerivatives(t, y, ydot);
00070     }
00071     catch (const Exception &e)
00072     {
00073         std::cerr << "CVODE RHS Exception: " << e.GetMessage()
00074                   << std::endl << std::flush;
00075         return -1;
00076     }
00077     return 0;
00078 }
00079 
00080 AbstractCvodeSystem::AbstractCvodeSystem(unsigned numberOfStateVariables)
00081     : AbstractParameterisedSystem<N_Vector>(numberOfStateVariables),
00082       mLastSolutionState(NULL),
00083       mLastSolutionTime(0.0),
00084       mAutoReset(true),
00085       mForceMinimalReset(false),
00086       mUseAnalyticJacobian(false),
00087       mpCvodeMem(NULL),
00088       mMaxSteps(0),
00089       mLastInternalStepSize(0)
00090 {
00091     SetTolerances(); // Set the tolerances to the defaults.
00092 }
00093 
00094 void AbstractCvodeSystem::Init()
00095 {
00096     DeleteVector(mStateVariables);
00097     mStateVariables = GetInitialConditions();
00098     DeleteVector(mParameters);
00099     mParameters = N_VNew_Serial(rGetParameterNames().size());
00100     for (int i=0; i<NV_LENGTH_S(mParameters); i++)
00101     {
00102         NV_Ith_S(mParameters, i) = 0.0;
00103     }
00104 }
00105 
00106 AbstractCvodeSystem::~AbstractCvodeSystem()
00107 {
00108     FreeCvodeMemory();
00109     DeleteVector(mStateVariables);
00110     DeleteVector(mParameters);
00111     DeleteVector(mLastSolutionState);
00112 }
00113 
00114 //
00115 //double AbstractCvodeSystem::CalculateRootFunction(double time, const std::vector<double>& rY)
00116 //{
00117 //    bool stop = CalculateStoppingEvent(time, rY);
00118 //    return stop ? 0.0 : 1.0;
00119 //}
00120 //
00121 //bool AbstractCvodeSystem::GetUseAnalyticJacobian()
00122 //{
00123 //    return mUseAnalyticJacobian;
00124 //}
00125 
00126 
00127 
00128 OdeSolution AbstractCvodeSystem::Solve(realtype tStart,
00129                                        realtype tEnd,
00130                                        realtype maxDt,
00131                                        realtype tSamp)
00132 {
00133     assert(tEnd >= tStart);
00134     assert(tSamp > 0.0);
00135 
00136     SetupCvode(mStateVariables, tStart, maxDt);
00137 
00138     TimeStepper stepper(tStart, tEnd, tSamp);
00139 
00140     // Set up ODE solution
00141     OdeSolution solutions;
00142     solutions.SetNumberOfTimeSteps(stepper.EstimateTimeSteps());
00143     solutions.rGetSolutions().push_back(MakeStdVec(mStateVariables));
00144     solutions.rGetTimes().push_back(tStart);
00145     solutions.SetOdeSystemInformation(mpSystemInfo);
00146 
00147     // Main time sampling loop
00148     while (!stepper.IsTimeAtEnd())
00149     {
00150         double cvode_stopped_at;
00151         int ierr = CVode(mpCvodeMem, stepper.GetNextTime(), mStateVariables,
00152                          &cvode_stopped_at, CV_NORMAL);
00153         if (ierr<0)
00154         {
00155             FreeCvodeMemory();
00156             CvodeError(ierr, "CVODE failed to solve system");
00157         }
00158         // Not root finding, so should have reached requested time
00159         assert(fabs(cvode_stopped_at - stepper.GetNextTime()) < DBL_EPSILON);
00160 
00161         VerifyStateVariables();
00162 
00163         // Store solution
00164         solutions.rGetSolutions().push_back(MakeStdVec(mStateVariables));
00165         solutions.rGetTimes().push_back(cvode_stopped_at);
00166         stepper.AdvanceOneTimeStep();
00167     }
00168 
00169     // stepper.EstimateTimeSteps may have been an overestimate...
00170     solutions.SetNumberOfTimeSteps(stepper.GetTotalTimeStepsTaken());
00171 
00172     int ierr = CVodeGetLastStep(mpCvodeMem, &mLastInternalStepSize);
00173     assert(ierr == CV_SUCCESS); ierr=ierr; // avoid unused var warning
00174 
00175     RecordStoppingPoint(tEnd);
00176 
00177     return solutions;
00178 }
00179 
00180 void AbstractCvodeSystem::Solve(realtype tStart,
00181                                 realtype tEnd,
00182                                 realtype maxDt)
00183 {
00184     assert(tEnd >= tStart);
00185 
00186     SetupCvode(mStateVariables, tStart, maxDt);
00187 
00188     double cvode_stopped_at;
00189     int ierr = CVode(mpCvodeMem, tEnd, mStateVariables, &cvode_stopped_at, CV_NORMAL);
00190     if (ierr<0)
00191     {
00192         FreeCvodeMemory();
00193         CvodeError(ierr, "CVODE failed to solve system");
00194     }
00195     // Not root finding, so should have reached requested time
00196     assert(fabs(cvode_stopped_at - tEnd) < DBL_EPSILON);
00197 
00198     ierr = CVodeGetLastStep(mpCvodeMem, &mLastInternalStepSize);
00199     assert(ierr == CV_SUCCESS); ierr=ierr; // avoid unused var warning
00200 
00201     RecordStoppingPoint(cvode_stopped_at);
00202 
00203     VerifyStateVariables();
00204 }
00205 
00206 
00207 void AbstractCvodeSystem::SetMaxSteps(long int numSteps)
00208 {
00209     mMaxSteps = numSteps;
00210 }
00211 
00212 long int AbstractCvodeSystem::GetMaxSteps()
00213 {
00214     return mMaxSteps;
00215 }
00216 
00217 void AbstractCvodeSystem::SetTolerances(double relTol, double absTol)
00218 {
00219     mRelTol = relTol;
00220     mAbsTol = absTol;
00221     ResetSolver();
00222 }
00223 
00224 double AbstractCvodeSystem::GetRelativeTolerance()
00225 {
00226     return mRelTol;
00227 }
00228 
00229 double AbstractCvodeSystem::GetAbsoluteTolerance()
00230 {
00231     return mAbsTol;
00232 }
00233 
00234 double AbstractCvodeSystem::GetLastStepSize()
00235 {
00236     return mLastInternalStepSize;
00237 }
00238 
00239 void AbstractCvodeSystem::SetAutoReset(bool autoReset)
00240 {
00241     mAutoReset = autoReset;
00242     if (mAutoReset)
00243     {
00244         ResetSolver();
00245     }
00246 }
00247 
00248 void AbstractCvodeSystem::SetMinimalReset(bool minimalReset)
00249 {
00250     mForceMinimalReset = minimalReset;
00251     if (mForceMinimalReset)
00252     {
00253         SetAutoReset(false);
00254     }
00255 }
00256 
00257 
00258 void AbstractCvodeSystem::ResetSolver()
00259 {
00260     DeleteVector(mLastSolutionState);
00261 }
00262 
00263 
00264 void AbstractCvodeSystem::SetupCvode(N_Vector initialConditions,
00265                                      realtype tStart,
00266                                      realtype maxDt)
00267 {
00268     assert((unsigned)NV_LENGTH_S(initialConditions) == GetNumberOfStateVariables());
00269     assert(maxDt >= 0.0);
00270 
00271     // Find out if we need to (re-)initialise
00272     //std::cout << "!mpCvodeMem = " << !mpCvodeMem << ", mAutoReset = " << mAutoReset << ", !mLastSolutionState = " << !mLastSolutionState << ", comp doubles = " << !CompareDoubles::WithinAnyTolerance(tStart, mLastSolutionTime) << "\n";
00273     bool reinit = !mpCvodeMem || mAutoReset || !mLastSolutionState || !CompareDoubles::WithinAnyTolerance(tStart, mLastSolutionTime);
00274     if (!reinit && !mForceMinimalReset)
00275     {
00276         const unsigned size = GetNumberOfStateVariables();
00277         for (unsigned i=0; i<size; i++)
00278         {
00279             if (!CompareDoubles::WithinAnyTolerance(GetVectorComponent(mLastSolutionState, i), GetVectorComponent(mStateVariables, i)))
00280             {
00281                 reinit = true;
00282                 break;
00283             }
00284         }
00285     }
00286 
00287     if (!mpCvodeMem)
00288     {
00289         mpCvodeMem = CVodeCreate(CV_BDF, CV_NEWTON);
00290         if (mpCvodeMem == NULL) EXCEPTION("Failed to SetupCvode CVODE"); // in one line to avoid coverage problem!
00291 
00292         // Set error handler
00293         CVodeSetErrHandlerFn(mpCvodeMem, CvodeErrorHandler, NULL);
00294         // Set the user data
00295 #if CHASTE_SUNDIALS_VERSION >= 20400
00296         CVodeSetUserData(mpCvodeMem, (void*)(this));
00297 #else
00298         CVodeSetFdata(mpCvodeMem, (void*)(this));
00299 #endif
00300         // Setup CVODE
00301 #if CHASTE_SUNDIALS_VERSION >= 20400
00302         CVodeInit(mpCvodeMem, AbstractCvodeSystemRhsAdaptor, tStart, initialConditions);
00303         CVodeSStolerances(mpCvodeMem, mRelTol, mAbsTol);
00304 #else
00305         CVodeMalloc(mpCvodeMem, AbstractCvodeSystemRhsAdaptor, tStart, initialConditions,
00306                     CV_SS, mRelTol, &mAbsTol);
00307 #endif
00308         // Attach a linear solver for Newton iteration
00309         CVDense(mpCvodeMem, NV_LENGTH_S(initialConditions));
00310     }
00311     else if (reinit)
00312     {
00313 #if CHASTE_SUNDIALS_VERSION >= 20400
00314         CVodeReInit(mpCvodeMem, tStart, initialConditions);
00315         CVodeSStolerances(mpCvodeMem, mRelTol, mAbsTol);
00316 #else
00317         CVodeReInit(mpCvodeMem, AbstractCvodeSystemRhsAdaptor, tStart, initialConditions,
00318                     CV_SS, mRelTol, &mAbsTol);
00319 #endif
00320     }
00321 
00322     // Set max dt and change max steps if wanted
00323     CVodeSetMaxStep(mpCvodeMem, maxDt);
00324     if (mMaxSteps > 0)
00325     {
00326         CVodeSetMaxNumSteps(mpCvodeMem, mMaxSteps);
00327         CVodeSetMaxErrTestFails(mpCvodeMem, 15);
00328     }
00329 }
00330 
00331 
00332 void AbstractCvodeSystem::RecordStoppingPoint(double stopTime)
00333 {
00334     if (!mAutoReset)
00335     {
00336         const unsigned size = GetNumberOfStateVariables();
00337         CreateVectorIfEmpty(mLastSolutionState, size);
00338         for (unsigned i=0; i<size; i++)
00339         {
00340             SetVectorComponent(mLastSolutionState, i, GetVectorComponent(mStateVariables, i));
00341         }
00342         mLastSolutionTime = stopTime;
00343     }
00344 }
00345 
00346 
00347 void AbstractCvodeSystem::FreeCvodeMemory()
00348 {
00349     if (mpCvodeMem)
00350     {
00351         CVodeFree(&mpCvodeMem);
00352     }
00353     mpCvodeMem = NULL;
00354 }
00355 
00356 
00357 void AbstractCvodeSystem::CvodeError(int flag, const char * msg)
00358 {
00359 
00360     std::stringstream err;
00361     char* p_flag_name = CVodeGetReturnFlagName(flag);
00362     err << msg << ": " << p_flag_name;
00363     free(p_flag_name);
00364     std::cerr << err.str() << std::endl << std::flush;
00365     EXCEPTION(err.str());
00366 }
00367 
00368 
00369 #endif // CHASTE_CVODE