Skip to content

Commit f539389

Browse files
authored
Revert unreleased interface changes (#1330)
These changes were introduced in the PRs #1295 and #1322, and are being reverted here.
2 parents 6721560 + 914def4 commit f539389

File tree

10 files changed

+290
-107
lines changed

10 files changed

+290
-107
lines changed

RELEASE_NOTES.md

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,11 @@
11
# Frequenz Python SDK Release Notes
22

3-
## Summary
4-
5-
<!-- Here goes a general summary of what this release is about -->
6-
73
## Upgrading
84

9-
<!-- Here goes notes on how to upgrade from previous versions, including deprecations and what they should be replaced with -->
10-
11-
- The `FormulaEngine` is now replaced by a newly implemented `Formula` type. This doesn't affect the high level interfaces.
5+
- The `FormulaEngine` is now replaced by a newly implemented `Formula` type. This doesn't affect the high level interfaces. `FormulaEngine` is now a deprecated wrapper to `Formula`.
126

137
- The `ComponentGraph` has been replaced by the `frequenz-microgrid-component-graph` package, which provides python bindings for the rust implementation.
148

159
## New Features
1610

1711
- The power manager algorithm for batteries can now be changed from the default ShiftingMatryoshka, by passing it as an argument to `microgrid.initialize()`
18-
19-
## Bug Fixes
20-
21-
<!-- Here goes notable bug fixes that are worth a special mention or explanation -->

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ dependencies = [
2929
# Make sure to update the mkdocs.yml file when
3030
# changing the version
3131
# (plugins.mkdocstrings.handlers.python.import)
32-
"frequenz-client-microgrid >= 0.18.0, < 0.19.0",
33-
"frequenz-microgrid-component-graph >= 0.2.0, < 0.3",
32+
"frequenz-client-microgrid >= 0.18.1, < 0.19.0",
33+
"frequenz-microgrid-component-graph >= 0.3.2, < 0.4",
3434
"frequenz-client-common >= 0.3.6, < 0.4.0",
3535
"frequenz-channels >= 1.6.1, < 2.0.0",
3636
"frequenz-quantities[marshmallow] >= 1.0.0, < 2.0.0",
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
# License: MIT
2+
# Copyright © 2025 Frequenz Energy-as-a-Service GmbH
3+
4+
"""Component graph representation for a microgrid."""
5+
6+
from frequenz.client.common.microgrid.components import ComponentId
7+
from frequenz.client.microgrid.component import (
8+
BatteryInverter,
9+
Chp,
10+
Component,
11+
ComponentConnection,
12+
EvCharger,
13+
SolarInverter,
14+
)
15+
from frequenz.microgrid_component_graph import ComponentGraph as BaseComponentGraph
16+
from typing_extensions import override
17+
18+
19+
class ComponentGraph(BaseComponentGraph[Component, ComponentConnection, ComponentId]):
20+
"""A representation of a microgrid's component graph."""
21+
22+
def is_pv_inverter(self, component: Component) -> bool:
23+
"""Check if the specified component is a PV inverter.
24+
25+
Args:
26+
component: The component to check.
27+
28+
Returns:
29+
Whether the specified component is a PV inverter.
30+
"""
31+
return isinstance(component, SolarInverter)
32+
33+
def is_pv_chain(self, component: Component) -> bool:
34+
"""Check if the specified component is part of a PV chain.
35+
36+
A component is part of a PV chain if it is either a PV inverter or a PV
37+
meter.
38+
39+
Args:
40+
component: The component to check.
41+
42+
Returns:
43+
Whether the specified component is part of a PV chain.
44+
"""
45+
return self.is_pv_inverter(component) or self.is_pv_meter(component)
46+
47+
@override
48+
def is_pv_meter(self, component: Component | ComponentId) -> bool:
49+
"""Check if the specified component is a PV meter.
50+
51+
Args:
52+
component: The component or component ID to check.
53+
54+
Returns:
55+
Whether the specified component is a PV meter.
56+
"""
57+
if isinstance(component, Component):
58+
return super().is_pv_meter(component.id)
59+
return super().is_pv_meter(component)
60+
61+
def is_ev_charger(self, component: Component) -> bool:
62+
"""Check if the specified component is an EV charger.
63+
64+
Args:
65+
component: The component to check.
66+
67+
Returns:
68+
Whether the specified component is an EV charger.
69+
"""
70+
return isinstance(component, EvCharger)
71+
72+
def is_ev_charger_chain(self, component: Component) -> bool:
73+
"""Check if the specified component is part of an EV charger chain.
74+
75+
A component is part of an EV charger chain if it is either an EV charger or an
76+
EV charger meter.
77+
78+
Args:
79+
component: The component to check.
80+
81+
Returns:
82+
Whether the specified component is part of an EV charger chain.
83+
"""
84+
return self.is_ev_charger(component) or self.is_ev_charger_meter(component)
85+
86+
@override
87+
def is_ev_charger_meter(self, component: Component | ComponentId) -> bool:
88+
"""Check if the specified component is an EV charger meter.
89+
90+
Args:
91+
component: The component or component ID to check.
92+
93+
Returns:
94+
Whether the specified component is an EV charger meter.
95+
"""
96+
if isinstance(component, Component):
97+
return super().is_ev_charger_meter(component.id)
98+
return super().is_ev_charger_meter(component)
99+
100+
def is_battery_inverter(self, component: Component) -> bool:
101+
"""Check if the specified component is a battery inverter.
102+
103+
Args:
104+
component: The component to check.
105+
106+
Returns:
107+
Whether the specified component is a battery inverter.
108+
"""
109+
return isinstance(component, BatteryInverter)
110+
111+
def is_battery_chain(self, component: Component) -> bool:
112+
"""Check if the specified component is part of a battery chain.
113+
114+
A component is part of a battery chain if it is either a battery inverter or a
115+
battery meter.
116+
117+
Args:
118+
component: The component to check.
119+
120+
Returns:
121+
Whether the specified component is part of a battery chain.
122+
"""
123+
return self.is_battery_inverter(component) or self.is_battery_meter(component)
124+
125+
@override
126+
def is_battery_meter(self, component: Component | ComponentId) -> bool:
127+
"""Check if the specified component is a battery meter.
128+
129+
Args:
130+
component: The component or component ID to check.
131+
132+
Returns:
133+
Whether the specified component is a battery meter.
134+
"""
135+
if isinstance(component, Component):
136+
return super().is_battery_meter(component.id)
137+
return super().is_battery_meter(component)
138+
139+
def is_chp(self, component: Component) -> bool:
140+
"""Check if the specified component is a CHP.
141+
142+
Args:
143+
component: The component to check.
144+
145+
Returns:
146+
Whether the specified component is a CHP.
147+
"""
148+
return isinstance(component, Chp)
149+
150+
def is_chp_chain(self, component: Component) -> bool:
151+
"""Check if the specified component is part of a CHP chain.
152+
153+
A component is part of a CHP chain if it is either a CHP or a CHP meter.
154+
155+
Args:
156+
component: The component to check.
157+
158+
Returns:
159+
Whether the specified component is part of a CHP chain.
160+
"""
161+
return self.is_chp(component) or self.is_chp_meter(component)
162+
163+
@override
164+
def is_chp_meter(self, component: Component | ComponentId) -> bool:
165+
"""Check if the specified component is a CHP meter.
166+
167+
Args:
168+
component: The component or component ID to check.
169+
170+
Returns:
171+
Whether the specified component is a CHP meter.
172+
"""
173+
if isinstance(component, Component):
174+
return super().is_chp_meter(component.id)
175+
return super().is_chp_meter(component)

src/frequenz/sdk/microgrid/connection_manager.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,13 @@
1313
from abc import ABC, abstractmethod
1414

1515
from frequenz.client.common.microgrid import MicrogridId
16-
from frequenz.client.common.microgrid.components import ComponentId
1716
from frequenz.client.microgrid import (
1817
Location,
1918
MicrogridApiClient,
2019
MicrogridInfo,
2120
)
22-
from frequenz.client.microgrid.component import Component, ComponentConnection
23-
from frequenz.microgrid_component_graph import ComponentGraph
21+
22+
from .component_graph import ComponentGraph
2423

2524
_logger = logging.getLogger(__name__)
2625

@@ -59,7 +58,7 @@ def api_client(self) -> MicrogridApiClient:
5958
@abstractmethod
6059
def component_graph(
6160
self,
62-
) -> ComponentGraph[Component, ComponentConnection, ComponentId]:
61+
) -> ComponentGraph:
6362
"""Get component graph.
6463
6564
Returns:
@@ -109,9 +108,7 @@ def __init__(self, server_url: str) -> None:
109108
self._client = MicrogridApiClient(server_url)
110109
# To create graph from the API client we need await.
111110
# So create empty graph here, and update it in `run` method.
112-
self._graph: (
113-
ComponentGraph[Component, ComponentConnection, ComponentId] | None
114-
) = None
111+
self._graph: ComponentGraph | None = None
115112

116113
self._microgrid: MicrogridInfo
117114
"""The microgrid information."""
@@ -140,9 +137,7 @@ def location(self) -> Location | None:
140137
return self._microgrid.location
141138

142139
@property
143-
def component_graph(
144-
self,
145-
) -> ComponentGraph[Component, ComponentConnection, ComponentId]:
140+
def component_graph(self) -> ComponentGraph:
146141
"""Get component graph.
147142
148143
Returns:
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# License: MIT
2+
# Copyright © 2025 Frequenz Energy-as-a-Service GmbH
3+
4+
"""Deprecated Formula Engine."""
5+
6+
7+
from typing_extensions import deprecated
8+
9+
from .._base_types import QuantityT
10+
from ..formulas._formula import Formula
11+
from ..formulas._formula_3_phase import Formula3Phase
12+
13+
14+
@deprecated(
15+
"The FormulaEngine class is deprecated and will be removed in a future release. "
16+
+ "Please use the Formula class instead."
17+
)
18+
class FormulaEngine(Formula[QuantityT]):
19+
"""Deprecated Formula Engine class.
20+
21+
This class is deprecated and will be removed in a future release.
22+
Please use the `Formula` and `Formula3Phase` classes directly.
23+
"""
24+
25+
26+
@deprecated(
27+
"The FormulaEngine3Phase class is deprecated and will be removed in a future release. "
28+
+ "Please use the Formula3Phase class instead."
29+
)
30+
class FormulaEngine3Phase(Formula3Phase[QuantityT]):
31+
"""Deprecated FormulaEngine3Phase class.
32+
33+
This class is deprecated and will be removed in a future release.
34+
Please use the `Formula3Phase` class directly.
35+
"""
36+
37+
38+
__all__ = ["FormulaEngine", "FormulaEngine3Phase"]

src/frequenz/sdk/timeseries/formulas/_formula.py

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
)
1818

