gsl_astep.h

00001 /*
00002   -------------------------------------------------------------------
00003   
00004   Copyright (C) 2006, 2007, 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 stepper (GSL)
00038       
00039       \todo Finish implementing the scaled "control"
00040       \todo Allow user to find out how many steps were taken, etc.
00041   */
00042   template<class param_t, class func_t, class vec_t=ovector_view,
00043     class alloc_vec_t=ovector, class alloc_t=ovector_alloc> class gsl_astep : 
00044   public adapt_step<param_t,func_t,vec_t,alloc_vec_t,alloc_t> {
00045     
00046 #ifndef DOXYGEN_INTERNAL
00047     
00048     protected:
00049     
00050     /// Size of allocated vectors
00051     size_t ndim;
00052     
00053     /// Memory allocator for objects of type \c alloc_vec_t
00054     alloc_t ao;
00055 
00056     /// Number of scalings
00057     size_t sdim;
00058     
00059     /// Scalings
00060     double *scale_abs;
00061 
00062     /** 
00063         \brief The GSL evolution object for \ref gsl_astep 
00064         [protected subclass]
00065     */
00066     class gsl_odeiv_evolve {
00067     public:
00068         
00069       /// Number of differential equations
00070       size_t dimension;
00071 
00072       /// The values of the functions
00073       alloc_vec_t y0;
00074       /// The uncertainty
00075       alloc_vec_t yerr;
00076       /// The input derivatives
00077       alloc_vec_t dydt_in;
00078       /// The output derivatives
00079       alloc_vec_t dydt_out;
00080       /// Size of the memory allocation
00081       size_t ndim;
00082       
00083       /// The size of the last step
00084       double last_step;
00085       /// The number of steps (?)
00086       unsigned long int count;
00087       /// The number of failed steps
00088       unsigned long int failed_steps;
00089     };
00090 
00091     /// Automatically adjust step-size
00092     int hadjust(size_t dim, unsigned int ord, const vec_t &y,
00093                 vec_t &yerr, vec_t &yp, double *h) 
00094     {
00095         
00096       gsl_ode_control *state = &con;
00097         
00098       const double eps_abs = state->eps_abs;
00099       const double eps_rel = state->eps_rel;
00100       const double a_y     = state->a_y;
00101       const double a_dydt  = state->a_dydt;
00102         
00103       const double S = 0.9;
00104       const double h_old = *h;
00105       
00106       double rmax = DBL_MIN;
00107       size_t i;
00108         
00109       for(i=0; i<dim; i++) {
00110         double D0;
00111         if (state->standard || sdim==0) {
00112           D0=eps_rel*(a_y*fabs(y[i])+a_dydt*fabs(h_old*yp[i]))+eps_abs;
00113         } else {
00114           D0=eps_rel*(a_y*fabs(y[i])+a_dydt*fabs(h_old*yp[i]))+
00115             eps_abs*scale_abs[i%sdim];
00116         }
00117         const double r=fabs(yerr[i]) / fabs(D0);
00118         rmax = GSL_MAX_DBL(r, rmax);
00119       }
00120         
00121       if(rmax > 1.1) {
00122 
00123         /* decrease step, no more than factor of 5, but a fraction S more
00124            than scaling suggests (for better accuracy) */
00125 
00126         double r =  S / pow(rmax, 1.0/ord);
00127           
00128         if (r < 0.2) {
00129           r = 0.2;
00130         }
00131           
00132         *h = r * h_old;
00133           
00134         return GSL_ODEIV_HADJ_DEC;
00135 
00136       } else if (rmax < 0.5) {
00137 
00138         /* increase step, no more than factor of 5 */
00139         double r = S / pow(rmax, 1.0/(ord+1.0));
00140           
00141         if (r > 5.0) {
00142           r = 5.0;
00143         }
00144           
00145         /* don't allow any decrease caused by S<1 */
00146         if (r < 1.0) {
00147           r = 1.0;
00148         }
00149           
00150         *h = r * h_old;
00151 
00152         return GSL_ODEIV_HADJ_INC;
00153 
00154       } else {
00155 
00156         /* no change */
00157         return GSL_ODEIV_HADJ_NIL;
00158 
00159       }
00160 
00161     }
00162 
00163     /// Apply the evolution for the next adaptive step
00164     int evolve_apply(gsl_odeiv_evolve *e, size_t nvar, param_t &pa, 
00165                      func_t &derivs, double *t, double t1, double *h, 
00166                      vec_t &y) 
00167     {
00168       double t0 = *t;
00169       double h0 = *h;
00170       int step_status;
00171       int final_step = 0;
00172       /* remaining time, possibly less than h */
00173       double dt = t1 - t0;  
00174         
00175       if ((dt < 0.0 && h0 > 0.0) || (dt > 0.0 && h0 < 0.0)) {
00176         GSL_ERROR ("step direction must match interval direction", 
00177                    GSL_EINVAL);
00178       }
00179         
00180       for(size_t ic=0;ic<nvar;ic++) e->y0[ic]=y[ic];
00181         
00182       /* Calculate initial dydt  */
00183       int status=derivs(t0,nvar,y,e->dydt_in,pa);
00184       if (status) return status;
00185         
00186       try_step:
00187   
00188       if ((dt >= 0.0 && h0 > dt) || (dt < 0.0 && h0 < dt)) {
00189         h0 = dt;
00190         final_step = 1;
00191       } else{
00192         final_step = 0;
00193       }
00194       
00195       step_status=this->stepp->step(t0,h0,nvar,y,e->dydt_in,y,e->yerr,
00196                                     e->dydt_out,pa,derivs);
00197         
00198       /* Check for stepper internal failure */
00199       if (step_status != gsl_success)   {
00200         // Notify user which step-size caused the failure
00201         *h=h0;
00202         return step_status;
00203       }
00204   
00205       e->count++;
00206       e->last_step = h0;
00207       if (final_step) {
00208         *t = t1;
00209       } else{
00210         *t = t0 + h0;
00211       }
00212   
00213       /* Check error and attempt to adjust the step. */
00214       const int hadjust_status=hadjust(nvar,this->stepp->get_order(),
00215                                        y,e->yerr,e->dydt_out,&h0);
00216         
00217       if (hadjust_status == GSL_ODEIV_HADJ_DEC) {
00218           
00219         /* Step was decreased. Undo and go back to try again. */
00220         for(size_t ic=0;ic<nvar;ic++) y[ic]=e->y0[ic];
00221         e->failed_steps++;
00222         goto try_step;
00223       }
00224         
00225       /* suggest step size for next time-step */
00226       *h = h0;  
00227   
00228       return step_status;
00229     }
00230       
00231 #endif
00232     
00233     public:
00234       
00235     /// Control structure
00236     typedef struct
00237     {
00238       /// Absolute precision
00239       double eps_abs;
00240       /// Relative precision
00241       double eps_rel;
00242       /// Function scaling factor
00243       double a_y;
00244       /// Derivative scaling factor
00245       double a_dydt;
00246       /// Use standard or scaled algorithm
00247       bool standard;
00248 
00249     } gsl_ode_control;
00250 
00251     /// Control specification
00252     gsl_ode_control con;
00253     
00254     gsl_astep() {
00255 
00256       this->verbose=0;
00257 
00258       con.eps_abs=1.0e-6;
00259       con.eps_rel=0.0;
00260       con.a_y=1.0;
00261       con.a_dydt=0.0;
00262       con.standard=true;
00263 
00264       ndim=0;
00265       sdim=0;
00266     }
00267       
00268     virtual ~gsl_astep() {
00269       if (sdim!=0) delete[] scale_abs;
00270     }
00271     
00272     /// Set scalings
00273     template<class svec_t> int set_scale(size_t nscal, const svec_t &scale) {
00274       if (nscal>=sdim) {
00275         if (sdim!=0) delete[] scale_abs;
00276         scale_abs=new double[nscal];
00277         sdim=nscal;
00278       }
00279       for(size_t i=0;i<nscal;i++) {
00280         scale_abs[i]=scale[i];
00281       }
00282       return 0;
00283     }
00284 
00285     /** \brief Make an adaptive integration step of the system 
00286         \c derivs 
00287           
00288         This attempts to take a step of size \c h from the point \c x
00289         of an \c n-dimensional system \c derivs starting with \c y. On
00290         exit, \c x and \c y contain the new values at the end of the
00291         step and \c h contains the size of the step. 
00292     */
00293     virtual int astep(double &x, double &h, double xmax, size_t n, 
00294                       vec_t &y, param_t &pa, func_t &derivs) 
00295     {
00296 
00297       // The evolution object
00298       gsl_odeiv_evolve goe;
00299       
00300       ao.allocate(goe.y0,n);
00301       ao.allocate(goe.yerr,n);
00302       ao.allocate(goe.dydt_in,n);
00303       ao.allocate(goe.dydt_out,n);
00304         
00305       // Set up odeiv_evolve object
00306       goe.dimension=n;
00307 
00308       goe.count=0;
00309       goe.failed_steps=0;
00310       goe.last_step=0.0;
00311       
00312       int ret=evolve_apply(&goe,n,pa,derivs,&x,xmax,&h,y);
00313         
00314       if (this->verbose>0) {
00315         std::cout << x << " ";
00316         for(size_t j=0;j<n;j++) std::cout << y[j] << " ";
00317         std::cout << std::endl;
00318       }
00319 
00320       ao.free(goe.y0);
00321       ao.free(goe.yerr);
00322       ao.free(goe.dydt_in);
00323       ao.free(goe.dydt_out);
00324         
00325       return ret;
00326     }
00327 
00328     /** \brief Make an adaptive integration step of the system 
00329         \c derivs 
00330           
00331         This attempts to take a step of size \c h from the point \c x
00332         of an \c n-dimensional system \c derivs starting with \c y. On
00333         exit, \c x and \c y contain the new values at the end of the
00334         step and \c h contains the size of the step. 
00335     */
00336     virtual int astep_derivs(double &x, double &h, double xmax, size_t n, 
00337                              vec_t &y, vec_t &dydx, param_t &pa, 
00338                              func_t &derivs) 
00339     {
00340 
00341       // The evolution object
00342       gsl_odeiv_evolve goe;
00343       
00344       ao.allocate(goe.y0,n);
00345       ao.allocate(goe.yerr,n);
00346       ao.allocate(goe.dydt_in,n);
00347       ao.allocate(goe.dydt_out,n);
00348         
00349       // Set up odeiv_evolve object
00350       goe.dimension=n;
00351 
00352       goe.count=0;
00353       goe.failed_steps=0;
00354       goe.last_step=0.0;
00355       
00356       int ret=evolve_apply(&goe,n,pa,derivs,&x,xmax,&h,y);
00357 
00358       // Compute the final derivatives
00359       derivs(x,n,y,dydx,pa);
00360         
00361       if (this->verbose>0) {
00362         std::cout << x << " ";
00363         for(size_t j=0;j<n;j++) std::cout << y[j] << " ";
00364         std::cout << std::endl;
00365       }
00366 
00367       ao.free(goe.y0);
00368       ao.free(goe.yerr);
00369       ao.free(goe.dydt_in);
00370       ao.free(goe.dydt_out);
00371         
00372       return ret;
00373     }
00374       
00375   };
00376   
00377 #ifndef DOXYGENP
00378 }
00379 #endif
00380 
00381 #endif

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