Fluid Dynamics Library
odestepper.hpp
Go to the documentation of this file.
00001 
00023 #ifndef __FDL_ODESTEPPER_HPP
00024 #define __FDL_ODESTEPPER_HPP
00025 
00026 #include <string>
00027 #include <sstream>
00028 #include "macbase.hpp"
00029 
00030 namespace fdl {
00031 
00034 template <typename scalar_t>
00035 class ODEStepper
00036 {
00037 protected:
00038         stepper_T rule_; 
00039         scalar_t tol_; 
00040         scalar_t err_; 
00041         unsigned npts_; 
00042         std::vector<scalar_t> xtmp, k1, k2, k3, k4;
00043         
00044 public:
00045         ODEStepper(unsigned npts =1);
00046         ~ODEStepper() {}
00047         
00048         void setRule(stepper_T rule) {rule_ = rule;}
00049         std::string getRule();
00050         void setTolerance(scalar_t tol) {tol_ = tol;}
00051         scalar_t getTolerance() const {return tol_;}
00053         scalar_t getError() const {return err_;}
00054         
00056         void resize(unsigned npts);
00057         
00059         void step(MACBase<scalar_t>* macGrid, 
00060                                           unsigned npts, scalar_t& dt,
00061                                           const scalar_t x0[], scalar_t x1[]);
00062 };
00063 
00064 template <typename scalar_t>
00065 ODEStepper<scalar_t>::ODEStepper(unsigned npts)
00066 : rule_(RK2), tol_(1), xtmp(), k1(), k2(), k3(), k4()
00067 {
00068         resize(npts);
00069 }
00070 
00071 template <typename scalar_t>
00072 void ODEStepper<scalar_t>::resize(unsigned npts)
00073 {
00074         xtmp.resize(npts * 3);
00075         k1.resize(npts * 3);
00076         k2.resize(npts * 3);
00077         k3.resize(npts * 3);
00078         k4.resize(npts * 3);
00079         npts_ = npts;
00080 }
00081 
00082 template <typename scalar_t>
00083 std::string ODEStepper<scalar_t>::getRule()
00084 {
00085         std::ostringstream oss;
00086         switch (rule_) {
00087                 case ARK23:
00088                         oss << "Adaptive Runge-Kutta (2, 3)";
00089                         break;
00090                 case RK2:
00091                 default:
00092                         oss << "Runge-Kutta order 2";
00093                         break;
00094         }
00095         return oss.str();
00096 }
00097 
00101 template <typename scalar_t>
00102 void ODEStepper<scalar_t>::step(MACBase<scalar_t>* macGrid, 
00103                                                                                                 unsigned npts, scalar_t& dt,
00104                                                                                                 const scalar_t x0[], scalar_t x1[])
00105 {
00106         if (npts > npts_) {
00107                 DEV() << "Resizing ODEStepper scratch space";
00108                 resize(npts);
00109         }
00110         
00111         /* We use the standard notation for Butcher tableaux; see
00112          <http://www.scholarpedia.org/article/Runge-Kutta_methods>.
00113          The c_j coefficients are not needed because the RHS is "stationary". 
00114          The coefficients e_j are for the error term. 
00115          */
00116         scalar_t a21, a31, a32, a41, a42, a43;
00117         scalar_t b1, b2, b3, b4, e1, e2, e3, e4;
00118         scalar_t delta;
00119         
00120         size_t j, dim = npts * 3;
00121         
00122         switch (rule_) 
00123         {                               
00124                 case ARK23: // Bogacki-Shampine method 
00125                         a21 = scalar_t(1)/2;
00126                         a31 = scalar_t(0); a32 = scalar_t(3)/4;
00127                         a41 = scalar_t(2)/9, a42 = scalar_t(1)/3, a43 = scalar_t(4)/9;
00128                         b1 = a41, b2 = a42, b3 = a43, b4 = scalar_t(0);
00129                         e1 = b1 - scalar_t(7)/24, 
00130                         e2 = b2 - scalar_t(1)/4, 
00131                         e3 = b3 - scalar_t(1)/3,
00132                         e4 = b4 - scalar_t(1)/8;
00133                         
00134                         do {
00135                                 macGrid->interpField(npts, &x0[0], &k1[0]);
00136                                 
00137                                 for (j = 0; j < dim; ++j)
00138                                         xtmp[j] = x0[j] + dt * a21 * k1[j];
00139                                 // xtmp[j] = x0[j] - dt * a21 * k1[j];
00140                                 macGrid->interpField(npts, &xtmp[0], &k2[0]);
00141                                 
00142                                 for (j = 0; j < dim; ++j)
00143                                         xtmp[j] = x0[j] + dt * (a31 * k1[j] + a32 * k2[j]);
00144                                 // xtmp[j] = x0[j] - dt * (a31 * k1[j] + a32 * k2[j]);
00145                                 macGrid->interpField(npts, &xtmp[0], &k3[0]);
00146                                 
00147                                 for (j = 0; j < dim; ++j)
00148                                         xtmp[j] = x0[j] + dt * (a41 * k1[j] + a42 * k2[j] + a43 * k3[j]);
00149                                 //      xtmp[j] = x0[j] - dt * (a41 * k1[j] + a42 * k2[j] + a43 * k3[j]);
00150                                 macGrid->interpField(npts, &xtmp[0], &k4[0]);
00151                                 
00152                                 err_ = scalar_t(0);
00153                                 for (j = 0; j < dim; ++j) {
00154                                         x1[j] = xtmp[j];
00155                                         // compute sup-norm |err| on the fly...
00156                                         delta = dt * fabs(e1 * k1[j] + e2 * k2[j] + e3 * k3[j] + e4 * k4[j]);
00157                                         if (delta > err_) 
00158                                                 err_ = delta;
00159                                 }
00160                                 if (err_ >= tol_) {
00161                                         dt /= 2;
00162                                         DEV() << "Time step adaptively adjusted: dt = " << dt;
00163                                 }
00164                         } while (err_ >= tol_);
00165                         break;
00166                         
00167                 case RK2:
00168                 default: // non-adaptive Runge-Kutta 2. 
00169                         a21 = scalar_t(1)/2;
00170                         macGrid->interpField(npts, &x0[0], &k1[0]);
00171                         
00172                         for (j = 0; j < dim; ++j) 
00173                                 xtmp[j] = x0[j] + dt * a21 * k1[j];
00174                         //      xtmp[j] = x0[j] - dt * a21 * k1[j];
00175                         macGrid->interpField(npts, &xtmp[0], &k2[0]);
00176                         
00177                         for (j = 0; j < dim; ++j)
00178                                 x1[j] = x0[j] + dt * k2[j];
00179                         // x1[j] = x0[j] - dt * k2[j];
00180                         break;
00181         }
00182 }       
00183         
00184 } // namespace fdl
00185 
00186 #endif // __FDL_ODESTEPPER_HPP