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-29-evening-peak-only-export-v41"
|
||||
PLANNER_BUILD_TAG = "2026-05-29-evening-push-budget-rank-v42"
|
||||
# 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).
|
||||
@@ -1642,22 +1642,26 @@ def _slot_evening_push_profitable(
|
||||
return float(slot.sell_price) > float(charge_acquisition_czk_kwh) + float(min_spread)
|
||||
|
||||
|
||||
def _evening_push_peak_candidates(slots: list[PlanningSlot]) -> list[int]:
|
||||
"""
|
||||
Kandidáti tvrdého večerního push: sloty na **max sell** v nočním úseku
|
||||
(ne široké pásmo peak−degrad — ten rozplizňoval export do levnějších slotů).
|
||||
"""
|
||||
candidates: list[int] = []
|
||||
for seg in _night_export_window_segments(slots):
|
||||
if not seg:
|
||||
def _evening_push_segment_candidates(
|
||||
slots: list[PlanningSlot],
|
||||
seg: list[int],
|
||||
*,
|
||||
charge_acquisition_czk_kwh: float,
|
||||
min_spread: float,
|
||||
) -> list[int]:
|
||||
"""Profitable sloty v nočním úseku — výběr pořadí a strop dělá rozpočet Wh (sell desc)."""
|
||||
if not seg:
|
||||
return []
|
||||
out: list[int] = []
|
||||
for t in seg:
|
||||
if not _slot_evening_push_profitable(
|
||||
slots[t],
|
||||
charge_acquisition_czk_kwh=charge_acquisition_czk_kwh,
|
||||
min_spread=min_spread,
|
||||
):
|
||||
continue
|
||||
seg_peak = max(float(slots[t].sell_price) for t in seg)
|
||||
if seg_peak <= 0.0:
|
||||
continue
|
||||
for t in seg:
|
||||
if float(slots[t].sell_price) >= seg_peak - 1e-6:
|
||||
candidates.append(t)
|
||||
return candidates
|
||||
out.append(t)
|
||||
return out
|
||||
|
||||
|
||||
def _evening_battery_export_push_indices(
|
||||
@@ -1673,24 +1677,13 @@ def _evening_battery_export_push_indices(
|
||||
evening_start_hour: int = 17,
|
||||
) -> list[int]:
|
||||
"""
|
||||
Noční push: plný ge_bat v tolika nejdražších peak slotech (shodná max sell v úseku),
|
||||
kolik unese Wh rozpočet. Řazení sell desc; přidávat sloty dokud kumulované Wh ≤ push_budget.
|
||||
Noční push: plný ge_bat v tolika nejdražších slotách (sell desc v rámci úseku),
|
||||
kolik unese Wh rozpočet — ne jen jeden slot s exact max sell (v41).
|
||||
per_slot_discharge_wh: volající předá min(BMS, export cap) × účinnost × 0,25 h.
|
||||
"""
|
||||
_ = evening_start_hour # kompatibilita volání
|
||||
if per_slot_discharge_wh <= 0.0:
|
||||
return []
|
||||
candidates = [
|
||||
t
|
||||
for t in _evening_push_peak_candidates(slots)
|
||||
if _slot_evening_push_profitable(
|
||||
slots[t],
|
||||
charge_acquisition_czk_kwh=charge_acquisition_czk_kwh,
|
||||
min_spread=degrad_czk_kwh,
|
||||
)
|
||||
]
|
||||
if not candidates:
|
||||
return []
|
||||
push_budget_wh = _evening_push_discharge_budget_wh(
|
||||
current_soc_wh=current_soc_wh,
|
||||
min_soc_wh=min_soc_wh,
|
||||
@@ -1699,18 +1692,27 @@ def _evening_battery_export_push_indices(
|
||||
)
|
||||
if push_budget_wh < per_slot_discharge_wh * 0.5:
|
||||
return []
|
||||
ranked = sorted(
|
||||
candidates,
|
||||
key=lambda i: (float(slots[i].sell_price), -i),
|
||||
reverse=True,
|
||||
)
|
||||
out: list[int] = []
|
||||
cum_wh = 0.0
|
||||
for t in ranked:
|
||||
if cum_wh + per_slot_discharge_wh > push_budget_wh + 1e-6:
|
||||
break
|
||||
out.append(t)
|
||||
cum_wh += per_slot_discharge_wh
|
||||
remaining_wh = float(push_budget_wh)
|
||||
for seg in _night_export_window_segments(slots):
|
||||
candidates = _evening_push_segment_candidates(
|
||||
slots,
|
||||
seg,
|
||||
charge_acquisition_czk_kwh=charge_acquisition_czk_kwh,
|
||||
min_spread=degrad_czk_kwh,
|
||||
)
|
||||
if not candidates:
|
||||
continue
|
||||
ranked = sorted(
|
||||
candidates,
|
||||
key=lambda i: (float(slots[i].sell_price), -i),
|
||||
reverse=True,
|
||||
)
|
||||
for t in ranked:
|
||||
if remaining_wh + 1e-6 < per_slot_discharge_wh:
|
||||
break
|
||||
out.append(t)
|
||||
remaining_wh -= per_slot_discharge_wh
|
||||
return sorted(out)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user