《OpenFOAM 源码深度剖析》系列-第 10 章 离散算子与插值格式:`grad` / `div` / `laplacian` / `ddt` 的实现

第 10 章 离散算子与插值格式:grad / div / laplacian / ddt 的实现

第 9 章讲了离散框架(fvMatrix/fvSchemes/fvSolution)。本章深入其内部,看清楚 OpenFOAM 到底怎么把梯度、散度、拉普拉斯、时间导数 变成矩阵,以及 linear、upwind、limitedLinear 这些格式背后在做什么数学。这一章是"数值方法"与"代码"真正交汇的地方。

10.1 有限体积法的四个基本离散

一切有限体积离散都建立在散度定理之上。对控制体 V(单元)与边界面 S:

ini 复制代码
∫_V ∇·f dV  =  ∮_S f·n dS   ⇒   离散: ∇·f ≈ (1/V) Σ_f (f_f · Sf)

其中 Sf 是面面积矢量(第 7 章),f_f 是面心处的值。由此得到四个算子的模板:

算子 连续形式 有限体积离散
梯度 grad ∇φ (1/V) Σ_f Sf φ_f
散度 div ∇·(φU) (1/V) Σ_f (Sf·U_f) φ_f
拉普拉斯 laplacian ∇·(Γ∇φ) (1/V) Σ_f Γ_f (Sf·(∇φ)_f)
时间导数 ddt ∂φ/∂t (φ - φ⁰)/Δt(Euler)

两个关键的中间量:

  • 面值 φ_f :由插值格式(interpolation scheme)从单元值得到。
  • 面法向梯度 (∇φ)_f :由 snGrad 格式得到。
  • 面上的 Sf·U_f :即通量 phi (这是在求解器里单独算出来的一个 surfaceScalarField,如 phi = fvc::flux(U) 或由 createPhi.H 创建)。

OpenFOAM 的全部离散格式,本质上就是对这些"面值/面梯度/通量"的不同近似方式。

10.2 梯度格式(gradSchemes)

10.2.1 gaussGrad:默认的 Gauss 梯度

gaussGrad.H(gradSchemes/gaussGrad/,第 33--35 行):

cpp 复制代码
Description
    Basic second-order gradient scheme using face-interpolation
    and Gauss' theorem.

它内部持有一个插值格式 (第 70--71 行 tmp<surfaceInterpolationScheme<Type>> tinterpScheme_),构造时默认用 linear(第 92--96 行):

cpp 复制代码
gaussGrad(const fvMesh& mesh)
:
    gradScheme<Type>(mesh),
    tinterpScheme_(new linear<Type>(mesh))
{}

于是 grad(φ) = (1/V) Σ_f Sf·(interpolate(φ))_f。若 fvSchemes 里写 grad(U) cellLimited Gauss linear 1;,则会构造 cellLimitedGrad<Type, ...> 包一个 gaussGrad。

10.2.2 其他梯度格式

格式 文件 特点
Gauss gaussGrad 二阶,Gauss 定理 + 面插值
leastSquares LeastSquaresGrad 基于最小二乘求梯度向量(非正交网格精度好)
fourth fourthGrad 四阶
cellLimited cellLimitedGrad 对梯度做单元限幅
faceLimited faceLimitedGrad 对面限幅
cellMDLimited cellMDLimitedGrad 多维限幅(分方向)
faceMDLimited faceMDLimitedGrad 面多维限幅
iterativeGauss iterativeGaussGrad 迭代 Gauss

**限幅(limiter)**是保证有界性/单调性的关键。cellLimitedGrad.H(第 31--38 行):

cpp 复制代码
Description
    cellLimitedGrad gradient scheme applied to a runTime selected base gradient
    scheme.

    The scalar limiter based on limiting the extrapolated face values
    between the maximum and minimum cell and cell neighbour values and is
    applied to all components of the gradient.

它内部持有一个基础梯度格式 (basicGradScheme_,第 70 行)与限幅系数 k_(第 73 行),先算基础梯度,再用"外推面值是否超出邻居单元极值"的判据限幅。cellLimitedGrad 支持多种 limiter 函数(gradientLimiters/ 下有 VenkatakrishnanGradientLimiter、minmodGradientLimiter、cubicGradientLimiter)。用法:

cpp 复制代码
gradSchemes
{
    default         Gauss linear;
    grad(U)         cellLimited Gauss linear 1;
}

10.3 散度/对流格式(divSchemes)

10.3.1 gaussDivScheme + 对流格式

