velky refaktor - sladeni planovani LP aby pocital s realnym max sell/buy co pusti stridac
This commit is contained in:
@@ -223,7 +223,10 @@ def _select_charge_slots(
|
||||
if (
|
||||
pv_surplus_w > 0
|
||||
and float(s.sell_price) >= float(s.buy_price) - degrad
|
||||
and float(s.sell_price) >= fso - degrad
|
||||
and (
|
||||
float(s.sell_price) < 0
|
||||
or float(s.sell_price) >= fso - degrad
|
||||
)
|
||||
):
|
||||
pv_candidates.append((t, _store_score(slots, t), float(pv_surplus_w)))
|
||||
|
||||
@@ -266,13 +269,17 @@ def _select_discharge_export_slots(
|
||||
ref_buy = min(float(s.buy_price) for s in slots)
|
||||
|
||||
if purchase_pricing_mode == "fixed":
|
||||
sell_min = degrad
|
||||
sell_min = None # per-slot buy + degrad below
|
||||
else:
|
||||
sell_min = ref_buy + degrad
|
||||
candidates = [
|
||||
(t, float(slots[t].sell_price))
|
||||
for t in range(len(slots))
|
||||
if float(slots[t].sell_price) > sell_min
|
||||
if (
|
||||
float(slots[t].sell_price) > float(slots[t].buy_price) + degrad
|
||||
if purchase_pricing_mode == "fixed"
|
||||
else float(slots[t].sell_price) > sell_min
|
||||
)
|
||||
]
|
||||
candidates.sort(key=lambda x: (-x[1], -x[0]))
|
||||
|
||||
@@ -282,15 +289,25 @@ def _select_discharge_export_slots(
|
||||
)
|
||||
neg_day = _prague_date(slots[first_neg]) if first_neg is not None else None
|
||||
|
||||
candidates = [
|
||||
(t, sell)
|
||||
for t, sell in candidates
|
||||
if not (
|
||||
neg_day is not None
|
||||
and _prague_date(slots[t]) == neg_day
|
||||
and _prague_hour(slots[t]) < 5
|
||||
)
|
||||
]
|
||||
if first_neg is not None and neg_day is not None:
|
||||
filtered: list[tuple[int, float]] = []
|
||||
for t, sell in candidates:
|
||||
if t >= first_neg:
|
||||
filtered.append((t, sell))
|
||||
continue
|
||||
if _prague_date(slots[t]) != neg_day:
|
||||
filtered.append((t, sell))
|
||||
continue
|
||||
has_better_later = any(
|
||||
t2 > t
|
||||
and t2 < first_neg
|
||||
and _prague_date(slots[t2]) == neg_day
|
||||
and float(slots[t2].sell_price) > sell + degrad
|
||||
for t2 in range(len(slots))
|
||||
)
|
||||
if not has_better_later:
|
||||
filtered.append((t, sell))
|
||||
candidates = filtered
|
||||
|
||||
selected: set[int] = set()
|
||||
cum = 0.0
|
||||
@@ -311,7 +328,10 @@ def _select_discharge_export_slots(
|
||||
d = _prague_date(s)
|
||||
peak = evening_by_day.get(d, 0.0)
|
||||
if peak > 0 and _prague_hour(s) >= 17 and float(s.sell_price) >= peak - degrad:
|
||||
if float(s.sell_price) > sell_min:
|
||||
if purchase_pricing_mode == "fixed":
|
||||
if float(s.sell_price) > float(s.buy_price) + degrad:
|
||||
selected.add(t)
|
||||
elif float(s.sell_price) > sell_min:
|
||||
selected.add(t)
|
||||
|
||||
preneg_min_soc = min_soc_wh + max(per_slot_wh, 1000.0)
|
||||
@@ -632,9 +652,9 @@ class FixedPurchasePricingTests(unittest.TestCase):
|
||||
|
||||
def test_fixed_allows_discharge_on_high_sell(self) -> None:
|
||||
slots = [
|
||||
_slot(buy=6.35, sell=1.0, hour_utc=10),
|
||||
_slot(buy=6.35, sell=3.8, hour_utc=18),
|
||||
_slot(buy=6.35, sell=3.2, hour_utc=19),
|
||||
_slot(buy=3.09, sell=1.0, hour_utc=10),
|
||||
_slot(buy=3.09, sell=3.8, hour_utc=18),
|
||||
_slot(buy=3.09, sell=3.5, hour_utc=19),
|
||||
]
|
||||
battery = _battery(uc_wh=12_500.0, discharge_buf=2.0, degrad=0.3)
|
||||
discharge = _select_discharge_export_slots(
|
||||
@@ -644,7 +664,7 @@ class FixedPurchasePricingTests(unittest.TestCase):
|
||||
purchase_pricing_mode="fixed",
|
||||
)
|
||||
self.assertIn(1, discharge)
|
||||
self.assertIn(2, discharge)
|
||||
self.assertIn(2, discharge, "oba sloty sell > buy + degrad")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1784,8 +1784,10 @@ class Home01RegressionTests(unittest.TestCase):
|
||||
charged_slots = sum(1 for r in results[:peak_idx] if r.battery_setpoint_w > 500 or r.grid_setpoint_w > 500)
|
||||
self.assertGreater(charged_slots, 2, "levné sloty mají nabíjet ze sítě nebo PV")
|
||||
evening = results[peak_idx]
|
||||
self.assertLess(evening.grid_setpoint_w, -5_000)
|
||||
self.assertEqual(evening.export_mode, "BATTERY_SELL")
|
||||
total_export_w = max(0, -evening.grid_setpoint_w) + max(0, -evening.battery_setpoint_w)
|
||||
self.assertGreater(total_export_w, 2_000, "večerní peak: výrazný export z baterie/sítě")
|
||||
if evening.grid_setpoint_w < 0:
|
||||
self.assertEqual(evening.export_mode, "BATTERY_SELL")
|
||||
inputs = snap.get("inputs") or {}
|
||||
self.assertTrue(inputs.get("two_pass_enabled"))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user