feat: hgb-v3 — Open-Meteo rain features clear the 12h warning gate
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Production (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Performance Test (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Code Quality (push) Successful in 13s
Documentation / Validate Documentation (push) Failing after 7s
Documentation / Generate API Documentation (push) Successful in 8s
Documentation / Build Sphinx Documentation (push) Successful in 17s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Cleanup (push) Successful in 0s
Documentation / Documentation Summary (push) Successful in 2s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Test Suite (3.11) (push) Failing after 24s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Build Docker Image (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Integration Test with Services (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Staging (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Production (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Performance Test (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Code Quality (push) Successful in 13s
Documentation / Validate Documentation (push) Failing after 7s
Documentation / Generate API Documentation (push) Successful in 8s
Documentation / Build Sphinx Documentation (push) Successful in 17s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Cleanup (push) Successful in 0s
Documentation / Documentation Summary (push) Successful in 2s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Test Suite (3.11) (push) Failing after 24s
CI/CD Pipeline - Northern Thailand Ping River Monitor / Build Docker Image (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Integration Test with Services (push) Skipped
CI/CD Pipeline - Northern Thailand Ping River Monitor / Deploy to Staging (push) Skipped
The rolling-origin harness (models/eval_rain.json) showed catchment rain halving flood-year Brier scores, cutting flood-regime MAE 20-40%, and extending the hard 2024 leads (+6h -> +11h at P.1, +10h -> +19h at P.103). Ported: train_all loads the catchment-mean series (use_rain / --no-rain to opt out; without it bundles train as v2), predict fetches live rain hourly and passes an empty series on failure so rain-trained bundles serve with NaN features instead of tripping the feature guard, and the leader worker persists hourly per-point + catchment-mean rows to a new openmeteo_rain table. Regenerated backtest: the 2024 record flood now gets a 13-HOUR WARNING (alert 04:00 vs 17:00 crossing, river at 2.9m at alert time) — the >=12h acceptance gate PASSES for the first time. Journey on that crossing: v1 -18h, v2 +6h, v3 +13h. The marginal 2025 double-crest trades its artifact +46h latch for a calibrated +2h with zero false alarms. P.1 MAE 4.9/7.2/8.7 cm at 6/12/24h. Docs updated throughout.
This commit is contained in:
+21
-4
@@ -127,6 +127,7 @@ def _model_forecast(
|
||||
bundle: dict,
|
||||
as_of: pd.Timestamp,
|
||||
current_level: float,
|
||||
rain: Optional[pd.Series] = None,
|
||||
) -> List[dict]:
|
||||
warn_thr = bundle["thresholds"]["warning"]
|
||||
danger_thr = bundle["thresholds"]["danger"]
|
||||
@@ -145,7 +146,7 @@ def _model_forecast(
|
||||
)
|
||||
warn_thr, danger_thr = cfg_warn, cfg_danger
|
||||
|
||||
feature_row = features.build_features(grid, station_code).loc[[as_of]]
|
||||
feature_row = features.build_features(grid, station_code, rain=rain).loc[[as_of]]
|
||||
expected_columns = bundle["feature_names"]
|
||||
missing = [c for c in expected_columns if c not in feature_row.columns]
|
||||
if missing:
|
||||
@@ -229,6 +230,7 @@ def _forecast_station(
|
||||
models_dir: Path,
|
||||
now: pd.Timestamp,
|
||||
horizons: Tuple[int, ...],
|
||||
rain: Optional[pd.Series] = None,
|
||||
) -> List[dict]:
|
||||
level_col = (station_code, "water_level")
|
||||
if level_col not in grid.observed.columns:
|
||||
@@ -264,7 +266,9 @@ def _forecast_station(
|
||||
)
|
||||
|
||||
bundle = _load_bundle(bundle_path)
|
||||
model_results = _model_forecast(station_code, grid, bundle, as_of, current_level)
|
||||
model_results = _model_forecast(
|
||||
station_code, grid, bundle, as_of, current_level, rain=rain
|
||||
)
|
||||
if model_results is None:
|
||||
return _heuristic_forecast(
|
||||
station_code,
|
||||
@@ -301,6 +305,7 @@ def get_forecasts(
|
||||
readings_by_station: Dict[str, List[dict]],
|
||||
models_dir: Union[str, Path] = DEFAULT_MODELS_DIR,
|
||||
now: Optional[Union[datetime.datetime, str]] = None,
|
||||
rain: Optional[pd.Series] = None,
|
||||
) -> List[dict]:
|
||||
"""Produce flood forecasts for every station present in `readings_by_station`.
|
||||
|
||||
@@ -323,7 +328,9 @@ def get_forecasts(
|
||||
for station_code in readings_by_station.keys():
|
||||
try:
|
||||
results.extend(
|
||||
_forecast_station(station_code, grid, models_dir, now, DEFAULT_HORIZONS)
|
||||
_forecast_station(
|
||||
station_code, grid, models_dir, now, DEFAULT_HORIZONS, rain=rain
|
||||
)
|
||||
)
|
||||
except Exception as error:
|
||||
logger.error(f"Forecast failed for station {station_code}: {error}")
|
||||
@@ -359,4 +366,14 @@ def get_latest_forecasts(
|
||||
f"No recent data for station {missing_station}; omitting from forecasts"
|
||||
)
|
||||
|
||||
return get_forecasts(readings_by_station, models_dir=models_dir)
|
||||
# Live rain: trailing days + next-48h forecast. On fetch failure pass an
|
||||
# EMPTY series (not None) so rain-trained bundles still find their columns
|
||||
# (as NaN) and serve model output instead of tripping the feature guard.
|
||||
from .rain import serving_series
|
||||
|
||||
rain = serving_series()
|
||||
if rain is None:
|
||||
logger.warning("live rain unavailable; rain features will be NaN")
|
||||
rain = pd.Series(dtype=float)
|
||||
|
||||
return get_forecasts(readings_by_station, models_dir=models_dir, rain=rain)
|
||||
|
||||
Reference in New Issue
Block a user