Loading [MathJax]/extensions/tex2jax.js

2019年4月7日日曜日

自作コンテナクラスでboost odeintのcontrolled stepperを使うメモ書き

先日

というような計算をしたときに、軌道を計算するためにscipyのodeintを使って計算したところ、思ったより簡単に、ちゃんとした公式の元で積分が行えるのでちょっと感動しました。
で、C95で公開したプログラムもodeintで実装できると助かるなぁと思ったんですが、scipy.integrate.odeintは引数がリストのみなので、Anderson-Walkerモデルみたいに13個もある式を真面目にやろうとすれば絶対どこかでindexを間違える未来が見えます。

そういうわけでscipyのodeintを調べているとboost::odeintというものがあり、こちらは自作のコンテナクラスも使えるということを知りました。自作コンテナクラスが使えるのであれば、メンバーにstd::vectorを持ち、それらのindexに対してsetter, getterを作れば、計算部分を書く時は構造体っぽく使えるので良さそうだな、と思いました(自作のPoint classも使えるんですが、operatorを何個も手打ちしたくなかったので今回はstd::vectorをメンバに持つクラスを作りました)。
ここまでの段階ではC++で常微分方程式:boost::odeint入門およびboost odeintのドキュメントを多く参考にしました。

さて、せっかくodeintのようなパッケージが使えるんですから、Controlled stepperを使って可変時間刻みの元で計算をしたいわけですが、

  1. void lorenz(const state_type &x, state_type &dxdt, const double t)
  2. {
  3. const double sigma(10.0);
  4. const double R(28.0);
  5. const double b(8.0 / 3.0);
  6.  
  7. dxdt[0] = sigma * (x[1] - x[0]);
  8. dxdt[1] = R * x[0] - x[1] - x[0] * x[2];
  9. dxdt[2] = -b * x[2] + x[0] * x[1];
  10. };
  11. using namespace boost::numeric::odeint;
  12.  
  13. int main()
  14. {
  15. state_type x;//git hubにあるサンプルコードに従って作った自作のコンテナクラス
  16. x[0] = 5.0;
  17. x[1] = 10.0;
  18. x[2] = 10.0;
  19. state_type xold = x;
  20.  
  21. BOOST_STATIC_ASSERT(is_resizeable::value == true);
  22. using base_stepper_type = runge_kutta_dopri5;
  23. auto Stepper = make_controlled(1e-6, 1e-6, base_stepper_type());
  24. double t = 0.;
  25. double dt_init = 1e-2;
  26. double dt = dt_init;
  27. double dt_max = 1.;
  28. double tmax = 10.;
  29. boost::numeric::odeint::controlled_step_result res;
  30. while (t < tmax)
  31. {
  32. xold = x;
  33. res = Stepper.try_step(lorenz, x, t, dt);
  34. if (res)
  35. {
  36. res = Stepper.try_step(lorenz, x, t, dt);
  37. }
  38. else
  39. {
  40. }
  41. if (dt > dt_max)
  42. {
  43. dt = dt_max;
  44. }
  45. std::cout << x.x() << x.y() << x.z() << std::endl;
  46. };
  47. }

みたいなことをやると、
/usr/include/boost/numeric/odeint/stepper/controlled_runge_kutta.hpp:89:40: error: cannot convert 'boost::numeric::odeint::norm_result_type<AwState, void>::type {aka AwState}' to 'boost::numeric::odeint::default_error_checker<double, boost::numeric::odeint::range_algebra, boost::numeric::odeint::default_operations>::value_type {aka double}' in return
         return algebra.norm_inf( x_err );
以下、大量のエラーを吐いて爆死します。で、これを回避したい、というのが今回の話題。
C++全然わからないマンなりにいろいろ試した結果、git hubにあるサンプルコード

  1. template< size_t MAX_N >
  2. class my_vector
  3. {
  4. typedef std::vector< double > vector;
  5.  
  6. public:
  7. typedef vector::iterator iterator;
  8. typedef vector::const_iterator const_iterator;
  9. public:
  10. my_vector( const size_t N )
  11. ...


  1. template< size_t MAX_N >
  2. class my_vector
  3. {
  4. typedef std::vector< double > vector;
  5. public:
  6. typedef vector::iterator iterator;
  7. typedef vector::const_iterator const_iterator;
  8. typedef vector::value_type value_type; //ここを追加
  9.  
  10. public:
  11. my_vector( const size_t N )
  12. ...

とすればなんの問題もなく通るようになりました。
cannot convert 'boost::numeric::odeint::norm_result_type<AwState, void>::type {aka AwState}' to 'boost::numeric::odeint::default_error_checker<double, boost::numeric::odeint::range_algebra, boost::numeric::odeint::default_operations>::value_type {aka double}' in return
         return algebra.norm_inf( x_err );
よく見たら、typeをvalue_typeに変換できないって書いてましたね。
そんなメモ書きでした。