Skip to content

config

frequenz.lib.notebooks.config ¤

Configuration for microgrids.

Attributes¤

frequenz.lib.notebooks.config.ComponentCategory module-attribute ¤

ComponentCategory = Literal[
    "meter", "inverter", "component"
]

Valid component categories.

frequenz.lib.notebooks.config.ComponentType module-attribute ¤

ComponentType = Literal[
    "grid", "pv", "battery", "consumption", "chp"
]

Valid component types.

Classes¤

frequenz.lib.notebooks.config.AssetsConfig dataclass ¤

Configuration of the assets in a microgrid.

Source code in frequenz/lib/notebooks/config.py
@dataclass(frozen=True)
class AssetsConfig:
    """Configuration of the assets in a microgrid."""

    pv: dict[str, PVConfig] | None = None
    """Configuration of the PV system."""

    wind: dict[str, WindConfig] | None = None
    """Configuration of the wind turbines."""

    battery: dict[str, BatteryConfig] | None = None
    """Configuration of the batteries."""
Attributes¤
battery class-attribute instance-attribute ¤
battery: dict[str, BatteryConfig] | None = None

Configuration of the batteries.

pv class-attribute instance-attribute ¤
pv: dict[str, PVConfig] | None = None

Configuration of the PV system.

wind class-attribute instance-attribute ¤
wind: dict[str, WindConfig] | None = None

Configuration of the wind turbines.

frequenz.lib.notebooks.config.BatteryConfig dataclass ¤

Configuration of a battery in a microgrid.

Source code in frequenz/lib/notebooks/config.py
@dataclass(frozen=True)
class BatteryConfig:
    """Configuration of a battery in a microgrid."""

    capacity: float | None = None
    """Capacity of the battery in Wh."""
Attributes¤
capacity class-attribute instance-attribute ¤
capacity: float | None = None

Capacity of the battery in Wh.

frequenz.lib.notebooks.config.ComponentTypeConfig dataclass ¤

Configuration of a microgrid component type.

Source code in frequenz/lib/notebooks/config.py
@dataclass
class ComponentTypeConfig:
    """Configuration of a microgrid component type."""

    component_type: ComponentType
    """Type of the component."""

    meter: list[int] | None = None
    """List of meter IDs for this component."""

    inverter: list[int] | None = None
    """List of inverter IDs for this component."""

    component: list[int] | None = None
    """List of component IDs for this component."""

    formula: dict[str, str] | None = None
    """Formula to calculate the power of this component."""

    def __post_init__(self) -> None:
        """Set the default formula if none is provided."""
        self.formula = self.formula or {}
        if "AC_ACTIVE_POWER" not in self.formula:
            self.formula["AC_ACTIVE_POWER"] = "+".join(
                [f"#{cid}" for cid in self._default_cids()]
            )
        if self.component_type == "battery" and "BATTERY_SOC_PCT" not in self.formula:
            if self.component:
                cids = self.component
                form = "+".join([f"#{cid}" for cid in cids])
                form = f"({form})/({len(cids)})"
                self.formula["BATTERY_SOC_PCT"] = form

    def cids(self, metric: str = "") -> list[int]:
        """Get component IDs for this component.

        By default, the meter IDs are returned if available, otherwise the inverter IDs.
        For components without meters or inverters, the component IDs are returned.

        If a metric is provided, the component IDs are extracted from the formula.

        Args:
            metric: Metric name of the formula.

        Returns:
            List of component IDs for this component.

        Raises:
            ValueError: If the metric is not supported or improperly set.
        """
        if metric:
            if not isinstance(self.formula, dict):
                raise ValueError("Formula must be a dictionary.")
            formula = self.formula.get(metric)
            if not formula:
                raise ValueError(
                    f"{metric} does not have a formula for {self.component_type}"
                )
            # Extract component IDs from the formula which are given as e.g. #123
            pattern = r"#(\d+)"
            return [int(e) for e in re.findall(pattern, self.formula[metric])]

        return self._default_cids()

    def _default_cids(self) -> list[int]:
        """Get the default component IDs for this component.

        If available, the meter IDs are returned, otherwise the inverter IDs.
        For components without meters or inverters, the component IDs are returned.

        Returns:
            List of component IDs for this component.

        Raises:
            ValueError: If no IDs are available.
        """
        if self.meter:
            return self.meter
        if self.inverter:
            return self.inverter
        if self.component:
            return self.component

        raise ValueError(f"No IDs available for {self.component_type}")

    @classmethod
    def is_valid_type(cls, ctype: str) -> bool:
        """Check if `ctype` is a valid enum value."""
        return ctype in get_args(ComponentType)
