观潮 · 模式预报与气象 AI 观察
MPAS 找不到土层:不是数据缺了,是字段名和代码对不上
MERRA2 转 WPS 中间文件跑 MPAS 限域,init_atmosphere 在插值土层时直接崩掉,日志里 139358 个网格点全部报缺土温和土湿。同一份中间文件喂给 WRF 却完全正常。
MPAS 开发组的最终结论:数据里什么都有,是 src/core_init_atmosphere/mpas_init_atm_cases.F 只认固定的那套土层字段名,MERRA2 的命名不在其中,于是所有土层被静默跳过,最后在插值阶段才崩。
这条帖子有个更实用的结尾:提问者自己动手改代码,改错了两次,最后靠官方提供的文件才跑通。 下面第 5 节是那两次失败的具体原因——如果你也要改这个文件,直接跳过去看。
---
报错长什么样
提问者的日志里有两条关键信息,缺一不可:
```
Bad st_fg and Bad sm_fg from 1 to 139358
CRITICAL ERROR: Error in interpolation of st_fg to MPAS grid: num_st = 368892
```
第一条是逐网格点的坏值计数,从 1 数到 139358 —— 正好等于他这个 mesh 的总网格数,也就是没有一个点是有土层的。
第二条的 num_st 不是网格数,而是通过筛选的土温站点数。提问者单进程跑时是 368892,官方用 1280 进程复现时变成 240。这个数掉到几百,说明送进插值器的有效站点几乎为零。
提问者自己也做了排查,确认不是数据的问题:
- 用
WPS/utils/rd_intermediate.exe看中间文件,"看起来就在那儿" - 同一份中间文件跑 WRF 的
real.exe和WRF.exe,都成功 - 用同一份数据加定制
METGRID.TBL跑metgrid,数据看起来是好的
也就是说,WPS 侧完全正常,问题必然在 MPAS 的读入层。这个对照提问者第一时间做了,省掉了大量猜测。
---
排查过程中被排除的三条线索
这三条都不是答案,但都是官方实际查过并排除的方向。记下来能省掉重复劳动。
一、static 文件版本不对。 官方一开始怀疑地形静态场有问题——用 1280 进程复现后查看研究区域地形。提问者提供的 static 文件是用 MPAS 8.3.1 生成的,他自己发现发错了,换成 8.4.0 重做,仍然崩。
二、填充值是 1.e+15。 官方检查数据后说,除了 ST 和 SM 的填充值之外"一切看起来都没问题",但提出把 MERRA2 的 ST/SM 填充值从 1.e+15 改成 0 再试,理由是"我不认为这些值会被用到,但以防万一"。
提问者花了些时间搞清楚怎么编辑 netCDF 的填充值,把填充值以及文件里所有实际等于 1.e+15 的值一并置 0,重新生成中间文件,结果仍然是同一个错。
三、编译 debug 版找负值。 官方重编译了 debug 版本、改了代码,想找出域内哪里出现了负的 ST/SM。结果发现模型几乎立刻停止,输出一些奇怪的量,比如某些海洋点上 st_fg 是负的,而中间文件里根本没有负的 ST 数据。提问者对此表示无法理解。
三条都排除了,问题才定位到字段名。
---
真正的根因:字段名对不上
提问者提供的 MERRA2 中间文件里,土层字段是这样命名的:
```
SM000010, SM010020, SM020040, SM040080, SM080150
```
ST 数据同样是这一套命名。
而 mpas_init_atm_cases.F 里,MPAS 处理的是这样一组名字:
```
SM010040, SM040100, SM100200, ...
```
两套命名除了最底层那个字段之外全不一样。
层 · MERRA2 中间文件字段名 · `mpas_init_atm_cases.F` 处理的字段名
1 · `SM000010` / `ST000010` · 一致——全帖唯一被正常处理的
2 · `SM010020` / `ST010020` · `SM010040` / `ST010040`
3 · `SM020040` / `ST020040` · `SM040100` / `ST040100`
4 · `SM040080` / `ST040080` · `SM100200` / `ST100200`
5 · `SM080150` / `ST080150` · 无对应
字段名匹配不上,mpas_init_atm_cases.F 就一个都不处理——不报错,只是跳过。等程序走到插值那一步,才因为手里没有土温数据而崩。这就是为什么日志里前面刷了一屏 Bad st_fg,而不是一个干脆的"文件缺字段"。
从字段名本身能读出层厚(名字里是层顶和层底的深度,单位厘米):MERRA2 是 0–1、1–2、2–4、4–8、8–15 五层,深度不均匀;而 MPAS 默认处理的那套是 0–1、1–4、4–10、10–20。层数不同、厚度也不同,这正是必须改代码而不能只改 namelist 的原因。
提问者把 nSoilLevels = 4、nFGSoilLevels = 5 这两个设置本身是对的——他对 MERRA2 有 5 层土壤温度和 5 层土壤湿度这件事理解得没问题。错的不是层数,是代码去哪儿找这些层。
---
官方给的修法
要改 src/core_init_atmosphere/mpas_init_atm_cases.F,两步:
- 把 MERRA2 的字段名加进代码里,让程序知道要处理这些名字
- 改
dzs_fg和zs_fg,让它们和 MERRA2 的土层厚度与深度定义保持一致
然后重新编译、重跑。
第 2 步是必须的。只改字段名不改厚度参数,程序会按自己那套层厚去解释这些数据,得到的土温剖面深度是错的——不会崩,但结果是错的,这比崩掉更难查。
---
提问者自己改代码,两次都没成功
这部分是帖子最有价值的地方,也是素材里没有、必须回论坛补的。
第一次尝试。 提问者收到官方定位后自己动手改:把 SM000010 那段代码整块复制粘贴,只替换字段名本身,以及对应的 dzs_fg 和 zs_fg。他的原话是"I don't really know any Fortran"——只做字面替换,不理解那段代码在干什么。
结果:仍然是同一个错。
官方的回应只有一句:"Your modification of mpas_init_atm_cases.F looks NOT correct."
第二次尝试。 隔了一天,官方在帖子里附了一个 323.7 KB 的文本文件 mpas_init_atm_cases.F.txt,让提问者整体替换自己改的代码,重新编译。
结果:跑通了。
提问者的原话是 init_atmosphere 完成了 init.nc 的生成,随后的 lbc/sst_update 也完成,模式实际跑起来了。
为什么字面替换不行
从官方那句"NOT correct"和"整体替换"能反推出:那段代码里除了字段名,还有依赖层号顺序、依赖具体深度数值的部分。复制一段、改个名字,编译能过、逻辑却不成立——这类改动最危险的地方在于它不报错。
提问者自己也在帖子里承认这一点,动手改这一轮"虽然问题还在,但让他学到很多 MPAS 的东西"。
如果你也要改这个文件,别自己拼,直接找 MPAS 团队要他们那份改好的文件。 官方在帖子里附的那份就是 MERRA2 初始场可直接用的版本。
---
作者承认的限制
这条帖子有几个必须写明的限定:
提问者验证的结果只到"模式跑起来"为止。 他的原话是 init_atmosphere 完成、lbc/sst_update 完成、"the actual model is running"。他没有检查 init.nc 里的土层剖面是否物理上正确——也就是说,第 4 节说的"第 2 步不改厚度参数会导致剖面深度错但结果看起来正常"这个风险,在这个帖子里并没有被验证或排除。跨数据源转初始场,改完字段名之后必须自己抽查土温垂直剖面。
这个修复不在官方发行版里。 它是论坛里给出的 mpas_init_atm_cases.F 修改,不是 8.3.1 或 8.4.0 的标准功能。升级 MPAS 版本后这个改动会丢失,需要重新应用。
帖子里没有给出 MERRA2 以外数据源的修法。 官方给的两步要求都是针对 MERRA2 这一套命名说的——把 MERRA 的字段名加进代码,再把层厚与深度参数改成与 MERRA 数据一致。其他数据源(RAP、GFS 等)的土层命名各不相同,各自需要单独改。有意思的是,2019 年另一位用户在 RAP 初始场上遇到过完全同类的问题,官方当时的解释也是同一句:代码里对已知土层的深度设置是硬编码在字段名上的(例如按 ST000010 这个名字来确定土层底深),当时并没有处理 RAP 土层的代码。那次提问者自己改代码成功了,但需要在 namelist 里同时调大 nfglevels 和 nfgsoillevels。
---
一句话判断
MPAS 侧"土层全缺"类崩溃,先查 src/core_init_atm_cases.F 里的字段名有没有匹配上你数据源的实际命名。 这个版本的处理逻辑是"名字对不上就静默跳过",不报缺字段错误,只在插值阶段以 Bad st_fg 和 num_st 崩给你看。逐点坏值计数从 1 连续数到网格总数、而不是零散分布,就是这个特征的指纹。跨数据源(MERRA2、ERA5、FNL、RAP)转换初始场时,这是第一个要核对的点;改的时候不要字面复制粘贴已有代码块,找官方要他们那份改好的完整文件。