Skip to content

Commit a5db21d

Browse files
Add formula generator for the grid 3-phase power
Signed-off-by: Daniel Zullo <[email protected]>
1 parent 1831a5a commit a5db21d

File tree

2 files changed

+92
-0
lines changed

2 files changed

+92
-0
lines changed

src/frequenz/sdk/timeseries/formula_engine/_formula_generators/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
FormulaGeneratorConfig,
1616
)
1717
from ._grid_current_formula import GridCurrentFormula
18+
from ._grid_power_3_phase_formula import GridPower3PhaseFormula
1819
from ._grid_power_formula import GridPowerFormula
1920
from ._producer_power_formula import ProducerPowerFormula
2021
from ._pv_power_formula import PVPowerFormula
@@ -30,6 +31,7 @@
3031
#
3132
"CHPPowerFormula",
3233
"ConsumerPowerFormula",
34+
"GridPower3PhaseFormula",
3335
"GridPowerFormula",
3436
"BatteryPowerFormula",
3537
"EVChargerPowerFormula",
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# License: MIT
2+
# Copyright © 2024 Frequenz Energy-as-a-Service GmbH
3+
4+
"""Formula generator from component graph for 3-phase Grid Power."""
5+
6+
from ....microgrid.component import Component, ComponentCategory, ComponentMetricId
7+
from ..._quantities import Power
8+
from .._formula_engine import FormulaEngine, FormulaEngine3Phase
9+
from ._formula_generator import FormulaGenerator
10+
11+
12+
class GridPower3PhaseFormula(FormulaGenerator[Power]):
13+
"""Create a formula engine for calculating the grid 3-phase power."""
14+
15+
def generate( # noqa: DOC502
16+
# ComponentNotFound is raised indirectly by _get_grid_component_successors
17+
self,
18+
) -> FormulaEngine3Phase[Power]:
19+
"""Generate a formula for calculating grid 3-phase power.
20+
21+
Raises:
22+
ComponentNotFound: when the component graph doesn't have a `GRID` component.
23+
24+
Returns:
25+
A formula engine that will calculate grid 3-phase power values.
26+
"""
27+
grid_successors = self._get_grid_component_successors()
28+
29+
return FormulaEngine3Phase(
30+
"grid-power-3-phase",
31+
Power.from_watts,
32+
(
33+
self._gen_phase_formula(
34+
grid_successors, ComponentMetricId.ACTIVE_POWER_PHASE_1
35+
),
36+
self._gen_phase_formula(
37+
grid_successors, ComponentMetricId.ACTIVE_POWER_PHASE_2
38+
),
39+
self._gen_phase_formula(
40+
grid_successors, ComponentMetricId.ACTIVE_POWER_PHASE_3
41+
),
42+
),
43+
)
44+
45+
def _gen_phase_formula(
46+
self,
47+
grid_successors: set[Component],
48+
metric_id: ComponentMetricId,
49+
) -> FormulaEngine[Power]:
50+
"""Generate a formula for calculating grid 3-phase power from the component graph.
51+
52+
Generate a formula that adds values from all components that are directly
53+
connected to the grid.
54+
55+
Args:
56+
grid_successors: The set of components that are directly connected to the grid.
57+
metric_id: The metric to use for the formula.
58+
59+
Returns:
60+
A formula engine that will calculate grid 3-phase power values.
61+
"""
62+
formula_builder = self._get_builder(
63+
"grid-power-3-phase", metric_id, Power.from_watts
64+
)
65+
66+
for idx, comp in enumerate(grid_successors):
67+
# When inverters or EV chargers produce `None` samples, they are
68+
# excluded from the calculation by treating their `None` values
69+
# as `0`s.
70+
#
71+
# This is not possible for Meters, so when they produce `None`
72+
# values, those values get propagated as the output.
73+
if comp.category in (
74+
ComponentCategory.INVERTER,
75+
ComponentCategory.EV_CHARGER,
76+
):
77+
nones_are_zeros = True
78+
elif comp.category == ComponentCategory.METER:
79+
nones_are_zeros = False
80+
else:
81+
continue
82+
83+
if idx > 0:
84+
formula_builder.push_oper("+")
85+
86+
formula_builder.push_component_metric(
87+
comp.component_id, nones_are_zeros=nones_are_zeros
88+
)
89+
90+
return formula_builder.build()

0 commit comments

Comments
 (0)