oprava
This commit is contained in:
@@ -71,7 +71,7 @@ NEG_BUY_CHARGE_SHORTFALL_PENALTY_CZK_KWH = 100.0
|
||||
PRE_NEG_CHARGE_PENALTY_CZK_KWH = 400.0
|
||||
PRE_NEG_BATT_EXPORT_SHORTFALL_PENALTY_CZK_KWH = 80.0
|
||||
PRE_NEG_BATT_EXPORT_MIN_SELL_CZK_KWH = 1.0
|
||||
PLANNER_BUILD_TAG = "2026-05-31-evening-push-relaxed-clear-v54"
|
||||
PLANNER_BUILD_TAG = "2026-05-31-evening-push-any-relaxed-v55"
|
||||
# Ranní slabá FVE: neaplikovat pv_store ge_pv=0 (jinak curtail při sell < večerní peak).
|
||||
DAWN_LOW_PV_NO_CURTAIL_W = 1500
|
||||
# Mimo evening_push: preferovat bd pro dům místo gi, když buy >> acq (účinná cena importu).
|
||||
@@ -234,20 +234,29 @@ def _maybe_add_planner_comparison(
|
||||
peer_version = _planner_peer_version(active_version)
|
||||
if peer_version == active_version:
|
||||
return None
|
||||
peer_results, peer_ms, peer_snapshot = solve_dispatch(
|
||||
slots,
|
||||
battery,
|
||||
heat_pump,
|
||||
grid,
|
||||
ev_sessions,
|
||||
vehicles,
|
||||
current_soc_wh,
|
||||
current_tuv_temp_c,
|
||||
tuv_delta_stats=tuv_delta_stats,
|
||||
operating_mode=operating_mode,
|
||||
charge_commitment_prev_w=charge_commitment_prev_w,
|
||||
planner_version=peer_version,
|
||||
)
|
||||
try:
|
||||
peer_results, peer_ms, peer_snapshot = solve_dispatch_two_pass(
|
||||
slots,
|
||||
battery,
|
||||
heat_pump,
|
||||
grid,
|
||||
ev_sessions,
|
||||
vehicles,
|
||||
current_soc_wh,
|
||||
current_tuv_temp_c,
|
||||
tuv_delta_stats=tuv_delta_stats,
|
||||
operating_mode=operating_mode,
|
||||
charge_commitment_prev_w=charge_commitment_prev_w,
|
||||
planner_version=peer_version,
|
||||
evening_push_ts_override=None,
|
||||
)
|
||||
except RuntimeError as exc:
|
||||
logger.warning(
|
||||
"Planner comparison peer (%s) failed, skipping compare run: %s",
|
||||
peer_version,
|
||||
exc,
|
||||
)
|
||||
return None
|
||||
# active_results / active_ms jsou doplněny později v calleru
|
||||
return {
|
||||
"peer_version": peer_version,
|
||||
@@ -2721,7 +2730,12 @@ def solve_dispatch(
|
||||
evening_push_hysteresis_retained = True
|
||||
else:
|
||||
evening_push_ts = computed_evening_push_ts
|
||||
if relaxed_neg_prep_window:
|
||||
if (
|
||||
relaxed_expensive_import
|
||||
or relaxed_neg_buy_charge
|
||||
or relaxed_neg_prep_window
|
||||
or neg_sell_phases_fallback
|
||||
):
|
||||
evening_push_ts = set()
|
||||
evening_push_hysteresis_retained = False
|
||||
last_pos_sell_pre_neg_buy = _last_non_negative_sell_before_neg_buy(
|
||||
@@ -2843,9 +2857,16 @@ def solve_dispatch(
|
||||
|
||||
commit_pen = float(getattr(battery, "planner_charge_commitment_penalty_czk_kwh", 0.2))
|
||||
commit_lp: list[tuple[int, pulp.LpVariable, float]] = []
|
||||
if charge_commitment_prev_w is not None and len(charge_commitment_prev_w) == T:
|
||||
commitment_for_solve = charge_commitment_prev_w
|
||||
if (
|
||||
relaxed_neg_buy_charge
|
||||
or relaxed_neg_prep_window
|
||||
or neg_sell_phases_fallback
|
||||
):
|
||||
commitment_for_solve = None
|
||||
if commitment_for_solve is not None and len(commitment_for_solve) == T:
|
||||
for t in range(T):
|
||||
prev = charge_commitment_prev_w[t]
|
||||
prev = commitment_for_solve[t]
|
||||
if prev is not None and prev > 500:
|
||||
cap_prev = float(prev)
|
||||
cv = pulp.LpVariable(f"ccommit_{t}", 0, cap_prev)
|
||||
@@ -4097,6 +4118,7 @@ def solve_dispatch(
|
||||
planner_version=planner_version,
|
||||
relaxed_expensive_import=True,
|
||||
relaxed_neg_buy_charge=True,
|
||||
evening_push_ts_override=evening_push_ts_override,
|
||||
)
|
||||
if not relaxed_neg_prep_window:
|
||||
logger.warning(
|
||||
@@ -4503,7 +4525,15 @@ def solve_dispatch(
|
||||
"evening_push_override_filtered_empty": bool(
|
||||
push_override_raw and not push_override_eff
|
||||
),
|
||||
"evening_push_cleared_on_relaxed_prep": bool(relaxed_neg_prep_window),
|
||||
"evening_push_cleared_on_relaxed_prep": bool(
|
||||
relaxed_neg_prep_window
|
||||
or relaxed_neg_buy_charge
|
||||
or relaxed_expensive_import
|
||||
or neg_sell_phases_fallback
|
||||
),
|
||||
"charge_commitment_ignored_on_relaxed": bool(
|
||||
commitment_for_solve is None and charge_commitment_prev_w is not None
|
||||
),
|
||||
"kv1_evening_push_morning_peak_rule": _kv1_block_export_fixed_evening_push(
|
||||
grid,
|
||||
purchase_fixed=purchase_fixed_pre,
|
||||
|
||||
@@ -2897,6 +2897,46 @@ class ChargeAcquisitionArbitrageTests(unittest.TestCase):
|
||||
self.assertNotIn(0, out)
|
||||
self.assertIn(1, out)
|
||||
|
||||
def test_relaxed_expensive_import_clears_evening_push(self) -> None:
|
||||
"""v55: už 1. retry (relaxed_expensive_import) vypne tvrdý evening push."""
|
||||
prague = ZoneInfo("Europe/Prague")
|
||||
slots = [
|
||||
PlanningSlot(
|
||||
interval_start=datetime(2026, 5, 30, 22, 0, tzinfo=prague).astimezone(timezone.utc),
|
||||
buy_price=3.0,
|
||||
sell_price=6.0,
|
||||
pv_a_forecast_w=0,
|
||||
pv_b_forecast_w=0,
|
||||
load_baseline_w=500,
|
||||
ev1_connected=False,
|
||||
ev2_connected=False,
|
||||
allow_discharge_export=True,
|
||||
),
|
||||
]
|
||||
battery = _battery(uc_wh=64_000.0)
|
||||
battery.max_discharge_power_w = 18_000
|
||||
grid = SimpleNamespace(
|
||||
max_export_power_w=13_500,
|
||||
max_import_power_w=17_000,
|
||||
block_export_on_negative_sell=False,
|
||||
purchase_pricing_mode="spot",
|
||||
)
|
||||
_results, _ms, snap = solve_dispatch(
|
||||
slots,
|
||||
battery,
|
||||
SimpleNamespace(rated_heating_power_w=0, tuv_min_temp_c=45.0, tuv_target_temp_c=55.0),
|
||||
grid,
|
||||
[None, None],
|
||||
[
|
||||
SimpleNamespace(max_charge_power_w=0, battery_capacity_kwh=1.0, default_target_soc_pct=80.0),
|
||||
SimpleNamespace(max_charge_power_w=0, battery_capacity_kwh=1.0, default_target_soc_pct=80.0),
|
||||
],
|
||||
current_soc_wh=16_000.0,
|
||||
current_tuv_temp_c=55.0,
|
||||
relaxed_expensive_import=True,
|
||||
)
|
||||
self.assertEqual(snap["inputs"].get("evening_push_ts"), [])
|
||||
|
||||
def test_relaxed_neg_prep_clears_computed_evening_push(self) -> None:
|
||||
"""v54: relaxed_neg_prep_window vypne tvrdý ge_bat push (ne jen hysterézní override)."""
|
||||
prague = ZoneInfo("Europe/Prague")
|
||||
|
||||
@@ -125,9 +125,11 @@ flowchart TD
|
||||
|
||||
8. **v53 — rolling hysteréze push:** při Infeasible retry se **`evening_push_ts_override` zahodí**; filtr override slotů (export maska, bez defer PV). Snap: `evening_push_override_dropped_on_retry`. Tag **`2026-05-31-evening-push-override-retry-v53`**.
|
||||
|
||||
9. **v54 — relaxed prep + two-pass:** při **`relaxed_neg_prep_window`** i vypočtený **`evening_push_ts = ∅`** (tvrdý push off); pass2 two-pass **nepoužívá override** a dědí relax vlajky z pass1. Snap: `evening_push_cleared_on_relaxed_prep`. Tag **`2026-05-31-evening-push-relaxed-clear-v54`**.
|
||||
9. **v54 — relaxed prep + two-pass:** při **`relaxed_neg_prep_window`** i vypočtený **`evening_push_ts = ∅`**; pass2 two-pass **nepoužívá override** a dědí relax vlajky z pass1. Tag **`2026-05-31-evening-push-relaxed-clear-v54`**.
|
||||
|
||||
**Funkce:** … Tag: **`2026-05-30-post-push-night-battery-v47`** (spot); KV1 v52 / home-01 v54 viz výše.
|
||||
10. **v55 — jakýkoli relaxed retry:** tvrdý push off už od **`relaxed_expensive_import`**; commitment z minulého plánu ignorovat od **`relaxed_neg_buy_charge`**; comparison v2 **non-fatal**. Tag **`2026-05-31-evening-push-any-relaxed-v55`**.
|
||||
|
||||
**Funkce:** … Tag: **`2026-05-30-post-push-night-battery-v47`** (spot); KV1 v52 / home-01 v55 viz výše.
|
||||
|
||||
### Arbitráž baterie — účtování mezi sloty (povinné čtení)
|
||||
|
||||
|
||||
@@ -5,6 +5,16 @@ Formát: **datum (ISO)** · stručný důvod · soubory · chování / ověřen
|
||||
|
||||
---
|
||||
|
||||
## 2026-05-31 — home-01: evening push při každém relaxed retry (v55)
|
||||
|
||||
**Problém:** v54 maže tvrdý push až u `relaxed_neg_prep_window` (3. retry). Retry 1–2 pořád držely **vypočtený** `evening_push_ts` → u ~25 % SoC často stále **Infeasible**. Ruční „Přeplánovat“ navíc spadlo, když **v2 comparison** peer selhal (active v1 prošel). V DB po pádu **žádný `api` run** — scheduler v51/v54 mezitím OK.
|
||||
|
||||
**Změna (v55):** tvrdý `evening_push_ts = ∅` při **jakékoli** relaxed vlajce; rolling commitment ignorovat od `relaxed_neg_buy_charge`; comparison peer = `solve_dispatch_two_pass` + **non-fatal** skip. Snap: `evening_push_cleared_on_relaxed_prep`, `charge_commitment_ignored_on_relaxed`.
|
||||
|
||||
Tag **`2026-05-31-evening-push-any-relaxed-v55`**.
|
||||
|
||||
---
|
||||
|
||||
## 2026-05-31 — home-01: tvrdý evening push po relaxed prep (v54)
|
||||
|
||||
**Problém:** v53 maže jen **hysterézní override**, ne **vypočtený** `evening_push_ts`. Po `relaxed_neg_prep_window` (typicky home-01 ~25 % SoC + neg den 31.5.) zůstávaly tvrdé `ge_bat`/`z_export` v push slotech → **`Solver: Infeasible`** i po celém retry řetězci. Pass2 two-pass znovu aplikoval override bez carryover relaxace.
|
||||
|
||||
Reference in New Issue
Block a user