@@ -203,11 +203,68 @@ class FormulaEngine(
203203 QuantityT ,
204204 ],
205205):
206- """
207- The FormulaEngine evaluates formulas and streams the results.
206+ """[`FormulaEngine`][frequenz.sdk.timeseries.formula_engine.FormulaEngine]s are a
207+ part of the SDK's data pipeline, and provide a way for the SDK to apply formulas on
208+ resampled data streams.
208209
209- Use the `FormulaBuilder` to create `FormulaEngine` instances.
210- """
210+ They are used in the SDK to calculate and stream metrics like
211+ [`grid_power`][frequenz.sdk.timeseries.logical_meter.LogicalMeter.grid_power],
212+ [`consumer_power`][frequenz.sdk.timeseries.logical_meter.LogicalMeter.consumer_power],
213+ etc., which are building blocks of the
214+ [Frequenz SDK Microgrid Model][frequenz.sdk.microgrid--frequenz-sdk-microgrid-model].
215+
216+ The SDK creates the formulas by analysing the configuration of components in the
217+ {{glossary("Component Graph")}}.
218+
219+ ### Streaming Interface
220+
221+ The
222+ [`FormulaEngine.new_receiver()`][frequenz.sdk.timeseries.formula_engine.FormulaEngine.new_receiver]
223+ method can be used to create a
224+ [Receiver](https://frequenz-floss.github.io/frequenz-channels-python/latest/reference/frequenz/channels/#frequenz.channels.Receiver)
225+ that streams the [Sample][frequenz.sdk.timeseries.Sample]s calculated by the formula
226+ engine.
227+
228+ ```python
229+ from frequenz.sdk import microgrid
230+
231+ battery_pool = microgrid.battery_pool()
232+
233+ async for power in battery_pool.power.new_receiver():
234+ print(f"{power=}")
235+ ```
236+
237+ ### Composition
238+
239+ Composite `FormulaEngine`s can be built using arithmetic operations on
240+ `FormulaEngine`s streaming the same type of data.
241+
242+ For example, if you're interested in a particular composite metric that can be
243+ calculated by subtracting
244+ [`battery_pool().power`][frequenz.sdk.timeseries.battery_pool.BatteryPool.power] and
245+ [`ev_charger_pool().power`][frequenz.sdk.timeseries.ev_charger_pool.EVChargerPool]
246+ from the
247+ [`logical_meter().grid_power`][frequenz.sdk.timeseries.logical_meter.LogicalMeter.grid_power],
248+ we can build a `FormulaEngine` that provides a stream of this calculated metric as
249+ follows:
250+
251+ ```python
252+ from frequenz.sdk import microgrid
253+
254+ logical_meter = microgrid.logical_meter()
255+ battery_pool = microgrid.battery_pool()
256+ ev_charger_pool = microgrid.ev_charger_pool()
257+
258+ # apply operations on formula engines to create a formula engine that would
259+ # apply these operations on the corresponding data streams.
260+ net_power = (
261+ logical_meter.grid_power - (battery_pool.power + ev_charger_pool.power)
262+ ).build("net_power")
263+
264+ async for power in net_power.new_receiver():
265+ print(f"{power=}")
266+ ```
267+ """ # noqa: D400, D205
211268
212269 def __init__ (
213270 self ,
@@ -356,11 +413,52 @@ class FormulaEngine3Phase(
356413 QuantityT ,
357414 ]
358415):
359- """
360- The FormulaEngine evaluates formulas and streams the results.
416+ """A
417+ [`FormulaEngine3Phase`][frequenz.sdk.timeseries.formula_engine.FormulaEngine3Phase]
418+ is similar to a
419+ [`FormulaEngine`][frequenz.sdk.timeseries.formula_engine.FormulaEngine], except that
420+ they stream [3-phase samples][frequenz.sdk.timeseries.Sample3Phase]. All the
421+ current formulas (like
422+ [`LogicalMeter.grid_current`][frequenz.sdk.timeseries.logical_meter.LogicalMeter.grid_current],
423+ [`EVChargerPool.current`][frequenz.sdk.timeseries.ev_charger_pool.EVChargerPool.current],
424+ etc.) are implemented as 3-phase formulas.
361425
362- Use the `FormulaBuilder` to create `FormulaEngine` instances.
363- """
426+ ### Streaming Interface
427+
428+ The
429+ [`FormulaEngine3Phase.new_receiver()`][frequenz.sdk.timeseries.formula_engine.FormulaEngine3Phase.new_receiver]
430+ method can be used to create a
431+ [Receiver](https://frequenz-floss.github.io/frequenz-channels-python/latest/reference/frequenz/channels/#frequenz.channels.Receiver)
432+ that streams the [Sample3Phase][frequenz.sdk.timeseries.Sample3Phase] values
433+ calculated by the formula engine.
434+
435+ ```python
436+ from frequenz.sdk import microgrid
437+
438+ ev_charger_pool = microgrid.ev_charger_pool()
439+
440+ async for sample in ev_charger_pool.current.new_receiver():
441+ print(f"Current: {sample}")
442+ ```
443+
444+ ### Composition
445+
446+ `FormulaEngine3Phase` instances can be composed together, just like `FormulaEngine`
447+ instances.
448+
449+ ```python
450+ from frequenz.sdk import microgrid
451+
452+ logical_meter = microgrid.logical_meter()
453+ ev_charger_pool = microgrid.ev_charger_pool()
454+
455+ # Calculate grid consumption current that's not used by the EV chargers
456+ other_current = (logical_meter.grid_current - ev_charger_pool.current).build("other_current")
457+
458+ async for sample in other_current.new_receiver():
459+ print(f"Other current: {sample}")
460+ ```
461+ """ # noqa: D205, D400
364462
365463 def __init__ (
366464 self ,
0 commit comments