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 #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
00051
00052
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);
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
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
00125
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
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
00205 assert(mSizeOfOdeSystem == pAbstractOdeSystem->GetNumberOfStateVariables());
00206
00207
00208 unsigned counter = 0;
00209
00210 const double eps = 1e-6;
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
00219 ComputeResidual(pAbstractOdeSystem, timeStep, time, currentYValues, current_guess);
00220 ComputeJacobian(pAbstractOdeSystem, timeStep, time, currentYValues, current_guess);
00221
00222
00223
00224
00225 SolveLinearSystem();
00226
00227
00228 norm = ComputeNorm(mUpdate);
00229
00230
00231 for (unsigned i=0; i<mSizeOfOdeSystem; i++)
00232 {
00233 current_guess[i] -= mUpdate[i];
00234 }
00235
00236 counter++;
00237 assert(counter < 20);
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
00249 mNumericalJacobianEpsilon = 1e-6;
00250 mForceUseOfNumericalJacobian = false;
00251
00252
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
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