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,1154 @@
1
+ """honeybee energy standards library commands."""
2
+ import click
3
+ import sys
4
+ import os
5
+ import logging
6
+ import json
7
+ import zipfile
8
+ from datetime import datetime
9
+
10
+ from honeybee.config import folders as hb_folders
11
+ from honeybee_energy.config import folders
12
+ from honeybee_energy.schedule.typelimit import ScheduleTypeLimit
13
+ from honeybee_energy.material.dictutil import dict_to_material, MATERIAL_TYPES
14
+ from honeybee_energy.run import run_osw, from_osm_osw, _parse_os_cli_failure
15
+
16
+ from honeybee_energy.lib.materials import opaque_material_by_identifier, \
17
+ window_material_by_identifier, OPAQUE_MATERIALS, WINDOW_MATERIALS
18
+ from honeybee_energy.lib.constructions import opaque_construction_by_identifier, \
19
+ window_construction_by_identifier, shade_construction_by_identifier, \
20
+ OPAQUE_CONSTRUCTIONS, WINDOW_CONSTRUCTIONS, SHADE_CONSTRUCTIONS, \
21
+ lib_dict_abridged_to_construction
22
+ from honeybee_energy.lib.constructionsets import construction_set_by_identifier, \
23
+ CONSTRUCTION_SETS, lib_dict_abridged_to_construction_set
24
+ from honeybee_energy.lib.scheduletypelimits import schedule_type_limit_by_identifier, \
25
+ SCHEDULE_TYPE_LIMITS
26
+ from honeybee_energy.lib.schedules import schedule_by_identifier, SCHEDULES, \
27
+ lib_dict_abridged_to_schedule
28
+ from honeybee_energy.lib.programtypes import program_type_by_identifier, PROGRAM_TYPES, \
29
+ lib_dict_abridged_to_program_type
30
+
31
+ from honeybee_energy.lib._loadtypelimits import load_type_limits_from_folder, \
32
+ _schedule_type_limits
33
+ from honeybee_energy.lib._loadschedules import load_schedules_from_folder, \
34
+ _default_schedules
35
+ from honeybee_energy.lib._loadprogramtypes import load_programtypes_from_folder, \
36
+ _default_programs
37
+ from honeybee_energy.lib._loadmaterials import load_materials_from_folder, \
38
+ _default_mats
39
+ from honeybee_energy.lib._loadconstructions import load_constructions_from_folder, \
40
+ _default_constrs
41
+ from honeybee_energy.lib._loadconstructionsets import \
42
+ load_constructionsets_from_folder, _default_sets
43
+
44
+ _logger = logging.getLogger(__name__)
45
+
46
+
47
+ @click.group(help='Commands for retrieving objects from the standards library.')
48
+ def lib():
49
+ pass
50
+
51
+
52
+ @lib.command('opaque-materials')
53
+ @click.option('--output-file', '-f', help='Optional file to output the JSON string of '
54
+ 'the object. By default, it will be printed out to stdout',
55
+ type=click.File('w'), default='-', show_default=True)
56
+ def opaque_materials(output_file):
57
+ """Get a list of all opaque materials in the standards library."""
58
+ try:
59
+ output_file.write(json.dumps(OPAQUE_MATERIALS))
60
+ except Exception as e:
61
+ _logger.exception('Failed to load opaque materials.\n{}'.format(e))
62
+ sys.exit(1)
63
+ else:
64
+ sys.exit(0)
65
+
66
+
67
+ @lib.command('window-materials')
68
+ @click.option('--output-file', '-f', help='Optional file to output the JSON string of '
69
+ 'the object. By default, it will be printed out to stdout',
70
+ type=click.File('w'), default='-', show_default=True)
71
+ def window_materials(output_file):
72
+ """Get a list of all window materials in the standards library."""
73
+ try:
74
+ output_file.write(json.dumps(WINDOW_MATERIALS))
75
+ except Exception as e:
76
+ _logger.exception('Failed to load window materials.\n{}'.format(e))
77
+ sys.exit(1)
78
+ else:
79
+ sys.exit(0)
80
+
81
+
82
+ @lib.command('opaque-constructions')
83
+ @click.option('--output-file', '-f', help='Optional file to output the JSON string of '
84
+ 'the object. By default, it will be printed out to stdout',
85
+ type=click.File('w'), default='-', show_default=True)
86
+ def opaque_constructions(output_file):
87
+ """Get a list of all opaque constructions in the standards library."""
88
+ try:
89
+ output_file.write(json.dumps(OPAQUE_CONSTRUCTIONS))
90
+ except Exception as e:
91
+ _logger.exception('Failed to load opaque constructions.\n{}'.format(e))
92
+ sys.exit(1)
93
+ else:
94
+ sys.exit(0)
95
+
96
+
97
+ @lib.command('window-constructions')
98
+ @click.option('--output-file', '-f', help='Optional file to output the JSON string of '
99
+ 'the object. By default, it will be printed out to stdout',
100
+ type=click.File('w'), default='-', show_default=True)
101
+ def window_constructions(output_file):
102
+ """Get a list of all window constructions in the standards library."""
103
+ try:
104
+ output_file.write(json.dumps(WINDOW_CONSTRUCTIONS))
105
+ except Exception as e:
106
+ _logger.exception('Failed to load window constructions.\n{}'.format(e))
107
+ sys.exit(1)
108
+ else:
109
+ sys.exit(0)
110
+
111
+
112
+ @lib.command('shade-constructions')
113
+ @click.option('--output-file', '-f', help='Optional file to output the JSON string of '
114
+ 'the object. By default, it will be printed out to stdout',
115
+ type=click.File('w'), default='-', show_default=True)
116
+ def shade_constructions(output_file):
117
+ """Get a list of all shade constructions in the standards library."""
118
+ try:
119
+ output_file.write(json.dumps(SHADE_CONSTRUCTIONS))
120
+ except Exception as e:
121
+ _logger.exception('Failed to load shade constructions.\n{}'.format(e))
122
+ sys.exit(1)
123
+ else:
124
+ sys.exit(0)
125
+
126
+
127
+ @lib.command('construction-sets')
128
+ @click.option('--output-file', '-f', help='Optional file to output the JSON string of '
129
+ 'the object. By default, it will be printed out to stdout',
130
+ type=click.File('w'), default='-', show_default=True)
131
+ def construction_sets(output_file):
132
+ """Get a list of all construction sets in the standards library."""
133
+ try:
134
+ output_file.write(json.dumps(CONSTRUCTION_SETS))
135
+ except Exception as e:
136
+ _logger.exception('Failed to load construction sets.\n{}'.format(e))
137
+ sys.exit(1)
138
+ else:
139
+ sys.exit(0)
140
+
141
+
142
+ @lib.command('schedule-type-limits')
143
+ @click.option('--output-file', '-f', help='Optional file to output the JSON string of '
144
+ 'the object. By default, it will be printed out to stdout',
145
+ type=click.File('w'), default='-', show_default=True)
146
+ def schedule_type_limits(output_file):
147
+ """Get a list of all schedule type limits in the standards library."""
148
+ try:
149
+ output_file.write(json.dumps(SCHEDULE_TYPE_LIMITS))
150
+ except Exception as e:
151
+ _logger.exception('Failed to load schedule type limits.\n{}'.format(e))
152
+ sys.exit(1)
153
+ else:
154
+ sys.exit(0)
155
+
156
+
157
+ @lib.command('schedules')
158
+ @click.option('--output-file', '-f', help='Optional file to output the JSON string of '
159
+ 'the object. By default, it will be printed out to stdout',
160
+ type=click.File('w'), default='-', show_default=True)
161
+ def schedules(output_file):
162
+ """Get a list of all schedules in the standards library."""
163
+ try:
164
+ output_file.write(json.dumps(SCHEDULES))
165
+ except Exception as e:
166
+ _logger.exception('Failed to load schedules.\n{}'.format(e))
167
+ sys.exit(1)
168
+ else:
169
+ sys.exit(0)
170
+
171
+
172
+ @lib.command('program-types')
173
+ @click.option('--output-file', '-f', help='Optional file to output the JSON string of '
174
+ 'the object. By default, it will be printed out to stdout',
175
+ type=click.File('w'), default='-', show_default=True)
176
+ def program_types(output_file):
177
+ """Get a list of all program_types in the standards library."""
178
+ try:
179
+ output_file.write(json.dumps(PROGRAM_TYPES))
180
+ except Exception as e:
181
+ _logger.exception('Failed to load program types.\n{}'.format(e))
182
+ sys.exit(1)
183
+ else:
184
+ sys.exit(0)
185
+
186
+
187
+ @lib.command('opaque-material-by-id')
188
+ @click.argument('material-id', type=str)
189
+ @click.option('--output-file', '-f', help='Optional file to output the JSON string of '
190
+ 'the object. By default, it will be printed out to stdout',
191
+ type=click.File('w'), default='-', show_default=True)
192
+ def opaque_material_by_id(material_id, output_file):
193
+ """Get an opaque material definition from the standards lib with its identifier.
194
+
195
+ \b
196
+ Args:
197
+ material_id: The identifier of an opaque material in the library.
198
+ """
199
+ try:
200
+ output_file.write(
201
+ json.dumps(opaque_material_by_identifier(material_id).to_dict())
202
+ )
203
+ except Exception as e:
204
+ _logger.exception(
205
+ 'Retrieval from opaque material library failed.\n{}'.format(e))
206
+ sys.exit(1)
207
+ else:
208
+ sys.exit(0)
209
+
210
+
211
+ @lib.command('window-material-by-id')
212
+ @click.argument('material-id', type=str)
213
+ @click.option('--output-file', '-f', help='Optional file to output the JSON string of '
214
+ 'the object. By default, it will be printed out to stdout',
215
+ type=click.File('w'), default='-', show_default=True)
216
+ def window_material_by_id(material_id, output_file):
217
+ """Get a window material definition from the standards lib with its identifier.
218
+
219
+ \b
220
+ Args:
221
+ material_id: The identifier of an window material in the library.
222
+ """
223
+ try:
224
+ output_file.write(
225
+ json.dumps(window_material_by_identifier(material_id).to_dict())
226
+ )
227
+ except Exception as e:
228
+ _logger.exception(
229
+ 'Retrieval from window material library failed.\n{}'.format(e))
230
+ sys.exit(1)
231
+ else:
232
+ sys.exit(0)
233
+
234
+
235
+ @lib.command('opaque-construction-by-id')
236
+ @click.argument('construction-id', type=str)
237
+ @click.option('--complete/--abridged', ' /-a', help='Flag to note whether an abridged '
238
+ 'definition should be returned.', default=True)
239
+ @click.option('--output-file', '-f', help='Optional file to output the JSON string of '
240
+ 'the object. By default, it will be printed out to stdout',
241
+ type=click.File('w'), default='-', show_default=True)
242
+ def opaque_construction_by_id(construction_id, complete, output_file):
243
+ """Get an opaque construction definition from the standards lib with its identifier.
244
+
245
+ \b
246
+ Args:
247
+ construction_id: The identifier of an opaque construction in the library.
248
+ """
249
+ try:
250
+ abridged = not complete
251
+ output_file.write(json.dumps(opaque_construction_by_identifier(
252
+ construction_id).to_dict(abridged=abridged)))
253
+ except Exception as e:
254
+ _logger.exception(
255
+ 'Retrieval from opaque construction library failed.\n{}'.format(e))
256
+ sys.exit(1)
257
+ else:
258
+ sys.exit(0)
259
+
260
+
261
+ @lib.command('window-construction-by-id')
262
+ @click.argument('construction-id', type=str)
263
+ @click.option('--complete/--abridged', ' /-a', help='Flag to note whether an abridged '
264
+ 'definition should be returned.', default=True)
265
+ @click.option('--output-file', '-f', help='Optional file to output the JSON string of '
266
+ 'the object. By default, it will be printed out to stdout',
267
+ type=click.File('w'), default='-', show_default=True)
268
+ def window_construction_by_id(construction_id, complete, output_file):
269
+ """Get a window construction definition from the standards lib with its identifier.
270
+
271
+ \b
272
+ Args:
273
+ construction_id: The identifier of a window construction in the library.
274
+ """
275
+ try:
276
+ abridged = not complete
277
+ output_file.write(json.dumps(window_construction_by_identifier(
278
+ construction_id).to_dict(abridged=abridged)))
279
+ except Exception as e:
280
+ _logger.exception(
281
+ 'Retrieval from window construction library failed.\n{}'.format(e))
282
+ sys.exit(1)
283
+ else:
284
+ sys.exit(0)
285
+
286
+
287
+ @lib.command('shade-construction-by-id')
288
+ @click.argument('construction-id', type=str)
289
+ @click.option('--output-file', '-f', help='Optional file to output the JSON string of '
290
+ 'the object. By default, it will be printed out to stdout',
291
+ type=click.File('w'), default='-', show_default=True)
292
+ def shade_construction_by_id(construction_id, output_file):
293
+ """Get a shade construction definition from the standards lib with its identifier.
294
+
295
+ \b
296
+ Args:
297
+ construction_id: The identifier of a shade construction in the library.
298
+ """
299
+ try:
300
+ output_file.write(json.dumps(shade_construction_by_identifier(
301
+ construction_id).to_dict()))
302
+ except Exception as e:
303
+ _logger.exception(
304
+ 'Retrieval from shade construction library failed.\n{}'.format(e))
305
+ sys.exit(1)
306
+ else:
307
+ sys.exit(0)
308
+
309
+
310
+ @lib.command('construction-set-by-id')
311
+ @click.argument('construction-set-id', type=str)
312
+ @click.option('--none-defaults/--include-defaults', ' /-d', help='Flag to note whether '
313
+ 'default constructions in the set should be included in or should be '
314
+ 'None.', default=True, show_default=True)
315
+ @click.option('--complete/--abridged', ' /-a', help='Flag to note whether an abridged '
316
+ 'definition should be returned.', default=True, show_default=True)
317
+ @click.option('--output-file', '-f', help='Optional file to output the JSON string of '
318
+ 'the object. By default, it will be printed out to stdout',
319
+ type=click.File('w'), default='-', show_default=True)
320
+ def construction_set_by_id(construction_set_id, none_defaults, complete, output_file):
321
+ """Get a construction set definition from the standards lib with its identifier.
322
+
323
+ \b
324
+ Args:
325
+ construction_set_id: The identifier of a construction set in the library.
326
+ """
327
+ try:
328
+ abridged = not complete
329
+ c_set = construction_set_by_identifier(construction_set_id)
330
+ output_file.write(json.dumps(c_set.to_dict(
331
+ none_for_defaults=none_defaults, abridged=abridged)))
332
+ except Exception as e:
333
+ _logger.exception(
334
+ 'Retrieval from construction set library failed.\n{}'.format(e))
335
+ sys.exit(1)
336
+ else:
337
+ sys.exit(0)
338
+
339
+
340
+ @lib.command('schedule-type-limit-by-id')
341
+ @click.argument('schedule-type-limit-id', type=str)
342
+ @click.option('--output-file', '-f', help='Optional file to output the JSON string of '
343
+ 'the object. By default, it will be printed out to stdout',
344
+ type=click.File('w'), default='-', show_default=True)
345
+ def schedule_type_limit_by_id(schedule_type_limit_id, output_file):
346
+ """Get a schedule type limit definition from the standards lib with its identifier.
347
+
348
+ \b
349
+ Args:
350
+ schedule_type_limit_id: The identifier of a schedule type limit in the library.
351
+ """
352
+ try:
353
+ output_file.write(json.dumps(schedule_type_limit_by_identifier(
354
+ schedule_type_limit_id).to_dict()))
355
+ except Exception as e:
356
+ _logger.exception(
357
+ 'Retrieval from schedule type limit library failed.\n{}'.format(e))
358
+ sys.exit(1)
359
+ else:
360
+ sys.exit(0)
361
+
362
+
363
+ @lib.command('schedule-by-id')
364
+ @click.argument('schedule-id', type=str)
365
+ @click.option('--complete/--abridged', ' /-a', help='Flag to note whether an abridged '
366
+ 'definition should be returned.', default=True, show_default=True)
367
+ @click.option('--output-file', '-f', help='Optional file to output the JSON string of '
368
+ 'the object. By default, it will be printed out to stdout',
369
+ type=click.File('w'), default='-', show_default=True)
370
+ def schedule_by_id(schedule_id, complete, output_file):
371
+ """Get a schedule definition from the standards lib with its identifier.
372
+
373
+ \b
374
+ Args:
375
+ schedule_id: The identifier of a schedule in the library.
376
+ """
377
+ try:
378
+ abridged = not complete
379
+ output_file.write(json.dumps(schedule_by_identifier(
380
+ schedule_id).to_dict(abridged=abridged)))
381
+ except Exception as e:
382
+ _logger.exception('Retrieval from schedule library failed.\n{}'.format(e))
383
+ sys.exit(1)
384
+ else:
385
+ sys.exit(0)
386
+
387
+
388
+ @lib.command('program-type-by-id')
389
+ @click.argument('program-type-id', type=str)
390
+ @click.option('--complete/--abridged', ' /-a', help='Flag to note whether an abridged '
391
+ 'definition should be returned.', default=True, show_default=True)
392
+ @click.option('--output-file', '-f', help='Optional file to output the JSON string of '
393
+ 'the object. By default, it will be printed out to stdout',
394
+ type=click.File('w'), default='-', show_default=True)
395
+ def program_type_by_id(program_type_id, complete, output_file):
396
+ """Get a program type definition from the standards lib with its identifier.
397
+
398
+ \b
399
+ Args:
400
+ program_type_id: The identifier of a program type in the library.
401
+ """
402
+ try:
403
+ abridged = not complete
404
+ output_file.write(json.dumps(program_type_by_identifier(
405
+ program_type_id).to_dict(abridged=abridged)))
406
+ except Exception as e:
407
+ _logger.exception('Retrieval from program type library failed.\n{}'.format(e))
408
+ sys.exit(1)
409
+ else:
410
+ sys.exit(0)
411
+
412
+
413
+ @lib.command('materials-by-id')
414
+ @click.argument('material-ids', nargs=-1)
415
+ @click.option('--output-file', '-f', help='Optional file to output the JSON strings of '
416
+ 'the objects. By default, it will be printed out to stdout',
417
+ type=click.File('w'), default='-', show_default=True)
418
+ def materials_by_id(material_ids, output_file):
419
+ """Get several material definitions from the standards lib at once.
420
+
421
+ \b
422
+ Args:
423
+ material_ids: Any number of material identifiers to be retrieved from
424
+ the library.
425
+ """
426
+ try:
427
+ mats = []
428
+ for mat_id in material_ids:
429
+ try:
430
+ mats.append(opaque_material_by_identifier(mat_id))
431
+ except ValueError:
432
+ mats.append(window_material_by_identifier(mat_id))
433
+ output_file.write(json.dumps([mat.to_dict() for mat in mats]))
434
+ except Exception as e:
435
+ _logger.exception(
436
+ 'Retrieval from material library failed.\n{}'.format(e))
437
+ sys.exit(1)
438
+ else:
439
+ sys.exit(0)
440
+
441
+
442
+ @lib.command('constructions-by-id')
443
+ @click.argument('construction-ids', nargs=-1)
444
+ @click.option('--complete/--abridged', ' /-a', help='Flag to note whether an abridged '
445
+ 'definition should be returned.', default=True, show_default=True)
446
+ @click.option('--output-file', '-f', help='Optional file to output the JSON strings of '
447
+ 'the objects. By default, it will be printed out to stdout',
448
+ type=click.File('w'), default='-', show_default=True)
449
+ def constructions_by_id(construction_ids, complete, output_file):
450
+ """Get several construction definitions from the standards lib at once.
451
+
452
+ \b
453
+ Args:
454
+ construction_ids: Any number of construction identifiers to be retrieved
455
+ from the library.
456
+ """
457
+ try:
458
+ abridged = not complete
459
+ cons = []
460
+ for con_id in construction_ids:
461
+ try:
462
+ cons.append(opaque_construction_by_identifier(
463
+ con_id).to_dict(abridged=abridged))
464
+ except ValueError:
465
+ try:
466
+ cons.append(window_construction_by_identifier(
467
+ con_id).to_dict(abridged=abridged))
468
+ except ValueError:
469
+ cons.append(shade_construction_by_identifier(con_id).to_dict())
470
+ output_file.write(json.dumps(cons))
471
+ except Exception as e:
472
+ _logger.exception(
473
+ 'Retrieval from construction library failed.\n{}'.format(e))
474
+ sys.exit(1)
475
+ else:
476
+ sys.exit(0)
477
+
478
+
479
+ @lib.command('construction-sets-by-id')
480
+ @click.argument('construction-set-ids', nargs=-1)
481
+ @click.option('--none-defaults/--include-defaults', ' /-d', help='Flag to note whether '
482
+ 'default constructions in the set should be included in detail or should '
483
+ 'be None.', default=True, show_default=True)
484
+ @click.option('--complete/--abridged', ' /-a', help='Flag to note whether an abridged '
485
+ 'definition should be returned.',
486
+ default=True, show_default=True)
487
+ @click.option('--output-file', '-f', help='Optional file to output the JSON string of '
488
+ 'the object. By default, it will be printed out to stdout',
489
+ type=click.File('w'), default='-', show_default=True)
490
+ def construction_sets_by_id(construction_set_ids, none_defaults, complete, output_file):
491
+ """Get several construction set definitions from the standards lib at once.
492
+
493
+ \b
494
+ Args:
495
+ construction_set_ids: Any number of construction set identifiers to be retrieved
496
+ from the library.
497
+ """
498
+ try:
499
+ abridged = not complete
500
+ cons = []
501
+ for con_id in construction_set_ids:
502
+ cons.append(construction_set_by_identifier(con_id))
503
+ output_file.write(json.dumps([con.to_dict(
504
+ none_for_defaults=none_defaults, abridged=abridged) for con in cons]))
505
+ except Exception as e:
506
+ _logger.exception(
507
+ 'Retrieval from construction set library failed.\n{}'.format(e))
508
+ sys.exit(1)
509
+ else:
510
+ sys.exit(0)
511
+
512
+
513
+ @lib.command('schedule-type-limits-by-id')
514
+ @click.argument('schedule-type-limit-ids', nargs=-1)
515
+ @click.option('--output-file', '-f', help='Optional file to output the JSON strings of '
516
+ 'the objects. By default, it will be printed out to stdout',
517
+ type=click.File('w'), default='-', show_default=True)
518
+ def schedule_type_limits_by_id(schedule_type_limit_ids, output_file):
519
+ """Get several schedule type limit definitions from the standards lib at once.
520
+
521
+ \b
522
+ Args:
523
+ schedule_type_limit_ids: Any number of schedule type limit identifiers to be
524
+ retrieved from the library.
525
+ """
526
+ try:
527
+ stls = []
528
+ for stl_id in schedule_type_limit_ids:
529
+ stls.append(schedule_type_limit_by_identifier(stl_id))
530
+ output_file.write(json.dumps([stl.to_dict() for stl in stls]))
531
+ except Exception as e:
532
+ _logger.exception(
533
+ 'Retrieval from schedule type limit library failed.\n{}'.format(e))
534
+ sys.exit(1)
535
+ else:
536
+ sys.exit(0)
537
+
538
+
539
+ @lib.command('schedules-by-id')
540
+ @click.argument('schedule-ids', nargs=-1)
541
+ @click.option('--complete/--abridged', ' /-a', help='Flag to note whether an abridged '
542
+ 'definition should be returned.', default=True, show_default=True)
543
+ @click.option('--output-file', '-f', help='Optional file to output the JSON strings of '
544
+ 'the objects. By default, it will be printed out to stdout',
545
+ type=click.File('w'), default='-', show_default=True)
546
+ def schedules_by_id(schedule_ids, complete, output_file):
547
+ """Get several schedule definitions from the standards lib at once.
548
+
549
+ \b
550
+ Args:
551
+ schedule_ids: Any number of schedule identifiers to be retrieved from
552
+ the library.
553
+ """
554
+ try:
555
+ abridged = not complete
556
+ schs = []
557
+ for sch_id in schedule_ids:
558
+ schs.append(schedule_by_identifier(sch_id))
559
+ output_file.write(json.dumps([sch.to_dict(abridged=abridged) for sch in schs]))
560
+ except Exception as e:
561
+ _logger.exception('Retrieval from schedule library failed.\n{}'.format(e))
562
+ sys.exit(1)
563
+ else:
564
+ sys.exit(0)
565
+
566
+
567
+ @lib.command('program-types-by-id')
568
+ @click.argument('program-type-ids', nargs=-1)
569
+ @click.option('--complete/--abridged', ' /-a', help='Flag to note whether an abridged '
570
+ 'definition should be returned.', default=True, show_default=True)
571
+ @click.option('--output-file', '-f', help='Optional file to output the JSON strings of '
572
+ 'the objects. By default, it will be printed out to stdout',
573
+ type=click.File('w'), default='-', show_default=True)
574
+ def program_types_by_id(program_type_ids, complete, output_file):
575
+ """Get several program type definitions from the standards lib at once.
576
+
577
+ \b
578
+ Args:
579
+ program_type_ids: Any number of program type identifiers to be retrieved
580
+ from the library.
581
+ """
582
+ try:
583
+ abridged = not complete
584
+ prgs = []
585
+ for prg_id in program_type_ids:
586
+ prgs.append(program_type_by_identifier(prg_id))
587
+ output_file.write(json.dumps([prg.to_dict(abridged=abridged) for prg in prgs]))
588
+ except Exception as e:
589
+ _logger.exception('Retrieval from program type library failed.\n{}'.format(e))
590
+ sys.exit(1)
591
+ else:
592
+ sys.exit(0)
593
+
594
+
595
+ @lib.command('to-model-properties')
596
+ @click.option(
597
+ '--standards-folder', '-s', default=None, help='A directory containing sub-folders '
598
+ 'of resource objects (constructions, constructionsets, schedules, programtypes) '
599
+ 'to be loaded as ModelEnergyProperties. Note that this standards folder MUST '
600
+ 'contain these sub-folders. Each sub-folder can contain JSON files of objects '
601
+ 'following honeybee schema or IDF files (if appropriate). If unspecified, the '
602
+ 'current user honeybee default standards folder will be used.', type=click.Path(
603
+ exists=True, file_okay=False, dir_okay=True, resolve_path=True)
604
+ )
605
+ @click.option(
606
+ '--exclude-abridged/--include-abridged', ' /-a', help='Flag to note whether '
607
+ 'fully abridged objects in the user standards library should be included in '
608
+ 'the output file. This is useful when some of the sub-objects contained within '
609
+ 'the user standards are referenced in another installed standards package that '
610
+ 'is not a part of the user personal standards library (eg. honeybee-energy-'
611
+ 'standards). When abridged objects are excluded, only objects that contain all '
612
+ 'sub-objects within the user library will be in the output-file.',
613
+ default=True, show_default=True
614
+ )
615
+ @click.option(
616
+ '--output-file', '-f', help='Optional JSON file to output the JSON string of '
617
+ 'the translation. By default this will be printed out to stdout',
618
+ type=click.File('w'), default='-', show_default=True
619
+ )
620
+ def to_model_properties(standards_folder, exclude_abridged, output_file):
621
+ """Translate a lib folder of standards to a JSON of honeybee ModelEnergyProperties.
622
+
623
+ This is useful in workflows where one must import everything within a user's
624
+ standards folder and requires all objects to be in a consistent format.
625
+ All objects in the resulting ModelEnergyProperties will be abridged and
626
+ duplicated objects in the folder will be removed such that there
627
+ is only one of each object.
628
+ """
629
+ try:
630
+ # set the folder to the default standards_folder if unspecified
631
+ folder = standards_folder if standards_folder is not None else \
632
+ folders.standards_data_folder
633
+
634
+ # load schedules from the standards folder
635
+ sch_folder = os.path.join(folder, 'schedules')
636
+ type_lim = load_type_limits_from_folder(sch_folder)
637
+ tl_with_default = type_lim.copy()
638
+ tl_with_default.update(_schedule_type_limits)
639
+ scheds = load_schedules_from_folder(sch_folder, tl_with_default)
640
+
641
+ # load program types from the standards folder
642
+ prog_folder = os.path.join(folder, 'programtypes')
643
+ all_progs, misc_p_scheds = load_programtypes_from_folder(prog_folder, scheds)
644
+
645
+ # load constructions from the standards folder
646
+ con_folder = os.path.join(folder, 'constructions')
647
+ opaque_m, window_m = load_materials_from_folder(con_folder)
648
+ all_m = opaque_m.copy()
649
+ all_m.update(window_m)
650
+ opaque_c, window_c, shade_c, opaque_mc, window_mc, misc_m, misc_c_scheds = \
651
+ load_constructions_from_folder(con_folder, all_m, scheds)
652
+ all_m.update(opaque_mc)
653
+ all_m.update(window_mc)
654
+ all_c = opaque_c.copy()
655
+ all_c.update(window_c)
656
+ all_c.update(shade_c)
657
+
658
+ # load construction sets from the standards folder
659
+ con_set_folder = os.path.join(folder, 'constructionsets')
660
+ all_con_sets, misc_c = load_constructionsets_from_folder(con_set_folder, all_c)
661
+
662
+ # get sets of unique objects
663
+ all_scheds = set(list(scheds.values()) + misc_p_scheds + misc_c_scheds)
664
+ sched_tl = [sch.schedule_type_limit for sch in all_scheds
665
+ if sch.schedule_type_limit is not None]
666
+ all_typ_lim = set(list(type_lim.values()) + sched_tl)
667
+ all_cons = set(list(all_c.values()) + misc_c)
668
+ misc_c_mats = []
669
+ for m_con in misc_c:
670
+ try:
671
+ misc_c_mats.extend(m_con.materials)
672
+ if m_con.has_frame:
673
+ misc_c_mats.append(m_con.frame)
674
+ if m_con.has_shade:
675
+ if m_con.is_switchable_glazing:
676
+ misc_c_mats.append(m_con.switched_glass_material)
677
+ except AttributeError: # not a construction with materials
678
+ pass
679
+ all_mats = set(list(all_m.values()) + misc_m + misc_c_mats)
680
+
681
+ # add all object dictionaries into one object
682
+ base = {'type': 'ModelEnergyProperties'}
683
+ base['schedule_type_limits'] = [tl.to_dict() for tl in all_typ_lim]
684
+ base['schedules'] = [sch.to_dict(abridged=True) for sch in all_scheds]
685
+ base['program_types'] = \
686
+ [pro.to_dict(abridged=True) for pro in all_progs.values()]
687
+ base['materials'] = [m.to_dict() for m in all_mats]
688
+ base['constructions'] = []
689
+ for con in all_cons:
690
+ try:
691
+ base['constructions'].append(con.to_dict(abridged=True))
692
+ except TypeError: # no abridged option
693
+ base['constructions'].append(con.to_dict())
694
+ base['construction_sets'] = \
695
+ [cs.to_dict(abridged=True) for cs in all_con_sets.values()]
696
+
697
+ # if set to include abridged, add any of such objects to the dictionary
698
+ if not exclude_abridged:
699
+ _add_abridged_objects(base['schedules'], sch_folder, ('ScheduleTypeLimit',))
700
+ _add_abridged_objects(base['program_types'], prog_folder)
701
+ _add_abridged_objects(base['constructions'], con_folder, MATERIAL_TYPES)
702
+ _add_abridged_objects(base['construction_sets'], con_set_folder)
703
+
704
+ # write out the JSON file
705
+ output_file.write(json.dumps(base, indent=4))
706
+ except Exception as e:
707
+ _logger.exception('Loading standards to properties failed.\n{}'.format(e))
708
+ sys.exit(1)
709
+ else:
710
+ sys.exit(0)
711
+
712
+
713
+ def _add_abridged_objects(model_prop_array, lib_folder, ex_types=()):
714
+ """Add abridged resource objects to an existing model properties array.
715
+
716
+ Args:
717
+ model_prop_array: An array of resource object dictionaries from a
718
+ ModelEnergyProperties dictionary.
719
+ lib_folder: A folder from which abridged objects will be loaded.
720
+ exclude_types: An optional tuple of object types to be excluded
721
+ from the result.
722
+ """
723
+ obj_ids = set(obj['identifier'] for obj in model_prop_array)
724
+ for f in os.listdir(lib_folder):
725
+ f_path = os.path.join(lib_folder, f)
726
+ if os.path.isfile(f_path) and f_path.endswith('.json'):
727
+ with open(f_path) as json_file:
728
+ data = json.load(json_file)
729
+ if 'type' in data: # single object
730
+ if data['identifier'] not in obj_ids and data['type'] not in ex_types:
731
+ model_prop_array.append(data)
732
+ else: # a collection of several objects
733
+ for obj_identifier in data:
734
+ if obj_identifier not in obj_ids and \
735
+ data[obj_identifier]['type'] not in ex_types:
736
+ model_prop_array.append(data[obj_identifier])
737
+
738
+
739
+ @lib.command('purge')
740
+ @click.option(
741
+ '--standards-folder', '-s', default=None, help='A directory containing sub-folders '
742
+ 'of resource objects (constructions, constructionsets, schedules, programtypes) '
743
+ 'to be purged of files. If unspecified, the current user honeybee '
744
+ 'default standards folder will be used.', type=click.Path(
745
+ exists=True, file_okay=False, dir_okay=True, resolve_path=True)
746
+ )
747
+ @click.option(
748
+ '--json-only/--all', ' /-a', help='Flag to note whether only JSON files should '
749
+ 'be purged from the library or all files should be purged, including IDF files. '
750
+ 'Given that all objects added to the library through the `add` command will always '
751
+ 'be JSON, only purging the JSONs is useful when one wishes to clear these objects '
752
+ 'while preserving objects that originated from other sources.',
753
+ default=True, show_default=True
754
+ )
755
+ @click.option(
756
+ '--backup/--no-backup', ' /-xb', help='Flag to note whether a backup .zip file '
757
+ 'of the user standards library should be made before the purging operation. '
758
+ 'This is done by default in case the user ever wants to recover their old '
759
+ 'standards but can be turned off if a backup is not desired.',
760
+ default=True, show_default=True
761
+ )
762
+ @click.option(
763
+ '--log-file', '-log', help='Optional file to output a log of the purging process. '
764
+ 'By default this will be printed out to stdout',
765
+ type=click.File('w'), default='-', show_default=True
766
+ )
767
+ def purge_lib(standards_folder, json_only, backup, log_file):
768
+ """Purge the library of all user energy standards that it contains.
769
+
770
+ This is useful when a user's standard library has become filled with duplicated
771
+ objects or the user wishes to start fresh by re-exporting updated objects.
772
+ """
773
+ try:
774
+ # set the folder to the default standards_folder if unspecified
775
+ folder = standards_folder if standards_folder is not None else \
776
+ folders.standards_data_folder
777
+ resources = ('constructions', 'constructionsets', 'schedules', 'programtypes')
778
+ sub_folders = [os.path.join(folder, std) for std in resources]
779
+
780
+ # make a backup of the folder if requested
781
+ if backup:
782
+ r_names, s_files, s_paths = [], [], []
783
+ for sf, r_name in zip(sub_folders, resources):
784
+ for s_file in os.listdir(sf):
785
+ s_path = os.path.join(sf, s_file)
786
+ if os.path.isfile(s_path):
787
+ r_names.append(r_name)
788
+ s_files.append(s_file)
789
+ s_paths.append(s_path)
790
+ if len(s_paths) != 0: # there are resources to back up
791
+ backup_name = '.standards_backup_{}.zip'.format(
792
+ str(datetime.now()).split('.')[0].replace(':', '-'))
793
+ backup_file = os.path.join(os.path.dirname(folder), backup_name)
794
+ with zipfile.ZipFile(backup_file, 'w') as zf:
795
+ for r_name, s_file, s_path in zip(r_names, s_files, s_paths):
796
+ zf.write(s_path, os.path.join(r_name, s_file))
797
+
798
+ # loop through the sub-folders and delete the files
799
+ rel_files = []
800
+ for sf in sub_folders:
801
+ for s_file in os.listdir(sf):
802
+ s_path = os.path.join(sf, s_file)
803
+ if os.path.isfile(s_path):
804
+ if json_only:
805
+ if s_file.lower().endswith('.json'):
806
+ rel_files.append(s_path)
807
+ else:
808
+ rel_files.append(s_path)
809
+ purged_files, fail_files = [], []
810
+ for rf in rel_files:
811
+ try:
812
+ os.remove(rf)
813
+ purged_files.append(rf)
814
+ except Exception:
815
+ fail_files.append(rf)
816
+
817
+ # report all of the deleted files in the log file
818
+ if len(rel_files) == 0:
819
+ log_file.write('The standards folder is empty so no files were removed.')
820
+ if len(purged_files) != 0:
821
+ msg = 'The following files were removed in the purging ' \
822
+ 'operations:\n{}\n'.format(' \n'.join(purged_files))
823
+ log_file.write(msg)
824
+ if len(fail_files) != 0:
825
+ msg = 'The following files could not be removed in the purging ' \
826
+ 'operations:\n{}\n'.format(' \n'.join(fail_files))
827
+ log_file.write(msg)
828
+ except Exception as e:
829
+ _logger.exception('Purging user standards library failed.\n{}'.format(e))
830
+ sys.exit(1)
831
+ else:
832
+ sys.exit(0)
833
+
834
+
835
+ @lib.command('add')
836
+ @click.argument('properties-file', type=click.Path(
837
+ exists=True, file_okay=True, dir_okay=False, resolve_path=True))
838
+ @click.option(
839
+ '--standards-folder', '-s', default=None, help='A directory containing sub-folders '
840
+ 'of resource objects (constructions, constructionsets, schedules, programtypes) '
841
+ 'to which the properties-file objects will be added. If unspecified, the current '
842
+ 'user honeybee default standards folder will be used.', type=click.Path(
843
+ exists=True, file_okay=False, dir_okay=True, resolve_path=True)
844
+ )
845
+ @click.option(
846
+ '--log-file', '-log', help='Optional file to output a log of the purging process. '
847
+ 'By default this will be printed out to stdout',
848
+ type=click.File('w'), default='-', show_default=True
849
+ )
850
+ def add_to_lib(properties_file, standards_folder, log_file):
851
+ """Add an object or set of objects to the user's standard library.
852
+
853
+ \b
854
+ Args:
855
+ properties_file: A JSON file of a ModelEnergyProperties object containing
856
+ the objects to be written into the user standards library. All sub-objects
857
+ within this ModelEnergyProperties object must be Abridged if the sub-object
858
+ has an abridged schema and these abridged schemas are allowed to
859
+ reference either other objects in the ModelEnergyProperties or existing
860
+ objects within the standards library.
861
+ """
862
+ try:
863
+ # set the folder to the default standards_folder if unspecified
864
+ folder = standards_folder if standards_folder is not None else \
865
+ folders.standards_data_folder
866
+
867
+ # load up the model energy properties from the JSON
868
+ with open(properties_file) as inf:
869
+ data = json.load(inf)
870
+ assert 'type' in data, 'Properties file lacks required type key.'
871
+ assert data['type'] == 'ModelEnergyProperties', 'Expected ' \
872
+ 'ModelEnergyProperties JSON object. Got {}.'.format(data['type'])
873
+ success_objects, dup_id_objects, mis_dep_objects = [], [], []
874
+
875
+ # extract, check, and write the schedule type limits
876
+ sch_tl = {}
877
+ if 'schedule_type_limits' in data and data['schedule_type_limits'] is not None:
878
+ for stl_obj in data['schedule_type_limits']:
879
+ msg = _object_message('Schedule Type Limit', stl_obj)
880
+ if stl_obj['identifier'] in _schedule_type_limits:
881
+ dup_id_objects.append(msg)
882
+ else:
883
+ try:
884
+ sch_tl[stl_obj['identifier']] = \
885
+ ScheduleTypeLimit.from_dict(stl_obj)
886
+ success_objects.append(msg)
887
+ except (ValueError, KeyError, AssertionError):
888
+ mis_dep_objects.append(msg)
889
+ if sch_tl:
890
+ sch_tl_dict = {tl.identifier: tl.to_dict() for tl in sch_tl.values()}
891
+ stl_json = os.path.join(folder, 'schedules', 'custom_type_limits.json')
892
+ _update_user_json(sch_tl_dict, stl_json)
893
+
894
+ # extract, check, and write the schedules
895
+ scheds = {}
896
+ if 'schedules' in data and data['schedules'] is not None:
897
+ for sch in data['schedules']:
898
+ msg = _object_message('Schedule', sch)
899
+ if sch['identifier'] in SCHEDULES:
900
+ dup_id_objects.append(msg)
901
+ else:
902
+ try:
903
+ scheds[sch['identifier']] = \
904
+ lib_dict_abridged_to_schedule(sch, sch_tl)
905
+ success_objects.append(msg)
906
+ except (ValueError, KeyError, AssertionError):
907
+ mis_dep_objects.append(msg)
908
+ if scheds:
909
+ sch_dict = {s.identifier: s.to_dict(abridged=True) for s in scheds.values()}
910
+ sch_json = os.path.join(folder, 'schedules', 'custom_schedules.json')
911
+ _update_user_json(sch_dict, sch_json)
912
+
913
+ # extract, check, and write the program types
914
+ progs = {}
915
+ if 'program_types' in data and data['program_types'] is not None:
916
+ for prog in data['program_types']:
917
+ msg = _object_message('Program', prog)
918
+ if prog['identifier'] in PROGRAM_TYPES:
919
+ dup_id_objects.append(msg)
920
+ else:
921
+ try:
922
+ progs[prog['identifier']] = \
923
+ lib_dict_abridged_to_program_type(prog, scheds)
924
+ success_objects.append(msg)
925
+ except (ValueError, KeyError, AssertionError):
926
+ mis_dep_objects.append(msg)
927
+ if progs:
928
+ prog_dict = {p.identifier: p.to_dict(abridged=True) for p in progs.values()}
929
+ program_json = os.path.join(folder, 'programtypes', 'custom_programs.json')
930
+ _update_user_json(prog_dict, program_json)
931
+
932
+ # extract, check, and write the materials
933
+ mats = {}
934
+ if 'materials' in data and data['materials'] is not None and \
935
+ len(data['materials']) != 0:
936
+ all_mats = OPAQUE_MATERIALS + WINDOW_MATERIALS
937
+ for mat_obj in data['materials']:
938
+ msg = _object_message('Material', mat_obj)
939
+ if mat_obj['identifier'] in all_mats:
940
+ dup_id_objects.append(msg)
941
+ else:
942
+ try:
943
+ mats[mat_obj['identifier']] = dict_to_material(mat_obj)
944
+ success_objects.append(msg)
945
+ except (ValueError, KeyError, AssertionError):
946
+ mis_dep_objects.append(msg)
947
+ if mats:
948
+ mat_dict = {m.identifier: m.to_dict() for m in mats.values()}
949
+ mat_json = os.path.join(folder, 'constructions', 'custom_materials.json')
950
+ _update_user_json(mat_dict, mat_json)
951
+
952
+ # extract, check, and write the constructions
953
+ cons = {}
954
+ if 'constructions' in data and data['constructions'] is not None and \
955
+ len(data['constructions']) != 0:
956
+ all_cons = OPAQUE_CONSTRUCTIONS + WINDOW_CONSTRUCTIONS + SHADE_CONSTRUCTIONS
957
+ for con in data['constructions']:
958
+ msg = _object_message('Construction', con)
959
+ if con['identifier'] in all_cons:
960
+ dup_id_objects.append(msg)
961
+ else:
962
+ try:
963
+ cons[con['identifier']] = \
964
+ lib_dict_abridged_to_construction(con, mats, scheds)
965
+ success_objects.append(msg)
966
+ except (ValueError, KeyError, AssertionError):
967
+ mis_dep_objects.append(msg)
968
+ if cons:
969
+ con_dict = {}
970
+ for c in cons.values():
971
+ try:
972
+ con_dict[c.identifier] = c.to_dict(abridged=True)
973
+ except TypeError: # ShadeConstruction
974
+ con_dict[c.identifier] = c.to_dict()
975
+ con_json = os.path.join(folder, 'constructions', 'custom_constructions.json')
976
+ _update_user_json(con_dict, con_json)
977
+
978
+ # extract, check, and write the construction sets
979
+ con_sets = {}
980
+ if 'construction_sets' in data and data['construction_sets'] is not None:
981
+ for cs in data['construction_sets']:
982
+ msg = _object_message('Construction Set', cs)
983
+ if cs['identifier'] in CONSTRUCTION_SETS:
984
+ dup_id_objects.append(msg)
985
+ else:
986
+ try:
987
+ con_sets[cs['identifier']] = \
988
+ lib_dict_abridged_to_construction_set(cs, cons)
989
+ success_objects.append(msg)
990
+ except (ValueError, KeyError, AssertionError):
991
+ mis_dep_objects.append(msg)
992
+ if con_sets:
993
+ cs_dict = {c.identifier: c.to_dict(abridged=True) for c in con_sets.values()}
994
+ cs_json = os.path.join(folder, 'constructionsets', 'custom_sets.json')
995
+ _update_user_json(cs_dict, cs_json)
996
+
997
+ # write a report of the objects that were or were not added
998
+ success_objects, dup_id_objects, mis_dep_objects
999
+ m_start = 'THESE OBJECTS'
1000
+ if len(success_objects) != 0:
1001
+ msg = '{} WERE SUCCESSFULLY ADDED TO THE STANDARDS LIBRARY:\n{}\n\n'.format(
1002
+ m_start, ' \n'.join(success_objects))
1003
+ log_file.write(msg)
1004
+ if len(dup_id_objects) != 0:
1005
+ msg = '{} WERE NOT ADDED SINCE THEY ALREADY EXIST IN THE STANDARDS ' \
1006
+ 'LIBRARY:\n{}\n\n'.format(m_start, ' \n'.join(dup_id_objects))
1007
+ log_file.write(msg)
1008
+ if len(mis_dep_objects) != 0:
1009
+ msg = '{} WERE NOT ADDED BECAUSE THEY ARE INVALID OR ARE MISSING ' \
1010
+ 'DEPENDENT OBJECTS:\n{}\n\n'.format(m_start, ' \n'.join(mis_dep_objects))
1011
+ log_file.write(msg)
1012
+ except Exception as e:
1013
+ _logger.exception('Adding to user standards library failed.\n{}'.format(e))
1014
+ sys.exit(1)
1015
+ else:
1016
+ sys.exit(0)
1017
+
1018
+
1019
+ def _object_message(obj_type, obj_dict):
1020
+ """Get the reporting message of an object to add to the user library."""
1021
+ obj_name = obj_dict['display_name'] if 'display_name' in obj_dict and \
1022
+ obj_dict['display_name'] is not None else obj_dict['identifier']
1023
+ return '{}: {}'.format(obj_type, obj_name)
1024
+
1025
+
1026
+ def _update_user_json(dict_to_add, user_json, indent=4):
1027
+ """Update a JSON file within a user standards folder."""
1028
+ if os.path.isfile(user_json):
1029
+ with open(user_json) as inf:
1030
+ exist_data = json.load(inf)
1031
+ dict_to_add.update(exist_data)
1032
+ with open(user_json, 'w') as outf:
1033
+ json.dump(dict_to_add, outf, indent=indent)
1034
+
1035
+
1036
+ @lib.command('add-osm')
1037
+ @click.argument('osm-file', type=click.Path(
1038
+ exists=True, file_okay=True, dir_okay=False, resolve_path=True))
1039
+ @click.option(
1040
+ '--standards-folder', '-s', default=None, help='A directory containing sub-folders '
1041
+ 'of resource objects (constructions, constructionsets, schedules, programtypes) '
1042
+ 'to which the properties-file objects will be added. If unspecified, the current '
1043
+ 'user honeybee default standards folder will be used.', type=click.Path(
1044
+ exists=True, file_okay=False, dir_okay=True, resolve_path=True))
1045
+ @click.option(
1046
+ '--indent', '-i', help='Optional integer to specify the indentation in '
1047
+ 'the output JSON file. Specifying an value here can produce more read-able'
1048
+ ' JSONs.', type=int, default=None, show_default=True)
1049
+ @click.option(
1050
+ '--osw-folder', '-osw', help='Folder on this computer, into which the '
1051
+ 'working files will be written. If None, it will be written into the a '
1052
+ 'temp folder in the default simulation folder.', default=None,
1053
+ type=click.Path(file_okay=False, dir_okay=True, resolve_path=True))
1054
+ @click.option(
1055
+ '--log-file', '-log', help='Optional file to output a log of the addition process. '
1056
+ 'By default this will be printed out to stdout',
1057
+ type=click.File('w'), default='-', show_default=True)
1058
+ def add_osm_to_lib(osm_file, standards_folder, indent, osw_folder, log_file):
1059
+ """Add all objects within an OSM file to the user's standard library.
1060
+
1061
+ \b
1062
+ Args:
1063
+ osm_file: Path to a OpenStudio Model (OSM) file.
1064
+ """
1065
+ try:
1066
+ # set the folder to the default standards_folder if unspecified
1067
+ folder = standards_folder if standards_folder is not None else \
1068
+ folders.standards_data_folder
1069
+ # translate the OSM to a HBJSON
1070
+ model_dict = _translate_osm_to_hbjson(osm_file, osw_folder)
1071
+ base_name = os.path.basename(osm_file).lower().replace('.osm', '')
1072
+
1073
+ # write each of the objects from the dictionary into the standards folder
1074
+ added_objs = []
1075
+ # write the materials
1076
+ con_folder = os.path.join(folder, 'constructions')
1077
+ mat_json = os.path.join(con_folder, '{}_materials.json'.format(base_name))
1078
+ mat_dict = {}
1079
+ for mat in model_dict['properties']['energy']['materials']:
1080
+ if mat['identifier'] not in _default_mats:
1081
+ added_objs.append(mat['identifier'])
1082
+ mat_dict[mat['identifier']] = mat
1083
+ _update_user_json(mat_dict, mat_json, indent)
1084
+ # write the constructions
1085
+ con_json = os.path.join(con_folder, '{}_constructions.json'.format(base_name))
1086
+ con_dict = {}
1087
+ for con in model_dict['properties']['energy']['constructions']:
1088
+ if con['identifier'] not in _default_constrs:
1089
+ added_objs.append(con['identifier'])
1090
+ con_dict[con['identifier']] = con
1091
+ _update_user_json(con_dict, con_json, indent)
1092
+ # write the construction sets
1093
+ c_set_folder = os.path.join(folder, 'constructionsets')
1094
+ c_set_json = os.path.join(c_set_folder, '{}.json'.format(base_name))
1095
+ c_set_dict = {}
1096
+ for c_set in model_dict['properties']['energy']['construction_sets']:
1097
+ if c_set['identifier'] not in _default_sets:
1098
+ added_objs.append(c_set['identifier'])
1099
+ c_set_dict[c_set['identifier']] = c_set
1100
+ _update_user_json(c_set_dict, c_set_json, indent)
1101
+ # write the type limits
1102
+ sched_folder = os.path.join(folder, 'schedules')
1103
+ stl_json = os.path.join(sched_folder, '{}_type_limits.json'.format(base_name))
1104
+ sch_tl_dict = {}
1105
+ for stl in model_dict['properties']['energy']['schedule_type_limits']:
1106
+ if stl['identifier'] not in _schedule_type_limits:
1107
+ added_objs.append(stl['identifier'])
1108
+ sch_tl_dict[stl['identifier']] = stl
1109
+ _update_user_json(sch_tl_dict, stl_json, indent)
1110
+ # write the schedules
1111
+ sch_json = os.path.join(sched_folder, '{}_schedules.json'.format(base_name))
1112
+ sch_dict = {}
1113
+ for sch in model_dict['properties']['energy']['schedules']:
1114
+ if sch['identifier'] not in _default_schedules:
1115
+ added_objs.append(sch['identifier'])
1116
+ sch_dict[sch['identifier']] = sch
1117
+ _update_user_json(sch_dict, sch_json, indent)
1118
+ # write the programs
1119
+ prog_folder = os.path.join(folder, 'programtypes')
1120
+ prog_json = os.path.join(prog_folder, '{}.json'.format(base_name))
1121
+ prog_dict = {}
1122
+ for prog in model_dict['properties']['energy']['program_types']:
1123
+ if prog['identifier'] not in _default_programs:
1124
+ added_objs.append(prog['identifier'])
1125
+ prog_dict[prog['identifier']] = prog
1126
+ _update_user_json(prog_dict, prog_json, indent)
1127
+
1128
+ # write a message into the log file
1129
+ msg = 'The following objects were successfully added:\n{}'.format(
1130
+ '\n'.join(added_objs))
1131
+ log_file.write(msg)
1132
+ except Exception as e:
1133
+ _logger.exception('Adding to user standards library failed.\n{}'.format(e))
1134
+ sys.exit(1)
1135
+ else:
1136
+ sys.exit(0)
1137
+
1138
+
1139
+ def _translate_osm_to_hbjson(osm_file, osw_folder):
1140
+ """Translate an OSM to a HBJSON for use in resource extraction commands."""
1141
+ # come up with a temporary path to write the HBJSON
1142
+ out_directory = os.path.join(
1143
+ hb_folders.default_simulation_folder, 'temp_translate')
1144
+ f_name = os.path.basename(osm_file).lower().replace('.osm', '.hbjson')
1145
+ out_path = os.path.join(out_directory, f_name)
1146
+ # run the OSW to translate the OSM to HBJSON
1147
+ osw = from_osm_osw(osm_file, out_path, osw_folder)
1148
+ # load the resulting HBJSON to a dictionary and return it
1149
+ _, idf = run_osw(osw, silent=True)
1150
+ if idf is not None and os.path.isfile(idf):
1151
+ with open(out_path) as json_file:
1152
+ return json.load(json_file)
1153
+ else:
1154
+ _parse_os_cli_failure(os.path.dirname(osw))