Skip to content

AERMOD Python Wrapper - Technical Architecture

Executive Summary

This document outlines the technical architecture for a Python wrapper around the AERMOD Fortran codebase. The wrapper aims to provide an accessible, API-driven interface for regulatory dispersion modeling without requiring expensive consulting services or direct Fortran expertise.

Architecture Overview

┌─────────────────────────────────────────────────────────────┐
│                     User Interface Layer                     │
│  ┌────────────┐  ┌──────────────┐  ┌──────────────────────┐│
│  │ CLI Tool   │  │ Python API   │  │ Web Dashboard (opt.) ││
│  └────────────┘  └──────────────┘  └──────────────────────┘│
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│              Core Python Wrapper (pyaermod)                  │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  Configuration & Parameter Management                 │  │
│  │  - Input validation                                   │  │
│  │  - Parameter serialization                            │  │
│  │  - Project templates                                  │  │
│  └──────────────────────────────────────────────────────┘  │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  Input File Generation                                │  │
│  │  - AERMOD control file (.inp)                        │  │
│  │  - Source configuration                               │  │
│  │  - Receptor grid generation                           │  │
│  └──────────────────────────────────────────────────────┘  │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  Process Management                                   │  │
│  │  - AERMOD executable wrapper                         │  │
│  │  - AERMET integration                                │  │
│  │  - AERMAP integration                                │  │
│  │  - Error handling & logging                          │  │
│  └──────────────────────────────────────────────────────┘  │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  Output Parsing & Processing                         │  │
│  │  - Result file parsing                               │  │
│  │  - Data structure conversion (to DataFrame/NetCDF)  │  │
│  │  - Statistical analysis                              │  │
│  └──────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│                  Data Integration Layer                      │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐ │
│  │ Meteorology  │  │ Terrain/Maps │  │ Emissions Data   │ │
│  │ APIs         │  │ APIs         │  │ Sources          │ │
│  └──────────────┘  └──────────────┘  └──────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│              Visualization & Export Layer                    │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐ │
│  │ Plotly/      │  │ Folium/      │  │ Report           │ │
│  │ Matplotlib   │  │ Leaflet      │  │ Generation       │ │
│  │ (Contours)   │  │ (Interactive)│  │ (PDF/HTML)       │ │
│  └──────────────┘  └──────────────┘  └──────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│                   AERMOD Fortran Core                        │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────────┐ │
│  │ AERMOD       │  │ AERMET       │  │ AERMAP           │ │
│  │ (dispersion) │  │ (met prep)   │  │ (terrain prep)   │ │
│  └──────────────┘  └──────────────┘  └──────────────────┘ │
└─────────────────────────────────────────────────────────────┘

Core Components

1. Configuration & Parameter Management

Purpose: Provide a Pythonic interface for AERMOD configuration

Key Features: - Object-oriented parameter definition using dataclasses or Pydantic models - JSON/YAML configuration file support - Input validation with clear error messages - Default parameter sets for common scenarios - Template system for regulatory compliance (EPA guidelines)

Example API:

from pyaermod import AERMODProject, PointSource, ReceptorGrid

project = AERMODProject(
    name="facility_assessment",
    pollutant="PM2.5",
    averaging_periods=["1HR", "24HR", "ANNUAL"]
)

# Define emission source
stack = PointSource(
    id="STACK1",
    location=(latitude, longitude, elevation),
    height=50.0,  # meters
    diameter=2.0,
    temperature=400.0,  # K
    velocity=15.0,  # m/s
    emission_rate=1.5  # g/s
)

# Define receptor grid
grid = CartesianGrid.from_bounds(
    bounds=(min_x, min_y, max_x, max_y),
    spacing=100,  # meters
    coordinate_system="UTM"
)

2. Input File Generation

Purpose: Convert Python objects to AERMOD-compatible input files

Components: - Control File Generator: Creates .inp files from Python objects - Receptor Generator: Handles Cartesian/polar grids, discrete receptors, fence lines - Source Configuration: Supports point, area, volume, line sources - Keyword Parser: Validates AERMOD keywords and options

File Structure:

CO STARTING
   TITLEONE  Project generated by pyaermod
   MODELOPT  CONC FLAT
   AVERTIME  1 24 ANNUAL
   POLLUTID  PM25
   RUNORNOT  RUN
CO FINISHED

SO STARTING
   LOCATION  STACK1 POINT <x> <y> <base_elev>
   SRCPARAM  STACK1 <emission> <height> <temp> <velocity> <diameter>
SO FINISHED

RE STARTING
   ... (receptor definitions)
RE FINISHED

3. Process Management & Execution

