pyaermod 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. pyaermod-1.0.0/CHANGELOG.md +121 -0
  2. pyaermod-1.0.0/CONTRIBUTING.md +139 -0
  3. pyaermod-1.0.0/LICENSE +29 -0
  4. pyaermod-1.0.0/MANIFEST.in +16 -0
  5. pyaermod-1.0.0/PKG-INFO +229 -0
  6. pyaermod-1.0.0/README.md +161 -0
  7. pyaermod-1.0.0/docs/api/advanced_viz.md +3 -0
  8. pyaermod-1.0.0/docs/api/aermap.md +3 -0
  9. pyaermod-1.0.0/docs/api/aermet.md +3 -0
  10. pyaermod-1.0.0/docs/api/bpip.md +3 -0
  11. pyaermod-1.0.0/docs/api/geospatial.md +3 -0
  12. pyaermod-1.0.0/docs/api/gui.md +3 -0
  13. pyaermod-1.0.0/docs/api/index.md +52 -0
  14. pyaermod-1.0.0/docs/api/input_generator.md +3 -0
  15. pyaermod-1.0.0/docs/api/output_parser.md +3 -0
  16. pyaermod-1.0.0/docs/api/postfile.md +3 -0
  17. pyaermod-1.0.0/docs/api/runner.md +3 -0
  18. pyaermod-1.0.0/docs/api/terrain.md +3 -0
  19. pyaermod-1.0.0/docs/api/validator.md +3 -0
  20. pyaermod-1.0.0/docs/api/visualization.md +3 -0
  21. pyaermod-1.0.0/docs/architecture.md +772 -0
  22. pyaermod-1.0.0/docs/gui-guide.md +387 -0
  23. pyaermod-1.0.0/docs/index.md +55 -0
  24. pyaermod-1.0.0/docs/quickstart.md +488 -0
  25. pyaermod-1.0.0/examples/area_sources.py +361 -0
  26. pyaermod-1.0.0/examples/bpip.py +174 -0
  27. pyaermod-1.0.0/examples/deposition_modeling.py +416 -0
  28. pyaermod-1.0.0/examples/end_to_end.py +523 -0
  29. pyaermod-1.0.0/examples/line_sources.py +540 -0
  30. pyaermod-1.0.0/examples/notebooks/01_Getting_Started.ipynb +427 -0
  31. pyaermod-1.0.0/examples/notebooks/02_Point_Source_Modeling.ipynb +589 -0
  32. pyaermod-1.0.0/examples/notebooks/03_Area_Source_Modeling.ipynb +738 -0
  33. pyaermod-1.0.0/examples/notebooks/04_Parameter_Sweeps.ipynb +619 -0
  34. pyaermod-1.0.0/examples/notebooks/05_Visualization.ipynb +605 -0
  35. pyaermod-1.0.0/examples/notebooks/06_Postfile_Analysis.ipynb +267 -0
  36. pyaermod-1.0.0/examples/notebooks/07_Advanced_Features.ipynb +305 -0
  37. pyaermod-1.0.0/examples/volume_sources.py +426 -0
  38. pyaermod-1.0.0/pyproject.toml +132 -0
  39. pyaermod-1.0.0/setup.cfg +4 -0
  40. pyaermod-1.0.0/setup.py +10 -0
  41. pyaermod-1.0.0/src/pyaermod/__init__.py +339 -0
  42. pyaermod-1.0.0/src/pyaermod/advanced_viz.py +475 -0
  43. pyaermod-1.0.0/src/pyaermod/aermap.py +382 -0
  44. pyaermod-1.0.0/src/pyaermod/aermet.py +391 -0
  45. pyaermod-1.0.0/src/pyaermod/bpip.py +263 -0
  46. pyaermod-1.0.0/src/pyaermod/geospatial.py +971 -0
  47. pyaermod-1.0.0/src/pyaermod/gui.py +3124 -0
  48. pyaermod-1.0.0/src/pyaermod/input_generator.py +1933 -0
  49. pyaermod-1.0.0/src/pyaermod/output_parser.py +731 -0
  50. pyaermod-1.0.0/src/pyaermod/postfile.py +760 -0
  51. pyaermod-1.0.0/src/pyaermod/runner.py +528 -0
  52. pyaermod-1.0.0/src/pyaermod/terrain.py +804 -0
  53. pyaermod-1.0.0/src/pyaermod/validator.py +1072 -0
  54. pyaermod-1.0.0/src/pyaermod/visualization.py +480 -0
  55. pyaermod-1.0.0/src/pyaermod.egg-info/PKG-INFO +229 -0
  56. pyaermod-1.0.0/src/pyaermod.egg-info/SOURCES.txt +77 -0
  57. pyaermod-1.0.0/src/pyaermod.egg-info/dependency_links.txt +1 -0
  58. pyaermod-1.0.0/src/pyaermod.egg-info/entry_points.txt +2 -0
  59. pyaermod-1.0.0/src/pyaermod.egg-info/requires.txt +48 -0
  60. pyaermod-1.0.0/src/pyaermod.egg-info/top_level.txt +1 -0
  61. pyaermod-1.0.0/tests/__init__.py +3 -0
  62. pyaermod-1.0.0/tests/conftest.py +112 -0
  63. pyaermod-1.0.0/tests/test_advanced_features.py +1006 -0
  64. pyaermod-1.0.0/tests/test_aermap.py +308 -0
  65. pyaermod-1.0.0/tests/test_aermet.py +446 -0
  66. pyaermod-1.0.0/tests/test_bpip.py +441 -0
  67. pyaermod-1.0.0/tests/test_epa_cases.py +920 -0
  68. pyaermod-1.0.0/tests/test_geospatial.py +922 -0
  69. pyaermod-1.0.0/tests/test_gui.py +1728 -0
  70. pyaermod-1.0.0/tests/test_init.py +96 -0
  71. pyaermod-1.0.0/tests/test_input_generator.py +1219 -0
  72. pyaermod-1.0.0/tests/test_integration.py +1199 -0
  73. pyaermod-1.0.0/tests/test_output_parser.py +621 -0
  74. pyaermod-1.0.0/tests/test_postfile.py +1142 -0
  75. pyaermod-1.0.0/tests/test_property_based.py +326 -0
  76. pyaermod-1.0.0/tests/test_runner.py +509 -0
  77. pyaermod-1.0.0/tests/test_terrain.py +771 -0
  78. pyaermod-1.0.0/tests/test_validator.py +1562 -0
  79. pyaermod-1.0.0/tests/test_visualization.py +570 -0
