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.
- fairyfly_therm/__init__.py +8 -0
- fairyfly_therm/__main__.py +4 -0
- fairyfly_therm/cli/__init__.py +47 -0
- fairyfly_therm/cli/setconfig.py +75 -0
- fairyfly_therm/condition/__init__.py +1 -0
- fairyfly_therm/condition/_base.py +167 -0
- fairyfly_therm/condition/comprehensive.py +412 -0
- fairyfly_therm/config.json +6 -0
- fairyfly_therm/config.py +315 -0
- fairyfly_therm/lib/__init__.py +1 -0
- fairyfly_therm/lib/_loadconditions.py +87 -0
- fairyfly_therm/lib/_loadgases.py +98 -0
- fairyfly_therm/lib/_loadmaterials.py +98 -0
- fairyfly_therm/lib/conditions.py +24 -0
- fairyfly_therm/lib/gases.py +36 -0
- fairyfly_therm/lib/materials.py +38 -0
- fairyfly_therm/material/__init__.py +1 -0
- fairyfly_therm/material/_base.py +182 -0
- fairyfly_therm/material/cavity.py +377 -0
- fairyfly_therm/material/gas.py +925 -0
- fairyfly_therm/material/solid.py +399 -0
- fairyfly_therm/material/xmlutil.py +45 -0
- fairyfly_therm-0.3.1.dist-info/METADATA +86 -0
- fairyfly_therm-0.3.1.dist-info/RECORD +28 -0
- fairyfly_therm-0.3.1.dist-info/WHEEL +5 -0
- fairyfly_therm-0.3.1.dist-info/entry_points.txt +2 -0
- fairyfly_therm-0.3.1.dist-info/licenses/LICENSE +661 -0
- fairyfly_therm-0.3.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Establish the default materials within the fairyfly_therm library."""
|
|
2
|
+
from ._loadmaterials import _solid_materials, _cavity_materials
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# establish variables for the default materials used across the library
|
|
6
|
+
concrete = _solid_materials['Generic HW Concrete']
|
|
7
|
+
air_cavity = _cavity_materials['Frame Cavity - CEN Simplified']
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# make lists of material identifiers to look up items in the library
|
|
11
|
+
SOLID_MATERIALS = tuple(_solid_materials.keys())
|
|
12
|
+
CAVITY_MATERIALS = tuple(_cavity_materials.keys())
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def solid_material_by_name(material_name):
|
|
16
|
+
"""Get a solid material from the library given the material name.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
material_name: A text string for the display_name of the material.
|
|
20
|
+
"""
|
|
21
|
+
try: # first check the default data
|
|
22
|
+
return _solid_materials[material_name]
|
|
23
|
+
except KeyError:
|
|
24
|
+
raise ValueError(
|
|
25
|
+
'"{}" was not found in the solid material library.'.format(material_name))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def cavity_material_by_name(material_name):
|
|
29
|
+
"""Get a cavity material from the library given the material name.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
material_name: A text string for the display_name of the material.
|
|
33
|
+
"""
|
|
34
|
+
try: # first check the default data
|
|
35
|
+
return _cavity_materials[material_name]
|
|
36
|
+
except KeyError:
|
|
37
|
+
raise ValueError(
|
|
38
|
+
'"{}" was not found in the cavity material library.'.format(material_name))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""fairyfly-therm materials."""
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""Base therm material."""
|
|
3
|
+
from __future__ import division
|
|
4
|
+
import uuid
|
|
5
|
+
import random
|
|
6
|
+
|
|
7
|
+
from ladybug.color import Color
|
|
8
|
+
from fairyfly._lockable import lockable
|
|
9
|
+
from fairyfly.typing import valid_uuid
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@lockable
|
|
13
|
+
class _ResourceObjectBase(object):
|
|
14
|
+
"""Base class for resources.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
identifier: Text string for a unique Material ID. Must be < 100 characters
|
|
18
|
+
and not contain any thermPlus special characters. This will be used to
|
|
19
|
+
identify the object across a model and in the exported IDF.
|
|
20
|
+
|
|
21
|
+
Properties:
|
|
22
|
+
* identifier
|
|
23
|
+
* display_name
|
|
24
|
+
* protected
|
|
25
|
+
* user_data
|
|
26
|
+
"""
|
|
27
|
+
__slots__ = ('_identifier', '_display_name', '_protected', '_user_data', '_locked')
|
|
28
|
+
|
|
29
|
+
def __init__(self, identifier):
|
|
30
|
+
"""Initialize resource object base."""
|
|
31
|
+
self._locked = False
|
|
32
|
+
self.identifier = identifier
|
|
33
|
+
self._display_name = None
|
|
34
|
+
self.protected = False
|
|
35
|
+
self._user_data = None
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def identifier(self):
|
|
39
|
+
"""Get or set a text string for the unique object identifier.
|
|
40
|
+
|
|
41
|
+
This must be a UUID in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
|
|
42
|
+
and it remains constant as the object is mutated, copied, and
|
|
43
|
+
serialized to different formats (eg. therm XML). As such, this
|
|
44
|
+
property is used to reference the object across a Model.
|
|
45
|
+
"""
|
|
46
|
+
return self._identifier
|
|
47
|
+
|
|
48
|
+
@identifier.setter
|
|
49
|
+
def identifier(self, value):
|
|
50
|
+
if value is None:
|
|
51
|
+
self._identifier = str(uuid.uuid4())
|
|
52
|
+
else:
|
|
53
|
+
self._identifier = valid_uuid(value, 'therm material identifier')
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def display_name(self):
|
|
57
|
+
"""Get or set a string for the object name without any character restrictions.
|
|
58
|
+
|
|
59
|
+
If not set, this will be equal to the identifier.
|
|
60
|
+
"""
|
|
61
|
+
if self._display_name is None:
|
|
62
|
+
return self._identifier
|
|
63
|
+
return self._display_name
|
|
64
|
+
|
|
65
|
+
@display_name.setter
|
|
66
|
+
def display_name(self, value):
|
|
67
|
+
if value is not None:
|
|
68
|
+
try:
|
|
69
|
+
value = str(value)
|
|
70
|
+
except UnicodeEncodeError: # Python 2 machine lacking the character set
|
|
71
|
+
pass # keep it as unicode
|
|
72
|
+
self._display_name = value
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def protected(self):
|
|
76
|
+
"""Get or set a boolean for whether the material is protected in THERM."""
|
|
77
|
+
return self._protected
|
|
78
|
+
|
|
79
|
+
@protected.setter
|
|
80
|
+
def protected(self, value):
|
|
81
|
+
try:
|
|
82
|
+
self._protected = bool(value)
|
|
83
|
+
except TypeError:
|
|
84
|
+
raise TypeError(
|
|
85
|
+
'Expected boolean for Material.protected. Got {}.'.format(value))
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def user_data(self):
|
|
89
|
+
"""Get or set an optional dictionary for additional meta data for this object.
|
|
90
|
+
|
|
91
|
+
This will be None until it has been set. All keys and values of this
|
|
92
|
+
dictionary should be of a standard Python type to ensure correct
|
|
93
|
+
serialization of the object to/from JSON (eg. str, float, int, list, dict)
|
|
94
|
+
"""
|
|
95
|
+
if self._user_data is not None:
|
|
96
|
+
return self._user_data
|
|
97
|
+
|
|
98
|
+
@user_data.setter
|
|
99
|
+
def user_data(self, value):
|
|
100
|
+
if value is not None:
|
|
101
|
+
assert isinstance(value, dict), 'Expected dictionary for fairyfly_therm' \
|
|
102
|
+
'object user_data. Got {}.'.format(type(value))
|
|
103
|
+
self._user_data = value
|
|
104
|
+
|
|
105
|
+
def duplicate(self):
|
|
106
|
+
"""Get a copy of this object."""
|
|
107
|
+
return self.__copy__()
|
|
108
|
+
|
|
109
|
+
def __copy__(self):
|
|
110
|
+
new_obj = self.__class__(self.identifier)
|
|
111
|
+
new_obj._display_name = self._display_name
|
|
112
|
+
new_obj._protected = self._protected
|
|
113
|
+
new_obj._user_data = None if self._user_data is None else self._user_data.copy()
|
|
114
|
+
return new_obj
|
|
115
|
+
|
|
116
|
+
def ToString(self):
|
|
117
|
+
"""Overwrite .NET ToString."""
|
|
118
|
+
return self.__repr__()
|
|
119
|
+
|
|
120
|
+
def __repr__(self):
|
|
121
|
+
return 'Base THERM Resource:\n{}'.format(self.display_name)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@lockable
|
|
125
|
+
class _ThermMaterialBase(_ResourceObjectBase):
|
|
126
|
+
"""Base therm material.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
identifier: Text string for a unique Material ID. Must be < 100 characters
|
|
130
|
+
and not contain any thermPlus special characters. This will be used to
|
|
131
|
+
identify the object across a model and in the exported IDF.
|
|
132
|
+
|
|
133
|
+
Properties:
|
|
134
|
+
* identifier
|
|
135
|
+
* display_name
|
|
136
|
+
* color
|
|
137
|
+
* protected
|
|
138
|
+
* user_data
|
|
139
|
+
"""
|
|
140
|
+
__slots__ = ('_color',)
|
|
141
|
+
|
|
142
|
+
def __init__(self, identifier):
|
|
143
|
+
"""Initialize therm material base."""
|
|
144
|
+
_ResourceObjectBase.__init__(self, identifier)
|
|
145
|
+
self.color = None
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def color(self):
|
|
149
|
+
"""Get or set an optional color for the material as it displays in THERM.
|
|
150
|
+
|
|
151
|
+
This will always be a Ladybug Color object when getting this property
|
|
152
|
+
but the setter supports specifying hex codes. If unspecified, a radom
|
|
153
|
+
color will automatically be assigned.
|
|
154
|
+
"""
|
|
155
|
+
return self._color
|
|
156
|
+
|
|
157
|
+
@color.setter
|
|
158
|
+
def color(self, value):
|
|
159
|
+
if value is None:
|
|
160
|
+
self._color = Color(
|
|
161
|
+
random.randint(0, 255),
|
|
162
|
+
random.randint(0, 255),
|
|
163
|
+
random.randint(0, 255)
|
|
164
|
+
)
|
|
165
|
+
elif isinstance(value, str):
|
|
166
|
+
value = value.replace('0x', '')
|
|
167
|
+
self._color = Color.from_hex(value)
|
|
168
|
+
else:
|
|
169
|
+
assert isinstance(value, Color), 'Expected ladybug Color object for ' \
|
|
170
|
+
'material color. Got {}.'.format(type(value))
|
|
171
|
+
self._color = value
|
|
172
|
+
|
|
173
|
+
def __copy__(self):
|
|
174
|
+
new_obj = self.__class__(self.identifier)
|
|
175
|
+
new_obj._display_name = self._display_name
|
|
176
|
+
new_obj._color = self._color
|
|
177
|
+
new_obj._protected = self._protected
|
|
178
|
+
new_obj._user_data = None if self._user_data is None else self._user_data.copy()
|
|
179
|
+
return new_obj
|
|
180
|
+
|
|
181
|
+
def __repr__(self):
|
|
182
|
+
return 'Base THERM Material:\n{}'.format(self.display_name)
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
# coding=utf-8
|
|
2
|
+
"""Cavity THERM material."""
|
|
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, therm_id_from_uuid, uuid_from_therm_id
|
|
8
|
+
|
|
9
|
+
from ._base import _ThermMaterialBase
|
|
10
|
+
from .gas import Gas
|
|
11
|
+
from ..lib.gases import air
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@lockable
|
|
15
|
+
class CavityMaterial(_ThermMaterialBase):
|
|
16
|
+
"""Typical cavity material.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
gas: A Gas material object for the gas that fills the cavity. (Default: air).
|
|
20
|
+
cavity_model: Text for the type of cavity model to be used to determine
|
|
21
|
+
the thermal resistance of the material. Choose from the following:
|
|
22
|
+
|
|
23
|
+
* CEN
|
|
24
|
+
* NFRC
|
|
25
|
+
* ISO15099
|
|
26
|
+
* ISO15099Ventilated
|
|
27
|
+
|
|
28
|
+
emissivity: Number between 0 and 1 for the infrared hemispherical
|
|
29
|
+
emissivity of the front side of the material. (Default: 0.9).
|
|
30
|
+
emissivity_back: Number between 0 and 1 for the infrared hemispherical
|
|
31
|
+
emissivity of the back side of the material. If None, this will
|
|
32
|
+
default to the same value specified for emissivity. (Default: None)
|
|
33
|
+
identifier: Text string for a unique object ID. Must be a UUID in the
|
|
34
|
+
format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. If None, a UUID will
|
|
35
|
+
automatically be generated. (Default: None).
|
|
36
|
+
|
|
37
|
+
Properties:
|
|
38
|
+
* identifier
|
|
39
|
+
* display_name
|
|
40
|
+
* therm_uuid
|
|
41
|
+
* gas
|
|
42
|
+
* cavity_model
|
|
43
|
+
* emissivity
|
|
44
|
+
* emissivity_back
|
|
45
|
+
* color
|
|
46
|
+
* protected
|
|
47
|
+
* user_data
|
|
48
|
+
"""
|
|
49
|
+
__slots__ = ('_gas', '_cavity_model', '_emissivity', '_emissivity_back')
|
|
50
|
+
CAVITY_MODELS = ('CEN', 'NFRC', 'ISO15099', 'ISO15099Ventilated')
|
|
51
|
+
|
|
52
|
+
def __init__(
|
|
53
|
+
self, gas=air, cavity_model='CEN', emissivity=0.9, emissivity_back=None,
|
|
54
|
+
identifier=None
|
|
55
|
+
):
|
|
56
|
+
"""Initialize therm material."""
|
|
57
|
+
_ThermMaterialBase.__init__(self, identifier)
|
|
58
|
+
self.gas = gas
|
|
59
|
+
self.cavity_model = cavity_model
|
|
60
|
+
self.emissivity = emissivity
|
|
61
|
+
self.emissivity_back = emissivity_back
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def gas(self):
|
|
65
|
+
"""Get or set a Gas object used to denote the gas in the cavity."""
|
|
66
|
+
return self._gas
|
|
67
|
+
|
|
68
|
+
@gas.setter
|
|
69
|
+
def gas(self, value):
|
|
70
|
+
if value is not None:
|
|
71
|
+
assert isinstance(value, Gas), 'Expected Gas object for CavityMaterial ' \
|
|
72
|
+
'gas. Got {}.'.format(type(value))
|
|
73
|
+
else:
|
|
74
|
+
value = air
|
|
75
|
+
self._gas = value
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def cavity_model(self):
|
|
79
|
+
"""Get or set text for the convection model to be used in the cavity."""
|
|
80
|
+
return self._cavity_model
|
|
81
|
+
|
|
82
|
+
@cavity_model.setter
|
|
83
|
+
def cavity_model(self, value):
|
|
84
|
+
if value is not None:
|
|
85
|
+
clean_input = str(value).lower()
|
|
86
|
+
for key in self.CAVITY_MODELS:
|
|
87
|
+
if key.lower() == clean_input:
|
|
88
|
+
value = key
|
|
89
|
+
break
|
|
90
|
+
else:
|
|
91
|
+
raise ValueError(
|
|
92
|
+
'Material cavity_model "{}" is not supported.\n'
|
|
93
|
+
'Choose from the following:\n{}'.format(
|
|
94
|
+
value, '\n'.join(self.CAVITY_MODELS)))
|
|
95
|
+
self._cavity_model = value
|
|
96
|
+
else:
|
|
97
|
+
self._cavity_model = self.CAVITY_MODELS[0]
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def emissivity(self):
|
|
101
|
+
"""Get or set the hemispherical emissivity of the front side of the material."""
|
|
102
|
+
return self._emissivity
|
|
103
|
+
|
|
104
|
+
@emissivity.setter
|
|
105
|
+
def emissivity(self, ir_e):
|
|
106
|
+
ir_e = float_in_range(ir_e, 0.0, 1.0, 'material emissivity')
|
|
107
|
+
self._emissivity = ir_e
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def emissivity_back(self):
|
|
111
|
+
"""Get or set the hemispherical emissivity of the back side of the material."""
|
|
112
|
+
return self._emissivity_back if self._emissivity_back is not None \
|
|
113
|
+
else self._emissivity
|
|
114
|
+
|
|
115
|
+
@emissivity_back.setter
|
|
116
|
+
def emissivity_back(self, ir_e):
|
|
117
|
+
if ir_e is not None:
|
|
118
|
+
ir_e = float_in_range(ir_e, 0.0, 1.0, 'material emissivity')
|
|
119
|
+
self._emissivity_back = ir_e
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def therm_uuid(self):
|
|
123
|
+
"""Get the UUID of this object as it would appear in a THERM XML or thmz file.
|
|
124
|
+
|
|
125
|
+
This is always derived from the object identifier but this is slightly
|
|
126
|
+
different than standard UUIDs, which have 4 more values in a 8-4-4-4-12
|
|
127
|
+
structure instead of a 8-4-4-12 structure used by THERM.
|
|
128
|
+
"""
|
|
129
|
+
return therm_id_from_uuid(self._identifier)
|
|
130
|
+
|
|
131
|
+
@classmethod
|
|
132
|
+
def from_therm_xml(cls, xml_element, gases):
|
|
133
|
+
"""Create a CavityMaterial from an XML element of a THERM Material.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
xml_element: An XML element of a THERM material.
|
|
137
|
+
gases: A dictionary with gas names as keys and Gas object instances
|
|
138
|
+
as values. These will be used to reassign the gas that fills
|
|
139
|
+
this cavity.
|
|
140
|
+
"""
|
|
141
|
+
# create the base material from the UUID and conductivity
|
|
142
|
+
xml_uuid = xml_element.find('UUID')
|
|
143
|
+
identifier = xml_uuid.text
|
|
144
|
+
if len(identifier) == 31:
|
|
145
|
+
identifier = uuid_from_therm_id(identifier)
|
|
146
|
+
xml_cavity = xml_element.find('Cavity')
|
|
147
|
+
xml_c_model = xml_cavity.find('CavityStandard')
|
|
148
|
+
cavity_model = xml_c_model.text
|
|
149
|
+
xml_gas = xml_cavity.find('Gas')
|
|
150
|
+
try:
|
|
151
|
+
gas = gases[xml_gas.text]
|
|
152
|
+
except KeyError as e:
|
|
153
|
+
raise ValueError('Failed to find {} in gases.'.format(e))
|
|
154
|
+
mat = CavityMaterial(gas, cavity_model, identifier=identifier)
|
|
155
|
+
# assign the other attributes if specified
|
|
156
|
+
xml_emiss1 = xml_cavity.find('EmissivitySide1')
|
|
157
|
+
if xml_emiss1 is not None:
|
|
158
|
+
mat.emissivity = xml_emiss1.text
|
|
159
|
+
xml_emiss2 = xml_cavity.find('EmissivitySide2')
|
|
160
|
+
if xml_emiss2 is not None:
|
|
161
|
+
mat.emissivity_back = xml_emiss2.text
|
|
162
|
+
# assign the name and color if they are specified
|
|
163
|
+
xml_name = xml_element.find('Name')
|
|
164
|
+
if xml_name is not None:
|
|
165
|
+
mat.display_name = xml_name.text
|
|
166
|
+
xml_col = xml_element.find('Color')
|
|
167
|
+
if xml_col is not None:
|
|
168
|
+
mat.color = xml_col.text
|
|
169
|
+
xml_protect = xml_element.find('Protected')
|
|
170
|
+
if xml_protect is not None:
|
|
171
|
+
mat.protected = True if xml_protect.text == 'true' else False
|
|
172
|
+
return mat
|
|
173
|
+
|
|
174
|
+
@classmethod
|
|
175
|
+
def from_therm_xml_str(cls, xml_str, gases):
|
|
176
|
+
"""Create a CavityMaterial from an XML text string of a THERM Material.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
xml_str: An XML text string of a THERM material.
|
|
180
|
+
gases: A dictionary with gas names as keys and Gas object instances
|
|
181
|
+
as values. These will be used to reassign the gas that fills
|
|
182
|
+
this cavity.
|
|
183
|
+
"""
|
|
184
|
+
root = ET.fromstring(xml_str)
|
|
185
|
+
return cls.from_therm_xml(root, gases)
|
|
186
|
+
|
|
187
|
+
@classmethod
|
|
188
|
+
def from_dict(cls, data):
|
|
189
|
+
"""Create a CavityMaterial from a dictionary.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
data: A python dictionary in the following format
|
|
193
|
+
|
|
194
|
+
.. code-block:: python
|
|
195
|
+
|
|
196
|
+
{
|
|
197
|
+
"type": 'CavityMaterial',
|
|
198
|
+
"identifier": '0b46bbd7-0dbc-c148-3afe87431bf0',
|
|
199
|
+
"display_name": 'Frame Cavity - CEN Simplified',
|
|
200
|
+
"gas": {}, # dictionary definition of a gas
|
|
201
|
+
"cavity_model": "CEN",
|
|
202
|
+
"emissivity": 0.9,
|
|
203
|
+
"emissivity_back": 0.9
|
|
204
|
+
}
|
|
205
|
+
"""
|
|
206
|
+
assert data['type'] == 'CavityMaterial', \
|
|
207
|
+
'Expected CavityMaterial. Got {}.'.format(data['type'])
|
|
208
|
+
|
|
209
|
+
emiss = data['emissivity'] if 'emissivity' in data and \
|
|
210
|
+
data['emissivity'] is not None else 0.9
|
|
211
|
+
emiss_b = data['emissivity_back'] if 'emissivity_back' in data else None
|
|
212
|
+
new_mat = cls(
|
|
213
|
+
Gas.from_dict(data['gas']), data['cavity_model'], emiss, emiss_b,
|
|
214
|
+
data['identifier'])
|
|
215
|
+
cls._assign_optional_from_dict(new_mat, data)
|
|
216
|
+
return new_mat
|
|
217
|
+
|
|
218
|
+
@classmethod
|
|
219
|
+
def from_dict_abridged(cls, data, gases):
|
|
220
|
+
"""Create a Gas from an abridged dictionary.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
data: An GasAbridged dictionary.
|
|
224
|
+
gases: A dictionary with Gas identifiers as keys and Gas object instances
|
|
225
|
+
as values. These will be used to reassign the gas that fills
|
|
226
|
+
this cavity.
|
|
227
|
+
|
|
228
|
+
.. code-block:: python
|
|
229
|
+
|
|
230
|
+
{
|
|
231
|
+
"type": 'CavityMaterial',
|
|
232
|
+
"identifier": '0b46bbd7-0dbc-c148-3afe87431bf0',
|
|
233
|
+
"display_name": 'Frame Cavity - CEN Simplified',
|
|
234
|
+
"gas": '6c2409e9-5296-46c1-be11-9029b59a549b',
|
|
235
|
+
"cavity_model": "CEN",
|
|
236
|
+
"emissivity": 0.9,
|
|
237
|
+
"emissivity_back": 0.9
|
|
238
|
+
}
|
|
239
|
+
"""
|
|
240
|
+
assert data['type'] == 'CavityMaterialAbridged', \
|
|
241
|
+
'Expected CavityMaterialAbridged. Got {}.'.format(data['type'])
|
|
242
|
+
try:
|
|
243
|
+
gas_obj = gases[data['gas']]
|
|
244
|
+
except KeyError as e:
|
|
245
|
+
raise ValueError('Failed to find {} in gases.'.format(e))
|
|
246
|
+
emiss = data['emissivity'] if 'emissivity' in data and \
|
|
247
|
+
data['emissivity'] is not None else 0.9
|
|
248
|
+
emiss_b = data['emissivity_back'] if 'emissivity_back' in data else None
|
|
249
|
+
new_mat = cls(gas_obj, data['cavity_model'], emiss, emiss_b, data['identifier'])
|
|
250
|
+
cls._assign_optional_from_dict(new_mat, data)
|
|
251
|
+
return new_mat
|
|
252
|
+
|
|
253
|
+
@staticmethod
|
|
254
|
+
def _assign_optional_from_dict(new_obj, data):
|
|
255
|
+
"""Assign optional attributes when serializing from dict."""
|
|
256
|
+
if 'display_name' in data and data['display_name'] is not None:
|
|
257
|
+
new_obj.display_name = data['display_name']
|
|
258
|
+
if 'color' in data and data['color'] is not None:
|
|
259
|
+
new_obj.color = data['color']
|
|
260
|
+
if 'protected' in data and data['protected'] is not None:
|
|
261
|
+
new_obj.protected = data['protected']
|
|
262
|
+
if 'user_data' in data and data['user_data'] is not None:
|
|
263
|
+
new_obj.user_data = data['user_data']
|
|
264
|
+
|
|
265
|
+
def to_therm_xml(self, materials_element=None):
|
|
266
|
+
"""Get an THERM XML element of the material.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
materials_element: An optional XML Element for the Materials to
|
|
270
|
+
which the generated objects will be added. If None, a new XML
|
|
271
|
+
Element will be generated.
|
|
272
|
+
|
|
273
|
+
.. code-block:: xml
|
|
274
|
+
|
|
275
|
+
<Material>
|
|
276
|
+
<UUID>0b46bbd7-0dbc-c148-3afe87431bf0</UUID>
|
|
277
|
+
<Name>Frame Cavity - CEN Simplified</Name>
|
|
278
|
+
<Protected>false</Protected>
|
|
279
|
+
<Color>0xB3FFB3</Color>
|
|
280
|
+
<Cavity>
|
|
281
|
+
<CavityStandard>CEN</CavityStandard>
|
|
282
|
+
<Gas>Air</Gas>
|
|
283
|
+
<EmissivitySide1>0.9</EmissivitySide1>
|
|
284
|
+
<EmissivitySide2>0.9</EmissivitySide2>
|
|
285
|
+
</Cavity>
|
|
286
|
+
</Material>
|
|
287
|
+
"""
|
|
288
|
+
# create a new Materials element if one is not specified
|
|
289
|
+
if materials_element is not None:
|
|
290
|
+
xml_mat = ET.SubElement(materials_element, 'Material')
|
|
291
|
+
else:
|
|
292
|
+
xml_mat = ET.Element('Material')
|
|
293
|
+
# add all of the required basic attributes
|
|
294
|
+
xml_id = ET.SubElement(xml_mat, 'UUID')
|
|
295
|
+
xml_id.text = self.therm_uuid
|
|
296
|
+
xml_name = ET.SubElement(xml_mat, 'Name')
|
|
297
|
+
xml_name.text = self.display_name
|
|
298
|
+
xml_protect = ET.SubElement(xml_mat, 'Protected')
|
|
299
|
+
xml_protect.text = 'true' if self.protected else 'false'
|
|
300
|
+
xml_color = ET.SubElement(xml_mat, 'Color')
|
|
301
|
+
xml_color.text = self.color.to_hex().replace('#', '0x')
|
|
302
|
+
xml_cavity = ET.SubElement(xml_mat, 'Cavity')
|
|
303
|
+
# add all of the required cavity attributes
|
|
304
|
+
xml_model = ET.SubElement(xml_cavity, 'CavityStandard')
|
|
305
|
+
xml_model.text = self.cavity_model
|
|
306
|
+
xml_gas = ET.SubElement(xml_cavity, 'Gas')
|
|
307
|
+
xml_gas.text = self.gas.display_name
|
|
308
|
+
xml_emiss = ET.SubElement(xml_cavity, 'EmissivitySide1')
|
|
309
|
+
xml_emiss.text = str(self.emissivity)
|
|
310
|
+
xml_emiss_b = ET.SubElement(xml_cavity, 'EmissivitySide2')
|
|
311
|
+
xml_emiss_b.text = str(self.emissivity_back)
|
|
312
|
+
return xml_mat
|
|
313
|
+
|
|
314
|
+
def to_therm_xml_str(self):
|
|
315
|
+
"""Get an THERM XML string of the material."""
|
|
316
|
+
xml_root = self.to_therm_xml()
|
|
317
|
+
try: # try to indent the XML to make it read-able
|
|
318
|
+
ET.indent(xml_root)
|
|
319
|
+
return ET.tostring(xml_root, encoding='unicode')
|
|
320
|
+
except AttributeError: # we are in Python 2 and no indent is available
|
|
321
|
+
return ET.tostring(xml_root)
|
|
322
|
+
|
|
323
|
+
def to_dict(self, abridged=False):
|
|
324
|
+
"""CavityMaterial dictionary representation."""
|
|
325
|
+
base = {'type': 'CavityMaterial'} if not abridged \
|
|
326
|
+
else {'type': 'CavityMaterialAbridged'}
|
|
327
|
+
base['identifier'] = self.identifier
|
|
328
|
+
base['gas'] = self.gas.identifier if abridged else self.gas.to_dict()
|
|
329
|
+
base['cavity_model'] = self.cavity_model
|
|
330
|
+
base['emissivity'] = self.emissivity
|
|
331
|
+
if self._emissivity_back is not None:
|
|
332
|
+
base['emissivity_back'] = self.emissivity_back
|
|
333
|
+
if self._display_name is not None:
|
|
334
|
+
base['display_name'] = self.display_name
|
|
335
|
+
base['protected'] = self._protected
|
|
336
|
+
base['color'] = self.color.to_hex()
|
|
337
|
+
if self._user_data is not None:
|
|
338
|
+
base['user_data'] = self.user_data
|
|
339
|
+
return base
|
|
340
|
+
|
|
341
|
+
def lock(self):
|
|
342
|
+
"""The lock() method will also lock the gas."""
|
|
343
|
+
self._locked = True
|
|
344
|
+
self.gas.lock()
|
|
345
|
+
|
|
346
|
+
def unlock(self):
|
|
347
|
+
"""The unlock() method will also unlock the gas."""
|
|
348
|
+
self._locked = False
|
|
349
|
+
self.gas.unlock()
|
|
350
|
+
|
|
351
|
+
def __key(self):
|
|
352
|
+
"""A tuple based on the object properties, useful for hashing."""
|
|
353
|
+
return (self.therm_uuid, hash(self.gas), self.cavity_model,
|
|
354
|
+
self.emissivity, self.emissivity_back)
|
|
355
|
+
|
|
356
|
+
def __hash__(self):
|
|
357
|
+
return hash(self.__key())
|
|
358
|
+
|
|
359
|
+
def __eq__(self, other):
|
|
360
|
+
return isinstance(other, CavityMaterial) and self.__key() == other.__key()
|
|
361
|
+
|
|
362
|
+
def __ne__(self, other):
|
|
363
|
+
return not self.__eq__(other)
|
|
364
|
+
|
|
365
|
+
def __copy__(self):
|
|
366
|
+
new_material = self.__class__(
|
|
367
|
+
self.gas.duplicate(), self.cavity_model,
|
|
368
|
+
self._emissivity, self._emissivity_back, self.identifier)
|
|
369
|
+
new_material._display_name = self._display_name
|
|
370
|
+
new_material._color = self._color
|
|
371
|
+
new_material._protected = self._protected
|
|
372
|
+
new_material._user_data = None if self._user_data is None \
|
|
373
|
+
else self._user_data.copy()
|
|
374
|
+
return new_material
|
|
375
|
+
|
|
376
|
+
def __repr__(self):
|
|
377
|
+
return 'Cavity THERM Material: {}'.format(self.display_name)
|