Skip to content

Vibration Frequencies

For more details on vibration data see this dedicated page in the reference section.

VibrationFrequencies(perfdb)

Class used for handling Vibration Frequencies. Can be accessed via perfdb.vibration.frequencies.

Parameters:

  • perfdb

    (PerfDB) –

    Top level object carrying all functionality and the connection handler.

Source code in echo_postgres/perfdb_root.py
def __init__(self, perfdb: e_pg.PerfDB) -> None:
    """Base class that all subclasses should inherit from.

    Parameters
    ----------
    perfdb : PerfDB
        Top level object carrying all functionality and the connection handler.

    """
    self._perfdb: e_pg.PerfDB = perfdb

get(object_names, reference_date=None, output_type='DataFrame')

Gets the applicable vibration frequencies for the given object names and reference date.

As the model of gearbox, generator, main bearing, etc. might have changed over time we will get the frequencies applicable to the reference date.

All the frequencies will be returned in orders using generator speed as the reference. If you want to convert to Hertz, just multiply the value by the generator speed in Hz.

For better readability the frequency names will have the FREQ prefix removed.

Parameters:

  • object_names

    (list[str]) –

    Names of the objects to get the frequencies for.

  • reference_date

    (datetime | Literal['all'] | None, default: None ) –

    Date to get the frequencies for. Can be one of:

    • datetime: The date to get the frequencies for.
    • "all": Will get all the frequencies for all dates. Useful when you want the history of the frequencies.
    • None: Will consider as the latest frequency (same as using reference_date=datetime.now()).

    Default is None.

  • output_type

    (Literal['dict', 'DataFrame'], default: 'DataFrame' ) –

    Output type of the data. Can be one of ["dict", "DataFrame"] By default "DataFrame"

Returns:

  • DataFrame

    If output_type is "DataFrame", returns a DataFrame with index as MultiIndex[object_name, component_type_name, subcomponent_type_name] and columns as the frequencies. Component model and subcomponent model will also be available in the columns.

    In case of "all" reference_date, start_date will be added as a fourth level in the index.

  • dict[str, dict[str, dict[str | None, dict[str, float]]]]

    If output_type is "dict", returns a dictionary in the format:

    {
        object_name: {
            component_type_name: {
                subcomponent_type_name | None: {
                    "FREQ1": 1.0,
                    "FREQ2": 2.0,
                    ...
                }
            }
        }
    }
    

    In case of "all" reference_date, start_date will be added as a fourth level in the dictionary.

