J'essaie d'implémenter une version 2D du document de Foster et Fedkiw, "Practical Animation of Liquids" ici: http://physbam.stanford.edu/~fedkiw/papers/stanford2001-02.pdf
Généralement, tout fonctionne, sauf pour la section 8: «Conservation de la masse». Là, nous avons mis en place une matrice d'équations pour calculer les pressions nécessaires pour libérer le liquide divergeant.
Je crois que mon code correspond au papier, mais je reçois une matrice insoluble lors de la conservation de l'étape de masse.
Voici mes étapes pour générer la matrice A:
- Définissez les entrées diagonales au négatif du nombre de cellules liquides adjacentes à la cellule i.
- Définissez les entrées et à 1 si les deux cellules i et j contiennent du liquide.
Notez que, dans mon implémentation, la cellule , dans la grille liquide correspond à la ligne gridWidth dans la matrice.
Le document mentionne, "L'objet statique et les cellules vides ne perturbent pas cette structure. Dans ce cas, les termes de pression et de vitesse peuvent disparaître des deux côtés", donc je supprime les colonnes et les lignes pour les cellules qui n'ont pas de liquide.
Ma question est donc la suivante: pourquoi ma matrice est-elle singulière? Suis-je en train de manquer une sorte de condition aux limites à un autre endroit du document? Est-ce le fait que mon implémentation est 2D?
Voici un exemple de matrice de mon implémentation pour une grille 2x2 où la cellule à 0,0 n'a pas de liquide:
-1 0 1
0 -1 1
1 1 -2
Éditer
Mes recherches m'ont amené à croire que je ne gère pas correctement les conditions aux limites.
Tout d'abord, à ce stade, je peux dire que ma matrice représente l'équation de Poisson à pression discrète. C'est l'analogue discret de l'application de l'opérateur laplacien couplant les changements de pression locaux à la divergence cellulaire.
Pour autant que je puisse comprendre, étant donné qu'il s'agit de différences de pression, des conditions aux limites sont nécessaires pour «ancrer» les pressions à une valeur de référence absolue. Sinon, il peut y avoir un nombre infini de solutions à l'ensemble des équations.
Dans ces notes , 3 façons différentes sont données pour appliquer des conditions aux limites, au mieux de ma compréhension:
Dirichlet - spécifie des valeurs absolues aux limites.
Neummann - spécifie la dérivée aux limites.
Robin - spécifie une sorte de combinaison linéaire de la valeur absolue et de la dérivée aux limites.
L'article de Foster et Fedki ne mentionne aucun de ces éléments, mais je pense qu'ils appliquent les conditions aux limites de Dirichlet, notamment en raison de cette déclaration à la fin du 7.1.2, "La pression dans une cellule de surface est réglée sur la pression atmosphérique."
J'ai lu les notes que j'ai liées plusieurs fois et je ne comprends toujours pas très bien le calcul. Comment appliquons-nous exactement ces conditions aux limites? En regardant d'autres implémentations, il semble y avoir une sorte de notion de cellules "fantômes" qui se trouvent à la frontière.
Ici, j'ai lié à quelques sources qui peuvent être utiles à ceux qui lisent ceci.
Remarques sur les conditions aux limites pour les matrices de Poisson
Poste de StackExchange en science computationnelle sur les conditions aux limites de Neumann
Publication de StackExchange en science informatique sur le solveur de Poisson
Mise en œuvre du Physbam de l'eau
Voici le code que j'utilise pour générer la matrice. Notez qu'au lieu de supprimer explicitement les colonnes et les lignes, je génère et utilise une carte des indices de cellules liquides aux colonnes / lignes de la matrice finale.
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
}
}
}