diff --git a/docs/FLOOD_FORECASTING.md b/docs/FLOOD_FORECASTING.md index 3774614..9ae4ebc 100644 --- a/docs/FLOOD_FORECASTING.md +++ b/docs/FLOOD_FORECASTING.md @@ -291,6 +291,21 @@ training half. So the model was retrained on data **ending 2024-08-31** and aske to forecast September–November 2024 cold, with no knowledge of the event that followed. This is the closest thing to a real operational test available. +![Observed P.1 level vs the model's 24 h-ahead predicted peak through the +October 2024 flood, with the warning probability below](img/backtest-2024-p1.png) + +The render above shows the whole event hour by hour. Top: the observed level +(blue) against the 24 h-ahead predicted peak the model issued at each hour +(amber, dashed) — the amber line leads the blue one into both flood waves, +which is the entire point of the system. Bottom: the model's probability of +flooding within 24 h; it fires its **first alert at 01:00 on 24 September, a +full day before the river crossed the 3.70 m flooding line**, stays pinned near +1.0 through both waves, and stands down between and after them. Also visible, +honestly: the predicted peak tops out ~0.4 m short of the actual 5.30 m record +(the extreme-compression limitation discussed below), and the prediction is +noisier on the recession limbs. The same model track drives the dashboard's +"Replay Oct 2024 flood" feature, so this chart can be watched live on the map. + - **25 September cold start.** P.1's first warning crossing of the episode was alerted **24–26 hours ahead**. This is the genuinely impressive case: the river was in normal state, and the alert came from upstream routing alone. diff --git a/docs/img/backtest-2024-p1.png b/docs/img/backtest-2024-p1.png new file mode 100644 index 0000000..610544d Binary files /dev/null and b/docs/img/backtest-2024-p1.png differ diff --git a/src/static/dashboard.html b/src/static/dashboard.html index de5500b..d2401a2 100644 --- a/src/static/dashboard.html +++ b/src/static/dashboard.html @@ -870,7 +870,12 @@ alertEl.textContent = pWarn == null ? '' : 'model: no flood expected'; alertEl.className = 'replay-alert note'; } - i += 2; // 2 h per frame (~40 s total) + // Variable speed: race through quiet days, slow to hour-by-hour while + // the model is detecting and the flood unfolds, so the alert sequence + // (elevated risk -> model alert -> flooding) is readable. + const critical = (pWarn != null && pWarn >= 0.5) || (state.p1Now != null && state.p1Now >= 3.5); + const elevated = (pWarn != null && pWarn >= 0.2) || (state.p1Now != null && state.p1Now >= 2.8); + i += critical ? 1 : elevated ? 2 : 4; if (i >= n + 40) endReplay(); // hold the last frame briefly }, 120); } catch (error) {