ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

CAMStoWRF完全指南:从CAMS数据下载到WRF-Chem初边界场配置

CAMStoWRF完全指南:从CAMS数据下载到WRF-Chem初边界场配置 做空气质量模拟的人应该都干过这件事把全球化学模式的输出结果塞进WRF-Chem里当初始场和边界场。早些年大家满世界找MOZART的nc文件后来慢慢有人开始用CAMS哥白尼大气监测服务的再分析数据。CAMS数据覆盖面广、化学物种相对齐全而且在国内外的发文章场景里认可度都还不错。可问题是这数据不是拿来就能喂给WRF-Chem的格式、网格、坐标系、变量定义全都不一样硬塞肯定报错。CAMStoWRF这个工具就是为了解决这件事出现的。它负责把CAMS的GRIB/NetCDF数据读取出来重映射到WRF-Chem的模拟网格上再写进wrfinput和wrfbdy文件里。整个过程听着不复杂真做起来坑很多。我前前后后用过好几遍这工具从编译到跑通再到结果验证每个环节都有值得记录的细节。这篇就围绕CAMS数据下载、CAMStoWRF配置、运行与踩坑这四块内容把整个流程掰开揉碎讲一遍。1. 项目思路拆解CAMS数据如何一步步变成WRF-Chem初边界场先说清楚一个最基础的问题为什么大家宁可折腾CAMS也不继续用传统的MOZART输出。这里头有两层原因。一是MOZART的那套全球输出数据用起来常会遇到时间上不对应的问题找个合适时刻的化学场得翻半天二是CAMS本质上是同化产品结合了卫星、地面观测和模式模拟做出来的化学场更接近真实大气的状态。对于局地重污染过程模拟来说初始场和边界场贴近实况能让模式少走很多弯路也更有利于分析污染来源和演变机理。但CAMS不能直接被WRF-Chem使用中间有三个大坑。1.1 CAMS资料为什么适合做化学初边界场CAMS全球大气成分预报产品的时空分辨率都很能打。空间分辨率大约0.25度时间上基本是三小时一组输出足够覆盖大部分中尺度模拟的需求。更重要的是它包含的化学变量相当全既有O3、NO2、CO、SO2这类常规痕量气体也有有机碳、黑碳、海盐、粉尘、硫酸盐等气溶胶组分还有多种VOCs前体物。很多做对流层臭氧研究和PM2.5来源解析的工作组都偏好用这套数据搭初边界场。从实用角度看CAMS也在持续更新还提供了一致性较好的再分析版本的整个长序列。做历史个例反演时可以快速拉到对应时段的全球化学场不用像以前那样到处找替代数据。这种便利性直接决定了它在一个模拟项目中能不能成为“默认选择”。我在实际项目里对比过用CAMS做初始场之后模式前几个小时的O3和PM2.5浓度明显比用默认profile边界场更贴近观测模式spin-up时间也能缩短不少。1.2 WRF-Chem与CAMS之间的三座大山坐标系、网格、物种定义CAMS数据存放在全球经纬度网格上这跟WRF-Chem的兰伯特正形或其他地图投影完全不是一回事。WRF-Chem需要的是在模拟域内均匀分布的格点场而全球数据直接投影过来会导致插值严重变形。第二座大山是垂直坐标CAMS用混合sigma-p坐标WRF-Chem也用eta坐标但这两种坐标的层顶、层厚分布完全不同必须做逐层的高度插值。第三座大山是物种定义差异。CAMS里的变量名和WRF-Chem机制里要求的变量名不是一个体系比如CAMS里叫“有机质aerosol organic matter”到WRF-Chem的CBMZ机制里可能得按OC或有机气溶胶的某种拆分方式去映射。这三座大山决定了不能简单用一个通用的“格式转换”工具解决需要一个专门针对CAMS变量表、坐标系和垂直层设计好的程序也就是CAMStoWRF存在的理由。1.3 CAMStoWRF在模拟流程中的定位与工作流程在实际的WRF-Chem模拟流程里CAMStoWRF的角色是承上启下。上游是数据下载下游是WRF-Chem主模拟。它的输入是CAMS数据文件加WRF-Chem自身的wrfinput文件输出是更新后的wrfinput含化学初始场和修正后的wrfbdy含化学边界场。这个处理流程非常贴合WRF-Chem原有的运行习惯所以不动主程序只改IO文件风险小也方便后续反复迭代做敏感性实验。我自己的使用习惯是先用WPS生成met_em文件并跑通real.exe生成wrfinput再用CAMStoWRF对wrfinput做化学变量的补充写入。这样气象场部分还是由WPS和real保证一致性CAMStoWRF只专注化学场。整个流程做顺了以后可以像流水线一样跑批量个例大大节省人工配置时间。2. CAMS数据下载实操从CDS注册到批量脚本落地这部分看起来最简单实际上很多人最后都卡在下载这步。CAMS数据存放在CDS平台上有网页交互式下载和API下载两种方式跑模拟当然推荐后者因为要下载的数据文件多一个时段动辄几十个变量手动点网页能点到怀疑人生。2.1 注册CDS账号与API认证去CDS平台做账号注册是第一步。注册过程不复杂用常用邮箱注册就行。重要的是注册完成后在个人页面能看到自己的用户IDUID和API Key这两个信息是API下载的凭证。下载前需要在本机配置认证文件。拿Linux环境举例在用户根目录下创建一个名为 .cdsapirc 的文件内容格式如下url: https://cds.climate.copernicus.eu/api key: UID:你的API密钥注意URL和key这两行之间不要有多余字符文件权限建议设置成600避免密钥泄露。配置好之后安装CDS专用下载包就可以开始用脚本取数了。这里有个容易踩的坑早期版本的CDS API包和新版数据接口有差异安装时要确认下载的包版本能匹配目标平台。否则会出现请求发出去返回404或者提示“request not found”之类的报错。2.2 数据下载脚本写法与参数选择CAMS数据下载的核心是构造一个request字典里面写入数据产品名、变量、年份月份、层级类型、区域范围、时间分辨率等信息。我常用的一个最小下载脚本长这样import cdsapi server cdsapi.Client() server.retrieve( cams-global-atmospheric-composition-forecasts, { variable: [ nitrogen_dioxide, sulphur_dioxide, ozone, carbon_monoxide ], model_time: 00:00, date: 2024-01-01/2024-01-02, type: forecast, pressure_level: 1000/975/950/925/900/850/800/700/600/500/400/300/200/150/100/70/50/30/20/10, data_format: grib, area: [50, 100, 20, 130], }, cams_chem.grib )脚本里头这几项参数要特别留心。variable选择决定了你最终能拿到哪些化学物种建议在转换前把CAMS提供的变量菜单完整看一遍。有些变量不常用但恰恰是机制里必须的提前漏选了后面转换时就会报“这个变量找不到”的错。pressure_level这行不同任务的使用差异很大有的场景走地面到对流层就够有些走臭氧柱分析想拉到平流层。常规WRF-Chem模拟中我倾向于下载到10hPa左右大概覆盖到平流层下部这对边界层高层的化学物质交换很有帮助。area是经纬度范围这里强烈建议设置为比WRF模拟域四周外扩至少1到1.5度防止插值时边界不足出现空白区。还有一个重要参数是typeCAMS有analysis和forecast之分。分析场贴近实况但有些时间段只有预报场可用需要根据个例时间段去适配合法的type设置和时间步长from 0 to 6 by 3这类设置。2.3 数据格式选择GRIB还是NetCDFCDS返回的数据格式有grib和netcdf可选。CAMStoWRF的原生读取能力偏向GRIBGRIB文件能保留完整的变量元数据和层级信息在某些情况下文件体积也相对可控。但GRIB文件对常见Python读取来说不够友好需要eccodes这类底层库支撑。NetCDF的好处是生态好容易用Python可视化检查。我个人经验是如果是要直接交给CAMStoWRF处理优先下GRIB格式省去后续格式转换步骤。如果下载后先用Python做质量检查、绘画平面图或者送入其他前处理流程那NetCDF更方便。不管选哪种都要保证下载时勾选了“所有需要的化学变量”不要只拿默认的那几项。因为Once你发现缺变量重新下载不仅浪费流量更浪费你重新匹配时间和变量的精力。2.4 存储规划与分批下载建议CAMS全球数据文件的体积不小如果下载精细度很高、区域覆盖大、时间跨度长一次请求可能返回几十GB数据。考虑模拟一般只覆盖几天到一周建议按个例分批次下载一天一天地下不要一次性拉整个月。这样有两个好处一是单个下载任务更稳定不容易出现网络中断导致整包重来二是方便你按日期做文件归档。下载落地之后的目录结构可以这样组织data/cams/20240101/ data/cams/20240102/ data/cams/20240103/每个目录里放对应日期的GRIB文件命名尽量带时间和变量段信息比如cams_20240101_00_chem.grib。这一步看着无关紧要实际会为我们后面写配置脚本提供极大便利尤其是做整月批量转换时目录规范能帮助你只用简单循环就完成全部处理。3. CAMStoWRF转换实现编译、配置、运行三步走数据拿到手之后紧接着的重头戏就是CAMStoWRF工具本身的落地。别看这个工具名字很直接从编译到配置每一步都能让人卡上半天。3.1 依赖库与编译环境CAMStoWRF用Fortran写成依赖关系主要集中在NetCDF库和GRIB解析库上。要编译它你首先需要确保系统里有可用的NetCDF Fortran接口能处理NetCDF4格式文件。GRIB读取方面需要安装grib_api或者eccodes并确保编译时能够找到对应的include和lib。很多人的经验是用环境管理器专门建一个编译环境把所有依赖装在统一路径下避免与系统自带库版本冲突。编译操作本身不算复杂进入工具源码目录后首先确认Makefile或configure脚本中的库路径设置然后执行make完成编译。这里有一个特别常见的坑编译器版本和库的位长必须一致。比如用Intel编译器编NetCDF库CAMStoWRF也用同样一套Intel环境去编不要混合使用GCC和Intel的库文件否则会在链接阶段报出一堆“undefined reference”。实际编译过程中还可能需要调整宏定义以适配本地NetCDF库的版本。老版本NetCDF会用nf_create等接口新版本差不多也兼容但Fortran编译器的flag可能不同比如需要将默认优化级别调低否则某些机器上会出现浮点异常。如果在一台比较老旧的Linux服务器上编译失败优先检查是不是优化flag的问题。3.2 文件命名、namelist配置与变量映射编译成功后使用CAMStoWRF的关键在于配置输入输出文件和变量映射。工具运行需要读取一个namelist文件里面指明输入CAMS文件路径、输出文件名通常是wrfinput_d01和wrfbdy_d01、模拟起止时间、网格嵌套层数等。文件命名方面CAMStoWRF要求CAMS数据文件遵循特定命名规范一般是前缀加上日期时间比如cams_2024010100. 命名不标准的话工具在内部调表时就会找不到对应文件。另一件容易忽略的事是wrfinput_d01文件必须是执行过real.exe之后得到的标准文件不能拿一个空壳子或自编数组去尝试。变量映射部分通常在源码目录内有一个变量对应关系表列出CAMS原变量和WRF-Chem目标变量的对应关系。比如CAMS的“ozone”对应WRF-Chem的“o3”“carbon_monoxide”对应“co”。气溶胶部分的映射要小心处理CAMS提供“organic_matter_aerosol”“black_carbon_aerosol”“sea_salt_aerosol”“dust_aerosol”等类别而WRF-Chem里可能区分为亲水性和疏水性组分并有多个粒径bin段这时需要在映射表中做拆分规则配置。默认映射表不一定适合你指定的化学机制换了机制以后一定要手动检查映射关系把没映射的变量补上把映射错乱的变量改正。3.3 运行输出与wrfinput/wrfbdy一致性核查配置好之后运行CAMStoWRF通常会输出一行行日志告诉你正在处理哪个时间层、读取了哪些变量、插值完成、写入了哪些字段。肉眼看到好多个“successfully”并不代表万事大吉还得自己去核查输出结果是否合理。我的标准核查动作有三步。第一步是查看wrfinput_d01里的化学变量是否已经更新可以借助ncdump检查变量维度是否跟气象场一致。第二步是用Python画一张模拟域内的O3或PM2.5空间分布图与CAMS原始数据的分布趋势做肉眼对比。若出现大片零值或异常极大值多半是插值范围设置错误或者映射没有完全生效。第三步是打开wrfbdy文件检查边界网格点上的化学变量是否按照设定频率正确更新没有出现时间维全零的情况。比较常见的现象是wrfinput更新了但wrfbdy没写好。这通常是因为边界文件的变量名格式与工具默认不匹配或者namelist里没有把“write_bdy”逻辑打开。我自己的经验是在运行之前先备份原始的wrfinput和wrfbdy万一跑出问题还可以对比原始文件和输出文件之间的差异快速定位问题出现在化学场写入阶段还是边界更新阶段。4. 高频问题与避坑清单这工具本身不算复杂但和真实模拟项目结合起来后各种各样的坑就冒出来了。下面列几个我遇到过的典型问题以及对应的排查思路方便直接“抄作业”。问题现象可能原因排查思路wrfinput化学变量全为0CAMS文件变量没选全在映射表里逐项核对变量是否存在wrfbdy边界化学场没有更新namelist边界写入开关未配置查看输出日志检查bdy写入函数是否调用模拟初始时刻O3浓度异常高压层选择偏高或CAMS时间与分析时刻不匹配比对CAMS变量在目标时刻的平面分布GRIB读取报错无法识别文件缺少eccodes或grib_api特定版本用grib_ls命令检查GRIB文件是否读取正常编译阶段intel库和netcdf库混杂编译环境不一致统一用同一套编译器环境重新编译4.1 时间错位与AOD对比表现异常有一次我跑某次污染过程模拟模式前6小时的PM2.5空间分布总跟实况差着几个经纬度的偏移AOD对比也一直偏高。一开始以为是化学机制参数的问题排查了半天最后才意识到CAMS数据文件时间名比模拟时间早了一个时次相当于初始场用了三个小时前的化学态。时间错位这种问题极其隐蔽因为不报错、不变态只是结果不对。建议在下载落地之后用一个简单脚本批量打印每个GRIB文件里的时间变量和模拟起止时间做严格比对一个数据一个数据地核对避免“想当然”的对应。涉及跨日、跨月的个例时尤其要小心有些下载请求过了UTC时间边界回来的文件可能多一段或缺一段。4.2 气溶胶物种映射对不齐气溶胶映射是CAMStoWRF用起来最头疼的地方。CAMS气溶胶类别中海盐和沙尘都有分粒径段的说法而WRF-Chem机制内的气溶胶模块比如GOCART或者MOSAIC对种类、粒径数、混合状态的要求都不一样。如果默认映射没有把CAMS的海盐质量拆到WRF-Chem对应的细/粗模态上模拟出来的沿海区域气溶胶浓度就普遍偏低出现奇怪的经纬向梯度。我的处理方式是先明确自己用的是哪一种WRF-Chem气溶胶方案再回头改映射表。比如GOCART方案下海盐通常分多个binCAMS的sea_salt_aerosol需要按照一定的质量比例分配到对应bin里。这个比例没有普适答案只能根据模拟区域和个例去测试调整。不要指望默认映射一步到位多跑两次敏感性实验比较模拟和观测的差别来判断拆分是否合理。4.3 GRIB读取与编译环境的幺蛾子CAMStoWRF编译过程中的障碍有一半以上是由GRIB读取库引起的。常见报错是“GRIB_API version too old”或者“eccodes not found”。遇到这种问题不要急着改代码先用系统命令检查库版本和安装路径。考虑到大多数人用的是Intel编译器建议使用Linux发行版自带的包管理统一安装依赖。如果自己手动编译eccodes记得在configure阶段打开Fortran接口支持否则编译CAMStoWRF时完全找不到函数入口。另外某些系统上存在多版本grib库并存的问题环境变量LD_LIBRARY_PATH会决定程序运行时到底找哪套库报错很随机。解决办法是在运行之前手动export一套明确的库路径并echo确认当前生效的是自己需要的哪个版本。4.4 嵌套域边界和更新频率的取舍多嵌套域模拟是CAMStoWRF使用中最需要提前规划的变量。如果你跑的是三层嵌套每一层都需要对应的wrfinput_d01、wrfinpurt_d02等文件并且各自生成的wrfbdy要配套对应。有人只处理了外层域内层域直接沿用默认边界模拟结果自然出现很明显的边缘污染高值。化学边界场的更新频率也值得思考。WRF-Chem气象边界一般可以用3或6小时间隔化学边界场的稳定性取决于你模拟区域的大小。区域大、物种寿命长、化学梯度平缓时6小时间隔也够用区域小、靠近强污染源时3小时更新更好。CAMStoWRF多数版本能按时间循环输出多层边界场配置时确认每一层的输出时间序列都覆盖完整模拟时段。这块我在实际赶工阶段吃过亏。最初只把d01边界场备好了d02和d03直接用父域插值结果污染气团从边界灌进来以后在子域内完全变形严重干扰了源解析结论。后来重新把所有嵌套域的wrfinput、wrfbdy全部跑通整个模拟才稳定下来。嵌套域的处理建议提前写进批处理脚本里一次性逐域生成省时省力还安全。最后一点个人体会跑过几次CAMStoWRF的完整流程我的真实感受是工具本身不复杂但环境依赖、变量映射和数据时间线这三个地方最容易出岔子。后来我把所有下载脚本、映射表、namelist以及核查程序都固化到一个项目模板目录里每次接新个例只需要改日期和区域流程稳定很多。这套流程也让我切身体会到数值模拟前处理的质量直接决定了模拟结果的可靠性花在初边界场检查上的时间永远比后续反复调参数更值得。最近一次做某区域夏季臭氧个例模拟我特意把CAMS初始场跑完后先画了一张O3垂直剖面图再和探空观测对比确认高层浓度形态对上了才放心交给WRF-Chem跑后面的预报。这种“跑前检查”的习惯虽然多花了半小时却能在后面几天的模拟周期里省出数倍的试错成本。如果你也正在折腾WRF-Chem的化学初边界场建议把这套流程完整走一遍把每一步的数据都留档再回头你就会发现化学初边界场不再是玄学。
RELATED READING

延伸阅读

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