honeybee-radiance 1.66.190__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.

Potentially problematic release.


This version of honeybee-radiance might be problematic. Click here for more details.

Files changed (152) hide show
  1. honeybee_radiance/__init__.py +11 -0
  2. honeybee_radiance/__main__.py +4 -0
  3. honeybee_radiance/_extend_honeybee.py +93 -0
  4. honeybee_radiance/cli/__init__.py +88 -0
  5. honeybee_radiance/cli/dc.py +400 -0
  6. honeybee_radiance/cli/edit.py +529 -0
  7. honeybee_radiance/cli/glare.py +118 -0
  8. honeybee_radiance/cli/grid.py +859 -0
  9. honeybee_radiance/cli/lib.py +458 -0
  10. honeybee_radiance/cli/modifier.py +133 -0
  11. honeybee_radiance/cli/mtx.py +226 -0
  12. honeybee_radiance/cli/multiphase.py +1034 -0
  13. honeybee_radiance/cli/octree.py +640 -0
  14. honeybee_radiance/cli/postprocess.py +1186 -0
  15. honeybee_radiance/cli/raytrace.py +219 -0
  16. honeybee_radiance/cli/rpict.py +125 -0
  17. honeybee_radiance/cli/schedule.py +56 -0
  18. honeybee_radiance/cli/setconfig.py +63 -0
  19. honeybee_radiance/cli/sky.py +545 -0
  20. honeybee_radiance/cli/study.py +66 -0
  21. honeybee_radiance/cli/sunpath.py +331 -0
  22. honeybee_radiance/cli/threephase.py +255 -0
  23. honeybee_radiance/cli/translate.py +400 -0
  24. honeybee_radiance/cli/util.py +121 -0
  25. honeybee_radiance/cli/view.py +261 -0
  26. honeybee_radiance/cli/viewfactor.py +347 -0
  27. honeybee_radiance/config.json +6 -0
  28. honeybee_radiance/config.py +427 -0
  29. honeybee_radiance/dictutil.py +50 -0
  30. honeybee_radiance/dynamic/__init__.py +5 -0
  31. honeybee_radiance/dynamic/group.py +479 -0
  32. honeybee_radiance/dynamic/multiphase.py +557 -0
  33. honeybee_radiance/dynamic/state.py +718 -0
  34. honeybee_radiance/dynamic/stategeo.py +352 -0
  35. honeybee_radiance/geometry/__init__.py +13 -0
  36. honeybee_radiance/geometry/bubble.py +42 -0
  37. honeybee_radiance/geometry/cone.py +215 -0
  38. honeybee_radiance/geometry/cup.py +54 -0
  39. honeybee_radiance/geometry/cylinder.py +197 -0
  40. honeybee_radiance/geometry/geometrybase.py +37 -0
  41. honeybee_radiance/geometry/instance.py +40 -0
  42. honeybee_radiance/geometry/mesh.py +38 -0
  43. honeybee_radiance/geometry/polygon.py +174 -0
  44. honeybee_radiance/geometry/ring.py +214 -0
  45. honeybee_radiance/geometry/source.py +182 -0
  46. honeybee_radiance/geometry/sphere.py +178 -0
  47. honeybee_radiance/geometry/tube.py +46 -0
  48. honeybee_radiance/lib/__init__.py +1 -0
  49. honeybee_radiance/lib/_loadmodifiers.py +72 -0
  50. honeybee_radiance/lib/_loadmodifiersets.py +69 -0
  51. honeybee_radiance/lib/modifiers.py +58 -0
  52. honeybee_radiance/lib/modifiersets.py +63 -0
  53. honeybee_radiance/lightpath.py +204 -0
  54. honeybee_radiance/lightsource/__init__.py +1 -0
  55. honeybee_radiance/lightsource/_gendaylit.py +479 -0
  56. honeybee_radiance/lightsource/dictutil.py +49 -0
  57. honeybee_radiance/lightsource/ground.py +160 -0
  58. honeybee_radiance/lightsource/sky/__init__.py +7 -0
  59. honeybee_radiance/lightsource/sky/_skybase.py +177 -0
  60. honeybee_radiance/lightsource/sky/certainirradiance.py +232 -0
  61. honeybee_radiance/lightsource/sky/cie.py +378 -0
  62. honeybee_radiance/lightsource/sky/climatebased.py +501 -0
  63. honeybee_radiance/lightsource/sky/hemisphere.py +160 -0
  64. honeybee_radiance/lightsource/sky/skydome.py +113 -0
  65. honeybee_radiance/lightsource/sky/skymatrix.py +163 -0
  66. honeybee_radiance/lightsource/sky/strutil.py +34 -0
  67. honeybee_radiance/lightsource/sky/sunmatrix.py +212 -0
  68. honeybee_radiance/lightsource/sunpath.py +247 -0
  69. honeybee_radiance/modifier/__init__.py +3 -0
  70. honeybee_radiance/modifier/material/__init__.py +30 -0
  71. honeybee_radiance/modifier/material/absdf.py +477 -0
  72. honeybee_radiance/modifier/material/antimatter.py +54 -0
  73. honeybee_radiance/modifier/material/ashik2.py +51 -0
  74. honeybee_radiance/modifier/material/brtdfunc.py +81 -0
  75. honeybee_radiance/modifier/material/bsdf.py +292 -0
  76. honeybee_radiance/modifier/material/dielectric.py +53 -0
  77. honeybee_radiance/modifier/material/glass.py +431 -0
  78. honeybee_radiance/modifier/material/glow.py +246 -0
  79. honeybee_radiance/modifier/material/illum.py +51 -0
  80. honeybee_radiance/modifier/material/interface.py +49 -0
  81. honeybee_radiance/modifier/material/light.py +206 -0
  82. honeybee_radiance/modifier/material/materialbase.py +36 -0
  83. honeybee_radiance/modifier/material/metal.py +167 -0
  84. honeybee_radiance/modifier/material/metal2.py +41 -0
  85. honeybee_radiance/modifier/material/metdata.py +41 -0
  86. honeybee_radiance/modifier/material/metfunc.py +41 -0
  87. honeybee_radiance/modifier/material/mirror.py +340 -0
  88. honeybee_radiance/modifier/material/mist.py +86 -0
  89. honeybee_radiance/modifier/material/plasdata.py +58 -0
  90. honeybee_radiance/modifier/material/plasfunc.py +59 -0
  91. honeybee_radiance/modifier/material/plastic.py +354 -0
  92. honeybee_radiance/modifier/material/plastic2.py +58 -0
  93. honeybee_radiance/modifier/material/prism1.py +57 -0
  94. honeybee_radiance/modifier/material/prism2.py +48 -0
  95. honeybee_radiance/modifier/material/spotlight.py +50 -0
  96. honeybee_radiance/modifier/material/trans.py +518 -0
  97. honeybee_radiance/modifier/material/trans2.py +49 -0
  98. honeybee_radiance/modifier/material/transdata.py +50 -0
  99. honeybee_radiance/modifier/material/transfunc.py +53 -0
  100. honeybee_radiance/modifier/mixture/__init__.py +6 -0
  101. honeybee_radiance/modifier/mixture/mixdata.py +49 -0
  102. honeybee_radiance/modifier/mixture/mixfunc.py +54 -0
  103. honeybee_radiance/modifier/mixture/mixpict.py +52 -0
  104. honeybee_radiance/modifier/mixture/mixtext.py +66 -0
  105. honeybee_radiance/modifier/mixture/mixturebase.py +28 -0
  106. honeybee_radiance/modifier/modifierbase.py +40 -0
  107. honeybee_radiance/modifier/pattern/__init__.py +9 -0
  108. honeybee_radiance/modifier/pattern/brightdata.py +49 -0
  109. honeybee_radiance/modifier/pattern/brightfunc.py +47 -0
  110. honeybee_radiance/modifier/pattern/brighttext.py +81 -0
  111. honeybee_radiance/modifier/pattern/colordata.py +56 -0
  112. honeybee_radiance/modifier/pattern/colorfunc.py +47 -0
  113. honeybee_radiance/modifier/pattern/colorpict.py +54 -0
  114. honeybee_radiance/modifier/pattern/colortext.py +73 -0
  115. honeybee_radiance/modifier/pattern/patternbase.py +34 -0
  116. honeybee_radiance/modifier/texture/__init__.py +4 -0
  117. honeybee_radiance/modifier/texture/texdata.py +29 -0
  118. honeybee_radiance/modifier/texture/texfunc.py +26 -0
  119. honeybee_radiance/modifier/texture/texturebase.py +27 -0
  120. honeybee_radiance/modifierset.py +1091 -0
  121. honeybee_radiance/mutil.py +60 -0
  122. honeybee_radiance/postprocess/__init__.py +1 -0
  123. honeybee_radiance/postprocess/annual.py +108 -0
  124. honeybee_radiance/postprocess/annualdaylight.py +425 -0
  125. honeybee_radiance/postprocess/annualglare.py +201 -0
  126. honeybee_radiance/postprocess/annualirradiance.py +187 -0
  127. honeybee_radiance/postprocess/electriclight.py +119 -0
  128. honeybee_radiance/postprocess/en17037.py +261 -0
  129. honeybee_radiance/postprocess/leed.py +304 -0
  130. honeybee_radiance/postprocess/solartracking.py +90 -0
  131. honeybee_radiance/primitive.py +554 -0
  132. honeybee_radiance/properties/__init__.py +1 -0
  133. honeybee_radiance/properties/_base.py +390 -0
  134. honeybee_radiance/properties/aperture.py +197 -0
  135. honeybee_radiance/properties/door.py +198 -0
  136. honeybee_radiance/properties/face.py +123 -0
  137. honeybee_radiance/properties/model.py +1291 -0
  138. honeybee_radiance/properties/room.py +490 -0
  139. honeybee_radiance/properties/shade.py +186 -0
  140. honeybee_radiance/properties/shademesh.py +116 -0
  141. honeybee_radiance/putil.py +44 -0
  142. honeybee_radiance/reader.py +214 -0
  143. honeybee_radiance/sensor.py +166 -0
  144. honeybee_radiance/sensorgrid.py +1008 -0
  145. honeybee_radiance/view.py +1101 -0
  146. honeybee_radiance/writer.py +951 -0
  147. honeybee_radiance-1.66.190.dist-info/METADATA +89 -0
  148. honeybee_radiance-1.66.190.dist-info/RECORD +152 -0
  149. honeybee_radiance-1.66.190.dist-info/WHEEL +5 -0
  150. honeybee_radiance-1.66.190.dist-info/entry_points.txt +2 -0
  151. honeybee_radiance-1.66.190.dist-info/licenses/LICENSE +661 -0
  152. honeybee_radiance-1.66.190.dist-info/top_level.txt +1 -0
