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 Control structure for gsl_astep
00038 
00039       This class implements both the "standard" and "scaled" 
00040       step control methods from GSL. The standard control method
00041       is the default. To use the scaled controle, set \ref standard
00042       to <tt>false</tt> and set the scale for each component using
00043       set_scale().
00044       
00045       The control object is a four parameter heuristic based on
00046       absolute and relative errors \ref eps_abs and \ref eps_rel, and
00047       scaling factors \ref a_y and \ref a_dydt for the system state
00048       \f$ y(t) \f$ and derivatives \f$ y^{\prime}(t) \f$ respectively.
00049 
00050       The step-size adjustment procedure for this method begins by
00051       computing the desired error level \f$ D_i \f$ for each
00052       component. In the unscaled version,
00053       \f[
00054       D_i = \mathrm{eps\_abs}+\mathrm{eps\_rel} \times
00055       \left( \mathrm{a\_y} | y_i| + \mathrm{a\_dydt}~h 
00056       | y_i^{\prime}| \right)
00057       \f]
00058       while in the scaled version the user specifies the scale for
00059       each component, \f$ s_i \f$,
00060       \f[
00061       D_i = \mathrm{eps\_abs}~s_i+\mathrm{eps\_rel} \times
00062       \left( \mathrm{a\_y} | y_i| + \mathrm{a\_dydt}~h 
00063       | y_i^{\prime}| \right)
00064       \f]
00065 
00066       The desired error level \f$ D_i \f$ is compared to then observed
00067       error \f$ E_i = |\mathrm{yerr}_i| \f$. If the observed error \f$
00068       E \f$ exceeds the desired error level \f$ D \f$ by more than 10
00069       percent for any component then the method reduces the step-size
00070       by an appropriate factor,
00071       \f[
00072       h_{\mathrm{new}} = S~h_{\mathrm{old}} 
00073       \left(\frac{E}{D}\right)^{-1/q}
00074       \f]
00075       where \f$ q \f$ is the consistency order of the method (e.g. \f$
00076       q=4 \f$ for 4(5) embedded RK), and \f$ S \f$ is a safety factor
00077       of 0.9. The ratio \f$ E/D \f$ is taken to be the maximum of the
00078       ratios \f$ E_i/D_i \f$.
00079       
00080       If the observed error E is less than 50 percent of the desired
00081       error level \f$ D \f$ for the maximum ratio \f$ E_i/D_i \f$ then
00082       the algorithm takes the opportunity to increase the step-size to
00083       bring the error in line with the desired level,
00084       \f[
00085       h_{\mathrm{new}} = S~h_{\mathrm{old}} 
00086       \left(\frac{E}{D}\right)^{-1/(q+1)}
00087       \f]
00088       This encompasses all the standard error scaling methods. To
00089       avoid uncontrolled changes in the stepsize, the overall
00090       scaling factor is limited to the range 1/5 to 5.
00091   */
00092   template<class vec_t> class gsl_ode_control {
00093 
00094   protected:
00095 
00096     /// Number of scalings
00097     size_t sdim;
00098     
00099     /// Scalings
00100     double *scale_abs;
00101 
00102   public:
00103 
00104     /// Absolute precision (default \f$ 10^{-6} \f$)
00105     double eps_abs;
00106     /// Relative precision (default 0)
00107     double eps_rel;
00108     /// Function scaling factor (default 1)
00109     double a_y;
00110     /// Derivative scaling factor (default 0)
00111     double a_dydt;
00112     /// Use standard or scaled algorithm (default true)
00113     bool standard;
00114 
00115     gsl_ode_control() {
00116       eps_abs=1.0e-6;
00117       eps_rel=0.0;
00118       a_y=1.0;
00119       a_dydt=0.0;
00120       standard=true;
00121 
00122       sdim=0;
00123     }
00124     
00125     virtual ~gsl_ode_control() {
00126       if (sdim!=0) delete[] scale_abs;
00127     }      
00128 
00129     /** 
00130         \brief Set the scaling for each differential equation
00131     */
00132     template<class svec_t> int set_scale(size_t nscal, const svec_t &scale) {
00133       if (nscal>=sdim) {
00134         if (sdim!=0) delete[] scale_abs;
00135         scale_abs=new double[nscal];
00136         sdim=nscal;
00137       }
00138       for(size_t i=0;i<nscal;i++) {
00139         scale_abs[i]=scale[i];
00140       }
00141       return 0;
00142     }
00143 
00144     /// Automatically adjust step-size
00145     virtual int hadjust(size_t dim, unsigned int ord, const vec_t &y,
00146                         vec_t &yerr, vec_t &yp, double *h) {
00147       
00148       const double S = 0.9;
00149       const double h_old = *h;
00150       
00151       double rmax = DBL_MIN;
00152       size_t i;
00153         
00154       for(i=0; i<dim; i++) {
00155         double D0;
00156         if (standard || sdim==0) {
00157           D0=eps_rel*(a_y*fabs(y[i])+a_dydt*fabs(h_old*yp[i]))+eps_abs;
00158         } else {
00159           D0=eps_rel*(a_y*fabs(y[i])+a_dydt*fabs(h_old*yp[i]))+
00160             eps_abs*scale_abs[i%sdim];
00161         }
00162         const double r=fabs(yerr[i]) / fabs(D0);
00163         rmax = GSL_MAX_DBL(r, rmax);
00164       }
00165         
00166       if(rmax > 1.1) {
00167 
00168         /* decrease step, no more than factor of 5, but a fraction S more
00169            than scaling suggests (for better accuracy) */
00170 
00171         double r =  S / pow(rmax, 1.0/ord);
00172           
00173         if (r < 0.2) {
00174           r = 0.2;
00175         }
00176           
00177         *h = r * h_old;
00178           
00179         return GSL_ODEIV_HADJ_DEC;
00180 
00181       } else if (rmax < 0.5) {
00182 
00183         /* increase step, no more than factor of 5 */
00184         double r = S / pow(rmax, 1.0/(ord+1.0));
00185           
00186         if (r > 5.0) {
00187           r = 5.0;
00188         }
00189           
00190         /* don't allow any decrease caused by S<1 */
00191         if (r < 1.0) {
00192           r = 1.0;
00193         }
00194           
00195         *h = r * h_old;
00196 
00197         return GSL_ODEIV_HADJ_INC;
00198 
00199       } else {
00200 
00201         /* no change */
00202         return GSL_ODEIV_HADJ_NIL;
00203 
00204       }
00205 
00206     }
00207 
00208   };
00209 
00210   /** 
00211       \brief Adaptive ODE stepper (GSL)
00212       
00213       To modify the ODE stepper which is used, use the
00214       adapt_step::set_step().
00215 
00216       Default template arguments
00217       - \c param_t - \ref multi_funct
00218       - \c func_t - \ref ode_funct
00219       - \c vec_t - \ref ovector_view
00220       - \c alloc_vec_t - \ref ovector
00221       - \c alloc_t - \ref ovector_alloc
00222 
00223       \future Fix so that memory allocation/deallocation is performed only
00224       as necessary
00225       \future Allow user to find out how many steps were taken, etc.
00226 
00227   */
00228   template<class param_t, class func_t=ode_funct<param_t>, 
00229     class vec_t=ovector_view, class alloc_vec_t=ovector, 
00230     class alloc_t=ovector_alloc> class gsl_astep : 
00231   public adapt_step<param_t,func_t,vec_t,alloc_vec_t,alloc_t> {
00232     
00233 #ifndef DOXYGEN_INTERNAL
00234     
00235     protected:
00236     
00237     /// Memory allocator for objects of type \c alloc_vec_t
00238     alloc_t ao;
00239     
00240     /// Temporary storage for yout
00241     alloc_vec_t yout;
00242 
00243     /// Temporary storage for dydx_out
00244     alloc_vec_t dydx_out;
00245     
00246     /// The size of the last step
00247     double last_step;
00248     /// The number of steps (?)
00249     unsigned long int count;
00250     /// The number of failed steps
00251     unsigned long int failed_steps;
00252 
00253     /// Apply the evolution for the next adaptive step
00254     int evolve_apply(double &t, double &h, double t1, size_t nvar,
00255                      vec_t &y, vec_t &dydx, vec_t &yout2, vec_t &yerr,
00256                      vec_t &dydx_out2, param_t &pa, func_t &derivs) {
00257       
00258       double t0 = t;
00259       double h0 = h;
00260       int step_status;
00261       int final_step = 0;
00262       /* remaining time, possibly less than h */
00263       double dt = t1 - t0;  
00264         
00265       if ((dt < 0.0 && h0 > 0.0) || (dt > 0.0 && h0 < 0.0)) {
00266         O2SCL_ERR("Step direction must match interval direction", 
00267                 gsl_einval);
00268       }
00269       
00270       bool try_step_again=true;
00271       while(try_step_again) {
00272 
00273         if ((dt >= 0.0 && h0 > dt) || (dt < 0.0 && h0 < dt)) {
00274           h0 = dt;
00275           final_step = 1;
00276         } else{
00277           final_step = 0;
00278         }
00279       
00280         step_status=this->stepp->step(t0,h0,nvar,y,dydx,yout2,yerr,
00281                                       dydx_out2,pa,derivs);
00282       
00283         /* Check for stepper internal failure */
00284         if (step_status != gsl_success) {
00285           // Notify user which step-size caused the failure
00286           h=h0;
00287           return step_status;
00288         }
00289   
00290         count++;
00291         last_step = h0;
00292         if (final_step) {
00293           t = t1;
00294         } else{
00295           t = t0 + h0;
00296         }
00297   
00298         /* Check error and attempt to adjust the step. */
00299         const int hadjust_status=con.hadjust(nvar,this->stepp->get_order(),
00300                                              yout2,yerr,dydx_out2,&h0);
00301         
00302         if (hadjust_status == GSL_ODEIV_HADJ_DEC) {
00303           
00304           /* Step was decreased. Undo and go back to try again. */
00305           failed_steps++;
00306           try_step_again=true;
00307         } else {
00308           try_step_again=false;
00309         }
00310 
00311       }
00312         
00313       /* suggest step size for next time-step */
00314       h = h0;  
00315   
00316       return step_status;
00317     }
00318       
00319 #endif
00320     
00321     public:
00322       
00323     /// Control specification
00324     gsl_ode_control<vec_t> con;
00325     
00326     gsl_astep() {
00327       this->verbose=0;
00328     }
00329       
00330     virtual ~gsl_astep() {
00331     }
00332     
00333     /** 
00334         \brief Make an adaptive integration step of the system 
00335         \c derivs with derivatives
00336           
00337         This attempts to take a step of size \c h from the point \c
00338         x of an \c n-dimensional system \c derivs starting with \c y
00339         and given the initial derivatives \c dydx. On exit, \c x, \c
00340         y and \c dydx contain the new values at the end of the step,
00341         \c h contains the size of the step, \c dydx
00342         contains the derivative at the end of the step, and \c yerr
00343         contains the estimated error at the end of the step.
00344     */
00345     virtual int astep_derivs(double &x, double &h, double xmax,
00346                              size_t n, vec_t &y, vec_t &dydx, 
00347                              vec_t &yerr, param_t &pa, 
00348                              func_t &derivs) {
00349       count=0;
00350       failed_steps=0;
00351       last_step=0.0;
00352 
00353       ao.allocate(yout,n);
00354       ao.allocate(dydx_out,n);
00355 
00356       int ret=evolve_apply(x,h,xmax,n,y,dydx,yout,yerr,dydx_out,
00357                            pa,derivs);
00358 
00359       for(size_t i=0;i<n;i++) {
00360         y[i]=yout[i];
00361         dydx[i]=dydx_out[i];
00362       }
00363 
00364       if (this->verbose>0) {
00365         std::cout << x << " ";
00366         for(size_t j=0;j<n;j++) std::cout << y[j] << " ";
00367         std::cout << std::endl;
00368       }
00369 
00370       ao.free(yout);
00371       ao.free(dydx_out);
00372 
00373       return ret;
00374     }
00375       
00376     /** 
00377         \brief Make an adaptive integration step of the system 
00378         \c derivs 
00379           
00380         This attempts to take a step of size \c h from the point \c
00381         x of an \c n-dimensional system \c derivs starting with \c
00382         y. On exit, \c x and \c y contain the new values at the end
00383         of the step, \c h contains the size of the step, \c dydx_out
00384         contains the derivative at the end of the step, and \c yerr
00385         contains the estimated error at the end of the step.
00386     */
00387     virtual int astep(double &x, double &h, double xmax,
00388                       size_t n, vec_t &y, vec_t &u_dydx_out,
00389                       vec_t &yerr, param_t &pa, 
00390                       func_t &derivs) {
00391       count=0;
00392       failed_steps=0;
00393       last_step=0.0;
00394 
00395       alloc_vec_t dydx;
00396       ao.allocate(yout,n);
00397       ao.allocate(dydx,n);
00398       
00399       int ret=derivs(x,n,y,dydx,pa);
00400       if (ret!=0) {
00401         O2SCL_ADD_ERR_RET("Initial derivative evalution failed in astep().",
00402                     gsl_efailed);
00403       }
00404       
00405       ret=evolve_apply(x,h,xmax,n,y,dydx,yout,yerr,u_dydx_out,
00406                        pa,derivs);
00407       
00408       for(size_t i=0;i<n;i++) {
00409         y[i]=yout[i];
00410       }
00411 
00412       if (this->verbose>0) {
00413         std::cout << x << " ";
00414         for(size_t j=0;j<n;j++) std::cout << y[j] << " ";
00415         std::cout << std::endl;
00416       }
00417 
00418       ao.free(yout);
00419       ao.free(dydx);
00420 
00421       return ret;
00422     }
00423       
00424   };
00425   
00426 #ifndef DOXYGENP
00427 }
00428 #endif
00429 
00430 #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