Ejemplo n.º 1
0
    def stcpr_aircx(self, current_time, stcpr_stpt_data, stcpr_data,
                    zn_dmpr_data, low_sf_cond, high_sf_cond):
        """
        Check duct static pressure AIRCx pre-requisites and manage analysis data set.
        :param current_time:
        :param stcpr_stpt_data:
        :param stcpr_data:
        :param zn_dmpr_data:
        :param low_sf_cond:
        :param high_sf_cond:
        :return:
        """
        if common.check_date(current_time, self.timestamp_array):
            common.pre_conditions(self.publish_results, INCONSISTENT_DATE,
                                  DX_LIST, current_time)
            self.reinitialize()

        run_status = common.check_run_status(self.timestamp_array,
                                             current_time, self.no_req_data,
                                             self.data_window)

        if run_status is None:
            _log.info(
                "{} - Insufficient data to produce a valid diagnostic result.".
                format(current_time))
            common.pre_conditions(self.publish_results, INSUFFICIENT_DATA,
                                  DX_LIST, current_time)
            self.reinitialize()

        if run_status:
            avg_stcpr_stpt, dx_string, dx_msg = common.setpoint_control_check(
                self.stcpr_stpt_array, self.stcpr_array,
                self.stpt_deviation_thr, DUCT_STC_RCX)
            self.publish_results(current_time, dx_string, dx_msg)
            self.low_stcpr_aircx(avg_stcpr_stpt)
            self.high_stcpr_aircx(avg_stcpr_stpt)
            self.reinitialize()

        self.stcpr_array.append(mean(stcpr_data))
        if stcpr_stpt_data:
            self.stcpr_stpt_array.append(mean(stcpr_stpt_data))

        zn_dmpr_data.sort(reverse=False)
        self.ls_dmpr_low_avg.extend(
            zn_dmpr_data[:int(math.ceil(len(zn_dmpr_data) * 0.5)
                              ) if len(zn_dmpr_data) != 1 else 1])
        self.ls_dmpr_high_avg.extend(
            zn_dmpr_data[int(math.ceil(len(zn_dmpr_data) * 0.5)) -
                         1 if len(zn_dmpr_data) != 1 else 0:])

        zn_dmpr_data.sort(reverse=True)
        self.hs_dmpr_high_avg.extend(
            zn_dmpr_data[:int(math.ceil(len(zn_dmpr_data) * 0.5)
                              ) if len(zn_dmpr_data) != 1 else 1])

        self.low_sf_condition.append(
            low_sf_cond if low_sf_cond is not None else 0)
        self.high_sf_condition.append(
            high_sf_cond if high_sf_cond is not None else 0)
        self.timestamp_array.append(current_time)
Ejemplo n.º 2
0
    def aggregate_power(self, peer, sender, bus, topic, headers, message):
        """
        Power measurements for devices are aggregated.
        :param peer:
        :param sender:
        :param bus:
        :param topic:
        :param headers:
        :param message:
        :return:
        """
        _log.debug("{}: received topic for power aggregation: {}".format(self.agent_name, topic))
        data = message[0]
        if not self.sim_flag:
            current_time = parser.parse(headers["Date"])
            to_zone = dateutil.tz.gettz(TIMEZONE)
            current_time = current_time.astimezone(to_zone)
        else:
            current_time = parser.parse(headers["Date"])
        self.current_datetime = current_time
        current_hour = current_time.hour

        try:
            current_points = self.demand_aggregation_working.pop(topic)
        except KeyError:
            if self.power_aggregation:
                self.current_power = sum(self.power_aggregation)
            else:
                self.current_power = 0.
            self.demand_aggregation_working = self.demand_aggregation_master.copy()

        conversion = current_points.get("conversion")
        point_list = []
        points = []
        for point in current_points.get("points", []):
            point_list.append((point, data[point]))
            points.append(point)
        if conversion is not None:
            value = float(self.conversion_handler(conversion, points, point_list))
        else:
            value = float(data[point])
        _log.debug("uncontrol - topic {} value {}".format(topic, value))
        self.power_aggregation.append(value)
        if not self.demand_aggregation_working:
            if self.power_aggregation:
                self.uc_load_array.append(sum(self.power_aggregation))
                self.normalize_to_hour += 1.0
                _log.debug("Current ts uncontrollable load: {}".format(sum(self.power_aggregation)))
            else:
                self.current_power = 0.
            self.power_aggregation = []
            self.demand_aggregation_working = self.demand_aggregation_master.copy()

            if self.current_hour is not None and current_hour != self.current_hour:
                self.q_uc[self.current_hour] = max(- mean(self.uc_load_array)*self.normalize_to_hour/60.0, 10.0)
                _log.debug("Current hour uncontrollable load: {}".format(mean(self.uc_load_array)*self.normalize_to_hour/60.0))
                self.uc_load_array = []
                self.normalize_to_hour = 0

            self.current_hour = current_hour
Ejemplo n.º 3
0
 def not_economizing_when_needed(self):
     """If the detected problems(s) are consistent then generate a fault message(s).
     No return
     """
     oaf = [(m - r) / (o - r) for o, r, m in zip(self.oat_values, self.rat_values, self.mat_values)]
     avg_oaf = max(0.0, min(100.0, mean(oaf) * 100.0))
     avg_damper_signal = mean(self.oad_values)
     diagnostic_msg = {}
     energy_impact = {}
     thresholds = zip(self.open_damper_threshold.items(), self.oaf_economizing_threshold.items())
     for (key, damper_thr), (key2, oaf_thr) in thresholds:
         if avg_damper_signal < damper_thr:
             msg = "{} - {}: {}".format(constants.ECON2, key, self.alg_result_messages[0])
             result = 11.1
             energy = self.energy_impact_calculation()
         else:
             if avg_oaf < oaf_thr:
                 msg = "{} - {}: {} - OAF={}".format(constants.ECON2, key, self.alg_result_messages[2], avg_oaf)
                 result = 12.1
                 energy = self.energy_impact_calculation()
             else:
                 msg = "{} - {}: {}".format(constants.ECON2, key, self.alg_result_messages[1])
                 result = 10.0
                 energy = 0.0
         _log.info(msg)
         diagnostic_msg.update({key: result})
         energy_impact.update({key: energy})
     _log.info(constants.table_log_format(self.analysis_name, self.timestamp[-1], (constants.ECON2 + constants.DX + ":" + str(diagnostic_msg))))
     _log.info(constants.table_log_format(self.analysis_name, self.timestamp[-1], (constants.ECON2 + constants.EI + ":" + str(energy_impact))))
     self.results_publish.append(constants.table_publish_format(self.analysis_name, self.timestamp[-1], (constants.ECON2 + constants.DX), diagnostic_msg))
     self.results_publish.append(constants.table_publish_format(self.analysis_name, self.timestamp[-1], (constants.ECON2 + constants.EI),  energy_impact))
     self.clear_data()
Ejemplo n.º 4
0
    def setpoint_reset_aircx(self, current_time, current_fan_status,
                             stcpr_stpt_data, sat_stpt_data, dx_result):
        """
        Main function for set point reset AIRCx - manages data arrays checks AIRCx run status.
        :param current_time:
        :param current_fan_status:
        :param stcpr_stpt_data:
        :param sat_stpt_data:
        :param dx_result:
        :return:
        """
        stcpr_run_status = check_run_status(
            self.timestamp_array,
            current_time,
            self.no_req_data,
            run_schedule="daily",
            minimum_point_array=self.stcpr_stpt_array)

        if not self.timestamp_array:
            return dx_result

        self.reset_table_key = reset_name = create_table_key(
            self.analysis, self.timestamp_array[0])
        if stcpr_run_status is None:
            dx_result.log("{} - Insufficient data to produce - {}".format(
                current_time, DUCT_STC_RCX3))
            dx_result = pre_conditions(INSUFFICIENT_DATA, [DUCT_STC_RCX3],
                                       reset_name, current_time, dx_result)
            self.stcpr_stpt_array = []
        elif stcpr_run_status:
            dx_result = self.no_static_pr_reset(dx_result)
            self.stcpr_stpt_array = []

        sat_run_status = check_run_status(
            self.timestamp_array,
            current_time,
            self.no_req_data,
            run_schedule="daily",
            minimum_point_array=self.sat_stpt_array)

        if sat_run_status is None:
            dx_result.log("{} - Insufficient data to produce - {}".format(
                current_time, SA_TEMP_RCX3))
            dx_result = pre_conditions(INSUFFICIENT_DATA, [SA_TEMP_RCX3],
                                       reset_name, current_time, dx_result)
            self.sat_stpt_array = []
            self.timestamp_array = []
        elif sat_run_status:
            dx_result = self.no_sat_stpt_reset(dx_result)
            self.sat_stpt_array = []
            self.timestamp_array = []

        if current_fan_status:
            if stcpr_stpt_data:
                self.stcpr_stpt_array.append(mean(stcpr_stpt_data))
            if sat_stpt_data:
                self.sat_stpt_array.append(mean(sat_stpt_data))

        return dx_result
Ejemplo n.º 5
0
    def high_sat(self, dx_result, avg_sat_stpt):
        """
        Diagnostic to identify and correct high supply-air temperature
        (correction by modifying SAT set point).
        :param dx_result:
        :param avg_sat_stpt:
        :return:
        """
        avg_zones_rht = mean(self.percent_rht) * 100.0
        thresholds = zip(self.percent_dmpr_thr.items(),
                         self.percent_rht_thr.items())
        diagnostic_msg = {}

        for (key, percent_dmpr_thr), (key2, percent_rht_thr) in thresholds:
            avg_zone_dmpr_data = mean(self.percent_dmpr[key]) * 100.0
            if avg_zone_dmpr_data > percent_dmpr_thr and avg_zones_rht < percent_rht_thr:
                if avg_sat_stpt is None:
                    # Create diagnostic message for fault
                    # when supply-air temperature set point
                    # is not available.
                    msg = "{} - The SAT too high but SAT set point data is not available.".format(
                        key)
                    result = 54.1
                elif self.auto_correct_flag:
                    aircx_sat_stpt = avg_sat_stpt - self.sat_retuning
                    # Create diagnostic message for fault condition
                    # with auto-correction
                    if aircx_sat_stpt >= self.min_sat_stpt:
                        dx_result.command(self.sat_stpt_cname, aircx_sat_stpt)
                        sat_stpt = "%s" % float("%.2g" % aircx_sat_stpt)
                        msg = "{} - SAT too high. SAT set point decreased to: {}{}F".format(
                            key, self.dgr_sym, sat_stpt)
                        result = 51.1
                    else:
                        # Create diagnostic message for fault condition
                        # where the maximum SAT has been reached
                        dx_result.command(self.sat_stpt_cname,
                                          self.min_sat_stpt)
                        sat_stpt = "%s" % float("%.2g" % self.min_sat_stpt)
                        msg = "{} - SAT too high. Auto-correcting to min SAT set point {}{}F".format(
                            key, self.dgr_sym, sat_stpt)
                        result = 52.1
                else:
                    # Create diagnostic message for fault condition
                    # without auto-correction
                    msg = "{} - The SAT too high but auto-correction is not enabled.".format(
                        key)
                    result = 53.1
            else:
                msg = "{} - No problem detected for High SAT diagnostic.".format(
                    key)
                result = 50.0
            diagnostic_msg.update({key: result})
            dx_result.log(msg)

        dx_result.insert_table_row(self.table_key,
                                   {SA_TEMP_RCX2 + DX: diagnostic_msg})
        return dx_result
