Metadata-Version: 2.4
Name: pyaermod
Version: 2.2.0
Summary: Python wrapper for EPA's AERMOD air dispersion model
Author-email: Shannon Capps <shannon.capps@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/atmmod/pyaermod
Project-URL: Documentation, https://atmmod.github.io/pyaermod/
Project-URL: Repository, https://github.com/atmmod/pyaermod
Project-URL: Issues, https://github.com/atmmod/pyaermod/issues
Project-URL: Changelog, https://github.com/atmmod/pyaermod/blob/main/CHANGELOG.md
Keywords: aermod,air quality,dispersion,modeling,atmospheric
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Science/Research
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Operating System :: OS Independent
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.24.0
Requires-Dist: pandas>=2.0.0
Provides-Extra: viz
Requires-Dist: matplotlib>=3.7.0; extra == "viz"
Requires-Dist: scipy>=1.10.0; extra == "viz"
Requires-Dist: folium>=0.14.0; extra == "viz"
Provides-Extra: geo
Requires-Dist: pyproj>=3.4.0; extra == "geo"
Requires-Dist: geopandas>=0.14.0; extra == "geo"
Requires-Dist: rasterio>=1.3.0; extra == "geo"
Requires-Dist: shapely>=2.0.0; extra == "geo"
Requires-Dist: scipy>=1.10.0; extra == "geo"
Provides-Extra: terrain
Requires-Dist: requests>=2.32.2; extra == "terrain"
Provides-Extra: met
Requires-Dist: requests>=2.32.2; extra == "met"
Provides-Extra: hpc
Requires-Dist: tqdm>=4.60.0; extra == "hpc"
Requires-Dist: requests>=2.32.2; extra == "hpc"
Provides-Extra: cad
Requires-Dist: ezdxf>=1.0.0; extra == "cad"
Provides-Extra: gui
Requires-Dist: nicegui>=3.0; extra == "gui"
Requires-Dist: matplotlib>=3.7.0; extra == "gui"
Provides-Extra: gui-desktop
Requires-Dist: nicegui>=3.0; extra == "gui-desktop"
Requires-Dist: matplotlib>=3.7.0; extra == "gui-desktop"
Requires-Dist: pywebview>=5.0; extra == "gui-desktop"
Provides-Extra: all
Requires-Dist: matplotlib>=3.7.0; extra == "all"
Requires-Dist: scipy>=1.10.0; extra == "all"
Requires-Dist: folium>=0.14.0; extra == "all"
Requires-Dist: pyproj>=3.4.0; extra == "all"
Requires-Dist: geopandas>=0.14.0; extra == "all"
Requires-Dist: rasterio>=1.3.0; extra == "all"
Requires-Dist: shapely>=2.0.0; extra == "all"
Requires-Dist: requests>=2.32.2; extra == "all"
Requires-Dist: tqdm>=4.60.0; extra == "all"
Requires-Dist: ezdxf>=1.0.0; extra == "all"
Requires-Dist: nicegui>=3.0; extra == "all"
Provides-Extra: e2e
Requires-Dist: playwright>=1.45; extra == "e2e"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: ruff>=0.4.0; extra == "dev"
Requires-Dist: hypothesis>=6.82.0; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"
Dynamic: license-file

# PyAERMOD

