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,640 @@
1
+ """honeybee radiance octree commands."""
2
+ import click
3
+ import sys
4
+ import logging
5
+ import os
6
+ import json
7
+
8
+ from honeybee_radiance_folder import ModelFolder
9
+ from honeybee_radiance_command.oconv import Oconv
10
+ from honeybee_radiance.config import folders
11
+
12
+ _logger = logging.getLogger(__name__)
13
+ OCTREE_RES = 32768 # resolution of the octree to use
14
+
15
+
16
+ @click.group(help='Commands to generate Radiance octree.')
17
+ def octree():
18
+ pass
19
+
20
+
21
+ @octree.command('from-folder')
22
+ @click.argument('folder', type=click.Path(exists=True, file_okay=False, dir_okay=True))
23
+ @click.option(
24
+ '--output', '-o', show_default=True, help='Path to output file (.oct). If a relative path'
25
+ ' is provided it should be relative to project folder.'
26
+ )
27
+ @click.option(
28
+ '--default/--black', ' /-b', default=True, show_default=True,
29
+ help='Flag to note whether the octree should be created completely with '
30
+ 'black materials.'
31
+ )
32
+ @click.option(
33
+ '--include-aperture/--exclude-aperture', ' /-xa', default=True,
34
+ show_default=True,
35
+ help='Flag to note whether static apertures should be included in the octree.'
36
+ )
37
+ @click.option(
38
+ '--black-groups/--exclude-groups', ' /-xg', default=True, show_default=True,
39
+ help='Flag to note whether dynamic aperture groups should blacked-out in '
40
+ 'the octree or they should simply be excluded, letting light pass through.'
41
+ )
42
+ @click.option(
43
+ '--first-shade-state/--exclude-shade-groups', ' /-xs', help='Flag to note whether '
44
+ 'dynamic shade groups should be included in the octree as the first shade state '
45
+ 'or they should simply be excluded.', default=True, show_default=True
46
+ )
47
+ @click.option(
48
+ '--include-ies/--exclude-ies', ' /-xi', default=True,
49
+ show_default=True,
50
+ help='Flag to note whether IES files should be included in the octree.'
51
+ )
52
+ @click.option(
53
+ '--add-before', type=click.STRING, multiple=True, default=None, show_default=True,
54
+ help='Path for a file to be added to octree before scene files.'
55
+ )
56
+ @click.option(
57
+ '--add-after', type=click.STRING, multiple=True, default=None, show_default=True,
58
+ help='Path for a file to be added to octree after scene files.'
59
+ )
60
+ @click.option(
61
+ '--dry-run', is_flag=True, default=False, show_default=True,
62
+ help='A flag to show the command without running it.'
63
+ )
64
+ def create_octree_from_folder(
65
+ folder, output, default, include_aperture, black_groups, first_shade_state,
66
+ include_ies, add_before, add_after, dry_run
67
+ ):
68
+ """Generate a static octree from a folder.
69
+
70
+ \b
71
+ Args:
72
+ folder: Path to a Radiance model folder.
73
+ """
74
+ model_folder = ModelFolder.from_model_folder(folder)
75
+ try:
76
+ black_out = False if default else True
77
+ scene_files = model_folder.scene_files(black_out=black_out)
78
+ if include_aperture: # no black out here
79
+ try:
80
+ aperture_files = model_folder.aperture_files()
81
+ scene_files += aperture_files
82
+ except Exception:
83
+ pass # no apertures available in the model
84
+ if black_groups:
85
+ try:
86
+ group_files = model_folder.aperture_group_files_black()
87
+ scene_files += group_files
88
+ except Exception:
89
+ pass # no aperture groups available in the model
90
+ if first_shade_state:
91
+ try:
92
+ dyn_folder = model_folder.dynamic_scene_folder(full=True)
93
+ dyn_shades = model_folder.dynamic_scene()
94
+ shd_g_files = [os.path.join(dyn_folder, grp.states[0].default)
95
+ for grp in dyn_shades]
96
+ scene_files += shd_g_files
97
+ except Exception:
98
+ pass # no shade groups available in the model
99
+ if include_ies:
100
+ try:
101
+ ies_folder = model_folder.ies_folder()
102
+ ies_files = [os.path.join(ies_folder, fp)
103
+ for fp in os.listdir(ies_folder)
104
+ if fp.endswith('rad')]
105
+ scene_files += ies_files
106
+ except Exception:
107
+ pass # no aperture groups available in the model
108
+ if add_after:
109
+ scene_files += list(add_after)
110
+ if add_before:
111
+ scene_files = list(add_before) + scene_files
112
+ cmd = Oconv(output=output, inputs=scene_files)
113
+ cmd.options.f = True
114
+ cmd.options.r = OCTREE_RES
115
+ if dry_run:
116
+ click.echo(cmd)
117
+ else:
118
+ env = None
119
+ if folders.env != {}:
120
+ env = folders.env
121
+ env = dict(os.environ, **env) if env else None
122
+ cmd.run(env=env, cwd=model_folder.folder)
123
+ except Exception:
124
+ _logger.exception('Failed to generate octree.')
125
+ sys.exit(1)
126
+ else:
127
+ sys.exit(0)
128
+
129
+
130
+ @octree.command('from-folder-multiphase')
131
+ @click.argument('folder', type=click.Path(exists=True, file_okay=False, dir_okay=True))
132
+ @click.option(
133
+ '--sun-path',
134
+ type=click.Path(exists=True, file_okay=True, dir_okay=False, resolve_path=True),
135
+ default=None, show_default=True,
136
+ help='Path for a sun-path file that will be added to octrees for direct sunlight '
137
+ 'studies. If sunpath is provided an extra octree for direct_sun will be created.'
138
+ )
139
+ @click.option(
140
+ '--phase', type=click.Choice(['2', '3', '5']), default='5', show_default=True,
141
+ help='Select a multiphase study for which octrees will be created. 3-phase includes '
142
+ '2-phase, and 5-phase includes 3-phase and 2-phase.'
143
+ )
144
+ @click.option("--output-folder", help="Output folder into which the files be written.",
145
+ default="octree", show_default=True)
146
+ def create_octree_from_folder_multiphase(folder, sun_path, phase, output_folder):
147
+ """Generate a set of octrees from a folder.
148
+
149
+ This command will generate octrees for both default and direct studies. It will do so
150
+ for static apertures and aperture groups, creating one octree for each light path,
151
+ i.e., all other light paths are blacked.
152
+
153
+ \b
154
+ Args:
155
+ folder: Path to a Radiance model folder.
156
+ """
157
+ model_folder = ModelFolder.from_model_folder(folder)
158
+
159
+ # check if sunpath file exist - otherwise continue without it
160
+ if sun_path and not os.path.isfile(sun_path):
161
+ sun_path = None
162
+
163
+ if phase == '5' and not sun_path:
164
+ raise RuntimeError(
165
+ 'To generate octrees for a 5 Phase study you must provide a sunpath.'
166
+ )
167
+
168
+ phases = {
169
+ '2': ['two_phase'],
170
+ '3': ['two_phase', 'three_phase'],
171
+ '5': ['two_phase', 'three_phase', 'five_phase']
172
+ }
173
+
174
+ try:
175
+ scene_mapping = model_folder.octree_scene_mapping()
176
+ if not os.path.isdir(output_folder):
177
+ os.mkdir(output_folder)
178
+ octree_mapping = []
179
+ for study, states in scene_mapping.items():
180
+ if study not in phases[phase]:
181
+ continue
182
+ study_type = []
183
+ for state in states:
184
+ info, commands = _generate_octrees_info(
185
+ state, output_folder, study, sun_path)
186
+ study_type.append(info)
187
+
188
+ for cmd in commands:
189
+ env = None
190
+ if folders.env != {}:
191
+ env = folders.env
192
+ env = dict(os.environ, **env) if env else None
193
+ cmd.run(env=env, cwd=model_folder.folder)
194
+
195
+ octree_mapping.append({study: study_type})
196
+ octree_output = os.path.join(
197
+ model_folder.folder, output_folder, '%s.json' % study
198
+ )
199
+ with open(octree_output, 'w') as fp:
200
+ json.dump(study_type, fp, indent=2)
201
+
202
+ octree_output = os.path.join(
203
+ model_folder.folder, output_folder, 'multi_phase.json'
204
+ )
205
+ with open(octree_output, 'w') as fp:
206
+ json.dump(octree_mapping, fp, indent=2)
207
+
208
+ except Exception:
209
+ _logger.exception('Failed to generate octrees.')
210
+ sys.exit(1)
211
+ else:
212
+ sys.exit(0)
213
+
214
+
215
+ @octree.command('from-abstracted-groups')
216
+ @click.argument('folder', type=click.Path(exists=True, file_okay=False, dir_okay=True))
217
+ @click.option(
218
+ '--sun-path', '-sp',
219
+ type=click.Path(exists=True, file_okay=True, dir_okay=False, resolve_path=True),
220
+ default=None, show_default=True,
221
+ help='Path for a sun-path file that will be added to octrees for direct sunlight '
222
+ 'studies. If sunpath is provided an extra octree for direct_sun will be created.'
223
+ )
224
+ @click.option('--output-folder', help='Output folder relative to the model folder into '
225
+ 'which the files be written.', default='octree', show_default=True)
226
+ def create_octree_from_abstracted_groups(folder, sun_path, output_folder):
227
+ """Generate a set of octrees from a folder containing abstracted aperture groups.
228
+
229
+ This command assumes that each aperture group in the radiance folder contains
230
+ only two states. The first is a 100 percent transmittance specular (or beam)
231
+ representation of the aperture group and the second is a 100 percent transmittance
232
+ diffuse representation of the aperture group. This abstracted representation is
233
+ intended to simulate contributions of dynamic groups when an external source
234
+ is able to provide specular and diffuse transmittances at each timestep.
235
+ For example, EnergyPlus can provide such values, which together can account
236
+ for several types of complex dynamic fenestration types.
237
+
238
+ Each aperture group will get at least two octrees (one with specular and one
239
+ with diffuse). If a sun-path is provided, a third octree will be added with
240
+ the suns included.
241
+
242
+ \b
243
+ Args:
244
+ folder: Path to a Radiance model folder.
245
+ """
246
+ model_folder = ModelFolder.from_model_folder(folder)
247
+ try:
248
+ # first, check to see if there are any aperture groups in the model
249
+ output_folder = os.path.join(model_folder.folder, output_folder)
250
+ if not os.path.isdir(output_folder):
251
+ os.mkdir(output_folder)
252
+ group_info_file = os.path.join(output_folder, 'group_info.json')
253
+ group_info, groups_exist = [], True
254
+ try:
255
+ grp_folder = model_folder.aperture_group_folder()
256
+ ap_groups = model_folder.aperture_groups()
257
+ if len(ap_groups) == 0:
258
+ groups_exist = False
259
+ except Exception:
260
+ groups_exist = False
261
+
262
+ if groups_exist:
263
+ # get the static scene files with blacked-out apertures
264
+ scene_files = model_folder.scene_files(black_out=False)
265
+ try:
266
+ aperture_files = model_folder.aperture_files(black_out=True)
267
+ scene_files += aperture_files
268
+ except Exception:
269
+ pass # no apertures available in the model
270
+
271
+ # get the environment variables
272
+ env = None
273
+ if folders.env != {}:
274
+ env = folders.env
275
+ env = dict(os.environ, **env) if env else None
276
+
277
+ # loop through the aperture groups and create the octrees
278
+ for i, a_grp in enumerate(ap_groups):
279
+ # create a sub-folder and get black versions of all other aperture groups
280
+ sub_folder = os.path.join(output_folder, a_grp.identifier)
281
+ if not os.path.isdir(sub_folder):
282
+ os.mkdir(sub_folder)
283
+ blk_grp = [_model_rel(grp_folder, ag.states[0].black)
284
+ for j, ag in enumerate(ap_groups) if j != i]
285
+ blk_grp = [bg.replace('\\', '/') for bg in blk_grp]
286
+ grp_scene_files = scene_files + blk_grp
287
+ # command for the octree for specular transmittance
288
+ spec_file = os.path.join(sub_folder, 'spec.oct')
289
+ spec_scene_files = grp_scene_files + \
290
+ [_model_rel(grp_folder, a_grp.states[0].default)]
291
+ cmd_s = Oconv(output=spec_file, inputs=spec_scene_files)
292
+ # command for the octree for diffuse transmittance
293
+ diff_file = os.path.join(sub_folder, 'diff.oct')
294
+ diff_scene_files = grp_scene_files + \
295
+ [_model_rel(grp_folder, a_grp.states[1].default)]
296
+ cmd_d = Oconv(output=diff_file, inputs=diff_scene_files)
297
+ cmds = [cmd_s, cmd_d]
298
+ # add info about the generated files
299
+ grp_info_dict = {
300
+ 'identifier': a_grp.identifier,
301
+ 'spec': os.path.basename(spec_file),
302
+ 'diff': os.path.basename(diff_file)
303
+ }
304
+ # command for the octree with suns
305
+ if sun_path and os.path.isfile(sun_path):
306
+ spec_sun_file = os.path.join(sub_folder, 'spec_sun.oct')
307
+ spec_sun_scene_files = [sun_path] + spec_scene_files
308
+ cmd_ss = Oconv(output=spec_sun_file, inputs=spec_sun_scene_files)
309
+ cmds.append(cmd_ss)
310
+ grp_info_dict['sun'] = os.path.basename(spec_sun_file)
311
+ # run all of the commands to create the octrees
312
+ for cmd in cmds:
313
+ cmd.options.f = True
314
+ cmd.options.r = OCTREE_RES
315
+ cmd.run(env=env, cwd=model_folder.folder)
316
+ group_info.append(grp_info_dict)
317
+
318
+ # write out a JSON with information about the octrees and groups
319
+ with open(group_info_file, 'w') as fp:
320
+ json.dump(group_info, fp, indent=2)
321
+ except Exception:
322
+ _logger.exception('Failed to generate abstracted group octrees.')
323
+ sys.exit(1)
324
+ else:
325
+ sys.exit(0)
326
+
327
+
328
+ @octree.command('from-shade-trans-groups')
329
+ @click.argument('folder', type=click.Path(exists=True, file_okay=False, dir_okay=True))
330
+ @click.option(
331
+ '--sun-path', '-sp',
332
+ type=click.Path(exists=True, file_okay=True, dir_okay=False, resolve_path=True),
333
+ default=None, show_default=True,
334
+ help='Path for a sun-path file that will be added to octrees for direct sunlight '
335
+ 'studies. If sunpath is provided an extra octree for direct_sun will be created.'
336
+ )
337
+ @click.option('--output-folder', help='Output folder relative to the model folder into '
338
+ 'which the files be written.', default='octree', show_default=True)
339
+ def create_octree_from_shade_trans_groups(folder, sun_path, output_folder):
340
+ """Generate a set of octrees from a folder containing shade transmittance groups.
341
+
342
+ This command assumes that each shade group in the radiance folder contains
343
+ only two states. The first is a completely opaque representation of the shade
344
+ group and the second is a completely transparent representation of the shade
345
+ group. This abstracted representation is intended to simulate dynamic shade
346
+ transmittance that changes at each timestep.
347
+
348
+ Each shade group will get an octree. If a sun-path is provided, an additional
349
+ octree will be added for each group.
350
+
351
+ \b
352
+ Args:
353
+ folder: Path to a Radiance model folder.
354
+ """
355
+ model_folder = ModelFolder.from_model_folder(folder)
356
+ try:
357
+ # first, check to see if there are any shade groups in the model
358
+ output_folder = os.path.join(model_folder.folder, output_folder)
359
+ if not os.path.isdir(output_folder):
360
+ os.mkdir(output_folder)
361
+ group_info_file = os.path.join(output_folder, 'trans_info.json')
362
+ group_info, groups_exist = [], True
363
+ try:
364
+ grp_folder = model_folder.dynamic_scene_folder()
365
+ shd_groups = model_folder.dynamic_scene()
366
+ if len(shd_groups) == 0:
367
+ groups_exist = False
368
+ except Exception:
369
+ groups_exist = False
370
+
371
+ if groups_exist:
372
+ # get the static scene files
373
+ scene_files = model_folder.scene_files(black_out=False)
374
+ try:
375
+ aperture_files = model_folder.aperture_files(black_out=False)
376
+ scene_files += aperture_files
377
+ except Exception:
378
+ pass # no apertures available in the model
379
+
380
+ # get the environment variables
381
+ env = None
382
+ if folders.env != {}:
383
+ env = folders.env
384
+ env = dict(os.environ, **env) if env else None
385
+
386
+ # loop through the shade groups and create the octrees
387
+ for i, s_grp in enumerate(shd_groups):
388
+ # gather files to represent the transparent shade group
389
+ dyn_grp = [_model_rel(grp_folder, sg.states[0].default)
390
+ for j, sg in enumerate(shd_groups) if j != i]
391
+ dyn_grp = [sg.replace('\\', '/') for sg in dyn_grp]
392
+ grp_scene_files = scene_files + dyn_grp
393
+ oct_file = os.path.join(output_folder, '{}.oct'.format(s_grp.identifier))
394
+ # command for the octree
395
+ cmds = [Oconv(output=oct_file, inputs=grp_scene_files)]
396
+ # add info about the generated files
397
+ grp_info_dict = {
398
+ 'identifier': s_grp.identifier,
399
+ 'default': os.path.basename(oct_file)
400
+ }
401
+ # command for the octree with suns
402
+ if sun_path and os.path.isfile(sun_path):
403
+ sun_file = os.path.join(
404
+ output_folder, '{}_sun.oct'.format(s_grp.identifier))
405
+ sun_scene_files = [sun_path] + grp_scene_files
406
+ cmd_ss = Oconv(output=sun_file, inputs=sun_scene_files)
407
+ cmds.append(cmd_ss)
408
+ grp_info_dict['sun'] = os.path.basename(sun_file)
409
+ # run all of the commands to create the octrees
410
+ for cmd in cmds:
411
+ cmd.options.f = True
412
+ cmd.options.r = OCTREE_RES
413
+ cmd.run(env=env, cwd=model_folder.folder)
414
+ group_info.append(grp_info_dict)
415
+
416
+ # write out a JSON with information about the octrees and groups
417
+ with open(group_info_file, 'w') as fp:
418
+ json.dump(group_info, fp, indent=2)
419
+ except Exception:
420
+ _logger.exception('Failed to generate shade trans group octrees.')
421
+ sys.exit(1)
422
+ else:
423
+ sys.exit(0)
424
+
425
+
426
+ def _model_rel(folder, rel_file):
427
+ """Get a file path relative to a model folder."""
428
+ return os.path.join(folder, os.path.normpath(rel_file)).replace('\\', '/')
429
+
430
+
431
+ def _generate_octrees_info(state, output_folder='octree', study='two_phase',
432
+ sun_path=None):
433
+ """Get octree information for default, direct, and direct sun. The
434
+ functions also generates the Radiance commands (oconv) for creating the
435
+ octrees.
436
+
437
+ Example of valid argument 'state':
438
+ {
439
+ 'light_path': '__static_apertures__',
440
+ 'identifier': '__static_apertures__',
441
+ 'scene_files': ['model/scene/envelope.mat', 'model/scene/envelope.rad'],
442
+ 'scene_files_direct': ['model/scene/envelope.mat', 'model/scene/envelope.rad'],
443
+ 'scene_files_direct': ['model/scene/envelope.mat', 'model/scene/envelope.rad'],
444
+ }
445
+
446
+ Args:
447
+ state: A state as a dictionary with information about which files to include in
448
+ each octree.
449
+ output_folder: Folder name to where the octrees will we generated.
450
+ study: A string of the study. There are either 'two_phase', 'three_phase', or
451
+ 'five_phase'.
452
+ sun_path: Path for a sun-path file that will be added to octrees for direct
453
+ sunlight studies. If sunpath is provided an extra octree for direct_sun
454
+ will be created.
455
+
456
+ Returns:
457
+ Two elements:
458
+ - octree information as dictionary
459
+ - oconv commands as a list
460
+ """
461
+ commands = []
462
+ info = {
463
+ 'identifier': state['identifier'],
464
+ 'light_path': state['light_path']}
465
+
466
+ # default
467
+ if 'scene_files' in state:
468
+ scene_files = state['scene_files']
469
+ if len(' '.join(scene_files)) > 8000:
470
+ if not os.path.isdir('scene'):
471
+ os.mkdir('scene')
472
+ scene_file = os.path.join('scene', state['identifier'] + '.rad')
473
+ scene_description = []
474
+ for sf in scene_files:
475
+ with open(sf, 'r') as sf:
476
+ scene_description.append(sf.read())
477
+ with open(scene_file, 'w') as sf:
478
+ sf.write('\n'.join(scene_description))
479
+ scene_files = [scene_file]
480
+ octree_name = state['identifier']
481
+ output = os.path.join(
482
+ output_folder, '%s.oct' % octree_name)
483
+ cmd = Oconv(output=output, inputs=scene_files)
484
+ cmd.options.f = True
485
+ cmd.options.r = OCTREE_RES
486
+ commands.append(cmd)
487
+
488
+ info['octree'] = '%s.oct' % octree_name
489
+
490
+ # direct - don't add them for 5 phase
491
+ if 'scene_files_direct' in state and study != 'five_phase':
492
+ scene_files_direct = state['scene_files_direct']
493
+ if len(' '.join(scene_files_direct)) > 8000:
494
+ if not os.path.isdir('scene'):
495
+ os.mkdir('scene')
496
+ scene_file = os.path.join('scene', '_'.join([state['identifier'], 'direct']) + '.rad')
497
+ scene_description = []
498
+ for sf in scene_files_direct:
499
+ with open(sf, 'r') as sf:
500
+ scene_description.append(sf.read())
501
+ with open(scene_file, 'w') as sf:
502
+ sf.write('\n'.join(scene_description))
503
+ scene_files_direct = [scene_file]
504
+ octree_direct_name = '%s_direct' % state['identifier']
505
+ output_direct = os.path.join(
506
+ output_folder, '%s.oct' % octree_direct_name)
507
+ cmd = Oconv(output=output_direct,
508
+ inputs=scene_files_direct)
509
+ cmd.options.f = True
510
+ cmd.options.r = OCTREE_RES
511
+ commands.append(cmd)
512
+
513
+ info['octree_direct'] = '%s.oct' % octree_direct_name
514
+
515
+ # direct sun - don't add them for 3-phase
516
+ if sun_path and study != 'three_phase':
517
+ scene_files_direct = state['scene_files_direct']
518
+ if len(' '.join(scene_files_direct)) > 8000:
519
+ if not os.path.isdir('scene'):
520
+ os.mkdir('scene')
521
+ scene_file = os.path.join('scene', '_'.join([state['identifier'], 'direct']) + '.rad')
522
+ scene_description = []
523
+ for sf in scene_files_direct:
524
+ with open(sf, 'r') as sf:
525
+ scene_description.append(sf.read())
526
+ with open(scene_file, 'w') as sf:
527
+ sf.write('\n'.join(scene_description))
528
+ scene_files_direct = [scene_file]
529
+ scene_files_direct_sun = [sun_path] + scene_files_direct
530
+ octree_direct_sun_name = '%s_direct_sun' % state['identifier']
531
+ output_direct = \
532
+ os.path.join(output_folder, '%s.oct' %
533
+ octree_direct_sun_name)
534
+ cmd = Oconv(output=output_direct,
535
+ inputs=scene_files_direct_sun)
536
+ cmd.options.f = True
537
+ cmd.options.r = OCTREE_RES
538
+
539
+ commands.append(cmd)
540
+
541
+ info['octree_direct_sun'] = '%s.oct' % octree_direct_sun_name
542
+
543
+ return info, commands
544
+
545
+
546
+ @octree.command('from-folder-static')
547
+ @click.argument('folder', type=click.Path(exists=True, file_okay=False, dir_okay=True))
548
+ @click.option(
549
+ '--output', '-o', show_default=True, help='Path to output file (.oct). If a relative path'
550
+ ' is provided it should be relative to project folder.'
551
+ )
552
+ @click.option(
553
+ '--add-before', type=click.STRING, multiple=True, default=None, show_default=True,
554
+ help='Path for a file to be added to octree before scene files.'
555
+ )
556
+ @click.option(
557
+ '--add-after', type=click.STRING, multiple=True, default=None, show_default=True,
558
+ help='Path for a file to be added to octree after scene files.'
559
+ )
560
+ @click.option(
561
+ '--dry-run', is_flag=True, default=False, show_default=True,
562
+ help='A flag to show the command without running it.'
563
+ )
564
+ def create_static_octree_from_folder(
565
+ folder, output, add_before, add_after, dry_run
566
+ ):
567
+ """Generate a static octree from a folder.
568
+
569
+ The octree will include the default state of aperture and shade groups if
570
+ any of those are in the model folder.
571
+
572
+ \b
573
+ Args:
574
+ folder: Path to a Radiance model folder.
575
+ """
576
+ model_folder = ModelFolder.from_model_folder(folder)
577
+
578
+ try:
579
+ scene_files = model_folder.scene_files()
580
+ try:
581
+ aperture_files = model_folder.aperture_files()
582
+ scene_files += aperture_files
583
+ except Exception:
584
+ pass # no apertures available in the model
585
+ try:
586
+ ap_group_folder = model_folder.aperture_group_folder(full=True)
587
+ aperture_groups = model_folder.aperture_groups()
588
+ ap_g_files = [
589
+ os.path.relpath(
590
+ os.path.join(ap_group_folder, grp.states[0].default),
591
+ model_folder.folder)
592
+ for grp in aperture_groups
593
+ ]
594
+ if len(' '.join(ap_g_files)) > 8000:
595
+ if not os.path.isdir('scene'):
596
+ os.mkdir('scene')
597
+ scene_file = os.path.join('scene', 'aperture_groups' + '.rad')
598
+ scene_description = []
599
+ for sf in ap_g_files:
600
+ with open(sf, 'r') as sf:
601
+ scene_description.append(sf.read())
602
+ with open(scene_file, 'w') as sf:
603
+ sf.write('\n'.join(scene_description))
604
+ scene_files += [scene_file]
605
+ else:
606
+ scene_files += ap_g_files
607
+ except Exception:
608
+ pass # no aperture groups available in the model
609
+ try:
610
+ dyn_folder = model_folder.dynamic_scene_folder(full=True)
611
+ dyn_shades = model_folder.dynamic_scene()
612
+ shd_g_files = [
613
+ os.path.relpath(
614
+ os.path.join(dyn_folder, grp.states[0].default),
615
+ model_folder.folder)
616
+ for grp in dyn_shades
617
+ ]
618
+ scene_files += shd_g_files
619
+ except Exception:
620
+ pass # no shade groups available in the model
621
+ if add_after:
622
+ scene_files += list(add_after)
623
+ if add_before:
624
+ scene_files = list(add_before) + scene_files
625
+ cmd = Oconv(output=output, inputs=scene_files)
626
+ cmd.options.f = True
627
+ cmd.options.r = OCTREE_RES
628
+ if dry_run:
629
+ click.echo(cmd)
630
+ else:
631
+ env = None
632
+ if folders.env != {}:
633
+ env = folders.env
634
+ env = dict(os.environ, **env) if env else None
635
+ cmd.run(env=env, cwd=model_folder.folder)
636
+ except Exception:
637
+ _logger.exception('Failed to generate octree.')
638
+ sys.exit(1)
639
+ else:
640
+ sys.exit(0)