Ejemplo n.º 6
0
    def low_stcpr_dx(self, dx_result, avg_stcpr_stpt):
        """Diagnostic to identify and correct low duct static pressure

        (correction by modifying duct static pressure set point).
        """
        zn_dmpr = deepcopy(self.zn_dmpr_arr)
        zn_dmpr.sort(reverse=False)
        zone_dmpr_lowtemp = zn_dmpr[:int(math.ceil(
            len(self.zn_dmpr_arr) * 0.5)) if len(self.zn_dmpr_arr) != 1 else 1]
        zn_dmpr_low_avg = mean(zone_dmpr_lowtemp)

        zone_dmpr_hightemp = zn_dmpr[int(math.
                                         ceil(len(self.zn_dmpr_arr) * 0.5)) -
                                     1 if len(self.zn_dmpr_arr) != 1 else 0:]
        zn_dmpr_high_avg = mean(zone_dmpr_hightemp)
        if zn_dmpr_high_avg > self.zone_high_dmpr_threshold and zn_dmpr_low_avg > self.zone_low_dmpr_threshold:
            if avg_stcpr_stpt is None:
                # Create diagnostic message for fault
                # when duct static pressure set point
                # is not available.
                msg = ('The duct static pressure set point has been '
                       'detected to be too low but but supply-air'
                       'temperature set point data is not available.')
                dx_msg = 14.1
            elif self.auto_correct_flag:
                auto_correct_stcpr_stpt = avg_stcpr_stpt + self.stcpr_retuning
                if auto_correct_stcpr_stpt <= self.max_stcpr_stpt:
                    dx_result.command(self.stcpr_stpt_cname,
                                      auto_correct_stcpr_stpt)
                    new_stcpr_stpt = '%s' % float(
                        '%.2g' % auto_correct_stcpr_stpt)
                    new_stcpr_stpt = new_stcpr_stpt + ' in. w.g.'
                    msg = ('The duct static pressure was detected to be '
                           'too low. The duct static pressure has been '
                           'increased to: {}'.format(new_stcpr_stpt))
                    dx_msg = 11.1
                else:
                    dx_result.command(self.stcpr_stpt_cname,
                                      self.max_stcpr_stpt)
                    new_stcpr_stpt = '%s' % float('%.2g' % self.max_stcpr_stpt)
                    new_stcpr_stpt = new_stcpr_stpt + ' in. w.g.'
                    msg = ('The duct static pressure set point is at the '
                           'maximum value configured by the building '
                           'operator: {})'.format(new_stcpr_stpt))
                    dx_msg = 12.1
            else:
                msg = ('The duct static pressure set point was detected '
                       'to be too low but auto-correction is not enabled.')
                dx_msg = 13.1
        else:
            msg = ('No re-tuning opportunity was detected during the low duct '
                   'static pressure diagnostic.')
            dx_msg = 10.0

        self.dx_table.update({DUCT_STC_RCX1 + DX: dx_msg})
        dx_result.log(msg, logging.INFO)
        return dx_result
Ejemplo n.º 7
0
    def low_stcpr_aircx(self, dx_result, avg_stcpr_stpt, low_sf_condition):
        """
        AIRCx to identify and correct low duct static pressure.
        :param dx_result:
        :param avg_stcpr_stpt:
        :param low_sf_condition:
        :return:
        """
        zn_dmpr = self.zn_dmpr_array[:]
        zn_dmpr.sort(reverse=False)
        dmpr_low_temps = zn_dmpr[:int(math.ceil(len(self.zn_dmpr_array)*0.5)) if len(self.zn_dmpr_array) != 1 else 1]
        dmpr_low_avg = mean(dmpr_low_temps)

        dmpr_high_temps = zn_dmpr[int(math.ceil(len(self.zn_dmpr_array)*0.5)) - 1 if len(self.zn_dmpr_array) != 1 else 0:]
        dmpr_high_avg = mean(dmpr_high_temps)
        thresholds = zip(self.zn_high_dmpr_thr.items(), self.zn_low_dmpr_thr.items())
        diagnostic_msg = {}

        for (key, zn_high_dmpr_thr), (key2, zn_low_dmpr_thr) in thresholds:
            if dmpr_high_avg > zn_high_dmpr_thr and dmpr_low_avg > zn_low_dmpr_thr:
                if low_sf_condition is not None and low_sf_condition:
                    msg = "{} - duct static pressure too low. Supply fan at maximum.".format(key)
                    result = 15.1
                elif avg_stcpr_stpt is None:
                    # Create diagnostic message for fault
                    # when duct static pressure set point
                    # is not available.
                    msg = "{} - duct static pressure is too low but set point data is not available.".format(key)
                    result = 14.1
                elif self.auto_correct_flag:
                    aircx_stcpr_stpt = avg_stcpr_stpt + self.stcpr_retuning
                    if aircx_stcpr_stpt <= self.max_stcpr_stpt:
                        dx_result.command(self.stcpr_stpt_cname, aircx_stcpr_stpt)
                        stcpr_stpt = "%s" % float("%.2g" % aircx_stcpr_stpt)
                        stcpr_stpt = stcpr_stpt + " in. w.g."
                        msg = "{} - duct static pressure too low. Set point increased to: {}".format(key,
                                                                                                     stcpr_stpt)
                        result = 11.1
                    else:
                        dx_result.command(self.stcpr_stpt_cname, self.max_stcpr_stpt)
                        stcpr_stpt = "%s" % float("%.2g" % self.max_stcpr_stpt)
                        stcpr_stpt = stcpr_stpt + " in. w.g."
                        msg = "{} - duct static pressure too low. Set point increased to max {}.".format(key,
                                                                                                         stcpr_stpt)
                        result = 12.1
                else:
                    msg = "{} - duct static pressure is too low but auto-correction is not enabled.".format(key)
                    result = 13.1
            else:
                msg = "{} - no retuning opportunities detected for Low duct static pressure diagnostic.".format(key)
                result = 10.0
            diagnostic_msg.update({key: result})
            dx_result.log(msg)

        dx_result.insert_table_row(self.table_key, {DUCT_STC_RCX1 + DX: diagnostic_msg})
        return dx_result
    def low_sat(self, avg_sat_stpt):
        """
        Diagnostic to identify and correct low supply-air temperature
        (correction by modifying SAT set point).
        :param avg_sat_stpt:
        :return:
        """
        avg_zones_rht = mean(self.percent_rht) * 100.0
        rht_avg = mean(self.rht_array)
        thresholds = zip(self.rht_valve_thr.items(),
                         self.percent_rht_thr.items())
        diagnostic_msg = {}

        for (key, rht_valve_thr), (key2, percent_rht_thr) in thresholds:
            if rht_avg > rht_valve_thr and avg_zones_rht > percent_rht_thr:
                if avg_sat_stpt is None:
                    # Create diagnostic message for fault
                    # when supply-air temperature set point
                    # is not available.
                    msg = "{} - The SAT too low but SAT set point data is not available.".format(
                        key)
                    result = 44.1
                elif self.auto_correct_flag and self.auto_correct_flag == key:
                    aircx_sat_stpt = avg_sat_stpt + self.sat_retuning
                    if aircx_sat_stpt <= self.max_sat_stpt:
                        self.send_autocorrect_command(self.sat_stpt_cname,
                                                      aircx_sat_stpt)
                        sat_stpt = "%s" % float("%.2g" % aircx_sat_stpt)
                        msg = "{} - SAT too low. SAT set point increased to: {}F".format(
                            key, sat_stpt)
                        result = 41.1
                    else:
                        self.send_autocorrect_command(self.sat_stpt_cname,
                                                      self.max_sat_stpt)
                        sat_stpt = "%s" % float("%.2g" % self.max_sat_stpt)
                        sat_stpt = str(sat_stpt)
                        msg = "{} - SAT too low. Auto-correcting to max SAT set point {}F".format(
                            key, sat_stpt)
                        result = 42.1
                else:
                    msg = "{} - SAT detected to be too low but auto-correction is not enabled.".format(
                        key)
                    result = 43.1
            else:
                msg = "{} - No retuning opportunities detected for Low SAT diagnostic.".format(
                    key)
                result = 40.0
            diagnostic_msg.update({key: result})
            _log.info(msg)

        _log.info(
            common.table_log_format(
                self.timestamp_array[-1],
                (SA_TEMP_RCX1 + DX + ": " + str(diagnostic_msg))))
        self.publish_results(self.timestamp_array[-1], SA_TEMP_RCX1 + DX,
                             diagnostic_msg)
 def aggregate_data(self):
     oa_ma = [(x - y) for x, y in zip(self.oat_values, self.mat_values)]
     ra_ma = [(x - y) for x, y in zip(self.rat_values, self.mat_values)]
     ma_oa = [(y - x) for x, y in zip(self.oat_values, self.mat_values)]
     ma_ra = [(y - x) for x, y in zip(self.rat_values, self.mat_values)]
     avg_oa_ma = mean(oa_ma)
     avg_ra_ma = mean(ra_ma)
     avg_ma_oa = mean(ma_oa)
     avg_ma_ra = mean(ma_ra)
     return avg_oa_ma, avg_ra_ma, avg_ma_oa, avg_ma_ra
 def aggregate_data(self):
     oa_ma = [(x - y) for x, y in zip(self.oat_values, self.mat_values)]
     ra_ma = [(x - y) for x, y in zip(self.rat_values, self.mat_values)]
     ma_oa = [(y - x) for x, y in zip(self.oat_values, self.mat_values)]
     ma_ra = [(y - x) for x, y in zip(self.rat_values, self.mat_values)]
     avg_oa_ma = mean(oa_ma)
     avg_ra_ma = mean(ra_ma)
     avg_ma_oa = mean(ma_oa)
     avg_ma_ra = mean(ma_ra)
     return avg_oa_ma, avg_ra_ma, avg_ma_oa, avg_ma_ra
Ejemplo n.º 11
0
    def duct_static(self, current_time, stcpr_stpt_data, stcpr_data,
                    zn_dmpr_data, low_dx_cond, high_dx_cond, dx_result,
                    validate):
        """Check duct static pressure RCx pre-requisites and assemble the

        duct static pressure analysis data set.
        """
        if check_date(current_time, self.timestamp_arr):
            self.reinitialize()
            return dx_result

        if low_dx_cond:
            dx_result.log(self.low_msg.format(current_time), logging.DEBUG)
            return dx_result
        if high_dx_cond:
            dx_result.log(self.high_msg.format(current_time), logging.DEBUG)
            return dx_result

        file_key = create_table_key(VALIDATE_FILE_TOKEN, current_time)
        data = validation_builder(validate, STCPR_VALIDATE, DATA)
        run_status = check_run_status(self.timestamp_arr, current_time, self.no_req_data)

        if run_status is None:
            dx_result.log('Current analysis data set has insufficient data '
                          'to produce a valid diagnostic result.')
            self.reinitialize()
            return dx_result

        if run_status:
            self.table_key = create_table_key(self.analysis, self.timestamp_arr[-1])
            avg_stcpr_stpt, dx_table = setpoint_control_check(self.stcpr_stpt_arr,
                                                              self.stcpr_arr,
                                                              self.stpt_allowable_dev,
                                                              DUCT_STC_RCX, DX,
                                                              STCPR_NAME, self.token_offset)

            self.dx_table.update(dx_table)
            dx_result = self.low_stcpr_dx(dx_result, avg_stcpr_stpt)
            dx_result = self.high_stcpr_dx(dx_result, avg_stcpr_stpt)
            dx_result.insert_table_row(self.table_key, self.dx_table)
            self.data.update({STCPR_VALIDATE + DATA + ST: 1})
            dx_result.insert_table_row(self.file_key, self.data)
            self.reinitialize()

        self.stcpr_stpt_arr.append(mean(stcpr_data))
        self.stcpr_arr.append(mean(stcpr_stpt_data))
        self.zn_dmpr_arr.append(mean(zn_dmpr_data))
        self.timestamp_arr.append(current_time)

        if self.data:
            self.data.update({STCPR_VALIDATE + DATA + ST: 0})
            dx_result.insert_table_row(self.file_key, self.data)
        self.data = data
        self.file_key = file_key
        return dx_result
