fairyfly-therm 0.3.1__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.
@@ -0,0 +1,412 @@
1
+ # coding=utf-8
2
+ """Comprehensive THERM condition."""
3
+ from __future__ import division
4
+ import xml.etree.ElementTree as ET
5
+
6
+ from fairyfly._lockable import lockable
7
+ from fairyfly.typing import float_in_range, float_positive, uuid_from_therm_id
8
+
9
+ from ._base import _ThermConditionBase
10
+
11
+
12
+ @lockable
13
+ class ComprehensiveCondition(_ThermConditionBase):
14
+ """Typical comprehensive condition.
15
+
16
+ Args:
17
+ temperature: A number for the temperature at the boundary in degrees Celsius.
18
+ For NFRC conditions, this temperature should be 21C for interior
19
+ boundary conditions and -18 C for winter exterior boundary conditions.
20
+ film_coefficient: A number in W/m2-K that represents the convective
21
+ resistance of the air film at the boundary condition. Typical film
22
+ coefficient values range from 36 W/m2-K (for an exterior condition
23
+ where outdoor wind strips away most convective resistance) to 2.5 W/m2-K
24
+ (for a vertically-oriented interior wood/vinyl surface). For NFRC
25
+ conditions, this should be 26 for exterior boundary conditions and
26
+ around 3 for interior boundary conditions.
27
+ emissivity: An optional number between 0 and 1 to set the emissivity
28
+ along the boundary, which represents the emissivity of the
29
+ environment to which the material in contact with the boundary is
30
+ radiating to. (Default: 1).
31
+ radiant_temperature: A number for the radiant temperature at the boundary
32
+ in degrees Celsius. If None, this will be the same as the specified
33
+ temperature. (Default: None).
34
+ heat_flux: An optional number in W/m2 that represents additional energy
35
+ flux across the boundary. This can be used to account for solar flux
36
+ among other forms of heat flux. (Default: 0).
37
+ relative_humidity: An optional value between 0 and 1 for the relative
38
+ humidity along the boundary. (Default: 0.5).
39
+ identifier: Text string for a unique object ID. Must be a UUID in the
40
+ format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. If None, a UUID will
41
+ automatically be generated. (Default: None).
42
+
43
+ Properties:
44
+ * identifier
45
+ * display_name
46
+ * temperature
47
+ * film_coefficient
48
+ * emissivity
49
+ * radiant_temperature
50
+ * heat_flux
51
+ * relative_humidity
52
+ * color
53
+ * protected
54
+ * project_tag
55
+ * user_data
56
+ """
57
+ __slots__ = ('_temperature', '_film_coefficient', '_emissivity',
58
+ '_radiant_temperature', '_heat_flux', '_relative_humidity')
59
+
60
+ def __init__(
61
+ self, temperature, film_coefficient, emissivity=1.0, radiant_temperature=None,
62
+ heat_flux=0, relative_humidity=0.5, identifier=None
63
+ ):
64
+ """Initialize therm material."""
65
+ _ThermConditionBase.__init__(self, identifier)
66
+ self.temperature = temperature
67
+ self.film_coefficient = film_coefficient
68
+ self.emissivity = emissivity
69
+ self.radiant_temperature = radiant_temperature
70
+ self.heat_flux = heat_flux
71
+ self.relative_humidity = relative_humidity
72
+
73
+ @property
74
+ def temperature(self):
75
+ """Get or set the temperature of the condition [C]."""
76
+ return self._temperature
77
+
78
+ @temperature.setter
79
+ def temperature(self, value):
80
+ self._temperature = \
81
+ float_in_range(value, mi=-273.15, input_name='condition temperature')
82
+
83
+ @property
84
+ def film_coefficient(self):
85
+ """Get or set the film coefficient along the boundary [ W/m2-K]."""
86
+ return self._film_coefficient
87
+
88
+ @film_coefficient.setter
89
+ def film_coefficient(self, value):
90
+ self._film_coefficient = float_positive(value, 'film coefficient')
91
+
92
+ @property
93
+ def emissivity(self):
94
+ """Get or set the hemispherical emissivity of the environment of the condition.
95
+ """
96
+ return self._emissivity
97
+
98
+ @emissivity.setter
99
+ def emissivity(self, ir_e):
100
+ ir_e = float_in_range(ir_e, 0.0, 1.0, 'condition emissivity')
101
+ self._emissivity = ir_e
102
+
103
+ @property
104
+ def radiant_temperature(self):
105
+ """Get or set the radiant temperature along the boundary."""
106
+ return self._radiant_temperature if self._radiant_temperature is not None \
107
+ else self._temperature
108
+
109
+ @radiant_temperature.setter
110
+ def radiant_temperature(self, ir_t):
111
+ if ir_t is not None:
112
+ ir_t = float_in_range(ir_t, mi=-273.15,
113
+ input_name='condition radiant temperature')
114
+ self._radiant_temperature = ir_t
115
+
116
+ @property
117
+ def heat_flux(self):
118
+ """Get or set the additional energy flux across the boundary [W/m2]."""
119
+ return self._heat_flux
120
+
121
+ @heat_flux.setter
122
+ def heat_flux(self, value):
123
+ self._heat_flux = float_positive(value, 'condition heat flux')
124
+
125
+ @property
126
+ def relative_humidity(self):
127
+ """Get or set a number between zero and one for the relative humidity."""
128
+ return self._relative_humidity
129
+
130
+ @relative_humidity.setter
131
+ def relative_humidity(self, value):
132
+ self._relative_humidity = \
133
+ float_in_range(value, 0.0, 1.0, 'condition relative humidity')
134
+
135
+ @classmethod
136
+ def from_therm_xml(cls, xml_element):
137
+ """Create ComprehensiveCondition from an XML element of a THERM BoundaryCondition.
138
+
139
+ Args:
140
+ xml_element: An XML element of a THERM BoundaryCondition.
141
+ """
142
+ # create the base material from the UUID, temperature, and film coefficient
143
+ xml_uuid = xml_element.find('UUID')
144
+ identifier = xml_uuid.text
145
+ if len(identifier) == 31:
146
+ identifier = uuid_from_therm_id(identifier)
147
+ xml_comp = xml_element.find('Comprehensive')
148
+ xml_conv = xml_comp.find('Convection')
149
+ xml_t = xml_conv.find('Temperature')
150
+ temperature = xml_t.text
151
+ xml_fc = xml_conv.find('FilmCoefficient')
152
+ film_coefficient = xml_fc.text
153
+ cond = ComprehensiveCondition(temperature, film_coefficient, identifier=identifier)
154
+ # assign the other attributes if specified
155
+ xml_rh = xml_comp.find('RelativeHumidity')
156
+ if xml_rh is not None:
157
+ cond.relative_humidity = xml_rh.text
158
+ xml_flux = xml_comp.find('ConstantFlux')
159
+ if xml_flux is not None:
160
+ xml_fl = xml_flux.find('Flux')
161
+ if xml_fl is not None:
162
+ cond.heat_flux = xml_fl.text
163
+ # assign the radiation properties if specified
164
+ xml_rad = xml_comp.find('Radiation')
165
+ if xml_rad is not None:
166
+ xml_rm = xml_rad.find('AutomaticEnclosure')
167
+ if xml_rm is not None:
168
+ xml_rm = xml_rad.find('BlackBodyRadiation')
169
+ if xml_rm is not None:
170
+ xml_mrt = xml_rm.find('Temperature')
171
+ if xml_mrt is not None:
172
+ cond.radiant_temperature = xml_mrt.text
173
+ xml_emiss = xml_rm.find('Emissivity')
174
+ if xml_emiss is not None:
175
+ cond.emissivity = xml_emiss.text
176
+ # assign the name and color if they are specified
177
+ xml_name = xml_element.find('Name')
178
+ if xml_name is not None:
179
+ cond.display_name = xml_name.text
180
+ xml_col = xml_element.find('Color')
181
+ if xml_col is not None:
182
+ cond.color = xml_col.text
183
+ xml_protect = xml_element.find('Protected')
184
+ if xml_protect is not None:
185
+ cond.protected = True if xml_protect.text == 'true' else False
186
+ xml_tag = xml_element.find('ProjectNameTag')
187
+ if xml_tag is not None:
188
+ cond.project_tag = xml_tag.text
189
+ return cond
190
+
191
+ @classmethod
192
+ def from_therm_xml_str(cls, xml_str):
193
+ """Create ComprehensiveCondition from an XML string of a THERM BoundaryCondition.
194
+
195
+ Args:
196
+ xml_str: An XML text string of a THERM BoundaryCondition.
197
+ """
198
+ root = ET.fromstring(xml_str)
199
+ return cls.from_therm_xml(root)
200
+
201
+ @classmethod
202
+ def from_dict(cls, data):
203
+ """Create a ComprehensiveCondition from a dictionary.
204
+
205
+ Args:
206
+ data: A python dictionary in the following format
207
+
208
+ .. code-block:: python
209
+
210
+ {
211
+ "type": 'ComprehensiveCondition',
212
+ "identifier": 'f26f3597-e3ee-43fe-a0b3-ec673f993d86',
213
+ "display_name": 'NFRC 100-2010 Exterior',
214
+ "temperature": -18,
215
+ "film_coefficient": 26,
216
+ "emissivity": 1,
217
+ "radiant_temperature": -18,
218
+ "heat_flux": 0,
219
+ "relative_humidity": 0.5
220
+ }
221
+ """
222
+ assert data['type'] == 'ComprehensiveCondition', \
223
+ 'Expected ComprehensiveCondition. Got {}.'.format(data['type'])
224
+
225
+ emiss = data['emissivity'] if 'emissivity' in data and \
226
+ data['emissivity'] is not None else 1.0
227
+ rad_temp = data['radiant_temperature'] if 'radiant_temperature' in data else None
228
+ heat_flux = data['heat_flux'] if 'heat_flux' in data else 0
229
+ rh = data['relative_humidity'] if 'relative_humidity' in data else 0.5
230
+
231
+ new_cond = cls(
232
+ data['temperature'], data['film_coefficient'],
233
+ emiss, rad_temp, heat_flux, rh, data['identifier']
234
+ )
235
+ if 'display_name' in data and data['display_name'] is not None:
236
+ new_cond.display_name = data['display_name']
237
+ if 'color' in data and data['color'] is not None:
238
+ new_cond.color = data['color']
239
+ if 'protected' in data and data['protected'] is not None:
240
+ new_cond.protected = data['protected']
241
+ if 'project_tag' in data and data['project_tag']:
242
+ new_cond._project_tag = data['project_tag']
243
+ if 'user_data' in data and data['user_data'] is not None:
244
+ new_cond.user_data = data['user_data']
245
+ return new_cond
246
+
247
+ def to_therm_xml(self, bcs_element=None):
248
+ """Get an THERM XML element of the boundary condition.
249
+
250
+ Args:
251
+ bcs_element: An optional XML Element for the BoundaryConditions to
252
+ which the generated objects will be added. If None, a new XML
253
+ Element will be generated.
254
+
255
+ .. code-block:: xml
256
+
257
+ <BoundaryCondition>
258
+ <UUID>1810f37a-de4d-4e2d-95d5-09fe01157c34</UUID>
259
+ <Name>NFRC 100-2010 Exterior</Name>
260
+ <ProjectNameTag></ProjectNameTag>
261
+ <Protected>true</Protected>
262
+ <Color>0x0080C0</Color>
263
+ <IGUSurface>false</IGUSurface>
264
+ <Comprehensive>
265
+ <RelativeHumidity>0.5</RelativeHumidity>
266
+ <Convection>
267
+ <Temperature>-18</Temperature>
268
+ <FilmCoefficient>26</FilmCoefficient>
269
+ </Convection>
270
+ <ConstantFlux>
271
+ <Flux>0</Flux>
272
+ </ConstantFlux>
273
+ <Radiation>
274
+ <BlackBodyRadiation>
275
+ <Temperature>-18</Temperature>
276
+ <Emissivity>1</Emissivity>
277
+ <ViewFactor>1</ViewFactor>
278
+ </BlackBodyRadiation>
279
+ </Radiation>
280
+ </Comprehensive>
281
+ </BoundaryCondition>
282
+ """
283
+ # create a new Materials element if one is not specified
284
+ if bcs_element is not None:
285
+ xml_cond = ET.SubElement(bcs_element, 'BoundaryCondition')
286
+ else:
287
+ xml_cond = ET.Element('BoundaryCondition')
288
+ # add all of the required basic attributes
289
+ xml_id = ET.SubElement(xml_cond, 'UUID')
290
+ xml_id.text = self.identifier
291
+ xml_name = ET.SubElement(xml_cond, 'Name')
292
+ xml_name.text = self.display_name
293
+ if self.project_tag:
294
+ xml_tag = ET.SubElement(xml_cond, 'ProjectNameTag')
295
+ xml_tag.text = self.project_tag
296
+ xml_protect = ET.SubElement(xml_cond, 'Protected')
297
+ xml_protect.text = 'true' if self.protected else 'false'
298
+ xml_color = ET.SubElement(xml_cond, 'Color')
299
+ xml_color.text = self.color.to_hex().replace('#', '0x')
300
+ xml_igu = ET.SubElement(xml_cond, 'IGUSurface')
301
+ xml_igu.text = 'false'
302
+ # add all of the comprehensive attributes
303
+ xml_comp = ET.SubElement(xml_cond, 'Comprehensive')
304
+ xml_rh = ET.SubElement(xml_comp, 'RelativeHumidity')
305
+ xml_rh.text = str(self.relative_humidity)
306
+ xml_conv = ET.SubElement(xml_comp, 'Convection')
307
+ xml_ct = ET.SubElement(xml_conv, 'Temperature')
308
+ xml_ct.text = str(self.temperature)
309
+ xml_fc = ET.SubElement(xml_conv, 'FilmCoefficient')
310
+ xml_fc.text = str(self.film_coefficient)
311
+ xml_heat = ET.SubElement(xml_comp, 'ConstantFlux')
312
+ xml_flux = ET.SubElement(xml_heat, 'Flux')
313
+ xml_flux.text = str(self.heat_flux)
314
+ xml_rad = ET.SubElement(xml_comp, 'Radiation')
315
+ xml_auto = ET.SubElement(xml_rad, 'Radiation')
316
+ xml_mrt = ET.SubElement(xml_auto, 'Temperature')
317
+ xml_mrt.text = str(self.radiant_temperature)
318
+ xml_emiss = ET.SubElement(xml_auto, 'Emissivity')
319
+ xml_emiss.text = str(self.emissivity)
320
+ return xml_cond
321
+
322
+ def to_therm_xml_str(self):
323
+ """Get an THERM XML string of the condition."""
324
+ xml_root = self.to_therm_xml()
325
+ try: # try to indent the XML to make it read-able
326
+ ET.indent(xml_root)
327
+ return ET.tostring(xml_root, encoding='unicode')
328
+ except AttributeError: # we are in Python 2 and no indent is available
329
+ return ET.tostring(xml_root)
330
+
331
+ def to_dict(self):
332
+ """ComprehensiveCondition dictionary representation."""
333
+ base = {
334
+ 'type': 'ComprehensiveCondition',
335
+ 'identifier': self.identifier,
336
+ 'temperature': self.temperature,
337
+ 'film_coefficient': self.film_coefficient
338
+ }
339
+ if self.emissivity != 1:
340
+ base['emissivity'] = self.emissivity
341
+ if self._radiant_temperature is not None:
342
+ base['radiant_temperature'] = self._radiant_temperature
343
+ if self.heat_flux != 0:
344
+ base['heat_flux'] = self.heat_flux
345
+ if self._relative_humidity != 0.5:
346
+ base['relative_humidity'] = self.relative_humidity
347
+ if self._display_name is not None:
348
+ base['display_name'] = self.display_name
349
+ base['protected'] = self._protected
350
+ base['color'] = self.color.to_hex()
351
+ if self._project_tag is not None:
352
+ base['project_tag'] = self.project_tag
353
+ if self._user_data is not None:
354
+ base['user_data'] = self.user_data
355
+ return base
356
+
357
+ @staticmethod
358
+ def extract_all_from_xml_file(xml_file):
359
+ """Extract all Condition objects from a THERM XML file.
360
+
361
+ Args:
362
+ xml_file: A path to an XML file containing objects Condition objects.
363
+
364
+ Returns:
365
+ A list of all Comprehensive Condition objects in the file.
366
+ """
367
+ # read the file and get the root
368
+ tree = ET.parse(xml_file)
369
+ root = tree.getroot()
370
+ # extract all of the PureGas objects
371
+ conditions = []
372
+ for con_obj in root:
373
+ if con_obj.tag == 'BoundaryCondition' and \
374
+ con_obj.find('Comprehensive') is not None:
375
+ ComprehensiveCondition.from_therm_xml(con_obj)
376
+ try:
377
+ conditions.append(ComprehensiveCondition.from_therm_xml(con_obj))
378
+ except Exception: # not a valid conditions
379
+ pass
380
+ return conditions
381
+
382
+ def __key(self):
383
+ """A tuple based on the object properties, useful for hashing."""
384
+ return (self.identifier, self.temperature, self.film_coefficient,
385
+ self.emissivity, self.radiant_temperature, self.heat_flux,
386
+ self.relative_humidity)
387
+
388
+ def __hash__(self):
389
+ return hash(self.__key())
390
+
391
+ def __eq__(self, other):
392
+ return isinstance(other, ComprehensiveCondition) and self.__key() == other.__key()
393
+
394
+ def __ne__(self, other):
395
+ return not self.__eq__(other)
396
+
397
+ def __copy__(self):
398
+ new_cond = self.__class__(
399
+ self.temperature, self.film_coefficient,
400
+ self._emissivity, self._radiant_temperature, self._heat_flux,
401
+ self._relative_humidity, self.identifier
402
+ )
403
+ new_cond._display_name = self._display_name
404
+ new_cond._color = self._color
405
+ new_cond._protected = self._protected
406
+ new_cond._project_tag = self._project_tag
407
+ new_cond._user_data = None if self._user_data is None \
408
+ else self._user_data.copy()
409
+ return new_cond
410
+
411
+ def __repr__(self):
412
+ return 'Comprehensive THERM Condition: {}'.format(self.display_name)
@@ -0,0 +1,6 @@
1
+ {
2
+ "__comment__": "Add full paths to folders (eg. C:/Program Files (x86)/lbnl/THERM8.1).",
3
+ "therm_path": "",
4
+ "lbnl_data_path": "",
5
+ "therm_lib_path": ""
6
+ }