交错网格上的几何多重网格方法
对于规则网格而言,未知量有多种不同的布置方式,以此为依据又可分为顶点中心网格(vertex-centered grid)、单元中心网格(cell-centered grid)和交错网格(staggered grid)。使用多重网格方法求解问题时,不同类型的网格需要采用不同的限制算子(restriction operator)和延拓算子(prolongation(interpolation) operator)。限制算子和延拓算子统称为传递算子(transfer operator)。
对于顶点中心网格,限制算子通常使用完全加权算子(full weighting operator),此外还有半加权算子(half weighting operator),injection operator,half injection operator等;对于单元中心网格,限制算子通常使用平均法,二维使用四点平均法,三维使用八点平均法。
由于非线性项的存在,在求解稳态不可压Navier-Stokes方程时采用了FAS(Full Approximation Scheme)。因此,需要将未知量从细网格限制到粗网格上。对于限制算子,使用邻近网格点的未知数的平均值来定义粗网格上的近似值。
从粗网格到细网格上残差的限制算子可以表示成
箱形松弛方法(box relaxation method): 对于任意网格单元,存在五个未知量:、、、和,箱形松弛方法将这五个变量视为一个集体,利用高斯-塞德尔迭代思想,使用其他未知量来更新这五个未知量。一次完整的迭代需要遍历所有网格单元,更新每个单元上的未知量。
线性插值算子
延拓算子可使用线性插值算子。线性插值推广到二维、三维,可以得到双线性插值和三线性插值,它们的公式分别为:
其中,为函数在线段、矩形或长方体顶点处的值,为区域内的坐标,和为
因此只要知道密网格和细网格的位置相对关系,就能确定延拓算子,三个分量的延拓算子可以定义成
可通过如下C++代码来检验。
#include <array>
#include <functional>
#include <iostream>
// 考虑GPU设备运行,使用函数指针而不是lamgbda函数
double L0(double x) { return 1.0 - x; }
double L1(double x) { return x; }
double (*L[2])(double) = {L0, L1};
int test_linears()
{
double f[2];
// f(x) = x+1
f[0] = 0; // (0)
f[1] = 1; // (1)
// std::array<std::function<double(double)>, 2> L = {{
// [](double x) { return (1.0-x); },
// [](double x) { return (x); }
// }};
double sum = 0.0;
double x = 0.1;
double y = 0.3;
for (int i = 0; i < 2; i++)
{
sum += f[i] * L[i](x);
std::cout << sum << std::endl;
}
return 0;
}
int test_bilinears()
{
double f[2][2];
// f(x,y) = x+y+2xy
f[0][0] = 0; // (0,0)
f[1][0] = 1; // (1,0)
f[0][1] = 1; // (0,1)
f[1][1] = 4; // (1,1)
// std::array<std::function<double(double)>, 2> L = {{
// [](double x) { return (1.0-x); },
// [](double x) { return (x); }
// }};
double sum = 0.0;
double x = 0.1;
double y = 0.3;
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++)
{
sum += f[i][j] * L[i](x) * L[j](y);
std::cout << sum << std::endl;
}
return 0;
}
// 三线性插值
double f_(double x, double y, double z)
{
return x + y + z + x * y + x * z + y * z + x * y * z + 1;
}
int main()
{
double f[2][2][2];
// f(x,y) = x+y+z+xy+xz+yz+xyz+1
f[0][0][0] = f_(0, 0, 0); //
f[1][0][0] = f_(1, 0, 0); //
f[0][1][0] = f_(0, 1, 0); //
f[1][1][0] = f_(1, 1, 0); //
f[0][0][1] = f_(0, 0, 1); //
f[1][0][1] = f_(1, 0, 1); //
f[0][1][1] = f_(0, 1, 1); //
f[1][1][1] = f_(1, 1, 1); //
// std::array<std::function<double(double)>, 2> L = {{
// [](double x) { return (1.0-x); },
// [](double x) { return (x); }
// }};
double sum = 0.0;
double x = 0.2;
double y = 0.3;
double z = 0.3;
for (int i = 0; i < 2; i++)
for (int j = 0; j < 2; j++)
for (int k = 0; k < 2; k++)
{
sum += f[i][j][k] * L[i](x) * L[j](y) * L[k](z);
}
std::cout << sum << std::endl;
std::cout << f_(x, y, z) << std::endl;
return 0;
}