CI / Test suite (push) Successful in 1m2s
CI / Format & lint (push) Successful in 22s
Rust / fmt, clippy, test, build (push) Successful in 9m53s
Security / Dependency vulnerabilities (push) Successful in 1m52s
Security / Static analysis (push) Successful in 20s
Security / License report (push) Successful in 1m41s
Phase 2a of docs/RUST_REWRITE.md. rust/forecast (ping-forecast) ports the serving half of src/ml: the HII mirror gap fill, the hourly grid, every feature (pandas' Kahan rolling sums included), the Mae Ngat dam frame, HistGradientBoosting tree inference, the heuristic fallback, stale-threshold handling and the P.1 flood-stage probabilities. Hand-over from Python (src/ml/export.py): - models/flood_<station>.json.gz next to each joblib bundle; train_all() writes it, retrain.sh promotes it, `python -m src.ml.export` exports existing ones. - rust/forecast/flood_config.json: thresholds, stages, leads and notify constants, compiled in. tests/test_model_export.py fails while it differs from the Python source. Parity (rust/forecast/tests/parity.rs, fixtures from testdata/gen_forecast_fixtures.py on the production code path): four scenarios incl. the 2024 flood with RID lagging behind HII. Merged readings, all features and all levels identical; with all 15 local models 377/384 probabilities bit-identical, the rest within 2 ulp (exp). Python refactor without behaviour change: predict.forecasts_from_long, and get_latest_forecasts/load_latest/serving_frame take now/rain for reproducible comparisons; the HII query orders its rows.
28 lines
858 B
Rust
28 lines
858 B
Rust
//! Catchment rain series (`src/ml/rain.py`: `catchment_mean`).
|
|
|
|
use chrono::NaiveDateTime;
|
|
use std::collections::BTreeMap;
|
|
|
|
/// Hourly catchment-average precipitation (mm); NaN = no value.
|
|
pub type RainSeries = BTreeMap<NaiveDateTime, f64>;
|
|
|
|
/// pandas `mean(axis=1)` over the catchment points: the points that have a
|
|
/// value, summed in column order, divided by their count.
|
|
pub fn catchment_mean(points: &[Option<f64>]) -> Option<f64> {
|
|
let mut sum = 0.0;
|
|
let mut n = 0usize;
|
|
for v in points.iter().flatten() {
|
|
sum += *v;
|
|
n += 1;
|
|
}
|
|
(n > 0).then(|| sum / n as f64)
|
|
}
|
|
|
|
/// The series on the given hours (`Series.reindex`), NaN where absent.
|
|
pub fn reindex(rain: &RainSeries, times: &[NaiveDateTime]) -> Vec<f64> {
|
|
times
|
|
.iter()
|
|
.map(|t| rain.get(t).copied().unwrap_or(f64::NAN))
|
|
.collect()
|
|
}
|