Ejemplo n.º 12
0
    def stcpr_aircx(self, current_time, stcpr_stpt_data, stcpr_data,
                    zn_dmpr_data, low_sf_cond, high_sf_cond, dx_result):
        """
        Check duct static pressure AIRCx pre-requisites and manage analysis data set.
        :param current_time:
        :param stcpr_stpt_data:
        :param stcpr_data:
        :param zn_dmpr_data:
        :param low_sf_cond:
        :param high_sf_cond:
        :param dx_result:
        :return:
        """
        try:
            if check_date(current_time, self.timestamp_array):
                dx_result = pre_conditions(INCONSISTENT_DATE, DX_LIST,
                                           self.analysis, current_time,
                                           dx_result)
                self.reinitialize()
                return dx_result

            run_status = check_run_status(self.timestamp_array, current_time,
                                          self.no_req_data, self.data_window)

            if run_status is None:
                dx_result.log(
                    "{} - Insufficient data to produce a valid diagnostic result."
                    .format(current_time))
                dx_result = pre_conditions(INSUFFICIENT_DATA, DX_LIST,
                                           self.analysis, current_time,
                                           dx_result)
                self.reinitialize()
                return dx_result

            if run_status:
                self.table_key = create_table_key(self.analysis,
                                                  self.timestamp_array[-1])
                avg_stcpr_stpt, dx_table, dx_result = setpoint_control_check(
                    self.stcpr_stpt_array, self.stcpr_array,
                    self.stpt_deviation_thr, DUCT_STC_RCX, self.dx_offset,
                    dx_result)

                dx_result.insert_table_row(self.table_key, dx_table)
                dx_result = self.low_stcpr_aircx(dx_result, avg_stcpr_stpt,
                                                 low_sf_cond)
                dx_result = self.high_stcpr_aircx(dx_result, avg_stcpr_stpt,
                                                  high_sf_cond)
                self.reinitialize()
            return dx_result
        finally:
            self.stcpr_stpt_array.append(mean(stcpr_data))
            self.stcpr_array.append(mean(stcpr_stpt_data))
            self.zn_dmpr_array.append(mean(zn_dmpr_data))
            self.timestamp_array.append(current_time)
Ejemplo n.º 13
0
    def setpoint_reset_aircx(self, current_time, current_fan_status,
                             stcpr_stpt_data, sat_stpt_data):
        """
        Main function for set point reset AIRCx - manages data arrays checks AIRCx run status.
        :param current_time:
        :param current_fan_status:
        :param stcpr_stpt_data:
        :param sat_stpt_data:
        :return:
        """
        stcpr_run_status = common.check_run_status(
            self.timestamp_array,
            current_time,
            self.no_req_data,
            run_schedule="daily",
            minimum_point_array=self.stcpr_stpt_array)

        if not self.timestamp_array:
            return

        if stcpr_run_status is None:
            _log.info("{} - Insufficient data to produce - {}".format(
                current_time, DUCT_STC_RCX3))
            common.pre_conditions(self.publish_results, INSUFFICIENT_DATA,
                                  [DUCT_STC_RCX3], current_time)
            self.stcpr_stpt_array = []
        elif stcpr_run_status:
            self.no_static_pr_reset()
            self.stcpr_stpt_array = []

        sat_run_status = common.check_run_status(
            self.timestamp_array,
            current_time,
            self.no_req_data,
            run_schedule="daily",
            minimum_point_array=self.sat_stpt_array)

        if sat_run_status is None:
            _log.info("{} - Insufficient data to produce - {}".format(
                current_time, SA_TEMP_RCX3))
            common.pre_conditions(self.publish_results, INSUFFICIENT_DATA,
                                  [SA_TEMP_RCX3], current_time)
            self.sat_stpt_array = []
            self.timestamp_array = []
        elif sat_run_status:
            self.no_sat_stpt_reset()
            self.sat_stpt_array = []
            self.timestamp_array = []

        if current_fan_status:
            if stcpr_stpt_data:
                self.stcpr_stpt_array.append(mean(stcpr_stpt_data))
            if sat_stpt_data:
                self.sat_stpt_array.append(mean(sat_stpt_data))
Ejemplo n.º 14
0
    def sched_rcx_alg(self, current_time, stcpr_data, stcpr_stpt_data,
                      sat_stpt_data, fan_stat_data, dx_result,
                      sched_val):
        """Check schedule status and unit operational status."""
        fan_status = None
        schedule = self.schedule[current_time.weekday()]
        run_diagnostic = False
        start_new_analysis_sat_stpt = None
        start_new_analysis_stcpr_stpt = None

        if self.timestamp and self.timestamp[-1].date() != current_time.date():
            start_new_analysis_time = current_time
            run_diagnostic = True

        if not run_diagnostic:
            if current_time.time() < schedule[0] or current_time.time() > schedule[1]:
                self.stcpr_arr.extend(stcpr_data)
                self.fanstat_values.append((current_time, int(max(fan_stat_data))))
                self.sched_time.append(current_time)
            if int(max(fan_stat_data)):
                self.stcpr_stpt_arr.append(mean(stcpr_stpt_data))
                self.sat_stpt_arr.append(mean(sat_stpt_data))
        fan_status = int(max(fan_stat_data))
        start_new_analysis_sat_stpt = mean(stcpr_stpt_data)
        start_new_analysis_stcpr_stpt = mean(sat_stpt_data)
        self.timestamp.append(current_time)

        data = validation_builder(sched_val, SCHED_VALIDATE, DATA)
        reset_key = create_table_key(self.reset_file_name_id, self.timestamp[0])
        schedule_key = create_table_key(self.sched_file_name_id, self.timestamp[0])
        file_key = create_table_key(VALIDATE_FILE_TOKEN, current_time)
        if run_diagnostic and len(self.timestamp) >= self.no_req_data:
            dx_result = self.unocc_fan_operation(dx_result)
            if len(self.stcpr_stpt_arr) >= self.no_req_data:
                dx_result = self.no_static_pr_reset(dx_result)
            if len(self.sat_stpt_arr) >= self.no_req_data:
                dx_result = self.no_sat_stpt_reset(dx_result)
            if self.dx_table:
                dx_result.insert_table_row(reset_key, self.dx_table)
            data.update({SCHED_VALIDATE + DATA + ST: 1})
            self.reinitialize(start_new_analysis_time, start_new_analysis_sat_stpt,
                              start_new_analysis_stcpr_stpt, stcpr_data, fan_status)
        elif run_diagnostic:
            dx_msg = 61.2
            dx_table = {SCHED_RCX + DX:  dx_msg}
            dx_result.insert_table_row(schedule_key, dx_table)
            data.update({SCHED_VALIDATE + DATA + ST: 2})
            self.reinitialize(start_new_analysis_time, start_new_analysis_sat_stpt,
                              start_new_analysis_stcpr_stpt, stcpr_data, fan_status)
        else:
            data.update({SCHED_VALIDATE + DATA + ST: 0})
        dx_result.insert_table_row(file_key, data)
        return dx_result
Ejemplo n.º 15
0
    def low_sat(self, dx_result, avg_sat_stpt):
        """
        Diagnostic to identify and correct low supply-air temperature
        (correction by modifying SAT set point).
        :param dx_result:
        :param avg_sat_stpt:
        :return:
        """
        avg_zones_rht = mean(self.percent_rht) * 100.0
        rht_avg = mean(self.rht_array)
        thresholds = zip(self.rht_valve_thr.items(),
                         self.percent_rht_thr.items())
        diagnostic_msg = {}

        for (key, rht_valve_thr), (key2, percent_rht_thr) in thresholds:
            if rht_avg > rht_valve_thr and avg_zones_rht > percent_rht_thr:
                if avg_sat_stpt is None:
                    # Create diagnostic message for fault
                    # when supply-air temperature set point
                    # is not available.
                    msg = "{} - The SAT too low but SAT set point data is not available.".format(
                        key)
                    result = 44.1
                elif self.auto_correct_flag:
                    aircx_sat_stpt = avg_sat_stpt + self.sat_retuning
                    if aircx_sat_stpt <= self.max_sat_stpt:
                        dx_result.command(self.sat_stpt_cname, aircx_sat_stpt)
                        sat_stpt = "%s" % float("%.2g" % aircx_sat_stpt)
                        msg = "{} - SAT too low. SAT set point increased to: {}{}F".format(
                            key, self.dgr_sym, sat_stpt)
                        result = 41.1
                    else:
                        dx_result.command(self.sat_stpt_cname,
                                          self.max_sat_stpt)
                        sat_stpt = "%s" % float("%.2g" % self.max_sat_stpt)
                        sat_stpt = str(sat_stpt)
                        msg = "{} - SAT too low. Auto-correcting to max SAT set point {}{}F".format(
                            key, self.dgr_sym, sat_stpt)
                        result = 42.1
                else:
                    msg = "{} - SAT detected to be too low but auto-correction is not enabled.".format(
                        key)
                    result = 43.1
            else:
                msg = "{} - No retuning opportunities detected for Low SAT diagnostic.".format(
                    key)
                result = 40.0
            diagnostic_msg.update({key: result})
            dx_result.log(msg)

        dx_result.insert_table_row(self.table_key,
                                   {SA_TEMP_RCX1 + DX: diagnostic_msg})
        return dx_result
Ejemplo n.º 16
0
    def low_stcpr_dx(self, dx_result, avg_stcpr_stpt):
        """Diagnostic to identify and correct low duct static pressure

        (correction by modifying duct static pressure set point).
        """
        zn_dmpr = deepcopy(self.zn_dmpr_arr)
        zn_dmpr.sort(reverse=False)
        zone_dmpr_lowtemp = zn_dmpr[:int(math.ceil(len(self.zn_dmpr_arr)*0.5)) if len(self.zn_dmpr_arr) != 1 else 1]
        zn_dmpr_low_avg = mean(zone_dmpr_lowtemp)

        zone_dmpr_hightemp = zn_dmpr[int(math.ceil(len(self.zn_dmpr_arr)*0.5)) - 1 if len(self.zn_dmpr_arr) != 1 else 0:]
        zn_dmpr_high_avg = mean(zone_dmpr_hightemp)
        if zn_dmpr_high_avg > self.zone_high_dmpr_threshold and zn_dmpr_low_avg > self.zone_low_dmpr_threshold:
            if avg_stcpr_stpt is None:
                # Create diagnostic message for fault
                # when duct static pressure set point
                # is not available.
                msg = ('The duct static pressure set point has been '
                       'detected to be too low but but supply-air'
                       'temperature set point data is not available.')
                dx_msg = 14.1
            elif self.auto_correct_flag:
                auto_correct_stcpr_stpt = avg_stcpr_stpt + self.stcpr_retuning
                if auto_correct_stcpr_stpt <= self.max_stcpr_stpt:
                    dx_result.command(self.stcpr_stpt_cname, auto_correct_stcpr_stpt)
                    new_stcpr_stpt = '%s' % float('%.2g' % auto_correct_stcpr_stpt)
                    new_stcpr_stpt = new_stcpr_stpt + ' in. w.g.'
                    msg = ('The duct static pressure was detected to be '
                           'too low. The duct static pressure has been '
                           'increased to: {}'
                           .format(new_stcpr_stpt))
                    dx_msg = 11.1
                else:
                    dx_result.command(self.stcpr_stpt_cname, self.max_stcpr_stpt)
                    new_stcpr_stpt = '%s' % float('%.2g' % self.max_stcpr_stpt)
                    new_stcpr_stpt = new_stcpr_stpt + ' in. w.g.'
                    msg = ('The duct static pressure set point is at the '
                           'maximum value configured by the building '
                           'operator: {})'.format(new_stcpr_stpt))
                    dx_msg = 12.1
            else:
                msg = ('The duct static pressure set point was detected '
                       'to be too low but auto-correction is not enabled.')
                dx_msg = 13.1
        else:
            msg = ('No re-tuning opportunity was detected during the low duct '
                   'static pressure diagnostic.')
            dx_msg = 10.0

        self.dx_table.update({DUCT_STC_RCX1 + DX: dx_msg})
        dx_result.log(msg, logging.INFO)
        return dx_result
Ejemplo n.º 17
0
    def high_sat(self, dx_result, avg_sat_stpt):
        """Diagnostic to identify and correct high supply-air temperature

        (correction by modifying SAT set point)
        """
        avg_zones_rht = mean(self.percent_rht) * 100
        avg_zone_dmpr_data = mean(self.percent_dmpr) * 100

        if avg_zone_dmpr_data > self.percent_dmpr_thr and avg_zones_rht < self.percent_rht_thr:
            if avg_sat_stpt is None:
                # Create diagnostic message for fault
                # when supply-air temperature set point
                # is not available.
                msg = ('The SAT has been detected to be too high but '
                       'but supply-air temperature set point data '
                       'is not available.')
                dx_msg = 54.1
            elif self.auto_correct_flag:
                autocorrect_sat_stpt = avg_sat_stpt - self.sat_retuning
                # Create diagnostic message for fault condition
                # with auto-correction
                if autocorrect_sat_stpt >= self.min_sat_stpt:
                    dx_result.command(self.sat_stpt_cname,
                                      autocorrect_sat_stpt)
                    sat_stpt = '%s' % float('%.2g' % autocorrect_sat_stpt)
                    msg = ('The SAT has been detected to be too high. The '
                           'SAT set point has been increased to: '
                           '{}{}F'.format(self.dgr_sym, sat_stpt))
                    dx_msg = 51.1
                else:
                    # Create diagnostic message for fault condition
                    # where the maximum SAT has been reached
                    dx_result.command(self.sat_stpt_cname, self.min_sat_stpt)
                    sat_stpt = '%s' % float('%.2g' % self.min_sat_stpt)
                    msg = ('The SAT was detected to be too high, '
                           'auto-correction has increased the SAT to the '
                           'minimum configured SAT: {}{}F'.format(
                               self.dgr_sym, sat_stpt))
                    dx_msg = 52.1
            else:
                # Create diagnostic message for fault condition
                # without auto-correction
                msg = ('The SAT has been detected to be too high but '
                       'auto-correction is not enabled.')
                dx_msg = 53.1
        else:
            msg = ('No problem detected for High Supply-air '
                   'Temperature diagnostic.')
            dx_msg = 50.0
        self.dx_table.update({SA_TEMP_RCX2 + DX: dx_msg})
        dx_result.log(msg, logging.INFO)
        return dx_result
