Я пытаюсь реализовать 2D-версию статьи Фостера и Федькова «Практическая анимация жидкостей» здесь: http://physbam.stanford.edu/~fedkiw/papers/stanford2001-02.pdf
В основном все работает, за исключением раздела 8: «Сохранение массы». Там мы создали матрицу уравнений для расчета давления, необходимого для освобождения расходящейся жидкости.
Я полагаю, что мой код соответствует статье, однако я получаю неразрешимую матрицу во время сохранения массового шага.
Вот мои шаги для генерации матрицы A:
- Установите диагональные элементы равными отрицательному числу соседних жидких ячеек для ячейки i.
- Установите записи и 1, если в обеих ячейках i и j есть жидкость.
Обратите внимание, что в моей реализации ячейка , в жидкостной сетке соответствует строке gridWidth в матрице.
В документе упоминается: «Статический объект и пустые ячейки не нарушают эту структуру. В этом случае члены давления и скорости могут исчезнуть с обеих сторон», поэтому я удаляю столбцы и строки для ячеек, в которых нет жидкости.
Итак, мой вопрос: почему моя матрица в единственном числе? Я пропускаю какое-то граничное условие в каком-то другом месте в статье? Это факт, что моя реализация 2D?
Вот пример матрицы из моей реализации для сетки 2x2, где в ячейке 0,0 нет жидкости:
-1 0 1
0 -1 1
1 1 -2
редактировать
Мои исследования привели меня к мысли, что я не справляюсь с граничными условиями.
Прежде всего, на данный момент я могу сказать, что моя матрица представляет собой уравнение Пуассона с дискретным давлением. Это дискретный аналог применения оператора Лапласа, связывающего локальные изменения давления с расходимостью ячейки.
Насколько я понимаю, поскольку мы имеем дело с перепадами давления, необходимы граничные условия, чтобы "привязать" давления к абсолютному контрольному значению. В противном случае может быть бесконечное количество решений для системы уравнений.
В этих заметках даны 3 различных способа применения граничных условий, насколько я понимаю:
Дирихле - указывает абсолютные значения на границах.
Neummann - указывает производную на границах.
Робин - указывает какую-то линейную комбинацию абсолютного значения и производной на границах.
В работе Фостера и Федки ничего не упоминается, но я считаю, что они обеспечивают соблюдение граничных условий Дирихле, что примечательно из-за этого утверждения в конце 7.1.2, «Давление в поверхностной ячейке установлено на атмосферное давление».
Я прочитал заметки, которые связывал несколько раз, и до сих пор не совсем понимаю, что происходит. Как именно мы соблюдаем эти граничные условия? Глядя на другие реализации, кажется, что существует какое-то понятие «призрачных» ячеек, которые лежат на границе.
Здесь я ссылаюсь на несколько источников, которые могут быть полезны для других, читающих это.
Заметки о граничных условиях для матриц Пуассона
Пост вычислительной науки StackExchange о граничных условиях Неймана
Пост вычислительной науки StackExchange на Poisson Solver
Вот код, который я использую для генерации матрицы. Обратите внимание, что вместо явного удаления столбцов и строк я создаю и использую карту от индексов жидких ячеек до окончательных столбцов / строк матрицы.
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
}
}
}
источник
Ответы:
Из вашего фрагмента кода и вашего результата для примера 2x2 я вижу, что вы фактически моделируете домен только с граничными условиями Неймана (скользящая стенка). В этом случае система содержит пустое пространство и ваша матрица является сингулярной.
Если это та конфигурация симуляции, которую вы хотите (т.е. без Dirichlet (давление) BC), вам нужно будет спроецировать пустое пространство из вашего решения. Это просто, если вы используете сопряженный градиент (CG), как предложено в этой статье. В каждой итерации вашей итерации CG просто возьмите текущий вектор решения и выполните где - нормализованное нулевое пространство оператора градиента: , .Икс x′=(I−u^u^T)x=x−(u^⋅x)u^ u^ u=(1,1,…,1) u^=u∥u∥
В противном случае, если вы хотите смоделировать воздух (свободная граница или Dirichlet BC), вам нужно будет различить стену и воздушную ячейку (т. Е. Иметь логическое значение
hasLiquid
недостаточно) и применить для них правильную дискретизацию (см. Ниже).В заключение, ваши диагональные записи отрицательны. Вы можете перевернуть знаки, чтобы метод CG работал.
Ниже я хотел бы показать более подробную информацию. Рассмотрим процесс проецирования давления. Обозначим скорость перед проекцией давления как . Это может быть расходящимся, поэтому мы вычисляем давление, чтобы исправить его и получить скорость без расхождения . То есть Возьмите расхождение об этом, и так как имеет расхождений, Предположим, что давление Dirichlet BC отсутствует, и мы имеем одно решение , тогда для любой постояннойv∗ vn+1 vn+1=v∗−Δtρ∇P vn+1 ∇⋅∇P=∇⋅v∗ P0 P0+c c также является решением, потому что .
- это пустое пространство, которое мы хотим спроецировать.∇⋅∇(P0+c)=∇⋅∇P0=∇⋅v∗ c
Для обработки Dirichlet BC, давайте рассмотрим 1D случай в качестве примера. Предположим, вы используете смещенную сетку, где давления расположены в центрах сетки, а скорости расположены на гранях между узлами и . Тогда общая дискретизация для одной ячейки: Предположим, что - воздушная ячейка, то есть ее давление было задано, тогда член перемещается в правую часть и исчезает из матрицы. Обратите внимание, что число диагональных членов по-прежнему равно двум. Вот почему я сказал, что ваш пример 2x2 не содержит Dirichlet BC.pi vi+1/2 i i+1 pi+1−pi−(pi−pi−1)Δx2=rhs pi+1 pi+1 pi
В случае Дирихле или Неймана BC матрица всегда симметрично положительно определена. Вот почему авторы сказали
источник