Purpose: Orchestrate AERMOD executable runs

Features: - Subprocess management with timeout controls - Platform-specific executable detection (Windows .exe, Linux binary) - Environment variable configuration - Progress monitoring and log capture - Parallel run support for sensitivity analysis - Resource management (temp directories, cleanup)

Implementation:

class AERMODRunner:
    def __init__(self, executable_path=None):
        self.executable = executable_path or self._find_aermod()

    def run(self, input_file, working_dir=None, timeout=3600):
        """Execute AERMOD with error handling"""
        # Validate inputs
        # Set up working directory
        # Execute subprocess
        # Capture stdout/stderr
        # Parse return codes
        # Return results object

    def run_batch(self, scenarios, n_workers=4):
        """Run multiple scenarios in parallel"""
        # Use multiprocessing Pool or concurrent.futures

4. Data Integration Layer

Purpose: Connect to external data sources

API Integrations:

Meteorological Data

  • NOAA ISD: Historical surface observations
  • ERA5/MERRA-2: Reanalysis data via climate APIs
  • Local weather stations: Custom data ingestion
  • AERMINUTE: 1-minute ASOS data for AERMET processing
from pyaermod.data import MeteorologicalData

# Fetch and prepare meteorological data
met_data = MeteorologicalData.from_noaa(
    station_id="KDEN",
    start_date="2023-01-01",
    end_date="2023-12-31"
)

# Automatically run AERMET preprocessing
surface_file, profile_file = met_data.prepare_for_aermod(
    output_dir="./met_processed"
)

Terrain/Elevation Data

  • USGS NED: National Elevation Dataset
  • SRTM: Shuttle Radar Topography Mission
  • OpenTopography: High-resolution DEM access
  • Automatic AERMAP execution
from pyaermod.terrain import TerrainProcessor

terrain = TerrainProcessor.from_usgs(
    bounds=(min_lon, min_lat, max_lon, max_lat),
    resolution=10  # meters
)

# Generate AERMAP terrain file
terrain_file = terrain.process_for_aermod(
    receptors=grid,
    sources=[stack]
)

Base Maps for Visualization

  • OpenStreetMap: Tile layers
  • Mapbox: Custom styling
  • ESRI: Satellite imagery
  • Census TIGER: Boundaries and features

5. Output Parsing & Analysis

Purpose: Extract and structure AERMOD results

Features: - Parse .out files for concentration/deposition results - Convert to pandas DataFrame or xarray Dataset - Calculate statistical summaries (max, percentiles, exceedances) - Spatial interpolation for smooth contours - Time-series extraction at specific receptors - Regulatory compliance checking (NAAQS comparison)

Output Structure:

class AERMODResults:
    def __init__(self, output_file):
        self.metadata = self._parse_header()
        self.concentrations = self._parse_concentrations()

    def to_dataframe(self, averaging_period="24HR"):
        """Returns DataFrame with columns: x, y, concentration, rank, etc."""

    def get_max_impact(self, averaging_period="24HR"):
        """Returns location and value of maximum impact"""

    def check_compliance(self, standard, background=0.0):
        """Compare against regulatory standard"""

    def to_netcdf(self, output_path):
        """Export as NetCDF for GIS integration"""

6. Visualization Layer

Purpose: Create publication-ready graphics and interactive maps

Visualization Types:

Static Plots (Matplotlib/Plotly)

  • Concentration contour plots
  • Isopleths with customizable levels
  • Source/receptor overlays
  • Time-series plots
  • Exceedance probability plots
from pyaermod.viz import plot_contours

fig = plot_contours(
    results,
    averaging_period="ANNUAL",
    levels=[5, 10, 15, 20, 25],  # μg/m³
    colormap="YlOrRd",
    show_sources=True,
    show_max_location=True
)
fig.save("output.png", dpi=300)

Interactive Maps (Folium/Plotly)

  • Leaflet-based web maps
  • Zoom/pan capabilities
  • Popup information on click
  • Multiple layer controls
  • Export to standalone HTML
from pyaermod.viz import create_interactive_map

map_obj = create_interactive_map(
    results,
    basemap="OpenStreetMap",
    opacity=0.6,
    add_legend=True
)
map_obj.save("results_map.html")

Automated Reports

  • PDF generation with charts and tables
  • HTML dashboards with embedded visualizations
  • Markdown reports for documentation
  • Compliance summary sheets

7. Project Management

Purpose: Handle multi-scenario projects

Features: - Project directory structure - Version control for configurations - Scenario comparison tools - Sensitivity analysis automation - Parameter sweep utilities

from pyaermod import Project

