![]() |
MechSys
1.0
Computing library for simulations in continuum and discrete mechanics
|
00001 /************************************************************************ 00002 * MechSys - Open Library for Mechanical Systems * 00003 * Copyright (C) 2005 Dorival M. Pedroso, Raul Durand * 00004 * Copyright (C) 2009 Sergio Galindo * 00005 * * 00006 * This program is free software: you can redistribute it and/or modify * 00007 * it under the terms of the GNU General Public License as published by * 00008 * the Free Software Foundation, either version 3 of the License, or * 00009 * any later version. * 00010 * * 00011 * This program is distributed in the hope that it will be useful, * 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00014 * GNU General Public License for more details. * 00015 * * 00016 * You should have received a copy of the GNU General Public License * 00017 * along with this program. If not, see <http://www.gnu.org/licenses/> * 00018 ************************************************************************/ 00019 00020 #ifndef MECHSYS_ODE_H 00021 #define MECHSYS_ODE_H 00022 00023 // STL 00024 #include <cmath> // for sqrt 00025 #include <cfloat> // for DBL_EPSILON 00026 #include <cstring> // for strcmp 00027 #include <sstream> // for ostringstream 00028 00029 // GSL 00030 #include <gsl/gsl_errno.h> // for GSL_SUCCESS 00031 #include <gsl/gsl_odeiv.h> 00032 00033 // MechSys 00034 #include <mechsys/util/fatal.h> 00035 #include <mechsys/util/util.h> 00036 #include <mechsys/util/numstreams.h> 00037 00038 namespace Numerical 00039 { 00040 00041 template<typename Instance> 00042 class ODESolver 00043 { 00044 public: 00045 // Typedefs 00046 typedef int (Instance::*pFun) (double t, double const Y[], double dYdt[]); 00047 typedef void (Instance::*pUpFun) (double t, double Y[]); 00048 00049 // Constructor 00050 ODESolver (Instance * p2Inst, pFun p2Fun, size_t NEq, char const * Scheme="RKDP89", 00051 double stol=1.0e-6, double h=1.0, double EPSREL=DBL_EPSILON); 00052 00053 // Destructor 00054 ~ODESolver (); 00055 00056 // Methods 00057 void Evolve (double tf); 00058 void Evolve (double tf, double dtOut, char const * Filename); 00059 00060 // Data 00061 double t; 00062 double * Y; 00063 pUpFun UpFun; 00064 String Scheme; 00065 00066 // ME data 00067 double STOL; 00068 size_t MaxSS; 00069 double mMin; 00070 double mMax; 00071 double T; 00072 double dTini; 00073 double dT; 00074 size_t SS; 00075 size_t SSs; 00076 00077 // Internal methods 00078 int _call_fun (double time, double const y[], double dydt[]) { return (_p2inst->*_p2fun)(time, y, dydt); } 00079 00080 private: 00081 // Auxiliary variables 00082 size_t _neq; 00083 Instance * _p2inst; 00084 pFun _p2fun; 00085 double _h; 00086 gsl_odeiv_step * _s; 00087 gsl_odeiv_control * _c; 00088 gsl_odeiv_evolve * _e; 00089 gsl_odeiv_system _sys; 00090 00091 // for FE/ME 00092 bool _FE; 00093 bool _ME; 00094 double * yFE; 00095 double * yME; 00096 double * dy1; 00097 double * dy2; 00098 }; 00099 00102 template<typename Instance> 00103 int __ode_call_fun__ (double time, double const y[], double dydt[], void * not_used_for_params) 00104 { 00105 ODESolver<Instance> * p2inst = static_cast<ODESolver<Instance>*>(not_used_for_params); 00106 return p2inst->_call_fun (time, y, dydt); 00107 } 00108 00109 00110 #ifdef USE_BOOST_PYTHON 00111 00112 class PyODESolver 00113 { 00114 public: 00116 PyODESolver (BPy::object & TheInstance, char const * ClassName, char const * MethodName) 00117 : Solver(NULL), Instance(TheInstance) 00118 { 00119 Util::GetPyMethod (ClassName, MethodName, Method, "__main__"); 00120 } 00121 00123 ~PyODESolver () { if (Solver!=NULL) delete Solver; } 00124 00126 void Init (double t0, BPy::list const & Y0, char const * Scheme="RKDP89", double stol=1.0e-6, double h=1.0, double EPSREL=DBL_EPSILON) 00127 { 00128 NEq = BPy::len (Y0); 00129 if (Solver!=NULL) delete Solver; 00130 Solver = new ODESolver<PyODESolver> (this, &PyODESolver::Fun, NEq, Scheme, stol, h, EPSREL); 00131 Solver->t = t0; 00132 for (size_t i=0; i<NEq; ++i) 00133 { 00134 double val = BPy::extract<double>(Y0[i])(); 00135 y .append (val); 00136 dydt.append (0.0); 00137 Solver->Y[i] = val; 00138 } 00139 } 00140 00142 void Evolve (double tf) 00143 { 00144 if (Solver==NULL) throw new Fatal("PyODESolver::Evolve: Init must be called first"); 00145 Solver->Evolve (tf); 00146 for (size_t i=0; i<NEq; ++i) y[i] = Solver->Y[i]; 00147 } 00148 00150 void EvolveOut (double tf, double dtOut, char const * Filename) 00151 { 00152 if (Solver==NULL) throw new Fatal("PyODESolver::EvolveOut: Init must be called first"); 00153 Solver->Evolve (tf, dtOut, Filename); 00154 for (size_t i=0; i<NEq; ++i) y[i] = Solver->Y[i]; 00155 } 00156 00158 int Fun (double t, double const Y[], double dYdt[]) 00159 { 00160 for (size_t i=0; i<NEq; ++i) y[i] = Y[i]; 00161 Method (Instance, t,y,dydt); 00162 for (size_t i=0; i<NEq; ++i) dYdt[i] = BPy::extract<double>(dydt[i])(); 00163 return GSL_SUCCESS; 00164 } 00165 00166 // Data 00167 size_t NEq; 00168 ODESolver<PyODESolver> * Solver; 00169 BPy::object Instance; 00170 BPy::object Method; 00171 BPy::list y; 00172 BPy::list dydt; 00173 }; 00174 00175 #endif 00176 00177 00179 00180 00181 template<typename Instance> 00182 inline ODESolver<Instance>::ODESolver (Instance * p2Inst, pFun p2Fun, size_t NEq, char const * sch, double stol, double h, double EPSREL) 00183 : t (-1.0), 00184 Y (NULL), 00185 UpFun (NULL), 00186 Scheme (sch), 00187 STOL (stol), 00188 MaxSS (2000), 00189 mMin (0.2), 00190 mMax (5.0), 00191 T (0.0), 00192 dTini (h), 00193 dT (h), 00194 SS (0), 00195 SSs (0), 00196 _neq (NEq), 00197 _p2inst (p2Inst), 00198 _p2fun (p2Fun), 00199 _h (h), 00200 _s (NULL), 00201 _c (NULL), 00202 _e (NULL), 00203 _FE (false), 00204 _ME (false), 00205 yFE (NULL), 00206 yME (NULL), 00207 dy1 (NULL), 00208 dy2 (NULL) 00209 { 00210 if (Scheme=="FE") 00211 { 00212 _FE = true; 00213 dy1 = new double [_neq]; 00214 } 00215 else if (Scheme=="ME" || Scheme=="RK12") 00216 { 00217 _ME = true; 00218 yFE = new double [_neq]; 00219 yME = new double [_neq]; 00220 dy1 = new double [_neq]; 00221 dy2 = new double [_neq]; 00222 } 00223 else 00224 { 00225 // set scheme 00226 gsl_odeiv_step_type const * scheme = gsl_odeiv_step_rk8pd; 00227 if (Scheme=="RK23" ) { scheme = gsl_odeiv_step_rk2; } 00228 else if (Scheme=="RK4" ) { scheme = gsl_odeiv_step_rk4; } 00229 else if (Scheme=="RKF45" ) { scheme = gsl_odeiv_step_rkf45; } 00230 else if (Scheme=="RKCK45") { scheme = gsl_odeiv_step_rkck; } 00231 else if (Scheme=="RKDP89") { scheme = gsl_odeiv_step_rk8pd; } 00232 else if (Scheme=="RK2I" ) { scheme = gsl_odeiv_step_rk2imp; } 00233 else if (Scheme=="RK4I" ) { scheme = gsl_odeiv_step_rk4imp; } 00234 else if (Scheme=="G1" ) { scheme = gsl_odeiv_step_gear1; } 00235 else if (Scheme=="G2" ) { scheme = gsl_odeiv_step_gear2; } 00236 else throw new Fatal("ODESolver::ODESolver: Scheme %s is invalid. Valid ones are: FE, ME/RK12, RK23, RK4, RKF45, RKCK45, RKDP89, RK2I, RK4I, G1, G2",Scheme.CStr()); 00237 00238 // allocate auxiliary variables 00239 _s = gsl_odeiv_step_alloc (scheme, _neq); 00240 _c = gsl_odeiv_control_y_new (stol, EPSREL); 00241 _e = gsl_odeiv_evolve_alloc (_neq); 00242 _sys.function = __ode_call_fun__<Instance>; 00243 _sys.jacobian = NULL; 00244 _sys.dimension = _neq; 00245 _sys.params = this; 00246 } 00247 00248 // allocate array 00249 Y = new double [_neq]; 00250 } 00251 00252 template<typename Instance> 00253 inline ODESolver<Instance>::~ODESolver () 00254 { 00255 if (Y !=NULL) delete [] Y; 00256 if (yFE!=NULL) delete [] yFE; 00257 if (yME!=NULL) delete [] yME; 00258 if (dy1!=NULL) delete [] dy1; 00259 if (dy2!=NULL) delete [] dy2; 00260 if (_e !=NULL) gsl_odeiv_evolve_free (_e); 00261 if (_c !=NULL) gsl_odeiv_control_free (_c); 00262 if (_s !=NULL) gsl_odeiv_step_free (_s); 00263 } 00264 00265 template<typename Instance> 00266 inline void ODESolver<Instance>::Evolve (double tf) 00267 { 00268 // check 00269 if (t<0.0) throw new Fatal("ODESolver::Evolve: Initial values (t, Y) must be set first"); 00270 00271 // solve 00272 if (_FE) 00273 { 00274 while (t<tf) 00275 { 00276 double dt = (t+_h>tf ? tf-t : _h); 00277 (_p2inst->*_p2fun) (t, Y, dy1); 00278 for (size_t j=0; j<_neq; ++j) Y[j] += dy1[j]*dt; 00279 t += dt; 00280 } 00281 } 00282 else if (_ME) 00283 { 00284 // for each pseudo time T 00285 double Dtf = tf - t; 00286 T = 0.0; 00287 dT = dTini; 00288 SS = 0; 00289 SSs = 0; 00290 for (SS=0; SS<MaxSS; ++SS) 00291 { 00292 // exit point 00293 if (T>=1.0) break; 00294 00295 // FE increments 00296 double dt = dT*Dtf; 00297 (_p2inst->*_p2fun) (t, Y, dy1); 00298 for (size_t j=0; j<_neq; ++j) yFE[j] = Y[j] + dy1[j]*dt; 00299 00300 // ME increments and local error 00301 (_p2inst->*_p2fun) (t+dt, yFE, dy2); 00302 double norm_yME = 0.0; 00303 double norm_err = 0.0; 00304 for (size_t j=0; j<_neq; ++j) 00305 { 00306 yME[j] = Y[j] + 0.5*(dy1[j]+dy2[j])*dt; 00307 norm_yME += yME[j]*yME[j]; 00308 norm_err += (yME[j]-yFE[j])*(yME[j]-yFE[j]); 00309 } 00310 00311 // local error estimate 00312 double err = sqrt(norm_err)/(1.0+sqrt(norm_yME)); 00313 00314 // step multiplier 00315 double m = (err>0.0 ? 0.9*sqrt(STOL/err) : mMax); 00316 00317 // update 00318 if (err<STOL) 00319 { 00320 // update state 00321 SSs++; 00322 T += dT; 00323 t += dt; 00324 for (size_t j=0; j<_neq; ++j) Y[j] = yME[j]; 00325 00326 // callback 00327 if (UpFun!=NULL) (_p2inst->*UpFun) (t, Y); 00328 00329 // limit change on stepsize 00330 if (m>mMax) m = mMax; 00331 } 00332 else if (m<mMin) m = mMin; 00333 00334 // change next step size 00335 dT = m * dT; 00336 00337 // check for last increment 00338 if (dT>1.0-T) dT = 1.0-T; 00339 } 00340 if (SS>=MaxSS) throw new Fatal("ODESolver::Evolve: RK12 (Modified-Euler) did not converge after %d substeps",SS); 00341 } 00342 else 00343 { 00344 while (t<tf) 00345 { 00346 int status = gsl_odeiv_evolve_apply (_e, _c, _s, &_sys, &t, tf, &_h, Y); 00347 if (status!=GSL_SUCCESS) throw new Fatal("ODESolver::Evolve: gsl_odeiv_evolve_apply failed. status=%d",status); 00348 //if (h<hmin) { h = hmin; } 00349 } 00350 } 00351 } 00352 00353 template<typename Instance> 00354 inline void ODESolver<Instance>::Evolve (double tf, double dtOut, char const * Filename) 00355 { 00356 // header and first output 00357 String buf; 00358 std::ostringstream oss; 00359 oss<<Util::_8s<<"t"; for (size_t i=0; i<_neq; ++i) { buf.Printf("y%zd",i); oss<<Util::_8s<<buf; } oss<<std::endl; 00360 oss<<Util::_8s<<t; for (size_t i=0; i<_neq; ++i) { oss<<Util::_8s<<Y[i]; } oss<<std::endl; 00361 00362 // solve 00363 double tout = t + dtOut; 00364 while (t<tf) 00365 { 00366 Evolve (tout); 00367 tout += dtOut; 00368 if (tout>tf) tout = tf; 00369 oss<<Util::_8s<<t; for (size_t i=0; i<_neq; ++i) { oss<<Util::_8s<<Y[i]; } oss<<std::endl; 00370 } 00371 00372 // file 00373 std::ofstream of(Filename, std::ios::out); 00374 of << oss.str(); 00375 of.close(); 00376 printf("File <%s%s%s> written\n", TERM_CLR_BLUE_H, Filename, TERM_RST); 00377 } 00378 00379 }; // namespace Numerical 00380 00381 #endif // MECHSYS_ODE_H