gaussDivScheme.H(第 31--32 行):

cpp 复制代码
Description
    Basic second-order div using face-gradients and Gauss' theorem.

但实际使用时,div(phi, φ)(带通量的对流散度)走的是 gaussConvectionScheme(divSchemes/gaussConvectionScheme/),它把"插值格式"组合进来:div(phi, φ) = (1/V) Σ_f phi_f φ_f,其中 φ_f 由选定的对流插值格式给出。

divSchemes 里常见的写法:

cpp 复制代码
divSchemes
{
    default         none;
    div(phi,U)      Gauss linearUpwind grad(U);
    div(phi,k)      Gauss limitedLinear 1;
    div(phi,epsilon) Gauss limitedLinear 1;
    div(phi,omega)  Gauss limitedLinear 1;
    div((nuEff*dev2(T(grad(U))))) Gauss linear;
}
  • div(phi,U) ... linearUpwind grad(U):对 U 用线性迎风(借助 U 的梯度),二阶、较低耗散。
  • div(phi,k) ... limitedLinear 1:对 k 用限幅线性,TVD,防止湍流量过冲。
  • boundedConvectionScheme(divSchemes/boundedConvectionScheme/):有界对流格式,用于保证标量(如相分数 α)在 0,1 内。
  • multivariateGaussConvectionScheme:多元格式,用于多相流 vof 的多个场一起插值。

10.3.2 通量 phi:散度的输入

注意散度格式的第一个参数是通量 phi (surfaceScalarField),不是场本身。phi = Sf·U_f 的符号与大小决定了对流方向。这是有限体积法"守衡"的关键:所有方程共用同一个离散通量,才能保证质量守恒。这也解释了 fvSchemes 里的 fluxRequired------它声明哪些场需要计算并保存 phi(典型是压力 p,用于压力修正)。

10.4 拉普拉斯格式与"非正交修正"

10.4.1 基本形式

gaussLaplacianScheme.H(第 33--34 行):

cpp 复制代码
Description
    Basic second-order laplacian using face-gradients and Gauss' theorem.

laplacian(Γ, φ) = (1/V) Σ_f Γ_f (Sf·(∇φ)_f)。它由两部分组成(laplacianScheme.H 第 77--79 行):

cpp 复制代码
const fvMesh& mesh_;
tmp<surfaceInterpolationScheme<GType>> tinterpGammaScheme_;   // Γ 的插值(默认 linear)
tmp<snGradScheme<Type>> tsnGradScheme_;                       // 面法向梯度(默认 corrected)

默认组合就是 Gauss linear corrected------即"Γ 用 linear 插值 + 面法向梯度用 corrected"。

10.4.2 非正交网格的"正交 + 修正"分解

这是 OpenFOAM 最重要的数值技术之一。在非正交网格 上,面法向向量 Sf 与"连接 owner/neighbour 体心的向量 d"不平行。于是把 Sf 分解为:

ini 复制代码
Sf = Δ (正交分量) + k (非正交分量)
  • 正交分量 Δ 沿着 d 方向,可直接用"两侧值之差 / 距离"隐式表达 → 贡献到矩阵的 upper/lower/diag。
  • 非正交分量 k 引入的"交叉扩散"不能隐式表达,只能作为显式修正项,在矩阵组装后迭代补偿。

这对应 snGradSchemes 的选择:

snGrad 格式 含义
uncorrected 只用正交分量(忽略非正交)
corrected(默认) 完全修正非正交
orthogonal 假定网格正交
limited 限幅的非正交修正
skewCorrected 额外修正"偏斜"(面心不在连接线上)
faceCorrected 面尺度修正

修正项存在 fvMatrix::faceFluxCorrectionPtr_(第 9 章),并在 fvSolution 的 nNonOrthogonalCorrectors 次外层迭代里被反复应用:

cpp 复制代码
laplacianSchemes
{
    default         Gauss linear corrected;
}

配合

cpp 复制代码
SIMPLE { nNonOrthogonalCorrectors 1; }   // 或 PIMPLE 里

每有一次 nNonOrthogonalCorrectors,就多做一轮"重算修正 → 再解一次"。这就是 nNonOrthogonalCorrectors 的全部含义。

10.5 时间格式(ddtSchemes)

ddt 格式决定"时间导数如何离散",直接影响时间精度与稳定性:

