fakt me to nebavi furt jsou tam chyby
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-06-06-home01-degraded-night-guard-v3"
|
||||
PLANNER_BUILD_TAG = "2026-06-06-home01-degraded-night-guard-v4"
|
||||
SOLVER_RELAX_STEPS: tuple[str, ...] = (
|
||||
"strict",
|
||||
"relaxed_expensive_import",
|
||||
@@ -1958,6 +1958,35 @@ def _degraded_relaxed_night_self_consume_indices(
|
||||
return out
|
||||
|
||||
|
||||
def _degraded_relaxed_evening_export_to_reserve_indices(
|
||||
slots: list[PlanningSlot],
|
||||
*,
|
||||
observed_soc_wh: float,
|
||||
reserve_soc_wh: float,
|
||||
first_neg_buy_idx: int | None,
|
||||
) -> set[int]:
|
||||
"""
|
||||
Nouzový solve: večer D0 smí vývoz bat k reserve_soc před dnem s buy<0 (headroom na zítra).
|
||||
Jen kalendářní večer 17–22h — po 22h už noc (dům z baterie, ne držet kvůli exportu).
|
||||
"""
|
||||
if first_neg_buy_idx is None or first_neg_buy_idx <= 0:
|
||||
return set()
|
||||
if observed_soc_wh <= float(reserve_soc_wh) + 500.0:
|
||||
return set()
|
||||
replan_day = _prague_calendar_date(slots[0])
|
||||
out: set[int] = set()
|
||||
for t, s in enumerate(slots):
|
||||
if _prague_calendar_date(s) != replan_day:
|
||||
continue
|
||||
h = _prague_hour(s)
|
||||
if h < NIGHT_EXPORT_EVENING_START_HOUR or h > 22:
|
||||
continue
|
||||
if float(s.sell_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],
|
||||
@@ -3046,6 +3075,7 @@ def solve_dispatch(
|
||||
night_self_consume_discourage_ts: set[int] = set()
|
||||
post_evening_push_night_ts: set[int] = set()
|
||||
degraded_relaxed_night_ts: set[int] = set()
|
||||
degraded_evening_export_ts: set[int] = set()
|
||||
evening_push_hysteresis_retained = False
|
||||
push_override_raw: Optional[set[int]] = None
|
||||
push_override_eff: Optional[set[int]] = None
|
||||
@@ -3215,6 +3245,15 @@ def solve_dispatch(
|
||||
battery_export_defer_pv_ts = set()
|
||||
evening_push_hard_suppressed = True
|
||||
degraded_relaxed_night_ts = _degraded_relaxed_night_self_consume_indices(slots)
|
||||
reserve_wh_degraded = float(
|
||||
getattr(battery, "reserve_soc_wh", getattr(battery, "min_soc_wh", 0.0))
|
||||
)
|
||||
degraded_evening_export_ts = _degraded_relaxed_evening_export_to_reserve_indices(
|
||||
slots,
|
||||
observed_soc_wh=observed_soc_wh,
|
||||
reserve_soc_wh=reserve_wh_degraded,
|
||||
first_neg_buy_idx=first_neg_buy_idx,
|
||||
)
|
||||
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(
|
||||
@@ -3325,6 +3364,7 @@ def solve_dispatch(
|
||||
neg_buy_charge_shortfall: list[tuple[int, pulp.LpVariable, float]] = []
|
||||
pre_neg_batt_export_shortfall: list[tuple[int, pulp.LpVariable, float]] = []
|
||||
pre_neg_buy_empty_shortfall: list[tuple[int, pulp.LpVariable, float]] = []
|
||||
degraded_evening_export_shortfall: list[tuple[int, pulp.LpVariable, float]] = []
|
||||
fixed_tariff_like = fixed_tariff_like_pre
|
||||
block_export_neg_sell = bool(getattr(grid, "block_export_on_negative_sell", False))
|
||||
if om == "AUTO":
|
||||
@@ -3373,6 +3413,15 @@ def solve_dispatch(
|
||||
continue
|
||||
sf_e = pulp.LpVariable(f"pre_neg_buy_empty_sf_{t_empty}", 0, export_cap_w)
|
||||
pre_neg_buy_empty_shortfall.append((t_empty, sf_e, export_cap_w))
|
||||
if relaxed_solver_masks and degraded_evening_export_ts:
|
||||
deg_cap = _battery_export_cap_w(battery, grid)
|
||||
for t_deg in sorted(degraded_evening_export_ts):
|
||||
sf_deg = pulp.LpVariable(
|
||||
f"deg_eve_reserve_export_{t_deg}",
|
||||
0,
|
||||
deg_cap,
|
||||
)
|
||||
degraded_evening_export_shortfall.append((t_deg, sf_deg, deg_cap))
|
||||
if not relaxed_neg_buy_charge:
|
||||
neg_buy_slot_indices = [
|
||||
t for t, s in enumerate(slots) if float(s.buy_price) < 0.0
|
||||
@@ -3681,6 +3730,10 @@ def solve_dispatch(
|
||||
sf * PEAK_EXPORT_SHORTFALL_PENALTY_CZK_KWH * INTERVAL_H / 1000.0
|
||||
for _t, sf, _cap in peak_export_shortfall
|
||||
)
|
||||
+ pulp.lpSum(
|
||||
sf * NEG_EVENING_PREP_DISCHARGE_SHORTFALL_PENALTY_CZK_KWH * INTERVAL_H / 1000.0
|
||||
for _t, sf, _cap in degraded_evening_export_shortfall
|
||||
)
|
||||
+ pulp.lpSum(
|
||||
sf * PV_CHARGE_SHORTFALL_PENALTY_CZK_KWH * INTERVAL_H / 1000.0
|
||||
for _t, sf, _cap in pv_charge_shortfall
|
||||
@@ -3817,6 +3870,8 @@ def solve_dispatch(
|
||||
prob += sf >= cap_w - ge_pv[t_sf]
|
||||
for t_sf, sf, cap_w in neg_evening_before_neg_shortfall:
|
||||
prob += sf >= cap_w - ge_bat[t_sf]
|
||||
for t_sf, sf, cap_w in degraded_evening_export_shortfall:
|
||||
prob += sf >= cap_w - ge_bat[t_sf]
|
||||
for t_sl, sl, reserve_tgt in neg_evening_reserve_soc_slack:
|
||||
prob += soc[t_sl] <= float(reserve_tgt) + sl
|
||||
preneg_export_min_soc_wh = float(min_soc_wh) + max(
|
||||
@@ -3878,13 +3933,23 @@ 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).
|
||||
# Nouzový relax: v noci jen vývoz k reserve večer D0; jinak ge_bat=0.
|
||||
if relaxed_solver_masks and not purchase_fixed_pre:
|
||||
reserve_wh_blk = float(
|
||||
getattr(battery, "reserve_soc_wh", getattr(battery, "min_soc_wh", 0.0))
|
||||
)
|
||||
for t_blk in range(T):
|
||||
if t_blk in degraded_evening_export_ts:
|
||||
continue
|
||||
if not _in_night_battery_export_window(slots[t_blk]):
|
||||
continue
|
||||
prob += ge_bat[t_blk] == 0
|
||||
prob += z_export[t_blk] == 0
|
||||
for t_ev in sorted(degraded_evening_export_ts):
|
||||
m_soc_deg = float(battery.usable_capacity_wh)
|
||||
prob += soc[t_ev] >= float(reserve_wh_blk) - m_soc_deg * (
|
||||
1 - z_export[t_ev]
|
||||
)
|
||||
# Ostatní profitable sloty: měkká shortfall penalizace (ne večerní push).
|
||||
if (
|
||||
last_pos_sell_pre_neg_buy is not None
|
||||
@@ -4500,25 +4565,37 @@ def solve_dispatch(
|
||||
expensive_import_slot = expensive_import_slot or (
|
||||
buy_t > charge_acquisition_czk_kwh + min_spread
|
||||
)
|
||||
if expensive_import_slot and t not in charge_slots and buy_t >= 0.0:
|
||||
# Strict: síť jen EV+TČ; baseload z baterie/FVE.
|
||||
# Relaxed: síť smí baseload jen mimo night_self_consume (v46).
|
||||
night_self_consume_slot = (
|
||||
om == "AUTO"
|
||||
and (
|
||||
t in night_self_consume_discourage_ts
|
||||
or t in post_evening_push_night_ts
|
||||
)
|
||||
if expensive_import_slot and buy_t >= 0.0:
|
||||
force_night_self_consume = (
|
||||
relaxed_solver_masks
|
||||
and t in degraded_relaxed_night_ts
|
||||
and t not in degraded_evening_export_ts
|
||||
)
|
||||
if relaxed_expensive_import and not night_self_consume_slot:
|
||||
prob += gi[t] <= ev_cap_t + hp[t] + float(s.load_baseline_w)
|
||||
else:
|
||||
prob += gi[t] <= ev_cap_t + hp[t]
|
||||
if (not relaxed_expensive_import or night_self_consume_slot) and om == "AUTO":
|
||||
prob += (
|
||||
bd[t] + pv_ld[t]
|
||||
>= float(s.load_baseline_w) + hp[t]
|
||||
if force_night_self_consume or (
|
||||
expensive_import_slot and t not in charge_slots
|
||||
):
|
||||
# Strict: síť jen EV+TČ; baseload z baterie/FVE.
|
||||
# Relaxed: síť smí baseload jen mimo night_self_consume (v46).
|
||||
night_self_consume_slot = (
|
||||
om == "AUTO"
|
||||
and (
|
||||
t in night_self_consume_discourage_ts
|
||||
or t in post_evening_push_night_ts
|
||||
or force_night_self_consume
|
||||
)
|
||||
)
|
||||
if relaxed_expensive_import and not night_self_consume_slot:
|
||||
prob += gi[t] <= ev_cap_t + hp[t] + float(s.load_baseline_w)
|
||||
else:
|
||||
prob += gi[t] <= ev_cap_t + hp[t]
|
||||
if (
|
||||
force_night_self_consume
|
||||
or (not relaxed_expensive_import or night_self_consume_slot)
|
||||
) and om == "AUTO":
|
||||
prob += (
|
||||
bd[t] + pv_ld[t]
|
||||
>= float(s.load_baseline_w) + hp[t]
|
||||
)
|
||||
# Anti souběžný vývoz FVE + významný import (mikrocyklus).
|
||||
if buy_t > sell_t + min_spread and pv_surplus_w > 0:
|
||||
prob += ge_pv[t] <= pv_surplus_w
|
||||
@@ -5191,6 +5268,10 @@ def solve_dispatch(
|
||||
slots[i].interval_start.isoformat()
|
||||
for i in sorted(degraded_relaxed_night_ts)
|
||||
],
|
||||
"degraded_evening_export_ts": [
|
||||
slots[i].interval_start.isoformat()
|
||||
for i in sorted(degraded_evening_export_ts)
|
||||
],
|
||||
},
|
||||
"masks": masks_snap,
|
||||
"soc_bounds": soc_bounds_snap,
|
||||
|
||||
Reference in New Issue
Block a user