uprava vypoctu slotu
This commit is contained in:
@@ -335,6 +335,9 @@ class PlanningSlot:
|
||||
future_avoided_buy_czk_kwh: float | None = None
|
||||
future_sell_opportunity_czk_kwh: float | None = None
|
||||
is_daytime_pv_surplus_slot: bool = False
|
||||
#: Vážená nákupní / opportunity cena zásoby před prvním exportním oknem (SQL odhad z masek).
|
||||
charge_acquisition_buy_czk_kwh: float | None = None
|
||||
charge_acquisition_cutoff_at: datetime | None = None
|
||||
|
||||
|
||||
# Lookahead pro relax spodní meze SoC: až 36 h od indexu slotu (pevné OTE ceny v horizontu).
|
||||
@@ -759,6 +762,13 @@ def solve_dispatch(
|
||||
avg_buy_terminal * terminal_factor / 1000.0
|
||||
)
|
||||
|
||||
charge_acq_raw = getattr(slots[0], "charge_acquisition_buy_czk_kwh", None)
|
||||
charge_acquisition_czk_kwh = (
|
||||
float(charge_acq_raw)
|
||||
if charge_acq_raw is not None
|
||||
else min(float(s.buy_price) for s in slots)
|
||||
)
|
||||
|
||||
# Kotva: poslední slot před prvním sell<0 by měl končit u planner floor (pokud relaxace existuje).
|
||||
# Slack penalizujeme v objective; samotné omezení přidáme až po definici soc.
|
||||
first_neg_sell_idx = next((i for i, s in enumerate(slots) if float(s.sell_price) < 0), None)
|
||||
@@ -813,6 +823,8 @@ def solve_dispatch(
|
||||
commit_lp.append((t, cv, cap_prev))
|
||||
|
||||
# --- Účelová funkce (jen OTE sloty; terminal SoC shadow price na konci horizontu) ---
|
||||
# Arbitráž baterie: ge_bat v exportních slotech + charge_acquisition (SQL, před 1. exportem).
|
||||
# Viz docs/04-modules/planning-arbitrage-accounting.md — ne stejnoslotové buy/sell.
|
||||
prob += (
|
||||
pulp.lpSum(
|
||||
gi[t] * slots[t].buy_price * INTERVAL_H / 1000
|
||||
@@ -829,6 +841,11 @@ def solve_dispatch(
|
||||
)
|
||||
+ gi_over[t] * IMPORT_OVER_BREAKER_PENALTY_CZK_KWH * INTERVAL_H / 1000
|
||||
+ 0.5 * (bc[t] + bd[t]) * degradation_cost_effective * INTERVAL_H / 1000
|
||||
+ (
|
||||
ge_bat[t] * charge_acquisition_czk_kwh * INTERVAL_H / 1000
|
||||
if om == "AUTO" and t in discharge_export_slots
|
||||
else 0
|
||||
)
|
||||
+ pulp.lpSum(
|
||||
ev_direct[e][t] * slots[t].buy_price * INTERVAL_H / 1000
|
||||
+ ev_via_bat[e][t] * slots[t].buy_price * EV_ROUNDTRIP_FACTOR * INTERVAL_H / 1000
|
||||
@@ -1309,6 +1326,12 @@ def solve_dispatch(
|
||||
"planner_daytime_charge_target_enabled": daytime_en,
|
||||
"planner_charge_commitment_penalty_czk_kwh": float(commit_pen),
|
||||
},
|
||||
"charge_acquisition_buy_czk_kwh": charge_acquisition_czk_kwh,
|
||||
"charge_acquisition_cutoff_at": (
|
||||
slots[0].charge_acquisition_cutoff_at.isoformat()
|
||||
if slots[0].charge_acquisition_cutoff_at is not None
|
||||
else None
|
||||
),
|
||||
},
|
||||
"masks": masks_snap,
|
||||
"soc_bounds": soc_bounds_snap,
|
||||
@@ -1872,7 +1895,8 @@ async def _load_slots(
|
||||
ev1_connected, ev2_connected, allow_charge, allow_discharge_export,
|
||||
night_baseload_target_wh, night_baseload_buffer_wh, safety_soc_target_wh,
|
||||
future_avoided_buy_czk_kwh, future_sell_opportunity_czk_kwh,
|
||||
is_daytime_pv_surplus_slot
|
||||
is_daytime_pv_surplus_slot,
|
||||
charge_acquisition_buy_czk_kwh, charge_acquisition_cutoff_at
|
||||
from ems.fn_load_planning_slots_full(
|
||||
$1::int, $2::timestamptz, $3::timestamptz, $4::numeric
|
||||
)
|
||||
@@ -1906,6 +1930,10 @@ async def _load_slots(
|
||||
d, "future_sell_opportunity_czk_kwh"
|
||||
),
|
||||
is_daytime_pv_surplus_slot=bool(d.get("is_daytime_pv_surplus_slot", False)),
|
||||
charge_acquisition_buy_czk_kwh=_slot_float_nullable(
|
||||
d, "charge_acquisition_buy_czk_kwh"
|
||||
),
|
||||
charge_acquisition_cutoff_at=d.get("charge_acquisition_cutoff_at"),
|
||||
)
|
||||
)
|
||||
if not out:
|
||||
|
||||
Reference in New Issue
Block a user