Skip to content

Commit ff58218

Browse files
committed
Rename set_power/(dis)charge -> propose_{power,(dis)charge}
These are methods in the battery pool, that now go through the power manager, which tries to reconcile the requests and find a target power. Signed-off-by: Sahas Subramanian <[email protected]>
1 parent 4bc950a commit ff58218

File tree

3 files changed

+60
-31
lines changed

3 files changed

+60
-31
lines changed

benchmarks/power_distribution/power_distributor.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ async def send_requests(batteries: set[int], request_num: int) -> list[Result]:
5454
results_rx = battery_pool.power_status.new_receiver()
5555
result: list[Any] = []
5656
for _ in range(request_num):
57-
await battery_pool.set_power(Power(float(random.randrange(100000, 1000000))))
57+
await battery_pool.propose_power(
58+
Power(float(random.randrange(100000, 1000000)))
59+
)
5860
try:
5961
output = await asyncio.wait_for(results_rx.receive(), timeout=3)
6062
if output is None:

src/frequenz/sdk/timeseries/battery_pool/_battery_pool_wrapper.py

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -57,52 +57,65 @@ def __init__(self, battery_pool: BatteryPool, source_id: str | None, priority: i
5757
self._source_id = unique_id if source_id is None else f"{source_id}-{unique_id}"
5858
self._priority = priority
5959

60-
async def set_power(
60+
async def propose_power(
6161
self,
62-
preferred_power: Power | None,
62+
power: Power | None,
6363
*,
6464
request_timeout: timedelta = timedelta(seconds=5.0),
6565
include_broken_batteries: bool = False,
66-
_bounds: timeseries.Bounds[Power | None] = timeseries.Bounds(None, None),
66+
bounds: timeseries.Bounds[Power | None] = timeseries.Bounds(None, None),
6767
) -> None:
68-
"""Set the given power for the batteries in the pool.
68+
"""Send a proposal to the power manager for the pool's set of batteries.
6969
7070
Power values need to follow the Passive Sign Convention (PSC). That is, positive
7171
values indicate charge power and negative values indicate discharge power.
7272
73-
When not using the Passive Sign Convention, the `charge` and `discharge` methods
74-
might be more convenient.
73+
If the same batteries are shared by multiple actors, the power manager will
74+
consider the priority of the actors, the bounds they set, and their preferred
75+
power, when calculating the target power for the batteries.
76+
77+
The preferred power of lower priority actors will take precedence as long as
78+
they respect the bounds set by higher priority actors. If lower priority actors
79+
request power values outside of the bounds set by higher priority actors, the
80+
target power will be closest value to the preferred power that is within the
81+
bounds.
82+
83+
When there are no other actors trying to use the same batteries, the actor's
84+
preferred power would be set as the target power, as long as it falls within the
85+
system power bounds for the batteries.
7586
7687
The result of the request can be accessed using the receiver returned from the
7788
`power_distribution_results` method.
7889
7990
Args:
80-
preferred_power: The power to set for the batteries in the pool.
91+
power: The power to propose for the batteries in the pool. If None, the
92+
proposed power of higher priority actors will take precedence as the
93+
target power.
8194
request_timeout: The timeout for the request.
8295
include_broken_batteries: if True, the power will be set for all batteries
8396
in the pool, including the broken ones. If False, then the power will be
8497
set only for the working batteries. This is not a guarantee that the
8598
power will be set for all working batteries, as the microgrid API may
8699
still reject the request.
87-
_bounds: The power bounds for the request. These bounds will apply to actors
88-
with a lower priority, and can be overridden by bounds from actors with
89-
a higher priority. If None, the power bounds will be set to the maximum
90-
power of the batteries in the pool. This is currently and experimental
91-
feature.
100+
bounds: The power bounds for the proposal. These bounds will apply to
101+
actors with a lower priority, and can be overridden by bounds from
102+
actors with a higher priority. If None, the power bounds will be set
103+
to the maximum power of the batteries in the pool. This is currently
104+
and experimental feature.
92105
"""
93106
await self._battery_pool._power_manager_requests_sender.send(
94107
_power_managing.Proposal(
95108
source_id=self._source_id,
96-
preferred_power=preferred_power,
97-
bounds=_bounds,
109+
preferred_power=power,
110+
bounds=bounds,
98111
battery_ids=self._battery_pool._batteries,
99112
priority=self._priority,
100113
request_timeout=request_timeout,
101114
include_broken_batteries=include_broken_batteries,
102115
)
103116
)
104117

105-
async def charge(
118+
async def propose_charge(
106119
self,
107120
power: Power | None,
108121
*,
@@ -113,14 +126,21 @@ async def charge(
113126
114127
Power values need to be positive values, indicating charge power.
115128
116-
When using the Passive Sign Convention, the `set_power` method might be more
129+
When using the Passive Sign Convention, the `propose_power` method might be more
117130
convenient.
118131
132+
If the same batteries are shared by multiple actors, the behaviour is the same
133+
as that of the `propose_power` method. The bounds for lower priority actors
134+
can't be specified with this method. If that's required, use the
135+
`propose_power` method instead.
136+
119137
The result of the request can be accessed using the receiver returned from
120-
the `power_distribution_results` method.
138+
the `power_status` method.
121139
122140
Args:
123-
power: Unsigned charge power to set for the batteries in the pool.
141+
power: The unsigned charge power to propose for the batteries in the pool.
142+
If None, the proposed power of higher priority actors will take
143+
precedence as the target power.
124144
request_timeout: The timeout for the request.
125145
include_broken_batteries: if True, the power will be set for all batteries
126146
in the pool, including the broken ones. If False, then the power will be
@@ -145,7 +165,7 @@ async def charge(
145165
)
146166
)
147167

148-
async def discharge(
168+
async def propose_discharge(
149169
self,
150170
power: Power | None,
151171
*,
@@ -156,14 +176,21 @@ async def discharge(
156176
157177
Power values need to be positive values, indicating discharge power.
158178
159-
When using the Passive Sign Convention, the `set_power` method might be more
179+
When using the Passive Sign Convention, the `propose_power` method might be more
160180
convenient.
161181
182+
If the same batteries are shared by multiple actors, the behaviour is the same
183+
as that of the `propose_power` method. The bounds for lower priority actors
184+
can't be specified with this method. If that's required, use the
185+
`propose_power` method instead.
186+
162187
The result of the request can be accessed using the receiver returned from
163-
the `power_distribution_results` method.
188+
the `power_status` method.
164189
165190
Args:
166-
power: Unsigned discharge power to set for the batteries in the pool.
191+
power: The unsigned discharge power to propose for the batteries in the
192+
pool. If None, the proposed power of higher priority actors will take
193+
precedence as the target power.
167194
request_timeout: The timeout for the request.
168195
include_broken_batteries: if True, the power will be set for all batteries
169196
in the pool, including the broken ones. If False, then the power will be

tests/timeseries/_battery_pool/test_battery_pool_control_methods.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ async def test_case_1(
191191
await bounds_rx.receive(), power=None, lower=-4000.0, upper=4000.0
192192
)
193193

194-
await battery_pool.set_power(Power.from_watts(1000.0))
194+
await battery_pool.propose_power(Power.from_watts(1000.0))
195195

196196
self._assert_report(
197197
await bounds_rx.receive(), power=1000.0, lower=-4000.0, upper=4000.0
@@ -220,7 +220,7 @@ async def side_effect(inv_id: int, _: float) -> None:
220220
await asyncio.sleep(1000.0)
221221

222222
set_power.side_effect = side_effect
223-
await battery_pool.set_power(
223+
await battery_pool.propose_power(
224224
Power.from_watts(100.0), request_timeout=timedelta(seconds=0.1)
225225
)
226226
self._assert_report(
@@ -289,7 +289,7 @@ async def test_case_2(self, mocks: Mocks, mocker: MockerFixture) -> None:
289289
self._assert_report(
290290
await bounds_2_rx.receive(), power=None, lower=-2000.0, upper=2000.0
291291
)
292-
await battery_pool_1.set_power(Power.from_watts(1000.0))
292+
await battery_pool_1.propose_power(Power.from_watts(1000.0))
293293
self._assert_report(
294294
await bounds_1_rx.receive(), power=1000.0, lower=-2000.0, upper=2000.0
295295
)
@@ -300,7 +300,7 @@ async def test_case_2(self, mocks: Mocks, mocker: MockerFixture) -> None:
300300
]
301301
set_power.reset_mock()
302302

303-
await battery_pool_2.set_power(Power.from_watts(1000.0))
303+
await battery_pool_2.propose_power(Power.from_watts(1000.0))
304304
self._assert_report(
305305
await bounds_2_rx.receive(), power=1000.0, lower=-2000.0, upper=2000.0
306306
)
@@ -335,9 +335,9 @@ async def test_case_3(self, mocks: Mocks, mocker: MockerFixture) -> None:
335335
self._assert_report(
336336
await bounds_2_rx.receive(), power=None, lower=-4000.0, upper=4000.0
337337
)
338-
await battery_pool_1.set_power(
338+
await battery_pool_1.propose_power(
339339
Power.from_watts(-1000.0),
340-
_bounds=timeseries.Bounds(Power.from_watts(-1000.0), Power.from_watts(0.0)),
340+
bounds=timeseries.Bounds(Power.from_watts(-1000.0), Power.from_watts(0.0)),
341341
)
342342
self._assert_report(
343343
await bounds_1_rx.receive(), power=-1000.0, lower=-4000.0, upper=4000.0
@@ -353,9 +353,9 @@ async def test_case_3(self, mocks: Mocks, mocker: MockerFixture) -> None:
353353
]
354354
set_power.reset_mock()
355355

356-
await battery_pool_2.set_power(
356+
await battery_pool_2.propose_power(
357357
Power.from_watts(0.0),
358-
_bounds=timeseries.Bounds(Power.from_watts(0.0), Power.from_watts(1000.0)),
358+
bounds=timeseries.Bounds(Power.from_watts(0.0), Power.from_watts(1000.0)),
359359
)
360360
self._assert_report(
361361
await bounds_1_rx.receive(), power=0.0, lower=-4000.0, upper=4000.0

0 commit comments

Comments
 (0)