Skip to content
Draft
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
59 changes: 29 additions & 30 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ crate-type = ["cdylib"]

[dependencies]
pyo3 = "0.29.0"
frequenz-microgrid-component-graph = "0.5"
frequenz-microgrid-component-graph = { git = "https://github.com/shsms/frequenz-microgrid-component-graph-rs", branch = "meter-subtraction" }
2 changes: 2 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

- Bumped PyO3 to 0.29 to pull in the fix for RUSTSEC out-of-bounds read advisory GHSA-36hh-v3qg-5jq4 (`nth`/`nth_back` on list/tuple iterators).

- `prefer_meters_in_component_formulas` now defaults to `False`: per-category formulas use the component reading as the primary source and the meter as the fallback. Set it to `True` to restore the previous meter-first behavior.

## New Features

<!-- Here goes the main new features and examples or instructions on how to use them -->
Expand Down
16 changes: 8 additions & 8 deletions python/frequenz/microgrid_component_graph/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class ComponentGraphConfig:
allow_unspecified_inverters: bool = False,
disable_fallback_components: bool = False,
include_phantom_loads_in_consumer_formula: bool = False,
prefer_meters_in_component_formulas: bool = True,
prefer_meters_in_component_formulas: bool = False,
formula_overrides: FormulaOverrides | None = None,
) -> None:
"""Initialize this instance.
Expand All @@ -50,25 +50,25 @@ class ComponentGraphConfig:
predecessor meters. When `false`, consumer formula is generated by
excluding production and battery components from the grid measurements.
prefer_meters_in_component_formulas: Default policy for the per-category
formulas. When `True` (the default), the meter measurement is the
primary source and the device measurement is the fallback for
formulas. When `False` (the default), the component measurement is the
primary source and the meter measurement is the fallback for
`battery_formula`, `chp_formula`, `pv_formula`, `wind_turbine_formula`,
`ev_charger_formula`, and `steam_boiler_formula`. When `False`, the
device is primary and the meter is the fallback. Has no effect on
`ev_charger_formula`, and `steam_boiler_formula`. When `True`, the
meter is primary and the component is the fallback. Has no effect on
`grid_formula`, `consumer_formula`, `producer_formula`, or any of the
coalesce formulas.
formula_overrides: Per-formula overrides for the meter/device preference;
formula_overrides: Per-formula overrides for the meter/component preference;
see `FormulaOverrides`. Each entry, when set, takes precedence over
`prefer_meters_in_component_formulas` for that formula.
"""

class FormulaOverrides:
"""Per-formula overrides for the meter/device preference.
"""Per-formula overrides for the meter/component preference.

Each parameter is `None` by default, meaning the corresponding formula
follows the global `prefer_meters_in_component_formulas` setting on
`ComponentGraphConfig`. Setting `True` forces the meter as primary
for that formula; `False` forces the device.
for that formula; `False` forces the component.
"""

def __init__(
Expand Down
2 changes: 1 addition & 1 deletion src/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ impl ComponentGraphConfig {
allow_unspecified_inverters = false,
disable_fallback_components = false,
include_phantom_loads_in_consumer_formula = false,
prefer_meters_in_component_formulas = true,
prefer_meters_in_component_formulas = false,
formula_overrides = None,
))]
fn new(
Expand Down
34 changes: 17 additions & 17 deletions tests/test_formula_preferences.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@

"""Behavioral tests for `ComponentGraphConfig` formula preferences.

These tests build a small, controllable graph (Grid -> Meter -> Device)
These tests build a small, controllable graph (Grid -> Meter -> Component)
for each per-category formula method and assert the actual formula
output for the four meter/device-preference combinations:
output for the four meter/component-preference combinations:

* default config -> meter primary
* global False -> device primary
* default config -> component primary
* global True -> meter primary
* per-formula override = True -> meter primary (override wins)
* per-formula override = False -> device primary (override wins)
* per-formula override = False -> component primary (override wins)

The drift test in `test_stub_drift.py` only checks signatures; this
file catches actual mis-wiring -- a swapped override, an inverted
Expand Down Expand Up @@ -46,9 +46,9 @@
_MGRID = MicrogridId(1)

