Drop dead backtest features, document consolidated results

Backtest on 11,431 native profiles (2026-04-11):
- Drop duct_usable_10/24/47ghz (always 1.0, no discrimination)
- Drop bulk_richardson (near-identical QSO vs baseline means)
- Document all feature results with signal strength assessment
- theta_e_jump is strongest native discriminator (44% lift)
- best_duct_freq and duct_thickness show clear physical signal
This commit is contained in:
Graham McIntire 2026-04-11 13:11:38 -05:00
parent 3077e102fc
commit d49ec5d32e
2 changed files with 48 additions and 6 deletions

36
algo.md
View file

@ -1958,9 +1958,10 @@ Features are grouped by data source and physical quantity:
| shear_at_top | hrrr_native_profiles | Wind shear (m/s) at inversion top |
| duct_thickness | hrrr_native_profiles | Max duct thickness (m) — larger ducts trap lower frequencies |
| best_duct_freq | hrrr_native_profiles | Lowest trapped frequency (GHz) — lower = stronger ducting |
| duct_usable_10ghz | hrrr_native_profiles | Binary: any duct traps 10 GHz? |
| duct_usable_24ghz | hrrr_native_profiles | Binary: any duct traps 24 GHz? |
| duct_usable_47ghz | hrrr_native_profiles | Binary: any duct traps 47 GHz? |
| ~~duct_usable_10ghz~~ | hrrr_native_profiles | DEAD — no discrimination (always 1.0 for both QSO and baseline) |
| ~~duct_usable_24ghz~~ | hrrr_native_profiles | DEAD — no discrimination (always 1.0) |
| ~~duct_usable_47ghz~~ | hrrr_native_profiles | DEAD — no discrimination (always 1.0) |
| ~~bulk_richardson~~ | hrrr_native_profiles | DEAD — near-identical means (24.8 vs 23.4), no lift |
**Climatology and remote sensing:**
@ -1976,6 +1977,35 @@ Features are grouped by data source and physical quantity:
| distance_to_front | — | Distance (km) to nearest detected front — awaiting frontal analysis pipeline |
| parallel_to_front | — | cos²(path-front angle) — awaiting frontal analysis + requires path bearing |
### Consolidated Backtest Results (2026-04-11)
Sample: 5,000 QSOs, 11,431 native profiles across 499 HRRR hours (2019-2024).
| Feature | QSO N | QSO Mean | QSO p50 | Baseline Mean | Baseline p50 | Signal |
|---|---|---|---|---|---|---|
| **theta_e_jump** | 4915 | 49.4 K | 5.8 K | 34.3 K | 2.1 K | **Strong** — 44% higher jumps during QSOs |
| **best_duct_freq** | 697 | 0.84 GHz | 0.40 GHz | 0.28 GHz | 0.26 GHz | **Strong** — QSO ducts trap lower freqs |
| **native_surface_refractivity** | 4915 | 331.7 | 334.4 | 324.3 | 330.1 | **Moderate** — higher N during QSOs |
| **duct_thickness** | 697 | 156 m | 159 m | 227 m | 217 m | **Inverted** — thinner ducts during QSOs (shallow surface ducts) |
| **td_depression** | 5000 | 6.8°C | 5.8°C | 5.9°C | 3.8°C | **Moderate** — wider depression during QSOs |
| **time_of_day** | 5000 | 16.7h | 17.9h | 11.9h | 11.8h | **Strong** — contests are evening-biased |
| **naive_gradient** | 5000 | -113 | -104 | -107 | -97 | **Weak** — small separation |
| **shear_at_top** | 4915 | 6.4 m/s | 4.0 m/s | 5.8 m/s | 3.3 m/s | **Marginal** |
| **pressure** | 5000 | 983.8 | 989.5 | 982.7 | 989.7 | **Weak** — near-identical |
| **nexrad_texture** | 1796 | 16.8 | 0.0 | 20.6 | 0.0 | **Weak/inverted** |
| bulk_richardson | 4915 | 24.8 | 6.3 | 23.4 | 5.8 | **Dead** — no discrimination |
| duct_usable_*ghz | 697 | ~1.0 | 1.0 | ~1.0 | 1.0 | **Dead** — always 1.0 |
| temperature_anomaly | 0 | — | — | — | — | NO DATA (climatology not built) |
**Key findings:**
- **theta_e_jump** is the single strongest native-level discriminator — large theta-e jumps at inversion tops indicate strong decoupling that traps microwave energy
- **best_duct_freq** confirms that QSO-producing ducts are physically stronger (trap lower frequencies)
- **duct_thickness** being inverted makes physical sense: shallow surface ducts (100-200m) produce the strongest trapping for microwave bands, while thick ducts (>200m) are weaker elevated features
- **duct_usable_*ghz** features are dead because nearly all detected ducts are thick enough to trap 10-47 GHz — the threshold is too low to discriminate
- **bulk_richardson** shows no signal, likely because both stable (high Ri) and unstable (low Ri) conditions can produce inversions; Ri alone doesn't predict duct quality
**Implications for real-time scoring:** The native features cannot currently be used in real-time propagation scoring because the native HRRR product (~530 MB/hour) is too expensive to fetch for the full CONUS grid. However, the findings validate the existing scorer's physics: humidity, td_depression, and refractivity factors capture the same mechanisms (moisture-driven refractivity, inversion strength) that the native features measure more directly. Future work could incorporate native data for specific paths or high-interest regions.
### HRRR Climatology
The `hrrr_climatology` table stores pre-computed mean and standard deviation of surface temperature from `hrrr_profiles`, keyed on `(lat, lon, month, hour)` at the HRRR grid resolution. This allows computing how anomalous the current surface temperature is relative to historical norms for the same location, season, and time of day. The meteorologist noted that extremely hot days (~10°F above normal in summer) produce enhanced ducting even in the afternoon when the time-of-day factor normally suppresses the score. The `temperature_anomaly` feature returns `surface_temp_c - climatological_mean` as a signed float.

View file

@ -29,11 +29,23 @@ defmodule Microwaveprop.Backtest.Features do
@doc """
Returns a map of all backtestable features: `%{name => fun/3}`.
Excludes `duct_usable_for_band/4` (4-arity) and the placeholder stubs
that always return nil (`distance_to_front`, `parallel_to_front`).
Excludes dead features (no discrimination in backtest), 4-arity helpers,
and placeholder stubs that always return nil.
"""
def all_features do
excluded = MapSet.new([:duct_usable_for_band, :distance_to_front, :parallel_to_front])
excluded =
MapSet.new([
# 4-arity helper
:duct_usable_for_band,
# Stubs (always nil)
:distance_to_front,
:parallel_to_front,
# Dead features — no discrimination in consolidated backtest (2026-04-11)
:duct_usable_10ghz,
:duct_usable_24ghz,
:duct_usable_47ghz,
:bulk_richardson
])
:functions
|> __MODULE__.__info__()