Exemple #1
0
class EAMS():
    def __init__(self, enableLog):

        self.err = EAMS_Error()
        self.PROBLEM_CONFIG_SPEC = 'Data/ConfigSpecs/eams_prob_spec.cfg'

        if enableLog:
            #           # activate log
            self.logger = logging.getLogger()
            self.logger.setLevel(logging.INFO)
#             self.logger.setLevel(logging.DEBUG)

        self.Z = None
        #=======================================================================
        # Zone -> Room. Eg: {'1': {'Room1': {'ZoneID': '1', 'Width': 10.0, 'Length': 6.0 ...
        #=======================================================================

        self.ZL = None
        #======================================================================
        # Zone -> Room Name. Eg: {'1': ['Room1'], '3': ['Room3'], '2': ['Room2']}
        #======================================================================

        self.RL = None
        #=======================================================================
        # Room list. Eg: ['Room1', 'Room3', 'Room2']
        #=======================================================================

        self.TS = None
        #=======================================================================
        #  Idx -> datetime. Eg: {0: datetime.datetime(2013, 1, 1, 8, 0), 1: datetime.datetime(2013, 1, 1, 8, 30), 2: datetime.datetime(2013, 1, 1, 9, 0), 3: datetime.datetime(2013, 1, 1, 9, 30), 4: datetime.datetime(2013, 1, 1, 10, 0),
        #=======================================================================

        self.ML = None
        #=======================================================================
        # List of namedtuple('Meeting_Desc', 'Key TimeWindows Duration Room Attendees')
        # [Meeting_Desc(Key='M110133', TimeWindows=[[datetime.datetime(2013, 1, 1, 9, 0), datetime.datetime(2013, 1, 1, 10, 30)], [datetime.datetime(2013, 1, 1, 12, 0), datetime.datetime(2013, 1, 1, 14, 0)], [datetime.datetime(2013, 1, 2, 14, 0), datetime.datetime(2013, 1, 2, 16, 0)]], Duration='3', Room='', Attendees=['1119', '2578', '3470', '4601', '6823', '7105', '7908', '12736', '12996', '20479']), Meeting_Desc(Key='M316335', TimeWindows=[[datetime.datetime(2013, 1, 1, 9, 0), datetime.datetime(2013, 1, 1, 18, 0)]], Duration='7', Room='', Attendees=['219', '246', '2852', '3304', '4095', '6845', '8495', '8811', '8927', '15695']), Meeting_Desc(Key='M325401', TimeWindows=[[datetime.datetime(2013, 1, 1, 9, 0), datetime.datetime(2013, 1, 1, 18, 0)]], Duration='7', Room='', Attendees=['264', '2165', '2528', '2890', '3167', '4273', '5258', '5363', '10719', '20204']), Meeting_Desc(Key='M433676', TimeWindows=[[datetime.datetime(2013, 1, 1, 9, 0), datetime.datetime(2013, 1, 1, 18, 0)]], Duration='7', Room='', Attendees=['413', '1104', '2192', '2674', '3201', '6866', '13958', '13998', '14601', '14607'])]
        #=======================================================================

        self.CALS = {}
        #=======================================================================
        # List of Conflicting Meetings follow the sequence of self.ML
        # eg. For ML[1] is conflict with ML[2] because of attendee['6153', '2425', '895']
        # 0 :  {}
        # 1 :  {2: ['6153', '2425', '895'], 3: ['6153']}
        # 2 :  {1: ['6153', '2425', '895'], 3: ['1068', '6153']}
        # 3 :  {1: ['6153'], 2: ['1068', '6153']}
        #=======================================================================

        self.MTYPE = []
        self.mtdesc = namedtuple('Meeting_Type', 'MTW MA MD MCA MLS')
        #=======================================================================
        # List of Meeting Clique
        # Meeting_Type(MTW=(66, 81, 162, 177), MA=15, MD=None, MCA=['6153', '2425', '895'], MLS=[1])
        # Meeting_Type(MTW=(66, 81, 162, 177), MA=15, MD=None, MCA=['6153', '2425', '895', '1068'], MLS=[2])
        # Meeting_Type(MTW=(66, 81, 162, 177), MA=15, MD=None, MCA=['6153', '1068'], MLS=[3])
        # Meeting_Type(MTW=(66, 81, 162, 177), MA=15, MD=None, MCA=[], MLS=[4, 9])
        # Meeting_Type(MTW=(66, 81), MA=15, MD=None, MCA=[], MLS=[6, 8])
        # Meeting_Type(MTW=(162, 177), MA=15, MD=None, MCA=[], MLS=[0, 5, 7])
        #=======================================================================

        self.CMT = {}
        #=======================================================================
        # List of Conflicting Meeting Type because of an attendee
        # Key: Attendee ID, Value: Follow offset of Meeting Type in self.MTYPE
        # {'1068': [1, 2], '6153': [0, 1, 2], '2425': [0, 1], '895': [0, 1]}
        #=======================================================================

        self.OAT = None
        #=======================================================================
        # Sorted outdoor temperature. Eg: OrderedDict([(datetime.datetime(2013, 1, 1, 8, 0), '26.00'), (datetime.datetime(2013, 1, 1, 8, 30), '26.00'), (datetime.datetime(2013, 1, 1, 9, 0), '28.00')
        #=======================================================================

        self.SH = None
        #=======================================================================
        # A list follow the sequence of TS
        #=======================================================================

        self.MTW = None
        #=======================================================================
        # A Double array follow the sequence of self.ML
        # [
        #    [[18, 21], [24, 28], [76, 80]],   <- array of time windows [Start_Time, End_Time]
        #    [[18, 36]],
        #    [[18, 36]]
        # ]
        #=======================================================================

        self.MR = None
        #=======================================================================
        # A Double array follow the sequence of self.ML
        #
        #=======================================================================

        self.MOM = None
        #=======================================================================
        # A Double array follow the sequence of self.ML with list of meetings which has similar attendee(s)
        #  [
        #    [1, 2],
        #    [2],
        #    []
        #  ]
        #=======================================================================

        self.MODE_CONFIG = ""
        self.STANDBY_MODE = ""
        self.MEETINGS_CONFIG_DATA = ""
        self.OUTDOOR_TEMP_DATA = ""
        self.ROOM_CONFIG_DATA = ""
        self.SCHEDULING_START_DATETIME = ""
        self.SCHEDULING_END_DATETIME = ""
        self.SCHEDULING_INTERVAL = -1
        self.DAILY_WORKING_HOUR_START = ""
        self.DAILY_WORKING_HOUR_END = ""
        self.HVAC_NON_PEAK_OFF = -1
        self.HVAC_SHUT_DOWN = ""
        self.HVAC_TURN_ON = ""
        self.SOLAR_GAIN_LOW = -1
        self.SOLAR_GAIN_MEDIUM = -1
        self.SOLAR_GAIN_HIGH = -1
        self.SOLAR_RADIATION_AM_START = ""
        self.SOLAR_RADIATION_NOON_START = ""
        self.SOLAR_RADIATION_PM_START = ""
        self.SOLAR_RADIATION_NIGHT_START = ""
        self.WALL_RESISTANCE_LOW = -1
        self.WALL_RESISTANCE_HIGH = -1
        self.WALL_CAPACITANCE_LOW = -1
        self.WALL_CAPACITANCE_HIGH = -1
        self.INITIAL_TEMPERATURE_MODE = -1
        self.INITIAL_TEMPERATURE = -1
        self.TEMPERATURE_UNOCC_MIN = -1
        self.TEMPERATURE_UNOCC_RANGE_INCR = -1
        self.TEMPERATURE_OCC_COMFORT_RANGE_INCR = -1
        self.TEMPERATURE_UNOCC_MAX = -1
        self.TEMPERATURE_OCC_COMFORT_RANGE_DECR = -1
        self.TEMPERATURE_CONDITIONED_AIR = -1
        self.TEMPERATURE_SUPPLY_AIR_HIGH = -1
        self.ALPHA_IAQ_FACTOR_OF_SAFETY = -1
        self.BETA_FAN_POWER_CONSTANT = -1
        self.AIR_HEAT_CAPACITY_AT_CONSTANT_PRESSURE = -1
        self.INITIAL_TEMPERATURE_SUPPLY_AIR_OCC = -1
        self.INITIAL_TEMPERATURE_SUPPLY_AIR_UNOCC = -1
        self.INITIAL_MASS_AIR_FLOW_SUPPLY_AIR_OCC = -1
        self.INITIAL_MASS_AIR_FLOW_SUPPLY_AIR_UNOCC = -1
        self.MASS_AIR_FLOW_SUPPLY_AIR_MIN = -1
        self.MASS_AIR_FLOW_SUPPLY_AIR_MAX = -1
        self.MASS_AIR_FLOW_SUPPLY_AIR_PER_PERSON = -1
        self.MASS_AIR_FLOW_OUTSIDE_AIR_PER_PERSON = -1
        self.MASS_AIR_FLOW_OUTSIDE_AIR_PER_METER_SQUARE = -1
        self.MASS_AIR_FLOW_RETURN_AIR_RATIO = -1
        self.OCCUPANT_SENSIBLE_HEAT_GAIN = -1
        self.OCCUPANT_WATER_VAPOR_RATE = -1
        self.PLOT_INTERVAL = -1
        self.PLOT_STEP = -1
        self.ZOOMPLOT_START_DATETIME = ""
        self.ZOOMPLOT_END_DATETIME = ""
        self.ZOOMPLOT_INTERVAL = -1
        self.ZOOMPLOT_STEP = -1

    def activateLogFile(self, f):
        # remove old handler
        for old_handler in self.logger.handlers:
            self.logger.removeHandler(old_handler)

        # create a handler with the name defined by the variable f
        handler = logging.FileHandler(f)
        # add that handler to the logger
        self.logger.addHandler(handler)

    def _critical_err(self):
        sys.exit("Pop, Bang, Whooo, Boom... Fire Alarm! Evacuate! EAMS exit.")

    def _validateProblemConfig(self, config):
        validator = Validator()
        results = config.validate(validator)
        logging.info("Validating problem configuration, result: %s" % results)

        if results != True:
            for (_, key, _) in flatten_errors(config, results):
                if key is not None:
                    logging.error('The "%s" key failed validation' % (key))
                else:
                    logging.error(
                        "Configuration error. Either some section was missing, duplicate key(s) etc..."
                    )
            return self.err.eams_config_problem_err()
        return 0

    def _loadProblemConfig(self, config):
        #TODO: should cast these variables according to their type here, so you need not cast them later on!
        self.MODE_CONFIG = config['MODE_CONFIG']
        self.STANDBY_MODE = config['STANDBY_MODE']
        self.MEETINGS_CONFIG_DATA = config['MEETINGS_CONFIG_DATA']
        self.OUTDOOR_TEMP_DATA = config['OUTDOOR_TEMP_DATA']
        self.ROOM_CONFIG_DATA = config['ROOM_CONFIG_DATA']
        self.SCHEDULING_START_DATETIME = config['SCHEDULING_START_DATETIME']
        self.SCHEDULING_END_DATETIME = config['SCHEDULING_END_DATETIME']
        self.SCHEDULING_INTERVAL = int(config['SCHEDULING_INTERVAL'])
        self.DAILY_WORKING_HOUR_START = config['DAILY_WORKING_HOUR_START']
        self.DAILY_WORKING_HOUR_END = config['DAILY_WORKING_HOUR_END']
        self.HVAC_NON_PEAK_OFF = config['HVAC_NON_PEAK_OFF']
        self.HVAC_SHUT_DOWN = config['HVAC_SHUT_DOWN']
        self.HVAC_TURN_ON = config['HVAC_TURN_ON']
        self.SOLAR_GAIN_LOW = config['SOLAR_GAIN_LOW']
        self.SOLAR_GAIN_MEDIUM = config['SOLAR_GAIN_MEDIUM']
        self.SOLAR_GAIN_HIGH = config['SOLAR_GAIN_HIGH']
        self.SOLAR_RADIATION_AM_START = config['SOLAR_RADIATION_AM_START']
        self.SOLAR_RADIATION_NOON_START = config['SOLAR_RADIATION_NOON_START']
        self.SOLAR_RADIATION_PM_START = config['SOLAR_RADIATION_PM_START']
        self.SOLAR_RADIATION_NIGHT_START = config[
            'SOLAR_RADIATION_NIGHT_START']
        self.WALL_RESISTANCE_LOW = config['WALL_RESISTANCE_LOW']
        self.WALL_RESISTANCE_HIGH = config['WALL_RESISTANCE_HIGH']
        self.WALL_CAPACITANCE_LOW = config['WALL_CAPACITANCE_LOW']
        self.WALL_CAPACITANCE_HIGH = config['WALL_CAPACITANCE_HIGH']
        self.INITIAL_TEMPERATURE_MODE = config['INITIAL_TEMPERATURE_MODE']
        self.INITIAL_TEMPERATURE = config['INITIAL_TEMPERATURE']
        self.TEMPERATURE_UNOCC_MIN = float(config['TEMPERATURE_UNOCC_MIN'])
        self.TEMPERATURE_UNOCC_RANGE_INCR = float(
            config['TEMPERATURE_UNOCC_RANGE_INCR'])
        self.TEMPERATURE_OCC_COMFORT_RANGE_INCR = float(
            config['TEMPERATURE_OCC_COMFORT_RANGE_INCR'])
        self.TEMPERATURE_UNOCC_MAX = float(config['TEMPERATURE_UNOCC_MAX'])
        self.TEMPERATURE_OCC_COMFORT_RANGE_DECR = float(
            config['TEMPERATURE_OCC_COMFORT_RANGE_DECR'])
        self.TEMPERATURE_CONDITIONED_AIR = float(
            config['TEMPERATURE_CONDITIONED_AIR'])
        self.TEMPERATURE_SUPPLY_AIR_HIGH = float(
            config['TEMPERATURE_SUPPLY_AIR_HIGH'])
        self.ALPHA_IAQ_FACTOR_OF_SAFETY = config['ALPHA_IAQ_FACTOR_OF_SAFETY']
        self.BETA_FAN_POWER_CONSTANT = config['BETA_FAN_POWER_CONSTANT']
        self.AIR_HEAT_CAPACITY_AT_CONSTANT_PRESSURE = float(
            config['AIR_HEAT_CAPACITY_AT_CONSTANT_PRESSURE'])
        self.INITIAL_TEMPERATURE_SUPPLY_AIR_OCC = float(
            config['INITIAL_TEMPERATURE_SUPPLY_AIR_OCC'])
        self.INITIAL_TEMPERATURE_SUPPLY_AIR_UNOCC = float(
            config['INITIAL_TEMPERATURE_SUPPLY_AIR_UNOCC'])
        self.INITIAL_MASS_AIR_FLOW_SUPPLY_AIR_OCC = float(
            config['INITIAL_MASS_AIR_FLOW_SUPPLY_AIR_OCC'])
        self.INITIAL_MASS_AIR_FLOW_SUPPLY_AIR_UNOCC = float(
            config['INITIAL_MASS_AIR_FLOW_SUPPLY_AIR_UNOCC'])
        self.MASS_AIR_FLOW_SUPPLY_AIR_MIN = float(
            config['MASS_AIR_FLOW_SUPPLY_AIR_MIN'])
        self.MASS_AIR_FLOW_SUPPLY_AIR_MAX = float(
            config['MASS_AIR_FLOW_SUPPLY_AIR_MAX'])
        self.MASS_AIR_FLOW_SUPPLY_AIR_PER_PERSON = float(
            config['MASS_AIR_FLOW_SUPPLY_AIR_PER_PERSON'])
        self.MASS_AIR_FLOW_OUTSIDE_AIR_PER_PERSON = float(
            config['MASS_AIR_FLOW_OUTSIDE_AIR_PER_PERSON'])
        self.MASS_AIR_FLOW_OUTSIDE_AIR_PER_METER_SQUARE = float(
            config['MASS_AIR_FLOW_OUTSIDE_AIR_PER_METER_SQUARE'])
        self.MASS_AIR_FLOW_RETURN_AIR_RATIO = float(
            config['MASS_AIR_FLOW_RETURN_AIR_RATIO'])
        self.OCCUPANT_SENSIBLE_HEAT_GAIN = float(
            config['OCCUPANT_SENSIBLE_HEAT_GAIN'])
        self.OCCUPANT_WATER_VAPOR_RATE = float(
            config['OCCUPANT_WATER_VAPOR_RATE'])
        self.PLOT_INTERVAL = config['PLOT_INTERVAL']
        self.PLOT_STEP = config['PLOT_STEP']
        self.ZOOMPLOT_START_DATETIME = config['ZOOMPLOT_START_DATETIME']
        self.ZOOMPLOT_END_DATETIME = config['ZOOMPLOT_END_DATETIME']
        self.ZOOMPLOT_INTERVAL = config['ZOOMPLOT_INTERVAL']
        self.ZOOMPLOT_STEP = config['ZOOMPLOT_STEP']
        logging.info("Problem configuration initialized.")

    def _diagMode(self):
        logging.info(
            "===============================================================")

        if self.MODE_CONFIG == 'TSRA':
            logging.info("EAMS for Room Allocation & Time Scheduling.")
        elif self.MODE_CONFIG == 'TS':
            logging.info("EAMS for Time Scheduling only.")
        else:
            logging.critical("Unknown or Unsupported mode < %s >" %
                             (self.MODE_CONFIG))
            return self.err.eams_config_problem_err()

        logging.info(
            "===============================================================")

        return 0

    def _populateRoomConfig(self):
        self.RC = RoomConfig(self.WALL_RESISTANCE_LOW,
                             self.WALL_RESISTANCE_HIGH,
                             self.WALL_CAPACITANCE_LOW,
                             self.WALL_CAPACITANCE_HIGH, self.SOLAR_GAIN_LOW,
                             self.SOLAR_GAIN_MEDIUM, self.SOLAR_GAIN_HIGH)
        ret = self.RC.loadRoomConfig(self.ROOM_CONFIG_DATA)
        if ret == 0:
            self.RC.populateRoomByZone()
            self.Z = self.RC.getRoomsInfoByZone("All")
            self.ZL = self.RC.getZoneList()
            self.RL = self.RC.getRoomList()
            self.RCL = self.RC.getRoomCapaList()
            self.RNL = self.RC.getRoomNeighboursList()
            logging.debug("Get rooms: %s" % self.Z)
            logging.debug("Get zonelist: %s" % (self.ZL))
            logging.debug("Get roomlist: %s" % (self.RL))
            logging.debug("Get room capa list: %s" % (self.RCL))
            logging.debug("Get room neighbours list: %s" % (self.RNL))

    # DEBUG_ONLY
    #         self.Z = self.RC.getRoomsInfoByZone("1")
    # DEBUG_ONLY

        return ret

    def _populateOutdoorTemperature(self):
        self.OTC = OutdoorTemperature()
        ret = self.OTC.loadOutdoorTemperature(self.OUTDOOR_TEMP_DATA)
        if ret == 0:
            # Populate outdoor temperature
            # option 1:
            #             if int(self.SCHEDULING_INTERVAL) >= 30:
            #                 self.OAT = self.OTC.getSingleDayOutdoorTemperature(self.SCHEDULING_START_DATETIME, self.SCHEDULING_END_DATETIME, self.SCHEDULING_INTERVAL)
            #             else:
            #                 self.OAT = self.OTC.getSingleDayOutdoorTemperatureShortInterval(self.SCHEDULING_START_DATETIME, self.SCHEDULING_END_DATETIME, self.SCHEDULING_INTERVAL)
            # option 2:
            self.OAT = self.OTC.getOutdoorTemperature(
                self.SCHEDULING_START_DATETIME, self.SCHEDULING_END_DATETIME,
                self.SCHEDULING_INTERVAL)

            logging.debug("OAT: %s" % (self.OAT))

            #             for k, v in self.OAT.iteritems():
            #                 logging.debug("[%s]:[%s]" %(k,v))

            logging.debug("len: %s" % (len(self.OAT)))
            if len(self.OAT) == 0:
                return self.err.eams_no_outdoor_temp_in_scheduling_range()

            # Set initial temperature is set to outdoor temperature at first slot