Ejemplo n.º 18
0
    def high_sat(self, dx_result, avg_sat_stpt):
        """Diagnostic to identify and correct high supply-air temperature

        (correction by modifying SAT set point)
        """
        avg_zones_rht = mean(self.percent_rht)*100
        avg_zone_dmpr_data = mean(self.percent_dmpr)*100

        if avg_zone_dmpr_data > self.percent_dmpr_thr and avg_zones_rht < self.percent_rht_thr:
            if avg_sat_stpt is None:
                # Create diagnostic message for fault
                # when supply-air temperature set point
                # is not available.
                msg = ('The SAT has been detected to be too high but '
                       'but supply-air temperature set point data '
                       'is not available.')
                dx_msg = 54.1
            elif self.auto_correct_flag:
                autocorrect_sat_stpt = avg_sat_stpt - self.sat_retuning
                # Create diagnostic message for fault condition
                # with auto-correction
                if autocorrect_sat_stpt >= self.min_sat_stpt:
                    dx_result.command(self.sat_stpt_cname, autocorrect_sat_stpt)
                    sat_stpt = '%s' % float('%.2g' % autocorrect_sat_stpt)
                    msg = ('The SAT has been detected to be too high. The '
                           'SAT set point has been increased to: '
                           '{}{}F'.format(self.dgr_sym, sat_stpt))
                    dx_msg = 51.1
                else:
                    # Create diagnostic message for fault condition
                    # where the maximum SAT has been reached
                    dx_result.command(self.sat_stpt_cname, self.min_sat_stpt)
                    sat_stpt = '%s' % float('%.2g' % self.min_sat_stpt)
                    msg = ('The SAT was detected to be too high, '
                           'auto-correction has increased the SAT to the '
                           'minimum configured SAT: {}{}F'
                           .format(self.dgr_sym, sat_stpt))
                    dx_msg = 52.1
            else:
                # Create diagnostic message for fault condition
                # without auto-correction
                msg = ('The SAT has been detected to be too high but '
                       'auto-correction is not enabled.')
                dx_msg = 53.1
        else:
            msg = ('No problem detected for High Supply-air '
                   'Temperature diagnostic.')
            dx_msg = 50.0
        self.dx_table.update({SA_TEMP_RCX2 + DX: dx_msg})
        dx_result.log(msg, logging.INFO)
        return dx_result
Ejemplo n.º 19
0
    def duct_static(self, current_time, stcpr_stpt_data, stcpr_data,
                    zn_dmpr_data, low_dx_cond, high_dx_cond, dx_result):
        """Check duct static pressure RCx pre-requisites and assemble the

        duct static pressure analysis data set.
        """
        dx_status = 0
        if check_date(current_time, self.timestamp_arr):
            dx_status = 0
            self.reinitialize()
            return dx_status, dx_result

        if low_dx_cond:
            dx_result.log(self.low_msg.format(current_time), logging.DEBUG)
            return dx_status, dx_result
        if high_dx_cond:
            dx_result.log(self.high_msg.format(current_time), logging.DEBUG)
            return dx_status, dx_result

        run_status = check_run_status(self.timestamp_arr, current_time,
                                      self.no_req_data)

        if run_status is None:
            dx_result.log('Current analysis data set has insufficient data '
                          'to produce a valid diagnostic result.')
            self.reinitialize()
            return dx_status, dx_result
        dx_status = 1
        if run_status:
            self.table_key = create_table_key(self.analysis,
                                              self.timestamp_arr[-1])
            avg_stcpr_stpt, dx_table = setpoint_control_check(
                self.stcpr_stpt_arr, self.stcpr_arr, self.stpt_allowable_dev,
                DUCT_STC_RCX, DX, STCPR_NAME, self.token_offset)

            self.dx_table.update(dx_table)
            dx_result = self.low_stcpr_dx(dx_result, avg_stcpr_stpt)
            dx_result = self.high_stcpr_dx(dx_result, avg_stcpr_stpt)
            dx_result.insert_table_row(self.table_key, self.dx_table)
            dx_status = 2
            self.reinitialize()

        self.stcpr_stpt_arr.append(mean(stcpr_data))
        self.stcpr_arr.append(mean(stcpr_stpt_data))
        self.zn_dmpr_arr.append(mean(zn_dmpr_data))
        self.timestamp_arr.append(current_time)

        return dx_status, dx_result
Ejemplo n.º 20
0
    def high_stcpr_aircx(self, dx_result, avg_stcpr_stpt, high_sf_condition):
        """
        AIRCx to identify and correct high duct static pressure.
        :param dx_result:
        :param avg_stcpr_stpt:
        :param high_sf_condition:
        :return:
        """
        zn_dmpr = self.zn_dmpr_array[:]
        zn_dmpr.sort(reverse=True)
        zn_dmpr = zn_dmpr[:int(math.ceil(len(self.zn_dmpr_array) * 0.5)
                               ) if len(self.zn_dmpr_array) != 1 else 1]
        avg_zn_damper = mean(zn_dmpr)
        diagnostic_msg = {}

        for key, hdzn_dmpr_thr in self.hdzn_dmpr_thr.items():
            if avg_zn_damper <= hdzn_dmpr_thr:
                if high_sf_condition is not None and high_sf_condition:
                    msg = "{} - duct static pressure too high. Supply fan at minimum.".format(
                        key)
                    result = 25.1
                elif avg_stcpr_stpt is None:
                    # Create diagnostic message for fault
                    # when duct static pressure set point
                    # is not available.
                    msg = "{} - duct static pressure is too high but set point data is not available.".format(
                        key)
                    result = 24.1
                elif self.auto_correct_flag:
                    aircx_stcpr_stpt = avg_stcpr_stpt - self.stcpr_retuning
                    if aircx_stcpr_stpt >= self.min_stcpr_stpt:
                        dx_result.command(self.stcpr_stpt_cname,
                                          aircx_stcpr_stpt)
                        stcpr_stpt = "%s" % float("%.2g" % aircx_stcpr_stpt)
                        stcpr_stpt = stcpr_stpt + " in. w.g."
                        msg = "{} - duct static pressure too high. Set point decreased to: {}".format(
                            key, stcpr_stpt)
                        result = 21.1
                    else:
                        dx_result.command(self.stcpr_stpt_cname,
                                          self.min_stcpr_stpt)
                        stcpr_stpt = "%s" % float("%.2g" % self.min_stcpr_stpt)
                        stcpr_stpt = stcpr_stpt + " in. w.g."
                        msg = "{} - duct static pressure too high. Set point decreased to min {}.".format(
                            key, stcpr_stpt)
                        result = 22.1
                else:
                    msg = "{} - duct static pressure is too high but auto-correction is not enabled.".format(
                        key)
                    result = 23.1
            else:
                msg = "{} - No retuning opportunities detected for high duct static pressure diagnostic.".format(
                    key)
                result = 20.0
            diagnostic_msg.update({key: result})
            dx_result.log(msg)

        dx_result.insert_table_row(self.table_key,
                                   {DUCT_STC_RCX2 + DX: diagnostic_msg})
        return dx_result
Ejemplo n.º 21
0
    def run_diagnostic(self):
        msg = ""
        if len(self.oat_values) > self.no_required_data:
            mat_oat_diff_list = [
                abs(x - y) for x, y in zip(self.oat_values, self.mat_values)
            ]
            open_damper_check = mean(mat_oat_diff_list)
            diagnostic_msg = {}
            for sensitivity, threshold in self.oat_mat_check.items():
                if open_damper_check > threshold:
                    msg = "{} - {}: OAT and MAT are inconsistent when OAD is near 100%".format(
                        constants.ECON1, str(sensitivity))
                    result = 0.1
                else:
                    msg = "{} - {}: OAT and MAT are consistent when OAD is near 100%".format(
                        constants.ECON1, str(sensitivity))
                    result = 0.0
                diagnostic_msg.update({sensitivity: result})

            _log.info(msg)
            _log.info(
                constants.table_log_format(self.analysis_name,
                                           self.timestamp[-1],
                                           (constants.ECON1 + constants.DX +
                                            ":" + str(diagnostic_msg))))
            self.results_publish.append(
                constants.table_publish_format(
                    self.analysis_name, self.timestamp[-1],
                    (constants.ECON1 + constants.DX), diagnostic_msg))
            self.clear_data()
            return True
        else:
            self.clear_data()
            return False
Ejemplo n.º 22
0
    def scrape_ending(self, topic):
        if not self.scalability_test:
            return
        
        try:
            self.waiting_to_finish.remove(topic)
        except KeyError:
            _log.warning(topic + " published twice before test finished, increase the length of scrape interval and rerun test")

        if not self.waiting_to_finish:
            end = datetime.now()
            delta = end - self.current_test_start
            delta = delta.total_seconds()
            self.test_results.append(delta)
            
            self.test_iterations += 1
            
            _log.info("publish {} took {} seconds".format(self.test_iterations, delta))
            
            if self.test_iterations >= self.scalability_test_iterations:
                #Test is now over. Button it up and shutdown.
                mean = math_utils.mean(self.test_results) 
                stdev = math_utils.stdev(self.test_results) 
                _log.info("Mean total publish time: "+str(mean))
                _log.info("Std dev publish time: "+str(stdev))
                sys.exit(0)
    def insufficient_oa(self):
        """If the detected problems(s) are consistent then generate a fault message(s).
        No return
        """
        oaf = [(mat - rat) / (oat - rat) for oat, rat, mat in zip(self.oat_values, self.rat_values, self.mat_values)]
        avg_oaf = mean(oaf) * 100.0
        diagnostic_msg = {}

        if avg_oaf < 0 or avg_oaf > 125.0:
            msg = ("{}: Inconclusive result, the OAF calculation led to an "
                   "unexpected value: {}".format(constants.ECON5, avg_oaf))
            _log.info(msg)
            _log.info(constants.table_log_format(self.analysis_name, self.timestamp[-1], (constants.ECON5 + constants.DX + ":" + str(self.invalid_oaf_dict))))
            self.results_publish.append(constants.table_publish_format(self.analysis_name, self.timestamp[-1], (constants.ECON5 + constants.DX), self.invalid_oaf_dict))
            self.clear_data()
            return

        avg_oaf = max(0.0, min(100.0, avg_oaf))
        for sensitivity, threshold in self.ventilation_oaf_threshold.items():
            if self.desired_oaf - avg_oaf > threshold:
                msg = "{}: Insufficient OA is being provided for ventilation - sensitivity: {}".format(constants.ECON5, sensitivity)
                result = 43.1
            else:
                msg = "{}: The calculated OAF was within acceptable limits - sensitivity: {}".format(constants.ECON5, sensitivity)
                result = 40.0
            _log.info(msg)
            diagnostic_msg.update({sensitivity: result})
        _log.info(constants.table_log_format(self.analysis_name, self.timestamp[-1], (constants.ECON5 + constants.DX + ":" + str(diagnostic_msg))))
        self.results_publish.append(constants.table_publish_format(self.analysis_name, self.timestamp[-1], (constants.ECON5 + constants.DX), diagnostic_msg))

        self.clear_data()
