Chaste Release::3.1
|
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 #include "RungeKuttaFehlbergIvpOdeSolver.hpp" 00037 #include <cmath> 00038 #include <cfloat> 00039 #include <iostream> 00040 #include "Exception.hpp" 00041 00042 /* 00043 * PROTECTED FUNCTIONS ========================================================= 00044 */ 00045 00046 void RungeKuttaFehlbergIvpOdeSolver::InternalSolve(OdeSolution& rSolution, 00047 AbstractOdeSystem* pOdeSystem, 00048 std::vector<double>& rYValues, 00049 std::vector<double>& rWorkingMemory, 00050 double startTime, 00051 double endTime, 00052 double maxTimeStep, 00053 double minTimeStep, 00054 double tolerance, 00055 bool outputSolution) 00056 { 00057 const unsigned number_of_variables = pOdeSystem->GetNumberOfStateVariables(); 00058 mError.reserve(number_of_variables); 00059 mk1.reserve(number_of_variables); 00060 mk2.reserve(number_of_variables); 00061 mk3.reserve(number_of_variables); 00062 mk4.reserve(number_of_variables); 00063 mk5.reserve(number_of_variables); 00064 mk6.reserve(number_of_variables); 00065 myk2.reserve(number_of_variables); 00066 myk3.reserve(number_of_variables); 00067 myk4.reserve(number_of_variables); 00068 myk5.reserve(number_of_variables); 00069 myk6.reserve(number_of_variables); 00070 00071 double current_time = startTime; 00072 double time_step = maxTimeStep; 00073 bool got_to_end = false; 00074 bool accurate_enough = false; 00075 unsigned number_of_time_steps = 0; 00076 00077 if (outputSolution) 00078 { // Write out ICs 00079 rSolution.rGetTimes().push_back(current_time); 00080 rSolution.rGetSolutions().push_back(rYValues); 00081 } 00082 00083 // should never get here if this bool has been set to true; 00084 assert(!mStoppingEventOccurred); 00085 while (!got_to_end) 00086 { 00087 //std::cout << "New timestep\n" << std::flush; 00088 while (!accurate_enough) 00089 { 00090 accurate_enough = true; // assume it is OK until we check and find otherwise 00091 00092 // Function that calls the appropriate one-step solver 00093 CalculateNextYValue(pOdeSystem, 00094 time_step, 00095 current_time, 00096 rYValues, 00097 rWorkingMemory); 00098 00099 // Find the maximum error in this vector 00100 double max_error = -DBL_MAX; 00101 for (unsigned i=0; i<number_of_variables; i++) 00102 { 00103 if (mError[i] > max_error) 00104 { 00105 max_error = mError[i]; 00106 } 00107 } 00108 00109 if (max_error > tolerance) 00110 {// Reject the step-size and do it again. 00111 accurate_enough = false; 00112 //std::cout << "Approximation rejected\n" << std::flush; 00113 } 00114 else 00115 { 00116 // step forward the time since step has now been made 00117 current_time = current_time + time_step; 00118 //std::cout << "Approximation accepted with time step = "<< time_step << "\n" << std::flush; 00119 //std::cout << "max_error = " << max_error << " tolerance = " << tolerance << "\n" << std::flush; 00120 if (outputSolution) 00121 { // Write out ICs 00122 //std::cout << "In solver Time = " << current_time << " y = " << rWorkingMemory[0] << "\n" << std::flush; 00123 rSolution.rGetTimes().push_back(current_time); 00124 rSolution.rGetSolutions().push_back(rWorkingMemory); 00125 number_of_time_steps++; 00126 } 00127 } 00128 00129 // Set a new step size based on the accuracy here 00130 AdjustStepSize(time_step, max_error, tolerance, maxTimeStep, minTimeStep); 00131 } 00132 00133 // For the next timestep check the step doesn't go past the end... 00134 if (current_time + time_step > endTime) 00135 { // Allow a smaller timestep for the final step. 00136 time_step = endTime - current_time; 00137 } 00138 00139 if ( pOdeSystem->CalculateStoppingEvent(current_time, 00140 rWorkingMemory) == true ) 00141 { 00142 mStoppingTime = current_time; 00143 mStoppingEventOccurred = true; 00144 } 00145 00146 if (mStoppingEventOccurred || current_time>=endTime) 00147 { 00148 got_to_end = true; 00149 } 00150 00151 // Approximation accepted - put it in rYValues 00152 rYValues.assign(rWorkingMemory.begin(), rWorkingMemory.end()); 00153 accurate_enough = false; // for next loop. 00154 //std::cout << "Finished Time Step\n" << std::flush; 00155 } 00156 rSolution.SetNumberOfTimeSteps(number_of_time_steps); 00157 } 00158 00159 void RungeKuttaFehlbergIvpOdeSolver::CalculateNextYValue(AbstractOdeSystem* pAbstractOdeSystem, 00160 double timeStep, 00161 double time, 00162 std::vector<double>& rCurrentYValues, 00163 std::vector<double>& rNextYValues) 00164 { 00165 const unsigned num_equations = pAbstractOdeSystem->GetNumberOfStateVariables(); 00166 00167 00168 std::vector<double>& dy = rNextYValues; // re-use memory (not that it makes much difference here!) 00169 00170 pAbstractOdeSystem->EvaluateYDerivatives(time, rCurrentYValues, dy); 00171 00172 for (unsigned i=0; i<num_equations; i++) 00173 { 00174 mk1[i] = timeStep*dy[i]; 00175 myk2[i] = rCurrentYValues[i] + 0.25*mk1[i]; 00176 } 00177 00178 pAbstractOdeSystem->EvaluateYDerivatives(time + 0.25*timeStep, myk2, dy); 00179 for (unsigned i=0; i<num_equations; i++) 00180 { 00181 mk2[i] = timeStep*dy[i]; 00182 myk3[i] = rCurrentYValues[i] + 0.09375*mk1[i] + 0.28125*mk2[i]; 00183 } 00184 00185 pAbstractOdeSystem->EvaluateYDerivatives(time + 0.375*timeStep, myk3, dy); 00186 for (unsigned i=0; i<num_equations; i++) 00187 { 00188 mk3[i] = timeStep*dy[i]; 00189 myk4[i] = rCurrentYValues[i] + m1932o2197*mk1[i] - m7200o2197*mk2[i] 00190 + m7296o2197*mk3[i]; 00191 } 00192 00193 pAbstractOdeSystem->EvaluateYDerivatives(time+m12o13*timeStep, myk4, dy); 00194 for (unsigned i=0; i<num_equations; i++) 00195 { 00196 mk4[i] = timeStep*dy[i]; 00197 myk5[i] = rCurrentYValues[i] + m439o216*mk1[i] - 8*mk2[i] 00198 + m3680o513*mk3[i]- m845o4104*mk4[i]; 00199 } 00200 00201 pAbstractOdeSystem->EvaluateYDerivatives(time+timeStep, myk5, dy); 00202 for (unsigned i=0; i<num_equations; i++) 00203 { 00204 mk5[i] = timeStep*dy[i]; 00205 myk6[i] = rCurrentYValues[i] - m8o27*mk1[i] + 2*mk2[i] - m3544o2565*mk3[i] 00206 + m1859o4104*mk4[i] - 0.275*mk5[i]; 00207 } 00208 00209 pAbstractOdeSystem->EvaluateYDerivatives(time+0.5*timeStep, myk6, dy); 00210 for (unsigned i=0; i<num_equations; i++) 00211 { 00212 mk6[i] = timeStep*dy[i]; 00213 mError[i] = (1/timeStep)*fabs(m1o360*mk1[i] - m128o4275*mk3[i] 00214 - m2197o75240*mk4[i] + 0.02*mk5[i]+ m2o55*mk6[i]); 00215 rNextYValues[i] = rCurrentYValues[i] + m25o216*mk1[i] + m1408o2565*mk3[i] 00216 + m2197o4104*mk4[i] - 0.2*mk5[i]; 00217 } 00218 } 00219 00220 void RungeKuttaFehlbergIvpOdeSolver::AdjustStepSize(double& rCurrentStepSize, 00221 const double& rError, 00222 const double& rTolerance, 00223 const double& rMaxTimeStep, 00224 const double& rMinTimeStep) 00225 { 00226 // Work out scaling factor delta for the step size 00227 double delta = pow(rTolerance/(2.0*rError), 0.25); 00228 00229 // Maximum adjustment is *0.1 or *4 00230 if (delta <= 0.1) 00231 { 00232 rCurrentStepSize *= 0.1; 00233 } 00234 else if (delta >= 4.0) 00235 { 00236 rCurrentStepSize *= 4.0; 00237 } 00238 else 00239 { 00240 rCurrentStepSize *= delta; 00241 } 00242 00243 if (rCurrentStepSize > rMaxTimeStep) 00244 { 00245 rCurrentStepSize = rMaxTimeStep; 00246 } 00247 00248 if (rCurrentStepSize < rMinTimeStep) 00249 { 00250 std::cout << "rCurrentStepSize = " << rCurrentStepSize << "\n" << std::flush; 00251 std::cout << "rMinTimeStep = " << rMinTimeStep << "\n" << std::flush; 00252 00253 EXCEPTION("RKF45 Solver: Ode needs a smaller timestep than the set minimum\n"); 00254 } 00255 00256 } 00257 00258 00259 /* 00260 * PUBLIC FUNCTIONS============================================================= 00261 */ 00262 00263 RungeKuttaFehlbergIvpOdeSolver::RungeKuttaFehlbergIvpOdeSolver() 00264 : m1932o2197(1932.0/2197.0), // you need the .0 s - caused me no end of trouble. 00265 m7200o2197(7200.0/2197.0), 00266 m7296o2197(7296.0/2197.0), 00267 m12o13(12.0/13.0), 00268 m439o216(439.0/216.0), 00269 m3680o513(3680.0/513.0), 00270 m845o4104(845.0/4104.0), 00271 m8o27(8.0/27.0), 00272 m3544o2565(3544.0/2565.0), 00273 m1859o4104(1859.0/4104.0), 00274 m1o360(1.0/360.0), 00275 m128o4275(128.0/4275.0), 00276 m2197o75240(2197.0/75240.0), 00277 m2o55(2.0/55.0), 00278 m25o216(25.0/216.0), 00279 m1408o2565(1408.0/2565.0), 00280 m2197o4104(2197.0/4104.0) 00281 { 00282 } 00283 00284 OdeSolution RungeKuttaFehlbergIvpOdeSolver::Solve(AbstractOdeSystem* pOdeSystem, 00285 std::vector<double>& rYValues, 00286 double startTime, 00287 double endTime, 00288 double timeStep, 00289 double tolerance) 00290 { 00291 assert(rYValues.size()==pOdeSystem->GetNumberOfStateVariables()); 00292 assert(endTime > startTime); 00293 assert(timeStep > 0.0); 00294 00295 mStoppingEventOccurred = false; 00296 if ( pOdeSystem->CalculateStoppingEvent(startTime, rYValues) == true ) 00297 { 00298 EXCEPTION("(Solve with sampling) Stopping event is true for initial condition"); 00299 } 00300 // Allocate working memory 00301 std::vector<double> working_memory(rYValues.size()); 00302 // And solve... 00303 OdeSolution solutions; 00304 //solutions.SetNumberOfTimeSteps((unsigned)(10.0*(startTime-endTime)/timeStep)); 00305 bool return_solution = true; 00306 InternalSolve(solutions, pOdeSystem, rYValues, working_memory, startTime, endTime, timeStep, 1e-5, tolerance, return_solution); 00307 return solutions; 00308 } 00309 00310 void RungeKuttaFehlbergIvpOdeSolver::Solve(AbstractOdeSystem* pOdeSystem, 00311 std::vector<double>& rYValues, 00312 double startTime, 00313 double endTime, 00314 double timeStep) 00315 { 00316 assert(rYValues.size()==pOdeSystem->GetNumberOfStateVariables()); 00317 assert(endTime > startTime); 00318 assert(timeStep > 0.0); 00319 00320 mStoppingEventOccurred = false; 00321 if ( pOdeSystem->CalculateStoppingEvent(startTime, rYValues) == true ) 00322 { 00323 EXCEPTION("(Solve without sampling) Stopping event is true for initial condition"); 00324 } 00325 // Allocate working memory 00326 std::vector<double> working_memory(rYValues.size()); 00327 // And solve... 00328 OdeSolution not_required_solution; 00329 bool return_solution = false; 00330 InternalSolve(not_required_solution, pOdeSystem, rYValues, working_memory, startTime, endTime, timeStep, 1e-4, 1e-5, return_solution); 00331 } 00332 00333 00334 // Serialization for Boost >= 1.36 00335 #include "SerializationExportWrapperForCpp.hpp" 00336 CHASTE_CLASS_EXPORT(RungeKuttaFehlbergIvpOdeSolver)