Некоторое время назад я искал описание операций, процессов, происходящих в библиотеке численного моделирования OpenFOAM. Нашел много абстрактных описаний работы метода конечных объёмов, классических разностных схем, различных физических уравнений. Мне же хотелось узнать более детально — откуда в таком-то выходном файле на такой-то итерации получились эти значения, какие выражения стоят за теми или иными параметрами в файлах настроек fvSchemes, fvSolution?
Для тех, кому это тоже интересно — эта статья. Те, кто хорошо знаком с OpenFOAM или с методами в нём реализованными — пишите о найденных ошибках и неточностях в личку.
На хабре уже была пара статей про OpenFOAM:
OpenFOAM на практике
OpenFOAM с точки зрения программиста-физика
Поэтому не буду останавливаться на том, что это «открытая (GPL) платформа для численнного моделирования, предназначенная для моделирования, связанного с решением уравнений в частных производных методом конечных объёмов, и широко используемая для решения задач механики сплошных сред».
Сегодня я на простом примере опишу операции, которые происходят в ходе расчёта в OpenFOAM.
Итак, дана геометрия — куб со стороной 1 метр:
Перед нами стоит задача смоделировать поток-распространение некоторого скалярного поля (температура, количество вещества), который задаётся следующим уравнением переноса (1) внутри объема тела.
(1)
,
где скалярная величина, например, выражает температуру [K] или концентрацию некоторого вещества, а выражает перенос вещества, массовый поток [кг/с].
Это уравнение, например, используют для моделирования распространения тепла
,
где k — теплопроводность, а — температура [K].
Граничные условия задачи следующие: есть грань-вход, грань-выход, остальные грани — гладкие стенки.
Наша сетка будет очень простая — делим куб на 5 равных ячеек вдоль оси Z.
Метод конечных объёмов предусматривает, что (1) в интегральной форме (2) будет выполняться для каждого конечного объёма .
(2)
,
где — геометрический центр конечного объёма.
Упростим, преобразуем первое слагаемое выражения (2) следующим образом:
(2.1) (HJ-3.12)*
Как видно — мы приняли, что скалярная величина изменяется внутри конечного объема линейно и значение величины в некоторой точке внутри конечного объёма можно вычислить как:
(2.2)
,
где
Для упрощения второго слагаемого выражения (2) используем обобщённую теорему Гаусса-Остроградского: интеграл от дивергенции векторного поля по объёму , равен потоку вектора через поверхность , ограничивающую данный объём. На человеческом языке «сумма всех потоков в/из конечного объема равна сумме потоков через грани этого конечного объема»:
(2.3)
,
где замкнутая поверхность, ограничивающая объём ,
— вектор, направленный по нормали от объёма .
Учитывая то, что конечный объём ограничен набором плоских граней, можно выражение (2.3) преобразовать к сумме интегралов по поверхности:
(2.4) (HJ-3.13)
,
где выражает значение переменной в центре грани,
— вектор площади, выходит из центра грани, направлен в сторону от ячейки (локально), в сторону от ячейки с меньшим индексом к ячейке с большим индексом (глобально).
Чтобы не хранить одни и те же параметры вектора два раза, т.к. очевидно, что у двух соседних ячеек вектор-нормали к грани между ячейками, направленный в сторону От центра ячейки будет различаться только направлением-знаком. Поэтому было создано owner-neighbour отношение между гранью и ячейкой. Если вектор площади (глобальный, положительное направление от ячейки с меньшим индексом к ячейке с большим индексом) указывает ОТ центра ячейки такое отношение между ячейкой и вектором , а точнее между ячейкой и гранью, обозначается owner). В случае если этот вектор указывает внутрь рассматриваемой ячейки, то отношение neighbour. Направление влияет на знак величины (+ для owner и — для neighbour) и это важно при суммировании см. далее.
(HJ-3.16)
Значение в центре грани вычисляется через значения в центрах прилегающих ячеек — способ такого выражения носит название разностной схемы. В OpenFOAM тип разностной схемы задается в файле /system/fvSchemes:
Gauss — означает, что выбрана центральная разностная схема;
linear — означает, что интерполяция с центров ячеек на центры граней будет происходить линейно.
Допустим, что наша скалярная величина изменяется внутри конечного объема от центра к граням линейно. Тогда аппроксимированное в центре грани значение будет вычисляться согласно формуле:
Где веса и рассчитываются как
Где — объемы ячеек.
Для случаев скошенных ячеек существуют более сложные формулы расчета весов аппроксимации.
Таким образом, значения phi_f в центрах граней ячеек вычисляются на основе значений в центрах ячеек. Значения градиентов grad(phi) вычисляются на основе значений phi_f.
И весь этот алгоритм может быть представлен в виде следующего псевдокода.
Учитывая (2.1) и (2.4) выражение (2) принимает вид:
(3)
Согласно методу конечных объёмов проводится дискретизация по времени и выражение (3) записывается как:
(4)
Проинтегрируем (4):
(4.1)
Разделим левую и правую часть на :
(5)
Теперь мы можем получить систему линейных уравнений для каждого конечного объёма .
Ниже представлена нумерация узлов сетки, которую мы будем использовать.
Нумерация узлов-центров ячеек (50, 51 — центры граничных граней):
Нумерация узлов-центров граней:
Объемы элементов:
Коэффициенты интерполяции, необходимые для вычисления значений на гранях ячеек. Индекс «e» обозначает «правая грань ячейки». Правая относительно вида, как на рисунке «Нумерация узлов-центров ячеек»:
Для P = 0.
Выражение (5), описывающее поведение величины
будет преобразовано в систему линейных алгебраических уравнений, каждое из которых вида:
или, согласно индексам точек на гранях
А еще все потоки в/из ячейки могут быть выражены в виде суммы
где, например, — коэффициент линеаризации потока в точке-центре ячейки E,
— коэффициент линеаризации потока в точке-центре грани,
— нелинейная часть (например, константа).
Согласно нумерации граней выражение примет вид:
С учетом граничных условий для элемента P_0 линейное алгебраическое уравнение может быть представлено в виде
… подставим ранее полученные коэффициенты…
Поток из inlet'a направлен в ячейку, поэтому имеет отрицательный знак.
Так как у нас в управляющем выражении присутствует кроме диффузионного еще и временной член, но конечное уравнение выглядит как
Для P = 1.
Для P = 4.
Система линейных алгебраических уравнений (СЛАУ) может быть представлена в матричном виде как
,
где
Далее полученная СЛАУ решается решателем, указанным в fvSchemes.
И в итоге получается вектор значений
на основе которого получаются значения для вектора
Затем вектор подставляется в СЛАУ и происходит новая итерация расчёта вектора .
И так до тех пор, пока невязка не достигнет требуемых пределов.
* Некоторые уравнения в этой статье взяты из диссертации Ясака Хрвое (HJ — номер уравнения) и если кому-то захочется прочитать про них подробнее (http://powerlab.fsb.hr/ped/kturbo/OpenFOAM/docs/HrvojeJasakPhD.pdf)
Скачать файлы задачи можно здесь:
github.com/j-avdeev/case
Файлы решателя:
github.com/j-avdeev/matrHyper1Foam
В качестве бонуса — видео, как распространяется концентрация .
Для тех, кому это тоже интересно — эта статья. Те, кто хорошо знаком с OpenFOAM или с методами в нём реализованными — пишите о найденных ошибках и неточностях в личку.
На хабре уже была пара статей про OpenFOAM:
OpenFOAM на практике
OpenFOAM с точки зрения программиста-физика
Поэтому не буду останавливаться на том, что это «открытая (GPL) платформа для численнного моделирования, предназначенная для моделирования, связанного с решением уравнений в частных производных методом конечных объёмов, и широко используемая для решения задач механики сплошных сред».
Сегодня я на простом примере опишу операции, которые происходят в ходе расчёта в OpenFOAM.
Итак, дана геометрия — куб со стороной 1 метр:
Перед нами стоит задача смоделировать поток-распространение некоторого скалярного поля (температура, количество вещества), который задаётся следующим уравнением переноса (1) внутри объема тела.
(1)
,
где скалярная величина, например, выражает температуру [K] или концентрацию некоторого вещества, а выражает перенос вещества, массовый поток [кг/с].
Это уравнение, например, используют для моделирования распространения тепла
,
где k — теплопроводность, а — температура [K].
Оператор дивергенции на самом деле это
оператор .
Напомню, что существует оператор набла (оператор Гамильтона), который записывается следующим образом:
,
где i, j, k — единичные векторы.
Если скалярно умножить оператор набла на векторную величину, то мы получим дивергенцию данного вектора:
«С точки зрения физики, дивергенция векторного поля является показателем того, в какой степени данная точка пространства является источником или стоком этого поля»
Если умножить оператор набла на скаляр, получается градиент этого скаляра:
Градиент показывает увеличение или уменьшение по какому-либо направлению величины скаляра .
Напомню, что существует оператор набла (оператор Гамильтона), который записывается следующим образом:
,
где i, j, k — единичные векторы.
Если скалярно умножить оператор набла на векторную величину, то мы получим дивергенцию данного вектора:
«С точки зрения физики, дивергенция векторного поля является показателем того, в какой степени данная точка пространства является источником или стоком этого поля»
Если умножить оператор набла на скаляр, получается градиент этого скаляра:
Градиент показывает увеличение или уменьшение по какому-либо направлению величины скаляра .
Граничные условия задачи следующие: есть грань-вход, грань-выход, остальные грани — гладкие стенки.
Разбиение объема куба на конечные объемы
Наша сетка будет очень простая — делим куб на 5 равных ячеек вдоль оси Z.
Много формул
Метод конечных объёмов предусматривает, что (1) в интегральной форме (2) будет выполняться для каждого конечного объёма .
(2)
,
где — геометрический центр конечного объёма.
Центр конечного объема
Упростим, преобразуем первое слагаемое выражения (2) следующим образом:
(2.1) (HJ-3.12)*
Как видно — мы приняли, что скалярная величина изменяется внутри конечного объема линейно и значение величины в некоторой точке внутри конечного объёма можно вычислить как:
(2.2)
,
где
Для упрощения второго слагаемого выражения (2) используем обобщённую теорему Гаусса-Остроградского: интеграл от дивергенции векторного поля по объёму , равен потоку вектора через поверхность , ограничивающую данный объём. На человеческом языке «сумма всех потоков в/из конечного объема равна сумме потоков через грани этого конечного объема»:
(2.3)
,
где замкнутая поверхность, ограничивающая объём ,
— вектор, направленный по нормали от объёма .
Вектор S
Учитывая то, что конечный объём ограничен набором плоских граней, можно выражение (2.3) преобразовать к сумме интегралов по поверхности:
(2.4) (HJ-3.13)
,
где выражает значение переменной в центре грани,
— вектор площади, выходит из центра грани, направлен в сторону от ячейки (локально), в сторону от ячейки с меньшим индексом к ячейке с большим индексом (глобально).
Еще немного про вектор S
Чтобы не хранить одни и те же параметры вектора два раза, т.к. очевидно, что у двух соседних ячеек вектор-нормали к грани между ячейками, направленный в сторону От центра ячейки будет различаться только направлением-знаком. Поэтому было создано owner-neighbour отношение между гранью и ячейкой. Если вектор площади (глобальный, положительное направление от ячейки с меньшим индексом к ячейке с большим индексом) указывает ОТ центра ячейки такое отношение между ячейкой и вектором , а точнее между ячейкой и гранью, обозначается owner). В случае если этот вектор указывает внутрь рассматриваемой ячейки, то отношение neighbour. Направление влияет на знак величины (+ для owner и — для neighbour) и это важно при суммировании см. далее.
(HJ-3.16)
Про разностные схемы
Значение в центре грани вычисляется через значения в центрах прилегающих ячеек — способ такого выражения носит название разностной схемы. В OpenFOAM тип разностной схемы задается в файле /system/fvSchemes:
divSchemes
{
default none;
div(phi,psi) Gauss linear;
}
Gauss — означает, что выбрана центральная разностная схема;
linear — означает, что интерполяция с центров ячеек на центры граней будет происходить линейно.
Допустим, что наша скалярная величина изменяется внутри конечного объема от центра к граням линейно. Тогда аппроксимированное в центре грани значение будет вычисляться согласно формуле:
Где веса и рассчитываются как
Где — объемы ячеек.
Для случаев скошенных ячеек существуют более сложные формулы расчета весов аппроксимации.
Таким образом, значения phi_f в центрах граней ячеек вычисляются на основе значений в центрах ячеек. Значения градиентов grad(phi) вычисляются на основе значений phi_f.
И весь этот алгоритм может быть представлен в виде следующего псевдокода.
1. Объявляем массив градиентов конечных объемов, инициализируем его нулями
2. Пробегаемся по всем внутренним граням (которые не граничные)
> Вычисляем flux_f = phi_f*S_f. Значения phi_f вычисляем на основе значений phi в центах ячеек
> Добавляем flux_f к градиенту элемента-owner и -flux_f к градиенту элемента-neighbour
3. Пробегаемся по всем граничным граням
> Вычисляем flux_f = phi_f*S_f
> Добавляем flux_f к градиенту элементу-owner (neighbour-элементов у граничных граней нет)
4. Пробегаемся по всем элементам
> Делим получившуюся сумму-градиент на объем элемента
Дискретизация по времени
Учитывая (2.1) и (2.4) выражение (2) принимает вид:
(3)
Согласно методу конечных объёмов проводится дискретизация по времени и выражение (3) записывается как:
(4)
Проинтегрируем (4):
(4.1)
Разделим левую и правую часть на :
(5)
Данные для матрицы дискретизации
Теперь мы можем получить систему линейных уравнений для каждого конечного объёма .
Ниже представлена нумерация узлов сетки, которую мы будем использовать.
Координаты узлов хранятся в /constant/polyMesh/points
24
(
(0 0 0)
(1 0 0)
(0 1 0)
(1 1 0)
(0 0 0.2)
(1 0 0.2)
(0 1 0.2)
(1 1 0.2)
(0 0 0.4)
(1 0 0.4)
(0 1 0.4)
(1 1 0.4)
(0 0 0.6)
(1 0 0.6)
(0 1 0.6)
(1 1 0.6)
(0 0 0.8)
(1 0 0.8)
(0 1 0.8)
(1 1 0.8)
(0 0 1)
(1 0 1)
(0 1 1)
(1 1 1)
)
Нумерация узлов-центров ячеек (50, 51 — центры граничных граней):
Нумерация узлов-центров граней:
Объемы элементов:
Коэффициенты интерполяции, необходимые для вычисления значений на гранях ячеек. Индекс «e» обозначает «правая грань ячейки». Правая относительно вида, как на рисунке «Нумерация узлов-центров ячеек»:
Формирование матрицы дискретизации
Для P = 0.
Выражение (5), описывающее поведение величины
будет преобразовано в систему линейных алгебраических уравнений, каждое из которых вида:
или, согласно индексам точек на гранях
А еще все потоки в/из ячейки могут быть выражены в виде суммы
где, например, — коэффициент линеаризации потока в точке-центре ячейки E,
— коэффициент линеаризации потока в точке-центре грани,
— нелинейная часть (например, константа).
Согласно нумерации граней выражение примет вид:
С учетом граничных условий для элемента P_0 линейное алгебраическое уравнение может быть представлено в виде
… подставим ранее полученные коэффициенты…
Поток из inlet'a направлен в ячейку, поэтому имеет отрицательный знак.
Так как у нас в управляющем выражении присутствует кроме диффузионного еще и временной член, но конечное уравнение выглядит как
Для P = 1.
Для P = 4.
Система линейных алгебраических уравнений (СЛАУ) может быть представлена в матричном виде как
,
где
=== A(i,j) ===
40.5 0.5 0 0 0
-0.5 40 0.5 0 0
0 -0.5 40 0.5 0
0 0 -0.5 40 0.5
0 0 0 -0.5 40.5
=== b(i,j) ===
1
0
0
0
0
Далее полученная СЛАУ решается решателем, указанным в fvSchemes.
И в итоге получается вектор значений
psi = dimensions [0 0 0 0 0 0 0];
internalField nonuniform List<scalar> 5(0.0246875 0.000308546 3.85622e-06 4.81954e-08 5.95005e-10);
на основе которого получаются значения для вектора
Затем вектор подставляется в СЛАУ и происходит новая итерация расчёта вектора .
И так до тех пор, пока невязка не достигнет требуемых пределов.
Ссылки
* Некоторые уравнения в этой статье взяты из диссертации Ясака Хрвое (HJ — номер уравнения) и если кому-то захочется прочитать про них подробнее (http://powerlab.fsb.hr/ped/kturbo/OpenFOAM/docs/HrvojeJasakPhD.pdf)
Скачать файлы задачи можно здесь:
github.com/j-avdeev/case
Файлы решателя:
github.com/j-avdeev/matrHyper1Foam
В качестве бонуса — видео, как распространяется концентрация .