sjednoceni forecastu
This commit is contained in:
97
.cursor/plans/unify_pv_correction_source_95b01fce.plan.md
Normal file
97
.cursor/plans/unify_pv_correction_source_95b01fce.plan.md
Normal file
@@ -0,0 +1,97 @@
|
||||
---
|
||||
name: Unify PV correction source
|
||||
status: draft
|
||||
owner: cursor-agent
|
||||
---
|
||||
|
||||
## Cíl
|
||||
|
||||
Uděláme **single source of truth** pro PV forecast používaný v plánování tak, aby:
|
||||
|
||||
- **solver** i **UI** četly *stejnou* PV řadu (žádné „dvě korekce dvěma cestami“),
|
||||
- kanonický výpočet byl v **PostgreSQL**,
|
||||
- výsledky byly auditovatelné (raw vs delta vs rolling-factor + decay).
|
||||
|
||||
## Zjištěný problém (dnes)
|
||||
|
||||
- Solver používá PV z DB + **multiplikativní rolling faktor** v Pythonu (`compute_correction_factor` + `apply_forecast_correction` v `backend/services/planning_engine.py`).
|
||||
- UI (Planning tabulka) zobrazuje PV přes endpoint **delta-korekce** (`/forecast/pv-slots-corrected` → `ems.fn_forecast_pv_slots_range_corrected`), což může být jiné číslo než PV, se kterým solver počítal.
|
||||
- Důsledek: v tabulce slotů nesedí výkonová bilance (UI ukáže např. 5.9 kW, ale plán implicitně pracuje s ~10.4 kW).
|
||||
|
||||
## Cílové chování (nová kanonická DB řada)
|
||||
|
||||
Kanonické PV pro plánování definujeme jako kombinaci obou korekcí:
|
||||
|
||||
1. **Delta-korekce (aditivní)** per PV array (odečíst `delta_profile[slot_of_day]`, clamp na 0)
|
||||
2. Agregace do **PV-A / PV-B** podle `ems.asset_pv_array.controllable`
|
||||
3. **Rolling faktor (multiplikativní)** z `ems.fn_pv_forecast_correction_factor(...)` aplikovaný na PV-A i PV-B
|
||||
4. **Decay (lineární útlum)** faktoru podle offsetu slotu od `now` (stejná logika jako dnes v `apply_forecast_correction`)
|
||||
|
||||
Výstup této kanonické řady se musí propsat do:
|
||||
|
||||
- `ems.fn_load_planning_slots_full` (vstup pro solver),
|
||||
- `ems.fn_plan_current_bundle` (výstup pro UI),
|
||||
- a do uložených sloupců `planning_interval.pv_*_forecast_solver_w` (audit).
|
||||
|
||||
## Návrh DB API (kanonická funkce)
|
||||
|
||||
Přidat novou repeatable rutinu, např.:
|
||||
|
||||
- `db/routines/R__0xx_fn_forecast_pv_slots_range_canonical_ab.sql`
|
||||
|
||||
Funkce vrátí JSON pole slotů pro `[from, to)` s minimálně:
|
||||
|
||||
- `interval_start`
|
||||
- `pv_a_forecast_raw_w`, `pv_b_forecast_raw_w`
|
||||
- `pv_a_forecast_delta_w`, `pv_b_forecast_delta_w` (po delta-korekci)
|
||||
- `rolling_factor` (globální faktor) + `rolling_effective_factor` (po decay pro slot)
|
||||
- `pv_a_forecast_canonical_w`, `pv_b_forecast_canonical_w` (delta × rolling_effective_factor)
|
||||
|
||||
Poznámky:
|
||||
|
||||
- Delta profil už dnes existuje (`ems.fn_pv_forecast_delta_profile`) a `fn_forecast_pv_slots_range_corrected` už umí per-array delty; tu logiku zrecyklujeme.
|
||||
- Rolling faktor už dnes existuje v DB (`ems.fn_pv_forecast_correction_factor`), jen se dnes aplikuje v Pythonu.
|
||||
- Decay parametrizovat (např. `p_decay_slots int default 16`, `p_min_clamp numeric`, `p_max_clamp numeric`, `p_window_h numeric`).
|
||||
|
||||
## Změny solveru (Python)
|
||||
|
||||
V `backend/services/planning_engine.py`:
|
||||
|
||||
- Přepnout loader PV slotů na kanonickou DB řadu (A/B corrected).
|
||||
- **Odstranit** aplikaci PV korekce v Pythonu (nebo ji dočasně nechat za feature flagem jen jako fallback při chybě DB funkce).
|
||||
- Uložit do `planning_run` diagnostiku (např. `forecast_correction_factor` nahradit/rozšířit o `pv_forecast_method = 'canonical_db_delta+rolling'` + `rolling_factor`).
|
||||
|
||||
## Změny DB pro plánovací sloty a current bundle
|
||||
|
||||
- `db/routines/R__063_fn_load_planning_slots_full.sql`:
|
||||
- zdroj PV A/B musí být `pv_*_forecast_canonical_w` z nové funkce.
|
||||
- zachovat raw/solver sloupce pro audit a UI.
|
||||
- `ems.fn_plan_current_bundle` (repeatable rutina ve `db/routines/`, dohledat a upravit):
|
||||
- pro intervaly z `planning_interval` vracet explicitně:
|
||||
- `pv_a_forecast_solver_w`, `pv_b_forecast_solver_w`, a `pv_forecast_total_w = pva+pvb` (aby UI nemuselo „domýšlet“ přes jiné endpointy),
|
||||
- pro sloty za horizontem (forecast extension) vracet `pv_forecast_total_w` jako **kanonický součet** (canonical A+B) z nové funkce.
|
||||
|
||||
## Změny UI
|
||||
|
||||
V `frontend/src/pages/Planning.tsx`:
|
||||
|
||||
- Pro tabulku slotů a graf použít **jen** PV z `/sites/{id}/plan/current`:
|
||||
- pro plánované sloty: `pv_a_forecast_solver_w + pv_b_forecast_solver_w` (ne `pv-slots-corrected`),
|
||||
- pro forecast-only sloty: `pv_forecast_total_w` (které už bude kanonické z DB).
|
||||
- Endpoint `/forecast/pv-slots-corrected` ponechat pro stránku forecastu a diagnostiku, ale **ne** jako zdroj pro Planning tabulku.
|
||||
|
||||
## Ověření
|
||||
|
||||
- Pro konkrétní slot (např. `home-01`, `10:15` Prague) musí sedět:
|
||||
- UI PV (z `/plan/current`) == PV v solver vstupu == uložené `planning_interval.pv_*_forecast_solver_w`.
|
||||
- Výkonová bilance v tabulce slotů: `PV - load - EV - HP = battery + export(+/- import)` bez „magické energie“.
|
||||
- Doplnit regresní test: UI zobrazuje stejné PV jako `planning_interval` (alespoň na DTO úrovni / snapshot).
|
||||
|
||||
## Dokumentace
|
||||
|
||||
Aktualizovat:
|
||||
|
||||
- `docs/04-modules/forecast.md` (kde vzniká kanonické PV: delta + rolling factor + decay),
|
||||
- `docs/04-modules/planning.md` (solver čte kanonický PV z DB; UI používá stejné sloupce z `/plan/current`),
|
||||
- případně krátká poznámka do `docs/02-architecture.md` k „read-model = single point of truth“ pro plán.
|
||||
|
||||
Reference in New Issue
Block a user