第 11 章 lduMatrix 与线性求解器:PCG / PBiCGStab / GAMG
第 9 章把 PDE 离散成了
fvMatrix,而fvMatrix直接继承自lduMatrix。本章深入这个"下-对角-上"(Lower-Diagonal-Upper)矩阵格式,以及建立在它之上的 Krylov 迭代求解器(PCG、PBiCGStab)与多重网格求解器(GAMG)。这是 OpenFOAM 数值性能的最后一公里:把稀疏线性方程组真正解出来。
11.1 lduMatrix:三数组的稀疏矩阵
11.1.1 存储结构
LduMatrix.H(src/OpenFOAM/matrices/lduMatrix/LduMatrix/,第 30--39 行):
cpp
Description
lduMatrix is a general matrix class in which the coefficients are
stored as three arrays, one for the upper triangle, one for the
lower triangle and a third for the diagonal.
Addressing arrays must be supplied for the upper and lower triangles.
It might be better if this class were organised as a hierarchy starting
from an empty matrix, then deriving diagonal, symmetric and asymmetric
matrices.
它只有三组系数数组:
| 数组 | 长度 | 含义 |
|---|---|---|
diag_ |
nCells | 对角 |
upper_ |
nInternalFaces | 上三角(owner→neighbour) |
lower_ |
nInternalFaces | 下三角(neighbour→owner) |
外加接口系数 (用于并行/耦合边界):interfaceBouCoeffs_、interfaceIntCoeffs_、interfaces(lduInterfaceFieldPtrsList)。
注释还透露了一个历史遗憾:本可以把 lduMatrix 组织成"空矩阵 → 对角 → 对称 → 非对称"的层次(即用继承区分对称/非对称),但现实中用运行期判断处理。这解释了后面 solver 为什么有 symMatrix/asymMatrix 两张表。
11.1.2 lduAddressing:寻址与 losort
lduAddressing.H(第 30--66 行)说明寻址的组织方式。除了 upperAddr()/lowerAddr()(面的 owner/neighbour),还有一个优化------owner-start 寻址:
cpp
owner neighbour
0 1
0 20
1 2
1 21
...
不重复存 owner,而是存"每个点的邻居起始位置":
cpp
Owner start list: 0 2 4 6 8 10 12 14 16 18
于是"已知 owner 和 neighbour 找面索引"变得高效:从 ownerStartowner 开始,线性扫描到 ownerStartowner+1,匹配到 neighbour 即得面号。
另一个关键概念是 losort :由于矩阵在 upper/lower 里分别按 owner/neighbour 排序,遍历时需要两套顺序(losortAddr()),以便在两个方向上都能顺序访问。这套寻址设计是针对有限体积的"面-体"结构量身定制的。
11.1.3 Amul:矩阵乘向量
求解器迭代的核心是"计算 A·ψ"。LduMatrixATmul.C 第 122--137 行给出最直观的版本:
cpp
for (label cell=0; cell<nCells; cell++)
{
ApsiPtr[cell] = diagPtr[cell]*psiPtr[cell];
}
const label nFaces = upper().size();
for (label face=0; face<nFaces; face++)
{
ApsiPtr[uPtr[face]] += lowerPtr[face]*psiPtr[lPtr[face]];
ApsiPtr[lPtr[face]] += upperPtr[face]*psiPtr[uPtr[face]];
}
即:
scss
(Aψ)_i = diag[i]·ψ_i + Σ_{面 f: owner(f)=i} lower[f]·ψ_{neighbour(f)}
+ Σ_{面 f: neighbour(f)=i} upper[f]·ψ_{owner(f)}
还有一个cell-based 变体 (第 76--121 行,hasLowerCSR() 分支):把 lower 转成 CSR(按单元分组)以改善缓存局部性。两种实现都出现在 Amul 里,按矩阵特征选择。所有指针都标了 __restrict__,提示编译器可积极向量化------这是 OpenFOAM 内层循环的标准手法。
并行/耦合边界的贡献由 initMatrixInterfaces / updateMatrixInterfaces 在两个方向上处理(第 61--72、139--149 行)。
11.2 solverPerformance:迭代的"仪表盘"
每个求解器都返回 solverPerformance,它记录 initialResidual()、finalResidual()、nIterations(),并提供 checkConvergence(tolerance, relTol, log)、checkSingularity()、great_(一个极大的初值)。
收敛判据是"相对 收敛":当 finalResidual < tolerance 或 finalResidual/initialResidual < relTol 时停止。这正是 fvSolution 里 tolerance 与 relTol 的语义------relTol 0.05 表示"残差降到初始的 5% 即可"(用于外迭代内层,省时间)。
11.3 PCG:对称矩阵的共轭梯度
11.3.1 算法
PCG(Preconditioned Conjugate Gradient)用于对称正定 矩阵(如拉普拉斯离散出的矩阵)。PCG.C(solvers/PCG/)的 scalarSolve(第 69--217 行)逐行对应标准 CG 公式:
| 数学 | 代码(PCG.C) |
|---|---|
| r = b − Aψ | rA = source - wA(wA = A·psi,第 97--100 行) |
| 归一化残差 | gSumMag(rA)/normFactor(第 119--121 行) |
| w = M⁻¹r(预条件) | preconPtr_->precondition(wA, rA, cmpt)(第 148 行) |
| ρ = r·w | wArA = gSumProd(wA, rA, ...)(第 151 行) |
| p = w(首步)/ p = w + βp | beta = wArA/wArAold; p = w + beta*p(第 162--167 行) |
| Ap | matrix_.Amul(wA, pA, ...)(第 172 行) |
| α = ρ / (p·Ap) | wApA = gSumProd(wA, pA); alpha = wArA/wApA(第 174、182 行) |
| ψ += αp;r −= αAp | psi += alpha*p; rA -= alpha*w(第 186--187 行) |
代码与公式一一对应,这是理解 OpenFOAM 求解器最省力的部分。注意:
gSumProd/gSumMag是并行归约(第 12 章):它们把所有进程的局部内积/范数求和,得到全局值。这保证并行下收敛判据一致。- 求解器用
solveScalar类型而非scalar------在SPDP混合精度构建下,存储用单精度、求解用双精度,兼顾内存与精度。
11.3.2 预条件子
PCG 通过 lduMatrix::preconditioner::New(*this, controlDict_) 创建预条件子(第 132--139 行),由 RTS 按字典里的名字选择。对称矩阵的常用预条件子是 DIC (对角不完全 Cholesky)。位置:src/OpenFOAM/matrices/lduMatrix/Preconditioners/:
| 预条件子 | 适用 | 特点 |
|---|---|---|
DIC |
对称 | 对角不完全 Cholesky |
DILU |
非对称 | 对角不完全 LU |
FDIC / FDILU |
--- | "final" 变体(用于分离式/特定场景) |
diagonal |
通用 | 仅用对角 |
GAMG |
通用 | 用 GAMG 作预条件子 |
none |
通用 | 无预条件 |
用法(fvSolution):
cpp
solvers
{
p
{
solver PCG;
preconditioner DIC;
tolerance 1e-06;
relTol 0.05;
}
}
11.4 PBiCGStab:非对称矩阵的稳定双共轭梯度
11.4.1 为什么需要它
对流项(div)离散出的矩阵是非对称 的,PCG 不适用。PBiCGStab(Preconditioned Bi-Conjugate Gradient Stabilized)用"稳定化"策略处理非对称系统。PBiCGStab.C 的 scalarSolve(第 73--260 行)核心公式与代码对应:
| 数学 | 代码 |
|---|---|
| r = b − Aψ;r₀ = r | rA = source - yA;rA0(rA)(第 100、144 行) |
| ρ = r₀·r | rA0rA = gSumProd(rA0, rA)(第 167 行) |
| β = (ρ/ρ_old)(α/ω) | beta = (rA0rA/rA0rAold)*(alpha/omega)(第 191 行) |
| p = r + β(p − ω·Ap) | pA = rA + beta*(pA - omega*AyA)(第 195--196 行) |
| ŷ = M⁻¹p;Aŷ | precondition(yA, pA);Amul(AyA, yA)(第 201、204 行) |
| α = ρ / (r₀·Aŷ) | alpha = rA0rA/rA0AyA(第 209 行) |
| s = r − α·Aŷ | sA = rA - alpha*AyA(第 214 行) |
| ż = M⁻¹s;t = Aż | precondition(zA, sA);Amul(tA, zA)(第 238、241 行) |
| ω = (t·s)/(t·t) | (下一段) |
并在中途用 s 的范数判断是否已收敛(第 218--235 行:若收敛,先更新 ψ 再返回)。这套"每次迭代做两步(BiCG + 稳定化 ω)"的结构,就是 "Stabilized" 的含义。
OpenFOAM 还有一批变体求解器:PBiCG、PBiCICG、PBiCCCG(solvers/ 目录),分别对应不同的双共轭/共轭形式,供特定场景选用。
11.5 GAMG:几何聚合代数多重网格
11.5.1 定位与特征
GAMGSolver.H(第 33--49 行)给出了它的全部特征:
cpp
Description
Geometric agglomerated algebraic multigrid solver.
Characteristics:
- Requires positive definite, diagonally dominant matrix.
- Agglomeration algorithm: selectable and optionally cached.
- Restriction operator: summation.
- Prolongation operator: injection.
- Smoother: Gauss-Seidel.
- Coarse matrix creation: central coefficient: summation of fine grid
central coefficients with the removal of intra-cluster face;
off-diagonal coefficient: summation of off-diagonal faces.
- Coarse matrix scaling: performed by correction scaling, using steepest
descent optimisation.
- Type of cycle: V-cycle with optional pre-smoothing.
- Coarsest-level matrix solved using any lduSolver (PCG, PBiCGStab,
smoothSolver) or direct solver on master processor
逐条解读:
- 几何聚合(agglomerated) :不像纯代数多重网格那样从矩阵结构推导粗化,而是基于网格几何把相邻单元聚成簇(cluster) ,逐层粗化。聚合算法可配置(
GAMGAgglomeration派生:pairGAMGAgglomeration、algebraicPairGAMGAgglomeration等)。 - 限制算子 = 求和(summation):细层残差在簇内求和成粗层。
- 延拓算子 = 注入(injection):粗层解直接注入到簇内。
- 平滑器 = Gauss-Seidel:粗/细层都用 Gauss-Seidel 平滑。
- 粗矩阵构造:簇内面的系数被"消去"(对角吸收簇内耦合),簇间面系数求和为粗矩阵的 off-diagonal。
- V-cycle:可选前置平滑,标准 V 形循环。
- 最粗层 :用任意
lduSolver(PCG/PBiCGStab/smoothSolver)或直接求解器 (主进程上用LUscalarMatrix做 LU 分解直接解)求解。
GAMGSolver 的私有数据(第 84--102 行)暴露了大量旋钮:nPreSweeps_、preSweepsLevelMultiplier_、maxPreSweeps_、nPostSweeps_、postSweepsLevelMultiplier_、maxPostSweeps_、nFinestSweeps_......这些正对应 fvSolution 里 GAMG 的高级参数(nPreSweeps、nPostSweeps、nFinestSweeps、cacheAgglomeration、agglomerator、nCellsInCoarsestLevel、mergeLevels 等)。
GAMG 对压力方程 (大、椭圆、病态)通常是最快的,所以几乎每个求解器的 p 方程都会用 GAMG 或 GAMG + DIC。
11.6 平滑求解器与对角求解器
| 求解器 | 用途 |
|---|---|
SmoothSolver |
各种平滑迭代(Gauss-Seidel、symGaussSeidel、DIC、DILU...),常作预条件/平滑 |
DiagonalSolver |
只解对角(对角占优/显式场景) |
PPCG/PPCR |
并行/投影类变体 |
PCICG/PBiCICG |
带不完全 Cholesky 的 CG 变体 |
FPCG |
"flexible" PCG |
11.7 求解器如何被选中:RTS 与 fvSolution
回到第 4 章:lduMatrix::solver 有两张 RTS 表(symMatrix / asymMatrix)。当 fvSolution 里写:
cpp
solvers
{
p { solver PCG; preconditioner DIC; tolerance 1e-06; relTol 0.01; }
U { solver PBiCGStab; preconditioner DILU; tolerance 1e-06; relTol 0.1; }
k { solver PBiCGStab; preconditioner DILU; tolerance 1e-08; relTol 0.1; }
"(U|k|epsilon)" { solver smoothSolver; smoother symGaussSeidel; ... }
}
- 矩阵对称(拉普拉斯类)→ 查
symMatrix表; - 矩阵非对称(对流类)→ 查
asymMatrix表; - 具体类由
solver名字经 RTS 构造。
solution 类还支持正则表达式的场名匹配 ("(U|k|epsilon)"),这靠 HashPtrTable + 模式匹配实现(第 9 章)。
11.8 归约与精度:两个易被忽视的细节
- 归约(reduction) :
gSumProd、gSumMag等把局部内积/范数汇总为全局值。没有它们,并行下各进程会各自判断收敛,导致不一致。这是"求解器正确性依赖并行框架"的直接体现(第 12 章)。 solveScalar:求解器内部用solveScalar,它在SPDP构建下可以与scalar不同(存储单精度、求解双精度),用于在超大网格上平衡内存与精度。
11.9 小结:一条完整的求解链
把第 9--11 章连起来,一个方程从离散到求解的全流程是:
bash
PDE
└─(fvSchemes 选格式)─▶ fvMatrix (= lduMatrix:diag/upper/lower + 边界系数)
└─(fvSolution 选 solver)─▶ lduMatrix::solver(RTS)
├─ Amul ← 网格拓扑决定的稀疏乘
├─ preconditioner(RTS:DIC/DILU/GAMG...)
├─ reduction(gSumProd/gSumMag)→ 并行一致
└─ PCG / PBiCGStab / GAMG ...
└─▶ solverPerformance(残差/迭代数)
"有限体积离散"与"线性代数求解"在 lduMatrix 处无缝衔接,这正是 OpenFOAM 架构之美:同一套矩阵格式既服务离散器,又服务求解器。
本章要点回顾
lduMatrix用三个数组 (diag/upper/lower)+ 接口系数表达任意网格上的离散矩阵;lduAddressing提供 owner/neighbour、owner-start 寻址与 losort。Amul实现 A·ψ(有 cell-based/CSR 与 face-based 两种),是迭代的基石。solverPerformance记录残差与迭代数;收敛判据是相对残差(tolerance/relTol)。- PCG (对称,配 DIC)与 PBiCGStab(非对称,配 DILU)的迭代公式与代码一一对应;变体有 PBiCG/PBiCICG/PBiCCCG/PPCG/PPCR/FPCG。
- GAMG:几何聚合多重网格,限制=求和、延拓=注入、平滑=Gauss-Seidel、V-cycle、最粗层用直接解;是压力方程的首选。
- 求解器与预条件子都由 RTS 从
fvSolution选择;并行一致性依赖gSumProd/gSumMag归约;求解用solveScalar支持混合精度。