From 5f96a4cf015a513cc1b3728bb048c47fb1ddd850 Mon Sep 17 00:00:00 2001 From: Dusan Vojacek Date: Sun, 26 Apr 2026 20:12:28 +0200 Subject: [PATCH] tuning BA81 --- backend/services/planning_engine.py | 10 +++++++++- .../V062__planner_terminal_soc_value_factor.sql | 9 +++++++++ db/routines/R__039_fn_planning_site_context.sql | 7 +++++++ docs/04-modules/operating-modes.md | 2 +- docs/04-modules/planning.md | 11 +++++++++++ 5 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 db/migration/V062__planner_terminal_soc_value_factor.sql diff --git a/backend/services/planning_engine.py b/backend/services/planning_engine.py index 5c3be60..c5cd108 100644 --- a/backend/services/planning_engine.py +++ b/backend/services/planning_engine.py @@ -577,9 +577,12 @@ def solve_dispatch( if horizon_slots_h24 > 0 else 4.0 ) + terminal_factor = float( + getattr(battery, "planner_terminal_soc_value_factor", TERMINAL_SOC_VALUE_FACTOR) + ) # Kč/Wh: ocenění energie ponechané v baterii na konci horizontu (receding horizon kotva). terminal_soc_kcz_per_wh = ( - avg_buy_terminal * TERMINAL_SOC_VALUE_FACTOR / 1000.0 + avg_buy_terminal * terminal_factor / 1000.0 ) # Kotva: poslední slot před prvním sell<0 by měl končit u planner floor (pokud relaxace existuje). @@ -687,6 +690,11 @@ def solve_dispatch( if s.sell_price < 0: prob += w_arb[t] == 0 prob += bd[t] <= pulp.lpSum(ev_via_bat[e][t] for e in range(EV)) + # BA81 (GEN port microinverters): pokud máme k dispozici GEN cut-off, držíme skutečný + # BLOCK_EXPORT jako hard constraint: export do sítě v okně se záporným prodejem je zakázaný. + # Přebytek pak řeší curtail PV A / nabíjení / případně GEN cut-off (reg 179). + if z_gen_cutoff is not None: + prob += ge[t] == 0 soc_prev_expr = current_soc_wh if t == 0 else soc[t - 1] arb_t = arb_floor_series[t] diff --git a/db/migration/V062__planner_terminal_soc_value_factor.sql b/db/migration/V062__planner_terminal_soc_value_factor.sql new file mode 100644 index 0000000..13784e9 --- /dev/null +++ b/db/migration/V062__planner_terminal_soc_value_factor.sql @@ -0,0 +1,9 @@ +alter table ems.asset_battery + add column if not exists planner_terminal_soc_value_factor numeric not null default 0.9; + +comment on column ems.asset_battery.planner_terminal_soc_value_factor is +'Váha terminal SoC shadow price v LP solveru. +0 = solver nemá motivaci držet energii v baterii na konci horizontu (agresivnější arbitráž / vybití). +1 = odpovídá ~průměrné nákupní ceně (konzervativní držení energie). +Používá se v backend/services/planning_engine.py (terminal_soc_kcz_per_wh).'; + diff --git a/db/routines/R__039_fn_planning_site_context.sql b/db/routines/R__039_fn_planning_site_context.sql index 5c9c677..3cb85f0 100644 --- a/db/routines/R__039_fn_planning_site_context.sql +++ b/db/routines/R__039_fn_planning_site_context.sql @@ -130,6 +130,13 @@ begin raise exception 'No site_grid_connection for site_id=%', p_site_id; end if; + -- LP tuning (battery-level): terminal SoC shadow price factor + v_b := jsonb_set( + v_b, + '{planner_terminal_soc_value_factor}', + to_jsonb(coalesce((v_b->>'planner_terminal_soc_value_factor')::numeric, 0.9)) + ); + select coalesce( jsonb_agg( jsonb_build_object( diff --git a/docs/04-modules/operating-modes.md b/docs/04-modules/operating-modes.md index 0769e8d..de92ea2 100644 --- a/docs/04-modules/operating-modes.md +++ b/docs/04-modules/operating-modes.md @@ -80,7 +80,7 @@ Z toho plyne: **cut-off GEN portu je smysluplné řídit podle očekávaného p ## EMS politiky (nejsou fyzické stavy Deye) - **PV_SELL_ONLY:** AUTO + constraint solveru `max_discharge_from_pv` -- **BLOCK_EXPORT:** AUTO + záporná sell_price → `ge[t]=0` +- **BLOCK_EXPORT:** AUTO + záporná sell_price → `ge[t]=0` (hard constraint, pokud má lokalita k dispozici GEN port cut-off; jinak solver export jen penalizuje přes zápornou cenu) - **NEGATIVE_HARVEST:** AUTO + záporná buy_price → max charge/load - **PROTECT:** SELF_SUSTAIN s konzervativními limity diff --git a/docs/04-modules/planning.md b/docs/04-modules/planning.md index b8be583..eb606f8 100644 --- a/docs/04-modules/planning.md +++ b/docs/04-modules/planning.md @@ -471,6 +471,17 @@ COMMENT ON COLUMN ems.planning_interval.pv_a_curtailed_w IS --- +## Tuning pro malé baterie (např. BA81) + +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) + +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é) ```env