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 control, 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 scaling 00108 factor is limited to the range 1/5 to 5. 00109 00110 If the user specified fewer scaling parameters than the number 00111 of ODEs, then the scaling parameters are reused as follows. If 00112 there are \f$ N \f$ ODEs and \f$ M \f$ scaling parameters, then 00113 for \f$ i>M \f$, the ith scaling parameter \f$ s_i \f$ is set to 00114 be \f$ s_{i\%M} \f$ . If the user selects the scaled control by 00115 setting \ref standard to <tt>false</tt> and no scale parameters 00116 are specified, this class reverts to the standard control. 00117 */ 00118 template<class vec_t> class gsl_ode_control { 00119 00120 protected: 00121 00122 /// Number of scalings 00123 size_t sdim; 00124 00125 /// Scalings 00126 double *scale_abs; 00127 00128 public: 00129 00130 /// Absolute precision (default \f$ 10^{-6} \f$) 00131 double eps_abs; 00132 /// Relative precision (default 0) 00133 double eps_rel; 00134 /// Function scaling factor (default 1) 00135 double a_y; 00136 /// Derivative scaling factor (default 0) 00137 double a_dydt; 00138 /// Use standard or scaled algorithm (default true) 00139 bool standard; 00140 00141 gsl_ode_control() { 00142 eps_abs=1.0e-6; 00143 eps_rel=0.0; 00144 a_y=1.0; 00145 a_dydt=0.0; 00146 standard=true; 00147 00148 sdim=0; 00149 } 00150 00151 virtual ~gsl_ode_control() { 00152 if (sdim!=0) delete[] scale_abs; 00153 } 00154 00155 /** 00156 \brief Set the scaling for each differential equation 00157 */ 00158 template<class svec_t> int set_scale(size_t nscal, const svec_t &scale) { 00159 if (nscal>=sdim) { 00160 if (sdim!=0) delete[] scale_abs; 00161 scale_abs=new double[nscal]; 00162 sdim=nscal; 00163 } 00164 for(size_t i=0;i<nscal;i++) { 00165 scale_abs[i]=scale[i]; 00166 } 00167 return 0; 00168 } 00169 00170 /// Automatically adjust step-size 00171 virtual int hadjust(size_t dim, unsigned int ord, const vec_t &y, 00172 vec_t &yerr, vec_t &yp, double &h) { 00173 00174 const double S = 0.9; 00175 const double h_old = h; 00176 00177 double rmax = DBL_MIN; 00178 size_t i; 00179 00180 for(i=0; i<dim; i++) { 00181 double D0; 00182 if (standard || sdim==0) { 00183 D0=eps_rel*(a_y*fabs(y[i])+a_dydt*fabs(h_old*yp[i]))+eps_abs; 00184 } else { 00185 D0=eps_rel*(a_y*fabs(y[i])+a_dydt*fabs(h_old*yp[i]))+ 00186 eps_abs*scale_abs[i%sdim]; 00187 } 00188 const double r=fabs(yerr[i]) / fabs(D0); 00189 rmax = GSL_MAX_DBL(r, rmax); 00190 } 00191 00192 if(rmax > 1.1) { 00193 00194 /* decrease step, no more than factor of 5, but a fraction S more 00195 than scaling suggests (for better accuracy) */ 00196 00197 double r = S / pow(rmax, 1.0/ord); 00198 00199 if (r < 0.2) { 00200 r = 0.2; 00201 } 00202 00203 h = r * h_old; 00204 00205 return GSL_ODEIV_HADJ_DEC; 00206 00207 } else if (rmax < 0.5) { 00208 00209 /* increase step, no more than factor of 5 */ 00210 double r = S / pow(rmax, 1.0/(ord+1.0)); 00211 00212 if (r > 5.0) { 00213 r = 5.0; 00214 } 00215 00216 /* don't allow any decrease caused by S<1 */ 00217 if (r < 1.0) { 00218 r = 1.0; 00219 } 00220 00221 h = r * h_old; 00222 00223 return GSL_ODEIV_HADJ_INC; 00224 00225 } else { 00226 00227 /* no change */ 00228 return GSL_ODEIV_HADJ_NIL; 00229 00230 } 00231 00232 } 00233 00234 }; 00235 00236 /** 00237 \brief Adaptive ODE stepper (GSL) 00238 00239 To modify the ODE stepper which is used, use the 00240 adapt_step::set_step(). 00241 00242 Default template arguments 00243 - \c param_t - (no default) 00244 - \c func_t - \ref ode_funct 00245 - \c vec_t - \ref ovector_base 00246 - \c alloc_vec_t - \ref ovector 00247 - \c alloc_t - \ref ovector_alloc 00248 */ 00249 template<class param_t, class func_t=ode_funct<param_t>, 00250 class vec_t=ovector_base, class alloc_vec_t=ovector, 00251 class alloc_t=ovector_alloc> class gsl_astep : 00252 public adapt_step<param_t,func_t,vec_t,alloc_vec_t,alloc_t> { 00253 00254 #ifndef DOXYGEN_INTERNAL 00255 00256 protected: 00257 00258 /// Memory allocator for objects of type \c alloc_vec_t 00259 alloc_t ao; 00260 00261 /// Temporary storage for yout 00262 alloc_vec_t yout; 00263 00264 /// Internal storage for dydx 00265 alloc_vec_t dydx_int; 00266 00267 /// The size of the last step 00268 double last_step; 00269 00270 /// The number of steps 00271 unsigned long int count; 00272 00273 /// The number of failed steps 00274 unsigned long int failed_steps; 00275 00276 /// The size of the allocated vectors 00277 size_t msize; 00278 00279 /// Apply the evolution for the next adaptive step 00280 int evolve_apply(double t0, double &t, double &h, double t1, size_t nvar, 00281 vec_t &y, vec_t &dydx, vec_t &yout2, vec_t &yerr, 00282 vec_t &dydx_out2, param_t &pa, func_t &derivs) { 00283 00284 double h0 = h; 00285 int step_status; 00286 int final_step = 0; 00287 /* remaining step size, possibly less than h */ 00288 double dt = t1 - t0; 00289 00290 if ((dt < 0.0 && h0 > 0.0) || (dt > 0.0 && h0 < 0.0)) { 00291 O2SCL_ERR2("Step direction must match interval direction in ", 00292 "gsl_astep::evolve_apply().",gsl_einval); 00293 } 00294 00295 bool try_step_again=true; 00296 while(try_step_again) { 00297 00298 if ((dt >= 0.0 && h0 > dt) || (dt < 0.0 && h0 < dt)) { 00299 h0 = dt; 00300 final_step = 1; 00301 } else{ 00302 final_step = 0; 00303 } 00304 00305 step_status=this->stepp->step(t0,h0,nvar,y,dydx,yout2,yerr, 00306 dydx_out2,pa,derivs); 00307 00308 /* Check for stepper internal failure */ 00309 if (step_status != gsl_success) { 00310 // Notify user which step-size caused the failure 00311 h=h0; 00312 return step_status; 00313 } 00314 00315 count++; 00316 last_step = h0; 00317 if (final_step) { 00318 t = t1; 00319 } else{ 00320 t = t0 + h0; 00321 } 00322 00323 /* Check error and attempt to adjust the step. */ 00324 const int hadjust_status=con.hadjust(nvar,this->stepp->get_order(), 00325 yout2,yerr,dydx_out2,h0); 00326 00327 if (hadjust_status == GSL_ODEIV_HADJ_DEC) { 00328 00329 /* Step was decreased. Undo and go back to try again. */ 00330 failed_steps++; 00331 try_step_again=true; 00332 } else { 00333 try_step_again=false; 00334 } 00335 00336 } 00337 00338 /* suggest step size for next time-step */ 00339 h = h0; 00340 00341 // This used to be return step_status, but the code never 00342 // reaches this point if step_status is anything other than zero. 00343 return gsl_success; 00344 } 00345 00346 #endif 00347 00348 public: 00349 00350 /// Control specification 00351 gsl_ode_control<vec_t> con; 00352 00353 gsl_astep() { 00354 this->verbose=0; 00355 msize=0; 00356 } 00357 00358 virtual ~gsl_astep() { 00359 if (msize>0) { 00360 ao.free(yout); 00361 ao.free(dydx_int); 00362 } 00363 } 00364 00365 /** 00366 \brief Make an adaptive integration step of the system 00367 \c derivs with derivatives 00368 00369 This attempts to take a step of size \c h from the point \c 00370 x of an \c n-dimensional system \c derivs starting with \c y 00371 and given the initial derivatives \c dydx. On exit, \c x, \c 00372 y and \c dydx contain the new values at the end of the step, 00373 \c h contains the size of the step, \c dydx 00374 contains the derivative at the end of the step, and \c yerr 00375 contains the estimated error at the end of the step. 00376 00377 If the base stepper returns a non-zero value, the step is 00378 aborted and \c y is unmodified. The error handler is never 00379 called. 00380 */ 00381 virtual int astep_derivs(double &x, double &h, double xmax, 00382 size_t n, vec_t &y, vec_t &dydx, 00383 vec_t &yerr, param_t &pa, func_t &derivs) { 00384 count=0; 00385 failed_steps=0; 00386 last_step=0.0; 00387 00388 if (n!=msize) { 00389 ao.allocate(yout,n); 00390 ao.allocate(dydx_int,n); 00391 msize=n; 00392 } 00393 00394 int ret=evolve_apply(x,x,h,xmax,n,y,dydx,yout,yerr,dydx_int, 00395 pa,derivs); 00396 00397 if (ret==gsl_success) { 00398 for(size_t i=0;i<n;i++) { 00399 y[i]=yout[i]; 00400 dydx[i]=dydx_int[i]; 00401 } 00402 } 00403 00404 if (this->verbose>0) { 00405 std::cout << x << " "; 00406 for(size_t j=0;j<n;j++) std::cout << y[j] << " "; 00407 std::cout << std::endl; 00408 } 00409 00410 return ret; 00411 } 00412 00413 /** \brief Make an adaptive integration step of the system 00414 \c derivs 00415 00416 This function performs an adaptive integration step with the 00417 \c n-dimensional system \c derivs and parameter \c pa. It 00418 Begins at \c x with initial stepsize \c h, ensuring that the 00419 step goes no farther than \c xmax. At the end of the step, the 00420 size of the step taken is \c h and the new value of \c x is in 00421 \c x_out. Initially, the function values and derivatives 00422 should be specified in \c y and \c dydx. The function values, 00423 derivatives, and the error at the end of the step are given in 00424 \c yout, \c yerr, and \c dydx_out. Unlike in \c odestep 00425 objects, the objects \c y, \c yout, \c dydx, and \c dydx_out 00426 must all be distinct. 00427 00428 If the base stepper returns a non-zero value, the step is 00429 aborted. The error handler is never called. 00430 00431 This adaptive stepper function is faster than astep() or 00432 astep_derivs() because it does not require any copying of 00433 vectors. 00434 */ 00435 virtual int astep_full(double x, double &h, double xmax, double &x_out, 00436 size_t n, vec_t &y, vec_t &dydx, 00437 vec_t &yout2, vec_t &yerr, vec_t &dydx_out, 00438 param_t &pa, func_t &derivs) { 00439 00440 count=0; 00441 failed_steps=0; 00442 last_step=0.0; 00443 00444 int ret=evolve_apply(x,x_out,h,xmax,n,y,dydx,yout2,yerr,dydx_out, 00445 pa,derivs); 00446 00447 if (this->verbose>0) { 00448 std::cout << x_out << " "; 00449 for(size_t j=0;j<n;j++) std::cout << yout2[j] << " "; 00450 std::cout << std::endl; 00451 } 00452 00453 return ret; 00454 } 00455 00456 /** 00457 \brief Make an adaptive integration step of the system 00458 \c derivs 00459 00460 This attempts to take a step of size \c h from the point \c 00461 x of an \c n-dimensional system \c derivs starting with \c 00462 y. On exit, \c x and \c y contain the new values at the end 00463 of the step, \c h contains the size of the step, \c dydx_out 00464 contains the derivative at the end of the step, and \c yerr 00465 contains the estimated error at the end of the step. 00466 00467 If the system \c derivs or the base stepper return a non-zero 00468 value, the adaptive step is aborted and \c y is unmodified. 00469 The error handler is never called. 00470 */ 00471 virtual int astep(double &x, double &h, double xmax, 00472 size_t n, vec_t &y, vec_t &dydx_out, 00473 vec_t &yerr, param_t &pa, func_t &derivs) { 00474 count=0; 00475 failed_steps=0; 00476 last_step=0.0; 00477 00478 if (n!=msize) { 00479 ao.allocate(yout,n); 00480 ao.allocate(dydx_int,n); 00481 msize=n; 00482 } 00483 00484 int ret=derivs(x,n,y,dydx_int,pa); 00485 if (ret!=0) return ret; 00486 00487 ret=evolve_apply(x,x,h,xmax,n,y,dydx_int,yout,yerr,dydx_out, 00488 pa,derivs); 00489 00490 if (ret==gsl_success) { 00491 for(size_t i=0;i<n;i++) { 00492 y[i]=yout[i]; 00493 } 00494 } 00495 00496 if (this->verbose>0) { 00497 std::cout << x << " "; 00498 for(size_t j=0;j<n;j++) std::cout << y[j] << " "; 00499 std::cout << std::endl; 00500 } 00501 00502 return ret; 00503 } 00504 00505 }; 00506 00507 #ifndef DOXYGENP 00508 } 00509 #endif 00510 00511 #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