1. 项目概述WRF-CMAQ 模型到底解决什么问题如果你刚接触空气质量数值模拟大概率会从各种论文、报告里频繁看到 WRF-CMAQ 这个词。它是一套把气象模拟与大气化学传输结合起来的建模系统简单说就是先用 WRF 把大气“物理状态”算出来再把结果喂给 CMAQ让后者在网格空间里模拟污染物怎样生成、沉降、传输和转化。我自己从研究生阶段开始折腾这套系统前后跑了不下几百个模拟案例可以负责任地讲WRF-CMAQ 是当前国内环境科研与业务预报中使用最广泛、文档最完整、社区最活跃的空气质量模型组合之一没有太多悬念。这套系统能解决的问题很明确。比如某地出现臭氧超标你想知道是本地生成的还是外来输送的某工业园区上马新项目你想评估它对下风向城市 PM2.5 浓度的增量贡献或者某次重污染过程中气象条件到底占了几成、排放变化占了几成。这些问题靠观测数据很难直接回答因为观测只能告诉你“发生了什么”很难告诉你“为什么发生、谁导致的”。WRF-CMAQ 的价值就是用物理和化学机制把大气变化过程尽可能真实地重演一遍从而给你一个可解释、可拆解的答案。从历史来看WRF 是新一代中尺度气象预报模型由美国大气研究中心等机构开发而 CMAQ 是美国环保署的社区多尺度空气质量模型。两者通过一套名为 MCIP 的接口程序衔接形成了从气象驱动到化学传输的完整模拟链路。值得一提的是国内很多省份的空气质量预报预警系统、重大活动空气质量保障系统底层用的就是这套组合。所以不管你是做科研、写环评论证还是搞业务预报这套流程都是绕不开的核心技能。这篇博文我想站在“把 CMAQ 用户手册第十三章的内容落地到实际跑通”的角度把 WRF-CMAQ 从原理到实操的完整框架做一次梳理。适合两类读者一是刚入门、正准备搭环境跑第一个案例的研究生二是已经有 WRF 基础、但对 CMAQ 数据流和配置细节还不太清楚的工程师。我会尽量讲清楚“为什么要这样做”而不仅是“怎么做”因为太多人在这一步只记住了命令换台机器或者换个版本就不会玩了。2. 为什么我坚持推荐 WRF-CMAQ 这套技术路线2.1 从模型架构看整条模拟链路一个完整 WRF-CMAQ 模拟系统由四层构成气象场、排放源、化学传输、后处理分析。WRF 负责第一层它输出风、温、湿、压、辐射、降水等气象要素时间频率一般要求逐小时甚至更高。CMAQ 负责第三层它把 WRF 提供的气象场作为“大背景”再叠加人为源和天然源排放在每一个网格里求解化学动力学方程算出 O3、PM2.5、NO2、SO2 等污染物的时空分布。第二层排放源是关键中的关键。CMAQ 本身不生成排放数据它需要的是分物种、分时间、分区域的网格化排放清单。国内常用的是清华大学开发的 MEIC 清单全球尺度则有 EDGAR、MEGAN天然源等。清单数据要先经过排放处理工具比如 SMOKE做时间分配、化学物种映射和空间分配才能变成 CMAQ 能识别的 NetCDF 格式文件。很多人跑模型跑出荒唐结果最后排查下来不是化学机制配错而是排放清单的格式或者投影没有配对。第四层后处理分析是出成果的地方常见工具有 R 语言的 OpenAir 包、Python 的 xarray/pyproj、NCL 等。模型输出的 NetCDF 文件里有几百个变量包括各物种浓度、干湿沉降通量、气溶胶组分等你可以诊断一个网格点的浓度时间序列也可以做区域平均、污染过程回演还可以做源贡献分析OSAT/ISAM来回答“谁的贡献大”。四个模块环环相扣任何一层的错都会传导到下游。我再强调一遍WRF 输出频率、排放清单时空分辨率、CMAQ 网格定义这三件大事必须在运行前就对齐否则后面全是糊涂账。2.2 与其它空气质量模型的横向对比有人会问现在开源模型那么多为什么不去用 GEOS-Chem或者用 CAMx我的看法是选模型不是选“最先进”而是选“最适合你的科学问题最可靠的技术支撑”。GEOS-Chem 的强项是全球化学输送模拟它的化学机制非常详尽适合做温室气体、对流层臭氧长距离传输这类问题但它本身的嵌套模拟在中尺度区域的精细过程描述上不如 CMAQ 成熟。CAMx 与 CMAQ 功能高度重合差不多的物理化学框架差不多的输入数据结构特别是有臭氧源分担技术OSAT和颗粒物源追踪PSAT在某些特定评估需求下更灵活但国内用户少、教程少遇到问题找资料比 CMAQ 费劲得多。CMAQ 的优势有三个一是 EPA 持续维护版本迭代稳定社区生态好二是物种映射、化学机制如 CB6r3、SAPRC07tic有成熟文档你论文里写了什么机制同行能直接复现三是有 MCIP、ISAM、DDM-3D 等一批配套工具从气象处理到敏感性分析全链条打通。相比之下 WRF-CMAQ 的学习曲线虽然不算平缓但一旦把主流程跑通后续扩展性非常强。用我们课题组常说的一句话总结“WRF-CMAQ 就像是空气质量模型里的‘大众汽车’——性能稳定、配件好找、修起来也容易找到人问。”对于环境科研和业务预报场景这已经足够优秀了。3. 核心组件拆解一次模拟跑通需要哪些模块3.1 WRF 气象输出被很多人忽视的“生命线”WRF 输出给 CMAQ 的并不是原始 WRF 输出文件本身而是 MCIP 重新处理的中间文件。但 WRF 怎么跑、输出哪些变量、多长时间一次直接决定了下游能否接得上。先提一个最常见的坑CMAQ 要求气象输入的时间间隔通常为 1 小时如果你的 WRF 输出是 3 小时一次MCIP 也能插值但插值会平滑掉一些短时气象过程尤其在风速和边界层高度变化剧烈的时刻污染物浓度模拟误差会一下子放大。我自己的标准做法是 WRF 输出频率设置为 1 小时遇到研究对流或局地环流的案例会加密到 30 分钟。WRF 版本选择上也要注意。目前主流组合是 WRF 4.3 或 4.4 配 CMAQ 5.3.2 或 5.4旧版本如 WRF 3.8 配 CMAQ 5.2 也能跑但新版本在化学机制和气溶胶模块上有不少更新不建议新手从旧版本起步。另外WRF 有两种垂直坐标选项CMAQ 接收的标准是质量坐标下的变量MCIP 会自动做转换你不用操心但有一点必须自己确认WRF 模拟区域必须完整覆盖 CMAQ 模拟区域或者最好完全一致。嵌套关系搞反了MCIP 会直接报错而且是那种查半天文档也摸不着头脑的报错。3.2 排放清单处理误差的重要来源很多人把模型模拟偏差归咎于气象场或者化学机制但我的经验是大部分情况下排放清单的误差才是最大的偏差来源。人群活动、工厂开工、柴油车运行这些数据的时空分布不可能完全精确所以模拟值和观测对不上未必是模型物理化错误很可能是“进去的垃圾不对出来的结果自然不对”。CMAQ 排放输入有两种格式一是 fortran 格式的 point/area/mobile 源文件二是 IO/API 标准的 NetCDF 格式。目前主流实践是用 SMOKE 把原始清单处理为 CMAQ 可直接读取的 NetCDF 格式。这个过程有三步空间分配把县级、地市级排放总量分配到你模拟区域内每个网格需要空间分配权重因子人口、道路、土地利用等。时间分配把年排放量分配到小时需要月变化系数、周变化系数和日变化曲线。化学物种映射把清单里的常规污染物如 NOx、VOC、PM2.5映射成 CMAQ 化学机制里的具体物种如 NO、NO2、ETH、TOL、XYL 等。这三步里最容易被忽略的是化学物种映射。CB6r3 机制里 VOC 被拆成十来种物种不同清单源谱的拆分比例各有差异直接用默认映射表虽然能跑通但臭氧模拟结果可能偏移很大。有条件的话应该用本地源谱数据去替代默认的 split factor。3.3 MCIP 接口模块理解它才能用好它MCIP 全程是 Meteorology-Chemistry Interface Processor它就是 WRF 和 CMAQ 之间的“翻译官”。它把 WRF 输出的大气变量转换成 CMAQ 所需的水平风、垂直扩散系数、云参数、辐射通量、边界层高度等字段同时重新插值到 CMAQ 的网格和垂直层上。MCIP 最常见的运行方式是读取 wrfout 文件输出 GRIDCRO/GRIDDES/METCRO 等若干类文件。你需要注意 MCIP 里的垂直层设置它从 WRF 获取垂直层数并按 CMAQ 控制文件里定义的层次做映射。CMAQR 的默认垂直结构是从地面到约 100 hPa大概 35 层左右近地面层加密。这个设置在大多数情景下够用但如果你研究的是边界层内污染物垂直混合建议在地面到 1 km 以内至少保留 12 层以上否则垂直扩散过程表达不足。有个实用建议跑 MCIP 前先把你 WRF 输出的时间范围和 CMAQ 要模拟的时间范围严格对齐。MCIP 是一个时次一个时次处理的如果 WRF 运行中途断了或者少了一个小时MCIP 那一步会直接中断而且错误提示并不明显通常是“error reading met file”这种让人抓狂的提示。所以养成好习惯WRF 跑完先检查 wrfout 文件数量和时间步完整性再做 MCIP。4. 从零开始装环境版本搭配与编译避坑这一节我按照自己多年实践验证过的一套组合来讲虽然不是唯一选择但至少能保证你在一台全新的 Linux 服务器上不抓狂。4.1 版本与依赖组合的推荐配置我用得最顺手的组合是WRF 4.4.2 CMAQ 5.4 配套工具链。依赖库方面NetCDF-C 4.9.2、NetCDF-Fortran 4.6.0、MPICH 4.0.2、IOAPI 3.2、curl 和 zlib 都是经典组合。需要特别提醒的是WRF 和 CMAQ 都必须用同一个 NetCDF-Fortran 编译否则运行时会出现“NetCDF: Unknown file format”之类的错误。如果你打算用 GCC 编译器建议 gcc/gfortran 版本不低于 9.3太老的编译器在编译 WRF 的“module_irr”等新模块时会报错。Intel 编译器也是一样ifort 2021 之后的版本对 WRF 4.4 系列兼容性更好老版本 ifort 2013 虽然也能编译但会碰到各种“unlimited format”警告不建议在 2024 年的服务器上再用。IOAPI 是 CMAQ 的底层文件库编译它时需要指定环境变量 BIN例如设置为 Linux2_x86_64gfortran这个字符串决定 Makefile 行为。很多人卡在这一步其实它的逻辑是 IOAPI 为不同平台和编译器预定义了目录结构你只要在 configure 或环境变量上匹配对编译就会顺利很多。4.2 WRF-CMAQ 编译步骤与关键变量WRF 编译流程大家都熟悉先运行 configure选择 15gfortran dmpar这类编号再运行 compile em_real最后检查 main 目录下是否生成 wrf.exe 和 real.exe。这里我只说几个容易忽视的点环境变量 WRF_EM_CORE1 要确保设置否则后续 CMAQ 调用 WRF 气象数据时可能因为缺少 WRF 核心定义导致数据读取异常。编译 WRF 时建议把“nesting”选项选为 1basic两层嵌套场景够用计算量也更小。跑 WRF 之前先把 geogrid 生成 geo_em 文件里土地利用数据检查一遍。国内很多区域如果用默认 MODIS 数据城市扩张区域可能没有及时更新这会直接影响边界层参数化和城市热岛效应模拟最终牵动臭氧和 PM2.5 浓度。CMAQ 编译相对更复杂一点。它采用 Linux 下的“configure”和“make”两级方式。我的建议是按照官网“CMAQ-5.4 Tutorial”的顺序先编译 ioapi再编译 cctm。关键环境变量有CMAQ_HOME指向你的工作目录CMAQ_DATA存放输入数据和输出结果的路径GRID_NAME网格名称比如GRIDCRO2D的文件名要与之匹配编译 CCTM 前需要确认 “BLDTYPE” 是 ser 还是 par。如果使用串行版本跑起来简单但巨慢一个 100×100×35 的三天模拟大概要十几个小时并行版本则需要设置好 MPI 参数尤其在集群上跑MPI_PROC_NUM和节点分布直接影响运行效率。5. 实操过程三层嵌套案例完整走一遍5.1 模拟区域与投影参数设置以中国东部区域为例一套典型的三层嵌套设计是这样外层 d01 覆盖整个中国及周边网格距 27 km中间 d02 覆盖华北平原和长三角网格距 9 km内层 d03 覆盖京津冀核心区网格距 3 km。这种“27-9-3”的嵌套方式很经典既能抓住大尺度天气过程又能解析局地环流和污染分布细节。投影参数上我习惯选用兰勃特投影Lambert Conformal两个标准纬线设为 30°N 和 60°N中心经度设为 110°E。设置投影参数时要注意CMAQ 模拟域的水平网格必须严格对应 WRF 模拟域也就是说 CMAQ 的 GRIDCRO2D 要和 WRF 的 geo_em 文件完全对齐。大多数人不愿手动对齐那你就用 MCIP 输出里自带的 GRIDDESC 文件它会自动生成投影信息你只需确保 CMAQ 读取时路径一致就行。还有一个非常重要但很多人忽略的配置WRF 的嵌套网格之间必须满足“奇数倍”关系比如 27km 到 9km 是 3 倍9km 到 3km 也是 3 倍。这是 WRF 网格嵌套的基本要求如果你不按要求设置会直接报错。5.2 排放清单的准备过程以 MEIC 清单为例它的原始数据格式是按省份、按部门、按月份分列的逐小时排放量。要让 CMAQ 用上需要将它转换为 NetCDF 格式的“emis”文件。具体做法是用 SMOKE 里的“Gridding”和“Temporal Allocation”模块处理。如果只是跑通一个测试案例也可以直接下载 CMQ 官方的“benchmark”排放文件先把流程走通再换自己的清单。这一步我要特别提示一个坑MEIC 的化学物种拆分表即meic2cb6r3_scale.txt中是按 CB6r3 机制拆分的如果你 CMAQ 编译时选了 SAPRC07tic 机制那这份拆分表完全不适用必须更换对应 SAPRC 的映射表。很多新手在跑 O3 模拟时发现模拟值偏高或偏低排查到底才发现机制和清单方向不匹配。所以切记化学机制的选项要和排放清单的物种拆分表保持同一套体系。5.3 运行流程与关键控制参数跑 CMAQ 的核心程序是 CCTMCMAQ Chemical Transport Model它读取 MCIP 的气象文件、排放文件、初始条件和边界条件输出每小时的浓度场文件。运行 CCTM 前要配置run_cctm.csh脚本里面有几个参数必须认真对待CTM_APPL控制这个案例的标识建议包含区域和时段信息比如d3_20250115后面找输出文件会方便很多。CTM_START和CTM_END模拟起止时间。CMAQ 的边界条件和初始条件文件必须覆盖这个时间范围。CTM_MAXSTEP最大时间步长一般设 60 或 120 秒。如果设置太大化学反应刚烈的时候容易发散导致浓度出现负值或 NaN。CTM_PROC并行核数。在 3km 网格上建议每核负责不超过 10 个网格列太多会影响负载均衡。运行完 CCTM你会在指定输出目录下得到CCTM_CCTM_APPL_*.nc文件用 Python 或 NCL 就可以实现后处理绘图了。6. 常见问题与排查技巧实录6.1 气象字段缺失或格式错误这是一个高频问题。现象是 MCIP 运行时报ERROR: variable not found最后发现是 WRF 输出里缺了某个变量最常缺的是T2、Q2、SWDOWN或RAINC/RAINNC。原因多半是 WRF 的namelist.input里io_form_auxinput或者输出变量的auxhist设置有问题。排查思路很简单先用ncdump -h wrfout_d01_*查看变量列表对照 MCIP 要求的变量清单逐项检查。发现缺了就回 namelist 里补上变量输出但要注意有些变量特别是辐射通量必须在 WRF 的physics设置里打开对应方案才会输出切不可强行在iofields里添加不存在的变量。6.2 排放清单物种映射报错CMAQ 运行时报Species not in mechanism, check emissions这类错误说明排放文件里有某个物种在化学机制里不存在。常见于手工修改清单映射表后新旧物种名混用。比如 CB6r3 机制里有ETH和ETHA但老清单里写的是C2H6就会直接报错。解决办法有两种一是修改排放文件的物种名使其与机制匹配二是在 CMAQ 的CSQY_DATA里加一个别名映射表将旧名映射到新名。我更推荐第一种做法因为别名映射会给后处理带来不必要的混乱。另外物种大小写也很关键IOAPI 对变量名大小写是敏感的NO和no会被当成两个物种。6.3 模拟结果出现明显空间错位假如你画出来的 PM2.5 浓度高值中心和实际监测点对不上尤其是高值区整体偏移了半个网格以上那大概率是投影坐标系不一致。CMAQ 输出用的是兰勃特投影下的网格坐标绘图时必须先做投影变换再用cartopy或者basemap叠加地图。用 matplotlib 直接画经纬度而没有转换投影矩阵出现空间错位就是必然结果。处理办法是在绘图前读取 GRIDDESC 文件里的投影参数用pyproj定义投影坐标系再把模型坐标转换成经纬度或者反过来把监测点经纬度投影到模型网格坐标。这一步看起来繁琐但跑通一次后可以固化成公共函数以后所有案例都用它一劳永逸。7. 按项目经验总结几个关键体会整套 WRF-CMAQ 流程跑下来我最深的感受是这不仅仅是一个软件操作的问题更是一种工程化思维。每一层都有大量隐性的“约定”从变量名到坐标系再到时间基准任何一个环节不遵守约定下游就会以各种奇怪的方式给你反馈。所以第一步永远是建立一张“数据流清单”从 WRF 输出到 MCIP 文件到排放文件到 CCTM 输出把变量名、时间频率、投影信息全部写清楚这个清单的价值会在你排查问题的关键时刻体现出来。另一个体会是“并行计算不是万能的”。很多人一上来就开 96 核跑 3km 网格结果发现加速比很差原因是 CMAQ 的并行瓶颈往往在 I/O 和通信而不是计算本身。更合理的做法是先用小范围单体跑通再扩大规模同时把输出频率控制好——除非你在做过程分析否则逐小时输出就足够了逐 10 分钟输出会把存储空间直接塞爆。最后分享一个实用性很强的小技巧当你拿到一套全新的服务器环境时不要急于直接编译 CMAQ。先跑一遍官方的 benchmark 案例对照参考输出验证结果。如果 benchmark 能通过说明编译器和依赖库没有问题接下来替换真实数据和网格风险会小很多。别嫌 benchmark 数据大那点时间换来的是后面几星期的省心这笔账太划算了。