prop/docs/algo-reports/2026-04-18-recalibration.md
Graham McIntire 5fd37a17fd
feat(propagation): per-band weight calibration from full-corpus correlation
Ran recalibrate_algo.py against the full local prop_dev (81,994 contacts,
18.6M HRRR rows) and derived per-band composite weights for the nine
bands with >=200 matched contacts. Moisture (dewpoint/PWAT/surface N)
is consistently beneficial through 5.76 GHz, reverses at 24 GHz; rain
scales sqrt(rain_k) not linearly so 24 GHz gets 0.215 rain weight
instead of the linear-ratio 0.95. 10 GHz stays as the reference band
(defaults); 47+ GHz inherits defaults (n<200).

- BandConfig.weights/1 returns per-band override or default fallback
- @band_configs carries :weights on 222/432/902/1296/2304/3400/5760/24G
- Recalibrator.compute_factors/3 + fit(band_mhz:) band-aware fitting
- Scorer + ContactLive.Show pass band_config to weights/1
- algo.md Part 2d documents the 2026-04-18 analysis + derivation rule
- scripts/derive_band_weights.py turns correlations into weight maps
- report preserved at docs/algo-reports/2026-04-18-recalibration.md
2026-04-18 10:19:26 -05:00

442 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Recalibration Analysis Report — 2026-04-18
> Auto-generated by `scripts/recalibrate_algo.py`. This report is the
> input for `algo.md` updates — diff against the prior dated section to
> see what's actually moved.
Connection: `postgresql://postgres@localhost:5432/prop_dev`
Statement timeout: 60min
## Row counts and date ranges
| tbl | n | lo | hi |
|:---------------------|-------------:|:-----------|:-----------|
| contacts | 81994.000 | 1991-05-04 | 2026-03-07 |
| hrrr_native_profiles | 11472.000 | 2019-03-09 | 2025-09-21 |
| hrrr_profiles | 18591740.000 | 2018-08-04 | 2026-04-05 |
| iemre_observations | 28438.000 | | |
| narr_profiles | 354.000 | 1991-05-04 | 2014-09-19 |
| nexrad_observations | 7286.000 | 2019-08-17 | 2024-09-22 |
| propagation_scores | nan | | |
| rtma_observations | 0.000 | | |
| soundings | 26933.000 | 1990-06-07 | 2026-04-18 |
| surface_observations | 599046.000 | 1991-05-03 | 2026-04-18 |
| terrain_profiles | 81994.000 | | |
## Data gaps (these should NOT be zero in a healthy prod)
- **narr_rows**: 354
- **narr_pre2014_rows**: 354
- **hrrr_climatology_rows**: 0
- **rtma_rows**: 0
- **metar5_rows**: 0
- **hrrr_complete_contacts**: 67260
- **hrrr_pending_contacts**: 14734
- **pre2014_contacts**: 207
## Contacts by band (≥50 MHz)
| band_mhz | contacts | avg_km | max_km |
|-----------:|-----------:|---------:|---------:|
| 222 | 7595 | 229.000 | 2306.000 |
| 432 | 9177 | 200.000 | 1752.000 |
| 902 | 1794 | 171.000 | 915.000 |
| 1296 | 2996 | 160.000 | 1752.000 |
| 2304 | 749 | 153.000 | 655.000 |
| 3400 | 351 | 151.000 | 643.000 |
| 5760 | 328 | 125.000 | 505.000 |
| 10000 | 54264 | 212.000 | 2393.000 |
| 24000 | 3806 | 95.000 | 710.000 |
| 47000 | 762 | 65.000 | 343.000 |
| 75000 | 106 | 60.000 | 289.000 |
| 122000 | 35 | 27.000 | 139.000 |
| 134000 | 5 | 65.000 | 157.000 |
| 142000 | 4 | 47.000 | 79.700 |
| 145000 | 2 | 40.000 | 79.600 |
| 241000 | 14 | 31.000 | 114.400 |
| 288000 | 1 | 1.000 | 1.246 |
| 322000 | 1 | 1.000 | 1.400 |
| 403000 | 1 | 1.000 | 1.400 |
| 411000 | 1 | 0.000 | 0.050 |
## Monthly sounding ducting
| month | soundings | ducting_pct | avg_min_grad | avg_pwat |
|--------:|------------:|--------------:|---------------:|-----------:|
| 1 | 95 | 28.400 | -171.400 | 10.900 |
| 2 | 131 | 29.000 | -163.100 | 7.900 |
| 3 | 81 | 17.300 | -148.100 | 9.400 |
| 4 | 134 | 31.300 | -175.600 | 13.500 |
| 5 | 311 | 51.800 | -286.400 | 25.600 |
| 6 | 380 | 69.500 | -355.900 | 32.000 |
| 7 | 302 | 67.500 | -293.800 | 29.700 |
| 8 | 5007 | 55.200 | -255.400 | 35.200 |
| 9 | 2335 | 56.200 | -279.800 | 27.200 |
| 10 | 155 | 57.400 | -307.100 | 19.300 |
| 11 | 158 | 50.000 | -256.700 | 12.000 |
| 12 | 140 | 25.700 | -185.200 | 10.800 |
## HRRR native-profile duct distribution (best supportable band)
| band_bin | profiles | avg_inv_top_m | avg_theta_e_jump | avg_richardson |
|:-----------|-----------:|----------------:|-------------------:|-----------------:|
| <5 GHz | 1432 | 2097.000 | 5.400 | 18.420 |
| 15-30 GHz | 10 | 2091.000 | 5.900 | 8.690 |
| 30-75 GHz | 4 | 3860.000 | 5.000 | 12.120 |
| 5-15 GHz | 98 | 4408.000 | 25.700 | 12.550 |
| none | 9928 | 11497.000 | 80.500 | 38.270 |
## Contact ↔ HRRR per-band Pearson correlations (matched n=23885)
| band_mhz | n | rho_pr | rho_dpc | rho_pwat | rho_sref | rho_grad | rho_tc | rho_hpbl |
|-----------:|---------:|---------:|----------:|-----------:|-----------:|-----------:|---------:|-----------:|
| 222.000 | 5392.000 | -0.038 | 0.063 | 0.077 | 0.077 | 0.057 | -0.086 | -0.033 |
| 432.000 | 6583.000 | 0.058 | 0.137 | 0.124 | 0.136 | 0.041 | -0.089 | -0.066 |
| 902.000 | 1317.000 | 0.044 | 0.096 | 0.049 | 0.079 | -0.014 | -0.060 | -0.010 |
| 1296.000 | 2146.000 | 0.013 | 0.085 | 0.049 | 0.085 | -0.031 | -0.032 | -0.041 |
| 2304.000 | 564.000 | -0.006 | 0.130 | 0.086 | 0.154 | -0.110 | -0.114 | -0.074 |
| 3400.000 | 280.000 | -0.025 | 0.150 | 0.131 | 0.176 | 0.046 | -0.103 | -0.023 |
| 5760.000 | 246.000 | 0.040 | 0.087 | 0.137 | 0.105 | 0.021 | -0.082 | -0.083 |
| 10000.000 | 6675.000 | -0.066 | -0.028 | 0.017 | 0.031 | -0.024 | 0.043 | -0.025 |
| 24000.000 | 613.000 | -0.178 | -0.208 | -0.172 | -0.103 | -0.251 | -0.007 | -0.201 |
| 47000.000 | 53.000 | -0.423 | -0.518 | -0.149 | 0.510 | -0.154 | 0.222 | -0.465 |
## Per-band HPBL bin distance distribution
One table per band with 50 matched contacts. A band-specific effect here means HPBL belongs in the scorer for that band.
**222 MHz:**
| bin | n | avg_km | p50_km |
|:----------|-----:|---------:|---------:|
| <200 | 956 | 273.500 | 210.000 |
| 200-500 | 1131 | 215.500 | 164.000 |
| 500-1000 | 1185 | 205.400 | 152.000 |
| 1000-1500 | 948 | 220.600 | 156.000 |
| 1500-2000 | 913 | 223.000 | 171.000 |
| >=2000 | 259 | 246.900 | 192.000 |
**432 MHz:**
| bin | n | avg_km | p50_km |
|:----------|-----:|---------:|---------:|
| <200 | 1258 | 223.000 | 179.000 |
| 200-500 | 1370 | 198.600 | 143.500 |
| 500-1000 | 1413 | 183.700 | 131.000 |
| 1000-1500 | 1124 | 198.000 | 145.000 |
| 1500-2000 | 1129 | 187.100 | 142.000 |
| >=2000 | 289 | 169.200 | 126.000 |
**902 MHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <200 | 215 | 205.300 | 165.000 |
| 200-500 | 275 | 152.500 | 133.000 |
| 500-1000 | 302 | 166.500 | 123.000 |
| 1000-1500 | 259 | 175.300 | 137.000 |
| 1500-2000 | 222 | 175.900 | 152.000 |
| >=2000 | 44 | 158.700 | 131.000 |
**1.296 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <200 | 355 | 179.600 | 151.000 |
| 200-500 | 484 | 159.300 | 129.500 |
| 500-1000 | 479 | 145.600 | 107.000 |
| 1000-1500 | 376 | 154.800 | 131.000 |
| 1500-2000 | 380 | 153.600 | 122.000 |
| >=2000 | 72 | 143.700 | 108.000 |
**2.304 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <200 | 72 | 188.000 | 160.000 |
| 200-500 | 103 | 174.800 | 156.000 |
| 500-1000 | 155 | 150.300 | 122.000 |
| 1000-1500 | 121 | 151.600 | 136.000 |
| 1500-2000 | 97 | 161.000 | 147.000 |
| >=2000 | 16 | 144.800 | 140.500 |
**3.4 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <200 | 39 | 157.400 | 131.000 |
| 200-500 | 57 | 162.500 | 140.000 |
| 500-1000 | 64 | 153.500 | 111.000 |
| 1000-1500 | 67 | 150.500 | 136.000 |
| 1500-2000 | 39 | 150.300 | 122.000 |
| >=2000 | 14 | 157.400 | 140.500 |
**5.76 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <200 | 24 | 174.500 | 138.500 |
| 200-500 | 49 | 134.600 | 135.000 |
| 500-1000 | 62 | 118.000 | 103.000 |
| 1000-1500 | 54 | 125.000 | 133.000 |
| 1500-2000 | 45 | 114.600 | 122.000 |
| >=2000 | 12 | 142.700 | 140.500 |
**10 GHz:**
| bin | n | avg_km | p50_km |
|:----------|-----:|---------:|---------:|
| <200 | 1573 | 213.900 | 178.800 |
| 200-500 | 2146 | 201.300 | 175.000 |
| 500-1000 | 1663 | 211.800 | 158.100 |
| 1000-1500 | 742 | 214.500 | 212.500 |
| 1500-2000 | 410 | 179.400 | 173.000 |
| >=2000 | 138 | 201.000 | 173.000 |
**24 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <200 | 115 | 120.600 | 110.000 |
| 200-500 | 236 | 95.400 | 99.000 |
| 500-1000 | 189 | 79.700 | 80.800 |
| 1000-1500 | 44 | 103.800 | 99.000 |
| 1500-2000 | 10 | 46.800 | 34.000 |
| >=2000 | 19 | 69.200 | 23.200 |
**47 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <200 | 9 | 111.200 | 85.300 |
| 200-500 | 19 | 82.200 | 58.500 |
| 500-1000 | 16 | 55.600 | 27.600 |
| 1000-1500 | 5 | 16.100 | 11.400 |
| >=2000 | 4 | 31.800 | 31.800 |
## Per-band pressure bin distance distribution
Surface pressure tends to proxy synoptic-scale stagnation, so we want to see longer distances under the 1015-1025 mb ridge at every band that ducts.
**222 MHz:**
| bin | n | avg_km | p50_km |
|:----------|-----:|---------:|---------:|
| <990 | 1967 | 228.800 | 177.000 |
| 990-1000 | 1289 | 248.500 | 191.000 |
| 1000-1010 | 1263 | 235.400 | 186.000 |
| 1010-1020 | 778 | 183.300 | 141.000 |
| >=1020 | 95 | 159.300 | 126.000 |
**432 MHz:**
| bin | n | avg_km | p50_km |
|:----------|-----:|---------:|---------:|
| <990 | 2548 | 189.600 | 140.000 |
| 990-1000 | 1580 | 223.200 | 173.000 |
| 1000-1010 | 1285 | 202.700 | 149.000 |
| 1010-1020 | 1050 | 172.700 | 129.000 |
| >=1020 | 120 | 144.800 | 84.500 |
**902 MHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <990 | 422 | 157.600 | 131.500 |
| 990-1000 | 400 | 206.400 | 157.000 |
| 1000-1010 | 241 | 189.200 | 147.000 |
| 1010-1020 | 223 | 138.800 | 143.000 |
| >=1020 | 31 | 72.000 | 32.000 |
**1.296 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <990 | 696 | 149.600 | 122.000 |
| 990-1000 | 579 | 183.700 | 145.000 |
| 1000-1010 | 451 | 161.600 | 108.000 |
| 1010-1020 | 385 | 130.500 | 114.000 |
| >=1020 | 35 | 112.700 | 112.000 |
**2.304 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <990 | 138 | 155.100 | 136.000 |
| 990-1000 | 201 | 168.200 | 141.000 |
| 1000-1010 | 97 | 178.500 | 117.000 |
| 1010-1020 | 116 | 147.200 | 152.000 |
| >=1020 | 12 | 126.300 | 137.000 |
**3.4 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <990 | 75 | 157.400 | 138.000 |
| 990-1000 | 134 | 156.800 | 135.000 |
| 1000-1010 | 34 | 179.700 | 131.000 |
| 1010-1020 | 28 | 122.200 | 124.000 |
| >=1020 | 9 | 114.600 | 131.000 |
**5.76 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <990 | 55 | 112.700 | 111.000 |
| 990-1000 | 120 | 131.600 | 119.500 |
| 1000-1010 | 27 | 128.000 | 100.000 |
| 1010-1020 | 36 | 146.900 | 152.000 |
| >=1020 | 8 | 123.000 | 135.000 |
**10 GHz:**
| bin | n | avg_km | p50_km |
|:----------|-----:|---------:|---------:|
| <990 | 3515 | 222.700 | 196.600 |
| 990-1000 | 1620 | 189.700 | 152.700 |
| 1000-1010 | 803 | 194.200 | 153.000 |
| 1010-1020 | 651 | 182.900 | 133.900 |
| >=1020 | 86 | 187.700 | 158.100 |
**24 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <990 | 282 | 97.300 | 98.700 |
| 990-1000 | 200 | 97.900 | 99.000 |
| 1000-1010 | 57 | 95.200 | 102.300 |
| 1010-1020 | 70 | 70.600 | 80.800 |
| >=1020 | 4 | 103.600 | 103.600 |
**47 GHz:**
| bin | n | avg_km | p50_km |
|:----------|----:|---------:|---------:|
| <990 | 19 | 80.500 | 43.900 |
| 990-1000 | 25 | 62.600 | 31.800 |
| 1000-1010 | 2 | 84.600 | 84.600 |
| 1010-1020 | 7 | 56.400 | 58.500 |
## Contact ↔ NARR per-band Pearson correlations (pre-2014 only, matched n=201)
Historical sanity check: if the HRRR-era signs and magnitudes survive on 30+ years of NARR reanalysis, the scoring factors are physical rather than HRRR-artefact. Diverging signs = flag it.
| band_mhz | n | rho_pr | rho_dpc | rho_pwat | rho_sref | rho_grad | rho_tc | rho_hpbl |
|-----------:|--------:|---------:|----------:|-----------:|-----------:|-----------:|---------:|-----------:|
| 10000.000 | 103.000 | -0.402 | -0.402 | -0.402 | -0.220 | -0.248 | -0.402 | -0.402 |
## NEXRAD composite reflectivity vs distance (matched n=20900)
Rain-attenuation sanity check one table per band with 50 matched contacts. At rain-sensitive bands (24+ GHz) higher `max_dbz` should map to shorter contacts; at 10 GHz and below the effect should be barely detectable.
**10 GHz:**
| bin | n | avg_km | p50_km |
|:-----------------|------:|---------:|---------:|
| none (<5) | 14110 | 210.000 | 197.000 |
| drizzle (5-20) | 1284 | 200.900 | 199.300 |
| light (20-30) | 1736 | 211.800 | 197.100 |
| moderate (30-45) | 1609 | 230.900 | 215.200 |
| heavy (>=45) | 677 | 191.900 | 146.900 |
**24 GHz:**
| bin | n | avg_km | p50_km |
|:-----------------|----:|---------:|---------:|
| none (<5) | 869 | 90.900 | 83.700 |
| drizzle (5-20) | 74 | 108.300 | 123.200 |
| light (20-30) | 151 | 88.200 | 70.900 |
| moderate (30-45) | 76 | 109.300 | 122.400 |
| heavy (>=45) | 49 | 86.600 | 102.300 |
**47 GHz:**
| bin | n | avg_km | p50_km |
|:-----------------|----:|---------:|---------:|
| none (<5) | 186 | 52.400 | 56.000 |
| light (20-30) | 30 | 59.300 | 66.300 |
| moderate (30-45) | 9 | 75.100 | 81.600 |
| heavy (>=45) | 15 | 71.400 | 82.800 |
## hrrr_native_profiles.best_duct_band_ghz vs distance (matched n=57669)
Validates the 1.15× boost in `Scorer.score_refractivity/4`: contacts where the native duct supports the target band should run longer than those where it does not. One table per band with ≥50 matched contacts.
**10 GHz:**
| bin | n | avg_km | p50_km |
|:----------|------:|---------:|---------:|
| none | 45395 | 212.700 | 195.000 |
| <5 GHz | 7151 | 200.500 | 171.800 |
| 5-10 GHz | 444 | 200.600 | 174.200 |
| 10-24 GHz | 146 | 203.300 | 225.200 |
| 24-47 GHz | 5 | 177.200 | 133.300 |
| >=47 GHz | 23 | 147.500 | 131.500 |
**24 GHz:**
| bin | n | avg_km | p50_km |
|:----------|---------:|---------:|---------:|
| none | 3188.000 | 95.000 | 92.600 |
| <5 GHz | 477.000 | 94.900 | 83.900 |
| 5-10 GHz | 36.000 | 97.500 | 113.700 |
| 10-24 GHz | 2.000 | 44.700 | 44.700 |
| >=47 GHz | 6.000 | 131.500 | 131.500 |
**47 GHz:**
| bin | n | avg_km | p50_km |
|:---------|--------:|---------:|---------:|
| none | 552.000 | 64.300 | 65.400 |
| <5 GHz | 131.000 | 56.800 | 61.800 |
| 5-10 GHz | 6.000 | 98.100 | 113.700 |
**75 GHz:**
| bin | n | avg_km | p50_km |
|:-------|-------:|---------:|---------:|
| none | 76.000 | 54.800 | 36.700 |
| <5 GHz | 15.000 | 13.900 | 14.600 |
## Commercial-link rx_power degradation vs contemporaneous DFW contacts (matched n=0)
_No DFW-zone contacts fall inside the commercial_samples date window yet. Expected to stay empty until the Aug/Sep contest season produces contacts that overlap with live SNMP polling._