Ejemplo n.º 24
0
 def economizing_when_not_needed(self):
     """If the detected problems(s) are consistent then generate a fault message(s).
     No return
     """
     desired_oaf = self.desired_oaf / 100.0
     avg_damper = mean(self.oad_values)
     diagnostic_msg = {}
     energy_impact = {}
     for sensitivity, threshold in self.excess_damper_threshold.items():
         if avg_damper > threshold:
             msg = "{} - {}: {}".format(constants.ECON3, sensitivity, self.alg_result_messages[0])
             # color_code = "RED"
             result = 21.1
             energy = self.energy_impact_calculation(desired_oaf)
         else:
             msg = "{} - {}: {}".format(constants.ECON3, sensitivity, self.alg_result_messages[1])
             # color_code = "GREEN"
             result = 20.0
             energy = 0.0
         _log.info(msg)
         diagnostic_msg.update({sensitivity: result})
         energy_impact.update({sensitivity: energy})
     _log.info(constants.table_log_format(self.analysis_name, self.timestamp[-1], (constants.ECON3 + constants.DX + ":" + str(diagnostic_msg))))
     self.results_publish.append(constants.table_publish_format(self.analysis_name, self.timestamp[-1], (constants.ECON3 + constants.DX), diagnostic_msg))
     _log.info(constants.table_log_format(self.analysis_name, self.timestamp[-1], (constants.ECON3 + constants.EI + ":" + str(energy_impact))))
     self.results_publish.append(constants.table_publish_format(self.analysis_name, self.timestamp[-1], (constants.ECON3 + constants.EI), energy_impact))
     self.clear_data()
    def check_fan_status(self, current_time):
        """Check the status and speed of the fan
        current_time: datetime time delta
        return int
        """
        if self.fan_status_data:
            supply_fan_status = int(max(self.fan_status_data))
        else:
            supply_fan_status = None

        if self.fan_sp_data:
            self.fan_speed = mean(self.fan_sp_data)
        else:
            self.fan_speed = None
        if supply_fan_status is None:
            if self.fan_speed > self.low_sf_thr:
                supply_fan_status = 1
            else:
                supply_fan_status = 0

        if not supply_fan_status:
            if self.unit_status is None:
                self.unit_status = current_time
        else:
            self.unit_status = None
        return supply_fan_status
    def check_prerequisites(self):
        """Evaluate realtime data to determine if the diagnostic
        prerequisites are met.  If this function returns False the
        diagnostics in the diagnostic list will not run.

        :return: bool
        """
        if self.prerequisites_data_required:
            avg_data = []
            prerequisite_eval_list = []
            for key, value in self.prerequisites_data_required.items():
                try:
                    avg_data.append((key, mean(value)))
                except ValueError as ex:
                    LOG.warning("Exception prerequisites %s", ex)
                    LOG.warning(
                        "Not enough data to verify "
                        "diagnostic prerequisites: %s", key)
                    return False
            for condition in self.prerequisites_expr_list:
                evaluation = condition.subs(avg_data)
                prerequisite_eval_list.append(evaluation)
                LOG.debug("Prerequisite %s evaluation: %s", condition,
                          evaluation)
            if prerequisite_eval_list:
                if False in prerequisite_eval_list:
                    return False
                return True
        return True
    def economizing_when_not_needed(self, dx_result, table_key):
        """
        If the detected problems(s) are consistent then generate a
        fault message(s).
        :param dx_result:
        :param table_key:
        :return:
        """
        desired_oaf = self.desired_oaf / 100.0
        avg_damper = mean(self.oad_values)
        diagnostic_msg = {}
        energy_impact = {}
        for key, threshold in self.excess_damper_threshold.items():
            if avg_damper - self.min_damper_sp > threshold:
                msg = "{}: {} - sensitivity: {}".format(
                    ECON3, self.alg_result_messages[0], key)
                # color_code = "RED"
                result = 21.1
                energy = self.energy_impact_calculation(desired_oaf)
            else:
                msg = "{}: {} - sensitivity: {}".format(
                    ECON3, self.alg_result_messages[1], key)
                # color_code = "GREEN"
                result = 20.0
                energy = 0.0
            dx_result.log(msg)
            diagnostic_msg.update({key: result})
            energy_impact.update({key: energy})

        dx_table = {ECON3 + DX: diagnostic_msg, ECON3 + EI: energy_impact}
        dx_result.insert_table_row(table_key, dx_table)
        self.clear_data()
        return dx_result
Ejemplo n.º 28
0
    def economizing_when_not_needed(self, dx_result, table_key):
        """
        If the detected problems(s) are consistent then generate a
        fault message(s).
        :param dx_result:
        :param table_key:
        :return:
        """
        desired_oaf = self.desired_oaf / 100.0
        avg_damper = mean(self.oad_values)
        diagnostic_msg = {}
        energy_impact = {}
        for sensitivity, threshold in self.excess_damper_threshold.items():
            if avg_damper > threshold:
                msg = "{} - {}: {}".format(ECON3, sensitivity, self.alg_result_messages[0])
                # color_code = "RED"
                result = 21.1
                energy = self.energy_impact_calculation(desired_oaf)
            else:
                msg = "{} - {}: {}".format(ECON3, sensitivity, self.alg_result_messages[1])
                # color_code = "GREEN"
                result = 20.0
                energy = 0.0
            dx_result.log(msg)
            diagnostic_msg.update({sensitivity: result})
            energy_impact.update({sensitivity: energy})

        dx_table = {
            ECON3 + DX: diagnostic_msg,
            ECON3 + EI: energy_impact
        }
        dx_result.insert_table_row(table_key, dx_table)
        self.clear_data()
        return dx_result
Ejemplo n.º 29
0
    def scrape_ending(self, topic):
        if not self.scalability_test:
            return

        try:
            self.waiting_to_finish.remove(topic)
        except KeyError:
            _log.warning(
                topic +
                " published twice before test finished, increase the length of scrape interval and rerun test"
            )

        if not self.waiting_to_finish:
            end = datetime.now()
            delta = end - self.current_test_start
            delta = delta.total_seconds()
            self.test_results.append(delta)

            self.test_iterations += 1

            _log.info("publish {} took {} seconds".format(
                self.test_iterations, delta))

            if self.test_iterations >= self.scalability_test_iterations:
                #Test is now over. Button it up and shutdown.
                mean = math_utils.mean(self.test_results)
                stdev = math_utils.stdev(self.test_results)
                _log.info("Mean total publish time: " + str(mean))
                _log.info("Std dev publish time: " + str(stdev))
                sys.exit(0)
Ejemplo n.º 30
0
    def low_sat(self, dx_result, avg_sat_stpt):
        """Diagnostic to identify and correct low supply-air temperature

        (correction by modifying SAT set point)
        """
        avg_zones_rht = mean(self.percent_rht)*100
        rht_avg = mean(self.rht_arr)
        if rht_avg > self.rht_valve_thr and avg_zones_rht > self.percent_rht_thr:
            if avg_sat_stpt is None:
                # Create diagnostic message for fault
                # when supply-air temperature set point
                # is not available.
                msg = ('The SAT has been detected to be too low but '
                       'but supply-air temperature set point data '
                       'is not available.')
                dx_msg = 43.1
            elif self.auto_correct_flag:
                autocorrect_sat_stpt = avg_sat_stpt + self.sat_retuning
                if autocorrect_sat_stpt <= self.max_sat_stpt:
                    dx_result.command(self.sat_stpt_cname, autocorrect_sat_stpt)
                    sat_stpt = '%s' % float('%.2g' % autocorrect_sat_stpt)
                    msg = ('The SAT has been detected to be too low. '
                           'The SAT set point has been increased to: '
                           '{}{}F'.format(self.dgr_sym, sat_stpt))
                    dx_msg = 41.1
                else:
                    dx_result.command(self.sat_stpt_cname, self.max_sat_stpt)
                    sat_stpt = '%s' % float('%.2g' % self.max_sat_stpt)
                    sat_stpt = str(sat_stpt)
                    msg = (
                        'The supply-air temperautre was detected to be '
                        'too low. Auto-correction has increased the '
                        'supply-air temperature set point to the maximum '
                        'configured supply-air tempeature set point: '
                        '{}{}F)'.format(self.dgr_sym, sat_stpt))
                    dx_msg = 42.1
            else:
                msg = ('The SAT has been detected to be too low but'
                       'auto-correction is not enabled.')
                dx_msg = 44.1
        else:
            msg = ('No problem detected for Low Supply-air '
                   'Temperature diagnostic.')
            dx_msg = 40.0
        self.dx_table.update({SA_TEMP_RCX1 + DX: dx_msg})
        dx_result.log(msg, logging.INFO)
        return dx_result
Ejemplo n.º 31
0
    def low_sat(self, dx_result, avg_sat_stpt):
        """Diagnostic to identify and correct low supply-air temperature

        (correction by modifying SAT set point)
        """
        avg_zones_rht = mean(self.percent_rht) * 100
        rht_avg = mean(self.rht_arr)
        if rht_avg > self.rht_valve_thr and avg_zones_rht > self.percent_rht_thr:
            if avg_sat_stpt is None:
                # Create diagnostic message for fault
                # when supply-air temperature set point
                # is not available.
                msg = ('The SAT has been detected to be too low but '
                       'but supply-air temperature set point data '
                       'is not available.')
                dx_msg = 43.1
            elif self.auto_correct_flag:
                autocorrect_sat_stpt = avg_sat_stpt + self.sat_retuning
                if autocorrect_sat_stpt <= self.max_sat_stpt:
                    dx_result.command(self.sat_stpt_cname,
                                      autocorrect_sat_stpt)
                    sat_stpt = '%s' % float('%.2g' % autocorrect_sat_stpt)
                    msg = ('The SAT has been detected to be too low. '
                           'The SAT set point has been increased to: '
                           '{}{}F'.format(self.dgr_sym, sat_stpt))
                    dx_msg = 41.1
                else:
                    dx_result.command(self.sat_stpt_cname, self.max_sat_stpt)
                    sat_stpt = '%s' % float('%.2g' % self.max_sat_stpt)
                    sat_stpt = str(sat_stpt)
                    msg = ('The supply-air temperautre was detected to be '
                           'too low. Auto-correction has increased the '
                           'supply-air temperature set point to the maximum '
                           'configured supply-air tempeature set point: '
                           '{}{}F)'.format(self.dgr_sym, sat_stpt))
                    dx_msg = 42.1
            else:
                msg = ('The SAT has been detected to be too low but'
                       'auto-correction is not enabled.')
                dx_msg = 44.1
        else:
            msg = ('No problem detected for Low Supply-air '
                   'Temperature diagnostic.')
            dx_msg = 40.0
        self.dx_table.update({SA_TEMP_RCX1 + DX: dx_msg})
        dx_result.log(msg, logging.INFO)
        return dx_result
Ejemplo n.º 32
0
    def high_stcpr_aircx(self, avg_stcpr_stpt):
        """
        AIRCx to identify and correct high duct static pressure.
        :param avg_stcpr_stpt::
        :return:
        """
        high_sf_condition = True if sum(self.high_sf_condition) / len(
            self.high_sf_condition) > 0.5 else False
        dmpr_high_avg = mean(self.hs_dmpr_high_avg)
        diagnostic_msg = {}

        for key, hdzn_dmpr_thr in self.hdzn_dmpr_thr.items():
            if dmpr_high_avg <= hdzn_dmpr_thr:
                if high_sf_condition is not None and high_sf_condition:
                    msg = "{} - duct static pressure too high. Supply fan at minimum.".format(
                        key)
                    result = 25.1
                elif avg_stcpr_stpt is None:
                    # Create diagnostic message for fault
                    # when duct static pressure set point
                    # is not available.
                    msg = "{} - duct static pressure is too high but set point data is not available.".format(
                        key)
                    result = 24.1
                elif self.auto_correct_flag and self.auto_correct_flag == key:
                    aircx_stcpr_stpt = avg_stcpr_stpt - self.stcpr_retuning
                    if aircx_stcpr_stpt >= self.min_stcpr_stpt:
                        self.send_autocorrect_command(self.stcpr_stpt_cname,
                                                      aircx_stcpr_stpt)
                        stcpr_stpt = "%s" % float("%.2g" % aircx_stcpr_stpt)
                        stcpr_stpt = stcpr_stpt + " in. w.g."
                        msg = "{} - duct static pressure too high. Set point decreased to: {}".format(
                            key, stcpr_stpt)
                        result = 21.1
                    else:
                        self.send_autocorrect_command(self.stcpr_stpt_cname,
                                                      self.min_stcpr_stpt)
                        stcpr_stpt = "%s" % float("%.2g" % self.min_stcpr_stpt)
                        stcpr_stpt = stcpr_stpt + " in. w.g."
                        msg = "{} - duct static pressure too high. Set point decreased to min {}.".format(
                            key, stcpr_stpt)
                        result = 22.1
                else:
                    msg = "{} - duct static pressure is too high but auto-correction is not enabled.".format(
                        key)
                    result = 23.1
            else:
                msg = "{} - No retuning opportunities detected for high duct static pressure diagnostic.".format(
                    key)
                result = 20.0
            diagnostic_msg.update({key: result})
            _log.info(msg)

        _log.info(
            common.table_log_format(
                self.timestamp_array[-1],
                (DUCT_STC_RCX2 + DX + ": " + str(diagnostic_msg))))
        self.publish_results(self.timestamp_array[-1], DUCT_STC_RCX2 + DX,
                             diagnostic_msg)
