hack のためのネタ帳, etc,,,

常微分方程式(ODEs, O.D.E.: Ordinary Differential Equations)の初期値問題(IVP: Initial Value Problems)を解くためのライブラリ。
オイラー法、ルンゲクッタ法を始めとした様々な手法を適用可能。

公式ページ等

参考になるページ等

Tips

連立常微分方程式

$x, y, ...$ 等、複数の変数に関する微分 $x', y', ... (= dx/dt, dy/dt, ...)$ があったとする。
この場合、$x, y, ... (= x_1, y_1, ...)$ と置けば、$\boldsymbol{x} = (x_1, x_2, ...)$ のようにベクトル表記してやると計算が簡略化できる。

高階の常微分方程式

1階の連立常微分方程式に変換してやることで、問題を簡略化、つまり1階の連立常微分方程式のソルバーで計算が可能になる。
例えば、$\boldsymbol{x}'' = f(t,\boldsymbol{x},\boldsymbol{x}')$ について、
$\left\{\begin{array}{l}\boldsymbol{x}_0(t) = \boldsymbol{x}(t)\\ \boldsymbol{x}_1(t) = \boldsymbol{x}'(t)\end{array}\right.$
と置けば、
$\left\#x7b;\begin{array}{l}\boldsymbol{x}'_0(t) = \boldsymbol{x}'(t) = \boldsymbol{x}_1(t)\\ \boldsymbol{x}'_1(t) = \boldsymbol{x}''(t) = f(t,\boldsymbol{x},\boldsymbol{x}')\end{array}\right.$
のように1階の連立常微分方程式に変換できる。更に高次の場合であっても、同様にして1次の連立常微分方程式に変換できる。

2階の連立常微分方程式の例

最も単純な例の一つとして、等加速度運動(放物運動)を挙げておく。

この例では UAM::operator() の引数 x[0,1] 及び x[2,3] が時刻 t における位置と速度であり、
これらの値を用いて、dxdt[0,1] 及び dxdt[2,3] に時刻 t における速度と加速度を算出し返してやる。
main() で呼んでいる integrate_const() の末尾に与えている lambda の引数 x[0,1] 及び x[2,3] により時刻 t における位置と速度が得られる。

stepper を変更すると、ソルバーを変更できる。

uam.cpp

/**
 * UAM - Uniform Accelerated Motion
 */

#include <cstdio>
#include <cstdlib>
#include <vector>
#include <boost/numeric/odeint.hpp>

struct UAM {
  typedef std::vector<double> State;
  State a; // a_{x,y}
  UAM(const State &_a) : a(_a) {}
  void operator()(const State& x, State& dxdt, const double t) {
    dxdt[0] = x[2]; // v_x(t)
    dxdt[1] = x[3]; // v_y(t)
    dxdt[2] = a[0]; // a_x(t)
    dxdt[3] = a[1]; // a_y(t)
  }
};

int main(int argc, char *argv[])
{
  UAM::State x(4); // position_{x,y}(t), v_{x,y}(t)
  UAM::State a(2); // a_{x,y}(t)
  UAM::State t(2); // t_{0,1}
  double dt;
  if (argc != 10) {
    fprintf(stderr, "Usage: %s <x(t0)> <y(t0)> <vx(t0)> <vy(t0)> <ax(t)> <ay(t)> <t0> <t1> <dt>\n", argv[0]);
    return EXIT_FAILURE;
  }
  x[0] = atof(argv[1]); // x(t0)
  x[1] = atof(argv[2]); // y(t0)
  x[2] = atof(argv[3]); // v_x(t0)
  x[3] = atof(argv[4]); // v_y(t0)
  a[0] = atof(argv[5]); // a_x(t)
  a[1] = atof(argv[6]); // a_y(t)
  t[0] = atof(argv[7]); // t_0
  t[1] = atof(argv[8]); // t_1
  dt   = atof(argv[9]);
  
  boost::numeric::odeint::runge_kutta4<UAM::State> stepper;
  UAM uam(a);
  size_t steps = boost::numeric::odeint::integrate_const(stepper, uam, x, t[0], t[1], dt,
    [&](const UAM::State& x, const double t) {
      printf("%f %f %f %f %f\n", t, x[0], x[1], x[2], x[3]);
    }
  );

  return EXIT_SUCCESS;
}
実行結果
$ g++ uam.cpp && ./a.out 0 0 1 1 0 -0.1 0 20 1 | tee uam.txt
0.000000 0.000000 0.000000 1.000000 1.000000
1.000000 1.000000 0.950000 1.000000 0.900000
2.000000 2.000000 1.800000 1.000000 0.800000
3.000000 3.000000 2.550000 1.000000 0.700000
4.000000 4.000000 3.200000 1.000000 0.600000
5.000000 5.000000 3.750000 1.000000 0.500000
6.000000 6.000000 4.200000 1.000000 0.400000
7.000000 7.000000 4.550000 1.000000 0.300000
8.000000 8.000000 4.800000 1.000000 0.200000
9.000000 9.000000 4.950000 1.000000 0.100000
10.000000 10.000000 5.000000 1.000000 -0.000000
11.000000 11.000000 4.950000 1.000000 -0.100000
12.000000 12.000000 4.800000 1.000000 -0.200000
13.000000 13.000000 4.550000 1.000000 -0.300000
14.000000 14.000000 4.200000 1.000000 -0.400000
15.000000 15.000000 3.750000 1.000000 -0.500000
16.000000 16.000000 3.200000 1.000000 -0.600000
17.000000 17.000000 2.550000 1.000000 -0.700000
18.000000 18.000000 1.800000 1.000000 -0.800000
19.000000 19.000000 0.950000 1.000000 -0.900000
20.000000 20.000000 -0.000000 1.000000 -1.000000
gnuplot によるプロット
plot [0:20] [0:6] 'uam.txt' using 2:3 w lp

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Wiki内検索

フリーエリア

管理人/副管理人のみ編集できます