Attributes¤
component class-attribute instance-attribute ¤
component: list[int] | None = None

List of component IDs for this component.

component_type instance-attribute ¤
component_type: ComponentType

Type of the component.

formula class-attribute instance-attribute ¤
formula: dict[str, str] | None = None

Formula to calculate the power of this component.

inverter class-attribute instance-attribute ¤
inverter: list[int] | None = None

List of inverter IDs for this component.

meter class-attribute instance-attribute ¤
meter: list[int] | None = None

List of meter IDs for this component.

Functions¤
__post_init__ ¤
__post_init__() -> None

Set the default formula if none is provided.

Source code in frequenz/lib/notebooks/config.py
def __post_init__(self) -> None:
    """Set the default formula if none is provided."""
    self.formula = self.formula or {}
    if "AC_ACTIVE_POWER" not in self.formula:
        self.formula["AC_ACTIVE_POWER"] = "+".join(
            [f"#{cid}" for cid in self._default_cids()]
        )
    if self.component_type == "battery" and "BATTERY_SOC_PCT" not in self.formula:
        if self.component:
            cids = self.component
            form = "+".join([f"#{cid}" for cid in cids])
            form = f"({form})/({len(cids)})"
            self.formula["BATTERY_SOC_PCT"] = form
_default_cids ¤
_default_cids() -> list[int]

Get the default component IDs for this component.

If available, the meter IDs are returned, otherwise the inverter IDs. For components without meters or inverters, the component IDs are returned.

RETURNS DESCRIPTION
list[int]

List of component IDs for this component.

RAISES DESCRIPTION
ValueError

If no IDs are available.

Source code in frequenz/lib/notebooks/config.py
def _default_cids(self) -> list[int]:
    """Get the default component IDs for this component.

    If available, the meter IDs are returned, otherwise the inverter IDs.
    For components without meters or inverters, the component IDs are returned.

    Returns:
        List of component IDs for this component.

    Raises:
        ValueError: If no IDs are available.
    """
    if self.meter:
        return self.meter
    if self.inverter:
        return self.inverter
    if self.component:
        return self.component

    raise ValueError(f"No IDs available for {self.component_type}")
cids ¤
cids(metric: str = '') -> list[int]

Get component IDs for this component.

By default, the meter IDs are returned if available, otherwise the inverter IDs. For components without meters or inverters, the component IDs are returned.

If a metric is provided, the component IDs are extracted from the formula.

PARAMETER DESCRIPTION
metric

Metric name of the formula.

TYPE: str DEFAULT: ''

RETURNS DESCRIPTION
list[int]

List of component IDs for this component.

RAISES DESCRIPTION
ValueError

If the metric is not supported or improperly set.

Source code in frequenz/lib/notebooks/config.py
def cids(self, metric: str = "") -> list[int]:
    """Get component IDs for this component.

    By default, the meter IDs are returned if available, otherwise the inverter IDs.
    For components without meters or inverters, the component IDs are returned.

    If a metric is provided, the component IDs are extracted from the formula.

    Args:
        metric: Metric name of the formula.

    Returns:
        List of component IDs for this component.

    Raises:
        ValueError: If the metric is not supported or improperly set.
    """
    if metric:
        if not isinstance(self.formula, dict):
            raise ValueError("Formula must be a dictionary.")
        formula = self.formula.get(metric)
        if not formula:
            raise ValueError(
                f"{metric} does not have a formula for {self.component_type}"
            )
        # Extract component IDs from the formula which are given as e.g. #123
        pattern = r"#(\d+)"
        return [int(e) for e in re.findall(pattern, self.formula[metric])]

    return self._default_cids()
is_valid_type classmethod ¤
is_valid_type(ctype: str) -> bool

Check if ctype is a valid enum value.

Source code in frequenz/lib/notebooks/config.py
@classmethod
def is_valid_type(cls, ctype: str) -> bool:
    """Check if `ctype` is a valid enum value."""
    return ctype in get_args(ComponentType)

frequenz.lib.notebooks.config.Metadata dataclass ¤

Metadata for a microgrid.

