0

我目前正在尝试做一个任务,我必须为受限的 3 体引力问题编写一个模拟,具有两个固定质量和一个测试质量。我得到的关于这个问题的信息是:查看这个链接,这是我到目前为止的程序:

#include<stdlib.h>
#include<stdio.h>
#include <math.h>

int main (int argc, char* argv[])
{
    double dt=0.005, x[20000],y[20000],xv,yv,ax,ay,mneg,mpos,time,radius=0.01;
    int n,validation=0;
    FILE* output=fopen("proj1.out", "w");

    printf("\n");


    if((argv[1]==NULL) || (argv[2]==NULL) || (argv[3]==NULL) || (argv[4]==NULL) ||     (argv[5]==NULL) || (argv[6]==NULL)) 
    {
        printf("************************ ERROR! ***********************\n");
        printf("**     Not enough comand line arguments input.       **\n");
        printf("**     Please run again with the correct amount (6). **\n");
        printf("*******************************************************\n");
        validation=1;
        goto VALIDATIONFAIL;
    }
if((sscanf(argv[1], "%lf", &mneg)==NULL) || (sscanf(argv[2], "%lf", &mpos)==NULL) || (sscanf(argv[3], "%lf", &x[0])==NULL) ||
(sscanf(argv[4], "%lf", &y[0])==NULL) || (sscanf(argv[5], "%lf", &xv)==NULL) || (sscanf(argv[6], "%lf", &yv)==NULL) )
{
    printf("************************* ERROR! ************************\n");
    printf("** Input values must be numbers. Please run again with **\n");                 
    printf("** with numerical inputs (6).                          **\n");
    printf("*********************************************************\n");
    validation=1;
  goto VALIDATIONFAIL;
}

sscanf(argv[1], "%lf", &mneg);
sscanf(argv[2], "%lf", &mpos);
sscanf(argv[3], "%lf", &x[0]);
sscanf(argv[4], "%lf", &y[0]);
sscanf(argv[5], "%lf", &xv);
sscanf(argv[6], "%lf", &yv);


    x[1]=x[0]+(xv*dt);
    y[1]=y[0]+(yv*dt);

for(n=1;n<10000;n++)
    {
        if(x[n-1]>=(1-radius) && x[n-1]<=(1+radius) && y[n-1]>=(0-radius) && y[n-1]<=(0+radius))
        {
            printf("Test mass has collided with M+ at (1,0), Exiting...\n");
            goto EXIT;
        }
        else if(x[n-1]>=(-1-radius) && x[n-1]<=(-1+radius) && y[n-1]>=(0-radius) && y[n-1]<=(0+radius))
        {
            printf("Test mass has collided with M- at (-1,0), Exiting...\n");
            goto EXIT;
        }
        else
        {
            double dxn = x[n] + 1;
            double dxp = x[n] - 1;
            double mnegdist = pow((dxn*dxn + (y[n]*y[n])), -1.5);
            double mposdist = pow((dxp*dxp + (y[n]*y[n])), -1.5);

            ax =  -(mpos*dxp*mposdist+mneg*dxn*mnegdist);
            ay =  -(mpos*y[n]*mposdist+mneg*y[n]*mnegdist); 


            x[n+1]=((2*x[n])-x[n-1] +(dt*dt*ax));
            y[n+1]=((2*y[n])-y[n-1]+(dt*dt*ay));


            fprintf(output, "%lf %lf\n",x[n-1], y[n-1]); 
        }
    }
VALIDATIONFAIL: 
printf("\n");
return(EXIT_FAILURE);   

EXIT:
return(EXIT_SUCCESS);
}

我的程序在一定程度上可以正常工作,但我遇到了一些奇怪的问题,希望有人能帮助我。

