Skip to content

Commit

Permalink
feat(room): Finish adding RoomDoe2Properties to assign HVAC parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
chriswmackey committed May 4, 2024
1 parent 4101ae0 commit a3f15e3
Show file tree
Hide file tree
Showing 6 changed files with 357 additions and 13 deletions.
25 changes: 25 additions & 0 deletions honeybee_doe2/_extend_honeybee.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,40 @@
# coding=utf-8
# import all of the modules for writing geometry to INP
from honeybee.properties import ModelProperties, RoomProperties
import honeybee.writer.shademesh as shade_mesh_writer
import honeybee.writer.door as door_writer
import honeybee.writer.aperture as aperture_writer
import honeybee.writer.shade as shade_writer
import honeybee.writer.face as face_writer
import honeybee.writer.room as room_writer
import honeybee.writer.model as model_writer

from .properties.model import ModelDoe2Properties
from .properties.room import RoomDoe2Properties
from .writer import model_to_inp, room_to_inp, face_to_inp, shade_to_inp, \
aperture_to_inp, door_to_inp, shade_mesh_to_inp

# set a hidden doe2 attribute on each core geometry Property class to None
# define methods to produce doe2 property instances on each Property instance
ModelProperties._doe2 = None
RoomProperties._doe2 = None

def model_doe2_properties(self):
if self._doe2 is None:
self._doe2 = ModelDoe2Properties(self.host)
return self._doe2


def room_doe2_properties(self):
if self._doe2 is None:
self._doe2 = RoomDoe2Properties(self.host)
return self._doe2

# add doe2 property methods to the Properties classes
ModelProperties.doe2 = property(model_doe2_properties)
RoomProperties.doe2 = property(room_doe2_properties)


# add writers to the honeybee-core modules
model_writer.inp = model_to_inp
room_writer.inp = room_to_inp
Expand Down
52 changes: 52 additions & 0 deletions honeybee_doe2/properties/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# coding=utf-8
"""Model DOE-2 Properties."""


class ModelDoe2Properties(object):
"""DOE-2 Properties for Honeybee Model.
Args:
host: A honeybee_core Model object that hosts these properties.
Properties:
* host
"""

def __init__(self, host):
"""Initialize ModelDoe2Properties."""
self._host = host

@property
def host(self):
"""Get the Model object hosting these properties."""
return self._host

def to_dict(self):
"""Return Model DOE-2 properties as a dictionary."""
return {'doe2': {'type': 'ModelDoe2Properties'}}

def apply_properties_from_dict(self, data):
"""Apply the energy properties of a dictionary to the host Model of this object.
Args:
data: A dictionary representation of an entire honeybee-core Model.
Note that this dictionary must have ModelEnergyProperties in order
for this method to successfully apply the energy properties.
"""
assert 'doe2' in data['properties'], \
'Dictionary possesses no ModelDoe2Properties.'
room_doe2_dicts = []
for room_dict in data['rooms']:
try:
room_doe2_dicts.append(room_dict['properties']['doe2'])
except KeyError:
room_doe2_dicts.append(None)
for room, r_dict in zip(self.host.rooms, room_doe2_dicts):
if r_dict is not None:
room.properties.doe2.apply_properties_from_dict(r_dict)

def ToString(self):
return self.__repr__()

def __repr__(self):
return 'Model DOE2 Properties: [host: {}]'.format(self.host.display_name)
97 changes: 84 additions & 13 deletions honeybee_doe2/properties/room.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# coding=utf-8
"""Room DOE-2 Properties."""
from honeybee.typing import float_in_range, float_positive
# ASSIGNED-FLOW, FLOW/AREA, MIN-FLOW-RATIO, MIN-FLOW/AREA, HMAX-FLOW-RATIO
from honeybee.altnumber import autocalculate


