HBV-Lab 1.4.1__tar.gz → 1.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/HBV_Lab/__init__.py +1 -1
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/HBV_Lab/mcp_server.py +78 -8
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/HBV_Lab.egg-info/PKG-INFO +10 -3
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/PKG-INFO +10 -3
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/README.md +9 -2
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/setup.py +1 -1
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/HBV_Lab/HBV_model.py +0 -0
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/HBV_Lab/calibration.py +0 -0
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/HBV_Lab/hbv_step.py +0 -0
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/HBV_Lab/response.py +0 -0
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/HBV_Lab/routing.py +0 -0
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/HBV_Lab/snow.py +0 -0
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/HBV_Lab/soil.py +0 -0
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/HBV_Lab/uncertainty.py +0 -0
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/HBV_Lab.egg-info/SOURCES.txt +0 -0
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/HBV_Lab.egg-info/dependency_links.txt +0 -0
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/HBV_Lab.egg-info/entry_points.txt +0 -0
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/HBV_Lab.egg-info/requires.txt +0 -0
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/HBV_Lab.egg-info/top_level.txt +0 -0
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/LICENSE +0 -0
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/setup.cfg +0 -0
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/tests/test_hbv_model.py +0 -0
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/tests/test_hbv_step.py +0 -0
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/tests/test_response.py +0 -0
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/tests/test_snow.py +0 -0
- {hbv_lab-1.4.1 → hbv_lab-1.4.2}/tests/test_soil.py +0 -0
|
@@ -140,6 +140,12 @@ def load_data(
|
|
|
140
140
|
optional but required for calibration, uncertainty analysis and performance
|
|
141
141
|
metrics. Dates use ``date_format`` (e.g. '%Y%m%d' for 19810101). ``warmup_end`` /
|
|
142
142
|
``start_date`` / ``end_date`` are optional date filters (same format).
|
|
143
|
+
|
|
144
|
+
Potential ET may be given as a full daily series OR as exactly 12 values (monthly
|
|
145
|
+
means, the HBV-light convention) — in the latter case it is **expanded to a daily
|
|
146
|
+
series automatically**. The return reports ``pet_handling`` so you know which path
|
|
147
|
+
was taken, plus a ``data_quality`` summary (valid/missing counts and min/max per
|
|
148
|
+
column) so you don't have to open the file to sanity-check the inputs.
|
|
143
149
|
"""
|
|
144
150
|
import pandas as pd
|
|
145
151
|
|
|
@@ -151,6 +157,9 @@ def load_data(
|
|
|
151
157
|
else:
|
|
152
158
|
df = pd.read_csv(file_path)
|
|
153
159
|
|
|
160
|
+
# PET in the raw file: 12 non-NaN values => monthly means (will be expanded daily)
|
|
161
|
+
raw_pet_valid = int(df[pet_column].notna().sum()) if pet_column in df.columns else 0
|
|
162
|
+
|
|
154
163
|
model.load_data(
|
|
155
164
|
data=df,
|
|
156
165
|
date_column=date_column,
|
|
@@ -163,6 +172,36 @@ def load_data(
|
|
|
163
172
|
start_date=(start_date or None),
|
|
164
173
|
end_date=(end_date or None),
|
|
165
174
|
)
|
|
175
|
+
|
|
176
|
+
# Per-column data-quality summary over the loaded (filtered) window
|
|
177
|
+
data_quality = {}
|
|
178
|
+
col_map = {
|
|
179
|
+
"precipitation": precip_column,
|
|
180
|
+
"temperature": temp_column,
|
|
181
|
+
"potential_et": pet_column,
|
|
182
|
+
"observed_discharge": (obs_q_column or None),
|
|
183
|
+
}
|
|
184
|
+
for label, col in col_map.items():
|
|
185
|
+
if col and col in model.data.columns:
|
|
186
|
+
s = model.data[col]
|
|
187
|
+
has = bool(s.notna().any())
|
|
188
|
+
data_quality[label] = {
|
|
189
|
+
"valid": int(s.notna().sum()),
|
|
190
|
+
"missing": int(s.isna().sum()),
|
|
191
|
+
"pct_missing": round(float(s.isna().mean() * 100), 1),
|
|
192
|
+
"min": round(float(s.min()), 3) if has else None,
|
|
193
|
+
"max": round(float(s.max()), 3) if has else None,
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
# How PET was interpreted
|
|
197
|
+
pet_in_data = pet_column in model.data.columns
|
|
198
|
+
if raw_pet_valid == 12:
|
|
199
|
+
pet_handling = "expanded_from_12_monthly_means"
|
|
200
|
+
elif pet_in_data and bool(model.data[pet_column].notna().all()):
|
|
201
|
+
pet_handling = "daily_series"
|
|
202
|
+
else:
|
|
203
|
+
pet_handling = "sparse_or_missing"
|
|
204
|
+
|
|
166
205
|
return {
|
|
167
206
|
"model_id": model_id,
|
|
168
207
|
"n_timesteps": int(len(model.data)),
|
|
@@ -170,6 +209,8 @@ def load_data(
|
|
|
170
209
|
"end_date": str(model.end_date),
|
|
171
210
|
"time_step": model.time_step,
|
|
172
211
|
"has_observed_discharge": bool(obs_q_column),
|
|
212
|
+
"pet_handling": pet_handling,
|
|
213
|
+
"data_quality": data_quality,
|
|
173
214
|
}
|
|
174
215
|
|
|
175
216
|
|
|
@@ -418,15 +459,23 @@ def evaluate_uncertainty(
|
|
|
418
459
|
objective: str = "NSE",
|
|
419
460
|
save_best: int = 10,
|
|
420
461
|
seed: int = 42,
|
|
462
|
+
output_file: str = "",
|
|
421
463
|
) -> dict:
|
|
422
464
|
"""Monte-Carlo uncertainty analysis (requires obs data loaded).
|
|
423
465
|
|
|
424
|
-
Samples the parameter ranges ``n_runs`` times and keeps the ``save_best`` runs
|
|
425
|
-
|
|
426
|
-
|
|
466
|
+
Samples the parameter ranges ``n_runs`` times and keeps the ``save_best`` runs, then
|
|
467
|
+
forms a 95% prediction band from them. Returns diagnostics: the 95% band
|
|
468
|
+
``coverage`` (fraction of observations inside the band), ``mean_band_width``, and the
|
|
469
|
+
per-parameter posterior **quantiles** (p5/p25/p50/p75/p95) across the best sets.
|
|
470
|
+
|
|
471
|
+
If ``output_file`` is given, the full **per-timestep prediction band** is written to
|
|
472
|
+
that CSV (Date, Observed, Calibrated, BestRun, Q5, Q95) — this is the band itself,
|
|
473
|
+
ready to plot. Sampling is uniform over the parameter ranges (this is parameter, not
|
|
474
|
+
predictive, uncertainty).
|
|
427
475
|
"""
|
|
428
476
|
model = _get(model_id)
|
|
429
477
|
import numpy as np
|
|
478
|
+
import pandas as pd
|
|
430
479
|
|
|
431
480
|
out = model.evaluate_uncertainty(
|
|
432
481
|
n_runs=n_runs,
|
|
@@ -450,17 +499,36 @@ def evaluate_uncertainty(
|
|
|
450
499
|
else:
|
|
451
500
|
coverage_95 = mean_band_width = None
|
|
452
501
|
|
|
453
|
-
# Per-parameter posterior
|
|
502
|
+
# Per-parameter posterior quantiles across the best parameter sets
|
|
454
503
|
posterior: dict = {}
|
|
455
504
|
for s in out["best_parameter_sets"]:
|
|
456
505
|
for params in s["parameters"].values():
|
|
457
506
|
for name, info in params.items():
|
|
458
|
-
posterior.setdefault(name, []).append(info["default"])
|
|
459
|
-
|
|
460
|
-
name: {"
|
|
507
|
+
posterior.setdefault(name, []).append(float(info["default"]))
|
|
508
|
+
parameter_posteriors = {
|
|
509
|
+
name: {f"p{p}": round(float(np.percentile(v, p)), 4) for p in (5, 25, 50, 75, 95)}
|
|
461
510
|
for name, v in posterior.items()
|
|
462
511
|
}
|
|
463
512
|
|
|
513
|
+
# Optionally write the per-timestep prediction band to CSV
|
|
514
|
+
band_file = None
|
|
515
|
+
if output_file:
|
|
516
|
+
band = pd.DataFrame()
|
|
517
|
+
if "dates" in df.columns:
|
|
518
|
+
band["Date"] = df["dates"].values
|
|
519
|
+
band["Observed"] = df["observed"].values
|
|
520
|
+
if "original" in df.columns:
|
|
521
|
+
band["Calibrated"] = df["original"].values
|
|
522
|
+
if "best_1" in df.columns:
|
|
523
|
+
band["BestRun"] = df["best_1"].values
|
|
524
|
+
band["Q5"] = df["q5"].values
|
|
525
|
+
band["Q95"] = df["q95"].values
|
|
526
|
+
out_dir = os.path.dirname(output_file)
|
|
527
|
+
if out_dir:
|
|
528
|
+
os.makedirs(out_dir, exist_ok=True)
|
|
529
|
+
band.to_csv(output_file, index=False)
|
|
530
|
+
band_file = os.path.abspath(output_file)
|
|
531
|
+
|
|
464
532
|
return {
|
|
465
533
|
"model_id": model_id,
|
|
466
534
|
"objective": objective,
|
|
@@ -470,7 +538,9 @@ def evaluate_uncertainty(
|
|
|
470
538
|
"current_performance": round(float(out["original_performance"]), 4),
|
|
471
539
|
"coverage_95": coverage_95, # fraction of observations inside the 95% band
|
|
472
540
|
"mean_band_width": mean_band_width, # mean (q95 - q5), mm/day
|
|
473
|
-
"
|
|
541
|
+
"parameter_posteriors": parameter_posteriors, # p5/p25/p50/p75/p95 per parameter
|
|
542
|
+
"band_file": band_file, # per-timestep band CSV, if output_file given
|
|
543
|
+
"uncertainty_type": "parameter", # uniform parameter sampling (not predictive)
|
|
474
544
|
}
|
|
475
545
|
|
|
476
546
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: HBV_Lab
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.2
|
|
4
4
|
Summary: An agentic, object-oriented Python implementation of a lumped conceptual HBV hydrological model — with calibration, uncertainty analysis, and a built-in MCP server that lets AI agents drive the workflow.
|
|
5
5
|
Home-page: https://github.com/abdallaox/HBV_python_implementation
|
|
6
6
|
Author: Abdalla Mohammed
|
|
@@ -284,9 +284,16 @@ inspect the improving metric, and decide whether to keep going, widen ranges (vi
|
|
|
284
284
|
model), `load_data` the validation window on the clone, and `run_model` — the calibrated parameters
|
|
285
285
|
carry over with no manual transfer. `compare_models` tabulates calibration vs. validation metrics.
|
|
286
286
|
|
|
287
|
+
**Transparent data loading.** `load_data` reports `pet_handling` (e.g.
|
|
288
|
+
`expanded_from_12_monthly_means` when PET is supplied as 12 monthly means — the HBV-light convention —
|
|
289
|
+
and auto-expanded to daily) and a per-column `data_quality` summary (valid/missing counts, min/max), so
|
|
290
|
+
an agent can sanity-check the forcing without opening the file.
|
|
291
|
+
|
|
287
292
|
**Richer uncertainty.** `evaluate_uncertainty` returns the 95% prediction-band **coverage** (fraction
|
|
288
|
-
of observations inside the band), **mean band width**, and per-parameter
|
|
289
|
-
|
|
293
|
+
of observations inside the band), **mean band width**, and per-parameter posterior **quantiles**
|
|
294
|
+
(p5/p25/p50/p75/p95). Pass `output_file` to write the full **per-timestep prediction band**
|
|
295
|
+
(Date, Observed, Calibrated, BestRun, Q5, Q95) to CSV, ready to plot. (Sampling is uniform over the
|
|
296
|
+
parameter ranges — parameter, not predictive, uncertainty.)
|
|
290
297
|
|
|
291
298
|
## Inputs & outputs
|
|
292
299
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: HBV_Lab
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.2
|
|
4
4
|
Summary: An agentic, object-oriented Python implementation of a lumped conceptual HBV hydrological model — with calibration, uncertainty analysis, and a built-in MCP server that lets AI agents drive the workflow.
|
|
5
5
|
Home-page: https://github.com/abdallaox/HBV_python_implementation
|
|
6
6
|
Author: Abdalla Mohammed
|
|
@@ -284,9 +284,16 @@ inspect the improving metric, and decide whether to keep going, widen ranges (vi
|
|
|
284
284
|
model), `load_data` the validation window on the clone, and `run_model` — the calibrated parameters
|
|
285
285
|
carry over with no manual transfer. `compare_models` tabulates calibration vs. validation metrics.
|
|
286
286
|
|
|
287
|
+
**Transparent data loading.** `load_data` reports `pet_handling` (e.g.
|
|
288
|
+
`expanded_from_12_monthly_means` when PET is supplied as 12 monthly means — the HBV-light convention —
|
|
289
|
+
and auto-expanded to daily) and a per-column `data_quality` summary (valid/missing counts, min/max), so
|
|
290
|
+
an agent can sanity-check the forcing without opening the file.
|
|
291
|
+
|
|
287
292
|
**Richer uncertainty.** `evaluate_uncertainty` returns the 95% prediction-band **coverage** (fraction
|
|
288
|
-
of observations inside the band), **mean band width**, and per-parameter
|
|
289
|
-
|
|
293
|
+
of observations inside the band), **mean band width**, and per-parameter posterior **quantiles**
|
|
294
|
+
(p5/p25/p50/p75/p95). Pass `output_file` to write the full **per-timestep prediction band**
|
|
295
|
+
(Date, Observed, Calibrated, BestRun, Q5, Q95) to CSV, ready to plot. (Sampling is uniform over the
|
|
296
|
+
parameter ranges — parameter, not predictive, uncertainty.)
|
|
290
297
|
|
|
291
298
|
## Inputs & outputs
|
|
292
299
|
|
|
@@ -243,9 +243,16 @@ inspect the improving metric, and decide whether to keep going, widen ranges (vi
|
|
|
243
243
|
model), `load_data` the validation window on the clone, and `run_model` — the calibrated parameters
|
|
244
244
|
carry over with no manual transfer. `compare_models` tabulates calibration vs. validation metrics.
|
|
245
245
|
|
|
246
|
+
**Transparent data loading.** `load_data` reports `pet_handling` (e.g.
|
|
247
|
+
`expanded_from_12_monthly_means` when PET is supplied as 12 monthly means — the HBV-light convention —
|
|
248
|
+
and auto-expanded to daily) and a per-column `data_quality` summary (valid/missing counts, min/max), so
|
|
249
|
+
an agent can sanity-check the forcing without opening the file.
|
|
250
|
+
|
|
246
251
|
**Richer uncertainty.** `evaluate_uncertainty` returns the 95% prediction-band **coverage** (fraction
|
|
247
|
-
of observations inside the band), **mean band width**, and per-parameter
|
|
248
|
-
|
|
252
|
+
of observations inside the band), **mean band width**, and per-parameter posterior **quantiles**
|
|
253
|
+
(p5/p25/p50/p75/p95). Pass `output_file` to write the full **per-timestep prediction band**
|
|
254
|
+
(Date, Observed, Calibrated, BestRun, Q5, Q95) to CSV, ready to plot. (Sampling is uniform over the
|
|
255
|
+
parameter ranges — parameter, not predictive, uncertainty.)
|
|
249
256
|
|
|
250
257
|
## Inputs & outputs
|
|
251
258
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|