00001 /* 00002 00003 Copyright (C) University of Oxford, 2005-2011 00004 00005 University of Oxford means the Chancellor, Masters and Scholars of the 00006 University of Oxford, having an administrative office at Wellington 00007 Square, Oxford OX1 2JD, UK. 00008 00009 This file is part of Chaste. 00010 00011 Chaste is free software: you can redistribute it and/or modify it 00012 under the terms of the GNU Lesser General Public License as published 00013 by the Free Software Foundation, either version 2.1 of the License, or 00014 (at your option) any later version. 00015 00016 Chaste is distributed in the hope that it will be useful, but WITHOUT 00017 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00018 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 00019 License for more details. The offer of Chaste under the terms of the 00020 License is subject to the License being interpreted in accordance with 00021 English Law and subject to any action against the University of Oxford 00022 being under the jurisdiction of the English Courts. 00023 00024 You should have received a copy of the GNU Lesser General Public License 00025 along with Chaste. If not, see <http://www.gnu.org/licenses/>. 00026 00027 */ 00028 00029 00030 #include "TimeStepper.hpp" 00031 #include "Exception.hpp" 00032 #include <cmath> 00033 #include <cfloat> 00034 #include <cassert> 00035 00036 const double SMIDGE = 1e-10; 00037 00038 TimeStepper::TimeStepper(double startTime, double endTime, double dt, bool enforceConstantTimeStep, std::vector<double> additionalTimes) 00039 : mStart(startTime), 00040 mEnd(endTime), 00041 mDt(dt), 00042 mTotalTimeStepsTaken(0), 00043 mAdditionalTimesReached(0), 00044 mTime(startTime) 00045 { 00046 if (startTime > endTime) 00047 { 00048 EXCEPTION("The simulation duration must be positive"); 00049 } 00050 00051 // Remove any additionalTimes entries which fall too close to a time when the stepper would stop anyway 00052 for(unsigned i=0;i<additionalTimes.size();i++) 00053 { 00054 if(i>0) 00055 { 00056 if(additionalTimes[i-1] >= additionalTimes[i]) 00057 { 00058 EXCEPTION("The additional times vector should be in ascending numerical order"); 00059 } 00060 } 00061 00062 double test_value=(additionalTimes[i]-startTime)/mDt; 00063 if(fabs(floor(test_value+0.5)-test_value)>1e-12) 00064 { 00065 mAdditionalTimes.push_back(additionalTimes[i]); 00066 } 00067 } 00068 00069 mNextTime = CalculateNextTime(); 00070 00071 // if enforceConstantTimeStep check whether the times are such that we won't have a variable dt 00072 if (enforceConstantTimeStep) 00073 { 00074 if ( fabs(mDt*EstimateTimeSteps()-mEnd+mStart) > SMIDGE ) 00075 { 00076 EXCEPTION("TimeStepper estimate non-constant timesteps will need to be used: check timestep divides (end_time-start_time) (or divides printing timestep)"); 00077 } 00078 } 00079 } 00080 00081 double TimeStepper::CalculateNextTime() 00082 { 00083 double next_time = mStart + (mTotalTimeStepsTaken-mAdditionalTimesReached+1) * mDt; 00084 00085 if ((next_time) + SMIDGE*(mDt) >= mEnd) 00086 { 00087 next_time = mEnd; 00088 } 00089 00090 if ( (mAdditionalTimes.size()>0) // any additional times given 00091 && (mAdditionalTimesReached<mAdditionalTimes.size()) // not yet done all the additional times 00092 && ((next_time) + SMIDGE*(mDt) >= mAdditionalTimes[mAdditionalTimesReached]) ) // this next step takes us over an additional time 00093 { 00094 next_time = mAdditionalTimes[mAdditionalTimesReached]; 00095 mAdditionalTimesReached++; 00096 } 00097 00098 return next_time; 00099 } 00100 00101 void TimeStepper::AdvanceOneTimeStep() 00102 { 00103 mTotalTimeStepsTaken++; 00104 if (mTotalTimeStepsTaken == 0) 00105 { 00106 EXCEPTION("Time step counter has overflowed."); 00107 } 00108 mTime = mNextTime; 00109 00110 mNextTime = CalculateNextTime(); 00111 } 00112 00113 double TimeStepper::GetTime() const 00114 { 00115 return mTime; 00116 } 00117 00118 double TimeStepper::GetNextTime() const 00119 { 00120 return mNextTime; 00121 } 00122 00123 double TimeStepper::GetNextTimeStep() 00124 { 00125 double dt = mDt; 00126 00127 if (mNextTime == mEnd) 00128 { 00129 dt = mEnd - mTime; 00130 } 00131 00132 // if the next time or the current time is one of the additional times, the timestep will not be mDt 00133 if ((mAdditionalTimesReached>0) && 00134 ( (mNextTime == mAdditionalTimes[mAdditionalTimesReached-1]) || (mTime == mAdditionalTimes[mAdditionalTimesReached-1]) ) ) 00135 { 00136 dt = mNextTime - mTime; 00137 assert(dt>0); 00138 } 00139 00140 return dt; 00141 } 00142 00143 bool TimeStepper::IsTimeAtEnd() const 00144 { 00145 return mTime >= mEnd; 00146 } 00147 00148 unsigned TimeStepper::EstimateTimeSteps() const 00149 { 00150 return (unsigned) floor((mEnd - mStart)/mDt+0.5) + mAdditionalTimes.size(); 00151 } 00152 00153 unsigned TimeStepper::GetTotalTimeStepsTaken() const 00154 { 00155 return mTotalTimeStepsTaken; 00156 } 00157 00158 void TimeStepper::ResetTimeStep(double dt) 00159 { 00160 if (fabs(mDt-dt) > 1e-8) 00161 { 00162 assert(dt>0); 00163 mDt = dt; 00164 mStart = mTime; 00165 mTotalTimeStepsTaken = 0; 00166 00167 mNextTime = CalculateNextTime(); 00168 } 00169 }