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 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)