Пример #1
0
def hvac_control(cfg,
                 advise_cfg,
                 tstats,
                 client,
                 thermal_model,
                 zone,
                 building,
                 now,
                 debug=False,
                 simulate=False):
    """
    
    :param cfg:
    :param advise_cfg:
    :param tstats:
    :param client:
    :param thermal_model:
    :param zone:
    :param now: datetime object in UTC which tells the control what now is.
    :param debug: wether to actuate the tstat.
    :param simulate: boolean whether to run the control as a simulation or to actually actuate.
    :return: boolean, dict. Success Boolean indicates whether writing action has succeeded. Dictionary {cooling_setpoint: float,
    heating_setpoint: float, override: bool, mode: int} and None if success boolean is flase.
    """

    try:

        zone_temperatures = {
            dict_zone: dict_tstat.temperature
            for dict_zone, dict_tstat in tstats.items()
        }
        tstat = tstats[zone]
        tstat_temperature = zone_temperatures[
            zone]  # to make sure we get all temperatures at the same time

        # get datamanagers
        dataManager = DataManager(cfg, advise_cfg, client, zone, now=now)
        thermal_data_manager = ThermalDataManager(cfg, client)

        safety_constraints = dataManager.safety_constraints()
        prices = dataManager.prices()
        building_setpoints = dataManager.building_setpoints()
        if simulate or not advise_cfg["Advise"]["Occupancy_Sensors"]:
            occ_predictions = dataManager.preprocess_occ_cfg()
        else:
            occ_predictions = dataManager.preprocess_occ_mdal()

        if not simulate:
            # TODO FIX THE UPDATE STEP. PUT THIS OUTSIDE OF HVAC CONTROL.
            # NOTE: call update before setWeatherPredictions and set_temperatures
            thermal_model.update(zone_temperatures,
                                 interval=cfg["Interval_Length"])

        # need to set weather predictions for every loop and set current zone temperatures.
        thermal_model.set_temperatures(zone_temperatures)

        # ===== Future and past outside temperature combine =====
        # Get correct weather predictions.
        # we might have that the given now is before the actual current time
        # hence need to get historic data and combine with weather predictions.

        # finding out where the historic/future intervals start and end.
        utc_now = utils.get_utc_now()

        # If simulation window is partially in the past and in the future
        if utils.in_between_datetime(
                utc_now, now, now + datetime.timedelta(
                    hours=advise_cfg["Advise"]["MPCPredictiveHorizon"])):
            historic_start = now
            historic_end = utc_now
            future_start = utc_now
            future_end = now + datetime.timedelta(
                hours=advise_cfg["Advise"]["MPCPredictiveHorizon"])

        # If simulation window is fully in the future
        elif now >= utc_now:
            historic_start = None
            historic_end = None
            future_start = now
            future_end = now + datetime.timedelta(
                hours=advise_cfg["Advise"]["MPCPredictiveHorizon"])

        # If simulation window is fully in the past
        else:
            historic_start = now
            historic_end = now + datetime.timedelta(
                hours=advise_cfg["Advise"]["MPCPredictiveHorizon"])
            future_start = None
            future_end = None

        # Populating the outside_temperatures dictionary for MPC use. Ouput is in cfg timezone.
        outside_temperatures = {}
        if future_start is not None:
            # TODO implement end for weather_fetch
            future_weather = dataManager.weather_fetch(start=future_start)
            outside_temperatures = future_weather

        # Combining historic data with outside_temperatures correctly if exists.
        if historic_start is not None:
            historic_weather = thermal_data_manager._get_outside_data(
                historic_start, historic_end, inclusive=True)
            historic_weather = thermal_data_manager._preprocess_outside_data(
                historic_weather.values())

            # Down sample the historic weather to hourly entries, and take the mean for each hour.
            historic_weather = historic_weather.groupby(
                [pd.Grouper(freq="1H")])["t_out"].mean()

            # Convert historic_weather to cfg timezone.
            historic_weather.index = historic_weather.index.tz_convert(
                tz=cfg["Pytz_Timezone"])

            # Popluate the outside_temperature array. If we have the simulation time in the past and future then
            # we will take a weighted averege of the historic and future temperatures in the hour in which
            # historic_end and future_start happen.
            for row in historic_weather.iteritems():
                row_time, t_out = row[0], row[1]

                # taking a weighted average of the past and future outside temperature since for now
                # we only have one outside temperature per hour.
                if row_time.hour in outside_temperatures and \
                                row_time.hour == historic_end.astimezone(tz=pytz.timezone(cfg["Pytz_Timezone"])).hour:

                    future_t_out = outside_temperatures[row_time.hour]

                    # Checking if start and end are in the same hour, because then we have to weigh the temperature by
                    # less.
                    if historic_end.astimezone(tz=pytz.timezone(cfg["Pytz_Timezone"])).hour ==\
                            historic_start.astimezone(tz=pytz.timezone(cfg["Pytz_Timezone"])).hour:
                        historic_weight = (historic_end -
                                           historic_start).seconds // 60
                    else:
                        historic_weight = historic_end.astimezone(
                            tz=pytz.timezone(cfg["Pytz_Timezone"])).minute
                    if future_start.astimezone(tz=pytz.timezone(cfg["Pytz_Timezone"])).hour ==\
                            future_end.astimezone(tz=pytz.timezone(cfg["Pytz_Timezone"])).hour:
                        future_weight = (future_end -
                                         future_start).seconds // 60
                    else:
                        # the remainder of the hour.
                        future_weight = 60 - future_start.astimezone(
                            tz=pytz.timezone(cfg["Pytz_Timezone"])).minute
                    # Normalize
                    total_weight = future_weight + historic_weight
                    future_weight /= float(total_weight)
                    historic_weight /= float(total_weight)

                    outside_temperatures[row_time.hour] = future_weight * future_t_out + \
                                                          historic_weight * float(t_out)

                else:
                    outside_temperatures[row_time.hour] = float(t_out)

        # setting outside temperature data for the thermal model.
        thermal_model.set_outside_temperature(outside_temperatures)

        # ===== END: Future and past outside temperature combine =====

        if (cfg["Pricing"]["DR"] and utils.in_between(
                now.astimezone(tz=pytz.timezone(cfg["Pytz_Timezone"])).time(),
                utils.get_time_datetime(cfg["Pricing"]["DR_Start"]),
                utils.get_time_datetime(cfg["Pricing"]["DR_Finish"]))):  # \
            # or now.weekday() == 4:  # TODO REMOVE ALWAYS HAVING DR ON FRIDAY WHEN DR SUBSCRIBE IS IMPLEMENTED
            DR = True
        else:
            DR = False

        adv_start = time.time()
        adv = Advise(
            [
                zone
            ],  # array because we might use more than one zone. Multiclass approach.
            now.astimezone(tz=pytz.timezone(cfg["Pytz_Timezone"])),
            occ_predictions,
            [tstat_temperature],
            thermal_model,
            prices,
            advise_cfg["Advise"]["General_Lambda"],
            advise_cfg["Advise"]["DR_Lambda"],
            DR,
            cfg["Interval_Length"],
            advise_cfg["Advise"]["MPCPredictiveHorizon"],
            advise_cfg["Advise"]["Heating_Consumption"],
            advise_cfg["Advise"]["Cooling_Consumption"],
            advise_cfg["Advise"]["Ventilation_Consumption"],
            advise_cfg["Advise"]["Thermal_Precision"],
            advise_cfg["Advise"]["Occupancy_Obs_Len_Addition"],
            building_setpoints,
            advise_cfg["Advise"]["Occupancy_Sensors"]
            if not simulate else False,
            # TODO Only using config file occupancy for now.
            safety_constraints)

        action = adv.advise()
        adv_end = time.time()

    except Exception:
        print("ERROR: For zone %s." % zone)
        print(traceback.format_exc())
        # TODO Find a better way for exceptions
        return False

    # action "0" is Do Nothing, action "1" is Heating, action "2" is Cooling
    if action == "0":
        heating_setpoint = tstat_temperature - advise_cfg["Advise"][
            "Minimum_Comfortband_Height"] / 2.
        cooling_setpoint = tstat_temperature + advise_cfg["Advise"][
            "Minimum_Comfortband_Height"] / 2.

        if heating_setpoint < safety_constraints[0][0]:
            heating_setpoint = safety_constraints[0][0]

            if (cooling_setpoint - heating_setpoint
                ) < advise_cfg["Advise"]["Minimum_Comfortband_Height"]:
                cooling_setpoint = min(
                    safety_constraints[0][1], heating_setpoint +
                    advise_cfg["Advise"]["Minimum_Comfortband_Height"])

        elif cooling_setpoint > safety_constraints[0][1]:
            cooling_setpoint = safety_constraints[0][1]

            if (cooling_setpoint - heating_setpoint
                ) < advise_cfg["Advise"]["Minimum_Comfortband_Height"]:
                heating_setpoint = max(
                    safety_constraints[0][0], cooling_setpoint -
                    advise_cfg["Advise"]["Minimum_Comfortband_Height"])

        # round to integers since the thermostats round internally.
        heating_setpoint = math.floor(heating_setpoint)
        cooling_setpoint = math.ceil(cooling_setpoint)

        p = {
            "override": True,
            "heating_setpoint": heating_setpoint,
            "cooling_setpoint": cooling_setpoint,
            "mode": 3
        }
        print "Doing nothing"

    # TODO Rethink how we set setpoints for heating and cooling and for DR events.
    # heating
    elif action == "1":
        heating_setpoint = tstat_temperature + 2 * advise_cfg["Advise"][
            "Hysterisis"]
        cooling_setpoint = heating_setpoint + advise_cfg["Advise"][
            "Minimum_Comfortband_Height"]

        if cooling_setpoint > safety_constraints[0][1]:
            cooling_setpoint = safety_constraints[0][1]

            # making sure we are in the comfortband
            if (cooling_setpoint - heating_setpoint
                ) < advise_cfg["Advise"]["Minimum_Comfortband_Height"]:
                heating_setpoint = max(
                    safety_constraints[0][0], cooling_setpoint -
                    advise_cfg["Advise"]["Minimum_Comfortband_Height"])

        # round to integers since the thermostats round internally.
        heating_setpoint = math.ceil(heating_setpoint)
        cooling_setpoint = math.ceil(cooling_setpoint)

        p = {
            "override": True,
            "heating_setpoint": heating_setpoint,
            "cooling_setpoint": cooling_setpoint,
            "mode": 3
        }
        print "Heating"

    # cooling
    elif action == "2":
        cooling_setpoint = tstat_temperature - 2 * advise_cfg["Advise"][
            "Hysterisis"]
        heating_setpoint = cooling_setpoint - advise_cfg["Advise"][
            "Minimum_Comfortband_Height"]

        if heating_setpoint < safety_constraints[0][0]:
            heating_setpoint = safety_constraints[0][0]

            # making sure we are in the comfortband
            if (cooling_setpoint - heating_setpoint
                ) < advise_cfg["Advise"]["Minimum_Comfortband_Height"]:
                cooling_setpoint = min(
                    safety_constraints[0][1], heating_setpoint +
                    advise_cfg["Advise"]["Minimum_Comfortband_Height"])

        # round to integers since the thermostats round internally.
        heating_setpoint = math.floor(heating_setpoint)
        cooling_setpoint = math.floor(cooling_setpoint)

        p = {
            "override": True,
            "heating_setpoint": heating_setpoint,
            "cooling_setpoint": cooling_setpoint,
            "mode": 3
        }
        print "Cooling"
    else:
        print "Problem with action."
        return False, None

    print("Zone: " + zone + ", action: " + str(p))

    # Plot the MPC graph.
    if advise_cfg["Advise"]["Print_Graph"]:
        adv.g_plot(zone)

    # Log the information related to the current MPC
    Debugger.debug_print(now,
                         building,
                         zone,
                         adv,
                         safety_constraints,
                         prices,
                         building_setpoints,
                         adv_end - adv_start,
                         file=True)

    # try to commit the changes to the thermostat, if it doesnt work 10 times in a row ignore and try again later
    for i in range(advise_cfg["Advise"]["Thermostat_Write_Tries"]):
        try:
            if not debug and not simulate:
                tstat.write(p)
            # Setting last action in the thermal model after we have succeeded in writing to the tstat.
            thermal_model.set_last_action(int(action))
            break
        except:
            if i == advise_cfg["Advise"]["Thermostat_Write_Tries"] - 1:
                e = sys.exc_info()[0]
                print e
                return False, None
            continue

    return True, p
