液体シミュレーションでの質量の節約


8

私はここでフォスターとフェドキウの論文「液体の実用的なアニメーション」の2Dバージョンを実装しようとしています:http ://physbam.stanford.edu/~fedkiw/papers/stanford2001-02.pdf

セクション8「質量の保存」を除いて、ほとんどすべてが機能します。そこで、液体の発散を自由にするために必要な圧力を計算するための方程式の行列を設定します。

私のコードは論文と一致していると思いますが、質量ステップの保存中に解けない行列を取得しています。

これが、行列Aを生成するための私のステップです。

  1. 斜めのエントリを設定する Ai,i セルiに隣接する液体セルの数の負数。
  2. エントリーを設定する Ai,j そして Aj,i セルiとjの両方に液体がある場合は1になります。

私の実装では、セルが ij 液体グリッドで行に対応 i+gridWidthj マトリックスで。

論文では、「静的オブジェクトと空のセルはこの構造を乱さない。その場合、圧力と速度の項は両側から消える可能性がある」と述べたので、液体のないセルの列と行を削除します。

だから私の質問です:なぜ私の行列は特異ですか?論文の他の場所にある種類の境界条件がありませんか?私の実装が2Dであるという事実ですか?

これは、0,0のセルに液体がない場合の2x2グリッドの実装からのマトリックスの例です。

-1   0   1

 0  -1   1

 1   1  -2

編集する

私の研究により、境界条件を適切に処理していないと思いました。

まず、この時点で、私の行列は離散圧力ポアソン方程式を表していると言えます。これは、局所的な圧力変化を細胞発散に結合するラプラシアン演算子を適用することの離散的な類似物です。

私が理解できる限りでは、圧力差を扱っているため、圧力を絶対参照値に「固定」するには境界条件が必要です。それ以外の場合、方程式のセットに対する解の数は無限になる可能性があります。

ではこれらのノート、3種類の方法は、私の理解の最高に、境界条件を適用するために与えられています。

  1. ディリクレ-境界での絶対値を指定します。

  2. Neummann-境界での導関数を指定します。

  3. ロビン-境界での絶対値と微分のある種の線形結合を指定します。

フォスターアンドフェドキの論文ではこれらについては触れられていませんが、7.1.2の最後にある「表面セルの圧力は大気圧に設定されている」というこの声明のために、ディリクレ境界条件を強制していると思います。

リンクを数回読みましたが、まだ数学がよくわかりません。これらの境界条件をどの程度正確に適用しますか?他の実装を見ると、境界にある「ゴースト」セルのある種の概念があるようです。

ここに私はこれを読んでいる他の人に役立つかもしれないいくつかの情報源にリンクしました。

ポアソン行列の境界条件に関する注記

Neumann境界条件に関する計算科学StackExchangeの投稿

ポアソンソルバーに関する計算科学StackExchangeの投稿

水Physbamの実装


これが、マトリックスの生成に使用するコードです。列と行を明示的に削除する代わりに、液体セルのインデックスから最終的な行列の列/行へのマップを生成して使用していることに注意してください。

