> 适用人群:环境监测/环评从业者,零基础入门
> 预计学习时间:4-6周
> 配套脚本:`aermod_postprocess.py`
> 编写日期:2026-08-08
---
## 目录
- [1. AERMOD 简介](#1-aermod-简介)
- [2. 理论基础](#2-理论基础)
- [3. 软件获取与安装](#3-软件获取与安装)
- [4. AERMET 气象预处理](#4-aermet-气象预处理)
- [5. AERMAP 地形预处理](#5-aermap-地形预处理)
- [6. AERMOD 主模型输入文件](#6-aermod-主模型输入文件)
- [7. 运行模型](#7-运行模型)
- [8. 结果后处理与出图](#8-结果后处理与出图)
- [9. 实战案例:钢铁厂SO₂排放预测](#9-实战案例钢铁厂so₂排放预测)
- [10. 常见问题与排查](#10-常见问题与排查)
- [附录A:完整输入文件模板](#附录a完整输入文件模板)
- [附录B:参数速查表](#附录b参数速查表)
---
## 1. AERMOD 简介
### 1.1 什么是 AERMOD
AERMOD 是美国环保署(EPA)开发的**稳态高斯烟羽扩散模型**,用于模拟工业源排放的污染物在 **50km 范围内**的浓度分布。它是:
- 美国 EPA 法规推荐模型(40 CFR Part 51, Appendix W)
- 中国《环境影响评价技术导则 大气环境》(HJ 2.2-2018)一级评价推荐模型
- 目前国内环评报告中最常用的大气扩散模型
### 1.2 AERMOD 模型体系
AERMOD 不是一个单独的程序,而是一套工具链:
```
┌──────────┐ ┌──────────┐ ┌──────────┐
│ AERMET │ │ AERMAP │ │ AERMOD │
│ 气象处理 │ │ 地形处理 │ │ 主模型 │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
▼ ▼ ▼
.SFC + .PFL .DEM .OUT + .PLT
气象文件 地形文件 结果文件
```
| 程序 | 功能 | 输入 | 输出 |
|------|------|------|------|
| AERMET | 气象数据预处理 | 地面气象 + 探空数据 | .SFC(地面参数)+ .PFL(廓线) |
| AERMAP | 地形数据预处理 | DEM 高程数据 | .DEM(受体点地形参数) |
| AERMOD | 浓度场计算 | .inp + 气象 + 地形 | .OUT(结果表)+ .PLT(浓度场) |
### 1.3 AERMOD 能做什么
- 计算点源、面源、体源、线源排放的污染物浓度
- 支持 1小时、24小时、月、年多种平均时间
- 复杂地形和平坦地形都能处理
- 城市和乡村扩散条件
- 多源叠加计算
- 网格受体 + 离散受体(敏感点)
### 1.4 AERMOD 不能做什么
- 不能模拟化学反应(如二次PM2.5生成)
- 不能模拟远距离传输(>50km用CALPUFF)
- 不能模拟沿海熏烟效应(用CALPUFF)
- 不能处理建筑物下洗效应(需配合BPIPPRM程序预处理)
---
## 2. 理论基础
### 2.1 高斯烟羽模型
AERMOD 的核心是高斯烟羽方程。对于高架连续点源,地面浓度公式为:
```
Q y² (H - z)²
C(x,y,z) = ───────────────────── × exp(─ ──) × exp(─ ──────)
2π × u × σ_y × σ_z 2σ_y² 2σ_z²
```
**各参数含义:**
| 符号 | 含义 | 单位 | 说明 |
|------|------|------|------|
| C | 污染物浓度 | g/m³ | 模型输出值 |
| Q | 源排放速率 | g/s | 污染物排放量 |
| u | 烟囱高度处风速 | m/s | 影响传输速度 |
| σ_y | 横向扩散参数 | m | 水平方向扩散宽度 |
| σ_z | 垂向扩散参数 | m | 垂直方向扩散宽度 |
| H | 烟囱有效高度 | m | 物理高度 + 抬升高度 |
| x | 下风向距离 | m | 离源距离 |
| y | 横风向偏移 | m | 偏离中心线距离 |
| z | 计算高度 | m | 通常取1.5m(呼吸带) |
**关键理解:**
- 风把烟羽往下游吹,浓度在烟羽中心线最高
- σ_y 和 σ_z 随下风距离增大而增大(烟羽越来越宽)
- 离源越远浓度越低(稀释扩散)
### 2.2 大气稳定度
大气稳定度决定了扩散的快慢。Pasquill 分类法将稳定度分为6级:
| 类别 | 描述 | 大气状态 | 扩散能力 |
|------|------|----------|----------|
| A | 极不稳定 | 强日照、微风 | 最强 |
| B | 中度不稳定 | 中等日照 | 强 |
| C | 弱不稳定 | 弱日照 | 中等 |
| D | 中性 | 多云/强风 | 中等 |
| E | 弱稳定 | 弱辐射、微风 | 弱 |
| F | 稳定 | 晴夜、微风 | 最弱 |
**稳定度查表法(Pasquill-Turner):**
根据太阳辐射(白天)或云量(夜间)+ 风速查表:
```
风速 (m/s)
<2 2-3 3-4 4-6 >6
白天强日 A A B B C
白天中日 A B B C C
白天弱日 B B C C D
夜间多云 - E D D D
夜间晴 - F E D D
```
> **AERMOD 不直接用 Pasquill 分类**,而是用更精确的莫宁-奥布霍夫长度(Monin-Obukhov length)和混合层高度来计算扩散参数。但理解 Pasquill 分类有助于建立直觉。
### 2.3 扩散参数
扩散参数 σ_y 和 σ_z 表示烟羽在横向和垂向的"展开宽度"。
**经验公式(Pasquill-Gifford):**
```
σ_y = a × x^b
σ_z = c × x^d
```
其中 a, b, c, d 是随稳定度变化的系数,x 是下风距离。
**示例值(D类稳定度):**
| 距离 x (m) | σ_y (m) | σ_z (m) |
|------------|---------|---------|
| 100 | 8 | 4 |
| 1000 | 70 | 30 |
| 5000 | 300 | 120 |
| 10000 | 550 | 220 |
| 50000 | 2500 | 800 |
> AERMOD 内部使用的是更复杂的 P-G 扩散曲线的连续函数形式,无需手动计算。
### 2.4 烟气抬升
热烟气排出烟囱后会因浮力继续上升,实际扩散高度 > 烟囱物理高度:
```
H_effective = H_stack + ΔH
```
ΔH 为抬升高度,常用 **Briggs 公式**计算:
**浮力抬升(热烟气):**
```
ΔH = 1.6 × F^(1/3) × x^(2/3) / u (x < x_f,最终抬升前)
其中:F = g × V_s × (D/2)² × (T_s - T_a) / T_s
g = 9.8 m/s²(重力加速度)
V_s = 烟气出口流速 (m/s)
D = 烟囱出口内径
T_s = 烟气温度 (K)
T_a = 环境温度 (K)
```
**关键影响因素:**
- 烟气温度越高 → 抬升越大 → 地面浓度越低
- 出口流速越大 → 抬升越大(动量抬升)
- 风速越大 → 抬升越小(被风压下来)
- 环境温度越低 → 温差越大 → 抬升越大
### 2.5 混合层高度
混合层是污染物能垂直扩散的上边界。AERMOD 中:
- **对流混合层**(白天):由地表加热产生,可达1-2km
- **机械混合层**(夜间):由风切变产生,通常几百米
当烟羽碰到混合层顶时会被反射回地面,AERMOD 会自动处理这种多次反射。
---
## 3. 软件获取与安装
### 3.1 下载地址
**EPA 官方下载(免费):**
```
https://www.epa.gov/scram/air-quality-dispersion-modeling-preferred-and-alternative-models
```
需要下载的文件:
| 文件名 | 说明 | 大小 |
|--------|------|------|
| AERMOD_Executable | AERMOD 主程序 | ~5MB |
| AERMET_Executable | AERMOD 气象预处理 | ~3MB |
| AERMAP_Executable | AERMOD 地形预处理 | ~3MB |
| BPIPPRM_Executable | 建筑物下洗预处理(可选) | ~2MB |
### 3.2 安装步骤
AERMOD 是命令行程序,无需安装,解压即用。
**步骤1:创建工作目录**
```
C:\AERMOD\
├── bin\ ← 放可执行文件
├── projects\ ← 放项目文件
└── data\ ← 放气象和地形数据
```
**步骤2:复制可执行文件**
将下载的 `aermod.exe`、`aermet.exe`、`aermap.exe` 复制到 `C:\AERMOD\bin\`
**步骤3:添加环境变量(可选但推荐)**
将 `C:\AERMOD\bin` 添加到系统 PATH:
```
右键此电脑 → 属性 → 高级系统设置 → 环境变量
→ 系统变量 → Path → 编辑 → 新建 → C:\AERMOD\bin
```
**步骤4:验证安装**
打开命令提示符,输入:
```cmd
aermod
```
如果显示 AERMOD 版本信息,说明安装成功。
### 3.3 商业GUI替代方案
如果不想用命令行,可以考虑:
| 软件 | 特点 | 价格 |
|------|------|------|
| BREEZE AERMOD | 美国商业软件,界面成熟 | 付费 |
| Lakes EISPro | 国内常用,有中文界面 | 付费 |
| AERMOD View | 商业封装 | 付费 |
| 环评助手AERMOD | 国产,有试用版 | 付费 |
> 建议先用命令行版跑通流程,理解原理后再考虑商业软件。
---
## 4. AERMET 气象预处理
### 4.1 概述
AERMET 将原始气象数据加工成 AERMOD 能识别的格式。处理分三个阶段:
```
Stage 1: 质量控制(QC)
原始地面数据 + 原始探空数据 → 经过质量控制的中间文件
Stage 2: 合并
地面中间文件 + 探空中间文件 → 合并文件
Stage 3: 输出
合并文件 + 站点参数 → .SFC + .PFL
```
### 4.2 输入数据格式
#### 4.2.1 地面气象数据
需要逐时数据(8760条/年),格式为 **CD-144 / SCRAM** 格式或 **ISHD** 格式。
**CD-144 格式字段说明:**
```
位置 字段 说明
1-6 站号 WBAN编号
7-12 日期 YYMMDD
13-14 时 00-23(GMT)
15-19 风向 0-360度(16方位×22.5度)
20-24 风速 节或m/s
25-30 干球温度 华氏度或摄氏度
31-35 云量(总) 0-10
36-40 云量(低) 0-10
```
> 实际操作中,通常从气象局获取CSV格式数据,再用脚本转成AERMET需要的格式。
#### 4.2.2 探空数据
需要至少一天两次(00Z和12Z)的高空探测数据,格式为 **FSL** 格式。
包含各高度层的:温度、露点、风向、风速、高度。
**获取途径:**
- 美国NOAA的FSL格式数据:`https://ruc.noaa.gov/raobs/`
- 中国气象局数据服务
- 最近的探空站(山东省:济南、青岛、郑州等)
### 4.3 AERMET 控制流文件
AERMET 通过一个控制流文件(control file)运行。以下是完整模板:
```
JOB AERMET 气象预处理控制文件
MESSAGES aermet.log
REPORT aermet.rep
SUMMARY aermet.sum
END
UPPERAIR 探空数据段
DATA sounding.fsl ! FSL格式探空数据
EXTRACT sounding.qa ! Stage 1 输出
AUDIT AUDIT_SND ! 审计变量
END
SURFACE 地面数据段
DATA surface.dat ! CD-144格式地面数据
EXTRACT surface.qa ! Stage 1 输出
AUDIT AUDIT_SFC ! 审计变量
END
MERGE Stage 2 合并段
INPUT surface.qa ! Stage 1 的地面输出
INPUT sounding.qa ! Stage 1 的探空输出
OUTPUT merge.dat ! 合并后文件
END
METPREP Stage 3 输出段
DATA merge.dat ! Stage 2 的合并文件
OUTPUT project.sfc ! 地面参数文件(给AERMOD用)
OUTPUT project.pfl ! 廓线文件(给AERMOD用)
AUDIT AUDIT_MET ! 审计变量
SEASON 4 ! 4=全年
UAWIND 10.0 ! 风速阈值(m/s),低于此值设为缺测
THRESHOLD 0.5 ! 最小风速阈值(m/s)
END
```
### 4.4 站点参数文件
需要创建一个站点参数文件 `siteinfo.dat`:
```
36.85 117.85 20.0 ! 纬度、经度、海拔(m)
-6.0 ! 时区(东八区 = -8,此处为UTC+8的负值)
```
> 时区说明:AERMET使用UTC时间。东八区需要根据实际处理方式调整。
### 4.5 运行 AERMET
```cmd
aermet aermet.inp
```
运行成功后生成:
- `project.sfc` — 地面参数文件
- `project.pfl` — 垂直廓线文件
- `aermet.log` — 运行日志
### 4.6 检查输出
打开 `aermet.log`,检查:
- 是否有大量缺测数据(>10%需要补测)
- 风速、温度等是否有异常值
- 探空数据是否覆盖完整
---
## 5. AERMAP 地形预处理
### 5.1 概述
AERMAP 读取 DEM 高程数据,为每个受体点计算:
- 地形高程
- 临界分隔高度(critical dividing height, Hc)
这些参数用于 AERMOD 的复杂地形算法。
### 5.2 获取 DEM 数据
**免费DEM数据源:**
| 数据源 | 分辨率 | 获取地址 |
|--------|--------|----------|
| SRTM | 30m / 90m | https://earthexplorer.usgs.gov/ |
| ASTER GDEM | 30m | https://gdemdl.earthdata.nasa.gov/ |
| 地理空间数据云 | 30m | https://www.gscloud.cn/ |
**下载步骤:**
1. 确定项目区域的经纬度范围
2. 在数据网站选择对应瓦片
3. 下载 GeoTIFF 格式
4. 如果需要多个瓦片,用 GDAL 合并
```cmd
gdal_merge.py -o merged_dem.tif tile1.tif tile2.tif
```
### 5.3 AERMAP 控制流文件
```
JOB AERMAP 地形预处理
MESSAGES aermap.log
REPORT aermap.rep
END
INPUTPARAM 输入参数
DATATYPE 1 ! 1=DEM格式
ANCHOR 36.85 117.85 ! 锚点经纬度(项目中心点)
END
INPUTFILE 输入DEM文件
merged_dem.tif
END
RECEPTORS 受体点定义
GRIDCART 笛卡尔网格
GRIDCOR 0.0 0.0 ! 网格原点UTM坐标
GRIDSIZE 50 ! 网格间距(m)
GRIDNUM 100 100 ! X方向100个 × Y方向100个
ENDGRID
DISCCART 离散受体点
DISCCART 25000 7500 ! 受体1的UTM坐标
DISCCART 27000 8500 ! 受体2的UTM坐标
DISCCART 23000 9500 ! 受体3的UTM坐标
ENDDISC
END
OUTPUT 输出文件
project.dem ! 输出给AERMOD用的地形文件
END
```
### 5.4 运行 AERMAP
```cmd
aermap aermap.inp
```
输出 `project.dem` 文件,包含每个受体点的地形信息。
### 5.5 何时需要 AERMAP
| 地形类型 | 是否需要AERMAP |
|----------|----------------|
| 平坦地形(高差<50m) | 不需要,设 FLATTERRAIN |
| 起伏地形 | 需要 |
| 山区/复杂地形 | 必须需要 |
---
## 6. AERMOD 主模型输入文件
AERMOD 的输入文件(.inp)是一个纯文本文件,由5个段组成:
```
CO ─── 控制参数(Control)
SO ─── 污染源(Source)
ME ─── 气象(Meteorology)
RE ─── 受体(Receptor)
OU ─── 输出(Output)
```
### 6.1 CO 段:控制参数
```
CO STARTING
TITLEONE AERMOD 示例项目 - 钢铁厂SO2排放预测
TITLETWO 环评一级评价
MODELOPT REGULATIVE CONCERVE ! REGULATIVE=法规模式, CONCERVE=浓度
AVERTIME 1 24 ANNUAL ! 计算1小时、24小时、年平均
URBANOPT 500000 ! 城市人口(>0时启用城市扩散)
POLLUTID SO2 ! 污染物类型
FLAGPOLE 1.5 ! 计算高度1.5m(呼吸带)
RUNORNOT RUN ! RUN=运行, NOTRUN=只检查输入
ELEVATED 1 ! 1=使用地形高程数据
CO FINISHED
```
**关键参数说明:**
| 参数 | 取值 | 说明 |
|------|------|------|
| MODELOPT | REGULATIVE | 法规模式(默认推荐) |
| | CONCERVE | 计算浓度 |
| | DEPOSITVE | 计算干湿沉降 |
| AVERTIME | 1 | 1小时平均 |
| | 24 | 24小时平均 |
| | ANNUAL | 年平均 |
| | MONTH | 月平均 |
| URBANOPT | 0 | 乡村模式 |
| | >0 | 城市模式,数值为城市人口 |
| POLLUTID | SO2/NO2/PM2.5/其他 | 污染物标识 |
| | OTHER | 自定义污染物 |
| FLAGPOLE | 0.0 | 地面浓度 |
| | 1.5 | 呼吸带高度(推荐) |
| ELEVATED | 0 | 平坦地形 |
| | 1 | 使用地形数据 |
### 6.2 SO 段:污染源
#### 6.2.1 点源(POINT)
```
SO STARTING
LOCATION STACK1 POINT 250000 4100000 80.0 ! UTM坐标 + 烟囱高度(m)
SRCPARAM STACK1 12.5 80.0 3.0 15.0 423.0 ! 参数见下
...
SO FINISHED
```
**SRCPARAM 参数:**
```
SRCPARAM 源名 Q H D Vs Ts
g/s m m m/s K
```
| 位置 | 参数 | 含义 | 单位 | 示例 |
|------|------|------|------|------|
| 1 | 源名 | 标识符 | - | STACK1 |
| 2 | Q | 排放速率 | g/s | 12.5 |
| 3 | H | 烟囱高度 | m | 80.0 |
| 4 | D | 烟囱出口内径 | m | 3.0 |
| 5 | Vs | 烟气出口流速 | m/s | 15.0 |
| 6 | Ts | 烟气温度 | K | 423.0(=150°C) |
> Ts 是开尔文温度,摄氏温度 + 273.15 转换。
#### 6.2.2 面源(AREA)
```
LOCATION AREA1 AREA 249000 4099000 0.0 ! 面源中心坐标 + 高度
SRCPARAM AREA1 0.005 500 500 2.0 300.0 ! 参数见下
```
**AREA 参数:**
```
SRCPARAM 源名 Q XLEN YLEN H Ts
g/s/m² m m m K
```
#### 6.2.3 体源(VOLUME)
```
LOCATION VOL1 VOLUME 250000 4100000 20.0 ! 中心坐标 + 高度
SRCPARAM VOL1 5.0 20.0 15.0 ! Q, 初始σy, 初始σz
```
#### 6.2.4 多个源
可以定义任意数量的源:
```
LOCATION STACK1 POINT 250000 4100000 80.0
SRCPARAM STACK1 12.5 80.0 3.0 15.0 423.0
LOCATION STACK2 POINT 250500 4100200 60.0
SRCPARAM STACK2 8.0 60.0 2.5 12.0 413.0
LOCATION AREA1 AREA 249000 4099000 0.0
SRCPARAM AREA1 0.005 500 500 2.0 300.0
```
#### 6.2.5 源参数组(可选)
对同类源进行分组:
```
SRCGROUP ALL ! 所有源归为一组
SRCGROUP GROUP1 STACK1 STACK2 ! 指定源归为一组
```
### 6.3 ME 段:气象
```
ME STARTING
INPUT FILE project.sfc ! AERMET 输出的地面文件
INPUT FILE project.pfl ! AERMET 输出的廓线文件
PROFBASE 20.0 ! 基准海拔(m)
ME FINISHED
```
| 参数 | 说明 |
|------|------|
| INPUT FILE | 两个文件路径 |
| PROFBASE | 地面站海拔高度(m),用于地形修正 |
### 6.4 RE 段:受体
#### 6.4.1 笛卡尔网格受体
```
RE STARTING
GRIDCART
GRIDCOR 249000 4099000 ! 网格原点UTM坐标(X, Y)
GRIDSIZE 200 ! 网格间距(m)
GRIDNUM 200 200 ! X方向200个 × Y方向200个
ENDGRID
RE FINISHED
```
此设置创建 200×200 = 40000个受体点,覆盖范围 40km × 40km。
#### 6.4.2 极坐标网格
```
GRIDPOLR
GRIDPOLR 250000 4100000 ! 原点坐标
GRIDDIST 100 200 500 1000 2000 5000 10000 20000 50000 ! 距离(m)
GRIDDIR 36 ! 方向数(36=每10度)
ENDGRID
```
#### 6.4.3 离散受体(敏感点)
```
DISCCART
DISCCART 252000 4102000 ! 受体1: 某居民区
DISCCART 248000 4101000 ! 受体2: 某学校
DISCCART 255000 4098000 ! 受体3: 某医院
ENDDISC
```
可以为离散受体命名(可选):
```
DISCCART
DISCCART 252000 4102000 RESIDENT_1
DISCCART 248000 4101000 SCHOOL_1
ENDDISC
```
### 6.5 OU 段:输出
```
OU STARTING
RECTABLE ALLAVE 1ST ! 输出所有源组、所有平均时间的最高值表
RECTABLE ALLAVE 2ND ! 第二高值
MAXTABLE ALLAVE ! 每个受体最大值汇总
PLOTFILE 1 ALL 1 plotfile.plt ! 输出1小时浓度场
PLOTFILE 24 ALL 1 plotfile24.plt ! 输出24小时浓度场
PLOTFILE ANNUAL ALL plotfile_yr.plt ! 输出年平均浓度场
SUMMFILE summary.txt ! 统计摘要文件
OU FINISHED
```
**PLOTFILE 参数说明:**
```
PLOTFILE 平均时间 源组 [序号] 文件名
```
| 平均时间 | 值 |
|----------|-----|
| 1小时 | 1 |
| 24小时 | 24 |
| 月 | MONTH |
| 年 | ANNUAL |
序号表示第几高值(1ST=最高, 2ND=第二高),年平均不用序号。
---
## 7. 运行模型
### 7.1 运行命令
```cmd
aermod project.inp
```
### 7.2 运行流程
```
1. 确保所有文件在同一目录或路径正确:
project.inp
project.sfc
project.pfl
project.dem (如果有地形)
2. 打开命令提示符
3. cd 到项目目录
4. 运行 aermod project.inp
5. 等待计算完成(通常1-30分钟,取决于受体数量和源数量)
```
### 7.3 输出文件
| 文件 | 内容 |
|------|------|
| project.out | 主结果文件,包含所有浓度数据表 |
| plotfile.plt | 网格浓度场(用于画等值线图) |
| summary.txt | 统计摘要 |
| project.tmp | 临时文件 |
### 7.4 检查运行结果
打开 `project.out`,检查:
1. **是否正常运行**:文件开头应该有 "AERMOD FINISHED SUCCESSFULLY"
2. **是否有警告**:搜索 "WARNING",评估是否影响结果
3. **最大浓度**:找到各平均时间的最大浓度值
4. **占标率**:与标准限值比较
---
## 8. 结果后处理与出图
### 8.1 POSTFILE 格式解析
AERMOD 的 PLOTFILE 是固定宽度文本格式:
```
第一行:标题行
第二行:列数、网格信息等
数据行:X Y 浓度值
```
每行包含:
- 第1-3列:受体编号
- X坐标 (UTM)
- Y坐标 (UTM)
- 平均时间标记
- 浓度值 (µg/m³)
### 8.2 Python 后处理脚本
配套脚本 `aermod_postprocess.py` 提供以下功能:
1. 解析 POSTFILE
2. 绘制浓度等值线图
3. 叠加卫星底图
4. 标注污染源和敏感点
5. 计算占标率
6. 生成报告表格
脚本使用方法见下一章实战案例。
### 8.3 QGIS 出图方案
如果不想用 Python,可以用 QGIS:
1. 将 `.plt` 文件导入 QGIS(Add Layer → Delimited Text)
2. 设置 X/Y 坐标字段
3. 使用 IDW 或 TIN 插值生成栅格
4. 叠加卫星底图
5. 出图导出
---
## 9. 实战案例:钢铁厂SO₂排放预测
### 9.1 项目背景
模拟某钢铁厂烧结机烟气排放SO₂对周边敏感点的影响。
| 参数 | 值 |
|------|-----|
| 源位置 | UTM: 250000E, 4100000N (示例) |
| 排放速率 Q | 12.5 g/s (约400 t/a) |
| 烟囱高度 H | 80 m |
| 烟囱内径 D | 3.0 m |
| 烟气流速 Vs | 15.0 m/s |
| 烟气温度 Ts | 423 K (150°C) |
| 环境温度 Ta | 293 K (20°C) |
| 评价范围 | 10km × 10km |
| 网格间距 | 200 m |
| 敏感点 | 5个(居民区、学校、医院) |
### 9.2 完整输入文件
见 [附录A](#附录a完整输入文件模板)。
### 9.3 运行步骤
```cmd
REM 1. 准备目录
mkdir C:\AERMOD\projects\steel_plant
cd C:\AERMOD\projects\steel_plant
REM 2. 复制文件
REM steel_plant.inp ← 输入文件
REM project.sfc ← AERMET输出的地面气象文件
REM project.pfl ← AERMET输出的探空文件
REM project.dem ← AERMAP输出的地形文件
REM 3. 运行AERMOD
aermod steel_plant.inp
REM 4. 检查输出
type steel_plant.out | findstr "MAXIMUM"
```
### 9.4 后处理出图
使用配套Python脚本:
```python
# -*- coding: utf-8 -*-
"""AERMOD 后处理 - 读取浓度场并绘图"""
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import font_manager
from scipy.interpolate import griddata
# 中文支持
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
# ============ 1. 读取 POSTFILE ============
def read_plotfile(filename):
"""解析 AERMOD PLOTFILE,返回 X, Y, Concentration 数组"""
x_list, y_list, c_list = [], [], []
with open(filename, 'r') as f:
lines = f.readlines()
# 跳过前几行标题
for line in lines[6:]:
parts = line.split()
if len(parts) >= 5:
try:
x = float(parts[1])
y = float(parts[2])
c = float(parts[4])
x_list.append(x)
y_list.append(y)
c_list.append(c)
except ValueError:
continue
return np.array(x_list), np.array(y_list), np.array(c_list)
# ============ 2. 网格化 ============
def grid_concentration(x, y, c, resolution=200):
"""将离散点插值为规则网格"""
xi = np.linspace(x.min(), x.max(), resolution)
yi = np.linspace(y.min(), y.max(), resolution)
Xi, Yi = np.meshgrid(xi, yi)
Ci = griddata((x, y), c, (Xi, Yi), method='linear')
return Xi, Yi, Ci
# ============ 3. 绘图 ============
def plot_concentration(Xi, Yi, Ci, sources, receptors, output_file):
"""绘制浓度等值线图"""
fig, ax = plt.subplots(figsize=(12, 9), dpi=150)
# 等值线填色
levels = np.linspace(0, np.nanmax(Ci), 15)
cf = ax.contourf(Xi, Yi, Ci, levels=levels, cmap='Reds', alpha=0.6)
cl = ax.contour(Xi, Yi, Ci, levels=levels[::2], colors='red',
linewidths=0.5, alpha=0.7)
ax.clabel(cl, inline=True, fontsize=8, fmt='%.1f')
# 色条
cbar = fig.colorbar(cf, ax=ax, shrink=0.8, pad=0.02)
cbar.set_label('SO₂浓度 (µg/m³)', fontsize=12)
# 污染源
for src in sources:
ax.scatter(src['x'], src['y'], s=100, c='red',
edgecolor='white', linewidth=1.5, zorder=5,
marker='o', label='污染源' if src == sources[0] else '')
ax.annotate(src['name'], xy=(src['x'], src['y']),
xytext=(8, 8), textcoords='offset points',
fontsize=9, color='darkred')
# 敏感点
for rec in receptors:
ax.scatter(rec['x'], rec['y'], s=80, c='royalblue',
edgecolor='white', linewidth=1.2, zorder=5,
marker='^', label='敏感点' if rec == receptors[0] else '')
ax.annotate(f"{rec['name']}\n{rec.get('conc', 'N/A')}",
xy=(rec['x'], rec['y']),
xytext=(8, -15), textcoords='offset points',
fontsize=8, color='navy',
bbox=dict(boxstyle='round,pad=0.3',
fc='white', ec='steelblue', alpha=0.85))
# 风向箭头
ax.annotate('', xy=(Xi.max()*0.95, Yi.max()*0.9),
xytext=(Xi.max()*0.8, Yi.max()*0.9),
arrowprops=dict(arrowstyle='->', lw=2, color='darkred'))
ax.text(Xi.max()*0.875, Yi.max()*0.92, '主导风向',
fontsize=10, color='darkred', ha='center')
# 坐标和标题
ax.set_xlabel('UTM X (m)', fontsize=12)
ax.set_ylabel('UTM Y (m)', fontsize=12)
ax.set_title('AERMOD 烟羽扩散影响预测 - SO₂ 1小时最大浓度', fontsize=14)
ax.grid(True, linestyle='--', alpha=0.3)
ax.legend(loc='upper right', fontsize=10)
ax.set_aspect('equal')
plt.tight_layout()
plt.savefig(output_file, dpi=200, bbox_inches='tight')
plt.show()
print(f'已保存: {output_file}')
# ============ 4. 占标率计算 ============
def calc_standard_rate(conc, standard=500):
"""计算占标率"""
rate = (conc / standard) * 100
return rate
# ============ 5. 主程序 ============
if __name__ == '__main__':
# 读取POSTFILE
x, y, c = read_plotfile('plotfile_1hr.plt')
# 网格化
Xi, Yi, Ci = grid_concentration(x, y, c, resolution=200)
# 污染源
sources = [
{'name': '烧结机烟囱', 'x': 250000, 'y': 4100000}
]
# 敏感点
receptors = [
{'name': '宝丰生活区', 'x': 252000, 'y': 4102000},
{'name': '东风社区', 'x': 248000, 'y': 4101000},
{'name': '某中学', 'x': 255000, 'y': 4098000},
{'name': '某医院', 'x': 247000, 'y': 4099000},
{'name': '某村庄', 'x': 253000, 'y': 4103000},
]
# 提取敏感点浓度
from scipy.interpolate import RegularGridInterpolator
xi_unique = np.unique(Xi[0, :])
yi_unique = np.unique(Yi[:, 0])
interp = RegularGridInterpolator(
(xi_unique, yi_unique), Ci.T,
bounds_error=False, fill_value=np.nan
)
for rec in receptors:
conc = interp([[rec['x'], rec['y']]])[0]
rate = calc_standard_rate(conc, standard=500)
rec['conc'] = f'{conc:.1f} µg/m³'
rec['rate'] = f'{rate:.1f}%'
# 打印占标率
print('\n=== 敏感点浓度及占标率 ===')
print(f'{"敏感点":<12} {"浓度(µg/m³)":<15} {"占标率(%)":<10}')
print('-' * 40)
for rec in receptors:
conc_val = float(rec['conc'].replace(' µg/m³', ''))
print(f'{rec["name"]:<12} {conc_val:<15.1f} {rec["rate"]:<10}')
# 绘图
plot_concentration(Xi, Yi, Ci, sources, receptors,
'AERMOD_SO2预测图.png')
```
### 9.5 输出结果解读
运行后应检查:
| 检查项 | 合格标准 | 不合格处理 |
|--------|----------|------------|
| 最大地面浓度 | 1小时 < 500 µg/m³(SO₂二级) | 减排或加高烟囱 |
| 敏感点占标率 | < 100% | 调整源参数或防护距离 |
| 等值线形状 | 沿主导风向延伸 | 检查气象数据 |
| 网格覆盖范围 | 等值线在边界外消失 | 扩大评价范围 |
---
## 10. 常见问题与排查
### 10.1 AERMET 常见错误
| 错误信息 | 原因 | 解决方法 |
|----------|------|----------|
| Error reading surface data | 数据格式不对 | 检查CD-144格式列位置 |
| Missing sounding data | 探空数据缺失 | 补充最近的探空站数据 |
| Too many calm hours | 静风频率过高 | 正常现象,AERMET会处理 |
| Wind speed below threshold | 风速低于阈值 | 调整 THRESHOLD 参数 |
### 10.2 AERMAP 常见错误
| 错误信息 | 原因 | 解决方法 |
|----------|------|----------|
| Cannot open DEM file | 文件路径错误 | 检查路径和文件名 |
| Receptor outside DEM range | 受体超出DEM范围 | 扩大DEM数据范围 |
### 10.3 AERMOD 常见错误
| 错误信息 | 原因 | 解决方法 |
|----------|------|----------|
| Cannot open .SFC file | 气象文件路径错误 | 检查 ME 段路径 |
| No output generated | 输入文件语法错误 | 检查 .inp 文件格式 |
| All concentrations zero | 源参数或气象有误 | 检查 Q 值和气象数据 |
| WARNING: Urban mode used | 城市模式警告 | 确认是否需要城市选项 |
| Concentration too high | 源强或气象异常 | 逐项检查源参数 |
### 10.4 结果异常排查
**浓度全部为零:**
1. 检查排放速率 Q 是否大于 0
2. 检查气象数据时间是否匹配
3. 检查 MODELOPT 是否设为 CONCERVE
**浓度异常偏高:**
1. 检查 Q 单位是否为 g/s(不是 kg/s 或 mg/s)
2. 检查烟囱高度是否正确
3. 检查烟气温度是否为开尔文(K = °C + 273.15)
4. 检查是否有静风频率过高的时段
**等值线形状异常:**
1. 检查风向数据是否正确
2. 检查坐标系是否统一(UTM vs 经纬度)
3. 检查网格原点是否在正确位置
### 10.5 坐标系统一
**最常见的问题**是坐标系统一。所有文件必须使用同一坐标系:
| 数据 | 推荐坐标系 |
|------|-----------|
| 污染源位置 | UTM(单位m) |
| 受体位置 | UTM(单位m) |
| DEM地形数据 | UTM(单位m) |
| 敏感点坐标 | UTM(单位m) |
**经纬度转UTM:**
```python
from pyproj import Transformer
transformer = Transformer.from_crs("EPSG:4326", "EPSG:32650") # 32650=UTM Zone 50N
x_utm, y_utm = transformer.transform(36.85, 117.85) # (纬度, 经度)
print(f'UTM坐标: {x_utm:.1f}, {y_utm:.1f}')
```
> 山东省位于 UTM Zone 50N(EPSG:32650),经度大约在114°-122°之间。
---
## 附录A:完整输入文件模板
```
CO STARTING
TITLEONE 钢铁厂SO2排放预测 - 环评一级评价
TITLETWO 评价范围 10km x 10km
MODELOPT REGULATIVE CONCERVE
AVERTIME 1 24 ANNUAL
URBANOPT 0
POLLUTID SO2
FLAGPOLE 1.5
RUNORNOT RUN
ELEVATED 1
CO FINISHED
SO STARTING
LOCATION STACK1 POINT 250000.0 4100000.0 80.0
SRCPARAM STACK1 12.5 80.0 3.0 15.0 423.0
SRCGROUP ALL
SO FINISHED
ME STARTING
INPUT FILE project.sfc
INPUT FILE project.pfl
PROFBASE 20.0
ME FINISHED
RE STARTING
GRIDCART
GRIDCOR 245000.0 4095000.0
GRIDSIZE 200.0
GRIDNUM 50 50
ENDGRID
DISCCART
DISCCART 252000.0 4102000.0 RESIDENT_1
DISCCART 248000.0 4101000.0 COMMUNITY_1
DISCCART 255000.0 4098000.0 SCHOOL_1
DISCCART 247000.0 4099000.0 HOSPITAL_1
DISCCART 253000.0 4103000.0 VILLAGE_1
ENDDISC
RE FINISHED
OU STARTING
RECTABLE ALLAVE 1ST
RECTABLE ALLAVE 2ND
MAXTABLE ALLAVE
PLOTFILE 1 ALL 1 plot_1hr.plt
PLOTFILE 24 ALL 1 plot_24hr.plt
PLOTFILE ANNUAL ALL plot_annual.plt
SUMMFILE summary.txt
OU FINISHED
```
---
## 附录B:参数速查表
### B.1 污染物环境质量标准(HJ 2.2-2018)
| 污染物 | 取值时间 | 二级标准(µg/m³) | 一级标准(µg/m³) |
|--------|----------|-----------------|-----------------|
| SO₂ | 1小时 | 500 | 150 |
| SO₂ | 24小时 | 150 | 50 |
| SO₂ | 年平均 | 60 | 20 |
| NO₂ | 1小时 | 200 | 100 |
| NO₂ | 24小时 | 80 | 40 |
| NO₂ | 年平均 | 40 | 15 |
| PM₁₀ | 24小时 | 150 | 50 |
| PM₁₀ | 年平均 | 70 | 40 |
| PM₂.₅ | 24小时 | 75 | 35 |
| PM₂.₅ | 年平均 | 35 | 15 |
| TSP | 24小时 | 300 | 120 |
| TSP | 年平均 | 200 | 80 |
### B.2 评价等级判定标准
| 评价等级 | 最大占标率 Pmax | 判定 |
|----------|----------------|------|
| 一级 | Pmax ≥ 10% | 详细预测 |
| 二级 | 1% ≤ Pmax < 10% | 简化预测 |
| 三级 | Pmax < 1% | 不需预测 |
### B.3 UTM 分区速查(山东省)
| 经度范围 | UTM Zone | EPSG代码 |
|----------|----------|----------|
| 114°-120° | 50N | EPSG:32650 |
| 120°-126° | 51N | EPSG:32651 |
> 山东省大部分地区使用 UTM Zone 50N(EPSG:32650)。
> 青岛东部(>120°E)使用 UTM Zone 51N(EPSG:32651)。
### B.4 温度换算
| 摄氏度(°C) | 开尔文(K) | 说明 |
|------------|-----------|------|
| 0 | 273.15 | 水的冰点 |
| 20 | 293.15 | 标准环境温度 |
| 100 | 373.15 | 水的沸点 |
| 150 | 423.15 | 常见烟气温度 |
换算公式:**K = °C + 273.15**
### B.5 排放速率换算
| 单位 | 换算 | 示例 |
|------|------|------|
| g/s | 基本单位 | 12.5 g/s |
| kg/h | ×3.6 | 45 kg/h |
| t/a | ×0.0317 | 0.396 t/a → 实际需按年运行小时数换算 |
换算公式(假设年运行8000小时):
```
Q(g/s) = 排放量(t/a) × 10^6 / (年运行小时 × 3600)
```
---
## 学习路线建议
| 阶段 | 内容 | 时间 |
|------|------|------|
| 1 | 通读本教程,理解理论概念 | 2-3天 |
| 2 | 下载安装软件,跑通示例 | 1周 |
| 3 | 用自己的项目数据跑一遍 | 2周 |
| 4 | 后处理出图,写报告 | 1周 |
| 5 | 独立完成完整环评项目 | 持续练习 |
---
## 推荐资源
| 资源 | 类型 | 地址 |
|------|------|------|
| EPA SCRAM | 官方文档+程序 | https://www.epa.gov/scram |
| HJ 2.2-2018 | 中国环评导则 | 生态环境部官网 |
| AERMOD Implementation Guide | 实施指南 | EPA官网 |
| B站搜索"AERMOD" | 中文视频教程 | bilibili.com |
| 环评俱乐部 | 中文论坛交流 | 各环评论坛 |
---
*本教程由小白白整理,如有疑问随时问我。*
夜雨聆风