tune microcycling
This commit is contained in:
@@ -13,22 +13,27 @@ Připojení k DB (deploy / Docker):
|
||||
- Nebo ``DATABASE_URL`` / ``postgresql://USER:PASS@HOST:5432/ems`` (na hostu HOST=127.0.0.1
|
||||
nebo EMS_DB_BIND, ne ``db`` — to je jen uvnitř Docker sítě).
|
||||
|
||||
Příklad:
|
||||
python3 scripts/analysis/battery_sizing_screen.py \\
|
||||
--db \\
|
||||
Příklad (syntetická FVE, flat nákup):
|
||||
python3 scripts/analysis/battery_sizing_screen.py --db \\
|
||||
--date-from 2024-04-01 --date-to 2026-04-01 \\
|
||||
--battery-kwh 12.5 32 48 \\
|
||||
--load-kw 1.2 \\
|
||||
--battery-kwh 12.5 32 48 --load-kw 1.2 \\
|
||||
--pv-daily-kwh-summer 55 --pv-daily-kwh-winter 12 \\
|
||||
--sell-margin-fixed -0.02 \\
|
||||
--buy-vat-kwh 4.443 \\
|
||||
--capex-per-kwh 9000
|
||||
--sell-margin-fixed -0.02 --buy-vat-kwh 4.443 --capex-per-kwh 9000
|
||||
|
||||
Příklad (PVGIS měsíční E_d + NT/VT):
|
||||
python3 scripts/analysis/battery_sizing_screen.py --db \\
|
||||
--pvgis-csv pole_A.csv --pvgis-csv pole_B.csv \\
|
||||
--buy-nt-kwh 5.25 --buy-vt-surcharge-kwh 2.0 --nt-from-hour 22 --nt-to-hour 6 \\
|
||||
... (ostatní jako výše)
|
||||
|
||||
Vyžaduje: pip install pulp (volitelně psycopg2 pro --db).
|
||||
|
||||
Omezení modelu: syntetický denní tvar FVE (kalibruj --pv-daily-kwh-* podle měření);
|
||||
mikroinvertory / GEN nejsou; zelený bonus není v účelové funkci; nákup je jedna flat
|
||||
sazba vč. DPH (reálné NT/VT přes HDO přidej později). Výsledek = screening, ne nabídka.
|
||||
Omezení modelu: FVE buď syntetický denní tvar (--pv-daily-kwh-*), nebo součet měsíčních
|
||||
E_d z PVGIS CSV (--pvgis-csv, opakovat pro více orientací); denní energie = E_d měsíce
|
||||
× normalizovaný tvar (stejný profil každý den v měsíci). Nákup: buď flat (--buy-vat-kwh),
|
||||
nebo NT/VT podle hodin Europe/Prague: --buy-nt-kwh, VT = NT + --buy-vt-surcharge-kwh,
|
||||
okno NT --nt-from-hour až --nt-to-hour (přes půlnoc, pokud from > to). Mikroinvertory / GEN
|
||||
nejsou; zelený bonus není v účelové funkci. Výsledek = screening, ne nabídka.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -40,7 +45,7 @@ import sys
|
||||
from dataclasses import dataclass
|
||||
from datetime import date, datetime, timedelta
|
||||
from pathlib import Path
|
||||
from typing import Iterable, Sequence
|
||||
from typing import Iterable, Sequence, Mapping
|
||||
|
||||
try:
|
||||
import pulp
|
||||
@@ -93,6 +98,72 @@ def daily_pv_wh(d: date, summer_kwh: float, winter_kwh: float, shape: Sequence[f
|
||||
return [base * 1000.0 * sh for sh in shape]
|
||||
|
||||
|
||||
def load_pvgis_monthly_ed_kwh(path: Path) -> dict[int, float]:
|
||||
"""Z PVGIS CSV (Fixed angle) načte E_d [kWh/d] pro měsíce 1–12."""
|
||||
text = path.read_text(encoding="utf-8", errors="replace").splitlines()
|
||||
start: int | None = None
|
||||
for i, line in enumerate(text):
|
||||
if line.strip().startswith("Fixed angle"):
|
||||
start = i + 2
|
||||
break
|
||||
if start is None:
|
||||
raise ValueError(f"PVGIS: řádek 'Fixed angle' nenalezen: {path}")
|
||||
out: dict[int, float] = {}
|
||||
for line in text[start:]:
|
||||
cells = [c.strip() for c in line.split("\t") if c.strip() != ""]
|
||||
if not cells:
|
||||
continue
|
||||
if cells[0] == "Year":
|
||||
break
|
||||
try:
|
||||
month = int(cells[0])
|
||||
except ValueError:
|
||||
continue
|
||||
if not (1 <= month <= 12):
|
||||
continue
|
||||
out[month] = float(cells[1].replace(",", "."))
|
||||
if len(out) != 12:
|
||||
raise ValueError(f"PVGIS: očekáváno 12 měsíců E_d v {path}, mám {sorted(out.keys())}")
|
||||
return out
|
||||
|
||||
|
||||
def merge_pvgis_monthly_ed_kwh(paths: Sequence[Path]) -> dict[int, float]:
|
||||
"""Sečte E_d jednotlivých polí (např. dvě orientace)."""
|
||||
total = {m: 0.0 for m in range(1, 13)}
|
||||
for p in paths:
|
||||
part = load_pvgis_monthly_ed_kwh(Path(p))
|
||||
for m in range(1, 13):
|
||||
total[m] += part[m]
|
||||
return total
|
||||
|
||||
|
||||
def daily_pv_wh_monthly(d: date, monthly_ed_kwh: Mapping[int, float], shape: Sequence[float]) -> list[float]:
|
||||
kwh = float(monthly_ed_kwh[d.month])
|
||||
return [kwh * 1000.0 * sh for sh in shape]
|
||||
|
||||
|
||||
def buy_prices_96_nt_vt(
|
||||
nt_kwh: float,
|
||||
vt_kwh: float,
|
||||
nt_from_hour: int,
|
||||
nt_to_hour: int,
|
||||
) -> list[float]:
|
||||
"""
|
||||
96 cen nákupu [Kč/kWh] podle začátku 15min slotu (hodina 0–23, Europe/Prague).
|
||||
Pokud nt_from_hour > nt_to_hour: NT pro hodiny >= from nebo < to (přes půlnoc).
|
||||
Jinak NT pro from <= h < to.
|
||||
"""
|
||||
out: list[float] = []
|
||||
for t in range(SLOTS_PER_DAY):
|
||||
h = t // 4
|
||||
if nt_from_hour > nt_to_hour:
|
||||
is_nt = h >= nt_from_hour or h < nt_to_hour
|
||||
else:
|
||||
is_nt = nt_from_hour <= h < nt_to_hour
|
||||
out.append(nt_kwh if is_nt else vt_kwh)
|
||||
return out
|
||||
|
||||
|
||||
def daily_load_wh(load_kw: float) -> list[float]:
|
||||
e_per_slot = load_kw * 1000.0 * DT_H
|
||||
return [e_per_slot] * SLOTS_PER_DAY
|
||||
@@ -221,7 +292,7 @@ def solve_one_day(
|
||||
pv_wh: Sequence[float],
|
||||
load_wh: Sequence[float],
|
||||
p_sell: Sequence[float],
|
||||
p_buy_flat: float,
|
||||
p_buy: Sequence[float],
|
||||
e_usable_wh: float,
|
||||
p_batt_w: float,
|
||||
site: SiteLimits,
|
||||
@@ -262,7 +333,7 @@ def solve_one_day(
|
||||
soc[t + 1]
|
||||
== soc[t] + site.eta_charge * ch[t] - dis[t] / site.eta_discharge
|
||||
), f"socdyn_{t}"
|
||||
obj.append(p_sell[t] * gexp[t] / 1000.0 - p_buy_flat * gimp[t] / 1000.0)
|
||||
obj.append(p_sell[t] * gexp[t] / 1000.0 - p_buy[t] * gimp[t] / 1000.0)
|
||||
|
||||
prob += pulp.lpSum(obj)
|
||||
|
||||
@@ -285,15 +356,23 @@ def simulate_year(
|
||||
site: SiteLimits,
|
||||
sell_margin_fixed: float,
|
||||
sell_margin_pct: float,
|
||||
buy_vat_kwh: float,
|
||||
buy_flat_kwh: float,
|
||||
buy_prices_96: Sequence[float] | None,
|
||||
summer_kwh: float,
|
||||
winter_kwh: float,
|
||||
load_kw: float,
|
||||
shape: Sequence[float],
|
||||
monthly_ed_kwh: Mapping[int, float] | None,
|
||||
) -> dict[str, float]:
|
||||
e_wh = usable_kwh * 1000.0
|
||||
p_batt = batt_power_cap_w(usable_kwh, site)
|
||||
load_wh = daily_load_wh(load_kw)
|
||||
if buy_prices_96 is not None:
|
||||
if len(buy_prices_96) != SLOTS_PER_DAY:
|
||||
raise ValueError("buy_prices_96 musí mít 96 hodnot")
|
||||
p_buy_day: Sequence[float] = buy_prices_96
|
||||
else:
|
||||
p_buy_day = [buy_flat_kwh] * SLOTS_PER_DAY
|
||||
cash_total = 0.0
|
||||
curt_total = 0.0
|
||||
dis_total = 0.0
|
||||
@@ -304,9 +383,12 @@ def simulate_year(
|
||||
continue
|
||||
raw = px_day[d]
|
||||
p_sell = [effective_sell_kc_kwh(x, sell_margin_fixed, sell_margin_pct) for x in raw]
|
||||
pv_wh = daily_pv_wh(d, summer_kwh, winter_kwh, shape)
|
||||
if monthly_ed_kwh is not None:
|
||||
pv_wh = daily_pv_wh_monthly(d, monthly_ed_kwh, shape)
|
||||
else:
|
||||
pv_wh = daily_pv_wh(d, summer_kwh, winter_kwh, shape)
|
||||
cash, soc_state, curt, dis = solve_one_day(
|
||||
pv_wh, load_wh, p_sell, buy_vat_kwh, e_wh, p_batt, site, soc_state
|
||||
pv_wh, load_wh, p_sell, p_buy_day, e_wh, p_batt, site, soc_state
|
||||
)
|
||||
cash_total += cash
|
||||
curt_total += curt
|
||||
@@ -346,7 +428,33 @@ def main() -> None:
|
||||
ap.add_argument("--pv-daily-kwh-winter", type=float, default=10.0)
|
||||
ap.add_argument("--sell-margin-fixed", type=float, default=-0.02)
|
||||
ap.add_argument("--sell-margin-pct", type=float, default=0.0)
|
||||
ap.add_argument("--buy-vat-kwh", type=float, default=4.443, help="Efektivní nákup Kč/kWh vč. DPH (flat screening)")
|
||||
ap.add_argument(
|
||||
"--buy-vat-kwh",
|
||||
type=float,
|
||||
default=4.443,
|
||||
help="Flat nákup Kč/kWh (když není --buy-nt-kwh)",
|
||||
)
|
||||
ap.add_argument(
|
||||
"--buy-nt-kwh",
|
||||
type=float,
|
||||
default=None,
|
||||
help="NT cena Kč/kWh; VT = NT + --buy-vt-surcharge-kwh; okno --nt-from-hour / --nt-to-hour (Europe/Prague)",
|
||||
)
|
||||
ap.add_argument(
|
||||
"--buy-vt-surcharge-kwh",
|
||||
type=float,
|
||||
default=0.0,
|
||||
help="Příplatek VT oproti NT (jako buy_fixed_vt_surcharge v EMS)",
|
||||
)
|
||||
ap.add_argument("--nt-from-hour", type=int, default=22, help="Začátek NT (hodina 0–23)")
|
||||
ap.add_argument("--nt-to-hour", type=int, default=6, help="Konec NT: první hodina VT (0–23); přes půlnoc pokud from > to")
|
||||
ap.add_argument(
|
||||
"--pvgis-csv",
|
||||
action="append",
|
||||
default=[],
|
||||
metavar="PATH",
|
||||
help="PVGIS měsíční E_d (Fixed angle); opakovat pro více polí/orientací, energie se sečte",
|
||||
)
|
||||
ap.add_argument("--max-export-w", type=float, default=16_000.0)
|
||||
ap.add_argument("--max-import-w", type=float, default=17_000.0)
|
||||
ap.add_argument("--inv-batt-max-w", type=float, default=12_000.0)
|
||||
@@ -379,6 +487,23 @@ def main() -> None:
|
||||
c_rate=args.c_rate,
|
||||
)
|
||||
|
||||
monthly_ed: dict[int, float] | None = None
|
||||
if args.pvgis_csv:
|
||||
monthly_ed = merge_pvgis_monthly_ed_kwh([Path(p) for p in args.pvgis_csv])
|
||||
|
||||
if args.buy_nt_kwh is not None:
|
||||
vt = args.buy_nt_kwh + args.buy_vt_surcharge_kwh
|
||||
buy_prices_96 = buy_prices_96_nt_vt(
|
||||
args.buy_nt_kwh,
|
||||
vt,
|
||||
args.nt_from_hour,
|
||||
args.nt_to_hour,
|
||||
)
|
||||
buy_flat = args.buy_vat_kwh
|
||||
else:
|
||||
buy_prices_96 = None
|
||||
buy_flat = args.buy_vat_kwh
|
||||
|
||||
day_list = [d0 + timedelta(days=i) for i in range((d1 - d0).days)]
|
||||
|
||||
results = []
|
||||
@@ -390,19 +515,40 @@ def main() -> None:
|
||||
site,
|
||||
args.sell_margin_fixed,
|
||||
args.sell_margin_pct,
|
||||
args.buy_vat_kwh,
|
||||
buy_flat,
|
||||
buy_prices_96,
|
||||
args.pv_daily_kwh_summer,
|
||||
args.pv_daily_kwh_winter,
|
||||
args.load_kw,
|
||||
shape,
|
||||
monthly_ed,
|
||||
)
|
||||
results.append((kwh, r))
|
||||
|
||||
baseline_kwh = min(args.battery_kwh)
|
||||
base = dict(results)[baseline_kwh]
|
||||
|
||||
print("Parametry: prodej = OTE + sell_margin_fixed (+ %), nákup = flat buy_vat_kwh")
|
||||
print(f" FVE tvar = syntetický den, léto {args.pv_daily_kwh_summer} kWh/d, zima {args.pv_daily_kwh_winter} kWh/d, load {args.load_kw} kW")
|
||||
print("Parametry: prodej = OTE + sell_margin_fixed (+ %)")
|
||||
if buy_prices_96 is not None:
|
||||
vt_show = args.buy_nt_kwh + args.buy_vt_surcharge_kwh
|
||||
print(
|
||||
f" Nákup = NT/VT: NT {args.buy_nt_kwh} Kč/kWh, VT {vt_show} Kč/kWh "
|
||||
f"(okno NT {args.nt_from_hour:02d}–{args.nt_to_hour:02d} h lokální)"
|
||||
)
|
||||
else:
|
||||
print(f" Nákup = flat {args.buy_vat_kwh} Kč/kWh")
|
||||
if monthly_ed is not None:
|
||||
edv = [monthly_ed[m] for m in range(1, 13)]
|
||||
print(
|
||||
f" FVE = PVGIS měsíční E_d (součet {len(args.pvgis_csv)} souborů), "
|
||||
f"rozsah {min(edv):.1f}–{max(edv):.1f} kWh/d, denní tvar = syntetika"
|
||||
)
|
||||
else:
|
||||
print(
|
||||
f" FVE = syntetický den, léto {args.pv_daily_kwh_summer} kWh/d, "
|
||||
f"zima {args.pv_daily_kwh_winter} kWh/d"
|
||||
)
|
||||
print(f" Load (konstanta) {args.load_kw} kW")
|
||||
print(f" Limity: export {args.max_export_w} W, import {args.max_import_w} W, P_batt = min({args.c_rate}*E_kWh, {args.inv_batt_max_w} W)")
|
||||
print()
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Latitude (decimal degrees): 49.241
|
||||
Longitude (decimal degrees): 17.472
|
||||
Radiation database: PVGIS-SARAH3
|
||||
Nominal power of the PV system (c-Si) (kWp): 8.0
|
||||
System losses(%): 14.0
|
||||
Fixed slope of modules (deg.): 15
|
||||
Orientation (azimuth) of modules (deg.): -76
|
||||
|
||||
Fixed angle
|
||||
Month E_d E_m H(i)_d H(i)_m SD_m
|
||||
1 6.51 201.76 1.01 31.39 40.32
|
||||
2 11.5 322.13 1.73 48.35 56.66
|
||||
3 20.7 641.58 3.11 96.47 92.53
|
||||
4 30.11 903.3 4.66 139.66 102.82
|
||||
5 33.5 1038.44 5.27 163.47 146.81
|
||||
6 37.65 1129.53 6.06 181.91 112.87
|
||||
7 35.78 1109.15 5.86 181.56 109.26
|
||||
8 31.19 967.04 5.07 157.2 88.94
|
||||
9 24.0 719.86 3.8 113.88 77.04
|
||||
10 14.63 453.54 2.29 70.9 71.15
|
||||
11 7.34 220.22 1.17 35.16 28.46
|
||||
12 4.93 152.73 0.81 25.12 22.46
|
||||
Year 21.53 654.94 3.41 103.76 23.94
|
||||
AOI loss (%) Spectral effects (%) Temperature and low irradiance loss (%) Combined loss (%)
|
||||
Fixed angle: -3.8 1.38 -5.92 -21.1
|
||||
|
||||
E_d: Average daily energy production from the given system (kWh/d)
|
||||
E_m: Average monthly energy production from the given system (kWh/mo)
|
||||
H(i)_d: Average daily sum of global irradiation per square meter received by the modules of the given system (kWh/m2/d)
|
||||
H(i)_m: Average monthly sum of global irradiation per square meter received by the modules of the given system (kWh/m2/mo)
|
||||
SD_m: Standard deviation of the monthly energy production due to year-to-year variation (kWh)
|
||||
|
||||
|
||||
PVGIS (c) European Union, 2001-2026
|
||||
|
@@ -0,0 +1,34 @@
|
||||
Latitude (decimal degrees): 49.241
|
||||
Longitude (decimal degrees): 17.474
|
||||
Radiation database: PVGIS-SARAH3
|
||||
Nominal power of the PV system (c-Si) (kWp): 8.0
|
||||
System losses(%): 14.0
|
||||
Fixed slope of modules (deg.): 15
|
||||
Orientation (azimuth) of modules (deg.): 104
|
||||
|
||||
Fixed angle
|
||||
Month E_d E_m H(i)_d H(i)_m SD_m
|
||||
1 5.17 160.32 0.85 26.26 23.55
|
||||
2 9.83 275.32 1.52 42.62 39.6
|
||||
3 18.58 575.98 2.83 87.86 74.67
|
||||
4 28.37 850.97 4.41 132.31 91.17
|
||||
5 32.39 1004.0 5.11 158.3 131.56
|
||||
6 36.78 1103.33 5.93 177.9 103.53
|
||||
7 34.87 1081.07 5.71 177.13 96.37
|
||||
8 29.5 914.54 4.81 149.23 71.24
|
||||
9 22.01 660.26 3.51 105.44 74.33
|
||||
10 12.87 398.83 2.06 63.77 60.0
|
||||
11 6.1 183.02 1.02 30.51 20.03
|
||||
12 3.84 118.91 0.67 20.83 12.24
|
||||
Year 20.07 610.54 3.21 97.68 18.01
|
||||
AOI loss (%) Spectral effects (%) Temperature and low irradiance loss (%) Combined loss (%)
|
||||
Fixed angle: -4.35 1.34 -6.28 -21.87
|
||||
|
||||
E_d: Average daily energy production from the given system (kWh/d)
|
||||
E_m: Average monthly energy production from the given system (kWh/mo)
|
||||
H(i)_d: Average daily sum of global irradiation per square meter received by the modules of the given system (kWh/m2/d)
|
||||
H(i)_m: Average monthly sum of global irradiation per square meter received by the modules of the given system (kWh/m2/mo)
|
||||
SD_m: Standard deviation of the monthly energy production due to year-to-year variation (kWh)
|
||||
|
||||
|
||||
PVGIS (c) European Union, 2001-2026
|
||||
|
104
scripts/analysis/ote_arbitrage_proxy.sql
Normal file
104
scripts/analysis/ote_arbitrage_proxy.sql
Normal file
@@ -0,0 +1,104 @@
|
||||
-- =============================================================
|
||||
-- Hrubý odhad „kolik by přineslo přesunout ~30 kWh/den“ z levných
|
||||
-- slotů (OTE sell < práh) do večerních/ranních špiček.
|
||||
--
|
||||
-- Zjednodušení:
|
||||
-- • Kalendářní den v Europe/Prague.
|
||||
-- • Bereme jen dny, kde existuje aspoň jeden 15min slot s sell < :cheap_thr.
|
||||
-- • „Levná“ strana: průměrná OTE sell ve všech slotech toho dne s sell < :cheap_thr.
|
||||
-- • „Drahá“ strana: okno večer+ráno (18:00–24:00 a 0:00–8:00), seřadíme sloty
|
||||
-- podle ceny DESC, vyhodíme :drop_top nejvyšších (malá baterie je už „sežere“),
|
||||
-- vezmeme dalších :take_slot 15min intervalů → :take_slot × 0,25 h × 12 kW = 30 kWh
|
||||
-- při 12 kW a take_slot = 10.
|
||||
-- • Hrubý přínos (Kč/den) ≈ :shift_kwh * (avg_peak - avg_cheap); bez účinnosti baterie.
|
||||
--
|
||||
-- Uprav parametry v nejníže (date_trunc rozsah, práhy, počty slotů).
|
||||
-- Spuštění: psql -v ON_ERROR_STOP=1 -f scripts/analysis/ote_arbitrage_proxy.sql
|
||||
-- =============================================================
|
||||
|
||||
WITH params AS (
|
||||
SELECT
|
||||
0.3::numeric AS cheap_thr,
|
||||
0::int AS drop_top, -- vynechat N nejdražších 15min ve večer+ráno okně
|
||||
12::int AS take_slot, -- dalších N slotů = 2,5 h při 15 min
|
||||
30::numeric AS shift_kwh -- objem energie pro hrubý spread (volitelně = take_slot * 0.25 * 12)
|
||||
),
|
||||
slots AS (
|
||||
SELECT
|
||||
interval_start,
|
||||
(interval_start AT TIME ZONE 'Europe/Prague')::date AS d,
|
||||
(interval_start AT TIME ZONE 'Europe/Prague')::time AS t,
|
||||
sell_raw_price_czk_kwh::numeric AS sell
|
||||
FROM ems.market_interval_price
|
||||
WHERE market_source = 'OTE_CZ'
|
||||
AND interval_start >= TIMESTAMPTZ '2025-04-01 Europe/Prague'
|
||||
AND interval_start < TIMESTAMPTZ '2026-04-01 Europe/Prague'
|
||||
),
|
||||
days_cheap AS (
|
||||
SELECT s.d
|
||||
FROM slots s
|
||||
GROUP BY s.d
|
||||
HAVING MIN(s.sell) < (SELECT cheap_thr FROM params)
|
||||
),
|
||||
cheap_side AS (
|
||||
SELECT
|
||||
s.d,
|
||||
AVG(s.sell) AS avg_cheap
|
||||
FROM slots s
|
||||
INNER JOIN days_cheap dc ON dc.d = s.d
|
||||
WHERE s.sell < (SELECT cheap_thr FROM params)
|
||||
GROUP BY s.d
|
||||
),
|
||||
evening_morning AS (
|
||||
SELECT
|
||||
s.d,
|
||||
s.sell,
|
||||
s.t
|
||||
FROM slots s
|
||||
INNER JOIN days_cheap dc ON dc.d = s.d
|
||||
WHERE s.t >= TIME '18:00'
|
||||
OR s.t < TIME '08:00'
|
||||
),
|
||||
ranked AS (
|
||||
SELECT
|
||||
em.d,
|
||||
em.sell,
|
||||
ROW_NUMBER() OVER (PARTITION BY em.d ORDER BY em.sell DESC, em.t) AS rn
|
||||
FROM evening_morning em
|
||||
),
|
||||
peak_pick AS (
|
||||
SELECT
|
||||
r.d,
|
||||
r.sell
|
||||
FROM ranked r
|
||||
WHERE r.rn > (SELECT drop_top FROM params)
|
||||
AND r.rn <= (SELECT drop_top + take_slot FROM params)
|
||||
),
|
||||
peak_side AS (
|
||||
SELECT d, AVG(sell) AS avg_peak
|
||||
FROM peak_pick
|
||||
GROUP BY d
|
||||
),
|
||||
per_day AS (
|
||||
SELECT
|
||||
c.d,
|
||||
c.avg_cheap,
|
||||
p.avg_peak,
|
||||
(SELECT shift_kwh FROM params) * (p.avg_peak - c.avg_cheap) AS rough_kc_day
|
||||
FROM cheap_side c
|
||||
INNER JOIN peak_side p ON p.d = c.d
|
||||
),
|
||||
period AS (
|
||||
SELECT
|
||||
(TIMESTAMPTZ '2026-04-01 Europe/Prague' - TIMESTAMPTZ '2024-04-01 Europe/Prague')
|
||||
AS len
|
||||
)
|
||||
SELECT
|
||||
COUNT(*)::int AS days_qualifying,
|
||||
ROUND(SUM(pd.rough_kc_day)::numeric, 2) AS spread_kc_sum_period,
|
||||
ROUND(AVG(pd.rough_kc_day)::numeric, 4) AS spread_kc_avg_per_qualifying_day,
|
||||
ROUND(
|
||||
(SUM(pd.rough_kc_day) / NULLIF(EXTRACT(EPOCH FROM (SELECT len FROM period)) / 86400.0, 0) * 365.0)::numeric,
|
||||
2
|
||||
) AS spread_kc_naive_per_solar_year
|
||||
FROM per_day pd;
|
||||
Reference in New Issue
Block a user