主要问题是,当测试质量到达其轨迹中的某个点时,它应该离开并开始围绕另一个质量运行,而不是沿着直线射向无穷远!起初我认为是质量发生碰撞,所以我进行了半径检查,但在某些情况下,这确实有效,在某些情况下,它不起作用,并且在某些情况下,质量在轨迹出错之前就更早发生了碰撞所以这显然不是问题。我不确定我是否已经解释得很好,所以这里有一张图片向您展示我的意思。(右边的模拟来自这里

线

然而,情况并非总是如此,有时不是直线前进,而是当它应该转到另一个质量时,轨迹会变得疯狂,就像这样: 疯狂的

我真的完全不知道发生了什么,我花了几天时间试图解决这个问题,但似乎无法到达任何地方,因此非常感谢任何帮助确定我的问题所在。

4

2 回答 2

4

这太长了,不适合发表评论,我也可能对未来的访问者有用。

为给定计算正确选择时间步长并非易事。Verlet 积分器家族是辛的,这意味着它们保留了相空间体积,因此应该在给定无限精度和无限小的时间步长的情况下保留系统的总能量,但不幸的是,真正的计算机以有限的精度运行,人类的生命也是如此简短,以便我们等待无限数量的时间步长。

Verlet 积分器,就像您已经实现的那个和速度 Verlet 方案一样,具有 O( Δt 2 ) 的全局误差。这意味着该算法仅对时间步长二次敏感,为了大大提高精度,必须将时间步长相应地减少所需精度改进因子的平方根的倍数。单击用于比较轨迹的 Flash 小程序的进入按钮,您会看到它使用了完全不同的积分器 - Euler-Cromer 算法(也称为半隐式 Euler 方法)。给定相同的时间步长,它具有不同的精度(实际上更糟比给定相同时间步长的 Verlet 方案),因此您不能也不应该直接比较两条轨迹,而只能比较它们的统计特性(例如平均能量、平均速度等)

我的观点是你必须减少时间步长,因为当测试体太靠近引力中心之一时,它太大而无法处理。这里隐藏着另一个问题,它是有限的数值精度。观察这个术语:

double dxp = x[n] - 1;
double mposdist = pow((dxp*dxp + (y[n]*y[n])), -1.5);

每当你减去两个相近的浮点数 (x[n]1.0) 时,就会发生一个非常不幸的事件,称为精度损失,因为它们尾数中的大多数高有效位相互抵消,最后,在归一化步骤之后,你会得到一个数字与原始两个数字相比,有效位要少得多。当结果被平方并用作分母时,这种精度损失变得更大。请注意,这主要发生在接近 的系统对称轴y[n]附近0。否则y[n]可能足够大,因此这dxp*dxp只是对价值的微小修正y[n]*y[n]. 最终结果是在每个固定质量附近的力将完全错误,并且通常在大小上大于实际力。当您测试超出规定的点时,这在您的情况下是可以防止的radius

在固定的时间步长下,更大的力会导致更大的位移。这也导致系统总能量的人为增加,即测试质量将倾向于比在更精细的模拟中移动得更快。也可能发生这样的情况,即测试体最终离引力中心太近,巨大的力乘以时间步长的平方可能会产生如此大的位移,以至于你的测试质量最终会离得很远,但这次是增加的总能量会导致高动能,并且身体实际上会从模拟体积中弹出。即使您以无限精度计算力也可能发生这种情况 - 只是两个时间步之间的位移可能非常大(因为时间步长),系统会在相空间中进行不切实际的跳跃到完全不同的能量等值面。并且在重力(以及静电)的情况下,随着力的增加,很容易达到这种情况1/r^2在半径附近,它比初始状态强许多数量级。

在给定最大预期力值的情况下,人们可能会想出不同的经验法则来估计时间步长的大小,但一般来说,最大力越高,时间步长应该越低。这类事情通常可以在给定初始条件的情况下进行粗略估计,这样可以节省大量由于“弹射”效应而导致的失败模拟。

现在由于 Verlet 方案是辛的,控制模拟正确性的最佳方法是观察系统的总能量。请注意,速度 Verlet 积分器要好一些,因为它在数值上更稳定(但它仍然具有相同的精度对时间步长平方的依赖性)。使用标准 Verlet 方案,您可以v[i]通过取(x[i+1] - x[i-1])/(2*dt). 对于速度 Verlet,速度明确包含在方程中。

无论哪种方式,在每个时间步取速度并计算系统的总能量并观察值都是有意义的。如果时间步长是恰到好处 (tm),那么总和应该几乎是守恒的,平均值周围的振荡相对较小。如果它向上疯狂,那么你的时间步长太大了,应该减少。

减少时间步长会相应地增加模拟的运行时间。还可以观察到,在远场中,力小,点移动慢,时间步长长就好。更短的时间步不会改善那里的解决方案,只会增加运行时间。这就是为什么人们发明了多时间步长算法和自适应时间步长算法来自动细化近场解决方案。也可以通过变换方程来应用另一种计算力的方法,以便不包括相近变量的减法。

(嗯,这比几个评论都要大)

于 2012-12-19T18:18:20.653 回答
0

我发现很难理解你的代码,所以如果我告诉你你已经知道的事情,我会尽力提供帮助并道歉。

我发现计算涉及重力的模拟物理的最佳方法是使用牛顿万有引力定律。这是由公式给出的:

F = ( ( -G * M1 * M2 ) / ( R * R ) ) * r_unit_vector;

在哪里:

G ~= 6.67e-11,M1是第一个物体的质量,M2是第二个物体的质量,R是两个物体之间的距离:sqrt(pow(X2 - X1, 2) + pow(Y2 - Y1, 2))

X1 为对象 1 的 X 坐标,X2 为对象 2 的 X 坐标,Y1 为对象 1 的 Y 坐标,Y2 为对象 2 的 Y 坐标。

r_unit_vector 是从对象 2 指向对象 1 的单位向量

struct r_unit_vector_struct{
    double x, y;
}r_unit_vector;

r_unit_vector 具有 ax 分量,即对象 2 的 x 坐标 - 对象 1 的 x 坐标,r_unit_vector 具有 y 分量,即对象 2 的 y 坐标 - 对象 1 的 y 坐标。

要使 r_unit_vector 成为单位向量,您必须将(x a 和 y 分量分别)除以其长度,由下式给出sqrt(pow(r_unit_vector.x, 2) + pow(r_unit_vector.y - Y1, 2))

你们都应该准备好了!希望这是有道理的。如果没有,我会给你写一堂课来做这些事情,或者如果可以的话,进一步解释一下!

于 2012-12-29T21:48:09.947 回答