Skip to content

Commit f75eced

Browse files
committed
impl inprogress
Signed-off-by: Sahas Subramanian <[email protected]>
1 parent 1247fa9 commit f75eced

File tree

3 files changed

+263
-219
lines changed

3 files changed

+263
-219
lines changed

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

Lines changed: 105 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
from frequenz.quantities import Power
2727
from typing_extensions import override
2828

29+
from frequenz.sdk.timeseries._base_types import Bounds
30+
2931
from ... import timeseries
3032
from . import _bounds
3133
from ._base_classes import BaseAlgorithm, Proposal, _Report
@@ -37,7 +39,7 @@
3739

3840

3941
class ShiftingMatryoshka(BaseAlgorithm):
40-
"""The matryoshka algorithm."""
42+
"""The ShiftingMatryoshka algorithm."""
4143

4244
def __init__(self, max_proposal_age: timedelta) -> None:
4345
"""Create a new instance of the matryoshka algorithm."""
@@ -78,62 +80,79 @@ def _calc_target_power(
7880
system_bounds.exclusion_bounds.lower != Power.zero()
7981
or system_bounds.exclusion_bounds.upper != Power.zero()
8082
):
81-
exclusion_bounds = system_bounds.exclusion_bounds
83+
exclusion_bounds = Bounds(
84+
system_bounds.exclusion_bounds.lower,
85+
system_bounds.exclusion_bounds.upper,
86+
)
8287

83-
unshifted_power = Power.zero()
8488
target_power = Power.zero()
8589
for next_proposal in sorted(proposals, reverse=True):
90+
unshifted_power = Power.zero()
91+
8692
if upper_bound < lower_bound:
8793
break
88-
if next_proposal.preferred_power:
89-
match _bounds.clamp_to_bounds(
90-
next_proposal.preferred_power,
94+
95+
proposal_lower = next_proposal.bounds.lower or lower_bound
96+
proposal_upper = next_proposal.bounds.upper or upper_bound
97+
proposal_power = next_proposal.preferred_power
98+
99+
if proposal_upper < proposal_lower:
100+
# TODO: needs to be logged elsewhere.
101+
continue
102+
103+
if proposal_power and (
104+
proposal_power < proposal_lower or proposal_power > proposal_upper
105+
):
106+
# TODO: needs to be logged elsewhere.
107+
continue
108+
109+
if proposal_lower >= upper_bound and proposal_power:
110+
proposal_power = upper_bound
111+
elif proposal_upper <= lower_bound and proposal_power:
112+
proposal_power = lower_bound
113+
else:
114+
lower_bound = max(lower_bound, proposal_lower)
115+
upper_bound = min(upper_bound, proposal_upper)
116+
117+
if proposal_power:
118+
clamped = _bounds.clamp_to_bounds(
119+
proposal_power,
91120
lower_bound,
92121
upper_bound,
93122
exclusion_bounds,
94-
):
123+
)
124+
match clamped:
95125
case (None, power) | (power, None) if power:
96126
unshifted_power = power
97127
case (power_low, power_high) if power_low and power_high:
98-
if (
99-
power_high - next_proposal.preferred_power
100-
< next_proposal.preferred_power - power_low
101-
):
128+
if power_high - proposal_power < proposal_power - power_low:
102129
unshifted_power = power_high
103130
else:
104131
unshifted_power = power_low
105132
case _:
106133
pass
107134

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
135+
lower_bound, upper_bound = _bounds.adjust_exclusion_bounds(
136+
lower_bound, upper_bound, exclusion_bounds
137+
)
138+
139+
lower_bound = lower_bound - unshifted_power
140+
upper_bound = upper_bound - unshifted_power
141+
target_power += unshifted_power
142+
143+
if exclusion_bounds is not None:
144+
exclusion_bounds = Bounds[Power](
145+
exclusion_bounds.lower - unshifted_power,
146+
exclusion_bounds.upper - unshifted_power,
147+
)
119148