#             option 1: override eams_probN.cfg INITIAL_TEMPERATURE
            if self.INITIAL_TEMPERATURE_MODE == '1':
                self.INITIAL_TEMPERATURE = float(
                    self.OAT.get(
                        datetime.strptime(self.SCHEDULING_START_DATETIME,
                                          "%Y-%m-%d %H:%M")))
                logging.debug("Initial OAT: %s" % (self.INITIAL_TEMPERATURE))
            # option 2:
#             self.INITIAL_TEMPERATURE = self.INITIAL_TEMPERATURE

            logging.debug("Set Initial Temperature : %.2f" %
                          (self.INITIAL_TEMPERATURE))
        return ret

    def _populateMeetingConfig(self):
        self.MC = MeetingConfig()
        ret = self.MC.loadMeetings(self.MEETINGS_CONFIG_DATA)
        if ret == 0:
            mr = self.MC.getMeetings()

            self.M = Meeting()
            ret = self.M.populateMeetingsForRoomAllocNSchedule(mr)
            if ret < 0:
                return ret

            self.ML = self.M.getMeetingsList()
            logging.debug("Total number of meetings: %d" % (len(self.ML)))
            logging.debug(self.ML)

        return ret

    def _populateSchedulingTimeSlot(self):
        """Form timeslot based on given work week and time slot interval"""
        self.TSC = TimeSlot(self.SCHEDULING_START_DATETIME,
                            self.SCHEDULING_END_DATETIME,
                            (int)(self.SCHEDULING_INTERVAL))
        self.TS = self.TSC.getTimeSlots()
        logging.debug("Timeslot: %s" % self.TS)
        logging.debug("Total number of timeslot: %d" % len(self.TS))

    def _populateHVACStdTimeslot(self):
        """Populate binary indicator list which denotes if the timeslot is in standard operating hour of HVAC (i.e. always on) """
        self.SH = []
        if self.HVAC_NON_PEAK_OFF == '1':
            s = datetime.strptime(self.HVAC_SHUT_DOWN, "%H:%M")
            e = datetime.strptime(self.HVAC_TURN_ON, "%H:%M")

            for _, v in self.TS.iteritems():
                if (v.time() >= s.time() or v.time() < e.time()):
                    self.SH.append(0)  #off HVAC
                else:
                    self.SH.append(1)
        else:
            for _, v in self.TS.iteritems():
                self.SH.append(1)

        logging.debug("HVAC Standard Hours:\n%s" % (self.SH))

    def _populateMeetingRequestTimeslot(self):
        """Populate slot index of meetings' time windows """
        self.MTW = []
        for i in xrange(len(self.ML)):
            self.MTW.append([])
            for j in xrange(len(self.ML[i].TimeWindows)):
                s = self.ML[i].TimeWindows[j][0]
                e = self.ML[i].TimeWindows[j][1]
                sidx = self.TSC.getTimeSlotIdxByDatetime(s)
                eidx = self.TSC.getTimeSlotIdxByDatetime(e)

                if sidx is None:
                    logging.error(
                        "Meeting request falls outside of timeslot range. HALT."
                    )
                    return self.err.eams_meeting_out_of_timeslot_range()
                if eidx is None:
                    logging.error("Meeting ends outside of timeslot range. ")
                    return self.err.eams_meeting_out_of_timeslot_range()

                logging.debug(
                    "Meeting %s start at %s [slot %d], deadline at %s[slot %d], duration of %s slot(s)"
                    % (self.ML[i].Key, s, sidx, e, eidx - 1,
                       self.ML[i].Duration))

                #                 logging.debug("sidx=%s eidx=%s d=%d" %(sidx, eidx-1, int(self.ML[i].Duration)))
                if (eidx - sidx) < int(self.ML[i].Duration):
                    logging.error(
                        "Meeting duration longer than Earliest-Start-Time to Latest-Finished-Time timeslot. HALT."
                    )
                    return self.err.eams_meeting_invalid_duration()

                self.MTW[i].append([
                    sidx, eidx - 1
                ])  #Note: Meeting end at the slot before 'End' time, hence -1

        logging.debug("Meetings' time windows:")
        logging.debug(self.MTW)
        return 0

    def _populateFeasibleRoomsForMeeting(self):
        """Does location l has the capacity to accommodate meeting midx? """

        logging.info("Populate feasible rooms for meeting(s)...")

        self.MR = []
        for i in xrange(len(self.ML)):
            self.MR.append([])

            a = len(self.ML[i].Attendees)

            if not self.ML[i].Room:  # no preferred room
                for j in xrange(len(self.RCL)):
                    if a <= self.RCL[j]:
                        self.MR[i].append(j)

                if not self.MR[i]:
                    logging.error("No feasible room for meeting [%d]" % i)
                    return self.err.eams_meeting_no_feasible_room()
            else:  # has preferred room
                if self.ML[i].Room not in self.RL:
                    logging.critical(
                        "Mode Config: %s. Preferred room [%s] for meeting [%s] does not exist in room list."
                        % (self.MODE_CONFIG, self.ML[i].Room, self.ML[i].Key))
                    return self.err.eams_config_meeting_err()

                ridx = self.RL.index(self.ML[i].Room)
                if len(self.ML[i].Attendees) > self.RCL[ridx]:
                    logging.critical(
                        "Mode Config: %s. Preferred room [%s] for meeting [%s] has smaller capacity (only for %d people) than the number of attendee."
                        % (self.MODE_CONFIG, self.ML[i].Room, self.ML[i].Key,
                           self.RCL[ridx]))
                    return self.err.eams_config_meeting_err()

                self.MR[i].append(
                    ridx)  # limit feasible room to preferred room

        logging.debug(
            "Feasible rooms based on meetings' attendees and room capacity:")
        logging.debug(self.MR)
        return 0

    def _populateMeetingsWithSimilarAttendees(self):
        """Does meeting m has similar attendee(s) as meeting om"""

        self.MOM = []
        for mx in xrange(len(self.ML)):
            self.MOM.append([])
            # TODO: use set.intersection instead! See _populateMeetingConflicts()
            mx_multiset = Counter(self.ML[mx].Attendees)
            for my in xrange(mx + 1, len(self.ML)):
                #                 logging.debug("checking m[%d, %d]" %(mx, my))
                my_multiset = Counter(self.ML[my].Attendees)
                if (list((mx_multiset & my_multiset).elements())):
                    logging.debug("Meeting %d and %d have similar attendees" %
                                  (mx, my))
                    self.MOM[mx].append(my)

        logging.debug("Meetings which has similar attendee(s)")
        logging.debug(self.MOM)

    def _populateRoomThermalCfg(self):
        """Populate individual room config to be used by Gurobi"""
        self.RTC = RoomThermalCfg(self.Z, self.TS,
                                  self.SOLAR_RADIATION_AM_START,
                                  self.SOLAR_RADIATION_NOON_START,
                                  self.SOLAR_RADIATION_PM_START,
                                  self.SOLAR_RADIATION_NIGHT_START)

    def _populateMeetingConflicts(self):
        """Which meeting is conflict with m, and who are the attendee that cause the conflict"""

        self.CALS = {}
        for mx in xrange(len(self.ML)):
            for my in xrange(mx + 1, len(self.ML)):
                oa = set(self.ML[mx].Attendees) & set(self.ML[my].Attendees)
                if oa:
                    if mx in self.CALS.keys():
                        self.CALS.get(mx).update({my: list(oa)})
                    else:
                        self.CALS[mx] = {my: list(oa)}

                    if my in self.CALS.keys():
                        self.CALS.get(my).update({mx: list(oa)})
                    else:
                        self.CALS[my] = {mx: list(oa)}

