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
fairyfly_therm/config.py
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
"""Fairyfly_therm configurations.
|
|
2
|
+
|
|
3
|
+
Import this into every module where access configurations are needed.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
|
|
7
|
+
.. code-block:: python
|
|
8
|
+
|
|
9
|
+
from fairyfly_therm.config import folders
|
|
10
|
+
print(folders.lbnl_data_path)
|
|
11
|
+
folders.lbnl_data_path = "C:/Users/Person/LBNL"
|
|
12
|
+
"""
|
|
13
|
+
import os
|
|
14
|
+
import json
|
|
15
|
+
import ladybug.config as lb_config
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class Folders(object):
|
|
19
|
+
"""Fairyfly_therm folders.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
config_file: The path to the config.json file from which folders are loaded.
|
|
23
|
+
If None, the config.json module included in this package will be used.
|
|
24
|
+
Default: None.
|
|
25
|
+
mute: If False, the paths to the various folders will be printed as they
|
|
26
|
+
are found. If True, no printing will occur upon initialization of this
|
|
27
|
+
class. Default: True.
|
|
28
|
+
|
|
29
|
+
Properties:
|
|
30
|
+
* therm_path
|
|
31
|
+
* therm_exe
|
|
32
|
+
* therm_version
|
|
33
|
+
* therm_version_str
|
|
34
|
+
* lbnl_data_path
|
|
35
|
+
* therm_settings_path
|
|
36
|
+
* therm_lib_path
|
|
37
|
+
* material_lib_file
|
|
38
|
+
* gas_lib_file
|
|
39
|
+
* bc_steady_state_lib_file
|
|
40
|
+
* config_file
|
|
41
|
+
* mute
|
|
42
|
+
"""
|
|
43
|
+
THERM_VERSION = (8, 1, 30)
|
|
44
|
+
|
|
45
|
+
def __init__(self, config_file=None, mute=True):
|
|
46
|
+
self.mute = bool(mute) # set the mute value
|
|
47
|
+
self.config_file = config_file # load paths from the config JSON file
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def therm_path(self):
|
|
51
|
+
"""Get or set the path to Therm installation folder.
|
|
52
|
+
|
|
53
|
+
This is typically the folder within the lbnl program files directory
|
|
54
|
+
that starts with THERM and contains executables and dlls.
|
|
55
|
+
"""
|
|
56
|
+
return self._therm_path
|
|
57
|
+
|
|
58
|
+
@therm_path.setter
|
|
59
|
+
def therm_path(self, t_path):
|
|
60
|
+
exe_name = 'THERM{}.exe'.format(self.THERM_VERSION[0])
|
|
61
|
+
if not t_path: # check the default installation location
|
|
62
|
+
t_path = self._find_therm_folder()
|
|
63
|
+
therm_exe_file = os.path.join(t_path, exe_name) if t_path is not None else None
|
|
64
|
+
|
|
65
|
+
# if the executable exists, set the variables
|
|
66
|
+
if t_path and os.path.isfile(therm_exe_file):
|
|
67
|
+
self._therm_path = t_path
|
|
68
|
+
self._therm_exe = therm_exe_file
|
|
69
|
+
self._therm_version = self.THERM_VERSION
|
|
70
|
+
self._therm_version_str = '.'.join(str(i) for i in self.THERM_VERSION)
|
|
71
|
+
if not self.mute:
|
|
72
|
+
print("Path to Therm is set to: %s" % self._therm_path)
|
|
73
|
+
else:
|
|
74
|
+
if t_path:
|
|
75
|
+
msg = '{} is not a valid path to a Therm installation.'.format(t_path)
|
|
76
|
+
print(msg)
|
|
77
|
+
self._therm_path = None
|
|
78
|
+
self._therm_exe = None
|
|
79
|
+
self._therm_version = None
|
|
80
|
+
self._therm_version_str = None
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def therm_exe(self):
|
|
84
|
+
"""Get the path to Therm executable.
|
|
85
|
+
|
|
86
|
+
Will be none if no Therm installation was found.
|
|
87
|
+
"""
|
|
88
|
+
return self._therm_exe
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def therm_version(self):
|
|
92
|
+
"""Get a tuple for the version of therm (eg. (8, 1, 30)).
|
|
93
|
+
|
|
94
|
+
This will be None if the version could not be sensed or if no Therm
|
|
95
|
+
installation was found.
|
|
96
|
+
"""
|
|
97
|
+
return self._therm_version
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def therm_version_str(self):
|
|
101
|
+
"""Get text for the full version of therm (eg."8.1.30").
|
|
102
|
+
|
|
103
|
+
This will be None if the version could not be sensed or if no Therm
|
|
104
|
+
installation was found.
|
|
105
|
+
"""
|
|
106
|
+
return self._therm_version_str
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def lbnl_data_path(self):
|
|
110
|
+
"""Get or set the path to the folder containing the LBNL data.
|
|
111
|
+
|
|
112
|
+
This folder typically exists under the User profile and contains sub-folders
|
|
113
|
+
for all installed versions of LBNL THERM and WINDOW. The THERM sub-folder
|
|
114
|
+
contains a lib sub-folder with XML files for all materials, boundary
|
|
115
|
+
conditions, etc.
|
|
116
|
+
"""
|
|
117
|
+
return self._lbnl_data_path
|
|
118
|
+
|
|
119
|
+
@lbnl_data_path.setter
|
|
120
|
+
def lbnl_data_path(self, path):
|
|
121
|
+
if not path: # check the default locations of the template library
|
|
122
|
+
path = self._find_lbnl_data_folder()
|
|
123
|
+
|
|
124
|
+
# gather all of the sub folders underneath the master folder
|
|
125
|
+
if path and os.path.isdir(path):
|
|
126
|
+
self._lbnl_data_path = path
|
|
127
|
+
self._therm_settings_path = self._check_therm_settings(path)
|
|
128
|
+
self._material_lib_file = self._check_therm_lib_file(path, 'Materials.xml')
|
|
129
|
+
self._gas_lib_file = self._check_therm_lib_file(path, 'Gases.xml')
|
|
130
|
+
self._bc_steady_state_lib_file = self._check_therm_lib_file(
|
|
131
|
+
path, 'BoundaryConditionsSteadyState.xml')
|
|
132
|
+
if not self.mute:
|
|
133
|
+
print('Path to LBNL data is set to: {}'.format(self._lbnl_data_path))
|
|
134
|
+
else:
|
|
135
|
+
if path:
|
|
136
|
+
msg = '{} is not a valid path to a LBNL data folder.'.format(path)
|
|
137
|
+
print(msg)
|
|
138
|
+
self._lbnl_data_path = None
|
|
139
|
+
self._therm_settings_path = None
|
|
140
|
+
self._material_lib_file = None
|
|
141
|
+
self._gas_lib_file = None
|
|
142
|
+
self._bc_steady_state_lib_file = None
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def therm_settings_path(self):
|
|
146
|
+
"""Get the path to the .ini file with THERM settings.
|
|
147
|
+
|
|
148
|
+
Will be None if no LBNL data folder was found.
|
|
149
|
+
"""
|
|
150
|
+
return self._therm_settings_path
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def therm_lib_path(self):
|
|
154
|
+
"""Get or set the path to the folder from which therm materials are loaded.
|
|
155
|
+
|
|
156
|
+
This will be the therm folder within within the user's standards folder
|
|
157
|
+
if it exists.
|
|
158
|
+
"""
|
|
159
|
+
return self._therm_lib_path
|
|
160
|
+
|
|
161
|
+
@therm_lib_path.setter
|
|
162
|
+
def therm_lib_path(self, path):
|
|
163
|
+
if not path: # check the default locations of the template library
|
|
164
|
+
path = self._find_therm_lib()
|
|
165
|
+
|
|
166
|
+
# gather all of the sub folders underneath the master folder
|
|
167
|
+
if path and os.path.isdir(path):
|
|
168
|
+
self._therm_lib_path = path
|
|
169
|
+
if not self.mute:
|
|
170
|
+
print('Path to THERM library is set to: {}'.format(self._lbnl_data_path))
|
|
171
|
+
else:
|
|
172
|
+
if path:
|
|
173
|
+
msg = '{} is not a valid path to a THERM standards library.'.format(path)
|
|
174
|
+
print(msg)
|
|
175
|
+
self._therm_lib_path = None
|
|
176
|
+
|
|
177
|
+
@property
|
|
178
|
+
def material_lib_file(self):
|
|
179
|
+
"""Get the path to the material library file."""
|
|
180
|
+
return self._material_lib_file
|
|
181
|
+
|
|
182
|
+
@property
|
|
183
|
+
def gas_lib_file(self):
|
|
184
|
+
"""Get the path to the gas library file."""
|
|
185
|
+
return self._gas_lib_file
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def bc_steady_state_lib_file(self):
|
|
189
|
+
"""Get the path to the modifierset library in the standards_data_folder."""
|
|
190
|
+
return self._bc_steady_state_lib_file
|
|
191
|
+
|
|
192
|
+
@property
|
|
193
|
+
def config_file(self):
|
|
194
|
+
"""Get or set the path to the config.json file from which folders are loaded.
|
|
195
|
+
|
|
196
|
+
Setting this to None will result in using the config.json module included
|
|
197
|
+
in this package.
|
|
198
|
+
"""
|
|
199
|
+
return self._config_file
|
|
200
|
+
|
|
201
|
+
@config_file.setter
|
|
202
|
+
def config_file(self, cfg):
|
|
203
|
+
if cfg is None:
|
|
204
|
+
cfg = os.path.join(os.path.dirname(__file__), 'config.json')
|
|
205
|
+
self._load_from_file(cfg)
|
|
206
|
+
self._config_file = cfg
|
|
207
|
+
|
|
208
|
+
def _load_from_file(self, file_path):
|
|
209
|
+
"""Set all of the the properties of this object from a config JSON file.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
file_path: Path to a JSON file containing the file paths. A sample of this
|
|
213
|
+
JSON is the config.json file within this package.
|
|
214
|
+
"""
|
|
215
|
+
# check the default file path
|
|
216
|
+
assert os.path.isfile(str(file_path)), \
|
|
217
|
+
ValueError('No file found at {}'.format(file_path))
|
|
218
|
+
|
|
219
|
+
# set the default paths to be all blank
|
|
220
|
+
default_path = {
|
|
221
|
+
"therm_path": r'',
|
|
222
|
+
"lbnl_data_path": r'',
|
|
223
|
+
"therm_lib_path": r''
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
with open(file_path, 'r') as cfg:
|
|
227
|
+
try:
|
|
228
|
+
paths = json.load(cfg)
|
|
229
|
+
except Exception as e:
|
|
230
|
+
print('Failed to load paths from {}.\n{}'.format(file_path, e))
|
|
231
|
+
else:
|
|
232
|
+
for key, p in paths.items():
|
|
233
|
+
if not key.startswith('__') and p.strip():
|
|
234
|
+
default_path[key] = p.strip()
|
|
235
|
+
|
|
236
|
+
# set paths for therm installations
|
|
237
|
+
self.therm_path = default_path["therm_path"]
|
|
238
|
+
self.lbnl_data_path = default_path["lbnl_data_path"]
|
|
239
|
+
self.therm_lib_path = default_path["therm_lib_path"]
|
|
240
|
+
|
|
241
|
+
@staticmethod
|
|
242
|
+
def _find_therm_folder():
|
|
243
|
+
"""Find the Therm installation in its default location.
|
|
244
|
+
|
|
245
|
+
This method will first attempt to return the path of a standalone Therm
|
|
246
|
+
installation.
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
File directory and full path to executable in case of success.
|
|
250
|
+
None, None in case of failure.
|
|
251
|
+
"""
|
|
252
|
+
# first check if there's a version installed in the ladybug_tools folder
|
|
253
|
+
# note that this option is not likely to be used because of the THERM license
|
|
254
|
+
lb_install = lb_config.folders.ladybug_tools_folder
|
|
255
|
+
thm_path = None
|
|
256
|
+
if os.path.isdir(lb_install):
|
|
257
|
+
test_path = os.path.join(lb_install, 'THERM')
|
|
258
|
+
thm_path = test_path if os.path.isdir(test_path) else None
|
|
259
|
+
|
|
260
|
+
# then check for the default location where standalone Therm is installed
|
|
261
|
+
if thm_path is not None:
|
|
262
|
+
pass # we found a version of therm in the ladybug_tools folder
|
|
263
|
+
elif os.name == 'nt': # search the C:/ drive on Windows
|
|
264
|
+
major, minor, _ = Folders.THERM_VERSION
|
|
265
|
+
test_path = 'C:/Program Files (x86)/lbnl/THERM{}.{}'.format(major, minor)
|
|
266
|
+
thm_path = test_path if os.path.isdir(test_path) else None
|
|
267
|
+
|
|
268
|
+
return thm_path
|
|
269
|
+
|
|
270
|
+
@staticmethod
|
|
271
|
+
def _find_lbnl_data_folder():
|
|
272
|
+
"""Find the the LBNL data folder in its default location."""
|
|
273
|
+
# then check the default location where the LBNL installer puts it
|
|
274
|
+
lib_folder = None
|
|
275
|
+
if os.name == 'nt': # search the C:/ drive on Windows
|
|
276
|
+
test_path = 'C:/Users/Public/LBNL/'
|
|
277
|
+
if os.path.isdir(test_path):
|
|
278
|
+
lib_folder = test_path
|
|
279
|
+
return lib_folder
|
|
280
|
+
|
|
281
|
+
@staticmethod
|
|
282
|
+
def _check_therm_settings(path):
|
|
283
|
+
"""Check that a settings file exists within the LBNL data folder."""
|
|
284
|
+
if not path: # first check that a path exists
|
|
285
|
+
return None
|
|
286
|
+
major, minor, _ = Folders.THERM_VERSION
|
|
287
|
+
settings_dir = os.path.join(path, 'Settings')
|
|
288
|
+
set_file = os.path.join(settings_dir, 'therm{}.{}.ini'.format(major, minor))
|
|
289
|
+
if os.path.isfile(set_file):
|
|
290
|
+
return set_file
|
|
291
|
+
|
|
292
|
+
@staticmethod
|
|
293
|
+
def _find_therm_lib():
|
|
294
|
+
"""Find the the LBNL data folder in its default location."""
|
|
295
|
+
# first check if there's a user-defined folder in AppData
|
|
296
|
+
app_folder = os.getenv('APPDATA')
|
|
297
|
+
if app_folder is not None:
|
|
298
|
+
lib_folder = os.path.join(app_folder, 'ladybug_tools', 'standards', 'therm')
|
|
299
|
+
if os.path.isdir(lib_folder):
|
|
300
|
+
return lib_folder
|
|
301
|
+
|
|
302
|
+
@staticmethod
|
|
303
|
+
def _check_therm_lib_file(path, lib_file):
|
|
304
|
+
"""Check that a XML file exists within the LBNL therm library."""
|
|
305
|
+
if not path: # first check that a path exists
|
|
306
|
+
return None
|
|
307
|
+
if os.name == 'nt':
|
|
308
|
+
major, minor, _ = Folders.THERM_VERSION
|
|
309
|
+
lib_path = os.path.join(path, 'THERM{}.{}/lib'.format(major, minor), lib_file)
|
|
310
|
+
if os.path.isfile(lib_path):
|
|
311
|
+
return lib_path
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
"""Object possesing all key folders within the configuration."""
|
|
315
|
+
folders = Folders(mute=True)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Library of materials and conditions."""
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Load all conditions from the LBNL XML files and JSON libraries."""
|
|
2
|
+
import os
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from fairyfly_therm.config import folders
|
|
6
|
+
from fairyfly_therm.condition.comprehensive import ComprehensiveCondition
|
|
7
|
+
|
|
8
|
+
# empty dictionaries to hold loaded conditions
|
|
9
|
+
_conditions = {}
|
|
10
|
+
|
|
11
|
+
# ensure that there is always a generic indoor and outdoor conditions
|
|
12
|
+
exterior_dict = {
|
|
13
|
+
'type': 'ComprehensiveCondition',
|
|
14
|
+
'identifier': 'de5cfad3-8a58-48b7-8583-b31a2650bc80',
|
|
15
|
+
'temperature': -18.0,
|
|
16
|
+
'film_coefficient': 26.0,
|
|
17
|
+
'display_name': 'Generic Exterior',
|
|
18
|
+
'protected': True,
|
|
19
|
+
'color': '#0080c0'
|
|
20
|
+
}
|
|
21
|
+
exterior = ComprehensiveCondition.from_dict(exterior_dict)
|
|
22
|
+
exterior.lock()
|
|
23
|
+
_conditions[exterior.display_name] = exterior
|
|
24
|
+
|
|
25
|
+
interior_dict = {
|
|
26
|
+
'type': 'ComprehensiveCondition',
|
|
27
|
+
'identifier': '7eba559e-ebbc-4dc4-82a7-266a888fe5a5',
|
|
28
|
+
'temperature': 21.0,
|
|
29
|
+
'film_coefficient': 3.12,
|
|
30
|
+
'display_name': 'Generic Interior',
|
|
31
|
+
'protected': True,
|
|
32
|
+
'color': '#ff8040'
|
|
33
|
+
}
|
|
34
|
+
interior = ComprehensiveCondition.from_dict(interior_dict)
|
|
35
|
+
interior.lock()
|
|
36
|
+
_conditions[interior.display_name] = interior
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# load the conditions from the LBNL library if they exist
|
|
40
|
+
if folders.bc_steady_state_lib_file is not None:
|
|
41
|
+
conds = ComprehensiveCondition.extract_all_from_xml_file(folders.bc_steady_state_lib_file)
|
|
42
|
+
for con in conds:
|
|
43
|
+
con.lock()
|
|
44
|
+
_conditions[con.display_name] = con
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def check_and_add_condition(con):
|
|
48
|
+
"""Check that a condition is not overwriting a default and add it."""
|
|
49
|
+
con.lock()
|
|
50
|
+
if con.display_name not in ('Generic Exterior', 'Generic Interior'):
|
|
51
|
+
if isinstance(con, ComprehensiveCondition):
|
|
52
|
+
_conditions[con.display_name] = con
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def load_conditions_from_folder(lib_folder):
|
|
56
|
+
"""Load all of the condition objects from a therm standards folder.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
lib_folder: Path to a sub-folder within a honeybee standards folder.
|
|
60
|
+
"""
|
|
61
|
+
for f in os.listdir(lib_folder):
|
|
62
|
+
f_path = os.path.join(lib_folder, f)
|
|
63
|
+
if os.path.isfile(f_path):
|
|
64
|
+
if f_path.endswith('.xml'):
|
|
65
|
+
conds = ComprehensiveCondition.extract_all_from_xml_file(f_path)
|
|
66
|
+
for c in conds:
|
|
67
|
+
check_and_add_condition(c)
|
|
68
|
+
elif f_path.endswith('.json'):
|
|
69
|
+
with open(f_path) as json_file:
|
|
70
|
+
data = json.load(json_file)
|
|
71
|
+
if 'type' in data: # single object
|
|
72
|
+
if data['type'] == 'ComprehensiveCondition':
|
|
73
|
+
check_and_add_condition(ComprehensiveCondition.from_dict(data))
|
|
74
|
+
else: # a collection of several objects
|
|
75
|
+
for m_id in data:
|
|
76
|
+
try:
|
|
77
|
+
m_dict = data[m_id]
|
|
78
|
+
if m_dict['type'] == 'ComprehensiveCondition':
|
|
79
|
+
check_and_add_condition(
|
|
80
|
+
ComprehensiveCondition.from_dict(m_dict))
|
|
81
|
+
except (TypeError, KeyError):
|
|
82
|
+
pass # not an acceptable JSON; possibly a comment
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# load therm gases from a user folder if we are not using the official THERM lib
|
|
86
|
+
if folders.therm_lib_path is not None:
|
|
87
|
+
load_conditions_from_folder(folders.therm_lib_path)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Load all gases from the standards library."""
|
|
2
|
+
from fairyfly_therm.config import folders
|
|
3
|
+
from fairyfly_therm.material.gas import PureGas, Gas
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import json
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# empty dictionary to hold loaded gases
|
|
10
|
+
_pure_gases = {}
|
|
11
|
+
_gases = {}
|
|
12
|
+
|
|
13
|
+
# first load the gasses from the LBNL library if they exist
|
|
14
|
+
if folders.gas_lib_file is not None:
|
|
15
|
+
gs, pgs = Gas.extract_all_from_xml_file(folders.gas_lib_file)
|
|
16
|
+
for pg in pgs:
|
|
17
|
+
pg.lock()
|
|
18
|
+
_pure_gases[pg.display_name] = pg
|
|
19
|
+
for g in gs:
|
|
20
|
+
g.lock()
|
|
21
|
+
_gases[g.display_name] = g
|
|
22
|
+
else: # if not, at least make sure that we have an Air gas
|
|
23
|
+
air_dict = {
|
|
24
|
+
'type': 'PureGas',
|
|
25
|
+
'identifier': '8d33196f-f052-46e6-8353-bccb9a779f9c',
|
|
26
|
+
'conductivity_coeff_a': 0.002873,
|
|
27
|
+
'viscosity_coeff_a': 3.723e-06,
|
|
28
|
+
'specific_heat_coeff_a': 1002.737,
|
|
29
|
+
'conductivity_coeff_b': 7.76e-05,
|
|
30
|
+
'viscosity_coeff_b': 4.94e-08,
|
|
31
|
+
'specific_heat_coeff_b': 0.012324,
|
|
32
|
+
'conductivity_coeff_c': 0.0,
|
|
33
|
+
'viscosity_coeff_c': 0.0,
|
|
34
|
+
'specific_heat_coeff_c': 0.0,
|
|
35
|
+
'specific_heat_ratio': 1.4,
|
|
36
|
+
'molecular_weight': 28.97,
|
|
37
|
+
'display_name': 'Air',
|
|
38
|
+
'protected': True,
|
|
39
|
+
'color': '#556d11'
|
|
40
|
+
}
|
|
41
|
+
pure_air = PureGas.from_dict(air_dict)
|
|
42
|
+
pure_air.lock()
|
|
43
|
+
_pure_gases['Air'] = pure_air
|
|
44
|
+
air_gas = Gas(
|
|
45
|
+
[pure_air], [1], identifier='6c2409e9-5296-46c1-be11-9029b59a549b'
|
|
46
|
+
)
|
|
47
|
+
air_gas.display_name = 'Air'
|
|
48
|
+
air_gas.protected = True
|
|
49
|
+
air_gas.lock()
|
|
50
|
+
_gases['Air'] = air_gas
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def check_and_add_gas(gas):
|
|
54
|
+
"""Check that a gas is not overwriting a default and add it."""
|
|
55
|
+
gas.lock()
|
|
56
|
+
if gas.display_name != 'Air':
|
|
57
|
+
if isinstance(gas, PureGas):
|
|
58
|
+
_pure_gases[gas.display_name] = gas
|
|
59
|
+
elif isinstance(gas, Gas):
|
|
60
|
+
_gases[gas.display_name] = gas
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def load_gases_from_folder(lib_folder):
|
|
64
|
+
"""Load all of the gas objects from a therm standards folder.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
lib_folder: Path to a sub-folder within a honeybee standards folder.
|
|
68
|
+
"""
|
|
69
|
+
for f in os.listdir(lib_folder):
|
|
70
|
+
f_path = os.path.join(lib_folder, f)
|
|
71
|
+
if os.path.isfile(f_path):
|
|
72
|
+
if f_path.endswith('.xml'):
|
|
73
|
+
gs, pgs = Gas.extract_all_from_xml_file(f_path)
|
|
74
|
+
for g in pgs + gs:
|
|
75
|
+
check_and_add_gas(g)
|
|
76
|
+
elif f_path.endswith('.json'):
|
|
77
|
+
with open(f_path) as json_file:
|
|
78
|
+
data = json.load(json_file)
|
|
79
|
+
if 'type' in data: # single object
|
|
80
|
+
if data['type'] == 'PureGas':
|
|
81
|
+
check_and_add_gas(PureGas.from_dict(data))
|
|
82
|
+
elif data['type'] == 'Gas':
|
|
83
|
+
check_and_add_gas(Gas.from_dict(data))
|
|
84
|
+
else: # a collection of several objects
|
|
85
|
+
for g_id in data:
|
|
86
|
+
try:
|
|
87
|
+
g_dict = data[g_id]
|
|
88
|
+
if g_dict['type'] == 'PureGas':
|
|
89
|
+
check_and_add_gas(PureGas.from_dict(g_dict))
|
|
90
|
+
elif g_dict['type'] == 'Gas':
|
|
91
|
+
check_and_add_gas(Gas.from_dict(g_dict))
|
|
92
|
+
except (TypeError, KeyError):
|
|
93
|
+
pass # not an acceptable JSON; possibly a comment
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# load therm gases from a user folder if we are not using the official THERM lib
|
|
97
|
+
if folders.therm_lib_path is not None:
|
|
98
|
+
load_gases_from_folder(folders.therm_lib_path)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Load all materials from the LBNL XML files and JSON libraries."""
|
|
2
|
+
import os
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
from fairyfly_therm.config import folders
|
|
6
|
+
from fairyfly_therm.material.solid import SolidMaterial
|
|
7
|
+
from fairyfly_therm.material.cavity import CavityMaterial
|
|
8
|
+
from fairyfly_therm.material.xmlutil import extract_all_materials_from_xml_file
|
|
9
|
+
from ._loadgases import _gases
|
|
10
|
+
|
|
11
|
+
# empty dictionaries to hold loaded materials
|
|
12
|
+
_solid_materials = {}
|
|
13
|
+
_cavity_materials = {}
|
|
14
|
+
|
|
15
|
+
# ensure that there is always a concrete material
|
|
16
|
+
concrete_dict = {
|
|
17
|
+
'type': 'SolidMaterial',
|
|
18
|
+
'identifier': '6442842d-7c8f-4231-a9b3-64302e3b2bc4',
|
|
19
|
+
'display_name': 'Generic HW Concrete',
|
|
20
|
+
'conductivity': 1.95,
|
|
21
|
+
'emissivity': 0.9,
|
|
22
|
+
'emissivity_back': 0.9,
|
|
23
|
+
'density': 2240,
|
|
24
|
+
'porosity': 0.24,
|
|
25
|
+
'specific_heat': 900,
|
|
26
|
+
'vapor_diffusion_resistance': 19,
|
|
27
|
+
'color': '#808080',
|
|
28
|
+
'protected': True
|
|
29
|
+
}
|
|
30
|
+
concrete = SolidMaterial.from_dict(concrete_dict)
|
|
31
|
+
concrete.lock()
|
|
32
|
+
_solid_materials[concrete.display_name] = concrete
|
|
33
|
+
|
|
34
|
+
# ensure that we always have an air cavity material
|
|
35
|
+
air_mat = CavityMaterial(identifier='0b46bbd7-0dbc-c148-3afe-87431bf0f66f')
|
|
36
|
+
air_mat.display_name = 'Frame Cavity - CEN Simplified'
|
|
37
|
+
air_mat.protected = True
|
|
38
|
+
air_mat.lock()
|
|
39
|
+
_cavity_materials[air_mat.display_name] = air_mat
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# load the materials from the LBNL library if they exist
|
|
43
|
+
if folders.material_lib_file is not None:
|
|
44
|
+
solid, cavit = extract_all_materials_from_xml_file(folders.material_lib_file, _gases)
|
|
45
|
+
for sol in solid:
|
|
46
|
+
sol.lock()
|
|
47
|
+
_solid_materials[sol.display_name] = sol
|
|
48
|
+
for cav in cavit:
|
|
49
|
+
cav.lock()
|
|
50
|
+
_cavity_materials[cav.display_name] = cav
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def check_and_add_material(mat):
|
|
54
|
+
"""Check that a mat is not overwriting a default and add it."""
|
|
55
|
+
mat.lock()
|
|
56
|
+
if mat.display_name not in ('Generic HW Concrete', 'Frame Cavity - CEN Simplified'):
|
|
57
|
+
if isinstance(mat, SolidMaterial):
|
|
58
|
+
_solid_materials[mat.display_name] = mat
|
|
59
|
+
elif isinstance(mat, CavityMaterial):
|
|
60
|
+
_cavity_materials[mat.display_name] = mat
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def load_materials_from_folder(lib_folder):
|
|
64
|
+
"""Load all of the material objects from a therm standards folder.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
lib_folder: Path to a sub-folder within a honeybee standards folder.
|
|
68
|
+
"""
|
|
69
|
+
for f in os.listdir(lib_folder):
|
|
70
|
+
f_path = os.path.join(lib_folder, f)
|
|
71
|
+
if os.path.isfile(f_path):
|
|
72
|
+
if f_path.endswith('.xml'):
|
|
73
|
+
solid, cavity = extract_all_materials_from_xml_file(f_path, _gases)
|
|
74
|
+
for m in solid + cavity:
|
|
75
|
+
check_and_add_material(m)
|
|
76
|
+
elif f_path.endswith('.json'):
|
|
77
|
+
with open(f_path) as json_file:
|
|
78
|
+
data = json.load(json_file)
|
|
79
|
+
if 'type' in data: # single object
|
|
80
|
+
if data['type'] == 'SolidMaterial':
|
|
81
|
+
check_and_add_material(SolidMaterial.from_dict(data))
|
|
82
|
+
elif data['type'] == 'CavityMaterial':
|
|
83
|
+
check_and_add_material(CavityMaterial.from_dict(data))
|
|
84
|
+
else: # a collection of several objects
|
|
85
|
+
for m_id in data:
|
|
86
|
+
try:
|
|
87
|
+
m_dict = data[m_id]
|
|
88
|
+
if m_dict['type'] == 'SolidMaterial':
|
|
89
|
+
check_and_add_material(SolidMaterial.from_dict(m_dict))
|
|
90
|
+
elif m_dict['type'] == 'CavityMaterial':
|
|
91
|
+
check_and_add_material(CavityMaterial.from_dict(m_dict))
|
|
92
|
+
except (TypeError, KeyError):
|
|
93
|
+
pass # not an acceptable JSON; possibly a comment
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# load therm gases from a user folder if we are not using the official THERM lib
|
|
97
|
+
if folders.therm_lib_path is not None:
|
|
98
|
+
load_materials_from_folder(folders.therm_lib_path)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Establish the default conditions within the fairyfly_therm library."""
|
|
2
|
+
from ._loadconditions import _conditions
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# establish variables for the default materials used across the library
|
|
6
|
+
exterior = _conditions['Generic Exterior']
|
|
7
|
+
interior = _conditions['Generic Interior']
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# make lists of condition names to look up items in the library
|
|
11
|
+
CONDITIONS = tuple(_conditions.keys())
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def condition_by_name(condition_name):
|
|
15
|
+
"""Get a solid condition from the library given the condition name.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
condition_name: A text string for the display_name of the condition.
|
|
19
|
+
"""
|
|
20
|
+
try: # first check the default data
|
|
21
|
+
return _conditions[condition_name]
|
|
22
|
+
except KeyError:
|
|
23
|
+
raise ValueError(
|
|
24
|
+
'"{}" was not found in the solid condition library.'.format(condition_name))
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Gas library."""
|
|
2
|
+
from ._loadgases import _pure_gases, _gases
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# establish variables for the default gasess used across the library
|
|
6
|
+
pure_air = _pure_gases['Air']
|
|
7
|
+
air = _gases['Air']
|
|
8
|
+
|
|
9
|
+
# make lists of gases to look up items in the library
|
|
10
|
+
PURE_GASES = tuple(_pure_gases.keys())
|
|
11
|
+
GASES = tuple(_gases.keys())
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def gas_by_name(gas_name):
|
|
15
|
+
"""Get a gas from the library given its name.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
gas_name: A text string for the display_name of the gas.
|
|
19
|
+
"""
|
|
20
|
+
try:
|
|
21
|
+
return _gases[gas_name]
|
|
22
|
+
except KeyError:
|
|
23
|
+
raise ValueError('"{}" was not found in the gas library.'.format(gas_name))
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def pure_gas_by_name(pure_gas_name):
|
|
27
|
+
"""Get a pure gas from the library given its name.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
gas_name: A text string for the display_name of the pure gas.
|
|
31
|
+
"""
|
|
32
|
+
try:
|
|
33
|
+
return _pure_gases[pure_gas_name]
|
|
34
|
+
except KeyError:
|
|
35
|
+
raise ValueError('"{}" was not found in the pure gas '
|
|
36
|
+
'library.'.format(pure_gas_name))
|