Source code in echo_postgres/vibration_frequencies.py
@validate_call
def get(
    self,
    object_names: list[str],
    reference_date: datetime | Literal["all"] | None = None,
    output_type: Literal["dict", "DataFrame"] = "DataFrame",
) -> dict[str, dict[str, dict[str | None, dict[str, float]]]] | DataFrame:
    """Gets the applicable vibration frequencies for the given object names and reference date.

    As the model of gearbox, generator, main bearing, etc. might have changed over time we will get the frequencies applicable to the reference date.

    All the frequencies will be returned in orders using generator speed as the reference. If you want to convert to Hertz, just multiply the value by the generator speed in Hz.

    For better readability the frequency names will have the FREQ prefix removed.

    Parameters
    ----------
    object_names : list[str]
        Names of the objects to get the frequencies for.
    reference_date : datetime | Literal["all"] | None
        Date to get the frequencies for. Can be one of:

        - datetime: The date to get the frequencies for.
        - "all": Will get all the frequencies for all dates. Useful when you want the history of the frequencies.
        - None: Will consider as the latest frequency (same as using reference_date=datetime.now()).

        Default is None.
    output_type : Literal["dict", "DataFrame"], optional
        Output type of the data. Can be one of ["dict", "DataFrame"]
        By default "DataFrame"

    Returns
    -------
    DataFrame
        If output_type is "DataFrame", returns a DataFrame with index as MultiIndex[object_name, component_type_name, subcomponent_type_name] and columns as the frequencies. Component model and subcomponent model will also be available in the columns.

        In case of "all" reference_date, start_date will be added as a fourth level in the index.
    dict[str, dict[str, dict[str | None, dict[str, float]]]]
        If output_type is "dict", returns a dictionary in the format:

        ```python
        {
            object_name: {
                component_type_name: {
                    subcomponent_type_name | None: {
                        "FREQ1": 1.0,
                        "FREQ2": 2.0,
                        ...
                    }
                }
            }
        }
        ```

        In case of "all" reference_date, start_date will be added as a fourth level in the dictionary.

    """
    if reference_date is None:
        reference_date = datetime.now() - timedelta(minutes=5)

    # defining period to get the data
    if reference_date == "all":
        period = DateTimeRange(datetime(1900, 1, 1), datetime.now())
    else:
        period = DateTimeRange(reference_date, reference_date)

    # getting the components and subcomponents models for the desired date and object names
    components = self._perfdb.components.instances.history.get(
        object_names=object_names,
        period=period,
        get_attributes=True,
    )
    subcomponents = self._perfdb.components.subcomponents.instances.history.get(
        object_names=object_names,
        period=period,
        get_attributes=True,
    )

    # adding start_date col if "all" is selected
    add_cols = ["start_date"] if reference_date == "all" else []

    # getting only the relevant columns
    comp_freqcols = [col for col in components.columns if col.startswith("FREQ")]
    components = components[["object_name", "component_type_name", "component_model_name", *add_cols, *comp_freqcols]].copy()
    subcomp_freqcols = [col for col in subcomponents.columns if col.startswith("FREQ")]
    subcomponents = subcomponents[
        ["object_name", "component_type_name", "subcomponent_type_name", "subcomponent_model_name", *add_cols, *subcomp_freqcols]
    ].copy()

    # Concatenate the components and subcomponents
    df = concat([components, subcomponents], axis=0)

    # filling component models where not filled (subcomponents)
    component_model_mapping = (
        df[["component_type_name", "component_model_name"]]
        .dropna(how="any")
        .drop_duplicates()
        .set_index("component_type_name")["component_model_name"]
        .to_dict()
    )
    df["component_model_name"] = df["component_model_name"].fillna(
        df["component_type_name"].apply(lambda x: component_model_mapping.get(x, NA)),
    )

    # start date is used if "all" is selected
    df = df.set_index(["object_name", "component_type_name", "subcomponent_type_name", *add_cols])

    # checking if the index is unique
    if not df.index.is_unique:
        raise ValueError(
            "The index is not unique. There are components/subcomponents of the same type in the same date for the same object.",
        )

    # dropping the rows with NaN values in all freq columns
    df = df.dropna(subset=comp_freqcols + subcomp_freqcols, how="all")

    # keeping only the FREQ columns and the model columns
    df = df[["component_model_name", "subcomponent_model_name", *comp_freqcols, *subcomp_freqcols]].copy()

    # forcing all freq cols to be float
    df = df.astype(dict.fromkeys([*comp_freqcols, *subcomp_freqcols], "double[pyarrow]"))

    # dropping duplicates ignoring the start_date columns as we can have component replacements for the same model which would result in the same freqs
    df = (
        df.reset_index()
        .drop_duplicates(
            subset=[
                "object_name",
                "component_type_name",
                "subcomponent_type_name",
                "component_model_name",
                "subcomponent_model_name",
                *comp_freqcols,
                *subcomp_freqcols,
            ],
            keep="first",
        )
        .set_index(["object_name", "component_type_name", "subcomponent_type_name", *add_cols])
    )

    # renaming the columns to remove the FREQ prefix
    df = df.rename(columns={col: col.split("FREQ-")[1] for col in [*comp_freqcols, *subcomp_freqcols]})

    # returning the data
    if output_type == "DataFrame":
        return df

    # converting to dict
    result = df.to_dict(orient="index")

    final_result = {}
    if reference_date != "all":
        for (object_name, component_type_name, subcomponent_type_name), values in result.items():
            # getting values as a dict that only contains non null values
            ok_values = {key: value for key, value in values.items() if not isna(value)}

            if object_name not in final_result:
                final_result[object_name] = {}
            if component_type_name not in final_result[object_name]:
                final_result[object_name][component_type_name] = {}
            final_result[object_name][component_type_name][subcomponent_type_name if not isna(subcomponent_type_name) else None] = (
                ok_values
            )
    else:
        for (object_name, component_type_name, subcomponent_type_name, start_date), values in result.items():
            # getting values as a dict that only contains non null values
            ok_values = {key: value for key, value in values.items() if not isna(value)}

            if object_name not in final_result:
                final_result[object_name] = {}
            if component_type_name not in final_result[object_name]:
                final_result[object_name][component_type_name] = {}
            if subcomponent_type_name not in final_result[object_name][component_type_name]:
                final_result[object_name][component_type_name][
                    subcomponent_type_name if not isna(subcomponent_type_name) else None
                ] = {}
            final_result[object_name][component_type_name][subcomponent_type_name if not isna(subcomponent_type_name) else None][
                start_date
            ] = ok_values

    return final_result