RungeKuttaFehlbergIvpOdeSolver.cpp

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