格式 文件 阶数/特点
steadyState steadyStateDdtScheme 稳态(ddt 项为零,用于 SIMPLE)
Euler EulerDdtScheme 一阶隐式/显式(最常用、最稳)
backward backwardDdtScheme 二阶隐式(用三个时间层)
CrankNicolson CrankNicolsonDdtScheme 二阶,带混合系数(可加 bounded 防过冲)
localEuler localEulerDdtScheme 局部时间步(LES 常用)
CoEuler CoEulerDdtScheme 按库朗数自适应
SLTS SLTSDdtScheme 局部时间步进
bounded boundedDdtScheme 有界包装

EulerDdtScheme.H(第 31--33 行)说明:

cpp 复制代码
Description
    Basic first-order Euler implicit/explicit ddt using only the current and
    previous time-step values.

即 ∂φ/∂t ≈ (φ - φ⁰)/Δt。实现上:

  • 隐式 :把 φ 留在对角(diag += V/Δt),φ⁰ 移到源项;
  • 显式:整个项作为源项。

二阶格式(backward、CrankNicolson)会用到 φ⁰、φ⁰⁰(旧时间/旧旧时间),这正是第 8 章 GeometricField::field0Ptr_ 存在的意义。bounded 变体在计算后对场做一次限幅以保证物理有界性。

10.6 插值格式(interpolationSchemes)与限幅框架

10.6.1 基类与"权重"

surfaceInterpolationScheme.H(interpolation/surfaceInterpolation/surfaceInterpolationScheme/,第 28--94 行):

cpp 复制代码
Description
    Abstract base class for surface interpolation schemes.

template<class Type>
class surfaceInterpolationScheme : public refCount
{
    const fvMesh& mesh_;
    ...
    declareRunTimeSelectionTable(tmp, surfaceInterpolationScheme, Mesh,
        (const fvMesh& mesh, Istream& schemeData), (mesh, schemeData));
    declareRunTimeSelectionTable(...);
};

核心接口是 interpolate(ctf):把体心场插值到面。线性插值 的定义是"用几何权重把 owner 与 neighbour 的值加权平均"。权重由 surfaceInterpolation(fvMesh 的成员)提供,基于面心在连接线上的位置。

10.6.2 格式家族

OpenFOAM 的插值格式非常多(interpolation/ 目录下几十个),按性质分类:

基本/高阶插值:

格式 特点
linear 中心差分(CD),二阶
pointLinear 基于点的线性
cubic 三阶
midPoint 中点线性
harmonic 调和平均
reverseLinear 反向线性

迎风类:

格式 特点
upwind 一阶迎风(稳定但耗散大)
linearUpwind 二阶迎风(用梯度)
downwind 顺风(不稳定,需 blending)
skewCorrected 校正偏斜

限幅/TVD 类(最常用):

格式 特点
limitedLinear TVD 限幅线性
vanLeer TVD,平滑
vanAlbada TVD
UMIST 高精度 TVD
SuperBee 高精度(压缩)
Minmod 最保守
QUICK 三阶迎风
LUST linearUpwind+SuperBee 混合
SFCD、OSPRE、limitedCubic、MUSCL、CLUSTER... 其它 TVD/高分辨率

10.6.3 限幅的框架实现:LimitedScheme + LimiterFunc

TVD 格式不是一个个独立实现,而是共享一套**"限幅函数 + 模板生成"**框架。limitedLinear.H(第 31--38 行):

cpp 复制代码
Description
    Class with limiter function which returns the limiter for the
    TVD limited linear differencing scheme based on r obtained from the
    LimiterFunc class.

    Used in conjunction with the template class LimitedScheme.

limitedLinearLimiter<LimiterFunc>(第 57--81 行)读入系数 k(0..1),构造时检查合法性并预计算 twoByk_ = 2/max(k, SMALL)。真正生成"格式"的是 LimitedScheme.H 里的模板:它把"限幅函数"与"面值计算"组合起来,形成 limitedLinear<Type> 等具体格式。这套框架让"新增一个 TVD 限幅器"只需写一个小的 limiter 类。

限幅的核心量是 r(相邻梯度比)与 NVD/TVD 判据(NVDTVD.H、NVDVTVDV.H、LimitFuncs.H)。blended 系列(blended.H、localBlended.H、limiterBlended.H)则允许在"迎风"与"中心差分"之间按权重混合。

10.7 面插值的基础设施:surfaceInterpolation

fvMesh 继承 surfaceInterpolation(第 8 章)。它提供:

  • 插值权重:由面心相对 owner/neighbour 体心的几何位置决定;
  • 非正交/偏斜修正向量 :nonOrthogonalCorrectionVectors、skewCorrectionVectors(skewCorrected/skewCorrectionVectors.H);
  • interpolate():把体心场插到面。

