honeybee-energy 1.116.106__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- honeybee_energy/__init__.py +24 -0
- honeybee_energy/__main__.py +4 -0
- honeybee_energy/_extend_honeybee.py +145 -0
- honeybee_energy/altnumber.py +21 -0
- honeybee_energy/baseline/__init__.py +2 -0
- honeybee_energy/baseline/create.py +608 -0
- honeybee_energy/baseline/data/__init__.py +1 -0
- honeybee_energy/baseline/data/constructions.csv +64 -0
- honeybee_energy/baseline/data/fen_ratios.csv +15 -0
- honeybee_energy/baseline/data/lpd_building.csv +21 -0
- honeybee_energy/baseline/data/pci_2016.csv +22 -0
- honeybee_energy/baseline/data/pci_2019.csv +22 -0
- honeybee_energy/baseline/data/pci_2022.csv +22 -0
- honeybee_energy/baseline/data/shw.csv +21 -0
- honeybee_energy/baseline/pci.py +512 -0
- honeybee_energy/baseline/result.py +371 -0
- honeybee_energy/boundarycondition.py +128 -0
- honeybee_energy/cli/__init__.py +69 -0
- honeybee_energy/cli/baseline.py +475 -0
- honeybee_energy/cli/edit.py +327 -0
- honeybee_energy/cli/lib.py +1154 -0
- honeybee_energy/cli/result.py +810 -0
- honeybee_energy/cli/setconfig.py +124 -0
- honeybee_energy/cli/settings.py +569 -0
- honeybee_energy/cli/simulate.py +380 -0
- honeybee_energy/cli/translate.py +1714 -0
- honeybee_energy/cli/validate.py +224 -0
- honeybee_energy/config.json +11 -0
- honeybee_energy/config.py +842 -0
- honeybee_energy/construction/__init__.py +1 -0
- honeybee_energy/construction/_base.py +374 -0
- honeybee_energy/construction/air.py +325 -0
- honeybee_energy/construction/dictutil.py +89 -0
- honeybee_energy/construction/dynamic.py +607 -0
- honeybee_energy/construction/opaque.py +460 -0
- honeybee_energy/construction/shade.py +319 -0
- honeybee_energy/construction/window.py +1096 -0
- honeybee_energy/construction/windowshade.py +847 -0
- honeybee_energy/constructionset.py +1655 -0
- honeybee_energy/dictutil.py +56 -0
- honeybee_energy/generator/__init__.py +5 -0
- honeybee_energy/generator/loadcenter.py +204 -0
- honeybee_energy/generator/pv.py +535 -0
- honeybee_energy/hvac/__init__.py +21 -0
- honeybee_energy/hvac/_base.py +124 -0
- honeybee_energy/hvac/_template.py +270 -0
- honeybee_energy/hvac/allair/__init__.py +22 -0
- honeybee_energy/hvac/allair/_base.py +349 -0
- honeybee_energy/hvac/allair/furnace.py +168 -0
- honeybee_energy/hvac/allair/psz.py +131 -0
- honeybee_energy/hvac/allair/ptac.py +163 -0
- honeybee_energy/hvac/allair/pvav.py +109 -0
- honeybee_energy/hvac/allair/vav.py +128 -0
- honeybee_energy/hvac/detailed.py +337 -0
- honeybee_energy/hvac/doas/__init__.py +28 -0
- honeybee_energy/hvac/doas/_base.py +345 -0
- honeybee_energy/hvac/doas/fcu.py +127 -0
- honeybee_energy/hvac/doas/radiant.py +329 -0
- honeybee_energy/hvac/doas/vrf.py +81 -0
- honeybee_energy/hvac/doas/wshp.py +91 -0
- honeybee_energy/hvac/heatcool/__init__.py +23 -0
- honeybee_energy/hvac/heatcool/_base.py +177 -0
- honeybee_energy/hvac/heatcool/baseboard.py +61 -0
- honeybee_energy/hvac/heatcool/evapcool.py +72 -0
- honeybee_energy/hvac/heatcool/fcu.py +92 -0
- honeybee_energy/hvac/heatcool/gasunit.py +53 -0
- honeybee_energy/hvac/heatcool/radiant.py +269 -0
- honeybee_energy/hvac/heatcool/residential.py +77 -0
- honeybee_energy/hvac/heatcool/vrf.py +54 -0
- honeybee_energy/hvac/heatcool/windowac.py +70 -0
- honeybee_energy/hvac/heatcool/wshp.py +62 -0
- honeybee_energy/hvac/idealair.py +699 -0
- honeybee_energy/internalmass.py +310 -0
- honeybee_energy/lib/__init__.py +1 -0
- honeybee_energy/lib/_loadconstructions.py +194 -0
- honeybee_energy/lib/_loadconstructionsets.py +117 -0
- honeybee_energy/lib/_loadmaterials.py +83 -0
- honeybee_energy/lib/_loadprogramtypes.py +125 -0
- honeybee_energy/lib/_loadschedules.py +87 -0
- honeybee_energy/lib/_loadtypelimits.py +64 -0
- honeybee_energy/lib/constructions.py +207 -0
- honeybee_energy/lib/constructionsets.py +95 -0
- honeybee_energy/lib/materials.py +67 -0
- honeybee_energy/lib/programtypes.py +125 -0
- honeybee_energy/lib/schedules.py +61 -0
- honeybee_energy/lib/scheduletypelimits.py +31 -0
- honeybee_energy/load/__init__.py +1 -0
- honeybee_energy/load/_base.py +190 -0
- honeybee_energy/load/daylight.py +397 -0
- honeybee_energy/load/dictutil.py +47 -0
- honeybee_energy/load/equipment.py +771 -0
- honeybee_energy/load/hotwater.py +543 -0
- honeybee_energy/load/infiltration.py +460 -0
- honeybee_energy/load/lighting.py +480 -0
- honeybee_energy/load/people.py +497 -0
- honeybee_energy/load/process.py +472 -0
- honeybee_energy/load/setpoint.py +816 -0
- honeybee_energy/load/ventilation.py +550 -0
- honeybee_energy/material/__init__.py +1 -0
- honeybee_energy/material/_base.py +166 -0
- honeybee_energy/material/dictutil.py +59 -0
- honeybee_energy/material/frame.py +367 -0
- honeybee_energy/material/gas.py +1087 -0
- honeybee_energy/material/glazing.py +854 -0
- honeybee_energy/material/opaque.py +1351 -0
- honeybee_energy/material/shade.py +1360 -0
- honeybee_energy/measure.py +472 -0
- honeybee_energy/programtype.py +723 -0
- honeybee_energy/properties/__init__.py +1 -0
- honeybee_energy/properties/aperture.py +333 -0
- honeybee_energy/properties/door.py +342 -0
- honeybee_energy/properties/extension.py +244 -0
- honeybee_energy/properties/face.py +274 -0
- honeybee_energy/properties/model.py +2640 -0
- honeybee_energy/properties/room.py +1747 -0
- honeybee_energy/properties/shade.py +314 -0
- honeybee_energy/properties/shademesh.py +262 -0
- honeybee_energy/reader.py +48 -0
- honeybee_energy/result/__init__.py +1 -0
- honeybee_energy/result/colorobj.py +648 -0
- honeybee_energy/result/emissions.py +290 -0
- honeybee_energy/result/err.py +101 -0
- honeybee_energy/result/eui.py +100 -0
- honeybee_energy/result/generation.py +160 -0
- honeybee_energy/result/loadbalance.py +890 -0
- honeybee_energy/result/match.py +202 -0
- honeybee_energy/result/osw.py +90 -0
- honeybee_energy/result/rdd.py +59 -0
- honeybee_energy/result/zsz.py +190 -0
- honeybee_energy/run.py +1577 -0
- honeybee_energy/schedule/__init__.py +1 -0
- honeybee_energy/schedule/day.py +626 -0
- honeybee_energy/schedule/dictutil.py +59 -0
- honeybee_energy/schedule/fixedinterval.py +1012 -0
- honeybee_energy/schedule/rule.py +619 -0
- honeybee_energy/schedule/ruleset.py +1867 -0
- honeybee_energy/schedule/typelimit.py +310 -0
- honeybee_energy/shw.py +315 -0
- honeybee_energy/simulation/__init__.py +1 -0
- honeybee_energy/simulation/control.py +214 -0
- honeybee_energy/simulation/daylightsaving.py +185 -0
- honeybee_energy/simulation/dictutil.py +51 -0
- honeybee_energy/simulation/output.py +646 -0
- honeybee_energy/simulation/parameter.py +606 -0
- honeybee_energy/simulation/runperiod.py +443 -0
- honeybee_energy/simulation/shadowcalculation.py +295 -0
- honeybee_energy/simulation/sizing.py +546 -0
- honeybee_energy/ventcool/__init__.py +5 -0
- honeybee_energy/ventcool/_crack_data.py +91 -0
- honeybee_energy/ventcool/afn.py +289 -0
- honeybee_energy/ventcool/control.py +269 -0
- honeybee_energy/ventcool/crack.py +126 -0
- honeybee_energy/ventcool/fan.py +493 -0
- honeybee_energy/ventcool/opening.py +365 -0
- honeybee_energy/ventcool/simulation.py +314 -0
- honeybee_energy/writer.py +1078 -0
- honeybee_energy-1.116.106.dist-info/METADATA +113 -0
- honeybee_energy-1.116.106.dist-info/RECORD +162 -0
- honeybee_energy-1.116.106.dist-info/WHEEL +5 -0
- honeybee_energy-1.116.106.dist-info/entry_points.txt +2 -0
- honeybee_energy-1.116.106.dist-info/licenses/LICENSE +661 -0
- honeybee_energy-1.116.106.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""Module for coloring Model geometry with energy simulation results."""
|
|
3
|
+
from __future__ import division
|
|
4
|
+
|
|
5
|
+
from .match import match_rooms_to_data, match_faces_to_data
|
|
6
|
+
|
|
7
|
+
from honeybee.face import Face
|
|
8
|
+
from honeybee.room import Room
|
|
9
|
+
from honeybee.facetype import Floor
|
|
10
|
+
from honeybee.typing import int_in_range
|
|
11
|
+
|
|
12
|
+
from ladybug.dt import Date, DateTime
|
|
13
|
+
from ladybug.analysisperiod import AnalysisPeriod
|
|
14
|
+
from ladybug.datacollection import MonthlyCollection, DailyCollection, \
|
|
15
|
+
MonthlyPerHourCollection, HourlyContinuousCollection, HourlyDiscontinuousCollection
|
|
16
|
+
from ladybug.graphic import GraphicContainer
|
|
17
|
+
from ladybug.legend import LegendParameters
|
|
18
|
+
|
|
19
|
+
from ladybug_geometry.geometry3d.pointvector import Point3D
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class _ColorObject(object):
|
|
23
|
+
"""Base class for coloring geometry with simulation results.
|
|
24
|
+
|
|
25
|
+
Properties:
|
|
26
|
+
* data_collections
|
|
27
|
+
* legend_parameters
|
|
28
|
+
* simulation_step
|
|
29
|
+
* geo_unit
|
|
30
|
+
* title_text
|
|
31
|
+
* data_type_text
|
|
32
|
+
* data_type
|
|
33
|
+
* unit
|
|
34
|
+
* analysis_period
|
|
35
|
+
* min_point
|
|
36
|
+
* max_point
|
|
37
|
+
"""
|
|
38
|
+
__slots__ = ('_data_collections', '_legend_parameters', '_simulation_step',
|
|
39
|
+
'_normalize', '_geo_unit', '_matched_objects', '_base_collection',
|
|
40
|
+
'_base_type', '_base_unit', '_min_point', '_max_point')
|
|
41
|
+
|
|
42
|
+
UNITS = ('m', 'mm', 'ft', 'in', 'cm')
|
|
43
|
+
|
|
44
|
+
def __init__(self, data_collections, legend_parameters=None,
|
|
45
|
+
simulation_step=None, geo_unit='m'):
|
|
46
|
+
"""Initialize ColorObject."""
|
|
47
|
+
# check the input collections
|
|
48
|
+
acceptable_colls = (MonthlyCollection, DailyCollection, MonthlyPerHourCollection,
|
|
49
|
+
HourlyContinuousCollection, HourlyDiscontinuousCollection)
|
|
50
|
+
try:
|
|
51
|
+
data_collections = list(data_collections)
|
|
52
|
+
except TypeError:
|
|
53
|
+
raise TypeError('Input data_collections must be an array. Got {}.'.format(
|
|
54
|
+
type(data_collections)))
|
|
55
|
+
assert len(data_collections) > 0, \
|
|
56
|
+
'ColorObject must have at least one data_collection.'
|
|
57
|
+
for i, coll in enumerate(data_collections):
|
|
58
|
+
assert isinstance(coll, acceptable_colls), 'Expected data collection for ' \
|
|
59
|
+
'ColorObject data_collections. Got {}.'.format(type(coll))
|
|
60
|
+
if not coll.validated_a_period:
|
|
61
|
+
data_collections[i] = coll.validate_analysis_period()
|
|
62
|
+
self._base_collection = data_collections[0]
|
|
63
|
+
self._base_type = self._base_collection.header.data_type
|
|
64
|
+
self._base_unit = self._base_collection.header.unit
|
|
65
|
+
for coll in data_collections[1:]:
|
|
66
|
+
assert coll.header.unit == self._base_unit, \
|
|
67
|
+
'ColorObject data_collections must all have matching units. ' \
|
|
68
|
+
'{} != {}.'.format(coll.header.unit, self._base_unit)
|
|
69
|
+
assert len(coll.values) == len(self._base_collection.values), \
|
|
70
|
+
'ColorObject data_collections must all be aligned with one another.' \
|
|
71
|
+
'{} != {}'.format(len(coll.values), len(self._base_collection.values))
|
|
72
|
+
self._data_collections = data_collections
|
|
73
|
+
|
|
74
|
+
# assign the other properties of this object
|
|
75
|
+
self.legend_parameters = legend_parameters
|
|
76
|
+
self.simulation_step = simulation_step
|
|
77
|
+
self.geo_unit = geo_unit
|
|
78
|
+
self._normalize = False
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def data_collections(self):
|
|
82
|
+
"""Get a tuple of data collections assigned to this object."""
|
|
83
|
+
return tuple(self._data_collections)
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def legend_parameters(self):
|
|
87
|
+
"""Get or set the legend parameters."""
|
|
88
|
+
return self._legend_parameters
|
|
89
|
+
|
|
90
|
+
@legend_parameters.setter
|
|
91
|
+
def legend_parameters(self, value):
|
|
92
|
+
if value is not None:
|
|
93
|
+
assert isinstance(value, LegendParameters), \
|
|
94
|
+
'Expected LegendParameters. Got {}.'.format(type(value))
|
|
95
|
+
self._legend_parameters = value.duplicate()
|
|
96
|
+
else:
|
|
97
|
+
self._legend_parameters = LegendParameters()
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def simulation_step(self):
|
|
101
|
+
"""Get or set an integer to select a specific step of the data collections."""
|
|
102
|
+
return self._simulation_step
|
|
103
|
+
|
|
104
|
+
@simulation_step.setter
|
|
105
|
+
def simulation_step(self, value):
|
|
106
|
+
if value is not None:
|
|
107
|
+
value = int_in_range(
|
|
108
|
+
value, 0, len(self._base_collection) - 1, 'simulation_step')
|
|
109
|
+
self._simulation_step = value
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def geo_unit(self):
|
|
113
|
+
"""Text to note the units that the object geometry is in.
|
|
114
|
+
|
|
115
|
+
This will be used to ensure the legend units display correctly when
|
|
116
|
+
data is floor-normalized. Examples include 'm', 'mm', 'ft'.
|
|
117
|
+
"""
|
|
118
|
+
return self._geo_unit
|
|
119
|
+
|
|
120
|
+
@geo_unit.setter
|
|
121
|
+
def geo_unit(self, value):
|
|
122
|
+
self._geo_unit = str(value)
|
|
123
|
+
assert self._geo_unit in self.UNITS, \
|
|
124
|
+
'Unit "{}" is not supported in color object.'.format(self._geo_unit)
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def title_text(self):
|
|
128
|
+
"""Text string for the title of the color zones."""
|
|
129
|
+
d_type_text = self.data_type_text
|
|
130
|
+
if self._simulation_step is not None: # specific index from all collections
|
|
131
|
+
time_text = self.time_interval_text(self.simulation_step)
|
|
132
|
+
if self._base_type.normalized_type is not None and self._normalize:
|
|
133
|
+
d_type_text = '{} {}'.format(d_type_text, 'Intensity')
|
|
134
|
+
else: # average or total the data
|
|
135
|
+
time_text = str(self.analysis_period).split('@')[0]
|
|
136
|
+
if self._base_type.normalized_type is None or not self._normalize:
|
|
137
|
+
if not self._base_type.cumulative:
|
|
138
|
+
d_type_text = '{} {}'.format('Average', d_type_text)
|
|
139
|
+
else:
|
|
140
|
+
d_type_text = '{} {}'.format('Total', d_type_text)
|
|
141
|
+
else:
|
|
142
|
+
if not self._base_type.cumulative:
|
|
143
|
+
d_type_text = '{} {} {}'.format('Average', d_type_text, 'Intensity')
|
|
144
|
+
else:
|
|
145
|
+
d_type_text = '{} {} {}'.format('Total', d_type_text, 'Intensity')
|
|
146
|
+
return '{}\n{}'.format('{} ({})'.format(d_type_text, self.unit), time_text)
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def data_type_text(self):
|
|
150
|
+
"""Text for the data type.
|
|
151
|
+
|
|
152
|
+
This will be the full name of the EnergyPlus output if the DataCollection
|
|
153
|
+
header metadata contains a 'type' key. Otherwise, this will be the name
|
|
154
|
+
of the data_type object.
|
|
155
|
+
"""
|
|
156
|
+
m_data = self._base_collection.header.metadata
|
|
157
|
+
return m_data['type'] if 'type' in m_data else str(self.data_type)
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def data_type(self):
|
|
161
|
+
"""The data type of this object's data collections."""
|
|
162
|
+
if self._base_type.normalized_type is None or not self._normalize:
|
|
163
|
+
return self._base_type
|
|
164
|
+
else:
|
|
165
|
+
return self._base_type.normalized_type()
|
|
166
|
+
|
|
167
|
+
@property
|
|
168
|
+
def unit(self):
|
|
169
|
+
"""The unit of this object's data collections."""
|
|
170
|
+
if self._base_type.normalized_type is not None and self._normalize:
|
|
171
|
+
_geo_unit = 'ft' if self._geo_unit in ('ft', 'in') else 'm'
|
|
172
|
+
return '{}/{}2'.format(self._base_unit, _geo_unit) if '/' not in \
|
|
173
|
+
self._base_unit else '{}-{}2'.format(self._base_unit, _geo_unit)
|
|
174
|
+
else:
|
|
175
|
+
return self._base_unit
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def analysis_period(self):
|
|
179
|
+
"""The analysis_period of this object's data collections."""
|
|
180
|
+
return self._base_collection.header.analysis_period
|
|
181
|
+
|
|
182
|
+
@property
|
|
183
|
+
def min_point(self):
|
|
184
|
+
"""Get a Point3D for the minimum of the box around the rooms."""
|
|
185
|
+
return self._min_point
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def max_point(self):
|
|
189
|
+
"""Get a Point3D for the maximum of the box around the rooms."""
|
|
190
|
+
return self._max_point
|
|
191
|
+
|
|
192
|
+
def time_interval_text(self, simulation_step):
|
|
193
|
+
"""Get text for a specific time simulation_step of the data collections.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
simulation_step: An integer for the step of simulation for which
|
|
197
|
+
text should be generated.
|
|
198
|
+
"""
|
|
199
|
+
hourly_colls = (HourlyContinuousCollection, HourlyDiscontinuousCollection)
|
|
200
|
+
if isinstance(self._base_collection, hourly_colls):
|
|
201
|
+
return str(self._base_collection.datetimes[simulation_step])
|
|
202
|
+
elif isinstance(self._base_collection, MonthlyCollection):
|
|
203
|
+
month_names = AnalysisPeriod.MONTHNAMES
|
|
204
|
+
return month_names[self._base_collection.datetimes[simulation_step]]
|
|
205
|
+
elif isinstance(self._base_collection, DailyCollection):
|
|
206
|
+
return str(Date.from_doy(self._base_collection.datetimes[simulation_step]))
|
|
207
|
+
elif isinstance(self._base_collection, MonthlyPerHourCollection):
|
|
208
|
+
dt_tuple = self._base_collection.datetimes[simulation_step]
|
|
209
|
+
date_time = DateTime(month=dt_tuple[0], hour=dt_tuple[1])
|
|
210
|
+
return date_time.strftime('%b %H:%M')
|
|
211
|
+
|
|
212
|
+
def _calculate_min_max(self, hb_objs):
|
|
213
|
+
"""Calculate maximum and minimum Point3D for a set of rooms."""
|
|
214
|
+
st_rm_min, st_rm_max = hb_objs[0].geometry.min, hb_objs[0].geometry.max
|
|
215
|
+
min_pt = [st_rm_min.x, st_rm_min.y, st_rm_min.z]
|
|
216
|
+
max_pt = [st_rm_max.x, st_rm_max.y, st_rm_max.z]
|
|
217
|
+
|
|
218
|
+
for room in hb_objs[1:]:
|
|
219
|
+
rm_min, rm_max = room.geometry.min, room.geometry.max
|
|
220
|
+
if rm_min.x < min_pt[0]:
|
|
221
|
+
min_pt[0] = rm_min.x
|
|
222
|
+
if rm_min.y < min_pt[1]:
|
|
223
|
+
min_pt[1] = rm_min.y
|
|
224
|
+
if rm_min.z < min_pt[2]:
|
|
225
|
+
min_pt[2] = rm_min.z
|
|
226
|
+
if rm_max.x > max_pt[0]:
|
|
227
|
+
max_pt[0] = rm_max.x
|
|
228
|
+
if rm_max.y > max_pt[1]:
|
|
229
|
+
max_pt[1] = rm_max.y
|
|
230
|
+
if rm_max.z > max_pt[2]:
|
|
231
|
+
max_pt[2] = rm_max.z
|
|
232
|
+
|
|
233
|
+
self._min_point = Point3D(min_pt[0], min_pt[1], min_pt[2])
|
|
234
|
+
self._max_point = Point3D(max_pt[0], max_pt[1], max_pt[2])
|
|
235
|
+
|
|
236
|
+
def ToString(self):
|
|
237
|
+
"""Overwrite .NET ToString."""
|
|
238
|
+
return self.__repr__()
|
|
239
|
+
|
|
240
|
+
def __repr__(self):
|
|
241
|
+
return 'Color Object:'
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
class ColorRoom(_ColorObject):
|
|
245
|
+
"""Object for visualization zone-level simulation results on Honeybee Room geometry.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
data_collections: An array of data collections of the same data type,
|
|
249
|
+
which will be used to color Rooms with simulation results. Data collections
|
|
250
|
+
can be of any class (eg. MonthlyCollection, DailyCollection) but they
|
|
251
|
+
should all have headers with metadata dictionaries with 'Zone' or
|
|
252
|
+
'System' keys. These keys will be used to match the data in the collections
|
|
253
|
+
to the input rooms.
|
|
254
|
+
rooms: An array of honeybee Rooms, which will be matched to the data_collections.
|
|
255
|
+
The length of these Rooms does not have to match the data_collections
|
|
256
|
+
and this object will only create visualizations for rooms that are
|
|
257
|
+
found to be matching.
|
|
258
|
+
legend_parameters: An optional LegendParameter object to change the display
|
|
259
|
+
of the ColorRooms (Default: None).
|
|
260
|
+
simulation_step: An optional integer (greater than or equal to 0) to select
|
|
261
|
+
a specific step of the data collections for which result values will be
|
|
262
|
+
generated. If None, the geometry will be colored with the total of
|
|
263
|
+
results in the data_collections if the data type is cumulative or with
|
|
264
|
+
the average of results if the data type is not cumulative. Default: None.
|
|
265
|
+
normalize_by_floor: Boolean to note whether results should be normalized
|
|
266
|
+
by the floor area of the Room if the data type of the data_collections
|
|
267
|
+
supports it. If False, values will be generated using sum total of
|
|
268
|
+
the data collection values. Note that this input has no effect if
|
|
269
|
+
the data type of the data_collections is not normalizable since data
|
|
270
|
+
collection values will always be averaged for this case. Default: True.
|
|
271
|
+
geo_unit: Optional text to note the units that the Room geometry is in.
|
|
272
|
+
This will be used to ensure the legend units display correctly when
|
|
273
|
+
data is floor-normalized. Examples include 'm', 'mm', 'ft'.
|
|
274
|
+
(Default: 'm' for meters).
|
|
275
|
+
space_based: Boolean to note whether the result is reported on the EnergyPlus
|
|
276
|
+
Space level instead of the Zone level. In this case, the matching to
|
|
277
|
+
the Room will account for the fact that the Space name is the Room
|
|
278
|
+
name with _Space added to it. (Default: False).
|
|
279
|
+
|
|
280
|
+
Properties:
|
|
281
|
+
* data_collections
|
|
282
|
+
* rooms
|
|
283
|
+
* legend_parameters
|
|
284
|
+
* simulation_step
|
|
285
|
+
* normalize_by_floor
|
|
286
|
+
* geo_unit
|
|
287
|
+
* space_based
|
|
288
|
+
* matched_rooms
|
|
289
|
+
* matched_data
|
|
290
|
+
* matched_values
|
|
291
|
+
* matched_floor_faces
|
|
292
|
+
* matched_floor_areas
|
|
293
|
+
* graphic_container
|
|
294
|
+
* title_text
|
|
295
|
+
* data_type_text
|
|
296
|
+
* data_type
|
|
297
|
+
* unit
|
|
298
|
+
* analysis_period
|
|
299
|
+
* min_point
|
|
300
|
+
* max_point
|
|
301
|
+
"""
|
|
302
|
+
__slots__ = ('_rooms', '_space_based')
|
|
303
|
+
|
|
304
|
+
def __init__(self, data_collections, rooms, legend_parameters=None,
|
|
305
|
+
simulation_step=None, normalize_by_floor=True, geo_unit='m',
|
|
306
|
+
space_based=False):
|
|
307
|
+
"""Initialize ColorRoom."""
|
|
308
|
+
# initialize the base object
|
|
309
|
+
_ColorObject.__init__(self, data_collections, legend_parameters,
|
|
310
|
+
simulation_step, geo_unit)
|
|
311
|
+
for coll in self._data_collections:
|
|
312
|
+
assert 'Zone' in coll.header.metadata or 'System' in coll.header.metadata, \
|
|
313
|
+
'ColorRoom data collection does not have metadata associated with Zones.'
|
|
314
|
+
|
|
315
|
+
try: # check the input rooms
|
|
316
|
+
rooms = tuple(rooms)
|
|
317
|
+
except TypeError:
|
|
318
|
+
raise TypeError('Input rooms must be an array. Got {}.'.format(type(rooms)))
|
|
319
|
+
assert len(rooms) > 0, 'ColorRooms must have at least one room.'
|
|
320
|
+
for room in rooms:
|
|
321
|
+
assert isinstance(room, Room), 'Expected honeybee Room for ' \
|
|
322
|
+
'ColorRoom rooms. Got {}.'.format(type(room))
|
|
323
|
+
self._rooms = rooms
|
|
324
|
+
self._calculate_min_max(self._rooms)
|
|
325
|
+
|
|
326
|
+
# match the rooms with the data collections
|
|
327
|
+
self._space_based = bool(space_based)
|
|
328
|
+
self._matched_objects = match_rooms_to_data(
|
|
329
|
+
data_collections, rooms, space_based=self._space_based,
|
|
330
|
+
zone_correct_mult=False)
|
|
331
|
+
if len(self._matched_objects) == 0:
|
|
332
|
+
raise ValueError('None of the ColorRoom data collections could be '
|
|
333
|
+
'matched to the input rooms')
|
|
334
|
+
|
|
335
|
+
# assign the normalize property
|
|
336
|
+
self.normalize_by_floor = normalize_by_floor
|
|
337
|
+
|
|
338
|
+
@property
|
|
339
|
+
def rooms(self):
|
|
340
|
+
"""Get a tuple of honeybee Rooms assigned to this object."""
|
|
341
|
+
return self._rooms
|
|
342
|
+
|
|
343
|
+
@property
|
|
344
|
+
def normalize_by_floor(self):
|
|
345
|
+
"""Get or set a boolean for whether results should be normalized by floor area.
|
|
346
|
+
"""
|
|
347
|
+
return self._normalize
|
|
348
|
+
|
|
349
|
+
@normalize_by_floor.setter
|
|
350
|
+
def normalize_by_floor(self, value):
|
|
351
|
+
self._normalize = bool(value)
|
|
352
|
+
|
|
353
|
+
@property
|
|
354
|
+
def space_based(self):
|
|
355
|
+
"""Get a boolean for whether results are set to be space-based."""
|
|
356
|
+
return self._space_based
|
|
357
|
+
|
|
358
|
+
@property
|
|
359
|
+
def matched_rooms(self):
|
|
360
|
+
"""Get a tuple of honeybee Rooms that have been matched to the data."""
|
|
361
|
+
return tuple(obj[0] for obj in self._matched_objects)
|
|
362
|
+
|
|
363
|
+
@property
|
|
364
|
+
def matched_data(self):
|
|
365
|
+
"""Get a tuple of data collections that have been matched to the rooms."""
|
|
366
|
+
return tuple(obj[1] for obj in self._matched_objects)
|
|
367
|
+
|
|
368
|
+
@property
|
|
369
|
+
def matched_values(self):
|
|
370
|
+
"""Get an array of numbers that correspond to the matched_rooms.
|
|
371
|
+
|
|
372
|
+
These values are derived from the data_collections but they will be
|
|
373
|
+
averaged/totaled and normalized by Room floor area depending on the
|
|
374
|
+
other inputs to this object.
|
|
375
|
+
"""
|
|
376
|
+
if self._simulation_step is not None: # specific index from all collections
|
|
377
|
+
if self._base_type.normalized_type is None or not self._normalize:
|
|
378
|
+
return tuple(obj[1][self._simulation_step] for obj in
|
|
379
|
+
self._matched_objects)
|
|
380
|
+
else: # normalize the data by the floor area
|
|
381
|
+
vals = []
|
|
382
|
+
for obj, f_area in zip(self._matched_objects, self.matched_floor_areas):
|
|
383
|
+
try:
|
|
384
|
+
vals.append(obj[1][self._simulation_step] / (f_area * obj[2]))
|
|
385
|
+
except ZeroDivisionError: # no floor faces in the Room
|
|
386
|
+
vals.append(0)
|
|
387
|
+
return vals
|
|
388
|
+
else: # average or total the data based on data type
|
|
389
|
+
if self._base_type.normalized_type is None or not self._normalize:
|
|
390
|
+
if self._base_type.cumulative:
|
|
391
|
+
return tuple(obj[1].total for obj in self._matched_objects)
|
|
392
|
+
else:
|
|
393
|
+
return tuple(obj[1].average for obj in self._matched_objects)
|
|
394
|
+
else: # normalize the data by floor area
|
|
395
|
+
vals = []
|
|
396
|
+
zip_obj = zip(self._matched_objects, self.matched_floor_areas)
|
|
397
|
+
if self._base_type.cumulative: # divide total values by floor area
|
|
398
|
+
for obj, f_area in zip_obj:
|
|
399
|
+
try:
|
|
400
|
+
vals.append(obj[1].total / (f_area * obj[2]))
|
|
401
|
+
except ZeroDivisionError: # no floor faces in the Room
|
|
402
|
+
vals.append(0)
|
|
403
|
+
else: # divide average values by floor area
|
|
404
|
+
for obj, f_area in zip_obj:
|
|
405
|
+
try:
|
|
406
|
+
vals.append(obj[1].average / f_area)
|
|
407
|
+
except ZeroDivisionError: # no floor faces in the Room
|
|
408
|
+
vals.append(0)
|
|
409
|
+
return vals
|
|
410
|
+
|
|
411
|
+
@property
|
|
412
|
+
def matched_floor_faces(self):
|
|
413
|
+
"""Get a nested array with each sub-array having all floor Face3Ds of each room.
|
|
414
|
+
"""
|
|
415
|
+
flr_faces = []
|
|
416
|
+
for room in self.matched_rooms:
|
|
417
|
+
flr_faces.append(
|
|
418
|
+
[face.geometry for face in room.faces if isinstance(face.type, Floor)])
|
|
419
|
+
return flr_faces
|
|
420
|
+
|
|
421
|
+
@property
|
|
422
|
+
def matched_floor_areas(self):
|
|
423
|
+
"""Get a list for all of the room floor areas that were matches with data.
|
|
424
|
+
|
|
425
|
+
These floor areas will always be in either square meters or square feet
|
|
426
|
+
depending on whether the geo_unit is either SI or IP.
|
|
427
|
+
"""
|
|
428
|
+
# correct for more than one room in a zone
|
|
429
|
+
if not self.space_based:
|
|
430
|
+
zones = {}
|
|
431
|
+
for room in self.matched_rooms:
|
|
432
|
+
try:
|
|
433
|
+
zones[room.zone] += room.floor_area
|
|
434
|
+
except KeyError: # first room to be found in the zone
|
|
435
|
+
zones[room.zone] = room.floor_area
|
|
436
|
+
floor_areas = [zones[room.zone] for room in self.matched_rooms]
|
|
437
|
+
else:
|
|
438
|
+
floor_areas = [room.floor_area for room in self.matched_rooms]
|
|
439
|
+
# convert units if they are not conventional
|
|
440
|
+
if self._geo_unit in ('m', 'ft'): # no need to do unit conversions
|
|
441
|
+
return floor_areas
|
|
442
|
+
elif self._geo_unit == 'mm': # convert to meters
|
|
443
|
+
return [ar / 1000000.0 for ar in floor_areas]
|
|
444
|
+
elif self._geo_unit == 'in': # convert to feet
|
|
445
|
+
return [ar / 144.0 for ar in floor_areas]
|
|
446
|
+
else: # assume it's cm; convert to meters
|
|
447
|
+
return [ar / 10000.0 for ar in floor_areas]
|
|
448
|
+
|
|
449
|
+
@property
|
|
450
|
+
def graphic_container(self):
|
|
451
|
+
"""Get a ladybug GraphicContainer that relates to this object.
|
|
452
|
+
|
|
453
|
+
The GraphicContainer possesses almost all things needed to visualize the
|
|
454
|
+
ColorRoom object including the legend, value_colors, lower_title_location,
|
|
455
|
+
upper_title_location, etc.
|
|
456
|
+
"""
|
|
457
|
+
return GraphicContainer(
|
|
458
|
+
self.matched_values, self.min_point, self.max_point,
|
|
459
|
+
self.legend_parameters, self.data_type, str(self.unit))
|
|
460
|
+
|
|
461
|
+
def __repr__(self):
|
|
462
|
+
"""Color Room representation."""
|
|
463
|
+
return 'Color Room: [{} Rooms] [{}]'.format(
|
|
464
|
+
len(self._matched_objects), self._base_collection.header)
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
class ColorFace(_ColorObject):
|
|
468
|
+
"""Object for visualization face and sub-face-level simulation results on geometry.
|
|
469
|
+
|
|
470
|
+
Args:
|
|
471
|
+
data_collections: An array of data collections of the same data type,
|
|
472
|
+
which will be used to color Faces with simulation results. Data collections
|
|
473
|
+
can be of any class (eg. MonthlyCollection, DailyCollection) but they
|
|
474
|
+
should all have headers with metadata dictionaries with 'Surface'
|
|
475
|
+
keys. These keys will be used to match the data in the collections
|
|
476
|
+
to the input faces.
|
|
477
|
+
faces: An array of honeybee Faces, Apertures, and/or Doors which will be
|
|
478
|
+
matched to the data_collections.
|
|
479
|
+
legend_parameters: An optional LegendParameter object to change the display
|
|
480
|
+
of the ColorFace (Default: None).
|
|
481
|
+
simulation_step: An optional integer (greater than or equal to 0) to select
|
|
482
|
+
a specific step of the data collections for which result values will be
|
|
483
|
+
generated. If None, the geometry will be colored with the total of
|
|
484
|
+
results in the data_collections if the data type is cumulative or with
|
|
485
|
+
the average of results if the data type is not cumulative. Default: None.
|
|
486
|
+
normalize: Boolean to note whether results should be normalized by the
|
|
487
|
+
face/sub-face area if the data type of the data_collections supports it.
|
|
488
|
+
If False, values will be generated using sum total of the data collection
|
|
489
|
+
values. Note that this input has no effect if the data type of the
|
|
490
|
+
data_collections is not normalizable since data collection values will
|
|
491
|
+
always be averaged for this case. Default: True.
|
|
492
|
+
geo_unit: Optional text to note the units that the Face geometry is in.
|
|
493
|
+
This will be used to ensure the legend units display correctly when
|
|
494
|
+
data is floor-normalized. Examples include 'm', 'mm', 'ft'.
|
|
495
|
+
(Default: 'm' for meters).
|
|
496
|
+
|
|
497
|
+
Properties:
|
|
498
|
+
* data_collections
|
|
499
|
+
* faces
|
|
500
|
+
* legend_parameters
|
|
501
|
+
* simulation_step
|
|
502
|
+
* normalize
|
|
503
|
+
* geo_unit
|
|
504
|
+
* matched_flat_faces
|
|
505
|
+
* matched_data
|
|
506
|
+
* matched_values
|
|
507
|
+
* matched_flat_geometry
|
|
508
|
+
* matched_flat_areas
|
|
509
|
+
* graphic_container
|
|
510
|
+
* title_text
|
|
511
|
+
* data_type_text
|
|
512
|
+
* data_type
|
|
513
|
+
* unit
|
|
514
|
+
* analysis_period
|
|
515
|
+
* min_point
|
|
516
|
+
* max_point
|
|
517
|
+
"""
|
|
518
|
+
__slots__ = ('_faces',)
|
|
519
|
+
|
|
520
|
+
def __init__(self, data_collections, faces, legend_parameters=None,
|
|
521
|
+
simulation_step=None, normalize=True, geo_unit='m'):
|
|
522
|
+
"""Initialize ColorFace."""
|
|
523
|
+
# initialize the base object
|
|
524
|
+
_ColorObject.__init__(self, data_collections, legend_parameters,
|
|
525
|
+
simulation_step, geo_unit)
|
|
526
|
+
for coll in self._data_collections:
|
|
527
|
+
assert 'Surface' in coll.header.metadata, 'ColorFace data collection ' \
|
|
528
|
+
'does not have metadata associated with Surfaces.'
|
|
529
|
+
|
|
530
|
+
try: # check the input faces
|
|
531
|
+
faces = tuple(faces)
|
|
532
|
+
except TypeError:
|
|
533
|
+
raise TypeError('Input faces must be an array. Got {}.'.format(type(faces)))
|
|
534
|
+
assert len(faces) > 0, 'ColorFaces must have at least one face.'
|
|
535
|
+
self._faces = faces
|
|
536
|
+
self._calculate_min_max(faces)
|
|
537
|
+
|
|
538
|
+
# match the faces with the data collections
|
|
539
|
+
self._matched_objects = match_faces_to_data(data_collections, faces)
|
|
540
|
+
if len(self._matched_objects) == 0:
|
|
541
|
+
raise ValueError('None of the ColorFace data collections could be '
|
|
542
|
+
'matched to the input faces')
|
|
543
|
+
|
|
544
|
+
# assign the normalize property
|
|
545
|
+
self.normalize = normalize
|
|
546
|
+
|
|
547
|
+
@property
|
|
548
|
+
def faces(self):
|
|
549
|
+
"""Get the honeybee Faces, Apertures, Doors and Shades assigned to this object.
|
|
550
|
+
"""
|
|
551
|
+
return self._faces
|
|
552
|
+
|
|
553
|
+
@property
|
|
554
|
+
def normalize(self):
|
|
555
|
+
"""Get or set a boolean for whether results are normalized by face/sub-face area.
|
|
556
|
+
"""
|
|
557
|
+
return self._normalize
|
|
558
|
+
|
|
559
|
+
@normalize.setter
|
|
560
|
+
def normalize(self, value):
|
|
561
|
+
self._normalize = bool(value)
|
|
562
|
+
|
|
563
|
+
@property
|
|
564
|
+
def matched_flat_faces(self):
|
|
565
|
+
"""Get a tuple of honeybee objects that have been matched to the data."""
|
|
566
|
+
return tuple(obj[0] for obj in self._matched_objects)
|
|
567
|
+
|
|
568
|
+
@property
|
|
569
|
+
def matched_data(self):
|
|
570
|
+
"""Get a tuple of data collections that have been matched to the flat_faces."""
|
|
571
|
+
return tuple(obj[1] for obj in self._matched_objects)
|
|
572
|
+
|
|
573
|
+
@property
|
|
574
|
+
def matched_values(self):
|
|
575
|
+
"""Get an array of numbers that correspond to the matched_flat_faces.
|
|
576
|
+
|
|
577
|
+
These values are derived from the data_collections but they will be
|
|
578
|
+
averaged/totaled and normalized by the face/sub-face area depending on the
|
|
579
|
+
other inputs to this object.
|
|
580
|
+
"""
|
|
581
|
+
if self._simulation_step is not None: # specific index from all collections
|
|
582
|
+
if self._base_type.normalized_type is None or not self._normalize:
|
|
583
|
+
return tuple(obj[1][self._simulation_step] for obj in
|
|
584
|
+
self._matched_objects)
|
|
585
|
+
else: # normalize the data by the face area
|
|
586
|
+
vals = []
|
|
587
|
+
for obj, f_area in zip(self._matched_objects, self.matched_flat_areas):
|
|
588
|
+
vals.append(obj[1][self._simulation_step] / f_area)
|
|
589
|
+
return vals
|
|
590
|
+
else: # average or total the data based on data type
|
|
591
|
+
if self._base_type.normalized_type is None or not self._normalize:
|
|
592
|
+
if self._base_type.cumulative:
|
|
593
|
+
return tuple(obj[1].total for obj in self._matched_objects)
|
|
594
|
+
else:
|
|
595
|
+
return tuple(obj[1].average for obj in self._matched_objects)
|
|
596
|
+
else: # normalize the data by face area
|
|
597
|
+
vals = []
|
|
598
|
+
zip_obj = zip(self._matched_objects, self.matched_flat_areas)
|
|
599
|
+
if self._base_type.cumulative: # divide total values by face area
|
|
600
|
+
for obj, f_area in zip_obj:
|
|
601
|
+
vals.append(obj[1].total / f_area)
|
|
602
|
+
else: # divide average values by face area
|
|
603
|
+
for obj, f_area in zip_obj:
|
|
604
|
+
vals.append(obj[1].average / f_area)
|
|
605
|
+
return vals
|
|
606
|
+
|
|
607
|
+
@property
|
|
608
|
+
def matched_flat_geometry(self):
|
|
609
|
+
"""Get non-nested array of faces/sub-faces on this object.
|
|
610
|
+
|
|
611
|
+
The geometries here align with the attributes and graphic_container colors.
|
|
612
|
+
"""
|
|
613
|
+
return tuple(face.geometry if not isinstance(face, Face)
|
|
614
|
+
else face.punched_geometry for face in self.matched_flat_faces)
|
|
615
|
+
|
|
616
|
+
@property
|
|
617
|
+
def matched_flat_areas(self):
|
|
618
|
+
"""Get a list numbers for the area of each of the matched_flat_faces.
|
|
619
|
+
|
|
620
|
+
These areas will always be in either square meters or square feet
|
|
621
|
+
depending on whether the geo_unit is either SI or IP. They also use
|
|
622
|
+
punched geometry in the case of a Face with child Apertures.
|
|
623
|
+
"""
|
|
624
|
+
if self._geo_unit in ('m', 'ft'): # no need to do unit conversions
|
|
625
|
+
return [face.area for face in self.matched_flat_geometry]
|
|
626
|
+
elif self._geo_unit == 'mm': # convert to meters
|
|
627
|
+
return [face.area / 1000000.0 for face in self.matched_flat_geometry]
|
|
628
|
+
elif self._geo_unit == 'in': # convert to feet
|
|
629
|
+
return [face.area / 144.0 for face in self.matched_flat_geometry]
|
|
630
|
+
else: # assume it's cm; convert to meters
|
|
631
|
+
return [face.area / 10000.0 for face in self.matched_flat_geometry]
|
|
632
|
+
|
|
633
|
+
@property
|
|
634
|
+
def graphic_container(self):
|
|
635
|
+
"""Get a ladybug GraphicContainer that relates to this object.
|
|
636
|
+
|
|
637
|
+
The GraphicContainer possesses almost all things needed to visualize the
|
|
638
|
+
ColorFace object including the legend, value_colors, lower_title_location,
|
|
639
|
+
upper_title_location, etc.
|
|
640
|
+
"""
|
|
641
|
+
return GraphicContainer(
|
|
642
|
+
self.matched_values, self.min_point, self.max_point,
|
|
643
|
+
self.legend_parameters, self.data_type, str(self.unit))
|
|
644
|
+
|
|
645
|
+
def __repr__(self):
|
|
646
|
+
"""Color Face representation."""
|
|
647
|
+
return 'Color Face: [{} Objects] [{}]'.format(
|
|
648
|
+
len(self._matched_objects), self._base_collection.header)
|