7.2 KiB
Plánování: historie 96h horizontu a budoucí rozšíření
Historické (do 2026-04): produkční solver používal horizont až 96 h s predikcí cen za hranicí OTE a váhami 1,0 / 0,7 / 0,4 v účelové funkci. Od 2026-04 je horizont dynamický pouze z OTE (
ems.fn_planning_horizon_end/fn_last_effective_ote), bez predikovaných cen v LP a s terminal SoC shadow price; limity stropu a min. délky jsou v SQL, ne v env. Níže zůstává popis původního návrhu pro referenci a budoucí rozšíření (např. fáze 3e počasí).
Motivace
OTE publikuje ceny max 36h dopředu. FVE forecast je dostupný na 7 dní. Rozšířením horizontu solver vidí vzdálené příležitosti (záporné ceny, levná okna) a může optimálně připravit baterii, TUV zásobník a EV nabíjení.
Klíčový princip: solver nepotřebuje explicitní "šetři baterii před zápornou cenou" constraint. Pokud dostane správné (odhadované) ceny pro celých 96h, sám pozná že je výhodnější počkat na zápornou cenu než vybíjet dnes za průměrnou.
Datové zdroje pro predikci cen za horizont OTE
Vrstva 1 – Sezónní průměr z historických OTE dat
Tabulka ems.market_price_stats (analogie consumption_baseline_stats):
SELECT
EXTRACT(DOW FROM interval_start AT TIME ZONE 'Europe/Prague') AS dow,
EXTRACT(HOUR FROM interval_start AT TIME ZONE 'Europe/Prague') AS hour,
AVG(buy_raw_price_czk_kwh) AS avg_price,
STDDEV(buy_raw_price_czk_kwh) AS stddev_price,
PERCENTILE_CONT(0.25) WITHIN GROUP (
ORDER BY buy_raw_price_czk_kwh) AS p25,
PERCENTILE_CONT(0.75) WITHIN GROUP (
ORDER BY buy_raw_price_czk_kwh) AS p75
FROM ems.market_interval_price
WHERE market_source IN ('OTE_CZ', 'OTE_CZ_DAM')
AND interval_start >= now() - INTERVAL '6 months'
GROUP BY dow, hour
Plnit denně po importu OTE. Min. 3 měsíce dat pro smysluplné průměry.
Vrstva 2 – Korekce počasím (proxy)
Záporné/nízké ceny korelují s vysokou FVE výrobou v celé síti CZ.
predicted_irradiance > historical_avg * 1.3 → cena * 0.70 (30% sleva)
predicted_irradiance < historical_avg * 0.5 → cena * 1.20 (20% přirážka)
Korelaci ověřit po 3+ měsících dat. Zatím použít konzervativní korekci ±15%.
Kombinovaná predikce s uncertainty margin
predicted_price[t] = seasonal_avg[dow, hour]
× weather_correction_factor[t]
× (1 ± uncertainty_margin[t])
uncertainty_margin roste s horizontem:
0-36h: 0% (přesné OTE ceny, žádná predikce)
36-72h: 20%
72-96h: 35%
Uncertainty weighting v objective function
Vzdálenější sloty mají nižší váhu – solver je konzervativnější:
def slot_weight(t: int, now_index: int) -> float:
hours_ahead = (t - now_index) * 0.25
if hours_ahead <= 36: return 1.0 # přesné OTE ceny
if hours_ahead <= 72: return 0.7 # predikce, střední jistota
return 0.4 # predikce, nízká jistota
# V objective function:
prob += pulp.lpSum(
slot_weight(t, now_index) * (
gi[t] * slots[t].buy_price * INTERVAL_H / 1000
- ge[t] * slots[t].sell_price * INTERVAL_H / 1000
+ ...
)
for t in range(T)
)
TUV predikce potřeby
Princip
TUV zásobník drží teplo ~24h. Solver může ohřát vodu v levném okně před očekávanou spotřebou. Potřebuje vědět:
- Aktuální teplotu zásobníku (z telemetrie)
- Kdy typicky klesá teplota (statistika per DOW+hodina)
- Minimální přijatelnou teplotu (tuv_min_temp_c)
Tabulka ems.tuv_usage_stats
Analogie consumption_baseline_stats pro TUV zásobník:
-- Průměrný pokles teploty zásobníku per DOW+hodina
-- (záporné = zásobník se ochladil, kladné = TČ ohřívalo)
SELECT
EXTRACT(DOW FROM measured_at AT TIME ZONE 'Europe/Prague') AS dow,
EXTRACT(HOUR FROM measured_at AT TIME ZONE 'Europe/Prague') AS hour,
AVG(temp_delta_c) AS avg_temp_delta, -- průměrná změna za hodinu
STDDEV(temp_delta_c) AS stddev_temp_delta
FROM (
SELECT
measured_at,
tuv_tank_temp_c - LAG(tuv_tank_temp_c) OVER (
PARTITION BY site_id ORDER BY measured_at
) AS temp_delta_c
FROM ems.telemetry_heat_pump
WHERE site_id = $1
AND measured_at >= now() - INTERVAL '30 days'
) sub
WHERE temp_delta_c IS NOT NULL
AND ABS(temp_delta_c) < 5 -- filtruj extrémní skoky (start TČ)
GROUP BY dow, hour
Použití v solveru
# Pro každý slot t zjisti predikovanou teplotu zásobníku:
tuv_predicted[t] = tuv_current + SUM(avg_temp_delta[dow, hour]
for slots before t)
# Pokud tuv_predicted[t] < tuv_min_temp + safety_margin:
# → solver musí naplánovat ohřev před tímto slotem
# → heat_pump_enabled[t-N] = True (kde N = počet slotů potřebných pro ohřev)
# Potřebný čas ohřevu (orientační):
# delta_temp = tuv_target - tuv_current
# time_h = delta_temp × volume_l × 1.163 / (cop × hp_power_w / 1000)
EV v rozšířeném horizontu
Tesla (s API – fáze 2)
Vstup: aktuální SoC z Tesla API, nastavený deadline uživatelem
Solver: deadline constraint přes celých 96h
nabij nejlevněji v rámci časového okna
Zoe (bez API)
Vstup: ev_arrival_stats (statistika příjezdů per DOW+hodina)
energy_delivered_wh z aktuální session (odhad SoC)
Solver: soft constraint – pravděpodobnost příjezdu jako váha
pokud P(příjezd v slot t) > 60%: rezervuj nabíjecí kapacitu
Predikce příjezdu v solveru
# Pro každý slot t kde P(příjezd) > 0.4:
arrival_prob = ev_arrival_stats[dow, hour] / total_arrivals_this_dow
# Soft constraint (ne hard – auto nemusí přijet):
# Přidej "expected EV consumption" jako součást load_baseline
ev_expected_w[t] = arrival_prob * ev_charge_power_typical
Implementační plán
Fáze 3a – Historické průměry cen (hotovo)
- Tabulka
ems.market_price_stats– migrace V022__extended_planning.sql fn_update_market_price_stats()–db/routines/R__018_fn_extended_planning.sql, APScheduler 14:45 (main.py)- Solver: slotová páteř
generate_series+COALESCE(effective_*, fn_get_predicted_price(...))v_load_slots
Fáze 3b – TUV statistika potřeby (hotovo)
- Tabulka
ems.tuv_usage_stats– V022 fn_update_tuv_usage_stats()– repeatable výše, job 00:45- Solver: look-ahead simulace teploty + součet
hpv okně 9 slotů (solve_dispatch)
Fáze 3c – Rozšíření solveru na 96h (hotovo)
HORIZON_HOURS = 96,slot_weight()– váhy 1,0 / 0,7 / 0,4 v účelové funkci- Příznak
PlanningSlot.is_predicted_price(z SQL(ep.effective_buy IS NULL))
Fáze 3d – EV v rozšířeném horizontu (závisí na Tesla API)
- Pravděpodobnostní příjezd ze statistiky
- Deadline constraint přes celých 96h
- Tesla API integrace
Fáze 3e – Korekce cen počasím
Po nasbírání 3+ měsíců korelačních dat rozšířit fn_get_predicted_price (viz vrstva 2 výše).
Prerekvizity
- Min. 3 měsíce historických OTE dat pro smysluplné průměry
- Min. 1 měsíc telemetrie TUV pro tuv_usage_stats
- Stabilní základní provoz (Modbus zápis, telemetrie)