#                 for k, v in self.CALS.iteritems():
#                     print k, ": ", v
#                 print ""

# Add empty set for meetings w/o conflicts!
        no_conflict = list(
            set(list(xrange(len(self.ML)))).difference(set(self.CALS.keys())))
        for i in xrange(len(no_conflict)):
            self.CALS[no_conflict[i]] = {}

        # debug_only----------------------------------------
#         for k, v in self.CALS.iteritems():
#             print k, ": ", v
#         print ""
#
#         for mx in xrange(len(self.ML)):
#             for my in xrange(mx+1, len(self.ML)):
#                 print "[", mx, "," , my, "] =", self._hasSameConflictProperties(mx, my)
#         print ""
#
#         for m in xrange(len(self.ML)):
#             print 'Meeting', m
#             print 'Attendees with multiple meetings:', self._getConflictAttendees(m)
#             print 'Meetings with same attendee:', self._getConflictMeetings(m)
#             print ""
# debug_only----------------------------------------

# debug_only----------------------------------------
#     def _hasSameConflictProperties(self, m1, m2):
#         # True:  if both meeting has the same conflict attendees and conflict meetings.
#         if (self.CALS[m1].values() != self.CALS[m2].values()):
#             return False
#
#         if m2 in self.CALS[m1].keys() and m1 in self.CALS[m2].keys():
#             k1 = deepcopy(self.CALS[m1].keys())#.remove(m2)
#             k2 = deepcopy(self.CALS[m2].keys())#.remove(m1)
#             k1.remove(m2)
#             k2.remove(m1)
#             if k1 == k2:
#                 return True
#         else:
#             return False
# debug_only----------------------------------------

    def _getConflictAttendees(self, m):
        return self.CALS[m].values()

    def _getConflictMeetings(self, m):
        return self.CALS[m].keys()

    # TODO: move this to new file which compute meeting clique!
    # TODO: For simplicity, we do not classify meeting further based on duration and room.
    def _populateMeetingClique(self):

        self.MTYPE = []
        # Group based on time window
        logging.debug("Group based on time window... ")
        dg_mtw = {}
        for i in xrange(len(self.ML)):
            #             print "ML[", i, "]=", self.ML[i].Key
            #             print "MTW[", i, "]:", self.MTW[i]
            #             print "MOM[", i, "]:", self.MOM[i]
            #             print "MA:", len(self.ML[i].Attendees)
            #             print "MD:", self.ML[i].Duration
            #             print "MR:", self.MR[i]

            mtw_tup = (
            )  # create tuple for MTW object [[66, 81], [114, 129]] --> (66, 81, 114, 129)
            for j in xrange(len(self.MTW[i])):
                mtw_tup = mtw_tup + tuple(self.MTW[i][j])

            if mtw_tup in dg_mtw.keys(
            ):  # use tuple as key, store index of ML which has that MTW properties
                dg_mtw.get(mtw_tup).append(
                    i
                )  #  (66, 81, 114, 129): [4]  --> ML[4] has  [[66, 81], [114, 129]
            else:
                dg_mtw[mtw_tup] = [i]

        logging.debug("Num MTW group:%d" % len(dg_mtw))
        logging.debug("%s" % dg_mtw)

        # Re-group based on number of attendee
        logging.debug(
            "\nWithin group of same time window, re-group based on number of attendee... "
        )
        logging.debug(
            "For simplicity, meetings are grouped based on 2-5, 6-15, 16-30 ppl"
        )
        for k, v in dg_mtw.iteritems():
            logging.debug("k=%s, ---v=%s" % (k, v))
            numals = {
                5: [],
                15: [],
                30: [],
                100: []
            }  # a dict of based on number of attendee (num attendee less than 'key')
            for m in xrange(len(v)):
                mid = v[m]
                numa = len(self.ML[mid].Attendees)
                if numa <= 5:
                    numals.get(5).append(mid)
                elif numa <= 15:
                    numals.get(15).append(mid)
                elif numa <= 30:
                    numals.get(30).append(mid)
                else:
                    numals.get(100).append(mid)

            for nk, nv in numals.iteritems():
                if len(nv):
                    self.MTYPE.append(self.mtdesc(k, nk, None, None, nv))

        logging.debug("%s" % (self.MTYPE))
        logging.debug("Num meeting type:%d" % len(self.MTYPE))

        # Re-group based on attendee conflict
        logging.debug(
            "\nWithin group of same time window & same num attendee group, Re-group based on attendee conflict... "
        )
        new_mtype = []
        for i in xrange(len(self.MTYPE)):
            mls = self.MTYPE[i].MLS
            logging.debug("Evaluating MTYPE %d: %s" % (i, mls))
            tmp_mtype = []
            for x in xrange(len(mls)):
                stat = False
                m = mls[x]
                mca = self._getConflictAttendees(m)
                # Get a unique list of mca
                uniq_mca = []
                for a in xrange(len(mca)):
                    for b in xrange(len(mca[a])):
                        if mca[a][b] not in uniq_mca:
                            uniq_mca.append(mca[a][b])
                logging.debug(uniq_mca)

                for y in xrange(len(tmp_mtype)):
                    #                     print "tmp_mtype[y].MCA:", tmp_mtype[y].MCA
                    #                     print "uniq_mca:", uniq_mca
                    #                     print tmp_mtype[y].MCA == uniq_mca
                    if tmp_mtype[y].MCA == uniq_mca:
                        tmp_mtype[y].MLS.append(m)
                        stat = True
                        break
                if stat == False:
                    tmp_mtype.append(
                        self.mtdesc(self.MTYPE[i].MTW, self.MTYPE[i].MA, None,
                                    uniq_mca, [m]))

            for j in xrange(len(tmp_mtype)):
                new_mtype.append(tmp_mtype[j])

        logging.info("%s" % new_mtype)
        logging.info("Number of new MTYPE:%d" % len(new_mtype))
        #         # debug only -------------
        #         for i in xrange(len(new_mtype)):
        #             print new_mtype[i]
        #         print "Num MTYPE:" + str(len(new_mtype))
        #         # debug only -------------
        for i in xrange(len(new_mtype)):
            logging.info("MTYPE[%d]:%s" % (i, str(new_mtype[i])))

        # Overwrite MTYPE
        self.MTYPE = new_mtype

    def _populateConflictMeetingTypesBasedOnAttendee(self):
        self.CMT = {}
        for i in xrange(len(self.MTYPE)):
            for j in xrange(len(self.MTYPE[i].MCA)):
                aid = self.MTYPE[i].MCA[j]
                if aid not in self.CMT.keys():
                    self.CMT[aid] = [i]
                else:
                    self.CMT.get(aid).append(i)

        logging.debug("ConflictMeetingTypes: %s" % self.CMT)

    def _populateProbData(self, filename):
        ret = 0
        try:
            # Load problem configuration
            config = ConfigObj(filename,
                               configspec=self.PROBLEM_CONFIG_SPEC,
                               file_error=True)
            ret = self._validateProblemConfig(config)
            if ret < 0:
                raise ValidateError()
            self._loadProblemConfig(config)
            ret = self._diagMode()
            if ret < 0:
                raise ValueError("Invalid problem configuration.")

            # Initialize & load room, meeting, timeslot, outdoor temperature etc information
            ret = self._populateRoomConfig()
            if ret < 0:
                raise ValueError("Invalid room configuration.")

            ret = self._populateMeetingConfig()
            if ret < 0:
                raise ValueError("Invalid meeting configuration.")

            ret = self._populateOutdoorTemperature()
            if ret < 0:
                raise ValueError("Invalid outdoor temperature configuration.")

            self._populateSchedulingTimeSlot()

            ret = self._populateMeetingRequestTimeslot()
            if ret < 0:
                raise ValueError("Meeting request out of timeslot range.")

            ret = self._populateFeasibleRoomsForMeeting()
            if ret < 0:
                raise ValueError("Invalid room configuration for a meeting.")

            self._populateHVACStdTimeslot()
            self._populateMeetingsWithSimilarAttendees()

            self._populateMeetingConflicts()
            self._populateMeetingClique()
            self._populateConflictMeetingTypesBasedOnAttendee()

            self._populateRoomThermalCfg()

        except (ConfigObjError, IOError), e:
            logging.critical('%s' % (e))
            return self.err.eams_config_problem_err()
        except (ValidateError), e:
            logging.critical("%s validation error. %d" % (filename, ret))
