Skip to content

design_values

design_values

Regulatory design-value calculations from POSTFILE output.

This module turns raw concentration time series (parsed via :mod:pyaermod.postfile) into the percentile / design-value figures that NAAQS regulatory submittals quote. Each function is annotated with its 40 CFR Part 50 citation and the EPA design-value guidance that prescribes the math.

All functions accept a :class:pandas.DataFrame shaped like the data attribute of :class:pyaermod.postfile.PostfileResult, i.e. with columns x, y, concentration, date (string in YYMMDDHH form), ave, grp.

Background concentrations (uniform or time-varying) can be added via :func:add_background before computing design values; per- pollutant pairing rules are in the function docstring.

DesignValue dataclass

Result of a design-value computation at one receptor.

add_background

add_background(df: DataFrame, background: Union[float, Series, dict]) -> pd.DataFrame

Add a background concentration to every row of df.

Parameters:

Name Type Description Default
df DataFrame

POSTFILE-shaped DataFrame.

required
background Union[float, Series, dict]

Either a uniform float, a pd.Series indexed by date string (YYMMDDHH), or a dict mapping date string -> float. Index format must match df['date'].

required

Returns:

Type Description
A new DataFrame with ``concentration`` += background. Rows whose
date has no background entry are left unchanged.

annual_mean

annual_mean(df: DataFrame, *, pollutant: str = '') -> pd.DataFrame

Annual-mean design-value calculation.

Used for the PM2.5 annual NAAQS (40 CFR 50.18) and the NO2 annual NAAQS (40 CFR 50.11). The standard form is the multi-year average of annual means; this returns per-receptor annual means — caller averages across years if a 3-year design value is wanted.

Returns one row per (x, y, year) with column concentration.

pm25_24hr_design_value

pm25_24hr_design_value(df: DataFrame, *, n_years: int = 3) -> pd.DataFrame

PM2.5 24-hour design value: 3-year avg of annual 98th percentiles.

Per 40 CFR 50, Appendix N. Standard: 35 µg/m³.

Input df must contain hourly (or 24-hr-averaged) concentrations; 24-hr daily values are computed by averaging within each (receptor, day) group when the AVE column equals "1-HR", otherwise rows are used as-is.

Returns one row per receptor with the n-year average of annual 98th percentile daily-max concentrations.

no2_1hr_design_value

no2_1hr_design_value(df: DataFrame, *, n_years: int = 3) -> pd.DataFrame

NO2 1-hour design value: 3-year avg of annual 98th percentile of daily max 1-hour concentrations.

Per 40 CFR 50, Appendix S. Standard: 100 ppb.

Returns one row per receptor.

so2_1hr_design_value

so2_1hr_design_value(df: DataFrame, *, n_years: int = 3) -> pd.DataFrame

SO2 1-hour design value: 3-year avg of annual 99th percentile of daily max 1-hour concentrations.

Per 40 CFR 50, Appendix T. Standard: 75 ppb.

pm10_24hr_design_value

pm10_24hr_design_value(df: DataFrame) -> pd.DataFrame

PM10 24-hour design value: max daily concentration not to be exceeded more than once per year on average over 5 years.

Per 40 CFR 50.6. Standard: 150 µg/m³. Functionally we return the "high, second-high" (H2H) per receptor — the 2nd-highest daily-max concentration in the multi-year record (allowing one exceedance).

Caller decides how to average the H2H across the standard's 5-year window; this function returns one H2H per receptor.

o3_8hr_design_value

o3_8hr_design_value(df_8hr: DataFrame, *, n_years: int = 3) -> pd.DataFrame

O3 8-hour design value: 3-year avg of annual 4th-highest daily max 8-hour average concentration.

Per 40 CFR 50.19. Standard: 70 ppb.

Note: this expects the 8-hour rolling-average concentrations as input (AERMOD AVE='8-HR' column). Each (receptor, day) max is taken to obtain the daily 8-hr max series.

naaqs_compliance_report

naaqs_compliance_report(pollutant: str, df: DataFrame, *, background: Optional[Union[float, dict, Series]] = None, n_years: int = 3) -> pd.DataFrame

One-stop compliance roll-up: design value + NAAQS comparison.

Dispatches to the right design-value function for pollutant, optionally adds background, joins the NAAQS row, and flags receptors that exceed.

Returns a DataFrame with columns:

  • x, y: receptor coords
  • design_value: numeric DV
  • naaqs_level, units, cfr_reference
  • exceeds: bool

Supported pollutants: PM2.5 (24-hour), PM10, NO2 (1-hour), SO2, O3, plus annual forms via pollutant="PM2.5_annual" / "NO2_annual".