def add_objective(self, building): """Creates and adds the meter needed by this objective to the building. :param building: the building to modify :return: None """ try: self.get_objective(building) except ValueError: pass # the objective is not present else: raise ValueError(f'Objective for {repr(self)} already exists') mode = ef.get_mode(building) new_object_dict = { ef.convert_format(field, 'field', mode): getattr(self, attr) for attr, field in self.field_pairs if getattr(self, attr) } if self.frequency is None: new_object_dict[ef.convert_format('Reporting_Frequency', 'field', mode)] = 'Hourly' if mode == 'idf': building.newidfobject(key=ef.convert_format( self.class_name, 'class', mode), **new_object_dict) elif mode == 'json': # this is equivalent to appending, but e+ uses a dictionary instead of a list objectives = building[self.class_name] num = len(objectives) new_key = f'{self.class_name} {num}' assert new_key not in building, f'The building has incorrectly numbered {self.class_name} entries' objectives[new_key] = new_object_dict
def get_objective(self, building): mode = ef.get_mode(building) if mode == 'idf': objectives = building.idfobjects[ef.convert_format( self.class_name, 'class', mode)] elif mode == 'json': objectives = building[ef.convert_format(self.class_name, 'class', mode)].values() else: raise ModeError(mode) for objective in objectives: if self.check_all(objective, mode): return objective raise ValueError(f'Cannot find the objective for {repr(self)}')
def get(self, building) -> List: """Gets the current values of this field from a building :param building: the building to retrieve values from :return: a list containing the current values of this selector's fields """ mode = ef.get_mode(building) objects = self.get_objects(building) field_name = ef.convert_format(self.field_name, 'field', mode) if mode == 'idf': return [getattr(o, field_name) for o in objects] if mode == 'json': return [o[field_name] for o in objects]
def set(self, building, value) -> None: """Sets this field in the building to the provided value :param building: the building to modify :param value: the value to set this field to :return: """ mode = ef.get_mode(building) objects = self.get_objects(building) field_name = ef.convert_format(self.field_name, 'field', mode) if mode == 'idf': for o in objects: setattr(o, field_name, value) if mode == 'json': for o in objects: assert field_name in o, f'{field_name} not in {repr(o)}' o[field_name] = value
def check_all(self, objective, mode): if mode == 'idf': def get(attribute): return getattr(objective, attribute) elif mode == 'json': def get(attribute): return objective[attribute] else: raise ModeError(mode) for self_attr, objective_attr in self.field_pairs: self_value = getattr(self, self_attr) objective_value = get( ef.convert_format(objective_attr, 'field', mode)) if self_value is not None and objective_value != self_value: return False return True
def find(prefix): return { x for x in building_items if x.startswith(ef.convert_format(prefix, 'class', mode)) }
def get_objects(self, building) -> List: """Retrieves the objects that this selector will affect from the building. :param building: the building to search :return: a list of the objects found """ mode = ef.get_mode(building) if mode == 'idf': if self.class_name is not None: class_name = ef.convert_format(self.class_name, 'class', 'idf') else: class_name = None if self.object_name == '*': if class_name is None: raise TypeError( "When object_name='*', class_name must be specified.") return building.idfobjects[class_name] if self.object_name and class_name: # this is probably the most reliable way to select an idfObject. return [ building.getobject(key=class_name, name=self.object_name) ] if self.object_name: # There should only one object matching the name, assuming the idf is valid return [ eppySupport.get_idfobject_from_name( building, self.object_name) ] if class_name is not None: # assume that we want the first object matching the key # TODO: is this specific enough, or should we remove it? our JSON code does not support this return [building.idfobjects[class_name][0]] else: # we have neither object_name nor class_name raise TypeError( 'Either class_name or object_name must be specified.') elif mode == 'json': if self.object_name == '*': if not self.class_name: raise TypeError( "When object_name='*', class_name must be specified.") return list(building[self.class_name].values()) if self.object_name and self.class_name: # this is probably the most reliable way to select an idfObject. return [building[self.class_name][self.object_name]] if self.object_name: # There should only one object matching the name, assuming the building is valid result = [ obj for objs in building.values() for name, obj in objs.items() if name == self.object_name ] if len(result) != 1: warnings.warn( f'found {len(result)} objects with object_name: {self.object_name}, expected 1' ) return result if self.class_name: result = list(building[self.class_name].items()) if len(result) == 1: return result raise ValueError( f'multiple objects with class_name {self.class_name}.' f'Cannot guarantee a reliable ordering') else: # we have neither object_name nor class_name raise TypeError( 'Either class_name or object_name must be specified.') raise ModeError(mode)