Skip to content

Commit eee3520

Browse files
committed
Use exclusion bounds while calculating target_power
Also extract the calculation logic into a separate function. Signed-off-by: Sahas Subramanian <[email protected]>
1 parent f86f6cd commit eee3520

File tree

1 file changed

+61
-9
lines changed

1 file changed

+61
-9
lines changed

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

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,60 @@
3636
_logger = logging.getLogger(__name__)
3737

3838

39+
# Just 20 lines of code in this function, but unfortunately 8 of those are return
40+
# statements, and that's too many for pylint.
41+
def _clamp_to_bounds( # pylint: disable=too-many-return-statements
42+
value: Power,
43+
lower_bound: Power,
44+
upper_bound: Power,
45+
exclusion_bounds: timeseries.Bounds[Power] | None,
46+
) -> Power:
47+
"""Clamp the given value to the given bounds.
48+
49+
Args:
50+
value: The value to clamp.
51+
lower_bound: The lower bound to clamp to.
52+
upper_bound: The upper bound to clamp to.
53+
exclusion_bounds: The exclusion bounds to clamp outside of.
54+
55+
Returns:
56+
The clamped value.
57+
"""
58+
# If the given bounds are within the exclusion bounds, return zero.
59+
#
60+
# And if the given bounds overlap with the exclusion bounds on one side, and the
61+
# given power is in that overlap region, clamp it to the exclusion bounds on that
62+
# side.
63+
if exclusion_bounds is not None:
64+
match _check_exclusion_bounds_overlap(
65+
lower_bound, upper_bound, exclusion_bounds
66+
):
67+
case (True, True):
68+
return Power.zero()
69+
case (True, False):
70+
if value < exclusion_bounds.upper:
71+
return exclusion_bounds.upper
72+
case (False, True):
73+
if value > exclusion_bounds.lower:
74+
return exclusion_bounds.lower
75+
76+
# If the given value is outside the given bounds, clamp it to the closest bound.
77+
if value < lower_bound:
78+
return lower_bound
79+
if value > upper_bound:
80+
return upper_bound
81+
82+
# If the given value is within the exclusion bounds and the exclusion bounds are
83+
# within the given bounds, clamp the given value to the closest exclusion bound.
84+
if exclusion_bounds is not None:
85+
if exclusion_bounds.lower < value < exclusion_bounds.upper:
86+
if value - exclusion_bounds.lower < exclusion_bounds.upper - value:
87+
return exclusion_bounds.lower
88+
return exclusion_bounds.upper
89+
90+
return value
91+
92+
3993
def _check_exclusion_bounds_overlap(
4094
lower_bound: Power,
4195
upper_bound: Power,
@@ -148,12 +202,12 @@ def _calc_target_power(
148202
if upper_bound < lower_bound:
149203
break
150204
if next_proposal.preferred_power:
151-
if next_proposal.preferred_power > upper_bound:
152-
target_power = upper_bound
153-
elif next_proposal.preferred_power < lower_bound:
154-
target_power = lower_bound
155-
else:
156-
target_power = next_proposal.preferred_power
205+
target_power = _clamp_to_bounds(
206+
next_proposal.preferred_power,
207+
lower_bound,
208+
upper_bound,
209+
exclusion_bounds,
210+
)
157211
proposal_lower, proposal_upper = (
158212
next_proposal.bounds.lower or lower_bound,
159213
next_proposal.bounds.upper or upper_bound,
@@ -308,10 +362,8 @@ def get_status(
308362
calc_lower_bound = max(lower_bound, proposal_lower)
309363
calc_upper_bound = min(upper_bound, proposal_upper)
310364
if calc_lower_bound <= calc_upper_bound:
311-
lower_bound = calc_lower_bound
312-
upper_bound = calc_upper_bound
313365
lower_bound, upper_bound = _adjust_exclusion_bounds(
314-
lower_bound, upper_bound, exclusion_bounds
366+
calc_lower_bound, calc_upper_bound, exclusion_bounds
315367
)
316368
else:
317369
break

0 commit comments

Comments
 (0)