Skip to content

Commit cac5473

Browse files
authored
Fix consumer_power() not working certain configurations. (#589)
- Fix MockMicrogrid bug not properly disabling main meter - Fix `consumer_power()` not working certain configurations. In microgrids without consumers and no main meter, the formula would never return any values. This was undiscovered as we didn't test that scenario in the MockMicrogrid class used for testing.
2 parents 17b3627 + 14f4cd8 commit cac5473

File tree

12 files changed

+194
-77
lines changed

12 files changed

+194
-77
lines changed

RELEASE_NOTES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@
1919
component.
2020
So far we only had configurations like this: Meter -> Inverter -> PV. However
2121
the scenario with Inverter -> PV is also possible and now handled correctly.
22+
- Fix `consumer_power()` not working certain configurations.
23+
In microgrids without consumers and no main meter, the formula
24+
would never return any values.

benchmarks/timeseries/benchmark_datasourcing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ async def benchmark_data_sourcing(
7070
num_ev_chargers * len(COMPONENT_METRIC_IDS) * num_msgs_per_battery
7171
)
7272
mock_grid = MockMicrogrid(
73-
grid_side_meter=False, num_values=num_msgs_per_battery, sample_rate_s=0.0
73+
grid_meter=False, num_values=num_msgs_per_battery, sample_rate_s=0.0
7474
)
7575

7676
mock_grid.add_ev_chargers(num_ev_chargers)

src/frequenz/sdk/timeseries/_formula_engine/_formula_generators/_consumer_power_formula.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,21 @@
55

66
from __future__ import annotations
77

8+
import logging
89
from collections import abc
910

1011
from ....microgrid import connection_manager
1112
from ....microgrid.component import Component, ComponentCategory, ComponentMetricId
1213
from ..._quantities import Power
1314
from .._formula_engine import FormulaEngine
1415
from .._resampled_formula_builder import ResampledFormulaBuilder
15-
from ._formula_generator import ComponentNotFound, FormulaGenerator
16+
from ._formula_generator import (
17+
NON_EXISTING_COMPONENT_ID,
18+
ComponentNotFound,
19+
FormulaGenerator,
20+
)
21+
22+
_logger = logging.getLogger(__name__)
1623

1724

1825
class ConsumerPowerFormula(FormulaGenerator[Power]):
@@ -121,4 +128,17 @@ def _gen_without_grid_meter(
121128
is_first = False
122129
builder.push_component_metric(successor.component_id, nones_are_zeros=False)
123130

131+
if len(builder.finalize()[0]) == 0:
132+
# If there are no consumer components, we have to send 0 values at the same
133+
# frequency as the other streams. So we subscribe with a non-existing
134+
# component id, just to get a `None` message at the resampling interval.
135+
builder.push_component_metric(
136+
NON_EXISTING_COMPONENT_ID, nones_are_zeros=True
137+
)
138+
_logger.warning(
139+
"Unable to find any consumers in the component graph. "
140+
"Subscribing to the resampling actor with a non-existing "
141+
"component id, so that `0` values are sent from the formula."
142+
)
143+
124144
return builder.build()

tests/actor/test_battery_pool_status.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ async def test_batteries_status(self, mocker: MockerFixture) -> None:
3030
Args:
3131
mock_microgrid: mock microgrid client
3232
"""
33-
mock_microgrid = MockMicrogrid(grid_side_meter=True)
33+
mock_microgrid = MockMicrogrid(grid_meter=True)
3434
mock_microgrid.add_batteries(3)
3535
await mock_microgrid.start(mocker)
3636

tests/actor/test_battery_status.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ async def test_sync_update_status_with_messages(
156156
Args:
157157
mock_microgrid: mock_microgrid fixture
158158
"""
159-
mock_microgrid = MockMicrogrid(grid_side_meter=True)
159+
mock_microgrid = MockMicrogrid(grid_meter=True)
160160
mock_microgrid.add_batteries(3)
161161
await mock_microgrid.start(mocker)
162162

@@ -318,7 +318,7 @@ async def test_sync_blocking_feature(self, mocker: MockerFixture) -> None:
318318
Args:
319319
mock_microgrid: mock_microgrid fixture
320320
"""
321-
mock_microgrid = MockMicrogrid(grid_side_meter=True)
321+
mock_microgrid = MockMicrogrid(grid_meter=True)
322322
mock_microgrid.add_batteries(3)
323323
await mock_microgrid.start(mocker)
324324

@@ -435,7 +435,7 @@ async def test_sync_blocking_interrupted_with_with_max_data(
435435
Args:
436436
mock_microgrid: mock_microgrid fixture
437437
"""
438-
mock_microgrid = MockMicrogrid(grid_side_meter=True)
438+
mock_microgrid = MockMicrogrid(grid_meter=True)
439439
mock_microgrid.add_batteries(3)
440440
await mock_microgrid.start(mocker)
441441

@@ -486,7 +486,7 @@ async def test_sync_blocking_interrupted_with_invalid_message(
486486
Args:
487487
mock_microgrid: mock_microgrid fixture
488488
"""
489-
mock_microgrid = MockMicrogrid(grid_side_meter=True)
489+
mock_microgrid = MockMicrogrid(grid_meter=True)
490490
mock_microgrid.add_batteries(3)
491491
await mock_microgrid.start(mocker)
492492

@@ -547,7 +547,7 @@ async def test_timers(self, mocker: MockerFixture) -> None:
547547
mock_microgrid: mock_microgrid fixture
548548
mocker: pytest mocker instance
549549
"""
550-
mock_microgrid = MockMicrogrid(grid_side_meter=True)
550+
mock_microgrid = MockMicrogrid(grid_meter=True)
551551
mock_microgrid.add_batteries(3)
552552
await mock_microgrid.start(mocker)
553553

@@ -609,7 +609,7 @@ async def test_async_battery_status(self, mocker: MockerFixture) -> None:
609609
Args:
610610
mock_microgrid: mock_microgrid fixture
611611
"""
612-
mock_microgrid = MockMicrogrid(grid_side_meter=True)
612+
mock_microgrid = MockMicrogrid(grid_meter=True)
613613
mock_microgrid.add_batteries(3)
614614
await mock_microgrid.start(mocker)
615615

@@ -690,7 +690,7 @@ async def setup_tracker(
690690
self, mocker: MockerFixture
691691
) -> AsyncIterator[tuple[MockMicrogrid, Receiver[Status]]]:
692692
"""Setup a BatteryStatusTracker instance to run tests with."""
693-
mock_microgrid = MockMicrogrid(grid_side_meter=True)
693+
mock_microgrid = MockMicrogrid(grid_meter=True)
694694
mock_microgrid.add_batteries(1)
695695
await mock_microgrid.start(mocker)
696696

tests/actor/test_power_distributing.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class TestPowerDistributingActor:
4747

4848
async def test_constructor(self, mocker: MockerFixture) -> None:
4949
"""Test if gets all necessary data."""
50-
mockgrid = MockMicrogrid(grid_side_meter=True)
50+
mockgrid = MockMicrogrid(grid_meter=True)
5151
mockgrid.add_batteries(2)
5252
mockgrid.add_batteries(1, no_meter=True)
5353
await mockgrid.start(mocker)
@@ -68,7 +68,7 @@ async def test_constructor(self, mocker: MockerFixture) -> None:
6868
await mockgrid.cleanup()
6969

7070
# Test if it works without grid side meter
71-
mockgrid = MockMicrogrid(grid_side_meter=False)
71+
mockgrid = MockMicrogrid(grid_meter=False)
7272
mockgrid.add_batteries(1)
7373
mockgrid.add_batteries(2, no_meter=True)
7474
await mockgrid.start(mocker)
@@ -108,7 +108,7 @@ async def init_component_data(self, mockgrid: MockMicrogrid) -> None:
108108

109109
async def test_power_distributor_one_user(self, mocker: MockerFixture) -> None:
110110
"""Test if power distribution works with single user works."""
111-
mockgrid = MockMicrogrid(grid_side_meter=False)
111+
mockgrid = MockMicrogrid(grid_meter=False)
112112
mockgrid.add_batteries(3)
113113
await mockgrid.start(mocker)
114114
await self.init_component_data(mockgrid)
@@ -157,7 +157,7 @@ async def test_power_distributor_one_user(self, mocker: MockerFixture) -> None:
157157

158158
async def test_battery_soc_nan(self, mocker: MockerFixture) -> None:
159159
"""Test if battery with SoC==NaN is not used."""
160-
mockgrid = MockMicrogrid(grid_side_meter=False)
160+
mockgrid = MockMicrogrid(grid_meter=False)
161161
mockgrid.add_batteries(3)
162162
await mockgrid.start(mocker)
163163
await self.init_component_data(mockgrid)
@@ -222,7 +222,7 @@ async def test_battery_soc_nan(self, mocker: MockerFixture) -> None:
222222

223223
async def test_battery_capacity_nan(self, mocker: MockerFixture) -> None:
224224
"""Test battery with capacity set to NaN is not used."""
225-
mockgrid = MockMicrogrid(grid_side_meter=False)
225+
mockgrid = MockMicrogrid(grid_meter=False)
226226
mockgrid.add_batteries(3)
227227
await mockgrid.start(mocker)
228228
await self.init_component_data(mockgrid)
@@ -280,7 +280,7 @@ async def test_battery_capacity_nan(self, mocker: MockerFixture) -> None:
280280

281281
async def test_battery_power_bounds_nan(self, mocker: MockerFixture) -> None:
282282
"""Test battery with power bounds set to NaN is not used."""
283-
mockgrid = MockMicrogrid(grid_side_meter=False)
283+
mockgrid = MockMicrogrid(grid_meter=False)
284284
mockgrid.add_batteries(3)
285285
await mockgrid.start(mocker)
286286
await self.init_component_data(mockgrid)
@@ -356,7 +356,7 @@ async def test_power_distributor_invalid_battery_id(
356356
self, mocker: MockerFixture
357357
) -> None:
358358
"""Test if power distribution raises error if any battery id is invalid."""
359-
mockgrid = MockMicrogrid(grid_side_meter=False)
359+
mockgrid = MockMicrogrid(grid_meter=False)
360360
mockgrid.add_batteries(3)
361361
await mockgrid.start(mocker)
362362
await self.init_component_data(mockgrid)
@@ -404,7 +404,7 @@ async def test_power_distributor_one_user_adjust_power_consume(
404404
self, mocker: MockerFixture
405405
) -> None:
406406
"""Test if power distribution works with single user works."""
407-
mockgrid = MockMicrogrid(grid_side_meter=False)
407+
mockgrid = MockMicrogrid(grid_meter=False)
408408
mockgrid.add_batteries(3)
409409
await mockgrid.start(mocker)
410410
await self.init_component_data(mockgrid)
@@ -457,7 +457,7 @@ async def test_power_distributor_one_user_adjust_power_supply(
457457
self, mocker: MockerFixture
458458
) -> None:
459459
"""Test if power distribution works with single user works."""
460-
mockgrid = MockMicrogrid(grid_side_meter=False)
460+
mockgrid = MockMicrogrid(grid_meter=False)
461461
mockgrid.add_batteries(3)
462462
await mockgrid.start(mocker)
463463
await self.init_component_data(mockgrid)
@@ -510,7 +510,7 @@ async def test_power_distributor_one_user_adjust_power_success(
510510
self, mocker: MockerFixture
511511
) -> None:
512512
"""Test if power distribution works with single user works."""
513-
mockgrid = MockMicrogrid(grid_side_meter=False)
513+
mockgrid = MockMicrogrid(grid_meter=False)
514514
mockgrid.add_batteries(3)
515515
await mockgrid.start(mocker)
516516
await self.init_component_data(mockgrid)
@@ -561,7 +561,7 @@ async def test_power_distributor_one_user_adjust_power_success(
561561

562562
async def test_not_all_batteries_are_working(self, mocker: MockerFixture) -> None:
563563
"""Test if power distribution works if not all batteries are working."""
564-
mockgrid = MockMicrogrid(grid_side_meter=False)
564+
mockgrid = MockMicrogrid(grid_meter=False)
565565
mockgrid.add_batteries(3)
566566
await mockgrid.start(mocker)
567567
await self.init_component_data(mockgrid)
@@ -615,7 +615,7 @@ async def test_use_all_batteries_none_is_working(
615615
self, mocker: MockerFixture
616616
) -> None:
617617
"""Test all batteries are used if none of them works."""
618-
mockgrid = MockMicrogrid(grid_side_meter=False)
618+
mockgrid = MockMicrogrid(grid_meter=False)
619619
mockgrid.add_batteries(3)
620620
await mockgrid.start(mocker)
621621
await self.init_component_data(mockgrid)
@@ -668,7 +668,7 @@ async def test_force_request_a_battery_is_not_working(
668668
self, mocker: MockerFixture
669669
) -> None:
670670
"""Test force request when a battery is not working."""
671-
mockgrid = MockMicrogrid(grid_side_meter=False)
671+
mockgrid = MockMicrogrid(grid_meter=False)
672672
mockgrid.add_batteries(3)
673673
await mockgrid.start(mocker)
674674
await self.init_component_data(mockgrid)
@@ -724,7 +724,7 @@ async def test_force_request_battery_nan_value_non_cached(
724724
) -> None:
725725
"""Test battery with NaN in SoC, capacity or power is used if request is forced."""
726726
# pylint: disable=too-many-locals
727-
mockgrid = MockMicrogrid(grid_side_meter=False)
727+
mockgrid = MockMicrogrid(grid_meter=False)
728728
mockgrid.add_batteries(3)
729729
await mockgrid.start(mocker)
730730
await self.init_component_data(mockgrid)
@@ -797,7 +797,7 @@ async def test_force_request_batteries_nan_values_cached(
797797
self, mocker: MockerFixture
798798
) -> None:
799799
"""Test battery with NaN in SoC, capacity or power is used if request is forced."""
800-
mockgrid = MockMicrogrid(grid_side_meter=False)
800+
mockgrid = MockMicrogrid(grid_meter=False)
801801
mockgrid.add_batteries(3)
802802
await mockgrid.start(mocker)
803803
await self.init_component_data(mockgrid)

tests/timeseries/_battery_pool/test_battery_pool.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,7 @@ async def run_test_battery_status_channel( # pylint: disable=too-many-arguments
461461

462462
async def test_battery_pool_power(mocker: MockerFixture) -> None:
463463
"""Test `BatteryPool.{,production,consumption}_power` methods."""
464-
mockgrid = MockMicrogrid(grid_side_meter=True)
464+
mockgrid = MockMicrogrid(grid_meter=True)
465465
mockgrid.add_batteries(2)
466466
await mockgrid.start(mocker)
467467

tests/timeseries/_formula_engine/test_formula_composition.py

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,17 @@ async def test_formula_composition( # pylint: disable=too-many-locals
2525
mocker: MockerFixture,
2626
) -> None:
2727
"""Test the composition of formulas."""
28-
mockgrid = MockMicrogrid(grid_side_meter=False)
28+
mockgrid = MockMicrogrid(grid_meter=False)
29+
mockgrid.add_consumer_meters()
2930
mockgrid.add_batteries(3)
3031
mockgrid.add_solar_inverters(2)
3132
await mockgrid.start(mocker)
3233

3334
logical_meter = microgrid.logical_meter()
3435
battery_pool = microgrid.battery_pool()
35-
main_meter_recv = get_resampled_stream(
36+
grid_meter_recv = get_resampled_stream(
3637
logical_meter._namespace, # pylint: disable=protected-access
37-
4,
38+
mockgrid.meter_ids[0],
3839
ComponentMetricId.ACTIVE_POWER,
3940
Power.from_watts,
4041
)
@@ -53,7 +54,7 @@ async def test_formula_composition( # pylint: disable=too-many-locals
5354
grid_pow = await grid_power_recv.receive()
5455
pv_pow = await pv_power_recv.receive()
5556
bat_pow = await battery_power_recv.receive()
56-
main_pow = await main_meter_recv.receive()
57+
main_pow = await grid_meter_recv.receive()
5758
inv_calc_pow = await inv_calc_recv.receive()
5859

5960
assert (
@@ -98,7 +99,7 @@ async def test_formula_composition( # pylint: disable=too-many-locals
9899

99100
async def test_formula_composition_missing_pv(self, mocker: MockerFixture) -> None:
100101
"""Test the composition of formulas with missing PV power data."""
101-
mockgrid = MockMicrogrid(grid_side_meter=False)
102+
mockgrid = MockMicrogrid(grid_meter=False)
102103
mockgrid.add_batteries(3)
103104
await mockgrid.start(mocker)
104105
battery_pool = microgrid.battery_pool()
@@ -136,7 +137,7 @@ async def test_formula_composition_missing_pv(self, mocker: MockerFixture) -> No
136137

137138
async def test_formula_composition_missing_bat(self, mocker: MockerFixture) -> None:
138139
"""Test the composition of formulas with missing battery power data."""
139-
mockgrid = MockMicrogrid(grid_side_meter=False)
140+
mockgrid = MockMicrogrid(grid_meter=False)
140141
mockgrid.add_solar_inverters(2)
141142
await mockgrid.start(mocker)
142143
battery_pool = microgrid.battery_pool()
@@ -149,9 +150,7 @@ async def test_formula_composition_missing_bat(self, mocker: MockerFixture) -> N
149150

150151
count = 0
151152
for _ in range(10):
152-
await mockgrid.mock_resampler.send_meter_power(
153-
[10.0 + count, 12.0 + count, 14.0 + count]
154-
)
153+
await mockgrid.mock_resampler.send_meter_power([12.0 + count, 14.0 + count])
155154
await mockgrid.mock_resampler.send_non_existing_component_value()
156155
bat_pow = await battery_power_recv.receive()
157156
pv_pow = await pv_power_recv.receive()
@@ -173,7 +172,7 @@ async def test_formula_composition_missing_bat(self, mocker: MockerFixture) -> N
173172

174173
async def test_formula_composition_constant(self, mocker: MockerFixture) -> None:
175174
"""Test the composition of formulas with constant values."""
176-
mockgrid = MockMicrogrid(grid_side_meter=True)
175+
mockgrid = MockMicrogrid(grid_meter=True)
177176
await mockgrid.start(mocker)
178177

179178
logical_meter = microgrid.logical_meter()
@@ -243,9 +242,7 @@ async def test_formula_composition_constant(self, mocker: MockerFixture) -> None
243242

244243
async def test_3_phase_formulas(self, mocker: MockerFixture) -> None:
245244
"""Test 3 phase formulas current formulas and their composition."""
246-
mockgrid = MockMicrogrid(
247-
grid_side_meter=False, sample_rate_s=0.05, num_namespaces=2
248-
)
245+
mockgrid = MockMicrogrid(grid_meter=False, sample_rate_s=0.05, num_namespaces=2)
249246
mockgrid.add_batteries(3)
250247
mockgrid.add_ev_chargers(1)
251248
await mockgrid.start(mocker)
@@ -266,7 +263,6 @@ async def test_3_phase_formulas(self, mocker: MockerFixture) -> None:
266263
[10.0, 12.0, 14.0],
267264
[10.0, 12.0, 14.0],
268265
[10.0, 12.0, 14.0],
269-
[10.0, 12.0, 14.0],
270266
]
271267
)
272268
await mockgrid.mock_resampler.send_evc_current(

0 commit comments

Comments
 (0)