BackwardEulerIvpOdeSolver.hpp

00001 /*
00002 
00003 Copyright (C) University of Oxford, 2005-2009
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 #ifndef BACKWARDEULERIVPODESOLVER_HPP_
00031 #define BACKWARDEULERIVPODESOLVER_HPP_
00032 
00033 #include "AbstractOneStepIvpOdeSolver.hpp"
00034 #include "AbstractOdeSystem.hpp"
00035 #include "AbstractOdeSystemWithAnalyticJacobian.hpp"
00036 #include "OdeSolution.hpp"
00037 #include <cassert>
00038 #include <vector>
00039 
00040 
00041 class BackwardEulerIvpOdeSolver  : public AbstractOneStepIvpOdeSolver
00042 {
00043 private:
00044     unsigned mSizeOfOdeSystem;
00045 
00047     double mNumericalJacobianEpsilon;
00048     bool mForceUseOfNumericalJacobian;
00049 
00050     // NOTE: we use (unsafe) double pointers here rather than
00051     // std::vectors because using std::vectors would lead to a
00052     // slow down by a factor of about 4.
00053 
00055     double* mResidual;
00057     double** mJacobian;
00059     double* mUpdate;
00060 
00061 
00062     void ComputeResidual(AbstractOdeSystem* pAbstractOdeSystem,
00063                          double timeStep,
00064                          double time,
00065                          std::vector<double>& currentYValues,
00066                          std::vector<double>& currentGuess)
00067     {
00068         std::vector<double> dy(mSizeOfOdeSystem);//For JC to optimize
00069         pAbstractOdeSystem->EvaluateYDerivatives(time, currentGuess, dy);
00070         for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00071         {
00072 
00073             mResidual[i] = currentGuess[i] - timeStep * dy[i] - currentYValues[i];
00074         }
00075     }
00076 
00077 
00078     void ComputeJacobian(AbstractOdeSystem* pAbstractOdeSystem,
00079                          double timeStep,
00080                          double time,
00081                          std::vector<double>& currentYValues,
00082                          std::vector<double>& currentGuess)
00083     {
00084         for (unsigned i = 0; i<mSizeOfOdeSystem; i++)
00085         {
00086             for (unsigned j = 0; j <mSizeOfOdeSystem; j++)
00087             {
00088                 mJacobian[i][j]=0.0;
00089             }
00090         }
00091 
00092         if (pAbstractOdeSystem->GetUseAnalytic() && !mForceUseOfNumericalJacobian)
00093         {
00094             // the ode system has an analytic jacobian, use that
00095             AbstractOdeSystemWithAnalyticJacobian *p_ode_system
00096             = static_cast<AbstractOdeSystemWithAnalyticJacobian*>(pAbstractOdeSystem);
00097             p_ode_system->AnalyticJacobian(currentGuess, mJacobian, time, timeStep);
00098         }
00099         else
00100         {
00101             ComputeNumericalJacobian(pAbstractOdeSystem,
00102                                      timeStep,
00103                                      time,
00104                                      currentYValues,
00105                                      currentGuess);
00106         }
00107     }
00108 
00109     void SolveLinearSystem()
00110     {
00111         double fact;
00112         for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00113         {
00114             for (unsigned ii=i+1; ii<mSizeOfOdeSystem; ii++)
00115             {
00116                 fact = mJacobian[ii][i]/mJacobian[i][i];
00117                 for (unsigned j=i; j<mSizeOfOdeSystem; j++)
00118                 {
00119                     mJacobian[ii][j] -= fact*mJacobian[i][j];
00120                 }
00121                 mResidual[ii] -= fact*mResidual[i];
00122             }
00123         }
00124         /* This needs to int, since a downloop in unsigned won't
00125          * terminate properly*/
00126         for (int i=mSizeOfOdeSystem-1; i>=0; i--)
00127         {
00128             mUpdate[i] = mResidual[i];
00129             for (unsigned j=i+1; j<mSizeOfOdeSystem; j++)
00130             {
00131                 mUpdate[i] -= mJacobian[i][j]*mUpdate[j];
00132             }
00133             mUpdate[i] /= mJacobian[i][i];
00134         }
00135     }
00136 
00137     double ComputeNorm(double* vector)
00138     {
00139         double norm = 0.0;
00140         for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00141         {
00142             if (fabs(vector[i]) > norm)
00143             {
00144                 norm = fabs(vector[i]);
00145             }
00146         }
00147         return norm;
00148     }
00149 
00150 
00151     void ComputeNumericalJacobian(AbstractOdeSystem* pAbstractOdeSystem,
00152                                   double timeStep,
00153                                   double time,
00154                                   std::vector<double>& currentYValues,
00155                                   std::vector<double>& currentGuess)
00156     {
00157         std::vector<double> residual(mSizeOfOdeSystem);
00158         std::vector<double> residual_perturbed(mSizeOfOdeSystem);
00159         std::vector<double> guess_perturbed(mSizeOfOdeSystem);
00160 
00161         double epsilon= mNumericalJacobianEpsilon;
00162 
00163         ComputeResidual(pAbstractOdeSystem, timeStep, time, currentYValues, currentGuess);
00164         for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00165         {
00166             residual[i] = mResidual[i];
00167         }
00168 
00169 
00170         for (unsigned global_column=0; global_column<mSizeOfOdeSystem; global_column++)
00171         {
00172             for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00173             {
00174                 guess_perturbed[i] = currentGuess[i];
00175             }
00176 
00177             guess_perturbed[global_column] += epsilon;
00178 
00179             ComputeResidual(pAbstractOdeSystem, timeStep, time, currentYValues, guess_perturbed);
00180             for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00181             {
00182                 residual_perturbed[i] = mResidual[i];
00183             }
00184 
00185             // compute residual_perturbed - residual
00186             double one_over_eps=1.0/epsilon;
00187             for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00188             {
00189                 mJacobian[i][global_column] = one_over_eps*(residual_perturbed[i] - residual[i]);
00190             }
00191         }
00192     }
00193 
00194 
00195 
00196 
00197 protected:
00198     void CalculateNextYValue(AbstractOdeSystem* pAbstractOdeSystem,
00199                              double timeStep,
00200                              double time,
00201                              std::vector<double>& currentYValues,
00202                              std::vector<double>& nextYValues)
00203     {
00204         // check the size of the ode system matches the solvers expected
00205         assert(mSizeOfOdeSystem == pAbstractOdeSystem->GetNumberOfStateVariables());
00206 
00207 
00208         unsigned counter = 0;
00209 //        const double eps = 1e-6 * rCurrentGuess[0]; // Our tolerance (should use min(guess) perhaps?)
00210         const double eps = 1e-6; // JonW tolerance
00211         double norm = 2*eps;
00212 
00213         std::vector<double> current_guess(mSizeOfOdeSystem);
00214         current_guess.assign(currentYValues.begin(), currentYValues.end());
00215 
00216         while (norm > eps)
00217         {
00218             // Calculate Jacobian and mResidual for current guess
00219             ComputeResidual(pAbstractOdeSystem, timeStep, time, currentYValues, current_guess);
00220             ComputeJacobian(pAbstractOdeSystem, timeStep, time, currentYValues, current_guess);
00221 //            // Update norm (our style)
00222 //            norm = ComputeNorm(mResidual);
00223 
00224             // Solve Newton linear system
00225             SolveLinearSystem();
00226 
00227             // Update norm (JonW style)
00228             norm = ComputeNorm(mUpdate);
00229 
00230             // Update current guess
00231             for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00232             {
00233                 current_guess[i] -= mUpdate[i];
00234             }
00235 
00236             counter++;
00237             assert(counter < 20); // avoid infinite loops
00238         }
00239         nextYValues.assign(current_guess.begin(), current_guess.end());
00240 
00241     }
00242 
00243 public:
00244     BackwardEulerIvpOdeSolver(unsigned sizeOfOdeSystem)
00245     {
00246         mSizeOfOdeSystem = sizeOfOdeSystem;
00247 
00248         // default epsilon
00249         mNumericalJacobianEpsilon = 1e-6;
00250         mForceUseOfNumericalJacobian = false;
00251 
00252         // allocate memory
00253         mResidual = new double[mSizeOfOdeSystem];
00254         mUpdate = new double[mSizeOfOdeSystem];
00255 
00256         mJacobian = new double*[mSizeOfOdeSystem];
00257         for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00258         {
00259             mJacobian[i] = new double[mSizeOfOdeSystem];
00260         }
00261     }
00262 
00263     ~BackwardEulerIvpOdeSolver()
00264     {
00265         // delete pointers
00266         delete[] mResidual;
00267         delete[] mUpdate;
00268 
00269         for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00270         {
00271             delete[] mJacobian[i];
00272         }
00273         delete[] mJacobian;
00274     }
00275 
00276     void SetEpsilonForNumericalJacobian(double epsilon)
00277     {
00278         assert(epsilon > 0);
00279         mNumericalJacobianEpsilon = epsilon;
00280     }
00281 
00285     void ForceUseOfNumericalJacobian()
00286     {
00287         mForceUseOfNumericalJacobian = true;
00288     }
00289 };
00290 
00291 
00292 #endif /*BACKWARDEULERIVPODESOLVER_HPP_*/

Generated on Wed Mar 18 12:51:56 2009 for Chaste by  doxygen 1.5.5