dalsi oprava
Some checks failed
CI and deploy / migration-check (push) Failing after 17s
CI and deploy / deploy (push) Has been skipped

This commit is contained in:
Dusan Vojacek
2026-05-27 07:45:50 +02:00
parent 8c7072da07
commit 4e5de5df90
3 changed files with 144 additions and 17 deletions

View File

@@ -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-v36f"
PLANNER_BUILD_TAG = "2026-05-28-neg-prep-window-v36g"
# 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).
@@ -1221,6 +1221,20 @@ def _pre_neg_pv_export_slot_indices(
return out
def _discharge_before_first_neg_sell_ts(
slots: list[PlanningSlot],
first_neg_sell_idx: int | None,
) -> set[int]:
"""Všechny kladné-sell sloty před 1. sell<0 (funguje i v rolling bez D1 večera v horizontu)."""
if first_neg_sell_idx is None or first_neg_sell_idx <= 0:
return set()
return {
t
for t in range(first_neg_sell_idx)
if float(slots[t].sell_price) >= 0.0
}
def _evening_discharge_before_neg_day_ts(
slots: list[PlanningSlot],
neg_sell_day_meta: dict[str, Any],
@@ -1257,8 +1271,9 @@ def _neg_evening_reserve_soc_anchors(
battery: Any,
) -> list[tuple[int, float]]:
"""
Kotva SoC ≤ reserve_soc na konci večera D1 (typ. 23:45) před pražským dnem D s sell<0.
Ranní slot před 1. sell<0 nekotvíme — koliduje s prep rampou v neg okně.
Kotvy SoC ≤ reserve_soc před neg oknem:
- večer D1 (23:45) pokud je v horizontu,
- slot těsně před 1. sell<0 (rolling: ráno bez včerejška v okně).
"""
from datetime import timedelta
@@ -1287,6 +1302,14 @@ def _neg_evening_reserve_soc_anchors(
if t_eve not in seen:
out.append((t_eve, reserve_wh))
seen.add(t_eve)
if first_neg > 0:
t_pre = first_neg - 1
if (
t_pre not in seen
and float(slots[t_pre].sell_price) >= 0.0
):
out.append((t_pre, reserve_wh))
seen.add(t_pre)
return out
@@ -2112,6 +2135,10 @@ def solve_dispatch(
slots,
neg_sell_day_meta,
)
neg_evening_before_neg_ts |= _discharge_before_first_neg_sell_ts(
slots,
first_neg_sell_idx,
)
neg_evening_reserve_anchors = _neg_evening_reserve_soc_anchors(
slots,
neg_sell_day_meta,
@@ -3297,6 +3324,15 @@ def solve_dispatch(
# FVE export před sell<0 jen pokud forecast v sell<0 okně pokryje dobítí (v33).
allow_pre_neg_pv_export = t in pre_neg_pv_export_ts
pv_store_val = _pv_store_value_czk_kwh(s, min_spread)
fixed_pre_neg_pv_export = (
purchase_fixed_pre
and sell_t >= 0.0
and pv_surplus_w > 500.0
and (
first_neg_sell_idx is None
or t < first_neg_sell_idx
)
)
skip_pv_store_block = (
float(s.pv_b_forecast_w) > 0
and not getattr(grid, "block_export_on_negative_sell", False)
@@ -3312,25 +3348,18 @@ def solve_dispatch(
not purchase_fixed_pre
and sell_t >= 0
and pv_surplus_w > 500
) or (
# 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 sell_t >= 0.0
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).
) or fixed_pre_neg_pv_export
# BA81: export pole B jen při kladném sell mimo pre-neg okno (jinak jen ge_pv≤pv_b → curtail A).
fixed_pv_b_export_cap = (
purchase_fixed_pre
and float(s.pv_b_forecast_w) > 0
and not getattr(grid, "block_export_on_negative_sell", False)
and sell_t >= 0
and not fixed_pre_neg_pv_export
)
if fixed_pv_b_export_cap:
if fixed_pre_neg_pv_export:
prob += ge_pv[t] <= max(0.0, pv_surplus_w)
elif fixed_pv_b_export_cap:
if z_gen_cutoff is not None:
prob += ge_pv[t] <= float(s.pv_b_forecast_w) * (1 - z_gen_cutoff[t])
else: