|
1 | 1 | # License: MIT |
2 | 2 | # Copyright © 2023 Frequenz Energy-as-a-Service GmbH |
3 | 3 |
|
4 | | -"""Tests for the power managing actor.""" |
| 4 | +"""Tests for the Matryoshka power manager algorithm.""" |
5 | 5 |
|
6 | 6 | from datetime import datetime, timezone |
7 | | -from unittest.mock import AsyncMock |
8 | 7 |
|
9 | | -from frequenz.channels import Broadcast |
10 | | -from pytest_mock import MockerFixture |
11 | | - |
12 | | -from frequenz.sdk.actor._channel_registry import ChannelRegistry |
13 | | -from frequenz.sdk.actor._power_managing import Proposal, ReportRequest |
14 | | -from frequenz.sdk.actor._power_managing._power_managing_actor import PowerManagingActor |
15 | | -from frequenz.sdk.actor.power_distributing.request import Request |
| 8 | +from frequenz.sdk.actor._power_managing import Proposal |
| 9 | +from frequenz.sdk.actor._power_managing._matryoshka import Matryoshka |
16 | 10 | from frequenz.sdk.timeseries import Power, battery_pool |
17 | 11 |
|
18 | 12 |
|
19 | | -async def test_power_managing_actor_matryoshka(mocker: MockerFixture) -> None: |
| 13 | +async def test_matryoshka_algorithm() -> None: |
20 | 14 | """Tests for the power managing actor.""" |
21 | | - input_channel = Broadcast[Proposal]("power managing proposals") |
22 | | - output_channel = Broadcast[Request]("Power managing outputs") |
23 | | - power_bounds_sub_channel = Broadcast[ReportRequest]("power bounds subscription") |
24 | | - channel_registry = ChannelRegistry(name="test_channel_registry") |
25 | | - input_tx = input_channel.new_sender() |
26 | | - output_rx = output_channel.new_receiver() |
27 | | - |
28 | 15 | batteries = frozenset({2, 5}) |
29 | 16 |
|
30 | | - mocker.patch( |
31 | | - "frequenz.sdk.actor._power_managing._power_managing_actor" |
32 | | - ".PowerManagingActor._add_bounds_tracker", |
33 | | - new_callable=AsyncMock, |
| 17 | + algorithm = Matryoshka() |
| 18 | + system_bounds = battery_pool.PowerMetrics( |
| 19 | + timestamp=datetime.now(tz=timezone.utc), |
| 20 | + inclusion_bounds=battery_pool.Bounds( |
| 21 | + lower=Power.from_watts(-200.0), upper=Power.from_watts(200.0) |
| 22 | + ), |
| 23 | + exclusion_bounds=battery_pool.Bounds(lower=Power.zero(), upper=Power.zero()), |
34 | 24 | ) |
35 | 25 |
|
36 | | - async def case( |
37 | | - *, |
| 26 | + call_count = 0 |
| 27 | + |
| 28 | + def case( |
38 | 29 | priority: int, |
39 | 30 | power: float, |
40 | | - bounds: tuple[float, float], |
| 31 | + bounds: tuple[float, float] | None, |
41 | 32 | expected: float | None, |
42 | 33 | ) -> None: |
43 | | - await input_tx.send( |
| 34 | + nonlocal call_count |
| 35 | + call_count += 1 |
| 36 | + tgt_power = algorithm.get_target_power( |
| 37 | + batteries, |
44 | 38 | Proposal( |
45 | 39 | battery_ids=batteries, |
46 | 40 | source_id=f"actor-{priority}", |
47 | 41 | preferred_power=Power.from_watts(power), |
48 | | - bounds=(Power.from_watts(bounds[0]), Power.from_watts(bounds[1])), |
| 42 | + bounds=(Power.from_watts(bounds[0]), Power.from_watts(bounds[1])) |
| 43 | + if bounds |
| 44 | + else None, |
49 | 45 | priority=priority, |
50 | | - ) |
51 | | - ) |
52 | | - if expected is not None: |
53 | | - assert (await output_rx.receive()).power.as_watts() == expected |
54 | | - |
55 | | - async with PowerManagingActor( |
56 | | - input_channel.new_receiver(), |
57 | | - power_bounds_sub_channel.new_receiver(), |
58 | | - output_channel.new_sender(), |
59 | | - channel_registry, |
60 | | - ) as powmgract: |
61 | | - powmgract._system_bounds[ # pylint: disable=protected-access |
62 | | - batteries |
63 | | - ] = battery_pool.PowerMetrics( |
64 | | - timestamp=datetime.now(tz=timezone.utc), |
65 | | - inclusion_bounds=battery_pool.Bounds( |
66 | | - lower=Power.from_watts(-200.0), upper=Power.from_watts(200.0) |
67 | | - ), |
68 | | - exclusion_bounds=battery_pool.Bounds( |
69 | | - lower=Power.zero(), upper=Power.zero() |
70 | 46 | ), |
| 47 | + system_bounds, |
71 | 48 | ) |
| 49 | + assert tgt_power == ( |
| 50 | + Power.from_watts(expected) if expected is not None else None |
| 51 | + ) |
| 52 | + |
| 53 | + case(priority=2, power=25.0, bounds=(25.0, 50.0), expected=25.0) |
| 54 | + case(priority=1, power=20.0, bounds=(20.0, 50.0), expected=None) |
| 55 | + case(priority=3, power=10.0, bounds=(10.0, 15.0), expected=15.0) |
| 56 | + case(priority=3, power=10.0, bounds=(10.0, 22.0), expected=22.0) |
| 57 | + case(priority=1, power=30.0, bounds=(20.0, 50.0), expected=None) |
| 58 | + case(priority=3, power=10.0, bounds=(10.0, 50.0), expected=30.0) |
| 59 | + case(priority=2, power=40.0, bounds=(40.0, 50.0), expected=40.0) |
| 60 | + case(priority=2, power=0.0, bounds=(-200.0, 200.0), expected=30.0) |
| 61 | + case(priority=4, power=-50.0, bounds=(-200.0, -50.0), expected=-50.0) |
| 62 | + case(priority=3, power=-0.0, bounds=(-200.0, 200.0), expected=None) |
| 63 | + case(priority=1, power=-150.0, bounds=(-200.0, -150.0), expected=-150.0) |
| 64 | + case(priority=4, power=-180.0, bounds=(-200.0, -50.0), expected=None) |
| 65 | + case(priority=4, power=50.0, bounds=(50.0, 200.0), expected=50.0) |
72 | 66 |
|
73 | | - await case(priority=2, power=25.0, bounds=(25.0, 50.0), expected=25.0) |
74 | | - await case(priority=1, power=20.0, bounds=(20.0, 50.0), expected=None) |
75 | | - await case(priority=3, power=10.0, bounds=(10.0, 15.0), expected=10.0) |
76 | | - await case(priority=3, power=10.0, bounds=(10.0, 22.0), expected=20.0) |
77 | | - await case(priority=1, power=30.0, bounds=(20.0, 50.0), expected=10.0) |
78 | | - await case(priority=3, power=10.0, bounds=(10.0, 50.0), expected=30.0) |
79 | | - await case(priority=2, power=40.0, bounds=(40.0, 50.0), expected=40.0) |
80 | | - await case(priority=2, power=0.0, bounds=(-200.0, 200.0), expected=30.0) |
81 | | - await case(priority=4, power=-50.0, bounds=(-200.0, -50.0), expected=-50.0) |
82 | | - await case(priority=3, power=-0.0, bounds=(-200.0, 200.0), expected=None) |
83 | | - await case(priority=1, power=-150.0, bounds=(-200.0, -150.0), expected=-150.0) |
84 | | - await case(priority=4, power=-180.0, bounds=(-200.0, -50.0), expected=None) |
85 | | - await case(priority=4, power=50.0, bounds=(50.0, 200.0), expected=50.0) |
| 67 | + case(priority=4, power=0.0, bounds=(-200.0, 200.0), expected=-150.0) |
| 68 | + case(priority=3, power=0.0, bounds=(-200.0, 200.0), expected=None) |
| 69 | + case(priority=2, power=50.0, bounds=(-100, 100), expected=-100.0) |
| 70 | + case(priority=1, power=100.0, bounds=(100, 200), expected=100.0) |
| 71 | + case(priority=1, power=50.0, bounds=(50, 200), expected=50.0) |
| 72 | + case(priority=1, power=200.0, bounds=(50, 200), expected=100.0) |
0 commit comments