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