diff --git a/src/analysis/probability_calibration.py b/src/analysis/probability_calibration.py index dff5499a..49aa8b66 100644 --- a/src/analysis/probability_calibration.py +++ b/src/analysis/probability_calibration.py @@ -378,6 +378,10 @@ def apply_probability_calibration( blend_alpha_mu = max(0.0, min(1.0, _coalesce_float(blending_cfg.get("alpha_mu"), 1.0))) blend_alpha_sigma = max(0.0, min(1.0, _coalesce_float(blending_cfg.get("alpha_sigma"), 1.0))) calibrated_mu = _blend_value(raw_mu, calibrated_mu, blend_alpha_mu) + if max_so_far is not None: + observed_floor = _sf(max_so_far) + if observed_floor is not None and calibrated_mu < observed_floor: + calibrated_mu = observed_floor calibrated_sigma = max(0.1, _blend_value(raw_sigma, calibrated_sigma, blend_alpha_sigma)) calibrated_sigma = _clamp_sigma(raw_sigma, calibrated_sigma, sigma_constraints) calibrated_distribution, calibrated_sorted = _bucket_probabilities( diff --git a/tests/test_probability_calibration.py b/tests/test_probability_calibration.py index 0120779b..4636f303 100644 --- a/tests/test_probability_calibration.py +++ b/tests/test_probability_calibration.py @@ -132,6 +132,37 @@ def test_primary_mode_switches_to_calibrated_distribution(tmp_path): assert result["distribution"][0]["value"] >= 10 +def test_primary_mode_respects_observed_max_floor(tmp_path): + calibration_path = _write_calibration(tmp_path) + features = build_probability_features( + city_name="ankara", + raw_mu=32.0, + raw_sigma=1.0, + deb_prediction=32.0, + ens_data={"median": 31.5, "p10": 30.0, "p90": 34.0}, + current_forecasts={"Open-Meteo": 32.0, "MGM": 31.8}, + max_so_far=33.0, + peak_status="in_window", + local_hour_frac=14.0, + ) + + result = apply_probability_calibration( + city_name="ankara", + temp_symbol="°C", + raw_mu=32.0, + raw_sigma=1.0, + max_so_far=33.0, + legacy_distribution=[{"value": 33, "range": "[32.5~33.5)", "probability": 0.7}], + features=features, + calibration_path=str(calibration_path), + mode=ENGINE_MODE_EMOS_PRIMARY, + ) + + assert result["engine"] == "emos" + assert result["calibrated_mu"] >= 33.0 + assert all(row["value"] >= 33 for row in result["distribution"]) + + def test_fit_calibration_returns_metrics(): samples = [ {