Skip to content

Commit 6d09aaa

Browse files
Add tests for 3-phase power metrics
Signed-off-by: Daniel Zullo <[email protected]>
1 parent 368466b commit 6d09aaa

File tree

4 files changed

+114
-1
lines changed

4 files changed

+114
-1
lines changed

tests/microgrid/test_component_data.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,17 @@ def test_inverter_data() -> None:
5353
phase_1=electrical_pb2.AC.ACPhase(
5454
current=metrics_pb2.Metric(value=12.3),
5555
voltage=metrics_pb2.Metric(value=229.8),
56+
power_active=metrics_pb2.Metric(value=33.1),
5657
),
5758
phase_2=electrical_pb2.AC.ACPhase(
5859
current=metrics_pb2.Metric(value=23.4),
5960
voltage=metrics_pb2.Metric(value=230.0),
61+
power_active=metrics_pb2.Metric(value=33.3),
6062
),
6163
phase_3=electrical_pb2.AC.ACPhase(
6264
current=metrics_pb2.Metric(value=34.5),
6365
voltage=metrics_pb2.Metric(value=230.2),
66+
power_active=metrics_pb2.Metric(value=33.8),
6467
),
6568
),
6669
),
@@ -78,6 +81,7 @@ def test_inverter_data() -> None:
7881
]
7982
assert inv_data.frequency == pytest.approx(50.1)
8083
assert inv_data.active_power == pytest.approx(100.2)
84+
assert inv_data.active_power_per_phase == pytest.approx((33.1, 33.3, 33.8))
8185
assert inv_data.current_per_phase == pytest.approx((12.3, 23.4, 34.5))
8286
assert inv_data.voltage_per_phase == pytest.approx((229.8, 230.0, 230.2))
8387
assert inv_data.active_power_inclusion_lower_bound == pytest.approx(-51_000.0)

tests/microgrid/test_grid.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,75 @@ async def test_grid_power_2(mocker: MockerFixture) -> None:
202202
assert equal_float_lists(results, meter_sums)
203203

204204

205+
async def test_grid_power_3_phase_side_meter(mocker: MockerFixture) -> None:
206+
"""Test the grid 3-phase power with a grid side meter."""
207+
mockgrid = MockMicrogrid(grid_meter=True, mocker=mocker)
208+
mockgrid.add_batteries(1, no_meter=True)
209+
mockgrid.add_batteries(1, no_meter=False)
210+
211+
async with mockgrid, AsyncExitStack() as stack:
212+
grid = microgrid.grid()
213+
assert grid, "Grid is not initialized"
214+
stack.push_async_callback(grid.stop)
215+
216+
grid_power_3_phase_recv = (
217+
grid._power_3_phase.new_receiver() # pylint: disable=protected-access
218+
)
219+
220+
for count in range(10):
221+
watts_delta = 1 if count % 2 == 0 else -1
222+
watts_phases: list[float | None] = [
223+
220.0 * watts_delta,
224+
219.8 * watts_delta,
225+
220.2 * watts_delta,
226+
]
227+
228+
await mockgrid.mock_resampler.send_meter_power_3_phase(
229+
[watts_phases, watts_phases]
230+
)
231+
232+
val = await grid_power_3_phase_recv.receive()
233+
assert val is not None
234+
assert val.value_p1 and val.value_p2 and val.value_p3
235+
assert val.value_p1.as_watts() == watts_phases[0]
236+
assert val.value_p2.as_watts() == watts_phases[1]
237+
assert val.value_p3.as_watts() == watts_phases[2]
238+
239+
240+
async def test_grid_power_3_phase_none_values(mocker: MockerFixture) -> None:
241+
"""Test the grid 3-phase power with None values."""
242+
mockgrid = MockMicrogrid(grid_meter=True, mocker=mocker)
243+
mockgrid.add_batteries(2, no_meter=False)
244+
245+
async with mockgrid, AsyncExitStack() as stack:
246+
grid = microgrid.grid()
247+
assert grid, "Grid is not initialized"
248+
stack.push_async_callback(grid.stop)
249+
250+
grid_power_3_phase_recv = (
251+
grid._power_3_phase.new_receiver() # pylint: disable=protected-access
252+
)
253+
254+
for count in range(10):
255+
watts_delta = 1 if count % 2 == 0 else -1
256+
watts_phases: list[float | None] = [
257+
220.0 * watts_delta,
258+
219.8 * watts_delta,
259+
220.2 * watts_delta,
260+
]
261+
262+
await mockgrid.mock_resampler.send_meter_power_3_phase(
263+
[watts_phases, [None, None, None], [None, 219.8, 220.2]]
264+
)
265+
266+
val = await grid_power_3_phase_recv.receive()
267+
assert val is not None
268+
assert val.value_p1 and val.value_p2 and val.value_p3
269+
assert val.value_p1.as_watts() == watts_phases[0]
270+
assert val.value_p2.as_watts() == watts_phases[1]
271+
assert val.value_p3.as_watts() == watts_phases[2]
272+
273+
205274
async def test_grid_production_consumption_power_consumer_meter(
206275
mocker: MockerFixture,
207276
) -> None:

tests/timeseries/mock_resampler.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
NON_EXISTING_COMPONENT_ID,
2222
)
2323

