Skip to content

Commit a840276

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

File tree

2 files changed

+278
-163
lines changed

2 files changed

+278
-163
lines changed

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

Lines changed: 120 additions & 66 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,88 @@ 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+
print(
125+
f"Clamping power {next_proposal.preferred_power} to bounds"
126+
+ f" {proposal_lower} and {proposal_upper} with exclusion bounds"
127+
+ f" {exclusion_bounds} gives {clamped}"
128+
)
129+
match clamped:
95130
case (None, power) | (power, None) if power:
96131
unshifted_power = power
97132
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-
):
133+
if power_high - proposal_power < proposal_power - power_low:
102134
unshifted_power = power_high
103135
else:
104136
unshifted_power = power_low
105137
case _:
106138
pass
107139

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
140+
lower_bound, upper_bound = _bounds.adjust_exclusion_bounds(
141+
lower_bound, upper_bound, exclusion_bounds
142+
)
143+
144+
lower_bound = lower_bound - unshifted_power
145+
upper_bound = upper_bound - unshifted_power
146+
target_power += unshifted_power
147+
148+
if exclusion_bounds is not None:
149+
exclusion_bounds = Bounds[Power](
150+
exclusion_bounds.lower - unshifted_power,
151+
exclusion_bounds.upper - unshifted_power,
152+
)
119153

120-
proposal_lower = next_proposal.bounds.lower or lower_bound
121-
proposal_upper = next_proposal.bounds.upper or upper_bound
122154
# If the bounds from the current proposal are fully within the exclusion
123155
# bounds, then don't use them to narrow the bounds further. This allows
124156
# 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(
157+
if _bounds.check_exclusion_bounds_overlap(
133158
lower_bound, upper_bound, exclusion_bounds
134-
)
135-
136-
target_power += unshifted_power
159+
) == (True, True):
160+
print(
161+
f"Bounds {lower_bound} and {upper_bound} are fully within"
162+
+ f" exclusion bounds {exclusion_bounds}."
163+
)
164+
continue
137165

138166
return target_power
139167

@@ -262,58 +290,84 @@ def get_status( # pylint: disable=too-many-locals
262290
system_bounds.exclusion_bounds.lower != Power.zero()
263291
or system_bounds.exclusion_bounds.upper != Power.zero()
264292
):
265-
exclusion_bounds = system_bounds.exclusion_bounds
293+
exclusion_bounds = Bounds(
294+
system_bounds.exclusion_bounds.lower,
295+
system_bounds.exclusion_bounds.upper,
296+
)
266297

267-
limited_power = Power.zero()
268298
for next_proposal in sorted(
269299
self._component_buckets.get(component_ids, []), reverse=True
270300
):
301+
unshifted_power = Power.zero()
271302
if next_proposal.priority <= priority:
272303
break
304+
305+
proposal_lower = next_proposal.bounds.lower or lower_bound
306+
proposal_upper = next_proposal.bounds.upper or upper_bound
307+
proposal_power = next_proposal.preferred_power
308+
309+
if proposal_upper < proposal_lower:
310+
continue
311+
312+
if proposal_power and (
313+
proposal_power < proposal_lower or proposal_power > proposal_upper
314+
):
315+
continue
316+
317+
if proposal_lower > upper_bound and proposal_power:
318+
proposal_power = upper_bound
319+
elif proposal_upper < lower_bound and proposal_power:
320+
proposal_power = lower_bound
321+
else:
322+
lower_bound = max(lower_bound, proposal_lower)
323+
upper_bound = min(upper_bound, proposal_upper)
324+
273325
if next_proposal.preferred_power:
274-
match _bounds.clamp_to_bounds(
326+
clamped = _bounds.clamp_to_bounds(
275327
next_proposal.preferred_power,
276328
lower_bound,
277329
upper_bound,
278330
exclusion_bounds,
279-
):
331+
)
332+
print(
333+
f"Clamping power {next_proposal.preferred_power} to bounds"
334+
+ f" {lower_bound} and {upper_bound} with exclusion bounds"
335+
+ f" {exclusion_bounds} gives {clamped}"
336+
)
337+
match clamped:
280338
case (None, power) | (power, None) if power:
281-
limited_power = power
339+
unshifted_power = power
282340
case (power_low, power_high) if power_low and power_high:
283341
if (
284342
power_high - next_proposal.preferred_power
285343
< next_proposal.preferred_power - power_low
286344
):
287-
limited_power = power_high
345+
unshifted_power = power_high
288346
else:
289-
limited_power = power_low
347+
unshifted_power = power_low
290348
case _:
291349
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
350+
351+
lower_bound, upper_bound = _bounds.adjust_exclusion_bounds(
352+
lower_bound, upper_bound, exclusion_bounds
353+
)
354+
lower_bound = lower_bound - unshifted_power
355+
upper_bound = upper_bound - unshifted_power
356+
357+
if exclusion_bounds is not None:
358+
exclusion_bounds = Bounds[Power](
359+
exclusion_bounds.lower - unshifted_power,
360+
exclusion_bounds.upper - unshifted_power,
314361
)
315-
else:
316-
break
362+
if _bounds.check_exclusion_bounds_overlap(
363+
lower_bound, upper_bound, exclusion_bounds
364+
) == (True, True):
365+
print(
366+
f"Bounds {lower_bound} and {upper_bound} are fully within"
367+
+ f" exclusion bounds {exclusion_bounds}."
368+
)
369+
continue
370+
317371
return _Report(
318372
target_power=target_power,
319373
_inclusion_bounds=timeseries.Bounds[Power](

0 commit comments

Comments
 (0)