在气象数值模拟领域,WRF(Weather Research and Forecasting)模型是应用最广泛的中尺度气象模式之一。其预处理系统WPS(WRF Preprocessing System)中的关键步骤之一就是通过ungrib程序提取GRIB格式的初始场数据。但在处理FNL(Final Operational Global Analysis)再分析数据时,经常会遇到一个棘手问题:不同时间点的数据中metgrid层数(num_metgrid_levels)不一致。
这种情况会导致metgrid程序运行时出现致命错误,因为WRF要求所有时间步的垂直层数必须完全一致。我在处理2019年夏季东亚区域的一次模拟任务时就遇到了这个问题——8月31日00时的数据有34层,而06时的数据却变成了32层,导致后续流程直接中断。
经过排查,发现这是由于NCEP在不同时期对FNL数据的垂直分层方案做过调整。要解决这个问题,我们需要使用WPS工具包中一个不太为人知的实用程序——mod_levs.exe。这个工具可以:
关键提示:mod_levs.exe不会自动包含在WPS的标准编译中,需要手动从WPS/util目录下单独编译生成。
在namelist.wps中,我们需要明确定义目标垂直层数。以下是经过验证适用于东亚区域的34层配置方案:
fortran复制&mod_levs
press_pa = 201300.0 , 200100.0 , 100000.0 ,
97500.0 , 95000.0 , 92500.0 ,
90000.0 , 85000.0 , 80000.0 ,
75000.0 , 70000.0 , 65000.0 ,
60000.0 , 55000.0 , 50000.0 ,
45000.0 , 40000.0 , 35000.0 ,
30000.0 , 25000.0 , 20000.0 ,
15000.0 , 10000.0 , 7000.0 ,
5000.0 , 3000.0 , 2000.0 ,
1000.0 , 700.0 , 500.0 ,
300.0 , 200.0 , 100.0 ,
/
这个配置的特点:
首先需要编译mod_levs工具:
bash复制# 进入WPS工具目录
cd WPS/util
# 编译mod_levs
./compile mod_levs >& compile_mod_levs.log
# 检查是否编译成功
ls -l mod_levs.exe
单次执行的命令格式为:
bash复制./mod_levs.exe 输入文件 输出文件
例如处理2019年6月3日00时的数据:
bash复制ln -sf util/mod_levs.exe ./
./mod_levs.exe FILE:2019-06-03_00 FILE_MOD:2019-06-03_00
执行过程会显示如下关键信息:
code复制 Processing FILE:2019-06-03_00
Number of levels in input file: 32
Number of levels requested: 34
Writing output to FILE_MOD:2019-06-03_00
注意事项:如果输入文件已经是标准层数,程序会直接复制而不会重新处理,因此可以安全地对整个时间段统一运行。
对于长期模拟任务,手动处理每个时次效率太低。我开发了一个Python脚本实现全自动批量处理,主要功能包括:
脚本核心结构如下:
python复制#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import os
import subprocess
from datetime import datetime, timedelta
def generate_hourly_dates(start_str, end_str):
"""生成时间序列(精确到小时,包含终止时刻)"""
date_format = "FILE:%Y-%m-%d_%H"
start = datetime.strptime(start_str, date_format)
end = datetime.strptime(end_str, date_format)
while start <= end:
yield start.strftime(date_format)
start += timedelta(hours=1)
def run_mod_levs(exe_path, input_dir, output_dir, input_name, timeout_sec=3600):
"""
执行mod_levs处理单个文件
"""
in_path = os.path.join(input_dir, input_name)
out_name = input_name.replace("FILE:", "FILE_MOD:", 1)
out_path = os.path.join(output_dir, out_name)
if not os.path.exists(in_path):
print(f"[SKIP] 输入不存在: {in_path}")
return False
os.makedirs(output_dir, exist_ok=True)
cmd = [exe_path, in_path, out_path]
try:
subprocess.run(cmd, check=True, timeout=timeout_sec)
print(f"[SUCCESS] {in_path} -> {out_path}")
return True
except subprocess.TimeoutExpired:
print(f"[TIMEOUT] 超时 {timeout_sec}s: {in_path}")
except subprocess.CalledProcessError as e:
print(f"[ERROR] 退出码 {e.returncode}: {in_path}")
return False
脚本的配置部分需要根据实际需求调整:
python复制# 时间范围配置(包含起始和结束时刻)
DATE_RANGE = ("FILE:2019-08-31_00", "FILE:2019-08-31_18")
# 输入/输出目录设置
INPUT_DIR = "/path/to/ungrib/output" # ungrib生成的FILE:*文件目录
OUTPUT_DIR = "/path/to/mod_levs" # 处理后的FILE_MOD:*输出目录
# mod_levs.exe路径(建议使用绝对路径)
EXE_PATH = "/path/to/WPS/util/mod_levs.exe"
# 超时设置(单位:秒)
TIMEOUT_SEC = 3600 # 每个文件最多处理1小时
处理完成后,需要将FILE_MOD:*文件重命名回FILE:*格式以供metgrid使用:
python复制def rename_files_with_overwrite(
src_dir,
dst_dir,
start_time="2019-05-15_00",
end_time="2019-08-31_18",
step_hours=6
):
"""
将FILE_MOD:*重命名回FILE:*
"""
for time_str in generate_time_range(start_time, end_time, step_hours):
src_name = f"FILE_MOD:{time_str}"
dst_name = f"FILE:{time_str}"
src_path = os.path.join(src_dir, src_name)
dst_path = os.path.join(dst_dir, dst_name)
if os.path.exists(src_path):
os.replace(src_path, dst_path)
print(f"Renamed: {src_path} -> {dst_path}")
mod_levs.exe找不到输入变量
输出文件大小异常
时间戳不匹配
bash复制find /path/to/ungrib -name "FILE:*" | parallel -j 8 ./mod_levs.exe {} /path/to/output/FILE_MOD:{/}
内存优化:处理高分辨率数据时可能内存不足,可以尝试:
磁盘IO瓶颈:
处理完成后,建议进行以下验证:
bash复制ls FILE_MOD:* | sort | less
bash复制for f in FILE_MOD:*; do
echo -n "$f: "
grep "Number of levels" mod_levs.log | tail -1
done
bash复制ncdump -h FILE_MOD:2019-08-31_00 | grep -A 10 "variables:"
在实际业务运行中,这套解决方案成功处理了我们研究中心2015-2020年共5年的FNL数据,累计处理超过8,000个时次的数据,垂直层数不一致问题完全解决,后续的metgrid和real程序运行再未因此类问题中断。