Exemple #2
0
class EAMS():
    def __init__(self):

        self.err = EAMS_Error()
        self.PROBLEM_CONFIG_SPEC = 'Data/ConfigSpecs/eams_prob_spec.cfg'
        # activate log
        self.logger = logging.getLogger()
        self.logger.setLevel(logging.INFO)
        #         self.logger.setLevel(logging.DEBUG)

        self.Z = None
        #=======================================================================
        # Zone -> Room. Eg: {'1': {'Room1': {'ZoneID': '1', 'Width': 10.0, 'Length': 6.0 ...
        #=======================================================================

        self.ZL = None
        #======================================================================
        # Zone -> Room Name. Eg: {'1': ['Room1'], '3': ['Room3'], '2': ['Room2']}
        #======================================================================

        self.RL = None
        #=======================================================================
        # Room list. Eg: ['Room1', 'Room3', 'Room2']
        #=======================================================================

        self.TS = None
        #=======================================================================
        #  Idx -> datetime. Eg: {0: datetime.datetime(2013, 1, 1, 8, 0), 1: datetime.datetime(2013, 1, 1, 8, 30), 2: datetime.datetime(2013, 1, 1, 9, 0), 3: datetime.datetime(2013, 1, 1, 9, 30), 4: datetime.datetime(2013, 1, 1, 10, 0),
        #=======================================================================

        self.ML = None
        #=======================================================================
        # List of namedtuple('Meeting_Desc', 'Key TimeWindows Duration Room Attendees')
        # [Meeting_Desc(Key='M110133', TimeWindows=[[datetime.datetime(2013, 1, 1, 9, 0), datetime.datetime(2013, 1, 1, 10, 30)], [datetime.datetime(2013, 1, 1, 12, 0), datetime.datetime(2013, 1, 1, 14, 0)], [datetime.datetime(2013, 1, 2, 14, 0), datetime.datetime(2013, 1, 2, 16, 0)]], Duration='3', Room='', Attendees=['1119', '2578', '3470', '4601', '6823', '7105', '7908', '12736', '12996', '20479']), Meeting_Desc(Key='M316335', TimeWindows=[[datetime.datetime(2013, 1, 1, 9, 0), datetime.datetime(2013, 1, 1, 18, 0)]], Duration='7', Room='', Attendees=['219', '246', '2852', '3304', '4095', '6845', '8495', '8811', '8927', '15695']), Meeting_Desc(Key='M325401', TimeWindows=[[datetime.datetime(2013, 1, 1, 9, 0), datetime.datetime(2013, 1, 1, 18, 0)]], Duration='7', Room='', Attendees=['264', '2165', '2528', '2890', '3167', '4273', '5258', '5363', '10719', '20204']), Meeting_Desc(Key='M433676', TimeWindows=[[datetime.datetime(2013, 1, 1, 9, 0), datetime.datetime(2013, 1, 1, 18, 0)]], Duration='7', Room='', Attendees=['413', '1104', '2192', '2674', '3201', '6866', '13958', '13998', '14601', '14607'])]
        #=======================================================================

        self.OAT = None
        #=======================================================================
        # Sorted outdoor temperature. Eg: OrderedDict([(datetime.datetime(2013, 1, 1, 8, 0), '26.00'), (datetime.datetime(2013, 1, 1, 8, 30), '26.00'), (datetime.datetime(2013, 1, 1, 9, 0), '28.00')
        #=======================================================================

        self.SH = None
        #=======================================================================
        # A list follow the sequence of TS
        #=======================================================================

        self.MTW = None
        #=======================================================================
        # A Double array follow the sequence of self.ML
        # [
        #    [[18, 21], [24, 28], [76, 80]],   <- array of time windows [Start_Time, End_Time]
        #    [[18, 36]],
        #    [[18, 36]]
        # ]
        #=======================================================================

        self.MR = None
        #=======================================================================
        # A Double array follow the sequence of self.ML
        #
        #=======================================================================

        self.MOM = None
        #=======================================================================
        # A Double array follow the sequence of self.ML with list of meetings which has similar attendee(s)
        #  [
        #    [1, 2],
        #    [2],
        #    []
        #  ]
        #=======================================================================

        self.MODE_CONFIG = ""
        self.STANDBY_MODE = ""
        self.MEETINGS_CONFIG_DATA = ""
        self.OUTDOOR_TEMP_DATA = ""
        self.ROOM_CONFIG_DATA = ""
        self.SCHEDULING_START_DATETIME = ""
        self.SCHEDULING_END_DATETIME = ""
        self.SCHEDULING_INTERVAL = -1
        self.DAILY_WORKING_HOUR_START = ""
        self.DAILY_WORKING_HOUR_END = ""
        self.HVAC_NON_PEAK_OFF = -1
        self.HVAC_SHUT_DOWN = ""
        self.HVAC_TURN_ON = ""
        self.SOLAR_GAIN_LOW = -1
        self.SOLAR_GAIN_MEDIUM = -1
        self.SOLAR_GAIN_HIGH = -1
        self.SOLAR_RADIATION_AM_START = ""
        self.SOLAR_RADIATION_NOON_START = ""
        self.SOLAR_RADIATION_PM_START = ""
        self.SOLAR_RADIATION_NIGHT_START = ""
        self.WALL_RESISTANCE_LOW = -1
        self.WALL_RESISTANCE_HIGH = -1
        self.WALL_CAPACITANCE_LOW = -1
        self.WALL_CAPACITANCE_HIGH = -1
        self.INITIAL_TEMPERATURE_MODE = -1
        self.INITIAL_TEMPERATURE = -1
        self.TEMPERATURE_UNOCC_MIN = -1
        self.TEMPERATURE_UNOCC_RANGE_INCR = -1
        self.TEMPERATURE_OCC_COMFORT_RANGE_INCR = -1
        self.TEMPERATURE_UNOCC_MAX = -1
        self.TEMPERATURE_OCC_COMFORT_RANGE_DECR = -1
        self.TEMPERATURE_CONDITIONED_AIR = -1
        self.TEMPERATURE_SUPPLY_AIR_HIGH = -1
        self.ALPHA_IAQ_FACTOR_OF_SAFETY = -1
        self.BETA_FAN_POWER_CONSTANT = -1
        self.AIR_HEAT_CAPACITY_AT_CONSTANT_PRESSURE = -1
        self.INITIAL_TEMPERATURE_SUPPLY_AIR_OCC = -1
        self.INITIAL_TEMPERATURE_SUPPLY_AIR_UNOCC = -1
        self.INITIAL_MASS_AIR_FLOW_SUPPLY_AIR_OCC = -1
        self.INITIAL_MASS_AIR_FLOW_SUPPLY_AIR_UNOCC = -1
        self.MASS_AIR_FLOW_SUPPLY_AIR_MIN = -1
        self.MASS_AIR_FLOW_SUPPLY_AIR_MAX = -1
        self.MASS_AIR_FLOW_SUPPLY_AIR_PER_PERSON = -1
        self.MASS_AIR_FLOW_OUTSIDE_AIR_PER_PERSON = -1
        self.MASS_AIR_FLOW_OUTSIDE_AIR_PER_METER_SQUARE = -1
        self.MASS_AIR_FLOW_RETURN_AIR_RATIO = -1
        self.OCCUPANT_SENSIBLE_HEAT_GAIN = -1
        self.OCCUPANT_WATER_VAPOR_RATE = -1
        self.PLOT_INTERVAL = -1
        self.PLOT_STEP = -1
        self.ZOOMPLOT_START_DATETIME = ""
        self.ZOOMPLOT_END_DATETIME = ""
        self.ZOOMPLOT_INTERVAL = -1
        self.ZOOMPLOT_STEP = -1

    def activateLogFile(self, f):
        # remove old handler
        for old_handler in self.logger.handlers:
            self.logger.removeHandler(old_handler)

        # create a handler with the name defined by the variable f
        handler = logging.FileHandler(f)
        # add that handler to the logger
        self.logger.addHandler(handler)

    def _critical_err(self):
        sys.exit("Pop, Bang, Whooo, Boom... Fire Alarm! Evacuate! EAMS exit.")

    def _validateProblemConfig(self, config):
        validator = Validator()
        results = config.validate(validator)
        logging.info("Validating problem configuration, result: %s" % results)

        if results != True:
            for (_, key, _) in flatten_errors(config, results):
                if key is not None:
                    logging.error('The "%s" key failed validation' % (key))
                else:
                    logging.error(
                        "Configuration error. Either some section was missing, duplicate key(s) etc..."
                    )
            return self.err.eams_config_problem_err()
        return 0

    def _loadProblemConfig(self, config):
        #TODO: should cast these variables according to their type here, so you need not cast them later on!
        self.MODE_CONFIG = config['MODE_CONFIG']
        self.STANDBY_MODE = config['STANDBY_MODE']
        self.MEETINGS_CONFIG_DATA = config['MEETINGS_CONFIG_DATA']
        self.OUTDOOR_TEMP_DATA = config['OUTDOOR_TEMP_DATA']
        self.ROOM_CONFIG_DATA = config['ROOM_CONFIG_DATA']
        self.SCHEDULING_START_DATETIME = config['SCHEDULING_START_DATETIME']
        self.SCHEDULING_END_DATETIME = config['SCHEDULING_END_DATETIME']
        self.SCHEDULING_INTERVAL = int(config['SCHEDULING_INTERVAL'])
        self.DAILY_WORKING_HOUR_START = config['DAILY_WORKING_HOUR_START']
        self.DAILY_WORKING_HOUR_END = config['DAILY_WORKING_HOUR_END']
        self.HVAC_NON_PEAK_OFF = config['HVAC_NON_PEAK_OFF']
        self.HVAC_SHUT_DOWN = config['HVAC_SHUT_DOWN']
        self.HVAC_TURN_ON = config['HVAC_TURN_ON']
        self.SOLAR_GAIN_LOW = config['SOLAR_GAIN_LOW']
        self.SOLAR_GAIN_MEDIUM = config['SOLAR_GAIN_MEDIUM']
        self.SOLAR_GAIN_HIGH = config['SOLAR_GAIN_HIGH']
        self.SOLAR_RADIATION_AM_START = config['SOLAR_RADIATION_AM_START']
        self.SOLAR_RADIATION_NOON_START = config['SOLAR_RADIATION_NOON_START']
        self.SOLAR_RADIATION_PM_START = config['SOLAR_RADIATION_PM_START']
        self.SOLAR_RADIATION_NIGHT_START = config[
            'SOLAR_RADIATION_NIGHT_START']
        self.WALL_RESISTANCE_LOW = config['WALL_RESISTANCE_LOW']
        self.WALL_RESISTANCE_HIGH = config['WALL_RESISTANCE_HIGH']
        self.WALL_CAPACITANCE_LOW = config['WALL_CAPACITANCE_LOW']
        self.WALL_CAPACITANCE_HIGH = config['WALL_CAPACITANCE_HIGH']
        self.INITIAL_TEMPERATURE_MODE = config['INITIAL_TEMPERATURE_MODE']
        self.INITIAL_TEMPERATURE = config['INITIAL_TEMPERATURE']
        self.TEMPERATURE_UNOCC_MIN = float(config['TEMPERATURE_UNOCC_MIN'])
        self.TEMPERATURE_UNOCC_RANGE_INCR = float(
            config['TEMPERATURE_UNOCC_RANGE_INCR'])
        self.TEMPERATURE_OCC_COMFORT_RANGE_INCR = float(
            config['TEMPERATURE_OCC_COMFORT_RANGE_INCR'])
        self.TEMPERATURE_UNOCC_MAX = float(config['TEMPERATURE_UNOCC_MAX'])
        self.TEMPERATURE_OCC_COMFORT_RANGE_DECR = float(
            config['TEMPERATURE_OCC_COMFORT_RANGE_DECR'])
        self.TEMPERATURE_CONDITIONED_AIR = float(
            config['TEMPERATURE_CONDITIONED_AIR'])
        self.TEMPERATURE_SUPPLY_AIR_HIGH = float(
            config['TEMPERATURE_SUPPLY_AIR_HIGH'])
        self.ALPHA_IAQ_FACTOR_OF_SAFETY = config['ALPHA_IAQ_FACTOR_OF_SAFETY']
        self.BETA_FAN_POWER_CONSTANT = config['BETA_FAN_POWER_CONSTANT']
        self.AIR_HEAT_CAPACITY_AT_CONSTANT_PRESSURE = float(
            config['AIR_HEAT_CAPACITY_AT_CONSTANT_PRESSURE'])
        self.INITIAL_TEMPERATURE_SUPPLY_AIR_OCC = float(
            config['INITIAL_TEMPERATURE_SUPPLY_AIR_OCC'])
        self.INITIAL_TEMPERATURE_SUPPLY_AIR_UNOCC = float(
            config['INITIAL_TEMPERATURE_SUPPLY_AIR_UNOCC'])
        self.INITIAL_MASS_AIR_FLOW_SUPPLY_AIR_OCC = float(
            config['INITIAL_MASS_AIR_FLOW_SUPPLY_AIR_OCC'])
        self.INITIAL_MASS_AIR_FLOW_SUPPLY_AIR_UNOCC = float(
            config['INITIAL_MASS_AIR_FLOW_SUPPLY_AIR_UNOCC'])
        self.MASS_AIR_FLOW_SUPPLY_AIR_MIN = float(
            config['MASS_AIR_FLOW_SUPPLY_AIR_MIN'])
        self.MASS_AIR_FLOW_SUPPLY_AIR_MAX = float(
            config['MASS_AIR_FLOW_SUPPLY_AIR_MAX'])
        self.MASS_AIR_FLOW_SUPPLY_AIR_PER_PERSON = float(
            config['MASS_AIR_FLOW_SUPPLY_AIR_PER_PERSON'])
        self.MASS_AIR_FLOW_OUTSIDE_AIR_PER_PERSON = float(
            config['MASS_AIR_FLOW_OUTSIDE_AIR_PER_PERSON'])
        self.MASS_AIR_FLOW_OUTSIDE_AIR_PER_METER_SQUARE = float(
            config['MASS_AIR_FLOW_OUTSIDE_AIR_PER_METER_SQUARE'])
        self.MASS_AIR_FLOW_RETURN_AIR_RATIO = float(
            config['MASS_AIR_FLOW_RETURN_AIR_RATIO'])
        self.OCCUPANT_SENSIBLE_HEAT_GAIN = float(
            config['OCCUPANT_SENSIBLE_HEAT_GAIN'])
        self.OCCUPANT_WATER_VAPOR_RATE = float(
            config['OCCUPANT_WATER_VAPOR_RATE'])
        self.PLOT_INTERVAL = config['PLOT_INTERVAL']
        self.PLOT_STEP = config['PLOT_STEP']
        self.ZOOMPLOT_START_DATETIME = config['ZOOMPLOT_START_DATETIME']
        self.ZOOMPLOT_END_DATETIME = config['ZOOMPLOT_END_DATETIME']
        self.ZOOMPLOT_INTERVAL = config['ZOOMPLOT_INTERVAL']
        self.ZOOMPLOT_STEP = config['ZOOMPLOT_STEP']
        logging.info("Problem configuration initialized.")

    def _diagMode(self):
        logging.info(
            "===============================================================")

        if self.MODE_CONFIG == 'TSRA':
            logging.info("EAMS for Room Allocation & Time Scheduling.")
        elif self.MODE_CONFIG == 'TS':
            logging.info("EAMS for Time Scheduling only.")
        else:
            logging.critical("Unknown or Unsupported mode < %s >" %
                             (self.MODE_CONFIG))
            return self.err.eams_config_problem_err()

        logging.info(
            "===============================================================")

        return 0

    def _populateRoomConfig(self):
        self.RC = RoomConfig(self.WALL_RESISTANCE_LOW,
                             self.WALL_RESISTANCE_HIGH,
                             self.WALL_CAPACITANCE_LOW,
                             self.WALL_CAPACITANCE_HIGH, self.SOLAR_GAIN_LOW,
                             self.SOLAR_GAIN_MEDIUM, self.SOLAR_GAIN_HIGH)
        ret = self.RC.loadRoomConfig(self.ROOM_CONFIG_DATA)
        if ret == 0:
            self.RC.populateRoomByZone()
            self.Z = self.RC.getRoomsInfoByZone("All")
            self.ZL = self.RC.getZoneList()
            self.RL = self.RC.getRoomList()
            self.RCL = self.RC.getRoomCapaList()
            self.RNL = self.RC.getRoomNeighboursList()
            logging.debug("Get rooms: %s" % self.Z)
            logging.debug("Get zonelist: %s" % (self.ZL))
            logging.debug("Get roomlist: %s" % (self.RL))
            logging.debug("Get room capa list: %s" % (self.RCL))
            logging.debug("Get room neighbours list: %s" % (self.RNL))

    # DEBUG_ONLY
    #         self.Z = self.RC.getRoomsInfoByZone("1")
    # DEBUG_ONLY

        return ret

    def _populateOutdoorTemperature(self):
        self.OTC = OutdoorTemperature()
        ret = self.OTC.loadOutdoorTemperature(self.OUTDOOR_TEMP_DATA)
        if ret == 0:
            # Populate outdoor temperature
            # option 1:
            #             if int(self.SCHEDULING_INTERVAL) >= 30:
            #                 self.OAT = self.OTC.getSingleDayOutdoorTemperature(self.SCHEDULING_START_DATETIME, self.SCHEDULING_END_DATETIME, self.SCHEDULING_INTERVAL)
            #             else:
            #                 self.OAT = self.OTC.getSingleDayOutdoorTemperatureShortInterval(self.SCHEDULING_START_DATETIME, self.SCHEDULING_END_DATETIME, self.SCHEDULING_INTERVAL)
            # option 2:
            self.OAT = self.OTC.getOutdoorTemperature(
                self.SCHEDULING_START_DATETIME, self.SCHEDULING_END_DATETIME,
                self.SCHEDULING_INTERVAL)

            logging.debug("OAT: %s" % (self.OAT))

            #             for k, v in self.OAT.iteritems():
            #                 logging.debug("[%s]:[%s]" %(k,v))

            logging.debug("len: %s" % (len(self.OAT)))
            if len(self.OAT) == 0:
                return self.err.eams_no_outdoor_temp_in_scheduling_range()

            # Set initial temperature is set to outdoor temperature at first slot
