Skip to content
Open
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
2 changes: 1 addition & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

## New Features

<!-- Here goes the main new features and examples or instructions on how to use them -->
- Automatically generate component formulas for the `AC_ENERGY_ACTIVE`, `AC_ENERGY_ACTIVE_CONSUMED`, and `AC_ENERGY_ACTIVE_DELIVERED` metrics when deriving microgrid configuration from the Assets API, in addition to the existing `AC_POWER_ACTIVE` formulas.

## Bug Fixes

Expand Down
11 changes: 9 additions & 2 deletions src/frequenz/gridpool/config/_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@

_logger = logging.getLogger(__name__)

_AUTO_FORMULA_METRICS = (
"AC_POWER_ACTIVE",
"AC_ENERGY_ACTIVE",
"AC_ENERGY_ACTIVE_CONSUMED",
"AC_ENERGY_ACTIVE_DELIVERED",
)


async def load_configs(
default_files: str | Path | list[str | Path] | None = None,
Expand Down Expand Up @@ -232,7 +239,7 @@ def _derive_component_configs(
) -> dict[str, ComponentTypeConfig]:
"""Build the component-type configs for a microgrid from its graph.

Derives, per component type, the `AC_POWER_ACTIVE` formula and the
Derives, per component type, formulas for active power and energy, and the
meter/inverter/component ID lists straight from the component graph. A
component type only appears in the result if the graph yields a non-zero
formula or at least one ID for it.
Expand All @@ -257,7 +264,7 @@ def as_formula(derived: str) -> dict[str, str] | None:
return None
except ValueError:
pass
return {"AC_POWER_ACTIVE": derived}
return {metric: derived for metric in _AUTO_FORMULA_METRICS}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Can't we resolve energies to power here instead? Otherwise we will end up duplicating the same summation formula for every metric.


configs: dict[str, ComponentTypeConfig] = {
"consumption": ComponentTypeConfig(
Expand Down
22 changes: 16 additions & 6 deletions tests/test_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@
_load_microgrids_from_api,
)

_FORMULA_METRICS = (
"AC_POWER_ACTIVE",
"AC_ENERGY_ACTIVE",
"AC_ENERGY_ACTIVE_CONSUMED",
"AC_ENERGY_ACTIVE_DELIVERED",
)


def _formulas(expression: str) -> dict[str, str]:
"""Build the expected auto-generated formulas for an expression."""
return {metric: expression for metric in _FORMULA_METRICS}


def _mock_client() -> MagicMock:
"""Mock an Assets API client for one microgrid: grid -> meter -> PV, + a meter."""
Expand Down Expand Up @@ -73,7 +85,7 @@ async def test_load_microgrids_from_api_derives_formulas_and_ids() -> None:

cfg = configs[10]
assert cfg.enterprise_id == 7
assert cfg.ctype["pv"].formula == {"AC_POWER_ACTIVE": "COALESCE(#4, #2, 0.0)"}
assert cfg.ctype["pv"].formula == _formulas("COALESCE(#4, #2, 0.0)")
assert cfg.ctype["pv"].inverter == [4]
assert cfg.ctype["pv"].meter == [2]
assert cfg.ctype["grid"].meter == [2, 3]
Expand Down Expand Up @@ -105,7 +117,7 @@ async def test_load_microgrids_from_api_honours_the_component_graph_config() ->

# Meter first, the opposite of the default order asserted above.
ctype = configs[10].ctype
assert ctype["pv"].formula == {"AC_POWER_ACTIVE": "COALESCE(#2, #4, 0.0)"}
assert ctype["pv"].formula == _formulas("COALESCE(#2, #4, 0.0)")


async def test_load_configs_forwards_the_component_graph_config() -> None:
Expand All @@ -120,9 +132,7 @@ async def test_load_configs_forwards_the_component_graph_config() -> None:
)
).microgrids

assert configs[10].ctype["pv"].formula == {
"AC_POWER_ACTIVE": "COALESCE(#2, #4, 0.0)"
}
assert configs[10].ctype["pv"].formula == _formulas("COALESCE(#2, #4, 0.0)")


async def test_load_configs_rejects_a_component_graph_config_without_a_client() -> None:
Expand Down Expand Up @@ -197,7 +207,7 @@ async def test_derive_component_configs_builds_formulas_and_ids() -> None:

configs = _derive_component_configs(graph)

assert configs["pv"].formula == {"AC_POWER_ACTIVE": "COALESCE(#4, #2, 0.0)"}
assert configs["pv"].formula == _formulas("COALESCE(#4, #2, 0.0)")
assert configs["pv"].inverter == [4]
assert configs["pv"].meter == [2]
assert configs["grid"].meter == [2, 3]
Expand Down