# In every per-category topology built below, component #2 is the meter
# and #3 is the device that appears in the formula.
# and #3 is the component that appears in the formula.
_METER_PRIMARY = "COALESCE(#2, #3, 0.0)"
_DEVICE_PRIMARY = "COALESCE(#3, #2, 0.0)"
_COMPONENT_PRIMARY = "COALESCE(#3, #2, 0.0)"

# Each per-category graph builder takes an optional `config` and returns a
# graph rooted at the same Grid -> Meter pair, so the assertion strings
Expand Down Expand Up @@ -104,7 +104,7 @@ def _battery_graph(
config: ComponentGraphConfig | None = None,
) -> ComponentGraph[Any, Any, Any]:
# Grid -> Meter -> BatteryInverter -> Battery; the formula references
# the inverter (#3) as the device, the battery (#4) doesn't appear.
# the inverter (#3) as the component, the battery (#4) doesn't appear.
return ComponentGraph(
components={
_grid(),
Expand Down Expand Up @@ -190,26 +190,26 @@ def _steam_boiler_graph(


@pytest.mark.parametrize("build_graph,method,override_field", _CATEGORIES)
def test_default_config_prefers_meter(
def test_default_config_prefers_component(
build_graph: GraphBuilder,
method: str,
override_field: str, # pylint: disable=unused-argument
) -> None:
"""Default config selects the meter as the primary source."""
"""Default config selects the component as the primary source."""
formula = getattr(build_graph(None), method)(None)
assert formula == _METER_PRIMARY
assert formula == _COMPONENT_PRIMARY


@pytest.mark.parametrize("build_graph,method,override_field", _CATEGORIES)
def test_global_false_prefers_device(
def test_global_true_prefers_meter(
build_graph: GraphBuilder,
method: str,
override_field: str, # pylint: disable=unused-argument
) -> None:
"""Setting `prefer_meters_in_component_formulas=False` selects the device."""
config = ComponentGraphConfig(prefer_meters_in_component_formulas=False)
"""Setting `prefer_meters_in_component_formulas=True` selects the meter."""
config = ComponentGraphConfig(prefer_meters_in_component_formulas=True)
formula = getattr(build_graph(config), method)(None)
assert formula == _DEVICE_PRIMARY
assert formula == _METER_PRIMARY


@pytest.mark.parametrize("build_graph,method,override_field", _CATEGORIES)
Expand All @@ -229,13 +229,13 @@ def test_override_true_wins_over_global_false(
def test_override_false_wins_over_global_true(
build_graph: GraphBuilder, method: str, override_field: str
) -> None:
"""A `False` per-formula override flips to device despite a `True` global."""
"""A `False` per-formula override flips to component despite a `True` global."""
config = ComponentGraphConfig(
prefer_meters_in_component_formulas=True,
formula_overrides=FormulaOverrides(**{override_field: False}),
)
formula = getattr(build_graph(config), method)(None)
assert formula == _DEVICE_PRIMARY
assert formula == _COMPONENT_PRIMARY


def _empty_graph() -> ComponentGraph[Any, Any, Any]:
Expand Down
7 changes: 4 additions & 3 deletions tests/test_microgrid_component_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,11 @@ def test_wind_turbine_graph() -> None:
}

# 3. Test Formula Generation
# References the Meter (ID 2) measuring the Turbine (ID 3).
# Component-first by default: the turbine (ID 3) is the primary source,
# the meter (ID 2) the fallback.
assert (
graph.wind_turbine_formula(wind_turbine_ids={ComponentId(3)})
== "COALESCE(#2, #3, 0.0)"
== "COALESCE(#3, #2, 0.0)"
)

# 4. Test Topology (Successors/Predecessors)
Expand Down Expand Up @@ -179,7 +180,7 @@ def test_steam_boiler_graph() -> None:
}
assert (
graph.steam_boiler_formula(steam_boiler_ids={ComponentId(3)})
== "COALESCE(#2, #3, 0.0)"
== "COALESCE(#3, #2, 0.0)"
)


Expand Down
Loading