Skip to content

Commit 318f236

Browse files
committed
Copy formula engine from SDK v1.0.0-rc901
1 parent 0882daf commit 318f236

23 files changed

+4495
-0
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# License: MIT
2+
# Copyright © 2023 Frequenz Energy-as-a-Service GmbH
3+
4+
"""Provides a way for the SDK to apply formulas on resampled data streams.
5+
6+
# Formula Engine
7+
8+
[`FormulaEngine`][frequenz.sdk.timeseries.formula_engine.FormulaEngine]s are used in the
9+
SDK to calculate and stream metrics like
10+
[`grid_power`][frequenz.sdk.timeseries.grid.Grid.power],
11+
[`consumer_power`][frequenz.sdk.timeseries.consumer.Consumer.power], etc., which are
12+
building blocks of the [Frequenz SDK Microgrid
13+
Model][frequenz.sdk.microgrid--frequenz-sdk-microgrid-model].
14+
15+
The SDK creates the formulas by analysing the configuration of components in the
16+
{{glossary("Component Graph")}}.
17+
18+
## Streaming Interface
19+
20+
The
21+
[`FormulaEngine.new_receiver()`][frequenz.sdk.timeseries.formula_engine.FormulaEngine.new_receiver]
22+
method can be used to create a [Receiver][frequenz.channels.Receiver] that streams the
23+
[Sample][frequenz.sdk.timeseries.Sample]s calculated by the formula engine.
24+
25+
```python
26+
from frequenz.sdk import microgrid
27+
28+
battery_pool = microgrid.new_battery_pool(priority=5)
29+
30+
async for power in battery_pool.power.new_receiver():
31+
print(f"{power=}")
32+
```
33+
34+
## Composition
35+
36+
Composite `FormulaEngine`s can be built using arithmetic operations on
37+
`FormulaEngine`s streaming the same type of data.
38+
39+
For example, if you're interested in a particular composite metric that can be
40+
calculated by subtracting
41+
[`new_battery_pool().power`][frequenz.sdk.timeseries.battery_pool.BatteryPool.power] and
42+
[`new_ev_charger_pool().power`][frequenz.sdk.timeseries.ev_charger_pool.EVChargerPool]
43+
from the
44+
[`grid().power`][frequenz.sdk.timeseries.grid.Grid.power],
45+
we can build a `FormulaEngine` that provides a stream of this calculated metric as
46+
follows:
47+
48+
```python
49+
from frequenz.sdk import microgrid
50+
51+
battery_pool = microgrid.new_battery_pool(priority=5)
52+
ev_charger_pool = microgrid.new_ev_charger_pool(priority=5)
53+
grid = microgrid.grid()
54+
55+
# apply operations on formula engines to create a formula engine that would
56+
# apply these operations on the corresponding data streams.
57+
net_power = (
58+
grid.power - (battery_pool.power + ev_charger_pool.power)
59+
).build("net_power")
60+
61+
async for power in net_power.new_receiver():
62+
print(f"{power=}")
63+
```
64+
65+
# Formula Engine 3-Phase
66+
67+
A [`FormulaEngine3Phase`][frequenz.sdk.timeseries.formula_engine.FormulaEngine3Phase]
68+
is similar to a
69+
[`FormulaEngine`][frequenz.sdk.timeseries.formula_engine.FormulaEngine], except that
70+
they stream [3-phase samples][frequenz.sdk.timeseries.Sample3Phase]. All the
71+
current formulas (like
72+
[`Grid.current_per_phase`][frequenz.sdk.timeseries.grid.Grid.current_per_phase],
73+
[`EVChargerPool.current_per_phase`][frequenz.sdk.timeseries.ev_charger_pool.EVChargerPool.current_per_phase],
74+
etc.) are implemented as per-phase formulas.
75+
76+
## Streaming Interface
77+
78+
The
79+
[`FormulaEngine3Phase.new_receiver()`][frequenz.sdk.timeseries.formula_engine.FormulaEngine3Phase.new_receiver]
80+
method can be used to create a [Receiver][frequenz.channels.Receiver] that streams the
81+
[Sample3Phase][frequenz.sdk.timeseries.Sample3Phase] values
82+
calculated by the formula engine.
83+
84+
```python
85+
from frequenz.sdk import microgrid
86+
87+
ev_charger_pool = microgrid.new_ev_charger_pool(priority=5)
88+
89+
async for sample in ev_charger_pool.current_per_phase.new_receiver():
90+
print(f"Current: {sample}")
91+
```
92+
93+
## Composition
94+
95+
`FormulaEngine3Phase` instances can be composed together, just like `FormulaEngine`
96+
instances.
97+
98+
```python
99+
from frequenz.sdk import microgrid
100+
101+
ev_charger_pool = microgrid.new_ev_charger_pool(priority=5)
102+
grid = microgrid.grid()
103+
104+
# Calculate grid consumption current that's not used by the EV chargers
105+
other_current = (grid.current_per_phase - ev_charger_pool.current_per_phase).build(
106+
"other_current"
107+
)
108+
109+
async for sample in other_current.new_receiver():
110+
print(f"Other current: {sample}")
111+
```
112+
"""
113+
114+
from ._formula_engine import FormulaEngine, FormulaEngine3Phase
115+
116+
__all__ = [
117+
"FormulaEngine",
118+
"FormulaEngine3Phase",
119+
]
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# License: MIT
2+
# Copyright © 2022 Frequenz Energy-as-a-Service GmbH
3+
4+
"""Formula Engine Exceptions."""
5+
6+
7+
class FormulaEngineError(Exception):
8+
"""An error occurred while fetching metrics or applying the formula on them."""

0 commit comments

Comments
 (0)