190 lines
6.7 KiB
Markdown
190 lines
6.7 KiB
Markdown
# 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) |