dalsi fix forecat tuningu
This commit is contained in:
@@ -1158,8 +1158,8 @@ async def run_rolling_replan(
|
|||||||
)
|
)
|
||||||
|
|
||||||
slots = await _load_slots(site_id, replan_from, horizon_to, db, soc_wh=soc_wh)
|
slots = await _load_slots(site_id, replan_from, horizon_to, db, soc_wh=soc_wh)
|
||||||
# PV forecast korekce je kanonicky v DB (delta + rolling faktor + decay),
|
# PV forecast korekce je kanonicky v DB (delta + rolling faktor + decay) a do LP vstupuje přes
|
||||||
# viz ems.fn_forecast_pv_slots_range_canonical_ab a ems.fn_load_planning_slots_full.
|
# ems.fn_load_planning_slots_full. Pro audit/debug ale chceme ukládat i RAW (bez korekcí).
|
||||||
correction_factor, correction_log = 1.0, {
|
correction_factor, correction_log = 1.0, {
|
||||||
"window_start": None,
|
"window_start": None,
|
||||||
"window_end": None,
|
"window_end": None,
|
||||||
@@ -1169,6 +1169,31 @@ async def run_rolling_replan(
|
|||||||
"reason": "canonical_db",
|
"reason": "canonical_db",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# RAW PV pro slot_inputs: přímý součet nejnovějších forecast_pv_interval per array/slot (bez delta/rolling).
|
||||||
|
raw_pv_rows = await db.fetchval(
|
||||||
|
"select ems.fn_forecast_pv_slots_range_raw_ab($1::int, $2::timestamptz, $3::timestamptz)",
|
||||||
|
site_id,
|
||||||
|
replan_from,
|
||||||
|
horizon_to,
|
||||||
|
)
|
||||||
|
raw_pv = raw_pv_rows if isinstance(raw_pv_rows, list) else json.loads(raw_pv_rows)
|
||||||
|
raw_by_ts: dict[str, tuple[int, int]] = {}
|
||||||
|
if isinstance(raw_pv, list):
|
||||||
|
for r in raw_pv:
|
||||||
|
if not isinstance(r, dict):
|
||||||
|
continue
|
||||||
|
ts = r.get("interval_start")
|
||||||
|
if isinstance(ts, str):
|
||||||
|
raw_by_ts[ts] = (
|
||||||
|
int(r.get("pv_a_forecast_raw_w") or 0),
|
||||||
|
int(r.get("pv_b_forecast_raw_w") or 0),
|
||||||
|
)
|
||||||
|
slots_raw_pv: list[PlanningSlot] = []
|
||||||
|
for s in slots:
|
||||||
|
key = s.interval_start.isoformat()
|
||||||
|
pva, pvb = raw_by_ts.get(key, (s.pv_a_forecast_w, s.pv_b_forecast_w))
|
||||||
|
slots_raw_pv.append(replace(s, pv_a_forecast_w=pva, pv_b_forecast_w=pvb))
|
||||||
|
|
||||||
commitment_prev = await _load_previous_plan_charge_commitment_prev_w(site_id, slots, db)
|
commitment_prev = await _load_previous_plan_charge_commitment_prev_w(site_id, slots, db)
|
||||||
|
|
||||||
results, duration_ms, solver_snapshot = solve_dispatch(
|
results, duration_ms, solver_snapshot = solve_dispatch(
|
||||||
@@ -1178,7 +1203,7 @@ async def run_rolling_replan(
|
|||||||
charge_commitment_prev_w=commitment_prev,
|
charge_commitment_prev_w=commitment_prev,
|
||||||
)
|
)
|
||||||
|
|
||||||
slot_inputs = _build_slot_inputs(slots, slots)
|
slot_inputs = _build_slot_inputs(slots_raw_pv, slots)
|
||||||
run_id = await _save_planning_run(
|
run_id = await _save_planning_run(
|
||||||
site_id,
|
site_id,
|
||||||
results,
|
results,
|
||||||
|
|||||||
@@ -25,30 +25,26 @@ begin
|
|||||||
and ti.measured_at >= p_window_start
|
and ti.measured_at >= p_window_start
|
||||||
and ti.measured_at < p_window_end;
|
and ti.measured_at < p_window_end;
|
||||||
|
|
||||||
with pv_arrays as (
|
-- Forecast pro korekční faktor bereme stejně jako pro plánování/UI:
|
||||||
select apa.id as pv_array_id
|
-- nejnovější `ok` run per (interval_start, pv_array_id) v daném okně.
|
||||||
from ems.asset_pv_array apa
|
select coalesce(sum(u.power_w) * 0.25 / 1000.0, 0)
|
||||||
where apa.site_id = p_site_id
|
|
||||||
),
|
|
||||||
latest_run as (
|
|
||||||
select distinct on (fpr.pv_array_id)
|
|
||||||
fpr.pv_array_id,
|
|
||||||
fpr.id as run_id
|
|
||||||
from pv_arrays pa
|
|
||||||
join ems.forecast_pv_run fpr
|
|
||||||
on fpr.pv_array_id = pa.pv_array_id
|
|
||||||
and fpr.site_id = p_site_id
|
|
||||||
where fpr.status = 'ok'
|
|
||||||
and fpr.created_at <= p_window_start
|
|
||||||
order by fpr.pv_array_id, fpr.created_at desc
|
|
||||||
)
|
|
||||||
select coalesce(sum(fpi.power_w) * 0.25 / 1000.0, 0)
|
|
||||||
into v_forecast
|
into v_forecast
|
||||||
from ems.forecast_pv_interval fpi
|
from (
|
||||||
join latest_run lr on lr.run_id = fpi.run_id
|
select distinct on (fpi.interval_start, fpr.pv_array_id)
|
||||||
where fpi.interval_start >= p_window_start
|
fpi.power_w
|
||||||
and fpi.interval_start < p_window_end
|
from ems.forecast_pv_interval fpi
|
||||||
and fpi.pv_array_id = lr.pv_array_id;
|
join ems.forecast_pv_run fpr
|
||||||
|
on fpr.id = fpi.run_id
|
||||||
|
and fpr.site_id = p_site_id
|
||||||
|
and fpr.pv_array_id = fpi.pv_array_id
|
||||||
|
and fpr.status = 'ok'
|
||||||
|
where fpi.interval_start >= p_window_start
|
||||||
|
and fpi.interval_start < p_window_end
|
||||||
|
and fpi.pv_array_id in (
|
||||||
|
select apa.id from ems.asset_pv_array apa where apa.site_id = p_site_id
|
||||||
|
)
|
||||||
|
order by fpi.interval_start, fpr.pv_array_id, fpr.created_at desc
|
||||||
|
) u;
|
||||||
|
|
||||||
if v_forecast < 0.1 or coalesce(v_actual, 0) < 0.05 then
|
if v_forecast < 0.1 or coalesce(v_actual, 0) < 0.05 then
|
||||||
return jsonb_build_object(
|
return jsonb_build_object(
|
||||||
|
|||||||
83
db/routines/R__089_fn_forecast_pv_slots_range_raw_ab.sql
Normal file
83
db/routines/R__089_fn_forecast_pv_slots_range_raw_ab.sql
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
-- ============================================================
|
||||||
|
-- PV forecast sloty (15min) – RAW (bez korekcí), rozdělené na PV-A/PV-B
|
||||||
|
--
|
||||||
|
-- Nejnovější `ok` forecast_pv_run per (interval_start, pv_array_id).
|
||||||
|
-- Slouží pro audit/debug v planning_interval.*_forecast_raw_w.
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
create or replace function ems.fn_forecast_pv_slots_range_raw_ab(
|
||||||
|
p_site_id int,
|
||||||
|
p_from timestamptz,
|
||||||
|
p_to timestamptz
|
||||||
|
)
|
||||||
|
returns jsonb
|
||||||
|
language sql
|
||||||
|
stable
|
||||||
|
set work_mem = '64MB'
|
||||||
|
as $fn$
|
||||||
|
with bounds as (
|
||||||
|
select
|
||||||
|
date_bin(interval '15 minutes', p_from, timestamptz '1970-01-01T00:00:00Z') as ts_from,
|
||||||
|
case
|
||||||
|
when p_to <= p_from then date_bin(interval '15 minutes', p_from, timestamptz '1970-01-01T00:00:00Z') + interval '15 minutes'
|
||||||
|
when p_to > p_from + interval '60 days' then date_bin(interval '15 minutes', p_from, timestamptz '1970-01-01T00:00:00Z') + interval '60 days'
|
||||||
|
else date_bin(interval '15 minutes', p_to, timestamptz '1970-01-01T00:00:00Z')
|
||||||
|
end as ts_to
|
||||||
|
),
|
||||||
|
slot_spine as (
|
||||||
|
select gs as interval_start
|
||||||
|
from bounds b,
|
||||||
|
generate_series(
|
||||||
|
b.ts_from,
|
||||||
|
(b.ts_to - interval '15 minutes')::timestamptz,
|
||||||
|
interval '15 minutes'
|
||||||
|
) as gs
|
||||||
|
),
|
||||||
|
fc_by_array as (
|
||||||
|
select distinct on (fpi.interval_start, fpr.pv_array_id)
|
||||||
|
fpi.interval_start,
|
||||||
|
apa.controllable,
|
||||||
|
fpi.power_w::bigint as power_w
|
||||||
|
from bounds b
|
||||||
|
join ems.forecast_pv_interval fpi
|
||||||
|
on fpi.interval_start >= b.ts_from
|
||||||
|
and fpi.interval_start < b.ts_to
|
||||||
|
and fpi.pv_array_id in (
|
||||||
|
select apa0.id from ems.asset_pv_array apa0 where apa0.site_id = p_site_id
|
||||||
|
)
|
||||||
|
join ems.forecast_pv_run fpr
|
||||||
|
on fpr.id = fpi.run_id
|
||||||
|
and fpr.site_id = p_site_id
|
||||||
|
and fpr.pv_array_id = fpi.pv_array_id
|
||||||
|
and fpr.status = 'ok'
|
||||||
|
join ems.asset_pv_array apa
|
||||||
|
on apa.id = fpr.pv_array_id
|
||||||
|
and apa.site_id = p_site_id
|
||||||
|
order by fpi.interval_start, fpr.pv_array_id, fpr.created_at desc
|
||||||
|
),
|
||||||
|
fc_ab as (
|
||||||
|
select
|
||||||
|
s.interval_start,
|
||||||
|
coalesce(sum(case when f.controllable then f.power_w else 0 end), 0)::bigint as pv_a_forecast_raw_w,
|
||||||
|
coalesce(sum(case when not f.controllable then f.power_w else 0 end), 0)::bigint as pv_b_forecast_raw_w
|
||||||
|
from slot_spine s
|
||||||
|
left join fc_by_array f on f.interval_start = s.interval_start
|
||||||
|
group by s.interval_start
|
||||||
|
)
|
||||||
|
select coalesce(
|
||||||
|
jsonb_agg(
|
||||||
|
jsonb_build_object(
|
||||||
|
'interval_start', r.interval_start,
|
||||||
|
'pv_a_forecast_raw_w', r.pv_a_forecast_raw_w,
|
||||||
|
'pv_b_forecast_raw_w', r.pv_b_forecast_raw_w
|
||||||
|
)
|
||||||
|
order by r.interval_start
|
||||||
|
),
|
||||||
|
'[]'::jsonb
|
||||||
|
)
|
||||||
|
from fc_ab r;
|
||||||
|
$fn$;
|
||||||
|
|
||||||
|
comment on function ems.fn_forecast_pv_slots_range_raw_ab is
|
||||||
|
'RAW PV forecast po 15 min (bez korekcí), rozdělený na PV-A/PV-B, jako nejnovější ok run per array a slot.';
|
||||||
|
|
||||||
Reference in New Issue
Block a user