Skip to content

Commit a0137e0

Browse files
committed
Implement shifting of power when proposals don't have bounds
When a proposal doesn't have bounds, the lower priority actors will see shifted bounds and their target powers will be shifted. When a proposal has bounds, lower priority actors will see restricted bounds based on the proposal. Signed-off-by: Sahas Subramanian <[email protected]>
1 parent baa09bf commit a0137e0

File tree

2 files changed

+59
-12
lines changed

2 files changed

+59
-12
lines changed

src/frequenz/sdk/microgrid/_power_managing/_shifting_matryoshka.py

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ def _calc_target_power(
8080
):
8181
exclusion_bounds = system_bounds.exclusion_bounds
8282

83+
unshifted_power = Power.zero()
8384
target_power = Power.zero()
8485
for next_proposal in sorted(proposals, reverse=True):
8586
if upper_bound < lower_bound:
@@ -92,15 +93,29 @@ def _calc_target_power(
9293
exclusion_bounds,
9394
):
9495
case (None, power) | (power, None) if power:
95-
target_power = power
96+
unshifted_power = power
9697
case (power_low, power_high) if power_low and power_high:
9798
if (
9899
power_high - next_proposal.preferred_power
99100
< next_proposal.preferred_power - power_low
100101
):
101-
target_power = power_high
102+
unshifted_power = power_high
102103
else:
103-
target_power = power_low
104+
unshifted_power = power_low
105+
case _:
106+
pass
107+
108+
# Shift the bounds if no clamping bounds are specified.
109+
if (
110+
next_proposal.bounds.lower is None
111+
and next_proposal.bounds.upper is None
112+
and next_proposal.preferred_power is not None
113+
):
114+
lower_bound = lower_bound - unshifted_power
115+
upper_bound = upper_bound - unshifted_power
116+
target_power += unshifted_power
117+
unshifted_power = Power.zero()
118+
continue
104119

105120
proposal_lower = next_proposal.bounds.lower or lower_bound
106121
proposal_upper = next_proposal.bounds.upper or upper_bound
@@ -118,6 +133,8 @@ def _calc_target_power(
118133
lower_bound, upper_bound, exclusion_bounds
119134
)
120135

136+
target_power += unshifted_power
137+
121138
return target_power
122139

123140
def _validate_component_ids(
@@ -212,7 +229,7 @@ def calculate_target_power(
212229
return None
213230

214231
@override
215-
def get_status(
232+
def get_status( # pylint: disable=too-many-locals
216233
self,
217234
component_ids: frozenset[int],
218235
priority: int,
@@ -247,11 +264,41 @@ def get_status(
247264
):
248265
exclusion_bounds = system_bounds.exclusion_bounds
249266

267+
limited_power = Power.zero()
250268
for next_proposal in sorted(
251269
self._component_buckets.get(component_ids, []), reverse=True
252270
):
253271
if next_proposal.priority <= priority:
254272
break
273+
if next_proposal.preferred_power:
274+
match _bounds.clamp_to_bounds(
275+
next_proposal.preferred_power,
276+
lower_bound,
277+
upper_bound,
278+
exclusion_bounds,
279+
):
280+
case (None, power) | (power, None) if power:
281+
limited_power = power
282+
case (power_low, power_high) if power_low and power_high:
283+
if (
284+
power_high - next_proposal.preferred_power
285+
< next_proposal.preferred_power - power_low
286+
):
287+
limited_power = power_high
288+
else:
289+
limited_power = power_low
290+
case _:
291+
pass
292+
# Shift the bounds if no clamping bounds are specified.
293+
if (
294+
next_proposal.bounds.lower is None
295+
and next_proposal.bounds.upper is None
296+
and next_proposal.preferred_power is not None
297+
):
298+
lower_bound = lower_bound - limited_power
299+
upper_bound = upper_bound - limited_power
300+
limited_power = Power.zero()
301+
continue
255302
proposal_lower = next_proposal.bounds.lower or lower_bound
256303
proposal_upper = next_proposal.bounds.upper or upper_bound
257304
match _bounds.check_exclusion_bounds_overlap(

tests/actor/_power_managing/test_shifting_matryoshka.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ async def test_matryoshka_no_excl() -> None: # pylint: disable=too-many-stateme
142142
tester.bounds(priority=2, expected_power=40.0, expected_bounds=(10.0, 50.0))
143143
tester.bounds(priority=1, expected_power=40.0, expected_bounds=(40.0, 50.0))
144144

145-
tester.tgt_power(priority=2, power=0.0, bounds=(None, None), expected=30.0)
145+
tester.tgt_power(priority=2, power=0.0, bounds=(-200.0, 200.0), expected=30.0)
146146
tester.bounds(priority=4, expected_power=30.0, expected_bounds=(-200.0, 200.0))
147147
tester.bounds(priority=3, expected_power=30.0, expected_bounds=(-200.0, 200.0))
148148
tester.bounds(priority=2, expected_power=30.0, expected_bounds=(10.0, 50.0))
@@ -329,13 +329,13 @@ async def test_matryoshka_with_excl_3() -> None:
329329
)
330330

331331
tester = StatefulTester(batteries, system_bounds)
332-
tester.tgt_power(priority=2, power=10.0, bounds=(None, None), expected=30.0)
333-
tester.tgt_power(priority=2, power=-10.0, bounds=(None, None), expected=-30.0)
334-
tester.tgt_power(priority=2, power=0.0, bounds=(None, None), expected=0.0)
335-
tester.tgt_power(priority=3, power=20.0, bounds=(None, None), expected=None)
336-
tester.tgt_power(priority=1, power=-20.0, bounds=(None, None), expected=-30.0)
337-
tester.tgt_power(priority=3, power=None, bounds=(None, None), expected=None)
338-
tester.tgt_power(priority=1, power=None, bounds=(None, None), expected=0.0)
332+
tester.tgt_power(priority=2, power=10.0, bounds=(-200.0, 200.0), expected=30.0)
333+
tester.tgt_power(priority=2, power=-10.0, bounds=(-200.0, 200.0), expected=-30.0)
334+
tester.tgt_power(priority=2, power=0.0, bounds=(-200.0, 200.0), expected=0.0)
335+
tester.tgt_power(priority=3, power=20.0, bounds=(-200.0, 200.0), expected=None)
336+
tester.tgt_power(priority=1, power=-20.0, bounds=(-200.0, 200.0), expected=-30.0)
337+
tester.tgt_power(priority=3, power=None, bounds=(-200.0, 200.0), expected=None)
338+
tester.tgt_power(priority=1, power=None, bounds=(-200.0, 200.0), expected=0.0)
339339

340340
tester.tgt_power(priority=2, power=25.0, bounds=(25.0, 50.0), expected=30.0)
341341
tester.bounds(priority=2, expected_power=30.0, expected_bounds=(-200.0, 200.0))

0 commit comments

Comments
 (0)