proj = Project("facility_permit_renewal")

# Define base scenario
base = proj.create_scenario("baseline", ...)

# Create variations
for rate in [0.5, 1.0, 1.5, 2.0]:
    scenario = base.copy()
    scenario.sources["STACK1"].emission_rate = rate
    proj.add_scenario(f"emission_{rate}", scenario)

# Run all scenarios
results = proj.run_all(parallel=True, n_workers=4)

# Compare results
comparison = proj.compare_scenarios(
    metric="max_24hr_concentration"
)

Technology Stack

Core Dependencies

Essential: - numpy: Numerical operations, array handling - pandas: Tabular data manipulation - pydantic or dataclasses: Configuration validation - click or typer: CLI interface - subprocess: AERMOD execution - pathlib: Cross-platform path handling

Data Integration: - requests: API calls - xarray: Multi-dimensional arrays (meteorological data) - rasterio: Geospatial raster data (terrain) - geopandas: Vector geospatial data - pyproj: Coordinate transformations

Visualization: - matplotlib: Basic plotting - plotly: Interactive charts - folium: Interactive maps - contextily: Basemap tiles - reportlab or weasyprint: PDF generation

Optional: - dask: Large-scale parallel computing - netCDF4: NetCDF file I/O - h5py: HDF5 for large datasets - nicegui: Web dashboard + desktop bundle - pytest: Testing framework - sphinx: Documentation generation

Fortran Integration Strategy

Approach 1: Subprocess Wrapper (Recommended for MVP) - No Fortran recompilation needed - Use existing EPA-certified binaries - Communicate via input/output files - Pros: Simple, regulatory acceptance maintained - Cons: File I/O overhead, no in-memory access

Approach 2: F2PY Integration (Future Enhancement) - Wrap specific Fortran subroutines with f2py - Call Fortran directly from Python - Pros: Performance, memory efficiency - Cons: Compilation complexity, certification concerns

Approach 3: ISO C Binding (Advanced) - Create C interface to Fortran - Use ctypes/cffi from Python - Pros: Performance, cleaner interface - Cons: Significant refactoring required

MVP Recommendation: Start with subprocess wrapper, design API to abstract execution method for future upgrades.

Data Flow

Typical Workflow

1. User Input (Python API or CLI)
2. Configuration Validation
3. Fetch External Data (met, terrain, maps)
4. Generate AERMOD Input Files (.inp)
5. Execute AERMOD Fortran Binary
6. Parse Output Files (.out)
7. Post-process Results (statistics, interpolation)
8. Generate Visualizations (plots, maps)
9. Create Reports (PDF/HTML)
10. Return Results to User

File Organization

pyaermod/
├── src/pyaermod/              # Main package (src layout)
│   ├── __init__.py            # Public API, version, optional dep checks
│   ├── api.py                 # Stable public surface (from pyaermod.api import *)
│   ├── _optional.py           # Shared optional-dependency helper
│   ├── input_generator.py     # Facade: re-exports + AERMODProject class
│   ├── pathways.py            # Enums, ControlPathway, MeteorologyPathway, OutputPathway, Events
│   ├── sources.py             # 12 source types, deposition params, SourcePathway, background
│   ├── receptors.py           # CartesianGrid, PolarGrid, DiscreteReceptor, ReceptorPathway
│   ├── input_reader.py        # .inp file reader (bidirectional round-trip)
│   ├── validator.py           # Per-field validation for all pathways
│   ├── validator_advanced.py  # Cross-field AERMOD checks (integrated by default)
│   ├── regulatory.py          # EPA Appendix W profile presets
│   ├── chemistry_presets.py   # OLM/PVMRM/GRSM factories, deposition defaults
│   ├── runner.py              # AERMODRunner, BatchRunner, run_aermod()
│   ├── runner_utils.py        # Progress, failure diagnostics, resume, SLURM
│   ├── output_parser.py       # .out file parsing to DataFrames
│   ├── aermod_outputs.py      # PLOTFILE/MAXIFILE/RANKFILE/SEASONHR/deposition readers
│   ├── postfile.py            # POSTFILE parser (text PLOT + binary UNFORM)
│   ├── visualization.py       # Contour plots, Folium maps
│   ├── advanced_viz.py        # 3D surfaces, wind roses, animations
│   ├── aermet.py              # AERMET Stages 1-3 input generation + SFC/PFL parsers
│   ├── aermet_runner.py       # AERMETRunner + three-stage pipeline
│   ├── met_ingest.py          # ASOS 1-min, ISD, IGRA, MMIF data ingest
│   ├── met_qaqc.py            # Meteorological data QA/QC checks
│   ├── aermap.py              # AERMAP input generation
│   ├── terrain.py             # DEM download, AERMAP runner, elevation pipeline
│   ├── terrain_utils.py       # Datums, SRTM, mosaic, reproject, diagnostics
│   ├── geospatial.py          # UTM/WGS84 transforms, GIS export
│   ├── bpip.py                # Building downwash (BPIP) calculations
│   ├── prime.py               # GEP stack height, cavity region, project-level BPIP
│   ├── cli.py                 # pyaermod command-line interface
│   └── gui.py                 # 7-page Streamlit web application
├── tests/                     # 1590+ tests across 30+ files
├── docs/                      # MkDocs documentation site
│   ├── quickstart.md
│   ├── gui-guide.md
│   ├── architecture.md
│   └── api/                   # Auto-generated API reference (mkdocstrings)
├── examples/                  # 6 example scripts + 5 Jupyter notebooks
├── benchmarks/                # Performance benchmarks
├── setup.py                   # Package metadata and dependencies
├── pyproject.toml             # Build system + ruff config
├── mkdocs.yml                 # Documentation site configuration
└── CHANGELOG.md