for (int i = 0; i < cells.length; i++) {
  for (int j = 0; j < cells[i].length; j++) {
    FluidGridCell cell = cells[i][j];

    if (!cell.hasLiquid)
      continue;

    // get indices for the grid and matrix
    int gridIndex = i + cells.length * j;
    int matrixIndex = gridIndexToMatrixIndex.get((Integer)gridIndex);

    // count the number of adjacent liquid cells
    int adjacentLiquidCellCount = 0;
    if (i != 0) {
      if (cells[i-1][j].hasLiquid)
        adjacentLiquidCellCount++;
    }
    if (i != cells.length-1) {
      if (cells[i+1][j].hasLiquid)
        adjacentLiquidCellCount++;
    }
    if (j != 0) {
      if (cells[i][j-1].hasLiquid)
      adjacentLiquidCellCount++;
    }
    if (j != cells[0].length-1) {
      if (cells[i][j+1].hasLiquid)
        adjacentLiquidCellCount++;
    }

    // the diagonal entries are the negative count of liquid cells
    liquidMatrix.setEntry(matrixIndex, // column
                          matrixIndex, // row
                          -adjacentLiquidCellCount); // value

    // set off-diagonal values of the pressure matrix
    if (cell.hasLiquid) {
      if (i != 0) {
        if (cells[i-1][j].hasLiquid) {
          int adjacentGridIndex = (i-1) + j * cells.length;
          int adjacentMatrixIndex = gridIndexToMatrixIndex.get((Integer)adjacentGridIndex);
          liquidMatrix.setEntry(matrixIndex, // column
                                adjacentMatrixIndex, // row
                                1.0); // value
          liquidMatrix.setEntry(adjacentMatrixIndex, // column
                                matrixIndex, // row
                                1.0); // value
        }
      }
      if (i != cells.length-1) {
        if (cells[i+1][j].hasLiquid) {
          int adjacentGridIndex = (i+1) + j * cells.length;
          int adjacentMatrixIndex = gridIndexToMatrixIndex.get((Integer)adjacentGridIndex);
          liquidMatrix.setEntry(matrixIndex, // column
                                adjacentMatrixIndex, // row
                                1.0); // value
          liquidMatrix.setEntry(adjacentMatrixIndex, // column
                                matrixIndex, // row
                                1.0); // value
        }
      }
      if (j != 0) {
        if (cells[i][j-1].hasLiquid) {
          int adjacentGridIndex = i + (j-1) * cells.length;
          int adjacentMatrixIndex = gridIndexToMatrixIndex.get((Integer)adjacentGridIndex);
          liquidMatrix.setEntry(matrixIndex, // column
                                adjacentMatrixIndex, // row
                                1.0); // value
          liquidMatrix.setEntry(adjacentMatrixIndex, // column
                                matrixIndex, // row
                                1.0); // value
        }
      }
      if (j != cells[0].length-1) {
        if (cells[i][j+1].hasLiquid) {
          int adjacentGridIndex = i + (j+1) * cells.length;
          int adjacentMatrixIndex = gridIndexToMatrixIndex.get((Integer)adjacentGridIndex);
          liquidMatrix.setEntry(matrixIndex, // column
                                adjacentMatrixIndex, // row
                                1.0); // value
          liquidMatrix.setEntry(adjacentMatrixIndex, // column
                                matrixIndex, // row
                                1.0); // value
        }
      }
    }

静的オブジェクトと空のセルが行と列の削除を許可する理由は明らかではありません。これらの行と列をゼロに設定していますか、それともそれらをすべて削除してより小さなマトリックスを作成していますか?
trichoplax 2016

問題が推測した場所以外の場所にある場合は、コードを確認しておくと役立ちます。これが共有して喜んでくれるものである場合に役立ちます。理想的にはMCVE
trichoplax 2016

こんにちは。行または列がすべて0の行列は、私が知る限り、特異です。そのため、それらを行列から削除して、より小さな行列(およびbベクトルの対応するエントリ)を作成します。
Jaredは2016

今夜、ソースのあるコンピューターの近くにいるときにMCVEを編集します。
Jaredは2016

また、コードのどこかで間違った仮定をしているのではないかと疑いましたが、これはマトリックス構造にのみ関係します(それが特異かどうかに関係ありません)。私が考えることができる唯一のことは、「表面セル」対空気セルまたは液体セルとして適格であるものです。これが空気セルに隣接する液体セルである場合、対応する列/行で行う必要がある何か違うことはありますか?
Jaredは2016

回答:


2

コードスニペットと2x2の例の結果から、実際にはノイマン境界条件(スリップウォール)のみでドメインをシミュレーションしていることがわかります。この場合、システムにはnullスペースが含まれ、行列は特異です。

これが必要なシミュレーション構成である場合(つまり、ディリクレ(圧力)BCがない場合)、ソリューションからヌル空間を投影する必要があります。その論文で提案されている共役勾配(CG)を使用している場合、これは簡単です。CGの反復の各反復で、現在の解ベクトルを取得するx、 そして、やります

x=(Iu^u^T)x=x(u^x)u^
どこ u^ 勾配演算子の正規化されたヌル空間です。 u=(1,1,,1)u^=uu

それ以外の場合、空気(自由境界またはディリクレBC)をシミュレートする場合は、壁と空気セルを区別し(ブール値hasLiquidでは不十分)、それらに適切な離散化を適用する必要があります(以下を参照)。

最後に、対角線のエントリはマイナスです。CG法が機能するように、標識を反転することもできます。


以下に詳細を示します。圧力予測プロセスを検討してください。圧力投影前の速度をとして表します。発散する可能性があるため、圧力を計算して修正し、発散のない速度を取得します。つまり、 発散しますそして、は発散しないため、 圧力ディリクレBCが存在せず、1つの解、定数に対してがあるとしvvn+1

vn+1=vΔtρP
vn+1
P=v
P0P0+ccため、これも解決策です。 は、投影するnullスペースです。(P0+c)=P0=vc

ディリクレBCを処理するために、例として1Dケースを考えてみましょう。スタッガードグリッドを使用していると仮定します。圧力はグリッドの中心にあり、速度はノードと間の面にあります。次に、1つのセルの一般的な離散化は ですは空気セルです。つまり、その圧力が指定されていると、項が右側に移動し、行列から消えます。対角項数はまだ2 であることに注意してください。そのため、2x2の例にはディリクレBCが含まれていないと述べました。pivi+1/2ii+1

pi+1pi(pipi1)Δx2=rhs
pi+1pi+1pi

ディリクレまたはノイマンBCのいずれかを使用すると、行列は常に対称正定値になります。それが著者が言った理由です

Static object and empty cells don’t disrupt this structure.
In that case pressure and velocity terms can disappear from both sides
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.