做空气污染模拟的朋友对 WRF-CMAQ 应该不陌生,这套“中尺度气象模式 + 区域空气质量模式”的耦合工具,基本是国内环境科研、预报业务的标配。气象场算不准,化学传输就无从谈起;化学机制编译不通,气象结果也只能停在风速风向层面。但说实话,我第一次接触 WRF-CMAQ 的时候,光环境配置这一关就被劝退了三次——不是缺库就是版本不对,好不容易编译完,跑测试案例又撞上各种段错误。这篇文章我从零开始,把从裸机到跑通第一个测试案例的完整过程拆开揉碎讲清楚,包括环境选型、依赖库安装、WRF 与 CMAQ 的编译运行,以及我踩过的坑和排查思路,给准备入坑或已经卡在配置环节的朋友一个可以直接照做的参考。
先说清楚这个东西能干什么。WRF(Weather Research and Forecasting)负责模拟气象场,CMAQ(Community Multiscale Air Quality)负责模拟污染物的排放、扩散、化学转化和沉降。两者耦合起来,可以用来做 PM2.5 和臭氧的来源解析、重污染过程复盘、减排效果评估,以及日常空气质量预报。适合谁看呢?如果你是大气科学、环境工程相关专业的研究生,或者环保系统搞模式预报的技术人员,甚至只是对空气质量模拟感兴趣、想自己试着跑的爱好者,这篇文章都适用。不过有一点要提前说明:WRF 和 CMAQ 的版本更新很快,不同版本的编译选项和脚本会有些差异,但核心思路——依赖库顺序、环境变量传递、编译选项选择——是通用的,掌握了思路,换版本只是换个参数的问题。
1. 环境准备与选型思路:先别急着敲编译命令
很多新手一上来就在自己电脑上装 VMware,然后进虚拟机开始下源码,结果编译到一半发现内存不够或者 CPU 核心太少。我建议在动手之前,先把三件事想清楚:机器配置够不够、系统用什么、编译器怎么选。
1.1 先搞清楚你的机器够不够用
WRF 和 CMAQ 都是典型的“吃核心”程序,尤其是 CMAQ 的 CCTM 主程序,跑起来之后多核并行效率很高,但核心数不足时耗时非常夸张。以我常用的测试配置为例:模拟一个 100×100 网格、水平分辨率 12km、垂直 35 层的区域,时间步长 60 秒,跑 24 小时的模拟,用 16 核的机器差不多需要 1.5 小时左右;如果只有 4 核,这个时间会拉到 6 小时以上。所以你如果只是跑跑理想化测试案例,8 核 16GB 内存的机器勉强够;想跑真实案例,建议至少 16 核、64GB 内存。磁盘也要注意,WRF 每小时会输出一个 wrfout 文件,CMAQ 每小时也会输出多个 CAMS 和 CONC 文件,跑几天模拟就是几十 GB 甚至上百 GB,千万别把家目录塞满。
存储类型的建议是:源码、编译目录和最终输出放机械硬盘没问题,但中间临时文件、特别是 WRF 的 met_em 文件和 CMAQ 的 CCTM 输出,建议放到 SSD 上。我实测过,同样一个案例,IO 等待时间能差出 15% 左右,对动辄几十小时的长期模拟来说,这个时间成本非常可观。
1.2 Linux 系统选择与用户环境准备
强烈建议用 Ubuntu Server LTS 或 CentOS 7/8 这类服务器版本,不要用带桌面的桌面版,省资源也减少干扰。我这些年用过 Ubuntu 18.04、20.04、22.04,也用过 CentOS 7.9,整体感觉 Ubuntu 的依赖包更容易装全,CentOS 则胜在稳定,但老版本的 GCC 可能会让你在编译新版 WRF 时抓狂。
装好系统后,建议创建一个非 root 的编译用户,比如叫wrfuser。道理很简单:WRF 和 CMAQ 的编译脚本可能会修改用户目录下的配置文件,如果用 root,万一某个脚本写坏了系统级配置,排查起来很痛苦。此外需要安装一批基础依赖,Ubuntu 下命令大概是:
sudo apt update sudo apt install -y build-essential gfortran g++ m4 perl csh make libtool automake autoconf wget curlCentOS 下则是:
sudo yum install -y gcc gcc-gfortran gcc-c++ m4 perl csh make libtool automake autoconf wget curl这里的csh特别提醒一下,CMAQ 官方的大部分脚本都是 C Shell 写的,如果你系统里没有 csh,后面跑脚本会直接报“No such file or directory”或者诡异的语法错误,很多人会卡在这里完全摸不着头脑。另外,如果用的是 CentOS,建议把 SELinux 关掉或者设为 permissive,不然编译过程中访问某些目录可能会被静默拦截,报错又特别隐晦。
1.3 编译器与 MPI 库选型:一次选对,少走弯路
编译器选择上,如果学生或者个人学习,直接用 GCC 套件中的 gfortran 就够了,完全开源免费。如果单位有 Intel 编译器授权,那用 ifort/icc 在某些浮点计算上确实有一定性能优势,但代价是编译选项和链接库要和 Intel 的 MPI(Intel MPI)配合,配置复杂度会高一些。我的建议是:初学者统一用 gfortran + OpenMPI 或 MPICH,遇到问题网上能搜到的经验最多,踩坑成本最低。
MPI 库我推荐 MPICH。不是说 OpenMPI 不好,而是 CMAQ 官方的一些测试脚本和 MPICH 的兼容性更好,WRF 对两者也都支持,但从我实际遇到的情况来看,OpenMPI 的版本更新比较激进,偶尔会出现和 netCDF 库的 Fortran 接口链接不上的情况。MPICH 相对保守,版本稳定,适合“配置好就不想再动”的模式运行场景。当然,同一个系统里最好不要混装两套 MPI,否则 mpirun 时经常出现库冲突,报错形形色色,排查起来能逼疯人。
依赖库的安装目录建议统一放在一个独立路径下,比如/home/wrfuser/wrf/libs,不要散装到系统目录。这样做的优势很明显:一是环境变量好写,指向一个统一前缀即可;二是以后换机器或换版本,直接把整个libs目录拷过去,重新配置一下环境变量就能用;三是避免污染系统库,不会因为升级系统包导致 WRF 链接的旧库失效。为了便于移植,我建议所有依赖库都用静态编译(--disable-shared),这样最后生成的可执行文件不依赖动态库,换机器时不需要再折腾 LD_LIBRARY_PATH。
2. 依赖库安装:一个都不能少的底层链路
WRF 和 CMAQ 对库的依赖关系非常明确,可以简化成一条链:zlib -> HDF5 -> netCDF-C -> netCDF-Fortran,再加上 IOAPI(CMAQ 专用)和 MPI 库。顺序不能乱,因为后一个库编译时要检测前一个库的头文件和链接库。这一节我把每一步的关键点写出来,并解释为什么必须这么做。
2.1 HDF5 与 netCDF-C:先打下数据存储的地基
我先装的是 zlib 和 szip,这两个是压缩相关的底层库,HDF5 编译时会用到。然后编译 HDF5。HDF5 是 netCDF-4 格式的底层存储引擎,相当于“仓库管理员”。装 HDF5 时,我把安装前缀设置为/home/wrfuser/wrf/libs/hdf5,编译命令大致如下:
cd /home/wrfuser/wrf/libs/src/hdf5-1.12.2 ./configure --prefix=/home/wrfuser/wrf/libs/hdf5 --enable-fortran --disable-shared --enable-static make -j8 make install这里的--enable-fortran很容易被忽略,但如果不开启,后面 netCDF-Fortran 编译时可能找不到 HDF5 的 Fortran 接口模块,报错很闹心。--disable-shared的作用我上面说了,是为了静态编译,让后面的程序不依赖动态库路径。如果你打算长期使用同一台机器,用共享库也可以,但前提是 LD_LIBRARY_PATH 必须写对。
装完 HDF5 后就是 netCDF-C。netCDF-C 是 WRF 读写气象数据的主要接口库,注意必须指定 CPPFLAGS 和 LDFLAGS,让它能找到 HDF5:
cd /home/wrfuser/wrf/libs/src/netcdf-c-4.9.2 export CPPFLAGS="-I/home/wrfuser/wrf/libs/hdf5/include" export LDFLAGS="-L/home/wrfuser/wrf/libs/hdf5/lib" ./configure --prefix=/home/wrfuser/wrf/libs/netcdf --disable-shared --enable-static make -j8 make install编译结束后,检查一下是否生成了nc-config命令,并确认nc-config --all能正常输出。这个命令在后续很多编译脚本里会被调用,如果它有问题,WRF 和 CMAQ 会一起出问题。顺便说一句,我在这一步吃过亏:当时漏掉了 CPPFLAGS 环境变量,HDF5 路径没传进去,结果 netCDF-C 编译时找不到 hdf5.h,报错“HDF5 header file not found”并直接停止。其实只要对着错误提示检查环境变量,很快就能定位,就怕你不想看编译日志。
2.2 netCDF-Fortran:接口层不能少
netCDF-C 只是 C 语言接口,WRF 和 CMAQ 的 Fortran 代码还需要 Fortran 接口,所以必须再编译一个 netCDF-Fortran。顺序上必须先装好 netCDF-C,再装 Fortran 接口,因为 configure 脚本会去查找 netcdf.h 并测试 C 编译器能否正常链接 netCDF 库。
cd /home/wrfuser/wrf/libs/src/netcdf-fortran-4.6.0 export LD_LIBRARY_PATH="/home/wrfuser/wrf/libs/hdf5/lib:/home/wrfuser/wrf/libs/netcdf/lib" export CPPFLAGS="-I/home/wrfuser/wrf/libs/hdf5/include -I/home/wrfuser/wrf/libs/netcdf/include" export LDFLAGS="-L/home/wrfuser/wrf/libs/hdf5/lib -L/home/wrfuser/wrf/libs/netcdf/lib" ./configure --prefix=/home/wrfuser/wrf/libs/netcdf --disable-shared --enable-static make -j8 make install注意这里 configure 时--prefix最好是和 netCDF-C 一样,放在同一个netcdf目录下,这样最终include目录里既有 netcdf.h 又有 netcdf.inc,lib目录里既有 libnetcdf.a 又有 libnetcdff.a,层次清楚,WRF configure 脚本找起来也方便。检查装好的标志是执行nf-config --all,如果有正常输出,说明 Fortran 接口已经生效。这一步很容易被忽略,很多人装完 netCDF-C 就不管了,结果编译 WRF 时提示找不到libnetcdff或者nf_*符号,白白浪费时间。
2.3 MPICH 与辅助工具:并行计算的血脉
MPICH 的编译相对独立,不像 HDF5 和 netCDF 之间有那么强的依赖关系。我建议把 MPICH 装在独立目录/home/wrfuser/wrf/libs/mpich,编译命令:
cd /home/wrfuser/wrf/libs/src/mpich-4.1.2 ./configure --prefix=/home/wrfuser/wrf/libs/mpich --disable-shared --enable-static make -j8 make install安装完成后,务必把mpich/bin加入 PATH,把mpich/lib加入 LD_LIBRARY_PATH。这里有个容易犯的错误:WRF configure 时如果搜不到 mpif90,会退回到串行编译模式,你看着好像编译成功了,但跑并行时根本不认 mpirun。所以编译前最好在终端里执行:
which mpif90 which mpirun确认输出是/home/wrfuser/wrf/libs/mpich/bin/...而不是/usr/bin/mpirun。如果系统自带了一个 MPI,环境变量顺序不对,就会选错 MPICH。
2.4 IOAPI 库:CMAQ 的文件读写命脉
IOAPI 是 CMAC 的 I/O API 库,专门用来读写 CMAQ 需要的 IOAPI 格式文件。它的编译比前面几个都折腾,因为不存在标准的自动 configure 脚本,而是要通过修改 Makefile 来适应环境。安装 IOAPI 时,需要先设置两个环境变量:BIN表示编译输出平台目录名,比如Linux2_x86_64gfort;BASEDIR指向 netCDF 的安装路径。然后进入ioapi目录,修改Makefile中的平台部分,或直接执行make configure交互式选择平台。以我常用的 3.2 版本为例,大致流程是:
export BIN=Linux2_x86_64gfort export BASEDIR=/home/wrfuser/wrf/libs/netcdf cd /home/wrfuser/wrf/libs/src/ioapi-3.2 make configure make make install这里的Linux2_x86_64gfort是 IOAPI 预定义的一个 BIN 目录名,对应 64 位 Linux 加 gfortran 编译器的组合。编译完成后,会在$BIN目录下生成一堆库文件,比如libioapi.a。之后 CMAQ 编译时通过环境变量IOAPI_INC_DIR和IOAPI_LIB_DIR去引用它们。如果编译 IOAPI 时找不到netcdf.inc,说明BASEDIR设置有问题,或者 netCDF 安装路径不是标准结构,需要回到第 2.2 节检查。
这部分我特别想强调一件事:所有依赖库配置完成后,一定要把环境变量写进~/.bashrc(如果默认 shell 是 bash)或者对应的 shell 配置文件里,并且把这些环境变量集中成一个独立脚本,比如/home/wrfuser/wrf/env.sh,每次打开新终端时 source 它。不要今天临时 export 一下,明天忘了,然后编译时这报错那报错。
3. WRF 模型安装与测试案例运行
依赖库装齐了,接下来是重头戏:WRF 编译。很多人以为 WRF 编译就是一条./compile -j8,其实前面还有一个关键的./configure交互选项,选错会导致后面白编译几小时。
3.1 下载 WRF 源码与理解目录分工
WRF 4.x 的主程序源码可以从官网直接下载,也支持 git clone。下载后解压,你会看到 WRF 根目录下有很多子目录,核心的几个是:main/(生成 wrf.exe 和 real.exe)、test/(放各种测试案例的 namelist 输入)、phys/、chem/(化学选项,但 CMAQ 是离线耦合,所以 WRF 里一般不启用 WRF-Chem)。
与 WRF 搭配的还有 WPS(WRF Preprocessing System),它的作用是准备气象初始场和边界场,包括三个程序:geogrid(定义模拟区域和静态地理数据)、ungrib(解码 GRIB 格式的再分析资料或预报场)、metgrid(将解码后的气象数据水平插值到模拟区域)。如果你只是跑理想化测试案例,WPS 可以暂时不装,WRF 自带ideal.exe生成理想场;但真实案例一定要装 WPS。我建议第一次学习时先把 WRF 主程序跑通,再回头搞 WPS。
3.2 configure 编译选项:别选错编号
进入 WRF 根目录,执行:
cd /home/wrfuser/wrf/WRF-4.5 export NETCDF=/home/wrfuser/wrf/libs/netcdf export PATH=/home/wrfuser/wrf/libs/mpich/bin:$PATH export LD_LIBRARY_PATH=/home/wrfuser/wrf/libs/netcdf/lib:/home/wrfuser/wrf/libs/hdf5/lib:$LD_LIBRARY_PATH ./configureconfigure 会列出几十种平台和编译方式的组合,需要输入数字选择。WRF 4.x 通常分两类:第一类选嵌套网格选项(1=basic,3=嵌套),这里测试案例选 1 就行,减少编译量;第二类选编译器,比如 gfortran + dmpar,对应可能是 34 或 35(不同小版本号会有差异),你需要查列表文字,找到类似Linux x86_64 gfortran (dmpar)的选项,记下编号。dmpar表示分布式内存并行,这是最常用的选择,对应 MPI 并行。
选完之后,打开生成的configure.wrf文件,重点检查几个变量:
NETCDF = /home/wrfuser/wrf/libs/netcdf DM_FC = mpif90 DM_CC = mpicc如果NETCDF路径是空的或者/usr,说明 configure 没有读取到环境变量 NETCDF。这种情况下编译大概率会失败,报错通常是找不到netcdf.inc或nf_*符号。修好这个文件再继续,不要硬着头皮往下跑。
3.3 编译 WRF 及理想化 em_bell 测试案例
configure 完成后,编译选 em_bell 理想化案例其实就是一条命令:
./compile em_bell >& compile.log & tail -f compile.log编译过程根据机器性能大约需要 30 分钟到 2 小时不等。结束之后,检查主目录下的main/文件夹,是否生成了wrf.exe和ideal.exe。这两个文件缺一不可,如果不存在,去compile.log末尾看报错。我见过太多人编译完不检查就直接去跑,结果提示wrf.exe不存在,才回头查日志。
跑 em_bell(这是一个背风波理想化试验)时,进入测试目录:
cd /home/wrfuser/wrf/WRF-4.5/test/em_bell ln -sf /home/wrfuser/wrf/WRF-4.5/main/ideal.exe . ln -sf /home/wrfuser/wrf/WRF-4.5/main/wrf.exe . ./ideal.exeideal.exe会读取namelist.input,生成wrfinput_d01文件。然后修改namelist.input中的运行时间。最关键的参数是time_step,它由网格距决定,经验法则是:
time_step(秒) ≤ 6 × dx(公里)比如 10km 网格距,时间步长不要超过 60 秒。如果设太大,模拟会因 CFL 条件不满足而数值发散,输出结果全是 NaN。跑并行:
mpirun -np 8 ./wrf.exe跑完后用 ncdump 或 Python 的 xarray 打开wrfout_d01_*文件,检查温度和位势高度场是否正常分布。跑通这个理想化案例,说明 WRF 主程序和底层依赖库全部正常。
3.4 真实案例的 WPS 流程简述
如果理想化案例不够过瘾,想跑真实气象案例,那要把 WPS 也编译出来,然后依次跑geogrid、ungrib、metgrid、real。以 GFS 再分析数据为例:geogrid 需要用geogrid.exe读namelist.wps帮你定义好模拟区域,并生成geo_em.d01.nc;然后 ungrib 需要把 GRIB 文件链接成FILE:YYYY-MM-DD_HH的中间文件,通过link_grib.csh脚本;最后 metgrid 将 ungrib 解出的等压面数据插值到 WRF 网格上,生成met_em.d01.*.nc。met_em 文件生成后,在 WRF 主目录用real.exe读取 met_em,生成wrfinput_d01和wrfbdy_d01,然后才能用 wrf.exe 跑正式模拟。
这一步里面坑比较多,比如 Vtable 选择不对(GFS 数据要 ln -sf 到合适的 Vtable 文件),ungrib 后变量缺失,metgrid 报错找不到输入文件等等。但作为入门,我建议先跑通 em_bell,再考虑真实案例,一步步来。
4. CMAQ 编译与测试案例运行
CMAQ 的编译和运行比 WRF 更“脚本化”,官方提供了成套的构建脚本和测试案例,这也是为什么建议先跑通 WRF 再来碰 CMAQ——两者的环境变量体系和路径引用方式有差别,混在一起容易乱。
4.1 CMAQ 版本选择与目录结构
CMAQ 目前常用的版本是 5.3.2 和 5.4,后续还有 5.5。我建议新手用 5.3.2 或者 5.4,因为网上的教程和论坛讨论最多,出问题时更容易找到解决方案。解压后你会看到scripts/目录下有多个子目录:bldit_cctm(编译 CCTM)、bldit_icon、bldit_bcon(分别生成初始浓度和边界浓度程序)、bldit_mcip(气象化学接口处理程序)等。其中 MCIP 负责把 WRF 输出的气象场转换为 CMAQ 需要的格式,CCTM 是 CMAQ 的核心化学传输模型。完整跑 CMAQ 案例的顺序是:MCIP -> ICON -> BCON -> CCTM。
4.2 配置环境变量与编译选项
CMAQ 的编译脚本依赖环境变量来定位 ioapi 和 netcdf。以 5.3.2 为例,在cmaq/scripts下执行source config.cmaq前,需要先把脚本里的变量改对。直接编辑config.cmaq,修改关键变量:
setenv CMAQ_HOME /home/wrfuser/cmaq setenv IOAPI_INC_DIR /home/wrfuser/wrf/libs/ioapi/include setenv IOAPI_LIB_DIR /home/wrfuser/wrf/libs/ioapi/Linux2_x86_64gfort setenv NETCDF /home/wrfuser/wrf/libs/netcdf这里的IOAPI_LIB_DIR要与编译 IOAPI 时的BIN目录对应。改完后执行:
cd /home/wrfuser/cmaq/scripts source config.cmaqconfig.cmaq会设置大量编译所需的环境变量,还定义了编译器名称,比如set compiler = gcc。确认无误后,进入bldit_cctm等子目录开始编译。
4.3 编译核心程序 CCTM
CMAQ 5.3.2 之后提供了自动构建脚本,比如在scripts/bldit_cctm下运行:
cd /home/wrfuser/cmaq/scripts/bldit_cctm ./bldit_cctm.csh gcc 2>&1 | tee bldit_cctm.log这里的gcc是编译器参数,脚本会自动在CMAQ_HOME下创建bld_cctm目录,并生成可执行文件CCTM_v532_gcc_xxx。编译过程中如果报错找不到 ioapi 模块,十有八九是IOAPI_INC_DIR设置不对。一个典型案例:IOAPI 编译目录的 include 目录里要有ioapi_inc.mod等 Fortran 模块文件,如果IOAPI_INC_DIR指错了路径,编译会提示cannot open module file 'ioapi_inc.mod'。这时候别怀疑编译命令,先去检查 IOAPI 是否真正安装成功、module 文件是否被生成。
CMAQ 有几个重要的编译选项需要提前了解:化学机制(gas_mechanism),比如cb6r5_ae7_aq是 5.3.2 常用的碳键机制加气溶胶模块,选什么取决于你要模拟的污染物和官方提供的案例;并行方式通常选mpi;嵌套层数默认是 1,足够测试用。这些选项在bldit_cctm.csh或者环境变量Mechanism、nested等中配置。如果以后业务化运行,可能还要调整横向和垂直层数、平流方案等,但第一次跑通不需要追求精度。
4.4 运行官方 benchmark 测试案例
CMAQ 官方提供了一套 benchmark 测试数据,里面包含了输入文件、MCIP 输出、初始条件、边界条件和观测值,专门用来验证安装是否正确。我强烈建议跑一遍官方 benchmark,而不是自己瞎造输入。方法是在论坛或官网找到与 CMAQ 版本对应的 benchmark 数据下载链接,解压后按数据集自带说明放到指定目录。之后进入scripts/run_cctm目录,编辑run_cctm.csh脚本,把输入输出路径、模拟时间窗口、网格描述、进程数等参数配置好。
模拟时间段示例:
setenv START_DATE 2016-07-01 setenv END_DATE 2016-07-02 setenv TIME_STEP 010000然后执行:
csh run_cctm.cshCCTM 会读取GRIDDESC(网格描述文件)、MCIP 输出的气象文件、ICON/BCON 浓度文件、排放清单文件等,逐小时向前积分。运行过程中会生成CTM_LOG_001之类的日志文件,建议实时查看,观察时间步是否正常推进、有无浮点错误或文件读取异常。
运行结束后,可以用官方脚本或 Python 打开 CCTM 输出的CCTM_CCTM_v532_gcc_*文件,检查 PM2.5、O3、NO2 等浓度场是否合理。一个常见问题:如果 ICON/BCON 文件的时间范围与 CCTM 设置不一致,程序会报“time conflict”错误,这时需要检查初始边界条件文件的日期是否覆盖模拟时段。
4.5 MCIP、ICON 和 BCON 的准备
虽然 benchmark 案例一般会提供 MCIP 输出和 ICON/BCON 文件,但如果你想跑自己的真实案例,这三个预处理程序必须自己生成。MCIP 的输入是 WRF 的 wrfout 文件,输出是 CMAQ 可读取的 METCRO3D、METDOT3D 等气象文件。ICON 和 BCON 分别生成初始浓度场和边界浓度场,可使用默认的 profile 数据,也可以从全球化学模式(比如 GEOS-Chem、MOZART)的结果插值而来。这个链条比较长,新手容易在文件格式和时间格式上出错,我建议先把 benchmark 跑通,再对照着替换自己的文件。
5. 常见问题与排查技巧实录
跑 WRF 和 CMAQ 时踩坑是家常便饭,这里我把几类最常见的问题和排查思路整理成表格,方便你对号入座。
5.1 编译阶段高频报错
| 报错特征 | 可能原因 | 排查与解决 |
|---|---|---|
configure: error: NetCDF not found | NETCDF 环境变量未设置或路径不对 | 检查echo $NETCDF,确认指向 netcdf 安装根目录 |
cannot find -lnetcdff | netCDF-Fortran 未安装,或库路径未加入 LDFLAGS | 安装 netCDF-Fortran,并确认 libnetcdff.a 在 netcdf/lib 下 |
cannot open module file ioapi_inc.mod | IOAPI 编译未成功或 IOAPI_INC_DIR 指向错误 | 检查 ioapi/BIN/include 下是否有 .mod 文件 |
mpif90: command not found | MPI 环境变量未生效,PATH 中没有 mpich/bin | 重新 source 环境,确认which mpif90输出正确 |
编译中断且日志末尾是killed | 内存不足,OOM 被系统杀掉 | 减少make -j并发数,或增大机器内存/swap |
其中内存不足这个问题要特别提一句:WRF 和 CMAQ 的编译都是高度并行的,make -j8时同时编译 8 个文件,每个文件编译都可能占用几百 MB 内存,如果机器只有 16GB 内存,再开其他大程序,很容易触发 OOM。如果你看到编译日志戛然而止、没有明确的编译错误,第一反应就去看dmesg | tail或journalctl -k有没有 OOM 记录。
5.2 运行阶段典型异常
| 现象 | 可能原因 | 排查思路 |
|---|---|---|
| wrf.exe 运行几小时后输出全是 NaN | 时间步长过大,CFL 条件不满足 | 减小 time_step,按 6×dx 经验法则设置 |
CCTM 运行报Time conflict | 输入文件的日期与模拟时段不匹配 | 检查 MCIP/ICON/BCON 文件的时间记录变量,确保覆盖模拟窗口 |
CCTM 运行中途崩溃,日志里有forrtl: severe (174) | 读取文件时遇到 EOF 或数据缺失 | 检查排放清单文件、气象文件是否有时间跳变 |
mpirun 报There are not enough slots | 请求进程数超过节点可用的核心数 | 降低-np参数,或配置--oversubscribe |
| 某个网格点污染物浓度异常高(十万量级) | 排放清单存在尖峰排放或气象场有强辐合 | 检查该网格点的排放数据和时间变化 profile |
说实话,WPS/CMAQ 的错误信息有时候写得不清不楚,排查手段主要是“看日志 + 二分法”。我的经验是:先跑短时段(比如 3 小时),确认能正常结束,再逐步延长;跑之前先检查磁盘剩余空间,不然跑一半磁盘满了,日志不报错但 output 文件写不进去,特别坑。
5.3 环境变量配置是最隐蔽的坑
我见过太多人卡在环境变量上:同一个终端里前一条命令用export设置好了,下一条命令里因为换行或 source 了其他脚本就被覆盖了;或者~/.bashrc里有老的 WRF 路径,新路径没生效。环境变量配置一开始就应从正规做法入手:写一个独立的环境配置脚本,统一管理。
我现在的做法是这样的:在/home/wrfuser/wrf/下放一个env.sh,内容大致是:
export WRF_ROOT=/home/wrfuser/wrf export NETCDF=$WRF_ROOT/libs/netcdf export HDF5=$WRF_ROOT/libs/hdf5 export PATH=$WRF_ROOT/libs/mpich/bin:$NETCDF/bin:$PATH export LD_LIBRARY_PATH=$NETCDF/lib:$HDF5/lib:$WRF_ROOT/libs/mpich/lib:$LD_LIBRARY_PATH export IOAPI_INC_DIR=$WRF_ROOT/libs/ioapi/include export IOAPI_LIB_DIR=$WRF_ROOT/libs/ioapi/Linux2_x86_64gfort每次打开新的终端,先source /home/wrfuser/wrf/env.sh,再去做编译或运行。这样环境始终一致,不会出现“昨天还好好的,今天报错找不到库”的尴尬。另外,不要把临时 export 写死在/etc/profile里,系统更新或重启后容易出乱子,用户的独立脚本最保险。
6. 实操总结与个人扩展建议
最后聊一点个人体会。WRF-CMAQ 这套东西,第一感觉是编译配置繁琐,第二感觉是运行门槛高,但真正跑通一次之后,你会发现它的架构其实非常清晰:一个环节的输出就是下一个环节的输入,只要把链路打通,剩下的事情就是攒案例、调参数。
我个人在实际操作中比较推荐的做法是:先跑官方 benchmark,不要一开始就追求自己的研究区域。因为 benchmark 的输入输出文件都验证过,跑通它能帮你确认“软件安装没问题,问题只可能在你的数据或配置上”。很多新手一上来就搞自己省市的模拟区域,WRF 跑了半天发现地形数据缺失或者 met_em 文件生成失败,然后怀疑是编译问题,其实编译早就成功了,是处理流程不熟。
另外分享一个小技巧:在跑长时间模拟之前,先跑一个 1 小时或 3 小时的短测试,检查 wrfout 和 CCTM 输出里的关键变量是否有正常的空间分布,没有明显异常再正式跑。我在实际工作中,经常用 Python 的 xarray 快速打开 wrfout 和 CCTM 输出文件,画出几个层位的温度场、风场和 PM2.5 浓度图,一分钟就能判断结果是否合理。你可以把常用的绘图脚本存成一个模板,以后每次模拟完直接套用,能节约大量时间。
如果你后续要深入研究,方向可以往这几个方面扩展:一是 WRF 的物理参数化方案选择,比如边界层方案、微物理方案对气象场的影响;二是 CMAQ 的化学机制差异,比如 CB6 和 SAPRC 系列对臭氧模拟的影响;三是排放清单的处理,这通常是决定模拟精度最关键的一环,也是业务化和科研中最耗时的部分。无论往哪个方向走,先把环境配置和测试案例运行这一步踏踏实实走完,后面就顺了。