三体问题

计算科学 C++
2021-12-17 05:11:54

我编写了以下程序来解决三体问题(太阳、地球、木星),初始化系统使得总角动量为 0。模拟的结果非常糟糕(太阳趋向于无穷大)。我看了一个多小时的代码试图找出问题所在。有人看到我的程序有什么问题吗?

非常感谢你。

#include <iostream>
using std::cout;
using std::endl;

#include <fstream>
using std::ofstream;

#include <cmath>
using std::sqrt;

int main()
{
    double time(0), dt(0.05);

    double G(1.9838e-29);

    double m1(6e24), m2(1.9e27), ms(1.99e30);

    double x1(1), y1(0), x2(5.2), y2(0), xs(0), ys(0);

    double T1(1), T2(11.8618);

    double vx1(0),vy1(2*M_PI*x1/T1), vx2(0), vy2(2*M_PI*x2/T2);

    double Rx( (m1*x1+m2*x2+ms*xs)/(m1+m2+ms) ), Ry( (m1*y1+m2*y2+ms*ys)/(m1+m2+ms) );

    x1 = x1 - Rx;
    x2 = x2 - Rx;
    xs = xs - Rx;
    y1 = y1 - Ry;
    y2 = y2 - Ry;
    ys = ys - Ry;

    double Lx1(x1*m1*vx1), Lx2(x2*m2*vx2);

    double vxs(0), vys( -(Lx1+Lx2)/(ms*xs) );

    double r12(0), r1s(0), r2s(0);

    ofstream output("TB_jupiter-earth.dat");

    output << time << ' ' << x1 << ' ' << y1 << ' ' << x2 << ' ' << y2 << ' ' << xs << ' ' << ys <<endl;

    while(time < 12)
    {
        r12 = sqrt( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) );
        r1s = sqrt( (x1-xs)*(x1-xs) + (y1-ys)*(y1-ys) );
        r2s = sqrt( (x2-xs)*(x2-xs) + (y2-ys)*(y2-ys) );

        vx1 = vx1 - G*ms*dt*(x1-xs)/(r1s*r1s*r1s) - G*m2*(x1-x2)*dt/(r12*r12*r12);
        vy1 = vy1 - G*ms*dt*(y1-ys)/(r1s*r1s*r1s) - G*m2*(y1-y2)*dt/(r12*r12*r12);

        vx2 = vx2 - G*ms*dt*x2/(r2s*r2s*r2s) - G*m1*(x2-x1)*dt/(r12*r12*r12);
        vy2 = vy2 - G*ms*dt*y2/(r2s*r2s*r2s) - G*m1*(y2-y1)*dt/(r12*r12*r12);

        vxs = vxs - G*m1*(xs-x1)*dt*(r1s*r1s*r1s) - G*m2*(xs-x2)*dt*(r2s*r2s*r2s);
        vys = vys - G*m1*(ys-y1)*dt*(r1s*r1s*r1s) - G*m2*(ys-y2)*dt*(r2s*r2s*r2s);


        x1 = x1 + vx1*dt;
        y1 = y1 + vy1*dt;

        x2 = x2 + vx2*dt;
        y2 = y2 + vy2*dt;

        xs = xs + vxs*dt;
        ys = ys + vys*dt;

        time += dt;

        output << time << ' ' << x1 << ' ' << y1 << ' ' << x2 << ' ' << y2 << ' ' << xs << ' ' << ys <<endl;
    }

    output.close();

    return 0;
}

编辑:

第一个答案的更正很好,但现在我改变了角动量的计算(它们是错误的)并且程序不再正常工作。代码如下:

#include <iostream>
using std::cout;
using std::endl;

#include <fstream>
using std::ofstream;

#include <cmath>
using std::sqrt;

