1

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

(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。现在,这适用于欧拉方法:如果我将此 ODE 积分为2 * pi * 10,给定初始条件(1, 0, 0, -1),并具有一定的0.003精度,我得到的位置与(1, 0)的范围内相当1 ± 0.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等于的位置,当理论上精度应该是,大约。我检查并发现 Runge_Kutta 的时期似乎几乎是一式四份(因为在一段时间内,从到),但我不明白为什么。这是我的主要内容:0.391E-62 * pix10.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;

先感谢您。

4

1 回答 1

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以至于voidofSetComponent成为一个因素。


更新二:发现第二个错误:在你的另一篇文章中,给出了线性向量类的代码。在那里,与加法( operator+) 相比,标量向量积( operator*(double)) 正在修改调用实例。因此在计算getk2的分量时与k1相乘h/2。但是后来这个修改过的k1(以及修改过的k2and k3)被用于组装结果,y导致一些几乎完全无用的状态更新。


原始快速原型设计:我可以告诉你,在 python 中精简的基本实现可以完美地工作

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)

以。。结束

(t, [ x,y,vx,vy]) = (6.2831853071794184,
    [  1.00000000e+00,  -6.76088739e-15,   4.23436503e-12,
    -1.00000000e+00])

正如预期的那样。您将需要一个调试器或中间输出来查找计算出错的地方。

于 2016-01-23T18:22:34.880 回答