fix nabijeni z gridu u fixnich tarifu
Some checks failed
CI and deploy / migration-check (push) Failing after 11s
CI and deploy / deploy (push) Has been skipped

This commit is contained in:
Dusan Vojacek
2026-05-16 16:38:45 +02:00
parent 49d0aa68a2
commit 27323fd77a
3 changed files with 102 additions and 39 deletions

View File

@@ -62,6 +62,7 @@ declare
v_night_buf_pct numeric;
v_degrad_czk_kwh numeric;
v_ref_buy_czk_kwh numeric;
v_purchase_pricing_mode text;
begin
drop table if exists _ems_plan_slot_wk;
create temp table _ems_plan_slot_wk on commit drop as
@@ -227,6 +228,17 @@ begin
raise exception 'No asset_battery for site_id=%', p_site_id;
end if;
select coalesce(smc.purchase_pricing_mode, 'spot')
into v_purchase_pricing_mode
from ems.site_market_config smc
where smc.site_id = p_site_id
and smc.valid_from <= p_from
and (smc.valid_to is null or smc.valid_to > p_from)
order by smc.valid_from desc
limit 1;
v_purchase_pricing_mode := coalesce(v_purchase_pricing_mode, 'spot');
v_per_slot_charge_wh := v_max_charge_w * v_charge_eff * 0.25;
v_per_slot_discharge_wh := v_max_discharge_w * v_discharge_eff * 0.25;
v_energy_to_fill := v_soc_max_wh - p_current_soc_wh;
@@ -266,13 +278,9 @@ begin
-- Toto je hlavní mechanismus proti mikro-cyklování z PV:
-- v drahých slotech se PV prodává přímo, nabíjení jen v levných.
--
-- B) Non-PV sloty (pv_surplus_w <= 0): AM/PM budget, OTE-first.
-- Nejlevnější non-PV sloty (dle buy_price) s prioritou OTE cen
-- před predikovanými (is_predicted_price::int ASC). AM a PM mají
-- oddělený rozpočet (50/50), aby solver nekoncentroval veškeré
-- nabíjení/vybíjení do jediné půlky dne (double-cycle ochrana).
-- OTE-first: levné OTE sloty aktuálního dne nesmí být vytlačeny
-- levnějšími predikovanými cenami vzdálených dní (den 34 z 96h).
-- B) Non-PV sloty (pv_surplus_w <= 0): AM/PM budget, OTE-first (jen spot nákup).
-- U purchase_pricing_mode = fixed se grid nabíjení neplánuje — buy je
-- v každém slotu stejný, cyklus ze sítě by byl čistá ztráta; nabíjení jen z FVE.
if v_charge_buf <= 0 then
update _ems_plan_slot_wk wk set allow_charge = true;
elsif v_energy_to_fill <= 0 then
@@ -293,35 +301,37 @@ begin
v_cum := v_cum + least(r_slot.pv_surplus_w, v_max_charge_w) * v_charge_eff * 0.25;
end loop;
-- B) Non-PV AM: OTE-first, then predicted, ordered by buy_price
v_cum := 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
order by wk.is_predicted_price::int, wk.buy_price, wk.slot_ord
loop
exit when v_cum >= v_chg_am_wh;
exit when v_per_slot_charge_wh <= 0;
update _ems_plan_slot_wk wk set allow_charge = true where wk.slot_ord = r_slot.slot_ord;
v_cum := v_cum + v_per_slot_charge_wh;
end loop;
if v_purchase_pricing_mode <> 'fixed' then
-- B) Non-PV AM: OTE-first, then predicted, ordered by buy_price
v_cum := 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
order by wk.is_predicted_price::int, wk.buy_price, wk.slot_ord
loop
exit when v_cum >= v_chg_am_wh;
exit when v_per_slot_charge_wh <= 0;
update _ems_plan_slot_wk wk set allow_charge = true where wk.slot_ord = r_slot.slot_ord;
v_cum := v_cum + v_per_slot_charge_wh;
end loop;
-- B) Non-PV PM: OTE-first, then predicted, ordered by buy_price
v_cum := 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
order by wk.is_predicted_price::int, wk.buy_price, wk.slot_ord
loop
exit when v_cum >= v_chg_pm_wh;
exit when v_per_slot_charge_wh <= 0;
update _ems_plan_slot_wk wk set allow_charge = true where wk.slot_ord = r_slot.slot_ord;
v_cum := v_cum + v_per_slot_charge_wh;
end loop;
-- B) Non-PV PM: OTE-first, then predicted, ordered by buy_price
v_cum := 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
order by wk.is_predicted_price::int, wk.buy_price, wk.slot_ord
loop
exit when v_cum >= v_chg_pm_wh;
exit when v_per_slot_charge_wh <= 0;
update _ems_plan_slot_wk wk set allow_charge = true where wk.slot_ord = r_slot.slot_ord;
v_cum := v_cum + v_per_slot_charge_wh;
end loop;
end if;
end if;
-- Referenční nákup pro arbitráž exportu: nejlevnější buy mezi sloty, kde lze nabíjet
@@ -346,7 +356,14 @@ begin
for r_slot in
select wk.slot_ord
from _ems_plan_slot_wk wk
where wk.sell_price > v_ref_buy_czk_kwh + v_degrad_czk_kwh
where (
case
when v_purchase_pricing_mode = 'fixed' then
wk.sell_price > v_degrad_czk_kwh
else
wk.sell_price > v_ref_buy_czk_kwh + v_degrad_czk_kwh
end
)
order by wk.sell_price desc, wk.slot_ord desc
loop
exit when v_cum >= v_discharge_target_wh;