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} + 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} + 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 \future Fix so that memory allocation/deallocation is performed only 00217 when necessary 00218 \future Allow user to find out how many steps were taken, etc. 00219 00220 */ 00221 template<class param_t, class func_t=ode_funct<param_t>, 00222 class vec_t=ovector_view, class alloc_vec_t=ovector, 00223 class alloc_t=ovector_alloc> class gsl_astep : 00224 public adapt_step<param_t,func_t,vec_t,alloc_vec_t,alloc_t> { 00225 00226 #ifndef DOXYGEN_INTERNAL 00227 00228 protected: 00229 00230 /// Memory allocator for objects of type \c alloc_vec_t 00231 alloc_t ao; 00232 00233 /// Temporary storage for yout 00234 alloc_vec_t yout; 00235 00236 /// Temporary storage for dydx_out 00237 alloc_vec_t dydx_out; 00238 00239 /// The size of the last step 00240 double last_step; 00241 /// The number of steps (?) 00242 unsigned long int count; 00243 /// The number of failed steps 00244 unsigned long int failed_steps; 00245 00246 /// Apply the evolution for the next adaptive step 00247 int evolve_apply(double &t, double &h, double t1, size_t nvar, 00248 vec_t &y, vec_t &dydx, vec_t &yout2, vec_t &yerr, 00249 vec_t &dydx_out2, param_t &pa, func_t &derivs) { 00250 00251 double t0 = t; 00252 double h0 = h; 00253 int step_status; 00254 int final_step = 0; 00255 /* remaining time, possibly less than h */ 00256 double dt = t1 - t0; 00257 00258 if ((dt < 0.0 && h0 > 0.0) || (dt > 0.0 && h0 < 0.0)) { 00259 set_err("Step direction must match interval direction", 00260 gsl_einval); 00261 } 00262 00263 bool try_step_again=true; 00264 while(try_step_again) { 00265 00266 if ((dt >= 0.0 && h0 > dt) || (dt < 0.0 && h0 < dt)) { 00267 h0 = dt; 00268 final_step = 1; 00269 } else{ 00270 final_step = 0; 00271 } 00272 00273 step_status=this->stepp->step(t0,h0,nvar,y,dydx,yout2,yerr, 00274 dydx_out2,pa,derivs); 00275 00276 /* Check for stepper internal failure */ 00277 if (step_status != gsl_success) { 00278 // Notify user which step-size caused the failure 00279 h=h0; 00280 return step_status; 00281 } 00282 00283 count++; 00284 last_step = h0; 00285 if (final_step) { 00286 t = t1; 00287 } else{ 00288 t = t0 + h0; 00289 } 00290 00291 /* Check error and attempt to adjust the step. */ 00292 const int hadjust_status=con.hadjust(nvar,this->stepp->get_order(), 00293 yout2,yerr,dydx_out2,&h0); 00294 00295 if (hadjust_status == GSL_ODEIV_HADJ_DEC) { 00296 00297 /* Step was decreased. Undo and go back to try again. */ 00298 failed_steps++; 00299 try_step_again=true; 00300 } else { 00301 try_step_again=false; 00302 } 00303 00304 } 00305 00306 /* suggest step size for next time-step */ 00307 h = h0; 00308 00309 return step_status; 00310 } 00311 00312 #endif 00313 00314 public: 00315 00316 /// Control specification 00317 gsl_ode_control<vec_t> con; 00318 00319 gsl_astep() { 00320 this->verbose=0; 00321 } 00322 00323 virtual ~gsl_astep() { 00324 } 00325 00326 /** 00327 \brief Make an adaptive integration step of the system 00328 \c derivs with derivatives 00329 00330 This attempts to take a step of size \c h from the point \c 00331 x of an \c n-dimensional system \c derivs starting with \c y 00332 and given the initial derivatives \c dydx. On exit, \c x, \c 00333 y and \c dydx contain the new values at the end of the step, 00334 \c h contains the size of the step, \c dydx 00335 contains the derivative at the end of the step, and \c yerr 00336 contains the estimated error at the end of the step. 00337 */ 00338 virtual int astep_derivs(double &x, double &h, double xmax, 00339 size_t n, vec_t &y, vec_t &dydx, 00340 vec_t &yerr, param_t &pa, 00341 func_t &derivs) { 00342 count=0; 00343 failed_steps=0; 00344 last_step=0.0; 00345 00346 ao.allocate(yout,n); 00347 ao.allocate(dydx_out,n); 00348 00349 int ret=evolve_apply(x,h,xmax,n,y,dydx,yout,yerr,dydx_out, 00350 pa,derivs); 00351 00352 for(size_t i=0;i<n;i++) { 00353 y[i]=yout[i]; 00354 dydx[i]=dydx_out[i]; 00355 } 00356 00357 if (this->verbose>0) { 00358 std::cout << x << " "; 00359 for(size_t j=0;j<n;j++) std::cout << y[j] << " "; 00360 std::cout << std::endl; 00361 } 00362 00363 ao.free(yout); 00364 ao.free(dydx_out); 00365 00366 return ret; 00367 } 00368 00369 /** 00370 \brief Make an adaptive integration step of the system 00371 \c derivs 00372 00373 This attempts to take a step of size \c h from the point \c 00374 x of an \c n-dimensional system \c derivs starting with \c 00375 y. On exit, \c x and \c y contain the new values at the end 00376 of the step, \c h contains the size of the step, \c dydx_out 00377 contains the derivative at the end of the step, and \c yerr 00378 contains the estimated error at the end of the step. 00379 */ 00380 virtual int astep(double &x, double &h, double xmax, 00381 size_t n, vec_t &y, vec_t &u_dydx_out, 00382 vec_t &yerr, param_t &pa, 00383 func_t &derivs) { 00384 count=0; 00385 failed_steps=0; 00386 last_step=0.0; 00387 00388 alloc_vec_t dydx; 00389 ao.allocate(yout,n); 00390 ao.allocate(dydx,n); 00391 00392 int ret=derivs(x,n,y,dydx,pa); 00393 if (ret!=0) { 00394 add_err_ret("Initial derivative evalution failed in astep().", 00395 gsl_efailed); 00396 } 00397 00398 ret=evolve_apply(x,h,xmax,n,y,dydx,yout,yerr,u_dydx_out, 00399 pa,derivs); 00400 00401 for(size_t i=0;i<n;i++) { 00402 y[i]=yout[i]; 00403 } 00404 00405 if (this->verbose>0) { 00406 std::cout << x << " "; 00407 for(size_t j=0;j<n;j++) std::cout << y[j] << " "; 00408 std::cout << std::endl; 00409 } 00410 00411 ao.free(yout); 00412 ao.free(dydx); 00413 00414 return ret; 00415 } 00416 00417 }; 00418 00419 #ifndef DOXYGENP 00420 } 00421 #endif 00422 00423 #endif
Documentation generated with Doxygen and provided under the GNU Free Documentation License. See License Information for details.
Project hosting provided by
,
O2scl Sourceforge Project Page