FIX RYCHLOST EKONOMIKA
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
|
||||
- **SQL-first:** horizont a sloty z DB funkcí (`fn_planning_horizon_end`, `fn_load_planning_slots_full`, …); viz **`CLAUDE.md`** → sekce *SQL-first a read-model*.
|
||||
- **Dynamický horizont (jen OTE):** konec plánu z **`ems.fn_planning_horizon_end(site_id, horizon_start)`** (výchozí strop **36 h**, minimum pro rolling **1 h** – obojí jako defaultní argumenty v SQL, úprava přes repeatable migraci). Pomocná `ems.fn_last_effective_ote` vrací konec posledního OTE intervalu. Rolling replan při `NULL` přeskočí; denní plán použije krátký (1 h) fallback v Pythonu. Sloty v solveru jsou bez predikovaných cen v rámci tohoto horizontu.
|
||||
- **Terminal SoC shadow price:** v objective je člen `−(avg_buy_prvních_24h × 0,9 / 1000) × soc[T−1]` (Kč), aby konec horizontu nekončil zbytečně vyprázdněnou baterií (receding horizon).
|
||||
- **Terminal SoC shadow price:** v objective je člen `−(avg_buy_prvních_24h × planner_terminal_soc_value_factor / 1000) × soc[T−1]` (Kč), kde faktor je **`ems.asset_battery.planner_terminal_soc_value_factor`** přes **`ems.fn_planning_site_context`** (default v DB **0.9**); viz sekci *Tuning pro malé baterie* níže. Účel: konec horizontu nemusí končit zbytečně vyprázdněnou baterií (receding horizon).
|
||||
- **Runtime guard v exportu setpointů (legacy):**
|
||||
- při `AUTO` + `is_predicted_price=true` se na exportní vrstvě vynutí PASSIVE/no-export chování (u nových plánů by `is_predicted_price` v horizontu nemělo nastat).
|
||||
- **Ekonomika baterie:**
|
||||
@@ -473,13 +473,31 @@ COMMENT ON COLUMN ems.planning_interval.pv_a_curtailed_w IS
|
||||
|
||||
## Tuning pro malé baterie (např. BA81)
|
||||
|
||||
### Terminal SoC shadow price (kritický parametr)
|
||||
|
||||
V účelové funkci LP je člen **„terminal SoC shadow price“**: energie ponechaná v baterii na konci horizontu je oceněná jako záporný příspěvek k nákladům (solver má motivaci držet část SoC, pokud se to ekonomicky vyplatí oproti okamžitému vývozu / nákupu).
|
||||
|
||||
**Výpočet (zjednodušeně):**
|
||||
`terminal_soc_kcz_per_wh = (průměr buy v prvních 24 h slotů) × planner_terminal_soc_value_factor / 1000`
|
||||
a v objective se přičítá `- terminal_soc_kcz_per_wh × soc[T−1]` (viz `solve_dispatch` v `backend/services/planning_engine.py`).
|
||||
|
||||
**Kde se bere faktor (jediný kanonický zdroj):**
|
||||
|
||||
1. Sloupec **`ems.asset_battery.planner_terminal_soc_value_factor`** (`NOT NULL`, default **0.9** — migrace **V062**, idempotentní upevnění **V069**).
|
||||
2. Hodnota se do solveru dostává výhradně přes **`ems.fn_planning_site_context(site_id)`** → pole `battery.planner_terminal_soc_value_factor` v JSONu.
|
||||
3. Backend v **`_load_site_context()`** mapuje JSON na `SimpleNamespace` a **`solve_dispatch()` už nemá žádný skrytý fallback z kódu** — chybí-li klíč v JSONu, je to chyba konfigurace / nasazení.
|
||||
|
||||
> **Historická chyba (opraveno):** dříve `fn_planning_site_context` sloupec z tabulky **nepropisoval** do `battery` JSONu a Python atribut **vůbec nenačítal**, takže se v praxi používala **pevná 0.9** z kódu bez ohledu na DB. To umělo zcela převrátit chování (např. BA81 s **0.2** v tabulce se chovalo jako **0.9**). Po opravě musí projít **repeatable** `R__039_fn_planning_site_context.sql` i backend.
|
||||
|
||||
### Doporučené hodnoty
|
||||
|
||||
Pokud solver „šetří baterku“ a raději importuje ze sítě (kvůli terminal SoC shadow price), lze per baterii upravit váhu této kotvy:
|
||||
|
||||
- `ems.asset_battery.planner_terminal_soc_value_factor`
|
||||
- `0.0` = žádná motivace držet SoC na konci horizontu (agresivnější arbitráž / vybití)
|
||||
- `0.9` = default (konzervativnější držení energie)
|
||||
- **`0.0`** = žádná motivace držet SoC na konci horizontu (agresivnější arbitráž / vybití)
|
||||
- **`0.9`** = výchozí default v DB (konzervativnější držení energie)
|
||||
|
||||
Pro BA81 typicky dává smysl menší hodnota (např. 0–0.3), aby solver klidně „vylil“ baterii do sítě při kladné `sell_price`
|
||||
Pro BA81 typicky dává smysl menší hodnota (např. **0–0.3**), aby solver klidně „vylil“ baterii do sítě při kladné `sell_price`
|
||||
a nechal si kapacitu na nabití v oknech záporných cen.
|
||||
|
||||
## Konfigurace (env proměnné)
|
||||
|
||||
Reference in New Issue
Block a user