1. 课程导入与全局认知

地下水数值模型(如 MODFLOW、FEFLOW、TOUGH2)在资源评价、水源地论证、污染物迁移预测、矿山疏干排水与地面沉降控制中被广泛使用。但任何模型都不是现实的复制品:含水层是非均质的,野外试验点稀疏,边界条件往往只能估计,补给量随气象与人类活动波动。于是建模者必须回答两个问题——哪些参数真正左右预测结果,以及预测结果本身有多大的可信区间。前者是敏感性分析,后者是不确定性分析,二者共同构成模型可信度评估的核心。

两个概念的分工

敏感性分析(Sensitivity Analysis, SA)研究的是"输入扰动→输出响应"的强弱关系。它回答:渗透系数 K 变化 20% 时,某观测井水位变化多少?若把 K 的扰动幅度与输出变化量之比定义为无量纲敏感度,就能对参数排序。常用方法包括一次一因子法(OAT)、局部敏感度、Morris 筛选法与 Sobol 全局方差分解。OAT 计算量小、易解释,但无法反映参数交互;Sobol 能给出主效应与交互效应,代价是样本量随参数维数快速增长。

不确定性分析(Uncertainty Analysis, UA)则把参数视为随机变量,通过蒙特卡洛抽样、拉丁超立方抽样(LHS)或贝叶斯反演(如 PEST、DREAM)得到输出的概率分布,进而给出 P10/P50/P90 分位数、置信区间与超标概率。敏感性分析常作为不确定性分析的前置筛选:先用 Morris 剔除无关参数,再对关键参数做全分布抽样,可显著降低计算成本。

在建模全流程中的位置

参数敏感性与不确定性分析不是建模末尾的"附加报告",而是贯穿概念模型、参数识别、校准与预测的闭环环节。下面的信号链图展示了它在建模流程中的位置与反馈关系:

概念模型 边界·分层·源汇 参数初值 K·S·R·T 模型校准 PEST / 试估 预测情景 开采·污染·沉降 决策支持 阈值·风险 敏感性分析 SA OAT · Morris 筛选 · Sobol 方差分解 输出:参数排序 · 敏感度指数 不确定性分析 UA Monte Carlo · LHS · 贝叶斯反演 输出:P10/P50/P90 · 置信区间 关键参数 指导调参 闭环要点: ① SA 在参数初值后即可介入,用于剔除无关参数、锁定校准重点;② UA 以 SA 结果为输入,量化预测区间; ③ 预测区间过宽时,应回到概念模型与参数初值补充勘察或收紧先验,而非仅调整算法。

图中虚线反馈尤其重要:当敏感性分析显示某参数几乎不影响目标输出时,不必为其投入昂贵的抽水试验;反之,若某参数敏感度极高而现场数据匮乏,就应优先补充勘察。不确定性分析得到的预测区间若过宽,说明问题往往不在算法,而在概念模型或先验信息不足。

典型工程与科研场景

在水源地论证中,常需评估开采量增加后某观测井的降深是否超过允许值。此时对 K、给水度、补给量做 LHS 抽样 500 次,统计降深超过阈值的样本比例,即得到超标概率。在污染物迁移问题中,弥散度与分配系数的不确定性会显著放大到达时间的分布范围,敏感性分析可帮助判断应优先开展哪类示踪试验。在矿山疏干与地面沉降场景中,弱透水层渗透系数与压缩系数的交互效应突出,OAT 容易低估其影响,宜采用 Sobol 或 Morris 全局方法。科研层面,参数敏感性与不确定性分析还是数据同化、模型结构辨识与多模型集合预报的基础。

学习路径与工具准备

本课程按"概念—方法—实现—案例—复盘"推进:先建立敏感度与不确定度的数学定义,再依次掌握 OAT、Morris、Sobol 与蒙特卡洛/贝叶斯反演,随后在 Python 生态中落地实现,最后通过完整案例串联。工具方面建议准备:

  • 建模内核:MODFLOW 6(配合 FloPy 脚本化建模)或 FEFLOW,用于生成不同参数组合下的运行结果。
  • 分析库:Python 的 numpy、pandas 做抽样与统计,SALib 提供 Morris/Sobol 采样与指数计算,chaospy 支持多项式混沌展开。
  • 反演工具:PEST / PEST++ 用于参数估计与线性化不确定性,pyEMU 可读取 PEST 结果做预报不确定性分析。
  • 可视化:matplotlib 绘制敏感度条形图、龙卷风图与输出累积分布曲线。

下面给出一个可直接运行的最小示例,用 SALib 对三个参数做 Morris 筛选,帮助建立"抽样—运行—计算指数"的完整手感(示例参数与结果均为教学假设,非实测):

import numpy as np
from SALib.sample import morris as ms
from SALib.analyze import morris as ma

problem = {
    "num_vars": 3,
    "names": ["K", "Sy", "R"],
    "bounds": [[1e-5, 1e-3], [0.05, 0.25], [50, 200]]
}
X = ms.sample(problem, N=20, num_levels=4)   # 生成轨迹样本
# Y 为每次 MODFLOW 运行得到的观测井降深(此处以假设函数代替)
Y = 0.8 * np.log10(X[:, 0] / 1e-5) + 2.0 * X[:, 1] + 0.01 * X[:, 2]
Si = ma.analyze(problem, X, Y, num_levels=4)
print(Si["mu_star"])   # 各参数平均绝对敏感度,用于排序

运行后若 mu_star 显示 Sy 与 K 明显大于 R,则后续蒙特卡洛抽样可只对前两者展开,计算量可下降一个数量级。这正是敏感性分析"降维"价值的直接体现。

学习建议:每学一种方法,都用同一套教学算例复算一遍,比较 OAT 与 Sobol 给出的参数排序是否一致。排序差异本身就是理解参数交互作用的最好入口。