120-
proposal_lower = next_proposal.bounds.lower or lower_bound
121-
proposal_upper = next_proposal.bounds.upper or upper_bound
122149
# If the bounds from the current proposal are fully within the exclusion
123150
# bounds, then don't use them to narrow the bounds further. This allows
124151
# subsequent proposals to not be blocked by the current proposal.
125-
match _bounds.check_exclusion_bounds_overlap(
126-
proposal_lower, proposal_upper, exclusion_bounds
127-
):
128-
case (True, True):
129-
continue
130-
lower_bound = max(lower_bound, proposal_lower)
131-
upper_bound = min(upper_bound, proposal_upper)
132-
lower_bound, upper_bound = _bounds.adjust_exclusion_bounds(
152+
if _bounds.check_exclusion_bounds_overlap(
133153
lower_bound, upper_bound, exclusion_bounds
134-
)
135-
136-
target_power += unshifted_power
154+
) == (True, True):
155+
continue
137156

138157
return target_power
139158

@@ -262,58 +281,72 @@ def get_status( # pylint: disable=too-many-locals
262281
system_bounds.exclusion_bounds.lower != Power.zero()
263282
or system_bounds.exclusion_bounds.upper != Power.zero()
264283
):
265-
exclusion_bounds = system_bounds.exclusion_bounds
284+
exclusion_bounds = Bounds(
285+
system_bounds.exclusion_bounds.lower,
286+
system_bounds.exclusion_bounds.upper,
287+
)
266288

267-
limited_power = Power.zero()
268289
for next_proposal in sorted(
269290
self._component_buckets.get(component_ids, []), reverse=True
270291
):
292+
unshifted_power = Power.zero()
271293
if next_proposal.priority <= priority:
272294
break
273-
if next_proposal.preferred_power:
274-
match _bounds.clamp_to_bounds(
275-
next_proposal.preferred_power,
295+
296+
proposal_lower = next_proposal.bounds.lower or lower_bound
297+
proposal_upper = next_proposal.bounds.upper or upper_bound
298+
proposal_power = next_proposal.preferred_power
299+
300+
if proposal_upper < proposal_lower:
301+
continue
302+
303+
if proposal_power and (
304+
proposal_power < proposal_lower or proposal_power > proposal_upper
305+
):
306+
continue
307+
308+
if proposal_lower > upper_bound and proposal_power:
309+
proposal_power = upper_bound
310+
elif proposal_upper < lower_bound and proposal_power:
311+
proposal_power = lower_bound
312+
else:
313+
lower_bound = max(lower_bound, proposal_lower)
314+
upper_bound = min(upper_bound, proposal_upper)
315+
316+
if proposal_power:
317+
clamped = _bounds.clamp_to_bounds(
318+
proposal_power,
276319
lower_bound,
277320
upper_bound,
278321
exclusion_bounds,
279-
):
322+
)
323+
match clamped:
280324
case (None, power) | (power, None) if power:
281-
limited_power = power
325+
unshifted_power = power
282326
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
327+
if power_high - proposal_power < proposal_power - power_low:
328+
unshifted_power = power_high
288329
else:
289-
limited_power = power_low
330+
unshifted_power = power_low
290331
case _:
291332
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
302-
proposal_lower = next_proposal.bounds.lower or lower_bound
303-
proposal_upper = next_proposal.bounds.upper or upper_bound
304-
match _bounds.check_exclusion_bounds_overlap(
305-
proposal_lower, proposal_upper, exclusion_bounds
306-
):
307-
case (True, True):
308-
continue
309-
calc_lower_bound = max(lower_bound, proposal_lower)
310-
calc_upper_bound = min(upper_bound, proposal_upper)
311-
if calc_lower_bound <= calc_upper_bound:
312-
lower_bound, upper_bound = _bounds.adjust_exclusion_bounds(
313-
calc_lower_bound, calc_upper_bound, exclusion_bounds
333+
334+
lower_bound, upper_bound = _bounds.adjust_exclusion_bounds(
335+
lower_bound, upper_bound, exclusion_bounds
336+
)
337+
lower_bound = lower_bound - unshifted_power
338+
upper_bound = upper_bound - unshifted_power
339+
340+
if exclusion_bounds is not None:
341+
exclusion_bounds = Bounds[Power](
342+
exclusion_bounds.lower - unshifted_power,
343+
exclusion_bounds.upper - unshifted_power,
314344
)
315-
else:
316-
break
345+
if _bounds.check_exclusion_bounds_overlap(
346+
lower_bound, upper_bound, exclusion_bounds
347+
) == (True, True):
348+
continue
349+
317350
return _Report(
318351
target_power=target_power,
319352
_inclusion_bounds=timeseries.Bounds[Power](

0 commit comments

Comments
 (0)