-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement the ShiftingMatryoshka
algorithm for the PowerManager
#1146
base: v1.x.x
Are you sure you want to change the base?
Conversation
649caf6
to
3c38ef5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just gave this a quick look, only to the structural changes.
I wonder why removing the old algorithm if we can support more than one. I can understand if it is to remove the maintenance cost, but I would delay the removal until we collected more experience.
src/frequenz/sdk/microgrid/_power_managing/_power_managing_actor.py
Outdated
Show resolved
Hide resolved
3c38ef5
to
7f405bb
Compare
This was because pylint was complaining about duplicate lines, plus it is a separate commit, which should be easy to revert if necessary. If you prefer, I can drop that commit, but we'll need to find a way to silence pylint either by refactoring the tests or pylint config. But if we remove the current PR's limiting-only option like I mentioned in some comments above, this would stop being an issue as well, so we can revisit this after we decide that. |
We should definitely not remove code because pylint complains about duplication :D Can't we just add a |
7f405bb
to
509c528
Compare
eff0cf3
to
6e13739
Compare
…requenz-floss#970)" This reverts commit 6a54a0b, reversing changes made to 4c71140. Signed-off-by: Sahas Subramanian <[email protected]>
…er (frequenz-floss#957)" This reverts commit d5d74a3, reversing changes made to 8e5d65e. Signed-off-by: Sahas Subramanian <[email protected]>
This makes the function easier to distinguish from per-priority bounds produced by the power manager. Signed-off-by: Sahas Subramanian <[email protected]>
The shifting logic will be implemented in the next commit. Signed-off-by: Sahas Subramanian <[email protected]>
6e13739
to
4b858b9
Compare
This is ready for review again. |
fdf5479
to
68c72cd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR replaces the original Matryoshka algorithm with the new ShiftingMatryoshka algorithm in the power manager while removing the “set_operating_point” parameter from pool APIs and associated documentation.
- Introduces a new algorithm implementation in _shifting_matryoshka.py that aggregates proposals using shifted bounds.
- Updates documentation, tests, and pool creation methods to reflect the removal of the set_operating_point parameter.
- Refactors the power managing actor to use a unified subscriptions structure and maps the algorithm selection accordingly.
Reviewed Changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated no comments.
Show a summary per file
File | Description |
---|---|
src/frequenz/sdk/microgrid/_power_managing/_shifting_matryoshka.py | New algorithm implementation and handling of bounds via power shifting. |
src/frequenz/sdk/microgrid/init.py | Updated documentation to describe the new handling of power proposals. |
src/frequenz/sdk/timeseries/* | Removed obsolete set_operating_point parameter and cleaned up related docstrings. |
tests/* | Updated tests to match changes in target power calculation and bounds shifting. |
RELEASE_NOTES.md | Updated to document removal of the set_operating_point parameter and details of the new algorithm. |
src/frequenz/sdk/microgrid/_power_managing/_power_managing_actor.py | Refactored to use the new algorithm selection and unified subscriptions mapping. |
src/frequenz/sdk/microgrid/_data_pipeline.py | Removed extra set_operating_point parameter from pool creation methods. |
Comments suppressed due to low confidence (2)
src/frequenz/sdk/microgrid/_power_managing/_shifting_matryoshka.py:147
- [nitpick] Verify that subtracting the proposal power from the lower bound produces the intended result in all edge cases. Consider adding an inline comment to explain the rationale behind shifting the bounds in this manner.
lower_bound = lower_bound - proposal_power
src/frequenz/sdk/microgrid/_power_managing/_power_managing_actor.py:79
- [nitpick] Consider renaming '_subscriptions' to a more descriptive name (e.g. '_report_subscriptions') to clarify that these senders are used for reporting status.
self._subscriptions: dict[frozenset[int], dict[int, Sender[_Report]]] = {}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't do a super in-depth review, in particular I didn't try to validate or fully understand the algorithm code, but I did follow the examples/new tests to understand the algorithm and it looks good to me and it was easier to follow (not sure if because I'm now more familiar with the algorithm or because the new wording focused on shifting :D).
Just a few minor comments, otherwise LGTM.
One final question though. The old matryoshka algorithm still works as before the operating point setting/group/shifting was added, right? The removal of the set_operating_point
option only affects that?
"""A power manager implementation that uses the matryoshka algorithm. | ||
|
||
When there are multiple proposals from different actors for the same set of components, | ||
the matryoshka algorithm will consider the priority of the actors, the bounds they set | ||
and their preferred power to determine the target power for the components. | ||
|
||
The preferred power of lower priority actors will take precedence as long as they | ||
respect the bounds set by higher priority actors. If lower priority actors request | ||
power values outside the bounds set by higher priority actors, the target power will | ||
be the closest value to the preferred power that is within the bounds. | ||
|
||
When there is only a single proposal for a set of components, its preferred power would | ||
be the target power, as long as it falls within the system power bounds for the | ||
components. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would move this docs to the class, as the module is private, it will never be rendered in the documentation website otherwise (unless you are explicitly including it in docs/
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh wow, I forgot to update these docs. These are for the old matryoshka algorithm. I will do that and move it to the class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hehe, right, I was reviewing commit by commit and commented on this when I saw you added the file but then didn't realized you didn't update it afterwards.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
proposals: set[Proposal], | ||
system_bounds: SystemBounds, | ||
priority: int | None = None, | ||
) -> tuple[Power | None, Bounds[Power]]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm shaking while about bounds due to PTSD after fighting with bounds updates for the v0.17 microgrid API. I hope it is not too hard to merge 😱
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it should be fine because you're handling it in the compatibility layer from the client right?
proposal_lower = next_proposal.bounds.lower or lower_bound | ||
proposal_upper = next_proposal.bounds.upper or upper_bound | ||
proposal_power = next_proposal.preferred_power | ||
|
||
if proposal_upper < proposal_lower: | ||
continue | ||
|
||
if proposal_power and ( | ||
proposal_power < proposal_lower or proposal_power > proposal_upper | ||
): | ||
continue | ||
|
||
if proposal_lower >= upper_bound: | ||
proposal_lower = upper_bound | ||
proposal_upper = upper_bound | ||
elif proposal_upper <= lower_bound: | ||
proposal_lower = lower_bound | ||
proposal_upper = lower_bound | ||
|
||
lower_bound = max(lower_bound, proposal_lower) | ||
upper_bound = min(upper_bound, proposal_upper) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are all these checks for proposal_power
, proposal_lower
and proposal_upper
intentionally covering for an eventual 0.0
besides None
or was the intention to only match None
? Since in the code below, you are actually checking for None
explicitly, I guess it is intentional, but maybe it would be best to add some comment to say it explicitly (either way).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
intentionally covering for an eventual
0.0
besidesNone
or was the intention to only matchNone
I don't understand what that means. Of course, I can add comments for these, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added more comments, also fixed a bug.
| Actor | Priority | System Bounds | Requested Bounds | Requested | Aggregate | | ||
| | | | | Power | Power | | ||
|-------|----------|-----------------|------------------|--------------|-----------| | ||
| A | 3 | -100kW .. 100kW | None | 20kW | 20kW | | ||
| B | 2 | -120kW .. 80kW | None | 50kW | 70kW | | ||
| C | 1 | -170kW .. 30kW | None | 50kW | 100kW | | ||
| | | | | target power | 100kW | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe like in the test, adding a column with the "adjusted power" could add some extra clarity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
@@ -174,53 +174,128 @@ | |||
controlling batteries, power could be distributed based on the `SoC` of the | |||
individual batteries, to keep the batteries in balance. | |||
|
|||
### Resolving conflicting power proposals | |||
### How to work with other actors |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All (or at least some of) the old documentation here should still be useful for people interested in the old Matryoshka
algorithm, so not sure if replacing it is the best choice. I wonder if the docs shouldn't be moved to the algorithms themselves, and either referenced here (or see if there is a way to include it with mkdocs, but this won't work/look nice when reading the docs from the editor). Maybe in here we can just say more generically that there are 2 available algorithms, say which is the default, and then just link to the algorithm docs for details? If the plan is to remove the old algo soon, I guess it is fine to move forward as it is, but if we really want to support more than one algorithm, the docs should be structured in a way that we can explain all of them and switch the default easily without rewriting the whole __init__.py
docs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code for the old algorithm is still there, but users cannot choose it. Chances are we'll not need the old algorithm anymore. We could introduce new algorithms in the future just for specific component types, like EVs, etc. But we don't have to change the docs structure until we offer multiple algorithms for them to choose from.
Signed-off-by: Sahas Subramanian <[email protected]>
Signed-off-by: Sahas Subramanian <[email protected]>
Signed-off-by: Sahas Subramanian <[email protected]>
Signed-off-by: Sahas Subramanian <[email protected]>
Signed-off-by: Sahas Subramanian <[email protected]>
With this we only have to write the details on how the power manager works only once. Signed-off-by: Sahas Subramanian <[email protected]>
Signed-off-by: Sahas Subramanian <[email protected]>
Earlier, the exclusion bounds were applied to every proposal, and in some cases, that was leading to much higher target powers than what was necessary. Signed-off-by: Sahas Subramanian <[email protected]>
And refactor the _calc_targets method to use the new function to eliminate code duplication. Signed-off-by: Sahas Subramanian <[email protected]>
68c72cd
to
9cb09d7
Compare
Instead the proposed powers need to be clamped to the available bounds. Signed-off-by: Sahas Subramanian <[email protected]>
Signed-off-by: Sahas Subramanian <[email protected]>
9cb09d7
to
95e326c
Compare
This PR removes the
set_operating_point
feature and replaces the power manager's originalMatryoshka
algorithm with the newShiftingMatryoshka
algorithm.With the new algorithm, power proposals from actors are added to get the target power for the components, and higher-priority actors can limit the bounds available to lower-priority actors.