dasli fix
Some checks failed
CI and deploy / migration-check (push) Failing after 23s
CI and deploy / deploy (push) Has been skipped

This commit is contained in:
Dusan Vojacek
2026-06-06 23:47:12 +02:00
parent 3ad5bec76b
commit b7903db714
3 changed files with 132 additions and 5 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-06-06-home01-late-replan-infeasible-v2"
PLANNER_BUILD_TAG = "2026-06-06-home01-degraded-night-guard-v3"
SOLVER_RELAX_STEPS: tuple[str, ...] = (
"strict",
"relaxed_expensive_import",
@@ -1940,6 +1940,24 @@ def _evening_push_segment_candidates(
return out
def _degraded_relaxed_night_self_consume_indices(
slots: list[PlanningSlot],
) -> set[int]:
"""
relaxed_solver_masks: celé noční okno — dům z baterie (až min_soc), ne import za spot buy.
"""
out: set[int] = set()
for t, s in enumerate(slots):
if not _in_night_battery_export_window(s):
continue
if float(s.load_baseline_w) <= 0:
continue
if float(s.buy_price) < 0.0:
continue
out.add(t)
return out
def _post_evening_push_night_self_consume_indices(
slots: list[PlanningSlot],
evening_push_ts: set[int],
@@ -3027,6 +3045,7 @@ def solve_dispatch(
evening_early_export_penalty_ts: set[int] = set()
night_self_consume_discourage_ts: set[int] = set()
post_evening_push_night_ts: set[int] = set()
degraded_relaxed_night_ts: set[int] = set()
evening_push_hysteresis_retained = False
push_override_raw: Optional[set[int]] = None
push_override_eff: Optional[set[int]] = None
@@ -3195,6 +3214,9 @@ def solve_dispatch(
evening_early_export_penalty_ts = set()
battery_export_defer_pv_ts = set()
evening_push_hard_suppressed = True
degraded_relaxed_night_ts = _degraded_relaxed_night_self_consume_indices(slots)
night_self_consume_discourage_ts |= degraded_relaxed_night_ts
post_evening_push_night_ts |= degraded_relaxed_night_ts
pre_neg_buy_soc_ceiling_wh = _pre_neg_buy_soc_ceiling_wh(
slots,
first_neg_buy_idx=first_neg_buy_idx,
@@ -3307,6 +3329,8 @@ def solve_dispatch(
block_export_neg_sell = bool(getattr(grid, "block_export_on_negative_sell", False))
if om == "AUTO":
for t in range(T):
if relaxed_solver_masks and not purchase_fixed_pre:
continue
if t not in discharge_export_slots:
continue
if t in evening_push_ts:
@@ -3854,6 +3878,13 @@ def solve_dispatch(
continue
prob += ge_bat[t_pv] == 0
prob += z_export[t_pv] == 0
# Nouzový relax: spot v noci neexportovat baterii za ~2,5 Kč (žádný tvrdý evening dump).
if relaxed_solver_masks and not purchase_fixed_pre:
for t_blk in range(T):
if not _in_night_battery_export_window(slots[t_blk]):
continue
prob += ge_bat[t_blk] == 0
prob += z_export[t_blk] == 0
# Ostatní profitable sloty: měkká shortfall penalizace (ne večerní push).
if (
last_pos_sell_pre_neg_buy is not None
@@ -4195,6 +4226,7 @@ def solve_dispatch(
else:
export_soc_floor_t = float(arb_base_wh)
# Večerní exportní slot: podlaha jen min_soc (ne safety ramp), aby šlo vybít při z_export=1.
# Nouzový relaxed_solver_masks: export nikdy pod reserve_soc (ekonomická podlaha).
if (
om == "AUTO"
and t in discharge_export_slots
@@ -4202,8 +4234,14 @@ def solve_dispatch(
t in evening_peak_export_ts
or t in neg_evening_push_ts
)
and not relaxed_solver_masks
):
export_soc_floor_t = float(min_soc_wh)
elif relaxed_solver_masks and om == "AUTO":
export_soc_floor_t = max(
export_soc_floor_t,
float(getattr(battery, "reserve_soc_wh", arb_base_wh)),
)
# Safety export floor: v běžných (ne high-sell) slotech nevybít exportem energii potřebnou pro
# robustnost/noční baseload. Použije se pouze pokud je safety target v SQL vyplněný.
tgt_s = slots[t].safety_soc_target_wh if daytime_en else None
@@ -5149,6 +5187,10 @@ def solve_dispatch(
slots[i].interval_start.isoformat()
for i in sorted(night_self_consume_discourage_ts)
],
"degraded_relaxed_night_ts": [
slots[i].interval_start.isoformat()
for i in sorted(degraded_relaxed_night_ts)
],
},
"masks": masks_snap,
"soc_bounds": soc_bounds_snap,