ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

WRF实战:手写ECMWF海温Vtable,解决ungrib无法识别SST的难题

WRF实战:手写ECMWF海温Vtable,解决ungrib无法识别SST的难题 WRF 跑区域气候或者短期预报的时候很多人卡在第一步——把外部数据喂进 WPS。GFS 数据有现成的 Vtable直接link_grib.csh加ungrib.exe就完事但一旦换成 ECMWF 的 ERA5 或者业务预报场尤其是要引入海温SST这种下边界强迫现成 Vtable 就不够用了。ECMWF 的 GRIB 里海温字段命名、层次、时间维度跟 NCEP 那套完全不是一回事ungrib 认不出来metgrid 里 SST 就是空的最后跑出来的结果海陆温差离谱沿海站点误差能飙到好几度。这篇东西就是把我自己折腾 ECMWF 海温数据接进 WRF 的完整过程摊开讲。从 Vtable 到底在 ungrib 里扮演什么角色到怎么根据 ECMWF 的 GRIB 表反查字段、手写映射、验证输出再到几个我踩过不止一次的坑。适合已经能跑通 GFS 流程、想换 ECMWF 数据源但被 Vtable 卡住的人。如果你连 WPS 都没装好建议先把基础流程走通再来看这篇不然容易越看越乱。1. 先搞清楚 Vtable 在 ungrib 里到底干了什么1.1 ungrib 不是解码器它是翻译官很多人对 ungrib 有个误解以为它负责把 GRIB 解码成 NetCDF。其实不是。GRIB 文件的解码是 WPS 内部依赖的 GRIB 库通常是 grib_api 或者 eccodes在做ungrib 真正干的事是把 GRIB 里五花八门的字段名翻译成 WRF 认识的统一中间名。这个统一中间名就是 Vtable 里定义的那套东西比如SST、TT、UU、VV、PSFC、SOILHGT这些。WRF 的 metgrid 和 real 只认这套名字它不管你原始数据是 ECMWF、GFS 还是 JMA 的。所以 Vtable 的本质是一张映射表左边是 WRF 要的中间名右边是数据源里实际的 GRIB 字段标识。ECMWF 的数据在这张表上跟 GFS 差异很大。GFS 的字段号是 NCEP 自己那套比如海温是TMP在 surfaceECMWF 用的是 WMO 标准的 GRIB 参数表海温对应的是参数号 34短名sst层次类型是 surface。如果你直接拿Vtable.GFS去 ungrib ECMWF 数据ungrib 会找不到匹配项日志里一堆Unknown或者干脆跳过最后FILE:*里 SST 就是缺的。1.2 为什么海温字段特别容易出问题海温在 WRF 里属于下边界强迫它不像温度、风场那样每个层次都有而是只在表面出现一次。ECMWF 的 ERA5 里海温字段有几个特点让新手特别容易翻车第一它可能不在你下载的那批变量里。ERA5 单层数据里 SST 的变量名是sea_surface_temperature很多人下载的时候只勾了 2m 温度、10m 风、海平面气压忘了勾 SST结果 ungrib 跑完发现没有还以为是 Vtable 写错了。第二它的时间分辨率可能跟大气场不一致。ERA5 的 SST 在早期版本里是 6 小时一次后来改成 hourly但如果你下载的时候选了不同的 product type时间戳可能对不上ungrib 按时间合并的时候就会错位。第三层次类型容易搞混。ECMWF 里 SST 的 level type 是Surface但有些数据集里它可能挂在sfc或者别的类型下Vtable 里level_type那一列写错了ungrib 就匹配不上。我见过最典型的情况是Vtable 里 SST 那行写的是SST | 34 | sst | Surface但实际数据里 level type 是sfc结果 ungrib 日志里 SST 一直是not foundmetgrid 跑完met_em文件里 SST 全是默认值。1.3 Vtable 的列结构逐列拆解标准 Vtable 是竖线分隔的几列以Vtable.ECMWF为例一行大概长这样SST | 34 | sst | Surface | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0从左到右依次是第 1 列WRF 中间名比如SST、TT、UU。这个名字是 metgrid 认的不能乱写必须用 WPS 文档里定义的那套。第 2 列GRIB 参数号parameter number。ECMWF 的 SST 是 34。第 3 列GRIB 短名shortName。ECMWF 里是sst。第 4 列层次类型level type。SST 是Surface。第 5 列层次值level value。表面字段一般是 0。后面若干列留给特定数据源的扩展字段ECMWF 一般用不到填 0 或者留空。关键点在于第 2、3、4 列必须跟实际 GRIB 文件里的元数据完全一致。差一个字符ungrib 就匹配不上。所以写 Vtable 之前第一件事是先把 GRIB 文件的字段清单 dump 出来看。2. 动手之前把 ECMWF 数据的字段清单摸清楚2.1 用 grib_ls 把字段全列出来假设你下载的是 ERA5 单层数据文件名类似era5_sfc_20200101.grib。第一步不是急着写 Vtable而是先看这个文件里到底有什么。如果你装的是 eccodes用grib_ls -p shortName,paramId,levelType,level,dataDate,dataTime era5_sfc_20200101.grib如果用的是老版 grib_api命令换成grib_ls -p shortName,paramId,typeOfLevel,level,dataDate,dataTime era5_sfc_20200101.grib输出大概长这样shortName paramId levelType level dataDate dataTime sst 34 surface 0 20200101 0 t2m 167 surface 0 20200101 0 u10 165 surface 0 20200101 0 v10 166 surface 0 20200101 0 msl 151 surface 0 20200101 0 ...这里你要重点确认三件事SST 的shortName是不是sstparamId是不是 34levelType是不是surface。如果levelType显示的是sfc而不是surface那 Vtable 第 4 列就得写sfc不能写Surface。提示不同版本的 eccodes 对 levelType 的命名可能不一样有的显示surface有的显示sfc。以你实际 dump 出来的为准不要照抄网上的 Vtable。2.2 确认时间维度和步长海温数据的时间戳必须跟大气场对齐否则 ungrib 按时间合并的时候会出问题。用grib_ls -p shortName,dataDate,dataTime,stepRange era5_sfc_20200101.grib重点看 SST 和 t2m、u10 这些字段的dataDate、dataTime是不是一致。如果 SST 是 6 小时一次而大气场是 1 小时一次那你在下载阶段就得把它们统一到相同时间分辨率或者在 ungrib 之前用工具做时间插值。我自己的习惯是下载阶段就把所有变量统一成相同的时间步长宁可多下一点数据也不要在 ungrib 阶段做时间对齐那个坑更深。2.3 检查 GRIB 版本和编码方式ECMWF 的数据有 GRIB1 和 GRIB2 两种。ERA5 一般是 GRIB1虽然官方也提供 NetCDF但业务预报场可能是 GRIB2。Vtable 对 GRIB1 和 GRIB2 的匹配逻辑略有不同尤其是参数号的表示方式。用grib_ls -p editionNumber era5_sfc_20200101.grib看editionNumber是 1 还是 2。如果是 2Vtable 里参数号那列可能要用paramId而不是parameterNumber具体取决于 WPS 版本。WPS 4.x 之后对 GRIB2 的支持好了很多但老版本 WPS 3.x 处理 GRIB2 的 ECMWF 数据经常出幺蛾子。注意如果你用的是 WPS 3.9 以前的版本强烈建议先把 ECMWF 数据转成 GRIB1 再 ungrib或者直接升级 WPS。我早期用 WPS 3.8 处理 ERA5 GRIB2ungrib 直接段错误折腾了两天才发现是版本问题。3. 手写 ECMWF 海温 Vtable 的完整过程3.1 从 Vtable.ECMWF 改起不要从零写WPS 的ungrib/Variable_Tables/目录下有一堆现成 Vtable其中Vtable.ECMWF是最接近的起点。直接复制一份cd WPS/ungrib/Variable_Tables cp Vtable.ECMWF Vtable.ECMWF_SST然后打开Vtable.ECMWF_SST找到 SST 相关的那行。如果原文件里没有 SST就手动加一行。ECMWF 的 SST 映射大概是这样SST | 34 | sst | surface | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0注意第 4 列我写的是surface这是根据我实际 dump 出来的 levelType 填的。如果你的数据里显示的是sfc就改成sfc。3.2 参数号、短名、层次类型三者必须自洽这是最容易出错的地方。Vtable 的匹配逻辑是ungrib 读 GRIB 文件里每个字段的元数据然后拿这些元数据去 Vtable 里逐行比对。比对的时候参数号、短名、层次类型三个都要对上只要有一个对不上这行就匹配失败。我遇到过一种情况参数号写对了34短名写对了sst但层次类型写的是Surface大写 S而实际数据里是surface小写 s。ungrib 是大小写敏感的结果就是匹配不上。日志里会显示类似SST not found in GRIB file但你去grib_ls里看SST 明明在。这种问题最坑因为看起来什么都对就是差一个字母的大小写。提示写 Vtable 的时候第 4 列建议直接从grib_ls的输出里复制粘贴不要手打。手打很容易把surface打成Surface或者surf。3.3 处理 ECMWF 特有的层次类型命名ECMWF 的层次类型命名跟 NCEP 不太一样。NCEP 里表面字段的层次类型通常是surface但 ECMWF 有时候用sfc有时候用surface取决于数据集和 GRIB 版本。更麻烦的是有些 ECMWF 数据集里海温挂在meanSea或者别的类型下。如果你 dump 出来的 levelType 是sfcVtable 第 4 列就写sfc。如果是surface就写surface。不要试图用通配符ungrib 不支持。还有一种情况ECMWF 的 SST 可能有两个层次值一个是 0一个是 1。这时候你要确认哪个是真正的海温。一般来说 level 0 是分析场level 1 可能是预报场或者别的。Vtable 里第 5 列填对应的 level 值。3.4 完整 Vtable 示例与逐行注释下面是我自己用的Vtable.ECMWF_SST的核心部分只列了海温相关的几行实际文件里还有温度、风、气压等! 注释行以感叹号开头 ! WRF中间名 | 参数号 | 短名 | 层次类型 | 层次值 | ... SST | 34 | sst | surface | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 T2 | 167 | t2m | surface | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 U10 | 165 | u10 | surface | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 V10 | 166 | v10 | surface | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 PSFC| 151 | msl | surface | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0每一行的第 1 列是 WRF 中间名这个不能乱改。SST就是SSTT2就是T2metgrid 认的就是这些名字。第 2 列参数号和第 3 列短名从grib_ls输出里抄。第 4 列层次类型也是从grib_ls输出里抄。注意PSFC那行我映射的是msl海平面气压这是 ECMWF 里最接近地面气压的字段。严格来说msl不是PSFC但在很多区域模拟里够用。如果你要做高精度气压场得用spsurface pressure参数号 134。4. 跑 ungrib 并验证 SST 是否真的进来了4.1 链接 Vtable 和 GRIB 数据Vtable 写好后在 WPS 目录下操作cd WPS ln -sf ungrib/Variable_Tables/Vtable.ECMWF_SST Vtable ./link_grib.csh /path/to/era5_sfc_*.griblink_grib.csh会在当前目录生成一堆GRIBFILE.AAA、GRIBFILE.AAB之类的软链接。确认链接数量跟你下载的 GRIB 文件数量一致。然后编辑namelist.wps重点改这几项share wrf_core ARW, max_dom 1, start_date 2020-01-01_00:00:00, end_date 2020-01-02_00:00:00, interval_seconds 21600, / ungrib out_format WPS, prefix FILE, /interval_seconds要跟你数据的时间步长一致。ERA5 如果是 6 小时一次就填 21600。4.2 跑 ungrib 并盯住日志./ungrib.exe ungrib.log跑完之后第一件事是看日志里有没有 SST 相关的报错。用grep -i sst ungrib.log如果看到类似SST not found in GRIB file说明 Vtable 没匹配上。如果看到SST found in GRIB file或者没有报错那大概率是进去了。更稳妥的办法是直接看输出文件。ungrib 生成的是FILE:2020-01-01_00这样的中间文件用rd_intermediate.exe查看内容./util/rd_intermediate.exe FILE:2020-01-01_00输出里会列出所有字段。找SST看它的层次、时间、数值范围。如果 SST 的数值范围是 270 到 310 开尔文左右那基本正常。如果全是 0 或者 9.99e30 这种填充值说明数据没进来。4.3 用 metgrid 输出反查 SSTungrib 跑完只是第一步真正要确认 SST 进了 WRF 的输入得看 metgrid 的输出。跑完 metgrid 后用 ncdump 看met_em.d01.2020-01-01_00:00:00.ncncdump -h met_em.d01.2020-01-01_00:00:00.nc | grep -i sst如果看到float SST(Time, south_north, west_east)这样的变量说明 SST 已经写进去了。再用ncdump -v SST met_em.d01.2020-01-01_00:00:00.nc | head -50看具体数值。如果数值在 270 到 310 之间且空间分布合理赤道暖、高纬冷那就没问题。如果全是 0 或者某个常数说明 ungrib 阶段 SST 就没进来或者 metgrid 的metgrid里fg_name没配对。提示metgrid 的namelist.wps里metgrid段的fg_name要跟 ungrib 的prefix一致。如果 ungrib 用的是FILEmetgrid 里也要写FILE。5. 几个我踩过不止一次的坑5.1 大小写和空格最不起眼但最致命Vtable 里第 4 列surface写成Surface或者多了一个空格ungrib 就匹配不上。这种问题在日志里不会明确告诉你大小写错了只会说not found。我一开始以为是数据问题重新下载了两遍 ERA5最后才发现是 Vtable 里一个字母的大小写。解决办法写 Vtable 的时候第 2、3、4 列全部从grib_ls输出里复制不要手打。复制完之后用cat -A Vtable看一下有没有多余的空格或者制表符。5.2 时间步长不一致导致 SST 错位ERA5 的 SST 在有些年份是 6 小时一次有些是 1 小时一次。如果你下载的时候没注意大气场是 1 小时一次SST 是 6 小时一次ungrib 按时间合并的时候SST 会被插值或者错位。结果就是某些时刻的 SST 是空的metgrid 里 SST 出现缺测。解决办法下载阶段就统一时间分辨率。如果已经下载了不一致的数据用 CDO 或者 eccodes 做时间插值把 SST 插到跟大气场一样的时间步长。cdo inttime,2020-01-01,00:00:00,1hour era5_sst_6h.grib era5_sst_1h.grib5.3 WPS 版本对 ECMWF GRIB2 的支持差异WPS 3.9 之前的版本处理 ECMWF GRIB2 数据经常出问题尤其是参数号匹配逻辑跟 GRIB1 不一样。我早期用 WPS 3.8 跑 ERA5 GRIB2ungrib 直接段错误日志里连报错都没有。后来升级到 WPS 4.0同样数据同样 Vtable一次跑通。如果你现在还在用 WPS 3.x建议要么升级要么把 ECMWF 数据转成 GRIB1。转 GRIB1 用 eccodes 的grib_setgrib_set -s editionNumber1 input.grib2 output.grib1但注意GRIB2 转 GRIB1 可能会丢失一些元数据转完之后要重新用grib_ls确认 SST 的短名和层次类型没变。5.4 metgrid 里 SST 被覆盖或者忽略有时候 ungrib 里 SST 明明有但 metgrid 输出里 SST 全是 0。这种情况通常是namelist.wps里metgrid段的constants_name或者fg_name配置有问题。比如你同时链接了一个SST的静态文件和一个FILE前缀的动态文件metgrid 可能会用静态文件覆盖动态的 SST。解决办法检查metgrid段确保fg_name只包含你 ungrib 生成的前缀不要混入其他来源的 SST 文件。如果确实需要静态 SST用constants_name单独指定并且确认它的优先级。6. 进阶让 SST 在 WRF 里真正起作用6.1 确认 WRF 的 namelist.input 里开了 SSTSST 进了 met_em 不代表 WRF 会用。namelist.input里physics段有几个开关跟 SST 相关physics sst_update 1, /sst_update 1表示 WRF 会从输入文件里读 SST 并随时间更新。如果你设成 0WRF 会用默认的 SST 或者忽略输入里的 SST。另外time_control里auxinput4_inname和auxinput4_interval也要配对这是 SST 更新的辅助输入流。具体配置取决于你用的是哪种 SST 更新方式。6.2 海温数据的空间分辨率匹配问题ERA5 的 SST 分辨率是 0.25 度而你的 WRF 域可能是 3 公里甚至 1 公里。metgrid 会把 0.25 度的 SST 插值到你的细网格上但插值方式会影响沿海地区的模拟效果。默认的插值可能是双线性在海岸线附近会出现 SST 平滑过度导致海陆温差偏小。如果你做的是沿海区域模拟建议在 metgrid 里把 SST 的插值方式改成最近邻或者更保守的方案。具体在namelist.wps的metgrid段里用interp_option指定metgrid interp_option nearest_neighbor, /但注意全局改成最近邻会影响所有变量最好只对 SST 单独设置。WPS 支持按变量指定插值方式具体语法参考 WPS 用户手册。6.3 验证 SST 是否真的影响了模拟结果跑完 WRF 之后怎么知道 SST 真的起作用了最直接的办法是对比两次模拟一次开sst_update 1一次关掉看沿海站点的 2 米温度差异。如果差异明显比如超过 1 度说明 SST 在起作用。另一个办法是看 WRF 输出里的SST变量。用 ncdump 看wrfout_d01_*ncdump -v SST wrfout_d01_2020-01-01_00:00:00 | head -30如果 SST 的空间分布跟你输入的 ERA5 SST 一致且随时间变化说明更新机制在工作。提示WRF 输出里的 SST 变量名可能是SST也可能是TSK地表温度。TSK在海面上就等于 SST在陆地上是地表温度。看的时候注意区分。7. 我个人的几个习惯和最后的小技巧折腾 ECMWF 海温数据这些年我养成了几个习惯分享出来可能对你有用。第一个习惯每次写新 Vtable 之前先花五分钟把 GRIB 文件的字段清单 dump 出来存成文本。命令是grib_ls -p shortName,paramId,levelType,level,dataDate,dataTime *.grib grib_fields.txt。这样写 Vtable 的时候直接查这个文本不用反复跑 grib_ls。而且以后换数据集对比一下字段清单就知道哪里变了。第二个习惯ungrib 跑完之后先别急着跑 metgrid先用 rd_intermediate 看一眼 SST 在不在。这一步花不了一分钟但能省掉后面 metgrid 跑完发现 SST 缺失再回头查的半小时。rd_intermediate 的输出里SST 的数值范围、层次、时间都一目了然。第三个习惯Vtable 里每一行都加注释。比如SST | 34 | sst | surface | 0 | ... ! ERA5 sea surface temperature, paramId 34这样过几个月再回来看或者把 Vtable 分享给别人的时候一眼就知道这行是干什么的。ECMWF 的参数号不是每个人都记得住注释能省很多沟通成本。最后一个小技巧如果你经常需要在不同数据源之间切换可以写一个简单的 shell 脚本自动根据 GRIB 文件的editionNumber和centre选择对应的 Vtable。比如#!/bin/bash centre$(grib_ls -p centre $1 | tail -1 | awk {print $2}) if [ $centre ecmf ]; then ln -sf ungrib/Variable_Tables/Vtable.ECMWF_SST Vtable elif [ $centre kwbc ]; then ln -sf ungrib/Variable_Tables/Vtable.GFS Vtable fi这样每次换数据源不用手动改 Vtable 链接少一步操作少一个出错的机会。ECMWF 的 centre 代码是ecmfNCEP 是kwbc这个在grib_ls输出里能看到。海温这东西在区域模拟里影响很大尤其是沿海和岛屿区域。Vtable 写对了SST 进来了后面的模拟才有意义。如果 SST 没进来WRF 会用默认值或者气候态跑出来的结果在沿海地区基本没法用。所以这一步值得多花点时间确认清楚。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进