Source code for honeybee_energy.result.match

# coding=utf-8
"""Utilities for matching Model geometry with energy simulation results."""
import re

from honeybee.door import Door
from honeybee.aperture import Aperture
from honeybee.face import Face


[docs]def match_rooms_to_data( data_collections, rooms, invert_multiplier=False, space_based=False): """Match honeybee Rooms to the Zone-level data collections from SQLiteResult. This method ensures that Room multipliers are correctly output for a given EnergyPlus output. Args: data_collections: An array of data collections that will matched to simulation results. Data collections can be of any class (eg. MonthlyCollection, DailyCollection) but they should all have headers with metadata dictionaries with 'Zone' or 'System' keys. These keys will be used to match the data in the collections to the input rooms. rooms: An array of honeybee Rooms, which will be matched to the data_collections. The length of these Rooms does not have to match the data_collections. invert_multiplier: Boolean to note whether the output room multiplier should be included when the data type values already account for the multiplier (False) or when they do not (True). (Default: False). space_based: Boolean to note whether the result is reported on the EnergyPlus Space level instead of the Zone level. In this case, the matching to the Room will account for the fact that the Space name is the Room name with _Space added to it. (Default: False). Returns: An array of tuples that contain matched rooms and data collections. All tuples have a length of 3 with the following: - room -- A honeybee Room object. - data_collection -- A data collection that matches the honeybee Room. - multiplier -- An integer for the Room multiplier, which may be useful for calculating total results. """ # extract the zone identifier from each of the data collections zone_ids = [] use_mult = False for data in data_collections: if 'Zone' in data.header.metadata: zone_ids.append(data.header.metadata['Zone']) else: # it's HVAC system data and we need to see if it's matchable hvac_id = data.header.metadata['System'] use_mult = True if ' IDEAL LOADS AIR SYSTEM' in hvac_id: # convention of E+ HVAC Templates zone_ids.append(hvac_id.split(' IDEAL LOADS AIR SYSTEM')[0]) elif '..' in hvac_id: # convention used for service hot water zone_ids.append(hvac_id.split('..')[-1]) use_mult = False if 'Gain' in data.header.metadata['type'] else True elif '_IDEALAIR' in hvac_id: # TODO: Remove once test files are updated zone_ids.append(hvac_id.split('_IDEALAIR')[0]) else: use_mult = False zone_ids.append(hvac_id) if invert_multiplier: use_mult = not use_mult if space_based: zone_ids = [zid.replace('_SPACE', '') for zid in zone_ids] # loop through the rooms and match the data to them matched_tuples = [] # list of matched rooms and data collections for room in rooms: rm_id = room.identifier.upper() for i, data_id in enumerate(zone_ids): if data_id == rm_id: mult = 1 if not use_mult else room.multiplier matched_tuples.append((room, data_collections[i], mult)) break return matched_tuples
[docs]def match_faces_to_data(data_collections, faces): """Match honeybee faces/sub-faces to data collections from SQLiteResult. This method will correctly match triangulated apertures and doors with a merged version of the relevant data_collections. Args: data_collections: An array of data collections that will be matched to the input faces Data collections can be of any class (eg. MonthlyCollection, DailyCollection) but they should all have headers with metadata dictionaries with 'Surface' keys. These keys will be used to match the data in the collections to the input faces. faces: An array of honeybee Faces, Apertures, and/or Doors which will be matched to the data_collections. Note that a given input Face should NOT have its child Apertures or Doors as a separate item. Returns: An array of tuples that contain matched faces/sub-faces and data collections. All tuples have a length of 2 with the following: - face -- A honeybee Face, Aperture, or Door object. - data_collection -- A data collection that matches the Face, Aperture, or Door. """ matched_tuples = [] # list of matched faces and data collections flat_f = [] # flatten the list of nested apertures and doors in the faces for face in faces: if isinstance(face, Face): flat_f.append(face) for ap in face.apertures: flat_f.append(ap) for dr in face.doors: flat_f.append(dr) elif isinstance(face, (Aperture, Door)): flat_f.append(face) else: raise ValueError('Expected honeybee Face, Aperture, Door or Shade ' 'for match_faces_to_data. Got {}.'.format(type(face))) # extract the surface id from each of the data collections srf_ids = [] tri_srf_ids = {} # track data collections from triangulated apertures/doors tri_pattern = re.compile(r".*\.\.\d") for data in data_collections: if 'Surface' in data.header.metadata: srf_ids.append(data.header.metadata['Surface']) if tri_pattern.match(data.header.metadata['Surface']) is not None: base_name = re.sub(r'(\.\.\d*)', '', data.header.metadata['Surface']) try: tri_srf_ids[base_name].append(data) except KeyError: # first triangulated piece found tri_srf_ids[base_name] = [data] # loop through the faces and match the data to them for face in flat_f: f_id = face.identifier.upper() for i, data_id in enumerate(srf_ids): if data_id == f_id: matched_tuples.append((face, data_collections[i])) break else: # check to see if it's a triangulated sub-face try: data_colls = tri_srf_ids[f_id] matched_tuples.append( (face, _merge_collections(data_colls, f_id))) except KeyError: pass # the face could not be matched with any data return matched_tuples
def _merge_collections(data_collections, surface_id): """Combine several data collections for a triangulated surface into one. This method will automatically decide to either sum the collection data or average it depending on whether the data collection data_type is cumulative. Args: data_collections: A list of data collections to be merged. surface_id: The identifier of the combined surface, encompassing all triangulated pieces. """ # total all of the values in the data collections merged_data = data_collections[0] for data in data_collections[1:]: merged_data = merged_data + data # divide by the number of collections if the data type is not cumulative if not data_collections[0].header.data_type.cumulative: merged_data = merged_data / len(data_collections) # create the final data collection merged_data = merged_data.duplicate() # duplicate to avoid editing the header merged_data.header.metadata['Surface'] = surface_id return merged_data