TimeStepper.cpp

00001 /*
00002 
00003 Copyright (c) 2005-2015, 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 #include <cassert>
00037 #include <cmath>
00038 
00039 #include "TimeStepper.hpp"
00040 #include "Exception.hpp"
00041 #include "MathsCustomFunctions.hpp"
00042 
00043 TimeStepper::TimeStepper(double startTime, double endTime, double dt, bool enforceConstantTimeStep, std::vector<double> additionalTimes)
00044     : mStart(startTime),
00045       mEnd(endTime),
00046       mDt(dt),
00047       mTotalTimeStepsTaken(0),
00048       mAdditionalTimesReachedDeprecated(0),
00049       mTime(startTime),
00050       mEpsilon(DBL_EPSILON)
00051 {
00052     if (startTime > endTime)
00053     {
00054         EXCEPTION("The simulation duration must be positive, not " << endTime-startTime);
00055     }
00056 
00057     // Remove any additionalTimes entries which fall too close to a time when the stepper would stop anyway
00058     for (unsigned i=0; i<additionalTimes.size(); i++)
00059     {
00060         if (i > 0)
00061         {
00062             if (additionalTimes[i-1] >= additionalTimes[i])
00063             {
00064                 EXCEPTION("The additional times vector should be in ascending numerical order; "
00065                           "entry " << i << " is less than or equal to entry " << i-1 << ".");
00066             }
00067         }
00068 
00069         double time_interval = additionalTimes[i] - startTime;
00070 
00071         // When mDt divides this interval (and the interval is positive) then we are going there anyway
00072         if (!Divides(mDt, time_interval) && (time_interval > DBL_EPSILON))
00073         {
00074             //mAdditionalTimes.push_back(additionalTimes[i]);
00075             EXCEPTION("Additional times are now deprecated.  Use only to check whether the given times are met: e.g. Electrode events should only happen on printing steps.");
00076         }
00077     }
00078 
00079     /*
00080      * Note that when mEnd is large then the error of subtracting two numbers of
00081      * that magnitude is about DBL_EPSILON*mEnd (1e-16*mEnd). When mEnd is small
00082      * then the error should be around DBL_EPSILON.
00083      */
00084     if (mEnd > 1.0)
00085     {
00086         mEpsilon = DBL_EPSILON*mEnd;
00087     }
00088 
00089     // If enforceConstantTimeStep check whether the times are such that we won't have a variable dt
00090     if (enforceConstantTimeStep)
00091     {
00092         double expected_end_time = mStart + mDt*EstimateTimeSteps();
00093 
00094         if (fabs( mEnd - expected_end_time ) > mEpsilon)
00095         {
00096             EXCEPTION("TimeStepper estimates non-constant timesteps will need to be used: check timestep "
00097                       "divides (end_time-start_time) (or divides printing timestep). "
00098                       "[End time=" << mEnd << "; start=" << mStart << "; dt=" << mDt << "; error="
00099                       << fabs(mEnd-expected_end_time) << "]");
00100         }
00101     }
00102 
00103     mNextTime = CalculateNextTime();
00104 }
00105 
00106 double TimeStepper::CalculateNextTime()
00107 {
00108     double next_time = mStart + (mTotalTimeStepsTaken + 1)*mDt;
00109 
00110     // Does the next time bring us very close to the end time?
00111     // Note that the inequality in this guard matches the inversion of the guard in the enforceConstantTimeStep
00112     // calculation of the constructor
00113     if (mEnd - next_time <= mEpsilon)
00114     {
00115         next_time = mEnd;
00116     }
00117 
00118     assert(mAdditionalTimesDeprecated.empty());
00119 
00120     return next_time;
00121 }
00122 
00123 void TimeStepper::AdvanceOneTimeStep()
00124 {
00125     mTotalTimeStepsTaken++;
00126     if (mTotalTimeStepsTaken == 0)
00127     {
00128         EXCEPTION("Time step counter has overflowed.");
00129     }
00130     if (mTime >= mNextTime)
00131     {
00132         EXCEPTION("TimeStepper incremented beyond end time.");
00133     }
00134     mTime = mNextTime;
00135 
00136     mNextTime = CalculateNextTime();
00137 }
00138 
00139 double TimeStepper::GetTime() const
00140 {
00141     return mTime;
00142 }
00143 
00144 double TimeStepper::GetNextTime() const
00145 {
00146     return mNextTime;
00147 }
00148 
00149 double TimeStepper::GetNextTimeStep()
00150 {
00151     double dt = mDt;
00152 
00153     if (mNextTime >= mEnd)
00154     {
00155         dt = mEnd - mTime;
00156     }
00157     assert(mAdditionalTimesDeprecated.empty());
00158     return dt;
00159 }
00160 double TimeStepper::GetIdealTimeStep()
00161 {
00162     return mDt;
00163 }
00164 
00165 bool TimeStepper::IsTimeAtEnd() const
00166 {
00167     return (mTime >= mEnd);
00168 }
00169 
00170 unsigned TimeStepper::EstimateTimeSteps() const
00171 {
00172     return (unsigned) floor((mEnd - mStart)/mDt + 0.5);
00173 }
00174 
00175 unsigned TimeStepper::GetTotalTimeStepsTaken() const
00176 {
00177     return mTotalTimeStepsTaken;
00178 }
00179 
00180 void TimeStepper::ResetTimeStep(double dt)
00181 {
00182     assert(dt > 0);
00183     /*
00184      * The error in subtracting two numbers of the same magnitude is about
00185      * DBL_EPSILON times that magnitude (we use the sum of the two numbers
00186      * here as a conservative estimate of their maximum). When both mDt and
00187      * dt are small then the error should be around DBL_EPSILON.
00188      */
00189     double scale = DBL_EPSILON*(mDt + dt);
00190     if (mDt + dt < 1.0)
00191     {
00192         scale = DBL_EPSILON;
00193     }
00194     if (fabs(mDt-dt) > scale)
00195     {
00196         mDt = dt;
00197         mStart = mTime;
00198         mTotalTimeStepsTaken = 0;
00199 
00200         mNextTime = CalculateNextTime();
00201     }
00202 }

Generated by  doxygen 1.6.2