Virtual Testbed
Ship dynamics simulator for extreme conditions
rkf45.hh
1 #ifndef VTESTBED_CORE_RKF45_HH
2 #define VTESTBED_CORE_RKF45_HH
3 
4 #include <cmath>
5 #include <iostream>
6 
7 #include <vtestbed/base/blitz.hh>
8 #include <vtestbed/core/math.hh>
9 
10 namespace vtb {
11 
12  namespace core {
13 
23  template <class T>
24  class RKF45 {
25 
26  public:
27  typedef T value_type;
28 
29  private:
31  T _minstep = T(1e-3);
33  T _maxstep = T(1);
35  T _tolerance = T(1e-6);
37  bool _verbose = false;
38 
39  public:
40 
41  RKF45() = default;
42  RKF45(const RKF45&) = default;
43  RKF45& operator=(const RKF45&) = default;
44  RKF45(RKF45&&) = default;
45  RKF45& operator=(RKF45&&) = default;
46  ~RKF45() = default;
47 
65  template <class Function, int N>
66  inline blitz::TinyVector<T,N>
67  solve(Function f, T t0, T t1, const blitz::TinyVector<T,N>& x0) {
68  typedef blitz::TinyVector<T,N> vec;
69  enum class state {
70  normal_iteration,
71  last_iteration,
72  end
73  };
74  state st = state::normal_iteration;
75  const T tol = this->_tolerance;
76  const T h_min = this->_minstep;
77  const T h_max = this->_maxstep;
78  T h = this->_minstep;
79  T t = t0 + h;
80  vec x_i(x0);
81  if (this->_verbose) {
82  std::clog
83  << std::setw(8) << "rkf45"
84  << std::setw(10) << t0
85  << std::setw(10) << h
86  << std::setw(10) << x0
87  << std::endl;
88  }
89  while (st != state::end) {
130  const vec k_1 = h*f(t, x_i);
131  const vec k_2 = h*f(
132  t + (T(1)/T(4))*h,
133  x_i
134  + (T(1)/T(4))*k_1
135  );
136  const vec k_3 = h*f(
137  t + (T(3)/T(8))*h,
138  x_i
139  + (T(3)/T(32))*k_1
140  + (T(9)/T(32))*k_2
141  );
142  const vec k_4 = h*f(
143  t + (T(12)/T(13))*h,
144  x_i
145  + (T(1932)/T(2197))*k_1
146  - (T(7200)/T(2197))*k_2
147  + (T(7296)/T(2197))*k_3
148  );
149  const vec k_5 = h*f(
150  t + h,
151  x_i
152  + (T(439)/T(216))*k_1
153  - T(8)*k_2
154  + (T(3680)/T(513))*k_3
155  - (T(845)/T(4104))*k_4
156  );
157  const vec k_6 = h*f(
158  t + (T(1)/T(2))*h,
159  x_i
160  - (T(8)/T(27))*k_1
161  + T(2)*k_2
162  - (T(3544)/T(2565))*k_3
163  + (T(1859)/T(4104))*k_4
164  - (T(11)/T(40))*k_5
165  );
176  vec x_ip1 = x_i
177  + (T(25)/T(216))*k_1
178  + (T(1408)/T(2565))*k_3
179  + (T(2197)/T(4101))*k_4
180  - (T(1)/T(5))*k_5;
193  vec z_ip1 = x_i
194  + (T(16)/T(135))*k_1
195  + (T(6656)/T(12825))*k_3
196  + (T(28561)/T(56430))*k_4
197  - (T(9)/T(50))*k_5;
198  + (T(2)/T(55))*k_6;
199  if (this->_verbose) {
200  std::clog
201  << std::setw(8) << "rkf45"
202  << std::setw(10) << t
203  << std::setw(10) << h
204  << std::setw(10) << x_ip1
205  << std::endl;
206  }
207  if (st == state::last_iteration) {
208  st = state::end;
209  } else {
218  using blitz::abs;
219  using blitz::max;
220  const T diff = max(abs(x_ip1 - z_ip1));
221  const T s = std::pow((tol*h) / (T(2)*diff), T(1)/T(4));
227  h = blitz::clamp(s*h, h_min, h_max);
228  if (t + h + h_min > t1) {
229  h = t1 - t;
230  t = t1;
231  st = state::last_iteration;
232  } else {
233  t += h;
234  }
235  }
236  x_i = x_ip1;
237  }
238  return x_i;
239  }
240 
242  template <class Function, int N>
243  inline blitz::TinyVector<T,N>
244  operator()(Function f, T t0, T t1, const blitz::TinyVector<T,N>& x0) {
245  return this->solve<Function,N>(f, t0, t1, x0);
246  }
247 
248  inline T
249  min_step() const noexcept {
250  return this->_minstep;
251  }
252 
253  inline void
254  min_step(T rhs) {
255  if (!(rhs > T(0))) {
256  throw std::invalid_argument("RKF45::min_step <= 0");
257  }
258  this->_minstep = rhs;
259  }
260 
261  inline T
262  max_step() const noexcept {
263  return this->_maxstep;
264  }
265 
266  inline void
267  max_step(T rhs) {
268  if (!(rhs > T(0))) {
269  throw std::invalid_argument("RKF45::max_step <= 0");
270  }
271  this->_maxstep = rhs;
272  }
273 
274  inline T
275  tolerance() const noexcept {
276  return this->_tolerance;
277  }
278 
279  inline void
280  tolerance(T rhs) {
281  if (!(rhs > T(0))) {
282  throw std::invalid_argument("RKF45::tolerance <= 0");
283  }
284  this->_tolerance = rhs;
285  }
286 
287  inline void
288  verbose(bool rhs) noexcept {
289  this->_verbose = rhs;
290  }
291 
292  inline bool
293  verbose() const noexcept {
294  return this->_verbose;
295  }
296 
297  };
298 
299  }
300 
301 }
302 
303 #endif // vim:filetype=cpp
blitz::TinyVector< T, N > solve(Function f, T t0, T t1, const blitz::TinyVector< T, N > &x0)
Solve system of ordinary differential equations with right hand sides f.
Definition: rkf45.hh:67
blitz::TinyVector< T, N > operator()(Function f, T t0, T t1, const blitz::TinyVector< T, N > &x0)
Solve system of ordinary differential equations with right hand sides f.
Definition: rkf45.hh:244
Runge—Khutta—Fehlberg initial value problem solver.
Definition: rkf45.hh:24
Main namespace.
Definition: convert.hh:9