Source code in frequenz/lib/notebooks/config.py
@dataclass(frozen=True)
class Metadata:
    """Metadata for a microgrid."""

    name: str | None = None
    """Name of the microgrid."""

    gid: int | None = None
    """Gridpool ID of the microgrid."""

    delivery_area: str | None = None
    """Delivery area of the microgrid."""

    latitude: float | None = None
    """Geographic latitude of the microgrid."""

    longitude: float | None = None
    """Geographic longitude of the microgrid."""

    altitude: float | None = None
    """Geographic altitude of the microgrid."""
Attributes¤
altitude class-attribute instance-attribute ¤
altitude: float | None = None

Geographic altitude of the microgrid.

delivery_area class-attribute instance-attribute ¤
delivery_area: str | None = None

Delivery area of the microgrid.

gid class-attribute instance-attribute ¤
gid: int | None = None

Gridpool ID of the microgrid.

latitude class-attribute instance-attribute ¤
latitude: float | None = None

Geographic latitude of the microgrid.

longitude class-attribute instance-attribute ¤
longitude: float | None = None

Geographic longitude of the microgrid.

name class-attribute instance-attribute ¤
name: str | None = None

Name of the microgrid.

frequenz.lib.notebooks.config.MicrogridConfig dataclass ¤

Configuration of a microgrid.

Source code in frequenz/lib/notebooks/config.py
@dataclass
class MicrogridConfig:
    """Configuration of a microgrid."""

    _metadata: Metadata
    """Metadata of the microgrid."""

    _assets_cfg: AssetsConfig
    """Configuration of the assets in the microgrid."""

    _component_types_cfg: dict[str, ComponentTypeConfig]
    """Mapping of component category types to ac power component config."""

    def __init__(self, config_dict: dict[str, Any]) -> None:
        """Initialize the microgrid configuration.

        Args:
            config_dict: Dictionary with component type as key and config as value.
        """
        self._metadata = Metadata(**(config_dict.get("meta") or {}))

        self._assets_cfg = AssetsConfig(
            pv=config_dict.get("pv") or {},
            wind=config_dict.get("wind") or {},
            battery=config_dict.get("battery") or {},
        )

        self._component_types_cfg = {
            ctype: ComponentTypeConfig(component_type=cast(ComponentType, ctype), **cfg)
            for ctype, cfg in config_dict.get("ctype", {}).items()
            if ComponentTypeConfig.is_valid_type(ctype)
        }

    @property
    def meta(self) -> Metadata:
        """Return the metadata of the microgrid."""
        return self._metadata

    @property
    def assets(self) -> AssetsConfig:
        """Return the assets configuration of the microgrid."""
        return self._assets_cfg

    def component_types(self) -> list[str]:
        """Get a list of all component types in the configuration."""
        return list(self._component_types_cfg.keys())

    def component_type_ids(
        self,
        component_type: str,
        component_category: str | None = None,
        metric: str = "",
    ) -> list[int]:
        """Get a list of all component IDs for a component type.

        Args:
            component_type: Component type to be aggregated.
            component_category: Specific category of component IDs to retrieve
                (e.g., "meter", "inverter", or "component"). If not provided,
                the default logic is used.
            metric: Metric name of the formula if CIDs should be extracted from the formula.

        Returns:
            List of component IDs for this component type.

        Raises:
            ValueError: If the component type is unknown.
            KeyError: If `component_category` is invalid.
        """
        cfg = self._component_types_cfg.get(component_type)
        if not cfg:
            raise ValueError(f"{component_type} not found in config.")

        if component_category:
            valid_categories = get_args(ComponentCategory)
            if component_category not in valid_categories:
                raise KeyError(
                    f"Invalid component category: {component_category}. "
                    f"Valid categories are {valid_categories}"
                )
            category_ids = cast(list[int], getattr(cfg, component_category, []))
            return category_ids

        return cfg.cids(metric)

    def formula(self, component_type: str, metric: str) -> str:
        """Get the formula for a component type.

        Args:
            component_type: Component type to be aggregated.
            metric: Metric to be aggregated.

        Returns:
            Formula to be used for this aggregated component as string.

        Raises:
            ValueError: If the component type is unknown or formula is missing.
        """
        cfg = self._component_types_cfg.get(component_type)
        if not cfg:
            raise ValueError(f"{component_type} not found in config.")
        if cfg.formula is None:
            raise ValueError(f"No formula set for {component_type}")
        formula = cfg.formula.get(metric)
        if not formula:
            raise ValueError(f"{component_type} is missing formula for {metric}")

        return formula

    @staticmethod
    def load_configs(*paths: str) -> dict[str, "MicrogridConfig"]:
        """Load multiple microgrid configurations from a file.

        Configs for a single microgrid are expected to be in a single file.
        Later files with the same microgrid ID will overwrite the previous configs.

        Args:
            *paths: Path(es) to the config file(s).

        Returns:
            Dictionary of single microgrid formula configs with microgrid IDs as keys.
        """
        microgrid_configs = {}
        for config_path in paths:
            with open(config_path, "rb") as f:
                cfg_dict = tomllib.load(f)
                for microgrid_id, mcfg in cfg_dict.items():
                    microgrid_configs[microgrid_id] = MicrogridConfig(mcfg)
        return microgrid_configs
