Runge Kutta的方法循环运动

时间:2016-01-23 16:22:36

标签: c++ differential-equations runge-kutta

我被要求解决这个微分方程:

(X,Y,VX,VY)=(VX,VY,VY,-vx)

应返回2*pi周期的圆周运动。 我实现了这个功能:

class FunzioneBase 
{
  public:
    virtual VettoreLineare Eval(double t, const VettoreLineare& v) const = 0; 
};

class Circonferenza: public FunzioneBase
{
  private:
    double _alpha;

  public:
    Circonferenza(double alpha) { _alpha = alpha; };
    void SetAlpha(double alpha) { _alpha = alpha; };
    virtual VettoreLineare Eval(double t, const VettoreLineare& v) const;
};

VettoreLineare Circonferenza::Eval(double t, const VettoreLineare& v) const
{
  VettoreLineare y(4);
  if (v.GetN() != 4) 
  {
    std::cout << "errore" << std::endl;
    return 0;
  };

  y.SetComponent(0, v.GetComponent(2));
  y.SetComponent(1, v.GetComponent(3));
  y.SetComponent(2, pow(pow(v.GetComponent(0), 2.) + pow(v.GetComponent(1), 2.), _alpha) * v.GetComponent(3));
  y.SetComponent(3, - pow(pow(v.GetComponent(0), 2.)  + pow(v.GetComponent(1), 2.), _alpha)) * v.GetComponent(2));

  return y;
};

其中_alpha等于0。 现在,使用Euler方法可以正常工作:如果我将此ODE集成到2 * pi * 10,给定初始条件(1, 0, 0, -1),精度为0.003,我得到的位置与(1, 0)相当我们应该期望在1 ± 0.1范围内{1}}。但是,如果我将相同的ODE与Runge Kutta的方法(0.003精度,2 * pi * 10秒)整合,实现如下:

class EqDifferenzialeBase 
{
  public:
    virtual VettoreLineare Passo (double t, VettoreLineare& x, double h, FunzioneBase* f) const = 0;
};

class Runge_Kutta: public EqDifferenzialeBase 
{
  public:
    virtual VettoreLineare Passo(double t, VettoreLineare& v, double h,  FunzioneBase* f) const;
};

VettoreLineare Runge_Kutta::Passo(double t, VettoreLineare& v, double h, FunzioneBase* _f) const
{ 
  VettoreLineare k1 = _f->Eval(t, v);
  VettoreLineare k2 = _f->Eval(t + h / 2., v + k1 *(h / 2.));
  VettoreLineare k3 = _f->Eval(t + h / 2., v + k2 * (h / 2.));
  VettoreLineare k4 = _f->Eval(t + h, v + k3 * h);
  VettoreLineare y = v + (k1 + k2 * 2. + k3 * 2. + k4) * (h / 6.);

  return y;
}

程序返回一个x位置,等于0.39近似,当精度理论上应该是4阶Runge Kutta的方法,大约1E-6。我查了一下,发现用Runge_Kutta的时间似乎几乎一式四份(因为2 * pi失效,x10.48),但我不知道明白为什么。这是我的主要内容:

VettoreLineare DatiIniziali (4);
Circonferenza* myCirc = new Circonferenza(0);

DatiIniziali.SetComponent(0, 1.);
DatiIniziali.SetComponent(1, 0.);
DatiIniziali.SetComponent(2, 0.);
DatiIniziali.SetComponent(3, -1.);
double passo = 0.003;
Runge_Kutta myKutta;

for(int i = 0; i <= 2. * M_PI / passo; i++)
{
  DatiIniziali = myKutta.Passo(0, DatiIniziali, passo, myCirc);
  cout << DatiIniziali.GetComponent(0) << endl;
};

cout << 1 - DatiIniziali.GetComponent(0) << endl;

提前谢谢。

1 个答案:

答案 0 :(得分:0)

更新:发现一个错误:始终使用-Wall选项进行编译,以捕获编译器的所有警告和自动代码更正。那你就找到了

fff: In member function ‘virtual VettoreLineare Circonferenza::Eval(double, const VettoreLineare&) const’:
fff:xxx:114: error: invalid operands of types ‘void’ and ‘double’ to binary ‘operator*’
     y.SetComponent(3, - pow(pow(v.GetComponent(0), 2.)  + pow(v.GetComponent(1), 2.), _alpha)) * v.GetComponent(2));
                                                                                                                  ^

_alpha之后提早结束,以便void的{​​{1}}成为一个因素。

更新II:识别出第二个错误:在你的另一篇文章中给出了线性向量类的代码。与添加SetComponent)相比,标量产品operator+)正在修改调用实例。因此,在计算operator*(double)k2的组件与k1相乘。但是,此修改后的h/2(以及修改后的k1k2)用于汇总结果k3,从而导致几乎完全无用的更新。

原创的快速原型设计:我可以告诉你,python中的一个简单的裸机实现工作完美无缺

y

结尾
import numpy as np

def odeint(f,t0,y0,tf,h):
    '''Classical RK4 with fixed step size, modify h to fit
        the full interval'''
    N = np.ceil( (tf-t0)/h )
    h = (tf-t0)/N

    t = t0
    y = np.array(y0)
    for k in range(int(N)):
        k1 = h*np.array(f(t      ,y       ))
        k2 = h*np.array(f(t+0.5*h,y+0.5*k1))
        k3 = h*np.array(f(t+0.5*h,y+0.5*k2))
        k4 = h*np.array(f(t+    h,y+    k3))
        y = y + (k1+2*(k2+k3)+k4)/6
        t = t + h
    return t, y

def odefunc(t,y):
    x,y,vx,vy = y
    return [ vx,vx,vy,-vx ]

pi = 4*np.arctan(1);

print odeint(odefunc, 0, [1,0,0,-1], 2*pi, 0.003)

正如所料。你需要一个调试器或中间输出来找出计算出错的地方。