[![Tests](https://github.com/atmmod/pyaermod/actions/workflows/tests.yml/badge.svg)](https://github.com/atmmod/pyaermod/actions/workflows/tests.yml)
![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue)
![License: MIT](https://img.shields.io/badge/license-MIT-green)

Python wrapper for EPA's AERMOD atmospheric dispersion model.

PyAERMOD automates input file generation, model execution, output parsing, and result visualization — replacing manual text-file editing with a type-safe Python API.

## Installation

```bash
pip install pyaermod             # core (input generation + output parsing)
pip install pyaermod[viz]        # + matplotlib/folium visualization
pip install pyaermod[geo]        # + geospatial export (GeoTIFF, Shapefile)
pip install pyaermod[gui]        # + NiceGUI app
pip install pyaermod[all]        # everything
```

For development:
```bash
git clone https://github.com/atmmod/pyaermod.git
cd pyaermod
pip install -e ".[dev,all]"
```

## Quick Start

### Generate AERMOD Input

```python
from pyaermod.input_generator import (
    AERMODProject, ControlPathway, SourcePathway, ReceptorPathway,
    MeteorologyPathway, OutputPathway, PointSource, CartesianGrid,
    PollutantType, TerrainType,
)

control = ControlPathway(
    title_one="My Facility",
    pollutant_id=PollutantType.PM25,
    averaging_periods=["ANNUAL", "24"],
    terrain_type=TerrainType.FLAT,
)

sources = SourcePathway()
sources.add_source(PointSource(
    source_id="STACK1", x_coord=500.0, y_coord=500.0,
    base_elevation=10.0, stack_height=50.0, stack_temp=400.0,
    exit_velocity=15.0, stack_diameter=2.0, emission_rate=1.5,
))

receptors = ReceptorPathway()
receptors.add_cartesian_grid(CartesianGrid.from_bounds(
    x_min=0, x_max=2000, y_min=0, y_max=2000, spacing=100,
))

meteorology = MeteorologyPathway(
    surface_file="met_data.sfc", profile_file="met_data.pfl",
)
output = OutputPathway(receptor_table=True, max_table=True)

project = AERMODProject(control, sources, receptors, meteorology, output)
project.write("facility.inp")
```

### Run AERMOD & Parse Results

```python
from pyaermod.runner import run_aermod
from pyaermod.output_parser import parse_aermod_output

result = run_aermod("facility.inp")
results = parse_aermod_output(result.output_file)

df = results.get_concentrations("ANNUAL")
print(results.summary())
```

### Parse POSTFILE Output

```python
from pyaermod.postfile import read_postfile

# Auto-detects text vs binary format
post = read_postfile("postfile.out")
df = post.to_dataframe()

# A run with several output types: a text file names its columns in its
# header; a binary file needs the run's MODELOPT line (or type names)
dep = read_postfile("depo_post.bin", output_types="CONC DDEP WDEP")
print(dep.output_types)                     # ('CONC', 'DDEP', 'WDEP')
print(dep.data[dep.column_for("WDEP")].max())  # the wet_depo column
```

## Features

### Source Types (10)
POINT, AREA, AREACIRC, AREAPOLY, VOLUME, LINE, RLINE, RLINEXT, BUOYLINE, OPENPIT

### Advanced Modeling
- **Background concentrations** — uniform, period-specific, or sector-dependent
- **Deposition** — dry, wet, or combined for gas and particle emissions
- **NO2/SO2 chemistry** — OLM, PVMRM, ARM2, GRSM with ozone data
- **Source groups** — custom groupings with per-group PLOTFILE output
- **EVENT processing** — date/receptor-specific analysis

### Preprocessors
- **AERMET** — meteorological data preprocessing (Stage 1 and METPREP runstreams in AERMET 24142/26135 syntax)
- **AERMAP** — terrain elevation extraction with DEM download pipeline

### Analysis & Visualization
- Output parsing to pandas DataFrames
- POSTFILE parser for timestep-level results (text and binary formats)
- Contour plots, interactive Folium maps, 3D surfaces, wind roses
- Geospatial export: GeoTIFF, GeoPackage, Shapefile, GeoJSON

### Validation & Automation
- Input validation across all AERMOD pathways
- Building downwash / BPIP integration (point, area, and volume sources)
- Batch processing with parallel execution
- Interactive NiceGUI app (`pyaermod-app` browser, `pyaermod-desktop` native window)

## Project Structure

```
src/pyaermod/
    __init__.py          # Public API
    input_generator.py   # AERMOD input file generation (all source types)
    validator.py         # Configuration validation
    runner.py            # AERMOD subprocess execution
    output_parser.py     # Output file parsing
    postfile.py          # POSTFILE output parser
    visualization.py     # Matplotlib/Folium plots
    advanced_viz.py      # 3D surfaces, wind roses, animations
    aermet.py            # AERMET preprocessor wrapper
    aermap.py            # AERMAP input generation
    terrain.py           # DEM download + AERMAP pipeline
    geospatial.py        # Coordinate transforms, GIS export
    bpip.py              # Building downwash calculations
    gui_v2/              # NiceGUI app + pywebview desktop wrapper
tests/                   # 2,200+ tests on every CI leg, coverage gated at 95%
examples/                # Example scripts and Jupyter notebooks
docs/                    # Architecture and quickstart guides
```

## Requirements

- Python >= 3.11
- numpy, pandas (core)
- AERMOD executable (free from [EPA SCRAM](https://www.epa.gov/scram))

## Documentation

- [Quick Start Guide](docs/quickstart.md)
- [Ten Common AERMOD Jobs](docs/use-cases.md)
- [GUI User Guide](docs/gui-guide.md)
- [Architecture](docs/architecture.md)
- [API Reference](https://atmmod.github.io/pyaermod/api/)
- [Examples](examples/)

## Citing PyAERMOD

If PyAERMOD contributes to published work, please cite the archival release.
The repository's [`CITATION.cff`](CITATION.cff) carries the metadata in a
form GitHub, Zenodo and reference managers read directly (GitHub's
"Cite this repository" button renders it as APA or BibTeX):

> Capps, S. (2026). *PyAERMOD: Python wrapper for EPA's AERMOD air
> dispersion model* (version 2.2.0) [Computer software].
> https://github.com/atmmod/pyaermod. DOI: 10.5281/zenodo.XXXXXXX
> (the Zenodo DOI is minted when the v2.2.0 release is published and is
> recorded in `CITATION.cff`).

A journal article describing PyAERMOD and its validation is in
preparation for the *Journal of the Air & Waste Management Association*;
once it is published, `CITATION.cff`'s `preferred-citation` will point to
it and citing the article is preferred.

## License

MIT

## Performance

Driving AERMOD through `pyaermod.runner` costs a run 1 to 20 ms over
calling the binary with `subprocess` directly (medians of 50 interleaved
runs of EPA's `aertest` case, AERMOD v26135, on a GitHub Actions runner
and in the release container), and nearly all of that is the latency of
CPython's polling wait under `subprocess.run(timeout=...)`, not
pyaermod's own file handling. A batch of 32 such runs through
`AERMODRunner.run_batch` goes from 440 to 520 runs per minute at one
worker to 1,370 to 1,410 at four on four-CPU machines (a 2.7× to 3.1×
speed-up). The numbers, the machines and the command that reproduces
them are in [docs/benchmarks.md](docs/benchmarks.md).

## Disclaimer

PyAERMOD is a wrapper around AERMOD, not a reimplementation. It uses official EPA binaries for all calculations and maintains regulatory acceptance. PyAERMOD is validated against EPA AERMOD v26135 / AERMET v26135 (EPA reference archives of 2026-07-09) — a gfortran build driven by pyaermod reproduces EPA's AERTEST plotfile bit-for-bit and every POSTFILE in EPA's test suite within EPA's own tolerance (see [docs/validation.md](docs/validation.md)); v24142 remains supported for cross-version regression. Always validate results against EPA test cases for your specific use case.
