Runge-Kutta四阶粒子平流代码样本

时间:2017-04-24 20:19:46

标签: c++ simulation runge-kutta

我一直在尝试将RK4集成实现到我正在进行的模拟中。根据{{​​3}}站点上第12页的公式,下面的函数是我最好尝试使用RK4在3维的力场上进行积分。

在我的代码中,Particle类实质上存储了速度和位置列表,并且可以计算给定位置的力(力与速度无关)。另外,我知道我的函数很长并且可以使用for循环来减少,但我(当前)想要匹配我链接的文章中使用的结构。

当我尝试使用此方法模拟粒子时,错误明显比使用蛙跳积分方法时差。因此我觉得我的RK4实现有问题。如果我误解了RK4如何工作,请用它来解决耦合微分方程,请告诉我。

// 4th Order Runge-Kutta
void Update(Particle * p, double dt) {

    double * v   = p->getVel();
    double * pos = p->getPos();

    double initPos[3] = {pos[0], pos[1], pos[2]};
    double initVel[3] = {v[0], v[1], v[2]};
    double mass = 0.01;

    double k[4][3]; // related to dv
    double l[4][3]; // related to dr

    p->findForce();

    k[0][0] = dt*p->force[0]/mass;
    k[0][1] = dt*p->force[1]/mass;
    k[0][2] = dt*p->force[2]/mass;

    l[0][0] = dt*v[0];
    l[0][1] = dt*v[1];
    l[0][2] = dt*v[2];

    // Set position to midpoint, using l[0]
    pos[0] = initPos[0] + l[0][0]/2;
    pos[1] = initPos[1] + l[0][1]/2;
    pos[2] = initPos[2] + l[0][2]/2;

     p->findForce();

    k[1][0] = dt*p->force[0]/mass;
    k[1][1] = dt*p->force[1]/mass;
    k[1][2] = dt*p->force[2]/mass;

    l[1][0] = dt*(v[0]+k[0][0]/2);
    l[1][1] = dt*(v[1]+k[0][1]/2);
    l[1][2] = dt*(v[2]+k[0][2]/2);

    // Set position to midpoint, using l[1]
    pos[0] = initPos[0] + l[1][0]/2;
    pos[1] = initPos[1] + l[1][1]/2;
    pos[2] = initPos[2] + l[1][2]/2;

    p->findForce();

    k[2][0] = dt*p->force[0]/mass;
    k[2][1] = dt*p->force[1]/mass;
    k[2][2] = dt*p->force[2]/mass;

    l[2][0] = dt*(v[0]+k[1][0]/2);
    l[2][1] = dt*(v[1]+k[1][1]/2);
    l[2][2] = dt*(v[2]+k[1][2]/2);

    // Set position to endpoint, using l[2]
    pos[0] = initPos[0] + l[2][0];
    pos[1] = initPos[1] + l[2][1];
    pos[2] = initPos[2] + l[2][2];

    p->findForce();

    k[3][0] = dt*p->force[0]/mass;
    k[3][1] = dt*p->force[1]/mass;
    k[3][2] = dt*p->force[2]/mass;

    l[3][0] = dt*(v[0]+k[2][0]);
    l[3][1] = dt*(v[1]+k[2][1]);
    l[3][2] = dt*(v[2]+k[2][2]);

    // Finalize pos and v
    pos[0] = initPos[0] + (l[0][0] + 2*l[1][0] + 2*l[2][0] + l[3][0])/6;
    pos[1] = initPos[1] + (l[0][1] + 2*l[1][1] + 2*l[2][1] + l[3][1])/6;
    pos[2] = initPos[2] + (l[0][2] + 2*l[1][2] + 2*l[2][2] + l[3][2])/6;

    v[0]   = initVel[0] + (k[0][0] + 2*k[1][0] + 2*k[2][0] + k[3][0])/6;
    v[1]   = initVel[1] + (k[0][1] + 2*k[1][1] + 2*k[2][1] + k[3][1])/6;
    v[2]   = initVel[2] + (k[0][2] + 2*k[1][2] + 2*k[2][2] + k[3][2])/6;
}

1 个答案:

答案 0 :(得分:1)

您不能一次整合一个粒子,这将导致粒子集合的1阶方法,因此在适合4阶方法的步长处产生大的漂移。

您必须一次整合所有粒子,即为所有粒子计算阶段0,为阶段1设置状态1,包括所有粒子,计算力和速度,即k数量,阶段1,所有粒子一次从状态1.然后计算阶段2的状态2,一次计算所有粒子的k矢量等。