@@ -0,0 +1,458 @@
1
+ """honeybee radiance 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_radiance.config import folders
11
+ from honeybee_radiance.lib.modifiers import modifier_by_identifier, MODIFIERS
12
+ from honeybee_radiance.lib.modifiersets import modifier_set_by_identifier, \
13
+ lib_dict_abridged_to_modifier_set, MODIFIER_SETS
14
+ from honeybee_radiance.mutil import dict_to_modifier
15
+
16
+ from honeybee_radiance.lib._loadmodifiers import load_modifiers_from_folder
17
+ from honeybee_radiance.lib._loadmodifiersets import load_modifiersets_from_folder
18
+
19
+ _logger = logging.getLogger(__name__)
20
+
21
+
22
+ @click.group(help='Commands for retrieving objects from the standards library.')
23
+ def lib():
24
+ pass
25
+
26
+
27
+ @lib.command('modifiers')
28
+ @click.option('--output-file', help='Optional file to output the JSON string of '
29
+ 'the object. By default, it will be printed out to stdout',
30
+ type=click.File('w'), default='-', show_default=True)
31
+ def modifiers(output_file):
32
+ """Get a list of all modifiers in the standards library."""
33
+ try:
34
+ output_file.write(json.dumps(MODIFIERS))
35
+ except Exception as e:
36
+ _logger.exception('Failed to load modifiers.\n{}'.format(e))
37
+ sys.exit(1)
38
+ else:
39
+ sys.exit(0)
40
+
41
+
42
+ @lib.command('modifier-sets')
43
+ @click.option('--output-file', help='Optional file to output the JSON string of '
44
+ 'the object. By default, it will be printed out to stdout',
45
+ type=click.File('w'), default='-', show_default=True)
46
+ def modifier_sets(output_file):
47
+ """Get a list of all modifier sets in the standards library."""
48
+ try:
49
+ output_file.write(json.dumps(MODIFIER_SETS))
50
+ except Exception as e:
51
+ _logger.exception('Failed to load modifier sets.\n{}'.format(e))
52
+ sys.exit(1)
53
+ else:
54
+ sys.exit(0)
55
+
56
+
57
+ @lib.command('modifier-by-id')
58
+ @click.argument('modifier-id', type=str)
59
+ @click.option('--output-file', help='Optional file to output the JSON string of '
60
+ 'the object. By default, it will be printed out to stdout',
61
+ type=click.File('w'), default='-', show_default=True)
62
+ def modifier_by_id(modifier_id, output_file):
63
+ """Get a modifier definition from the standards lib with its identifier.
64
+ \n
65
+ Args:
66
+ modifier_id: The identifier of a modifier in the library.
67
+ """
68
+ try:
69
+ output_file.write(json.dumps(modifier_by_identifier(modifier_id).to_dict()))
70
+ except Exception as e:
71
+ _logger.exception(
72
+ 'Retrieval from modifier library failed.\n{}'.format(e))
73
+ sys.exit(1)
74
+ else:
75
+ sys.exit(0)
76
+
77
+
78
+ @lib.command('modifier-set-by-id')
79
+ @click.argument('modifier-set-id', type=str)
80
+ @click.option('--none-defaults', help='Boolean to note whether default modifiers '
81
+ 'in the set should be included in detail (False) or should be None '
82
+ '(True).', type=bool, default=True, show_default=True)
83
+ @click.option('--abridged', help='Optional boolean to note wether an abridged definition'
84
+ ' should be returned.', type=bool, default=False, show_default=True)
85
+ @click.option('--output-file', help='Optional file to output the JSON string of '
86
+ 'the object. By default, it will be printed out to stdout',
87
+ type=click.File('w'), default='-', show_default=True)
88
+ def modifier_set_by_id(modifier_set_id, none_defaults, abridged, output_file):
89
+ """Get an modifier set definition from the standards lib with its identifier.
90
+ \n
91
+ Args:
92
+ modifier_set_id: The identifier of a modifier set in the library.
93
+ """
94
+ try:
95
+ m_set = modifier_set_by_identifier(modifier_set_id)
96
+ output_file.write(json.dumps(m_set.to_dict(
97
+ none_for_defaults=none_defaults, abridged=abridged)))
98
+ except Exception as e:
99
+ _logger.exception(
100
+ 'Retrieval from modifier set library failed.\n{}'.format(e))
101
+ sys.exit(1)
102
+ else:
103
+ sys.exit(0)
104
+
105
+
106
+ @lib.command('modifiers-by-id')
107
+ @click.argument('modifier-ids', nargs=-1)
108
+ @click.option('--output-file', help='Optional file to output the JSON strings of '
109
+ 'the objects. By default, it will be printed out to stdout',
110
+ type=click.File('w'), default='-', show_default=True)
111
+ def modifiers_by_id(modifier_ids, output_file):
112
+ """Get several modifier definitions from the standards lib at once.
113
+ \n
114
+ Args:
115
+ modifier_ids: A list of modifier identifiers to be retrieved from the library.
116
+ """
117
+ try:
118
+ mods = [modifier_by_identifier(mod_id) for mod_id in modifier_ids]
119
+ output_file.write(json.dumps([mod.to_dict() for mod in mods]))
120
+ except Exception as e:
121
+ _logger.exception(
122
+ 'Retrieval from modifier library failed.\n{}'.format(e))
123
+ sys.exit(1)
124
+ else:
125
+ sys.exit(0)
126
+
127
+
128
+ @lib.command('modifier-sets-by-id')
129
+ @click.argument('modifier-set-ids', nargs=-1)
130
+ @click.option('--none-defaults', help='Boolean to note whether default modifiers '
131
+ 'in the set should be included in detail (False) or should be None '
132
+ '(True).', type=bool, default=True, show_default=True)
133
+ @click.option('--abridged', help='Optional boolean to note wether an abridged definition'
134
+ ' should be returned.', type=bool, default=False, show_default=True)
135
+ @click.option('--output-file', help='Optional file to output the JSON string of '
136
+ 'the object. By default, it will be printed out to stdout',
137
+ type=click.File('w'), default='-', show_default=True)
138
+ def modifier_sets_by_id(modifier_set_ids, none_defaults, abridged, output_file):
139
+ """Get several modifier set definitions from the standards lib at once.
140
+ \n
141
+ Args:
142
+ modifier_set_ids: A list of modifier set identifiers to be retrieved
143
+ from the library.
144
+ """
145
+ try:
146
+ m_sets = [modifier_set_by_identifier(m_id) for m_id in modifier_set_ids]
147
+ output_file.write(json.dumps([ms.to_dict(
148
+ none_for_defaults=none_defaults, abridged=abridged) for ms in m_sets]))
149
+ except Exception as e:
150
+ _logger.exception(
151
+ 'Retrieval from modifier set library failed.\n{}'.format(e))
152
+ sys.exit(1)
153
+ else:
154
+ sys.exit(0)
155
+
156
+
157
+ @lib.command('to-model-properties')
158
+ @click.option(
159
+ '--standards-folder', '-s', default=None, help='A directory containing sub-folders '
160
+ 'of resource objects (modifiers, modifiersets) to be loaded as '
161
+ 'ModelRadianceProperties. Note that this standards folder MUST contain these '
162
+ 'sub-folders. Each sub-folder can contain JSON files of objects following '
163
+ 'honeybee schema or RAD/MAT files (if appropriate). If None, the honeybee '
164
+ 'default standards folder will be used.', type=click.Path(
165
+ exists=True, file_okay=False, dir_okay=True, resolve_path=True)
166
+ )
167
+ @click.option(
168
+ '--exclude-abridged/--include-abridged', ' /-a', help='Flag to note whether '
169
+ 'fully abridged objects in the user standards library should be included in '
170
+ 'the output file. This is useful when some of the sub-objects contained within '
171
+ 'the user standards are referenced in another installed standards package that '
172
+ 'is not a part of the user personal standards library (eg. honeybee-standards). '
173
+ 'When abridged objects are excluded, only objects that contain all '
174
+ 'sub-objects within the user library will be in the output-file.',
175
+ default=True, show_default=True
176
+ )
177
+ @click.option(
178
+ '--output-file', '-f', help='Optional JSON file to output the JSON string of '
179
+ 'the translation. By default this will be printed out to stdout',
180
+ type=click.File('w'), default='-', show_default=True
181
+ )
182
+ def to_model_properties(standards_folder, exclude_abridged, output_file):
183
+ """Translate a lib folder of standards to a JSON of honeybee ModelRadianceProperties.
184
+
185
+ This is useful in workflows where one must import everything within a user's
186
+ standards folder and requires all objects to be in a consistent format.
187
+ All objects in the resulting ModelRadianceProperties will be abridged and
188
+ duplicated objects in the folder will be removed such that there
189
+ is only one of each object.
190
+ """
191
+ try:
192
+ # set the folder to the default standards_folder if unspecified
193
+ folder = standards_folder if standards_folder is not None else \
194
+ folders.standards_data_folder
195
+
196
+ # load modifiers from the standards folder
197
+ mod_folder = os.path.join(folder, 'modifiers')
198
+ all_m = load_modifiers_from_folder(mod_folder)
199
+
200
+ # load modifier sets from the standards folder
201
+ mod_set_folder = os.path.join(folder, 'modifiersets')
202
+ all_mod_sets, misc_m = load_modifiersets_from_folder(mod_set_folder, all_m)
203
+ all_mods = set(list(all_m.values()) + misc_m)
204
+
205
+ # add all object dictionaries into one object
206
+ base = {'type': 'ModelRadianceProperties'}
207
+ base['modifiers'] = [m.to_dict() for m in all_mods]
208
+ base['modifier_sets'] = \
209
+ [ms.to_dict(abridged=True) for ms in all_mod_sets.values()]
210
+
211
+ # if set to include abridged, add any of such objects to the dictionary
212
+ if not exclude_abridged:
213
+ _add_abridged_objects(base['modifiers'], mod_folder)
214
+ _add_abridged_objects(base['modifier_sets'], mod_set_folder)
215
+
216
+ # write out the JSON file
217
+ output_file.write(json.dumps(base))
218
+ except Exception as e:
219
+ _logger.exception('Loading standards to properties failed.\n{}'.format(e))
220
+ sys.exit(1)
221
+ else:
222
+ sys.exit(0)
223
+
224
+
225
+ def _add_abridged_objects(model_prop_array, lib_folder):
226
+ """Add abridged resource objects to an existing model properties array.
227
+
228
+ Args:
229
+ model_prop_array: An array of resource object dictionaries from a
230
+ ModelRadianceProperties dictionary.
231
+ lib_folder: A folder from which abridged objects will be loaded.
232
+ """
233
+ obj_ids = set(obj['identifier'] for obj in model_prop_array)
234
+ for f in os.listdir(lib_folder):
235
+ f_path = os.path.join(lib_folder, f)
236
+ if os.path.isfile(f_path) and f_path.endswith('.json'):
237
+ with open(f_path) as json_file:
238
+ data = json.load(json_file)
239
+ if 'type' in data: # single object
240
+ if data['identifier'] not in obj_ids:
241
+ model_prop_array.append(data)
242
+ else: # a collection of several objects
243
+ for obj_identifier in data:
244
+ if obj_identifier not in obj_ids:
245
+ model_prop_array.append(data[obj_identifier])
246
+
247
+
248
+ @lib.command('purge')
249
+ @click.option(
250
+ '--standards-folder', '-s', default=None, help='A directory containing sub-folders '
251
+ 'of resource objects (modifiers, modifiersets) to be purged of files. If '
252
+ 'unspecified, the current user honeybee default standards folder will be used.',
253
+ type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True)
254
+ )
255
+ @click.option(
256
+ '--json-only/--all', ' /-a', help='Flag to note whether only JSON files should '
257
+ 'be purged from the library or all files should be purged, including RAD files. '
258
+ 'Given that all objects added to the library through the `add` command will always '
259
+ 'be JSON, only purging the JSONs is useful when one wishes to clear these objects '
260
+ 'while preserving objects that originated from other sources.',
261
+ default=True, show_default=True
262
+ )
263
+ @click.option(
264
+ '--backup/--no-backup', ' /-xb', help='Flag to note whether a backup .zip file '
265
+ 'of the user standards library should be made before the purging operation. '
266
+ 'This is done by default in case the user ever wants to recover their old '
267
+ 'standards but can be turned off if a backup is not desired.',
268
+ default=True, show_default=True
269
+ )
270
+ @click.option(
271
+ '--log-file', '-log', help='Optional file to output a log of the purging process. '
272
+ 'By default this will be printed out to stdout',
273
+ type=click.File('w'), default='-', show_default=True
274
+ )
275
+ def purge_lib(standards_folder, json_only, backup, log_file):
276
+ """Purge the library of all user radiance standards that it contains.
277
+
278
+ This is useful when a user's standard library has become filled with duplicated
279
+ objects or the user wishes to start fresh by re-exporting updated objects.
280
+ """
281
+ try:
282
+ # set the folder to the default standards_folder if unspecified
283
+ folder = standards_folder if standards_folder is not None else \
284
+ folders.standards_data_folder
285
+ resources = ('modifiers', 'modifiersets')
286
+ sub_folders = [os.path.join(folder, std) for std in resources]
287
+
288
+ # make a backup of the folder if requested
289
+ if backup:
290
+ r_names, s_files, s_paths = [], [], []
291
+ for sf, r_name in zip(sub_folders, resources):
292
+ for s_file in os.listdir(sf):
293
+ s_path = os.path.join(sf, s_file)
294
+ if os.path.isfile(s_path):
295
+ r_names.append(r_name)
296
+ s_files.append(s_file)
297
+ s_paths.append(s_path)
298
+ if len(s_paths) != 0: # there are resources to back up
299
+ backup_name = '.standards_backup_{}.zip'.format(
300
+ str(datetime.now()).split('.')[0].replace(':', '-'))
301
+ backup_file = os.path.join(os.path.dirname(folder), backup_name)
302
+ with zipfile.ZipFile(backup_file, 'w') as zf:
303
+ for r_name, s_file, s_path in zip(r_names, s_files, s_paths):
304
+ zf.write(s_path, os.path.join(r_name, s_file))
305
+
306
+ # loop through the sub-folders and delete the files
307
+ rel_files = []
308
+ for sf in sub_folders:
309
+ for s_file in os.listdir(sf):
310
+ s_path = os.path.join(sf, s_file)
311
+ if os.path.isfile(s_path):
312
+ if json_only:
313
+ if s_file.lower().endswith('.json'):
314
+ rel_files.append(s_path)
315
+ else:
316
+ rel_files.append(s_path)
317
+ purged_files, fail_files = [], []
318
+ for rf in rel_files:
319
+ try:
320
+ os.remove(rf)
321
+ purged_files.append(rf)
322
+ except Exception:
323
+ fail_files.append(rf)
324
+
325
+ # report all of the deleted files in the log file
326
+ if len(rel_files) == 0:
327
+ log_file.write('The standards folder is empty so no files were removed.')
328
+ if len(purged_files) != 0:
329
+ msg = 'The following files were removed in the purging ' \
330
+ 'operations:\n{}\n'.format(' \n'.join(purged_files))
331
+ log_file.write(msg)
332
+ if len(fail_files) != 0:
333
+ msg = 'The following files could not be removed in the purging ' \
334
+ 'operations:\n{}\n'.format(' \n'.join(fail_files))
335
+ log_file.write(msg)
336
+ except Exception as e:
337
+ _logger.exception('Purging user standards library failed.\n{}'.format(e))
338
+ sys.exit(1)
339
+ else:
340
+ sys.exit(0)
341
+
342
+
343
+ @lib.command('add')
344
+ @click.argument('properties-file', type=click.Path(
345
+ exists=True, file_okay=True, dir_okay=False, resolve_path=True))
346
+ @click.option(
347
+ '--standards-folder', '-s', default=None, help='A directory containing sub-folders '
348
+ 'of resource objects (modifiers, modifiersets) to which the properties-file objects '
349
+ 'will be added. If unspecified, the current user honeybee default standards folder '
350
+ 'will be used.', type=click.Path(
351
+ exists=True, file_okay=False, dir_okay=True, resolve_path=True)
352
+ )
353
+ @click.option(
354
+ '--log-file', '-log', help='Optional file to output a log of the purging process. '
355
+ 'By default this will be printed out to stdout',
356
+ type=click.File('w'), default='-', show_default=True
357
+ )
358
+ def add_to_lib(properties_file, standards_folder, log_file):
359
+ """Add an object or set of objects to the user's standard library.
360
+
361
+ \b
362
+ Args:
363
+ properties_file: A JSON file of a ModelRadianceProperties object containing
364
+ the objects to be written into the user standards library. All sub-objects
365
+ within this ModelRadianceProperties object must be Abridged if the sub-object
366
+ has an abridged schema and these abridged schemas are allowed to
367
+ reference either other objects in the ModelRadianceProperties or existing
368
+ objects within the standards library.
369
+ """
370
+ try:
371
+ # set the folder to the default standards_folder if unspecified
372
+ folder = standards_folder if standards_folder is not None else \
373
+ folders.standards_data_folder
374
+
375
+ # load up the model radiance properties from the JSON
376
+ with open(properties_file) as inf:
377
+ data = json.load(inf)
378
+ assert 'type' in data, 'Properties file lacks required type key.'
379
+ assert data['type'] == 'ModelRadianceProperties', 'Expected ' \
380
+ 'ModelRadianceProperties JSON object. Got {}.'.format(data['type'])
381
+ success_objects, dup_id_objects, mis_dep_objects = [], [], []
382
+
383
+ # extract, check, and write the modifiers
384
+ mods = {}
385
+ if 'modifiers' in data and data['modifiers'] is not None and \
386
+ len(data['modifiers']) != 0:
387
+ for mod_obj in data['modifiers']:
388
+ msg = _object_message('Modifier', mod_obj)
389
+ if mod_obj['identifier'] in MODIFIERS:
390
+ dup_id_objects.append(msg)
391
+ else:
392
+ try:
393
+ mods[mod_obj['identifier']] = dict_to_modifier(mod_obj)
394
+ success_objects.append(msg)
395
+ except (ValueError, KeyError, AssertionError):
396
+ mis_dep_objects.append(msg)
397
+ if mods:
398
+ mod_dict = {m.identifier: m.to_dict() for m in mods.values()}
399
+ mod_json = os.path.join(folder, 'modifiers', 'custom_modifiers.json')
400
+ _update_user_json(mod_dict, mod_json)
401
+
402
+ # extract, check, and write the modifier sets
403
+ mod_sets = {}
404
+ if 'modifier_sets' in data and data['modifier_sets'] is not None:
405
+ for ms in data['modifier_sets']:
406
+ msg = _object_message('Modifier Set', ms)
407
+ if ms['identifier'] in MODIFIER_SETS:
408
+ dup_id_objects.append(msg)
409
+ else:
410
+ try:
411
+ mod_sets[ms['identifier']] = \
412
+ lib_dict_abridged_to_modifier_set(ms, mods)
413
+ success_objects.append(msg)
414
+ except (ValueError, KeyError, AssertionError):
415
+ mis_dep_objects.append(msg)
416
+ if mod_sets:
417
+ ms_dict = {m.identifier: m.to_dict(abridged=True) for m in mod_sets.values()}
418
+ ms_json = os.path.join(folder, 'modifiersets', 'custom_sets.json')
419
+ _update_user_json(ms_dict, ms_json)
420
+
421
+ # write a report of the objects that were or were not added
422
+ success_objects, dup_id_objects, mis_dep_objects
423
+ m_start = 'THESE OBJECTS'
424
+ if len(success_objects) != 0:
425
+ msg = '{} WERE SUCCESSFULLY ADDED TO THE STANDARDS LIBRARY:\n{}\n\n'.format(
426
+ m_start, ' \n'.join(success_objects))
427
+ log_file.write(msg)
428
+ if len(dup_id_objects) != 0:
429
+ msg = '{} WERE NOT ADDED SINCE THEY ALREADY EXIST IN THE STANDARDS ' \
430
+ 'LIBRARY:\n{}\n\n'.format(m_start, ' \n'.join(dup_id_objects))
431
+ log_file.write(msg)
432
+ if len(mis_dep_objects) != 0:
433
+ msg = '{} WERE NOT ADDED BECAUSE THEY ARE INVALID OR ARE MISSING ' \
434
+ 'DEPENDENT OBJECTS:\n{}\n\n'.format(m_start, ' \n'.join(mis_dep_objects))
435
+ log_file.write(msg)
436
+ except Exception as e:
437
+ _logger.exception('Adding to user standards library failed.\n{}'.format(e))
438
+ sys.exit(1)
439
+ else:
440
+ sys.exit(0)
441
+
442
+
443
+ def _object_message(obj_type, obj_dict):
444
+ """Get the reporting message of an object to add to the user library."""
445
+ obj_name = obj_dict['display_name'] if 'display_name' in obj_dict and \
446
+ obj_dict['display_name'] is not None else obj_dict['identifier']
447
+ return '{}: {}'.format(obj_type, obj_name)
448
+
449
+
450
+ def _update_user_json(dict_to_add, user_json):
451
+ """Update a JSON file within a user standards folder."""
452
+ if os.path.isfile(user_json):
453
+ with open(user_json) as inf:
454
+ exist_data = json.load(inf)
455
+ dict_to_add.update(exist_data)
456
+ with open(user_json, 'w') as outf:
457
+ json.dump(dict_to_add, outf, indent=4)
458
+
@@ -0,0 +1,133 @@
1
+ """honeybee radiance modifier commands."""
2
+ import click
3
+ import sys
4
+ import os
5
+ import logging
6
+ import json
7
+
8
+ from honeybee_radiance.sensorgrid import SensorGrid
9
+
10
+
11
+ _logger = logging.getLogger(__name__)
12
+
13
+
14
+ @click.group(help='Commands for generating and modifying Radiance modifiers.')
15
+ def modifier():
16
+ pass
17
+
18
+
19
+ @modifier.command('split-modifiers')
20
+ @click.argument('modifier-file', type=click.Path(
21
+ exists=True, file_okay=True, dir_okay=False, resolve_path=True))
22
+ @click.argument(
23
+ 'output-folder',
24
+ type=click.Path(file_okay=False, dir_okay=True, resolve_path=True))
25
+ @click.option(
26
+ '--sensor-count', '-sc', help='The number of sensors in the sensor grid '
27
+ 'that will be used in rcontrib with the distributed modifiers.',
28
+ type=int, default=5000, show_default=True)
29
+ @click.option(
30
+ '--grid-file', '-gf', help='Full path to a sensor grid file. This file '
31
+ 'is used to count the number of sensors and will override the '
32
+ '--sensor-count option.',
33
+ type=click.Path(exists=True, file_okay=True, dir_okay=False, resolve_path=True),
34
+ default=None, show_default=True)
35
+ @click.option(
36
+ '--max-value', '-max', help='An optional integer to define the maximum value '
37
+ 'allowed when multiplying the number of sensors with the number of modifiers '
38
+ 'in the distributed modifiers. Default: 40000000.',
39
+ type=int, default=40000000, show_default=True)
40
+ @click.option(
41
+ '--sensor-multiplier', '-d', help='An optional integer to be multiplied by '
42
+ 'the grid count to yield a final number of the sensor count. This is useful '
43
+ 'in workflows where the sensor grids are modified such as when calculating '
44
+ 'view factor. Default: 1.', type=int, default=1)
45
+ def split_modifiers(
46
+ modifier_file, output_folder, sensor_count, grid_file, max_value,
47
+ sensor_multiplier
48
+ ):
49
+ """Split a list of modifiers into multiple files.
50
+
51
+ This command splits the modifiers based on the sensor count and the max value.
52
+ The max value is divided by the sensor count to calculate the maximum number
53
+ of modifiers in each distributed file of modifiers.
54
+
55
+ This command creates a new folder with evenly distributed modifiers. The folder
56
+ will include a ``_dist_info.json`` file which has the information to recreate the
57
+ original input files from this folder and the results generated based on the modifiers
58
+ in this folder.
59
+
60
+ ``_redist_info.json`` file includes an array of JSON objects. Each object has
61
+ the distribution information, which in comparison to the command to split grids
62
+ is much simpler.
63
+
64
+ \b
65
+ Args:
66
+ modifier_file: Full path to a file with Radiance modifiers. The modifiers
67
+ must be the identifiers of the modifiers and not the actual Radiance
68
+ description of the modifiers.
69
+ output_folder: A new folder to write the newly created files.
70
+ """
71
+ try:
72
+ if grid_file:
73
+ sg = SensorGrid.from_file(grid_file)
74
+ sensor_count = sg.count
75
+ sensor_count *= sensor_multiplier
76
+
77
+ with open(modifier_file, 'r') as file:
78
+ modifiers = [modifier.strip() for modifier in file.readlines()]
79
+
80
+ modifier_count = len(modifiers)
81
+
82
+ max_lines_per_file = int(max_value / sensor_count)
83
+ lines_per_file = max(1, max_lines_per_file)
84
+ dist_count = int(len(modifiers) / lines_per_file)
85
+ if len(modifiers) % lines_per_file != 0:
86
+ dist_count += 1
87
+
88
+ mod_per_dist = int(round(modifier_count / dist_count)) or 1
89
+
90
+ # create output folder
91
+ if not os.path.isdir(output_folder):
92
+ os.mkdir(output_folder)
93
+
94
+ remainder = modifier_count % dist_count # extra lines to distribute
95
+
96
+ start_index = 0
97
+ dist_info = []
98
+ out_dist_info = []
99
+ for i in range(dist_count):
100
+ end_index = start_index + mod_per_dist + (1 if i <= remainder else 0)
101
+
102
+ lines_to_write = modifiers[start_index:end_index]
103
+
104
+ dist_info.append(
105
+ {
106
+ 'dist_info': [{'identifier': i}]
107
+ }
108
+ )
109
+ out_dist_info.append(
110
+ {
111
+ 'identifier': str(i),
112
+ 'count': len(lines_to_write)
113
+ }
114
+ )
115
+ # create a file and write the lines
116
+ with open(os.path.join(output_folder, '%s.mod' % i), 'w') as f:
117
+ f.write('\n'.join(lines_to_write))
118
+
119
+ # update the start index for the next iteration
120
+ start_index = end_index
121
+
122
+ dist_info_file = os.path.join(output_folder, '_redist_info.json')
123
+ with open(dist_info_file, 'w') as dist_out_file:
124
+ json.dump(dist_info, dist_out_file, indent=2)
125
+
126
+ dist_info_file = os.path.join(output_folder, '_info.json')
127
+ with open(dist_info_file, 'w') as dist_out_file:
128
+ json.dump(out_dist_info, dist_out_file, indent=2)
129
+ except Exception:
130
+ _logger.exception('Failed to distribute sensor grids in folder.')
131
+ sys.exit(1)
132
+ else:
133
+ sys.exit(0)