2016-12-23 8 views
3

私は粒子のシステムを持っています。彼らは加速、速度と位置を持っています。この粒子系はどのようにしてエネルギーが増加し続けていますか?

粒子が壁に当たったとき、その速度が反転します。 2個の粒子が互いに近づくと、それらはこの力によって互いに反発:

F=1/r^2 

又は

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

システムが実行中であった場合、私は全ての粒子の合計速度が増加している感じていました。どちらが奇妙ですか?粒子はそのエネルギーを に与えるべきです。したがって、システムの総エネルギーは一定に保たれなければなりません。

システムの運動エネルギーは、私は、システム全体の総エネルギーを監視維持しcout介して印刷

E_k=Sigma v^2 

に等しく、それは増加し続けることを確認します。それはエネルギーの保守性に反する。コード内で私はどこで間違いを犯していますか?

#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


この質問のタグを変更することは避けてください。

私が物理的なフォーラムでこの質問をすると、彼らは物理的ではなくプログラミングの問題であると私に教えてくれるでしょう。

+4

あなたは神粒子を紛失しています:P –

+1

あなたの計算が正しいと仮定すると、数値エラーが蓄積されている可能性があります。 – CodeMonkey

+1

また、離散化手法に問題がある可能性があります。解像度を上げたり下げたりして、解像度が結果を変えるかどうかを調べることができます。 – CodeMonkey

答えて

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; 

そして、積分中の時間ステップを適用します。 (ランダム初期化、これはこの場合には問題ではないので、[速度] Verletはわずかに異なる初期値をオイラーシンプレクティックである。)

 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; 

また、出力には運動エネルギーと潜在エネルギーが結合されています。これらの変更により、私は運動エネルギーが着実に成長している間、グラフ

energy graph

1はそれを見ることができるようになど

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 

などの出力を取得し、総エネルギーが移動するだけで非常にゆっくりと。この後者は

  • シンプレクティック積分法のほぼ正確保存量が着実にまとめること修飾エネルギーの小さなジャンプを導入することができる修飾されたエネルギー関数及び境界で
  • 反射され2つのソースを有することができます全体のエネルギーの小さな変化。
+0

力の0.01倍はm = 0.01と解釈することもできます。しかし、主な問題はステップサイズです。潜在的なエネルギーを追加してくれてありがとう。 – ar2015

+0

私はいくつかの変更を行い、新しい[code](http://pastebin.com/K5hQafWc)を作成しました。さて、私は[プロフィール](https://s30.postimg.org/oon7dyj4h/aaaaa.png)に従って総エネルギーの減少を見る。あなたはどこに問題が見えますか? – ar2015

+0

潜在エネルギーを2回カウントしています。それ以前は、運動エネルギーの2倍を計算して以来、問題はありませんでした。しかし、物理的に正しいものにしたい場合は、 'i LutzL

2

粒子が互いに非常に接近すると、エネルギーが増加する可能性があります。 極小のステップを無制限に取った場合は、すべて問題なく動作します。 しかし、有限のサイズのステップを取ると、粒子はある点から別の点に飛びます。 他のパーティクルのすぐ隣にジャンプした場合、反発力F_x=delta(x)/r^3は非常に大きくなります。これは、潜在的なエネルギーが増加してはならないことを表します。ステップが小さなステップに分割された場合、パーティクルは減速してしまい、そんなに近づけないでしょう。

解決策がわかりません。しかし、あるステップでエネルギーの増加が検出された場合、そのステップを再帰的に小さなステップに細分することができます。

+0

または魔法の冷蔵庫を追加してください。全熱が一定の値を上回ると、全てが一段階下になるように調整します。 –

+0

問題は時間ステップを減らすことで緩和されます。どうもありがとう。 – ar2015

1

あなたの物理学の方程式はdtは、シミュレーション時間ステップである

F = dr/r^3 
a = 0.01 * F 
v += a * dt 
x += v * dt 

ことになっています。パーティクルを更新する2番目のループでは、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; 
    

のように見えるように第二のループ新たな変数dt

  • 変更を導入:

    だからアクションがある提案しました1Dで見つけたと dt = 0.001は100の私の束のためによく見えるパーティクル。

  • 関連する問題