Source code for dragonfly_energy.measure

# coding=utf-8
"""Mange OpenStudio measures that can be mapped to different buildings in a model."""
from __future__ import division

import os
import xml.etree.ElementTree as ElementTree
from honeybee_energy.measure import Measure, MeasureArgument


[docs]class MapperMeasure(Measure): """An OpenStudio measure that can be mapped to different buildings in a model. Args: folder: Path to the folder in which the measure exists. This folder must contain a measure.rb and a measure.xml file. Other files are optional. Properties: * folder * metadata_file * program_file * resources_folder * identifier * display_name * description * type * arguments """ __slots__ = () def __init__(self, folder): """Initialize MapperMeasure.""" Measure.__init__(self, folder)
[docs] @classmethod def from_dict(cls, data, folder='.'): """Initialize a MapperMeasure from a dictionary. Args: data: A dictionary in the format below. folder: Path to a destination folder to save the measure files. (Default '.') .. code-block:: python { "type": "MapperMeasure", "identifier": string, # Measure identifier "xml_data": string, # XML file data as string "rb_data": string, # Ruby file data as string "resource_data": {}, # Dictionary of strings for any resource ruby files "argument_values": [], # List of values for each of the measure arguments } """ assert data['type'] == 'MapperMeasure', \ 'Expected MapperMeasure dictionary. Got {}.'.format(data['type']) fp = os.path.join(folder, data['identifier']) if not os.path.isdir(fp): os.makedirs(fp) # write out the contents of the measure xml_fp = os.path.join(fp, 'measure.xml') cls._decompress_to_file(data['xml_data'], xml_fp) rb_fp = os.path.join(fp, 'measure.rb') cls._decompress_to_file(data['rb_data'], rb_fp) if 'resource_data' in data and data['resource_data'] is not None: resource_path = os.path.join(fp, 'resources') os.makedirs(resource_path) for f_name, res in data['resource_data'].items(): res_fp = os.path.join(resource_path, f_name) cls._decompress_to_file(res, res_fp) # create the measure object and assign the arguments new_measure = cls(fp) for arg, val in zip(new_measure.arguments, data['argument_values']): if val is not None: arg.value = val return new_measure
[docs] def to_dict(self): """Convert MapperMeasure to a dictionary.""" base = Measure.to_dict(self) base['type'] = 'MapperMeasure' return base
[docs] def to_osw_dict(self, full_path=False): """Get a Python dictionary that can be written to an OSW JSON. Specifically, this dictionary can be appended to the "steps" key of the OpenStudio Workflow (.osw) JSON dictionary in order to include the measure in the workflow. Note that this method does not perform any checks to validate that the Measure has all required values and only arguments with values will be included in the dictionary. Validation should be done separately with the validate method. Args: full_path: Boolean to note whether the full path to the measure should be written under the 'measure_dir_name' key or just the measure base name. (Default: False) """ meas_dir = self.folder if full_path else os.path.basename(self.folder) base = {'measure_dir_name': meas_dir, 'arguments': {}} for arg in self._arguments: if arg.value is not None: base['arguments'][arg.identifier] = arg.value \ if not isinstance(arg.value, tuple) else arg.value[0] return base
def _parse_metadata_file(self): """Parse measure properties from the measure.xml file.""" # create an element tree object tree = ElementTree.parse(self._metadata_file) root = tree.getroot() # parse the measure properties from the element tree self._identifier = root.find('name').text self._display_name = root.find('display_name').text self._description = root.find('description').text self._type = None for atr in root.find('attributes'): if atr.find('name').text == 'Measure Type': self._type = atr.find('value').text # parse the measure arguments self._arguments = [] for arg in root.find('arguments'): arg_obj = MapperMeasureArgument(arg) if arg_obj.model_dependent: # TODO: Figure out how to implement model-dependent arguments raise NotImplementedError( 'Model dependent arguments are not yet supported and measure ' 'argument is "{}" model dependent.'.format(arg_obj.identifier)) self._arguments.append(arg_obj) def __repr__(self): return 'MapperMeasure: {}'.format(self.display_name)
[docs]class MapperMeasureArgument(MeasureArgument): """Object representing a single mapper measure argument. Args: xml_element: A Python XML Element object taken from the <arguments> section of the measure.xml file. Properties: * identifier * display_name * value * default_value * type * type_text * required * description * model_dependent * valid_choices """ __slots__ = () def __init__(self, xml_element): """Initialize MeasureArgument.""" MeasureArgument.__init__(self, xml_element) @property def value(self): """Get or set the value or list of values for the argument. When using a list, the length of it must match the number of buildings in the dragonfly Model and each value corresponds to a building under the Model.buildings property. If not set, this will be equal to the default_value and, if no default value is included for this argument, it will be None. """ if self._value is not None: return self._value return self._default_value @value.setter def value(self, val): if val is not None: e_msg = 'Value for measure argument "' + self.identifier + \ '" must be a {}. Got {}' if not isinstance(val, (list, tuple)): val = (val,) try: val = tuple(self._type(v) for v in val) except Exception: raise TypeError(e_msg.format(self._type, type(val))) if self._valid_choices: assert all(v in self._valid_choices for v in val), \ 'Choice measure argument "{}" ' \ 'must be one of the following:\n{}\nGot {}'.format( self.identifier, self._valid_choices, val) self._value = val if val is None or len(val) != 1 else val[0]