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
,
O2scl Sourceforge Project Page