zruseni fixnich konstant
This commit is contained in:
@@ -68,7 +68,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-pre-neg-batt-discharge-v23"
|
||||
PLANNER_BUILD_TAG = "2026-05-28-evening-push-dynamic-budget-v24"
|
||||
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
|
||||
@@ -935,37 +935,71 @@ def _evening_peak_export_indices(
|
||||
return out
|
||||
|
||||
|
||||
def _evening_push_discharge_budget_wh(
|
||||
*,
|
||||
current_soc_wh: float,
|
||||
min_soc_wh: float,
|
||||
soc_max_wh: float,
|
||||
discharge_slot_buffer: float,
|
||||
) -> float:
|
||||
"""
|
||||
Rozpočet Wh pro tvrdý večerní push — stejný princip jako R__063 (discharge_slot_buffer).
|
||||
Tvrdý push nesmí překročit energii nad min_soc na začátku horizontu (jinak Infeasible).
|
||||
"""
|
||||
exportable_full_wh = max(0.0, float(soc_max_wh) - float(min_soc_wh))
|
||||
available_wh = max(0.0, float(current_soc_wh) - float(min_soc_wh))
|
||||
buf = float(discharge_slot_buffer)
|
||||
if buf <= 0.0:
|
||||
return available_wh
|
||||
return min(available_wh, exportable_full_wh * buf)
|
||||
|
||||
|
||||
def _evening_battery_export_push_indices(
|
||||
slots: list[PlanningSlot],
|
||||
*,
|
||||
profitable_export_ts: set[int],
|
||||
degrad_czk_kwh: float,
|
||||
current_soc_wh: float,
|
||||
min_soc_wh: float,
|
||||
soc_max_wh: float,
|
||||
per_slot_discharge_wh: float,
|
||||
discharge_slot_buffer: float,
|
||||
evening_start_hour: int = 17,
|
||||
max_slots_per_day: int = 3,
|
||||
) -> list[int]:
|
||||
"""
|
||||
Tvrdý push ge_bat jen u několika nejlepších večerních slotů/den (profitable ∩ peak).
|
||||
Jinak součet ge_bat × z_export přes celý peak pásmo může překročit dostupné SoC → Infeasible.
|
||||
Tvrdý push ge_bat u večerních peak slotů (profitable ∩ pásmo ≥17:00 − degrad).
|
||||
Počet slotů = kolik jich unese rozpočet Wh (ne pevné top-3 / ≥2 sloty).
|
||||
"""
|
||||
if per_slot_discharge_wh <= 0.0:
|
||||
return []
|
||||
peak_ts = _evening_peak_export_indices(
|
||||
slots,
|
||||
degrad_czk_kwh=degrad_czk_kwh,
|
||||
evening_start_hour=evening_start_hour,
|
||||
)
|
||||
by_day: dict = {}
|
||||
for t in peak_ts:
|
||||
if t not in profitable_export_ts:
|
||||
continue
|
||||
d = _prague_calendar_date(slots[t])
|
||||
by_day.setdefault(d, []).append(t)
|
||||
candidates = [t for t in peak_ts if t in profitable_export_ts]
|
||||
if not candidates:
|
||||
return []
|
||||
push_budget_wh = _evening_push_discharge_budget_wh(
|
||||
current_soc_wh=current_soc_wh,
|
||||
min_soc_wh=min_soc_wh,
|
||||
soc_max_wh=soc_max_wh,
|
||||
discharge_slot_buffer=discharge_slot_buffer,
|
||||
)
|
||||
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] = []
|
||||
for d in sorted(by_day.keys()):
|
||||
ranked = sorted(
|
||||
by_day[d],
|
||||
key=lambda i: float(slots[i].sell_price),
|
||||
reverse=True,
|
||||
)
|
||||
out.extend(ranked[:max_slots_per_day])
|
||||
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
|
||||
return sorted(out)
|
||||
|
||||
|
||||
@@ -1687,6 +1721,12 @@ def solve_dispatch(
|
||||
* INTERVAL_H,
|
||||
1000.0,
|
||||
)
|
||||
per_slot_discharge_wh = max(
|
||||
float(battery.max_discharge_power_w)
|
||||
* float(battery.discharge_efficiency)
|
||||
* INTERVAL_H,
|
||||
0.0,
|
||||
)
|
||||
if om == "AUTO":
|
||||
profitable_export_ts = profitable_export_ts_pre
|
||||
export_push_w = _battery_export_cap_w(battery, grid)
|
||||
@@ -1695,17 +1735,21 @@ def solve_dispatch(
|
||||
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]
|
||||
discharge_buf = float(getattr(battery, "discharge_slot_buffer", 0) or 0)
|
||||
evening_push_ts = _evening_battery_export_push_indices(
|
||||
slots,
|
||||
profitable_export_ts=profitable_export_ts,
|
||||
degrad_czk_kwh=float(degradation_cost_effective),
|
||||
current_soc_wh=float(current_soc_wh),
|
||||
min_soc_wh=float(min_soc_wh),
|
||||
soc_max_wh=float(battery.soc_max_wh),
|
||||
per_slot_discharge_wh=per_slot_discharge_wh,
|
||||
discharge_slot_buffer=discharge_buf,
|
||||
)
|
||||
# Push jen při reálném večerním okně (≥2 sloty); 1-slot regresní testy bez tvrdého push.
|
||||
if len(evening_push_ts) >= 2:
|
||||
for t_peak in evening_push_ts:
|
||||
if t_peak not in discharge_export_slots:
|
||||
continue
|
||||
prob += ge_bat[t_peak] >= export_push_w * z_export[t_peak]
|
||||
for t_peak in evening_push_ts:
|
||||
if t_peak not in discharge_export_slots:
|
||||
continue
|
||||
prob += ge_bat[t_peak] >= export_push_w * z_export[t_peak]
|
||||
# Ostatní profitable sloty: shortfall penalizace (ne tvrdý push na celý horizont).
|
||||
if t_anchor is not None and soc_anchor_slack is not None:
|
||||
target_floor_wh = float(planner_floor_effective_wh)
|
||||
|
||||
Reference in New Issue
Block a user