diff --git a/backend/services/planning_engine.py b/backend/services/planning_engine.py index 08bea9d..eda2bfb 100644 --- a/backend/services/planning_engine.py +++ b/backend/services/planning_engine.py @@ -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-28-neg-prep-window-v36e" +PLANNER_BUILD_TAG = "2026-05-28-neg-prep-window-v36f" # Po t_detach v prep: necpát PV do bat (měkké; tvrdý hold přes soc_target z rampy). NEG_SELL_POST_DETACH_BCPV_DISCOURAGE_CZK_KWH = 250.0 # Večer před neg dnem: výboj do sítě (měkký shortfall na ge_bat). @@ -1336,7 +1336,7 @@ def _dispatch_grid_setpoint_w( ge_total = max(0.0, float(ge_w)) ge_bat_v = max(0.0, float(ge_bat_w)) cap = float(max_export_power_w) - if ge_bat_v >= GE_MIN_EXPORT_W: + if ge_bat_v >= GE_MIN_EXPORT_W and ge_bat_v >= max(ge_total * 0.5, 500.0): export_w = min(cap, max(ge_total, ge_bat_v + max(0.0, float(ge_pv_w)))) return -int(round(export_w)), "BATTERY_SELL" if ge_total >= GE_MIN_EXPORT_W: @@ -3313,13 +3313,15 @@ def solve_dispatch( and sell_t >= 0 and pv_surplus_w > 500 ) or ( - # KV1 (fixed + block_export, jen PV A): bez pole B neplatí fixed_pv_b_export_cap; - # jinak ge_pv==0 → plný curtail při plné baterii místo prodeje do site. + # Fixed (BA81/KV1): před prvním sell<0 a sell≥0 neblokovat ge_pv — export A+B do site, + # ne curtail (fixed_pv_b_export_cap pokrývá jen MI / pole B). purchase_fixed_pre - and bool(getattr(grid, "block_export_on_negative_sell", False)) - and float(s.pv_b_forecast_w) <= 0.0 and sell_t >= 0.0 - and pv_surplus_w > 500 + and pv_surplus_w > 500.0 + and ( + first_neg_sell_idx is None + or t < first_neg_sell_idx + ) ) # BA81: export pole B jen při kladném sell (po sell<0 jinak ge==0 výše). fixed_pv_b_export_cap = ( diff --git a/docs/planning-changelog.md b/docs/planning-changelog.md index ba63643..bc716c6 100644 --- a/docs/planning-changelog.md +++ b/docs/planning-changelog.md @@ -11,6 +11,12 @@ Formát: **datum (ISO)** · stručný důvod · soubory · chování / ověřen **Rozhodnutí home-01** (souhrn v [`docs/06-open-questions.md`](06-open-questions.md)): rampa/**T** odvozené z PV B (bez fixních 80 % v LP); TČ ne v pre-neg exportu; bazén min 4 h/den + Shelly; spirála Loxone; **workshop UI flex zátěží před v37** (§ 9.1 strategie). +## 2026-05-28 — Fixed tarif: export FVE před sell<0 (v36f) + +**Problém:** BA81 (fixed, sell>3 Kč ráno): plán **curtail** PV A (~3 kW) + export jen **~600 W** (`ge_pv` jen přes pole B). Střídač reálně valí celou FVE — ekonomicky správně, ale plán nesedí. Příčina: `ge_pv=0` při `sell < future_sell` (pv_store); `fixed_pv_b_export_cap` uvolní jen MI. + +**Změna (v36f):** `skip_pv_store_block` i pro **všechny fixed** sloty před prvním `sell<0` při `sell≥0`. `export_mode`: **BATTERY_SELL** jen když `ge_bat` je významný (≥500 W), jinak **PV_SURPLUS** (oprava matoucího labelu při ~600 W exportu). + ## 2026-05-28 — KV1 fixed + block_export (v36e) **Kód:** `planning_engine.py` tag `2026-05-28-neg-prep-window-v36e`; `R__063_fn_load_planning_slots_full.sql`.