zkraceni intervalu planneru na max 35h
Some checks failed
CI and deploy / migration-check (push) Failing after 13s
CI and deploy / deploy (push) Has been skipped

This commit is contained in:
Dusan Vojacek
2026-04-19 21:09:48 +02:00
parent e33207f3fa
commit f48a7aad61
16 changed files with 247 additions and 91 deletions

View File

@@ -3,7 +3,7 @@
from __future__ import annotations
import unittest
from datetime import datetime, timezone
from datetime import datetime, timedelta, timezone
from types import SimpleNamespace
from services.planning_engine import (
@@ -128,7 +128,6 @@ class PlanningDispatchMilpTests(unittest.TestCase):
50.0,
tuv_delta_stats=None,
operating_mode="AUTO",
price_failsafe_active=False,
)
self.assertGreaterEqual(ms, 0)
self.assertEqual(len(results), 1)
@@ -169,7 +168,6 @@ class PlanningDispatchMilpTests(unittest.TestCase):
50.0,
tuv_delta_stats=None,
operating_mode="AUTO",
price_failsafe_active=False,
)
self.assertEqual(len(results), 2)
@@ -206,7 +204,6 @@ class PlanningDispatchMilpTests(unittest.TestCase):
50.0,
tuv_delta_stats=None,
operating_mode="AUTO",
price_failsafe_active=False,
)
self.assertGreaterEqual(results[0].grid_setpoint_w, 0)
@@ -247,7 +244,6 @@ class PlanningDispatchMilpTests(unittest.TestCase):
50.0,
tuv_delta_stats=None,
operating_mode="AUTO",
price_failsafe_active=False,
)
reserve_pct = 20.0
for r in results:
@@ -302,7 +298,6 @@ class PlanningDispatchMilpTests(unittest.TestCase):
50.0,
tuv_delta_stats=None,
operating_mode="AUTO",
price_failsafe_active=False,
)
self.assertEqual(len(results), 3)
self.assertGreaterEqual(
@@ -312,5 +307,79 @@ class PlanningDispatchMilpTests(unittest.TestCase):
)
class TerminalSocShadowTests(unittest.TestCase):
"""Terminal SoC shadow price v objective drží konec horizontu nad holým minimem."""
def test_terminal_soc_shadow_price_prevents_drain(self) -> None:
base = datetime(2026, 4, 3, 12, 0, tzinfo=timezone.utc)
slots = []
for i in range(3):
slots.append(
PlanningSlot(
interval_start=base + timedelta(minutes=15 * i),
buy_price=2.0,
sell_price=0.6,
pv_a_forecast_w=0,
pv_b_forecast_w=0,
load_baseline_w=600,
ev1_connected=False,
ev2_connected=False,
is_predicted_price=False,
)
)
slots.append(
PlanningSlot(
interval_start=base + timedelta(minutes=45),
buy_price=2.0,
sell_price=14.0,
pv_a_forecast_w=0,
pv_b_forecast_w=0,
load_baseline_w=600,
ev1_connected=False,
ev2_connected=False,
is_predicted_price=False,
)
)
battery = _battery(uc_wh=12_000.0, min_pct=12.0, arb_pct=20.0)
hp = SimpleNamespace(
rated_heating_power_w=0,
tuv_min_temp_c=45.0,
tuv_target_temp_c=55.0,
)
grid = SimpleNamespace(max_import_power_w=20_000, max_export_power_w=20_000)
vehicles = [
SimpleNamespace(
max_charge_power_w=0,
battery_capacity_kwh=1.0,
default_target_soc_pct=80.0,
),
SimpleNamespace(
max_charge_power_w=0,
battery_capacity_kwh=1.0,
default_target_soc_pct=80.0,
),
]
soc0 = 0.5 * battery.usable_capacity_wh
results, _ms = solve_dispatch(
slots,
battery,
hp,
grid,
[None, None],
vehicles,
soc0,
50.0,
tuv_delta_stats=None,
operating_mode="AUTO",
)
self.assertEqual(len(results), 4)
# Bez shadow price by solver mohl končit u min SoC; kotva drží znatelnou rezervu.
self.assertGreaterEqual(
results[-1].battery_soc_target,
15.0,
msg="terminal SoC shadow price should keep end-of-horizon SoC above bare minimum",
)
if __name__ == "__main__":
unittest.main()