私はここでフォスターとフェドキウの論文「液体の実用的なアニメーション」の2Dバージョンを実装しようとしています:http ://physbam.stanford.edu/~fedkiw/papers/stanford2001-02.pdf
セクション8「質量の保存」を除いて、ほとんどすべてが機能します。そこで、液体の発散を自由にするために必要な圧力を計算するための方程式の行列を設定します。
私のコードは論文と一致していると思いますが、質量ステップの保存中に解けない行列を取得しています。
これが、行列Aを生成するための私のステップです。
- 斜めのエントリを設定する セルiに隣接する液体セルの数の負数。
- エントリーを設定する そして セルiとjの両方に液体がある場合は1になります。
私の実装では、セルが 、 液体グリッドで行に対応 gridWidth マトリックスで。
論文では、「静的オブジェクトと空のセルはこの構造を乱さない。その場合、圧力と速度の項は両側から消える可能性がある」と述べたので、液体のないセルの列と行を削除します。
だから私の質問です:なぜ私の行列は特異ですか?論文の他の場所にある種類の境界条件がありませんか?私の実装が2Dであるという事実ですか?
これは、0,0のセルに液体がない場合の2x2グリッドの実装からのマトリックスの例です。
-1 0 1
0 -1 1
1 1 -2
編集する
私の研究により、境界条件を適切に処理していないと思いました。
まず、この時点で、私の行列は離散圧力ポアソン方程式を表していると言えます。これは、局所的な圧力変化を細胞発散に結合するラプラシアン演算子を適用することの離散的な類似物です。
私が理解できる限りでは、圧力差を扱っているため、圧力を絶対参照値に「固定」するには境界条件が必要です。それ以外の場合、方程式のセットに対する解の数は無限になる可能性があります。
ではこれらのノート、3種類の方法は、私の理解の最高に、境界条件を適用するために与えられています。
ディリクレ-境界での絶対値を指定します。
Neummann-境界での導関数を指定します。
ロビン-境界での絶対値と微分のある種の線形結合を指定します。
フォスターアンドフェドキの論文ではこれらについては触れられていませんが、7.1.2の最後にある「表面セルの圧力は大気圧に設定されている」というこの声明のために、ディリクレ境界条件を強制していると思います。
リンクを数回読みましたが、まだ数学がよくわかりません。これらの境界条件をどの程度正確に適用しますか?他の実装を見ると、境界にある「ゴースト」セルのある種の概念があるようです。
ここに私はこれを読んでいる他の人に役立つかもしれないいくつかの情報源にリンクしました。
Neumann境界条件に関する計算科学StackExchangeの投稿
ポアソンソルバーに関する計算科学StackExchangeの投稿
これが、マトリックスの生成に使用するコードです。列と行を明示的に削除する代わりに、液体セルのインデックスから最終的な行列の列/行へのマップを生成して使用していることに注意してください。
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
}
}
}