Chaste Release::3.1
BackwardEulerIvpOdeSolver.cpp
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 
00037 #include "BackwardEulerIvpOdeSolver.hpp"
00038 #include <cmath>
00039 
00040 void BackwardEulerIvpOdeSolver::ComputeResidual(AbstractOdeSystem* pAbstractOdeSystem,
00041                                                 double timeStep,
00042                                                 double time,
00043                                                 std::vector<double>& rCurrentYValues,
00044                                                 std::vector<double>& rCurrentGuess)
00045 {
00046     std::vector<double> dy(mSizeOfOdeSystem); //For JC to optimize
00047     pAbstractOdeSystem->EvaluateYDerivatives(time+timeStep, rCurrentGuess, dy);
00048     for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00049     {
00050         mResidual[i] = rCurrentGuess[i] - timeStep * dy[i] - rCurrentYValues[i];
00051     }
00052 }
00053 
00054 void BackwardEulerIvpOdeSolver::ComputeJacobian(AbstractOdeSystem* pAbstractOdeSystem,
00055                                                 double timeStep,
00056                                                 double time,
00057                                                 std::vector<double>& rCurrentYValues,
00058                                                 std::vector<double>& rCurrentGuess)
00059 {
00060     for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00061     {
00062         for (unsigned j=0; j<mSizeOfOdeSystem; j++)
00063         {
00064             mJacobian[i][j] = 0.0;
00065         }
00066     }
00067 
00068     if (pAbstractOdeSystem->GetUseAnalyticJacobian() && !mForceUseOfNumericalJacobian)
00069     {
00070         // The ODE system has an analytic jacobian, so use that
00071         AbstractOdeSystemWithAnalyticJacobian* p_ode_system
00072             = static_cast<AbstractOdeSystemWithAnalyticJacobian*>(pAbstractOdeSystem);
00073         p_ode_system->AnalyticJacobian(rCurrentGuess, mJacobian, time, timeStep);
00074     }
00075     else
00076     {
00077         ComputeNumericalJacobian(pAbstractOdeSystem,
00078                                  timeStep,
00079                                  time,
00080                                  rCurrentYValues,
00081                                  rCurrentGuess);
00082     }
00083 }
00084 
00085 void BackwardEulerIvpOdeSolver::SolveLinearSystem()
00086 {
00087     double fact;
00088     for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00089     {
00090         for (unsigned ii=i+1; ii<mSizeOfOdeSystem; ii++)
00091         {
00092             fact = mJacobian[ii][i]/mJacobian[i][i];
00093             for (unsigned j=i; j<mSizeOfOdeSystem; j++)
00094             {
00095                 mJacobian[ii][j] -= fact*mJacobian[i][j];
00096             }
00097             mResidual[ii] -= fact*mResidual[i];
00098         }
00099     }
00100     // This needs to int, since a downloop in unsigned won't terminate properly
00101     for (int i=mSizeOfOdeSystem-1; i>=0; i--)
00102     {
00103         mUpdate[i] = mResidual[i];
00104         for (unsigned j=i+1; j<mSizeOfOdeSystem; j++)
00105         {
00106             mUpdate[i] -= mJacobian[i][j]*mUpdate[j];
00107         }
00108         mUpdate[i] /= mJacobian[i][i];
00109     }
00110 }
00111 
00112 double BackwardEulerIvpOdeSolver::ComputeNorm(double* pVector)
00113 {
00114     double norm = 0.0;
00115     for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00116     {
00117         if (fabs(pVector[i]) > norm)
00118         {
00119             norm = fabs(pVector[i]);
00120         }
00121     }
00122     return norm;
00123 }
00124 
00125 void BackwardEulerIvpOdeSolver::ComputeNumericalJacobian(AbstractOdeSystem* pAbstractOdeSystem,
00126                                                          double timeStep,
00127                                                          double time,
00128                                                          std::vector<double>& rCurrentYValues,
00129                                                          std::vector<double>& rCurrentGuess)
00130 {
00131     std::vector<double> residual(mSizeOfOdeSystem);
00132     std::vector<double> residual_perturbed(mSizeOfOdeSystem);
00133     std::vector<double> guess_perturbed(mSizeOfOdeSystem);
00134 
00135     double epsilon = mNumericalJacobianEpsilon;
00136 
00137     ComputeResidual(pAbstractOdeSystem, timeStep, time, rCurrentYValues, rCurrentGuess);
00138     for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00139     {
00140         residual[i] = mResidual[i];
00141     }
00142 
00143     for (unsigned global_column=0; global_column<mSizeOfOdeSystem; global_column++)
00144     {
00145         for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00146         {
00147             guess_perturbed[i] = rCurrentGuess[i];
00148         }
00149 
00150         guess_perturbed[global_column] += epsilon;
00151 
00152         ComputeResidual(pAbstractOdeSystem, timeStep, time, rCurrentYValues, guess_perturbed);
00153         for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00154         {
00155             residual_perturbed[i] = mResidual[i];
00156         }
00157 
00158         // Compute residual_perturbed - residual
00159         double one_over_eps = 1.0/epsilon;
00160         for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00161         {
00162             mJacobian[i][global_column] = one_over_eps*(residual_perturbed[i] - residual[i]);
00163         }
00164     }
00165 }
00166 
00167 void BackwardEulerIvpOdeSolver::CalculateNextYValue(AbstractOdeSystem* pAbstractOdeSystem,
00168                                                     double timeStep,
00169                                                     double time,
00170                                                     std::vector<double>& rCurrentYValues,
00171                                                     std::vector<double>& rNextYValues)
00172 {
00173     // Check the size of the ODE system matches the solvers expected
00174     assert(mSizeOfOdeSystem == pAbstractOdeSystem->GetNumberOfStateVariables());
00175 
00176     unsigned counter = 0;
00177 //        const double eps = 1e-6 * rCurrentGuess[0]; // Our tolerance (should use min(guess) perhaps?)
00178     const double eps = 1e-6; // JonW tolerance
00179     double norm = 2*eps;
00180 
00181     std::vector<double> current_guess(mSizeOfOdeSystem);
00182     current_guess.assign(rCurrentYValues.begin(), rCurrentYValues.end());
00183 
00184     while (norm > eps)
00185     {
00186         // Calculate Jacobian and mResidual for current guess
00187         ComputeResidual(pAbstractOdeSystem, timeStep, time, rCurrentYValues, current_guess);
00188         ComputeJacobian(pAbstractOdeSystem, timeStep, time, rCurrentYValues, current_guess);
00189 //            // Update norm (our style)
00190 //            norm = ComputeNorm(mResidual);
00191 
00192         // Solve Newton linear system
00193         SolveLinearSystem();
00194 
00195         // Update norm (JonW style)
00196         norm = ComputeNorm(mUpdate);
00197 
00198         // Update current guess
00199         for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00200         {
00201             current_guess[i] -= mUpdate[i];
00202         }
00203 
00204         counter++;
00205         assert(counter < 20); // avoid infinite loops
00206     }
00207     rNextYValues.assign(current_guess.begin(), current_guess.end());
00208 }
00209 
00210 BackwardEulerIvpOdeSolver::BackwardEulerIvpOdeSolver(unsigned sizeOfOdeSystem)
00211 {
00212     mSizeOfOdeSystem = sizeOfOdeSystem;
00213 
00214     // default epsilon
00215     mNumericalJacobianEpsilon = 1e-6;
00216     mForceUseOfNumericalJacobian = false;
00217 
00218     // allocate memory
00219     mResidual = new double[mSizeOfOdeSystem];
00220     mUpdate = new double[mSizeOfOdeSystem];
00221 
00222     mJacobian = new double*[mSizeOfOdeSystem];
00223     for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00224     {
00225         mJacobian[i] = new double[mSizeOfOdeSystem];
00226     }
00227 }
00228 
00229 BackwardEulerIvpOdeSolver::~BackwardEulerIvpOdeSolver()
00230 {
00231     // Delete pointers
00232     delete[] mResidual;
00233     delete[] mUpdate;
00234 
00235     for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00236     {
00237         delete[] mJacobian[i];
00238     }
00239     delete[] mJacobian;
00240 }
00241 
00242 void BackwardEulerIvpOdeSolver::SetEpsilonForNumericalJacobian(double epsilon)
00243 {
00244     assert(epsilon > 0);
00245     mNumericalJacobianEpsilon = epsilon;
00246 }
00247 
00248 void BackwardEulerIvpOdeSolver::ForceUseOfNumericalJacobian()
00249 {
00250     mForceUseOfNumericalJacobian = true;
00251 }
00252 
00253 
00254 // Serialization for Boost >= 1.36
00255 #include "SerializationExportWrapperForCpp.hpp"
00256 CHASTE_CLASS_EXPORT(BackwardEulerIvpOdeSolver)