0

私は現在、2つの固定質量と1つのテスト質量を使用して、制限された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インテグレーターのファミリーはシンプレクティックです。つまり、位相空間の体積を維持するため、無限の精度と無限に小さなタイムステップでシステムの総エネルギーを維持する必要がありますが、残念ながら、実際のコンピューターは有限の精度で動作し、人間の生活も同様です。無限のタイムステップを待つために短い。

実装したものや速度ベレスキームのようなベレ積分器には、O(Δt2)であるグローバルエラーがあります。これは、アルゴリズムがタイムステップに二乗的にのみ敏感であることを意味し、精度を大幅に向上させるには、それに応じて、目的の精度向上係数の平方根の数倍だけタイムステップを減らす必要があります。軌跡を比較するFlashアプレットの[入力]ボタンをクリックすると、まったく異なるインテグレータであるオイラー-クロマーアルゴリズム(半暗黙オイラー法とも呼ばれます)が使用されていることがわかります。同じタイムステップで精度が異なります(実際にはもっと悪いです同じタイムステップが与えられたVerletスキームよりも)、したがって、両方の軌道を直接比較することはできず、直接比較するべきではありません。統計的特性(平均エネルギー、平均速度など)のみを比較する必要があります。

私のポイントは、テストボディが重力中心の1つに近づきすぎると、タイムステップが大きすぎて処理できないため、タイムステップを短くする必要があるということでした。ここに隠された別の問題があり、それは有限の数値精度です。この用語を守ってください:

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

x[n]2つの密接に値する浮動小数点数(および)を減算するたび1.0に、非常に不幸なイベントが発生します。これは、仮数の上位ビットのほとんどが互いに打ち消し合うため、精度の低下として知られています。最終的に、正規化ステップの後、数値が得られます。元の2つの数値よりもはるかに少ない重要なビットで。結果が二乗されて分母として使用されるため、この精度の低下はさらに大きくなります。これは主に、に近づくシステムの対称軸の近くで発生することに注意してy[n]ください0。それ以外の場合y[n]は十分に大きい可能性があるためdxp*dxpy[n]*y[n]。最終的な結果として、力は各固定質量の近くで完全に間違って出てきて、通常は実際の力よりも大きさが大きくなります。あなたが規定の外にあるポイントをテストするとき、これはあなたの場合に防がれますradius

一定のタイムステップが与えられると、力が大きくなると変位が大きくなります。これはまた、システムの総エネルギーの人為的な増加につながります。つまり、テスト質量は、より細かいシミュレーションよりも速く移動する傾向があります。また、テストボディが重力中心に非常に近くなり、タイムステップの2乗に大きな力を掛けると、非常に大きな変位が発生し、テスト質量がはるかに遠くなる可能性がありますが、今回は総エネルギーが増加すると、高い運動エネルギーが発生し、ボディは実際にはシミュレーションボリュームから排出されます。これは、力を無限の精度で計算した場合でも発生する可能性があります。2つのタイムステップ間の変位が非常に大きいため(タイムステップが大きいため)、システムが位相空間で完全に異なるエネルギー等値面に非現実的にジャンプする可能性があります。そして、重力(および静電気)を使用すると、力が次のように増加するため、このようなケースに簡単に到達できます。1/r^2半径の近くでは、初期状態よりも何桁も強くなっています。

最大の予想される力の値が与えられた場合にタイムステップのサイズを推定するためのさまざまなルールを思い付くかもしれませんが、一般に、最大の力が高いほど、タイムステップは低くなります。これらの種類のものは通常、初期条件を考慮して大まかに見積もることができ、「放出」効果のために失敗したシミュレーションの多くを節約できます。

Verletスキームはシンプレクティックであるため、シミュレーションの正確さを制御する最良の方法は、システムの総エネルギーを観察することです。速度Verlet積分器は、数値的に安定しているため、少し優れていることに注意してください(ただし、精度はタイムステップの2乗に同じように依存します)。v[i]標準のVerletスキームでは、をとることで速度の概算を得ることができます(x[i+1] - x[i-1])/(2*dt)。速度Verletを使用すると、速度は方程式に明示的に含まれます。

いずれにせよ、速度を取得し、各タイムステップでシステムの総エネルギーを計算し、値を観察することは理にかなっています。タイムステップがJustRight(tm)の場合、平均値の周りの比較的小さな振動で合計がほぼ保存されるはずです。それが上向きに狂った場合、あなたのタイムステップは大きすぎるので、減らす必要があります。

タイムステップを短くすると、それに応じてシミュレーションの実行時間が長くなります。また、遠方界では力が小さく、ポイントがゆっくりと移動し、長いタイムステップで十分であることがわかります。タイムステップを短くしても、そこでのソリューションは改善されませんが、実行時間が増えるだけです。そのため、人々はマルチタイムステップアルゴリズムと、近接場のソリューションを自動的に改良する適応タイムステップアルゴリズムを発明しました。また、力を計算するための別の方法は、密接に評価された変数の減算を含まないように方程式を変換することによってそこに適用される可能性があります。

(まあ、これはいくつかのコメントよりもはるかに大きくなりました)

于 2012-12-19T18:18:20.653 に答える
0

あなたのコードを理解するのは難しいと思ったので、あなたがすでに知っていることをあなたに話しているなら、私はできる限り助けて謝罪するように努めます。

重力を含むシミュレーションの物理学を計算するために私が見つけた最良の方法は、万有引力のニュートンの法則を使用することです。これは次の式で与えられます。

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

どこ:

G〜= 6.67e-11、M1は最初のオブジェクトの質量、M2は2番目のオブジェクトの質量、Rは2つのオブジェクト間の距離です。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には、オブジェクト2のx座標(オブジェクト1のx座標)であるaxコンポーネントがあり、r_unit_vectorには、オブジェクト2のy座標(オブジェクト1のy座標)であるayコンポーネントがあります。

r_unit_vectorを単位ベクトルにするには、(xとyの両方のコンポーネントを別々に)その長さで割る必要があります。sqrt(pow(r_unit_vector.x, 2) + pow(r_unit_vector.y - Y1, 2))

そして、あなたはすべて行く準備ができているはずです!うまくいけば、これは理にかなっています。そうでない場合は、このようなことを行うためのクラスを作成するか、可能であればさらに説明します。

于 2012-12-29T21:48:09.947 に答える