1. 背景误差协方差矩阵在WRFDA中的作用
1.1 为什么说B矩阵是“看不见的发动机”
做气象资料同化这几年,我最大的感受是:很多人第一次把WRFDA跑通后,最先盯着的都是观测资料、边界层方案、外层嵌套怎么设,但真正决定同化效果下限的,反而是那个几乎没人愿意细看的背景误差协方差矩阵。这个矩阵在WRFDA里通常以一个叫be.dat(或者be.cv5、be.cv7之类的名字)的文件存在,由背景误差统计模块预先算好,然后在变分同化迭代求解过程中被反复调用。
如果没有这个矩阵,3DVar、4DVar根本没办法把观测信息和背景场信息按权重融合起来。你可以把它理解成一个“翻译官”:背景场(通常是WRF的预报场)告诉我们大气的先验状态,观测告诉我们大气的真实状态,但这两者各有各的误差,B矩阵负责告诉同化系统——背景场的误差在空间上有多远还有关联、在垂直方向上大概延伸到几层、不同变量之间的误差是同步涨落还是彼此独立的。
Piggy_Packages V2026.1的帮助文档把WRFDA这一大块单独列出来,说明这套工具链把B矩阵相关的脚本、参数、checklist都做了整合。如果你之前手动编译过WRFDA,一定记得那一堆环境变量、链接库路径和.csh脚本的排列组合,稍微漏一个库,gen_be生成出来的数据就可能是错的。Piggy_Packages做的事情就是把这些东西打包好,让做同化的人不用每次都在编译阶段干耗时间。
1.2 B矩阵解决的同化核心矛盾
先抛一个朴素的问题:同化系统凭什么相信背景场?又凭什么相信观测?答案是——都不全信,按各自的误差协方差来加权。
这个“加权”在代价函数里写得很直白:
[ J(x) = \frac{1}{2}(x - x_b)^T B^{-1} (x - x_b) + \frac{1}{2}(y - H(x))^T R^{-1} (y - H(x)) ]
左边那一项是背景项,右边那一项是观测项。B矩阵是背景误差协方差矩阵,R矩阵是观测误差协方差矩阵。两个矩阵的相对大小,决定了同化结果到底是更贴近背景场还是更贴近观测。
这句话说起来容易,真正做起来就麻烦了。B矩阵不是标量,它是一个N×N的矩阵,N是模式状态变量的总自由度。中尺度模式哪怕一个区域域少说也有几十万个格点,每个格点有u、v、t、q、ps五个基本变量,再算上各种水凝物,B矩阵的完整规模几百万维乘几百万维,直接存储和求逆根本不现实。
所以大家在工程上不会去显式地存这个巨大矩阵,而是用“控制变量变换”的方式隐式表达它。WRFDA里最常见的做法是把B矩阵分解成水平相关、垂直相关、物理变量间相关三个部分,分别用不同的数学工具去表达,然后通过一系列变换算子组合起来。这就是后面要讲的CV3、CV5、CV6选项的由来。B矩阵这个“发动机”虽然看不见,但它的转速和扭矩,直接决定同化跑出来的分析场是平滑可信的,还是一堆噪声。
2. 控制变量变换:B矩阵如何被“压缩”进可计算框架
2.1 从N×N矩阵到几条相关函数
B矩阵直接算不可行,那怎么办?数据同化的前辈们想出了一个工程上极其聪明的办法:不直接估计背景误差协方差,而是估计背景误差在“控制变量空间”里的统计特征。
打个比方,你想知道一群人之间的身高差异,不需要把每个人的身高跟其他所有人两两配对比较,只需要知道人群总体的方差,以及“人与人的身高差异在多大程度上随距离衰减”这个规律就够了。对B矩阵来说,“方差”就是背景误差的强度,“随距离衰减的规律”就是空间相关尺度。
WRFDA里的控制变量变换,本质上是把模式空间的物理量(u、v、t、ps、q等)通过一个平衡变换加上一组物理变换,转换成一组统计上更“干净”的控制变量。这些控制变量之间尽量不相关,每个控制变量再单独配一个空间相关模型。这样一来,原本N×N的稠密矩阵就被拆成了几块:对角块是各控制变量的方差(用标准差场表示),非对角结构由平衡关系系数和空间相关算子表达。
这套思路具体到代码里,就是WRFDA的VarBC和gen_be这两大模块的分工。gen_be负责从一组背景场差分的样本里统计出标准差、水平相关尺度、垂直特征向量、平衡系数;VarBC则在高斯变分求解时,通过一系列算子把这个统计信息“作用”到增量场上。
2.2 CV3、CV5、CV6:三种控制变量版本怎么选
在namelist.input或者var4d的配置里,你经常会看到cv_options这个参数。Piggy_Packages的帮助文档里应该也强调了,这个选项必须在编译期就定死,运行时改不了。它有几个常见取值:3、5、6,分别对应不同的控制变量组合方案。
| 选项 | 控制变量组成 | 适用场景 | 特点 |
|---|---|---|---|
| cv_options=3 | psi, chi_u, t_u, ps_u, rh_u(或q_u) | 中尺度3DVar经典配置 | 变量间通过平衡方程耦合,物理变换用湿度相对湿度,资料同化系统里最成熟的一档 |
| cv_options=5 | psi, chi, t, ps, q(各变量全非平衡+平衡分离) | 4DVar和高分辨率常用 | 对温湿场有更灵活的垂直结构表示,背景误差统计更细 |
| cv_options=6 | 类似CV5但湿度控制变量可选RH或q,并对水凝物做扩展 | 云分辨尺度同化、雷达资料同化 | 带动量、热量、水物质水凝物变量的显式表示,适合云尺度预报误差描述 |
我个人的建议是:如果你刚刚接触WRFDA,或者你的模式分辨率在9km以上、主要做常规观测同化,直接用cv_options=3就够用了,它的统计最稳定、调试资料最多。如果做到4DVar或者台风、暴雨这类对流尺度个例,再考虑cv_options=5,配合更高的水平分辨率和更密的探空资料。至于cv_options=6,除非你做的是云分辨尺度的雷达同化,或者你的模式域里有大量云解析网格,否则它的水凝物控制变量反而会让gen_be的样本需求翻倍,小样本跑出来的统计噪声会非常大。
这里有一个很容易踩的坑:cv_options改了之后,之前用gen_be生成的be.dat必须重新生成。很多人只改了namelist里的cv_options,忘了重新跑gen_be,结果同化的时候报错或者分析场完全失真。Piggy_Packages里其实提供了一个脚本可以检查be.dat的头部信息,建议每次切换cv_options之后都跑一遍,确认文件里的控制变量维度和namelist一致。
2.3 平衡变换:B矩阵里的“变量耦合”到底怎么来的
B矩阵并不仅仅描述单个变量的误差,它还描述变量之间的误差关联。这种关联里面,有很大一部分是动力和热力上的“平衡约束”。比如质量场和风场之间的地转平衡、静力平衡下的温度和高度的关系,这些平衡关系在背景误差统计里表现为显著的协方差。
在WRFDA的CV3方案里,控制变量被拆成两部分:平衡部分和非平衡部分。平衡部分通过回归系数从流函数psi中衍生出来,比如非平衡温度t_u的方程就写成:
[ t_u = t - N \cdot \psi ]
其中N是由gen_be回归出来的平衡系数矩阵,代表“psi对温度场的平衡贡献”。同理,非平衡地面气压ps_u、非平衡湿度rh_u也都有各自的回归系数。这样一来,原本u、v、t、ps、q五个变量之间的复杂耦合,就被转化成了psi与若干个非平衡量之间的简单结构——psi有自己的方差和空间相关,每个非平衡量也有自己的方差和空间相关,而这些非平衡量之间假设不相关。
这个假设当然不完全成立,但它极大地简化了问题,而且实践证明在中尺度同化里面够用。做同化的老手通常会在alpha_correction或者cv_options升级的时候,特别关注平衡系数矩阵的变化,因为如果平衡系数和你的个例背景场差距太大,分析场的增量会出现“拧麻花”现象,也就是质量场和风场之间打架。
在Piggy_Packages的环境里,gen_be生成的平衡系数会直接写进be.dat的二进制流里,运行同化时由da_update_bc等模块读取。我看到有一些用户试图手动修改这些系数做“调B”,我的建议是:除非你有充分的统计样本和严格的检验方案,否则别动它。调B矩阵的正确姿势是通过标准差尺度因子和水平相关尺度因子去压,而不是往平衡系数里塞主观值。
3. 用Piggy_Packages生成B矩阵的实操全流程
3.1 环境准备和gen_be的启动方式
先聊一下Piggy_Packages解决了什么问题。常规的WRFDA编译部署,需要手动设置netCDF、mpich、HDF5、zlib等一堆库的路径,还要处理32位和64位浮点的编译选项。Piggy_Packages把这些编译依赖做成了自动化的环境配置脚本,你在它的shell环境里执行gen_be相关命令,不需要像以前那样手动source一堆setenv文件。
B矩阵的生成,标准方法是NMC方法,简单说就是拿同一时刻不同预报时效的预报场之差作为背景误差的样本。通常做法是取T24减去T12的预报差,收集一个月以上、每天多个时次的数据,统计出误差的标准差和相关尺度。如果你做的是对流尺度同化,预报差可以选择T12减T06或者更短时效的组合,视模式配置而定。
在实际操作中,我会先把WRF跑出一批历史预报。假设你有一个月的模拟时段,每天跑00时和12时两个起报时次,每个时次输出12小时和24小时预报。这样每天能得两个差分样本(24h预报减12h预报),一个月下来约60个样本。样本量虽然不算多,但勉强能撑起中尺度B矩阵的统计。
生成B矩阵的命令大致是:
cd ${Piggy_Packages}/WRFDA/var/run export BE_DIR=/path/to/your/gen_be_dir export WRFDA_DIR=/path/to/Piggy_Packages/WRFDA export START_DATE=2015060100 export END_DATE=2015063023 export INTERVAL=12 export NUM_LEVELS=42这些环境变量是gen_be那套脚本约定好的,不能用别的名字替代。设置好之后执行:
${WRFDA_DIR}/var/gen_be/gen_be_wrapper.csh3.2 gen_be每个步骤的输出是什么
gen_be_wrapper.csh跑起来,会依次调用多个子步骤,每一步都有对应的输出文件。不理解这些文件的人会觉得就是一串中间产物,但其实每个文件都在后续同化中有明确的用途。
首先,gen_be_stage0会做基本的样本重命名和格式整理,检查你的差分场文件是否齐全。这个步骤看着不起眼,但很多报错都来自这里——文件时次不连续、变量名不匹配、维度和namelist不一致,都会在这一步被卡住。
然后是gen_be_stage2,它会输出各个变量在模式层上的背景误差标准差。这些标准差会以一个叫std_开头的文件存下来,后续做尺度化因子调整时,会用到这些文件里的数值范围来判断偏差是否合理。
gen_be_stage3输出的是水平相关尺度。对每个变量在每一层上,系统会拟合一维或二维的相关系数函数,然后反算出一个代表相关衰减距离的尺度值。这个值在后面运行时被读取,用于构建水平相关算子。
gen_be_stage4输出的是垂直误差相关,实际存储形式是垂直特征向量和特征值。它是通过对垂直协方差矩阵做EOF分解得到的,同化系统在求解时只取前若干个主导模态,把垂直维的复杂相关结构压缩成低秩近似。
gen_be_stage5处理的是平衡系数,输出balance_*等相关文件。这部分的数值合理性高度依赖样本质量,如果样本里有明显有问题的极端天气个例,回归系数会偏离正常范围。
Piggy_Packages的帮助文档给了一个很好的建议:每一步gen_be跑完后,都把输出目录里的log文件扫一眼,重点关注有没有NaN、Inf、zero variance这些关键词。出现这些词通常意味着样本里有异常场,得先回去排查WRF输出,而不是继续往下跑。
3.3 尺度化因子:新手最容易忽略的微调手段
be.dat生成之后,大部分人的做法是直接拿去同化,跑出来发现分析场要么太“软”——观测信息几乎没有被用上,要么太“硬”——分析场出现明显的棋盘格噪音。这时候最有效的调节手段不是重新跑gen_be,而是调整尺度化因子(scale factor)。
尺度化因子在运行时配置,对应的参数名在WRFDA 3DVar里是sf_scale、rf_scale、cv_options相关的几个系数。在4DVar里则有更精细的len_scaling1、len_scaling2、variance_scaling等。
我自己在做台风个例同化时,有个经常用的经验:如果分析场对观测响应过强、增量过猛,先把sf_scale调小到0.5附近,等效于把背景误差标准差调小,让背景场更“强势”一些。反过来,如果同化后分析场跟观测差得很远,说明背景误差权重被压得太低,这时可以尝试把sf_scale恢复到1.0甚至1.1,再配合水平相关尺度的len_scaling微调。
这些参数有点像一个放大镜,它不会改变B矩阵的“形状”,只改变B矩阵的“幅度”。形状不对(比如水平相关尺度严重小于实际网格距)就不是scale factor能救回来的,得回头改gen_be的统计区间或样本集。
4. 常见问题与排查技巧实录
4.1 错误一:be.dat维度与控制变量选项不匹配
这是大家刚上手时遇到频率最高的问题。表现是运行da_wrfvar时直接报类似“dimension mismatch”或者“error reading be.dat”的错误,最隐蔽的一种是不报错,但分析场完全乱掉。
排查方法很简单:用Piggy_Packages里自带的be.dat查看工具(通常在$Piggy_Packages/WRFDA/var/run/下能找到gen_be_etkf或者read_be等工具)直接读be.dat的头信息,看看里面记录的变量数量、层数、控制变量类型是否和namelist里的cv_options一致。如果一致还报错,再检查编译WRFDA时用的浮点精度和生成be.dat时是否一致。最常见的坑是生成be.dat时机器是大端,运行机器是小端,导致读取错位。Piggy_Packages在打包时通常已经处理了这个问题,但如果你的数据是在集群上生成的,再拿到本地跑,就必须留意字节序。
4.2 错误二:样本数不足导致统计噪声过大
B矩阵说到底是一个统计量,样本太少,统计出来的相关尺度就会出现锯齿状分布。具体表现是:水平相关尺度在各层之间毫无规律地跳变,垂直特征向量出现负值主导的模态,同化后分析场出现奇异点。
解决的方向有两个。一是增加样本量,把统计时段拉长到两个月甚至三个月,或者增加每天的输出频次。二是降维处理,gen_be支持对垂直EOF只保留前若干个模态,运行时也有num_modes之类的参数可调。把模态数从默认值压缩一半,通常能显著降低尾模态噪声。
这里要特别提醒一点:不要为了省事把每天的06时、18时也混进样本里,除非你的模式在那两个时次有对应的12小时和24小时预报。混入不同预报时效的差分场,会污染误差的时间尺度特征,导致相关尺度整体偏移。
4.3 错误三:同化后分析场出现“微笑”形虚假增量
这个现象在低纬度或台风个例里特别常见。分析场在观测周围出现明显的“牛眼”结构,增量沿某个方向拉长成弧形。常见原因就一个:水平相关尺度被设得过大,同时背景误差标准差也被放大,导致观测的影响被过度外推。
遇到这种情况,我的做法是三步走。第一步,把水平相关长度直接减小到网格距的2到3倍附近重新尝试。第二步,把标准差的尺度化因子下调20%到50%,让观测被局部吸收而不是大范围扩散。第三步,如果前两步都不行,就去看gen_be输出的水平相关尺度文件,检查它在高层是不是有异常偏大的层,必要时在gen_be里单独对该层做平滑处理。
4.4 错误四:湿变量(q)的同化不收敛
做湿度场同化时,经常遇到的问题是增量反复震荡,湿度场在某些格点出现负值或者超过饱和比湿。这个问题的根源多半在背景误差协方差里湿度变量的处理方式。
WRFDA的CV3方案默认对湿度用相对湿度作为控制变量,这在中纬度常规观测同化里问题不大,但在强对流或台风环境下,相对湿度误差的统计分布并不服从高斯假设,同化时容易出现边界溢出。此时你可以考虑切换成比湿控制变量(cv_options=5或6),或者在namelist里打开湿度增量限制开关,把湿度增量的最大幅度限制在某个物理合理范围内,比如相对湿度的0%到100%之间。
Piggy_Packages的帮助文档在湿度同化这部分花了不少篇幅,我建议一定要仔细读一遍。湿度场的B矩阵调节没有统一套路,每个区域的气候背景差异太大,唯一的通用方法就是多做敏感性试验,看哪个配置组合得到的分析场最接近探空和雷达观测。
5. 经验沉淀:B矩阵调优的几个方向性建议
5.1 不要迷信默认be.dat
有很多人图省事,直接从网上下一个通用be.dat,也不管自己的模式分辨率、区域位置、季节是不是匹配,拿来就用。这样做运气好能跑通,运气不好分析场的误差会大到让你怀疑观测资料本身有问题。
通用be.dat的最大问题不是“错”,而是“不匹配”。它的水平相关尺度和垂直特征向量是基于某个特定区域、特定网格距统计出来的,换到你的华东地区或者青藏高原,误差统计特征完全不同。一个直观的检验方法:跑一次同化,然后对比分析场减去背景场的增量图,如果增量结构里出现明显的、与观测分布无关的大尺度波纹,多半就是B矩阵和你的区域不匹配。
所以在做重要个例研究之前,花三天时间用自己的历史预报场跑一遍gen_be,绝对值得。
5.2 分阶段做同化敏感性试验
B矩阵调优是典型的“高维参数搜索问题”,别指望一次调对,也别一上来就同时动五六个参数。我的习惯是先固定其他所有参数,只调一个尺度化因子,看分析场的增量均方根误差变化;然后再固定它,去调水平相关长度;最后才去碰平衡系数或者垂直模态数。
每次只动一个参数,并且记录下前后两次分析场的差异。这样做的好处是,你建立的参数经验是可迁移的,换一个区域或季节,你至少有初始参考值,而不是从头盲试。
5.3 用好Piggy_Packages自带的诊断工具
Piggy_Packages V2026.1相比我自己手动配置的WRFDA环境,最大的改进是它把诊断工具集成进了工作流。比如你可以直接调用它的plot脚本查看gen_be输出的标准差剖面,或者查看同化后的增量分布。之前这些功能要自己写NCL和Python脚本去处理netCDF输出,现在都省了。
建议每个做同化的新手,务必花时间把Piggy_Packages里几个plot_脚本跑一遍,把B矩阵的统计特征可视化出来看一遍。看到那些标准差剖面和垂直特征向量的第一眼,你对B矩阵的理解会从“抽象的数学对象”变成“直观的物理图像”,这对后面所有调参工作都有帮助。