MechSys  1.0
Computing library for simulations in continuum and discrete mechanics
/home/dorival/mechsys/lib/numerical/odesolver.h
Go to the documentation of this file.
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines