Chaste Release::3.1
CardiacNewtonSolver.hpp
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 #ifndef CARDIACNEWTONSOLVER_HPP_
00036 #define CARDIACNEWTONSOLVER_HPP_
00037 
00038 #include <cmath>
00039 #include "IsNan.hpp"
00040 #include "UblasCustomFunctions.hpp"
00041 #include "AbstractBackwardEulerCardiacCell.hpp"
00042 #include "Warnings.hpp"
00043 
00056 template<unsigned SIZE, typename CELLTYPE>
00057 class CardiacNewtonSolver
00058 {
00059 public:
00065     static CardiacNewtonSolver<SIZE, CELLTYPE>* Instance()
00066     {
00067         static CardiacNewtonSolver<SIZE, CELLTYPE> inst;
00068         return &inst;
00069     }
00070 
00078     void Solve(CELLTYPE &rCell,
00079                double time,
00080                double rCurrentGuess[SIZE])
00081     {
00082         unsigned counter = 0;
00083         const double eps = 1e-6; // JonW tolerance
00084 
00085         // check that the initial guess that was given gives a valid residual
00086         rCell.ComputeResidual(time, rCurrentGuess, mResidual.data());
00087         double norm_of_residual = norm_inf(mResidual);
00088         assert(!std::isnan(norm_of_residual));
00089         double norm_of_update = 0.0; //Properly initialised in the loop
00090         do
00091         {
00092             // Calculate Jacobian for current guess
00093             rCell.ComputeJacobian(time, rCurrentGuess, mJacobian);
00094 
00095             // Solve Newton linear system for mUpdate, given mJacobian and mResidual
00096             SolveLinearSystem();
00097 
00098             // Update norm (JonW style)
00099             norm_of_update = norm_inf(mUpdate);
00100 
00101             // Update current guess and recalculate residual
00102             for (unsigned i=0; i<SIZE; i++)
00103             {
00104                 rCurrentGuess[i] -= mUpdate[i];
00105             }
00106             double norm_of_previous_residual = norm_of_residual;
00107             rCell.ComputeResidual(time, rCurrentGuess, mResidual.data());
00108             norm_of_residual = norm_inf(mResidual);
00109             if (norm_of_residual > norm_of_previous_residual && norm_of_update > eps)
00110             {
00111                 //Second part of guard:
00112                 //Note that if norm_of_update < eps (converged) then it's
00113                 //likely that both the residual and the previous residual were
00114                 //close to the root.
00115 
00116                 //Work out where the biggest change in the guess has happened.
00117                 double relative_change_max = 0.0;
00118                 unsigned relative_change_direction = 0;
00119                 for (unsigned i=0; i<SIZE; i++)
00120                 {
00121                     double relative_change = fabs(mUpdate[i]/rCurrentGuess[i]);
00122                     if (relative_change > relative_change_max)
00123                     {
00124                         relative_change_max = relative_change;
00125                         relative_change_direction = i;
00126                     }
00127                 }
00128 
00129                 if (relative_change_max > 1.0)
00130                 {
00131                     //Only walk 0.2 of the way in that direction (put back 0.8)
00132                     rCurrentGuess[relative_change_direction] += 0.8*mUpdate[relative_change_direction];
00133                     rCell.ComputeResidual(time, rCurrentGuess, mResidual.data());
00134                     norm_of_residual = norm_inf(mResidual);
00135                     WARNING("Residual increasing and one direction changing radically - back tracking in that direction");
00136                 }
00137             }
00138             counter++;
00139 
00140             // avoid infinite loops
00141             if (counter > 15)
00142             {
00143 #define COVERAGE_IGNORE
00144                 EXCEPTION("Newton method diverged in CardiacNewtonSolver::Solve()");
00145 #undef COVERAGE_IGNORE
00146             }
00147         }
00148         while (norm_of_update > eps);
00149 
00150 #define COVERAGE_IGNORE
00151 #ifndef NDEBUG
00152         if (norm_of_residual > 2e-10)
00153         { //This line is for correlation - in case we use norm_of_residual as convergence criterion
00154             WARN_ONCE_ONLY("Newton iteration terminated because update vector norm is small, but residual norm is not small.");
00155         }
00156 #endif // NDEBUG
00157 #undef COVERAGE_IGNORE
00158     }
00159 
00160 protected:
00162     CardiacNewtonSolver()
00163     {}
00165     CardiacNewtonSolver(const CardiacNewtonSolver<SIZE, CELLTYPE>&);
00167     CardiacNewtonSolver<SIZE, CELLTYPE>& operator= (const CardiacNewtonSolver<SIZE, CELLTYPE>&);
00168 
00178     void SolveLinearSystem()
00179     {
00180         for (unsigned i=0; i<SIZE; i++)
00181         {
00182             for (unsigned ii=i+1; ii<SIZE; ii++)
00183             {
00184                 double fact = mJacobian[ii][i]/mJacobian[i][i];
00185                 for (unsigned j=i; j<SIZE; j++)
00186                 {
00187                     mJacobian[ii][j] -= fact*mJacobian[i][j];
00188                 }
00189                 mResidual[ii] -= fact*mResidual[i];
00190             }
00191         }
00192         for (unsigned i=SIZE; i-- > 0; )
00193         {
00194             mUpdate[i] = mResidual[i];
00195             for (unsigned j=i+1; j<SIZE; j++)
00196             {
00197                 mUpdate[i] -= mJacobian[i][j]*mUpdate[j];
00198             }
00199             mUpdate[i] /= mJacobian[i][i];
00200         }
00201     }
00202 
00203 private:
00205     c_vector<double, SIZE> mResidual;
00207     double mJacobian[SIZE][SIZE];
00209     c_vector<double, SIZE> mUpdate;
00210 };
00211 
00212 #endif /*CARDIACNEWTONSOLVER_HPP_*/