Skip to content
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
4 changes: 2 additions & 2 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.27.1"
frequenz-microgrid-component-graph = "0.4"
frequenz-microgrid-component-graph = "0.5"
6 changes: 6 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,9 @@
## Upgrading

- This release updates the `frequenz-microgrid-component-graph` rust crate version to 0.4.

- This release updates the `frequenz-microgrid-component-graph` rust crate version to 0.5. `ComponentGraphConfig` is restructured to match: the six per-category `prefer_X_in_Y_formula` flags are replaced by a global `prefer_meters_in_component_formulas` plus per-formula overrides via the new `FormulaOverrides` class. Also exposes the new `steam_boiler_formula` method, and renames `battery_coalesce_formula` / `pv_coalesce_formula` to `battery_ac_coalesce_formula` / `pv_ac_coalesce_formula`.

- `ComponentGraphConfig.__init__` is now declared as keyword-only in the type stubs (the runtime was already keyword-only, so positional calls were already failing at runtime).

- The per-category preference flags inverted polarity: where the old flags selected the *device*, the new override entries select the *meter*. E.g. `ComponentGraphConfig(prefer_inverters_in_pv_formula=True)` becomes `ComponentGraphConfig(formula_overrides=FormulaOverrides(prefer_meters_in_pv_formula=False))`.
2 changes: 2 additions & 0 deletions python/frequenz/microgrid_component_graph/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
ComponentGraph,
ComponentGraphConfig,
FormulaGenerationError,
FormulaOverrides,
InvalidGraphError,
)

__all__ = [
"ComponentGraph",
"ComponentGraphConfig",
"FormulaGenerationError",
"FormulaOverrides",
"InvalidGraphError",
]
89 changes: 62 additions & 27 deletions python/frequenz/microgrid_component_graph/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,14 @@ class ComponentGraphConfig:

def __init__(
self,
*,
allow_component_validation_failures: bool = False,
allow_unconnected_components: bool = False,
allow_unspecified_inverters: bool = False,
disable_fallback_components: bool = False,
include_phantom_loads_in_consumer_formula: bool = False,
prefer_inverters_in_battery_formula: bool = False,
prefer_inverters_in_pv_formula: bool = False,
prefer_chp_in_chp_formula: bool = False,
prefer_ev_chargers_in_ev_formula: bool = False,
prefer_wind_turbines_in_wind_formula: bool = False,
prefer_meters_in_component_formulas: bool = True,
formula_overrides: FormulaOverrides | None = None,
) -> None:
"""Initialize this instance.

Expand All @@ -51,26 +49,47 @@ class ComponentGraphConfig:
the measurements of successor meters from the measurements of their
predecessor meters. When `false`, consumer formula is generated by
excluding production and battery components from the grid measurements.
prefer_inverters_in_battery_formula: Whether to prefer battery inverters
when generating Battery formulas. When this is `true`, battery inverters
will be the primary source and battery meters will be secondary. When
`false`, battery meters will be the primary source.
prefer_inverters_in_pv_formula: Whether to prefer PV inverters when
generating PV formulas. When this is `true`, PV inverters will be the
primary source and PV meters will be secondary. When `false`, PV meters
will be the primary source.
prefer_chp_in_chp_formula: Whether to prefer CHP when generating CHP
formulas. When this is `true`, CHPs will be the primary source and CHP
meters will be secondary. When `false`, CHP meters will be the primary
source.
prefer_ev_chargers_in_ev_formula: Whether to prefer EV chargers when
generating EV charger formulas. When this is `true`, EV chargers will be
the primary source and EV meters will be secondary. When `false`, EV
meters will be the primary source.
prefer_wind_turbines_in_wind_formula: Whether to prefer wind turbines when
generating wind turbine formulas. When this is `true`, wind turbines
will be the primary source and wind meters will be secondary. When
`false`, wind meters will be the primary source.
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
`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
`grid_formula`, `consumer_formula`, `producer_formula`, or any of the
coalesce formulas.
formula_overrides: Per-formula overrides for the meter/device 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.

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.
"""