Ejemplo n.º 33
0
 def aggregate_data(self):
     """ Calculate averages used for calculations within the class.  Needs oat, mat,rat values set in class
     return
     avg_oa_ma: float
     avg_ra_ma: float
     avg_ma_oa: float
     avg_ma_ra: float
     """
     oa_ma = [(x - y) for x, y in zip(self.oat_values, self.mat_values)]
     ra_ma = [(x - y) for x, y in zip(self.rat_values, self.mat_values)]
     ma_oa = [(y - x) for x, y in zip(self.oat_values, self.mat_values)]
     ma_ra = [(y - x) for x, y in zip(self.rat_values, self.mat_values)]
     avg_oa_ma = mean(oa_ma)
     avg_ra_ma = mean(ra_ma)
     avg_ma_oa = mean(ma_oa)
     avg_ma_ra = mean(ma_ra)
     return avg_oa_ma, avg_ra_ma, avg_ma_oa, avg_ma_ra
    def setpoint_reset_aircx(self, current_time, current_fan_status, stcpr_stpt_data, sat_stpt_data, dx_result):
        """
        Main function for set point reset AIRCx - manages data arrays checks AIRCx run status.
        :param current_time:
        :param current_fan_status:
        :param stcpr_stpt_data:
        :param sat_stpt_data:
        :param dx_result:
        :return:
        """
        stcpr_run_status = check_run_status(self.timestamp_array, current_time, self.no_req_data,
                                            run_schedule="daily", minimum_point_array=self.stcpr_stpt_array)

        if not self.timestamp_array:
            return dx_result

        self.reset_table_key = reset_name = create_table_key(self.analysis, self.timestamp_array[0])
        if stcpr_run_status is None:
            dx_result.log("{} - Insufficient data to produce - {}".format(current_time, DUCT_STC_RCX3))
            dx_result = pre_conditions(INSUFFICIENT_DATA, [DUCT_STC_RCX3], reset_name, current_time, dx_result)
            self.stcpr_stpt_array = []
        elif stcpr_run_status:
            dx_result = self.no_static_pr_reset(dx_result)
            self.stcpr_stpt_array = []

        sat_run_status = check_run_status(self.timestamp_array, current_time, self.no_req_data,
                                          run_schedule="daily", minimum_point_array=self.sat_stpt_array)

        if sat_run_status is None:
            dx_result.log("{} - Insufficient data to produce - {}".format(current_time, SA_TEMP_RCX3))
            dx_result = pre_conditions(INSUFFICIENT_DATA, [SA_TEMP_RCX3], reset_name, current_time, dx_result)
            self.sat_stpt_array = []
            self.timestamp_array = []
        elif sat_run_status:
            dx_result = self.no_sat_stpt_reset(dx_result)
            self.sat_stpt_array = []
            self.timestamp_array = []

        if current_fan_status:
            if stcpr_stpt_data:
                self.stcpr_stpt_array.append(mean(stcpr_stpt_data))
            if sat_stpt_data:
                self.sat_stpt_array.append(mean(sat_stpt_data))

        return dx_result
    def not_economizing_when_needed(self, dx_result, table_key):
        """
        If the detected problems(s) are consistent then generate a fault
        message(s).
        :param dx_result:
        :param table_key:
        :return:
        """
        oaf = [(m - r) / (o - r) for o, r, m in zip(
            self.oat_values, self.rat_values, self.mat_values)]
        avg_oaf = max(0.0, min(100.0, mean(oaf) * 100.0))
        avg_damper_signal = mean(self.oad_values)
        diagnostic_msg = {}
        energy_impact = {}
        thresholds = zip(self.open_damper_threshold.items(),
                         self.oaf_economizing_threshold.items())
        for (key, damper_thr), (key2, oaf_thr) in thresholds:
            if avg_damper_signal - self.minimum_damper_setpoint < damper_thr:
                msg = "{}: {} - sensitivity: {}".format(
                    ECON2, self.alg_result_messages[0], key)
                # color_code = "RED"
                result = 11.1
                energy = self.energy_impact_calculation()
            else:
                if 100.0 - avg_oaf <= oaf_thr:
                    msg = "{}: {} - sensitivity: {}".format(
                        ECON2, self.alg_result_messages[1], key)
                    # color_code = "GREEN"
                    result = 10.0
                    energy = 0.0
                else:
                    msg = "{}: {} --OAF: {} - sensitivity: {}".format(
                        ECON2, self.alg_result_messages[2], avg_oaf, key)
                    # color_code = "RED"
                    result = 12.1
                    energy = self.energy_impact_calculation()
            dx_result.log(msg)
            diagnostic_msg.update({key: result})
            energy_impact.update({key: energy})

        dx_table = {ECON2 + DX: diagnostic_msg, ECON2 + EI: energy_impact}
        dx_result.insert_table_row(table_key, dx_table)
        self.clear_data()
        return dx_result
Ejemplo n.º 36
0
    def unocc_fan_operation(self, dx_result):
        """If the AHU/RTU is operating during unoccupied periods inform the
        building operator.
        """
        avg_duct_stcpr = 0
        percent_on = 0
        fanstat_on = [(fan[0].hour, fan[1]) for fan in self.fanstat_values if int(fan[1]) == 1]
        fanstat = [(fan[0].hour, fan[1]) for fan in self.fanstat_values]
        hourly_counter = []

        for counter in range(24):
            fan_on_count = [fan_status_time[1] for fan_status_time in fanstat_on if fan_status_time[0] == counter]
            fan_count = [fan_status_time[1] for fan_status_time in fanstat if fan_status_time[0] == counter]
            if len(fan_count):
                hourly_counter.append(fan_on_count.count(1)/len(fan_count)*100)
            else:
                hourly_counter.append(0)

        if self.sched_time:
            if self.fanstat_values:
                percent_on = (len(fanstat_on)/len(self.fanstat_values)) * 100.0
            if self.stcpr_arr:
                avg_duct_stcpr = mean(self.stcpr_arr)

            if percent_on > self.unocc_time_threshold:
                msg = 'Supply fan is on during unoccupied times.'
                dx_msg = 63.1
            else:
                if avg_duct_stcpr < self.unocc_stp_threshold:
                    msg = 'No problems detected for schedule diagnostic.'
                    dx_msg = 60.0
                else:
                    msg = ('Fan status show the fan is off but the duct static '
                           'pressure is high, check the functionality of the '
                           'pressure sensor.')
                    dx_msg = 64.2
        else:
            msg = 'No problems detected for schedule diagnostic.'
            dx_msg = 60.0

        if dx_msg != 64.2:
            for _hour in range(24):
                push_time = self.timestamp[0].date()
                push_time = datetime.combine(push_time, datetime.min.time())
                push_time = push_time.replace(hour=_hour)
                dx_table = {SCHED_RCX + DX: 60.0}
                if hourly_counter[_hour] > self.unocc_time_threshold:
                    dx_table = {SCHED_RCX + DX:  dx_msg}
                table_key = create_table_key(self.sched_file_name_id, push_time)
                dx_result.insert_table_row(table_key, dx_table)
        else:
            push_time = self.timestamp[0].date()
            table_key = create_table_key(self.sched_file_name_id, push_time)
            dx_result.insert_table_row(table_key, {SCHED_RCX + DX:  dx_msg})
        dx_result.log(msg, logging.INFO)
        return dx_result
Ejemplo n.º 37
0
    def high_stcpr_dx(self, dx_result, avg_stcpr_stpt):
        """Diagnostic to identify and correct high duct static pressure

        (correction by modifying duct static pressure set point)
        """
        zn_dmpr = deepcopy(self.zn_dmpr_arr)
        zn_dmpr.sort(reverse=True)
        zn_dmpr = zn_dmpr[:int(math.ceil(len(self.zn_dmpr_arr) * 0.5)
                               ) if len(self.zn_dmpr_arr) != 1 else 1]
        avg_zone_damper = mean(zn_dmpr)

        if avg_zone_damper <= self.hdzn_dmpr_thr:
            if avg_stcpr_stpt is None:
                # Create diagnostic message for fault
                # when duct static pressure set point
                # is not available.
                msg = ('The duct static pressure set point has been '
                       'detected to be too high but but duct static '
                       'pressure set point data is not available.'
                       'temperature set point data is not available.')
                dx_msg = 24.1
            elif self.auto_correct_flag:
                auto_correct_stcpr_stpt = avg_stcpr_stpt - self.stcpr_retuning
                if auto_correct_stcpr_stpt >= self.min_stcpr_stpt:
                    dx_result.command(self.stcpr_stpt_cname,
                                      auto_correct_stcpr_stpt)
                    new_stcpr_stpt = '%s' % float(
                        '%.2g' % auto_correct_stcpr_stpt)
                    new_stcpr_stpt = new_stcpr_stpt + ' in. w.g.'
                    msg = ('The duct static pressure was detected to be '
                           'too high. The duct static pressure set point '
                           'has been reduced to: {}'.format(new_stcpr_stpt))
                    dx_msg = 21.1
                else:
                    dx_result.command(self.stcpr_stpt_cname,
                                      self.min_stcpr_stpt)
                    new_stcpr_stpt = '%s' % float('%.2g' % self.min_stcpr_stpt)
                    new_stcpr_stpt = new_stcpr_stpt + ' in. w.g.'
                    msg = ('The duct static pressure set point is at the '
                           'minimum value configured by the building '
                           'operator: {})'.format(new_stcpr_stpt))
                    dx_msg = 22.1
            else:
                msg = ('Duct static pressure set point was detected to be '
                       'too high but auto-correction is not enabled.')
                dx_msg = 23.1
        else:
            msg = (
                'No re-tuning opportunity was detected during the high duct '
                'static pressure diagnostic.')
            dx_msg = 20.0

        self.dx_table.update({DUCT_STC_RCX2 + DX: dx_msg})
        dx_result.log(msg, logging.INFO)
        return dx_result
Ejemplo n.º 38
0
    def not_economizing_when_needed(self, dx_result, table_key):
        """
        If the detected problems(s) are consistent then generate a fault
        message(s).
        :param dx_result:
        :param table_key:
        :return:
        """
        oaf = [(m - r) / (o - r) for o, r, m in zip(self.oat_values, self.rat_values, self.mat_values)]
        avg_oaf = max(0.0, min(100.0, mean(oaf)*100.0))
        avg_damper_signal = mean(self.oad_values)
        diagnostic_msg = {}
        energy_impact = {}
        thresholds = zip(self.open_damper_threshold.items(), self.oaf_economizing_threshold.items())
        for (key, damper_thr), (key2, oaf_thr) in thresholds:
            if avg_damper_signal < damper_thr:
                msg = "{} - {}: {}".format(ECON2, key, self.alg_result_messages[0])
                # color_code = "RED"
                result = 11.1
                energy = self.energy_impact_calculation()
            else:
                if avg_oaf < oaf_thr:
                    msg = "{} - {}: {} - OAF={}".format(ECON2, key, self.alg_result_messages[2], avg_oaf)
                    # color_code = "RED"
                    result = 12.1
                    energy = self.energy_impact_calculation()
                else:
                    msg = "{} - {}: {}".format(ECON2, key, self.alg_result_messages[1])
                    # color_code = "GREEN"
                    result = 10.0
                    energy = 0.0
            dx_result.log(msg)
            diagnostic_msg.update({key: result})
            energy_impact.update({key: energy})

        dx_table = {
            ECON2 + DX: diagnostic_msg,
            ECON2 + EI: energy_impact
        }
        dx_result.insert_table_row(table_key, dx_table)
        self.clear_data()
        return dx_result
