doladeni odpoledniho dobiti
This commit is contained in:
@@ -106,7 +106,13 @@ def _select_charge_slots(
|
||||
eta = float(getattr(battery, "charge_efficiency", 1.0) or 1.0)
|
||||
max_p_w = float(getattr(battery, "max_charge_power_w", 0.0) or 0.0)
|
||||
per_slot_full_wh = max_p_w * eta * INTERVAL_H
|
||||
if current_soc_wh >= reserve_wh:
|
||||
soc_max_wh = float(getattr(battery, "soc_max_wh", 0) or 0)
|
||||
if charge_buf > 0:
|
||||
charge_target_wh = min(
|
||||
max(energy_to_fill, 0.0) * charge_buf,
|
||||
max(soc_max_wh - float(current_soc_wh), 0.0),
|
||||
)
|
||||
elif current_soc_wh >= reserve_wh:
|
||||
charge_target_wh = max(energy_to_fill, 0.0)
|
||||
else:
|
||||
charge_target_wh = min(
|
||||
@@ -129,13 +135,26 @@ def _select_charge_slots(
|
||||
selected: set[int] = set()
|
||||
grid_filled_wh = 0.0
|
||||
|
||||
buf_mult = charge_buf if charge_buf > 0 else 1.0
|
||||
cap_am = (
|
||||
max(1, min(_MAX_GRID_CHARGE_CAP, int(chg_am / per_slot_full_wh) + 1))
|
||||
max(
|
||||
1,
|
||||
min(
|
||||
_MAX_GRID_CHARGE_CAP,
|
||||
int(chg_am / per_slot_full_wh * buf_mult) + 1,
|
||||
),
|
||||
)
|
||||
if per_slot_full_wh > 0
|
||||
else 6
|
||||
)
|
||||
cap_pm = (
|
||||
max(1, min(_MAX_GRID_CHARGE_CAP, int(chg_pm / per_slot_full_wh) + 1))
|
||||
max(
|
||||
1,
|
||||
min(
|
||||
_MAX_GRID_CHARGE_CAP,
|
||||
int(chg_pm / per_slot_full_wh * buf_mult) + 1,
|
||||
),
|
||||
)
|
||||
if per_slot_full_wh > 0
|
||||
else 6
|
||||
)
|
||||
@@ -166,6 +185,15 @@ def _select_charge_slots(
|
||||
cum += per_slot_full_wh
|
||||
grid_am += 1
|
||||
grid_filled_wh += cum
|
||||
chg_pm = max(chg_pm, charge_target_wh - grid_filled_wh)
|
||||
if per_slot_full_wh > 0:
|
||||
cap_pm = max(
|
||||
cap_pm,
|
||||
min(
|
||||
_MAX_GRID_CHARGE_CAP,
|
||||
int(chg_pm / per_slot_full_wh * buf_mult) + 1,
|
||||
),
|
||||
)
|
||||
|
||||
pm_candidates = [
|
||||
(t, getattr(slots[t], "is_predicted_price", False), float(slots[t].buy_price))
|
||||
@@ -462,6 +490,24 @@ class SelectChargeSlotsTests(unittest.TestCase):
|
||||
self.assertNotIn(0, out, "Při malém rozpočtu má přednost levnější NT, ne VT 1.49")
|
||||
self.assertTrue({1, 2} & out, "NT slot(y) mohou být vybrány")
|
||||
|
||||
def test_pm_grid_gets_unused_am_wh_budget(self) -> None:
|
||||
"""Nečerpaný AM rozpočet → odpolední levné PM sloty mohou dostat allow_charge."""
|
||||
base = datetime(2026, 5, 22, 6, 0, tzinfo=timezone.utc)
|
||||
slots = [
|
||||
_slot(buy=0.55, sell=-0.2, hour_utc=6, interval_start=base),
|
||||
_slot(buy=0.58, sell=-0.2, hour_utc=7, interval_start=base + timedelta(hours=1)),
|
||||
_slot(buy=0.52, sell=-0.25, hour_utc=14, interval_start=base + timedelta(hours=8)),
|
||||
_slot(buy=0.50, sell=-0.25, hour_utc=15, interval_start=base + timedelta(hours=9)),
|
||||
_slot(buy=5.5, sell=3.8, hour_utc=20, interval_start=base + timedelta(hours=14)),
|
||||
]
|
||||
battery = _battery(charge_buf=1.3, uc_wh=64_000.0)
|
||||
out = _select_charge_slots(slots, battery, current_soc_wh=0.12 * battery.usable_capacity_wh)
|
||||
pm_cheap = {2, 3}
|
||||
self.assertTrue(
|
||||
pm_cheap & out,
|
||||
"po levném AM má PM dostat grid charge z nevyčerpaného rozpočtu",
|
||||
)
|
||||
|
||||
def test_ote_slots_prioritized_over_predicted(self) -> None:
|
||||
"""Při stejné ceně má OTE (is_predicted=false) přednost před predikovaným."""
|
||||
slots = [
|
||||
|
||||
Reference in New Issue
Block a user