From 75c257e70b896d0e5e798d91fd74d9b4dd6196f4 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Wed, 6 Sep 2023 10:16:05 +0200 Subject: [PATCH 1/6] Clear release notes Signed-off-by: Leandro Lucarella --- RELEASE_NOTES.md | 120 ++--------------------------------------------- 1 file changed, 4 insertions(+), 116 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 091f2b8dc..61ee6f2ad 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,128 +2,16 @@ ## Summary -This release replaces the `@actor` decorator with a new `Actor` class. + ## Upgrading - -- The `frequenz.sdk.power` package contained the power distribution algorithm, which is for internal use in the sdk, and is no longer part of the public API. - -- `PowerDistributingActor`'s result type `OutOfBound` has been renamed to `OutOfBounds`, and its member variable `bound` has been renamed to `bounds`. - -- The `@actor` decorator was replaced by the new `Actor` class. The main differences between the new class and the old decorator are: - - * It doesn't start automatically, `start()` needs to be called to start an actor (using the `frequenz.sdk.actor.run()` function is recommended). - * The method to implement the main logic was renamed from `run()` to `_run()`, as it is not intended to be run externally. - * Actors can have an optional `name` (useful for debugging/logging purposes). - * The actor will only be restarted if an unhandled `Exception` is raised by `_run()`. It will not be restarted if the `_run()` method finishes normally. If an unhandled `BaseException` is raised instead, it will be re-raised. For normal cancellation the `_run()` method should handle `asyncio.CancelledError` if the cancellation shouldn't be propagated (this is the same as with the decorator). - * The `_stop()` method is public (`stop()`) and will `cancel()` and `await` for the task to finish, catching the `asyncio.CancelledError`. - * The `join()` method is renamed to `wait()`, but they can also be awaited directly ( `await actor`). - * For deterministic cleanup, actors can now be used as `async` context managers. - - Most actors can be migrated following these steps: - - 1. Remove the decorator - 2. Add `Actor` as a base class - 3. Rename `run()` to `_run()` - 4. Forward the `name` argument (optional but recommended) - - For example, this old actor: - - ```python - from frequenz.sdk.actor import actor - - @actor - class TheActor: - def __init__(self, actor_args) -> None: - # init code - - def run(self) -> None: - # run code - ``` - - Can be migrated as: - - ```python - import asyncio - from frequenz.sdk.actor import Actor - - class TheActor(Actor): - def __init__(self, actor_args, - *, - name: str | None = None, - ) -> None: - super().__init__(name=name) - # init code - - def _run(self) -> None: - # run code - ``` - - Then you can instantiate all your actors first and then run them using: - - ```python - from frequenz.sdk.actor import run - # Init code - actor = TheActor() - other_actor = OtherActor() - # more setup - await run(actor, other_actor) # Start and await for all the actors - ``` - -- The `MovingWindow` is now a `BackgroundService`, so it needs to be started manually with `window.start()`. It is recommended to use it as an `async` context manager if possible though: - - ```python - async with MovingWindow(...) as window: - # The moving windows is started here - use(window) - # The moving window is stopped here - ``` - -- The base actors (`ConfigManagingActor`, `ComponentMetricsResamplingActor`, `DataSourcingActor`, `PowerDistributingActor`) now inherit from the new `Actor` class, if you are using them directly, you need to start them manually with `actor.start()` and you might need to do some other adjustments. - -- The `BatteryPool.power_distribution_results` method has been enhanced to provide power distribution results in the form of `Power` objects, replacing the previous use of `float` values. - -- In the `Request` class: - * The attribute `request_timeout_sec` has been updated and is now named `request_timeout` and it is represented by a `timedelta` object rather than a `float`. - * The attribute `power` is now presented as a `Power` object, as opposed to a `float`. - -- Within the `EVChargerPool.set_bounds` method, the parameter `max_amps` has been redefined as `max_current`, and it is now represented using a `Current` object instead of a `float`. - -- The argument `nones_are_zeros` in `FormulaEngine` and related classes and methods is now a keyword-only argument. + ## New Features -- Added `DFS` to the component graph - -- `BackgroundService`: This new abstract base class can be used to write other classes that runs one or more tasks in the background. It provides a consistent API to start and stop these services and also takes care of the handling of the background tasks. It can also work as an `async` context manager, giving the service a deterministic lifetime and guaranteed cleanup. - - All classes spawning tasks that are expected to run for an indeterminate amount of time are likely good candidates to use this as a base class. - -- `Actor`: This new class inherits from `BackgroundService` and it replaces the `@actor` decorator. - -- Newly added `min` and `max` functions for Formulas. They can be used as follows: - - ```python - formula1.min(formula2) - ``` + ## Bug Fixes -- Fixes a bug in the ring buffer updating the end timestamp of gaps when they are outdated. - -- Properly handles PV configurations with no or only some meters before the PV component. - - So far we only had configurations like this: `Meter -> Inverter -> PV`. However the scenario with `Inverter -> PV` is also possible and now handled correctly. - -- Fix `consumer_power()` not working certain configurations. - - In microgrids without consumers and no main meter, the formula would never return any values. - -- Fix `pv_power` not working in setups with 2 grid meters by using a new reliable function to search for components in the components graph - -- Fix `consumer_power` and `producer_power` similar to `pv_power` - -- Zero value requests received by the `PowerDistributingActor` will now always be accepted, even when there are non-zero exclusion bounds. - -- Hold on to a reference to all streaming tasks in the microgrid API client, so they don't get garbage collected. + From 783489603411c90789a764017e12fe781d618236 Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Wed, 6 Sep 2023 10:11:10 +0200 Subject: [PATCH 2/6] Replace the `section-index` plugin with `navigation.indexes` The `section-index` plugin has a bug[^1] that causes the navigation bar to display a wrong text for the **API Reference** link. There is a fix for it, but `mkdocs-material` already provides the feature natively[^2], so we can just use it instead, as it is much less likely to have issues. [^1]: https://github.com/oprypin/mkdocs-section-index/issues/18 [^2]: https://squidfunk.github.io/mkdocs-material/setup/setting-up-navigation/#section-index-pages Signed-off-by: Leandro Lucarella --- RELEASE_NOTES.md | 2 +- mkdocs.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 61ee6f2ad..a5333e624 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -14,4 +14,4 @@ ## Bug Fixes - +- Fix the **API Reference** link in the documentation website navigation bar. diff --git a/mkdocs.yml b/mkdocs.yml index 13fe0f1c9..c86bb14ae 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -24,6 +24,7 @@ theme: features: - content.code.annotate - content.code.copy + - navigation.indexes - navigation.instant - navigation.tabs - navigation.top @@ -115,7 +116,6 @@ plugins: - https://typing-extensions.readthedocs.io/en/stable/objects.inv - https://watchfiles.helpmanual.io/objects.inv - search - - section-index # Preview controls watch: From 51382d44416d152637ef9f88e077ef6e400ca2ae Mon Sep 17 00:00:00 2001 From: Leandro Lucarella Date: Wed, 6 Sep 2023 10:18:45 +0200 Subject: [PATCH 3/6] Prepare release notes for the release Signed-off-by: Leandro Lucarella --- RELEASE_NOTES.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index a5333e624..7570df258 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,17 +1,5 @@ # Frequenz Python SDK Release Notes -## Summary - - - -## Upgrading - - - -## New Features - - - ## Bug Fixes - Fix the **API Reference** link in the documentation website navigation bar. From 6d068b1e79c5b3d435a7fa462a077080d4167a76 Mon Sep 17 00:00:00 2001 From: Matthias Wende Date: Fri, 8 Sep 2023 10:26:56 +0200 Subject: [PATCH 4/6] Fix consumer power formula All grid meters have to be summed for this formula but in the current implemantation there was minus pushed to the operator stack. In this commit the sign error has been corrected. Signed-off-by: Matthias Wende --- .../_formula_generators/_consumer_power_formula.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frequenz/sdk/timeseries/_formula_engine/_formula_generators/_consumer_power_formula.py b/src/frequenz/sdk/timeseries/_formula_engine/_formula_generators/_consumer_power_formula.py index 2dc18f3fc..393a4d82c 100644 --- a/src/frequenz/sdk/timeseries/_formula_engine/_formula_generators/_consumer_power_formula.py +++ b/src/frequenz/sdk/timeseries/_formula_engine/_formula_generators/_consumer_power_formula.py @@ -106,7 +106,7 @@ def non_consumer_component(component: Component) -> bool: # push all grid meters for idx, grid_meter in enumerate(grid_meters): if idx > 0: - builder.push_oper("-") + builder.push_oper("+") builder.push_component_metric( grid_meter.component_id, nones_are_zeros=False ) From 337d4b825b23084c05d3293975e5f6d36fc3e4f5 Mon Sep 17 00:00:00 2001 From: Matthias Wende Date: Fri, 8 Sep 2023 10:29:41 +0200 Subject: [PATCH 5/6] Add test for consumer power formula This adds a tests with two grid meters. Signed-off-by: Matthias Wende --- tests/timeseries/test_logical_meter.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/timeseries/test_logical_meter.py b/tests/timeseries/test_logical_meter.py index 0153c927e..4effa1b21 100644 --- a/tests/timeseries/test_logical_meter.py +++ b/tests/timeseries/test_logical_meter.py @@ -17,7 +17,7 @@ # pylint: disable=too-many-locals -class TestLogicalMeter: +class TestLogicalMeter: # pylint: disable=too-many-public-methods """Tests for the logical meter.""" async def test_grid_power_1(self, mocker: MockerFixture) -> None: @@ -278,6 +278,22 @@ async def test_consumer_power_no_grid_meter(self, mocker: MockerFixture) -> None await mockgrid.mock_resampler.send_meter_power([20.0, 2.0, 3.0, 4.0, 5.0]) assert (await consumer_power_receiver.receive()).value == Power.from_watts(20.0) + async def test_consumer_power_2_grid_meters( + self, + mocker: MockerFixture, + ) -> None: + """Test the grid power formula with grid meters.""" + mockgrid = MockMicrogrid(grid_meter=False) + # with no further sucessor these will be detected as grid meters + mockgrid.add_consumer_meters(2) + await mockgrid.start(mocker) + + logical_meter = microgrid.logical_meter() + grid_consumption_recv = logical_meter.grid_consumption_power.new_receiver() + + await mockgrid.mock_resampler.send_meter_power([1.0, 2.0]) + assert (await grid_consumption_recv.receive()).value == Power.from_watts(3.0) + async def test_consumer_power_no_grid_meter_no_consumer_meter( self, mocker: MockerFixture ) -> None: From 267884b01afed7e85af4344d73094131a8486bd6 Mon Sep 17 00:00:00 2001 From: Matthias Wende Date: Fri, 8 Sep 2023 11:33:52 +0200 Subject: [PATCH 6/6] Update release notes Signed-off-by: Matthias Wende --- RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 7570df258..acc2d56ed 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -3,3 +3,5 @@ ## Bug Fixes - Fix the **API Reference** link in the documentation website navigation bar. + +- Fix the consumer power formula