oprava vecerniho nevybijei
Some checks failed
CI and deploy / migration-check (push) Failing after 26s
CI and deploy / deploy (push) Has been skipped

This commit is contained in:
Dusan Vojacek
2026-05-26 14:34:39 +02:00
parent 398e658d16
commit 96b16b9ff9
4 changed files with 104 additions and 29 deletions

View File

@@ -4139,7 +4139,74 @@ class NegSellPrepWindowV36Tests(unittest.TestCase):
day11_first = int(meta["days"][1]["first_neg_idx"])
prev = _prague_calendar_date(slots[day11_first]) - timedelta(days=1)
a11 = [(t, w) for t, w in anchors if _prague_calendar_date(slots[t]) == prev]
self.assertEqual(len(a11), 1)
self.assertGreaterEqual(len(a11), 1)
def test_evening_reserve_soc_near_reserve_after_discharge(self) -> None:
"""v36d: capped slack + večerní ge_bat → SoC u kotvy ≤ reserve + max slack."""
base = datetime(2026, 6, 10, 10, 0, tzinfo=ZoneInfo("Europe/Prague")).astimezone(
timezone.utc
)
slots: list[PlanningSlot] = []
for i in range(96):
local = (base + timedelta(minutes=15 * i)).astimezone(
ZoneInfo("Europe/Prague")
)
h = local.hour + local.minute / 60.0
if local.date().day == 10:
sell = 3.2
else:
sell = -0.2 if 9 <= h < 15 else 2.8
slots.append(
PlanningSlot(
interval_start=base + timedelta(minutes=15 * i),
buy_price=2.0,
sell_price=sell,
pv_a_forecast_w=4000,
pv_b_forecast_w=5000,
load_baseline_w=500,
ev1_connected=False,
ev2_connected=False,
allow_charge=True,
allow_discharge_export=True,
)
)
bat = _battery(uc_wh=64_000.0, max_pct=95.0, arb_pct=20.0)
bat.reserve_soc_wh = 0.20 * bat.usable_capacity_wh
bat.planner_neg_sell_prep_soc_percent = 80.0
bat.planner_neg_sell_full_soc_tail_slots = 4
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=13_500,
block_export_on_negative_sell=False,
)
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),
]
results, _, snap = solve_dispatch(
slots,
bat,
hp,
grid,
[None, None],
vehicles,
0.55 * bat.soc_max_wh,
50.0,
operating_mode="AUTO",
)
self.assertEqual(snap.get("planner_build_tag"), "2026-05-28-neg-prep-window-v36d")
anchors = snap["inputs"].get("neg_evening_reserve_soc_anchors") or []
self.assertGreaterEqual(len(anchors), 1)
anchor_iso = anchors[-1]["slot"]
anchor_idx = next(
i for i, s in enumerate(slots) if s.interval_start.isoformat() == anchor_iso
)
cap_wh = float(bat.reserve_soc_wh) + 400.0
soc_wh = results[anchor_idx].battery_soc_target / 100.0 * bat.soc_max_wh
self.assertLessEqual(soc_wh, cap_wh + 800.0)
eve_slots = snap["inputs"].get("neg_evening_before_neg_slots") or []
self.assertGreater(len(eve_slots), 8)
if __name__ == "__main__":