dalsi fixy
Some checks failed
CI and deploy / migration-check (push) Failing after 12s
CI and deploy / deploy (push) Has been skipped

This commit is contained in:
Dusan Vojacek
2026-05-21 13:44:13 +02:00
parent 52bedcf67d
commit ba1cdcbee4
4 changed files with 188 additions and 122 deletions

View File

@@ -64,11 +64,16 @@ declare
v_night_buf_pct numeric;
v_degrad_czk_kwh numeric;
v_ref_buy_czk_kwh numeric;
v_ref_buy_am_czk_kwh numeric;
v_ref_buy_pm_czk_kwh numeric;
v_purchase_pricing_mode text;
v_lookahead_slots int := 4;
v_grid_charge_cap_am int := 6;
v_grid_charge_cap_pm int := 6;
v_grid_charge_cap_am int;
v_grid_charge_cap_pm int;
v_buy_lookahead_eps numeric := 0.05;
v_buy_charge_band_czk_kwh numeric := 0.40;
v_grid_filled_wh numeric := 0;
v_pv_layer_cap_wh numeric;
v_grid_slots_am int := 0;
v_grid_slots_pm int := 0;
v_acquisition_cutoff timestamptz;
@@ -268,11 +273,21 @@ begin
end if;
v_discharge_target_wh := v_exportable * v_discharge_buf;
-- Referenční nákup pro arbitráž (celý horizont, ne jen allow_charge).
-- Referenční nákup: globální min (export brána) + per AM/PM pás (grid nabíjení).
select coalesce(min(wk.buy_price), 0)
into v_ref_buy_czk_kwh
from _ems_plan_slot_wk wk;
select coalesce(min(wk.buy_price), v_ref_buy_czk_kwh)
into v_ref_buy_am_czk_kwh
from _ems_plan_slot_wk wk
where extract(hour from wk.interval_start at time zone 'Europe/Prague') < 12;
select coalesce(min(wk.buy_price), v_ref_buy_czk_kwh)
into v_ref_buy_pm_czk_kwh
from _ems_plan_slot_wk wk
where extract(hour from wk.interval_start at time zone 'Europe/Prague') >= 12;
-- Lookahead min buy (VT→NT) a store_score pro vrstvu A.
alter table _ems_plan_slot_wk
add column if not exists future_sell_lookahead numeric,
@@ -330,45 +345,42 @@ begin
v_chg_pm_wh := v_grid_target_wh - v_chg_am_wh;
end if;
-- charge mask: dvě nezávislé vrstvy (tenký anti-mikrocyklus, ekonomika z cen)
if v_per_slot_charge_wh > 0 then
v_grid_charge_cap_am := greatest(
1,
least(24, ceil(v_chg_am_wh / v_per_slot_charge_wh)::int)
);
v_grid_charge_cap_pm := greatest(
1,
least(24, ceil(v_chg_pm_wh / v_per_slot_charge_wh)::int)
);
else
v_grid_charge_cap_am := 6;
v_grid_charge_cap_pm := 6;
end if;
-- charge mask: grid arbitráž (B) před FVE (A); AM/PM rozpočet Wh zůstává 50/50.
--
-- A) PV-surplus: ranking store_score DESC (future_sell sell max(0,buysell)).
-- Sloty s nejvyšší hodnotou uložení vs export pokrývají charge target.
-- Zbylé PV-surplus → allow_charge=false (PV jen do sítě / bc≤surplus v LP).
--
-- B) Non-PV grid: jen spot, buy ≤ ref_buy+degrad, buy ≤ min(next N)+ε,
-- cap K slotů AM/PM; nikdy při sell < buy degrad (ztrátový slot).
-- B) Grid ze sítě: spot, buy v pásmu AM/PM ≤ min(buy v pásmu)+band, lookahead VT→NT;
-- i při pv_surplus>0; cap slotů ∝ rozpočet Wh / per_slot_charge_wh.
-- A) PV-surplus: store_score DESC, doplní jen zbytek do energy_to_fill po vrstvě B.
if v_charge_buf <= 0 then
update _ems_plan_slot_wk wk set allow_charge = true;
elsif v_energy_to_fill <= 0 then
update _ems_plan_slot_wk wk set allow_charge = true;
else
update _ems_plan_slot_wk wk set allow_charge = false;
-- A) PV-surplus: nejvyšší store_score (ukládat FVE vs exportovat)
v_cum := 0;
for r_slot in
select wk.slot_ord, wk.pv_surplus_w
from _ems_plan_slot_wk wk
where wk.pv_surplus_w > 0
and wk.sell_price >= wk.buy_price - v_degrad_czk_kwh
order by wk.store_score desc nulls last, wk.slot_ord
loop
exit when v_cum >= v_grid_target_wh;
update _ems_plan_slot_wk wk set allow_charge = true where wk.slot_ord = r_slot.slot_ord;
v_cum := v_cum + least(r_slot.pv_surplus_w, v_max_charge_w) * v_charge_eff * 0.25;
end loop;
v_grid_filled_wh := 0;
if v_purchase_pricing_mode <> 'fixed' then
-- B) Non-PV AM: OTE-first, levný buy + lookahead, cap slotů
-- B) Grid AM (dříve než PV, vlastní 50 % rozpočtu Wh)
v_cum := 0;
v_grid_slots_am := 0;
for r_slot in
select wk.slot_ord
from _ems_plan_slot_wk wk
where wk.pv_surplus_w <= 0
and extract(hour from wk.interval_start at time zone 'Europe/Prague') < 12
and wk.buy_price <= v_ref_buy_czk_kwh + v_degrad_czk_kwh
where extract(hour from wk.interval_start at time zone 'Europe/Prague') < 12
and wk.buy_price <= v_ref_buy_am_czk_kwh + v_buy_charge_band_czk_kwh
and (
wk.buy_min_next_n is null
or wk.buy_price <= wk.buy_min_next_n + v_buy_lookahead_eps
@@ -382,16 +394,16 @@ begin
v_cum := v_cum + v_per_slot_charge_wh;
v_grid_slots_am := v_grid_slots_am + 1;
end loop;
v_grid_filled_wh := v_grid_filled_wh + v_cum;
-- B) Non-PV PM
-- B) Grid PM
v_cum := 0;
v_grid_slots_pm := 0;
for r_slot in
select wk.slot_ord
from _ems_plan_slot_wk wk
where wk.pv_surplus_w <= 0
and extract(hour from wk.interval_start at time zone 'Europe/Prague') >= 12
and wk.buy_price <= v_ref_buy_czk_kwh + v_degrad_czk_kwh
where extract(hour from wk.interval_start at time zone 'Europe/Prague') >= 12
and wk.buy_price <= v_ref_buy_pm_czk_kwh + v_buy_charge_band_czk_kwh
and (
wk.buy_min_next_n is null
or wk.buy_price <= wk.buy_min_next_n + v_buy_lookahead_eps
@@ -405,7 +417,23 @@ begin
v_cum := v_cum + v_per_slot_charge_wh;
v_grid_slots_pm := v_grid_slots_pm + 1;
end loop;
v_grid_filled_wh := v_grid_filled_wh + v_cum;
end if;
-- A) PV-surplus: jen zbytek kapacity po grid vrstvě B
v_pv_layer_cap_wh := greatest(v_energy_to_fill - v_grid_filled_wh, 0);
v_cum := 0;
for r_slot in
select wk.slot_ord, wk.pv_surplus_w
from _ems_plan_slot_wk wk
where wk.pv_surplus_w > 0
and wk.sell_price >= wk.buy_price - v_degrad_czk_kwh
order by wk.store_score desc nulls last, wk.slot_ord
loop
exit when v_cum >= v_pv_layer_cap_wh;
update _ems_plan_slot_wk wk set allow_charge = true where wk.slot_ord = r_slot.slot_ord;
v_cum := v_cum + least(r_slot.pv_surplus_w, v_max_charge_w) * v_charge_eff * 0.25;
end loop;
end if;
-- discharge-export mask
@@ -443,6 +471,7 @@ begin
from _ems_plan_slot_wk wk
where wk.allow_discharge_export;
-- Acquisition: vážený buy v allow_charge slotech před 1. exportem (ne future_sell z FVE).
select
coalesce(sum(
case
@@ -455,17 +484,6 @@ begin
else 0
end
), 0),
coalesce(sum(
case
when wk.pv_surplus_w > 0
and (
v_acquisition_cutoff is null
or wk.interval_start < v_acquisition_cutoff
)
then least(wk.pv_surplus_w, v_max_charge_w) * v_charge_eff * 0.25
else 0
end
), 0),
coalesce(sum(
case
when wk.allow_charge
@@ -476,31 +494,22 @@ begin
then wk.buy_price * v_per_slot_charge_wh
else 0
end
), 0),
coalesce(sum(
case
when wk.pv_surplus_w > 0
and (
v_acquisition_cutoff is null
or wk.interval_start < v_acquisition_cutoff
)
then coalesce(wk.future_sell_lookahead, wk.sell_price)
* least(wk.pv_surplus_w, v_max_charge_w) * v_charge_eff * 0.25
else 0
end
), 0)
into
v_est_grid_wh,
v_est_pv_wh,
v_est_grid_cost,
v_est_pv_cost
v_est_grid_cost
from _ems_plan_slot_wk wk;
if (v_est_grid_wh + v_est_pv_wh) > 0 then
v_charge_acquisition := (v_est_grid_cost + v_est_pv_cost)
/ (v_est_grid_wh + v_est_pv_wh);
v_est_pv_wh := 0;
v_est_pv_cost := 0;
if v_est_grid_wh > 0 then
v_charge_acquisition := v_est_grid_cost / v_est_grid_wh;
else
v_charge_acquisition := v_ref_buy_czk_kwh;
v_charge_acquisition := coalesce(
(v_ref_buy_am_czk_kwh + v_ref_buy_pm_czk_kwh) / 2.0,
v_ref_buy_czk_kwh
);
end if;
return query
@@ -602,5 +611,5 @@ comment on function ems.fn_load_planning_slots_full is
'Strop SoC pro výpočet energie k dobití: coalesce(planner_max_soc_percent, max_soc_percent). '
'Denní safety vstupy: night_baseload_* (20:0006:00 Europe/Prague), safety_soc_target_wh (619), '
'lookahead max buy/sell pro měkké LP penalizace. '
'charge_acquisition_buy_czk_kwh: vážený průměr grid (allow_charge) + FVE (pv_surplus, opportunity future_sell) '
'jen pro sloty před charge_acquisition_cutoff_at (= začátek prvního allow_discharge_export).';
'charge_acquisition_buy_czk_kwh: vážený buy v allow_charge slotech před charge_acquisition_cutoff_at. '
'Grid maska B běží před PV vrstvou A; AM/PM rozpočet Wh 50/50; cap slotů z rozpočtu / per_slot_charge_wh.';