11 KiB
Modul: Forecast (Predikce výroby FVE)
Co modul dělá
- Stahuje meteorologická data (irradiance, teplota) pro každé FVE pole zvlášť
- Vypočítává predikovaný výkon v 15min intervalech
- Ukládá výsledek per
pv_array_id+run_id - Predikce se spouští každé 2 hodiny v
:05a ručně přes API. Plánovač používá poslední dostupné uložené forecasty; forecast nespouští implicitně před každým plánovacím během.
Kanonický forecast pro plánování (single source of truth)
Pro plánování (solver) a UI tabulky slotů je kanonický výkon FVE počítaný v DB funkcí:
ems.fn_forecast_pv_slots_range_canonical_ab(...)
Ta kombinuje dvě korekce do jedné řady:
- delta-korekci per
pv_array_id(zems.fn_pv_forecast_delta_profile) - rolling multiplikativní faktor vs telemetrie (z
ems.fn_pv_forecast_correction_factor) s lineárním decay do 1.0
Výstup je rozdělený na PV‑A (controllable=true, curtailment v LP) a PV‑B (controllable=false).
FVE pole na první instalaci (home-01)
| Pole | Výkon | Azimut | Sklon | Střídač | Řízení |
|---|---|---|---|---|---|
| A | 10 kWp | 184° | 22° | Deye 20kW | řídíme |
| B | 10 kWp | 184° | 35° | Ongridový | autonomní, predikujeme jako samostatné pole |
Aktuální implementace: Forecast služba počítá všechna FVE pole lokality, která mají vyplněný
azimuth_degatilt_deg; plánovač pracuje odděleně spv_a_forecast_wipv_b_forecast_w.
Azimut je uložen v kompasové / pvlib konvenci:
0=N,90=E,180=S,270=W.
Zdroj meteorologických dat
Primární: Open-Meteo (open-meteo.com)
- Zdarma pro nekomerční použití, API bez registrace
- Poskytuje GHI (Global Horizontal Irradiance), DNI, teplotu, oblačnost
- Historická data + forecast na 7–16 dní dopředu
- 15min granularita nativně ✓
Endpoint:
GET https://api.open-meteo.com/v1/forecast
?latitude={lat}
&longitude={lon}
&minutely_15=direct_normal_irradiance,diffuse_radiation,shortwave_radiation,temperature_2m
&timezone=auto
&forecast_days=7
Záložní / budoucí: Solcast
- Přesnější pro FVE, ale placený
- Podporuje per-array predikci s azimutem a sklonem přímo
- Zatím neimplementujeme, architektura to umožňuje přes
forecast_source
Výpočet výkonu z irradiance
Implementace používá pvlib a model POA irradiance haydavies:
poa_global = pvlib.irradiance.get_total_irradiance(
surface_tilt=tilt_deg,
surface_azimuth=azimuth_deg, # 0=N, 90=E, 180=S, 270=W
solar_zenith=solar_pos["apparent_zenith"],
solar_azimuth=solar_pos["azimuth"],
dni=dni,
ghi=ghi,
dhi=dhi,
dni_extra=dni_extra,
model="haydavies",
)["poa_global"].fillna(0).clip(lower=0)
area_m2 = nominal_power_wp / (1000.0 * 0.20)
power_w = (poa_global * area_m2 * 0.20 * shading_factor).clip(
lower=0,
upper=nominal_power_wp * 1.1,
)
Kdo spouští predikci
Python service: forecast_service
Kdy se spouští
| Trigger | Čas | Popis |
|---|---|---|
| Scheduled (cron) | každé 2 hodiny v :05 |
Průběžný refresh forecastu pro všechny aktivní site |
| Manual trigger | na vyžádání | POST /api/v1/sites/{site_id}/forecast/run |
Implementované provozní změny (2026-03)
- Forecast horizont je konfigurovatelný přes
open_meteo_forecast_days. - Runtime guard: hodnota se clampuje do rozmezí
2..16. - Default je
7dní. - Endpoint
GET /api/v1/sites/{site_id}/forecast/pv?date=YYYY-MM-DDvrací vždy posledníokrun per(interval_start, pv_array_id)(DISTINCT ON), takže UI nevidí duplikáty z historických běhů. - Kalibrace delty:
GET /api/v1/sites/{site_id}/forecast/pv-delta-profile?from=…&to=…vrací JSON zems.fn_pv_forecast_delta_profile(deltas,deltas_by_array,delta_learn_min_tszems.site_pv_forecast_calibration). Volitelné query parametry:half_life_days,threshold_w,top_n_days,non_top_day_factor,day_weight_gamma(NULL u numerických přepsání = hodnota z kalibrační tabulky / default funkce). - Cache delty (V079): sloupce
delta_profile_cache/delta_profile_cached_atvsite_pv_forecast_calibration; refreshems.fn_refresh_site_pv_delta_profile_cache(site_id)pofn_fill_forecast_accuracya po PATCH kalibrace; čtení pro plánování/UI přesfn_pv_forecast_delta_profile_cached(TTL 30 min, pak fallback na plný přepočet). - Úprava kalibrace z API:
PATCH /api/v1/sites/{site_id}/configuration/pv-forecast-calibrations JSON tělem (částečný update); odpověď je aktuální řádek kalibrace. Souhrn konfigurace vGET …/configurationobsahuje klíčpv_forecast_calibration. - Telemetrie pro učení delty:
telemetry_collectorpři Modbus poll čte reg. 145 a 178;fn_telemetry_inverter_sampleukládáis_export_limited/pv_derating_flags(bity 1 = solar sell off, 2 = GEN/MI cut-off aktivní dle masky(reg178 & 3) == 3).fn_fill_forecast_accuracysloty s těmito signály označítelemetry_derating.
Logika běhu predikce
def run_forecast(site_id: int):
site = db.get_site(site_id)
arrays = db.get_pv_arrays_with_azimuth_and_tilt(site_id)
for array in arrays:
# 1. Stáhnout meteorologická data
weather = open_meteo_client.fetch(
lat=site.lat, lon=site.lon,
forecast_days=clamp(OPEN_METEO_FORECAST_DAYS, 2, 16)
)
# 2. Vytvořit forecast_pv_run
run = db.create_forecast_run(
site_id=site_id,
pv_array_id=array.id,
forecast_source="open_meteo",
horizon_start=today_00,
horizon_end=today_end + horizon_days
)
# 3. Vypočítat a uložit intervaly (15min)
intervals = []
for slot in weather.slots_15min:
power = calculate_pv_power(
irradiance_wm2=slot.shortwave_radiation,
temp_c=slot.temperature_2m,
nominal_power_wp=array.nominal_power_wp,
azimuth_deg=array.azimuth_deg,
tilt_deg=array.tilt_deg,
shading_factor=array.shading_factor
)
intervals.append(ForecastInterval(
run_id=run.id,
pv_array_id=array.id,
interval_start=slot.time,
power_w=power,
irradiance_wm2=slot.shortwave_radiation,
temp_c=slot.temperature_2m
))
db.insert_forecast_intervals(intervals)
DB struktura
Viz 03-data-model.md:
forecast_pv_run– každý běh predikceforecast_pv_interval– 15min výsledky per pole a běh
Tracking přesnosti forecastu
ems.forecast_accuracy– pro každý úspěšnýforecast_pv_runa každý 15min slot ukládá predikovaný výkon, čas vzniku predikce, lead time (hodiny před začátkem slotu), později doplněnou skutečnost z telemetrie a odchylku (error_w,error_pct). Záznamy se uchovávají trvale (včetně všech historických běhů vforecast_pv_run/forecast_pv_interval– ty se nemazají).ems.fn_fill_forecast_accuracy(site_id, lookback_hours)– inkrementálně vloží nebo aktualizuje řádky zforecast_pv_interval+ run metadata a dopočteactual_power_wjako průměr 1min telemetrie ve slotu (pole B:gen_port_power_w, pole A:pv1_power_w+pv2_power_w). Volat každých 15 minut (např. spolu s audit fillerem); parametrlookback_hoursomezuje okno zpětného zpracování (např. 48 h běžně, větší hodnota pro jednorázový backfill).ems.vw_forecast_accuracy_by_lead_time– agregace přesnosti podle bucketů lead time (0–6 h, …, 48 h+); noční sloty s nízkou výrobou (actual_power_w≤ 100 W) se v metrikách typicky vynechávají.ems.vw_forecast_accuracy_daily– denní součty forecast vs actual v kWh (Praha kalendářní den) a relativní odchylka dne.- Po 4+ týdnech dat lze statistiky použít pro kalibraci
safety_factor(nebo obdobných parametrů) v solveru – viz plánovací modul.
Operace SQL: mazání řádků PV forecastu za den (provozní výjimka)
Projekt standardně nemá mazat forecast_pv_interval / forecast_pv_run, aby zůstala historie pro přesnost. Když záměrně promāžeš den (např. před regenerací výstupu předpovědi), použij ems.fn_delete_forecast_pv_prague_calendar_day(p_day date, p_site_id int DEFAULT NULL) (db/routines/R__086_fn_forecast_pv_prague_day_ops.sql). Hranice dne jsou Europe/Prague půlnoc (ne timezone lokality); p_site_id NULL = všechny lokality.
Příklad: select * from ems.fn_delete_forecast_pv_prague_calendar_day('2026-05-02'::date, 2);
Odstranění jde přes páry forecast_accuracy → řádek forecast_pv_interval→ prázdné forecast_pv_run, které měly jen interval v mazané množině (stejně jako dříve skript).
Na bazální spotřebu (consumption_baseline_stats) to nesahá → ems.fn_rebuild_consumption_baseline_stats v R__085.
Referenční dny při učení delty („hezky svítily“ zpětně)
Profil ems.fn_pv_forecast_delta_profile se nemerguje jako samostatný soubor — při každém načítání (fn_load_planning_slots_full / API) znovu agreguje chybu z forecast_accuracy v okně (lookback/exponenta half_life, rank top dnů odvozený od energie a hladkosti dnů).
„Zapošto“ k existující logice:
- Ověř, že máš
forecast_accuracypro ty dny (po skutečnosti slotů zactual_power_wz telemetrie) — obvykle díkyfn_fill_forecast_accuracy. - Založ řádek v
ems.site_pv_forecast_reference_day(site_id, day_local, notes).day_localmusí sedět na(interval_start AT TIME ZONE site.timezone)::dateslovní hodiny lokality (typicky datum v Praze jako u home-01Europe/Prague). - (Volitelně) nastav
site_pv_forecast_calibration.reference_day_weight_mult(NULL = výchozí násobitel 3, minimum v kódu 1). Ostatní dny berou jako dosud jejich váhy(top_n, non_top_day_factor, decay…)současně — „referenční den“ je multiplikátor navíc, nesamostatný paralelní model.
Hromadně: ems.fn_pv_forecast_sync_reference_days(site_id, p_days_local date[], p_replace_existing bool default false) — nahrazením true nejdřív vymaže dřívější řádky reference pro site, pak doplní unnest; vrací celkový počet pinů lokality po operaci.
Co to nedělá: nepřepisuje zpětně uložené forecast_pv_interval; mění jen to, jak moc vstupuje ten den do aktuálních δ slotů používaných v plánění.
Konfigurace (env proměnné)
OPEN_METEO_API_URL=https://api.open-meteo.com/v1/forecast
OPEN_METEO_FORECAST_DAYS=7
Monitoring
- Zatím není samostatný
/health/forecastendpoint. - Stav se kontroluje přes logy běhu
scheduled_forecast_refresh, přes forecast API a přes obecné health endpointy. - Log každého běhu (délka horizontu, počet intervalů, trvání, zdroj)
Otevřené body
- Ověřit přesný azimut a sklon obou FVE polí proti skutečné instalaci
- Solcast jako alternativa v budoucnu –
forecast_sourceto umožňuje bez DB změn