class RoomDoe2Properties(object):
Expand Down Expand Up @@ -37,11 +37,12 @@ class RoomDoe2Properties(object):
* min_flow_per_area
* hmax_flow_ratio
"""

__slots__ = (
'_host', '_assigned_flow', '_flow_per_area', '_min_flow_ratio',
'_min_flow_per_area', '_hmax_flow_ratio'
)
INP_ATTR = ('ASSIGNED-FLOW', 'FLOW/AREA', 'MIN-FLOW-RATIO',
'MIN-FLOW/AREA', 'HMAX-FLOW-RATIO')

def __init__(self, host, assigned_flow=None, flow_per_area=None, min_flow_ratio=None,
min_flow_per_area=None, hmax_flow_ratio=None):
Expand Down Expand Up @@ -140,34 +141,104 @@ def from_dict(cls, data, host):
assert data['type'] == 'RoomDoe2Properties', \
'Expected RoomDoe2Properties. Got {}.'.format(data['type'])
new_prop = cls(host)
if 'assigned_flow' in data:
auto_dict = autocalculate.to_dict()
if 'assigned_flow' in data and data['assigned_flow'] != auto_dict:
new_prop.assigned_flow = data['assigned_flow']
if 'flow_per_area' in data:
if 'flow_per_area' in data and data['flow_per_area'] != auto_dict:
new_prop.flow_per_area = data['flow_per_area']
if 'min_flow_ratio' in data:
if 'min_flow_ratio' in data and data['min_flow_ratio'] != auto_dict:
new_prop.min_flow_ratio = data['min_flow_ratio']
if 'min_flow_per_area' in data:
if 'min_flow_per_area' in data and data['min_flow_per_area'] != auto_dict:
new_prop.min_flow_per_area = data['min_flow_per_area']
if 'hmax_flow_ratio' in data:
if 'hmax_flow_ratio' in data and data['hmax_flow_ratio'] != auto_dict:
new_prop.hmax_flow_ratio = data['hmax_flow_ratio']
return new_prop

def to_dict(self):
def apply_properties_from_dict(self, data):
"""Apply properties from a RoomDoe2Properties dictionary.
Args:
data: A RoomDoe2Properties dictionary (typically coming from a Model).
"""
auto_dict = autocalculate.to_dict()
if 'assigned_flow' in data and data['assigned_flow'] != auto_dict:
self.assigned_flow = data['assigned_flow']
if 'flow_per_area' in data and data['flow_per_area'] != auto_dict:
self.flow_per_area = data['flow_per_area']
if 'min_flow_ratio' in data and data['min_flow_ratio'] != auto_dict:
self.min_flow_ratio = data['min_flow_ratio']
if 'min_flow_per_area' in data and data['min_flow_per_area'] != auto_dict:
self.min_flow_per_area = data['min_flow_per_area']
if 'hmax_flow_ratio' in data and data['hmax_flow_ratio'] != auto_dict:
self.hmax_flow_ratio = data['hmax_flow_ratio']

def apply_properties_from_user_data(self):
"""Apply properties from a the user_data assigned to the host room.
For this method to successfully assign properties from user_data, the
properties on this object must currently be None and the keys in
user_data must use the INP convention for each of the attributes,
which must be CAPITALIZED like the following:
.. code-block:: python
{
"ASSIGNED-FLOW": 100, # number in cfm
"FLOW/AREA": 1, # number in cfm/ft2
"MIN-FLOW-RATIO": 0.3, # number between 0 and 1
"MIN-FLOW/AREA": 0.3, # number in cfm/ft2
"HMAX-FLOW-RATIO": 0.3 # number between 0 and 1
}
"""
attrs = ('assigned_flow', 'flow_per_area', 'min_flow_ratio',
'min_flow_per_area', 'hmax_flow_ratio')
data = self.host.user_data
if data is not None:
for key, attr in zip(self.INP_ATTR, attrs):
if key in data and getattr(self, attr) is None:
try:
setattr(self, attr, data[key])
except Exception:
pass # it's user_data; users are allowed to make mistakes

def to_dict(self, abridged=False):
"""Return Room Doe2 properties as a dictionary."""
base = {'doe2': {}}
base['doe2']['type'] = 'RoomDoe2Properties'
if self.assigned_flow is not None:
base['assigned_flow'] = self.assigned_flow
base['doe2']['assigned_flow'] = self.assigned_flow
if self.flow_per_area is not None:
base['flow_per_area'] = self.flow_per_area
base['doe2']['flow_per_area'] = self.flow_per_area
if self.min_flow_ratio is not None:
base['min_flow_ratio'] = self.min_flow_ratio
base['doe2']['min_flow_ratio'] = self.min_flow_ratio
if self.min_flow_per_area is not None:
base['min_flow_per_area'] = self.min_flow_per_area
base['doe2']['min_flow_per_area'] = self.min_flow_per_area
if self.hmax_flow_ratio is not None:
base['hmax_flow_ratio'] = self.hmax_flow_ratio
base['doe2']['hmax_flow_ratio'] = self.hmax_flow_ratio
return base

def to_inp(self):
"""Get RoomDoe2Properties as INP (Keywords, Values).
Returns:
A tuple with two elements.
- keywords: A list of text strings for keywords to assign to the room.
- values: A list of text strings that aligns with the keywords and
denotes the value for each keyword.
"""
keywords = []
values = []
attrs = ('assigned_flow', 'flow_per_area', 'min_flow_ratio',
'min_flow_per_area', 'hmax_flow_ratio')
for key, attr in zip(self.INP_ATTR, attrs):
attr_value = getattr(self, attr)
if attr_value is not None:
keywords.append(key)
values.append(attr_value)
return keywords, values

def duplicate(self, new_host=None):
"""Get a copy of this object.
Expand Down
6 changes: 6 additions & 0 deletions honeybee_doe2/writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,9 @@ def model_to_inp(
)
# reset identifiers to make them unique and derived from the display names
model.reset_ids()
# assign attributes from user_data
for room in model.rooms:
room.properties.doe2.apply_properties_from_user_data()

