【问题标题】:Runge-Kutta 4th order Particle Advection code sampleRunge-Kutta 4 阶粒子对流代码示例
【发布时间】:2017-09-21 15:56:08
【问题描述】:

我一直在尝试将 RK4 集成到我正在做的模拟中。下面的函数是我使用 RK4 在 3 维力场上积分的最佳尝试,基于 this 站点第 12 页上的方程。

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

当我尝试使用这种方法模拟粒子时,错误比我使用越级积分方法时要严重得多。因此我认为我的 RK4 实现有问题。如果我在使用 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;
}

【问题讨论】:

    标签: c++ simulation runge-kutta


    【解决方案1】:

    您不能一次积分一个粒子,这将导致粒子集合的 1 阶方法,从而在适合 4 阶方法的步长处出现较大的漂移。

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

    【讨论】:

    • 我应该澄清一下,粒子之间没有相互作用。对于每个粒子与之交互的一个对象,我有一个不同的类(分子)。因此,我认为我不需要一次整合所有粒子。如果我不正确,请告诉我。
    • 那么我什么都没有,积分步骤对于力场中的单个粒子看起来是正确的。纸上的注释:虽然 jumpfrog-Verlet 需要一个恒定的时间步长,但velocity Verlet 允许自适应时间步长。
    猜你喜欢
    • 2023-02-18
    • 1970-01-01
    • 1970-01-01
    • 2015-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-21
    • 1970-01-01
    相关资源
    最近更新 更多