CardiacNewtonSolver.hpp
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
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;
00084
00085
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;
00090 do
00091 {
00092
00093 rCell.ComputeJacobian(time, rCurrentGuess, mJacobian);
00094
00095
00096 SolveLinearSystem();
00097
00098
00099 norm_of_update = norm_inf(mUpdate);
00100
00101
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
00112
00113
00114
00115
00116
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
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
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 {
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