Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unpin mkdocs-autorefs and fix multple URLs for warnings #1051

Merged
merged 3 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions docs/user-guide/formula-engine.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
# Formula Engine

::: frequenz.sdk.timeseries.formula_engine.FormulaEngine
options:
members: None
show_bases: false
show_root_full_path: false
show_source: false

::: frequenz.sdk.timeseries.formula_engine.FormulaEngine3Phase
::: frequenz.sdk.timeseries.formula_engine
options:
members: None
show_bases: false
show_root_full_path: false
show_root_heading: false
show_root_toc_entry: false
show_source: false
2 changes: 2 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ markdown_extensions:
permalink: "¤"

plugins:
- autorefs:
resolve_closest: true
- gen-files:
scripts:
- docs/_scripts/mkdocstrings_autoapi.py
Expand Down
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ dev-mkdocs = [
"black == 24.8.0",
"Markdown==3.7",
"mike == 2.1.3",
"mkdocs-autorefs >= 1.0.1, < 1.1",
"mkdocs-gen-files == 0.5.0",
"mkdocs-literate-nav == 0.6.1",
"mkdocs-macros-plugin == 1.0.5",
Expand Down
112 changes: 107 additions & 5 deletions src/frequenz/sdk/timeseries/formula_engine/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,114 @@
# License: MIT
# Copyright © 2023 Frequenz Energy-as-a-Service GmbH

"""The formula engine module.
"""Provides a way for the SDK to apply formulas on resampled data streams.

This module exposes the
[FormulaEngine][frequenz.sdk.timeseries.formula_engine.FormulaEngine] and
[FormulaEngine3Phase][frequenz.sdk.timeseries.formula_engine.FormulaEngine3Phase]
classes.
# Formula Engine

[`FormulaEngine`][frequenz.sdk.timeseries.formula_engine.FormulaEngine]s are used in the
SDK to calculate and stream metrics like
[`grid_power`][frequenz.sdk.timeseries.grid.Grid.power],
[`consumer_power`][frequenz.sdk.timeseries.consumer.Consumer.power], etc., which are
building blocks of the [Frequenz SDK Microgrid
Model][frequenz.sdk.microgrid--frequenz-sdk-microgrid-model].

The SDK creates the formulas by analysing the configuration of components in the
{{glossary("Component Graph")}}.

## Streaming Interface

The
[`FormulaEngine.new_receiver()`][frequenz.sdk.timeseries.formula_engine.FormulaEngine.new_receiver]
method can be used to create a [Receiver][frequenz.channels.Receiver] that streams the
[Sample][frequenz.sdk.timeseries.Sample]s calculated by the formula engine.

```python
from frequenz.sdk import microgrid

battery_pool = microgrid.new_battery_pool(priority=5)

async for power in battery_pool.power.new_receiver():
print(f"{power=}")
```

## Composition

Composite `FormulaEngine`s can be built using arithmetic operations on
`FormulaEngine`s streaming the same type of data.

For example, if you're interested in a particular composite metric that can be
calculated by subtracting
[`new_battery_pool().power`][frequenz.sdk.timeseries.battery_pool.BatteryPool.power] and
[`new_ev_charger_pool().power`][frequenz.sdk.timeseries.ev_charger_pool.EVChargerPool]
from the
[`grid().power`][frequenz.sdk.timeseries.grid.Grid.power],
we can build a `FormulaEngine` that provides a stream of this calculated metric as
follows:

```python
from frequenz.sdk import microgrid

battery_pool = microgrid.new_battery_pool(priority=5)
ev_charger_pool = microgrid.new_ev_charger_pool(priority=5)
grid = microgrid.grid()

# apply operations on formula engines to create a formula engine that would
# apply these operations on the corresponding data streams.
net_power = (
grid.power - (battery_pool.power + ev_charger_pool.power)
).build("net_power")

async for power in net_power.new_receiver():
print(f"{power=}")
```

# Formula Engine 3-Phase

A [`FormulaEngine3Phase`][frequenz.sdk.timeseries.formula_engine.FormulaEngine3Phase]
is similar to a
[`FormulaEngine`][frequenz.sdk.timeseries.formula_engine.FormulaEngine], except that
they stream [3-phase samples][frequenz.sdk.timeseries.Sample3Phase]. All the
current formulas (like
[`Grid.current_per_phase`][frequenz.sdk.timeseries.grid.Grid.current_per_phase],
[`EVChargerPool.current_per_phase`][frequenz.sdk.timeseries.ev_charger_pool.EVChargerPool.current_per_phase],
etc.) are implemented as per-phase formulas.

## Streaming Interface

The
[`FormulaEngine3Phase.new_receiver()`][frequenz.sdk.timeseries.formula_engine.FormulaEngine3Phase.new_receiver]
method can be used to create a [Receiver][frequenz.channels.Receiver] that streams the
[Sample3Phase][frequenz.sdk.timeseries.Sample3Phase] values
calculated by the formula engine.

```python
from frequenz.sdk import microgrid

ev_charger_pool = microgrid.new_ev_charger_pool(priority=5)

async for sample in ev_charger_pool.current_per_phase.new_receiver():
print(f"Current: {sample}")
```

## Composition

`FormulaEngine3Phase` instances can be composed together, just like `FormulaEngine`
instances.

```python
from frequenz.sdk import microgrid

ev_charger_pool = microgrid.new_ev_charger_pool(priority=5)
grid = microgrid.grid()

# Calculate grid consumption current that's not used by the EV chargers
other_current = (grid.current_per_phase - ev_charger_pool.current_per_phase).build(
"other_current"
)

async for sample in other_current.new_receiver():
print(f"Other current: {sample}")
```
"""

from ._formula_engine import FormulaEngine, FormulaEngine3Phase
Expand Down
158 changes: 56 additions & 102 deletions src/frequenz/sdk/timeseries/formula_engine/_formula_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,68 +57,39 @@


class FormulaEngine(Generic[QuantityT]):
"""[`FormulaEngine`][frequenz.sdk.timeseries.formula_engine.FormulaEngine]s are a
part of the SDK's data pipeline, and provide a way for the SDK to apply formulas on
resampled data streams.
"""An engine to apply formulas on resampled data streams.

They are used in the SDK to calculate and stream metrics like
[`grid_power`][frequenz.sdk.timeseries.grid.Grid.power],
[`consumer_power`][frequenz.sdk.timeseries.consumer.Consumer.power],
etc., which are building blocks of the
[Frequenz SDK Microgrid Model][frequenz.sdk.microgrid--frequenz-sdk-microgrid-model].
Please refer to the [module documentation][frequenz.sdk.timeseries.formula_engine]
for more information on how formula engines are used throughout the SDK.

The SDK creates the formulas by analysing the configuration of components in the
{{glossary("Component Graph")}}.

### Streaming Interface

The
[`FormulaEngine.new_receiver()`][frequenz.sdk.timeseries.formula_engine.FormulaEngine.new_receiver]
method can be used to create a
[Receiver](https://frequenz-floss.github.io/frequenz-channels-python/latest/reference/frequenz/channels/#frequenz.channels.Receiver)
that streams the [Sample][frequenz.sdk.timeseries.Sample]s calculated by the formula
engine.

```python
from frequenz.sdk import microgrid

battery_pool = microgrid.new_battery_pool(priority=5)

async for power in battery_pool.power.new_receiver():
print(f"{power=}")
```

### Composition
Example: Streaming the power of a battery pool.
```python
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're already saying please refer to the module documentation, so why can't they get their examples from there too? It looks a bit weird to have examples after the line that tells people to go elsewhere.

Besides, the formula engine examples already take so long in CI. Now it is going to be twice as long. :-((

from frequenz.sdk import microgrid

Composite `FormulaEngine`s can be built using arithmetic operations on
`FormulaEngine`s streaming the same type of data.
battery_pool = microgrid.new_battery_pool(priority=5)

For example, if you're interested in a particular composite metric that can be
calculated by subtracting
[`new_battery_pool().power`][frequenz.sdk.timeseries.battery_pool.BatteryPool.power] and
[`new_ev_charger_pool().power`][frequenz.sdk.timeseries.ev_charger_pool.EVChargerPool]
from the
[`grid().power`][frequenz.sdk.timeseries.grid.Grid.power],
we can build a `FormulaEngine` that provides a stream of this calculated metric as
follows:
async for power in battery_pool.power.new_receiver():
print(f"{power=}")
```

```python
from frequenz.sdk import microgrid
Example: Composition of formula engines.
```python
from frequenz.sdk import microgrid

battery_pool = microgrid.new_battery_pool(priority=5)
ev_charger_pool = microgrid.new_ev_charger_pool(priority=5)
grid = microgrid.grid()
battery_pool = microgrid.new_battery_pool(priority=5)
ev_charger_pool = microgrid.new_ev_charger_pool(priority=5)
grid = microgrid.grid()

# apply operations on formula engines to create a formula engine that would
# apply these operations on the corresponding data streams.
net_power = (
grid.power - (battery_pool.power + ev_charger_pool.power)
).build("net_power")
# apply operations on formula engines to create a formula engine that would
# apply these operations on the corresponding data streams.
net_power = (
grid.power - (battery_pool.power + ev_charger_pool.power)
).build("net_power")

async for power in net_power.new_receiver():
print(f"{power=}")
```
""" # noqa: D400, D205
async for power in net_power.new_receiver():
print(f"{power=}")
```
"""

def __init__(
self,
Expand Down Expand Up @@ -392,54 +363,37 @@ def new_receiver(


class FormulaEngine3Phase(Generic[QuantityT]):
"""A
[`FormulaEngine3Phase`][frequenz.sdk.timeseries.formula_engine.FormulaEngine3Phase]
is similar to a
[`FormulaEngine`][frequenz.sdk.timeseries.formula_engine.FormulaEngine], except that
they stream [3-phase samples][frequenz.sdk.timeseries.Sample3Phase]. All the
current formulas (like
[`Grid.current_per_phase`][frequenz.sdk.timeseries.grid.Grid.current_per_phase],
[`EVChargerPool.current_per_phase`][frequenz.sdk.timeseries.ev_charger_pool.EVChargerPool.current_per_phase],
etc.) are implemented as per-phase formulas.

### Streaming Interface

The
[`FormulaEngine3Phase.new_receiver()`][frequenz.sdk.timeseries.formula_engine.FormulaEngine3Phase.new_receiver]
method can be used to create a
[Receiver](https://frequenz-floss.github.io/frequenz-channels-python/latest/reference/frequenz/channels/#frequenz.channels.Receiver)
that streams the [Sample3Phase][frequenz.sdk.timeseries.Sample3Phase] values
calculated by the formula engine.

```python
from frequenz.sdk import microgrid

ev_charger_pool = microgrid.new_ev_charger_pool(priority=5)

async for sample in ev_charger_pool.current_per_phase.new_receiver():
print(f"Current: {sample}")
```

### Composition

`FormulaEngine3Phase` instances can be composed together, just like `FormulaEngine`
instances.

```python
from frequenz.sdk import microgrid

ev_charger_pool = microgrid.new_ev_charger_pool(priority=5)
grid = microgrid.grid()

# Calculate grid consumption current that's not used by the EV chargers
other_current = (grid.current_per_phase - ev_charger_pool.current_per_phase).build(
"other_current"
)

async for sample in other_current.new_receiver():
print(f"Other current: {sample}")
```
""" # noqa: D205, D400
"""An engine to apply formulas on 3-phase resampled data streams.

Please refer to the [module documentation][frequenz.sdk.timeseries.formula_engine]
for more information on how formula engines are used throughout the SDK.

Example: Streaming the current of an EV charger pool.
```python
from frequenz.sdk import microgrid

ev_charger_pool = microgrid.new_ev_charger_pool(priority=5)

async for sample in ev_charger_pool.current_per_phase.new_receiver():
print(f"Current: {sample}")
```

Example: Composition of formula engines.
```python
from frequenz.sdk import microgrid

ev_charger_pool = microgrid.new_ev_charger_pool(priority=5)
grid = microgrid.grid()

# Calculate grid consumption current that's not used by the EV chargers
other_current = (grid.current_per_phase - ev_charger_pool.current_per_phase).build(
"other_current"
)

async for sample in other_current.new_receiver():
print(f"Other current: {sample}")
```
"""

def __init__(
self,
Expand Down
Loading