Attributes¤
_assets_cfg instance-attribute ¤
_assets_cfg: AssetsConfig = AssetsConfig(
    pv=get("pv") or {},
    wind=get("wind") or {},
    battery=get("battery") or {},
)

Configuration of the assets in the microgrid.

_component_types_cfg instance-attribute ¤
_component_types_cfg: dict[str, ComponentTypeConfig] = {
    ctype: ComponentTypeConfig(
        component_type=cast(ComponentType, ctype), **cfg
    )
    for (ctype, cfg) in items()
    if is_valid_type(ctype)
}

Mapping of component category types to ac power component config.

_metadata instance-attribute ¤
_metadata: Metadata = Metadata(**get('meta') or {})

Metadata of the microgrid.

assets property ¤
assets: AssetsConfig

Return the assets configuration of the microgrid.

meta property ¤
meta: Metadata

Return the metadata of the microgrid.

Functions¤
__init__ ¤
__init__(config_dict: dict[str, Any]) -> None

Initialize the microgrid configuration.

PARAMETER DESCRIPTION
config_dict

Dictionary with component type as key and config as value.

TYPE: dict[str, Any]

Source code in frequenz/lib/notebooks/config.py
def __init__(self, config_dict: dict[str, Any]) -> None:
    """Initialize the microgrid configuration.

    Args:
        config_dict: Dictionary with component type as key and config as value.
    """
    self._metadata = Metadata(**(config_dict.get("meta") or {}))

    self._assets_cfg = AssetsConfig(
        pv=config_dict.get("pv") or {},
        wind=config_dict.get("wind") or {},
        battery=config_dict.get("battery") or {},
    )

    self._component_types_cfg = {
        ctype: ComponentTypeConfig(component_type=cast(ComponentType, ctype), **cfg)
        for ctype, cfg in config_dict.get("ctype", {}).items()
        if ComponentTypeConfig.is_valid_type(ctype)
    }
component_type_ids ¤
component_type_ids(
    component_type: str,
    component_category: str | None = None,
    metric: str = "",
) -> list[int]

Get a list of all component IDs for a component type.

PARAMETER DESCRIPTION
component_type

Component type to be aggregated.

TYPE: str

component_category

Specific category of component IDs to retrieve (e.g., "meter", "inverter", or "component"). If not provided, the default logic is used.

TYPE: str | None DEFAULT: None

metric

Metric name of the formula if CIDs should be extracted from the formula.

TYPE: str DEFAULT: ''

RETURNS DESCRIPTION
list[int]

List of component IDs for this component type.

RAISES DESCRIPTION
ValueError

If the component type is unknown.

KeyError

If component_category is invalid.

Source code in frequenz/lib/notebooks/config.py
def component_type_ids(
    self,
    component_type: str,
    component_category: str | None = None,
    metric: str = "",
) -> list[int]:
    """Get a list of all component IDs for a component type.

    Args:
        component_type: Component type to be aggregated.
        component_category: Specific category of component IDs to retrieve
            (e.g., "meter", "inverter", or "component"). If not provided,
            the default logic is used.
        metric: Metric name of the formula if CIDs should be extracted from the formula.

    Returns:
        List of component IDs for this component type.

    Raises:
        ValueError: If the component type is unknown.
        KeyError: If `component_category` is invalid.
    """
    cfg = self._component_types_cfg.get(component_type)
    if not cfg:
        raise ValueError(f"{component_type} not found in config.")

    if component_category:
        valid_categories = get_args(ComponentCategory)
        if component_category not in valid_categories:
            raise KeyError(
                f"Invalid component category: {component_category}. "
                f"Valid categories are {valid_categories}"
            )
        category_ids = cast(list[int], getattr(cfg, component_category, []))
        return category_ids

    return cfg.cids(metric)
