Skip to content

Commit 992855e

Browse files
Move grid power and current formulas to Grid
Now that there is a microgrid.grid object, the grid metrics are published there. Signed-off-by: Daniel Zullo <[email protected]>
1 parent bc81489 commit 992855e

File tree

12 files changed

+304
-244
lines changed

12 files changed

+304
-244
lines changed

docs/tutorials/getting_started.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ async def run() -> None:
8787
...
8888

8989
# Define your application logic here
90-
grid_meter = microgrid.logical_meter().grid_power.new_receiver()
90+
grid_meter = microgrid.grid().power.new_receiver()
9191

9292
async for power in grid_meter:
9393
print(power.value)
@@ -117,7 +117,7 @@ async def run() -> None:
117117
)
118118

119119
# Define your application logic here
120-
grid_meter = microgrid.logical_meter().grid_power.new_receiver()
120+
grid_meter = microgrid.grid().power.new_receiver()
121121

122122
async for power in grid_meter:
123123
print(power.value)

docs/user-guide/glossary.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ microgrid are connected with each other. Some of the ways in which the SDK uses
166166
the component graph are:
167167

168168
- figure out how to calculate high level metrics like
169-
[`grid_power`][frequenz.sdk.timeseries.logical_meter.LogicalMeter.grid_power],
169+
[`grid_power`][frequenz.sdk.timeseries.grid.Grid.power],
170170
[`consumer_power`][frequenz.sdk.timeseries.logical_meter.LogicalMeter.consumer_power],
171171
etc. for a microgrid, using the available components.
172172
- identify the available {{glossary("battery", "batteries")}} or

src/frequenz/sdk/microgrid/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
6060
This refers to a microgrid's connection to the external Grid. The power flowing through
6161
this connection can be streamed through
62-
[`grid_power`][frequenz.sdk.timeseries.logical_meter.LogicalMeter.grid_power].
62+
[`grid_power`][frequenz.sdk.timeseries.grid.Grid.power].
6363
6464
In locations without a grid connection, this method remains accessible, and streams zero
6565
values.

