简介:数值天气预报输出的NetCDF文件包含海量物理量,科研与工程项目中常需提取变量、插值到等压面并完成可视化。此类后处理依赖wrf-python等Python库,而其Fortran扩展在安装时对编译器和NetCDF依赖要求严格,常成入门障碍。掌握依赖管理、conda环境隔离以及基于cartopy的投影绘图流程,可显著提升WRF数据使用效率。本文从源码包特征出发,解析安装避坑要点,给出读取变量、等压面插值、站点坐标转换和批量出图的完整方案,帮助气象与环境领域从业者快速上手wrf-python,打通从模式输出到业务图表的最后一公里。
1. 从 tar.gz 到第一张 WRF 图:为什么最卡的是安装
做气象、环境预报或风资源评估的人,对“wrfout_d01_……”这一串 NetCDF 文件八成不陌生。WRF 模式跑完后,几百个变量和几十个时次堆在一起,要读其中某个物理量、插到 850hPa,再配一张带地图投影的图,前后处理的时间经常比模式运行时间还长。wrf-python 1.1.3 就是用来顶替这段手工劳动的库:它提供读取 WRF 输出、去交错、等压面插值、诊断量计算和 cartopy 投影的一整套 Python 接口。但这个库有个特点:作为 tar.gz 源码包,它需要在目标机器上现场编译 Fortran 扩展,本机编译器和 NetCDF 库缺一不可,不少人在安装阶段就被劝退。这篇笔记从下载包开始,把依赖、安装、读取、插值、出图和常见报错一次讲完。
2. 安装前先做三个判断:版本、解释器、依赖从哪来
2.1 认清 tar.gz 里的东西:Python 源码、Fortran 扩展和一堆依赖
wrf-python-1.1.3.tar.gz 在 PyPI 上属于 sdist(源码发行包),不是 wheel。它里面包含 Python 源代码、setup.py,以及用 Fortran 写的数值内核。以 1.1.3 发布时的生态来看,这套内核需要 f2py 在目标机器上现场编译,系统里必须同时有 C 编译器、Fortran 编译器和 NetCDF 的 C/Fortran 两套库。漏掉任何一项,都会在 pip install 阶段以奇怪的链接错误收场。
我一般会先跑一遍下面的命令,把关键依赖的版本一次性看清:
# 查看编译器是否可用 gcc --version gfortran --version # 查看 NetCDF C 库 nc-config --all # 查看 NetCDF Fortran 库(这个最容易漏装) nf-config --all说明:gcc 和 gfortran 是编译 Fortran 扩展的基础;nc-config 来自 netcdf-c,nf-config 来自 netcdf-fortran,二者返回的头文件路径与库路径会写进 setup.py 的构建参数。如果 nf-config 不存在,源码安装基本注定失败,这是最常见的一处坑,后面避坑章会再细说。
常见发行版的安装命令如下,命令行参数的含义就是让系统把开发头文件一并装上,避免“库找到了但头文件找不到”的尴尬局面:
# Ubuntu / Debian sudo apt install gcc gfortran libnetcdf-dev libnetcdf-fortran-dev # macOS 用 Homebrew 时 brew install gcc netcdf netcdf-fortran此外,wrf-python 1.1.3 在 Python 端的依赖一般包含 numpy、scipy、matplotlib、netCDF4、xarray、cartopy、wrapt、six。重量级的是 cartopy,它会带着 geos、proj、shapely、pyproj 一起装。所以安装思路很明确:能用 conda 让依赖统一解析,就不要让 pip 盲目拉取“当前最新版”,否则 cartopy 新版本和 1.1.3 的兼容性会变成新的难题。
依赖项可以按下表核对:编译器看 gcc/gfortran,读 NetCDF 看 netcdf-c,Fortran 接口看 netcdf-fortran,Python 侧则让 conda 或 pip 自动解析。检查的重点其实是 netcdf-fortran,很多人装完 netcdf-c 就以为万事大吉,结果编译到一半才报错。
| 依赖 | 检查命令 | 缺失时的典型后果 |
|---|---|---|
| GCC | gcc --version | f2py 无法编译 C 部分 |
| GFortran | gfortran --version | Fortran 内核无法构建 |
| netcdf-c | nc-config --all | 找不到 netcdf 头文件 |
| netcdf-fortran | nf-config --all | 链接时找不到 libnetcdff |
| Python 3.8 | python --version | 1.1.3 在 3.8 下最稳,3.9+ 要额外折腾 |
2.2 优先用 conda 建隔离环境,pip 源码安装做后手
实际项目里,我几乎不会直接把 wrf-python 装进 base 环境。因为同一个机器上还可能有 TensorFlow、GPU 驱动对应的 numpy 版本,wrf-python 又对 numpy 和 cartopy 有版本要求,混在一起很容易把环境搞坏。常见做法是单独建一个干净环境,锁死 Python 版本:
# 创建 Python 3.8 的独立环境 conda create -n wrf python=3.8 -y # 进入环境 conda activate wrf # 从 conda-forge 安装 wrf-python 1.1.3 conda install -c conda-forge wrf-python=1.1.3 -y解释一下参数:python=3.8是刻意指定的,wrf-python 1.1.3 发布时主要验证到 3.8,用 3.8 能少踩 f2py 在更新解释器上的兼容坑;-c conda-forge告诉 conda 去社区频道找预编译包,而不是自己从源码构建。conda 会把 netcdf4、cartopy、numpy 等依赖按频道内互相兼容的版本统一解析,这是 pip 做不到的。
只有以下几种情况我才会用 tar.gz 源码安装:内网机器无法访问 conda 频道,或者公司要求所有 Python 依赖必须走 pip 的私有源。源码安装的前提是 2.1 节那条检查命令全部通过,然后执行:
# 如果 nc-config/nf-config 不在 PATH 里,先指一下目录 export NETCDF_DIR=/usr/local # 在当前目录下直接构建安装 pip install wrf-python-1.1.3.tar.gzNETCDF_DIR 的作用是告诉 setup.py 到哪里去找 NetCDF 的头文件和库文件。常见错误是只设了 NETCDF_DIR 却忘了 Fortran 接口,或者系统里有多个 NetCDF 版本导致路径指向混乱,这些坑在第五章会专门讲。
2.3 内网机器或离线环境:先下载全部依赖再分发
科研内网经常不让直接访问外网包源,此时分两步走。第一步在外网开发机上用 pip download 把源码包和依赖都拉下来:
# 把 wrf-python 及其依赖下载到指定目录 pip download wrf-python==1.1.3 --no-deps -d ./wheels pip download cartopy==0.18.0 netCDF4==1.5.3 -d ./wheels第二步把整个 wheels 目录拷到内网机器,然后离线安装:
# 离线安装时禁止访问外网索引,只从本地目录找包 pip install wrf-python-1.1.3.tar.gz --no-index --find-links=./wheels这里的血泪经验是:直接把 tar.gz 拷到内网机器再运行 pip install 不算完整方案,因为 pip 一旦发现缺依赖,仍然会去外网索引查询,超时后整体失败。只有把依赖全部备齐,离线安装才顺畅。如果内网允许用 conda,更舒服的方式是外网机器上conda-pack整个 wrf 环境打包过去,但那属于环境迁移,不是安装,适合一劳永逸的场景。
装完以后验证一下:
# 验证导入和版本 python -c "import wrf; print(wrf.__version__)"能看到 1.1.3 且不报 ImportError,说明基础安装完成。此时再拿一个 WRF 输出文件做冒烟测试,确认能读到变量:
python -c "from netCDF4 import Dataset; import wrf; print(wrf.getvar(Dataset('wrfout_d01_2019-07-01_00:00:00'), 'T2', timeidx=0).shape)"输出一个二维 shape 才对,例如(399, 399)或(100, 100),代表南北和东西方向格点数。如果这一步就报错,说明读取层有问题,继续往下看第三章。
3. 读取 WRF 输出并插值到等压面:最小可运行代码
3.1 用 netCDF4.Dataset 当入口,别把 xarray 直接塞进去
wrf-python 的 getvar 接口对输入对象有要求:它认 netCDF4 的 Dataset 对象,也认 PyNIO 的 NioFile,但不认裸的 xarray DataArray。这一点是新手最容易翻车的地方。很多人用 xarray 打开 wrfout 文件后,顺手把 DataArray 传给 getvar,结果报错或返回的坐标信息缺失。
正确做法是明确以 netCDF4.Dataset 打开文件:
from netCDF4 import Dataset import wrf # WRF 输出文件通常以 wrfout_ 开头 ds = Dataset("wrfout_d01_2019-07-01_00:00:00") # 读取 2 米温度,timeidx=0 表示取文件的第一个时次 t2 = wrf.getvar(ds, "T2", timeidx=0) # 返回值是 xarray DataArray,带维度、单位和投影信息 print(t2.shape) # 二维,(south_north, west_east) print(t2.units) # K print(t2.dims) # 能看到维度名,确认没有意外排序 ds.close()参数说明:timeidx=0取第一个时次,timeidx=-1取最后一个时次;对于多层变量,返回的数组是三维(bottom_top, south_north, west_east),二维变量则是(south_north, west_east)。注意这里变量名 “T2” 是 wrf-python 事先定义好的短名,不是文件里的原始变量名。原始变量名可能是T2,但更多时候像U、V、PH这种需要去交错和计算才能用的量,wrf-python 在 getvar 内部帮你处理了,所以别自己去文件变量列表里找同名变量。
ds.close() 这行不能省。尤其后面批量处理几十个文件时,不关闭句柄会导致文件描述符耗尽,报 too many open files。多进程场景下更要小心,每条进程都要独立打开文件,不能把一个 Dataset 对象传递给子进程,这个问题第五章会展开。
3.2 一次 getvar 拿到三维场,再用 interplevel 插到等压面
读取单个二维变量只是热身。真正提高效率的是读取完整三维气压场和风场,然后一次性插值到想要的等压面。WRF 原始文件里的竖向坐标是地形追随坐标,气压层不是现成的,需要先计算气压,再做垂直插值。
最小代码长这样:
from netCDF4 import Dataset import numpy as np import wrf ds = Dataset("wrfout_d01_2019-07-01_00:00:00") # 读取完整气压场(Pa),三维 p = wrf.getvar(ds, "pressure", timeidx=0) # 读取水平风场,ua/va 是 WRF 质量格点上的风分量 ua = wrf.getvar(ds, "ua", timeidx=0) va = wrf.getvar(ds, "va", timeidx=0) # 插值到 850hPa,注意单位是 Pa,不要写成 850 u_850 = wrf.interplevel( field=ua, vert=p, desiredlev=85000.0, missing=wrf.default_fill(ua.dtype), ) v_850 = wrf.interplevel( field=va, vert=p, desiredlev=85000.0, missing=wrf.default_fill(va.dtype), ) # 合成风速 wspd_850 = np.sqrt(u_850**2 + v_850**2) print(wspd_850.shape) ds.close()逻辑说明:pressure变量由 wrf-python 根据 WRF 的气压诊断关系计算得到,单位 Pa,不是 hPa;ua和va是从交错的 U、V 场去交错后的质量格点风,单位 m/s。interplevel 把三维场沿垂直方向线性插值到指定气压层,核心参数有三个:field是被插值的三维场,vert是同 shape 的三维气压场,desiredlev是目标气压值,必须和 vert 单位一致。
参数说明里最容易错的是missing。很多人习惯写np.nan,但 Fortran 内核内部用 NetCDF 的_FillValue语义处理缺测,类型不匹配时容易踩雷。wrf-python 提供了wrf.default_fill(dtype),它会按数据类型返回合适的填充值,float 场就用-9999.0这类特定值,绘图前用wrf.to_np()转回 numpy 数组时再处理掩膜。
另一个常见问题是乱传维度:如果 field 是(Time, bottom_top, south_north, west_east)的完整四维场,但 vert 只取了某个 timeidx 的三维场,shape 对不上,interplevel 会报维度错误。getvar 返回的 timeidx 指定结果已经是去掉 Time 维的三维数组,所以这里不需要再加索引。
3.3 经纬度坐标转换:用 ll_to_xy 匹配站点位置
WRF 网格的索引和经纬度不是一一对应关系,因为大多数 wrfout 都采用 Lambert 投影或极地投影。做站点验证时,你需要把一个站点的经纬度换算成网格索引,才能提取该点的模式值;反过来,想知道某个网格点对应什么经纬度,就用 latlon_coords。
from netCDF4 import Dataset import wrf ds = Dataset("wrfout_d01_2019-07-01_00:00:00") t2 = wrf.getvar(ds, "T2", timeidx=0) # 获取每个网格点的经纬度 lats, lons = wrf.latlon_coords(t2) # 把站点经纬度换算为网格索引 x, y x, y = wrf.ll_to_xy(ds, 30.1, 115.6) print("网格索引 x, y:", x, y) print("对应经纬度:", lats[y, x], lons[y, x]) ds.close()参数说明:ll_to_xy的入口是 Dataset 对象,因为投影信息存放在文件的 global attributes 中,包括 MAP_PROJ、CEN_LAT、CEN_LON、STAND_LON 等,DataArray 里并不携带这些元数据,所以别拿 t2 去调用。返回的 x、y 是浮点索引,实际提取时要取整,或者用双线性插值获取更精确的站点值。
这里有一个容易忽略的边界坑:站点如果落在模拟区域外,ll_to_xy 不会报错,而是返回超出网格范围的索引。所以提取后要检查索引是否在0 <= x < lons.shape[1]和0 <= y < lats.shape[0]范围内。我习惯把这一步做成一个防御函数,索引越界时直接返回 None,避免后续数组越界引起整个脚本崩溃。这也是后面自动化批量处理能稳定跑完的前提之一。
4. 用 cartopy 把变量画成平面图:四个必调参数
4.1 四步套路:投影、范围、数据、图层
wrf-python 出图最大的优势是投影坐标不用自己算。它提供了get_cartopy、cartopy_xlim、cartopy_ylim三个函数,能从 DataArray 的元数据里直接生成 cartopy 的投影对象和范围。画一张等值线/填色图只需要按固定套路走:取投影、定范围、传数据、叠加地理要素。
import matplotlib.pyplot as plt import cartopy.crs as crs from netCDF4 import Dataset import wrf ds = Dataset("wrfout_d01_2019-07-01_00:00:00") # 读取海平面气压 slp = wrf.getvar(ds, "slp", timeidx=0) # 第一步:获取 cartopy 投影对象 cart_proj = wrf.get_cartopy(slp) # 第二步:用 wrf 自带函数取 x/y 范围 xlim = wrf.cartopy_xlim(slp) ylim = wrf.cartopy_ylim(slp) # 第三步:创建带投影的坐标轴 fig, ax = plt.subplots( 1, 1, subplot_kw={"projection": cart_proj}, figsize=(10, 7), ) # 第四步:叠加地图要素并填色 ax.coastlines(linewidth=0.8) ax.set_extent([xlim[0], xlim[1], ylim[0], ylim[1]], crs=cart_proj) # 把经纬度网格交给 cartopy,用 PlateCarree 变换到投影坐标系 lats, lons = wrf.latlon_coords(slp) cf = ax.contourf( lons, lats, wrf.to_np(slp), levels=20, cmap="Spectral_r", transform=crs.PlateCarree(), ) fig.colorbar(cf, ax=ax, shrink=0.8) fig.savefig("slp.png", dpi=150, bbox_inches="tight") plt.close(fig) ds.close()四个必调参数的核心作用:get_cartopy(slp)读取 DataArray 里的投影元数据,生成与 WRF 网格匹配的投影坐标轴,省去手写 Lambert 投影参数;cartopy_xlim和cartopy_ylim计算模拟域在投影坐标系中的物理边界,直接喂给set_extent,避免图幅过大或裁掉模拟域边缘;transform=crs.PlateCarree()告诉 cartopy,lons/lats 是以经纬度形式给出的原始数据坐标,必须经过投影变换;wrf.to_np(slp)则是把 DataArray 转成 numpy 数组,因为部分 matplotlib 版本对 xarray 的传递兼容性不够稳。
有个细节值得注意:这里没有直接用transform=cart_proj并只传二维数组,而是显式传 lons/lats 加PlateCarree。如果网格间距很小,两者视觉差异不大;但当你只传二维数组而不传坐标时,cartopy 会把数组索引当成投影坐标,容易得到一张偏移的图,这在新版 cartopy 里尤其坑。显式传经纬度虽然慢一点,但结果不会骗你。
4.2 批量出图时,timeidx 与文件句柄怎么管理
实际业务中不可能只画一张图。跑完一次 72 小时预报,至少 72 个时次,每个时次可能还要出风场、温度、气压多张图。批量循环时最容易犯两个错误:一是循环里反复调用Dataset()但从不 close,二是把同一个 Dataset 对象在多个函数间传递后忘记时次对应关系。
import glob from netCDF4 import Dataset import wrf # 按文件名排序,保证时次顺序 file_list = sorted(glob.glob("wrfout_d01_2019-07-01_*")) for idx, f in enumerate(file_list): # 每个文件单独打开,用完即关 ds = Dataset(f) # 取最后一个时次,通常是该文件的预报时刻 t2 = wrf.getvar(ds, "T2", timeidx=-1) # 这里可以继续做插值、绘图等操作 ds.close()逻辑说明:有些 WRF 输出是每 6 或 12 小时写一个文件,每个文件里包含多个累积时次;有些则是一个时次一个文件。用glob和sorted能保证文件读取顺序稳定。timeidx=-1表示取文件内最后一个时次,对单时次文件来说就是唯一时刻,对多时次文件来说正好是文件对应的预报时刻,这是最省心的一种写法。
批量出图还有一个经常出现的小麻烦:如果每张图的标题写的是 UTC 时间且长度不一,图下方的横坐标文字挤成一团。这个“python 画图横坐标太密集”的问题在时间序列图里尤其明显。处理方法有两种,一是把时间字符串转成 datetime 后用 matplotlib 的日期格式化器控制刻度间距,二是直接用fig.autofmt_xdate()旋转日期标签。后者一行代码能解决 80% 的密集问题:
from matplotlib.dates import DateFormatter ax.xaxis.set_major_formatter(DateFormatter("%m-%d %H:%M")) fig.autofmt_xdate()如果横坐标上不是日期,而是站点编号或文件名,更简单的做法是用ax.set_xticks([0, 5, 10, 15])手动限制定点数量,别让 matplotlib 自动塞满所有刻度。这一行代码在网格站点很多时非常管用。
5. 避坑:安装与运行中最常见的 5 个报错
5.1 源码编译报 cannot find -lnetcdff
现象:执行pip install wrf-python-1.1.3.tar.gz时,编译过程在最后链接阶段报错cannot find -lnetcdff,或者类似ld returned 1 exit status。
原因:-lnetcdff是链接器对 netcdf-fortran 库的缩写引用。系统里装了 netcdf-c 并不代表有 netcdf-fortran,后者是 NetCDF 的 Fortran 接口库,wrf-python 的 Fortran 内核需要它来完成 NetCDF 读写。
解决:先确认nf-config --version能正常输出版本;装不上就用系统包管理器安装,左面 2.1 节里那条 Ubuntu 安装命令已经包含。安装后再把NETCDF_DIR环境变量指向包含 include 和 lib 的公共前缀目录,例如/usr/local,避免 f2py 找不到头文件。
5.2 conda 环境里 import wrf 报 libnetcdff.so 找不到
现象:用 conda 安装后,执行import wrf报错,提示libnetcdff.so.6: cannot open shared object file或类似动态库缺失。
原因:conda 环境里的动态库搜索路径没有包含 netcdf-fortran 的库目录,或者安装时 conda 只装了 Python 包,没有把配套的 netcdf-fortran 库作为显式依赖拉进来。
解决:在同一个环境里补齐 netcdf-fortran,让动态库路径统一:
conda install -c conda-forge netcdf-fortran -y装完再用ldd检查一下 wrf 扩展到底依赖哪个路径的库:
python -c "import wrf; print(wrf.__version__)"如果还报错,可以考虑conda install -c conda-forge compilers,这会把与 conda 环境兼容的编译器工具链补齐,避免运行时链接到系统目录里版本不一致的 libnetcdff。
5.3 把 xarray 传给 getvar 报 Wrong number of dimensions
现象:用xr.open_dataset()读文件后,取某个变量传给wrf.getvar(),报错维度数不对,或者返回的数组维度和文件原始维度完全错乱。
原因:wrf-python 内核在解析输入时依赖文件级全局属性和维度顺序,xarray Dataset 虽然能读 NetCDF,但它并不在上面的接口协议里。直接传 DataArray 等于绕过了 wrf-python 预期的文件句柄逻辑。
解决:统一用netCDF4.Dataset打开文件再传给 getvar。如果项目里已经用 xarray 做了大量预处理,不要硬传给 wrf-python,先把需要的时次切片单独写成临时 nc 文件,再用 netCDF4.Dataset 打开,流程更稳:
import xarray as xr from netCDF4 import Dataset import wrf ds_xr = xr.open_dataset("wrfout_d01_2019-07-01_00:00:00") # 只保留一个时次,避免文件过大 ds_xr.isel(Time=0).to_netcdf("tmp_one_time.nc") ds = Dataset("tmp_one_time.nc") t2 = wrf.getvar(ds, "T2", timeidx=0) ds.close()5.4 多进程并发处理同一份 WRF 文件崩溃
现象:用 multiprocessing 池并行处理不同时次或不同区域时,子进程随机崩溃,报错信息指向 Fortran 层的段错误或非法访问。
原因:wrf-python 的 Fortran 扩展在 1.1.3 版本上并不是完全线程安全的,而且 NetCDF 的文件句柄也不能跨进程直接继承。多个进程同时读同一个 Dataset 对象,底层缓冲区和文件指针会互相覆盖。
解决:每个子进程内部独立打开文件,不要把父进程的 Dataset 当参数传进去。常见做法是子进程只接收文件路径,打开文件、处理、关闭全在进程内完成:
def process_one(path): ds = Dataset(path) # 必须在子进程内打开 t2 = wrf.getvar(ds, "T2", timeidx=-1) ds.close() return t2.mean().values如果每个任务还要读取区域附近的多层变量,尽量让输入区域小一点。Fortran 侧在处理大数组时会做很多内部复制,内存占用往往是 numpy 数组自身的两三倍,这也是并发场景下容易 OOM 的一个隐藏原因。
5.5 pip 和 conda 混装后,cartopy 版本被拉高,绘图行为异常
现象:conda 环境里已经装好 wrf-python,之后又用 pip 装了个新包,pip 顺手把 cartopy 升级到较新版本,接下来绘图时ax.set_extent、gridlines或contourf行为诡异,与之前的图明显不一致。
原因:wrf-python 1.1.3 对 cartopy 的接口依赖停留在较老版本,新版 cartopy 对投影坐标和 matplotlib 接口做了调整。pip 安装依赖时会选择满足该新包要求的最新版 cartopy,在同一环境中覆盖 conda 解析好的旧版本。
解决:不要在同一环境混用 pip 和 conda 管理同一批依赖。要升级别的包时,先固定 wrf-python 相关依赖再操作,或者直接新建环境。最稳妥的做法:
conda list | grep cartopy # 如果看到版本明显超过 0.18/0.19,就回到干净环境重装 conda create -n wrf python=3.8 -y conda install -c conda-forge wrf-python=1.1.3 netcdf-fortran -y6. 进阶:把出图函数封装成能跑 72 小时的批量工具
6.1 函数本体
把前面的读取、插值、绘图逻辑收拢成一个函数,输入文件路径、变量名、等压面高度和输出路径,返回生成好的图片路径。这个函数可以直接放进气象业务脚本或定时任务里。
import os import matplotlib.pyplot as plt import cartopy.crs as crs from netCDF4 import Dataset import wrf def plot_wrf_var(nc_path, varname, level_hpa=None, out_png="out.png", cmap="viridis"): """读取 wrfout 文件,绘制变量平面图,支持插值到指定等压面""" ds = Dataset(nc_path) # 读取变量,取文件最后一个时次 field = wrf.getvar(ds, varname, timeidx=-1) # 如果指定了等压面高度,则先插值 if level_hpa is not None: p = wrf.getvar(ds, "pressure", timeidx=-1) field = wrf.interplevel( field, p, float(level_hpa) * 100.0, missing=wrf.default_fill(field.dtype), ) # 投影、范围、绘图 cart_proj = wrf.get_cartopy(field) xlim = wrf.cartopy_xlim(field) ylim = wrf.cartopy_ylim(field) lats, lons = wrf.latlon_coords(field) fig, ax = plt.subplots( 1, 1, subplot_kw={"projection": cart_proj}, figsize=(10, 7) ) ax.coastlines(linewidth=0.8) ax.set_extent([xlim[0], xlim[1], ylim[0], ylim[1]], crs=cart_proj) cf = ax.contourf( lons, lats, wrf.to_np(field), levels=20, cmap=cmap, transform=crs.PlateCarree(), ) fig.colorbar(cf, ax=ax, shrink=0.8) fig.savefig(out_png, dpi=150, bbox_inches="tight") plt.close(fig) ds.close() return out_pngPython 定义函数时,把接口参数设计成文件路径而不是文件对象,是批量任务稳定性的关键。这个函数有三个要点:level_hpa=None时直接画地面量或二维量;float(level_hpa) * 100.0负责把用户习惯的 hPa 转成 wrf-python 内部的 Pa;plt.close(fig)和ds.close()保证批量循环不泄漏文件句柄和内存。
6.2 批量调用与结果验证
配合现成的切片循环,72 小时的图可以一次性跑完:
import glob file_list = sorted(glob.glob("wrfout_d01_2019-07-01_*")) for nc_path in file_list: # 用文件名前缀生成输出文件名,便于时间排序 tag = os.path.basename(nc_path).replace("wrfout_", "") plot_wrf_var( nc_path, varname="T2", level_hpa=None, out_png=f"T2_{tag}.png", cmap="RdYlBu_r", )参数说明:函数内部每次都会重新打开 Dataset,这比传句柄安全,也不会出现多进程并发时句柄互踩的问题。批量跑完后验证不要只看图,选一个时次用 ll_to_xy 提取站点值,和自动站观测或再分析资料对比一下,确认插值结果在合理范围内。画图正确不代表数值正确,尤其是等压面插值,很多“看起来正常”的图其实插值到了错误的气压层。
我当初第一次跑 72 小时批量出图时,在 Windows 机器上连续装坏两个 conda 环境,后来才意识到 1.1.3 的时代背景和与 cartopy 的绑定关系。现在团队里的新机器都固定成同一套 Python 3.8 加 conda-forge 安装流程,这个库再没出过幺蛾子。希望帮到你。
本文还有配套的精品资源,点击获取