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,479 @@
1
+ # coding=utf-8
2
+ """Object representing a group of sub-faces or shades that change states in unison."""
3
+ from __future__ import division
4
+
5
+ from .state import RadianceShadeState, RadianceSubFaceState
6
+ from ..geometry import Polygon
7
+ from ..modifier.material import BSDF
8
+ from ..lib.modifiers import white_glow
9
+
10
+ from honeybee.typing import valid_rad_string
11
+ from honeybee.shade import Shade
12
+ from honeybee.aperture import Aperture
13
+ from honeybee.door import Door
14
+ from honeybee.boundarycondition import Surface
15
+
16
+ from honeybee_radiance_command.options.rfluxmtx import RfluxmtxControlParameters
17
+
18
+ import os
19
+
20
+
21
+ class DynamicShadeGroup(object):
22
+ """Object representing a group of dynamic shades that change states in unison.
23
+
24
+ Note that this object is not intended to set any Model properties. It is only
25
+ used by the Model to collect objects with the same dynamic_group_identifier
26
+ and export a common set of radiance files from them.
27
+
28
+ Args:
29
+ identifier: Text string for a unique Group ID. Must not contain spaces
30
+ or special characters. This will be used to identify the object across
31
+ a model and in the exported Radiance files.
32
+ dynamic_objects: An array of Shades that are contained within the group.
33
+
34
+ Properties:
35
+ * identifier
36
+ * dynamic_objects
37
+ * state_count
38
+ * is_opaque
39
+ * is_indoor
40
+ * states_json_list
41
+ """
42
+
43
+ __slots__ = ('_identifier', '_dynamic_objects', '_state_count')
44
+
45
+ def __init__(self, identifier, dynamic_objects):
46
+ """Initialize DynamicShadeGroup."""
47
+ # set identifier
48
+ self._identifier = valid_rad_string(identifier)
49
+
50
+ # set the dynamic_objects
51
+ if not isinstance(dynamic_objects, tuple):
52
+ dynamic_objects = tuple(dynamic_objects)
53
+ self._check_dynamic_objects(dynamic_objects)
54
+ self._dynamic_objects = dynamic_objects
55
+
56
+ # compute the state_count
57
+ count = 1
58
+ for obj in self.dynamic_objects:
59
+ s_cnt = len(obj.properties.radiance._states)
60
+ if s_cnt > count:
61
+ count = s_cnt
62
+ self._state_count = count
63
+
64
+ @property
65
+ def identifier(self):
66
+ """Get a text string for the unique group identifier."""
67
+ return self._identifier
68
+
69
+ @property
70
+ def dynamic_objects(self):
71
+ """Get a tuple of objects contained within the group."""
72
+ return self._dynamic_objects
73
+
74
+ @property
75
+ def state_count(self):
76
+ """Gat an integer for the total number of states of the dynamic group.
77
+
78
+ This is equal to the number of states in the dynamic_object with the
79
+ highest number of states. After a dynamic_object with fewer states than that
80
+ of it's dynamic group has hit its highest state, it just remains in that
81
+ state as the other dynamic_objects continue to change.
82
+ """
83
+ return self._state_count
84
+
85
+ @property
86
+ def is_opaque(self):
87
+ """Get a boolean for whether all states of all shades in the group are opaque.
88
+ """
89
+ for obj in self._dynamic_objects:
90
+ for state in obj.properties.radiance._states:
91
+ if not state.modifier.is_opaque:
92
+ return False
93
+ return True
94
+
95
+ @property
96
+ def is_indoor(self):
97
+ """Get a boolean for whether all shades in the group are indoor."""
98
+ for obj in self._dynamic_objects:
99
+ if not obj.is_indoor:
100
+ return False
101
+ return True
102
+
103
+ @property
104
+ def states_json_list(self):
105
+ """A list for the states.json file that notes the files for each state.
106
+
107
+ The files will follow a standard naming convention as follows.
108
+ <dynamic group identifier>..<field name>..<state count>.rad.
109
+ For instance skylight..direct..000.rad
110
+ """
111
+ ident = self.identifier
112
+ states_list = []
113
+ for st_i in range(self.state_count):
114
+ states_list.append({
115
+ 'identifier': '{}_{}'.format(st_i, ident),
116
+ 'default': './{}..default..{}.rad'.format(ident, str(st_i)),
117
+ 'direct': './{}..direct..{}.rad'.format(ident, str(st_i))
118
+ })
119
+ return states_list
120
+
121
+ def states_by_index(self, state_index):
122
+ """Get an array of state objects representing a single state for this group.
123
+
124
+ The resulting array will parallel the dynamic_objects of this group, with
125
+ one state object per dynamic object.
126
+
127
+ Args:
128
+ state_index: An integer from 0 up to the state_count - 1 , which notes
129
+ the state of the group for which a rad string will be produced.
130
+ """
131
+ # make sure that the state_index is valid
132
+ assert 0 <= state_index < self.state_count, '"{}" is not a valid state index ' \
133
+ 'for dynamic group "{}".'.format(state_index, self.identifier)
134
+
135
+ # gather the state objects that correspond to the state_index
136
+ states = []
137
+ for obj in self._dynamic_objects:
138
+ try: # try to get the state at the correct index
139
+ states.append(obj.properties.radiance._states[state_index])
140
+ except IndexError: # use the last state that is available
141
+ try:
142
+ states.append(obj.properties.radiance._states[-1])
143
+ except IndexError: # no states assigned; create default a dummy state
144
+ st = RadianceShadeState()
145
+ st._parent = obj
146
+ states.append(st)
147
+ return states
148
+
149
+ def to_radiance(self, state_index, direct=False, minimal=False):
150
+ """Generate a RAD string representation of a state for this group.
151
+
152
+ The resulting string includes everything going into a single .rad file
153
+ to simulate the state, including all geometry and modifiers.
154
+
155
+ Args:
156
+ state_index: An integer from 0 up to the state_count - 1 , which notes
157
+ the state of the group for which a rad string will be produced.
158
+ direct: Boolean to note whether to write the "direct" version of the
159
+ state. (Default: False)
160
+ minimal: Boolean to note whether the radiance string should be written
161
+ in a minimal format (with spaces instead of line breaks). Default: False.
162
+ """
163
+ states = self.states_by_index(state_index)
164
+
165
+ # gather all unique modifiers across the geometry of the state
166
+ modifiers = []
167
+ if not direct:
168
+ for state in states:
169
+ mod = state.modifier
170
+ if not self._instance_in_array(mod, modifiers):
171
+ modifiers.append(mod)
172
+ for shd in state._shades:
173
+ mod = shd.modifier
174
+ if not self._instance_in_array(mod, modifiers):
175
+ modifiers.append(mod)
176
+ else: # use modifier_direct
177
+ for state in states:
178
+ mod = state.modifier_direct
179
+ if not self._instance_in_array(mod, modifiers):
180
+ modifiers.append(mod)
181
+ for shd in state._shades:
182
+ mod = shd.modifier_direct
183
+ if not self._instance_in_array(mod, modifiers):
184
+ modifiers.append(mod)
185
+ modifiers = list(set(modifiers))
186
+
187
+ # get rad strings for all modifier and geometry.
188
+ state_str = ['# STATE {} for "{}"'.format(state_index, self.identifier)]
189
+ for mod in modifiers:
190
+ if isinstance(mod, BSDF):
191
+ self._process_bsdf_modifier(mod, state_str, minimal)
192
+ else:
193
+ state_str.append(mod.to_radiance(minimal))
194
+ for state in states:
195
+ state_str.append(state.to_radiance(direct, minimal))
196
+ return '\n\n'.join(state_str)
197
+
198
+ @staticmethod
199
+ def _check_dynamic_objects(dynamic_objects):
200
+ for obj in dynamic_objects:
201
+ assert isinstance(obj, Shade), \
202
+ 'Expected Shade for DynamicShadeGroup. Got {}.'.format(type(obj))
203
+
204
+ @staticmethod
205
+ def _process_bsdf_modifier(modifier, mod_strs, minimal):
206
+ """Process a BSDF modifier for a radiance model folder."""
207
+ bsdf_name = os.path.split(modifier.bsdf_file)[-1]
208
+ mod_dup = modifier.duplicate() # duplicate to avoid editing the original
209
+ # the hidden _bsdf_file property is edited since the file has not yet been copied
210
+ mod_dup._bsdf_file = os.path.join('model', 'bsdf', bsdf_name)
211
+ mod_strs.insert(1, mod_dup.to_radiance(minimal))
212
+
213
+ @staticmethod
214
+ def _instance_in_array(object_instance, object_array):
215
+ """Check if a specific object instance is already in an array.
216
+
217
+ This can be much faster than `if object_instance in object_array`
218
+ when you expect to be testing a lot of the same instance of an object for
219
+ inclusion in an array since the builtin method uses an == operator to
220
+ test inclusion.
221
+ """
222
+ for val in object_array:
223
+ if val is object_instance:
224
+ return True
225
+ return False
226
+
227
+ def __repr__(self):
228
+ return 'Dynamic Shade Group: {}'.format(self.identifier)
229
+
230
+
231
+ class DynamicSubFaceGroup(DynamicShadeGroup):
232
+ """A group of dynamic Apertures and Doors that change states in unison.
233
+
234
+ Note that this object is not intended to set any Model properties. It is only
235
+ used by the Model to collect objects with the same dynamic_group_identifier
236
+ and export a common set of radiance files from them.
237
+
238
+ Args:
239
+ identifier: Text string for a unique Group ID. Must not contain spaces
240
+ or special characters. This will be used to identify the object across
241
+ a model and in the exported Radiance files.
242
+ dynamic_objects: An array of Shades that are contained within the group.
243
+
244
+ Properties:
245
+ * identifier
246
+ * dynamic_objects
247
+ * state_count
248
+ * is_opaque
249
+ * is_indoor
250
+ * states_json_list
251
+
252
+ """
253
+ __slots__ = ()
254
+
255
+ def __init__(self, modifier, dynamic_objects):
256
+ """Initialize DynamicSubFaceGroup."""
257
+ DynamicShadeGroup.__init__(self, modifier, dynamic_objects)
258
+
259
+ @property
260
+ def is_opaque(self):
261
+ """Always False for dynamic sub-faces."""
262
+ return False
263
+
264
+ @property
265
+ def is_indoor(self):
266
+ """Get a boolean for whether all sub-faces in the group have a Surface BC."""
267
+ for obj in self._dynamic_objects:
268
+ if not isinstance(obj.boundary_condition, Surface):
269
+ return False
270
+ return True
271
+
272
+ @property
273
+ def states_json_list(self):
274
+ """A list for the states.json file that notes the files for each state.
275
+
276
+ The files will follow a standard naming convention as follows.
277
+ <dynamic group identifier>..<field name>..<state count>.rad.
278
+ For instance skylight..direct..000.rad
279
+
280
+ Note that this list does not contain the tmtx, vmtx, or dmtx keys
281
+ and these should be added separately if this states.json is to be used for
282
+ 3-phase studies.
283
+ """
284
+ ident = self.identifier
285
+ states_list = []
286
+ for st_i in range(self.state_count):
287
+ states_list.append({
288
+ 'identifier': '{}_{}'.format(st_i, ident),
289
+ 'default': './{}..default..{}.rad'.format(ident, str(st_i)),
290
+ 'direct': './{}..direct..{}.rad'.format(ident, str(st_i)),
291
+ 'black': './{}..black.rad'.format(ident)
292
+ })
293
+ return states_list
294
+
295
+ def rfluxmtx_control_params(self, state_index=0, sampling=None, up_direction=None):
296
+ """Create a formatted Rfluxmtx control parameters string.
297
+
298
+ The optional values are sampling and up_direction. If these values are not
299
+ provided they will be assigned based on the first State of the dynamic object.
300
+
301
+ The control params are only meaningful for BSDF modifiers with one of the
302
+ sampling types listed below.
303
+
304
+ Args:
305
+ sampling: Set hemisphere sampling type. Acceptable inputs for hemisphere
306
+ sampling type are:
307
+
308
+ * u for uniform.(Usually applicable for ground).
309
+ * kf for klems full.
310
+ * kh for klems half.
311
+ * kq for klems quarter.
312
+ * rN for Reinhart - Tregenza type skies. N stands for subdivisions
313
+ and defaults to 1.
314
+ * scN for shirley-chiu subdivisions.
315
+
316
+ Add a ``-`` in front of the input for left-handed coordinates. For more
317
+ information see rfluxmtx docs.
318
+ https://www.radiance-online.org/learning/documentation/manual-pages/pdfs/rfluxmtx.pdf/at_download/file
319
+ up_direction: Orient the "up" direction for the hemisphere using the
320
+ indicated axis or a direction vector. Valid inputs include the
321
+ following. [-]{X|Y|Z|ux,uy,uz}. (Default: Y).
322
+
323
+ Returns:
324
+ RfluxmtxControlParameters
325
+
326
+ """
327
+ state = self.states_by_index(state_index)[0]
328
+ if up_direction is None:
329
+ up_direction = state.vmtx_geometry.plane.y
330
+ if sampling is None:
331
+ sampling = state.modifier.sampling_type
332
+ if sampling is None:
333
+ raise ValueError(
334
+ 'Rfluxmtx control parameters can only be generated for states '
335
+ 'with BSDF modifier. Current modifier: %s is a %s.' % (
336
+ state.modifier, self.modifier.__class__.__name__)
337
+ )
338
+ up_direction = tuple(up_direction)
339
+ return RfluxmtxControlParameters(sampling, up_direction)
340
+
341
+ def states_by_index(self, state_index):
342
+ """Get an array of state objects representing a single state for this group.
343
+
344
+ The resulting array will parallel the dynamic_objects of this group, with
345
+ one state object per dynamic object.
346
+
347
+ Args:
348
+ state_index: An integer from 0 up to the state_count - 1 , which notes
349
+ the state of the group for which a rad string will be produced.
350
+ """
351
+ # make sure that the state_index is valid
352
+ assert 0 <= state_index < self.state_count, '"{}" is not a valid state index ' \
353
+ 'for dynamic group "{}".'.format(state_index, self.identifier)
354
+
355
+ # gather the state objects that correspond to the state_index
356
+ states = []
357
+ for obj in self._dynamic_objects:
358
+ try: # try to get the state at the correct index
359
+ states.append(obj.properties.radiance._states[state_index])
360
+ except IndexError: # use the last state that is available
361
+ try:
362
+ states.append(obj.properties.radiance._states[-1])
363
+ except IndexError: # no states assigned; create default a dummy state
364
+ st = RadianceSubFaceState()
365
+ st._parent = obj
366
+ states.append(st)
367
+ return states
368
+
369
+ def blk_to_radiance(self, minimal=False):
370
+ """Generate a RAD string for the black representation of this group.
371
+
372
+ The resulting string includes everything going into the black .rad file,
373
+ including all geometry and modifiers.
374
+
375
+ Args:
376
+ minimal: Boolean to note whether the radiance string should be written
377
+ in a minimal format (with spaces instead of line breaks). Default: False.
378
+ """
379
+ # gather all unique modifier_blk and write geometry rad strings
380
+ blk_str = ['# BLACK representation for "{}"'.format(self.identifier)]
381
+ modifiers = []
382
+ for obj in self._dynamic_objects:
383
+ mod = obj.properties.radiance.modifier_blk
384
+ base_poly = Polygon(obj.identifier, obj.vertices, mod)
385
+ blk_str.append(base_poly.to_radiance(minimal, False, False))
386
+ if not self._instance_in_array(mod, modifiers):
387
+ modifiers.append(mod)
388
+ modifiers = list(set(modifiers))
389
+
390
+ # get rad strings for all modifiers.
391
+ for mod in modifiers:
392
+ if isinstance(mod, BSDF):
393
+ self._process_bsdf_modifier(mod, blk_str, minimal)
394
+ else:
395
+ blk_str.insert(1, mod.to_radiance(minimal))
396
+ return '\n\n'.join(blk_str)
397
+
398
+ def tmxt_bsdf(self, state_index):
399
+ """A BSDF modifier representing the tranmission matrix of a state if it exists.
400
+
401
+ This will be None unless all of the objects in the group have the same
402
+ BSDF modifier for the state. Note that having a single tmxt_bsdf
403
+ is a requirement in order to be compatible with 3-phase and 5-phase
404
+ simulation.
405
+
406
+ Args:
407
+ state_index: An integer from 0 up to the state_count - 1 , which notes
408
+ the state of the group for which a rad string will be produced.
409
+ """
410
+ bsdf_mods = []
411
+ for state in self.states_by_index(state_index):
412
+ mod = state.modifier
413
+ if not isinstance(mod, BSDF):
414
+ return None # not a BSDF; not valid for 3-phase
415
+ elif not self._instance_in_array(mod, bsdf_mods):
416
+ bsdf_mods.append(mod)
417
+ if len(set(bsdf_mods)) != 1:
418
+ return None # more than one type of BSDF; not valid for 3-phase
419
+ return bsdf_mods[0] # just one BSDF for the t_mtx; it's valid!
420
+
421
+ def vmtx_to_radiance(self, state_index, minimal=False):
422
+ """Generate a vmtx RAD string representation of a state.
423
+
424
+ The resulting string has all geometry geometry and the white_glow modifier.
425
+
426
+ Args:
427
+ state_index: An integer from 0 up to the state_count - 1 , which notes
428
+ the state of the group for which a rad string will be produced.
429
+ minimal: Boolean to note whether the radiance string should be written
430
+ in a minimal format (with spaces instead of line breaks). Default: False.
431
+ """
432
+ states = self.states_by_index(state_index)
433
+ # create unique glow modifier for aperture group
434
+ unique_glow = white_glow.duplicate()
435
+ unique_glow.identifier = 'white_glow_{}'.format(self.identifier)
436
+ # get rad strings for the white_glow modifier and geometry.
437
+ state_str = ['# VMTX representation for "{}"'.format(self.identifier),
438
+ unique_glow.to_radiance(minimal)]
439
+
440
+ # use first state to add the header
441
+ header = self.rfluxmtx_control_params(state_index=state_index)
442
+ state_str.append(header.to_radiance())
443
+
444
+ for state in states:
445
+ state_str.append(state.vmtx_to_radiance(unique_glow, minimal))
446
+ return '\n\n'.join(state_str)
447
+
448
+ def dmtx_to_radiance(self, state_index, minimal=False):
449
+ """Generate a dmtx RAD string representation of a state.
450
+
451
+ The resulting string has all geometry geometry and the white_glow modifier.
452
+
453
+ Args:
454
+ state_index: An integer from 0 up to the state_count - 1 , which notes
455
+ the state of the group for which a rad string will be produced.
456
+ minimal: Boolean to note whether the radiance string should be written
457
+ in a minimal format (with spaces instead of line breaks). Default: False.
458
+ """
459
+ states = self.states_by_index(state_index)
460
+ # get rad strings for the white_glow modifier and geometry.
461
+ state_str = ['# DMTX representation for "{}"'.format(self.identifier),
462
+ white_glow.to_radiance(minimal)]
463
+
464
+ # use first state to add the header
465
+ header = self.rfluxmtx_control_params()
466
+ state_str.append(header.to_radiance())
467
+
468
+ for state in states:
469
+ state_str.append(state.dmtx_to_radiance(minimal))
470
+ return '\n\n'.join(state_str)
471
+
472
+ @staticmethod
473
+ def _check_dynamic_objects(dynamic_objects):
474
+ for obj in dynamic_objects:
475
+ assert isinstance(obj, (Aperture, Door)), 'Expected Aperture or Door ' \
476
+ 'for DynamicSubFaceGroup. Got {}.'.format(type(obj))
477
+
478
+ def __repr__(self):
479
+ return 'Dynamic SubFace Group: {}'.format(self.identifier)