Skip to content

Required Alarms

Overview

The RequiredAlarms class is a subclass of CalculationRequirement that is used to check if a list of alarms are present for an object. This is useful for calculations based on alarms.

Usage

This requirement can be instantiated with a list of alarms that need to be present for each object. Below there is an example of how to use this requirement:

requirement = RequiredAlarms(alarms={"SDM1-VRN1-01": [1, 2]})

After calling check and get_data methods, the data attribute of the requirement will be a DataFrame with the required alarms for the desired period.

Database Requirements

This requirement expects that the alarms definition table is set with the necessary alarms for the model of the wanted object.

To check if the alarms are set correctly, go to the v_alarms_def view in the database.

Class Definition

RequiredAlarms(alarms, match_id_on='manufacturer_id', optional=False)

Subclass of CalculationRequirement that defines the alarms that are required for the calculation.

This will check the performance database for the existence of the required alarms for the wanted objects.

Parameters:

  • alarms

    (dict[str, list[int]]) –

    Dictionary with the alarms that are required for the calculation.

    This must be in the following format: {object_name: [alarm_id_1, alarm_id_2, ...], ...}

  • match_id_on

    (Literal['manufacturer_id', 'id'], default: 'manufacturer_id' ) –

    In which column should we match the desired ids. Can be one of ["manufacturer_id", "id"]. See view v_alarms_def in performance_db for more information.

    By default "manufacturer_id"

  • optional

    (bool, default: False ) –

    Set to True if this is an optional requirement. by default False

Source code in echo_energycalc/calculation_requirement_alarms.py
def __init__(
    self,
    alarms: dict[str, list[int]],
    match_id_on: Literal["manufacturer_id", "id"] = "manufacturer_id",
    optional: bool = False,
) -> None:
    """
    Constructor of the RequiredAlarms class.

    This will check the performance database for the existence of the required alarms for the wanted objects.

    Parameters
    ----------
    alarms : dict[str, list[int]]
        Dictionary with the alarms that are required for the calculation.

        This must be in the following format: {object_name: [alarm_id_1, alarm_id_2, ...], ...}
    match_id_on : Literal["manufacturer_id", "id"], optional
        In which column should we match the desired ids. Can be one of ["manufacturer_id", "id"]. See view `v_alarms_def` in performance_db for more information.

        By default "manufacturer_id"
    optional : bool, optional
        Set to True if this is an optional requirement. by default False
    """
    super().__init__(optional)

    # checking if "match_id_on" is a valid value
    if match_id_on not in ["manufacturer_id", "id"]:
        raise ValueError(f"match_id_on must be 'manufacturer_id' or 'id', not {match_id_on}")
    self._match_id_on = match_id_on

    # check if alarms is a dict with str keys and list of int values
    if not isinstance(alarms, dict):
        raise TypeError(f"alarms must be a dict, not {type(alarms)}")
    if not all(isinstance(key, str) for key in alarms):
        raise TypeError(f"all alarms keys must be str, not {[type(key) for key in alarms]}")
    if not all(isinstance(value, list) for value in alarms.values()):
        raise TypeError(f"all alarms values must be list, not {[type(value) for value in alarms.values()]}")
    if not all(all(isinstance(item, int) for item in value) for value in alarms.values()):
        raise TypeError(f"all alarms values must be list of int, not {[type(item) for value in alarms.values() for item in value]}")

    self._alarms = alarms

alarms property

Alarms required.

Returns:

  • dict[str, list[int]]

    Alarms required.

checked property

Attribute that defines if the requirement has been checked. It's value will start as False and will be set to True after the check method is called.

Returns:

  • bool

    True if the requirement has been checked.

data property

Data required for the calculation.

Returns:

  • DataFrame

    DataFrame with the alarms history for the required alarms and period.

match_id_on property

Type of id used to match the alarms.

Returns:

  • str

    Type of id used to match the alarms.

optional property

Attribute that defines if the requirement is optional.

If optional is True, the requirement is only validated to check if it could exist, not if it is actually present. This is useful for requirements that are not necessary for all calculations, but are useful for some of them.

Returns:

  • bool

    True if the requirement is optional.

check()

Method used to check if all required alarms are present in the database for each object.

This will raise an error if any of the required alarms are missing.

Returns:

  • bool

    Returns True if all required alarms are present in the database for each object.

Source code in echo_energycalc/calculation_requirement_alarms.py
def check(self) -> bool:
    """
    Method used to check if all required alarms are present in the database for each object.

    This will raise an error if any of the required alarms are missing.

    Returns
    -------
    bool
        Returns True if all required alarms are present in the database for each object.
    """
    if self.optional:
        return True

    # iterating each object and checking if all alarms are present
    for object_name, alarms in self.alarms.items():
        # getting object model
        object_model = self._perfdb.objects.instances.get(object_names=[object_name], output_type="dict")[object_name][
            "object_model_name"
        ]

        # getting existing alarms
        existing_alarms = self._perfdb.alarms.definitions.get_ids(
            object_models=[object_model],
            alarm_ids=alarms,
            match_alarm_id_on=self.match_id_on,
        )[object_model]

        # checking if all required alarms are present
        if self.match_id_on == "manufacturer_id":
            missing_alarms = set(alarms) - set(existing_alarms.keys())
        else:
            missing_alarms = set(alarms) - {
                alarm_id for alarm_type_def in existing_alarms.values() for alarm_id in alarm_type_def.values()
            }
        if len(missing_alarms) > 0:
            raise ValueError(f"Alarms ({self.match_id_on}) {missing_alarms} do not exist for object {object_name}")

    self._checked = True

    return True

get_data(period, **kwargs)

Method used to get the alarms history for the required alarms and period.

Parameters:

  • period

    (DateTimeRange) –

    Desired period for the features.

Returns:

  • DataFrame

    DataFrame with the alarms history for the required alarms and period.

Source code in echo_energycalc/calculation_requirement_alarms.py
def get_data(self, period: DateTimeRange, **kwargs) -> DataFrame:  # noqa: ARG002
    """
    Method used to get the alarms history for the required alarms and period.

    Parameters
    ----------
    period : DateTimeRange
        Desired period for the features.

    Returns
    -------
    DataFrame
        DataFrame with the alarms history for the required alarms and period.
    """
    # checking if necessary keyword arguments are present
    if not isinstance(period, DateTimeRange):
        raise TypeError(f"period must be a DateTimeRange object, not {type(period)}")

    # check if requirement has been checked
    if not self._checked:
        self.check()

    try:
        # creating empty DataFrame
        alarms_df = DataFrame()

        # iterating each object and getting alarms history
        for object_name, alarms in self.alarms.items():
            # getting alarms history
            alarms_history = self._perfdb.alarms.history.get(
                period=period,
                object_names=[object_name],
                match_alarm_id_on=self.match_id_on,
                alarm_ids=alarms,
                output_type="DataFrame",
            )
            # appending to data
            alarms_df = concat([alarms_df, alarms_history], axis=0, ignore_index=True)

        # resetting index
        alarms_df = alarms_df.reset_index(drop=True)

        # saving data
        self._data = alarms_df

    except Exception as e:
        if self.optional:
            self._data = None
        else:
            raise e

    return self.data