Wissenschaftliche Methodik
← zurück zum Hub
Alle Skripte unter /opt/rhein/poller/. Ergebnisse landen im InfluxDB-Bucket rhein_derived.
Phase 2.1 — NHN-Höhen + Wasserspiegelgefälle¶
Theorie¶
Wasserstand wird in cm über Pegelnullpunkt (PNP) gemessen. Absolute Höhe in m über NHN:
$$H_{\text{NHN}}(t) = \text{PNP} + \frac{W(t)}{100}$$
Wasserspiegelgefälle zwischen zwei Stationen (cm/km):
$$I_{ij}(t) = \frac{H_{\text{NHN},i}(t) - H_{\text{NHN},j}(t)}{\Delta x_{ij}} \cdot 100$$
mit i = stromaufwärts, j = stromabwärts.
Implementation¶
phase2_nhn_v2.py — jahresweise Verarbeitung (Influx-schonend), bei Mainz PNP-Validity ab 2019-11-01 beachtet (vor diesem Datum wird NHN nicht berechnet, weil PNP-Wert unbekannt).
Drei Abschnitte: - WORMS → NIERSTEIN (Δx = 37,236 km) - NIERSTEIN → MAINZ (Δx = 17,664 km) - WORMS → MAINZ (Δx = 54,9 km, Gesamtabschnitt)
Ergebnisse¶
Längsgefälle Tagesmittel 2024-06-02 bis -08:
| Abschnitt | Gefälle (cm/km) |
|---|---|
| WORMS → NIERSTEIN | 9,5–11,6 |
| NIERSTEIN → MAINZ | 11,4–12,5 |
| WORMS → MAINZ | 10,1–11,9 |
Plausibel für Mittelrhein (typisch 8–15 cm/km).
Volumen: 844 607 NHN-Punkte + 764 359 Gefälle-Punkte in rhein_derived für 2017-07-18 bis heute.
Phase 2.2 — Globale Cross-Korrelation (CCF)¶
Theorie¶
Wellen-Celerität c zwischen zwei Stationen wird aus dem Lag τ* des CCF-Maximums abgeleitet:
$$\rho_{xy}(\tau) = \frac{\sum_t (x(t) - \bar{x})(y(t+\tau) - \bar{y})}{\sqrt{\sum_t (x-\bar{x})^2 \sum_t (y-\bar{y})^2}}$$
$$c = \frac{\Delta x}{\tau^*}$$
CCF wird auf der 1. Differenz dW/dt berechnet, um langsame Trends zu eliminieren.
Ergebnisse 2020–2026 (5 Segments)¶
| Segment | Δx (km) | τ* (h) | c (m/s) | r_max |
|---|---|---|---|---|
| WORMS → NIERSTEIN | 37,2 | 3,75 | 2,76 | 0,11 |
| NIERSTEIN → MAINZ | 17,7 | 1,00 | 4,91 | 0,09 |
| WORMS → MAINZ | 54,9 | 7,25 | 2,10 | 0,09 |
| SPEYER → WORMS | 42,8 | 7,75 | 1,53 | 0,11 |
| SPEYER → MAINZ | 97,7 | 17,00 | 1,60 | 0,07 |
Bewertung¶
- Lighthill-Whitham-Test besteht: c ≈ (5/3) · v erwartet für kinematische Wellen-Celerität in breiten Rechteckgerinnen nach Manning. Bei Fließgeschwindigkeit v ≈ 1,2–1,8 m/s am Mittelrhein ergibt sich c_erwartet ≈ 2,0–3,0 m/s. Hauptergebnis 2,1 m/s passt.
- r_max niedrig (0,07–0,11): über 6 Jahre überlagern sich viele Hochwasser- und Niedrigwasserregimes, das Wellensignal verwässert. Daher in Phase 2.4 event-basierte CCF.
- Inkonsistenz der Lags (3,75 + 1,00 ≠ 7,25 h): Das kurze Segment NIERSTEIN→MAINZ hat zu schwaches CCF-Maximum für sauberen Lag.
Phase 2.3 — Autokorrelation + Welch-Spektrum¶
Theorie¶
ACF misst System-Gedächtnis: wie lange dauert es, bis der Wasserstand „seine Geschichte vergisst"? Quantifiziert über die e-fold-Abklingzeit (ACF unter 1/e ≈ 0,368).
Welch-Periodogramm zerlegt die Zeitreihe in Frequenzbeiträge — interessant ist der 1/24h-Peak als möglicher Indikator für Tagesperiodik (Schweizer Schwellbetrieb der Laufwasserkraftwerke?).
Ergebnisse (Jahr 2024 als Referenz)¶
| Station | r(24 h) | r(72 h) | e-fold (h) | SNR @ 1/24 h |
|---|---|---|---|---|
| WORMS | 0,973 | 0,860 | > 168 | 1,56 |
| NIERSTEIN | 0,977 | 0,870 | > 168 | 3,18 |
| MAINZ | 0,977 | 0,866 | > 168 | 1,60 |
| SPEYER | 0,970 | 0,871 | > 168 | 2,24 |
| MANNHEIM | 0,971 | 0,856 | > 168 | 1,24 |
| MANNHEIM_NECKAR | 0,969 | 0,843 | > 168 | 2,84 |
| RAUNHEIM | 0,936 | 0,704 | > 168 | 2,25 |
Bewertung¶
- Systemgedächtnis > 7 Tage (e-fold nicht erreicht). Der Mittelrhein ist sehr träge — heute ist mit gestern zu 95 % korreliert.
- 1/24h-Peak deutlich bei Nierstein (SNR 3,18) und Mannheim-Neckar (2,84).
- Vorsicht bei Interpretation: Tagesperiodik kann aus Schiffsverkehr, Verdunstungs-Tagesgang oder Mess-Drift kommen — Schweizer Schwellbetrieb ist eine Hypothese, kein Beweis.
Phase 2.4 — Event-basierte CCF¶
Motivation¶
Globale CCF mittelt über sehr unterschiedliche Regimes. Wirft man stattdessen ein Fenster (5 Tage vor + 10 Tage nach dem Peak) um identifizierte HW-Ereignisse, isoliert man das Wellensignal.
HW-Identifikation: Worms-W > 450 cm, Mindestabstand zwischen Events 14 Tage. Im Zeitraum 2016–2026 wurden ~14 Events gefunden.
Ergebnisse — Highlights¶
| Event | Peak (cm) | WORMS→NIERSTEIN c (m/s) | r_max | WORMS→MAINZ c (m/s) | r_max |
|---|---|---|---|---|---|
| 2018-01-07 „HW Januar 2018" | 621 | 3,45 | 0,44 | 4,07 | 0,47 |
| 2020-02-05 (Sturmtief) | 523 | 1,53 | 0,62 | 2,65 | 0,54 |
| 2024-06-04 (Sommer-HW) | 695 | 1,18 | 0,52 | 0,91 | 0,54 |
| 2019-05-23 | 467 | 1,33 | 0,55 | 2,65 | 0,48 |
| 2018-01-25 (Welle 2) | 637 | 0,86 | 0,36 | 1,91 | 0,33 |
Bewertung¶
- r_max 0,4–0,6 ist 4-6× besser als globale CCF (0,07–0,11). Event-Fenstering hat den Signal-zu-Rausch dramatisch verbessert.
- Median c (Worms→Mainz) über saubere Events ≈ 2,5–4 m/s. Konsistent mit Lighthill-Whitham.
- Einzelne Events liefern absurde Lags (z. B. τ* = 0,25 h → c = 41 m/s) wenn r_max < 0,3 — diese sind als unzuverlässig zu markieren.
Phase 3.1 — Muskingum-Routing¶
Theorie¶
Klassisches lineares Routing-Modell:
$$Q_{\text{out}}(t+\Delta t) = C_0 \cdot Q_{\text{in}}(t+\Delta t) + C_1 \cdot Q_{\text{in}}(t) + C_2 \cdot Q_{\text{out}}(t)$$
mit $$C_0 = \frac{\Delta t - 2 K x}{2 K (1-x) + \Delta t}, \quad C_1 = \frac{\Delta t + 2 K x}{2 K (1-x) + \Delta t}, \quad C_2 = \frac{2 K (1-x) - \Delta t}{2 K (1-x) + \Delta t}$$
- K = Wellen-Storage-Time (h)
- x ∈ [0, 0,5] = Wichtung Inflow vs Storage (0 = pure Reservoir, 0,5 = pure Translation)
- Δt = 0,25 h (15 min)
v2: Dual-Input mit Q_lat¶
Erste Naive-Variante (nur Worms→Mainz, Nelder-Mead) konvergierte nicht und unterprädizierte den Peak systematisch (Main fehlte als Input). v2 macht es richtig:
$$Q_{\text{Mainz,pred}}(t) = \mathcal{M}1(Q}}; K_1, x_1) + \mathcal{M2(Q$$}}; K_2, x_2) + Q_{\text{lat}
5 Parameter ($K_1, x_1, K_2, x_2, Q_{\text{lat}}$) werden via Differential Evolution auf einem HW-Event kalibriert. Globale Parameter sind der Median über mehrere Train-Events.
Ergebnisse¶
Globale Parameter (Median über 3 Train-Events): - $K_1 = 14{,}26$ h (Worms → Mainz Storage-Time) - $x_1 = 0{,}00$ (Reservoir-Limit, kein Translation-Anteil) - $K_2 = 0{,}40$ h (Raunheim → Mainz, kurze Strecke) - $x_2 = 0{,}012$ - $Q_{\text{lat}} = -20$ m³/s (kleiner Mess-Bias)
Train-NSE:
| Event | NSE | RMSE | Peak obs / pred |
|---|---|---|---|
| HW Jan 2018 | 0,994 | 62 | 4990 / 5065 m³/s |
| HW Feb 2020 | 0,998 | 36 | 3940 / 3918 |
| HW Juni 2024 | 0,973 | 130 | 4960 / 5205 |
Test-NSE (mit globalen Parametern):
| Event | NSE | RMSE | Peak obs / pred |
|---|---|---|---|
| HW Jan 2018 W2 | 0,983 | 69 | 4680 / 4852 |
| HW Mai 2019 | 0,987 | 77 | 3390 / 3285 |
| HW Dez 2023 | 0,988 | 51 | 4430 / 4537 |
Alle Test-NSE > 0,97 — das ist ein vorzeigbares Ergebnis. Peak-Fehler typisch < 3 %.
Caveats¶
- $K_1 = 14$ h ist deutlich länger als die CCF-Wellen-Lag (~7 h). Muskingum-K ist nicht der reine Translation-Lag, sondern der Storage-Constant — er enthält Anteile aus Speicher- und Translations-Effekt.
- $Q_{\text{lat}} = -20$ m³/s ist physikalisch unwahrscheinlich (negativer lateraler Zufluss). Wahrscheinlich ein Mess-Bias der Q-Werte (Pegelonline-Q ist W→Q-umgerechnet via Abflusskurve, mit eigener Unsicherheit).
- Niedrigwasser nicht im Training — Modell nur auf HW-Events kalibriert.
Phase 3.2 — ARX-Baseline-Vorhersage für H_Mainz¶
Modell¶
$$H_{\text{Mainz}}(t) = a_1 H_{\text{Mainz}}(t-1) + a_2 H_{\text{Mainz}}(t-2) + b_1 H_{\text{Worms}}(t-7) + b_2 H_{\text{Nierstein}}(t-1) + b_3 H_{\text{Raunheim}}(t-1) + \varepsilon(t)$$
- AR(2)-Autoregression auf Mainz-H
- Exogene Lags entsprechen aus Phase 2 abgeleiteten Wellen-Laufzeiten
- Implementiert mit
statsmodels.tsa.statespace.sarimax.SARIMAX - 1h-Auflösung (1h-Aggregation der 15-min-Reihen für Geschwindigkeit)
- Train: 2020-01-01 .. 2024-01-01 (35056 h), Test: 2024-01-01 .. 2025-01-01 (8785 h)
Koeffizienten (gefittet)¶
| Parameter | Wert |
|---|---|
| AR1 | 0,2211 |
| AR2 | 0,3653 |
| WORMS_lag7 | 0,1451 |
| NIERSTEIN_lag1 | 1,2925 ← dominanter Prediktor |
| RAUNHEIM_lag1 | -0,2925 |
| σ² | 0,4096 |
Ergebnisse¶
One-Step-Ahead-Vorhersage auf Testperiode 2024: - NSE = 0,949 - RMSE = 16,5 cm (Mainz-H schwankt typisch 100–300 cm → Relativfehler ~5 %)
Bekannter Bug¶
Die Vorlauf-Zeit-Differenzierung (1h, 4h, 8h, 12h) ist im aktuellen Skript nicht korrekt implementiert — alle Vorlauf-Zeiten geben identisches Ergebnis. Echte Multi-Vorlauf-Zeit-Vorhersage (Direct-Method, je ein Modell pro Vorlauf-Zeit) ist ausstehend (Phase 3.5).
Vergleich der Modelle¶
| Modell | Größe | Skill (Test) | Vorlauf-Zeit |
|---|---|---|---|
| Muskingum v2 | Q_Mainz | NSE > 0,97 | implizit ~K-Stunden |
| ARX (SARIMAX) | H_Mainz | NSE = 0,95 | One-Step (1h) |
Beide sind komplementär: - Muskingum modelliert Q (Volumenstrom) und ist physikalisch interpretierbar. - ARX modelliert H (Wasserstand) und liefert eine direkt operationelle Vorhersage in cm.
Für eine echte Live-Vorhersage Mainz mit 4-12 h Vorlauf-Zeit müsste das ARX-Modell auf Direct-Method umgestellt werden.
Phase 6 — Wellen-Propagation Worms→Nierstein (40 Segmente)¶
Idee¶
Statt 40 Pegel an 40 Punkten installieren: ein einziger Worms-Pegel + Wellen-Celerität + Geometrie. Für Segment $s$ bei km-Distanz $x_s$ vom Worms-Pegel:
$$W_s(t) = W_{\text{Worms}}\left(t - \frac{x_s \cdot 1000}{c}\right) \quad\text{mit}\quad c = 2{,}5\ \text{m/s}$$
Die Welle ist dadurch sichtbar als zeitversetzter Worms-Wert entlang der 38 km Strecke. Bei Hochwasser-Anstieg sieht man eine farbige Welle wandern.
Geometrie¶
- 40 Segmente entlang OSM-Rhein-Polyline Worms (km 443,37) → Nierstein (km 480,61), Länge je ~0,95 km
- Aus 13 OSM-Way-Stücken zusammengesetzt (greedy endpoint-match)
- Polyline-Gesamtlänge: ~38 km, kalibriert gegen amtliche Pegel-km
- Skript:
geometry.py(lokal), Output:rhein_segments.geojson+rhein_segments_meta.csv
Wahl der Wellen-Celerität¶
$c = 2{,}5$ m/s aus zwei unabhängigen Quellen bestätigt: - Phase 2 Event-CCF Median-Wert über saubere HW-Events 2018-2024 - Lighthill-Whitham $c \approx \frac{5}{3} v$ mit mittlerer Strömungsgeschwindigkeit ~1,5 m/s
Konstantes c ist eine bewusste Vereinfachung — bei sehr hohem oder sehr niedrigem Wasserstand variiert c, aber der Fehler in der km-Position ist im Mittel < 5 % über die 4 h Laufzeit.
Pipeline (Live, alle 15 min)¶
Worms-W (rhein_raw)
↓
propagate.py (lag = km · 1000 / c)
↓
rhein_derived.rhein_segment (Influx)
+
/srv/web/maps/rhein_segments_live.geojson (per-Feature stroke-Farbe, SimpleStyle-Spec)
- Cron:
*/15 * * * *→ 200 Punkte pro Lauf (5 Zeitpunkte × 40 Segmente) - Initial-Backfill: 14 d → 53.760 Punkte
- Farbschema (Worms-Schwellwerte aus Schatzinsel-Kühkopf): <100 cm Treideln, 100-150 knapp, 150-200 befahrbar, 200-430 Mittelwasser, 430-500 Wege sperren, 500-640 HW, >640 Vollsperrung
Phase 6b — Worms-Vorhersage (Direct-Method-ARX, 4 Vorlauf-Zeiten)¶
Architektur¶
Statt eines rekursiven One-Step-Modells (das den Vorlauf-Zeit-Bug aus Phase 3.2 erzeugt) vier separate Ridge-Regression-Modelle, je eines pro Vorlauf-Zeit:
$$\hat W_{\text{Worms}}(t + h) = \sum_i \beta_i^{(h)} x_i(t) \quad\text{für}\quad h \in {1, 4, 8, 12}\ \text{h}$$
Features (26 pro Modell)¶
- 13 Speyer-W-Lags: $h, h+1, h+2, \ldots, h+12$ (Speyer ist 43 km flussaufwärts → Hauptprediktor)
- 5 Mannheim-W-Lags: $h, h+2, h+4, h+8, h+12$
- 4 Worms-AR-Lags: $h, h+1, h+3, h+6$
- 4 Saisonalitäts-Features: sin/cos(2π·t/24h), sin/cos(2π·t/Jahr)
Alle Features bei $t$ bekannt → Direct-Method (kein Vorhersage-Cascading-Fehler).
Training¶
- Train: 2018-01-01 .. 2024-01-01 (61.355 h)
- Test: 2024-01-01 .. 2025-01-01 (8.785 h)
- Ridge-Regression mit Cross-Validated alpha (sklearn
RidgeCV) - Standardisierung: Mean+Std aus Train
Ergebnisse Testperiode 2024-2025¶
| Lead | NSE | RMSE | Skill vs. Persistenz |
|---|---|---|---|
| +1 h | 1,0000 | 0,4 cm | 62 % |
| +4 h | 0,9999 | 1,4 cm | 67 % |
| +8 h | 0,9994 | 3,0 cm | 68 % |
| +12 h | 0,9978 | 4,5 cm | 65 % |
NSE-Werte > 0,997 sind nicht überraschend — Worms-W ist hochautokorreliert (e-fold > 7 d, $r(24h) \approx 0{,}97$). Aussagekräftiger ist der Skill gegen Persistenz: 62-68 % der Persistenz-Restfehler-Varianz wird durch das Modell erklärt.
Pipeline (Live, stündlich)¶
arx_worms.py (einmalig) → /app/data/worms_arx_models.pkl (5,4 KB, 4 Modelle)
↓
forecast_cron.py (cron 5 * * * *)
↓
rhein_forecast.worms_arx_forecast rhein_forecast.rhein_segment_forecast
(4 Lead-Punkte/Stunde) (40 Segmente × 12 h)
Vorhersage-Segment-Werte werden mit derselben km-zu-Zeit-Lag-Formel ($x \cdot 1000 / c$) auf die 40 Segmente propagiert → die ARX-Welle wandert sichtbar in die Zukunft.
Phase 6.5 — Leaflet-Iframe für Per-Segment-Färbung¶
Problem¶
Grafana 13 Geomap-Panel rendert GeoJSON-LineStrings ausschließlich mit einer panelweiten Style-Farbe; per-Feature stroke-Properties (SimpleStyle-Spec) werden ignoriert. Bei 40 Segmenten mit unterschiedlicher Wasserstand-Farbe ergibt sich nur eine einfarbige Linie.
Lösung¶
Externes Leaflet-Frontend welle.html (Leaflet 1.9.4 via CDN), per <iframe> ins Dashboard eingebettet. Same-Origin (beide hinter Caddy auf rhein-pegel.duckdns.org) → keine X-Frame-Options-Issues.
propagate.py schreibt zusätzlich zur Influx auch eine styled GeoJSON-Datei nach /srv/web/maps/rhein_segments_live.geojson mit per-Feature:
{ "stroke": "#8B0000", "stroke-width": 6, "stroke-opacity": 0.9, "w_cm": 86.0 }
Leaflet liest die Properties direkt und färbt jedes Segment individuell.
Grafana-Konfiguration¶
GF_PANELS_DISABLE_SANITIZE_HTML=true ist Pflicht — sonst entfernt Grafana das <iframe> aus dem Text-Panel-Content.
Live-URL¶
- Karte direkt: https://rhein-pegel.duckdns.org/maps/welle.html
- Eingebettet im RCN-Dashboard: Panel "Karte — Wellenausbreitung Worms→Nierstein"
- Auto-Refresh alle 60 s
Vergleich der Vorhersage-Modelle¶
| Modell | Größe | Vorlauf-Zeit | Skill (Test) |
|---|---|---|---|
| Muskingum v2 | Q_Mainz | implizit ~K-Stunden | NSE > 0,97 |
| ARX SARIMAX (Phase 3.2) | H_Mainz | One-Step (1h), buggy für >1h | NSE = 0,95 |
| Direct-Method-ARX (Phase 6b) | W_Worms | 1/4/8/12 h, korrekt | NSE 0,998-1,000, Skill 62-68 % |
Die in Phase 3.2 dokumentierte Vorlauf-Zeit-Bug-Limitierung ist mit Phase 6b für Worms gelöst. Eine analoge Direct-Method-Variante für Mainz ist offen und wäre ein sauberer Phase-3.5-Schritt.
Phase 6c — Erweiterung auf 11 Pegel + 24 h Vorhersage + HW-Warner¶
Motivation¶
Phase 6/6b war auf den Streckenabschnitt Worms→Nierstein begrenzt (40 Segmente, 12 h Vorhersage). Für eine echte HW-Vorwarnung am Bootshaus reicht das nicht — die Vorwarnzeit muss länger sein als die Reaktionszeit der Vereinsorganisation. Ziel: 24 h Vorhersage mit nutzbarem Skill, plus automatische Pushover-Benachrichtigung.
4 zusätzliche Oberlauf-Pegel¶
| Pegel | km | Lag → Worms (c=2,5 m/s) |
|---|---|---|
| Iffezheim | 336,20 | ~12 h |
| Plittersdorf | 340,20 | ~11 h |
| Maxau | 362,33 | ~9 h |
| Philippsburg | 389,33 | ~6 h |
Backfill via Pegelonline-Archiv-API (siehe Phase 0): ~292 k Punkte/Station × Parameter, total ~2 Mio. neue Punkte (2018-2026). Live-Polling alle 15 min.
Wichtige Beobachtung: Philippsburg-Pegelonline-Live hat ~12 h Latenz (Q wird nur einmal täglich gerechnet). Deshalb wurde Philippsburg aus den ARX-v2-Features ausgeschlossen, um den Vorhersage-Anker frisch zu halten.
Direct-Method-ARX v2¶
$$\hat W_{\text{Worms}}(t + h) = \sum_i \beta_i^{(h)} x_i(t) \quad\text{für}\quad h \in {1, 4, 8, 12, 16, 20, 24}\ \text{h}$$
102 Features pro Modell: - IFFEZHEIM lags 0..28 h (29) - PLITTERSDORF lags 0,2,4,...,24 h (13) - MAXAU lags 0..20 h (21) - SPEYER lags 0..14 h (15) - MANNHEIM lags 0..6 h (7) - WORMS-AR lags 0..3 h (4) - Saisonalität sin/cos (24 h, Jahr): 4 - 4 weitere ungenutzte Slots
Train 2018-2024 (61 355 h), Test 2024-2026 (12 000 h).
Ergebnisse v2 (Testperiode 2024-2026)¶
| Lead | NSE | RMSE | Skill vs. Persistenz |
|---|---|---|---|
| +1 h | 1,0000 | 0,42 cm | 60 % |
| +4 h | 0,9998 | 1,21 cm | 69 % |
| +8 h | 0,9994 | 2,40 cm | 68 % |
| +12 h | 0,9986 | 3,58 cm | 68 % |
| +16 h | 0,9975 | 4,82 cm | 67 % |
| +20 h | 0,9959 | 6,18 cm | 65 % |
| +24 h | 0,9936 | 7,77 cm | 63 % |
24 h-Vorhersage ist nutzbar: RMSE 7,77 cm, MAPE_HW 2,9 %. Im Hochwasser-Bereich (Worms > 400 cm) ist das eine Vorhersagepräzision von ±12 cm — genau genug, um eine Schwellwert-Überschreitung 24 h voraus anzukündigen.
Karte Phase 6c (Maxau → Mainz + Altrhein)¶
Statt zeitversetztem Worms-W (Phase 6) jetzt räumliche Interpolation zwischen den 7 amtlichen Pegeln. Für Hauptrhein-Segment km_x finde umgebenden Pegel km_u < km_x < km_d:
$$W_x(t) = (1 - \alpha) \cdot W_u(t) + \alpha \cdot W_d(t), \quad \alpha = \frac{km_x - km_u}{km_d - km_u}$$
Für Altrhein-Segment α (0 = Stockstadt-Mündung, 1 = Erfelden-Mündung):
$$W_{\text{alt}}(t, \alpha) = (1 - \alpha) \cdot W_{\text{Stockstadt}}(t) + \alpha \cdot W_{\text{Erfelden}}(t)$$
wobei $W_{\text{Stockstadt}}$ und $W_{\text{Erfelden}}$ selbst aus Pegel-Interpolation auf km 468,4 bzw. 473,9 gewonnen werden. Damit ist der Altrhein hydraulisch konsistent mit dem Hauptrhein modelliert (offen an beiden Enden, quasi-statische Wasserstand-Anpassung).
136 Hauptrhein-Segmente (~1 km/Segment, Maxau km 362 → Mainz km 498) + 18 Altrhein-Segmente = 154 Features in rhein_segments_v2_live.geojson.
⚠ PNP-Einschränkung¶
Die Farbskala der Karte (RC-Neptun-Worms-Schwellwerte 100/150/200/430/500/640 cm) gilt streng nur am Worms-Pegel. Die anderen Pegel haben unterschiedliche Pegelnullpunkte (PNP) — Maxau bei 405 cm ist Mittelwasser, nicht "Wege sperren wie bei Worms 405 cm". Die Färbung im Maxau-Bereich ist eine grobe Indikation.
Eine Phase 6d mit lokalen Skalen (MNW/MHW pro Pegel via Pegelonline-charvalues, siehe charvalues.py) ist ein offenes TODO.
Pushover-HW-Warner¶
hw_alert.py läuft alle 15 min via Cron. Liest aktuellen Worms-W + ARX-v2-Vorhersage (1..24 h). Trigger:
| Schwelle | Bedeutung | Pushover-Priority |
|---|---|---|
| 430 cm | Fasanenweg sperrt | 0 (normal) |
| 500 cm | Stockstadt-Zugang sperrt | 1 (high) |
| 640 cm | NSG komplett gesperrt | 2 (emergency) |
Hysterese: 6 h Cooldown pro Schwelle. State persistiert in /app/data/hw_alert_state.json.
Vorwarnzeit: Da der Trigger schon beim ersten ARX-Vorhersage > Schwelle feuert, ergibt sich eine Vorwarnzeit von 1-24 h, je nach wie weit die Welle in Maxau bereits sichtbar ist.
Pipeline-Übersicht (Phase 6c, 2026-05-05)¶
Pegelonline (11 Stationen)
↓ poll alle 15 min
rhein_raw (Influx)
↓
├─ propagate_v2.py (cron */15) → rhein_segment_v2 + rhein_segments_v2_live.geojson
├─ forecast_cron_v2.py (cron 5 *) → worms_arx_v2_forecast (rhein_forecast)
└─ hw_alert.py (cron */15) → Pushover (wenn Schwelle aktiv/Forecast)
Phase 6d — Lokale MNW/MHW-Skala für die Karten-Färbung¶
Problem mit Phase 6c¶
Die Phase-6c-Karte hat Worms-Bootshaus-Schwellwerte (100/150/200/430/500/640 cm) auf alle Hauptrhein-Segmente angewendet. Da die Pegel unterschiedliche Pegelnullpunkte (PNP) haben, war die Färbung farblich misleadingend:
| Pegel | MNW [cm] | MW [cm] | MHW [cm] |
|---|---|---|---|
| Iffezheim | 104 | 240 | 518 |
| Maxau | 353 | 496 | 785 |
| Speyer | 214 | 361 | 699 |
| Mannheim | 132 | 293 | 644 |
| Worms | 46 | 195 | 529 |
| Nierstein | 112 | 259 | 554 |
| Mainz | 159 | 288 | 547 |
Z.B. 405 cm @ Maxau ist Mittelwasser, nicht "fast Hochwasser" wie bei Worms 405 cm. Mit Worms-Schwellen wäre Maxau-Bereich grün (200-430 = "Mittelwasser") gefärbt — irreführend, weil "Mittelwasser bei Worms" und "Mittelwasser bei Maxau" hydraulisch verschiedene Lagen sind.
Lösung: Ratio-basierte Färbung pro Segment¶
Pro Segment km_x werden MNW und MHW räumlich-linear zwischen den umgebenden Pegeln interpoliert. Dann:
$$\text{ratio}(km_x, t) = \frac{W(km_x, t) - \text{MNW}\text{lokal}(km_x)}{\text{MHW}\text{lokal}(km_x) - \text{MNW}_\text{lokal}(km_x)}$$
Diskrete Farbskala (8 Stufen) auf Basis von ratio:
| Ratio | Farbe | Bedeutung |
|---|---|---|
| < −0,2 | dunkelblau | extrem niedrig (NW unter MNW) |
| −0,2..0,2 | blau | knapp niedrig |
| 0,2..0,5 | hellblau | Niedrigwasser |
| 0,5..0,8 | grün | Mittelwasser |
| 0,8..1,0 | gelb | Annäherung MHW |
| 1,0..1,3 | orange | leichtes Hochwasser |
| 1,3..1,8 | rot | Hochwasser |
| > 1,8 | dunkelrot | extreme HW |
Damit sind 405 cm @ Maxau und 87 cm @ Worms beide korrekt als blau (Niedrigwasser) eingefärbt — visuell konsistent.
Altrhein bleibt bei Worms-Bootshaus-Skala¶
Für die Altrhein-Segmente ist die RC-Neptun-Befahrbarkeit relevant, nicht die hydrologische Lage. Daher bleibt dort die Worms-Schwellen-Skala (100/150/200/430/500/640) — durchgehend in 8 unterscheidbaren Farben:
| Worms-W | Stufe | Farbe |
|---|---|---|
| < 100 cm | Treideln | lila |
| 100-150 cm | knapp | hell-lila |
| 150-200 cm | befahrbar | cyan |
| 200-430 cm | Mittelwasser ideal | grün |
| 430-500 cm | Wege sperren | gelb |
| 500-550 cm | HW unterer | orange |
| 550-640 cm | HW oberer | rot |
| > 640 cm | komplette Sperrung | dunkelrot |
(Die alte Skala hatte sowohl Treideln als auch Sperrung in dunkelrot — nicht unterscheidbar.)
Bias-Korrektur in der Pushover-Message¶
Aus dem Rückblickende Validierung HW Juni 2024 wurde der Modell-Bias pro Vorlauf-Zeit bestimmt. Der Bias ist negativ (Modell unterschätzt im HW-Bereich systematisch):
| Lead | Bias [cm] | RMSE [cm] |
|---|---|---|
| +1 h | −0,1 | 0,6 |
| +4 h | −0,2 | 2,2 |
| +8 h | −1,2 | 5,8 |
| +12 h | −2,8 | 10,4 |
| +16 h | −4,8 | 15,8 |
| +20 h | −7,1 | 21,5 |
| +24 h | −9,7 | 27,3 |
Die Pushover-Message zeigt jetzt den bias-korrigierten Wert ± RMSE als 1-σ-Konfidenzbereich, plus den Roh-Pred-Wert zur Transparenz.
Phase 6f — Muskingum-Sub-Routing für die Karte¶
Motivation¶
Phase 6c interpolierte räumlich-linear zwischen den Pegeln zur gleichen Zeit. Das ignoriert die Wellenausbreitung — eine HW-Welle erscheint als räumlich gemittelter Gradient, nicht als wandernde physikalische Welle.
Lösung: Muskingum-Routing pro Pegel-Reach¶
Klassisches Muskingum (linear, dt = 1 h):
$$Q_\text{out}(t) = C_0 \cdot Q_\text{in}(t) + C_1 \cdot Q_\text{in}(t-\Delta t) + C_2 \cdot Q_\text{out}(t-\Delta t)$$
mit den Routing-Koeffizienten
$$C_0 = \frac{-Kx + 0{,}5\Delta t}{K - Kx + 0{,}5\Delta t}, \quad C_1 = \frac{Kx + 0{,}5\Delta t}{K - Kx + 0{,}5\Delta t}, \quad C_2 = \frac{K - Kx - 0{,}5\Delta t}{K - Kx + 0{,}5\Delta t}$$
K = Storage-Konstante [h], x = Weighting [0..0,5]. Stabilitätsbedingung: 2Kx ≤ Δt ≤ 2K(1-x).
MW-Anomalie-Trick (PNP-Korrektur)¶
Direktes Routing der Wasserstand-Werte schlug fehl (NSE −2,4 für Iffezheim→Plittersdorf), weil die Pegelnullpunkte unterschiedlich sind. Lösung: Pegel-Anomalie routen:
$$W'_u(t) = W_u(t) - \text{MW}_u, \quad W'_d^\text{pred} = \text{Muskingum}(W'_u, K, x)$$
$$W_d^\text{pred}(t) = W'_d^\text{pred}(t) + \text{MW}_d$$
Damit wird der Mittel-Versatz aus dem Routing-Modell herausgenommen und nur noch die Wellen-Dynamik trainiert.
Kalibrierung 8 Reaches (Train 2018-2024)¶
Für jeden Reach (Pegel-Paar entlang Rhein) wurden K und x via Differential Evolution auf SSE der MW-Anomalie kalibriert.
| Reach | Δkm | K [h] | x | NSE_W | RMSE [cm] | c_implied [m/s] |
|---|---|---|---|---|---|---|
| Iffezheim → Plittersdorf | 4,0 | 1,27 | 0,000 | 0,994 | 6,7 | 0,88 |
| Plittersdorf → Maxau | 22,1 | 6,68 | 0,075 | 0,992 | 8,5 | 0,92 |
| Maxau → Philippsburg | 27,0 | 9,40 | 0,053 | 0,990 | 10,2 | 0,80 |
| Philippsburg → Speyer | 11,3 | 3,45 | 0,144 | 0,996 | 7,1 | 0,91 |
| Speyer → Mannheim | 24,1 | 2,90 | 0,000 | 0,983 | 14,7 | 2,31 |
| Mannheim → Worms | 18,6 | 3,04 | 0,000 | 0,994 | 8,0 | 1,70 |
| Worms → Nierstein | 37,2 | 8,65 | 0,000 | 0,992 | 9,0 | 1,20 |
| Nierstein → Mainz | 17,7 | 1,57 | 0,000 | 0,965 | 16,3 | 3,13 |
Beobachtung: Implizite Wellengeschwindigkeit (c = Δx/K) variiert stark — 0,8 m/s im Süden (gestaut durch Iffezheim-Wehr), 2-3 m/s im Mittel- und Untermain.
Sub-Routing pro Karten-Segment¶
Pro Hauptrhein-Segment km_x in Reach (km_u → km_d): - α = (km_x − km_u) / (km_d − km_u) - K_seg = max(0,5, α · K_full) - W_seg(t) = Muskingum(W_u-Reihe, K_seg, x) + (MW_u + α · (MW_d − MW_u))
Damit wandert eine HW-Welle physikalisch korrekt mit reach-spezifischer Geschwindigkeit durch die Karte. Die räumlich-zeitliche Verteilung ist konsistent mit den 8 trainierten Reaches.
Validierung Phase 6b/c — Rückblickende Validierung HW Juni 2024¶
Der Testperiode (Test-Set 2024-2026) enthält das schwere Sommer-Hochwasser vom 02.-15. Juni 2024 mit Peak 695 cm am 04.06. Damit lässt sich das ARX-v2-Modell retrospektiv validieren — hätte der HW-Warner damals rechtzeitig getriggered?
Verlaufs-Plot¶

Schwarze Linie = beobachteter Worms-Pegel. Bunte Linien (Viridis von dunkelviolett +1h zu gelb +24h) = Vorhersage-Werte zum jeweiligen Target-Zeitpunkt. Gestrichelte Linien = Schwellwerte 430/500/640 cm.
Beobachtung: Lead-1h und Lead-4h kleben praktisch auf der Beobachtung. Lead-24h hinkt sichtbar zurück und unterschätzt den Peak (665 cm prognostiziert vs. 695 cm beobachtet).
Skill pro Vorlauf-Zeit¶

RMSE wächst von 0,6 cm @ +1h auf 27,3 cm @ +24h. Modell-Bias (Pred − Obs) ist durchgehend negativ — der Mittel-Pull der Ridge-Regression dämpft die Spitze.
Schwellwert-Crossing — HW-Warner-Simulation¶
Wenn der HW-Warner damals gelaufen wäre (alle 7 Vorlauf-Zeiten parallel checkt):
| Schwelle | Obs erstmals erreicht | Modell triggert (Vorab-Auslösung) | Vorwarnzeit |
|---|---|---|---|
| 430 cm (Fasanenweg) | 01.06. 15:00 | 01.06. 06:00 (Lead +24h, pred 434) | +9 h |
| 500 cm (Stockstadt) | 02.06. 02:00 | 01.06. 14:00 (Lead +24h, pred 509) | +12 h |
| 640 cm (NSG) | 03.06. 05:00 | 02.06. 12:00 (Lead +24h, pred 643) | +17 h |
Alle drei Schwellen wären rechtzeitig getriggered worden — null False Alarms über alle 7 × 3 = 21 Lead-Trigger-Kombinationen. Der HW-Warner hätte am 01.06. um 06:00 Uhr UTC die erste Pushover-Notification (Vorwarnung Fasanenweg) gesendet, mit 9 Stunden Vorlauf.
Statistik im HW-Bereich (Worms-Obs > 400 cm, n = 278-301)¶
| Lead | NSE | RMSE | MAE | Bias |
|---|---|---|---|---|
| +1 h | 1,0000 | 0,59 cm | 0,46 cm | +0,1 |
| +4 h | 0,9994 | 2,18 cm | 1,56 cm | −0,2 |
| +8 h | 0,9954 | 5,81 cm | 3,83 cm | −1,2 |
| +12 h | 0,9852 | 10,4 cm | 6,57 cm | −2,8 |
| +16 h | 0,9660 | 15,8 cm | 9,68 cm | −4,8 |
| +20 h | 0,9373 | 21,5 cm | 12,8 cm | −7,1 |
| +24 h | 0,8986 | 27,3 cm | 16,2 cm | −9,7 |
NSE bleibt > 0,9 bis Lead +20h. Bei +24h fällt sie auf 0,90 — das Modell ist dann nicht mehr signifikant besser als Persistenz für Niedrigwasser-Bereich, aber im HW-Bereich noch nutzbar (Skill ≈ 63 % gegen Persistenz).
Phase 6g — Multi-Event-Rückblickende Validierung (alle 11 HW-Events 2018-2026)¶
Methodik¶
Aus den Phase-2-Event-CCF-Daten (rhein_derived.ccf_event) wurden 11 saubere Hochwasser-Events identifiziert:
| Event-ID | Datum (Peak) | Peak-W [cm] |
|---|---|---|
| 20180107 | 07.01.2018 | 622 |
| 20180125 | 25.01.2018 | 637 |
| 20190317 | 17.03.2019 | 451 |
| 20190523 | 23.05.2019 | 467 |
| 20200205 | 05.02.2020 | 524 |
| 20210204 | 04.02.2021 | 613 |
| 20210718 | 18.07.2021 | 604 |
| 20231119 | 19.11.2023 | 543 |
| 20231215 | 15.12.2023 | 610 |
| 20240604 | 04.06.2024 | 695 |
| 20250110 | 10.01.2025 | 457 |
Pro Event: ARX-v2 wurde stündlich für 17 Tage simuliert (−7..+10 Tage um Peak), Vorhersage für alle 7 Vorlauf-Zeiten bei jeder Stunde berechnet, Ergebnisse gegen Beobachtung verglichen.
Ergebnis: HW-Warner-Trefferquote¶

| Schwelle | Events erreicht | Vorab-Auslösung | Post-Fire | Miss | Hit-Rate | Mean-Vorwarnzeit |
|---|---|---|---|---|---|---|
| 430 cm (Fasanenweg) | 11 | 10 | 1 | 0 | 91 % | +15.8 h |
| 500 cm (Stockstadt) | 8 | 8 | 0 | 0 | 100 % | +20.5 h |
| 640 cm (NSG) | 1 | 1 | 0 | 0 | 100 % | +17.0 h |
19 von 20 Schwellen-Triggern Vorab-Auslösung (95 % gesamt). Null Misses. Der 1 Post-Fire-Fall wurde noch innerhalb 1 h des Beobachtungs-Eintretens getriggered — also nur knapp zu spät, nie ganz verpasst.
Skill pro Vorlauf-Zeit (gemittelt über 11 Events)¶

| Lead | NSE_HW | RMSE_HW | Bias_HW |
|---|---|---|---|
| +1 h | 0.9997 | 0.5 cm | +0.0 |
| +4 h | 0.9958 | 1.8 cm | +0.1 |
| +8 h | 0.9799 | 4.0 cm | +0.0 |
| +12 h | 0.9405 | 6.6 cm | −0.2 |
| +16 h | 0.8582 | 9.7 cm | −0.7 |
| +20 h | 0.7633 | 13.0 cm | −1.4 |
| +24 h | 0.6137 | 16.8 cm | −2.4 |
Wichtige Beobachtung: Der Mean-Bias bei +24 h ist nur −2.4 cm gemittelt über 11 Events — viel kleiner als die −9.7 cm des einzelnen 2024-Events (das ein Extremereignis war). Das war Anlass zur Phase-6h-Anpassung der HW-Alarm-Konfidenzbänder.
Phase 6h-Conformal — Empirische Quantil-Konfidenzbänder¶
Problem mit Phase 6d Bias-Korrektur¶
Phase 6d nutzte Bias und RMSE aus dem einzelnen 2024-HW-Event und annahm normalverteilte Residuen. Das war: - Zu konservativ in der Korrektur (−9.7 cm Bias bei Lead 24h, gemessen nur an einem Event) - Annahme einer Normalverteilung der Vorhersagefehler — was bei HW-Events nicht stimmt (rechts-skewed: das Modell unterschätzt extreme Peaks öfter, aber selten überschätzt)
Conformal-Prediction-Ansatz¶
Statt Annahmen über die Verteilung: direkte empirische Quantile der Train-Residuen auf dem HW-Subset (obs > 400 cm, n = 2 438 Stunden 2018-2024).
Pro Vorlauf-Zeit (h) sind die Quantile (in cm, residual = obs − pred):
| Lead | q05 | q50 | q95 | Asymmetrie |
|---|---|---|---|---|
| +1 h | −0.78 | −0.04 | +0.67 | symmetrisch |
| +4 h | −2.68 | −0.18 | +2.49 | leicht asymmetrisch |
| +8 h | −5.16 | −0.36 | +5.41 | symmetrisch |
| +12 h | −7.94 | −0.53 | +9.60 | rechts-skewed |
| +16 h | −10.15 | −0.77 | +14.00 | stark rechts-skewed |
| +20 h | −11.91 | −0.98 | +21.06 | sehr asymmetrisch |
| +24 h | −13.54 | −1.15 | +29.86 | extrem rechts-skewed |
Bei Lead +24 h ist q95 = +30 cm (also der Peak könnte 30 cm höher sein als das mittlere Modell vorhersagt) während q05 = −13 cm. Diese Asymmetrie spiegelt wider, dass Extremereignisse das Modell nach oben überraschen, selten nach unten.
Pushover-Message: 90%-PI statt RMSE-Annahme¶
Die Pushover-Notification zeigt jetzt:
Worms-Forecast +24h: 105 cm (90%-Bereich 91–135 cm).
Schwelle 430 cm im Worst-Case-Fall erreicht voraussichtlich 07.05. 11:00 MESZ.
Trigger-Logik nutzt q95-Grenze (oberer 90%-PI) — konservativer Vorab-Auslösung bei steigendem Trend, aber kein False Alarm bei stabilem Niedrigwasser.
Phase 6e — Time-Slider-Animation¶
Welle-Animation auf der Karte (welle.html, Slider unten rechts): - Bereich −12 h (historisch) bis +24 h (ARX-Vorhersage) - Toggle LIVE / ANIM - Auto-Play (350 ms/Tick) - Worms-Wert + JETZT-Marker + Schwellwert-Status pro Tick
Backend animation_state.py erzeugt alle 15 min einen JSON-Snapshot (37 Stundenscheiben × 154 Segmente, ~250 KB) unter /maps/welle_animation_state.json. Bei jedem Stunden-Tick wird über alle Hauptrhein-Pegel das Muskingum-Sub-Routing rückwärts in der Vergangenheit + vorwärts mittels ARX-v2-Vorhersage (Worms) und Persistenz-Annahme (alle anderen Pegel) gerechnet.
Phase-7-Roadmap (offene wissenschaftliche Verbesserungen)¶
Phase 7.1 — DWD-Niederschlag-Vorhersage als ARX-Feature¶
Idee: Niederschlagsmengen über das Schwarzwald-Einzugsgebiet als zusätzliche Features ins ARX, um Vorlauf-Zeit auf 48-72 h zu erweitern.
Datenquelle: Brightsky API (DWD-Wrapper, JSON-API). Parameter precipitation und precipitation_probability_6h für Stationen Karlsruhe, Freudenstadt, Triberg.
Aufwand: ~1 Tag (Backfill 2018-2024 für 6 Wetter-Stationen, ARX-Re-Training, Skill-Vergleich).
Erwarteter Skill-Gewinn: Lead 48 h NSE > 0.8 (statt 0.6 bei 24 h), Lead 72 h NSE > 0.7. Kritisch für Vorwarnung über mehrere Tage.
Phase 7.2 — Saint-Venant-Routing (statt Muskingum)¶
Idee: 1D-Hydrodynamik-Modell mit Querschnitten + Sohlgefälle für physikalisch genaue Wellenausbreitung.
Gleichungen (vereinfacht, kinematische Welle):
$$\frac{\partial A}{\partial t} + \frac{\partial Q}{\partial x} = 0 \quad\text{(Massenerhaltung)}$$
$$S_f = S_0 \quad\text{(Impuls, kinematisch)}$$
mit $A$ = Querschnittsfläche, $Q$ = Abfluss, $S_f$ = Reibungs-Slope, $S_0$ = Sohlgefälle.
Datenbedarf: Querschnittsdaten der Wasserstraßen- und Schifffahrtsverwaltung (WSV) — aufwändig zu beschaffen, evtl. nur als Anfrage.
Aufwand: mehrere Tage, Forschungsniveau. Diplomarbeits-tauglich.
Vorteil: echte Physik (Kontinuitäts- + Impulsgleichung) statt linearem Routing-Modell. Bei Extrem-HW genauere Spitzen-Vorhersage.
Phase 7.3 — BfG-WaVo-Vergleichsbenchmark¶
Status: keine öffentliche API gefunden. Manuelles Web-Scraping der BfG-Wasserstandsvorhersagezentrale wäre möglich aber instabil.
Alternative: EFAS (European Flood Awareness System) hat akademische API — Interface zur Validierung wäre ein eigenes Forschungsprojekt.
Aufwand: ~2 Tage Recherche + Scraping.
Phase 7.4 — Echte Quantile-Regression statt Conformal¶
Conformal-Prediction (Phase 6h) nutzt globale Train-Residuen-Quantile. Echte heteroskedastische Quantile-Regression (ein Modell pro Quantil, sklearn QuantileRegressor) gäbe state-abhängige PIs — bei steigender Welle ggf. anders breit als bei stabiler Lage. Initial-Test mit Subsampling war zu langsam (~7 Min/Lead), Optimierung nötig.
Aufwand: ~1 Tag mit GBM (Gradient Boosting Quantile-Loss, schneller als LP-basiert).
Phase 7.1 — DWD-Niederschlag-Feature (ARX-v4)¶
Datenquelle: Brightsky-API (DWD-Wrapper)¶
3 Wetter-Stationen im Schwarzwald-/Rhein-Einzugsgebiet:
| Station | Lat / Lon | Höhe / Charakter |
|---|---|---|
| Karlsruhe | 49.04 / 8.40 | zentrales Oberrhein |
| Freudenstadt | 48.46 / 8.41 | Schwarzwald-Höhe (842 m) |
| Triberg | 48.13 / 8.23 | südlicher Schwarzwald |
Backfill 2018-01-01 .. 2026-05-06 mittels brightsky_backfill.py: 425 k Niederschlag-Punkte geschrieben in rhein_raw.weather. Live-Poller brightsky_poller.py (Cron alle 60 min) holt zusätzlich DWD-ICON-Vorhersagen (0-72 h) nach rhein_forecast.weather_forecast.
Feature-Engineering¶
Pro Station × 3 Cumul-Fenster = 9 zusätzliche Features:
$$\text{PRECIP_KARLSRUHE_24h} = \sum_{i=t-24}^{t} P_\text{KARLSRUHE}(i)$$
(analog für Freudenstadt, Triberg und Fenster 24/48/72 h)
Damit wächst der Feature-Vektor von 93 auf 102 Spalten. Die Niederschlag-Features kodieren die "geladene" Hydrologie des Einzugsgebiets — relevant für Vorlauf-Zeiten > 24 h, weil dort die direkte Pegel-Wellenausbreitung (max ~12 h von Iffezheim nach Worms) nicht mehr ausreicht.
Skill-Vergleich v3 (ohne Niederschlag) vs. v4 (mit Niederschlag)¶
| Lead | v3 RMSE | v4 RMSE | Verbesserung | v4 NSE | v4 Skill vs. Persistenz |
|---|---|---|---|---|---|
| +1 h | 0.42 cm | 0.42 cm | — | 1.0000 | 60 % |
| +4 h | 1.21 | 1.18 | −2 % | 0.9999 | 70 % |
| +8 h | 2.40 | 2.31 | −4 % | 0.9994 | 70 % |
| +12 h | 3.58 | 3.40 | −5 % | 0.9988 | 69 % |
| +16 h | 4.82 | 4.54 | −6 % | 0.9978 | 69 % |
| +20 h | 6.18 | 5.79 | −6 % | 0.9964 | 68 % |
| +24 h | 7.77 | 7.25 | −7 % | 0.9944 | 65 % |
| +36 h | — | 13.45 | NEU | 0.9807 | 55 % |
| +48 h | — | 21.27 | NEU | 0.9516 | 42 % |
| +72 h | — | 36.28 | NEU | 0.8592 | 25 % |
Zwei Wirkungen: 1. Marginale Verbesserung bei kurzen Leads (~5-7 % RMSE-Reduktion bei +24 h). Bei strukturellen HW-Events sind die Pegel-Lags die dominante Information. 2. Neue Vorlauf-Zeiten +36/+48/+72 h wurden freigeschaltet. NSE > 0.95 bis +48 h ist nutzbar; +72 h NSE 0.86 mit Skill 25 % gegen Persistenz reicht für strategische HW-Vorbereitung (Vereinsmaterial sichern, Wege-Sperrungen kommunizieren).
Operationelle Pipeline¶
Brightsky-API (DWD-Wrapper)
├─ brightsky_poller.py (cron 30 *) → rhein_raw.weather (Obs)
│ + rhein_forecast.weather_forecast (DWD-ICON Forecasts)
│
ARX-v4 (Pegel-Lags + Niederschlag-Cumuls)
└─ forecast_cron_v4.py (cron 10 *) → rhein_forecast.worms_arx_v4_forecast
(Lead 1/4/8/12/16/20/24/36/48/72 h)
Phase 7.1b — DWD-ICON-Niederschlag-Vorhersage als Feature (offen)¶
Aktuell nutzt v4 nur historische Niederschlag-Beobachtungen (Cumul-Fenster). Die nächste Verbesserung wäre, die DWD-ICON-Niederschlag-Vorhersagen (0-72 h) als zusätzliche Features einzubringen. Das ist konzeptionell anspruchsvoller, da das Modell trainiert werden müsste mit "wie sah der ICON-Vorhersage vor X Stunden aus" — historische ICON-Archive sind verfügbar (DWD CDC), aber Aufwand erheblich.
Erwarteter zusätzlicher Skill-Gewinn: bei Lead +48..72 h könnte NSE von 0.86 auf > 0.92 steigen.
Phase 7.1b — DWD-ICON-Niederschlag-Vorhersage als ARX-Feature (v5)¶
Idee¶
Phase 7.1 (v4) nutzte nur historische Niederschlag-Cumul-Werte. Phase 7.1b (v5) erweitert um zukunfts-gerichtete Cumul-Fenster (0..+24 h, 0..+48 h, 0..+72 h pro Wetter-Station = 9 zusätzliche Future-Features).
Methodische Herausforderung: Training mit Perfect-Foresight¶
Im Training-Set 2018-2024 stehen keine historischen DWD-ICON-Vorhersage-Archive zur Verfügung. Brightsky speichert nur die aktuellen Vorhersagen. Daher Training mit Perfect-Foresight:
$$\text{PRECIP_KARLSRUHE_FUT_24h}(t) = \sum_{i=t+1}^{t+24} P_\text{KARLSRUHE}^\text{obs}(i)$$
Das ist physikalisch nicht korrekt für ein Live-Modell — die echte Niederschlagsmenge der nächsten 24 h ist erst 24 h später bekannt. Im Live-System (forecast_cron_v5.py) werden diese Features stattdessen aus dem DWD-ICON-Vorhersage (gespeichert in rhein_forecast.weather_forecast durch Brightsky-Poller) gefüllt.
Konsequenz: der Test-Skill auf Testperiode 2024-2026 ist eine OBERE SCHRANKE. Im Live-Betrieb wird der Skill etwas niedriger sein, da ICON-D2-Niederschlag-Vorhersagen einen RMSE von typisch 1-2 mm/24 h haben. Eine echte Phase 7.1c würde mit DWD-CDC-ICON-Archiven trainieren — aufwändig (GRIB2-Files, separate Pipeline).
Skill-Vergleich v4 vs. v5 (Testperiode 2024-2026)¶
| Lead | v4 RMSE | v5 RMSE | RMSE-Reduktion | v4 Skill | v5 Skill | Skill-Sprung |
|---|---|---|---|---|---|---|
| +1 h | 0.42 | 0.42 | — | 60 % | 60 % | — |
| +4 h | 1.18 | 1.18 | — | 70 % | 70 % | — |
| +8 h | 2.31 | 2.30 | −0 % | 70 % | 70 % | — |
| +12 h | 3.40 | 3.35 | −1 % | 69 % | 70 % | + 1 |
| +16 h | 4.54 | 4.40 | −3 % | 69 % | 70 % | + 1 |
| +20 h | 5.79 | 5.46 | −6 % | 68 % | 69 % | + 1 |
| +24 h | 7.25 | 6.60 | −9 % | 65 % | 68 % | + 3 |
| +36 h | 13.45 | 11.10 | −17 % | 55 % | 63 % | + 8 |
| +48 h | 21.27 | 16.15 | −24 % | 42 % | 56 % | + 14 |
| +72 h | 36.28 | 24.26 | −33 % | 25 % | 50 % | + 25 |
Riesen-Sprung bei langen Leads: +72 h Skill verdoppelt (25 → 50 %), RMSE um ein Drittel reduziert. Das macht 3-Tage-Vorhersagen erstmals operationell nutzbar.
Live-Vorhersage (jetzt)¶
Stand 2026-05-06 16:30 UTC mit ICON-Niederschlag-Vorhersage:
KARLSRUHE Future-Precip 24h=11.6 mm
FREUDENSTADT Future-Precip 24h=7.1 mm
TRIBERG Future-Precip 24h=7.0 mm
| Lead | v4 (ohne Vorhersage) | v5 (mit ICON) | Differenz |
|---|---|---|---|
| +24 h | 107.7 cm | 112.8 cm | +5.1 cm |
| +48 h | 114.0 cm | 126.8 cm | +12.8 cm |
| +72 h | 117.1 cm | 127.6 cm | +10.5 cm |
V5 prognostiziert höhere Pegel weil das Modell die kommende Niederschlag-Aktivität "antizipiert".
Pipeline¶
brightsky_poller.py (cron 30 *)
├─ rhein_raw.weather (DWD-Beobachtung)
└─ rhein_forecast.weather_forecast (DWD-ICON-Forecast 0-72h)
arx_worms_v5.py (einmalig, gepickelt)
Train 2018-2024 mit Perfect-Foresight-Niederschlag
forecast_cron_v5.py (cron 20 *)
Past-Niederschlag aus rhein_raw.weather (Cumul -24/-48/-72 h)
Future-Niederschlag aus rhein_forecast.weather_forecast (Cumul +24/+48/+72 h)
→ rhein_forecast.worms_arx_v5_forecast (Lead 1..72 h)
Phase 7.1c — TIGGE-Reforecast als echte Vorhersage-Features (v6)¶
Idee¶
Phase 7.1b (v5) trainierte mit Perfect-Foresight: die echten zukünftigen Niederschlag-Beobachtungen ersetzten den (zu der Zeit nicht archivierten) ICON-Vorhersage. Das ist physikalisch nicht korrekt — der resultierende Test-Skill ist eine obere Schranke, weil das Modell während des Trainings „weiß", was später wirklich passiert.
Phase 7.1c schließt diese methodische Lücke: Training mit echten historischen Vorhersage-Archiven, downloaded aus dem TIGGE-Datensatz auf dem ECMWF Data Store (ECDS).
Datenquelle: TIGGE auf ECDS¶
- Endpoint:
https://ecds.ecmwf.int/api, gleicher CDS-Token (SSO) - Dataset:
tigge-forecasts - Modell: ECMWF-IFS,
origin=ecmwf,type=control_forecast(deterministisch) - Variable:
total_precipitationin kg m⁻² (= mm) - Init: 2× pro Tag (00z, 12z)
- Lead-Steps: 0/6/12/…/72 h
- Räumliche Auflösung: Reduced Gaussian Grid, ~25 km
- Stationen: Karlsruhe (49,04 / 8,40), Freudenstadt (48,46 / 8,41), Triberg (48,13 / 8,23) — gleiche Stationen wie in v5
Backfill-Pipeline¶
tigge_pilot.py / tigge_backfill.py (einmaliger Lauf)
├─ cdsapi-Request pro Monat 2018-01..2024-12
├─ GRIB2 → cfgrib → xarray
├─ Spatial-Extraktion: nächster Gitterpunkt pro Station
└─ rhein.weather_forecast_archive (measurement tigge_forecast,
tags: station, origin, lead_h,
time = init_time)
84 Monate × 60 Inits × 13 Lead-Steps × 3 Stationen = ~196 000 Datenpunkte, ~30 MB. Backfill 2026-05-06 21:00–23:16 UTC, alle 84 Monate erfolgreich.
Lookup-Logik im Training¶
Pro Trainings-Stunde t findet tigge_to_fut_features(...):
- den jüngsten Init-Run mit
init_time ≤ t(max 12 h alt → entspricht der Live-Latenz von ICON) - Δh = t − init_time, dann lineare Interpolation der Cumul-Werte zwischen den verfügbaren Lead-Stützstellen
- Future-Feature
PRECIP_<STATION>_FUT_<W>h := tp(min(72, Δ+W)) − tp(Δ)
Die Feature-Namen sind identisch zu v5 → direkter Skill-Vergleich.
Methodische Caveats (verbleibend)¶
| Caveat | Stand v5 (Phase 7.1b) | Stand v6 (Phase 7.1c) |
|---|---|---|
| Perfect-Foresight im Training | ✗ vorhanden | ✓ aufgehoben |
| Modell-Version konstant über Trainingsperiode | n/a (Beobachtungen) | ⚠ TIGGE-IFS-Version drifted seit 2018 (operationelle ECMWF-Releases) |
| Train-Modell ↔ Live-Modell identisch | ✓ (beide Brightsky/ICON) | ⚠ Train: TIGGE-ECMWF-IFS / Live: Brightsky-DWD-ICON |
Die verbleibenden Caveats sind milder: TIGGE-IFS-Drift wirkt nur über mehrere Jahre, ist aber methodisch deutlich näher am realen Live-Modell als die Perfect-Foresight-Substitution. Saubere konstante Reforecast-Modelle (EFAS-Reforecast auf EWDS) sind für Phase 7.1d vorgesehen, aktuell aber gefroren (Stand 2024-11-11).
Skill-Vergleich v5 (Perfect-Foresight) vs. v6 (echte Vorhersagen) — Testperiode 2024-2026¶
| Lead | v5 RMSE | v6 RMSE | Differenz | v5 Skill | v6 Skill | Skill-Verlust |
|---|---|---|---|---|---|---|
| +1 h | 0,42 | 0,40 | −0,02 | 60 % | 63 % | +3 (sic!) |
| +4 h | 1,18 | 1,25 | +0,07 | 70 % | 69 % | −1 |
| +8 h | 2,30 | 2,51 | +0,21 | 70 % | 69 % | −1 |
| +12 h | 3,35 | 3,74 | +0,39 | 70 % | 68 % | −2 |
| +16 h | 4,40 | 4,99 | +0,59 | 70 % | 68 % | −2 |
| +20 h | 5,46 | 6,28 | +0,82 | 69 % | 67 % | −2 |
| +24 h | 6,60 | 7,71 | +1,11 | 68 % | 65 % | −3 |
| +36 h | 11,10 | 12,93 | +1,83 | 63 % | 59 % | −4 |
| +48 h | 16,15 | 18,59 | +2,44 | 56 % | 52 % | −4 |
| +72 h | 24,26 | 27,85 | +3,59 | 50 % | 45 % | −5 |
Bewertung der Ergebnisse:
- Bei kurzen Leads (1-12 h) sind v5 und v6 nahezu identisch. Das ARX-Modell wird hier dominant durch die Pegel-Lags (Speyer, Mannheim, Worms-Persistenz) getrieben — die Niederschlag-Vorhersagen spielen kaum eine Rolle.
- Bei langen Leads (24-72 h) ist v6 systematisch ~3-5 Skill-Punkte schlechter als v5. Genau das war erwartet: das quantifiziert den ICON/IFS-Vorhersage-Fehler-Effekt, der in v5 versteckt war.
- Bei +72 h ist v6 mit 45 % Skill und RMSE 27,85 cm immer noch ein deutlich besseres Modell als die Persistenz-Baseline (NSE 0,7211). 3-Tage-Vorhersagen bleiben operationell nutzbar — aber jetzt mit ehrlich verifizierten Skill-Werten ohne methodischen Vorbehalt.
Live-Pipeline (Phase 7.1c)¶
brightsky_poller.py (unverändert, cron 30 *)
└─ rhein_forecast.weather_forecast (DWD-ICON-Forecast 0-72h, Live)
arx_worms_v6.py (einmaliger Lauf, 2026-05-06)
Train 2018-2024 mit TIGGE-Reforecast-Features
→ /app/data/worms_arx_v6_models.pkl (42 kB, 10 Lead-Modelle)
forecast_cron_v6.py (cron 25 *, parallel zu v5 cron 20 *)
Past-Niederschlag aus rhein_raw.weather (gleicher Code wie v5)
Future-Niederschlag aus rhein_forecast.weather_forecast (Brightsky-DWD-ICON live)
→ rhein_forecast.worms_arx_v6_forecast (Lead 1..72 h)
Cron-Slot 25 statt 20, damit v5 und v6 parallel laufen — Vergleich der Live-Vorhersagen ist im Dashboard möglich.
Status (2026-05-06)¶
- Backfill: 84 / 84 Monate erfolgreich (Failed months: keine), 196 k Influx-Punkte
- Training: 73 104 h gesamt, 61 346 h mit allen 111 Features non-NaN, n_train = 52 555, n_test = 8 791
- Pickle:
/app/data/worms_arx_v6_models.pkl, 42 kB - Live-Cron deployed:
/etc/cron.d/rhein-v6ab 2026-05-06 23:16 CEST