def __init__(
self,
*,
prefer_meters_in_pv_formula: bool | None = None,
prefer_meters_in_battery_formula: bool | None = None,
prefer_meters_in_chp_formula: bool | None = None,
prefer_meters_in_ev_charger_formula: bool | None = None,
prefer_meters_in_wind_turbine_formula: bool | None = None,
prefer_meters_in_steam_boiler_formula: bool | None = None,
) -> None:
"""Initialize this instance.

Args:
prefer_meters_in_pv_formula: Override for `pv_formula`.
prefer_meters_in_battery_formula: Override for `battery_formula`.
prefer_meters_in_chp_formula: Override for `chp_formula`.
prefer_meters_in_ev_charger_formula: Override for `ev_charger_formula`.
prefer_meters_in_wind_turbine_formula: Override for `wind_turbine_formula`.
prefer_meters_in_steam_boiler_formula: Override for `steam_boiler_formula`.
"""

class ComponentIdProtocol(Protocol):
Expand Down Expand Up @@ -341,14 +360,30 @@ class ComponentGraph(Generic[ComponentT, ConnectionT, ComponentIdT]):
are not wind turbines.
"""

def steam_boiler_formula(self, steam_boiler_ids: Set[ComponentIdT] | None) -> str:
"""Generate the steam boiler formula for this component graph.

Args:
steam_boiler_ids: The set of steam boiler component IDs to include in
the formula. If `None`, all steam boilers in the graph will be
included.

Returns:
The steam boiler formula as a string.

Raises:
FormulaGenerationError: if the given component IDs don't exist or
are not steam boilers.
"""

def grid_coalesce_formula(self) -> str:
"""Generate the grid coalesce formula for this component graph.

Returns:
The grid coalesced formula as a string.
"""

def battery_coalesce_formula(self, battery_ids: Set[ComponentIdT] | None) -> str:
def battery_ac_coalesce_formula(self, battery_ids: Set[ComponentIdT] | None) -> str:
"""Generate the battery coalesce formula for this component graph.

Args:
Expand All @@ -364,7 +399,7 @@ class ComponentGraph(Generic[ComponentT, ConnectionT, ComponentIdT]):
are not batteries.
"""

def pv_coalesce_formula(self, pv_inverter_ids: Set[ComponentIdT] | None) -> str:
def pv_ac_coalesce_formula(self, pv_inverter_ids: Set[ComponentIdT] | None) -> str:
"""Generate the PV coalesce formula for this component graph.

Args:
Expand Down
100 changes: 78 additions & 22 deletions src/graph.rs
Comment thread
llucax marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -32,37 +32,81 @@ impl ComponentGraphConfig {
allow_unspecified_inverters = false,
disable_fallback_components = false,
include_phantom_loads_in_consumer_formula = false,
prefer_inverters_in_battery_formula = false,
prefer_inverters_in_pv_formula = false,
prefer_chp_in_chp_formula = false,
prefer_ev_chargers_in_ev_formula = false,
prefer_wind_turbines_in_wind_formula = false,
prefer_meters_in_component_formulas = true,
formula_overrides = None,
))]
fn new(
allow_component_validation_failures: bool,
allow_unconnected_components: bool,
allow_unspecified_inverters: bool,
disable_fallback_components: bool,
include_phantom_loads_in_consumer_formula: bool,
prefer_inverters_in_battery_formula: bool,
prefer_inverters_in_pv_formula: bool,
prefer_chp_in_chp_formula: bool,
prefer_ev_chargers_in_ev_formula: bool,
prefer_wind_turbines_in_wind_formula: bool,
prefer_meters_in_component_formulas: bool,
formula_overrides: Option<FormulaOverrides>,
) -> Self {
let mut builder = cg::ComponentGraphConfig::builder()
.allow_component_validation_failures(allow_component_validation_failures)
.allow_unconnected_components(allow_unconnected_components)
.allow_unspecified_inverters(allow_unspecified_inverters)
.disable_fallback_components(disable_fallback_components)
.include_phantom_loads_in_consumer_formula(include_phantom_loads_in_consumer_formula)
.prefer_meters_in_component_formulas(prefer_meters_in_component_formulas);
if let Some(overrides) = formula_overrides {
builder = builder.formula_overrides(overrides.overrides);
}
ComponentGraphConfig {
config: cg::ComponentGraphConfig {
allow_component_validation_failures,
allow_unconnected_components,
allow_unspecified_inverters,
disable_fallback_components,
include_phantom_loads_in_consumer_formula,
prefer_inverters_in_battery_formula,
prefer_inverters_in_pv_formula,
prefer_chp_in_chp_formula,
prefer_ev_chargers_in_ev_formula,
prefer_wind_turbines_in_wind_formula,
},
config: builder.build(),
}
}
}