Ejemplo n.º 39
0
    def init_outputs(self, outputs):
        for output_info in outputs:
            # Topic to subscribe to for data (currently data format must be
            # consistent with a MasterDriverAgent all publish)
            topic = output_info["topic"]
            # Point name from as published by MasterDriverAgent
            point = output_info.pop("point")
            mapped = output_info.pop("mapped")
            # Options for release are None or default
            # None assumes BACnet release via priority array
            # default will safe original value, at start of agent run for control restoration
            # TODO: Update release value anytime agent has state transition for actuation_enable
            release = output_info.get("release", None)
            # Constant offset to apply to apply to determined actuation value
            offset = output_info.get("offset", 0.0)
            # VIP identity of Actuator to call via RPC to perform control of device
            actuator = output_info.get("actuator", "platform.actuator")
            # This is the flexibility range for the market commodity the
            # transactive agent will utilize
            flex = output_info["flexibility_range"]
            # This is the flexibility of the control point, by default the same as the
            # market commodity but not necessarily
            ct_flex = output_info.get("control_flexibility", flex)
            ct_flex, flex = self.set_control(ct_flex, flex)
            self.ct_flexibility = ct_flex
            fallback = output_info.get("fallback", mean(ct_flex))
            # TODO:  Use condition to determine multiple output scenario
            condition = output_info.get("condition", True)

            try:
                value = self.vip.rpc.call(actuator, 'get_point',
                                          topic).get(timeout=10)
            except (RemoteError, gevent.Timeout, errors.VIPError) as ex:
                _log.warning("Failed to get {} - ex: {}".format(
                    topic, str(ex)))
                value = fallback
            if isinstance(release, str) and release.lower(
            ) == "default" and value is not None:
                release_value = value
            else:
                release_value = None
            off_setpoint = output_info.get("off_setpoint", value)
            self.outputs[mapped] = {
                "point": point,
                "topic": topic,
                "actuator": actuator,
                "release": release_value,
                "value": value,
                "off_setpoint": off_setpoint,
                "offset": offset,
                "flex": flex,
                "ct_flex": ct_flex,
                "condition": condition
            }
Ejemplo n.º 40
0
    def high_stcpr_dx(self, dx_result, avg_stcpr_stpt):
        """Diagnostic to identify and correct high duct static pressure

        (correction by modifying duct static pressure set point)
        """
        zn_dmpr = deepcopy(self.zn_dmpr_arr)
        zn_dmpr.sort(reverse=True)
        zn_dmpr = zn_dmpr[:int(math.ceil(len(self.zn_dmpr_arr)*0.5))if len(self.zn_dmpr_arr) != 1 else 1]
        avg_zone_damper = mean(zn_dmpr)

        if avg_zone_damper <= self.hdzn_dmpr_thr:
            if avg_stcpr_stpt is None:
                # Create diagnostic message for fault
                # when duct static pressure set point
                # is not available.
                msg = ('The duct static pressure set point has been '
                       'detected to be too high but but duct static '
                       'pressure set point data is not available.'
                       'temperature set point data is not available.')
                dx_msg = 24.1
            elif self.auto_correct_flag:
                auto_correct_stcpr_stpt = avg_stcpr_stpt - self.stcpr_retuning
                if auto_correct_stcpr_stpt >= self.min_stcpr_stpt:
                    dx_result.command(self.stcpr_stpt_cname, auto_correct_stcpr_stpt)
                    new_stcpr_stpt = '%s' % float('%.2g' % auto_correct_stcpr_stpt)
                    new_stcpr_stpt = new_stcpr_stpt + ' in. w.g.'
                    msg = ('The duct static pressure was detected to be '
                           'too high. The duct static pressure set point '
                           'has been reduced to: {}'
                           .format(new_stcpr_stpt))
                    dx_msg = 21.1
                else:
                    dx_result.command(self.stcpr_stpt_cname, self.min_stcpr_stpt)
                    new_stcpr_stpt = '%s' % float('%.2g' % self.min_stcpr_stpt)
                    new_stcpr_stpt = new_stcpr_stpt + ' in. w.g.'
                    msg = ('The duct static pressure set point is at the '
                           'minimum value configured by the building '
                           'operator: {})'.format(new_stcpr_stpt))
                    dx_msg = 22.1
            else:
                msg = ('Duct static pressure set point was detected to be '
                       'too high but auto-correction is not enabled.')
                dx_msg = 23.1
        else:
            msg = ('No re-tuning opportunity was detected during the high duct '
                   'static pressure diagnostic.')
            dx_msg = 20.0

        self.dx_table.update({DUCT_STC_RCX2 + DX: dx_msg})
        dx_result.log(msg, logging.INFO)
        return dx_result
    def econ_alg(self, dx_result, oat, mat, oad, cur_time):
        """
        Check diagnostic prerequisites and manage data arrays.
        :param dx_result:
        :param oat:
        :param mat:
        :param oad:
        :param cur_time:
        :return:
        """
        if oad > self.oad_temperature_threshold:
            if self.steady_state is None:
                self.steady_state = cur_time
            elif cur_time - self.steady_state >= self.econ_time_check:
                self.oat_values.append(oat)
                self.mat_values.append(mat)
                self.timestamp.append(cur_time)
        else:
            self.steady_state = None

        elapsed_time = self.timestamp[-1] - self.timestamp[0] if self.timestamp else td(minutes=0)

        if elapsed_time >= self.data_window:
            if len(self.oat_values) > self.no_required_data:
                mat_oat_diff_list = [abs(x - y) for x, y in zip(self.oat_values, self.mat_values)]
                open_damper_check = mean(mat_oat_diff_list)
                table_key = create_table_key(self.analysis, self.timestamp[-1])
                diagnostic_msg = {}
                for sensitivity, threshold in self.oat_mat_check.items():
                    if open_damper_check > threshold:
                        msg = "{} - {}: OAT and MAT are inconsistent when OAD is near 100%".format(ECON1, sensitivity)
                        result = 0.1
                    else:
                        msg = "{} - {}: OAT and MAT are consistent when OAD is near 100%".format(ECON1, sensitivity)
                        result = 0.0
                    diagnostic_msg.update({sensitivity: result})

                dx_result.log(msg)
                dx_table = {ECON1 + DX: diagnostic_msg}
                dx_result.insert_table_row(table_key, dx_table)
            self.clear_data()
        return dx_result
Ejemplo n.º 42
0
        def publish_to_historian(self, to_publish_list):

            for item in to_publish_list:
                if self._gather_timing_data:
                    turnaround_time = add_timing_data_to_header(item["headers"],
                                                                self.core.agent_uuid or self.core.identity,
                                                                "published")
                    self._turnaround_times.append(turnaround_time)
                    if len(self._turnaround_times) > 10000:
                        # Test is now over. Button it up and shutdown.
                        mean = math_utils.mean(self._turnaround_times)
                        stdev = math_utils.stdev(self._turnaround_times)
                        _log.info("Mean time from collection to publish: " + str(mean))
                        _log.info("Std dev time from collection to publish: " + str(stdev))
                        self._turnaround_times = []
                #_log.debug("publishing {}".format(item))

            _log.debug("recieved {} items to publish"
                       .format(len(to_publish_list)))

            self.report_all_handled()
Ejemplo n.º 43
0
    def calculate_average_power(self, current_power, current_time):
        """
        Calculate the average power.
        :param current_power:
        :param current_time:
        :return:
        """
        if self.simulation_running:
            self.check_schedule(current_time)

        if self.bldg_power:
            current_average_window = self.bldg_power[-1][0] - self.bldg_power[0][0] + td(seconds=15)
        else:
            current_average_window = td(minutes=0)

        if current_average_window >= self.average_building_power_window and current_power > 0:
            self.bldg_power.append((current_time, current_power))
            self.bldg_power.pop(0)
        elif current_power > 0:
            self.bldg_power.append((current_time, current_power))

        smoothing_constant = 2.0/(len(self.bldg_power) + 1.0)*2.0 if self.bldg_power else 1.0
        smoothing_constant = smoothing_constant if smoothing_constant <= 1.0 else 1.0
        power_sort = list(self.bldg_power)
        power_sort.sort(reverse=True)
        average_power = 0

        for n in xrange(len(self.bldg_power)):
            average_power += power_sort[n][1] * smoothing_constant * (1.0 - smoothing_constant) ** n

        norm_list = [float(i[1]) for i in self.bldg_power]
        normal_average_power = mean(norm_list) if norm_list else 0.0

        _log.debug("Reported time: {} - instantaneous power: {}".format(current_time, current_power))
        _log.debug(
            "{} minute average power: {} - exponential power: {}".format(current_average_window, normal_average_power,
                                                                         average_power))
        return average_power, normal_average_power, current_average_window
    def insufficient_oa(self, dx_result, table_key):
        """
        If the detected problems(s) are
        consistent generate a fault message(s).
        :param dx_result:
        :param cur_time:
        :param table_key:
        :return:
        """
        oaf = [(m - r) / (o - r) for o, r, m in zip(self.oat_values, self.rat_values, self.mat_values)]
        avg_oaf = mean(oaf) * 100.0
        diagnostic_msg = {}

        if avg_oaf < 0 or avg_oaf > 125.0:
            msg = ("{}: Inconclusive result, the OAF calculation led to an "
                   "unexpected value: {}".format(ECON5, avg_oaf))
            # color_code = "GREY"
            dx_table = {ECON5 + DX: self.invalid_oaf_dict}
            dx_result.log(msg)
            dx_result.insert_table_row(table_key, dx_table)
            self.clear_data()
            return dx_result

        avg_oaf = max(0.0, min(100.0, avg_oaf))
        for sensitivity, threshold in self.ventilation_oaf_threshold.items():
            if self.desired_oaf - avg_oaf > threshold:
                msg = "{}: Insufficient OA is being provided for ventilation - sensitivity: {}".format(ECON5, sensitivity)
                result = 43.1
            else:
                msg = "{}: The calculated OAF was within acceptable limits - sensitivity: {}".format(ECON5, sensitivity)
                result = 40.0
            dx_result.log(msg)
            diagnostic_msg.update({sensitivity: result})

        dx_table = {ECON5 + DX: diagnostic_msg}
        dx_result.insert_table_row(table_key, dx_table)
        self.clear_data()
        return dx_result
