import os
from seeq import spy
import pandas as pd
import numpy as np

# Set the compatibility option so that you maximize the chance that SPy will remain compatible with your notebook/script
spy.options.compatibility = 191
# Log into Seeq Server if you're not using Seeq Data Lab:
spy.login(url='http://localhost:34216', credentials_file='../credentials.key', force=False)

Asset Trees 4: Creating Accelerator Templates

The Seeq platform provides the basis for doing all sorts of analytics – leveraging Seeq Workbench, Seeq Organizer, Seeq Data Lab – with Seeq Cortex providing data connection and calculation capabilities.

It can be useful for the Seeq community of partners and users to create accelerators that deploy proven, standardized analytic approaches in rapid fashion using SPy’s asset/workbook/topic templating capabilities in spy.assets. For example, an expert in monitoring and optimizing large-scale HVAC installations might develop an Accelerator Template to deploy a set of useful calculations, visualizations and reports or dashboards that can handle variations in manufacturer, configuration etc and provide immediate value that can be expanded and built upon over time.

This notebook illustrates how to use spy.assets infrastructure to create Accelerator Templates. If you haven’t used it before, it is recommended that you first explore the following:

This notebook will use the HVAC example data that is installed by default with the Seeq system.

“Requirements”

The most important concept with Accelerator Templates is Requirements. A Requirement specifies an “input” to the template – something that must be provided in order for the template to function. For example, if the template needs an ambient temperature signal in order to calculate an operational efficiency metric, that temperature signal is a Requirement. Furthermore, the operating limits may need to be provided, or perhaps the manufacturer/model of the equipment. These Requirements are the things that vary from installation to installation. The template can be written to adjust to these variations as long as it has the inputs it needs.

Requirements are specified in a class like so:

from seeq.spy.assets import Asset

class HVAC_Monitoring_Requirements(Asset):

    @Asset.Requirement()
    def Compressor_Power(self, metadata):
        """
        Compressor Power as measured at the power supply of the equipment
        itself.
        """
        return {
            'Type': 'Signal',
            'Unit Of Measure Family': 'kW'
        }

    @Asset.Requirement()
    def Temperature(self, metadata):
        """
        The ambient *external* temperature as measured in a neutral location
        away from HVAC inlet/outlet sites.

        > Note that this value may be derived from equipment sensors with a
        > suitable coefficient to account for location bias.
        """
        return {
            'Type': 'Signal',
            'Unit Of Measure Family': 'F',
            'Optional': True
        }

Similar to @Asset.Attribute, Requirements are specified as functions within a Python class, decorated with @Asset.Requirement. The documentation for the function is useful to describe the expectations for the Requirement. For example, the Temperature signal that is supplied to meet one of the Requirements above must be an external temperature that is not affected by the operation of the HVAC unit.

The return value is a Python dictionary that further constrains the input. In this case, Temperature must be a Signal whose unit of measure is compatible with F (so, either Fahrenheit, Celsius or Kelvin).

Temperature is also marked with 'Optional' equal to True. This means that the template can handle the case where an adequate temperature signal cannot be supplied. The template may not be able to provide as many calculations, or perhaps those calculations will be less accurate.

Analytics!

Now let’s create a simple class that makes use of these two requirements. In this example, an Inefficient Operation condition is calculated by comparing Compressor Power and Temperature against hard-coded limits. (Such limits should likely be determined by manufacturer specifications or process control limits, but for now we’ll keep it simple.)

Note how this new HVAC_Monitoring class is derived from the HVAC_Monitoring_Requirements. This allows the Inefficient Operation calculation to refer to the members of the requirements class.

class HVAC_Monitoring(HVAC_Monitoring_Requirements):

    @Asset.Attribute()
    def Inefficient_Operation(self, metadata):
        """
        A condition that is present whenever the HVAC is running in ostensibly
        unnecessary environmental situations, resulting in inefficient use of power.

        Examples:

        * Ambient temperature is within control bounds and does not warrant
          cooling
        * Environmental conditions are such that HVAC system is operating outside
          of its range of capabilities
        """

        if not self.Temperature():
            return None

        return {
            'Type': 'Condition',
            'Formula Parameters': {
                '$power': self.Compressor_Power(),
                '$temp': self.Temperature()
            },
            'Formula': '$power > 20 kW and $temp < 60 F'
        }

Input Tree

The template is applied to an existing input tree. This tree must already be constructed, in whatever way makes sense for a particular use case. It may be different in each application of the template – different hierarchy, different organization. The template will be applied to the tree as it is constructed, and any roll-ups will correspond to the particular hierarchy present in the input tree.

In this example, we will use the Example asset tree that comes with Seeq. So we assemble a metadata DataFrame by doing spy.search(recursive=True).

# Grab the whole tree as the basis of the metadata DataFrame
metadata_df = spy.search({
    'Path': 'Example'
}, recursive=True)

# Reduce the number of columns (just for readability)
metadata_df = metadata_df[['ID', 'Path', 'Asset', 'Name', 'Type']]

# This call prepares the metadata DataFrame by adding 'Build Path' and
# 'Build Asset' columns. You can also specify a new root_asset_name, which
# is often desired so that you can differentiate the output tree from the
# input tree.
spy.assets.prepare(metadata_df, root_asset_name='HVAC Monitoring')

# Specify the Build Template. We want to target all leaf "Area" assets (for now)
metadata_df.at[metadata_df['Build Asset'].str.startswith('Area '), 'Build Template'] = 'HVAC_Monitoring'

metadata_df.head()

Build and Push

As with other uses of spy.assets, we must now build and then push the results.

build_df = spy.assets.build(HVAC_Monitoring, metadata_df)
push_results_df = spy.push(metadata=build_df)

If you click on the workbook link, you should see a new HVAC Monitoring tree in the Data tab with the expected hierarchy and an Inefficient Operation condition under each Area.

Brochures

Once you have an accelerator created, you can generate a brochure that advertises the cool analytics that the accelerator provides.

Execute the spy.assets.brochure() function like so:

html = spy.assets.brochure(HVAC_Monitoring, output='html')

# Write it to a file so we can view it in the browser
os.makedirs('Output', exist_ok=True)
with open('Output/hvac_monitoring_brochure.html', 'w') as f:
    f.write(html)