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
}
}
}
la source
Réponses:
À partir de votre extrait de code et de votre résultat pour un exemple 2x2, je peux voir que vous simulez en fait un domaine avec uniquement les conditions aux limites de Neumann (paroi de glissement). Dans ce cas, le système contient un espace nul et votre matrice est singulière.
S'il s'agit de la configuration de simulation que vous souhaitez (c'est-à-dire pas de Dirichlet (pression) BC), vous devrez projeter l'espace nul de votre solution. C'est simple si vous utilisez le gradient conjugué (CG) comme suggéré dans cet article. Dans chaque itération de votre itération CG, prenez simplement le vecteur de solution actuelx , et fait
x′=(I−u^u^T)x=x−(u^⋅x)u^
où u^ est l'espace nul normalisé de l'opérateur de gradient:
u =(1,1,…,1) , u^=u∥ u ∥ .
Sinon, si vous voulez simuler de l'air (frontière libre ou Dirichlet BC), vous devrez distinguer un mur et une cellule d'air (c'est-à-dire avoir un booléen
hasLiquid
ne suffit pas) et leur appliquer une discrétisation correcte (voir ci-dessous).Enfin, vos entrées en diagonale sont négatives. Vous souhaiterez peut-être retourner les signes afin que la méthode CG fonctionne.
Ci-dessous, je voudrais montrer plus de détails. Considérez le processus de projection de pression. Désigner la vitesse avant la projection de pression commev∗ . Cela peut être divergent, donc nous calculons la pression pour le corriger et obtenir la vitesse libre de divergencevn+1 . C'est,
vn+1=v∗−Δtρ∇P
Prenez-en la divergence, et depuis vn+1 est sans divergence,
∇⋅∇P=∇⋅v∗
Supposons qu'il n'y ait pas de pression que Dirichlet BC présente et que nous ayons une solution P0 , puis P0+c pour toute constante c est aussi une solution car ∇⋅∇(P0+c)=∇⋅∇P0=∇⋅v∗ .
c est l'espace nul que nous voulons projeter.
Pour gérer le Dirichlet BC, considérons le cas 1D comme exemple. Supposons que vous utilisez une grille décalée, où les pressionspi sont situés aux centres de grille et aux vitesses vi + une / deux sont situés sur les faces entre les nœuds je et i + 1 . Ensuite, la discrétisation générale pour une cellule est
pi + 1-pje- (pje-pi - 1)ΔX2= rhs
Supposer que pi + 1 est une cellule à air, c'est-à-dire que sa pression a été spécifiée, pi + 1 terme est déplacé vers la droite et disparaît de la matrice. Notez que le nombre de termes diagonauxpje est toujours deux. C'est pourquoi j'ai dit que votre exemple 2x2 ne contenait pas de Dirichlet BC.
Avec Dirichlet ou Neumann BC, la matrice est toujours définie positive symétrique. Voilà pourquoi les auteurs ont dit
la source