# write the simulation parameters into the string
model_str = ['INPUT ..\n\n']
Expand Down Expand Up @@ -848,6 +851,9 @@ def model_to_inp(
vt_kwd, vt_val = ventilation_to_inp(room.properties.energy.ventilation)
zone_keys.extend(vt_kwd)
zone_vals.extend(vt_val)
hvac_kwd, hvac_val = room.properties.doe2.to_inp()
zone_keys.extend(hvac_kwd)
zone_vals.extend(hvac_val)
zone_def = generate_inp_string(zone_name, 'ZONE', zone_keys, zone_vals)
model_str.append(zone_def)

Expand Down
35 changes: 35 additions & 0 deletions tests/model_extend_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""Tests the features that honeybee_doe2 adds to honeybee_core Model."""
from honeybee.room import Room
from honeybee.model import Model
from honeybee_energy.lib.programtypes import office_program


def test_from_dict():
"""Test the Room from_dict method with doe2 properties."""
room = Room.from_box('ShoeBox', 5, 10, 3)
room.properties.energy.program_type = office_program
room.properties.energy.add_default_ideal_air()
south_face = room[3]
south_face.apertures_by_ratio(0.4, 0.01)
south_face.apertures[0].overhang(0.5, indoor=False)
south_face.apertures[0].overhang(0.5, indoor=True)

room.properties.doe2.assigned_flow = 100
room.properties.doe2.flow_per_area = 1
room.properties.doe2.min_flow_ratio = 0.3
room.properties.doe2.min_flow_per_area = 0.35
room.properties.doe2.hmax_flow_ratio = 0.5

model = Model('Tiny_House', [room])


model_dict = model.to_dict()
new_model = Model.from_dict(model_dict)
assert new_model.to_dict() == model_dict

new_room = new_model.rooms[0]
assert new_room.properties.doe2.assigned_flow == 100
assert new_room.properties.doe2.flow_per_area == 1
assert new_room.properties.doe2.min_flow_ratio == 0.3
assert new_room.properties.doe2.min_flow_per_area == 0.35
assert new_room.properties.doe2.hmax_flow_ratio == 0.5
Loading

0 comments on commit a3f15e3

Please sign in to comment.