Пример #2
0
				};""" % cfg["Building"]
    import pickle

    with open("../Thermal Data/ciee_thermal_data_demo", "r") as f:
        thermal_data = pickle.load(f)
    dm = DataManager(cfg, advise_cfg, c, ZONE)
    tstat_query_data = hc.do_query(q)['Rows']
    tstats = {tstat["?zone"]: Thermostat(c, tstat["?uri"]) for tstat in tstat_query_data}

    # TODO INTERVAL SHOULD NOT BE IN config_file.yml, THERE SHOULD BE A DIFFERENT INTERVAL FOR EACH ZONE
    from ThermalModel import *

    thermal_model = MPCThermalModel(thermal_data, interval_length=cfg["Interval_Length"])
    thermal_model.setZoneTemperaturesAndFit(
        {dict_zone: dict_tstat.temperature for dict_zone, dict_tstat in tstats.items()}, dt=cfg["Interval_Length"])
    thermal_model.setWeahterPredictions(dm.weather_fetch())

    adv = Advise(["HVAC_Zone_Centralzone"],
                 datetime.datetime.utcnow().replace(tzinfo=pytz.utc).astimezone(
                     tz=pytz.timezone("America/Los_Angeles")),
                 dm.preprocess_occ(),
                 [80],  # [{dict_zone: dict_tstat.temperature for dict_zone, dict_tstat in tstats.items()}[ZONE]],
                 thermal_model,
                 dm.prices(),
                 0.995, 0.995, False, 15, 2, True, 0.075, 1.25, 0.01, 400., 4,
                 dm.building_setpoints(),
                 advise_cfg["Advise"]["Occupancy_Sensors"],
                 dm.safety_constraints())

    print adv.advise()
Пример #3
0
def hvac_control(cfg, advise_cfg, tstats, client, thermal_model, zone):
    """
    
    :param cfg: 
    :param advise_cfg: 
    :param tstats: 
    :param client: 
    :param thermal_model: 
    :param zone: 
    :return: boolean, dict. Success Boolean indicates whether writing action has succeeded. Dictionary {cooling_setpoint: float,
    heating_setpoint: float, override: bool, mode: int} and None if success boolean is flase. 
    """

    # now in UTC time.
    now = pytz.timezone("UTC").localize(datetime.datetime.utcnow())
    try:
        zone_temperatures = {
            dict_zone: dict_tstat.temperature
            for dict_zone, dict_tstat in tstats.items()
        }
        tstat = tstats[zone]
        tstat_temperature = zone_temperatures[
            zone]  # to make sure we get all temperatures at the same time

        dataManager = DataManager(cfg, advise_cfg, client, zone, now=now)
        safety_constraints = dataManager.safety_constraints()

        # need to set weather predictions for every loop and set current zone temperatures and fit the model given the new data (if possible).
        # NOTE: call setZoneTemperaturesAndFit before setWeahterPredictions
        # TODO Double Check if update to new thermal model was correct
        thermal_model.set_temperatures_and_fit(
            zone_temperatures,
            interval=cfg["Interval_Length"],
            now=now.astimezone(tz=pytz.timezone(cfg["Pytz_Timezone"])))

        # TODO Look at the weather_fetch function and make sure correct locks are implemented and we are getting the right data.
        weather = dataManager.weather_fetch()
        thermal_model.set_weather_predictions(weather)

        if (cfg["Pricing"]["DR"] and in_between(
                now.astimezone(tz=pytz.timezone(cfg["Pytz_Timezone"])).time(),
                getDatetime(cfg["Pricing"]["DR_Start"]),
                getDatetime(cfg["Pricing"]["DR_Finish"]))):  #\
            #or now.weekday() == 4:  # TODO REMOVE ALLWAYS HAVING DR ON FRIDAY WHEN DR SUBSCRIBE IS IMPLEMENTED
            DR = True
        else:
            DR = False

        adv = Advise(
            [
                zone
            ],  # array because we might use more than one zone. Multiclass approach.
            now.astimezone(tz=pytz.timezone(cfg["Pytz_Timezone"])),
            dataManager.preprocess_occ(),
            [tstat_temperature],
            thermal_model,
            dataManager.prices(),
            advise_cfg["Advise"]["General_Lambda"],
            advise_cfg["Advise"]["DR_Lambda"],
            DR,
            cfg["Interval_Length"],
            advise_cfg["Advise"]["MPCPredictiveHorizon"],
            advise_cfg["Advise"]["Print_Graph"],
            advise_cfg["Advise"]["Heating_Consumption"],
            advise_cfg["Advise"]["Cooling_Consumption"],
            advise_cfg["Advise"]["Ventilation_Consumption"],
            advise_cfg["Advise"]["Thermal_Precision"],
            advise_cfg["Advise"]["Occupancy_Obs_Len_Addition"],
            dataManager.building_setpoints(),
            advise_cfg["Advise"]["Occupancy_Sensors"],
            safety_constraints)

        action = adv.advise()

    except Exception:

        print(traceback.format_exc())
        # TODO Find a better way for exceptions
        return False

    # action "0" is Do Nothing, action "1" is Heating, action "2" is Cooling
    if action == "0":
        heating_setpoint = tstat_temperature - advise_cfg["Advise"][
            "Minimum_Comfortband_Height"] / 2.
        cooling_setpoint = tstat_temperature + advise_cfg["Advise"][
            "Minimum_Comfortband_Height"] / 2.

        if heating_setpoint < safety_constraints[0][0]:
            heating_setpoint = safety_constraints[0][0]

            if (cooling_setpoint - heating_setpoint
                ) < advise_cfg["Advise"]["Minimum_Comfortband_Height"]:
                cooling_setpoint = min(
                    safety_constraints[0][1], heating_setpoint +
                    advise_cfg["Advise"]["Minimum_Comfortband_Height"])

        elif cooling_setpoint > safety_constraints[0][1]:
            cooling_setpoint = safety_constraints[0][1]

            if (cooling_setpoint - heating_setpoint
                ) < advise_cfg["Advise"]["Minimum_Comfortband_Height"]:
                heating_setpoint = max(
                    safety_constraints[0][0], cooling_setpoint -
                    advise_cfg["Advise"]["Minimum_Comfortband_Height"])

        # round to integers since the thermostats round internally.
        heating_setpoint = math.floor(heating_setpoint)
        cooling_setpoint = math.ceil(cooling_setpoint)

        p = {
            "override": True,
            "heating_setpoint": heating_setpoint,
            "cooling_setpoint": cooling_setpoint,
            "mode": 3
        }
        print "Doing nothing"

    # TODO Rethink how we set setpoints for heating and cooling and for DR events.
    # heating
    elif action == "1":
        heating_setpoint = tstat_temperature + 2 * advise_cfg["Advise"][
            "Hysterisis"]
        cooling_setpoint = heating_setpoint + advise_cfg["Advise"][
            "Minimum_Comfortband_Height"]

        if cooling_setpoint > safety_constraints[0][1]:
            cooling_setpoint = safety_constraints[0][1]

            # making sure we are in the comfortband
            if (cooling_setpoint - heating_setpoint
                ) < advise_cfg["Advise"]["Minimum_Comfortband_Height"]:
                heating_setpoint = max(
                    safety_constraints[0][0], cooling_setpoint -
                    advise_cfg["Advise"]["Minimum_Comfortband_Height"])

        # round to integers since the thermostats round internally.
        heating_setpoint = math.ceil(heating_setpoint)
        cooling_setpoint = math.ceil(cooling_setpoint)

        p = {
            "override": True,
            "heating_setpoint": heating_setpoint,
            "cooling_setpoint": cooling_setpoint,
            "mode": 3
        }
        print "Heating"

    # cooling
    elif action == "2":
        cooling_setpoint = tstat_temperature - 2 * advise_cfg["Advise"][
            "Hysterisis"]
        heating_setpoint = cooling_setpoint - advise_cfg["Advise"][
            "Minimum_Comfortband_Height"]

        if heating_setpoint < safety_constraints[0][0]:
            heating_setpoint = safety_constraints[0][0]

            # making sure we are in the comfortband
            if (cooling_setpoint - heating_setpoint
                ) < advise_cfg["Advise"]["Minimum_Comfortband_Height"]:
                cooling_setpoint = min(
                    safety_constraints[0][1], heating_setpoint +
                    advise_cfg["Advise"]["Minimum_Comfortband_Height"])

        # round to integers since the thermostats round internally.
        heating_setpoint = math.floor(heating_setpoint)
        cooling_setpoint = math.floor(cooling_setpoint)

        p = {
            "override": True,
            "heating_setpoint": heating_setpoint,
            "cooling_setpoint": cooling_setpoint,
            "mode": 3
        }
        print "Cooling"
    else:
        print "Problem with action."
        return False, None

    print("Zone: " + zone + ", action: " + str(p))

    # try to commit the changes to the thermostat, if it doesnt work 10 times in a row ignore and try again later
    for i in range(advise_cfg["Advise"]["Thermostat_Write_Tries"]):
        try:
            # TODO Uncomment
            tstat.write(p)
            thermal_model.set_last_action(
                action
            )  # TODO Document that this needs to be set after we are sure that writing has succeeded.
            break
        except:
            if i == advise_cfg["Advise"]["Thermostat_Write_Tries"] - 1:
                e = sys.exc_info()[0]
                print e
                return False, None
            continue

    return True, p
Пример #4
0
    zone_thermal_models = {
        thermal_zone: MPCThermalModel(zone,
                                      zone_data[zone_data['dt'] == 5],
                                      interval_length=cfg["Interval_Length"],
                                      thermal_precision=0.05)
        for thermal_zone, zone_data in all_data.items()
    }
    print("Trained Thermal Model")
    # --------------------------------------

    advise_cfg = utils.get_zone_config(building, zone)

    dataManager = DataManager(cfg, advise_cfg, client, zone, now=now)
    safety_constraints = dataManager.safety_constraints()
    prices = dataManager.prices()
    building_setpoints = dataManager.building_setpoints()

    temperature = 65
    DR = False

    # set outside temperatures and zone temperatures for each zone.
    for thermal_zone, zone_thermal_model in zone_thermal_models.items():
        zone_thermal_model.set_weather_predictions([70] * 24)
        zone_thermal_model.set_temperatures_and_fit({
            temp_thermal_zone: 70
            for temp_thermal_zone in zone_thermal_models.keys()
        })

    adv = Advise(
        [
            zone