TimeStepper.cpp
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include <cassert>
00030 #include <cmath>
00031
00032 #include "TimeStepper.hpp"
00033 #include "Exception.hpp"
00034 #include "MathsCustomFunctions.hpp"
00035
00036 TimeStepper::TimeStepper(double startTime, double endTime, double dt, bool enforceConstantTimeStep, std::vector<double> additionalTimes)
00037 : mStart(startTime),
00038 mEnd(endTime),
00039 mDt(dt),
00040 mTotalTimeStepsTaken(0),
00041 mAdditionalTimesReached(0),
00042 mTime(startTime),
00043 mEpsilon(DBL_EPSILON)
00044 {
00045 if (startTime > endTime)
00046 {
00047 EXCEPTION("The simulation duration must be positive, not " << endTime-startTime);
00048 }
00049
00050
00051 for (unsigned i=0; i<additionalTimes.size(); i++)
00052 {
00053 if (i > 0)
00054 {
00055 if (additionalTimes[i-1] >= additionalTimes[i])
00056 {
00057 EXCEPTION("The additional times vector should be in ascending numerical order; "
00058 "entry " << i << " is less than or equal to entry " << i-1 << ".");
00059 }
00060 }
00061
00062 double time_interval = additionalTimes[i] - startTime;
00063
00064
00065 if (!Divides(mDt, time_interval) && (time_interval > DBL_EPSILON))
00066 {
00067 mAdditionalTimes.push_back(additionalTimes[i]);
00068 }
00069 }
00070
00071
00072
00073
00074
00075
00076 if (mEnd > 1.0)
00077 {
00078 mEpsilon = DBL_EPSILON*mEnd;
00079 }
00080
00081
00082 if (enforceConstantTimeStep)
00083 {
00084 double expected_end_time = mStart + mDt*EstimateTimeSteps();
00085
00086 if (fabs( mEnd - expected_end_time ) > mEpsilon)
00087 {
00088 EXCEPTION("TimeStepper estimates non-constant timesteps will need to be used: check timestep "
00089 "divides (end_time-start_time) (or divides printing timestep). "
00090 "[End time=" << mEnd << "; start=" << mStart << "; dt=" << mDt << "; error="
00091 << fabs(mEnd-expected_end_time) << "]");
00092 }
00093 }
00094
00095 mNextTime = CalculateNextTime();
00096 }
00097
00098 double TimeStepper::CalculateNextTime()
00099 {
00100 double next_time = mStart + (mTotalTimeStepsTaken - mAdditionalTimesReached + 1)*mDt;
00101
00102
00103
00104
00105 if (mEnd - next_time <= mEpsilon)
00106 {
00107 next_time = mEnd;
00108 }
00109
00110 if (!mAdditionalTimes.empty())
00111 {
00112 if (mAdditionalTimesReached < mAdditionalTimes.size())
00113 {
00114
00115 double next_additional_time = mAdditionalTimes[mAdditionalTimesReached];
00116 double epsilon = next_additional_time > 1 ? next_additional_time*DBL_EPSILON : DBL_EPSILON;
00117 if (next_additional_time - next_time <= epsilon)
00118 {
00119 next_time = next_additional_time;
00120 mAdditionalTimesReached++;
00121 }
00122 }
00123 }
00124 return next_time;
00125 }
00126
00127 void TimeStepper::AdvanceOneTimeStep()
00128 {
00129 mTotalTimeStepsTaken++;
00130 if (mTotalTimeStepsTaken == 0)
00131 {
00132 EXCEPTION("Time step counter has overflowed.");
00133 }
00134 if (mTime == mNextTime)
00135 {
00136 EXCEPTION("TimeStepper incremented beyond end time.");
00137 }
00138 mTime = mNextTime;
00139
00140 mNextTime = CalculateNextTime();
00141 }
00142
00143 double TimeStepper::GetTime() const
00144 {
00145 return mTime;
00146 }
00147
00148 double TimeStepper::GetNextTime() const
00149 {
00150 return mNextTime;
00151 }
00152
00153 double TimeStepper::GetNextTimeStep()
00154 {
00155 double dt = mDt;
00156
00157 if (mNextTime == mEnd)
00158 {
00159 dt = mEnd - mTime;
00160 }
00161
00162
00163 if (mAdditionalTimesReached > 0)
00164 {
00165 if ((mNextTime == mAdditionalTimes[mAdditionalTimesReached-1]) || (mTime == mAdditionalTimes[mAdditionalTimesReached-1]))
00166 {
00167 dt = mNextTime - mTime;
00168 assert(dt > 0);
00169 }
00170 }
00171
00172 return dt;
00173 }
00174 double TimeStepper::GetIdealTimeStep()
00175 {
00176 return(mDt);
00177 }
00178
00179 bool TimeStepper::IsTimeAtEnd() const
00180 {
00181 return (mTime >= mEnd);
00182 }
00183
00184 unsigned TimeStepper::EstimateTimeSteps() const
00185 {
00186 return (unsigned) floor((mEnd - mStart)/mDt + 0.5) + mAdditionalTimes.size();
00187 }
00188
00189 unsigned TimeStepper::GetTotalTimeStepsTaken() const
00190 {
00191 return mTotalTimeStepsTaken;
00192 }
00193
00194 void TimeStepper::ResetTimeStep(double dt)
00195 {
00196 assert(dt > 0);
00197
00198
00199
00200
00201
00202
00203 double scale = DBL_EPSILON*(mDt + dt);
00204 if (mDt + dt < 1.0)
00205 {
00206 scale = DBL_EPSILON;
00207 }
00208 if (fabs(mDt-dt) > scale)
00209 {
00210 mDt = dt;
00211 mStart = mTime;
00212 mTotalTimeStepsTaken = 0;
00213
00214 mNextTime = CalculateNextTime();
00215 }
00216 }