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 clear_outputs(building, outputs: Union[str, List[str]] = 'all') -> None: """Disable certain types of output. :param building: the building to modify :param outputs: Can be the class_name of the output to clear, a shortcut for different types of output, or a list that combines the above. :return: None """ # `empty` is used so that different fields do not reference each the same container mode = ef.get_mode(building) if mode == 'idf': building_items = building.idfobjects empty = list elif mode == 'json': building_items = building empty = dict else: raise ModeError(mode) def find(prefix): return { x for x in building_items if x.startswith(ef.convert_format(prefix, 'class', mode)) } # These lists are probably incomplete! class_names = dict() # ENVIRONMENTALIMPACTFACTORS adds a bunch of meters, but is not a meter itself class_names['output_meters'] = find('Output:Meter:') | { 'Output:Meter', 'Output:EnvironmentalImpactFactors' } class_names['internal_meters'] = {'Meter:Custom', 'Meter:CustomDecrement'} class_names['tables'] = find('Output:Table:') class_names['output'] = find('Output:') class_names['outputcontrol'] = find('OutputControl:') class_names['meters'] = class_names['output_meters'] | class_names[ 'internal_meters'] if outputs == 'all': outputs = chain(*class_names.values()) else: if isinstance(outputs, str): # make outputs a list outputs = [outputs] # convert each element in outputs via the class_names shortcuts outputs = set( chain(*(class_names.get(output, [output]) for output in outputs))) # clear the selected outputs for output in outputs: building_items[output] = empty()
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 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 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 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)