Object-oriented Scientific Computing Library: Version 0.910
gsl_astep.h
00001 /*
00002   -------------------------------------------------------------------
00003   
00004   Copyright (C) 2006-2012, 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 /* ode-initval2/evolve.c
00024  * 
00025  * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman
00026  * 
00027  * This program is free software; you can redistribute it and/or modify
00028  * it under the terms of the GNU General Public License as published by
00029  * the Free Software Foundation; either version 3 of the License, or (at
00030  * your option) any later version.
00031  * 
00032  * This program is distributed in the hope that it will be useful, but
00033  * WITHOUT ANY WARRANTY; without even the implied warranty of
00034  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00035  * General Public License for more details.
00036  * 
00037  * You should have received a copy of the GNU General Public License
00038  * along with this program; if not, write to the Free Software
00039  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
00040  * 02110-1301, USA.
00041  */
00042 #ifndef O2SCL_GSL_ASTEP_H
00043 #define O2SCL_GSL_ASTEP_H
00044 
00045 #include <string>
00046 
00047 #include <gsl/gsl_math.h>
00048 #include <gsl/gsl_odeiv.h>
00049 
00050 #include <o2scl/adapt_step.h>
00051 
00052 #ifndef DOXYGENP
00053 namespace o2scl {
00054 #endif
00055 
00056   /** \brief Control structure for gsl_astep
00057 
00058       This class implements both the "standard" and "scaled" step
00059       control methods from GSL. The standard control method is the
00060       default. To use the scaled control, set \ref standard to
00061       <tt>false</tt> and set the scale for each component using
00062       set_scale().
00063       
00064       The control object is a four parameter heuristic based on
00065       absolute and relative errors \ref eps_abs and \ref eps_rel, and
00066       scaling factors \ref a_y and \ref a_dydt for the system state
00067       \f$ y(t) \f$ and derivatives \f$ y^{\prime}(t) \f$ respectively.
00068 
00069       The step-size adjustment procedure for this method begins by
00070       computing the desired error level \f$ D_i \f$ for each
00071       component. In the unscaled version,
00072       \f[
00073       D_i = \mathrm{eps\_abs}+\mathrm{eps\_rel} \times
00074       \left( \mathrm{a\_y} | y_i| + \mathrm{a\_dydt}~h 
00075       | y_i^{\prime}| \right)
00076       \f]
00077       while in the scaled version the user specifies the scale for
00078       each component, \f$ s_i \f$,
00079       \f[
00080       D_i = \mathrm{eps\_abs}~s_i+\mathrm{eps\_rel} \times
00081       \left( \mathrm{a\_y} | y_i| + \mathrm{a\_dydt}~h 
00082       | y_i^{\prime}| \right)
00083       \f]
00084 
00085       The desired error level \f$ D_i \f$ is compared to then observed
00086       error \f$ E_i = |\mathrm{yerr}_i| \f$. If the observed error \f$
00087       E \f$ exceeds the desired error level \f$ D \f$ by more than 10
00088       percent for any component then the method reduces the step-size
00089       by an appropriate factor,
00090       \f[
00091       h_{\mathrm{new}} = S~h_{\mathrm{old}} 
00092       \left(\frac{E}{D}\right)^{-1/q}
00093       \f]
00094       where \f$ q \f$ is the consistency order of the method (e.g. \f$
00095       q=4 \f$ for 4(5) embedded RK), and \f$ S \f$ is a safety factor
00096       of 0.9. The ratio \f$ E/D \f$ is taken to be the maximum of the
00097       ratios \f$ E_i/D_i \f$.
00098       
00099       If the observed error E is less than 50 percent of the desired
00100       error level \f$ D \f$ for the maximum ratio \f$ E_i/D_i \f$ then
00101       the algorithm takes the opportunity to increase the step-size to
00102       bring the error in line with the desired level,
00103       \f[
00104       h_{\mathrm{new}} = S~h_{\mathrm{old}} 
00105       \left(\frac{E}{D}\right)^{-1/(q+1)}
00106       \f]
00107       This encompasses all the standard error scaling methods. To
00108       avoid uncontrolled changes in the stepsize, the overall scaling
00109       factor is limited to the range 1/5 to 5.
00110 
00111       If the user specified fewer scaling parameters than the number
00112       of ODEs, then the scaling parameters are reused as follows. If
00113       there are \f$ N \f$ ODEs and \f$ M \f$ scaling parameters, then
00114       for \f$ i>M \f$, the ith scaling parameter \f$ s_i \f$ is set to
00115       be \f$ s_{i\%M} \f$ . If the user selects the scaled control by
00116       setting \ref standard to <tt>false</tt> and no scale parameters
00117       are specified, this class reverts to the standard control.
00118 
00119       \todo Double check that the improvements in the ode-initval2
00120       routines are available here
00121   */
00122   template<class vec_t=ovector_base> class gsl_ode_control {
00123   
00124   protected:
00125   
00126   /// Scalings
00127   uvector scale_abs;
00128   
00129   public:
00130   
00131   /// \name Adjustment specification
00132   //@{
00133   /// No adjustment required
00134   static const size_t hadj_nil=0;
00135   /// Recommend step decrease
00136   static const size_t hadj_dec=1;
00137   /// Recommend step increase
00138   static const size_t hadj_inc=2;
00139   //@}
00140   
00141   /// Absolute precision (default \f$ 10^{-6} \f$)
00142   double eps_abs;
00143   /// Relative precision (default 0)
00144   double eps_rel;
00145   /// Function scaling factor (default 1)
00146   double a_y;
00147   /// Derivative scaling factor (default 0)
00148   double a_dydt;
00149   /// Use standard or scaled algorithm (default true)
00150   bool standard;
00151 
00152   gsl_ode_control() {
00153     eps_abs=1.0e-6;
00154     eps_rel=0.0;
00155     a_y=1.0;
00156     a_dydt=0.0;
00157     standard=true;
00158   }
00159     
00160   virtual ~gsl_ode_control() {
00161   }      
00162 
00163   /** \brief Set the scaling for each differential equation
00164    */
00165   template<class svec_t> int set_scale(size_t nscal, const svec_t &scale) {
00166     scale_abs.allocate(nscal);
00167     for(size_t i=0;i<nscal;i++) {
00168       scale_abs[i]=scale[i];
00169     }
00170     return 0;
00171   }
00172 
00173   /// Automatically adjust step-size
00174   virtual int hadjust(size_t dim, unsigned int ord, const vec_t &y,
00175                       vec_t &yerr, vec_t &yp, double &h) {
00176       
00177     const double S=0.9;
00178     const double h_old=h;
00179       
00180     double rmax=DBL_MIN;
00181     size_t i;
00182       
00183     for(i=0;i<dim;i++) {
00184       double D0;
00185       if (standard || scale_abs.size()==0) {
00186         D0=eps_rel*(a_y*fabs(y[i])+a_dydt*fabs(h_old*yp[i]))+eps_abs;
00187       } else {
00188         D0=eps_rel*(a_y*fabs(y[i])+a_dydt*fabs(h_old*yp[i]))+
00189           eps_abs*scale_abs[i%scale_abs.size()];
00190       }
00191       const double r=fabs(yerr[i]) / fabs(D0);
00192       rmax=GSL_MAX_DBL(r, rmax);
00193     }
00194         
00195     if (rmax > 1.1) {
00196 
00197       /* decrease step, no more than factor of 5, but a fraction S more
00198          than scaling suggests (for better accuracy) */
00199       double r= S / pow(rmax, 1.0/ord);
00200           
00201       if (r < 0.2) {
00202         r=0.2;
00203       }
00204       h=r*h_old;
00205       return hadj_dec;
00206 
00207     } else if (rmax < 0.5) {
00208 
00209       /* increase step, no more than factor of 5 */
00210       double r=S / pow(rmax, 1.0/(ord+1.0));
00211       if (r > 5.0) {
00212         r=5.0;
00213       }
00214       /* don't allow any decrease caused by S<1 */
00215       if (r < 1.0) {
00216         r=1.0;
00217       }
00218       h=r*h_old;
00219       return hadj_inc;
00220 
00221     } else {
00222 
00223       /* no change */
00224       return hadj_nil;
00225 
00226     }
00227     
00228   }
00229   
00230   };
00231   
00232   /** \brief Adaptive ODE stepper (GSL)
00233       
00234       This class performs an adaptive step of a system of ODEs. To
00235       modify the ODE stepper which is used, use the function \ref
00236       adapt_step::set_step().
00237 
00238       Note, this has been updated to correspond to the
00239       <tt>ode-initval2</tt> functions in GSL.
00240 
00241       There is an example for the usage of this class in
00242       <tt>examples/ex_ode.cpp</tt> documented in the \ref ex_ode_sect
00243       section.
00244 
00245       \todo Document what happens when the stepper function returns
00246       a non-zero value, as it's different now with the ode-initval2
00247       function.
00248       \todo Document count, failed_steps, etc.
00249       \future Compare more directly to GSL
00250 
00251       Default template arguments
00252       - \c func_t - \ref ode_funct
00253       - \c vec_t - \ref ovector_base
00254       - \c alloc_vec_t - \ref ovector
00255       - \c alloc_t - \ref ovector_alloc
00256   */
00257   template<class func_t=ode_funct<>, class vec_t=ovector_base, 
00258     class alloc_vec_t=ovector, class alloc_t=ovector_alloc> 
00259     class gsl_astep : public adapt_step<func_t,vec_t,alloc_vec_t,alloc_t> {
00260     
00261 #ifndef DOXYGEN_INTERNAL
00262     
00263   protected:
00264     
00265   /// Memory allocator for objects of type \c alloc_vec_t
00266   alloc_t ao;
00267     
00268   /// Temporary storage for yout
00269   alloc_vec_t yout_int;
00270 
00271   /// Internal storage for dydx
00272   alloc_vec_t dydx_int;
00273     
00274   /// The size of the last step
00275   double last_step;
00276 
00277   /// The number of steps
00278   unsigned long int count;
00279 
00280   /// The number of failed steps
00281   unsigned long int failed_steps;
00282 
00283   /// The size of the allocated vectors
00284   size_t msize;
00285 
00286   /** \brief Apply the evolution for the next adaptive step
00287 
00288       This function is based on <tt>gsl_odeiv2_evolve_apply</tt>.
00289       
00290       \note This function requres that \c y, \c yout, \c dydx and \c
00291       dydx_out are all distinct vectors. 
00292   */
00293   int evolve_apply(double t0, double t1, double &t, double &h, size_t nvar,
00294                    vec_t &y, vec_t &dydx, vec_t &yout, vec_t &yerr,
00295                    vec_t &dydx_out, func_t &derivs) {
00296     
00297     double h0=h;
00298     int step_status;
00299     bool final_step=false;
00300     // Remaining step size, possibly less than h
00301     double dt=t1-t0; 
00302     
00303     if ((dt < 0.0 && h0 > 0.0) || (dt > 0.0 && h0 < 0.0)) {
00304       std::string str="Interval direction (t1-t0="+dtos(dt)+
00305         ") does not match step direction (h="+dtos(h)+
00306         " in gsl_astep::evolve_apply().";
00307       O2SCL_ERR_RET(str.c_str(),gsl_einval);
00308     }
00309 
00310     // Copy the initial point from y to yout. The stepper won't modify
00311     // y, so we can undo the step by going back to the values in y.
00312     vector_copy(nvar,y,yout);
00313       
00314     bool try_step_again=true;
00315     while (try_step_again) {
00316       try_step_again=false;
00317       
00318       if ((dt >= 0.0 && h0 > dt) || (dt < 0.0 && h0 < dt)) {
00319         h0=dt;
00320         final_step=true;
00321       } else{
00322         final_step=false;
00323       }
00324         
00325       step_status=this->stepp->step(t0,h0,nvar,yout,dydx,yout,yerr,
00326                                     dydx_out,derivs);
00327 
00328       // [GSL] Return if the stepper indicates a pointer or user function
00329       // failure
00330       if (step_status == gsl_efault || step_status==gsl_ebadfunc) {
00331         return step_status;
00332       }
00333 
00334       // [GSL] Check for stepper internal failure
00335       if (step_status!=gsl_success) {
00336 
00337         // [GSL] Stepper was unable to calculate step. Try decreasing
00338         // stepsize
00339         double h_old=h0;
00340         h0*=0.5;
00341 
00342         // [GSL] Check that an actual decrease in h0 occured and that
00343         // the suggested h0 will change the independent variable
00344         // will change by at least 1ulp
00345         //double t_curr=GSL_COERCE_DBL(t);
00346         //double t_next=GSL_COERCE_DBL(t+h0);
00347         double t_curr=t;
00348         double t_next=t+h0;
00349         
00350         if (fabs(h0) < fabs(h_old) && t_next != t_curr) {
00351           // [GSL] Step was decreased. Undo step, and try again with 
00352           // new h0. 
00353           vector_copy(nvar,y,yout);
00354           failed_steps++;
00355           try_step_again=true;
00356         } else {
00357           // [GSL] notify user of step-size which caused the failure 
00358           h=h0;            
00359           // [GSL] restore original t value
00360           t=t0;            
00361           return step_status;
00362         }
00363       }
00364 
00365       if (try_step_again==false) {
00366       
00367         count++;
00368         last_step=h0;
00369         if (final_step) {
00370           t=t1;
00371         } else{
00372           t=t0+h0;
00373         }
00374   
00375         // [GSL] Check error and attempt to adjust the step.
00376         double h_old=h0;
00377         const size_t hadjust_status=con.hadjust
00378           (nvar,this->stepp->get_order(),yout,yerr,dydx_out,h0);
00379       
00380         if (hadjust_status == gsl_ode_control<vec_t>::hadj_dec) {
00381         
00382           // [GSL] Check that the reported status is correct (i.e. an actual
00383           // decrease in h0 occured) and the suggested h0 will change
00384           // the independent variable by at least 1 ulp
00385 
00386           //double t_curr=GSL_COERCE_DBL (*t);
00387           //double t_next=GSL_COERCE_DBL ((*t) + h0);
00388           double t_curr=t;
00389           double t_next=t+h0;
00390           
00391           if (fabs(h0) < fabs(h_old) && t_next != t_curr) {
00392             // [GSL] Step was decreased. Undo step, and try again with 
00393             // new h0.
00394             vector_copy(nvar,y,yout);
00395             failed_steps++;
00396             try_step_again=true;
00397           } else {
00398             /* [GSL] Can not obtain required error tolerance, and can
00399                not decrease step-size any further, so give up and return
00400                gsl_failure.
00401             */
00402             // [GSL] notify user of step-size which caused the failure
00403             h=h0;         
00404             return gsl_failure;
00405           }
00406         }
00407       }
00408       
00409     }
00410 
00411     if (final_step==false) {
00412       // [GSL] Suggest step size for next time-step. Change of step 
00413       // size is not suggested in the final step, because that step 
00414       // can be very small compared to previous step, to reach t1. 
00415       h=h0; 
00416     }
00417   
00418     // This used to be return step_status, but the code never 
00419     // reaches this point if step_status is anything other than zero.
00420     return step_status;
00421   }
00422       
00423 #endif
00424     
00425   public:
00426       
00427   gsl_astep() {
00428     this->verbose=0;
00429     msize=0;
00430   }
00431       
00432   virtual ~gsl_astep() {
00433     if (msize>0) {
00434       ao.free(yout_int);
00435       ao.free(dydx_int);
00436     }
00437   }
00438     
00439   /// Control specification
00440   gsl_ode_control<vec_t> con;
00441     
00442   /** \brief Make an adaptive integration step of the system 
00443       \c derivs 
00444           
00445       This attempts to take a step of size \c h from the point \c
00446       x of an \c n-dimensional system \c derivs starting with \c
00447       y. On exit, \c x and \c y contain the new values at the end
00448       of the step, \c h contains the size of the step, \c dydx_out
00449       contains the derivative at the end of the step, and \c yerr
00450       contains the estimated error at the end of the step.
00451   */
00452   virtual int astep(double &x, double xmax, double &h, 
00453                     size_t n, vec_t &y, vec_t &dydx_out,
00454                     vec_t &yerr, func_t &derivs) {
00455     count=0;
00456     failed_steps=0;
00457     last_step=0.0;
00458 
00459     if (n!=msize) {
00460       ao.allocate(yout_int,n);
00461       ao.allocate(dydx_int,n);
00462       msize=n;
00463     }
00464       
00465     int ret=derivs(x,n,y,dydx_int);
00466     if (ret!=0) return ret;
00467       
00468     ret=evolve_apply(x,xmax,x,h,n,y,dydx_int,yout_int,yerr,dydx_out,derivs);
00469 
00470     if (ret==gsl_success) {
00471       for(size_t i=0;i<n;i++) {
00472         y[i]=yout_int[i];
00473       }
00474     }
00475 
00476     if (this->verbose>0) {
00477       std::cout << "gsl_astep step:";
00478       std::cout << x << " ";
00479       for(size_t j=0;j<n;j++) std::cout << y[j] << " ";
00480       std::cout << std::endl;
00481     }
00482 
00483     return ret;
00484   }
00485 
00486   /** \brief Make an adaptive integration step of the system 
00487       \c derivs with derivatives
00488           
00489       This attempts to take a step of size \c h from the point \c x
00490       of an \c n-dimensional system \c derivs starting with \c y and
00491       given the initial derivatives \c dydx. On exit, \c x, \c y and
00492       \c dydx contain the new values at the end of the step, \c h
00493       contains the size of the step, \c dydx contains the derivative
00494       at the end of the step, and \c yerr contains the estimated
00495       error at the end of the step.
00496   */
00497   virtual int astep_derivs(double &x, double xmax, double &h, 
00498                            size_t n, vec_t &y, vec_t &dydx, 
00499                            vec_t &yerr, func_t &derivs) {
00500     count=0;
00501     failed_steps=0;
00502     last_step=0.0;
00503 
00504     if (n!=msize) {
00505       ao.allocate(yout_int,n);
00506       ao.allocate(dydx_int,n);
00507       msize=n;
00508     }
00509 
00510     int ret=evolve_apply(x,xmax,x,h,n,y,dydx,yout_int,yerr,dydx_int,derivs);
00511 
00512     if (ret==gsl_success) {
00513       for(size_t i=0;i<n;i++) {
00514         y[i]=yout_int[i];
00515         dydx[i]=dydx_int[i];
00516       }
00517     }
00518         
00519     if (this->verbose>0) {
00520       std::cout << "gsl_astep step:";
00521       std::cout << x << " ";
00522       for(size_t j=0;j<n;j++) std::cout << y[j] << " ";
00523       std::cout << std::endl;
00524     }
00525 
00526     return ret;
00527   }
00528   
00529   /** \brief Make an adaptive integration step of the system 
00530       \c derivs
00531 
00532       This function performs an adaptive integration step with the
00533       \c n-dimensional system \c derivs and parameter \c pa. It
00534       Begins at \c x with initial stepsize \c h, ensuring that the
00535       step goes no farther than \c xmax. At the end of the step, the
00536       size of the step taken is \c h and the new value of \c x is in
00537       \c x_out. Initially, the function values and derivatives
00538       should be specified in \c y and \c dydx. The function values,
00539       derivatives, and the error at the end of the step are given in
00540       \c yout, \c yerr, and \c dydx_out. Unlike in \c ode_step
00541       objects, the objects \c y, \c yout, \c dydx, and \c dydx_out
00542       must all be distinct.
00543 
00544       This adaptive stepper function is faster than astep() or
00545       astep_derivs() because it does not require any copying of
00546       vectors.
00547   */
00548   virtual int astep_full(double x, double xmax, double &x_out, double &h, 
00549                          size_t n, vec_t &y, vec_t &dydx, 
00550                          vec_t &yout, vec_t &yerr, vec_t &dydx_out, 
00551                          func_t &derivs) {
00552 
00553     count=0;
00554     failed_steps=0;
00555     last_step=0.0;
00556     
00557     int ret=evolve_apply(x,xmax,x_out,h,n,y,dydx,yout,yerr,dydx_out,
00558                          derivs);
00559     
00560     if (this->verbose>0) {
00561       std::cout << "gsl_astep step:";
00562       std::cout << x_out << " ";
00563       for(size_t j=0;j<n;j++) std::cout << yout[j] << " ";
00564       std::cout << std::endl;
00565     }
00566 
00567     return ret;
00568   }
00569             
00570   };
00571 
00572 #ifndef DOXYGENP
00573 }
00574 #endif
00575 
00576 #endif
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Documentation generated with Doxygen. Provided under the GNU Free Documentation License (see License Information).

Get Object-oriented Scientific Computing
Lib at SourceForge.net. Fast, secure and Free Open Source software
downloads.