Skip to content

met_qaqc

met_qaqc

PyAERMOD Meteorological Data QA/QC

Quality assurance checks and diagnostics for processed met data (SFC / PFL files, or intermediate hourly records). These checks catch common AERMET output problems that cause silent AERMOD failures:

  • Missing-data run lengths (AERMOD tolerates <10% missing; longer gaps mean the hour will be skipped, biasing statistics).
  • Physical-extreme flags (temperature, wind speed, mixing heights, Monin-Obukhov length, friction velocity).
  • Stability-class consistency between surface heat-flux sign and mixing-height regime (e.g. L<0 should align with CBL, not SBL).
  • Low-wind bias screening (high fraction of sub-threshold speeds).
  • Temperature-profile monotonicity flags for upper-air soundings.

The functions here operate on plain Python dicts / lists so they can be applied both to AERMET .SFC output (via read_surface_file) and to raw ingested hourly data (e.g. from ISDFetcher.read_hourly).

QAQCFinding dataclass

One QA/QC issue found in a met record set.

QAQCReport dataclass

Aggregated QA/QC report for a dataset.

find_missing_runs

find_missing_runs(records: Sequence[Dict[str, Any]], field_name: str, min_run: int = 6) -> List[tuple]

Return (start_idx, end_idx, length) for runs of missing data.

A value is "missing" if it is None. Runs shorter than min_run are ignored (default 6 hours = AERMET Stage 1 'substantive gap').

check_missing_data

check_missing_data(records: Sequence[Dict[str, Any]], fields: Iterable[str] = ('wind_speed_ms', 'wind_dir', 'temp_c'), max_missing_fraction: float = 0.1, long_run_hours: int = 24) -> QAQCReport

Report missing-data issues across a time series.

Raises warnings for long missing runs and an error if any field exceeds max_missing_fraction (default 10% — AERMET guidance).

check_extremes

check_extremes(records: Sequence[Dict[str, Any]]) -> QAQCReport

Flag records whose values are outside physically plausible ranges.

check_stability_consistency

check_stability_consistency(records: Sequence[Dict[str, Any]]) -> QAQCReport

Flag mismatches between Monin-Obukhov length sign and mixing height.

In a convective boundary layer (CBL): L < 0 and both zic (convective mixing height) and zim (mechanical mixing height) should be populated, with zic > zim typically.

In a stable boundary layer (SBL): L > 0 and zic should be 0/unused.

AERMET writes an SFC record with both; inconsistency is a red flag that the MMIF or raw obs data were misinterpreted.

check_low_wind_bias

check_low_wind_bias(records: Sequence[Dict[str, Any]], threshold_ms: float = LOW_WIND_THRESHOLD_MS, warn_fraction: float = LOW_WIND_FRACTION_WARN) -> QAQCReport

Flag datasets where an unusually high fraction of hours are near-calm.

An inflated calm fraction is a strong signal of bad anemometer placement, poor starting threshold, or incorrect AERMINUTE processing. AERMOD has its own LOWWIND options; this is a data quality alarm, not a runtime config check.

check_profile_monotonic

check_profile_monotonic(levels: Sequence[Dict[str, Any]]) -> QAQCReport

Flag sounding levels whose pressure is not strictly decreasing.

Radiosonde ascents must have monotonically decreasing pressure. Any reversal is an instrument/QC artifact AERMET Stage 1 will either drop or mis-process.

run_all_qaqc

run_all_qaqc(records: Sequence[Dict[str, Any]]) -> QAQCReport

Run every surface-data QA/QC check and return a merged report.