int main()
{
    double time(0), dt(0.005);

    double G(1.9838e-29);

    double m1(6e24), m2(500*1.9e27), ms(1.99e30);

    double x1(1), y1(0), x2(5.2), y2(0), xs(0), ys(0);

    double T1(1), T2(11.8618);

    double vx1(0),vy1(2*M_PI*x1/T1), vx2(0), vy2(2*M_PI*x2/T2);

    double Rx( (m1*x1+m2*x2+ms*xs)/(m1+m2+ms) ), Ry( (m1*y1+m2*y2+ms*ys)/(m1+m2+ms) );

    x1 = x1 - Rx;
    x2 = x2 - Rx;
    xs = xs - Rx;
    y1 = y1 - Ry;
    y2 = y2 - Ry;
    ys = ys - Ry;

    double Ly1(x1*m1*vy1), Ly2(x2*m2*vy2);

    double vxs(0), vys( -(Ly1+Ly2)/(ms*xs) );

    double r12(0), r1s(0), r2s(0);

    ofstream output("TB_jupiter-earth.dat");

    output << time << ' ' << x1 << ' ' << y1 << ' ' << x2 << ' ' << y2 << ' ' << xs << ' ' << ys <<endl;

    cout << x1 << ' ' << y1 << ' '<< vx1 << ' ' << vy1 << endl;
    cout << x2 << ' ' << y2 << ' '<< vx2 << ' ' << vy2 << endl;
    cout << xs << ' ' << ys << ' '<< vxs << ' ' << vys << endl;

    while(time < 4)
    {
        r12 = sqrt( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) );
        r1s = sqrt( (x1-xs)*(x1-xs) + (y1-ys)*(y1-ys) );
        r2s = sqrt( (x2-xs)*(x2-xs) + (y2-ys)*(y2-ys) );

        vx1 = vx1 - G*ms*dt*(x1-xs)/(r1s*r1s*r1s) - G*m2*(x1-x2)*dt/(r12*r12*r12);
        vy1 = vy1 - G*ms*dt*(y1-ys)/(r1s*r1s*r1s) - G*m2*(y1-y2)*dt/(r12*r12*r12);

        vx2 = vx2 - G*ms*dt*(x2-xs)/(r2s*r2s*r2s) - G*m1*(x2-x1)*dt/(r12*r12*r12);
        vy2 = vy2 - G*ms*dt*(y2-ys)/(r2s*r2s*r2s) - G*m1*(y2-y1)*dt/(r12*r12*r12);

        vxs = vxs - G*m1*(xs-x1)*dt/(r1s*r1s*r1s) - G*m2*(xs-x2)*dt/(r2s*r2s*r2s);
        vys = vys - G*m1*(ys-y1)*dt/(r1s*r1s*r1s) - G*m2*(ys-y2)*dt/(r2s*r2s*r2s);


        x1 = x1 + vx1*dt;
        y1 = y1 + vy1*dt;

        x2 = x2 + vx2*dt;
        y2 = y2 + vy2*dt;

        xs = xs + vxs*dt;
        ys = ys + vys*dt;

        time += dt;

        output << time << ' ' << x1 << ' ' << y1 << ' ' << x2 << ' ' << y2 << ' ' << xs << ' ' << ys <<endl;
    }

    output.close();

    return 0;
}
2个回答

您的问题是欧拉变体是引力系统的错误方法。您需要研究Symplectic Integrators这将解决您遇到的稳定性问题,因为这种类型的积分器保持某些值不变(在这种情况下为能量)。

此外,作为对代码的注释,您应该在更新状态变量之前使用临时变量来保存派生。正如您现在所拥有的,一个一个地更新变量,混合了来自两个时间步的信息。现在,您正在显式更新速度和隐式更新位置。除非您有充分的理由这样做,否则最好坚持使用单一方法解决问题,以便您了解稳定性特征。

编辑: 正如评论所述,您使用的是辛积分器,所以这不是问题。您的代码中只是有几个小错别字。

首先,在 v2 更新的第二个任期内,您使用了x2andy2而不是(x2-xs)and (y2-ys)不过,这应该没什么区别,因为您的太阳应该保持在 0,0 附近。

您的主要错误是在太阳速度的更新中,您乘以半径的乘积而不是除以它。四行固定在这里:

    vx2 = vx2 - G*ms*dt*(x2-xs)/(r2s*r2s*r2s) - G*m1*(x2-x1)*dt/(r12*r12*r12);
    vy2 = vy2 - G*ms*dt*(y2-ys)/(r2s*r2s*r2s) - G*m1*(y2-y1)*dt/(r12*r12*r12);

    vxs = vxs - G*m1*(xs-x1)*dt/(r1s*r1s*r1s) - G*m2*(xs-x2)*dt/(r2s*r2s*r2s);
    vys = vys - G*m1*(ys-y1)*dt/(r1s*r1s*r1s) - G*m2*(ys-y2)*dt/(r2s*r2s*r2s);

双G(1.9838e-29);??

也许使用:G = 6.673e-11

试试这个:q = q*q*q; // 一

f = 1/q;g = 2/q;h = 3/q; // 二三, ...