Сохранение массы в симуляции жидкости

8

Я пытаюсь реализовать 2D-версию статьи Фостера и Федькова «Практическая анимация жидкостей» здесь: http://physbam.stanford.edu/~fedkiw/papers/stanford2001-02.pdf

В основном все работает, за исключением раздела 8: «Сохранение массы». Там мы создали матрицу уравнений для расчета давления, необходимого для освобождения расходящейся жидкости.

Я полагаю, что мой код соответствует статье, однако я получаю неразрешимую матрицу во время сохранения массового шага.

Вот мои шаги для генерации матрицы A:

  1. Установите диагональные элементы равными отрицательному числу соседних жидких ячеек для ячейки i.Ai,i
  2. Установите записи и 1, если в обеих ячейках i и j есть жидкость.Ai,jAj,i

Обратите внимание, что в моей реализации ячейка , в жидкостной сетке соответствует строке gridWidth в матрице.iji+j

В документе упоминается: «Статический объект и пустые ячейки не нарушают эту структуру. В этом случае члены давления и скорости могут исчезнуть с обеих сторон», поэтому я удаляю столбцы и строки для ячеек, в которых нет жидкости.

Итак, мой вопрос: почему моя матрица в единственном числе? Я пропускаю какое-то граничное условие в каком-то другом месте в статье? Это факт, что моя реализация 2D?

Вот пример матрицы из моей реализации для сетки 2x2, где в ячейке 0,0 нет жидкости:

-1   0   1

 0  -1   1

 1   1  -2

редактировать

Мои исследования привели меня к мысли, что я не справляюсь с граничными условиями.

Прежде всего, на данный момент я могу сказать, что моя матрица представляет собой уравнение Пуассона с дискретным давлением. Это дискретный аналог применения оператора Лапласа, связывающего локальные изменения давления с расходимостью ячейки.

Насколько я понимаю, поскольку мы имеем дело с перепадами давления, необходимы граничные условия, чтобы "привязать" давления к абсолютному контрольному значению. В противном случае может быть бесконечное количество решений для системы уравнений.

В этих заметках даны 3 различных способа применения граничных условий, насколько я понимаю:

  1. Дирихле - указывает абсолютные значения на границах.

  2. Neummann - указывает производную на границах.

  3. Робин - указывает какую-то линейную комбинацию абсолютного значения и производной на границах.

В работе Фостера и Федки ничего не упоминается, но я считаю, что они обеспечивают соблюдение граничных условий Дирихле, что примечательно из-за этого утверждения в конце 7.1.2, «Давление в поверхностной ячейке установлено на атмосферное давление».

Я прочитал заметки, которые связывал несколько раз, и до сих пор не совсем понимаю, что происходит. Как именно мы соблюдаем эти граничные условия? Глядя на другие реализации, кажется, что существует какое-то понятие «призрачных» ячеек, которые лежат на границе.

Здесь я ссылаюсь на несколько источников, которые могут быть полезны для других, читающих это.

Заметки о граничных условиях для матриц Пуассона

Пост вычислительной науки StackExchange о граничных условиях Неймана

Пост вычислительной науки StackExchange на Poisson Solver

Реализация Water 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
        }
      }
    }
Джаред рассчитывает
источник
Непонятно, почему статические объекты и пустые ячейки должны позволять удалять строки и столбцы. Вы устанавливаете эти строки и столбцы в ноль или удаляете их вообще, чтобы получить меньшую матрицу?
трихоплакс
Если проблема не в том, о чем вы думаете, это поможет увидеть код, если вы с радостью поделитесь им. В идеале MCVE
трихоплакс
Эй, трихоплакс. Насколько мне известно, матрица с нулевой строкой или столбцом была бы единственной, поэтому я вместо этого удаляю их из матрицы, чтобы сделать меньшую матрицу (а также соответствующие им элементы в векторе b).
Джаред считает
Я отредактирую MCVE сегодня вечером, когда буду рядом с моим компьютером с источником.
Джаред считает
Я также подозревал, что, возможно, я делал неправильные предположения где-то еще в коде, однако это относится только к матричной структуре (и независимо от того, является она единственной или нет). Единственное, о чем я могу думать, это то, что квалифицируется как «поверхностная ячейка» против воздушной ячейки или жидкой ячейки. Если это жидкая ячейка рядом с воздушной ячейкой, есть ли что-то другое, что я должен делать с соответствующими столбцами / строками?
Джаред считает

Ответы:

2

Из вашего фрагмента кода и вашего результата для примера 2x2 я вижу, что вы фактически моделируете домен только с граничными условиями Неймана (скользящая стенка). В этом случае система содержит пустое пространство и ваша матрица является сингулярной.

Если это та конфигурация симуляции, которую вы хотите (т.е. без Dirichlet (давление) BC), вам нужно будет спроецировать пустое пространство из вашего решения. Это просто, если вы используете сопряженный градиент (CG), как предложено в этой статье. В каждой итерации вашей итерации CG просто возьмите текущий вектор решения и выполните где - нормализованное нулевое пространство оператора градиента: , .x

x=(Iu^u^T)x=x(u^x)u^
u^u=(1,1,,1)u^=uu

В противном случае, если вы хотите смоделировать воздух (свободная граница или Dirichlet BC), вам нужно будет различить стену и воздушную ячейку (т. Е. Иметь логическое значение hasLiquidнедостаточно) и применить для них правильную дискретизацию (см. Ниже).

В заключение, ваши диагональные записи отрицательны. Вы можете перевернуть знаки, чтобы метод CG работал.


Ниже я хотел бы показать более подробную информацию. Рассмотрим процесс проецирования давления. Обозначим скорость перед проекцией давления как . Это может быть расходящимся, поэтому мы вычисляем давление, чтобы исправить его и получить скорость без расхождения . То есть Возьмите расхождение об этом, и так как имеет расхождений, Предположим, что давление Dirichlet BC отсутствует, и мы имеем одно решение , тогда для любой постояннойvvn+1

vn+1=vΔtρP
vn+1
P=v
P0P0+ccтакже является решением, потому что . - это пустое пространство, которое мы хотим спроецировать.(P0+c)=P0=vc

Для обработки Dirichlet BC, давайте рассмотрим 1D случай в качестве примера. Предположим, вы используете смещенную сетку, где давления расположены в центрах сетки, а скорости расположены на гранях между узлами и . Тогда общая дискретизация для одной ячейки: Предположим, что - воздушная ячейка, то есть ее давление было задано, тогда член перемещается в правую часть и исчезает из матрицы. Обратите внимание, что число диагональных членов по-прежнему равно двум. Вот почему я сказал, что ваш пример 2x2 не содержит Dirichlet 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
TheBusyTypist
источник