src/frequenz/sdk/timeseries/_periodic_feature_extractor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class PeriodicFeatureExtractor:
8282
8383
async with MovingWindow(
8484
size=timedelta(days=35),
85-
resampled_data_recv=microgrid.logical_meter().grid_power.new_receiver(),
85+
resampled_data_recv=microgrid.grid().power.new_receiver(),
8686
input_sampling_period=timedelta(seconds=1),
8787
) as moving_window:
8888
feature_extractor = PeriodicFeatureExtractor(

src/frequenz/sdk/timeseries/formula_engine/_formula_engine.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ class FormulaEngine(
231231
resampled data streams.
232232
233233
They are used in the SDK to calculate and stream metrics like
234-
[`grid_power`][frequenz.sdk.timeseries.logical_meter.LogicalMeter.grid_power],
234+
[`grid_power`][frequenz.sdk.timeseries.grid.Grid.power],
235235
[`consumer_power`][frequenz.sdk.timeseries.logical_meter.LogicalMeter.consumer_power],
236236
etc., which are building blocks of the
237237
[Frequenz SDK Microgrid Model][frequenz.sdk.microgrid--frequenz-sdk-microgrid-model].
@@ -267,7 +267,7 @@ class FormulaEngine(
267267
[`battery_pool().power`][frequenz.sdk.timeseries.battery_pool.BatteryPool.power] and
268268
[`ev_charger_pool().power`][frequenz.sdk.timeseries.ev_charger_pool.EVChargerPool]
269269
from the
270-
[`logical_meter().grid_power`][frequenz.sdk.timeseries.logical_meter.LogicalMeter.grid_power],
270+
[`grid().power`][frequenz.sdk.timeseries.grid.Grid.power],
271271
we can build a `FormulaEngine` that provides a stream of this calculated metric as
272272
follows:
273273
@@ -277,11 +277,12 @@ class FormulaEngine(
277277
logical_meter = microgrid.logical_meter()
278278
battery_pool = microgrid.battery_pool()
279279
ev_charger_pool = microgrid.ev_charger_pool()
280+
grid = microgrid.grid()
280281
281282
# apply operations on formula engines to create a formula engine that would
282283
# apply these operations on the corresponding data streams.
283284
net_power = (
284-
logical_meter.grid_power - (battery_pool.power + ev_charger_pool.power)
285+
grid.power - (battery_pool.power + ev_charger_pool.power)
285286
).build("net_power")
286287
287288
async for power in net_power.new_receiver():
@@ -442,7 +443,7 @@ class FormulaEngine3Phase(
442443
[`FormulaEngine`][frequenz.sdk.timeseries.formula_engine.FormulaEngine], except that
443444
they stream [3-phase samples][frequenz.sdk.timeseries.Sample3Phase]. All the
444445
current formulas (like
445-
[`LogicalMeter.grid_current`][frequenz.sdk.timeseries.logical_meter.LogicalMeter.grid_current],
446+
[`Grid.current`][frequenz.sdk.timeseries.grid.Grid.current],
446447
[`EVChargerPool.current`][frequenz.sdk.timeseries.ev_charger_pool.EVChargerPool.current],
447448
etc.) are implemented as 3-phase formulas.
448449
@@ -474,9 +475,10 @@ class FormulaEngine3Phase(
474475
475476
logical_meter = microgrid.logical_meter()
476477
ev_charger_pool = microgrid.ev_charger_pool()
478+
grid = microgrid.grid()
477479
478480
# Calculate grid consumption current that's not used by the EV chargers
479-
other_current = (logical_meter.grid_current - ev_charger_pool.current).build("other_current")
481+
other_current = (grid.current - ev_charger_pool.current).build("other_current")
480482
481483
async for sample in other_current.new_receiver():
482484
print(f"Other current: {sample}")

src/frequenz/sdk/timeseries/grid.py

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
from ..microgrid import connection_manager
1919
from ..microgrid.component._component import ComponentCategory
2020
from . import Fuse
21+
from ._quantities import Current, Power
22+
from .formula_engine import FormulaEngine, FormulaEngine3Phase
2123
from .formula_engine._formula_engine_pool import FormulaEnginePool
24+
from .formula_engine._formula_generators import GridCurrentFormula, GridPowerFormula
2225

2326
if TYPE_CHECKING:
2427
# Break circular import
@@ -29,14 +32,90 @@
2932

3033
@dataclass(frozen=True)
3134
class Grid:
32-
"""A grid connection point."""
35+
"""A grid connection point.
36+
37+
!!! note
38+
The `Grid` instance is not meant to be created directly by users.
39+
Use the [`microgrid.grid`][frequenz.sdk.microgrid.grid] method for
40+
creating or getting the `Grid` instance.
41+
42+
Example:
43+
```python
44+
from datetime import timedelta
45+
46+
from frequenz.sdk import microgrid
47+
from frequenz.sdk.timeseries import ResamplerConfig
48+
49+
await microgrid.initialize(
50+
"127.0.0.1",
51+
50051,
52+
ResamplerConfig(resampling_period=timedelta(seconds=1))
53+
)
54+
55+
grid = microgrid.grid()
56+
assert grid, "Grid is not initialized"
57+
58+
# Get a receiver for a builtin formula
59+
grid_power_recv = grid.power.new_receiver()
60+
async for grid_power_sample in grid_power_recv:
61+
print(grid_power_sample)
62+
```
63+
"""
3364

3465
fuse: Fuse
3566
"""The fuse protecting the grid connection point."""
3667

3768
_formula_pool: FormulaEnginePool
3869
"""The formula engine pool to generate grid metrics."""
3970

71+
@property
72+
def power(self) -> FormulaEngine[Power]:
73+
"""Fetch the grid power for the microgrid.
74+
75+
This formula produces values that are in the Passive Sign Convention (PSC).
76+
77+
If a formula engine to calculate grid power is not already running, it will be
78+
started.
79+
80+
A receiver from the formula engine can be created using the `new_receiver`
81+
method.
82+
83+
Returns:
84+
A FormulaEngine that will calculate and stream grid power.
85+
"""
86+
engine = self._formula_pool.from_power_formula_generator(
87+
"grid_power",
88+
GridPowerFormula,
89+
)
90+
assert isinstance(engine, FormulaEngine)
91+
return engine
92+
93+
@property
94+
def current(self) -> FormulaEngine3Phase[Current]:
95+
"""Fetch the grid power for the microgrid.
96+
97+
This formula produces values that are in the Passive Sign Convention (PSC).
98+
99+
If a formula engine to calculate grid current is not already running, it will be
100+
started.
101+
102+
A receiver from the formula engine can be created using the `new_receiver`
103+
method.
104+
105+
Returns:
106+
A FormulaEngine that will calculate and stream grid current.
107+
"""
108+
engine = self._formula_pool.from_3_phase_current_formula_generator(
109+
"grid_current",
110+
GridCurrentFormula,
111+
)
112+
assert isinstance(engine, FormulaEngine3Phase)
113+
return engine
114+
115+
async def stop(self) -> None:
116+
"""Stop all formula engines."""
117+
await self._formula_pool.stop()
118+
40119

41120
_GRID: Grid | None = None
42121

src/frequenz/sdk/timeseries/logical_meter/_logical_meter.py

Lines changed: 7 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,12 @@
1010

1111
from ...actor import ChannelRegistry, ComponentMetricRequest
1212
from ...microgrid.component import ComponentMetricId
13-
from .._quantities import Current, Power, Quantity
14-
from ..formula_engine import FormulaEngine, FormulaEngine3Phase
13+
from .._quantities import Power, Quantity
14+
from ..formula_engine import FormulaEngine
1515
from ..formula_engine._formula_engine_pool import FormulaEnginePool
1616
from ..formula_engine._formula_generators import (
1717
CHPPowerFormula,
1818
ConsumerPowerFormula,
19-
GridCurrentFormula,
20-
GridPowerFormula,
2119
ProducerPowerFormula,
2220
PVPowerFormula,
2321
)
@@ -49,17 +47,17 @@ class LogicalMeter:
4947
)
5048
5149
logical_meter = microgrid.logical_meter()
50+
grid = microgrid.grid()
5251
5352
# Get a receiver for a builtin formula
54-
grid_power_recv = logical_meter.grid_power.new_receiver()
55-
async for grid_power_sample in grid_power_recv:
56-
print(grid_power_sample)
53+
consumer_power_recv = logical_meter.consumer_power.new_receiver()
54+
async for consumer_power_sample in consumer_power_recv:
55+
print(consumer_power_sample)
5756
5857
# or compose formulas to create a new formula
5958
net_power_recv = (
6059
(
61-
logical_meter.grid_power
62-
- logical_meter.pv_power
60+
grid.power - logical_meter.pv_power
6361
)
6462
.build("net_power")
6563
.new_receiver()
@@ -128,50 +126,6 @@ def start_formula(
128126
formula, component_metric_id, nones_are_zeros=nones_are_zeros
129127
)
130128

131-
@property
132-
def grid_power(self) -> FormulaEngine[Power]:
133-
"""Fetch the grid power for the microgrid.
134-
135-
This formula produces values that are in the Passive Sign Convention (PSC).
136-
137-
If a formula engine to calculate grid power is not already running, it will be
138-
started.
139-
140-
A receiver from the formula engine can be created using the `new_receiver`
141-
method.
142-
143-
Returns:
144-
A FormulaEngine that will calculate and stream grid power.
145-
"""
146-
engine = self._formula_pool.from_power_formula_generator(
147-
"grid_power",
148-
GridPowerFormula,
149-
)
150-
assert isinstance(engine, FormulaEngine)
151-
return engine
152-
153-
@property
154-
def grid_current(self) -> FormulaEngine3Phase[Current]:
155-
"""Fetch the grid power for the microgrid.
156-
157-
This formula produces values that are in the Passive Sign Convention (PSC).
158-
159-
If a formula engine to calculate grid current is not already running, it will be
160-
started.
161-
162-
A receiver from the formula engine can be created using the `new_receiver`
163-
method.
164-
165-
Returns:
166-
A FormulaEngine that will calculate and stream grid current.
167-
"""
168-
engine = self._formula_pool.from_3_phase_current_formula_generator(
169-
"grid_current",
170-
GridCurrentFormula,
171-
)
172-
assert isinstance(engine, FormulaEngine3Phase)
173-
return engine
174-
175129
@property
176130
def consumer_power(self) -> FormulaEngine[Power]:
177131
"""Fetch the consumer power for the microgrid.

0 commit comments

Comments
 (0)