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 #ifndef O2SCL_ODE_IV_SOLVE_H 00024 #define O2SCL_ODE_IV_SOLVE_H 00025 00026 #include <string> 00027 #include <o2scl/collection.h> 00028 #include <o2scl/adapt_step.h> 00029 #include <o2scl/gsl_astep.h> 00030 00031 #ifndef DOXYGENP 00032 namespace o2scl { 00033 #endif 00034 00035 /** 00036 \brief Solve an initial-value ODE problems given an adaptive ODE 00037 stepper 00038 */ 00039 template<class param_t, class func_t=ode_funct<param_t>, 00040 class vec_t=ovector_base, class alloc_vec_t=ovector, 00041 class alloc_t=ovector_alloc> class ode_iv_solve { 00042 00043 #ifndef DOXYGEN_INTERNAL 00044 00045 protected: 00046 00047 /// Memory allocator 00048 alloc_t ao; 00049 00050 /// The adaptive stepper 00051 adapt_step<param_t,func_t,vec_t,alloc_vec_t,alloc_t> *astp; 00052 00053 /// Print out iteration information 00054 virtual int print_iter(double x, size_t nv, vec_t &y) { 00055 std::cout << type() << " x: " << x << " y: "; 00056 for(size_t i=0;i<nv;i++) std::cout << y[i] << " "; 00057 std::cout << std::endl; 00058 if (verbose>1) { 00059 char ch; 00060 std::cin >> ch; 00061 } 00062 return 0; 00063 } 00064 00065 #endif 00066 00067 public: 00068 00069 ode_iv_solve() { 00070 verbose=0; 00071 ntrial=1000; 00072 nsteps_out=10; 00073 astp=&gsl_astp; 00074 exit_on_fail=true; 00075 } 00076 00077 virtual ~ode_iv_solve() {} 00078 00079 /// Set the adaptive stepper to use 00080 int set_adapt_step(adapt_step<param_t,func_t,vec_t, 00081 alloc_vec_t,alloc_t> &as) { 00082 astp=&as; 00083 return 0; 00084 } 00085 00086 /** 00087 \brief Solve the initial-value problem and output a table 00088 00089 Initially, \c x_sol should be a vector of size \c n_sol, and \c 00090 y_sol, \c dydx_sol, and \c yerr_sol should be matrices with size 00091 \c [n_sol][n]. On exit, \c n_sol will will be the size of the 00092 solution table, less than or equal to the original value of \c 00093 n_sol. This function avoids performing extra calls to the 00094 adaptive stepper, and the table will be approximately evenly 00095 spaced. 00096 00097 This template function works with any matrix class \c mat_t 00098 which can be accessed using successive applications of 00099 operator[] and which has an associated class \c mat_row2_t 00100 which returns a row of a matrix of type \c mat_t as 00101 an object with type \c vec_t. 00102 00103 If \ref verbose is greater than zero, The solution at each 00104 internal point will be written to \c std::cout. If \ref verbose 00105 is greater than one, a character will be required after each 00106 point. 00107 00108 \comment 00109 10/23/09 - Some copying can be avoided by removing the "yerrl" 00110 and "dydx2" temporary vectors, for example. 00111 \endcomment 00112 */ 00113 template<class mat_t, class mat_row2_t> 00114 int solve_table(double x0, double x1, double h, size_t n, 00115 vec_t &ystart, size_t &n_sol, vec_t &x_sol, 00116 mat_t &y_sol, mat_t &dydx_sol, mat_t &yerr_sol, 00117 param_t &pa, func_t &derivs) { 00118 00119 int ret=0; 00120 int first_ret=0; 00121 size_t nmax=n_sol; 00122 nsteps=0; 00123 00124 // Stepsize for next verbose output. Use nsteps_out-1 instead of 00125 // nsteps_out since the first point is always output below. 00126 double dx_verb=(x1-x0)/((double)(nsteps_out-1)); 00127 // Stepsize for next table entry 00128 double dx_tab=(x1-x0)/((double)(n_sol-1)); 00129 00130 double x_verb=x0+dx_verb, x_tab=x0+dx_tab; 00131 00132 // Output first point 00133 if (verbose>0) { 00134 print_iter(x0,n,ystart); 00135 if (verbose>1) { 00136 char ch; 00137 std::cin >> ch; 00138 } 00139 } 00140 00141 // Allocate space for errors and derivatives 00142 alloc_vec_t yerrl, dydx, dydx2; 00143 ao.allocate(yerrl,n); 00144 ao.allocate(dydx,n); 00145 ao.allocate(dydx2,n); 00146 00147 // Initial derivative evaulation 00148 derivs(x0,n,ystart,dydx,pa); 00149 00150 // Add first point to table 00151 x_sol[0]=x0; 00152 for(size_t j=0;j<n;j++) { 00153 y_sol[0][j]=ystart[j]; 00154 dydx_sol[0][j]=dydx[j]; 00155 yerr_sol[0][j]=0.0; 00156 } 00157 00158 // Copy first point to table again for first step 00159 size_t icurr=1; 00160 x_sol[1]=x0; 00161 for(size_t j=0;j<n;j++) y_sol[1][j]=ystart[j]; 00162 00163 double xnext; 00164 00165 bool done=false; 00166 while (done==false) { 00167 00168 // Use ystart as temporary storage for the end of the current 00169 // adaptive step 00170 00171 mat_row2_t yrow(y_sol,icurr); 00172 ret=astp->astep_full(x0,h,x1,xnext,n,yrow,dydx,ystart,yerrl, 00173 dydx2,pa,derivs); 00174 00175 if (ret!=0) { 00176 if (exit_on_fail) { 00177 n_sol=icurr+1; 00178 00179 ao.free(dydx); 00180 ao.free(dydx2); 00181 ao.free(yerrl); 00182 00183 // call error handler 00184 O2SCL_ERR2_RET("Adaptive stepper returned non-zero in ", 00185 "ode_iv_solve::solve_table().",gsl_efailed); 00186 } else if (first_ret==0) { 00187 first_ret=ret; 00188 } 00189 } 00190 00191 // Update step counter and abort if necessary 00192 nsteps++; 00193 if (nsteps>ntrial) { 00194 ao.free(dydx); 00195 ao.free(dydx2); 00196 ao.free(yerrl); 00197 00198 O2SCL_ERR2_RET("Too many steps required in ", 00199 "ode_iv_solve::solve_table().",gsl_emaxiter); 00200 } 00201 00202 // If we've made enough progress, do verbose output 00203 if (xnext>=x_verb && verbose>0) { 00204 print_iter(xnext,n,ystart); 00205 if (verbose>1) { 00206 char ch; 00207 std::cin >> ch; 00208 } 00209 x_verb+=dx_verb; 00210 } 00211 00212 // If we're at the end 00213 if (xnext>=x1) { 00214 00215 // Exit the loop 00216 done=true; 00217 00218 // Store the final entry in the table 00219 x_sol[icurr]=xnext; 00220 for(size_t j=0;j<n;j++) { 00221 y_sol[icurr][j]=ystart[j]; 00222 dydx_sol[icurr][j]=dydx2[j]; 00223 yerr_sol[icurr][j]=yerrl[j]; 00224 } 00225 00226 // Update the table size 00227 n_sol=icurr+1; 00228 00229 } else { 00230 00231 if (xnext>=x_tab && icurr!=nmax-1) { 00232 00233 // If we've made enough progress, store an entry in the table 00234 x_sol[icurr]=xnext; 00235 for(size_t j=0;j<n;j++) { 00236 y_sol[icurr][j]=ystart[j]; 00237 dydx_sol[icurr][j]=dydx2[j]; 00238 yerr_sol[icurr][j]=yerrl[j]; 00239 00240 // Also prepare for the next step 00241 y_sol[icurr+1][j]=ystart[j]; 00242 dydx[j]=dydx2[j]; 00243 } 00244 x_tab+=dx_tab; 00245 00246 // Update x0 and the current and next indicies 00247 x0=xnext; 00248 icurr++; 00249 00250 } else { 00251 00252 // Otherwise, just prepare for the next step 00253 x0=xnext; 00254 for(size_t j=0;j<n;j++) { 00255 dydx[j]=dydx2[j]; 00256 y_sol[icurr][j]=ystart[j]; 00257 } 00258 00259 } 00260 00261 } 00262 00263 } 00264 00265 ao.free(dydx); 00266 ao.free(dydx2); 00267 ao.free(yerrl); 00268 00269 return first_ret; 00270 } 00271 00272 /** \brief Solve the initial-value problem from \c x0 to \c x1 over 00273 a grid storing derivatives 00274 00275 Initially, \c xsol should be an array of size \c nsol, and \c 00276 ysol should be a \c omatrix of size \c [nsol][n]. This function 00277 never takes a step larger than the grid size. 00278 00279 If \ref verbose is greater than zero, The solution at each grid 00280 point will be written to \c std::cout. If \ref verbose is 00281 greater than one, a character will be required after each point. 00282 */ 00283 template<class alloc_mat_t, class mat_row_t, class mat_alloc_t> 00284 int solve_grid(double x0, double x1, double h, size_t n, 00285 vec_t &ystart, size_t nsol, vec_t &xsol, 00286 alloc_mat_t &ysol, alloc_mat_t &dydx_sol, 00287 param_t &pa, func_t &derivs) { 00288 int ret=0; 00289 mat_alloc_t amat; 00290 alloc_mat_t err_sol; 00291 amat.allocate(err_sol,nsol,n); 00292 solve_grid<alloc_mat_t,mat_row_t>(x0,x1,h,n,ystart,nsol,xsol,ysol, 00293 dydx_sol,err_sol,pa,derivs); 00294 amat.free(err_sol,nsol); 00295 return ret; 00296 } 00297 00298 /** \brief Solve the initial-value problem from \c x0 to \c x1 over 00299 a grid storing derivatives 00300 00301 Initially, \c xsol should be an array of size \c nsol, and \c 00302 ysol should be a \c omatrix of size \c [nsol][n]. This function 00303 never takes a step larger than the grid size. 00304 00305 If \ref verbose is greater than zero, The solution at each grid 00306 point will be written to \c std::cout. If \ref verbose is 00307 greater than one, a character will be required after each point. 00308 */ 00309 template<class mat_t, class mat_row_t> 00310 int solve_grid(double x0, double x1, double h, size_t n, 00311 vec_t &ystart, size_t nsol, vec_t &xsol, 00312 mat_t &ysol, mat_t &dydx_sol, mat_t &err_sol, 00313 param_t &pa, func_t &derivs) { 00314 00315 double x=x0, xnext; 00316 int ret=0, first_ret=0; 00317 nsteps=0; 00318 00319 // Copy the initial point to the first row 00320 xsol[0]=x0; 00321 for(size_t j=0;j<n;j++) ysol[0][j]=ystart[j]; 00322 00323 // Verbose output for the first row 00324 if (verbose>0) print_iter(xsol[0],n,ystart); 00325 00326 // Create space for the initial point of each step 00327 vec_t &y_tmp=ystart; 00328 alloc_vec_t dydx_tmp; 00329 ao.allocate(dydx_tmp,n); 00330 00331 // Evalulate the first derivative 00332 derivs(x0,n,y_tmp,dydx_tmp,pa); 00333 00334 for(size_t i=1;i<nsol && ret==0;i++) { 00335 00336 xnext=x0+(x1-x0)*((double)i)/((double)(nsol-1)); 00337 00338 mat_row_t y_row(ysol,i); 00339 mat_row_t dydx_row(dydx_sol,i); 00340 mat_row_t yerr_row(err_sol,i); 00341 00342 // Step until we reach the next grid point 00343 bool done=false; 00344 while(done==false && ret==0) { 00345 00346 ret=astp->astep_full(x,h,xnext,xsol[i],n,y_tmp,dydx_tmp, 00347 y_row,yerr_row,dydx_row,pa,derivs); 00348 nsteps++; 00349 if (ret!=0) { 00350 if (exit_on_fail) { 00351 ao.free(dydx_tmp); 00352 O2SCL_ERR2_RET("Adaptive stepper failed in ", 00353 "ode_iv_solve::solve_grid()",ret); 00354 } else if (first_ret!=0) { 00355 first_ret=ret; 00356 } 00357 } 00358 00359 if (nsteps>ntrial) { 00360 ao.free(dydx_tmp); 00361 O2SCL_ERR2_RET("Too many steps required in ", 00362 "ode_iv_solve::solve_grid().",gsl_emaxiter); 00363 } 00364 00365 // Copy the results of the last step to the starting 00366 // point for the next step 00367 x=xsol[i]; 00368 for(size_t j=0;j<n;j++) { 00369 y_tmp[j]=y_row[j]; 00370 dydx_tmp[j]=dydx_row[j]; 00371 } 00372 00373 // Decide if we have reached the grid point 00374 if (x1>x0) { 00375 if (x>=xnext) done=true; 00376 } else { 00377 if (x<=xnext) done=true; 00378 } 00379 00380 } 00381 00382 if (verbose>0) print_iter(xsol[i],n,dydx_row); 00383 00384 } 00385 00386 ao.free(dydx_tmp); 00387 00388 return first_ret; 00389 } 00390 00391 /** \brief Solve the initial-value problem 00392 to get the final value 00393 00394 If \ref verbose is greater than zero, The solution at less 00395 than or approximately equal to \ref nsteps_out points will be 00396 written to \c std::cout. If \ref verbose is greater than one, 00397 a character will be required after each selected point. 00398 */ 00399 int solve_final_value(double x0, double x1, double h, size_t n, 00400 vec_t &ystart, vec_t ¥d, 00401 param_t &pa, func_t &derivs) { 00402 00403 alloc_vec_t dydx, dydx2, yerr; 00404 ao.allocate(dydx,n); 00405 ao.allocate(dydx2,n); 00406 ao.allocate(yerr,n); 00407 int ret=solve_final_value(x0,x1,h,n,ystart,dydx,yend,yerr, 00408 dydx2,pa,derivs); 00409 ao.free(dydx); 00410 ao.free(dydx2); 00411 ao.free(yerr); 00412 return ret; 00413 } 00414 00415 /** \brief Solve the initial-value problem 00416 to get the final value 00417 00418 If \ref verbose is greater than zero, The solution at less 00419 than or approximately equal to \ref nsteps_out points will be 00420 written to \c std::cout. If \ref verbose is greater than one, 00421 a character will be required after each selected point. 00422 */ 00423 int solve_final_value(double x0, double x1, double h, size_t n, 00424 vec_t &ystart, vec_t ¥d, vec_t &yerr, 00425 param_t &pa, func_t &derivs) { 00426 00427 alloc_vec_t dydx, dydx2; 00428 ao.allocate(dydx,n); 00429 ao.allocate(dydx2,n); 00430 int ret=solve_final_value(x0,x1,h,n,ystart,dydx,yend,yerr, 00431 dydx2,pa,derivs); 00432 ao.free(dydx); 00433 ao.free(dydx2); 00434 return ret; 00435 } 00436 00437 /** \brief Solve the initial-value problem 00438 to get the final value and derivative 00439 00440 If \ref verbose is greater than zero, The solution at less 00441 than or approximately equal to \ref nsteps_out points will be 00442 written to \c std::cout. If \ref verbose is greater than one, 00443 a character will be required after each selected point. 00444 00445 The solution fails if more than \ref ntrial steps are required. 00446 00447 This function will also fail if <tt>x1>x0</tt> and <tt>h<0</tt> 00448 or if <tt>x1<x0</tt> but <tt>h>0</tt>. 00449 */ 00450 int solve_final_value(double x0, double x1, double h, size_t n, 00451 vec_t &ystart, vec_t &dydx_start, vec_t ¥d, 00452 vec_t &yerr, vec_t &dydx_end, 00453 param_t &pa, func_t &derivs) { 00454 00455 if ((x1>x0 && h<=0.0) || (x0>x1 && h>=0.0)) { 00456 O2SCL_ERR_RET("Interval ordering and sign of h don't match.", 00457 gsl_einval); 00458 } 00459 00460 00461 double x=x0, xverb=0.0, dxverb=0.0, xnext; 00462 int ret=0, first_ret=0; 00463 00464 nsteps=0; 00465 00466 // Compute stepsize for verbose output 00467 if (verbose>0) { 00468 print_iter(x0,n,ystart); 00469 if (verbose>1) { 00470 char ch; 00471 std::cin >> ch; 00472 } 00473 if (x1>x0) { 00474 dxverb=(x1-x0)/((double)nsteps_out); 00475 xverb=x0+dxverb; 00476 } else { 00477 dxverb=(x0-x1)/((double)nsteps_out); 00478 xverb=x0-dxverb; 00479 } 00480 } 00481 00482 // Use yend as workspace 00483 for(size_t i=0;i<n;i++) yend[i]=ystart[i]; 00484 00485 // Compute initial derivative 00486 derivs(x,n,yend,dydx_start,pa); 00487 for(size_t i=0;i<n;i++) dydx_end[i]=dydx_start[i]; 00488 00489 bool done=false; 00490 while (done==false) { 00491 00492 // Take a step of the first type 00493 ret=astp->astep_full(x,h,x1,xnext,n,ystart,dydx_start,yend,yerr, 00494 dydx_end,pa,derivs); 00495 00496 if (ret!=0) { 00497 if (exit_on_fail) { 00498 O2SCL_ERR2_RET("Adaptive stepper failed in ", 00499 "ode_iv_solve::solve_grid()",ret); 00500 } else if (first_ret!=0) { 00501 first_ret=ret; 00502 } 00503 } 00504 00505 // Print out verbose info 00506 if (verbose>0 && xnext>xverb) { 00507 print_iter(xnext,n,yend); 00508 if (verbose>1) { 00509 char ch; 00510 std::cin >> ch; 00511 } 00512 xverb+=dxverb; 00513 } 00514 00515 // Check number of iterations 00516 nsteps++; 00517 if (nsteps>ntrial) { 00518 O2SCL_ERR2_RET("Exceeded max number of iterations in ", 00519 "ode_iv_solve::solve_final_value().", 00520 gsl_emaxiter); 00521 } 00522 00523 if (ret!=0) { 00524 done=true; 00525 } else { 00526 if (x1>x0) { 00527 if (xnext>=x1) done=true; 00528 } else { 00529 if (xnext<=x1) done=true; 00530 } 00531 } 00532 00533 if (done==false) { 00534 00535 // Take a step of the second type 00536 ret=astp->astep_full(xnext,h,x1,x,n,yend,dydx_end,ystart,yerr, 00537 dydx_start,pa,derivs); 00538 00539 if (ret!=0) { 00540 if (exit_on_fail) { 00541 O2SCL_ERR2_RET("Adaptive stepper failed in ", 00542 "ode_iv_solve::solve_grid()",ret); 00543 } else if (first_ret!=0) { 00544 first_ret=ret; 00545 } 00546 } 00547 00548 // Print out verbose info 00549 if (verbose>0 && x>xverb) { 00550 print_iter(x,n,ystart); 00551 if (verbose>1) { 00552 char ch; 00553 std::cin >> ch; 00554 } 00555 xverb+=dxverb; 00556 } 00557 00558 // Check number of iterations 00559 nsteps++; 00560 if (nsteps>ntrial) { 00561 O2SCL_ERR2_RET("Exceeded max number of iterations in ", 00562 "ode_iv_solve::solve_final_value().", 00563 gsl_emaxiter); 00564 } 00565 00566 if (ret!=0) { 00567 done=true; 00568 } else { 00569 if (x1>x0) { 00570 if (x>=x1) { 00571 done=true; 00572 for(size_t j=0;j<n;j++) { 00573 yend[j]=ystart[j]; 00574 dydx_end[j]=dydx_start[j]; 00575 } 00576 } 00577 } else { 00578 if (x<=x1) { 00579 done=true; 00580 for(size_t j=0;j<n;j++) { 00581 yend[j]=ystart[j]; 00582 dydx_end[j]=dydx_start[j]; 00583 } 00584 } 00585 } 00586 } 00587 00588 } 00589 00590 // End of while loop 00591 } 00592 00593 // Print out final verbose info 00594 if (verbose>0) { 00595 print_iter(x1,n,yend); 00596 if (verbose>1) { 00597 char ch; 00598 std::cin >> ch; 00599 } 00600 } 00601 00602 return first_ret; 00603 } 00604 00605 /// Set output level 00606 int verbose; 00607 00608 /** 00609 \brief Number of output points if \ref verbose is greater 00610 than zero (default 10) 00611 00612 This is used in functions solve_table() and solve_final_value() 00613 to control how often steps are output when verbose is greater 00614 than zero. 00615 */ 00616 size_t nsteps_out; 00617 00618 /// Maximum number of applications of the adaptive stepper (default 1000) 00619 size_t ntrial; 00620 00621 /// Number of adaptive steps employed 00622 size_t nsteps; 00623 00624 /** 00625 \brief If true, stop the solution if the adaptive stepper fails 00626 00627 If this is false, then failures in the adaptive stepper are 00628 ignored. 00629 */ 00630 bool exit_on_fail; 00631 00632 /// The default adaptive stepper 00633 gsl_astep<param_t,func_t,vec_t,alloc_vec_t,alloc_t> gsl_astp; 00634 00635 /// Return the type, \c "ode_iv_solve". 00636 virtual const char *type() { return "ode_iv_solve"; } 00637 00638 }; 00639 00640 #ifndef DOXYGENP 00641 } 00642 #endif 00643 00644 #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