web-dev-qa-db-ja.com

重力n体シミュレーションに必要なストレージの量を削減するにはどうすればよいですか?

私は現在、修正されたBarnes-Hutアルゴリズムを使用して重力n体シミュレーションを作成し、GPU計算によりよく対応できるようにしています。これは主に学習プロジェクトです。私の目標は、実際の銀河に匹敵する数々の星をシミュレートすることです。つまり、数千億から数十兆のオーダーですが、数百万でも有用です。これを表示に近い速度で計算できる可能性はほとんどありません。つまり、データを事前に計算し、計算が終了した後でそれを確認する必要があります。これを行うには、データを保存する方法の最初のアイデアは、すべての星の場所を離散化された時間の各瞬間に連結したファイルを作成し、次の瞬間をそれに連結して、次のようなものを作成することです、各括弧内のデータは単一のフレームを表します:

{x₁, y₁, z₁, x₂, y₂, z₂, …. xₙ, yₙ, zₙ}, {x₁, y₁, z₁, x₂, y₂, z₂, …. xₙ, yₙ, zₙ}, {x₁, y₁, z₁, x₂, y₂, z₂, …. xₙ, yₙ, zₙ}

または、この形式はC++ pusedo-codeで記述できます(C++実装間の移植性は重要ではありません)。

void writeData(std::vector<Frame> frames, std::ostream &out){
    //decoding knows how many points there are in each frame as a property of the file format, so it    can read an entire frame at a time until EOF
    for(const Frame &frame : frames){
        for(Point &point : frame.points()){
            float x = point.x();
            float y = point.y();
            float z = point.z();
            out.write(&x, sizeof(float));
            out.write(&y, sizeof(float));
            out.write(&z, sizeof(float));
        }
    }
}

この形式でバイト単位で指定されるデータのサイズは、n個の粒子の場合、n * 3 * 2 * 60 * 25、1分のデータ、25フレーム/秒、および半精度浮動小数点数です。 10億個のパーティクルの場合、これは10億個のパーティクルを含む1分のビデオで16テラバイトに相当します。これは、保存するのに十分なハードドライブに物理的に近づいていない(そして私は多くのハードドライブを持っている)ものです。また、このデータは、バイナリの観点からはかなり構造化されていないため、zlibなどの標準の可逆圧縮アルゴリズムでうまく圧縮されるかどうかも疑問です。また、このデータに対して適切に機能する、かなり単純な圧縮アルゴリズムについては考えられません。

もちろん、粒子のレンダリングされたフレームを2次元画像にエンコードして、多くの最新の圧縮アルゴリズムのいずれかでビデオを作成できますが、これにより、データを観察しながらカメラの位置を変更する機能が犠牲になります。シミュレートされた粒子の3次元レイアウトの十分な理解(つまり、地球が銀河を移動するときに、地質学的時間スケールで星座が完全に異なるように見えることを考慮してください)。さまざまな視点から複数のビデオをエンコードすることでこれは多少軽減されますが、再生中にカメラの位置と角度を制御する機能に匹敵するものはありません。

大量のハードドライブに何千ドルも費やすことなく、事前に計算されたデータと多数の粒子でカメラの動きを有効にするにはどうすればよいですか?これは、通常の2次元のビデオが同様の問題であり、実用的に役立つように十分に解決されたため、および他の人がn体シミュレーションを行ったために可能であると思います(n-にしかできないビデオを見てきたように、ニュース出版物における体のシミュレーション)。

6
john01dav

フロート警告

浮動小数点の精度に注意してください。マンテッサはxビットの長さしかないため、ゼロに近い値の場合、非常に短い距離を表すことができますが、銀河系の距離では、エラーが非常に大きくなる可能性があります。

また、多くの論理圧縮技術が困難になります。

固定点デルタ

固定小数点数を使用する場合は、デルタを格納できます。これは、ほとんどのオブジェクトが最後のフレームからそれほど遠くまで移動していないという観察に基づいています。したがって、座標ごとに3*Nバイトを格納する代わりに、たとえば3または6バイトを使用することができます。

次のk座標のデルタの長さを事前に通知する必要があるため、これはエンコード方式を少し複雑にします。しかし、それはサイズの面で大きな利益をもたらすはずです。

エラー修正された推測

これらのポイントが動いているとすれば、次のポイントがどこで発生するかを「推測」できます。したがって、最初の2つまたは3つの座標は、フルレングス、またはフルレングスにオフセット座標を加えたものです。その後の次の座標は、前のXポイントに適合する直線または曲線を使用して推測されます。書き留められた座標情報は、その推測とシミュレーションによって生成された実際の座標との間の誤差です。

参照フレーム

オブジェクトが一方向に移動している場合、デルタをグローバル座標からローカル座標にリフレームできます。ローカル座標系では、同じまたは同様のデルタが発生し、一般に圧縮効率が向上します。

同様に、ローカル座標では、前のデルタは次の場所についての推測の一種と見なすことができ、デルタは推測を修正するため、結果として小さくなります。

加速度などの物理デルタをエンコードします

場所だけが気になっているように見えるので、加速度や速度など、物理学が提供する自然に組み込まれたデルタを利用できます。

ニュートンの位置/速度/加速度は、消費側での計算が比較的簡単です(映画を制作するには十分です)。シミュレーションは、物理的な説明を使用して実際の問題を解決し、グローバル座標系でこれを説明できます。次に、これをニュートン計算を使用して逆に説明し、ストレージと後でレンダリングするための適切なデルタを生成します。

この意味で、ファイルは場所+速度で始まります。後続のすべての座標は、その動的に加速度を追加し、次のフレームのためにそれを更新することから得られます。アクセラレーションは、生、デルタ、推測によるエラー修正のいずれかでエンコードできます。

粒度

通常、オブジェクトの多くは同様のパスに沿って移動します。同じ場所に配置されたオブジェクトのグループの平均/モード/平均の動きを見つけて、その操作(並進+回転)を高レベルで記述し、次に新しい座標と実際の座標でのグローバル/グループレベルの「推測」。

動きが十分に一貫している場合、これは通常、はるかに小さなデルタを与えます。

単項およびビットパッキング

上記のデルタを絞り込んで、非常に小さいデルタ/エラー訂正を頻繁に行う場合は、単項およびビットパッキングを使用して、さらに小さくすることができます。

単項は、数えるために指を離さないことと似ていない数のエンコーディングです。

0           //1 bit long, represents the number 0
10          //2 bits long, represents the number 1
11110       //5 bits long, represents the number 4
11111111110 //11 bits long, represents the number 10

これらの単項数を直接使用して小さなデルタを表すか、座標デルタに入るビット数などを表す式の指数として使用できます。 2*2^kは、2ビット、4ビット、6ビットなどの座標を示します。

64ビットWordにビットパッキングすることで、10-2ビットの座標ペアを圧縮できます。

0123456789ABCDEF 0123456789ABCDEF 0123456789ABCDEF 0123456789ABCDEF
110-Q1Q2Q3R1R2R3 S1S2S3T1T2T3U1U2 U3V1V2V3W1W2W3X1 X2X3Y1Y2Y3Z1Z2Z3

//the first 3bits is unary describing that the 64-bit record is 2bit co-ordinate triples.
//the - is an unused bit.
//Q1Q2Q3 through Z1Z2Z3 are the co-ordinate triples.

ただし、走行距離は変化するため、シミュレーションで生成されたデータの一部を分析し、タイムステップ間でどれだけ類似しているかを分析します。

7
Kain0_0