00001 #ifndef INDII_ML_ODE_NUMERICALSOLVER_HPP 00002 #define INDII_ML_ODE_NUMERICALSOLVER_HPP 00003 00004 #include "../aux/vector.hpp" 00005 00006 #include <gsl/gsl_odeiv.h> 00007 00008 namespace indii { 00009 namespace ml { 00010 namespace ode { 00011 00012 /** 00013 * Type of functions for calculating derivatives. 00014 */ 00015 typedef int df_t(double t, const double y[], double dydt[], void* params); 00016 00017 /** 00018 * Abstract numerical solver for a system of differential equations. 00019 * 00020 * @author Lawrence Murray <lawrence@indii.org> 00021 * @version $Rev: 521 $ 00022 * @date $Date: 2008-08-16 16:58:51 +0100 (Sat, 16 Aug 2008) $ 00023 */ 00024 class NumericalSolver { 00025 public: 00026 /** 00027 * Constructor. 00028 * 00029 * @param dimensions Dimensionality of the state space. 00030 * 00031 * The time is initialised to zero, but the state is uninitialised 00032 * and should be set with setVariable() or setState(). 00033 */ 00034 NumericalSolver(const unsigned int dimensions); 00035 00036 /** 00037 * Constructor. 00038 * 00039 * @param y0 Initial state. 00040 * 00041 * The time is initialised to zero and the state to that given. 00042 */ 00043 NumericalSolver(const indii::ml::aux::vector& y0); 00044 00045 /** 00046 * Destructor. 00047 */ 00048 virtual ~NumericalSolver(); 00049 00050 /** 00051 * Get dimensionality of the state space. 00052 */ 00053 unsigned int getDimensions(); 00054 00055 /** 00056 * Get the current time. 00057 */ 00058 double getTime(); 00059 00060 /** 00061 * Set the current time. 00062 * 00063 * @param t Time. 00064 * 00065 * @see setVariable() or setState() to update the state for the new 00066 * time also. 00067 */ 00068 void setTime(const double t); 00069 00070 /** 00071 * Get the value of a state variable at the current time. 00072 * 00073 * @param index Index of the state variable to retrieve. 00074 * 00075 * @return The value of the state variable at the current time. 00076 */ 00077 double getVariable(const unsigned int index); 00078 00079 /** 00080 * Set the value of a state variable. 00081 * 00082 * @param index Index of the state variable to set. 00083 * @param value The value to which to set the state variable. 00084 */ 00085 void setVariable(const unsigned int index, const double value); 00086 00087 /** 00088 * Get the state at the current time. 00089 * 00090 * @return Current state of the system. 00091 */ 00092 indii::ml::aux::vector getState(); 00093 00094 /** 00095 * Set the state at the current time. 00096 * 00097 * @param y New state of the system. 00098 */ 00099 void setState(const indii::ml::aux::vector& y); 00100 00101 /** 00102 * Advance the system one time step. The size of the step is chosen 00103 * to be optimal given the provided error bounds. State variables 00104 * are updated to this new time after completion of the step. 00105 * 00106 * @param upper Upper bound on time. This must be greater than the 00107 * current time. The current time is guaranteed not to exceed this 00108 * value after the step is complete. It may equal this time, and 00109 * indeed if step() is continuously called with the same upper bound 00110 * it will eventually do so. 00111 * 00112 * @return The new current time. 00113 */ 00114 virtual double step(double upper); 00115 00116 /** 00117 * Advance the system to a particular time. 00118 * 00119 * This convenience method internally calls step() as many times as 00120 * necessary to advance the system to the given time. This is useful 00121 * if fixed time steps are required rather than the variable steps 00122 * taken by step(). 00123 * 00124 * @param to The time to which to advance the system. This must be greater 00125 * than the current time. The current time is guaranteed to match this value 00126 * at the end of the call. 00127 */ 00128 void stepTo(double to); 00129 00130 /** 00131 * Rewind the system one time step. This works in the same fashion 00132 * at step(), except that the step is backwards in time. 00133 * 00134 * @param lower Lower bound on time. This must be less than the 00135 * current time. The current time is guaranteed not to be below this 00136 * value after the step is complete. It may equal this time, and 00137 * indeed if stepBack() is continuously called with the same lower 00138 * bound it will eventually do so. 00139 * 00140 * @return The new current time. 00141 */ 00142 virtual double stepBack(double lower); 00143 00144 /** 00145 * Rewind the system to a specific time. 00146 * 00147 * This convenience method internally calls stepBack() as many times 00148 * as necessary to rewind the system to a given point in time. This 00149 * is useful if fixed time steps are required rather than the 00150 * variable steps taken by stepBack(). 00151 * 00152 * @param to The time to which to rewind the system. This must be 00153 * less than the current time. The current time is guaranteed to 00154 * match this value at the end of the call. 00155 */ 00156 void stepBackTo(double to); 00157 00158 /** 00159 * Set the error bounds. Smaller error bounds produce more accurate 00160 * estimates, but reduce the size of time steps, so that more steps 00161 * are required in order to estimate functions over the same length 00162 * of time. 00163 * 00164 * @param maxAbsoluteError The maximum permitted absolute error of 00165 * estimated values compared to real values. 00166 * @param maxRelativeError The maximum permitted relative error of 00167 * estimated values compared to real values. 00168 */ 00169 void setErrorBounds(double maxAbsoluteError = 1e-6, 00170 double maxRelativeError = 1e-6); 00171 00172 /** 00173 * Set the proposed size for the next time step. This is useful for 00174 * handling discontinuities, where step() may be called to advance 00175 * to the discontinuity, then setStepSize() used to propose a small 00176 * step size ensuring that the discontinuity is tiptoed across 00177 * rather than leapt. 00178 * 00179 * It should be called immediately before any call to step() or 00180 * stepTo(). Any other methods called in between may themselves adjust the 00181 * proposed step size. 00182 * 00183 * @param stepSize The proposed time step size for the next call to 00184 * step(). 00185 * 00186 * Note that this sets only the proposed step size -- it will be 00187 * optimised, within some constraints, relative to the permitted 00188 * error bounds. 00189 */ 00190 void setStepSize(double stepSize); 00191 00192 /** 00193 * Get the proposed size for the next time step. 00194 * 00195 * @return The proposed time step size for the next call to step(). 00196 */ 00197 double getStepSize(); 00198 00199 /** 00200 * Set the suggested step size. The step size is set to this 00201 * immediately, and subsequently after any calls to 00202 * setDiscontinuity(). 00203 * 00204 * @param stepSize The suggested step size. 00205 * 00206 * Note that this sets only the proposed step size -- it will be 00207 * optimised, within some constraints, relative to the permitted 00208 * error bounds. 00209 */ 00210 void setSuggestedStepSize(double stepSize = 1.0e-7); 00211 00212 /** 00213 * Get the suggested step size. 00214 * 00215 * @return The suggested step size. 00216 */ 00217 double getSuggestedStepSize(); 00218 00219 /** 00220 * Set the maximum step size. 00221 * 00222 * @param stepSize The maximum step size. 00223 * 00224 * This is useful for bounding the step size on schemes which may propose 00225 * steps that, for some models, produce inf or nan derivative calculations. 00226 * Empirically, we particularly observe this with implicit schemes. By 00227 * default the step size is not bound. 00228 */ 00229 void setMaxStepSize(double stepSize = 0.0); 00230 00231 /** 00232 * Get the maximum step size. 00233 * 00234 * @return The maximum step size. 00235 */ 00236 double getMaxStepSize(); 00237 00238 /** 00239 * Indicates a discontinuity at the current time. Internally, this simply 00240 * calls setStepSize() with the suggested step size. 00241 */ 00242 void setDiscontinuity(); 00243 00244 /** 00245 * Set the stepping method. 00246 * 00247 * @param stepType The stepping method. 00248 * 00249 * This allows modification of the underlying numerical scheme used by 00250 * the solver, as specified by the GSL. For advanced users only. 00251 */ 00252 void setStepType(const gsl_odeiv_step_type* stepType); 00253 00254 /** 00255 * Calculate derivatives for forwards step. 00256 */ 00257 virtual int calculateDerivativesForward(double t, const double y[], 00258 double dydt[]) = 0; 00259 00260 /** 00261 * Calculate derivatives for backwards step. 00262 */ 00263 virtual int calculateDerivativesBackward(double t, const double y[], 00264 double dydt[]) = 0; 00265 00266 protected: 00267 /** 00268 * Dimensionality of the system of differential equations. 00269 */ 00270 const size_t dimensions; 00271 00272 /** 00273 * \f$t\f$; the current time 00274 */ 00275 double t; 00276 00277 /** 00278 * Array of state variables of the system. 00279 */ 00280 double* y; 00281 00282 /** 00283 * Base time used for backward steps. 00284 */ 00285 double base; 00286 00287 /** 00288 * Proposed size for the next time step. 00289 */ 00290 double stepSize; 00291 00292 /** 00293 * Maximum step size. 00294 */ 00295 double maxStepSize; 00296 00297 /** 00298 * Suggested step size. 00299 */ 00300 double suggestedStepSize; 00301 00302 /** 00303 * GSL ordinary differential equations step structure. 00304 */ 00305 gsl_odeiv_step* gslStep; 00306 00307 /** 00308 * GSL ordinary differential equations control structure. 00309 */ 00310 gsl_odeiv_control* gslControl; 00311 00312 /** 00313 * GSL ordinary differential equations evolution structure. 00314 */ 00315 gsl_odeiv_evolve* gslEvolve; 00316 00317 /** 00318 * GSL system of ordinary differential equations for forward steps 00319 * in time. 00320 */ 00321 gsl_odeiv_system gslForwardSystem; 00322 00323 /** 00324 * GSL system of ordinary differential equations for backward steps 00325 * in time. 00326 */ 00327 gsl_odeiv_system gslBackwardSystem; 00328 00329 /** 00330 * Initialise the system. Should be called before any calls to 00331 * step() or stepTo(). 00332 */ 00333 void init(); 00334 00335 /** 00336 * Terminate the system. Should be called after work with the system 00337 * has been finished. Will be called by the destructor if it hasn't 00338 * been already. 00339 */ 00340 void terminate(); 00341 00342 /** 00343 * Reset the model. This is used internally to reset GSL structures when the 00344 * next step will not be a continuation of the last, usually when the time 00345 * or state is changed with a call to setTime(), setVariable() or 00346 * setState(). 00347 */ 00348 virtual void reset(); 00349 00350 private: 00351 /** 00352 * Function for calculating derivatives when moving forward in time. 00353 * 00354 * Required by gsl_odeiv_system struct of GSL, which is in turn 00355 * passed to the gsl_odeiv_evolve_apply() function. Acts as a 00356 * wrapper to call the calculateForwardDerivatives() function of the 00357 * particular NumericalSolver object. 00358 * 00359 * @see gsl_odeiv_system data type of GSL. 00360 * @see gsl_odeiv_evolve_apply() of GSL. 00361 */ 00362 static int gslForwardFunction(double t, const double y[], double dydt[], 00363 void* params); 00364 00365 /** 00366 * Function for calculating derivatives when moving backward in time. 00367 * Acts as a wrapper to call the calculateBackwardDerivatives() function 00368 * of the particular NumericalSolver object. 00369 * 00370 * @see gslForwardFunction() 00371 */ 00372 static int gslBackwardFunction(double t, const double y[], double dydt[], 00373 void* params); 00374 00375 }; 00376 00377 00378 } 00379 } 00380 } 00381 00382 inline unsigned int indii::ml::ode::NumericalSolver::getDimensions() { 00383 return dimensions; 00384 } 00385 00386 inline double indii::ml::ode::NumericalSolver::getTime() { 00387 return t; 00388 } 00389 00390 inline double indii::ml::ode::NumericalSolver::getVariable( 00391 const unsigned int index) { 00392 /* pre-condition */ 00393 assert (index < dimensions); 00394 00395 return y[index]; 00396 } 00397 00398 inline indii::ml::aux::vector indii::ml::ode::NumericalSolver::getState() { 00399 indii::ml::aux::vector y(dimensions); 00400 unsigned int i; 00401 00402 for (i = 0; i < dimensions; i++) { 00403 y(i) = this->y[i]; 00404 } 00405 return y; 00406 } 00407 00408 inline double indii::ml::ode::NumericalSolver::getStepSize() { 00409 return stepSize; 00410 } 00411 00412 inline double indii::ml::ode::NumericalSolver::getSuggestedStepSize() { 00413 return suggestedStepSize; 00414 } 00415 00416 inline double indii::ml::ode::NumericalSolver::getMaxStepSize() { 00417 return maxStepSize; 00418 } 00419 00420 #endif 00421
1.5.3