10.8 一致性与守恒:为什么"共享通量"如此重要

有限体积法的生命力在于守恒。OpenFOAM 通过两条约定保证守恒:

  1. 同一个 phi 被所有方程共享:质量、动量、能量方程用同一个面通量,离散后总量守恒。
  2. owner/neighbour 镜像对称 :内部面的 upper/lower 用同一组系数,保证"流出 A 的等于流入 B 的"。

这也是为什么 fvSchemes 有 fluxRequired、求解器里总有 #include "createPhi.H"------通量是"共享的离散结构",必须显式创建与复用。

10.9 从配置到代码:一次 fvm::laplacian 的旅程

把本章串起来,fvm::laplacian(nu, U) 的执行流程是:

  1. 从 fvSchemes 的 laplacianSchemes 取到 Gauss linear corrected;
  2. laplacianScheme::New 经 RTS 构造 gaussLaplacianScheme<Type, GType>;
  3. 内部用 linear 插值 nu 到面,得到 Γ_f;
  4. 用 correctedSnGrad 计算 (∇U)_f(正交部分隐式进矩阵,非正交部分进 faceFluxCorrectionPtr_);
  5. 遍历面组装 fvMatrix:diag/upper/lower(正交部分)+ 源项(非正交修正);
  6. 若 nNonOrthogonalCorrectors > 0,在 solve 外层重复步骤 4--5 补偿修正项。

每一步都可配置、可替换、可组合------这正是 OpenFOAM "用户可完全掌控数值格式"的体现。


本章要点回顾

  • 四个基本离散:grad = (1/V)Σ Sf φ_f、div = (1/V)Σ phi_f φ_f、laplacian = (1/V)Σ Γ_f Sf·(∇φ)_f、ddt(Euler 一阶/backward·CrankNicolson 二阶)。
  • 面值由插值格式 给出,面法向梯度由 snGrad 格式 给出;通量 phi 是被所有方程共享的关键结构(守恒之源)。
  • 梯度格式:Gauss/leastSquares/fourth + cellLimited/faceLimited/cellMDLimited/faceMDLimited 限幅。
  • 散度/对流:gaussConvectionScheme 组合插值格式;bounded/multivariate 变体。
  • 拉普拉斯的核心是非正交修正 :Sf = Δ(正交,隐式) + k(非正交,显式),由 snGrad 的 uncorrected/corrected/orthogonal/limited/skewCorrected/faceCorrected 控制,并由 nNonOrthogonalCorrectors 迭代补偿。
  • 时间格式:steadyState/Euler(一阶)/backward·CrankNicolson(二阶)/localEuler/CoEuler/SLTS/bounded。
  • 插值格式:linear/upwind/linearUpwind/cubic/QUICK + TVD 限幅族(limitedLinear/vanLeer/UMIST/SuperBee/Minmod...),由 LimitedScheme+LimiterFunc 框架统一生成。
相关推荐
Tisfy1 小时前
LeetCode 3727.最大交替平方和:排序 (双指针可选)
算法·leetcode·题解·贪心·数组·排序
鶴哥只手遮天1 小时前
《OpenFOAM 源码深度剖析》系列-第 9 章 有限体积离散框架:`fvMatrix`、`fvSchemes` 与 `fvSolution`
算法·开源
鶴哥只手遮天1 小时前
《OpenFOAM 源码深度剖析》系列-第 7 章 网格与拓扑体系:`polyMesh`、`primitiveMesh` 与 patch
算法·开源
CHEEVEN_QY1 小时前
热仿真在液冷板设计中的应用价值与局限性客观评估
人工智能·算法
无忧.芙桃1 小时前
图的精髓:邻接矩阵与遍历全解析
c++·算法·面试·深度优先·广度优先
Nil2081 小时前
leetcode 1143最长公共子序列
算法·leetcode·职场和发展
脚踏实地,坚持不懈!1 小时前
Linux 内存回收机制:水位线、kswapd 与 Direct Reclaim
linux·jvm·算法
文慧的科技江湖1 小时前
2026年10月7日算力行业招投标早报:算力网进了“六张网”协调机制,调度软件仍没有标段 | 慧知开源智算调度平台
开源·算力·算力调度·智算·招投标
Logic1012 小时前
C语言/数据结构动态规划题解:字符串变回文的最少操作次数——最长回文子序列(LPS)
c语言·数据结构·算法·动态规划·时间复杂度·回文串·最长回文子序列