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.
Files changed (162) hide show
  1. honeybee_energy/__init__.py +24 -0
  2. honeybee_energy/__main__.py +4 -0
  3. honeybee_energy/_extend_honeybee.py +145 -0
  4. honeybee_energy/altnumber.py +21 -0
  5. honeybee_energy/baseline/__init__.py +2 -0
  6. honeybee_energy/baseline/create.py +608 -0
  7. honeybee_energy/baseline/data/__init__.py +1 -0
  8. honeybee_energy/baseline/data/constructions.csv +64 -0
  9. honeybee_energy/baseline/data/fen_ratios.csv +15 -0
  10. honeybee_energy/baseline/data/lpd_building.csv +21 -0
  11. honeybee_energy/baseline/data/pci_2016.csv +22 -0
  12. honeybee_energy/baseline/data/pci_2019.csv +22 -0
  13. honeybee_energy/baseline/data/pci_2022.csv +22 -0
  14. honeybee_energy/baseline/data/shw.csv +21 -0
  15. honeybee_energy/baseline/pci.py +512 -0
  16. honeybee_energy/baseline/result.py +371 -0
  17. honeybee_energy/boundarycondition.py +128 -0
  18. honeybee_energy/cli/__init__.py +69 -0
  19. honeybee_energy/cli/baseline.py +475 -0
  20. honeybee_energy/cli/edit.py +327 -0
  21. honeybee_energy/cli/lib.py +1154 -0
  22. honeybee_energy/cli/result.py +810 -0
  23. honeybee_energy/cli/setconfig.py +124 -0
  24. honeybee_energy/cli/settings.py +569 -0
  25. honeybee_energy/cli/simulate.py +380 -0
  26. honeybee_energy/cli/translate.py +1714 -0
  27. honeybee_energy/cli/validate.py +224 -0
  28. honeybee_energy/config.json +11 -0
  29. honeybee_energy/config.py +842 -0
  30. honeybee_energy/construction/__init__.py +1 -0
  31. honeybee_energy/construction/_base.py +374 -0
  32. honeybee_energy/construction/air.py +325 -0
  33. honeybee_energy/construction/dictutil.py +89 -0
  34. honeybee_energy/construction/dynamic.py +607 -0
  35. honeybee_energy/construction/opaque.py +460 -0
  36. honeybee_energy/construction/shade.py +319 -0
  37. honeybee_energy/construction/window.py +1096 -0
  38. honeybee_energy/construction/windowshade.py +847 -0
  39. honeybee_energy/constructionset.py +1655 -0
  40. honeybee_energy/dictutil.py +56 -0
  41. honeybee_energy/generator/__init__.py +5 -0
  42. honeybee_energy/generator/loadcenter.py +204 -0
  43. honeybee_energy/generator/pv.py +535 -0
  44. honeybee_energy/hvac/__init__.py +21 -0
  45. honeybee_energy/hvac/_base.py +124 -0
  46. honeybee_energy/hvac/_template.py +270 -0
  47. honeybee_energy/hvac/allair/__init__.py +22 -0
  48. honeybee_energy/hvac/allair/_base.py +349 -0
  49. honeybee_energy/hvac/allair/furnace.py +168 -0
  50. honeybee_energy/hvac/allair/psz.py +131 -0
  51. honeybee_energy/hvac/allair/ptac.py +163 -0
  52. honeybee_energy/hvac/allair/pvav.py +109 -0
  53. honeybee_energy/hvac/allair/vav.py +128 -0
  54. honeybee_energy/hvac/detailed.py +337 -0
  55. honeybee_energy/hvac/doas/__init__.py +28 -0
  56. honeybee_energy/hvac/doas/_base.py +345 -0
  57. honeybee_energy/hvac/doas/fcu.py +127 -0
  58. honeybee_energy/hvac/doas/radiant.py +329 -0
  59. honeybee_energy/hvac/doas/vrf.py +81 -0
  60. honeybee_energy/hvac/doas/wshp.py +91 -0
  61. honeybee_energy/hvac/heatcool/__init__.py +23 -0
  62. honeybee_energy/hvac/heatcool/_base.py +177 -0
  63. honeybee_energy/hvac/heatcool/baseboard.py +61 -0
  64. honeybee_energy/hvac/heatcool/evapcool.py +72 -0
  65. honeybee_energy/hvac/heatcool/fcu.py +92 -0
  66. honeybee_energy/hvac/heatcool/gasunit.py +53 -0
  67. honeybee_energy/hvac/heatcool/radiant.py +269 -0
  68. honeybee_energy/hvac/heatcool/residential.py +77 -0
  69. honeybee_energy/hvac/heatcool/vrf.py +54 -0
  70. honeybee_energy/hvac/heatcool/windowac.py +70 -0
  71. honeybee_energy/hvac/heatcool/wshp.py +62 -0
  72. honeybee_energy/hvac/idealair.py +699 -0
  73. honeybee_energy/internalmass.py +310 -0
  74. honeybee_energy/lib/__init__.py +1 -0
  75. honeybee_energy/lib/_loadconstructions.py +194 -0
  76. honeybee_energy/lib/_loadconstructionsets.py +117 -0
  77. honeybee_energy/lib/_loadmaterials.py +83 -0
  78. honeybee_energy/lib/_loadprogramtypes.py +125 -0
  79. honeybee_energy/lib/_loadschedules.py +87 -0
  80. honeybee_energy/lib/_loadtypelimits.py +64 -0
  81. honeybee_energy/lib/constructions.py +207 -0
  82. honeybee_energy/lib/constructionsets.py +95 -0
  83. honeybee_energy/lib/materials.py +67 -0
  84. honeybee_energy/lib/programtypes.py +125 -0
  85. honeybee_energy/lib/schedules.py +61 -0
  86. honeybee_energy/lib/scheduletypelimits.py +31 -0
  87. honeybee_energy/load/__init__.py +1 -0
  88. honeybee_energy/load/_base.py +190 -0
  89. honeybee_energy/load/daylight.py +397 -0
  90. honeybee_energy/load/dictutil.py +47 -0
  91. honeybee_energy/load/equipment.py +771 -0
  92. honeybee_energy/load/hotwater.py +543 -0
  93. honeybee_energy/load/infiltration.py +460 -0
  94. honeybee_energy/load/lighting.py +480 -0
  95. honeybee_energy/load/people.py +497 -0
  96. honeybee_energy/load/process.py +472 -0
  97. honeybee_energy/load/setpoint.py +816 -0
  98. honeybee_energy/load/ventilation.py +550 -0
  99. honeybee_energy/material/__init__.py +1 -0
  100. honeybee_energy/material/_base.py +166 -0
  101. honeybee_energy/material/dictutil.py +59 -0
  102. honeybee_energy/material/frame.py +367 -0
  103. honeybee_energy/material/gas.py +1087 -0
  104. honeybee_energy/material/glazing.py +854 -0
  105. honeybee_energy/material/opaque.py +1351 -0
  106. honeybee_energy/material/shade.py +1360 -0
  107. honeybee_energy/measure.py +472 -0
  108. honeybee_energy/programtype.py +723 -0
  109. honeybee_energy/properties/__init__.py +1 -0
  110. honeybee_energy/properties/aperture.py +333 -0
  111. honeybee_energy/properties/door.py +342 -0
  112. honeybee_energy/properties/extension.py +244 -0
  113. honeybee_energy/properties/face.py +274 -0
  114. honeybee_energy/properties/model.py +2640 -0
  115. honeybee_energy/properties/room.py +1747 -0
  116. honeybee_energy/properties/shade.py +314 -0
  117. honeybee_energy/properties/shademesh.py +262 -0
  118. honeybee_energy/reader.py +48 -0
  119. honeybee_energy/result/__init__.py +1 -0
  120. honeybee_energy/result/colorobj.py +648 -0
  121. honeybee_energy/result/emissions.py +290 -0
  122. honeybee_energy/result/err.py +101 -0
  123. honeybee_energy/result/eui.py +100 -0
  124. honeybee_energy/result/generation.py +160 -0
  125. honeybee_energy/result/loadbalance.py +890 -0
  126. honeybee_energy/result/match.py +202 -0
  127. honeybee_energy/result/osw.py +90 -0
  128. honeybee_energy/result/rdd.py +59 -0
  129. honeybee_energy/result/zsz.py +190 -0
  130. honeybee_energy/run.py +1577 -0
  131. honeybee_energy/schedule/__init__.py +1 -0
  132. honeybee_energy/schedule/day.py +626 -0
  133. honeybee_energy/schedule/dictutil.py +59 -0
  134. honeybee_energy/schedule/fixedinterval.py +1012 -0
  135. honeybee_energy/schedule/rule.py +619 -0
  136. honeybee_energy/schedule/ruleset.py +1867 -0
  137. honeybee_energy/schedule/typelimit.py +310 -0
  138. honeybee_energy/shw.py +315 -0
  139. honeybee_energy/simulation/__init__.py +1 -0
  140. honeybee_energy/simulation/control.py +214 -0
  141. honeybee_energy/simulation/daylightsaving.py +185 -0
  142. honeybee_energy/simulation/dictutil.py +51 -0
  143. honeybee_energy/simulation/output.py +646 -0
  144. honeybee_energy/simulation/parameter.py +606 -0
  145. honeybee_energy/simulation/runperiod.py +443 -0
  146. honeybee_energy/simulation/shadowcalculation.py +295 -0
  147. honeybee_energy/simulation/sizing.py +546 -0
  148. honeybee_energy/ventcool/__init__.py +5 -0
  149. honeybee_energy/ventcool/_crack_data.py +91 -0
  150. honeybee_energy/ventcool/afn.py +289 -0
  151. honeybee_energy/ventcool/control.py +269 -0
  152. honeybee_energy/ventcool/crack.py +126 -0
  153. honeybee_energy/ventcool/fan.py +493 -0
  154. honeybee_energy/ventcool/opening.py +365 -0
  155. honeybee_energy/ventcool/simulation.py +314 -0
  156. honeybee_energy/writer.py +1078 -0
  157. honeybee_energy-1.116.106.dist-info/METADATA +113 -0
  158. honeybee_energy-1.116.106.dist-info/RECORD +162 -0
  159. honeybee_energy-1.116.106.dist-info/WHEEL +5 -0
  160. honeybee_energy-1.116.106.dist-info/entry_points.txt +2 -0
  161. honeybee_energy-1.116.106.dist-info/licenses/LICENSE +661 -0
  162. honeybee_energy-1.116.106.dist-info/top_level.txt +1 -0
