为什么这个粒子系统能量不断增加?

时间:2016-12-23 11:19:50

标签: c physics

我有一个粒子系统。它们具有加速度,速度和位置。

当粒子撞击墙壁时,其速度会翻转。当两个粒子彼此接近时,它们会用这种力相互排斥:

F=1/r^2

F_x=delta(x)/r^3
F_y=delta(y)/r^3

当系统运行时,我感觉所有粒子的总速度都在增加。这很奇怪。粒子应该给它的能量 另一个。因此,系统的总能量必须保持不变。

系统的动能等于

E_k=Sigma v^2

我一直在监控整个系统的总能量并通过cout进行打印,我发现它不断增加。它与能量的保守性相矛盾。我在代码里面犯了什么错误?

#include <vector>
#include <cstdlib>
#include <cmath>
#include <iostream>

constexpr size_t N=1000;

struct Point
{
    double x, y;
    double v_x, v_y;
    double a_x, a_y;
};
Point points[N];

void next_frame()
{
    double energy=0.0;

    // calculate forces
    for( size_t i = 0; i < N; ++i )
    {
        double fx=0.0,fy=0.0;

        for( size_t j = 0; j < N; ++j )
        {
            if(i!=j)
            {
                double dx=points[i].x-points[j].x;
                double dy=points[i].y-points[j].y;
                double r2=dx*dx+dy*dy;
                if(r2>0.01 && r2<100.0) // avoid nan and also unnecessary computation
                {
                    // F=1/r^2
                    double r=sqrt(r2);
                    fx+=dx/(r*r*r);
                    fy+=dy/(r*r*r);
                }
            }
        }

        points[i].a_x=0.01*fx;
        points[i].a_y=0.01*fy;
        energy+=points[i].v_x*points[i].v_x+points[i].v_y*points[i].v_y;
    }
    std::cout<<energy<<std::endl;

    for( size_t i = 0; i < N; ++i )
    {
        // integrations
        points[i].v_x += points[i].a_x;
        points[i].v_y += points[i].a_y;
        points[i].x += points[i].v_x;
        points[i].y += points[i].v_y;

        // wall
        if( points[i].x < -50.0 )
            points[i].v_x = +std::abs(points[i].v_x);
        else if( points[i].x > +50.0 )
            points[i].v_x = -std::abs(points[i].v_x);

        if( points[i].y < -50.0 )
            points[i].v_y = +std::abs(points[i].v_y);
        else if( points[i].y > +50.0 )
            points[i].v_y = -std::abs(points[i].v_y);

    }

}

int main(int argc, char **argv)
{
    // initialize particles
    for( size_t i = 0; i < N; ++i )
    {
        Point p;
        p.x = -50 + ((rand() % 1000)/1000.0)*100.0;
        p.y = -50 + ((rand() % 1000)/1000.0)*100.0;
        p.a_x=0.0;
        p.a_y=0.0;
        p.v_x=0.001*((rand() % 1000)/1000.0-0.5);
        p.v_y=0.001*((rand() % 1000)/1000.0-0.5);
        points[i]=p;
    }   

    while(1)
    {
        next_frame();
    }

    return 0;
}

这是迭代的能量分布:

Energy

请避免更改此问题的标签。

如果我在物理论坛上问这个问题,他们会告诉我这是一个编程问题,而不是物理问题。

3 个答案:

答案 0 :(得分:3)

当使用dt = 0.01的时间步长时,我将力的乘法重新解释为0.01。然后使用的速度实际上是实际速度的0.01倍。为了提取时间步的这种隐式处理,用100倍的因子初始化速度,

        p.v_x=0.1*((rand() % 1000)/1000.0-0.5);
        p.v_y=0.1*((rand() % 1000)/1000.0-0.5);

并删除力和加速度之间的因子

        points[i].a_x=fx;
        points[i].a_y=fy;

然后在集成期间应用时间步。 ([Velocity] Verlet是辛的Euler,初始值略有不同。由于随机初始化,在这种情况下无关紧要。)

        points[i].v_x += points[i].a_x*dt;
        points[i].v_y += points[i].a_y*dt;
        points[i].x += points[i].v_x*dt;
        points[i].y += points[i].v_y*dt;

为了避免平滑且几乎物理的奇点,改变使用修改半径的潜力

r2 = dx*dx + dy*dy + 1e-2; r=sqrt(r2);

然后您可以删除条件评估。在同一个循环中添加势能的总和

            double r2=dx*dx + dy*dy + 1e-2;
           // V=1/r, F=1/r^2
            double r=sqrt(r2);
            fx+=dx/(r*r*r);
            fy+=dy/(r*r*r);
            potential += 1/r;

并且在输出中还结合了动能和势能。通过这些更改,我得到了像

这样的输出
kin= 1.70606,    pot= 29897.4,   tot= 29899.1
kin= 3.28869,    pot= 29895.9,   tot= 29899.2
kin= 7.98328,    pot= 29891.3,   tot= 29899.2
kin= 15.4178,    pot= 29884.1,   tot= 29899.5
kin= 24.9195,    pot= 29875,     tot= 29900
kin= 35.686,     pot= 29864.9,   tot= 29900.6
kin= 47.0385,    pot= 29854.2,   tot= 29901.3
kin= 58.5285,    pot= 29843.4,   tot= 29901.9
kin= 69.9214,    pot= 29832.6,   tot= 29902.5
kin= 81.1222,    pot= 29822,     tot= 29903.1
kin= 92.1124,    pot= 29811.5,   tot= 29903.6
kin= 102.946,    pot= 29801.1,   tot= 29904
kin= 113.739,    pot= 29790.6,   tot= 29904.4
kin= 124.69,     pot= 29779.9,   tot= 29904.6
kin= 136.055,    pot= 29768.8,   tot= 29904.9
kin= 147.937,    pot= 29757.3,   tot= 29905.2
kin= 160.059,    pot= 29745.7,   tot= 29905.7

或图表

energy graph

可以看出,虽然动能稳定增长,但总能量的移动速度非常缓慢。后者可能有两个来源,

  • 几乎完全保留的辛积分方法的数量是修正的能量函数和
  • 边界处的反射可能会在修正的能量中引入小的跳跃,总能量总能量变化很小。

答案 1 :(得分:2)

当粒子彼此非常接近时,能量会增加。 如果你采取无限小的步骤,一切都会好起来的。 但是如果你采取有限大小的步长,粒子会从空间的一个点跳到另一个点。 如果碰巧跳到另一个粒子旁边,它的排斥力F_x=delta(x)/r^3将非常大,这代表了它不应该得到的潜在能量的增加。如果将步骤分解为较小的步骤,则颗粒会减慢并且不会如此接近。

我不知道解决方案。但也许如果你发现一个步骤的能量增加,那么这个步骤可以递归地细分为更小的步骤。

答案 2 :(得分:1)

你的物理方程应该是

dt

其中dt是模拟时间步长。在更新粒子的第二个循环中,您不会乘以dt。那是错的。

此外,您的解决方案方法也不稳定。您必须选择时间步以保持稳定性。由于您的系统最终稳定,您可以尝试使用它。

所以建议的行动是:

  1. 引入新变量points[i].v_x += dt * points[i].a_x; points[i].v_y += dt * points[i].a_y; points[i].x += dt * points[i].v_x; points[i].y += dt * points[i].v_y;
  2. 将第二个循环更改为

    dt = 0.001
  3. 我在1D中尝试过少量粒子并发现了这一点 Graphics2D对于我的100粒粒子看起来很好。