zruseni fixnich konstant
This commit is contained in:
@@ -11,6 +11,8 @@ from services.planning_engine import (
|
||||
PlanningSlot,
|
||||
_dynamic_arb_floor_wh_series,
|
||||
_dispatch_result_comparison,
|
||||
_evening_battery_export_push_indices,
|
||||
_evening_push_discharge_budget_wh,
|
||||
_pre_neg_peak_sell_idx,
|
||||
_prague_hour,
|
||||
_prewindow_deferral_slots,
|
||||
@@ -50,6 +52,7 @@ def _battery(
|
||||
arb_pct: float = 20.0,
|
||||
max_pct: float = 95.0,
|
||||
terminal_soc_value_factor: float = 0.9,
|
||||
discharge_slot_buffer: float = 1.5,
|
||||
) -> SimpleNamespace:
|
||||
uc = uc_wh
|
||||
min_wh = min_pct / 100.0 * uc
|
||||
@@ -65,10 +68,79 @@ def _battery(
|
||||
degradation_cost_czk_kwh=0.15,
|
||||
max_charge_power_w=10_000,
|
||||
max_discharge_power_w=10_000,
|
||||
discharge_slot_buffer=discharge_slot_buffer,
|
||||
planner_terminal_soc_value_factor=terminal_soc_value_factor,
|
||||
)
|
||||
|
||||
|
||||
class EveningPushBudgetTests(unittest.TestCase):
|
||||
"""Večerní tvrdý push: počet slotů z rozpočtu Wh (ne pevné top-3)."""
|
||||
|
||||
@staticmethod
|
||||
def _evening_slots(n: int = 8) -> list[PlanningSlot]:
|
||||
base = datetime(2026, 5, 25, 15, 0, tzinfo=timezone.utc)
|
||||
slots: list[PlanningSlot] = []
|
||||
for i in range(n):
|
||||
slots.append(
|
||||
PlanningSlot(
|
||||
interval_start=base + timedelta(minutes=15 * i),
|
||||
buy_price=2.0,
|
||||
sell_price=4.0 + 0.01 * i,
|
||||
pv_a_forecast_w=0,
|
||||
pv_b_forecast_w=0,
|
||||
load_baseline_w=1000,
|
||||
ev1_connected=False,
|
||||
ev2_connected=False,
|
||||
allow_discharge_export=True,
|
||||
charge_acquisition_buy_czk_kwh=0.5,
|
||||
)
|
||||
)
|
||||
return slots
|
||||
|
||||
def test_budget_scales_with_soc_not_fixed_three(self) -> None:
|
||||
slots = self._evening_slots(8)
|
||||
per_slot = 17_000 * 0.95 * 0.25
|
||||
bat = _battery(uc_wh=64_000.0, min_pct=10.0, max_pct=95.0)
|
||||
soc_high = 0.92 * bat.soc_max_wh
|
||||
profitable = set(range(len(slots)))
|
||||
push_hi = _evening_battery_export_push_indices(
|
||||
slots,
|
||||
profitable_export_ts=profitable,
|
||||
degrad_czk_kwh=0.15,
|
||||
current_soc_wh=soc_high,
|
||||
min_soc_wh=bat.min_soc_wh,
|
||||
soc_max_wh=bat.soc_max_wh,
|
||||
per_slot_discharge_wh=per_slot,
|
||||
discharge_slot_buffer=1.5,
|
||||
)
|
||||
self.assertGreater(len(push_hi), 3)
|
||||
soc_low = bat.min_soc_wh + 100.0
|
||||
push_lo = _evening_battery_export_push_indices(
|
||||
slots,
|
||||
profitable_export_ts=profitable,
|
||||
degrad_czk_kwh=0.15,
|
||||
current_soc_wh=soc_low,
|
||||
min_soc_wh=bat.min_soc_wh,
|
||||
soc_max_wh=bat.soc_max_wh,
|
||||
per_slot_discharge_wh=per_slot,
|
||||
discharge_slot_buffer=1.5,
|
||||
)
|
||||
self.assertEqual(len(push_lo), 0)
|
||||
|
||||
def test_evening_push_budget_matches_r063_formula(self) -> None:
|
||||
bat = _battery(uc_wh=64_000.0, min_pct=10.0, max_pct=95.0)
|
||||
soc = 0.85 * bat.soc_max_wh
|
||||
budget = _evening_push_discharge_budget_wh(
|
||||
current_soc_wh=soc,
|
||||
min_soc_wh=bat.min_soc_wh,
|
||||
soc_max_wh=bat.soc_max_wh,
|
||||
discharge_slot_buffer=1.5,
|
||||
)
|
||||
exportable_full = bat.soc_max_wh - bat.min_soc_wh
|
||||
available = soc - bat.min_soc_wh
|
||||
self.assertAlmostEqual(budget, min(available, exportable_full * 1.5))
|
||||
|
||||
|
||||
class SlotsUntilSellNegativeTests(unittest.TestCase):
|
||||
def test_slots_until_first_negative_sell(self) -> None:
|
||||
base = datetime(2026, 4, 3, 0, 0, tzinfo=timezone.utc)
|
||||
@@ -1230,7 +1302,7 @@ class NegativeSellPvChargeTests(unittest.TestCase):
|
||||
50.0,
|
||||
operating_mode="AUTO",
|
||||
)
|
||||
self.assertEqual(snap.get("planner_build_tag"), "2026-05-28-pre-neg-batt-discharge-v23")
|
||||
self.assertEqual(snap.get("planner_build_tag"), "2026-05-28-evening-push-dynamic-budget-v24")
|
||||
self.assertGreater(
|
||||
results[0].battery_setpoint_w,
|
||||
5_500,
|
||||
@@ -1380,7 +1452,7 @@ class NegativeSellPvChargeTests(unittest.TestCase):
|
||||
50.0,
|
||||
operating_mode="AUTO",
|
||||
)
|
||||
self.assertEqual(snap.get("planner_build_tag"), "2026-05-28-pre-neg-batt-discharge-v23")
|
||||
self.assertEqual(snap.get("planner_build_tag"), "2026-05-28-evening-push-dynamic-budget-v24")
|
||||
self.assertEqual(len(results), len(slots))
|
||||
|
||||
def test_gen_cutoff_full_soc_neg_sell_with_pv_b_feasible(self) -> None:
|
||||
@@ -1444,7 +1516,7 @@ class NegativeSellPvChargeTests(unittest.TestCase):
|
||||
55.0,
|
||||
operating_mode="AUTO",
|
||||
)
|
||||
self.assertEqual(snap.get("planner_build_tag"), "2026-05-28-pre-neg-batt-discharge-v23")
|
||||
self.assertEqual(snap.get("planner_build_tag"), "2026-05-28-evening-push-dynamic-budget-v24")
|
||||
self.assertEqual(len(results), len(slots))
|
||||
|
||||
def test_fixed_tariff_neg_sell_no_grid_export(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user