24-
# pylint: disable=too-many-instance-attributes
24+
# pylint: disable=too-many-instance-attributes disable=too-many-locals
2525

2626

2727
class MockResampler:
@@ -159,13 +159,26 @@ def voltage_senders(ids: list[int]) -> list[list[Sender[Sample[Quantity]]]]:
159159
),
160160
)
161161

162+
def power_3_phase_senders(
163+
ids: list[int],
164+
) -> list[list[Sender[Sample[Quantity]]]]:
165+
return multi_phase_senders(
166+
ids,
167+
(
168+
ComponentMetricId.ACTIVE_POWER_PHASE_1,
169+
ComponentMetricId.ACTIVE_POWER_PHASE_2,
170+
ComponentMetricId.ACTIVE_POWER_PHASE_3,
171+
),
172+
)
173+
162174
self._bat_inverter_current_senders = current_senders(bat_inverter_ids)
163175
self._pv_inverter_current_senders = current_senders(pv_inverter_ids)
164176
self._ev_current_senders = current_senders(evc_ids)
165177
self._chp_current_senders = current_senders(chp_ids)
166178
self._meter_current_senders = current_senders(meter_ids)
167179

168180
self._meter_voltage_senders = voltage_senders(meter_ids)
181+
self._meter_power_3_phase_senders = power_3_phase_senders(meter_ids)
169182

170183
self._next_ts = datetime.now()
171184

@@ -325,3 +338,12 @@ async def send_meter_voltage(self, values: list[list[float | None]]) -> None:
325338
for phase, value in enumerate(meter_values):
326339
sample = self.make_sample(value)
327340
await chan[phase].send(sample)
341+
342+
async def send_meter_power_3_phase(self, values: list[list[float | None]]) -> None:
343+
"""Send the given values as resampler output for meter active power."""
344+
assert len(values) == len(self._meter_power_3_phase_senders)
345+
for chan, meter_values in zip(self._meter_power_3_phase_senders, values):
346+
assert len(meter_values) == 3 # 3 values for phases
347+
for phase, value in enumerate(meter_values):
348+
sample = self.make_sample(value)
349+
await chan[phase].send(sample)

tests/utils/component_data_wrapper.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ def __init__( # pylint: disable=too-many-arguments
101101
component_id: int,
102102
timestamp: datetime,
103103
active_power: float = math.nan,
104+
active_power_per_phase: tuple[float, float, float] = (
105+
math.nan,
106+
math.nan,
107+
math.nan,
108+
),
104109
current_per_phase: tuple[float, float, float] = (math.nan, math.nan, math.nan),
105110
voltage_per_phase: tuple[float, float, float] = (math.nan, math.nan, math.nan),
106111
active_power_inclusion_lower_bound: float = math.nan,
@@ -122,6 +127,7 @@ def __init__( # pylint: disable=too-many-arguments
122127
component_id=component_id,
123128
timestamp=timestamp,
124129
active_power=active_power,
130+
active_power_per_phase=active_power_per_phase,
125131
current_per_phase=current_per_phase,
126132
voltage_per_phase=voltage_per_phase,
127133
active_power_inclusion_lower_bound=active_power_inclusion_lower_bound,
@@ -157,6 +163,11 @@ def __init__( # pylint: disable=too-many-arguments
157163
component_id: int,
158164
timestamp: datetime,
159165
active_power: float = math.nan,
166+
active_power_per_phase: tuple[float, float, float] = (
167+
math.nan,
168+
math.nan,
169+
math.nan,
170+
),
160171
current_per_phase: tuple[float, float, float] = (math.nan, math.nan, math.nan),
161172
voltage_per_phase: tuple[float, float, float] = (math.nan, math.nan, math.nan),
162173
active_power_inclusion_lower_bound: float = math.nan,
@@ -176,6 +187,7 @@ def __init__( # pylint: disable=too-many-arguments
176187
component_id=component_id,
177188
timestamp=timestamp,
178189
active_power=active_power,
190+
active_power_per_phase=active_power_per_phase,
179191
current_per_phase=current_per_phase,
180192
voltage_per_phase=voltage_per_phase,
181193
active_power_inclusion_lower_bound=active_power_inclusion_lower_bound,
@@ -211,6 +223,11 @@ def __init__( # pylint: disable=too-many-arguments
211223
component_id: int,
212224
timestamp: datetime,
213225
active_power: float = math.nan,
226+
active_power_per_phase: tuple[float, float, float] = (
227+
math.nan,
228+
math.nan,
229+
math.nan,
230+
),
214231
current_per_phase: tuple[float, float, float] = (math.nan, math.nan, math.nan),
215232
voltage_per_phase: tuple[float, float, float] = (math.nan, math.nan, math.nan),
216233
frequency: float = math.nan,
@@ -224,6 +241,7 @@ def __init__( # pylint: disable=too-many-arguments
224241
component_id=component_id,
225242
timestamp=timestamp,
226243
active_power=active_power,
244+
active_power_per_phase=active_power_per_phase,
227245
current_per_phase=current_per_phase,
228246
voltage_per_phase=voltage_per_phase,
229247
frequency=frequency,

0 commit comments

Comments
 (0)