Skip to content

Commit d8d50ff

Browse files
authored
Add reactive power for inverters, meters and EV chargers (#30)
Fixes #28.
2 parents 56b1884 + f0e9d25 commit d8d50ff

File tree

5 files changed

+145
-34
lines changed

5 files changed

+145
-34
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66

77
## Introduction
88

9-
Microgrid API client for Python
10-
11-
TODO(cookiecutter): Improve the README file
9+
This project provides a Python client for the [Frequenz Microgrid
10+
API](https://frequenz-floss.github.io/frequenz-api-microgrid/) that is more
11+
idiomatic to use in Python than the automatically-generated gRPC client
12+
provided by `protoc`.
1213

1314
## Supported Platforms
1415

RELEASE_NOTES.md

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,5 @@
11
# Frequenz Microgrid API Client Release Notes
22

3-
## Summary
4-
5-
<!-- Here goes a general summary of what this release is about -->
6-
7-
## Upgrading
8-
9-
<!-- Here goes notes on how to upgrade from previous versions, including deprecations and what they should be replaced with -->
10-
113
## New Features
124

13-
<!-- Here goes the main new features and examples or instructions on how to use them -->
14-
15-
## Bug Fixes
16-
17-
<!-- Here goes notable bug fixes that are worth a special mention or explanation -->
5+
- The `reactive_power` is exposed for meters, inverters and EV chargers.

src/frequenz/client/microgrid/_component.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,16 @@ class ComponentMetricId(Enum):
207207
ACTIVE_POWER_PHASE_3 = "active_power_phase_3"
208208
"""Active power in phase 3."""
209209

210+
REACTIVE_POWER = "reactive_power"
211+
"""Reactive power."""
212+
213+
REACTIVE_POWER_PHASE_1 = "reactive_power_phase_1"
214+
"""Reactive power in phase 1."""
215+
REACTIVE_POWER_PHASE_2 = "reactive_power_phase_2"
216+
"""Reactive power in phase 2."""
217+
REACTIVE_POWER_PHASE_3 = "reactive_power_phase_3"
218+
"""Reactive power in phase 3."""
219+
210220
CURRENT_PHASE_1 = "current_phase_1"
211221
"""Current in phase 1."""
212222
CURRENT_PHASE_2 = "current_phase_2"

src/frequenz/client/microgrid/_component_data.py

Lines changed: 120 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -70,18 +70,46 @@ class MeterData(ComponentData):
7070
"""A wrapper class for holding meter data."""
7171

7272
active_power: float
73-
"""The 3-phase active power, in Watts, represented in the passive sign convention.
74-
+ve current means consumption, away from the grid.
75-
-ve current means supply into the grid.
73+
"""The total active 3-phase AC power, in Watts (W).
74+
75+
Represented in the passive sign convention.
76+
77+
* Positive means consumption from the grid.
78+
* Negative means supply into the grid.
7679
"""
7780

7881
active_power_per_phase: tuple[float, float, float]
79-
"""The AC active power for phase/line 1,2 and 3 respectively."""
82+
"""The per-phase AC active power for phase 1, 2, and 3 respectively, in Watt (W).
83+
84+
Represented in the passive sign convention.
85+
86+
* Positive means consumption from the grid.
87+
* Negative means supply into the grid.
88+
"""
89+
90+
reactive_power: float
91+
"""The total reactive 3-phase AC power, in Volt-Ampere Reactive (VAr).
92+
93+
* Positive power means capacitive (current leading w.r.t. voltage).
94+
* Negative power means inductive (current lagging w.r.t. voltage).
95+
"""
96+
97+
reactive_power_per_phase: tuple[float, float, float]
98+
"""The per-phase AC reactive power, in Volt-Ampere Reactive (VAr).
99+
100+
The provided values are for phase 1, 2, and 3 respectively.
101+
102+
* Positive power means capacitive (current leading w.r.t. voltage).
103+
* Negative power means inductive (current lagging w.r.t. voltage).
104+
"""
80105

81106
current_per_phase: tuple[float, float, float]
82107
"""AC current in Amperes (A) for phase/line 1,2 and 3 respectively.
83-
+ve current means consumption, away from the grid.
84-
-ve current means supply into the grid.
108+
109+
Represented in the passive sign convention.
110+
111+
* Positive means consumption from the grid.
112+
* Negative means supply into the grid.
85113
"""
86114

87115
voltage_per_phase: tuple[float, float, float]
@@ -111,6 +139,12 @@ def from_proto(cls, raw: PbComponentData) -> Self:
111139
raw.meter.data.ac.phase_2.power_active.value,
112140
raw.meter.data.ac.phase_3.power_active.value,
113141
),
142+
reactive_power=raw.meter.data.ac.power_reactive.value,
143+
reactive_power_per_phase=(
144+
raw.meter.data.ac.phase_1.power_reactive.value,
145+
raw.meter.data.ac.phase_2.power_reactive.value,
146+
raw.meter.data.ac.phase_3.power_reactive.value,
147+
),
114148
current_per_phase=(
115149
raw.meter.data.ac.phase_1.current.value,
116150
raw.meter.data.ac.phase_2.current.value,
@@ -241,18 +275,46 @@ class InverterData(ComponentData): # pylint: disable=too-many-instance-attribut
241275
"""A wrapper class for holding inverter data."""
242276

243277
active_power: float
244-
"""The 3-phase active power, in Watts, represented in the passive sign convention.
245-
+ve current means consumption, away from the grid.
246-
-ve current means supply into the grid.
278+
"""The total active 3-phase AC power, in Watts (W).
279+
280+
Represented in the passive sign convention.
281+
282+
* Positive means consumption from the grid.
283+
* Negative means supply into the grid.
247284
"""
248285

249286
active_power_per_phase: tuple[float, float, float]
250-
"""The AC active power for phase/line 1, 2 and 3 respectively."""
287+
"""The per-phase AC active power for phase 1, 2, and 3 respectively, in Watt (W).
288+
289+
Represented in the passive sign convention.
290+
291+
* Positive means consumption from the grid.
292+
* Negative means supply into the grid.
293+
"""
294+
295+
reactive_power: float
296+
"""The total reactive 3-phase AC power, in Volt-Ampere Reactive (VAr).
297+
298+
* Positive power means capacitive (current leading w.r.t. voltage).
299+
* Negative power means inductive (current lagging w.r.t. voltage).
300+
"""
301+
302+
reactive_power_per_phase: tuple[float, float, float]
303+
"""The per-phase AC reactive power, in Volt-Ampere Reactive (VAr).
304+
305+
The provided values are for phase 1, 2, and 3 respectively.
306+
307+
* Positive power means capacitive (current leading w.r.t. voltage).
308+
* Negative power means inductive (current lagging w.r.t. voltage).
309+
"""
251310

252311
current_per_phase: tuple[float, float, float]
253312
"""AC current in Amperes (A) for phase/line 1, 2 and 3 respectively.
254-
+ve current means consumption, away from the grid.
255-
-ve current means supply into the grid.
313+
314+
Represented in the passive sign convention.
315+
316+
* Positive means consumption from the grid.
317+
* Negative means supply into the grid.
256318
"""
257319

258320
voltage_per_phase: tuple[float, float, float]
@@ -335,6 +397,12 @@ def from_proto(cls, raw: PbComponentData) -> Self:
335397
raw.inverter.data.ac.phase_2.power_active.value,
336398
raw.inverter.data.ac.phase_3.power_active.value,
337399
),
400+
reactive_power=raw.inverter.data.ac.power_reactive.value,
401+
reactive_power_per_phase=(
402+
raw.inverter.data.ac.phase_1.power_reactive.value,
403+
raw.inverter.data.ac.phase_2.power_reactive.value,
404+
raw.inverter.data.ac.phase_3.power_reactive.value,
405+
),
338406
current_per_phase=(
339407
raw.inverter.data.ac.phase_1.current.value,
340408
raw.inverter.data.ac.phase_2.current.value,
@@ -363,18 +431,46 @@ class EVChargerData(ComponentData): # pylint: disable=too-many-instance-attribu
363431
"""A wrapper class for holding ev_charger data."""
364432

365433
active_power: float
366-
"""The 3-phase active power, in Watts, represented in the passive sign convention.
367-
+ve current means consumption, away from the grid.
368-
-ve current means supply into the grid.
434+
"""The total active 3-phase AC power, in Watts (W).
435+
436+
Represented in the passive sign convention.
437+
438+
* Positive means consumption from the grid.
439+
* Negative means supply into the grid.
369440
"""
370441

371442
active_power_per_phase: tuple[float, float, float]
372-
"""The AC active power for phase/line 1,2 and 3 respectively."""
443+
"""The per-phase AC active power for phase 1, 2, and 3 respectively, in Watt (W).
444+
445+
Represented in the passive sign convention.
446+
447+
* Positive means consumption from the grid.
448+
* Negative means supply into the grid.
449+
"""
373450

374451
current_per_phase: tuple[float, float, float]
375452
"""AC current in Amperes (A) for phase/line 1,2 and 3 respectively.
376-
+ve current means consumption, away from the grid.
377-
-ve current means supply into the grid.
453+
454+
Represented in the passive sign convention.
455+
456+
* Positive means consumption from the grid.
457+
* Negative means supply into the grid.
458+
"""
459+
460+
reactive_power: float
461+
"""The total reactive 3-phase AC power, in Volt-Ampere Reactive (VAr).
462+
463+
* Positive power means capacitive (current leading w.r.t. voltage).
464+
* Negative power means inductive (current lagging w.r.t. voltage).
465+
"""
466+
467+
reactive_power_per_phase: tuple[float, float, float]
468+
"""The per-phase AC reactive power, in Volt-Ampere Reactive (VAr).
469+
470+
The provided values are for phase 1, 2, and 3 respectively.
471+
472+
* Positive power means capacitive (current leading w.r.t. voltage).
473+
* Negative power means inductive (current lagging w.r.t. voltage).
378474
"""
379475

380476
voltage_per_phase: tuple[float, float, float]
@@ -455,6 +551,12 @@ def from_proto(cls, raw: PbComponentData) -> Self:
455551
raw.ev_charger.data.ac.phase_2.power_active.value,
456552
raw.ev_charger.data.ac.phase_3.power_active.value,
457553
),
554+
reactive_power=raw.ev_charger.data.ac.power_reactive.value,
555+
reactive_power_per_phase=(
556+
raw.ev_charger.data.ac.phase_1.power_reactive.value,
557+
raw.ev_charger.data.ac.phase_2.power_reactive.value,
558+
raw.ev_charger.data.ac.phase_3.power_reactive.value,
559+
),
458560
current_per_phase=(
459561
raw.ev_charger.data.ac.phase_1.current.value,
460562
raw.ev_charger.data.ac.phase_2.current.value,

tests/test_component_data.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,28 @@ def test_inverter_data() -> None:
5252
system_exclusion_bounds=Bounds(lower=-501.0, upper=501.0),
5353
system_inclusion_bounds=Bounds(lower=-51_000.0, upper=51_000.0),
5454
),
55+
power_reactive=Metric(
56+
value=200.3,
57+
system_exclusion_bounds=Bounds(lower=-502.0, upper=502.0),
58+
system_inclusion_bounds=Bounds(lower=-52_000.0, upper=52_000.0),
59+
),
5560
phase_1=AC.ACPhase(
5661
current=Metric(value=12.3),
5762
voltage=Metric(value=229.8),
5863
power_active=Metric(value=33.1),
64+
power_reactive=Metric(value=10.1),
5965
),
6066
phase_2=AC.ACPhase(
6167
current=Metric(value=23.4),
6268
voltage=Metric(value=230.0),
6369
power_active=Metric(value=33.3),
70+
power_reactive=Metric(value=10.2),
6471
),
6572
phase_3=AC.ACPhase(
6673
current=Metric(value=34.5),
6774
voltage=Metric(value=230.2),
6875
power_active=Metric(value=33.8),
76+
power_reactive=Metric(value=10.3),
6977
),
7078
),
7179
),
@@ -84,6 +92,8 @@ def test_inverter_data() -> None:
8492
assert inv_data.frequency == pytest.approx(50.1)
8593
assert inv_data.active_power == pytest.approx(100.2)
8694
assert inv_data.active_power_per_phase == pytest.approx((33.1, 33.3, 33.8))
95+
assert inv_data.reactive_power == pytest.approx(200.3)
96+
assert inv_data.reactive_power_per_phase == pytest.approx((10.1, 10.2, 10.3))
8797
assert inv_data.current_per_phase == pytest.approx((12.3, 23.4, 34.5))
8898
assert inv_data.voltage_per_phase == pytest.approx((229.8, 230.0, 230.2))
8999
assert inv_data.active_power_inclusion_lower_bound == pytest.approx(-51_000.0)

0 commit comments

Comments
 (0)