@@ -0,0 +1,310 @@
1
+ # coding=utf-8
2
+ """Room internal mass, including construction and surface area."""
3
+ from __future__ import division
4
+
5
+ from honeybee._lockable import lockable
6
+ from honeybee.typing import valid_ep_string, float_positive
7
+ from honeybee.units import conversion_factor_to_meters
8
+
9
+ from .construction.opaque import OpaqueConstruction
10
+ from .material.opaque import EnergyMaterialVegetation
11
+ from .reader import parse_idf_string
12
+ from .writer import generate_idf_string
13
+
14
+
15
+ @lockable
16
+ class InternalMass(object):
17
+ """Room internal mass, including construction and surface area.
18
+
19
+ Note that internal masses assigned this way cannot "see" solar radiation that
20
+ may potentially hit them and, as such, caution should be taken when using this
21
+ component with internal mass objects that are not always in shade. Masses are
22
+ factored into the the thermal calculations of the Room by undergoing heat
23
+ transfer with the indoor air.
24
+
25
+ Args:
26
+ identifier: Text string for a unique InternalMass ID. Must be < 100 characters
27
+ and not contain any EnergyPlus special characters. This will be used to
28
+ identify the object across a model and in the exported IDF.
29
+ construction: An OpaqueConstruction object that represents the material
30
+ that the internal thermal mass is composed of.
31
+ area: A number representing the surface area of the internal mass that
32
+ is exposed to the Room air. This value should always be in square
33
+ meters regardless of what units system the parent model is a part of.
34
+
35
+
36
+ Properties:
37
+ * identifier
38
+ * display_name
39
+ * construction
40
+ * area
41
+ * user_data
42
+ """
43
+ __slots__ = ('_identifier', '_display_name', '_construction', '_area',
44
+ '_locked', '_user_data')
45
+
46
+ def __init__(self, identifier, construction, area):
47
+ self._locked = False # unlocked by default
48
+ self.identifier = identifier
49
+ self._display_name = None
50
+ self.construction = construction
51
+ self.area = area
52
+ self._user_data = None
53
+
54
+ @property
55
+ def identifier(self):
56
+ """Get or set the text string for object identifier."""
57
+ return self._identifier
58
+
59
+ @identifier.setter
60
+ def identifier(self, identifier):
61
+ self._identifier = valid_ep_string(identifier)
62
+
63
+ @property
64
+ def display_name(self):
65
+ """Get or set a string for the object name without any character restrictions.
66
+
67
+ If not set, this will be equal to the identifier.
68
+ """
69
+ if self._display_name is None:
70
+ return self._identifier
71
+ return self._display_name
72
+
73
+ @display_name.setter
74
+ def display_name(self, value):
75
+ if value is not None:
76
+ try:
77
+ value = str(value)
78
+ except UnicodeEncodeError: # Python 2 machine lacking the character set
79
+ pass # keep it as unicode
80
+ self._display_name = value
81
+
82
+ @property
83
+ def construction(self):
84
+ """Get or set a Construction for the material the internal mass is composed of.
85
+ """
86
+ return self._construction
87
+
88
+ @construction.setter
89
+ def construction(self, value):
90
+ assert isinstance(value, OpaqueConstruction), \
91
+ 'Expected Opaque Construction for InternalMass. Got {}'.format(type(value))
92
+ assert not isinstance(value.materials[0], EnergyMaterialVegetation), \
93
+ 'InternalMass constructions cannot contain vegetation materials'
94
+ value.lock() # lock editing in case construction has multiple references
95
+ self._construction = value
96
+
97
+ @property
98
+ def area(self):
99
+ """Get or set a number for the surface area of the mass exposed to the Room air.
100
+ """
101
+ return self._area
102
+
103
+ @area.setter
104
+ def area(self, value):
105
+ self._area = float_positive(value, 'internal mass area')
106
+
107
+ @property
108
+ def user_data(self):
109
+ """Get or set an optional dictionary for additional meta data for this object.
110
+
111
+ This will be None until it has been set. All keys and values of this
112
+ dictionary should be of a standard Python type to ensure correct
113
+ serialization of the object to/from JSON (eg. str, float, int, list, dict)
114
+ """
115
+ if self._user_data is not None:
116
+ return self._user_data
117
+
118
+ @user_data.setter
119
+ def user_data(self, value):
120
+ if value is not None:
121
+ assert isinstance(value, dict), 'Expected dictionary for honeybee_energy' \
122
+ 'object user_data. Got {}.'.format(type(value))
123
+ self._user_data = value
124
+
125
+ @classmethod
126
+ def from_geometry(cls, identifier, construction, geometry, units='Meters'):
127
+ """Create an InternalMass object from a list of geometries.
128
+
129
+ Args:
130
+ identifier: Text string for a unique InternalMass ID. Must be < 100
131
+ characters and not contain any EnergyPlus special characters. This
132
+ will be used to identify the object across a model and in the
133
+ exported IDF.
134
+ construction: An OpaqueConstruction object that represents the material
135
+ that the internal thermal mass is composed of.
136
+ geometry: An array of Face3D representing the exposed surface of the
137
+ internal mass. Note that these Face3D are assumed to be one-sided
138
+ so, if they are meant to represent a 2-sided object, the Face3D
139
+ should be duplicated and offset.
140
+ units: Text for the units system of the geometry. Choose from the following:
141
+
142
+ * Meters
143
+ * Millimeters
144
+ * Feet
145
+ * Inches
146
+ * Centimeters
147
+ """
148
+ area = sum(geo.area for geo in geometry) * conversion_factor_to_meters(units)
149
+ return cls(identifier, construction, area)
150
+
151
+ @classmethod
152
+ def from_idf(cls, idf_string, construction_dict):
153
+ """Create an InternalMass object from an EnergyPlus IDF text string.
154
+
155
+ Args:
156
+ idf_string: A text string fully describing an EnergyPlus InternalMass
157
+ definition.
158
+ construction_dict: A dictionary with construction identifiers as keys
159
+ and honeybee construction objects as values. This will be used
160
+ to assign the construction to the InternalMass object.
161
+
162
+ Returns:
163
+ An InternalMass object loaded from the idf_string.
164
+ """
165
+ ep_strs = parse_idf_string(idf_string, 'InternalMass,')
166
+ obj_id = ep_strs[0].split('..')[0]
167
+ return cls(obj_id, construction_dict[ep_strs[1]], ep_strs[4])
168
+
169
+ @classmethod
170
+ def from_dict(cls, data):
171
+ """Create an InternalMass object from a dictionary.
172
+
173
+ Note that the dictionary must be a non-abridged version for this
174
+ classmethod to work.
175
+
176
+ Args:
177
+ data: An InternalMass dictionary in following the format below.
178
+
179
+ .. code-block:: python
180
+
181
+ {
182
+ "type": 'InternalMass',
183
+ "identifier": 'Kitchen_Table_Wood_050',
184
+ "display_name": 'Kitchen Table',
185
+ "construction": {}, # OpaqueConstruction definition
186
+ "area": 5 # surface area of internal mass in square meters
187
+ }
188
+ """
189
+ assert data['type'] == 'InternalMass', \
190
+ 'Expected InternalMass dictionary. Got {}.'.format(data['type'])
191
+ constr = OpaqueConstruction.from_dict(data['construction'])
192
+ new_obj = cls(data['identifier'], constr, data['area'])
193
+ if 'display_name' in data and data['display_name'] is not None:
194
+ new_obj.display_name = data['display_name']
195
+ if 'user_data' in data and data['user_data'] is not None:
196
+ new_obj.user_data = data['user_data']
197
+ return new_obj
198
+
199
+ @classmethod
200
+ def from_dict_abridged(cls, data, construction_dict):
201
+ """Create a InternalMass from an abridged dictionary.
202
+
203
+ Args:
204
+ data: An InternalMassAbridged dictionary.
205
+ construction_dict: A dictionary with construction identifiers as keys
206
+ and honeybee construction objects as values. This will be used
207
+ to assign the construction to the InternalMass object.
208
+
209
+ .. code-block:: python
210
+
211
+ {
212
+ "type": 'InternalMassAbridged',
213
+ "identifier": 'Kitchen_Table_Wood_050',
214
+ "display_name": 'Kitchen Table',
215
+ "construction": 'Hardwood_050' # OpaqueConstruction identifier
216
+ "area": 5 # surface area of internal mass in square meters
217
+ }
218
+ """
219
+ assert data['type'] == 'InternalMassAbridged', \
220
+ 'Expected InternalMassAbridged dictionary. Got {}.'.format(data['type'])
221
+ new_obj = cls(
222
+ data['identifier'], construction_dict[data['construction']], data['area'])
223
+ if 'display_name' in data and data['display_name'] is not None:
224
+ new_obj.display_name = data['display_name']
225
+ if 'user_data' in data and data['user_data'] is not None:
226
+ new_obj.user_data = data['user_data']
227
+ return new_obj
228
+
229
+ def to_idf(self, room_identifier, is_zone=True):
230
+ """IDF string representation of InternalMass object.
231
+
232
+ Note that this method only outputs a single string for the InternalMass
233
+ object and, to write everything needed to describe the object into an IDF,
234
+ this object's construction must also be written.
235
+
236
+ Args:
237
+ room_identifier: Text for the Room identifier that the InternalMass
238
+ object is assigned to.
239
+ is_zone: Boolean for whether the room_identifier refers to a Zone
240
+ object or a Space object. (Default: True).
241
+
242
+ .. code-block:: shell
243
+
244
+ InternalMass,
245
+ Zn002:IntM001, !- Surface Name
246
+ INTERIOR, !- Construction Name
247
+ DORM ROOMS AND COMMON AREAS, !- Zone or ZoneList Name
248
+ , !- Space or SpaceList Name
249
+ 408.7734; !- Total area exposed to Zone {m2}
250
+ """
251
+ int_m_id = '{}..{}'.format(self.identifier, room_identifier)
252
+ values = [int_m_id, self.construction.identifier]
253
+ if is_zone:
254
+ values.extend([room_identifier, '', self.area])
255
+ else:
256
+ values.extend(['', room_identifier, self.area])
257
+ comments = ('name', 'construction name', 'zone name', 'space name',
258
+ 'surface area')
259
+ return generate_idf_string('InternalMass', values, comments)
260
+
261
+ def to_dict(self, abridged=False):
262
+ """InternalMass dictionary representation.
263
+
264
+ Args:
265
+ abridged: Boolean to note whether the full dictionary describing the
266
+ object should be returned (False) or just an abridged version
267
+ (True), which only specifies the identifiers of the
268
+ construction. (Default: False).
269
+ """
270
+ base = {'type': 'InternalMass'} if not abridged \
271
+ else {'type': 'InternalMassAbridged'}
272
+ base['identifier'] = self.identifier
273
+ base['construction'] = self.construction.to_dict() if not \
274
+ abridged else self.construction.identifier
275
+ base['area'] = self.area
276
+ if self._display_name is not None:
277
+ base['display_name'] = self.display_name
278
+ if self._user_data is not None:
279
+ base['user_data'] = self.user_data
280
+ return base
281
+
282
+ def duplicate(self):
283
+ """Get a copy of this object."""
284
+ return self.__copy__()
285
+
286
+ def __copy__(self):
287
+ new_obj = InternalMass(self.identifier, self.construction, self.area)
288
+ new_obj._display_name = self._display_name
289
+ new_obj._user_data = None if self._user_data is None else self._user_data.copy()
290
+ return new_obj
291
+
292
+ def __key(self):
293
+ """A tuple based on the object properties, useful for hashing."""
294
+ return (self.identifier, hash(self.construction), self.area)
295
+
296
+ def __hash__(self):
297
+ return hash(self.__key())
298
+
299
+ def __eq__(self, other):
300
+ return isinstance(other, InternalMass) and self.__key() == other.__key()
301
+
302
+ def __ne__(self, other):
303
+ return not self.__eq__(other)
304
+
305
+ def ToString(self):
306
+ """Overwrite .NET ToString."""
307
+ return self.__repr__()
308
+
309
+ def __repr__(self):
310
+ return 'InternalMass: {}'.format(self.display_name)
@@ -0,0 +1 @@
1
+ """Library of schedules, constructions, and other template objects."""
@@ -0,0 +1,194 @@
1
+ """Load all materials and constructions from the IDF libraries."""
2
+ from honeybee_energy.config import folders
3
+ from honeybee_energy.construction.opaque import OpaqueConstruction
4
+ from honeybee_energy.construction.window import WindowConstruction
5
+ from honeybee_energy.construction.windowshade import WindowConstructionShade
6
+ from honeybee_energy.construction.dynamic import WindowConstructionDynamic
7
+ from honeybee_energy.construction.air import AirBoundaryConstruction
8
+ from honeybee_energy.construction.dictutil import dict_abridged_to_construction, \
9
+ dict_to_construction
10
+
11
+ from ._loadmaterials import _opaque_materials, _window_materials, _default_mats
12
+ from ._loadschedules import _schedules
13
+
14
+ import os
15
+ import json
16
+
17
+
18
+ # dictionary of all materials loaded from JSON
19
+ _all_materials = _opaque_materials.copy() # start with opaque materials
20
+ _all_materials.update(_window_materials) # add window constructions
21
+
22
+
23
+ # empty dictionaries to hold idf-loaded materials and constructions
24
+ _opaque_constructions = {}
25
+ _window_constructions = {}
26
+ _shade_constructions = {}
27
+
28
+ # groups useful for construction classification
29
+ _opa_types = (OpaqueConstruction, AirBoundaryConstruction)
30
+ _win_types = (WindowConstruction, WindowConstructionShade, WindowConstructionDynamic)
31
+
32
+
33
+ # first load the honeybee defaults
34
+ with open(folders.defaults_file) as json_file:
35
+ default_data = json.load(json_file)['constructions']
36
+ for con_dict in default_data:
37
+ constr = dict_abridged_to_construction(con_dict, _all_materials, _schedules, False)
38
+ constr.lock()
39
+ if isinstance(constr, _opa_types):
40
+ _opaque_constructions[con_dict['identifier']] = constr
41
+ elif isinstance(constr, _win_types):
42
+ _window_constructions[con_dict['identifier']] = constr
43
+ else: # it's a shade construction
44
+ _shade_constructions[con_dict['identifier']] = constr
45
+ _default_constrs = set(
46
+ list(_opaque_constructions.keys()) + list(_window_constructions.keys()) +
47
+ list(_shade_constructions.keys()))
48
+
49
+
50
+ # then load honeybee extension data into a dictionary but don't make the objects yet
51
+ _opaque_constr_standards_dict = {}
52
+ _window_constr_standards_dict = {}
53
+ _shade_constr_standards_dict = {}
54
+
55
+ for ext_folder in folders.standards_extension_folders:
56
+ _data_dir = os.path.join(ext_folder, 'constructions')
57
+ _opaque_dir = os.path.join(_data_dir, 'opaque_construction.json')
58
+ if os.path.isfile(_opaque_dir):
59
+ with open(_opaque_dir, 'r') as f:
60
+ _opaque_constr_standards_dict.update(json.load(f))
61
+ _window_dir = os.path.join(_data_dir, 'window_construction.json')
62
+ if os.path.isfile(_window_dir):
63
+ with open(_window_dir, 'r') as f:
64
+ _window_constr_standards_dict.update(json.load(f))
65
+ _shade_dir = os.path.join(_data_dir, 'shade_construction.json')
66
+ if os.path.isfile(_shade_dir):
67
+ with open(_shade_dir, 'r') as f:
68
+ _shade_constr_standards_dict.update(json.load(f))
69
+
70
+
71
+ # then load materials and constructions from the user-supplied files
72
+ def lock_and_check_material(mat):
73
+ """Lock a material and check that it's not overwriting a default."""
74
+ mat.lock()
75
+ assert mat.identifier not in _default_mats, 'Cannot overwrite ' \
76
+ 'default material "{}".'.format(mat.identifier)
77
+
78
+
79
+ def lock_and_check_construction(constr):
80
+ """Lock a construction and check that it's not overwriting a default."""
81
+ constr.lock()
82
+ assert constr.identifier not in _default_constrs, 'Cannot overwrite ' \
83
+ 'default construction "{}".'.format(constr.identifier)
84
+
85
+
86
+ def load_construction_object(
87
+ con_dict, load_mats, load_sch, opaque_cons, window_cons, shade_cons,
88
+ misc_mats, misc_sch):
89
+ """Load a construction object from a dictionary and add it to the library dict."""
90
+ try:
91
+ constr = dict_abridged_to_construction(con_dict, load_mats, load_sch, False)
92
+ if constr is None:
93
+ constr = dict_to_construction(con_dict, False)
94
+ try:
95
+ misc_mats.extend(constr.materials)
96
+ if constr.has_frame:
97
+ misc_mats.append(constr.frame)
98
+ if constr.has_shade:
99
+ if constr.is_switchable_glazing:
100
+ misc_mats.append(constr.switched_glass_material)
101
+ except AttributeError: # construction without materials
102
+ pass
103
+ if isinstance(constr, (WindowConstructionShade, WindowConstructionDynamic)):
104
+ if constr.schedule is not None:
105
+ misc_sch.append(constr.schedule)
106
+ elif isinstance(constr, AirBoundaryConstruction):
107
+ if constr.air_mixing_schedule is not None:
108
+ misc_sch.append(constr.air_mixing_schedule)
109
+ if constr is not None:
110
+ lock_and_check_construction(constr)
111
+ if isinstance(constr, _opa_types):
112
+ opaque_cons[con_dict['identifier']] = constr
113
+ elif isinstance(constr, _win_types):
114
+ window_cons[con_dict['identifier']] = constr
115
+ else: # it's a shade construction
116
+ shade_cons[con_dict['identifier']] = constr
117
+ except (TypeError, KeyError, ValueError):
118
+ try: # see if the construction set is built with constructions in standards
119
+ import honeybee_energy.lib.materials as _m
120
+ if 'materials' in con_dict:
121
+ if con_dict['type'] == 'OpaqueConstructionAbridged':
122
+ for mat in con_dict['materials']:
123
+ load_mats[mat] = _m.opaque_material_by_identifier(mat)
124
+ constr = dict_abridged_to_construction(
125
+ con_dict, load_mats, load_sch, False)
126
+ opaque_cons[con_dict['identifier']] = constr
127
+ elif con_dict['type'] == 'WindowConstructionAbridged':
128
+ for mat in con_dict['materials']:
129
+ load_mats[mat] = _m.window_material_by_identifier(mat)
130
+ constr = dict_abridged_to_construction(
131
+ con_dict, load_mats, load_sch, False)
132
+ window_cons[con_dict['identifier']] = constr
133
+ except Exception:
134
+ pass # not a Honeybee Construction JSON; possibly a comment
135
+
136
+
137
+ def load_constructions_from_folder(
138
+ construction_lib_folder, loaded_materials, loaded_schedules):
139
+ """Load all of the construction objects from a construction standards folder.
140
+
141
+ Args:
142
+ construction_lib_folder: Path to a constructions sub-folder within a
143
+ honeybee standards folder.
144
+ loaded_materials: A dictionary of materials that have already been loaded
145
+ from the library.
146
+ loaded_schedules: A dictionary of materials that have already been loaded
147
+ from the library.
148
+ """
149
+ opaque_mats, window_mats = {}, {}
150
+ opaque_cons, window_cons, shade_cons = {}, {}, {}
151
+ misc_mats, misc_sch = [], []
152
+ for f in os.listdir(construction_lib_folder):
153
+ f_path = os.path.join(construction_lib_folder, f)
154
+ if os.path.isfile(f_path):
155
+ if f_path.endswith('.idf'):
156
+ constructions, materials = \
157
+ OpaqueConstruction.extract_all_from_idf_file(f_path)
158
+ for mat in materials:
159
+ lock_and_check_material(mat)
160
+ opaque_mats[mat.identifier] = mat
161
+ for cnstr in constructions:
162
+ lock_and_check_construction(cnstr)
163
+ opaque_cons[cnstr.identifier] = cnstr
164
+ constructions, materials = \
165
+ WindowConstruction.extract_all_from_idf_file(f_path)
166
+ for mat in materials:
167
+ lock_and_check_material(mat)
168
+ window_mats[mat.identifier] = mat
169
+ for cnstr in constructions:
170
+ lock_and_check_construction(cnstr)
171
+ window_cons[cnstr.identifier] = cnstr
172
+ if f_path.endswith('.json'):
173
+ with open(f_path) as json_file:
174
+ data = json.load(json_file)
175
+ if 'type' in data: # single object
176
+ load_construction_object(
177
+ data, loaded_materials, loaded_schedules,
178
+ opaque_cons, window_cons, shade_cons, misc_mats, misc_sch)
179
+ else: # a collection of several objects
180
+ for constr_identifier in data:
181
+ load_construction_object(
182
+ data[constr_identifier], loaded_materials, loaded_schedules,
183
+ opaque_cons, window_cons, shade_cons, misc_mats, misc_sch)
184
+ return opaque_cons, window_cons, shade_cons, opaque_mats, window_mats, \
185
+ misc_mats, misc_sch
186
+
187
+
188
+ opaque_c, window_c, shade_c, opaque_m, window_m, misc_m, misc_s = \
189
+ load_constructions_from_folder(folders.construction_lib, _all_materials, _schedules)
190
+ _opaque_materials.update(opaque_m)
191
+ _window_materials.update(window_m)
192
+ _opaque_constructions.update(opaque_c)
193
+ _window_constructions.update(window_c)
194
+ _shade_constructions.update(shade_c)
@@ -0,0 +1,117 @@
1
+ """Load all construction sets from the JSON libraries."""
2
+ from honeybee_energy.config import folders
3
+ from honeybee_energy.constructionset import ConstructionSet
4
+
5
+ from ._loadconstructions import _opaque_constructions, _window_constructions, \
6
+ _shade_constructions
7
+
8
+ import os
9
+ import json
10
+
11
+
12
+ # make a dictionary of all constructions loaded from JSON
13
+ _all_constructions = _opaque_constructions.copy() # start with opaque constructions
14
+ _all_constructions.update(_window_constructions) # add window constructions
15
+ _all_constructions.update(_shade_constructions) # add shade constructions
16
+
17
+ # empty dictionary to hold loaded construction sets
18
+ _construction_sets = {}
19
+
20
+
21
+ # first load the honeybee defaults
22
+ with open(folders.defaults_file) as json_file:
23
+ default_data = json.load(json_file)['construction_sets']
24
+ for cset_dict in default_data:
25
+ constructionset = ConstructionSet.from_dict_abridged(cset_dict, _all_constructions)
26
+ constructionset.lock()
27
+ _construction_sets[cset_dict['identifier']] = constructionset
28
+ _default_sets = set(list(_construction_sets.keys()))
29
+
30
+
31
+ # then load honeybee extension data into a dictionary but don't make the objects yet
32
+ _construction_set_standards_dict = {}
33
+
34
+ for ext_folder in folders.standards_extension_folders:
35
+ _data_dir = os.path.join(ext_folder, 'constructionsets')
36
+ for _c_set_json in os.listdir(_data_dir):
37
+ if _c_set_json.endswith('.json'):
38
+ _c_set_dir = os.path.join(_data_dir, _c_set_json)
39
+ with open(_c_set_dir, 'r') as f:
40
+ _construction_set_standards_dict.update(json.load(f))
41
+
42
+
43
+ # then load construction sets from the user-supplied files
44
+ def load_construction_set_object(cset_dict, load_cons, con_sets, misc_cons):
45
+ """Load a construction set object from a dictionary and add it to the lib dict."""
46
+ try:
47
+ if cset_dict['type'] == 'ConstructionSetAbridged':
48
+ cset = ConstructionSet.from_dict_abridged(cset_dict, load_cons)
49
+ else:
50
+ cset = ConstructionSet.from_dict(cset_dict)
51
+ misc_cons.extend(cset.modified_constructions)
52
+ cset.lock()
53
+ assert cset_dict['identifier'] not in _default_sets, 'Cannot overwrite ' \
54
+ 'default construction set "{}".'.format(cset_dict['identifier'])
55
+ con_sets[cset_dict['identifier']] = cset
56
+ except Exception:
57
+ try: # see if the construction set is built with constructions in standards
58
+ import honeybee_energy.lib.constructions as _c
59
+ for key in cset_dict:
60
+ if isinstance(cset_dict[key], dict):
61
+ sub_dict = cset_dict[key]
62
+ for sub_key in sub_dict:
63
+ if sub_key == 'type' or sub_key in load_cons:
64
+ continue
65
+ if sub_dict[sub_key] is not None and \
66
+ sub_dict[sub_key] not in load_cons:
67
+ try:
68
+ load_cons[sub_dict[sub_key]] = \
69
+ _c.opaque_construction_by_identifier(
70
+ sub_dict[sub_key])
71
+ except ValueError:
72
+ load_cons[sub_dict[sub_key]] = \
73
+ _c.window_construction_by_identifier(
74
+ sub_dict[sub_key])
75
+ elif key == 'shade_construction' and cset_dict[key] is not None \
76
+ and cset_dict[key] not in load_cons:
77
+ load_cons[cset_dict[key]] = \
78
+ _c.shade_construction_by_identifier(cset_dict[key])
79
+ elif key == 'air_boundary_construction' \
80
+ and cset_dict[key] is not None \
81
+ and cset_dict[key] not in load_cons:
82
+ load_cons[cset_dict[key]] = \
83
+ _c.opaque_construction_by_identifier(cset_dict[key])
84
+ con_sets[cset_dict['identifier']] = \
85
+ ConstructionSet.from_dict_abridged(cset_dict, load_cons)
86
+ except Exception:
87
+ pass # not a Honeybee ConstructionSet JSON; possibly a comment
88
+
89
+
90
+ def load_constructionsets_from_folder(constructionset_lib_folder, loaded_constructions):
91
+ """Load all of the ConstructionSet objects from a constructionset standards folder.
92
+
93
+ Args:
94
+ constructionset_lib_folder: Path to a constructionsets sub-folder within a
95
+ honeybee standards folder.
96
+ loaded_constructions: A dictionary of constructions that have already
97
+ been loaded from the library.
98
+ """
99
+ con_sets, misc_cons = {}, []
100
+ for f in os.listdir(constructionset_lib_folder):
101
+ f_path = os.path.join(constructionset_lib_folder, f)
102
+ if os.path.isfile(f_path) and f_path.endswith('.json'):
103
+ with open(f_path, 'r') as json_file:
104
+ c_dict = json.load(json_file)
105
+ if 'type' in c_dict: # single object
106
+ load_construction_set_object(
107
+ c_dict, loaded_constructions, con_sets, misc_cons)
108
+ else: # a collection of several objects
109
+ for c_id in c_dict:
110
+ load_construction_set_object(
111
+ c_dict[c_id], loaded_constructions, con_sets, misc_cons)
112
+ return con_sets, misc_cons
113
+
114
+
115
+ loaded_sets, misc_c = \
116
+ load_constructionsets_from_folder(folders.constructionset_lib, _all_constructions)
117
+ _construction_sets.update(loaded_sets)