Skip to content

Commit 6fc4ca3

Browse files
committed
Accept None values in BatteryPool control methods
This allows lower priority actors to completely yield control, which causes the algorithm to use the perferred power of the higher priority actor as the target power. Signed-off-by: Sahas Subramanian <[email protected]>
1 parent c328883 commit 6fc4ca3

File tree

4 files changed

+30
-17
lines changed

4 files changed

+30
-17
lines changed

src/frequenz/sdk/actor/_power_managing/_base_classes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ class Proposal:
7878
"""A proposal for a battery to be charged or discharged."""
7979

8080
source_id: str
81-
preferred_power: Power
81+
preferred_power: Power | None
8282
bounds: tuple[Power, Power] | None
8383
battery_ids: frozenset[int]
8484
priority: int

src/frequenz/sdk/actor/_power_managing/_matryoshka.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,13 @@ def _calc_target_power(
6262
for next_proposal in reversed(proposals):
6363
if upper_bound < lower_bound:
6464
break
65-
if next_proposal.preferred_power > upper_bound:
66-
target_power = upper_bound
67-
elif next_proposal.preferred_power < lower_bound:
68-
target_power = lower_bound
69-
else:
70-
target_power = next_proposal.preferred_power
65+
if next_proposal.preferred_power:
66+
if next_proposal.preferred_power > upper_bound:
67+
target_power = upper_bound
68+
elif next_proposal.preferred_power < lower_bound:
69+
target_power = lower_bound
70+
else:
71+
target_power = next_proposal.preferred_power
7172
if next_proposal.bounds:
7273
lower_bound = max(lower_bound, next_proposal.bounds[0])
7374
upper_bound = min(upper_bound, next_proposal.bounds[1])

src/frequenz/sdk/timeseries/battery_pool/_battery_pool_wrapper.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def __init__(self, battery_pool: BatteryPool, source_id: str | None, priority: i
5858

5959
async def set_power(
6060
self,
61-
preferred_power: Power,
61+
preferred_power: Power | None,
6262
*,
6363
request_timeout: timedelta = timedelta(seconds=5.0),
6464
include_broken_batteries: bool = False,
@@ -103,7 +103,7 @@ async def set_power(
103103

104104
async def charge(
105105
self,
106-
power: Power,
106+
power: Power | None,
107107
*,
108108
request_timeout: timedelta = timedelta(seconds=5.0),
109109
include_broken_batteries: bool = False,
@@ -130,7 +130,7 @@ async def charge(
130130
Raises:
131131
ValueError: If the given power is negative.
132132
"""
133-
if power < Power.zero():
133+
if power and power < Power.zero():
134134
raise ValueError("Charge power must be positive.")
135135
await self._battery_pool._power_manager_requests_sender.send(
136136
_power_managing.Proposal(
@@ -146,7 +146,7 @@ async def charge(
146146

147147
async def discharge(
148148
self,
149-
power: Power,
149+
power: Power | None,
150150
*,
151151
request_timeout: timedelta = timedelta(seconds=5.0),
152152
include_broken_batteries: bool = False,
@@ -173,7 +173,7 @@ async def discharge(
173173
Raises:
174174
ValueError: If the given power is negative.
175175
"""
176-
if power < Power.zero():
176+
if power and power < Power.zero():
177177
raise ValueError("Discharge power must be positive.")
178178
await self._battery_pool._power_manager_requests_sender.send(
179179
_power_managing.Proposal(

tests/actor/_power_managing/test_matryoshka.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ async def test_matryoshka_algorithm() -> None: # pylint: disable=too-many-state
2828

2929
def test_tgt_power(
3030
priority: int,
31-
power: float,
31+
power: float | None,
3232
bounds: tuple[float, float] | None,
3333
expected: float | None,
3434
) -> None:
@@ -39,7 +39,7 @@ def test_tgt_power(
3939
Proposal(
4040
battery_ids=batteries,
4141
source_id=f"actor-{priority}",
42-
preferred_power=Power.from_watts(power),
42+
preferred_power=None if power is None else Power.from_watts(power),
4343
bounds=(Power.from_watts(bounds[0]), Power.from_watts(bounds[1]))
4444
if bounds
4545
else None,
@@ -52,11 +52,17 @@ def test_tgt_power(
5252
)
5353

5454
def test_bounds(
55-
priority: int, expected_power: float, expected_bounds: tuple[float, float]
55+
priority: int,
56+
expected_power: float | None,
57+
expected_bounds: tuple[float, float],
5658
) -> None:
5759
report = algorithm.get_status(batteries, priority, system_bounds, None)
58-
assert report.target_power is not None and report.inclusion_bounds is not None
59-
assert report.target_power.as_watts() == expected_power
60+
if expected_power is None:
61+
assert report.target_power is None
62+
else:
63+
assert report.target_power is not None
64+
assert report.target_power.as_watts() == expected_power
65+
assert report.inclusion_bounds is not None
6066
assert report.inclusion_bounds.lower.as_watts() == expected_bounds[0]
6167
assert report.inclusion_bounds.upper.as_watts() == expected_bounds[1]
6268

@@ -142,3 +148,9 @@ def test_bounds(
142148

143149
test_tgt_power(priority=1, power=200.0, bounds=(50, 200), expected=100.0)
144150
test_bounds(priority=1, expected_power=100.0, expected_bounds=(-100.0, 100.0))
151+
152+
test_tgt_power(priority=1, power=0.0, bounds=(-200, 200), expected=0.0)
153+
test_bounds(priority=1, expected_power=0.0, expected_bounds=(-100.0, 100.0))
154+
155+
test_tgt_power(priority=1, power=None, bounds=(-200, 200), expected=50.0)
156+
test_bounds(priority=1, expected_power=50.0, expected_bounds=(-100.0, 100.0))

0 commit comments

Comments
 (0)