component_types ¤
component_types() -> list[str]

Get a list of all component types in the configuration.

Source code in frequenz/lib/notebooks/config.py
def component_types(self) -> list[str]:
    """Get a list of all component types in the configuration."""
    return list(self._component_types_cfg.keys())
formula ¤
formula(component_type: str, metric: str) -> str

Get the formula for a component type.

PARAMETER DESCRIPTION
component_type

Component type to be aggregated.

TYPE: str

metric

Metric to be aggregated.

TYPE: str

RETURNS DESCRIPTION
str

Formula to be used for this aggregated component as string.

RAISES DESCRIPTION
ValueError

If the component type is unknown or formula is missing.

Source code in frequenz/lib/notebooks/config.py
def formula(self, component_type: str, metric: str) -> str:
    """Get the formula for a component type.

    Args:
        component_type: Component type to be aggregated.
        metric: Metric to be aggregated.

    Returns:
        Formula to be used for this aggregated component as string.

    Raises:
        ValueError: If the component type is unknown or formula is missing.
    """
    cfg = self._component_types_cfg.get(component_type)
    if not cfg:
        raise ValueError(f"{component_type} not found in config.")
    if cfg.formula is None:
        raise ValueError(f"No formula set for {component_type}")
    formula = cfg.formula.get(metric)
    if not formula:
        raise ValueError(f"{component_type} is missing formula for {metric}")

    return formula
load_configs staticmethod ¤
load_configs(*paths: str) -> dict[str, MicrogridConfig]

Load multiple microgrid configurations from a file.

Configs for a single microgrid are expected to be in a single file. Later files with the same microgrid ID will overwrite the previous configs.

PARAMETER DESCRIPTION
*paths

Path(es) to the config file(s).

TYPE: str DEFAULT: ()

RETURNS DESCRIPTION
dict[str, MicrogridConfig]

Dictionary of single microgrid formula configs with microgrid IDs as keys.

Source code in frequenz/lib/notebooks/config.py
@staticmethod
def load_configs(*paths: str) -> dict[str, "MicrogridConfig"]:
    """Load multiple microgrid configurations from a file.

    Configs for a single microgrid are expected to be in a single file.
    Later files with the same microgrid ID will overwrite the previous configs.

    Args:
        *paths: Path(es) to the config file(s).

    Returns:
        Dictionary of single microgrid formula configs with microgrid IDs as keys.
    """
    microgrid_configs = {}
    for config_path in paths:
        with open(config_path, "rb") as f:
            cfg_dict = tomllib.load(f)
            for microgrid_id, mcfg in cfg_dict.items():
                microgrid_configs[microgrid_id] = MicrogridConfig(mcfg)
    return microgrid_configs

frequenz.lib.notebooks.config.PVConfig dataclass ¤

Configuration of a PV system in a microgrid.

Source code in frequenz/lib/notebooks/config.py
@dataclass(frozen=True)
class PVConfig:
    """Configuration of a PV system in a microgrid."""

    peak_power: float | None = None
    """Peak power of the PV system in Watt."""

    rated_power: float | None = None
    """Rated power of the inverters in Watt."""
Attributes¤
peak_power class-attribute instance-attribute ¤
peak_power: float | None = None

Peak power of the PV system in Watt.

rated_power class-attribute instance-attribute ¤
rated_power: float | None = None

Rated power of the inverters in Watt.

frequenz.lib.notebooks.config.WindConfig dataclass ¤

Configuration of a wind turbine in a microgrid.

Source code in frequenz/lib/notebooks/config.py
@dataclass(frozen=True)
class WindConfig:
    """Configuration of a wind turbine in a microgrid."""

    turbine_model: str | None = None
    """Model name of the wind turbine."""

    rated_power: float | None = None
    """Rated power of the wind turbine in Watt."""

    turbine_height: float | None = None
    """Height of the wind turbine in meters."""
Attributes¤
rated_power class-attribute instance-attribute ¤
rated_power: float | None = None

Rated power of the wind turbine in Watt.

turbine_height class-attribute instance-attribute ¤
turbine_height: float | None = None

Height of the wind turbine in meters.

turbine_model class-attribute instance-attribute ¤
turbine_model: str | None = None

Model name of the wind turbine.