![]() |
Object-oriented Scientific Computing Library: Version 0.910
|
00001 /* 00002 ------------------------------------------------------------------- 00003 00004 Copyright (C) 2006-2012, 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-initval2/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 <string> 00046 00047 #include <gsl/gsl_math.h> 00048 #include <gsl/gsl_odeiv.h> 00049 00050 #include <o2scl/adapt_step.h> 00051 00052 #ifndef DOXYGENP 00053 namespace o2scl { 00054 #endif 00055 00056 /** \brief Control structure for gsl_astep 00057 00058 This class implements both the "standard" and "scaled" step 00059 control methods from GSL. The standard control method is the 00060 default. To use the scaled control, set \ref standard to 00061 <tt>false</tt> and set the scale for each component using 00062 set_scale(). 00063 00064 The control object is a four parameter heuristic based on 00065 absolute and relative errors \ref eps_abs and \ref eps_rel, and 00066 scaling factors \ref a_y and \ref a_dydt for the system state 00067 \f$ y(t) \f$ and derivatives \f$ y^{\prime}(t) \f$ respectively. 00068 00069 The step-size adjustment procedure for this method begins by 00070 computing the desired error level \f$ D_i \f$ for each 00071 component. In the unscaled version, 00072 \f[ 00073 D_i = \mathrm{eps\_abs}+\mathrm{eps\_rel} \times 00074 \left( \mathrm{a\_y} | y_i| + \mathrm{a\_dydt}~h 00075 | y_i^{\prime}| \right) 00076 \f] 00077 while in the scaled version the user specifies the scale for 00078 each component, \f$ s_i \f$, 00079 \f[ 00080 D_i = \mathrm{eps\_abs}~s_i+\mathrm{eps\_rel} \times 00081 \left( \mathrm{a\_y} | y_i| + \mathrm{a\_dydt}~h 00082 | y_i^{\prime}| \right) 00083 \f] 00084 00085 The desired error level \f$ D_i \f$ is compared to then observed 00086 error \f$ E_i = |\mathrm{yerr}_i| \f$. If the observed error \f$ 00087 E \f$ exceeds the desired error level \f$ D \f$ by more than 10 00088 percent for any component then the method reduces the step-size 00089 by an appropriate factor, 00090 \f[ 00091 h_{\mathrm{new}} = S~h_{\mathrm{old}} 00092 \left(\frac{E}{D}\right)^{-1/q} 00093 \f] 00094 where \f$ q \f$ is the consistency order of the method (e.g. \f$ 00095 q=4 \f$ for 4(5) embedded RK), and \f$ S \f$ is a safety factor 00096 of 0.9. The ratio \f$ E/D \f$ is taken to be the maximum of the 00097 ratios \f$ E_i/D_i \f$. 00098 00099 If the observed error E is less than 50 percent of the desired 00100 error level \f$ D \f$ for the maximum ratio \f$ E_i/D_i \f$ then 00101 the algorithm takes the opportunity to increase the step-size to 00102 bring the error in line with the desired level, 00103 \f[ 00104 h_{\mathrm{new}} = S~h_{\mathrm{old}} 00105 \left(\frac{E}{D}\right)^{-1/(q+1)} 00106 \f] 00107 This encompasses all the standard error scaling methods. To 00108 avoid uncontrolled changes in the stepsize, the overall scaling 00109 factor is limited to the range 1/5 to 5. 00110 00111 If the user specified fewer scaling parameters than the number 00112 of ODEs, then the scaling parameters are reused as follows. If 00113 there are \f$ N \f$ ODEs and \f$ M \f$ scaling parameters, then 00114 for \f$ i>M \f$, the ith scaling parameter \f$ s_i \f$ is set to 00115 be \f$ s_{i\%M} \f$ . If the user selects the scaled control by 00116 setting \ref standard to <tt>false</tt> and no scale parameters 00117 are specified, this class reverts to the standard control. 00118 00119 \todo Double check that the improvements in the ode-initval2 00120 routines are available here 00121 */ 00122 template<class vec_t=ovector_base> class gsl_ode_control { 00123 00124 protected: 00125 00126 /// Scalings 00127 uvector scale_abs; 00128 00129 public: 00130 00131 /// \name Adjustment specification 00132 //@{ 00133 /// No adjustment required 00134 static const size_t hadj_nil=0; 00135 /// Recommend step decrease 00136 static const size_t hadj_dec=1; 00137 /// Recommend step increase 00138 static const size_t hadj_inc=2; 00139 //@} 00140 00141 /// Absolute precision (default \f$ 10^{-6} \f$) 00142 double eps_abs; 00143 /// Relative precision (default 0) 00144 double eps_rel; 00145 /// Function scaling factor (default 1) 00146 double a_y; 00147 /// Derivative scaling factor (default 0) 00148 double a_dydt; 00149 /// Use standard or scaled algorithm (default true) 00150 bool standard; 00151 00152 gsl_ode_control() { 00153 eps_abs=1.0e-6; 00154 eps_rel=0.0; 00155 a_y=1.0; 00156 a_dydt=0.0; 00157 standard=true; 00158 } 00159 00160 virtual ~gsl_ode_control() { 00161 } 00162 00163 /** \brief Set the scaling for each differential equation 00164 */ 00165 template<class svec_t> int set_scale(size_t nscal, const svec_t &scale) { 00166 scale_abs.allocate(nscal); 00167 for(size_t i=0;i<nscal;i++) { 00168 scale_abs[i]=scale[i]; 00169 } 00170 return 0; 00171 } 00172 00173 /// Automatically adjust step-size 00174 virtual int hadjust(size_t dim, unsigned int ord, const vec_t &y, 00175 vec_t &yerr, vec_t &yp, double &h) { 00176 00177 const double S=0.9; 00178 const double h_old=h; 00179 00180 double rmax=DBL_MIN; 00181 size_t i; 00182 00183 for(i=0;i<dim;i++) { 00184 double D0; 00185 if (standard || scale_abs.size()==0) { 00186 D0=eps_rel*(a_y*fabs(y[i])+a_dydt*fabs(h_old*yp[i]))+eps_abs; 00187 } else { 00188 D0=eps_rel*(a_y*fabs(y[i])+a_dydt*fabs(h_old*yp[i]))+ 00189 eps_abs*scale_abs[i%scale_abs.size()]; 00190 } 00191 const double r=fabs(yerr[i]) / fabs(D0); 00192 rmax=GSL_MAX_DBL(r, rmax); 00193 } 00194 00195 if (rmax > 1.1) { 00196 00197 /* decrease step, no more than factor of 5, but a fraction S more 00198 than scaling suggests (for better accuracy) */ 00199 double r= S / pow(rmax, 1.0/ord); 00200 00201 if (r < 0.2) { 00202 r=0.2; 00203 } 00204 h=r*h_old; 00205 return 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 if (r > 5.0) { 00212 r=5.0; 00213 } 00214 /* don't allow any decrease caused by S<1 */ 00215 if (r < 1.0) { 00216 r=1.0; 00217 } 00218 h=r*h_old; 00219 return hadj_inc; 00220 00221 } else { 00222 00223 /* no change */ 00224 return hadj_nil; 00225 00226 } 00227 00228 } 00229 00230 }; 00231 00232 /** \brief Adaptive ODE stepper (GSL) 00233 00234 This class performs an adaptive step of a system of ODEs. To 00235 modify the ODE stepper which is used, use the function \ref 00236 adapt_step::set_step(). 00237 00238 Note, this has been updated to correspond to the 00239 <tt>ode-initval2</tt> functions in GSL. 00240 00241 There is an example for the usage of this class in 00242 <tt>examples/ex_ode.cpp</tt> documented in the \ref ex_ode_sect 00243 section. 00244 00245 \todo Document what happens when the stepper function returns 00246 a non-zero value, as it's different now with the ode-initval2 00247 function. 00248 \todo Document count, failed_steps, etc. 00249 \future Compare more directly to GSL 00250 00251 Default template arguments 00252 - \c func_t - \ref ode_funct 00253 - \c vec_t - \ref ovector_base 00254 - \c alloc_vec_t - \ref ovector 00255 - \c alloc_t - \ref ovector_alloc 00256 */ 00257 template<class func_t=ode_funct<>, class vec_t=ovector_base, 00258 class alloc_vec_t=ovector, class alloc_t=ovector_alloc> 00259 class gsl_astep : public adapt_step<func_t,vec_t,alloc_vec_t,alloc_t> { 00260 00261 #ifndef DOXYGEN_INTERNAL 00262 00263 protected: 00264 00265 /// Memory allocator for objects of type \c alloc_vec_t 00266 alloc_t ao; 00267 00268 /// Temporary storage for yout 00269 alloc_vec_t yout_int; 00270 00271 /// Internal storage for dydx 00272 alloc_vec_t dydx_int; 00273 00274 /// The size of the last step 00275 double last_step; 00276 00277 /// The number of steps 00278 unsigned long int count; 00279 00280 /// The number of failed steps 00281 unsigned long int failed_steps; 00282 00283 /// The size of the allocated vectors 00284 size_t msize; 00285 00286 /** \brief Apply the evolution for the next adaptive step 00287 00288 This function is based on <tt>gsl_odeiv2_evolve_apply</tt>. 00289 00290 \note This function requres that \c y, \c yout, \c dydx and \c 00291 dydx_out are all distinct vectors. 00292 */ 00293 int evolve_apply(double t0, double t1, double &t, double &h, size_t nvar, 00294 vec_t &y, vec_t &dydx, vec_t &yout, vec_t &yerr, 00295 vec_t &dydx_out, func_t &derivs) { 00296 00297 double h0=h; 00298 int step_status; 00299 bool final_step=false; 00300 // Remaining step size, possibly less than h 00301 double dt=t1-t0; 00302 00303 if ((dt < 0.0 && h0 > 0.0) || (dt > 0.0 && h0 < 0.0)) { 00304 std::string str="Interval direction (t1-t0="+dtos(dt)+ 00305 ") does not match step direction (h="+dtos(h)+ 00306 " in gsl_astep::evolve_apply()."; 00307 O2SCL_ERR_RET(str.c_str(),gsl_einval); 00308 } 00309 00310 // Copy the initial point from y to yout. The stepper won't modify 00311 // y, so we can undo the step by going back to the values in y. 00312 vector_copy(nvar,y,yout); 00313 00314 bool try_step_again=true; 00315 while (try_step_again) { 00316 try_step_again=false; 00317 00318 if ((dt >= 0.0 && h0 > dt) || (dt < 0.0 && h0 < dt)) { 00319 h0=dt; 00320 final_step=true; 00321 } else{ 00322 final_step=false; 00323 } 00324 00325 step_status=this->stepp->step(t0,h0,nvar,yout,dydx,yout,yerr, 00326 dydx_out,derivs); 00327 00328 // [GSL] Return if the stepper indicates a pointer or user function 00329 // failure 00330 if (step_status == gsl_efault || step_status==gsl_ebadfunc) { 00331 return step_status; 00332 } 00333 00334 // [GSL] Check for stepper internal failure 00335 if (step_status!=gsl_success) { 00336 00337 // [GSL] Stepper was unable to calculate step. Try decreasing 00338 // stepsize 00339 double h_old=h0; 00340 h0*=0.5; 00341 00342 // [GSL] Check that an actual decrease in h0 occured and that 00343 // the suggested h0 will change the independent variable 00344 // will change by at least 1ulp 00345 //double t_curr=GSL_COERCE_DBL(t); 00346 //double t_next=GSL_COERCE_DBL(t+h0); 00347 double t_curr=t; 00348 double t_next=t+h0; 00349 00350 if (fabs(h0) < fabs(h_old) && t_next != t_curr) { 00351 // [GSL] Step was decreased. Undo step, and try again with 00352 // new h0. 00353 vector_copy(nvar,y,yout); 00354 failed_steps++; 00355 try_step_again=true; 00356 } else { 00357 // [GSL] notify user of step-size which caused the failure 00358 h=h0; 00359 // [GSL] restore original t value 00360 t=t0; 00361 return step_status; 00362 } 00363 } 00364 00365 if (try_step_again==false) { 00366 00367 count++; 00368 last_step=h0; 00369 if (final_step) { 00370 t=t1; 00371 } else{ 00372 t=t0+h0; 00373 } 00374 00375 // [GSL] Check error and attempt to adjust the step. 00376 double h_old=h0; 00377 const size_t hadjust_status=con.hadjust 00378 (nvar,this->stepp->get_order(),yout,yerr,dydx_out,h0); 00379 00380 if (hadjust_status == gsl_ode_control<vec_t>::hadj_dec) { 00381 00382 // [GSL] Check that the reported status is correct (i.e. an actual 00383 // decrease in h0 occured) and the suggested h0 will change 00384 // the independent variable by at least 1 ulp 00385 00386 //double t_curr=GSL_COERCE_DBL (*t); 00387 //double t_next=GSL_COERCE_DBL ((*t) + h0); 00388 double t_curr=t; 00389 double t_next=t+h0; 00390 00391 if (fabs(h0) < fabs(h_old) && t_next != t_curr) { 00392 // [GSL] Step was decreased. Undo step, and try again with 00393 // new h0. 00394 vector_copy(nvar,y,yout); 00395 failed_steps++; 00396 try_step_again=true; 00397 } else { 00398 /* [GSL] Can not obtain required error tolerance, and can 00399 not decrease step-size any further, so give up and return 00400 gsl_failure. 00401 */ 00402 // [GSL] notify user of step-size which caused the failure 00403 h=h0; 00404 return gsl_failure; 00405 } 00406 } 00407 } 00408 00409 } 00410 00411 if (final_step==false) { 00412 // [GSL] Suggest step size for next time-step. Change of step 00413 // size is not suggested in the final step, because that step 00414 // can be very small compared to previous step, to reach t1. 00415 h=h0; 00416 } 00417 00418 // This used to be return step_status, but the code never 00419 // reaches this point if step_status is anything other than zero. 00420 return step_status; 00421 } 00422 00423 #endif 00424 00425 public: 00426 00427 gsl_astep() { 00428 this->verbose=0; 00429 msize=0; 00430 } 00431 00432 virtual ~gsl_astep() { 00433 if (msize>0) { 00434 ao.free(yout_int); 00435 ao.free(dydx_int); 00436 } 00437 } 00438 00439 /// Control specification 00440 gsl_ode_control<vec_t> con; 00441 00442 /** \brief Make an adaptive integration step of the system 00443 \c derivs 00444 00445 This attempts to take a step of size \c h from the point \c 00446 x of an \c n-dimensional system \c derivs starting with \c 00447 y. On exit, \c x and \c y contain the new values at the end 00448 of the step, \c h contains the size of the step, \c dydx_out 00449 contains the derivative at the end of the step, and \c yerr 00450 contains the estimated error at the end of the step. 00451 */ 00452 virtual int astep(double &x, double xmax, double &h, 00453 size_t n, vec_t &y, vec_t &dydx_out, 00454 vec_t &yerr, func_t &derivs) { 00455 count=0; 00456 failed_steps=0; 00457 last_step=0.0; 00458 00459 if (n!=msize) { 00460 ao.allocate(yout_int,n); 00461 ao.allocate(dydx_int,n); 00462 msize=n; 00463 } 00464 00465 int ret=derivs(x,n,y,dydx_int); 00466 if (ret!=0) return ret; 00467 00468 ret=evolve_apply(x,xmax,x,h,n,y,dydx_int,yout_int,yerr,dydx_out,derivs); 00469 00470 if (ret==gsl_success) { 00471 for(size_t i=0;i<n;i++) { 00472 y[i]=yout_int[i]; 00473 } 00474 } 00475 00476 if (this->verbose>0) { 00477 std::cout << "gsl_astep step:"; 00478 std::cout << x << " "; 00479 for(size_t j=0;j<n;j++) std::cout << y[j] << " "; 00480 std::cout << std::endl; 00481 } 00482 00483 return ret; 00484 } 00485 00486 /** \brief Make an adaptive integration step of the system 00487 \c derivs with derivatives 00488 00489 This attempts to take a step of size \c h from the point \c x 00490 of an \c n-dimensional system \c derivs starting with \c y and 00491 given the initial derivatives \c dydx. On exit, \c x, \c y and 00492 \c dydx contain the new values at the end of the step, \c h 00493 contains the size of the step, \c dydx contains the derivative 00494 at the end of the step, and \c yerr contains the estimated 00495 error at the end of the step. 00496 */ 00497 virtual int astep_derivs(double &x, double xmax, double &h, 00498 size_t n, vec_t &y, vec_t &dydx, 00499 vec_t &yerr, func_t &derivs) { 00500 count=0; 00501 failed_steps=0; 00502 last_step=0.0; 00503 00504 if (n!=msize) { 00505 ao.allocate(yout_int,n); 00506 ao.allocate(dydx_int,n); 00507 msize=n; 00508 } 00509 00510 int ret=evolve_apply(x,xmax,x,h,n,y,dydx,yout_int,yerr,dydx_int,derivs); 00511 00512 if (ret==gsl_success) { 00513 for(size_t i=0;i<n;i++) { 00514 y[i]=yout_int[i]; 00515 dydx[i]=dydx_int[i]; 00516 } 00517 } 00518 00519 if (this->verbose>0) { 00520 std::cout << "gsl_astep step:"; 00521 std::cout << x << " "; 00522 for(size_t j=0;j<n;j++) std::cout << y[j] << " "; 00523 std::cout << std::endl; 00524 } 00525 00526 return ret; 00527 } 00528 00529 /** \brief Make an adaptive integration step of the system 00530 \c derivs 00531 00532 This function performs an adaptive integration step with the 00533 \c n-dimensional system \c derivs and parameter \c pa. It 00534 Begins at \c x with initial stepsize \c h, ensuring that the 00535 step goes no farther than \c xmax. At the end of the step, the 00536 size of the step taken is \c h and the new value of \c x is in 00537 \c x_out. Initially, the function values and derivatives 00538 should be specified in \c y and \c dydx. The function values, 00539 derivatives, and the error at the end of the step are given in 00540 \c yout, \c yerr, and \c dydx_out. Unlike in \c ode_step 00541 objects, the objects \c y, \c yout, \c dydx, and \c dydx_out 00542 must all be distinct. 00543 00544 This adaptive stepper function is faster than astep() or 00545 astep_derivs() because it does not require any copying of 00546 vectors. 00547 */ 00548 virtual int astep_full(double x, double xmax, double &x_out, double &h, 00549 size_t n, vec_t &y, vec_t &dydx, 00550 vec_t &yout, vec_t &yerr, vec_t &dydx_out, 00551 func_t &derivs) { 00552 00553 count=0; 00554 failed_steps=0; 00555 last_step=0.0; 00556 00557 int ret=evolve_apply(x,xmax,x_out,h,n,y,dydx,yout,yerr,dydx_out, 00558 derivs); 00559 00560 if (this->verbose>0) { 00561 std::cout << "gsl_astep step:"; 00562 std::cout << x_out << " "; 00563 for(size_t j=0;j<n;j++) std::cout << yout[j] << " "; 00564 std::cout << std::endl; 00565 } 00566 00567 return ret; 00568 } 00569 00570 }; 00571 00572 #ifndef DOXYGENP 00573 } 00574 #endif 00575 00576 #endif
Documentation generated with Doxygen. Provided under the GNU Free Documentation License (see License Information).