gsl_astep.h

00001 /*
00002   -------------------------------------------------------------------
00003   
00004   Copyright (C) 2006, 2007, 2008, Andrew W. Steiner
00005   
00006   This file is part of O2scl.
00007   
00008   O2scl is free software; you can redistribute it and/or modify
00009   it under the terms of the GNU General Public License as published by
00010   the Free Software Foundation; either version 3 of the License, or
00011   (at your option) any later version.
00012   
00013   O2scl is distributed in the hope that it will be useful,
00014   but WITHOUT ANY WARRANTY; without even the implied warranty of
00015   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016   GNU General Public License for more details.
00017   
00018   You should have received a copy of the GNU General Public License
00019   along with O2scl. If not, see <http://www.gnu.org/licenses/>.
00020 
00021   -------------------------------------------------------------------
00022 */
00023 
00024 #ifndef O2SCL_GSL_ASTEP_H
00025 #define O2SCL_GSL_ASTEP_H
00026 
00027 #include <gsl/gsl_math.h>
00028 #include <gsl/gsl_odeiv.h>
00029 
00030 #include <o2scl/adapt_step.h>
00031 
00032 #ifndef DOXYGENP
00033 namespace o2scl {
00034 #endif
00035 
00036   /** 
00037       \brief Adaptive ODE stepper (GSL)
00038       
00039       To modify the ODE stepper which is used, use the
00040       adapt_step::set_step().
00041 
00042       \todo Finish implementing the scaled "control"
00043       
00044       \future Fix so that memory allocation/deallocation is performed only
00045       when necessary
00046       \future Allow user to find out how many steps were taken, etc.
00047 
00048   */
00049   template<class param_t, class func_t=ode_funct<param_t>, 
00050     class vec_t=ovector_view,
00051     class alloc_vec_t=ovector, class alloc_t=ovector_alloc> class gsl_astep : 
00052   public adapt_step<param_t,func_t,vec_t,alloc_vec_t,alloc_t> {
00053     
00054 #ifndef DOXYGEN_INTERNAL
00055     
00056     protected:
00057     
00058     /// Size of allocated vectors
00059     size_t ndim;
00060     
00061     /// Memory allocator for objects of type \c alloc_vec_t
00062     alloc_t ao;
00063 
00064     /// Number of scalings
00065     size_t sdim;
00066     
00067     /// Scalings
00068     double *scale_abs;
00069 
00070     /** 
00071         \brief The evolution object for \ref gsl_astep 
00072         [protected subclass]
00073     */
00074     class gsl_odeiv_evolve {
00075     public:
00076         
00077       /// Number of differential equations
00078       size_t dimension;
00079 
00080       /// The values of the functions
00081       alloc_vec_t y0;
00082       /// The uncertainty
00083       alloc_vec_t yerr;
00084       /// The input derivatives
00085       alloc_vec_t dydt_in;
00086       /// The output derivatives
00087       alloc_vec_t dydt_out;
00088       /// Size of the memory allocation
00089       size_t ndim;
00090       
00091       /// The size of the last step
00092       double last_step;
00093       /// The number of steps (?)
00094       unsigned long int count;
00095       /// The number of failed steps
00096       unsigned long int failed_steps;
00097     };
00098 
00099     /// Automatically adjust step-size
00100     int hadjust(size_t dim, unsigned int ord, const vec_t &y,
00101                 vec_t &yerr, vec_t &yp, double *h) 
00102     {
00103         
00104       gsl_ode_control *state = &con;
00105         
00106       const double eps_abs = state->eps_abs;
00107       const double eps_rel = state->eps_rel;
00108       const double a_y     = state->a_y;
00109       const double a_dydt  = state->a_dydt;
00110         
00111       const double S = 0.9;
00112       const double h_old = *h;
00113       
00114       double rmax = DBL_MIN;
00115       size_t i;
00116         
00117       for(i=0; i<dim; i++) {
00118         double D0;
00119         if (state->standard || sdim==0) {
00120           D0=eps_rel*(a_y*fabs(y[i])+a_dydt*fabs(h_old*yp[i]))+eps_abs;
00121         } else {
00122           D0=eps_rel*(a_y*fabs(y[i])+a_dydt*fabs(h_old*yp[i]))+
00123             eps_abs*scale_abs[i%sdim];
00124         }
00125         const double r=fabs(yerr[i]) / fabs(D0);
00126         rmax = GSL_MAX_DBL(r, rmax);
00127       }
00128         
00129       if(rmax > 1.1) {
00130 
00131         /* decrease step, no more than factor of 5, but a fraction S more
00132            than scaling suggests (for better accuracy) */
00133 
00134         double r =  S / pow(rmax, 1.0/ord);
00135           
00136         if (r < 0.2) {
00137           r = 0.2;
00138         }
00139           
00140         *h = r * h_old;
00141           
00142         return GSL_ODEIV_HADJ_DEC;
00143 
00144       } else if (rmax < 0.5) {
00145 
00146         /* increase step, no more than factor of 5 */
00147         double r = S / pow(rmax, 1.0/(ord+1.0));
00148           
00149         if (r > 5.0) {
00150           r = 5.0;
00151         }
00152           
00153         /* don't allow any decrease caused by S<1 */
00154         if (r < 1.0) {
00155           r = 1.0;
00156         }
00157           
00158         *h = r * h_old;
00159 
00160         return GSL_ODEIV_HADJ_INC;
00161 
00162       } else {
00163 
00164         /* no change */
00165         return GSL_ODEIV_HADJ_NIL;
00166 
00167       }
00168 
00169     }
00170 
00171     /// Apply the evolution for the next adaptive step
00172     int evolve_apply(gsl_odeiv_evolve *e, size_t nvar, param_t &pa, 
00173                      func_t &derivs, double *t, double t1, double *h, 
00174                      vec_t &y) 
00175     {
00176       double t0 = *t;
00177       double h0 = *h;
00178       int step_status;
00179       int final_step = 0;
00180       /* remaining time, possibly less than h */
00181       double dt = t1 - t0;  
00182         
00183       if ((dt < 0.0 && h0 > 0.0) || (dt > 0.0 && h0 < 0.0)) {
00184         GSL_ERROR ("step direction must match interval direction", 
00185                    GSL_EINVAL);
00186       }
00187         
00188       for(size_t ic=0;ic<nvar;ic++) e->y0[ic]=y[ic];
00189         
00190       /* Calculate initial dydt  */
00191       int status=derivs(t0,nvar,y,e->dydt_in,pa);
00192       if (status) return status;
00193         
00194       try_step:
00195   
00196       if ((dt >= 0.0 && h0 > dt) || (dt < 0.0 && h0 < dt)) {
00197         h0 = dt;
00198         final_step = 1;
00199       } else{
00200         final_step = 0;
00201       }
00202       
00203       step_status=this->stepp->step(t0,h0,nvar,y,e->dydt_in,y,e->yerr,
00204                                     e->dydt_out,pa,derivs);
00205         
00206       /* Check for stepper internal failure */
00207       if (step_status != gsl_success)   {
00208         // Notify user which step-size caused the failure
00209         *h=h0;
00210         return step_status;
00211       }
00212   
00213       e->count++;
00214       e->last_step = h0;
00215       if (final_step) {
00216         *t = t1;
00217       } else{
00218         *t = t0 + h0;
00219       }
00220   
00221       /* Check error and attempt to adjust the step. */
00222       const int hadjust_status=hadjust(nvar,this->stepp->get_order(),
00223                                        y,e->yerr,e->dydt_out,&h0);
00224         
00225       if (hadjust_status == GSL_ODEIV_HADJ_DEC) {
00226           
00227         /* Step was decreased. Undo and go back to try again. */
00228         for(size_t ic=0;ic<nvar;ic++) y[ic]=e->y0[ic];
00229         e->failed_steps++;
00230         goto try_step;
00231       }
00232         
00233       /* suggest step size for next time-step */
00234       *h = h0;  
00235   
00236       return step_status;
00237     }
00238       
00239 #endif
00240     
00241     public:
00242       
00243     /** 
00244         \brief Control structure for gsl_astep [public typedef]
00245 
00246         This next section is taken directly from the GSL manual:
00247         
00248         The standard control object is a four parameter heuristic
00249         based on absolute and relative errors eps_abs and eps_rel, and
00250         scaling factors a_y and a_dydt for the system state y(t) and
00251         derivatives y'(t) respectively.
00252 
00253         The step-size adjustment procedure for this method begins by
00254         computing the desired error level D_i for each component,
00255 
00256         D_i = eps_abs + eps_rel * (a_y |y_i| + a_dydt h |y'_i|)
00257 
00258         and comparing it with the observed error E_i = |yerr_i|. If
00259         the observed error E exceeds the desired error level D by more
00260         than 10% for any component then the method reduces the
00261         step-size by an appropriate factor,
00262         
00263         h_new = h_old * S * (E/D)^(-1/q)
00264 
00265         where q is the consistency order of the method (e.g. q=4 for
00266         4(5) embedded RK), and S is a safety factor of 0.9. The ratio
00267         E/D is taken to be the maximum of the ratios E_i/D_i.
00268 
00269         If the observed error E is less than 50% of the desired error
00270         level D for the maximum ratio E_i/D_i then the algorithm takes
00271         the opportunity to increase the step-size to bring the error
00272         in line with the desired level,
00273 
00274         h_new = h_old * S * (E/D)^(-1/(q+1))
00275 
00276         This encompasses all the standard error scaling methods. To
00277         avoid uncontrolled changes in the stepsize, the overall
00278         scaling factor is limited to the range 1/5 to 5.
00279     */
00280     typedef struct
00281     {
00282       /// Absolute precision
00283       double eps_abs;
00284       /// Relative precision
00285       double eps_rel;
00286       /// Function scaling factor
00287       double a_y;
00288       /// Derivative scaling factor
00289       double a_dydt;
00290       /// Use standard or scaled algorithm
00291       bool standard;
00292 
00293     } gsl_ode_control;
00294 
00295     /// Control specification
00296     gsl_ode_control con;
00297     
00298     gsl_astep() {
00299 
00300       this->verbose=0;
00301 
00302       con.eps_abs=1.0e-6;
00303       con.eps_rel=0.0;
00304       con.a_y=1.0;
00305       con.a_dydt=0.0;
00306       con.standard=true;
00307 
00308       ndim=0;
00309       sdim=0;
00310     }
00311       
00312     virtual ~gsl_astep() {
00313       if (sdim!=0) delete[] scale_abs;
00314     }
00315     
00316     /** 
00317         \brief Set the scaling for each differential equation
00318     */
00319     template<class svec_t> int set_scale(size_t nscal, const svec_t &scale) {
00320       if (nscal>=sdim) {
00321         if (sdim!=0) delete[] scale_abs;
00322         scale_abs=new double[nscal];
00323         sdim=nscal;
00324       }
00325       for(size_t i=0;i<nscal;i++) {
00326         scale_abs[i]=scale[i];
00327       }
00328       return 0;
00329     }
00330 
00331     /** \brief Make an adaptive integration step of the system 
00332         \c derivs 
00333           
00334         This attempts to take a step of size \c h from the point \c x
00335         of an \c n-dimensional system \c derivs starting with \c y. On
00336         exit, \c x and \c y contain the new values at the end of the
00337         step and \c h contains the size of the step. 
00338     */
00339     virtual int astep(double &x, double &h, double xmax, size_t n, 
00340                       vec_t &y, param_t &pa, func_t &derivs) 
00341     {
00342 
00343       // The evolution object
00344       gsl_odeiv_evolve goe;
00345       
00346       ao.allocate(goe.y0,n);
00347       ao.allocate(goe.yerr,n);
00348       ao.allocate(goe.dydt_in,n);
00349       ao.allocate(goe.dydt_out,n);
00350         
00351       // Set up odeiv_evolve object
00352       goe.dimension=n;
00353 
00354       goe.count=0;
00355       goe.failed_steps=0;
00356       goe.last_step=0.0;
00357       
00358       int ret=evolve_apply(&goe,n,pa,derivs,&x,xmax,&h,y);
00359         
00360       if (this->verbose>0) {
00361         std::cout << x << " ";
00362         for(size_t j=0;j<n;j++) std::cout << y[j] << " ";
00363         std::cout << std::endl;
00364       }
00365 
00366       ao.free(goe.y0);
00367       ao.free(goe.yerr);
00368       ao.free(goe.dydt_in);
00369       ao.free(goe.dydt_out);
00370         
00371       return ret;
00372     }
00373 
00374     /** \brief Make an adaptive integration step of the system 
00375         \c derivs 
00376           
00377         This attempts to take a step of size \c h from the point \c x
00378         of an \c n-dimensional system \c derivs starting with \c y. On
00379         exit, \c x and \c y contain the new values at the end of the
00380         step and \c h contains the size of the step. 
00381     */
00382     virtual int astep_derivs(double &x, double &h, double xmax, size_t n, 
00383                              vec_t &y, vec_t &dydx, param_t &pa, 
00384                              func_t &derivs) 
00385     {
00386 
00387       // The evolution object
00388       gsl_odeiv_evolve goe;
00389       
00390       ao.allocate(goe.y0,n);
00391       ao.allocate(goe.yerr,n);
00392       ao.allocate(goe.dydt_in,n);
00393       ao.allocate(goe.dydt_out,n);
00394         
00395       // Set up odeiv_evolve object
00396       goe.dimension=n;
00397 
00398       goe.count=0;
00399       goe.failed_steps=0;
00400       goe.last_step=0.0;
00401       
00402       int ret=evolve_apply(&goe,n,pa,derivs,&x,xmax,&h,y);
00403 
00404       // Compute the final derivatives
00405       derivs(x,n,y,dydx,pa);
00406         
00407       if (this->verbose>0) {
00408         std::cout << x << " ";
00409         for(size_t j=0;j<n;j++) std::cout << y[j] << " ";
00410         std::cout << std::endl;
00411       }
00412 
00413       ao.free(goe.y0);
00414       ao.free(goe.yerr);
00415       ao.free(goe.dydt_in);
00416       ao.free(goe.dydt_out);
00417         
00418       return ret;
00419     }
00420       
00421   };
00422   
00423 #ifndef DOXYGENP
00424 }
00425 #endif
00426 
00427 #endif

Documentation generated with Doxygen and provided under the GNU Free Documentation License. See License Information for details.

Project hosting provided by SourceForge.net Logo, O2scl Sourceforge Project Page