Skip to content

Commit 0390bf3

Browse files
authored
Cleanup power formula tests (#1353)
The generation and evaluation of the formulas are now implemented and tested separately. So here we only need to test we are calling the correct formulas.
2 parents fc7ef0d + 1ee9466 commit 0390bf3

6 files changed

Lines changed: 10 additions & 224 deletions

File tree

RELEASE_NOTES.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66

77
## Upgrading
88

9-
<!-- Here goes notes on how to upgrade from previous versions, including deprecations and what they should be replaced with -->
9+
- The minimum required version of `frequenz-microgrid-component-graph` is now `0.3.4`. This reverts the formula generation to what it was like before the migration to the external component graph library. So
10+
* in component formulas, meters get used as primary sources and inverters, ev chargers, etc. as fallback sources.
11+
* consumer formula are generated by excluding producer and battery powers from the grid power, or when there is no grid meter, by adding all the non battery and producer meters.
1012

1113
## New Features
1214

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ dependencies = [
3030
# changing the version
3131
# (plugins.mkdocstrings.handlers.python.import)
3232
"frequenz-client-microgrid >= 0.18.1, < 0.19.0",
33-
"frequenz-microgrid-component-graph >= 0.3.2, < 0.4",
33+
"frequenz-microgrid-component-graph >= 0.3.4, < 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",

tests/timeseries/_battery_pool/test_battery_pool.py

Lines changed: 2 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,7 @@
2121
import time_machine
2222
from frequenz.channels import Receiver, Sender
2323
from frequenz.client.common.microgrid.components import ComponentId
24-
from frequenz.client.microgrid.component import (
25-
Battery,
26-
Component,
27-
ComponentCategory,
28-
InverterType,
29-
)
30-
from frequenz.microgrid_component_graph import (
31-
FormulaGenerationError,
32-
InvalidGraphError,
33-
)
24+
from frequenz.client.microgrid.component import Battery, Component
3425
from frequenz.quantities import Energy, Percentage, Power, Temperature
3526
from pytest_mock import MockerFixture
3627

@@ -46,7 +37,6 @@
4637
from frequenz.sdk.timeseries import Bounds, ResamplerConfig2, Sample
4738
from frequenz.sdk.timeseries._base_types import SystemBounds
4839
from frequenz.sdk.timeseries.battery_pool import BatteryPool
49-
from tests.utils.graph_generator import GraphGenerator
5040

5141
from ...timeseries.mock_microgrid import MockMicrogrid
5242
from ...utils.component_data_streamer import MockComponentDataStreamer
@@ -503,10 +493,6 @@ async def run_test_battery_status_channel(
503493
compare_messages(msg, all_pool_result)
504494

505495

506-
@pytest.mark.skip(
507-
reason="Needs to be adapted to the new component graph behavior, see "
508-
"https://github.com/frequenz-floss/frequenz-sdk-python/issues/1345"
509-
)
510496
async def test_battery_pool_power(mocker: MockerFixture) -> None:
511497
"""Test `BatteryPool.power` method."""
512498
mockgrid = MockMicrogrid(grid_meter=True, mocker=mocker)
@@ -518,165 +504,9 @@ async def test_battery_pool_power(mocker: MockerFixture) -> None:
518504
power_receiver = battery_pool.power.new_receiver()
519505

520506
# send meter power [grid_meter, battery1_meter, battery2_meter]
521-
await mockgrid.mock_resampler.send_meter_power([100.0, 2.0, 3.0])
522-
await mockgrid.mock_resampler.send_bat_inverter_power([20.0, 30.0])
507+
await mockgrid.mock_resampler.send_meter_power([100.0, 20.0, 30.0])
523508
assert (await power_receiver.receive()).value == Power.from_watts(50.0)
524509

525-
await mockgrid.mock_resampler.send_meter_power([100.0, -2.0, -5.0])
526-
await mockgrid.mock_resampler.send_bat_inverter_power([-20.0, -50.0])
527-
assert (await power_receiver.receive()).value == Power.from_watts(-70.0)
528-
529-
await mockgrid.mock_resampler.send_meter_power([100.0, 2.0, -5.0])
530-
await mockgrid.mock_resampler.send_bat_inverter_power([20.0, -50.0])
531-
assert (await power_receiver.receive()).value == Power.from_watts(-30.0)
532-
533-
534-
@pytest.mark.skip(
535-
reason="Needs to be adapted to the new component graph behavior, see "
536-
"https://github.com/frequenz-floss/frequenz-sdk-python/issues/1345"
537-
)
538-
async def test_battery_pool_power_two_inverters_per_battery(
539-
mocker: MockerFixture,
540-
) -> None:
541-
"""Test power method with two inverters per battery."""
542-
gen = GraphGenerator()
543-
bat = gen.component(ComponentCategory.BATTERY)
544-
mockgrid = MockMicrogrid(
545-
graph=gen.to_graph(
546-
(ComponentCategory.METER, gen.battery_with_inverter(bat, 2))
547-
),
548-
mocker=mocker,
549-
)
550-
async with mockgrid, AsyncExitStack() as stack:
551-
battery_pool = microgrid.new_battery_pool(priority=5)
552-
stack.push_async_callback(battery_pool.stop)
553-
power_receiver = battery_pool.power.new_receiver()
554-
555-
# send meter power [grid_meter, battery1_meter]
556-
# Fallback formula - use only meter power, inverter and batteries are not used.
557-
await mockgrid.mock_resampler.send_meter_power([100.0, 2.0])
558-
await mockgrid.mock_resampler.send_bat_inverter_power([20.0, 30.0])
559-
assert (await power_receiver.receive()).value == Power.from_watts(50.0)
560-
561-
await mockgrid.mock_resampler.send_meter_power([100.0, -5.0])
562-
await mockgrid.mock_resampler.send_bat_inverter_power([-20.0, -50.0])
563-
assert (await power_receiver.receive()).value == Power.from_watts(-70.0)
564-
565-
await mockgrid.mock_resampler.send_meter_power([100.0, -5.0])
566-
await mockgrid.mock_resampler.send_bat_inverter_power([20.0, -50.0])
567-
assert (await power_receiver.receive()).value == Power.from_watts(-30.0)
568-
569-
570-
@pytest.mark.skip(
571-
reason="Needs to be adapted to the new component graph behavior, see "
572-
"https://github.com/frequenz-floss/frequenz-sdk-python/issues/1345"
573-
)
574-
async def test_batter_pool_power_two_batteries_per_inverter(
575-
mocker: MockerFixture,
576-
) -> None:
577-
"""Test power method with two batteries per inverter."""
578-
gen = GraphGenerator()
579-
mockgrid = MockMicrogrid(
580-
graph=gen.to_graph(
581-
[
582-
(
583-
ComponentCategory.METER,
584-
(
585-
ComponentCategory.INVERTER,
586-
[ComponentCategory.BATTERY, ComponentCategory.BATTERY],
587-
),
588-
),
589-
(
590-
ComponentCategory.METER,
591-
(
592-
ComponentCategory.INVERTER,
593-
[ComponentCategory.BATTERY, ComponentCategory.BATTERY],
594-
),
595-
),
596-
]
597-
),
598-
mocker=mocker,
599-
)
600-
601-
async with mockgrid, AsyncExitStack() as stack:
602-
battery_pool = microgrid.new_battery_pool(priority=5)
603-
stack.push_async_callback(battery_pool.stop)
604-
power_receiver = battery_pool.power.new_receiver()
605-
606-
# send meter power [battery1_meter, battery2_meter]
607-
# Fallback formula - use only meter power, inverter and batteries are not used.
608-
await mockgrid.mock_resampler.send_meter_power([100.0, 3.0])
609-
await mockgrid.mock_resampler.send_bat_inverter_power([20.0, 30.0])
610-
assert (await power_receiver.receive()).value == Power.from_watts(50.0)
611-
612-
await mockgrid.mock_resampler.send_meter_power([100.0, -5.0])
613-
await mockgrid.mock_resampler.send_bat_inverter_power([-20.0, -50.0])
614-
assert (await power_receiver.receive()).value == Power.from_watts(-70.0)
615-
616-
await mockgrid.mock_resampler.send_meter_power([3.0, -5.0])
617-
await mockgrid.mock_resampler.send_bat_inverter_power([20.0, -50.0])
618-
assert (await power_receiver.receive()).value == Power.from_watts(-30.0)
619-
620-
621-
async def test_batter_pool_power_no_batteries(mocker: MockerFixture) -> None:
622-
"""Test power method with no batteries."""
623-
graph_gen = GraphGenerator()
624-
mockgrid = MockMicrogrid(
625-
graph=graph_gen.to_graph(
626-
(
627-
ComponentCategory.METER,
628-
[
629-
graph_gen.component(ComponentCategory.INVERTER, InverterType.SOLAR),
630-
graph_gen.component(ComponentCategory.INVERTER, InverterType.SOLAR),
631-
],
632-
)
633-
)
634-
)
635-
await mockgrid.start(mocker)
636-
battery_pool = microgrid.new_battery_pool(priority=5)
637-
power_receiver = battery_pool.power.new_receiver()
638-
639-
await mockgrid.mock_resampler.send_non_existing_component_value()
640-
assert (await power_receiver.receive()).value == Power.from_watts(0)
641-
642-
643-
async def test_battery_pool_power_with_no_inverters(mocker: MockerFixture) -> None:
644-
"""Test power method with no inverters."""
645-
with pytest.raises(InvalidGraphError):
646-
mockgrid = MockMicrogrid(
647-
graph=GraphGenerator().to_graph(
648-
(ComponentCategory.METER, ComponentCategory.BATTERY)
649-
)
650-
)
651-
await mockgrid.start(mocker)
652-
653-
654-
async def test_battery_pool_power_incomplete_bat_request(mocker: MockerFixture) -> None:
655-
"""Test power method when not all requested ids are behind the same inverter."""
656-
gen = GraphGenerator()
657-
bats = gen.components(
658-
ComponentCategory.BATTERY, ComponentCategory.BATTERY, ComponentCategory.BATTERY
659-
)
660-
661-
mockgrid = MockMicrogrid(
662-
graph=gen.to_graph(
663-
(
664-
ComponentCategory.METER,
665-
gen.batteries_with_inverter(bats, 2),
666-
)
667-
)
668-
)
669-
await mockgrid.start(mocker)
670-
671-
with pytest.raises(FormulaGenerationError):
672-
# Request only two of the three batteries behind the inverters
673-
battery_pool = microgrid.new_battery_pool(
674-
priority=5, component_ids=set([bats[1].id, bats[0].id])
675-
)
676-
power_receiver = battery_pool.power.new_receiver()
677-
await mockgrid.mock_resampler.send_bat_inverter_power([2.0])
678-
assert (await power_receiver.receive()).value == Power.from_watts(2.0)
679-
680510

681511
async def run_capacity_test( # pylint: disable=too-many-locals
682512
fake_time: time_machine.Coordinates, setup_args: SetupArgs

tests/timeseries/_ev_charger_pool/test_ev_charger_pool.py

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"""Tests for the `EVChargerPool`."""
55

66

7-
import pytest
87
from frequenz.quantities import Power
98
from pytest_mock import MockerFixture
109

@@ -15,10 +14,6 @@
1514
class TestEVChargerPool:
1615
"""Tests for the `EVChargerPool`."""
1716

18-
@pytest.mark.skip(
19-
reason="Needs to be adapted to the new component graph behavior, see "
20-
"https://github.com/frequenz-floss/frequenz-sdk-python/issues/1345"
21-
)
2217
async def test_ev_power( # pylint: disable=too-many-locals
2318
self,
2419
mocker: MockerFixture,
@@ -31,10 +26,5 @@ async def test_ev_power( # pylint: disable=too-many-locals
3126
ev_pool = microgrid.new_ev_charger_pool(priority=5)
3227
power_receiver = ev_pool.power.new_receiver()
3328

34-
await mockgrid.mock_resampler.send_meter_power([None])
35-
await mockgrid.mock_resampler.send_evc_power([2.0, 4.0, 10.0])
29+
await mockgrid.mock_resampler.send_meter_power([16.0])
3630
assert (await power_receiver.receive()).value == Power.from_watts(16.0)
37-
38-
await mockgrid.mock_resampler.send_meter_power([None])
39-
await mockgrid.mock_resampler.send_evc_power([2.0, 4.0, -10.0])
40-
assert (await power_receiver.receive()).value == Power.from_watts(-4.0)

tests/timeseries/_formulas/test_formula_composition.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,6 @@ async def test_formula_composition_missing_bat(self, mocker: MockerFixture) -> N
206206

207207
assert count == 10
208208

209-
@pytest.mark.skip(
210-
reason="Needs to be adapted to the new component graph behavior, see "
211-
"https://github.com/frequenz-floss/frequenz-sdk-python/issues/1345"
212-
)
213209
async def test_formula_composition_min_max(self, mocker: MockerFixture) -> None:
214210
"""Test the composition of formulas with the min and max."""
215211
mockgrid = MockMicrogrid(grid_meter=True, mocker=mocker, num_namespaces=2)
@@ -237,13 +233,13 @@ async def test_formula_composition_min_max(self, mocker: MockerFixture) -> None:
237233
assert (
238234
str(formula_min)
239235
== "[grid_power_min]("
240-
+ "MIN([grid_power](COALESCE(#4, #7)), [chp_power](COALESCE(#5, #7, 0.0)))"
236+
+ "MIN([grid_power](#4), [chp_power](COALESCE(#7, #5, 0.0)))"
241237
+ ")"
242238
)
243239
assert (
244240
str(formula_max)
245241
== "[grid_power_max]("
246-
+ "MAX([grid_power](COALESCE(#4, #7)), [chp_power](COALESCE(#5, #7, 0.0)))"
242+
+ "MAX([grid_power](#4), [chp_power](COALESCE(#7, #5, 0.0)))"
247243
+ ")"
248244
)
249245

tests/timeseries/test_logical_meter.py

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
from contextlib import AsyncExitStack
88

9-
import pytest
109
from frequenz.quantities import Power
1110
from pytest_mock import MockerFixture
1211

@@ -41,10 +40,6 @@ async def test_chp_power(self, mocker: MockerFixture) -> None:
4140
await mockgrid.mock_resampler.send_chp_power([-12.0])
4241
assert (await chp_power_receiver.receive()).value == Power.from_watts(-12.0)
4342

44-
@pytest.mark.skip(
45-
reason="Needs to be adapted to the new component graph behavior, see "
46-
"https://github.com/frequenz-floss/frequenz-sdk-python/issues/1345"
47-
)
4843
async def test_pv_power(self, mocker: MockerFixture) -> None:
4944
"""Test the pv power formula."""
5045
mockgrid = MockMicrogrid(grid_meter=False, mocker=mocker)
@@ -55,32 +50,5 @@ async def test_pv_power(self, mocker: MockerFixture) -> None:
5550
stack.push_async_callback(pv_pool.stop)
5651
pv_power_receiver = pv_pool.power.new_receiver()
5752

58-
await mockgrid.mock_resampler.send_meter_power([-1.0, -2.0])
59-
await mockgrid.mock_resampler.send_pv_inverter_power([-10.0, -20.0])
53+
await mockgrid.mock_resampler.send_meter_power([-10.0, -20.0])
6054
assert (await pv_power_receiver.receive()).value == Power.from_watts(-30.0)
61-
62-
async def test_pv_power_no_meter(self, mocker: MockerFixture) -> None:
63-
"""Test the pv power formula."""
64-
mockgrid = MockMicrogrid(grid_meter=False, mocker=mocker)
65-
mockgrid.add_solar_inverters(2, no_meter=True)
66-
67-
async with mockgrid, AsyncExitStack() as stack:
68-
pv_pool = microgrid.new_pv_pool(priority=5)
69-
stack.push_async_callback(pv_pool.stop)
70-
pv_power_receiver = pv_pool.power.new_receiver()
71-
72-
await mockgrid.mock_resampler.send_pv_inverter_power([-1.0, -2.0])
73-
assert (await pv_power_receiver.receive()).value == Power.from_watts(-3.0)
74-
75-
async def test_pv_power_no_pv_components(self, mocker: MockerFixture) -> None:
76-
"""Test the pv power formula without having any pv components."""
77-
async with (
78-
MockMicrogrid(grid_meter=True, mocker=mocker) as mockgrid,
79-
AsyncExitStack() as stack,
80-
):
81-
pv_pool = microgrid.new_pv_pool(priority=5)
82-
stack.push_async_callback(pv_pool.stop)
83-
pv_power_receiver = pv_pool.power.new_receiver()
84-
85-
await mockgrid.mock_resampler.send_non_existing_component_value()
86-
assert (await pv_power_receiver.receive()).value == Power.zero()

0 commit comments

Comments
 (0)