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