Ejemplo n.º 45
0
def setpoint_control_check(set_point_array, point_array, setpoint_deviation_threshold, dx_name, dx_offset, dx_result):
    """
    Verify that point if tracking with set point - identify potential control or sensor problems.
    :param set_point_array:
    :param point_array:
    :param allowable_deviation:
    :param dx_name:
    :param dx_offset:
    :param dx_result:
    :return:
    """
    avg_set_point = None
    diagnostic_msg = {}
    for key, threshold in setpoint_deviation_threshold.items():
        if set_point_array:
            avg_set_point = sum(set_point_array)/len(set_point_array)
            zipper = (set_point_array, point_array)
            set_point_tracking = [abs(x - y) for x, y in zip(*zipper)]
            set_point_tracking = mean(set_point_tracking)/avg_set_point*100.

            if set_point_tracking > threshold:
                # color_code = 'red'
                msg = '{} - {}: point deviating significantly from set point.'.format(key, dx_name)
                result = 1.1 + dx_offset
            else:
                # color_code = 'green'
                msg = " {} - No problem detected for {} set".format(key, dx_name)
                result = 0.0 + dx_offset
        else:
            # color_code = 'grey'
            msg = "{} - {} set point data is not available.".format(key, dx_name)
            result = 2.2 + dx_offset
        dx_result.log(msg)
        diagnostic_msg.update({key: result})
        dx_table = {dx_name + DX: diagnostic_msg}

    return avg_set_point, dx_table, dx_result
    def run(self, cur_time, points):
        device_dict = {}
        dx_result = Results()
        fan_status_data = []
        supply_fan_off = False
        low_dx_cond = False
        high_dx_cond = False

        for key, value in points.items():
            point_device = [_name.lower() for _name in key.split('&')]
            if point_device[0] not in device_dict:
                device_dict[point_device[0]] = [(point_device[1], value)]
            else:
                device_dict[point_device[0]].append((point_device[1], value))

        if self.fan_status_name in device_dict:
            fan_status = device_dict[self.fan_status_name]
            fan_status = [point[1] for point in fan_status]
            fan_status = [status for status in fan_status if status is not None]
            if fan_status_data:
                fan_status_data.append(min(fan_status))
                if not int(fan_status_data[0]):
                    supply_fan_off = True
                    self.warm_up_flag = True

        if self.fansp_name in device_dict:
            fan_speed = device_dict[self.fansp_name]
            fan_speed = mean([point[1] for point in fan_speed])
            if self.fan_status_name is None:
                if not int(fan_speed):
                    supply_fan_off = True
                    self.warm_up_flag = True
                fan_status_data.append(bool(int(fan_speed)))

            if fan_speed > self.high_sf_threshold:
                low_dx_cond = True
            elif fan_speed < self.low_sf_threshold:
                high_dx_cond = True

        stc_pr_data = []
        stcpr_sp_data = []
        zn_dmpr_data = []
        satemp_data = []
        rht_data = []
        sat_stpt_data = []
        validate = {}
        sched_val = {}

        def validate_builder(value_tuple, point_name):
            value_list = []
            for item in value_tuple:
                tag = item[0] + '/' + point_name
                validate.update({tag: item[1]})
                value_list.append(item[1])
            return value_list

        for key, value in device_dict.items():
            data_name = key
            if value is None:
                continue
            if data_name == self.duct_stp_stpt_name:
                stcpr_sp_data = validate_builder(value, data_name)
                sched_val.update(validate)
            elif data_name == self.sat_stpt_name:
                sat_stpt_data = validate_builder(value, data_name)
                sched_val.update(validate)
            elif data_name == self.duct_stp_name:
                sched_val.update(validate)
                stc_pr_data = validate_builder(value, data_name)
                sched_val.update(validate)
            elif data_name == self.sa_temp_name:
                satemp_data = validate_builder(value, data_name)
                sched_val.update(validate)
            elif data_name == self.zone_reheat_name:
                rht_data = validate_builder(value, data_name)
            elif data_name == self.zone_damper_name:
                zn_dmpr_data = validate_builder(value, data_name)

        missing_data = []
        if not satemp_data:
            missing_data.append(self.sa_temp_name)
        if not rht_data:
            missing_data.append(self.zone_reheat_name)
        if not sat_stpt_data:
            dx_result.log('Supply-air temperature set point data is '
                          'missing. This will limit the effectiveness of '
                          'the supply-air temperature diagnostics.')
        if not stc_pr_data:
            missing_data.append(self.duct_stp_name)
        if not stcpr_sp_data:
            dx_result.log('Duct static pressure set point data is '
                          'missing. This will limit the effectiveness of '
                          'the duct static pressure diagnostics.')
        if not zn_dmpr_data:
            missing_data.append(self.zone_damper_name)
        if not fan_status:
            missing_data.append(self.fan_status_name)
        if missing_data:
            raise Exception('Missing required data: {}'.format(missing_data))
            return dx_result
        dx_result = (
            self.sched_occ_dx.sched_rcx_alg(cur_time, stc_pr_data,
                                            stcpr_sp_data, sat_stpt_data,
                                            fan_status, dx_result,
                                            sched_val))
        if supply_fan_off:
            dx_result.log('Supply fan is off. Data will not be used for '
                          'retuning diagnostics.')
            return dx_result
        if self.warm_up_flag:
            self.warm_up_flag = False
            self.warm_up_start = cur_time
            return dx_result
        time_check = td(minutes=self.warm_up_time)
        if (self.warm_up_start is not None and
                (cur_time - self.warm_up_start) < time_check):
            dx_result.log('Unit is in warm-up. Data will not be analyzed.')
            return dx_result
        dx_result = (
            self.static_dx.duct_static(cur_time, stcpr_sp_data, stc_pr_data,
                                       zn_dmpr_data, low_dx_cond, high_dx_cond,
                                       dx_result, validate))
        dx_result = (
            self.sat_dx.sat_rcx(cur_time, satemp_data, sat_stpt_data, rht_data,
                                zn_dmpr_data, dx_result, validate))
        return dx_result
    def unocc_fan_operation(self, dx_result):
        """
        AIRCx to determine if AHU is operating excessively in unoccupied mode.
        :param dx_result:
        :return:
        """
        avg_duct_stcpr = 0
        percent_on = 0
        fan_status_on = [(fan[0].hour, fan[1]) for fan in self.fan_status_array if int(fan[1]) == 1]
        fanstat = [(fan[0].hour, fan[1]) for fan in self.fan_status_array]
        hourly_counter = []
        thresholds = zip(self.unocc_time_thr.items(), self.unocc_stcpr_thr.items())
        diagnostic_msg = {}

        for counter in range(24):
            fan_on_count = [fan_status_time[1] for fan_status_time in fan_status_on if fan_status_time[0] == counter]
            fan_count = [fan_status_time[1] for fan_status_time in fanstat if fan_status_time[0] == counter]
            if len(fan_count):
                hourly_counter.append(fan_on_count.count(1)/len(fan_count)*100)
            else:
                hourly_counter.append(0)

        if self.schedule_time_array:
            if self.fan_status_array:
                percent_on = (len(fan_status_on)/len(self.fan_status_array)) * 100.0
            if self.stcpr_array:
                avg_duct_stcpr = mean(self.stcpr_array)

            for (key, unocc_time_thr), (key2, unocc_stcpr_thr) in thresholds:
                if percent_on > unocc_time_thr:
                    msg = "{} - Supply fan is on during unoccupied times".format(key)
                    result = 63.1
                else:
                    if avg_duct_stcpr < unocc_stcpr_thr:
                        msg = "{} - No problems detected for schedule diagnostic.".format(key)
                        result = 60.0
                    else:
                        msg = ("{} - Fan status show the fan is off but the duct static "
                               "pressure is high, check the functionality of the "
                               "pressure sensor.".format(key))
                        result = 64.2
                diagnostic_msg.update({key: result})
                dx_result.log(msg)
        else:
            msg = "ALL - No problems detected for schedule diagnostic."
            dx_result.log(msg)
            diagnostic_msg = {"low": 60.0, "normal": 60.0, "high": 60.0}

        if 64.2 not in diagnostic_msg.values():
            for _hour in range(24):
                diagnostic_msg = {}
                utc_offset = self.timestamp_array[0].isoformat()[-6:]
                push_time = self.timestamp_array[0].date()
                push_time = datetime.combine(push_time, datetime.min.time())
                push_time = push_time.replace(hour=_hour)
                for key, unocc_time_thr in self.unocc_time_thr.items():
                    diagnostic_msg.update({key: 60.0})
                    if hourly_counter[_hour] > unocc_time_thr:
                        diagnostic_msg.update({key: 63.1})
                dx_table = {SCHED_RCX + DX:  diagnostic_msg}
                table_key = create_table_key(self.analysis, push_time) + utc_offset
                dx_result.insert_table_row(table_key, dx_table)
        else:
            push_time = self.timestamp_array[0].date()
            table_key = create_table_key(self.analysis, push_time)
            dx_result.insert_table_row(table_key, {SCHED_RCX + DX:  diagnostic_msg})

        return dx_result
Ejemplo n.º 48
0
    def sat_rcx(self, current_time, sat_data, sat_stpt_data,
                zone_rht_data, zone_dmpr_data, dx_result, validate):
        """Manages supply-air diagnostic data sets.

        Args:
            current_time (datetime): current timestamp for trend data.
            sat_data (lst of floats): supply-air temperature measurement for
                AHU.
            sat_stpt_data (List[floats]): supply-air temperature set point
                data for AHU.
            zone_rht_data (List[floats]): reheat command for terminal boxes
                served by AHU.
            zone_dmpr_data (List[floats]): damper command for terminal boxes
                served by AHU.
            dx_result (Object): Object for interacting with platform and devices.

        Returns:
            Results object (dx_result) to Application.

        """
        if check_date(current_time, self.timestamp_arr):
            self.reinitialize()
            return dx_result

        file_key = create_table_key(VALIDATE_FILE_TOKEN, current_time)
        tot_rht = sum(1 if val > self.rht_on_thr else 0 for val in zone_rht_data)
        count_rht = len(zone_rht_data)
        tot_dmpr = sum(1 if val > self.high_dmpr_thr else 0 for val in zone_dmpr_data)
        count_damper = len(zone_dmpr_data)

        data = validation_builder(validate, SA_VALIDATE, DATA)
        run_status = check_run_status(self.timestamp_arr, current_time, self.no_req_data)

        if run_status is None:
            dx_result.log('Current analysis data set has insufficient data '
                          'to produce a valid diagnostic result.')
            self.reinitialize()
            return dx_result

        if run_status:
            self.table_key = create_table_key(self.analysis, self.timestamp_arr[-1])
            avg_sat_stpt, dx_table = setpoint_control_check(self.sat_stpt_arr,
                                                            self.satemp_arr,
                                                            self.stpt_allowable_dev,
                                                            SA_TEMP_RCX,
                                                            DX, SAT_NAME,
                                                            self.token_offset)
            self.dx_table.update(dx_table)
            dx_result = self.low_sat(dx_result, avg_sat_stpt)
            dx_result = self.high_sat(dx_result, avg_sat_stpt)

            dx_result.insert_table_row(self.table_key, self.dx_table)
            self.data.update({SA_VALIDATE + DATA + ST: 1})
            dx_result.insert_table_row(self.file_key, self.data)
            self.reinitialize()

        self.satemp_arr.append(mean(sat_data))
        self.rht_arr.append(mean(zone_rht_data))
        self.sat_stpt_arr.append(mean(sat_stpt_data))
        self.percent_rht.append(tot_rht/count_rht)
        self.percent_dmpr.append(tot_dmpr/count_damper)
        self.timestamp_arr.append(current_time)
        if self.data:
            self.data.update({SA_VALIDATE + DATA + ST: 0})
            dx_result.insert_table_row(self.file_key, self.data)
        self.data = data
        self.file_key = file_key
        return dx_result
    def excess_oa(self, dx_result, table_key):
        """
        If the detected problems(s) are consistent generate a fault message(s).
        :param dx_result:
        :param table_key:
        :return:
        """
        oaf = [(m - r) / (o - r) for o, r, m in zip(self.oat_values, self.rat_values, self.mat_values)]
        avg_oaf = mean(oaf) * 100.0
        avg_damper = mean(self.oad_values)
        desired_oaf = self.desired_oaf / 100.0
        diagnostic_msg = {}
        energy_impact = {}

        if avg_oaf < 0 or avg_oaf > 125.0:
            msg = ("{}: Inconclusive result, unexpected OAF value: {}".format(ECON4, avg_oaf))
            # color_code = "GREY"
            dx_table = {ECON4 + DX: self.invalid_oaf_dict}
            dx_result.log(msg)
            dx_result.insert_table_row(table_key, dx_table)
            self.clear_data()
            return dx_result

        avg_oaf = max(0.0, min(100.0, avg_oaf))
        thresholds = zip(self.excess_damper_threshold.items(), self.excess_oaf_threshold.items())
        for (key, damper_thr), (key2, oaf_thr) in thresholds:
            if avg_damper > damper_thr:
                msg = "{}: The OAD should be at the minimum but is significantly higher.".format(ECON4)
                # color_code = "RED"
                result = 32.1
                if avg_oaf - self.desired_oaf > oaf_thr:
                    msg = ("{}: The OAD should be at the minimum for ventilation "
                           "but is significantly above that value. Excess outdoor air is "
                           "being provided; This could significantly increase "
                           "heating and cooling costs".format(ECON4))
                    energy = self.energy_impact_calculation(desired_oaf)
                    result = 34.1
            elif avg_oaf - self.desired_oaf > oaf_thr:
                msg = ("{}: Excess outdoor air is being provided, this could "
                       "increase heating and cooling energy consumption.".format(ECON4))
                # color_code = "RED"
                energy = self.energy_impact_calculation(desired_oaf)
                result = 33.1
            else:
                # color_code = "GREEN"
                msg = ("{}: The calculated OAF is within configured limits.".format(ECON4))
                result = 30.0
                energy = 0.0

            dx_result.log(msg)
            energy_impact.update({key: energy})
            diagnostic_msg.update({key: result})

        dx_table = {
            ECON4 + DX: diagnostic_msg,
            ECON4 + EI: energy_impact
        }

        dx_result.insert_table_row(table_key, dx_table)
        self.clear_data()
        return dx_result