1919
from ...actor import BackgroundService
20-
from .. import ReceiverFetcher, Sample
20+
from .. import Sample
2121
from .._base_types import QuantityT
2222
from . import _ast
2323
from ._base_ast_node import AstNode
@@ -39,7 +39,7 @@ async def fetcher(f: Formula[QuantityT]) -> Receiver[Sample[QuantityT]]:
3939
return lambda: fetcher(formula)
4040

4141

42-
class Formula(BackgroundService, ReceiverFetcher[Sample[QuantityT]]):
42+
class Formula(BackgroundService, Generic[QuantityT]):
4343
"""A formula represented as an AST."""
4444

4545
def __init__( # pylint: disable=too-many-arguments
@@ -84,12 +84,11 @@ def __str__(self) -> str:
8484
"""Return a string representation of the formula."""
8585
return f"[{self._name}]({self._root})"
8686

87-
@override
88-
def new_receiver(self, *, limit: int = 50) -> Receiver[Sample[QuantityT]]:
87+
def new_receiver(self, *, max_size: int = 50) -> Receiver[Sample[QuantityT]]:
8988
"""Subscribe to the formula evaluator to get evaluated samples."""
9089
if not self._evaluator.is_running:
9190
self.start()
92-
return self._channel.new_receiver(limit=limit)
91+
return self._channel.new_receiver(limit=max_size)
9392

9493
@override
9594
def start(self) -> None:
@@ -128,24 +127,24 @@ def __truediv__(self, other: float) -> FormulaBuilder[QuantityT]:
128127

129128
def coalesce(
130129
self,
131-
other: list[FormulaBuilder[QuantityT] | QuantityT | Formula[QuantityT]],
130+
*other: FormulaBuilder[QuantityT] | QuantityT | Formula[QuantityT],
132131
) -> FormulaBuilder[QuantityT]:
133132
"""Create a coalesce operation node."""
134-
return FormulaBuilder(self, self._create_method).coalesce(other)
133+
return FormulaBuilder(self, self._create_method).coalesce(*other)
135134

136135
def min(
137136
self,
138-
other: list[FormulaBuilder[QuantityT] | QuantityT | Formula[QuantityT]],
137+
*other: FormulaBuilder[QuantityT] | QuantityT | Formula[QuantityT],
139138
) -> FormulaBuilder[QuantityT]:
140139
"""Create a min operation node."""
141-
return FormulaBuilder(self, self._create_method).min(other)
140+
return FormulaBuilder(self, self._create_method).min(*other)
142141

143142
def max(
144143
self,
145-
other: list[FormulaBuilder[QuantityT] | QuantityT | Formula[QuantityT]],
144+
*other: FormulaBuilder[QuantityT] | QuantityT | Formula[QuantityT],
146145
) -> FormulaBuilder[QuantityT]:
147146
"""Create a max operation node."""
148-
return FormulaBuilder(self, self._create_method).max(other)
147+
return FormulaBuilder(self, self._create_method).max(*other)
149148

150149

151150
class FormulaBuilder(Generic[QuantityT]):
@@ -266,7 +265,7 @@ def __truediv__(
266265

267266
def coalesce(
268267
self,
269-
other: list[FormulaBuilder[QuantityT] | QuantityT | Formula[QuantityT]],
268+
*other: FormulaBuilder[QuantityT] | QuantityT | Formula[QuantityT],
270269
) -> FormulaBuilder[QuantityT]:
271270
"""Create a coalesce operation node."""
272271
right_nodes: list[AstNode[QuantityT]] = []
@@ -299,7 +298,7 @@ def coalesce(
299298

300299
def min(
301300
self,
302-
other: list[FormulaBuilder[QuantityT] | QuantityT | Formula[QuantityT]],
301+
*other: FormulaBuilder[QuantityT] | QuantityT | Formula[QuantityT],
303302
) -> FormulaBuilder[QuantityT]:
304303
"""Create a min operation node."""
305304
right_nodes: list[AstNode[QuantityT]] = []
@@ -332,7 +331,7 @@ def min(
332331

333332
def max(
334333
self,
335-
other: list[FormulaBuilder[QuantityT] | QuantityT | Formula[QuantityT]],
334+
*other: FormulaBuilder[QuantityT] | QuantityT | Formula[QuantityT],
336335
) -> FormulaBuilder[QuantityT]:
337336
"""Create a max operation node."""
338337
right_nodes: list[AstNode[QuantityT]] = []

0 commit comments

Comments
 (0)