#             option 1: override eams_probN.cfg INITIAL_TEMPERATURE
            if self.INITIAL_TEMPERATURE_MODE == '1':
                logging.debug("Initial OAT: %s" % (self.OAT.get(
                    datetime.strptime(self.SCHEDULING_START_DATETIME,
                                      "%Y-%m-%d %H:%M"))))
                self.INITIAL_TEMPERATURE = float(
                    self.OAT.get(
                        datetime.strptime(self.SCHEDULING_START_DATETIME,
                                          "%Y-%m-%d %H:%M")))
            # option 2:
#             self.INITIAL_TEMPERATURE = self.INITIAL_TEMPERATURE

            logging.debug("Set Initial Temperature : %.2f" %
                          (self.INITIAL_TEMPERATURE))
        return ret

    def _populateMeetingConfig(self):
        self.MC = MeetingConfig()
        ret = self.MC.loadMeetings(self.MEETINGS_CONFIG_DATA)
        if ret == 0:
            mr = self.MC.getMeetings()

            self.M = Meeting()
            #             if self.MODE_CONFIG == 'RA':
            #                 self.M.populateMeetingsWithFixedScheduleForRoomAlloc(mr)
            #             elif self.MODE_CONFIG == 'FIXED':
            #                 self.M.populateMeetingsWithFixedScheduleFixedRoom(mr)
            #             else:
            ret = self.M.populateMeetingsForRoomAllocNSchedule(mr)
            if ret < 0:
                return ret

            self.ML = self.M.getMeetingsList()
            logging.debug("Total number of meetings: %d" % (len(self.ML)))
            logging.debug(self.ML)

        return ret

    def _populateSchedulingTimeSlot(self):
        """Form timeslot based on given work week and time slot interval"""
        self.TSC = TimeSlot(self.SCHEDULING_START_DATETIME,
                            self.SCHEDULING_END_DATETIME,
                            (int)(self.SCHEDULING_INTERVAL))
        self.TS = self.TSC.getTimeSlots()
        logging.debug("Timeslot: %s" % self.TS)
        logging.debug("Total number of timeslot: %d" % len(self.TS))

    def _populateHVACStdTimeslot(self):
        """Populate binary indicator list which denotes if the timeslot is in standard operating hour of HVAC (i.e. always on) """
        self.SH = []
        if self.HVAC_NON_PEAK_OFF == '1':
            s = datetime.strptime(self.HVAC_SHUT_DOWN, "%H:%M")
            e = datetime.strptime(self.HVAC_TURN_ON, "%H:%M")

            for _, v in self.TS.iteritems():
                if (v.time() >= s.time() or v.time() < e.time()):
                    self.SH.append(0)  #off HVAC
                else:
                    self.SH.append(1)
        else:
            for _, v in self.TS.iteritems():
                self.SH.append(1)

        logging.debug("HVAC Standard Hours:\n%s" % (self.SH))

    def _populateMeetingRequestTimeslot(self):
        """Populate slot index of meetings' time windows """
        self.MTW = []
        for i in xrange(len(self.ML)):
            self.MTW.append([])
            for j in xrange(len(self.ML[i].TimeWindows)):
                s = self.ML[i].TimeWindows[j][0]
                e = self.ML[i].TimeWindows[j][1]
                sidx = self.TSC.getTimeSlotIdxByDatetime(s)
                eidx = self.TSC.getTimeSlotIdxByDatetime(e)

                if sidx is None:
                    logging.error(
                        "Meeting request falls outside of timeslot range. HALT."
                    )
                    return self.err.eams_meeting_out_of_timeslot_range()
                if eidx is None:
                    logging.error("Meeting ends outside of timeslot range. ")
                    return self.err.eams_meeting_out_of_timeslot_range()

                logging.debug(
                    "Meeting %s start at %s [slot %d], deadline at %s[slot %d], duration of %s slot(s)"
                    % (self.ML[i].Key, s, sidx, e, eidx - 1,
                       self.ML[i].Duration))

                #                 logging.debug("sidx=%s eidx=%s d=%d" %(sidx, eidx-1, int(self.ML[i].Duration)))
                if (eidx - sidx) < int(self.ML[i].Duration):
                    logging.error(
                        "Meeting duration longer than Earliest-Start-Time to Latest-Finished-Time timeslot. HALT."
                    )
                    return self.err.eams_meeting_invalid_duration()

                self.MTW[i].append([
                    sidx, eidx - 1
                ])  #Note: Meeting end at the slot before 'End' time, hence -1

        logging.debug("Meetings' time windows:")
        logging.debug(self.MTW)
        return 0

    def _populateFeasibleRoomsForMeeting(self):
        """Does location l has the capacity to accommodate meeting midx? """

        logging.info("Populate feasible rooms for meeting(s)...")

        self.MR = []
        for i in xrange(len(self.ML)):
            self.MR.append([])

            a = len(self.ML[i].Attendees)

            if not self.ML[i].Room:  # no preferred room
                for j in xrange(len(self.RCL)):
                    if a <= self.RCL[j]:
                        self.MR[i].append(j)

                if not self.MR[i]:
                    logging.error("No feasible room for meeting [%d]" % i)
                    return self.err.eams_meeting_no_feasible_room()
            else:  # has preferred room
                if self.ML[i].Room not in self.RL:
                    logging.critical(
                        "Mode Config: %s. Preferred room [%s] for meeting [%s] does not exist in room list."
                        % (self.MODE_CONFIG, self.ML[i].Room, self.ML[i].Key))
                    return self.err.eams_config_meeting_err()

                ridx = self.RL.index(self.ML[i].Room)
                if len(self.ML[i].Attendees) > self.RCL[ridx]:
                    logging.critical(
                        "Mode Config: %s. Preferred room [%s] for meeting [%s] has smaller capacity (only for %d people) than the number of attendee."
                        % (self.MODE_CONFIG, self.ML[i].Room, self.ML[i].Key,
                           self.RCL[ridx]))
                    return self.err.eams_config_meeting_err()

                self.MR[i].append(
                    ridx)  # limit feasible room to preferred room

        logging.debug(
            "Feasible rooms based on meetings' attendees and room capacity:")
        logging.debug(self.MR)
        return 0

    def _populateMeetingsWithSimilarAttendees(self):
        """Does meeting m has similar attendee(s) as meeting om"""

        self.MOM = []
        for mx in xrange(len(self.ML)):
            self.MOM.append([])
            mx_multiset = Counter(self.ML[mx].Attendees)
            for my in xrange(mx + 1, len(self.ML)):
                #                 logging.debug("checking m[%d, %d]" %(mx, my))
                my_multiset = Counter(self.ML[my].Attendees)
                if (list((mx_multiset & my_multiset).elements())):
                    logging.debug("Meeting %d and %d have similar attendees" %
                                  (mx, my))
                    self.MOM[mx].append(my)

        logging.debug("Meetings which has similar attendee(s)")
        logging.debug(self.MOM)

    def _populateRoomThermalCfg(self):
        """Populate individual room config to be used by Gurobi"""
        self.RTC = RoomThermalCfg(self.Z, self.TS,
                                  self.SOLAR_RADIATION_AM_START,
                                  self.SOLAR_RADIATION_NOON_START,
                                  self.SOLAR_RADIATION_PM_START,
                                  self.SOLAR_RADIATION_NIGHT_START)

    def _populateProbData(self, filename):
        ret = 0
        try:
            # Load problem configuration
            config = ConfigObj(filename,
                               configspec=self.PROBLEM_CONFIG_SPEC,
                               file_error=True)
            ret = self._validateProblemConfig(config)
            if ret < 0:
                raise ValidateError()
            self._loadProblemConfig(config)
            ret = self._diagMode()
            if ret < 0:
                raise ValueError("Invalid problem configuration.")

            # Initialize & load room, meeting, timeslot, outdoor temperature etc information
            ret = self._populateRoomConfig()
            if ret < 0:
                raise ValueError("Invalid room configuration.")

            ret = self._populateMeetingConfig()
            if ret < 0:
                raise ValueError("Invalid meeting configuration.")

            ret = self._populateOutdoorTemperature()
            if ret < 0:
                raise ValueError("Invalid outdoor temperature configuration.")

            self._populateSchedulingTimeSlot()

            ret = self._populateMeetingRequestTimeslot()
            if ret < 0:
                raise ValueError("Meeting request out of timeslot range.")

            ret = self._populateFeasibleRoomsForMeeting()
            if ret < 0:
                raise ValueError("Invalid room configuration for a meeting.")

            self._populateHVACStdTimeslot()
            self._populateMeetingsWithSimilarAttendees()
            self._populateRoomThermalCfg()

        except (ConfigObjError, IOError), e:
            logging.critical('%s' % (e))
            return self.err.eams_config_problem_err()
        except (ValidateError), e:
            logging.critical("%s validation error. %d" % (filename, ret))