在非结构网格上拯救梯度的权重 — 移动最小二乘(MLS)近似
从散布点同时恢复函数值与导数的局部多项式拟合方法
非结构网格上梯度崩坏的那天#
下午两点,在非结构四面体网格上,LSQ(最小二乘)梯度在某一个网格单元突然跳到负值。相邻单元为 0.3、下一个为 0.5,而中间那个却是 -4.1。回到网格仔细看,那一格的七个邻居距离参差不齐。一个近邻点与六个远邻点以相同的权重进入正规方程。
解决方案在 1981 年就已经存在,只是出自另一个学科的另一个名字。
1981 年,从曲面拟合借来的式子#
Lancaster 和 Salkauskas 在一篇曲面重建论文中做了一个约定:"在每个评估点,只用该点附近的数据重新拟合多项式。"他们把它命名为 moving least-squares(MLS)。这本是图形学里用来构造光滑曲面的工具。
二十年后,Belytschko 的 EFG(Element-Free Galerkin)把同一个公式借走,作为无网格结构分析的骨架。又过十年,非结构 FVM 把它取来做梯度重构。SPH 的一致性修正导数也站在同一位置。出发点不是网格而是离散点,这一事实让每个领域都召唤出同一个公式。
权重函数把距离转换为影响#
MLS 的出发式很简单。在评估点 附近,用多项式基底拟合函数。
是多项式基向量(2D 一阶时为 ,二阶时为 ), 是系数向量。
关键在于 是 评估点 的函数。评估点一动,系数就被重新求解。这就是 "moving" 最小二乘的名字由来。
系数通过最小化加权残差平方和 确定。
权重函数 起决定性作用。靠近评估点的点权重大,远处的点权重几乎为零。高斯函数 、三次样条、四次多项式 — 它们有一个共同点:距离超过 后衰减到零。这个 就是 kernel radius(支撑半径)。
正规方程与形状函数的登场#
由 得到正规方程:
其中
为节点值向量,。把系数代回原式:
就是 形状函数(shape function)。但这个形状函数带来一个虽小却麻烦的性质 — 。也就是说,在节点上插值并不精确。这在 EFG 中使得 Dirichlet 边界条件的施加变得棘手。在 CFD 梯度重构中并非大问题:我们要的是光滑的导数,而不是节点值的精确插值。
Python — 用 25 个点重新对 sin 函数求导#
将基底取为 Taylor 形式 ,系数本身就成为导数 — 。
import numpy as np
from math import factorial
def mls_taylor_1d(xs, us, x_star, h, order=2):
"""在评估点 x_star 处使用 Taylor 基底的加权最小二乘。
返回的 a[k] 是 u^(k)(x_star) 的近似。
"""
m = order + 1
A = np.zeros((m, m))
B = np.zeros(m)
for x_i, u_i in zip(xs, us):
dx = x_i - x_star
w = np.exp(-(dx / h) ** 2)
if w < 1e-10:
continue
p = np.array([(dx ** k) / factorial(k) for k in range(m)])
A += w * np.outer(p, p)
B += w * p * u_i
return np.linalg.solve(A, B)
# 验证 — 恢复 sin(2 pi x) 的导数
np.random.seed(7)
xs = np.sort(np.random.uniform(0, 1, 25))
us = np.sin(2 * np.pi * xs)
x_eval = np.linspace(0.05, 0.95, 200)
du_hat = np.array([mls_taylor_1d(xs, us, xq, h=0.07, order=2)[1] for xq in x_eval])
du_true = 2 * np.pi * np.cos(2 * np.pi * x_eval)
rmse = np.sqrt(np.mean((du_hat - du_true) ** 2))
print(f"derivative RMSE: {rmse:.3e}")
# derivative RMSE: 2.1e-01 (h=0.07, order=2)25 个随机点下,导数 RMSE 约 0.21。相对于振幅 ,相对误差大约 3%。把 order 提高到 3 并将 设为 0.10 会进一步降低误差。但 过大会带来另一种问题。
在下面的模拟里直接调整参数试试看。
把 缩到 0.025,某些评估点的活动节点过少, 接近奇异,RMSE 爆炸。反过来把 调到 0.20,函数的振幅被平均掉,导数变钝。两个极端之间有一段狭窄的 sweet spot。
核半径 h — 大小两端各自损坏什么#
在梯度重构中, 在两件事之间做权衡。
- 小 : 的条件数失控。1〜2 个近邻点承担所有权重,用少于 个活动点去拟合 阶多项式,结果对噪声非常敏感。
- 大 : 远处点的信息混入并被平均化。零阶矩还能保留,高阶导数则被削弱。
实务上, 取 局部平均节点间距的 1.5〜3 倍,使支撑域内包含约 1.5〜2 倍 的活动节点。在非结构网格上必须按单元自适应调整 — 全局 几乎总是失败。
扩展到二维。28 个散布节点上给出 。拖动黄色评估点,观察权重的变化。
把评估点拖到左上角空白处,活动节点降到 以下, 变奇异。再拖回节点密集区,误差又变小。这正是 MLS 在非结构网格上的结构性弱点,也是实际中必须使用自适应 的原因。
在 SPH、EFG、FVM 中留下的痕迹#
MLS 今天以不同的名字活在三个地方。
- SPH 一致性修正: 标准 SPH 微分在非均匀粒子分布下连一阶精度都会丢失。corrected SPH 与 reproducing-kernel SPH 通过加权最小二乘修正恢复一阶一致性 — 本质上就是一阶 MLS。
- EFG / MLPG 无网格结构分析: 形状函数就是 MLS。节点处插值不精确使 Dirichlet 边界条件的施加变得棘手,Lagrange 乘子法或惩罚法是标准对策。
- 非结构 FVM 梯度重构: Compact Least-Squares Reconstruction 在单元中心上求解正规方程,不仅取出梯度还提取曲率。OpenFOAM 的
leastSquares选项也是一阶多项式 MLS 的变体。
三个领域解的是同一组正规方程。变化的只有权重函数和多项式基底的维数。
回到梯度崩坏的那天#
回到开头的那个单元。LSQ 梯度之所以跳到 -4.1,原因很简单:两个近邻点与五个远邻点以相同权重进入了正规方程。引入距离权重之后,-4.1 变成 0.4,发散消失了。
权重函数构造的局部性、每个评估点重新求解的正规方程、形状函数的非可分性 — 这三件事是 MLS 从图形学里借给我们的骨架。今天所有无网格积分与一半的非结构积分,都站在这副骨架之上。
明天也要记住的事#
- MLS 在每个评估点上求解加权正规方程,拟合局部多项式。采用 Taylor 基底时,系数本身就是导数。
- 核半径 是条件数(小 )与平均化(大 )之间的折衷。从节点间距的 1.5〜3 倍开始。
- 形状函数不会让节点值直接通过。Dirichlet 边界条件的施加因此变得棘手,但若只关心梯度则无碍。
如果对您有帮助,请分享。