API Design Principles

1. Pythonic Interface

  • Follow PEP 8 naming conventions
  • Use context managers for resource handling
  • Provide sensible defaults
  • Support both OOP and functional styles

2. Progressive Disclosure

  • Simple tasks should be simple
  • Advanced features available but not required
  • Sane defaults based on EPA guidance

3. Type Safety

  • Use type hints throughout
  • Runtime validation with Pydantic
  • Clear error messages

4. Flexibility

  • Support multiple input formats (JSON, YAML, Python objects)
  • Allow both high-level and low-level control
  • Provide escape hatches for custom AERMOD keywords

Error Handling & Logging

Error Categories

  1. Configuration Errors: Invalid parameters, missing required fields
  2. Execution Errors: AERMOD runtime failures, missing executables
  3. Data Errors: API failures, invalid meteorological data
  4. I/O Errors: File access, disk space issues

Logging Strategy

import logging

# Structured logging with different levels
logger = logging.getLogger("pyaermod")

# User-friendly messages
logger.info("Starting AERMOD run for project: facility_assessment")
logger.warning("Meteorological data contains gaps, interpolating...")
logger.error("AERMOD execution failed: invalid source configuration")

# Debug mode for development
logger.debug(f"Generated input file:\n{input_content}")

User Feedback

  • Progress bars for long operations (tqdm)
  • Clear error messages with suggestions
  • Validation errors before execution (fail fast)
  • Summary statistics after completion

Testing Strategy

Unit Tests

  • Configuration object creation and validation
  • Input file generation (compare against known-good files)
  • Output parsing (use EPA test cases)
  • Coordinate transformations
  • Data validation logic

Integration Tests

  • Full AERMOD runs with EPA test datasets
  • API data fetching (with mocking)
  • End-to-end workflow tests

Regression Tests

  • Compare results against previous versions
  • Validate against EPA reference implementations
  • Ensure regulatory compliance is maintained

Test Data

  • Include EPA's test case suite
  • Create minimal synthetic examples
  • Document expected outputs

Documentation Requirements

User Documentation

  1. Quick Start Guide
  2. Installation instructions
  3. First model run in 5 minutes
  4. Basic concepts

  5. Tutorial Series

  6. Point source modeling
  7. Area source modeling
  8. Working with meteorological data
  9. Visualization options
  10. Batch processing

  11. API Reference

  12. Auto-generated from docstrings (Sphinx)
  13. Type annotations
  14. Examples for each function

  15. How-To Guides

  16. Regulatory compliance workflows
  17. Data integration recipes
  18. Custom visualization
  19. Performance optimization

  20. FAQ & Troubleshooting

  21. Common errors and solutions
  22. AERMOD-specific gotchas
  23. Platform-specific issues

Developer Documentation

  1. Architecture Overview (this document)
  2. Contributing Guide
  3. Code style
  4. Testing requirements
  5. Pull request process

  6. API Design Rationale

  7. Fortran Interface Details
  8. Release Process

Regulatory Documentation

  1. EPA Compliance Statement
  2. Which AERMOD version is wrapped
  3. Certification status
  4. Limitations and disclaimers

  5. Validation Report

  6. Comparison with EPA test cases
  7. Numerical precision analysis
  8. Known differences (if any)

Performance Considerations

