predvybiti baterky
This commit is contained in:
@@ -66,7 +66,9 @@ EXTREME_BUY_DUMP_PREWINDOW_SLOTS = 12
|
||||
NEG_SELL_BAT_DUMP_SHORTFALL_PENALTY_CZK_KWH = 80.0
|
||||
NEG_BUY_CHARGE_SHORTFALL_PENALTY_CZK_KWH = 100.0
|
||||
PRE_NEG_CHARGE_PENALTY_CZK_KWH = 400.0
|
||||
PLANNER_BUILD_TAG = "2026-05-28-buy-sell-split-v22b"
|
||||
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-pre-neg-batt-discharge-v23"
|
||||
CORRECTION_WINDOW_H = 1 # hodina zpět pro výpočet korekčního faktoru
|
||||
CORRECTION_MIN_CLAMP = 0.5 # spodní limit korekčního faktoru
|
||||
CORRECTION_MAX_CLAMP = 1.5 # horní limit korekčního faktoru
|
||||
@@ -877,6 +879,38 @@ def _morning_pre_neg_export_indices(
|
||||
return out
|
||||
|
||||
|
||||
def _pre_neg_buy_discharge_indices(
|
||||
slots: list[PlanningSlot],
|
||||
first_neg_buy_idx: int | None,
|
||||
*,
|
||||
charge_acquisition_czk_kwh: float,
|
||||
min_spread: float,
|
||||
fixed_tariff: bool,
|
||||
) -> set[int]:
|
||||
"""
|
||||
Sloty před prvním buy<0: výboj baterie do sítě při kladném sell (včetně noci).
|
||||
Bez rozšíření discharge_export_slots (v19b — jinak w_arb → Infeasible).
|
||||
"""
|
||||
if first_neg_buy_idx is None or first_neg_buy_idx <= 0:
|
||||
return set()
|
||||
out: set[int] = set()
|
||||
for i in range(first_neg_buy_idx):
|
||||
s = slots[i]
|
||||
if float(s.buy_price) < 0.0:
|
||||
continue
|
||||
if float(s.sell_price) < PRE_NEG_BATT_EXPORT_MIN_SELL_CZK_KWH:
|
||||
continue
|
||||
if not _slot_profitable_battery_export(
|
||||
s,
|
||||
charge_acquisition_czk_kwh=charge_acquisition_czk_kwh,
|
||||
min_spread=min_spread,
|
||||
fixed_tariff=fixed_tariff,
|
||||
):
|
||||
continue
|
||||
out.add(i)
|
||||
return out
|
||||
|
||||
|
||||
def _evening_peak_export_indices(
|
||||
slots: list[PlanningSlot],
|
||||
*,
|
||||
@@ -1295,6 +1329,15 @@ def solve_dispatch(
|
||||
min_spread_pre = float(degradation_cost_effective)
|
||||
purchase_fixed_pre = _purchase_pricing_fixed(grid)
|
||||
fixed_tariff_like_pre = purchase_fixed_pre or _horizon_fixed_tariff_like(slots)
|
||||
pre_neg_buy_discharge_ts: set[int] = set()
|
||||
if om == "AUTO" and first_neg_buy_idx is not None and first_neg_buy_idx > 0:
|
||||
pre_neg_buy_discharge_ts = _pre_neg_buy_discharge_indices(
|
||||
slots,
|
||||
first_neg_buy_idx,
|
||||
charge_acquisition_czk_kwh=charge_acquisition_czk_kwh,
|
||||
min_spread=min_spread_pre,
|
||||
fixed_tariff=fixed_tariff_like_pre,
|
||||
)
|
||||
neg_sell_bat_dump_slots = _neg_sell_bat_dump_slots(
|
||||
slots,
|
||||
operating_mode=om,
|
||||
@@ -1392,6 +1435,7 @@ def solve_dispatch(
|
||||
neg_sell_bat_dump_shortfall: list[tuple[int, pulp.LpVariable, float]] = []
|
||||
neg_sell_soc_underfill: list[tuple[int, pulp.LpVariable]] = []
|
||||
neg_buy_charge_shortfall: list[tuple[int, pulp.LpVariable, float]] = []
|
||||
pre_neg_batt_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":
|
||||
@@ -1411,6 +1455,10 @@ def solve_dispatch(
|
||||
))
|
||||
sf = pulp.LpVariable(f"export_shortfall_{t}", 0, cap_w)
|
||||
peak_export_shortfall.append((t, sf, cap_w))
|
||||
export_cap_w = _battery_export_cap_w(battery, grid)
|
||||
for t_pnd in sorted(pre_neg_buy_discharge_ts):
|
||||
sf_pnd = pulp.LpVariable(f"pre_neg_bat_export_sf_{t_pnd}", 0, export_cap_w)
|
||||
pre_neg_batt_export_shortfall.append((t_pnd, sf_pnd, export_cap_w))
|
||||
if not relaxed_neg_buy_charge:
|
||||
neg_buy_slot_indices = [
|
||||
t for t, s in enumerate(slots) if float(s.buy_price) < 0.0
|
||||
@@ -1597,6 +1645,10 @@ def solve_dispatch(
|
||||
sf * NEG_BUY_CHARGE_SHORTFALL_PENALTY_CZK_KWH * INTERVAL_H / 1000.0
|
||||
for _t, sf, _cap in neg_buy_charge_shortfall
|
||||
)
|
||||
+ pulp.lpSum(
|
||||
sf * PRE_NEG_BATT_EXPORT_SHORTFALL_PENALTY_CZK_KWH * INTERVAL_H / 1000.0
|
||||
for _t, sf, _cap in pre_neg_batt_export_shortfall
|
||||
)
|
||||
+ pulp.lpSum(
|
||||
(bc_pv[t] + bc_gi[t])
|
||||
* PRE_NEG_CHARGE_PENALTY_CZK_KWH
|
||||
@@ -1627,6 +1679,8 @@ def solve_dispatch(
|
||||
prob += us >= float(battery.soc_max_wh) - soc[t_us]
|
||||
for t_sf, sf, cap_w in neg_buy_charge_shortfall:
|
||||
prob += sf >= cap_w - (bc_gi[t_sf] + bc_pv[t_sf])
|
||||
for t_sf, sf, cap_w in pre_neg_batt_export_shortfall:
|
||||
prob += sf >= cap_w - ge_bat[t_sf]
|
||||
preneg_export_min_soc_wh = float(min_soc_wh) + max(
|
||||
float(battery.max_discharge_power_w)
|
||||
* float(battery.discharge_efficiency)
|
||||
@@ -1639,6 +1693,8 @@ def solve_dispatch(
|
||||
for t_peak in morning_pre_neg_export_ts:
|
||||
if t_peak in profitable_export_ts:
|
||||
prob += ge_bat[t_peak] >= export_push_w * z_export[t_peak]
|
||||
for t_pnd in pre_neg_buy_discharge_ts:
|
||||
prob += ge_bat[t_pnd] >= export_push_w * z_export[t_pnd]
|
||||
evening_push_ts = _evening_battery_export_push_indices(
|
||||
slots,
|
||||
profitable_export_ts=profitable_export_ts,
|
||||
@@ -1903,6 +1959,8 @@ def solve_dispatch(
|
||||
and floor_pct is not None
|
||||
):
|
||||
export_soc_floor_t = float(planner_floor_effective_wh)
|
||||
elif om == "AUTO" and t in pre_neg_buy_discharge_ts:
|
||||
export_soc_floor_t = float(min_soc_wh)
|
||||
elif (
|
||||
om == "AUTO"
|
||||
and t in morning_pre_neg_export_ts
|
||||
@@ -2004,7 +2062,11 @@ def solve_dispatch(
|
||||
prob += bc_pv[t] == 0
|
||||
else:
|
||||
prob += bc_pv[t] <= float(pv_surplus_w)
|
||||
if t not in discharge_export_slots and t not in neg_sell_bat_dump_slots:
|
||||
if (
|
||||
t not in discharge_export_slots
|
||||
and t not in neg_sell_bat_dump_slots
|
||||
and t not in pre_neg_buy_discharge_ts
|
||||
):
|
||||
prob += ge_bat[t] == 0
|
||||
prob += z_export[t] == 0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user