这行字刚读完,我脑子里就开始自动过WRF的编译参数了。想当年第一次碰WRF,光装库就连着折腾了一周,最后跑通一个12小时的台风案例,看到d02里那个清晰的螺旋雨带时,整个人是长舒一口气的。用WRF这几年,从GFS当驱动跑到ERA5做再分析验证,从最简单的一场暴雨案例到后面动土地利用、改地形跑敏感性试验,中间填过的坑加起来可以写一本《模式报错日志》。这篇东西我尽量把从环境搭建到跑通案例,再到设计试验和用Python做诊断分析这条完整链路的关键点都梳理出来,里面包含了大量实测和踩坑后的调整思路,希望帮你少走一些路。
1. 搭建WRF模拟环境:核心不是装多快,而是想办法让依赖不打架
1.1 编译环境选型策略
很多新手一上来就问“WRF装在Windows还是Linux”,这个问题没有悬念:不要用Windows硬扛。WRF从4.0之后虽然也能在Windows Subsystem for Linux里跑,但IO性能和MPI稳定性都打折,后面做嵌套、跑长时间积分时你会非常痛苦。我的建议是直接用一台Ubuntu 20.04或22.04的物理机或云服务器,内存16G起步,32G比较舒服,磁盘给模式输出留个500G,后面你就知道WRF跑起来存储有多吃紧。
编译器选型上,很多教程一上来就推荐Intel编译器,说性能能高个十几二十个百分点,但如果你不是要跑控制实验跑一整年,用GCC加GNU Fortran就完全够用。原因很现实:Intel编译器需要申请免费的商业许可证,虽然个人使用不花钱,但它的环境变量、库关联经常因为版本更替出奇怪问题,而GCC的出错信息更直接,网上案例也多。我自己目前的环境是gcc/gfortran 9.4配OpenMPI 4.1,编译WRF 4.5,一年多没出过什么岔子。
依赖库的顺序建议是zlib -> hdf5 -> netcdf-c -> netcdf-fortran -> mpich或openmpi,每一步都要指定独立的安装前缀,别一股脑全丢进/usr/local。因为WRF对netCDF的版本非常敏感,如果系统里有多个版本的netCDF,链接时它自己会懵掉。为了方便版本隔离,我习惯在~/.bashrc里写清楚每一组环境变量,然后给每套依赖单独设一个root目录,比如~/libs/wrf_libs,这样切版本时只改几行export,不用重装系统依赖。
提示:编译WRF前一定要确认
$NETCDF环境变量指向的是netcdf-c的安装目录,而$NETCDF_FORTRAN指向netcdf-fortran的目录。只设置一个NETCDF变量的老教程在新版本里已经不完全适用了,最好两个变量都配上,否则configure阶段大概率会报找不到libnetcdff。
1.2 并行库选择和配套工具链
并行库我只推荐两种:MPICH或OpenMPI。用Intel编译器时用Intel MPI效率更好,用GCC时MPICH和OpenMPI都行。实际经验上,如果你是在单机多核上跑,MPICH更稳;如果是跨节点跑集群,OpenMPI在多网卡环境下更省心。绑定进程时我常看到有人用mpirun -np 36一条命令就冲出去了,完全没考虑CPU亲和性。模式节点多的时候进程互相抢缓存,速度反而会掉。建议用--map-by core --bind-to core这类参数来绑核。
配套工具链方面,除了WPS和WRF本体,有三样东西几乎每次都要用:
CDO:处理ERA5和GFS的GRIB数据非常方便,比如做变量提取、时间插值、区域裁剪。NCO:处理NetCDF文件的神器,尤其做变量重命名、维度合并时比Python脚本来得直观。Python环境:建议装Miniconda,然后单独建一个环境,这里放netCDF4、xarray、wrf-python、matplotlib、cartopy这些核心库。尽量别直接用系统自带的Python,很多气象库对版本敏感,conda可以帮你维护好依赖关系。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 驱动场数据怎么选怎么处理:GFS和ERA5的完整使用路径
2.1 两种驱动场的本质差异
刚开始跑WRF时我犯过一个大错误:把GFS和ERA5当成一回事,谁下载方便就用谁,后来做敏感性试验审稿人问“你初始场和边界场的时间分辨率是多少、有没有spatial mismatch”,我才回头认真研究差异。
GFS是NCEP的全球预报产品,属于实时运行的模式输出,每6小时更新一次,时间分辨率0到120小时一般是3小时间隔,120到384小时是12小时间隔,空间分辨率目前是0.25度。它的最大优势是时效性,适合做实时台风预报、个例回算,但对于历史事件研究来说,它背后是一个固定的业务模式版本,如果你回算2010年的台风,GFS数据用的是当年的模式版本,物理过程和现在不完全一样,这本身就会带来系统性误差。
ERA5是ECMWF的再分析产品,不是“预报”而是把历史观测同化进模式后得到的“最优估计”,水平分辨率约0.25度,时间分辨率到小时级。ERA5的优势是长时间序列均一性好,非常适合做气候尺度的敏感性试验和历史个例研究。它的代价是数据量大,下载一个完整年份的全球数据轻松破TB,即使只下载一个区域也需要做好时间切片和变量裁剪。
所以在案例选择上我给的建议很直接:模拟历史的台风暴雨事件就用ERA5,模拟近期或需要实时更新的事件就用GFS,两者不要混着用。混用会导致模式初始场来自A、边界场来自B,虽然理论上WRF也可以跑,但后续你做物理过程归因时很难说清楚差异到底是物理方案造成的还是驱动场不一致造成的。
2.2 GFS数据的处理流程
GFS的原始数据是GRIB2格式,下载地址比较多,我一般用NCEP官网的NOMADS服务器或者镜像站点用wget拉取,关键字大概是gfs.t00z.pgrb2.0p25.f000等系列文件,f000是初始时刻,后面的f006、f012代表预报时效。
拿到GRIB2后,WPS处理时链路是固定的:ungrib -> metgrid。ungrib这一步要做的核心工作是把GRIB2文件中的变量转成WRF中间格式(Intermediate Format),而让ungrib能识别GRIB2内容的钥匙叫Vtable。
GFS数据通常使用的Vtable是Vtable.GFS,就在WPS/ungrib/Variable_Tables目录下。操作时先链接Vtable到ungrib目录下:
bash复制cd WPS/ungrib
ln -sf Variable_Tables/Vtable.GFS Vtable
./link_grib.csh /path/to/gfs_files
./ungrib.exe
跑完ungrib后会在当前目录生成FILE:YYYY-MM-DD_HH这样的中间文件,然后进入metgrid阶段。metgrid需要先运行geogrid生成geo_em文件,再用metgrid.exe把ungrib生成的气象场插值到模拟区域的网格上。
GFS处理中容易被忽略的一点是:GFS的GRIB2文件里包含多种气压层和地表变量,ungrib会对每个变量做单位转换和量纲处理,如果你下载的文件不完整,比如缺失某些高空层,ungrib运行时会报Variable not found或Out of memory之类的问题。建议下载前先确认grib2文件命名里的0p25和f000字段是否匹配好,同时确保链接的文件列表里不混入重复的预测时效文件。
2.3 ERA5的更强处理技巧
用ERA5跑WRF有两条路子,一条是直接用ERA5的GRIB数据配合Vtable.ERA5做ungrib,另一条是把ERA5转成NetCDF再走中间格式。目前ERA5数据可以从CDS(Climate Data Store)下载,下载时如果选择了GRIB格式,直接配合Vtable.ERA5就能走通常规流程。
但ERA5真正爽的点在于可以自己挑变量打包下载,把需要的气压层变量和地面变量一次性拉下来,避免下载整球数据。通常驱动WRF需要的气压层变量包括位势、气温、u风、v风、相对湿度,有时还需要垂直速度;地面变量主要是海平面气压、2米温度、2米露点温度、10米u/v风、海表温度、土壤温湿度等。
下载时CDS会给你一个Python API脚本,我强烈建议把时间维度切成逐小时或逐3小时,不要下载时间分辨率过高的大文件。ERA5默认是逐小时,跑长时效模拟时边界更新频率太高,文件体积大,实际模拟精度提升并不明显。我之前用ERA5逐小时驱动做过测试,和3小时间隔的边界场相比,差别基本在可忽略范围内,但磁盘占用和IO压力却大了好几倍,后来就固定用3小时数据做长模拟。
CDS脚本样板大致是这样:
python复制import cdsapi
c = cdsapi.Client()
c.retrieve(
'reanalysis-era5-pressure-levels',
{
'product_type': 'reanalysis',
'variable': [
'geopotential', 'relative_humidity', 'temperature',
'u_component_of_wind', 'v_component_of_wind',
],
'pressure_level': [
'1000', '975', '950', '925', '900', '850',
'800', '700', '600', '500', '400', '300',
'250', '200', '150', '100',
],
'year': '2020',
'month': '08',
'day': '01',
'time': '00:00',
},
'era5_pressure_20200801.grib')
如果你的ERA5下载成了NetCDF格式,需要额外用CDO或NCO做格式转换,把时间变量转换成WRF能识别的hours since格式,并把经纬度dimension顺序调好。这个环节比较烦,但只要转成功一次并写好脚本,以后碰到大批次同规格数据就可以沿用。
bash复制cdo selvar,t,pres,rho,uwind,vwind ... input.nc output.nc
具体要转哪些变量取决于你下载时的命名,EWA5的NetCDF变量名和WRF中间格式不一定一致,所以需要频繁使用ncdump -h查看变量命名和单位。这里有个通用建议:下载时默认选CDS提供的变量名,如果命名里包含u_component_of_wind这种,就用Python预处理脚本统一重命名为U、V、T、GPH,再写进WRF中间格式,能省掉很多麻烦。
3. 跑通台风与暴雨个例:WPS和WRF配置的实操细节
3.1 模拟区域设计
案例区域的d01和d02选多大是很多人纠结的第一个问题。以台风案例为例,d01至少要把台风整个环流和外围雨带包进去,通常取东西向1500到2000公里,南北向1200到1800公里,网格距10到12公里左右。如果研究台风路径转折,范围要更大,否则路径受边界影响明显,越靠近边界区域误差越大。
d02的放置原则是紧盯关键天气系统中心。台风案例切不可把d02固定在一个地方,因为台风在移动,固定d02会导致模拟后半段主要目标移出高分辨率区域。WRF从4.3版本开始加入了移动嵌套(moving nest)功能,namelist.wps里可以用max_dom控制,在&time_control中设置grid_fdda等选项。不过我自己的实践是,如果模拟时间不超过48小时,直接静态嵌套加上扩展d02范围也能接受,但模拟超过72小时且台风移动路径长,还是建议使用移动嵌套功能。
暴雨案例的d01设计就不太一样。梅雨锋或华北暴雨这类过程,天气尺度强迫大多来自中纬度槽和低空急流,d01不需要太大,但d02的网格距往往要压到3公里以下才能看到对流系统的组织化发展。我用4公里网格距跑过一场特大暴雨,雨带位置和强度都还行,但局地极端降水中心偏弱,后来换成2公里并搭配微物理方案调整,雨团的发展和obs站点对比就贴近了很多。
WPS的namelist.wps核心配置我给个模板:
code复制&geogrid
parent_grid_ratio = 1, 3, 3
i_parent_start = 1, 40, 55
j_parent_start = 1, 30, 40
e_we = 200, 250, 300
e_sn = 180, 200, 240
geog_data_res = 'default','default','default'
dx = 12000
dy = 12000
map_proj = 'lambert'
ref_lat = 25.0
ref_lon = 120.0
truelat1 = 25.0
truelat2 = 40.0
stand_lon = 120.0
/
注意i_parent_start和j_parent_start指的是d02左下角在d01网格中的起始索引,不是经纬度,很考验人。计算方式可以这样想:d01第i个网格点对应的经度是ref_lon + (i - moad_known_i) * dx,用这个公式反推d02的起点索引就行。以前我总在网格位置设置上翻车,模拟出来的d02经常偏到海上,后来先画一张d01的网格图,把台风目标位置标注出来,再用Python或者直接用plotgrids.exe工具确认d02的位置,基本不会再跑偏。
3.2 namelist.input参数踩坑记录
WRF运行的核心配置在namelist.input,这个文件可以说是整个模拟工程的中枢。几个关键模块我分开说。
&time_control里必须盯死start_year、end_year这些日期参数和GFS或ERA5数据的时间覆盖范围严格对齐,刚开始运行real.exe时会检查数据时间,时间不匹配会直接报错。interval_seconds对GFS实例一般设21600(6小时),对ERA5实例设10800(3小时)也可以,网格嵌套时父网格和子网格的时间更新频率自动按网格比调整,这个参数只是读取驱动场的频次。
&domains里最关键的是time_step。WRF时间步长的经验公式是:time_step <= 6 * dx,其中dx单位是公里,结果单位是秒。如果d02网格距是4公里,理论上最大时间步长不超过24秒,保守起见我常用18秒。很多人为了省运行时间直接把这个参数放大,结果积分没几步就CFL报错,整个作业崩溃,前功尽弃。
&physics模块的选择直接决定案例模拟的成败。微物理方案我用最多的组合:
- 台风案例:WSM6或Thompson方案,搭配YSU边界层和Noah陆面过程。台风暖心结构和螺旋雨带对微物理过程的粒子分档比较敏感,Thompson整体表现更均衡。
- 强对流暴雨案例:如果网格距小于3公里,建议用WSM6或Morrison双参数方案,关闭积云参数化,因为网格已经能显式解析对流;如果网格距大于9公里,必须打开积云参数化,否则对流发展严重不足。
关于积云参数化还要提一句:有经验的模式人员都知道“灰色区域”(gray zone)这个词,3到9公里网格距之间对流的显式和参数化都不完全成立,这个区间出来的降水对方案选择极其敏感。我曾经在6公里网格距下对比过开着和关掉积云参数化的结果,两者6小时累计降水量可以差出60%以上。如果论文要研究降水机制,建议要么老老实实上2到3公里云分辨模拟,要么谨慎处理方案组合并多做一组对照,否则很难说服审稿人。
&fdda是另一个经常被忽略但关系重大的模块。RRTMG辐射方案和SURFACE_LAYER方案之间的配合相对稳定,但FDDA(分析松弛逼近)如果不设置,边界条件对模拟的约束松弛,长时间积分会产生漂移。网格尺度FDDA里面grid_fdda一般只用d01,d02如果开分析nudging,会产生“人为去除中尺度发展”的副作用,反而抹掉了对流信号。我建议:短期模拟24小时内可以不开FDDA,跑10天以上的长期模拟必须给d01开分析nudging,并且只对风场、温度和湿度变量做u、v、t、q的nudging,不要输出倾向项。
3.3 real.exe和wrf.exe运行技巧
运行real.exe前先做两件事:第一件事检查met_em文件是否存在,文件名里的日期要和namelist.input一致;第二件事把整个met_em文件列表和namelist.input里的max_dom一一对照,确保每个域都有完整时间序列的met_em文件。
real.exe运行时一个常见错误是Error in real: check for consistent data,通常是父域和嵌套域的时间覆盖不一致引起的,比如d01有所有时次,d02只生成了部分时次。检查方式是打开模式的rsl.out.0000日志,搜索ERROR关键词。
wrf.exe运行时最让人头疼的是CFL报错直接中断。CFL violation本质上是时间步长太大或局地风速太强导致数值不稳定,但也存在另一种情况:地形过于陡峭且垂直坐标拉伸太快。如果报错集中在大地形区域,可以试试调整epssm(在&domains中,默认0.1,适当调大到0.2)或减少time_step。如果报错集中在强对流发展核心区,优先考虑关闭积云参数化或更换更适合的微物理方案。
模式输出时间的设置也有讲究。history_interval决定wrfout文件的输出频率,跑72小时模拟时如果每小时输出一个完整wrfout,24个变量、三个域,总文件体积可以轻松突破50G。研究台风路径需要高频输出(比如逐小时输出海平面气压和风场),研究降水过程只需要逐小时甚至逐3小时输出就够,而做热力学诊断分析时还需要在&time_control里把需要额外输出的变量通过io_form_auxhist2和auxhist2_interval等配置加上。前期在namelist.input里定义好io_form_history等参数能省很多空间。
4. 敏感性试验设计的工程正确姿势
4.1 修改土地利用和地形文件
当你想回答“城市扩张对局地暴雨有什么影响”这类问题时,核心思路是改变模式中的下垫面状态。WRF默认的静态地理数据来自MODIS或USGS,在WPS的geogrid阶段会生成geo_em.d01.nc这类文件。修改土地利用的常用手法是直接改geo_em文件里的LU_INDEX或LANDUSEF变量。
最直接的方法是先用Python(netCDF4库)读取geo_em文件,选一个目标区域,把该区域内的LANDUSEF全部替换成城市类别,或者修改LU_INDEX为城市类型编号。注意MODIS分类里的城市类别编号是13,USGS分类里则不同,替换前必须确认你geogrid用的是什么数据源,否则改动无效。
分享一下我改土地利用时的经典代码片段:
python复制import netCDF4 as nc
ds = nc.Dataset('geo_em.d02.nc', 'a')
lu = ds.variables['LU_INDEX'][0]
# 假设模拟区域某块区域经纬度范围对应网格索引 i_range, j_range
# 把该范围内所有格点设为城市类别(MODIS=13)
lu[j_start:j_end, i_start:i_end] = 13
ds.variables['LU_INDEX'][0] = lu
ds.close()
这里有一个很隐蔽的坑:LU_INDEX是用于显示当前主要类别,但模式运行时实际读取的是LANDUSEF中的多类别百分比,然后根据最大比例决定网格归属。因此在做城市敏感性试验时,只改LU_INDEX并不能百分之百确保陆面过程使用城市类别,还需要同步把LANDUSEF里城市类别的比例改为接近1.0,同时把其他类别比例压低。我遇到过一次只改LU_INDEX后跑出来的2米温度完全没有城市热岛信号,排查了半天,根源就在这。
地形修改也类似。如果你想做个理想化试验,例如把某个山脉高度降低30%,直接用Python修改geo_em文件里的HGT_M变量就行,但修改之后必须注意两点:一是修改后的地形会造成气压梯度力变化,初始场和气象驱动场仍基于真实地形,那模式积分初期会有spin-up过程的适应调整;二是如果地形修改幅度太大,CFL不稳定的概率会显著增加,建议降低时间步长或者做平滑过渡,比如在边缘处使用渐变而不是一刀切。
4.2 物理参数化敏感性试验设计
WRF跑敏感性试验最常见的是微物理方案、积云参数化、边界层方案这三个维度。这说起来简单,但实际设计对照实验时容易忽略几个隐蔽干扰源。
第一,单变量原则贯彻问题。比如研究微物理方案A和微物理方案B对降雨的影响,必须保证辐射方案、边界层方案、积云方案、陆面过程完全一致。这个道理大家都懂,但实际运行过程中,如果模式因为在某个方案组合下不稳定而中途崩溃,新手容易下意识地顺手改了其它设置来救场,这样出来的结果就没有说服力了。
第二,spin-up时间的一致性。不同物理方案对初始场的适应时间不一样,如果每个试验都从同一时刻起步,一般需要进行前6到12小时的spatial spin-up分析,确定模式进入相对稳定状态后再开始统计。如果试验A的第0到6小时还处于剧烈调整期,试验B已经平稳,直接算差异会误导归因。
第三,扰动的一致性问题。WRF的初值在同化后可能包含微小的湿度、温度扰动,如果不同试验中运行real.exe时使用的随机种子不一致,后续差异很可能来自初始随机扰动而不是物理方案本身。处理办法是给每个试验固定namelist中的seed相关参数,或者直接在同一份wrfinput基础上运行不同物理方案。
注意:单点敏感性试验分析结果时务必要区分“统计显著性”和“物理意义”。一次个例模拟中模式A比模式B多下了20毫米雨,不代表方案A在气候平均上优于方案B,它可能只是对流触发的位置不同导致的系统性偏移。有条件的话可以把试验扩展到多个相似个例,或者至少做一个时段滑动分析,避免单次事件的偶然性主导结论。
4.3 试验矩阵管理与批量实现思路
做敏感性试验前我建议先画一个试验设计矩阵表,列清楚每个试验的驱动场、物理选项、初始时刻、模拟时长、输出频率,以及唯一的发生变化项。这个简单操作帮我在文章返修时节省了大量时间,审稿人问“你试验C用的是哪个边界层?”,我直接翻表就能回答,不需要一个个去看namelist历史记录。
批量运行的工程方法也很重要。写一个bash脚本定义所有试验目录和namelist模板,用sed批量替换关键参数,再用一个循环把所有WRF运行提交到后台。我的标准目录结构大概是下面这种形式:
code复制case_study/
├── exp_ctrl/
│ ├── namelist.input
│ ├── namelist.wps
│ └── wrfout/
├── exp_lu_city/
├── exp_micro_wsm6/
├── exp_micro_thompson/
└── exp_pbl_ysu/
这种结构最大的好处是在统计分析时可以很轻松地用Python同时读取多个试验目录里的wrfout文件,不需要一个个找路径。批量提交时注意不要让两个试验在同一个目录里运行WRF,否则会互相覆盖wrfout文件,那种事故一旦发生基本要全部重跑。
5. 结果诊断与Python实现:让数据说话
5.1 Python环境的配置和常用数据处理思路
WRF输出文件虽然是NetCDF格式,但它不是标准NetCDF那么简单。它的经纬度是随地形和投影变化的二维数组,气压也是三维扰动场,所以直接读文件画图会出很多尺寸错误。用Python处理WRF输出有两个主力方向:一是用wrf-python库做诊断和插值,二是用xarray配合cftime和cfgrib等库来做数据处理。
wrf-python是WRF官方推荐的Python工具,前面提到的Python环境建设里提过,强烈建议在Miniconda一个独立环境里安装。安装时直接用conda install -c conda-forge wrf-python通常最顺利,因为需要编译的组件都预编译好了,而pip安装在某些平台上可能因为gfortran库版本不一致而失败。之前看到一个学弟用pip装失败,折腾了半天最后换conda一下就装好了,所以我后来一直推荐conda方式。
如果环境里还没装,可以参考这个步骤:
bash复制conda create -n wrf python=3.9
conda activate wrf
conda install -c conda-forge wrf-python netcdf4 xarray matplotlib cartopy
5.2 用Python读取WRF输出做降水分析
读取wrfout最常用的方式是wrf-python里的getvar函数,它把很多在WRF内部才有定义的变量(比如T2、RAINC、SLP等)转变成了标准气象变量。统计降水时需要对RAINC和RAINNC做累计差值,而不是直接读某个时刻的瞬时值,这一点对新手很关键。
我来写一个非常实用的累计降水提取示例,注意RAINC是对流参数化产生的累计降水,RAINNC是显式微物理方案产生的累计降水,wrfout里存的都是自模拟开始以来的总累计量,不是每个时刻的增量。
python复制import netCDF4 as nc
import numpy as np
file_in = 'wrfout_d02_2020-08-01_00:00:00'
file_out = 'wrfout_d02_2020-08-01_12:00:00'
nc_in = nc.Dataset(file_in)
nc_out = nc.Dataset(file_out)
rainc_in = nc_in.variables['RAINC'][0]
rainnc_in = nc_in.variables['RAINNC'][0]
rainc_out = nc_out.variables['RAINC'][0]
rainnc_out = nc_out.variables['RAINNC'][0]
total_precip = (rainc_out + rainnc_out) - (rainc_in + rainnc_in)
lat = nc_in.variables['XLAT'][0]
lon = nc_in.variables['XLONG'][0]
这个小脚本可以应付大部分累计降水需求。如果要做降水空间分布图,再把total_precip加上经纬度画contourf即可。风场、温度场类似,只是要记得wrf-python里的getvar能直接给出更专业的物理量,比如用slp = getvar(ncfile, 'slp')提取海平面气压,tk = getvar(ncfile, 'tk')提取气温,ua = getvar(ncfile, 'ua')提取纬向风。
5.3 wrf-python画台风路径与降水雨带
台风的路径很好提取,只要寻找每个输出时次海平面气压最小点的经纬度即可。为了方便,可以用一个循环读取时间序列上的wrfout文件,再用np.unravel_index(np.argmin(slp))找到最低压位置。需要注意,如果模拟区域内存在陆地上的中尺度低压系统,它可能比台风中心更低,所以要做半径搜索,限制在距离上一次台风中心位置一定半径内寻找最小值。不然画出的路径会出现“跳跃点”,看起来像台风瞬移了一样。
给一个搜索算法的通用思路:
python复制from wrf import getvar, latlon_coords
def find_center(slp, lat, lon, prev_lat, prev_lon, search_radius_deg=3.0):
# 计算每个格点到上时刻中心的距离,然后只在这个范围内找slp最小值
dist = np.sqrt((lat - prev_lat)**2 + (lon - prev_lon)**2)
mask = dist <= search_radius_deg
idx = np.unravel_index(np.where(mask, slp, np.nan).argmin(), slp.shape)
return lat[idx], lon[idx]
降水的可视化层面,用cartopy画带地形底图的降水填色图是最常见的。要注意的是如果你画的是d02的高分辨率结果,直接用经纬度等距坐标会和实际投影相差很大,建议使用cartopy.crs.PlateCarree()作为目标投影并设置适当的transform,或者参考wrf-python官方文档里的cartopy_xlim和cartopy_ylim来限定区域。
5.4 敏感性试验对比分析:差异场比绝对场更重要
做敏感性试验的对比时,不要只画每个试验各自的降水分布,那看不出差异,推荐画“试验A减试验B”的差异场,并配合显著性分析。这个小技巧在写论文时特别重要,审稿人一眼就能抓住物理过程变化的本质。
一个常规操作是取多个输出时次做时间平均,再做空间差异图。如果把“城市扩展影响降水”作为科学问题,差异场能直接显示降水增强或减弱的信号集中在城市下风方向还是城市中心,这个空间信号是解释机制的关键证据。
但必须再次重申,单次个例的差异场哪怕形态很漂亮,也很难排除天气尺度随机性。所以在画差异场的同时,建议对比一下控制试验和敏感性试验的模拟路径或系统位置,确认不是由于系统位置的微小偏移导致的降水带移动。如果真的要量化这种不确定性,做多组扰动试验并对差异场做t检验或bootstrap分析,形成显著性打点图,会更有说服力。
6. 运行调试中的黄金经验
忍不住分享几个使用频率极高的经验。
关于ERA5数据驱动,我最常犯的错是时间覆盖差一小时。ERA5是逐小时数据,下载时通常选time: 00:00到23:00,如果模拟开始时间是08:00,驱动场必须包含08:00这个时次。但有时CDS的下载脚本默认会只保留整点,如果不仔细检查,ungrib生成的FILE文件会少一个时次,metgrid阶段可能不会立刻报错,但real.exe会因为找不到边界层数据而异常退出。每次遇到real.exe的奇葩报错,先检查数据时间覆盖,往往问题出在这里。
关于运行效率与IO问题,WRF写入大量输出时容易出现“磁盘忙”的假死状态,特别是当你在机械硬盘上跑高分辨率嵌套的时候。建议固定输出目录并使用高性能SSD作临时输出区,后期再把需要的wrfout传输到机械硬盘存档。
有个细节可能只有跑过长时间模拟的人才知道:wrfout文件里的时间变量会以“minutes since”格式存储,如果模拟跨了月份,时间解析必须使用cftime库而不是标准datetime,否则跨月的日期计算会错上一天。
关于移动嵌套,我建议一般研究天气系统的学者慎用。因为它虽然能让你一直用高分辨率跟随系统中心,但嵌套边界上的不适配问题较多,需要不断测试反馈。静态嵌套虽然计算量略大,胜在稳定性高,适合新手。
关于真实业务场景中的台风路径模拟,我实测下来比较有效的做法是,把驱动场从GFS换成ERA5之后再做一轮对比,因为GFS的初始场有时会和台风最佳路径存在明显偏差,尤其在大尺度引导气流较弱、台风内力占主导时。ERA5同化后的初始场更接近实况,但计算成本高,用哪个方案取决于你更关注“实时可复现”还是“更接近历史实况”。这个取舍没有标准答案,针对不同案例做预实验才是正道。
最后分享一个实用技巧:wrfout文件特别多的时候,建议写一个小的Python文件管理所有模拟试验的元信息,包括每个试验使用的namelist.input、namelink的Vtable名称、物理方案组合等。这种东西叫“实验记录”,比论文里的method部分更全面,等过半年你想重新做分析发现记不清细节时,就知道这步有多值钱了。我自己用了这个办法之后,几乎没有再遇到过“忘了这个试验当时怎么设定”的情况。
