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,261 @@
1
+ """honeybee radiance view commands."""
2
+ import click
3
+ import sys
4
+ import os
5
+ import logging
6
+ import re
7
+ import json
8
+ import shutil
9
+
10
+ from honeybee_radiance_command.rpict import Rpict, RpictOptions
11
+ from honeybee_radiance_command.pcompos import Pcompos
12
+ from honeybee_radiance_command.pfilt import Pfilt
13
+ from honeybee_radiance_command.getinfo import Getinfo
14
+
15
+ from honeybee_radiance.view import View
16
+ from honeybee_radiance.config import folders
17
+
18
+ _logger = logging.getLogger(__name__)
19
+
20
+
21
+ @click.group(help='Commands for generating and modifying views.')
22
+ def view():
23
+ pass
24
+
25
+
26
+ @view.command('split-count')
27
+ @click.argument(
28
+ 'view-info-file',
29
+ type=click.Path(exists=True, file_okay=True, dir_okay=False, resolve_path=True)
30
+ )
31
+ @click.argument('cpu-count', type=int)
32
+ @click.option(
33
+ '--output-file', '-f', help='Optional file to output the integer for the number '
34
+ 'of times to split the view. By default this will be printed to stdout',
35
+ type=click.File('w'), default='-', show_default=True
36
+ )
37
+ def split_count_from_cpu_count(view_info_file, cpu_count, output_file):
38
+ """Get the number of times to split each view in a model using a CPU count.
39
+
40
+ \b
41
+ Args:
42
+ view_info_file: Input view info file.
43
+ cpu_count: Number of processes that will be used to run
44
+ the simulations in parallel.
45
+ """
46
+ try:
47
+ with open(view_info_file) as inf:
48
+ view_count = len(json.load(inf))
49
+ opt_split = int(cpu_count / view_count)
50
+ opt_split = 1 if opt_split == 0 else opt_split
51
+ output_file.write(str(opt_split))
52
+ except Exception:
53
+ _logger.exception('Failed to compute the view split-count from cpu-count.')
54
+ sys.exit(1)
55
+ else:
56
+ sys.exit(0)
57
+
58
+
59
+ @view.command('split')
60
+ @click.argument(
61
+ 'view', type=click.Path(
62
+ exists=True, file_okay=True, dir_okay=False, resolve_path=True)
63
+ )
64
+ @click.argument('count', type=int)
65
+ @click.option(
66
+ '--skip-overture/--overture', ' /-o', help='Flag to note whether an ambient '
67
+ 'file (.amb) should be generated for an overture calculation before the view is '
68
+ 'split into smaller views. The .amb file will have the same name as the view-file. '
69
+ 'With an overture calculation, the ambient file (aka ambient cache) is first '
70
+ 'populated with values. Thereby ensuring that - when reused to create an image - '
71
+ 'Radiance uses interpolation between already calculated values rather than less '
72
+ 'reliable extrapolation. The overture calculation has comparatively small '
73
+ 'computation time to full rendering but is single core can become time '
74
+ 'consuming in situations with very high numbers of rendering multiprocessors.',
75
+ default=True, show_default=True
76
+ )
77
+ @click.option(
78
+ '--resolution', '-r', default=None, type=int, show_default=True,
79
+ help='An optional integer for the maximum dimension of the image in pixels. '
80
+ 'Specifying a value here will automatically lower the input --count to ensure '
81
+ 'the resulting images can be combined to meet this dimension. If unspecified, '
82
+ 'the --count will always be respected and the resulting images might not be '
83
+ 'combine-able to meet a specific target dimension.'
84
+ )
85
+ @click.option(
86
+ '--octree', '-oct', help='Octree file for the overture calculation. This must be '
87
+ 'specified when the overture is not skipped.', default=None, show_default=True,
88
+ type=click.Path(file_okay=True, dir_okay=False, resolve_path=True)
89
+ )
90
+ @click.option(
91
+ '--rad-params', '-rp', help='Radiance parameters for the overture calculation. '
92
+ 'If unspecified, default rpict paramters will be used.'
93
+ )
94
+ @click.option(
95
+ '--folder', '-f', help='Output folder.', default='.', show_default=True,
96
+ type=click.Path(file_okay=False, dir_okay=True, resolve_path=True)
97
+ )
98
+ @click.option(
99
+ '--log-file', '-log', help='Optional log file to output the name of the newly'
100
+ ' created views. By default the list will be printed out to stdout',
101
+ type=click.File('w'), default='-'
102
+ )
103
+ def split_view(view, count, resolution, skip_overture, octree, rad_params,
104
+ folder, log_file):
105
+ """Split a radiance view file into smaller views based on count.
106
+
107
+ \b
108
+ Args:
109
+ view: Full path to input sensor view file.
110
+ count: Maximum number of sensors in new files. The number will be rounded to
111
+ closest round number for each file. For example if the input file has 21
112
+ sensors and input count is set to 5 this command will generate 4 files where
113
+ the first three files will have 5 sensors and the last file will have 6.
114
+ """
115
+ try:
116
+ # correct the count to meet the target resolution
117
+ if resolution is not None and resolution % count != 0:
118
+ while resolution % count != 0:
119
+ count = count - 1
120
+
121
+ # split the view into smaller views
122
+ view_obj = View.from_file(view)
123
+ views = view_obj.grid(y_div_count=count)
124
+ views_info = []
125
+ for c, v in enumerate(views):
126
+ name = '%s_%04d' % (view_obj.identifier, c)
127
+ path = '%s.vf' % name
128
+ full_path = os.path.join(folder, path)
129
+ v.to_file(folder, path, mkdir=True)
130
+ views_info.append({
131
+ 'name': name,
132
+ 'path': path,
133
+ 'full_path': full_path
134
+ })
135
+
136
+ # create the ambient cache file if specified
137
+ amb_file = os.path.join(folder, os.path.basename(view).replace('.vf', '.amb'))
138
+ if not skip_overture:
139
+ options = RpictOptions()
140
+ if rad_params:
141
+ options.update_from_string(rad_params.strip())
142
+ # overwrite default image size to be small for the ambient cache (64 x 64)
143
+ options.x = 64
144
+ options.y = 64
145
+ options.af = amb_file
146
+
147
+ # create command and run it to get the .amb file
148
+ assert octree is not None, \
149
+ 'Octree must be specified for an overture calculation.'
150
+ out_file = os.path.join(
151
+ folder, os.path.basename(view).replace('.vf', '.unf'))
152
+ rpict = Rpict(options=options, output=out_file, octree=octree, view=view)
153
+ env = None
154
+ if folders.env != {}:
155
+ env = folders.env
156
+ env = dict(os.environ, **env) if env else None
157
+ rpict.run(env=env)
158
+ os.remove(out_file)
159
+
160
+ # record all of the view files that were generated
161
+ log_file.write(json.dumps(views_info))
162
+ except Exception:
163
+ _logger.exception('Failed to split view file.')
164
+ sys.exit(1)
165
+ else:
166
+ sys.exit(0)
167
+
168
+
169
+ @view.command('merge')
170
+ @click.argument('input-folder', type=click.Path(
171
+ file_okay=False, dir_okay=True, resolve_path=True))
172
+ @click.argument('base-name', type=str)
173
+ @click.argument('extension', default='.unf', type=str)
174
+ @click.option('--view', '-vf', type=click.Path(
175
+ exists=False, file_okay=True, dir_okay=False, resolve_path=True),
176
+ help='Full path to the original view file.'
177
+ )
178
+ @click.option(
179
+ '--scale-factor', '-s', default=1, type=float, show_default=True,
180
+ help='A number that will be used to scale the dimensions of the output image '
181
+ 'as it is filtered for anti-aliasing.'
182
+ )
183
+ @click.option('--folder', '-f', help='Optional output folder.',
184
+ default='.', show_default=True)
185
+ @click.option('--name', '-n', help='Optional output filename. Default is base-name.')
186
+ def merge_view(input_folder, base_name, extension, view, scale_factor, folder, name):
187
+ """Merge several radiance HDR image files into a single file.
188
+
189
+ This command will also perform an anti-aliasing operation on the output and
190
+ replace the view information in the header of the merged file if a single .vf
191
+ file is found within the root of the input-folder.
192
+
193
+ \b
194
+ Args:
195
+ input_folder: Input folder.
196
+ base_name: File base name. All of the files must start with base name and
197
+ continue with _ and an integer values.
198
+ extension: File extension. [Default: .unf]
199
+ """
200
+ try:
201
+ # view is an optional input. Make sure we can handle the case that the view
202
+ # file doesn't exist
203
+ view = None if (view and not os.path.isfile(view)) else view
204
+
205
+ # get all of the files in the folder with the given extension
206
+ pattern = r'{}_\d+{}'.format(base_name, extension)
207
+ images = sorted(f for f in os.listdir(input_folder) if re.match(pattern, f))
208
+ if len(images) == 0:
209
+ raise ValueError('Found no files to merge.')
210
+ name = name or base_name
211
+
212
+ # get the new dir name as view name might be group/name
213
+ dirname = os.path.dirname(os.path.normpath(os.path.join(folder, name)))
214
+ if dirname and not os.path.exists(dirname):
215
+ os.makedirs(dirname)
216
+ temp_output = os.path.join(dirname, name + '_temp.HDR')
217
+ output_file = os.path.join(dirname, name + '.HDR')
218
+
219
+ # set up the pcompos command
220
+ in_dirname = os.path.normpath(input_folder)
221
+ pcompos = Pcompos(output=temp_output)
222
+ pcompos.input = [os.path.join(in_dirname, img) for img in images]
223
+ pcompos.options.a = 1
224
+
225
+ # setup the pfilt command to perform anti-aliasing on the output
226
+ pfilt = Pfilt(input=temp_output)
227
+ pfilt.options.r = 0.6
228
+ if scale_factor != 1:
229
+ pfilt.options.x = '/{}'.format(scale_factor)
230
+ pfilt.options.y = '/{}'.format(scale_factor)
231
+
232
+ # search for a single .vf in the folder and, if it's found, grab the info
233
+ views = sorted(f for f in os.listdir(input_folder) if f.endswith('.vf'))
234
+ if view and len(views) != 1:
235
+ view_obj = View.from_file(view)
236
+ getinfo = Getinfo(output=output_file)
237
+ getinfo.options.a = 'VIEW= {}'.format(view_obj)
238
+ pfilt.pipe_to = getinfo
239
+ elif len(views) == 1: # replace the header with the info in the view
240
+ view_obj = View.from_file(os.path.join(input_folder, views[0]))
241
+ getinfo = Getinfo(output=output_file)
242
+ getinfo.options.a = 'VIEW= {}'.format(view_obj)
243
+ pfilt.pipe_to = getinfo
244
+ else: # just let the output of pfilt be the final output
245
+ pfilt.output = output_file
246
+
247
+ # run the commands in series
248
+ env = None
249
+ if folders.env != {}:
250
+ env = folders.env
251
+ env = dict(os.environ, **env) if env else None
252
+ pcompos.run(env)
253
+ try:
254
+ pfilt.run(env)
255
+ except RuntimeError: # the image was too bright or dark; just use pcompos output
256
+ shutil.copyfile(temp_output, output_file)
257
+ except Exception:
258
+ _logger.exception('Failed to merge image files.')
259
+ sys.exit(1)
260
+ else:
261
+ sys.exit(0)
@@ -0,0 +1,347 @@
1
+ """Commands to compute view factors to geometry."""
2
+ import click
3
+ import os
4
+ import sys
5
+ import logging
6
+ import math
7
+ from itertools import islice
8
+
9
+ from honeybee_radiance.config import folders
10
+ from honeybee_radiance.geometry import Polygon
11
+ from honeybee_radiance.modifier.material import Plastic
12
+
13
+ from honeybee_radiance_command.oconv import Oconv
14
+ from honeybee_radiance_command.rcontrib import Rcontrib, RcontribOptions
15
+ from honeybee_radiance_command._command_util import run_command
16
+
17
+ from honeybee.model import Model
18
+ from honeybee.facetype import AirBoundary
19
+ from honeybee.boundarycondition import Surface
20
+ from ladybug.futil import preparedir
21
+
22
+ _logger = logging.getLogger(__name__)
23
+
24
+
25
+ @click.group(help='Commands to compute view factors to geometry.')
26
+ def view_factor():
27
+ pass
28
+
29
+
30
+ @view_factor.command('modifiers')
31
+ @click.argument('model-file', type=click.Path(
32
+ exists=True, file_okay=True, dir_okay=False, resolve_path=True))
33
+ @click.option(
34
+ '--exclude-sky/--include-sky', ' /-s',
35
+ help='Flag to note whether a sky dome should be included in the resulting octree. '
36
+ 'The inclusion of the sky dome enables the sky view to be computed in the '
37
+ 'resulting calculation.', default=True, show_default=True
38
+ )
39
+ @click.option(
40
+ '--exclude-ground/--include-ground', ' /-g',
41
+ help='Flag to note whether a ground dome should be included in the resulting octree.'
42
+ ' The inclusion of the ground dome enables the ground view to be computed in the '
43
+ 'resulting calculation.', default=True, show_default=True
44
+ )
45
+ @click.option(
46
+ '--individual-shades/--grouped-shades', ' /-shd',
47
+ help='Flag to note whether the shade geometries should be included in the '
48
+ 'list of modifiers. Note that they are still included in the resulting octree '
49
+ 'but are just excluded from the list of modifiers.', default=True, show_default=True
50
+ )
51
+ @click.option(
52
+ '--triangulate/--skip-triangulate', ' /-t', help='Flag to note whether '
53
+ 'the Apertures and Doors of the output model should be triangulated if '
54
+ 'they have more than 4 vertices. This triangulation is necessary to '
55
+ 'align a model with EnergyPlus results since E+ cannot accept sub-faces '
56
+ 'with more than 4 vertices.', default=True
57
+ )
58
+ @click.option(
59
+ '--folder', default='.', help='Output folder into which the modifier and '
60
+ 'octree files will be written.'
61
+ )
62
+ @click.option(
63
+ '--name', default='scene', help='File name, which will be used for both the '
64
+ 'modifiers and the octree.'
65
+ )
66
+ def create_view_factor_modifiers(
67
+ model_file, exclude_sky, exclude_ground, individual_shades, triangulate,
68
+ folder, name):
69
+ """Translate a Model into an Octree and corresponding modifier list for view factors.
70
+
71
+ \b
72
+ Args:
73
+ model_file: Full path to a Model JSON file (HBJSON) or a Model pkl (HBpkl) file.
74
+ """
75
+ try:
76
+ # create the directory if it's not there
77
+ if not os.path.isdir(folder):
78
+ preparedir(folder)
79
+
80
+ # load the model and ensure the properties align with the energy model
81
+ model = Model.from_file(model_file)
82
+ original_units = None
83
+ if model.units != 'Meters':
84
+ original_units = model.units
85
+ model.convert_to_units('Meters')
86
+ for room in model.rooms:
87
+ room.remove_colinear_vertices_envelope(
88
+ tolerance=0.01, delete_degenerate=True)
89
+ if original_units is not None:
90
+ model.convert_to_units(original_units)
91
+
92
+ # triangulate the sub-faces if requested
93
+ if triangulate:
94
+ apertures, parents_to_edit = model.triangulated_apertures()
95
+ for tri_aps, edit_infos in zip(apertures, parents_to_edit):
96
+ if len(edit_infos) == 3:
97
+ for room in model._rooms:
98
+ if room.identifier == edit_infos[2]:
99
+ break
100
+ for face in room._faces:
101
+ if face.identifier == edit_infos[1]:
102
+ break
103
+ for i, ap in enumerate(face._apertures):
104
+ if ap.identifier == edit_infos[0]:
105
+ break
106
+ face._apertures.pop(i) # remove the aperture to replace
107
+ face._apertures.extend(tri_aps)
108
+ doors, parents_to_edit = model.triangulated_doors()
109
+ for tri_drs, edit_infos in zip(doors, parents_to_edit):
110
+ if len(edit_infos) == 3:
111
+ for room in model._rooms:
112
+ if room.identifier == edit_infos[2]:
113
+ break
114
+ for face in room._faces:
115
+ if face.identifier == edit_infos[1]:
116
+ break
117
+ for i, dr in enumerate(face._doors):
118
+ if dr.identifier == edit_infos[0]:
119
+ break
120
+ face._doors.pop(i) # remove the doors to replace
121
+ face._doors.extend(tri_drs)
122
+
123
+ # set values to be used throughout the modifier assignment
124
+ offset = model.tolerance * -1
125
+ white_plastic = Plastic('white_plastic', 1, 1, 1)
126
+ geo_strs, mod_strs, mod_names = [], [], []
127
+
128
+ def _add_geo_and_modifier(hb_obj):
129
+ """Add a honeybee object to the geometry and modifier strings."""
130
+ mod_name = '%s_mod' % hb_obj.identifier
131
+ mod_names.append(mod_name)
132
+ white_plastic.identifier = mod_name
133
+ rad_poly = Polygon(hb_obj.identifier, hb_obj.vertices, white_plastic)
134
+ geo_strs.append(rad_poly.to_radiance(False, False, False))
135
+ mod_strs.append(white_plastic.to_radiance(True, False, False))
136
+
137
+ # loop through all geometry in the model and get radiance strings
138
+ for room in model.rooms:
139
+ for face in room.faces:
140
+ if not isinstance(face.type, AirBoundary):
141
+ if isinstance(face.boundary_condition, Surface):
142
+ face.move(face.normal * offset)
143
+ _add_geo_and_modifier(face)
144
+ for ap in face.apertures:
145
+ _add_geo_and_modifier(ap)
146
+ for dr in face.doors:
147
+ _add_geo_and_modifier(dr)
148
+ all_shades = model.shades + model._orphaned_faces + \
149
+ model._orphaned_apertures + model._orphaned_doors
150
+ if individual_shades:
151
+ for shade in all_shades:
152
+ _add_geo_and_modifier(shade)
153
+ for shade_mesh in model.shade_meshes:
154
+ shd_id = shade_mesh.identifier
155
+ str_vertices = tuple(tuple(str(v) for v in pt.to_array())
156
+ for pt in shade_mesh.vertices)
157
+ for fi, f_geo in enumerate(shade_mesh.faces):
158
+ coords = tuple(v for pt in f_geo for v in str_vertices[pt])
159
+ poly_id = '{}_{}'.format(shd_id, fi)
160
+ mod_name = '%s_mod' % poly_id
161
+ mod_names.append(mod_name)
162
+ white_plastic.identifier = mod_name
163
+ base_geo = white_plastic.identifier + ' polygon {} 0 0 {} {}'
164
+ geo_str = base_geo.format(poly_id, len(coords), ' '.join(coords))
165
+ geo_strs.append(geo_str)
166
+ mod_strs.append(white_plastic.to_radiance(True, False, False))
167
+ else:
168
+ white_plastic.identifier = 'shade_plastic_mod'
169
+ mod_names.append(white_plastic.identifier)
170
+ mod_strs.append(white_plastic.to_radiance(True, False, False))
171
+ for shade in all_shades:
172
+ rad_poly = Polygon(shade.identifier, shade.vertices, white_plastic)
173
+ geo_strs.append(rad_poly.to_radiance(False, False, False))
174
+ for shade_mesh in model.shade_meshes:
175
+ shade_mesh.properties.radiance.modifier = white_plastic
176
+ base_geo = white_plastic.identifier + ' polygon {} 0 0 {} {}'
177
+ shd_id = shade_mesh.identifier
178
+ str_vertices = tuple(tuple(str(v) for v in pt.to_array())
179
+ for pt in shade_mesh.vertices)
180
+ for fi, f_geo in enumerate(shade_mesh.faces):
181
+ coords = tuple(v for pt in f_geo for v in str_vertices[pt])
182
+ poly_id = '{}_{}'.format(shd_id, fi)
183
+ geo_str = base_geo.format(poly_id, len(coords), ' '.join(coords))
184
+ geo_strs.append(geo_str)
185
+
186
+ # add the ground and sky domes if requested
187
+ if not exclude_sky:
188
+ mod_names.append('sky_glow_mod')
189
+ mod_strs.append('void glow sky_glow_mod 0 0 4 1 1 1 0')
190
+ geo_strs.append('sky_glow_mod source sky_dome 0 0 4 0 0 1 180')
191
+ if not exclude_ground:
192
+ mod_names.append('ground_glow_mod')
193
+ mod_strs.append('void glow ground_glow_mod 0 0 4 1 1 1 0')
194
+ geo_strs.append('ground_glow_mod source ground_dome 0 0 4 0 0 -1 180')
195
+
196
+ # write the radiance strings to the output folder
197
+ geo_file = os.path.join(folder, '{}.rad'.format(name))
198
+ mod_file = os.path.join(folder, '{}.mod'.format(name))
199
+ oct_file = os.path.join(folder, '{}.oct'.format(name))
200
+ with open(geo_file, 'w') as gf:
201
+ gf.write('\n\n'.join(mod_strs + geo_strs))
202
+ with open(mod_file, 'w') as mf:
203
+ mf.write('\n'.join(mod_names))
204
+
205
+ # use the radiance files to create an octree
206
+ cmd = Oconv(output=oct_file, inputs=[geo_file])
207
+ cmd.options.f = True
208
+ run_command(cmd.to_radiance(), env=folders.env)
209
+ except Exception as e:
210
+ _logger.exception('Model translation failed.\n{}'.format(e))
211
+ sys.exit(1)
212
+ else:
213
+ sys.exit(0)
214
+
215
+
216
+ @view_factor.command('contrib')
217
+ @click.argument(
218
+ 'octree', type=click.Path(exists=True, file_okay=True, resolve_path=True)
219
+ )
220
+ @click.argument(
221
+ 'sensor-grid', type=click.Path(exists=True, file_okay=True, resolve_path=True)
222
+ )
223
+ @click.argument(
224
+ 'modifiers', type=click.Path(exists=True, file_okay=True, resolve_path=True)
225
+ )
226
+ @click.option(
227
+ '--ray-count', type=click.INT, default=6, show_default=True,
228
+ help='The number of rays to be equally distributed over a sphere to compute '
229
+ 'the view factor for each of the input sensors.'
230
+ )
231
+ @click.option(
232
+ '--rad-params', show_default=True, help='Radiance parameters.'
233
+ )
234
+ @click.option(
235
+ '--rad-params-locked', show_default=True, help='Protected Radiance parameters. '
236
+ 'These values will overwrite user input rad parameters.'
237
+ )
238
+ @click.option(
239
+ '--folder', default='.', help='Output folder into which the modifier and '
240
+ 'octree files will be written.'
241
+ )
242
+ @click.option(
243
+ '--name', default='view_factor', help='File name, which will be used for the '
244
+ 'rebuilt sensor-grid, the matrix and the resulting CSV with view factors.'
245
+ )
246
+ def rcontrib_command_with_view_postprocess(
247
+ octree, sensor_grid, modifiers, ray_count, rad_params, rad_params_locked,
248
+ folder, name
249
+ ):
250
+ """Run rcontrib to get spherical view factors from a sensor grid.
251
+
252
+ \b
253
+ Args:
254
+ octree: Path to octree file.
255
+ sensor-grid: Path to sensor grid file.
256
+ modifiers: Path to modifiers file.
257
+ """
258
+ try:
259
+ # create the directory if it's not there
260
+ if not os.path.isdir(folder):
261
+ preparedir(folder)
262
+
263
+ # generate the ray vectors to be used in the view factor calculation
264
+ if ray_count == 6:
265
+ rays = ((1, 0, 0), (0, 1, 0), (0, 0, 1), (-1, 0, 0), (0, -1, 0), (0, 0, -1))
266
+ else:
267
+ rays = _fibonacci_spiral(ray_count)
268
+ ray_str = [' {} {} {}\n'.format(*ray) for ray in rays]
269
+
270
+ # create a new .pts file with the view vectors
271
+ ray_file = os.path.abspath(os.path.join(folder, '{}.pts'.format(name)))
272
+ total_rays = 0
273
+ with open(sensor_grid) as sg_file:
274
+ with open(ray_file, 'w') as r_file:
275
+ for line in sg_file:
276
+ for ray in ray_str:
277
+ try:
278
+ r_file.write(' '.join(line.split()[:3]) + ray)
279
+ total_rays += 1
280
+ except Exception:
281
+ pass # we are at the end of the file
282
+
283
+ # set up the Rcontrib options
284
+ options = RcontribOptions()
285
+ if rad_params: # parse input radiance parameters
286
+ options.update_from_string(rad_params.strip())
287
+ if rad_params_locked: # overwrite input values with protected ones
288
+ options.update_from_string(rad_params_locked.strip())
289
+ # overwrite specific options that would otherwise break the command
290
+ options.M = modifiers
291
+ options.update_from_string('-I -V- -y {}'.format(total_rays))
292
+
293
+ # create the rcontrib command and run it
294
+ mtx_file = os.path.abspath(os.path.join(folder, '{}.mtx'.format(name)))
295
+ rcontrib = Rcontrib(options=options, octree=octree, sensors=ray_file)
296
+ cmd = rcontrib.to_radiance().replace('\\', '/')
297
+ cmd = '{} | rmtxop -fa - -c .333 .333 .334'.format(cmd)
298
+ cmd = '{} | getinfo - > "{}"'.format(cmd, mtx_file.replace('\\', '/'))
299
+ run_command(cmd, env=folders.env)
300
+
301
+ # load the resulting matrix and process the results into view factors
302
+ view_fac_mtx = []
303
+ with open(mtx_file) as mtx_data:
304
+ while True:
305
+ sens_lines = list(islice(mtx_data, ray_count))
306
+ if not sens_lines:
307
+ break
308
+ sens_mtx = ((float(v) for v in ln.strip().split()) for ln in sens_lines)
309
+ s_facs = []
310
+ for sens_facs in zip(*sens_mtx):
311
+ s_facs.append(sum(sens_facs) / (math.pi * ray_count))
312
+ view_fac_mtx.append(s_facs)
313
+
314
+ # write the final view factors into a CSV file
315
+ view_file = os.path.join(folder, '{}.csv'.format(name))
316
+ with open(view_file, 'w') as v_file:
317
+ for facs in view_fac_mtx:
318
+ v_file.write(','.join((str(v) for v in facs)) + '\n')
319
+ except Exception:
320
+ _logger.exception('Failed to compute view factor contributions.')
321
+ sys.exit(1)
322
+ else:
323
+ sys.exit(0)
324
+
325
+
326
+ def _fibonacci_spiral(point_count=24):
327
+ """Get points distributed uniformly across a unit spherical surface.
328
+
329
+ Args:
330
+ point_count: Integer for the number of points to be distributed.
331
+
332
+ Returns:
333
+ List of tuple, each with 3 values representing the XYZ coordinates of
334
+ the points that were generated.
335
+ """
336
+ points = []
337
+ phi = math.pi * (3. - math.sqrt(5.))
338
+
339
+ for i in range(point_count):
340
+ y = 1 - (i / float(point_count - 1)) * 2
341
+ radius = math.sqrt(1 - y * y)
342
+ theta = phi * i
343
+ x = math.cos(theta) * radius
344
+ z = math.sin(theta) * radius
345
+ points.append((x, y, z))
346
+
347
+ return points
@@ -0,0 +1,6 @@
1
+ {
2
+ "__comment__": "Add full paths to folders (eg. C:/Radiance/bin).",
3
+ "radiance_path": "",
4
+ "standards_data_folder": "",
5
+ "defaults_file": ""
6
+ }