@@ -0,0 +1,121 @@
1
+ # Changelog
2
+
3
+ All notable changes to PyAERMOD will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [1.0.0] - 2026-02-14
11
+
12
+ ### Added
13
+
14
+ #### Source Types
15
+ - **AreaSource** — rectangular area sources with rotation angle
16
+ - **AreaCircSource** — circular area sources with configurable vertex count
17
+ - **AreaPolySource** — irregular polygonal area sources
18
+ - **VolumeSource** — 3D emission volumes with initial dispersion
19
+ - **LineSource** — general linear sources (conveyors, pipelines)
20
+ - **RLineSource** — roadway-specific sources with mobile source physics
21
+ - **RLineExtSource** — extended roadway with per-endpoint elevations, optional barriers and road depression
22
+ - **BuoyLineSource** / **BuoyLineSegment** — buoyant line source groups with BLPINPUT/BLPGROUP
23
+ - **OpenPitSource** — open pit mine/quarry sources
24
+
25
+ #### Modules
26
+ - **Validator** (`pyaermod.validator`) — configuration validation for all 5 AERMOD pathways with cross-field checks
27
+ - **BPIP** (`pyaermod.bpip`) — building downwash / BPIP integration with 36-direction building parameters
28
+ - **AERMET** (`pyaermod.aermet`) — meteorological preprocessor input generation (Stages 1-3)
29
+ - **AERMAP** (`pyaermod.aermap`) — terrain preprocessor input generation with `from_aermod_project()` bridge
30
+ - **POSTFILE** (`pyaermod.postfile`) — POSTFILE output parser with timestep/receptor queries, auto-detection of text (PLOT) and binary (UNFORM) formats
31
+ - **Geospatial** (`pyaermod.geospatial`) — coordinate transforms (UTM/WGS84), GeoDataFrame creation, contour generation, GeoTIFF/GeoPackage/Shapefile/GeoJSON export
32
+ - **Terrain** (`pyaermod.terrain`) — DEM tile download from USGS TNM, AERMAP runner, output parser, elevation update pipeline
33
+ - **GUI** (`pyaermod.gui`) — 7-page Streamlit web application for interactive AERMOD workflow
34
+
35
+ #### Background Concentrations
36
+ - `BackgroundConcentration` and `BackgroundSector` dataclasses for ambient background levels
37
+ - Three modes: uniform value, period-specific values, or sector-dependent concentrations
38
+ - `SourcePathway.background` field generating `BACKGRND` and `BGSECTOR` keywords
39
+
40
+ #### Deposition Modeling
41
+ - `DepositionMethod` enum (`DRYDPLT`, `WETDPLT`, `GASDEPVD`, `GASDEPDF`)
42
+ - `GasDepositionParams` and `ParticleDepositionParams` for gas and particle deposition settings
43
+ - Deposition fields added to all 10 source types with shared `_deposition_to_aermod_lines()` helper
44
+ - `OutputPathway.output_type` for selecting concentration vs. deposition output
45
+
46
+ #### EVENT Processing
47
+ - `EventPeriod` and `EventPathway` dataclasses for event-based analysis
48
+ - `ControlPathway.eventfil` for linking event file
49
+ - `AERMODProject.write(event_filename=...)` generates EV pathway with `EVENTPER` records
50
+
51
+ #### NO2 / SO2 Chemistry Options
52
+ - `ChemistryMethod` enum: OLM, PVMRM, ARM2, GRSM
53
+ - `ChemistryOptions` dataclass with method, default NO2/NOx ratio, and ozone data
54
+ - `OzoneData` dataclass supporting ozone file, uniform value, or sector-specific values
55
+ - `ControlPathway.chemistry` field generating `MODELOPT`, `O3VALUES`, `OZONEFIL`, `NOXFIL` keywords
56
+ - Per-source `no2_ratio` field on `PointSource`
57
+
58
+ #### Source Group Management
59
+ - `SourceGroupDefinition` dataclass with group name, member source IDs, and description
60
+ - `SourcePathway.group_definitions` generating `SRCGROUP` keywords
61
+ - Per-group PLOTFILE output via `OutputPathway.plot_file_groups`
62
+
63
+ #### Building Downwash Expansion
64
+ - Building downwash (PRIME) fields extended from `PointSource` to also support `AreaSource` and `VolumeSource`
65
+ - `_building_downwash_lines()` and `_set_building_from_bpip()` module-level helpers shared across source types
66
+ - Terrain grid elevations via `CartesianGrid.terrain_elevations` and `PolarGrid.terrain_elevations`
67
+
68
+ #### Binary POSTFILE Deposition
69
+ - `UnformattedPostfileParser` now handles deposition records with `has_deposition` parameter (auto-detect or explicit)
70
+ - Parses 3N floats into concentration, dry deposition, and wet deposition columns
71
+
72
+ #### GUI Enhancements
73
+ - **ProjectSerializer**: JSON save/load for complete session state with round-trip fidelity
74
+ - **AreaCirc/AreaPoly forms** in SourceFormFactory
75
+ - **BPIP integration**: building forms, BPIP calculator wired to point, area, and volume sources
76
+ - **AERMAP elevation import**: 5th tab in receptor editor for terrain elevation upload
77
+ - **AERMET configuration**: dual-mode meteorology page (existing files vs. 3-stage AERMET config)
78
+ - **POSTFILE viewer**: 4th tab in Results Viewer with timestep slider, receptor time-series, animation GIF
79
+ - **Chemistry Options UI**: NO2 chemistry configuration with method, ozone data, and NOx file inputs
80
+ - **Source Groups UI**: create/delete source groups, per-group PLOTFILE checkboxes
81
+ - **Statistics helpers**: cross-period summary table, ranked receptor table, model complexity indicator
82
+ - **Export format detection**: dynamic format list based on installed optional dependencies
83
+
84
+ #### Testing & Quality
85
+ - 1166 tests across 18 test files, 95% code coverage
86
+ - 315 EPA v24142 integration tests parsing official test case outputs
87
+ - End-to-end mock pipeline tests (input generation → output parsing → visualization → postfile)
88
+ - `conftest.py` with shared fixtures for all test files
89
+ - Property-based testing with Hypothesis strategies for source types
90
+ - `ruff` linting (replaced flake8) with comprehensive rule set
91
+ - `.pre-commit-config.yaml` for automated lint on commit
92
+ - Performance benchmarks in `benchmarks/` directory
93
+
94
+ #### Documentation
95
+ - 7 Jupyter tutorial notebooks (Getting Started through Advanced Features)
96
+ - 7 example scripts (area sources, volume sources, line sources, BPIP, chemistry, deposition, end-to-end)
97
+ - MkDocs documentation site with Material theme and mkdocstrings API reference
98
+
99
+ ### Changed
100
+ - **Package layout**: moved from flat root modules to `src/pyaermod/` package structure
101
+ - **Imports**: `from pyaermod.input_generator import ...` (was `from pyaermod_input_generator import ...`)
102
+ - **Python**: minimum version raised to 3.11 (was 3.8) — required by NumPy 2.1+, SciPy 1.14+, Pandas 2.3+
103
+ - Updated `setup.py` extras: added `[geo]`, `[gui]`, `[terrain]`, `[all]` dependency groups
104
+ - CI matrix runs Python 3.11, 3.12, 3.13 with GDAL system dependencies
105
+
106
+ ## [0.1.0] - 2026-02-04
107
+
108
+ ### Added
109
+ - **PointSource** with full stack parameters and building downwash (PRIME) support
110
+ - **Receptor grids**: Cartesian, polar, and discrete receptors
111
+ - **AERMOD input generation** for all 5 pathways (CO, SO, RE, ME, OU)
112
+ - **Output parser**: parse `.out` files to pandas DataFrames, extract metadata, find max concentrations
113
+ - **Visualization**: contour plots (matplotlib), interactive maps (folium)
114
+ - **Runner**: `AERMODRunner` with subprocess execution, `BatchRunner` for parallel processing
115
+ - Project setup: `setup.py`, MIT license, `.gitignore`
116
+
117
+ ---
118
+
119
+ [Unreleased]: https://github.com/atmmod/pyaermod/compare/v1.0.0...HEAD
120
+ [1.0.0]: https://github.com/atmmod/pyaermod/compare/v0.1.0...v1.0.0
121
+ [0.1.0]: https://github.com/atmmod/pyaermod/releases/tag/v0.1.0
@@ -0,0 +1,139 @@
1
+ # Contributing to PyAERMOD
2
+
3
+ Thank you for your interest in contributing to PyAERMOD! This guide covers
4
+ the development workflow, coding standards, and submission process.
5
+
6
+ ## Development Setup
7
+
8
+ ```bash
9
+ # Clone the repository
10
+ git clone https://github.com/atmmod/pyaermod.git
11
+ cd pyaermod
12
+
13
+ # Install in editable mode with all dev dependencies
14
+ pip install -e ".[dev,all]"
15
+
16
+ # Install pre-commit hooks
17
+ pre-commit install
18
+ ```
19
+
20
+ ### System Dependencies
21
+
22
+ For geospatial features (GeoTIFF export, coordinate transforms), you need
23
+ GDAL:
24
+
25
+ ```bash
26
+ # Ubuntu / Debian
27
+ sudo apt-get install libgdal-dev gdal-bin
28
+
29
+ # macOS (Homebrew)
30
+ brew install gdal
31
+ ```
32
+
33
+ ## Running Tests
34
+
35
+ ```bash
36
+ # Run full test suite with coverage
37
+ pytest
38
+
39
+ # Run a specific test file
40
+ pytest tests/test_input_generator.py
41
+
42
+ # Run tests matching a keyword
43
+ pytest -k "test_point_source"
44
+
45
+ # Skip slow property-based tests
46
+ pytest -m "not slow"
47
+ ```
48
+
49
+ The project targets **89%+ code coverage**. New features should include
50
+ tests that maintain or improve this threshold.
51
+
52
+ ## Code Style
53
+
54
+ PyAERMOD uses [ruff](https://docs.astral.sh/ruff/) for linting and import
55
+ sorting. Configuration is in `pyproject.toml`.
56
+
57
+ ```bash
58
+ # Check for lint errors
59
+ ruff check src/ tests/
60
+
61
+ # Auto-fix where possible
62
+ ruff check --fix src/ tests/
63
+ ```
64
+
65
+ Key style rules:
66
+
67
+ - **Line length**: 120 characters
68
+ - **Python**: 3.11+ (type hints use `typing` module style, not `X | Y`)
69
+ - **Imports**: sorted by ruff/isort with `pyaermod` as first-party
70
+ - **Docstrings**: NumPy style
71
+ - **Dataclasses**: used throughout for configuration objects
72
+
73
+ ## Project Layout
74
+
75
+ ```
76
+ src/pyaermod/ # Package source (src layout)
77
+ tests/ # Test files (test_*.py)
78
+ docs/ # MkDocs documentation
79
+ examples/ # Example scripts and notebooks
80
+ benchmarks/ # Performance benchmarks
81
+ ```
82
+
83
+ ### Key Patterns
84
+
85
+ - **Source types** are dataclasses with a `to_aermod_input() -> str` method
86
+ - **Validation** uses `isinstance` dispatch in `validator.py`
87
+ - **Optional dependencies** use `try/except ImportError` with `HAS_*` flags
88
+ and `_require_*()` guard functions
89
+ - **GUI** uses Streamlit with `st.session_state` for state management
90
+
91
+ ## Adding a New Source Type
92
+
93
+ 1. Define the dataclass in `src/pyaermod/input_generator.py` following
94
+ existing patterns (e.g., `PointSource`)
95
+ 2. Implement `to_aermod_input()` to generate valid AERMOD keywords
96
+ 3. Add validation rules in `src/pyaermod/validator.py`
97
+ 4. Add the type to `SourceFormFactory.SOURCE_TYPES` in `gui.py`
98
+ 5. Update `__init__.py` exports and `__all__`
99
+ 6. Add tests in `tests/test_input_generator.py` and `tests/test_gui.py`
100
+ 7. Update documentation in `docs/quickstart.md`
101
+
102
+ ## Submitting Changes
103
+
104
+ 1. **Fork** the repository and create a feature branch
105
+ 2. **Write tests** for any new functionality
106
+ 3. **Run the full test suite**: `pytest`
107
+ 4. **Run the linter**: `ruff check src/ tests/`
108
+ 5. **Update documentation** if you changed user-facing behavior
109
+ 6. **Open a pull request** against `main` with a clear description
110
+
111
+ ### Commit Messages
112
+
113
+ Use clear, descriptive commit messages:
114
+
115
+ - `Add RLINEXT source type with barrier support`
116
+ - `Fix POSTFILE binary parser for big-endian systems`
117
+ - `Update quickstart guide with deposition examples`
118
+
119
+ ### Pull Request Checklist
120
+
121
+ - [ ] Tests pass (`pytest`)
122
+ - [ ] Linter passes (`ruff check src/ tests/`)
123
+ - [ ] New features have tests
124
+ - [ ] Documentation updated (if applicable)
125
+ - [ ] CHANGELOG.md updated under `[Unreleased]`
126
+
127
+ ## Reporting Issues
128
+
129
+ Please include:
130
+
131
+ - Python version and OS
132
+ - PyAERMOD version (`python -c "import pyaermod; print(pyaermod.__version__)"`)
133
+ - Minimal reproducible example
134
+ - Full error traceback
135
+
136
+ ## License
137
+
138
+ By contributing, you agree that your contributions will be licensed under the
139
+ MIT License.
pyaermod-1.0.0/LICENSE ADDED
@@ -0,0 +1,29 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shannon Capps
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ---
24
+
25
+ AERMOD Disclaimer:
26
+ This software is a wrapper around the EPA's AERMOD atmospheric dispersion model.
27
+ AERMOD is public domain software developed by the U.S. Environmental Protection Agency.
28
+ This wrapper does not modify AERMOD's calculations and maintains regulatory compliance.
29
+ Users must obtain the AERMOD executable separately from EPA's SCRAM website.
@@ -0,0 +1,16 @@
1
+ include LICENSE
2
+ include README.md
3
+ include CHANGELOG.md
4
+ include CONTRIBUTING.md
5
+ include pyproject.toml
6
+
7
+ recursive-include src/pyaermod *.py
8
+ recursive-include tests *.py
9
+ recursive-include docs *.md *.yml
10
+ recursive-include examples *.py *.ipynb
11
+
12
+ prune aermod
13
+ prune aermap
14
+ prune .github
15
+ prune benchmarks
16
+ prune .claude
@@ -0,0 +1,229 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyaermod
3
+ Version: 1.0.0
4
+ Summary: Python wrapper for EPA's AERMOD air dispersion model
5
+ Author-email: Shannon Capps <shannon.capps@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/atmmod/pyaermod
8
+ Project-URL: Documentation, https://atmmod.github.io/pyaermod/
9
+ Project-URL: Repository, https://github.com/atmmod/pyaermod
10
+ Project-URL: Issues, https://github.com/atmmod/pyaermod/issues
11
+ Project-URL: Changelog, https://github.com/atmmod/pyaermod/blob/main/CHANGELOG.md
12
+ Keywords: aermod,air quality,dispersion,modeling,atmospheric
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Operating System :: OS Independent
22
+ Requires-Python: >=3.11
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: numpy>=1.20.0
26
+ Requires-Dist: pandas>=1.3.0
27
+ Provides-Extra: viz
28
+ Requires-Dist: matplotlib>=3.3.0; extra == "viz"
29
+ Requires-Dist: scipy>=1.7.0; extra == "viz"
30
+ Requires-Dist: folium>=0.12.0; extra == "viz"
31
+ Provides-Extra: geo
32
+ Requires-Dist: pyproj>=3.0.0; extra == "geo"
33
+ Requires-Dist: geopandas>=0.10.0; extra == "geo"
34
+ Requires-Dist: rasterio>=1.2.0; extra == "geo"
35
+ Requires-Dist: shapely>=1.8.0; extra == "geo"
36
+ Requires-Dist: scipy>=1.7.0; extra == "geo"
37
+ Provides-Extra: gui
38
+ Requires-Dist: streamlit>=1.28.0; extra == "gui"
39
+ Requires-Dist: streamlit-folium>=0.15.0; extra == "gui"
40
+ Requires-Dist: folium>=0.14.0; extra == "gui"
41
+ Requires-Dist: pyproj>=3.0.0; extra == "gui"
42
+ Requires-Dist: geopandas>=0.10.0; extra == "gui"
43
+ Requires-Dist: rasterio>=1.2.0; extra == "gui"
44
+ Requires-Dist: shapely>=1.8.0; extra == "gui"
45
+ Requires-Dist: scipy>=1.7.0; extra == "gui"
46
+ Requires-Dist: matplotlib>=3.3.0; extra == "gui"
47
+ Provides-Extra: terrain
48
+ Requires-Dist: requests>=2.25.0; extra == "terrain"
49
+ Provides-Extra: all
50
+ Requires-Dist: matplotlib>=3.3.0; extra == "all"
51
+ Requires-Dist: scipy>=1.7.0; extra == "all"
52
+ Requires-Dist: folium>=0.14.0; extra == "all"
53
+ Requires-Dist: pyproj>=3.0.0; extra == "all"
54
+ Requires-Dist: geopandas>=0.10.0; extra == "all"
55
+ Requires-Dist: rasterio>=1.2.0; extra == "all"
56
+ Requires-Dist: shapely>=1.8.0; extra == "all"
57
+ Requires-Dist: streamlit>=1.28.0; extra == "all"
58
+ Requires-Dist: streamlit-folium>=0.15.0; extra == "all"
59
+ Requires-Dist: requests>=2.25.0; extra == "all"
60
+ Provides-Extra: dev
61
+ Requires-Dist: pytest>=7.0; extra == "dev"
62
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
63
+ Requires-Dist: ruff>=0.4.0; extra == "dev"
64
+ Requires-Dist: hypothesis>=6.82.0; extra == "dev"
65
+ Requires-Dist: build; extra == "dev"
66
+ Requires-Dist: twine; extra == "dev"
67
+ Dynamic: license-file
68
+
69
+ # PyAERMOD
70
+
71
+ Python wrapper for EPA's AERMOD atmospheric dispersion model.
72
+
73
+ PyAERMOD automates input file generation, model execution, output parsing, and result visualization — replacing manual text-file editing with a type-safe Python API.
74
+
75
+ ## Installation
76
+
77
+ ```bash
78
+ pip install pyaermod # core (input generation + output parsing)
79
+ pip install pyaermod[viz] # + matplotlib/folium visualization
80
+ pip install pyaermod[geo] # + geospatial export (GeoTIFF, Shapefile)
81
+ pip install pyaermod[gui] # + Streamlit interactive GUI
82
+ pip install pyaermod[all] # everything
83
+ ```
84
+
85
+ For development:
86
+ ```bash
87
+ git clone https://github.com/atmmod/pyaermod.git
88
+ cd pyaermod
89
+ pip install -e ".[dev,all]"
90
+ ```
91
+
92
+ ## Quick Start
93
+
94
+ ### Generate AERMOD Input
95
+
96
+ ```python
97
+ from pyaermod.input_generator import (
98
+ AERMODProject, ControlPathway, SourcePathway, ReceptorPathway,
99
+ MeteorologyPathway, OutputPathway, PointSource, CartesianGrid,
100
+ PollutantType, TerrainType,
101
+ )
102
+
103
+ control = ControlPathway(
104
+ title_one="My Facility",
105
+ pollutant_id=PollutantType.PM25,
106
+ averaging_periods=["ANNUAL", "24"],
107
+ terrain_type=TerrainType.FLAT,
108
+ )
109
+
110
+ sources = SourcePathway()
111
+ sources.add_source(PointSource(
112
+ source_id="STACK1", x_coord=500.0, y_coord=500.0,
113
+ base_elevation=10.0, stack_height=50.0, stack_temp=400.0,
114
+ exit_velocity=15.0, stack_diameter=2.0, emission_rate=1.5,
115
+ ))
116
+
117
+ receptors = ReceptorPathway()
118
+ receptors.add_cartesian_grid(CartesianGrid.from_bounds(
119
+ x_min=0, x_max=2000, y_min=0, y_max=2000, spacing=100,
120
+ ))
121
+
122
+ meteorology = MeteorologyPathway(
123
+ surface_file="met_data.sfc", profile_file="met_data.pfl",
124
+ )
125
+ output = OutputPathway(receptor_table=True, max_table=True)
126
+
127
+ project = AERMODProject(control, sources, receptors, meteorology, output)
128
+ project.write("facility.inp")
129
+ ```
130
+
131
+ ### Run AERMOD & Parse Results
132
+
133
+ ```python
134
+ from pyaermod.runner import run_aermod
135
+ from pyaermod.output_parser import parse_aermod_output
136
+
137
+ result = run_aermod("facility.inp")
138
+ results = parse_aermod_output(result.output_file)
139
+
140
+ df = results.get_concentrations("ANNUAL")
141
+ print(results.summary())
142
+ ```
143
+
144
+ ### Parse POSTFILE Output
145
+
146
+ ```python
147
+ from pyaermod.postfile import read_postfile
148
+
149
+ # Auto-detects text vs binary format
150
+ post = read_postfile("postfile.out")
151
+ df = post.to_dataframe()
152
+
153
+ # Binary postfile with deposition data
154
+ dep = read_postfile("depo_post.out", has_deposition=True)
155
+ print(dep.to_dataframe()[["concentration", "dry_depo", "wet_depo"]])
156
+ ```
157
+
158
+ ## Features
159
+
160
+ ### Source Types (10)
161
+ POINT, AREA, AREACIRC, AREAPOLY, VOLUME, LINE, RLINE, RLINEXT, BUOYLINE, OPENPIT
162
+
163
+ ### Advanced Modeling
164
+ - **Background concentrations** — uniform, period-specific, or sector-dependent
165
+ - **Deposition** — dry, wet, or combined for gas and particle emissions
166
+ - **NO2/SO2 chemistry** — OLM, PVMRM, ARM2, GRSM with ozone data
167
+ - **Source groups** — custom groupings with per-group PLOTFILE output
168
+ - **EVENT processing** — date/receptor-specific analysis
169
+
170
+ ### Preprocessors
171
+ - **AERMET** — meteorological data preprocessing (Stages 1-3)
172
+ - **AERMAP** — terrain elevation extraction with DEM download pipeline
173
+
174
+ ### Analysis & Visualization
175
+ - Output parsing to pandas DataFrames
176
+ - POSTFILE parser for timestep-level results (text and binary formats)
177
+ - Contour plots, interactive Folium maps, 3D surfaces, wind roses
178
+ - Geospatial export: GeoTIFF, GeoPackage, Shapefile, GeoJSON
179
+
180
+ ### Validation & Automation
181
+ - Input validation across all AERMOD pathways
182
+ - Building downwash / BPIP integration (point, area, and volume sources)
183
+ - Batch processing with parallel execution
184
+ - Interactive Streamlit GUI (`pyaermod-gui`)
185
+
186
+ ## Project Structure
187
+
188
+ ```
189
+ src/pyaermod/
190
+ __init__.py # Public API
191
+ input_generator.py # AERMOD input file generation (all source types)
192
+ validator.py # Configuration validation
193
+ runner.py # AERMOD subprocess execution
194
+ output_parser.py # Output file parsing
195
+ postfile.py # POSTFILE output parser
196
+ visualization.py # Matplotlib/Folium plots
197
+ advanced_viz.py # 3D surfaces, wind roses, animations
198
+ aermet.py # AERMET preprocessor wrapper
199
+ aermap.py # AERMAP input generation
200
+ terrain.py # DEM download + AERMAP pipeline
201
+ geospatial.py # Coordinate transforms, GIS export
202
+ bpip.py # Building downwash calculations
203
+ gui.py # Streamlit web GUI
204
+ tests/ # 1166 tests, 95% coverage
205
+ examples/ # Example scripts and Jupyter notebooks
206
+ docs/ # Architecture and quickstart guides
207
+ ```
208
+
209
+ ## Requirements
210
+
211
+ - Python >= 3.11
212
+ - numpy, pandas (core)
213
+ - AERMOD executable (free from [EPA SCRAM](https://www.epa.gov/scram))
214
+
215
+ ## Documentation
216
+
217
+ - [Quick Start Guide](docs/quickstart.md)
218
+ - [GUI User Guide](docs/gui-guide.md)
219
+ - [Architecture](docs/architecture.md)
220
+ - [API Reference](https://atmmod.github.io/pyaermod/api/)
221
+ - [Examples](examples/)
222
+
223
+ ## License
224
+
225
+ MIT
226
+
227
+ ## Disclaimer
228
+
229
+ PyAERMOD is a wrapper around AERMOD, not a reimplementation. It uses official EPA binaries for all calculations and maintains regulatory acceptance. Always validate results against EPA test cases for your specific use case.
@@ -0,0 +1,161 @@
1
+ # PyAERMOD
2
+
3
+ Python wrapper for EPA's AERMOD atmospheric dispersion model.
4
+
5
+ PyAERMOD automates input file generation, model execution, output parsing, and result visualization — replacing manual text-file editing with a type-safe Python API.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install pyaermod # core (input generation + output parsing)
11
+ pip install pyaermod[viz] # + matplotlib/folium visualization
12
+ pip install pyaermod[geo] # + geospatial export (GeoTIFF, Shapefile)
13
+ pip install pyaermod[gui] # + Streamlit interactive GUI
14
+ pip install pyaermod[all] # everything
15
+ ```
16
+
17
+ For development:
18
+ ```bash
19
+ git clone https://github.com/atmmod/pyaermod.git
20
+ cd pyaermod
21
+ pip install -e ".[dev,all]"
22
+ ```
23
+
24
+ ## Quick Start
25
+
26
+ ### Generate AERMOD Input
27
+
28
+ ```python
29
+ from pyaermod.input_generator import (
30
+ AERMODProject, ControlPathway, SourcePathway, ReceptorPathway,
31
+ MeteorologyPathway, OutputPathway, PointSource, CartesianGrid,
32
+ PollutantType, TerrainType,
33
+ )
34
+
35
+ control = ControlPathway(
36
+ title_one="My Facility",
37
+ pollutant_id=PollutantType.PM25,
38
+ averaging_periods=["ANNUAL", "24"],
39
+ terrain_type=TerrainType.FLAT,
40
+ )
41
+
42
+ sources = SourcePathway()
43
+ sources.add_source(PointSource(
44
+ source_id="STACK1", x_coord=500.0, y_coord=500.0,
45
+ base_elevation=10.0, stack_height=50.0, stack_temp=400.0,
46
+ exit_velocity=15.0, stack_diameter=2.0, emission_rate=1.5,
47
+ ))
48
+
49
+ receptors = ReceptorPathway()
50
+ receptors.add_cartesian_grid(CartesianGrid.from_bounds(
51
+ x_min=0, x_max=2000, y_min=0, y_max=2000, spacing=100,
52
+ ))
53
+
54
+ meteorology = MeteorologyPathway(
55
+ surface_file="met_data.sfc", profile_file="met_data.pfl",
56
+ )
57
+ output = OutputPathway(receptor_table=True, max_table=True)
58
+
59
+ project = AERMODProject(control, sources, receptors, meteorology, output)
60
+ project.write("facility.inp")
61
+ ```
62
+
63
+ ### Run AERMOD & Parse Results
64
+
65
+ ```python
66
+ from pyaermod.runner import run_aermod
67
+ from pyaermod.output_parser import parse_aermod_output
68
+
69
+ result = run_aermod("facility.inp")
70
+ results = parse_aermod_output(result.output_file)
71
+
72
+ df = results.get_concentrations("ANNUAL")
73
+ print(results.summary())
74
+ ```
75
+
76
+ ### Parse POSTFILE Output
77
+
78
+ ```python
79
+ from pyaermod.postfile import read_postfile
80
+
81
+ # Auto-detects text vs binary format
82
+ post = read_postfile("postfile.out")
83
+ df = post.to_dataframe()
84
+
85
+ # Binary postfile with deposition data
86
+ dep = read_postfile("depo_post.out", has_deposition=True)
87
+ print(dep.to_dataframe()[["concentration", "dry_depo", "wet_depo"]])
88
+ ```
89
+
90
+ ## Features
91
+
92
+ ### Source Types (10)
93
+ POINT, AREA, AREACIRC, AREAPOLY, VOLUME, LINE, RLINE, RLINEXT, BUOYLINE, OPENPIT
94
+
95
+ ### Advanced Modeling
96
+ - **Background concentrations** — uniform, period-specific, or sector-dependent
97
+ - **Deposition** — dry, wet, or combined for gas and particle emissions
98
+ - **NO2/SO2 chemistry** — OLM, PVMRM, ARM2, GRSM with ozone data
99
+ - **Source groups** — custom groupings with per-group PLOTFILE output
100
+ - **EVENT processing** — date/receptor-specific analysis
101
+
102
+ ### Preprocessors
103
+ - **AERMET** — meteorological data preprocessing (Stages 1-3)
104
+ - **AERMAP** — terrain elevation extraction with DEM download pipeline
105
+
106
+ ### Analysis & Visualization
107
+ - Output parsing to pandas DataFrames
108
+ - POSTFILE parser for timestep-level results (text and binary formats)
109
+ - Contour plots, interactive Folium maps, 3D surfaces, wind roses
110
+ - Geospatial export: GeoTIFF, GeoPackage, Shapefile, GeoJSON
111
+
112
+ ### Validation & Automation
113
+ - Input validation across all AERMOD pathways
114
+ - Building downwash / BPIP integration (point, area, and volume sources)
115
+ - Batch processing with parallel execution
116
+ - Interactive Streamlit GUI (`pyaermod-gui`)
117
+
118
+ ## Project Structure
119
+
120
+ ```
121
+ src/pyaermod/
122
+ __init__.py # Public API
123
+ input_generator.py # AERMOD input file generation (all source types)
124
+ validator.py # Configuration validation
125
+ runner.py # AERMOD subprocess execution
126
+ output_parser.py # Output file parsing
127
+ postfile.py # POSTFILE output parser
128
+ visualization.py # Matplotlib/Folium plots
129
+ advanced_viz.py # 3D surfaces, wind roses, animations
130
+ aermet.py # AERMET preprocessor wrapper
131
+ aermap.py # AERMAP input generation
132
+ terrain.py # DEM download + AERMAP pipeline
133
+ geospatial.py # Coordinate transforms, GIS export
134
+ bpip.py # Building downwash calculations
135
+ gui.py # Streamlit web GUI
136
+ tests/ # 1166 tests, 95% coverage
137
+ examples/ # Example scripts and Jupyter notebooks
138
+ docs/ # Architecture and quickstart guides
139
+ ```
140
+
141
+ ## Requirements
142
+
143
+ - Python >= 3.11
144
+ - numpy, pandas (core)
145
+ - AERMOD executable (free from [EPA SCRAM](https://www.epa.gov/scram))
146
+
147
+ ## Documentation
148
+
149
+ - [Quick Start Guide](docs/quickstart.md)
150
+ - [GUI User Guide](docs/gui-guide.md)
151
+ - [Architecture](docs/architecture.md)
152
+ - [API Reference](https://atmmod.github.io/pyaermod/api/)
153
+ - [Examples](examples/)
154
+
155
+ ## License
156
+
157
+ MIT
158
+
159
+ ## Disclaimer
160
+
161
+ PyAERMOD is a wrapper around AERMOD, not a reimplementation. It uses official EPA binaries for all calculations and maintains regulatory acceptance. Always validate results against EPA test cases for your specific use case.