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 #ifndef CARDIACNEWTONSOLVER_HPP_
00029 #define CARDIACNEWTONSOLVER_HPP_
00030
00031 #include <cmath>
00032 #include "IsNan.hpp"
00033 #include "UblasCustomFunctions.hpp"
00034 #include "AbstractBackwardEulerCardiacCell.hpp"
00035 #include "Warnings.hpp"
00036
00049 template<unsigned SIZE>
00050 class CardiacNewtonSolver
00051 {
00052 public:
00058 static CardiacNewtonSolver<SIZE>* Instance()
00059 {
00060 static CardiacNewtonSolver<SIZE> inst;
00061 return &inst;
00062 }
00063
00071 void Solve(AbstractBackwardEulerCardiacCell<SIZE> &rCell,
00072 double time,
00073 double rCurrentGuess[SIZE])
00074 {
00075 unsigned counter = 0;
00076 const double eps = 1e-6;
00077
00078
00079 rCell.ComputeResidual(time, rCurrentGuess, mResidual.data());
00080 double norm_of_residual = norm_inf(mResidual);
00081 assert(!std::isnan(norm_of_residual));
00082 double norm_of_update=0.0;
00083 do
00084 {
00085
00086 rCell.ComputeJacobian(time, rCurrentGuess, mJacobian);
00087
00088
00089 SolveLinearSystem();
00090
00091
00092 norm_of_update = norm_inf(mUpdate);
00093
00094
00095 double norm_of_new_guess=0.0;
00096 for (unsigned i=0; i<SIZE; i++)
00097 {
00098 rCurrentGuess[i] -= mUpdate[i];
00099 norm_of_new_guess += rCurrentGuess[i];
00100 }
00101 double norm_of_previous_residual = norm_of_residual;
00102 rCell.ComputeResidual(time, rCurrentGuess, mResidual.data());
00103 norm_of_residual=norm_inf(mResidual);
00104 if (norm_of_residual > norm_of_previous_residual && norm_of_update > eps)
00105 {
00106
00107
00108
00109
00110
00111
00112 double relative_change_max=0.0;
00113 unsigned relative_change_direction=0;
00114 for (unsigned i=0; i<SIZE; i++)
00115 {
00116 double relative_change=fabs(mUpdate[i])/fabs(rCurrentGuess[i]);
00117 if (relative_change > relative_change_max)
00118 {
00119 relative_change_max = relative_change;
00120 relative_change_direction = i;
00121 }
00122 }
00123
00124 if(relative_change_max > 1.0)
00125 {
00126
00127 rCurrentGuess[relative_change_direction] += 0.8*mUpdate[relative_change_direction];
00128 rCell.ComputeResidual(time, rCurrentGuess, mResidual.data());
00129 norm_of_residual=norm_inf(mResidual);
00130 WARNING("Residual increasing and one direction changing radically - back tracking in that direction");
00131 }
00132 }
00133 counter++;
00134
00135
00136
00137 if (counter > 15)
00138 {
00139 TERMINATE("Newton method diverged in CardiacNewtonSolver::Solve()");
00140 }
00141 }
00142 while (norm_of_update > eps);
00143 assert(norm_of_residual < 2e-10);
00144 }
00145
00146
00147
00148
00149
00150 protected:
00152 CardiacNewtonSolver()
00153 {}
00155 CardiacNewtonSolver(const CardiacNewtonSolver<SIZE>&);
00157 CardiacNewtonSolver<SIZE>& operator= (const CardiacNewtonSolver<SIZE>&);
00158
00168 void SolveLinearSystem()
00169 {
00170 for (unsigned i=0; i<SIZE; i++)
00171 {
00172 for (unsigned ii=i+1; ii<SIZE; ii++)
00173 {
00174 double fact = mJacobian[ii][i]/mJacobian[i][i];
00175 for (unsigned j=i; j<SIZE; j++)
00176 {
00177 mJacobian[ii][j] -= fact*mJacobian[i][j];
00178 }
00179 mResidual[ii] -= fact*mResidual[i];
00180 }
00181 }
00182
00183 for (int i=SIZE-1; i>=0; i--)
00184 {
00185 mUpdate[i] = mResidual[i];
00186 for (unsigned j=i+1; j<SIZE; j++)
00187 {
00188 mUpdate[i] -= mJacobian[i][j]*mUpdate[j];
00189 }
00190 mUpdate[i] /= mJacobian[i][i];
00191 }
00192 }
00193
00194 private:
00196 c_vector<double, SIZE> mResidual;
00198 double mJacobian[SIZE][SIZE];
00200 c_vector<double, SIZE> mUpdate;
00201 };
00202
00203 #endif