16
16
from . import _bounds
17
17
18
18
if typing .TYPE_CHECKING :
19
- from ...timeseries .battery_pool import PowerMetrics
19
+ from ...timeseries ._base_types import SystemBounds
20
20
from .. import power_distributing
21
21
22
22
@@ -27,8 +27,8 @@ class ReportRequest:
27
27
source_id : str
28
28
"""The source ID of the actor sending the request."""
29
29
30
- battery_ids : frozenset [int ]
31
- """The battery IDs to report on."""
30
+ component_ids : frozenset [int ]
31
+ """The component IDs to report on."""
32
32
33
33
priority : int
34
34
"""The priority of the actor ."""
@@ -40,15 +40,67 @@ def get_channel_name(self) -> str:
40
40
The channel name to use to identify the corresponding report channel
41
41
from the channel registry.
42
42
"""
43
- return f"power_manager.report.{ self .battery_ids = } .{ self .priority = } "
43
+ return f"power_manager.report.{ self .component_ids = } .{ self .priority = } "
44
+
45
+
46
+ class Report (typing .Protocol ):
47
+ """Current PowerManager report for a set of components.
48
+
49
+ This protocol can be specialized by different component pools to provide more
50
+ specific details and documentation for the reports.
51
+ """
52
+
53
+ @property
54
+ def bounds (self ) -> timeseries .Bounds [Power ] | None :
55
+ """The bounds for the components.
56
+
57
+ These bounds are adjusted to any restrictions placed by actors with higher
58
+ priorities.
59
+
60
+ There might be exclusion zones within these bounds. If necessary, the
61
+ `adjust_to_bounds` method may be used to check if a desired power value fits the
62
+ bounds, or to get the closest possible power values that do fit the bounds.
63
+ """
64
+
65
+ @abc .abstractmethod
66
+ def adjust_to_bounds (self , power : Power ) -> tuple [Power | None , Power | None ]:
67
+ """Adjust a power value to the bounds.
68
+
69
+ This method can be used to adjust a desired power value to the power bounds
70
+ available to the actor.
71
+
72
+ If the given power value falls within the usable bounds, it will be returned
73
+ unchanged.
74
+
75
+ If it falls outside the usable bounds, the closest possible value on the
76
+ corresponding side will be returned. For example, if the given power is lower
77
+ than the lowest usable power, only the lowest usable power will be returned, and
78
+ similarly for the highest usable power.
79
+
80
+ If the given power falls within an exclusion zone that's contained within the
81
+ usable bounds, the closest possible power values on both sides will be returned.
82
+
83
+ !!! note
84
+ It is completely optional to use this method to adjust power values before
85
+ proposing them, because the PowerManager will do this automatically. This
86
+ method is provided for convenience, and for granular control when there are
87
+ two possible power values, both of which fall within the available bounds.
88
+
89
+ Args:
90
+ power: The power value to adjust.
91
+
92
+ Returns:
93
+ A tuple of the closest power values to the desired power that fall within
94
+ the available bounds for the actor.
95
+ """
44
96
45
97
46
98
@dataclasses .dataclass (frozen = True , kw_only = True )
47
- class Report :
48
- """Current PowerManager report for a set of batteries ."""
99
+ class _Report ( Report ) :
100
+ """Current PowerManager report for a set of components ."""
49
101
50
102
target_power : Power | None
51
- """The currently set power for the batteries ."""
103
+ """The currently set power for the components ."""
52
104
53
105
distribution_result : power_distributing .Result | None
54
106
"""The result of the last power distribution.
@@ -57,14 +109,14 @@ class Report:
57
109
"""
58
110
59
111
_inclusion_bounds : timeseries .Bounds [Power ] | None
60
- """The available inclusion bounds for the batteries , for the actor's priority.
112
+ """The available inclusion bounds for the components , for the actor's priority.
61
113
62
114
These bounds are adjusted to any restrictions placed by actors with higher
63
115
priorities.
64
116
"""
65
117
66
118
_exclusion_bounds : timeseries .Bounds [Power ] | None
67
- """The exclusion bounds for the batteries .
119
+ """The exclusion bounds for the components .
68
120
69
121
The power manager doesn't manage exclusion bounds, so these are aggregations of
70
122
values reported by the microgrid API.
@@ -75,15 +127,14 @@ class Report:
75
127
76
128
@property
77
129
def bounds (self ) -> timeseries .Bounds [Power ] | None :
78
- """The bounds for the batteries .
130
+ """The bounds for the components .
79
131
80
132
These bounds are adjusted to any restrictions placed by actors with higher
81
133
priorities.
82
134
83
135
There might be exclusion zones within these bounds. If necessary, the
84
- [`adjust_to_bounds`][frequenz.sdk.timeseries.battery_pool.Report.adjust_to_bounds]
85
- method may be used to check if a desired power value fits the bounds, or to get
86
- the closest possible power values that do fit the bounds.
136
+ `adjust_to_bounds` method may be used to check if a desired power value fits the
137
+ bounds, or to get the closest possible power values that do fit the bounds.
87
138
"""
88
139
return self ._inclusion_bounds
89
140
@@ -106,29 +157,9 @@ def adjust_to_bounds(self, power: Power) -> tuple[Power | None, Power | None]:
106
157
107
158
!!! note
108
159
It is completely optional to use this method to adjust power values before
109
- proposing them through the battery pool, because the battery pool will do
110
- this automatically. This method is provided for convenience, and for
111
- granular control when there are two possible power values, both of which
112
- fall within the available bounds.
113
-
114
- Example:
115
- ```python
116
- from frequenz.sdk import microgrid
117
-
118
- power_status_rx = microgrid.battery_pool().power_status.new_receiver()
119
- power_status = await power_status_rx.receive()
120
- desired_power = Power.from_watts(1000.0)
121
-
122
- match power_status.adjust_to_bounds(desired_power):
123
- case (power, _) if power == desired_power:
124
- print("Desired power is available.")
125
- case (None, power) | (power, None) if power:
126
- print(f"Closest available power is {power}.")
127
- case (lower, upper) if lower and upper:
128
- print(f"Two options {lower}, {upper} to propose to battery pool.")
129
- case (None, None):
130
- print("No available power")
131
- ```
160
+ proposing them, because the PowerManager will do this automatically. This
161
+ method is provided for convenience, and for granular control when there are
162
+ two possible power values, both of which fall within the available bounds.
132
163
133
164
Args:
134
165
power: The power value to adjust.
@@ -150,13 +181,13 @@ def adjust_to_bounds(self, power: Power) -> tuple[Power | None, Power | None]:
150
181
151
182
@dataclasses .dataclass (frozen = True , kw_only = True )
152
183
class Proposal :
153
- """A proposal for a battery to be charged or discharged."""
184
+ """A proposal for a set of components to be charged or discharged."""
154
185
155
186
source_id : str
156
187
"""The source ID of the actor sending the request."""
157
188
158
189
preferred_power : Power | None
159
- """The preferred power to be distributed to the batteries .
190
+ """The preferred power to be distributed to the components .
160
191
161
192
If `None`, the preferred power of higher priority actors will get precedence.
162
193
"""
@@ -166,12 +197,12 @@ class Proposal:
166
197
167
198
These bounds will apply to actors with a lower priority, and can be overridden by
168
199
bounds from actors with a higher priority. If None, the power bounds will be set to
169
- the maximum power of the batteries in the pool. This is currently an experimental
200
+ the maximum power of the components in the pool. This is currently an experimental
170
201
feature.
171
202
"""
172
203
173
- battery_ids : frozenset [int ]
174
- """The battery IDs to distribute the power to."""
204
+ component_ids : frozenset [int ]
205
+ """The component IDs to distribute the power to."""
175
206
176
207
priority : int
177
208
"""The priority of the actor sending the proposal."""
@@ -207,23 +238,23 @@ class BaseAlgorithm(abc.ABC):
207
238
@abc .abstractmethod
208
239
def calculate_target_power (
209
240
self ,
210
- battery_ids : frozenset [int ],
241
+ component_ids : frozenset [int ],
211
242
proposal : Proposal | None ,
212
- system_bounds : PowerMetrics ,
243
+ system_bounds : SystemBounds ,
213
244
must_return_power : bool = False ,
214
245
) -> Power | None :
215
- """Calculate and return the target power for the given batteries .
246
+ """Calculate and return the target power for the given components .
216
247
217
248
Args:
218
- battery_ids : The battery IDs to calculate the target power for.
249
+ component_ids : The component IDs to calculate the target power for.
219
250
proposal: If given, the proposal to added to the bucket, before the target
220
251
power is calculated.
221
- system_bounds: The system bounds for the batteries in the proposal.
252
+ system_bounds: The system bounds for the components in the proposal.
222
253
must_return_power: If `True`, the algorithm must return a target power,
223
254
even if it hasn't changed since the last call.
224
255
225
256
Returns:
226
- The new target power for the batteries , or `None` if the target power
257
+ The new target power for the components , or `None` if the target power
227
258
didn't change.
228
259
"""
229
260
@@ -232,19 +263,19 @@ def calculate_target_power(
232
263
@abc .abstractmethod
233
264
def get_status (
234
265
self ,
235
- battery_ids : frozenset [int ],
266
+ component_ids : frozenset [int ],
236
267
priority : int ,
237
- system_bounds : PowerMetrics ,
268
+ system_bounds : SystemBounds ,
238
269
distribution_result : power_distributing .Result | None ,
239
- ) -> Report :
240
- """Get the bounds for a set of batteries , for the given priority.
270
+ ) -> _Report :
271
+ """Get the bounds for a set of components , for the given priority.
241
272
242
273
Args:
243
- battery_ids : The IDs of the batteries to get the bounds for.
274
+ component_ids : The IDs of the components to get the bounds for.
244
275
priority: The priority of the actor for which the bounds are requested.
245
- system_bounds: The system bounds for the batteries .
276
+ system_bounds: The system bounds for the components .
246
277
distribution_result: The result of the last power distribution.
247
278
248
279
Returns:
249
- The bounds for the batteries .
280
+ The bounds for the components .
250
281
"""
0 commit comments