乐于分享
好东西不私藏

# AERMOD 完整教程:从安装到出图

# AERMOD 完整教程:从安装到出图

> 适用人群:环境监测/环评从业者,零基础入门  

> 预计学习时间: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 |

| 环评俱乐部 | 中文论坛交流 | 各环评论坛 |

---

*本教程由小白白整理,如有疑问随时问我。*