#[pyclass(subclass)]
#[derive(Clone, Default, Debug)]
pub struct FormulaOverrides {
overrides: cg::FormulaOverrides,
}

#[pymethods]
impl FormulaOverrides {
#[new]
#[pyo3(signature = (
*,
prefer_meters_in_pv_formula = None,
prefer_meters_in_battery_formula = None,
prefer_meters_in_chp_formula = None,
prefer_meters_in_ev_charger_formula = None,
prefer_meters_in_wind_turbine_formula = None,
prefer_meters_in_steam_boiler_formula = None,
))]
fn new(
prefer_meters_in_pv_formula: Option<bool>,
prefer_meters_in_battery_formula: Option<bool>,
prefer_meters_in_chp_formula: Option<bool>,
prefer_meters_in_ev_charger_formula: Option<bool>,
prefer_meters_in_wind_turbine_formula: Option<bool>,
prefer_meters_in_steam_boiler_formula: Option<bool>,
) -> Self {
let mut builder = cg::FormulaOverrides::builder();
if let Some(v) = prefer_meters_in_pv_formula {
builder = builder.prefer_meters_in_pv_formula(v);
}
if let Some(v) = prefer_meters_in_battery_formula {
builder = builder.prefer_meters_in_battery_formula(v);
}
if let Some(v) = prefer_meters_in_chp_formula {
builder = builder.prefer_meters_in_chp_formula(v);
}
if let Some(v) = prefer_meters_in_ev_charger_formula {
builder = builder.prefer_meters_in_ev_charger_formula(v);
}
if let Some(v) = prefer_meters_in_wind_turbine_formula {
builder = builder.prefer_meters_in_wind_turbine_formula(v);
}
if let Some(v) = prefer_meters_in_steam_boiler_formula {
builder = builder.prefer_meters_in_steam_boiler_formula(v);
}
FormulaOverrides {
overrides: builder.build(),
}
}
}
Expand Down Expand Up @@ -308,6 +352,18 @@ impl ComponentGraph {
.map_err(|e| PyErr::new::<FormulaGenerationError, _>(e.to_string()))
}

#[pyo3(signature = (steam_boiler_ids=None))]
fn steam_boiler_formula(
&self,
py: Python<'_>,
steam_boiler_ids: Option<Bound<'_, PyAny>>,
) -> PyResult<String> {
self.graph
.steam_boiler_formula(extract_ids(py, steam_boiler_ids)?)
.map(|f| f.to_string())
.map_err(|e| PyErr::new::<FormulaGenerationError, _>(e.to_string()))
}

fn grid_coalesce_formula(&self) -> PyResult<String> {
self.graph
.grid_coalesce_formula()
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ mod _component_graph {
use crate::graph::ComponentGraph;
#[pymodule_export]
use crate::graph::ComponentGraphConfig;
#[pymodule_export]
use crate::graph::FormulaOverrides;
}
Loading
Loading