Optimization Strategies

  1. Parallel Execution
  2. Multi-scenario runs via multiprocessing
  3. Batch processing for sensitivity analysis

  4. Caching

  5. Meteorological data caching
  6. Terrain data caching
  7. Parsed output caching

  8. Lazy Loading

  9. Only load data when needed
  10. Stream large output files

  11. Efficient Data Structures

  12. Use numpy arrays for receptor grids
  13. Consider HDF5 for large datasets

Scalability

  • Support for large receptor grids (>10,000 points)
  • Multiple years of meteorological data
  • Batch processing 100+ scenarios
  • Cloud deployment considerations (Docker containers)

Security & Validation

Input Validation

  • Sanitize user inputs to prevent injection attacks
  • Validate coordinate ranges
  • Check file paths for directory traversal

API Key Management

  • Support environment variables for API credentials
  • Secure storage recommendations
  • No hardcoded secrets

Output Validation

  • Verify AERMOD execution success
  • Check for numerical anomalies
  • Validate against physical constraints

Deployment Options

Local Installation

pip install pyaermod
# Installs Python package, user provides AERMOD binaries

Docker Container

FROM python:3.11
RUN apt-get update && apt-get install -y gfortran
COPY aermod-binaries/ /usr/local/bin/
RUN pip install pyaermod

Cloud Deployment

  • AWS Lambda for API endpoints (if execution time permits)
  • EC2/GCE for long-running jobs
  • Kubernetes for scalable batch processing

Licensing & Compliance

Wrapper License

  • MIT or Apache 2.0 (permissive for commercial use)
  • Clear attribution requirements

AERMOD Binaries

  • EPA public domain (no license restrictions)
  • Include EPA's disclaimer
  • Cite appropriate technical documentation

Dependencies

  • Verify all dependencies have compatible licenses
  • Document any GPL dependencies (if used)

Roadmap & Phasing

Phase 1: MVP (Months 1-3)

  • Core subprocess wrapper
  • Basic input file generation
  • Simple output parsing
  • CLI tool
  • Point and area sources
  • Static visualization
  • Documentation

Phase 2: Data Integration (Months 4-6)

  • NOAA meteorological data API
  • USGS terrain data integration
  • AERMET/AERMAP automation
  • Interactive maps
  • Basic web dashboard

Phase 3: Advanced Features (Months 7-9)

  • Volume and line sources
  • Building downwash (BPIP integration)
  • Sensitivity analysis tools
  • Advanced visualization (3D, time-lapse)
  • Automated report generation
  • Performance optimization

Phase 4: Enterprise Features (Months 10-12)

  • Multi-user project management
  • Version control integration
  • Cloud deployment
  • API authentication
  • Database backend for results
  • Advanced statistical analysis

Success Metrics

Technical Metrics

  • 100% pass rate on EPA test cases
  • <5% performance overhead vs. direct AERMOD execution
  • Support for 95% of common use cases
  • <100ms for input file generation
  • <10s for result parsing (typical run)

User Metrics

  • Reduce modeling time by 50% vs. manual workflow
  • Enable non-experts to run compliant models
  • 80% user satisfaction score

  • <2 hours to first successful model run (new users)

Business Metrics

  • Eliminate need for consulting fees for routine models
  • Enable in-house regulatory compliance
  • Reduce project turnaround time
  • Lower barrier to entry for air quality modeling

Risk Mitigation

Regulatory Acceptance Risk

  • Mitigation: Maintain exact AERMOD binary usage, provide validation documentation
  • Fallback: Export inputs for verification in AERMOD View or other approved tools

Fortran Dependency Risk

  • Mitigation: Abstract execution layer to support multiple AERMOD versions
  • Fallback: Document manual AERMOD execution procedures

Data API Reliability Risk

  • Mitigation: Implement caching, support offline mode with user-provided data
  • Fallback: Detailed documentation for manual data preparation

Maintenance Burden Risk

  • Mitigation: Comprehensive test suite, modular architecture
  • Fallback: Community contribution model, commercial support option

Conclusion

This architecture provides a robust foundation for a Python wrapper around AERMOD that balances: - Accessibility: Pythonic API, clear documentation - Regulatory Compliance: Maintains EPA-certified calculations - Extensibility: Modular design for future enhancements - Performance: Efficient data handling and parallel execution - Practicality: Focused on real-world use cases

The phased approach allows for incremental development while delivering value at each stage. The MVP focuses on core functionality to eliminate consulting dependencies, while later phases add sophisticated features for power users.

References

  • EPA AERMOD Implementation Guide
  • AERMOD Model Formulation Document
  • EPA SCRAM Website (Support Center for Regulatory Atmospheric Modeling)
  • ISO 19115 (Geographic Information Metadata)
  • CF Conventions (Climate and Forecast Metadata)