# Rozšířený horizont plánování (96h) ## 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`): ```sql 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ší: ```python 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: ```sql -- 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 ```python # 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 ```python # 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) 1. Tabulka `ems.market_price_stats` – migrace **V022__extended_planning.sql** 2. `fn_update_market_price_stats()` – `db/routines/R__fn_extended_planning.sql`, APScheduler **14:45** (`main.py`) 3. Solver: slotová páteř `generate_series` + `COALESCE(effective_*, fn_get_predicted_price(...))` v `_load_slots` ### Fáze 3b – TUV statistika potřeby (hotovo) 1. Tabulka `ems.tuv_usage_stats` – V022 2. `fn_update_tuv_usage_stats()` – repeatable výše, job **00:45** 3. Solver: look-ahead simulace teploty + součet `hp` v okně 9 slotů (`solve_dispatch`) ### Fáze 3c – Rozšíření solveru na 96h (hotovo) 1. `HORIZON_HOURS = 96`, `slot_weight()` – váhy **1,0 / 0,7 / 0,4** v účelové funkci 2. 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) 1. Pravděpodobnostní příjezd ze statistiky 2. Deadline constraint přes celých 96h 3. 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)