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,1186 @@
1
+ """honeybee radiance daylight postprocessing commands."""
2
+ import click
3
+ import sys
4
+ import os
5
+ import json
6
+ import shutil
7
+ import logging
8
+
9
+ from ladybug.wea import Wea
10
+ from ladybug.legend import LegendParameters
11
+ from ladybug.color import Colorset, Color
12
+ from ladybug.datatype.generic import GenericType
13
+ from ladybug.datatype.fraction import Fraction
14
+ from ladybug.datatype.illuminance import Illuminance
15
+ from ladybug.datatype.luminance import Luminance
16
+ from ladybug.datatype.time import Time
17
+
18
+ from honeybee_radiance.postprocess.annualdaylight import metrics_to_folder
19
+ from honeybee_radiance.postprocess.en17037 import en17037_to_folder
20
+ from honeybee_radiance.postprocess.annualglare import glare_autonomy_to_folder
21
+ from honeybee_radiance.postprocess.annualirradiance import annual_irradiance_to_folder, \
22
+ _annual_irradiance_config, _annual_irradiance_vis_metadata
23
+ from honeybee_radiance.postprocess.electriclight import daylight_control_schedules
24
+ from honeybee_radiance.postprocess.leed import leed_illuminance_to_folder
25
+ from honeybee_radiance.postprocess.solartracking import post_process_solar_tracking
26
+ from honeybee_radiance.cli.util import get_compare_func, remove_header
27
+
28
+ _logger = logging.getLogger(__name__)
29
+
30
+
31
+ @click.group(help='Commands to post-process Radiance results.')
32
+ def post_process():
33
+ pass
34
+
35
+
36
+ @post_process.command('convert-to-binary')
37
+ @click.argument(
38
+ 'input-matrix', type=click.Path(exists=True, file_okay=True, resolve_path=True)
39
+ )
40
+ @click.option(
41
+ '--output', '-o', help='Optional path to output file to output the name of the newly'
42
+ ' created matrix. By default the list will be printed out to stdout',
43
+ type=click.File('w'), default='-')
44
+ @click.option(
45
+ '--minimum', type=float, default='-inf', help='Minimum range for values to be '
46
+ 'converted to 1.'
47
+ )
48
+ @click.option(
49
+ '--maximum', type=float, default='+inf', help='Maximum range for values to be '
50
+ 'converted to 1.'
51
+ )
52
+ @click.option(
53
+ '--include-max/--exclude-max', is_flag=True, help='A flag to include the maximum '
54
+ 'threshold itself. By default the threshold value will be included.', default=True
55
+ )
56
+ @click.option(
57
+ '--include-min/--exclude-min', is_flag=True, help='A flag to include the minimum '
58
+ 'threshold itself. By default the threshold value will be included.', default=True
59
+ )
60
+ @click.option(
61
+ '--comply/--reverse', is_flag=True, help='A flag to reverse the selection logic. '
62
+ 'This is useful for cases that you want to all the values outside a certain range '
63
+ 'to be converted to 1. By default the input logic will be used as is.', default=True
64
+ )
65
+ def convert_matrix_to_binary(
66
+ input_matrix, output, minimum, maximum, include_max, include_min, comply
67
+ ):
68
+ """Postprocess a Radiance matrix and convert it to 0-1 values.
69
+
70
+ \b
71
+ This command is useful for translating Radiance results to outputs like sunlight
72
+ hours. Input matrix must be in ASCII format. The header in the input file will be
73
+ ignored.
74
+
75
+ """
76
+
77
+ compare = get_compare_func(include_min, include_max, comply)
78
+ minimum = float(minimum)
79
+ maximum = float(maximum)
80
+ try:
81
+ first_line, input_file = remove_header(input_matrix)
82
+ values = [
83
+ '1' if compare(float(v), minimum, maximum) else '0'
84
+ for v in first_line.split()
85
+ ]
86
+ output.write('\t'.join(values) + '\n')
87
+ for line in input_file:
88
+ # write binary values to new file
89
+ values = [
90
+ '1' if compare(float(v), minimum, maximum) else '0'
91
+ for v in line.split()
92
+ ]
93
+ output.write('\t'.join(values) + '\n')
94
+ except Exception:
95
+ _logger.exception('Failed to convert the input file to binary format.')
96
+ sys.exit(1)
97
+ else:
98
+ sys.exit(0)
99
+ finally:
100
+ input_file.close()
101
+
102
+
103
+ @post_process.command('count')
104
+ @click.argument(
105
+ 'input-matrix', type=click.Path(exists=True, file_okay=True, resolve_path=True)
106
+ )
107
+ @click.option(
108
+ '--output', '-o', help='Optional path to output file to output the name of the newly'
109
+ ' created matrix. By default the list will be printed out to stdout',
110
+ type=click.File('w'), default='-')
111
+ @click.option(
112
+ '--minimum', type=float, default='-inf', help='Minimum range for values to be '
113
+ 'converted to 1.'
114
+ )
115
+ @click.option(
116
+ '--maximum', type=float, default='+inf', help='Maximum range for values to be '
117
+ 'converted to 1.'
118
+ )
119
+ @click.option(
120
+ '--include-max/--exclude-max', is_flag=True, help='A flag to include the maximum '
121
+ 'threshold itself. By default the threshold value will be included.', default=True
122
+ )
123
+ @click.option(
124
+ '--include-min/--exclude-min', is_flag=True, help='A flag to include the minimum '
125
+ 'threshold itself. By default the threshold value will be included.', default=True
126
+ )
127
+ @click.option(
128
+ '--comply/--reverse', is_flag=True, help='A flag to reverse the selection logic. '
129
+ 'This is useful for cases that you want to all the values outside a certain range '
130
+ 'to be converted to 1. By default the input logic will be used as is.', default=True
131
+ )
132
+ def count_values(
133
+ input_matrix, output, minimum, maximum, include_max, include_min, comply
134
+ ):
135
+ """Count values in a row that meet a certain criteria.
136
+
137
+ \b
138
+ This command is useful for post processing results like the number of sensors
139
+ which receive more than X lux at any timestep.
140
+
141
+ """
142
+ compare = get_compare_func(include_min, include_max, comply)
143
+ minimum = float(minimum)
144
+ maximum = float(maximum)
145
+ try:
146
+ first_line, input_file = remove_header(input_matrix)
147
+ value = sum(
148
+ 1 if compare(float(v), minimum, maximum) else 0
149
+ for v in first_line.split()
150
+ )
151
+ output.write('%d\n' % value)
152
+ for line in input_file:
153
+ # write binary values to new file
154
+ value = sum(
155
+ 1 if compare(float(v), minimum, maximum) else 0
156
+ for v in line.split()
157
+ )
158
+ output.write('%d\n' % value)
159
+ except Exception:
160
+ _logger.exception('Failed to convert the input file to binary format.')
161
+ sys.exit(1)
162
+ else:
163
+ sys.exit(0)
164
+ finally:
165
+ input_file.close()
166
+
167
+
168
+ @post_process.command('sum-row')
169
+ @click.argument(
170
+ 'input-matrix', type=click.Path(exists=True, file_okay=True, resolve_path=True)
171
+ )
172
+ @click.option(
173
+ '--divisor', type=float, default=1, help='An optional number, that the summed '
174
+ 'row will be divided by. For example, this can be a timestep, which can be used '
175
+ 'to ensure that a summed row of irradiance yields cumulative radiation over '
176
+ 'the entire time period of the matrix.'
177
+ )
178
+ @click.option(
179
+ '--output', '-o', help='Optional path to output file to output the name of the newly'
180
+ ' created matrix. By default the list will be printed out to stdout',
181
+ type=click.File('w'), default='-')
182
+ def sum_matrix_rows(input_matrix, divisor, output):
183
+ """Postprocess a Radiance matrix and add all the numbers in each row.
184
+
185
+ \b
186
+ This command is useful for translating Radiance results to outputs like radiation
187
+ to total radiation. Input matrix must be in ASCII format. The header in the input
188
+ file will be ignored.
189
+ """
190
+ try:
191
+ first_line, input_file = remove_header(input_matrix)
192
+ value = sum(float(v) for v in first_line.split()) / divisor
193
+ output.write('%s\n' % value)
194
+ for line in input_file:
195
+ # write sum to a new file
196
+ value = sum(float(v) for v in line.split()) / divisor
197
+ output.write('%s\n' % value)
198
+ except Exception:
199
+ _logger.exception('Failed to sum numbers in each row.')
200
+ sys.exit(1)
201
+ else:
202
+ sys.exit(0)
203
+ finally:
204
+ input_file.close()
205
+
206
+
207
+ @post_process.command('average-row')
208
+ @click.argument(
209
+ 'input-matrix', type=click.Path(exists=True, file_okay=True, resolve_path=True)
210
+ )
211
+ @click.option(
212
+ '--output', '-o', help='Optional path to output file to output the name of the newly'
213
+ ' created matrix. By default the list will be printed out to stdout',
214
+ type=click.File('w'), default='-')
215
+ def average_matrix_rows(input_matrix, output):
216
+ """Postprocess a Radiance matrix and average the numbers in each row.
217
+
218
+ \b
219
+ This command is useful for translating Radiance results to outputs like radiation
220
+ to average radiation. Input matrix must be in ASCII format. The header in the input
221
+ file will be ignored.
222
+ """
223
+ try:
224
+ first_line, input_file = remove_header(input_matrix)
225
+
226
+ # calculate the values for the first line
227
+ values = [float(v) for v in first_line.split()]
228
+ count = len(values)
229
+ output.write('%s\n' % sum(values) / count)
230
+
231
+ # write rest of the lines
232
+ for line in input_file:
233
+ # write sum to a new file
234
+ value = sum(float(v) for v in line.split())
235
+ output.write('%s\n' % value / count)
236
+
237
+ except Exception:
238
+ _logger.exception('Failed to average the numbers in each row.')
239
+ sys.exit(1)
240
+ else:
241
+ sys.exit(0)
242
+ finally:
243
+ input_file.close()
244
+
245
+
246
+ @post_process.command('cumulative-radiation')
247
+ @click.argument(
248
+ 'average-irradiance', type=click.Path(exists=True, file_okay=True, resolve_path=True)
249
+ )
250
+ @click.argument(
251
+ 'wea', type=click.Path(exists=True, file_okay=True, resolve_path=True)
252
+ )
253
+ @click.option(
254
+ '--timestep', type=int, default=1, help='The timestep of the Wea file, which '
255
+ 'is used to to compute cumulative radiation over the time period of the Wea.'
256
+ )
257
+ @click.option(
258
+ '--output', '-o', help='Optional path to output file to output the name of the newly'
259
+ ' created matrix. By default the list will be printed out to stdout',
260
+ type=click.File('w'), default='-')
261
+ def cumulative_radiation(average_irradiance, wea, timestep, output):
262
+ """Postprocess average irradiance (W/m2) into cumulative radiation (kWh/m2).
263
+
264
+ \b
265
+ Args:
266
+ average_irradiance: A single-column matrix of average irradiance values.
267
+ This input matrix must be in ASCII format.
268
+ wea: The .wea file that was used in the irradiance simulation. This
269
+ will be used to determine the duration of the analysis for computing
270
+ cumulative radiation. This can also be an .epw file.
271
+ """
272
+ try:
273
+ with open(wea) as inf:
274
+ first_word = inf.read(5)
275
+ is_wea = True if first_word == 'place' else False
276
+ if not is_wea:
277
+ _wea_file = os.path.join(os.path.dirname(wea), 'epw_to_wea.wea')
278
+ wea = Wea.from_epw_file(wea, timestep).write(_wea_file)
279
+ # parse the Wea and the average_irradiance matrix
280
+ conversion = Wea.count_timesteps(wea) / (timestep * 1000)
281
+ first_line, input_file = remove_header(average_irradiance)
282
+ # calculate the value for the first line
283
+ output.write('%s\n' % (float(first_line) * conversion))
284
+ # write rest of the lines
285
+ for line in input_file:
286
+ output.write('%s\n' % (float(line) * conversion))
287
+ except Exception:
288
+ _logger.exception('Failed to compute cumulative radiation.')
289
+ sys.exit(1)
290
+ else:
291
+ sys.exit(0)
292
+ finally:
293
+ input_file.close()
294
+
295
+
296
+ @post_process.command('annual-irradiance')
297
+ @click.argument(
298
+ 'folder',
299
+ type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True)
300
+ )
301
+ @click.argument(
302
+ 'wea', type=click.Path(exists=True, file_okay=True, resolve_path=True)
303
+ )
304
+ @click.option(
305
+ '--timestep', type=int, default=1, help='The timestep of the Wea file, which '
306
+ 'is used to ensure the summed row of irradiance yields cumulative radiation over '
307
+ 'the time period of the Wea.'
308
+ )
309
+ @click.option(
310
+ '--sub-folder', '-sf', help='Optional relative path for subfolder to write output '
311
+ 'metric files.', default='metrics'
312
+ )
313
+ def annual_irradiance(folder, wea, timestep, sub_folder):
314
+ """Compute irradiance metrics in a folder and write them in a subfolder.
315
+
316
+ \b
317
+ This command generates 3 files for each input grid.
318
+ average_irradiance/{grid-name}.res -> Average Irradiance (W/m2)
319
+ peak_irradiance/{grid-name}.res -> Peak Irradiance (W/m2)
320
+ cumulative_radiation/{grid-name}.res -> Cumulative Radiation (kWh/m2)
321
+
322
+ \b
323
+ Args:
324
+ folder: Results folder from an annual irradiance recipe.
325
+ wea: The .wea file that was used in the annual irradiance simulation. This
326
+ will be used to determine the duration of the analysis for computing
327
+ cumulative radiation. This can also be an .epw file.
328
+ """
329
+ try:
330
+ with open(wea) as inf:
331
+ first_word = inf.read(5)
332
+ is_wea = True if first_word == 'place' else False
333
+ if not is_wea:
334
+ _wea_file = os.path.join(os.path.dirname(wea), 'epw_to_wea.wea')
335
+ wea = Wea.from_epw_file(wea, timestep).write(_wea_file)
336
+ annual_irradiance_to_folder(folder, wea, timestep, sub_folder)
337
+ except Exception:
338
+ _logger.exception('Failed to compute irradiance metrics.')
339
+ sys.exit(1)
340
+ else:
341
+ sys.exit(0)
342
+
343
+
344
+ @post_process.command('annual-daylight')
345
+ @click.argument(
346
+ 'folder',
347
+ type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True)
348
+ )
349
+ @click.option(
350
+ '--schedule', '-sch', help='Path to an annual schedule file. Values should be 0-1 '
351
+ 'separated by new line. If not provided an 8-5 annual schedule will be created.',
352
+ type=click.Path(exists=False, file_okay=True, dir_okay=False, resolve_path=True)
353
+ )
354
+ @click.option(
355
+ '--threshold', '-t', help='Threshold illuminance level for daylight autonomy.',
356
+ default=300, type=int, show_default=True
357
+ )
358
+ @click.option(
359
+ '--lower-threshold', '-lt',
360
+ help='Minimum threshold for useful daylight illuminance.', default=100, type=int,
361
+ show_default=True
362
+ )
363
+ @click.option(
364
+ '--upper-threshold', '-ut',
365
+ help='Maximum threshold for useful daylight illuminance.', default=3000, type=int,
366
+ show_default=True
367
+ )
368
+ @click.option(
369
+ '--grids-filter', '-gf', help='A pattern to filter the grids.', default='*',
370
+ show_default=True
371
+ )
372
+ @click.option(
373
+ '--sub_folder', '-sf', help='Optional relative path for subfolder to write output '
374
+ 'metric files.', default='metrics'
375
+ )
376
+ def annual_metrics(
377
+ folder, schedule, threshold, lower_threshold, upper_threshold, grids_filter,
378
+ sub_folder
379
+ ):
380
+ """Compute annual metrics in a folder and write them in a subfolder.
381
+
382
+ \b
383
+ This command generates 5 files for each input grid.
384
+ da/{grid-name}.da -> Daylight Autonomy
385
+ cda/{grid-name}.cda -> Continuos Daylight Autonomy
386
+ udi/{grid-name}.udi -> Useful Daylight Illuminance
387
+ udi_lower/{grid-name}_upper.udi -> Upper Useful Daylight Illuminance
388
+ udi_upper/{grid-name}_lower.udi -> Lower Useful Daylight Illuminance
389
+
390
+ \b
391
+ Args:
392
+ folder: Results folder. This folder is an output folder of annual
393
+ daylight recipe. Folder should include grids_info.json and sun-up-hours.txt.
394
+ The command uses the list in grids_info.json to find the result files for each
395
+ sensor grid.
396
+ """
397
+ # optional input - only check if the file exist otherwise ignore
398
+ if schedule and os.path.isfile(schedule):
399
+ with open(schedule) as hourly_schedule:
400
+ schedule = [int(float(v)) for v in hourly_schedule]
401
+ else:
402
+ schedule = None
403
+
404
+ try:
405
+ metrics_to_folder(
406
+ folder, schedule, threshold, lower_threshold, upper_threshold,
407
+ grids_filter, sub_folder
408
+ )
409
+ except Exception:
410
+ _logger.exception('Failed to calculate annual metrics.')
411
+ sys.exit(1)
412
+ else:
413
+ sys.exit(0)
414
+
415
+
416
+ @post_process.command('annual-daylight-en17037')
417
+ @click.argument(
418
+ 'folder',
419
+ type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True)
420
+ )
421
+ @click.argument(
422
+ 'schedule',
423
+ type=click.Path(exists=True, file_okay=True, dir_okay=False, resolve_path=True)
424
+ )
425
+ @click.option(
426
+ '--grids-filter', '-gf', help='A pattern to filter the grids.', default='*',
427
+ show_default=True
428
+ )
429
+ @click.option(
430
+ '--sub_folder', '-sf', help='Optional relative path for subfolder to write output '
431
+ 'metric files.', default='metrics'
432
+ )
433
+ def annual_en17037_metrics(
434
+ folder, schedule, grids_filter, sub_folder
435
+ ):
436
+ """Compute annual EN 17037 metrics in a folder and write them in a subfolder.
437
+
438
+ \b
439
+ This command generates multiple files for each input grid. Files for target
440
+ illuminance and minimum illuminance will be calculated for three levels of
441
+ recommendation: minimum, medium, high.
442
+
443
+ \b
444
+ Args:
445
+ folder: Results folder. This folder is an output folder of annual
446
+ daylight recipe. Folder should include grids_info.json and sun-up-hours.txt.
447
+ The command uses the list in grids_info.json to find the result files for
448
+ each sensor grid.
449
+ schedule: Path to an annual schedule file. Values should be 0-1 separated by new
450
+ line. This should be a daylight hours schedule.
451
+ """
452
+ with open(schedule) as hourly_schedule:
453
+ schedule = [int(float(v)) for v in hourly_schedule]
454
+ try:
455
+ en17037_to_folder(folder, schedule, grids_filter, sub_folder)
456
+ except Exception:
457
+ _logger.exception('Failed to calculate annual EN 17037 metrics.')
458
+ sys.exit(1)
459
+ else:
460
+ sys.exit(0)
461
+
462
+
463
+ @post_process.command('annual-glare')
464
+ @click.argument(
465
+ 'folder',
466
+ type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True)
467
+ )
468
+ @click.option(
469
+ '--schedule', '-sch', help='Path to an annual schedule file. Values should be 0-1 '
470
+ 'separated by new line. If not provided an 8-5 annual schedule will be created.',
471
+ type=click.Path(exists=False, file_okay=True, dir_okay=False, resolve_path=True)
472
+ )
473
+ @click.option(
474
+ '--glare-threshold', '-gt', help='A fractional number for the threshold of DGP '
475
+ 'above which conditions are considered to induce glare.',
476
+ default=0.4, type=float, show_default=True
477
+ )
478
+ @click.option(
479
+ '--grids-filter', '-gf', help='A pattern to filter the grids.', default='*',
480
+ show_default=True
481
+ )
482
+ @click.option(
483
+ '--sub_folder', '-sf', help='Optional relative path for subfolder to write output '
484
+ 'metric files.', default='metrics'
485
+ )
486
+ def annual_glare(
487
+ folder, schedule, glare_threshold, grids_filter, sub_folder
488
+ ):
489
+ """Compute annual glare autonomy in a folder and write them in a subfolder.
490
+
491
+ \b
492
+ This command generates 1 file for each input grid.
493
+ ga/{grid-name}.ga -> Glare Autonomy
494
+
495
+ \b
496
+ Args:
497
+ folder: Results folder. This folder is an output folder of imageless annual
498
+ glare recipe. Folder should include grids_info.json and sun-up-hours.txt.
499
+ The command uses the list in grids_info.json to find the result files for each
500
+ sensor grid.
501
+ """
502
+ # optional input - only check if the file exist otherwise ignore
503
+ if schedule and os.path.isfile(schedule):
504
+ with open(schedule) as hourly_schedule:
505
+ schedule = [int(float(v)) for v in hourly_schedule]
506
+ else:
507
+ schedule = None
508
+
509
+ try:
510
+ glare_autonomy_to_folder(
511
+ folder, schedule, glare_threshold, grids_filter, sub_folder
512
+ )
513
+ except Exception:
514
+ _logger.exception('Failed to calculate annual glare autonomy.')
515
+ sys.exit(1)
516
+ else:
517
+ sys.exit(0)
518
+
519
+
520
+ @post_process.command('electric-lighting')
521
+ @click.argument(
522
+ 'folder',
523
+ type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True)
524
+ )
525
+ @click.option(
526
+ '--base-schedule', '-s', help='Path to a CSV file for the lighting schedule '
527
+ 'without any daylight controls. The values of this schedule will be multiplied '
528
+ 'by the hourly dimming fraction to yield the output lighting schedules. If '
529
+ 'unspecified, a schedule from 9AM to 5PM on weekdays will be used.',
530
+ type=click.Path(exists=False, file_okay=True, dir_okay=False, resolve_path=True)
531
+ )
532
+ @click.option(
533
+ '--ill-setpoint', '-i', help='A number for the illuminance setpoint in lux beyond '
534
+ 'which electric lights are dimmed if there is sufficient daylight.',
535
+ default=300, type=int, show_default=True
536
+ )
537
+ @click.option(
538
+ '--min-power-in', '-p',
539
+ help='A number between 0 and 1 for the the lowest power the lighting system can '
540
+ 'dim down to, expressed as a fraction of maximum input power.', default=0.3,
541
+ type=float, show_default=True
542
+ )
543
+ @click.option(
544
+ '--min-light-out', '-l',
545
+ help='A number between 0 and 1 the lowest lighting output the lighting system can '
546
+ 'dim down to, expressed as a fraction of maximum light output. Note that setting '
547
+ 'this to 1 means lights are not dimmed at all until the illuminance setpoint is '
548
+ 'reached. This can be used to approximate manual light-switching behavior when '
549
+ 'used in conjunction with the off_at_min_ output below.', default=0.2,
550
+ type=float, show_default=True
551
+ )
552
+ @click.option(
553
+ '--on-at-min/--off-at-min', ' /-oam', help='Flag to note whether lights should '
554
+ 'switch off completely when they get to the minimum power input.',
555
+ default=True, show_default=True
556
+ )
557
+ @click.option(
558
+ '--output-file', '-f', help='Optional JSON file to output a summary of the number '
559
+ 'of LEED credits and the percentage of sensor area that meets the criteria. '
560
+ 'By default this will be printed out to stdout',
561
+ type=click.File('w'), default='-', show_default=True
562
+ )
563
+ def electric_lighting(
564
+ folder, base_schedule, ill_setpoint, min_power_in, min_light_out, on_at_min,
565
+ output_file
566
+ ):
567
+ """Generate electric lighting schedules from annual daylight results.
568
+
569
+ Such controls will dim the lights according to whether the illuminance values
570
+ at the sensor locations are at a target illuminance setpoint. The results can be
571
+ used to account for daylight controls in energy simulations.
572
+
573
+ This function will generate one schedule per sensor grid in the simulation. Each
574
+ grid should have sensors at the locations in space where daylight dimming sensors
575
+ are located. Grids with one, two, or more sensors can be used to model setups
576
+ where fractions of each room are controlled by different sensors. If the sensor
577
+ grids are distributed over the entire floor of the rooms, the resulting schedules
578
+ will be idealized, where light dimming has been optimized to supply the minimum
579
+ illuminance setpoint everywhere in the room.
580
+
581
+ \b
582
+ Args:
583
+ folder: Results folder. This folder is an output folder of the annual
584
+ daylight recipe. Folder should include grids_info.json and
585
+ sun-up-hours.txt. The command uses the list in grids_info.json to
586
+ find the result files for each sensor grid.
587
+ """
588
+ try:
589
+ # optional input - only check if the file exist otherwise ignore
590
+ if base_schedule and os.path.isfile(base_schedule):
591
+ with open(base_schedule) as hourly_schedule:
592
+ schedule = [float(v) for v in hourly_schedule]
593
+ else:
594
+ schedule = None
595
+
596
+ off_at_min = not on_at_min
597
+ schedules, _ = daylight_control_schedules(
598
+ folder, schedule, ill_setpoint, min_power_in, min_light_out, off_at_min
599
+ )
600
+
601
+ for line in zip(*schedules):
602
+ output_file.write(','.join([str(v) for v in line]) + '\n')
603
+ except Exception:
604
+ _logger.exception('Failed to calculate electric lighting.')
605
+ sys.exit(1)
606
+ else:
607
+ sys.exit(0)
608
+
609
+
610
+ @post_process.command('leed-illuminance')
611
+ @click.argument(
612
+ 'folder',
613
+ type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True)
614
+ )
615
+ @click.option(
616
+ '--glare-control/--no-glare-control', ' /-ngc', help='Flag to note whether '
617
+ 'the model has "view-preserving automatic (with manual override) glare-control '
618
+ 'devices," which means that illuminance only needs to be above 300 lux and not '
619
+ 'between 300 and 3000 lux.', default=True, show_default=True
620
+ )
621
+ @click.option(
622
+ '--grids-filter', '-gf', help='A pattern to filter the grids for just the '
623
+ 'regularly occupied spaces.', default='*', show_default=True
624
+ )
625
+ @click.option(
626
+ '--sub-folder', '-sf', help='Optional relative path for a subfolder to write the '
627
+ 'pass/fail files for each sensor grid.', default=None
628
+ )
629
+ @click.option(
630
+ '--output-file', help='Optional JSON file to output a summary of the number '
631
+ 'of LEED credits and the percentage of sensor area that meets the criteria. '
632
+ 'By default this will be printed out to stdout',
633
+ type=click.File('w'), default='-', show_default=True
634
+ )
635
+ def leed_illuminance(folder, glare_control, grids_filter, sub_folder, output_file):
636
+ """Estimate LEED daylight credits from two point-in-time illuminance folders.
637
+
638
+ \b
639
+ Args:
640
+ folder: Project folder for a LEED illuminance simulation. It should contain
641
+ a HBJSON model and two sub-folders of complete point-in-time illuminance
642
+ simulations labeled "9AM" and "3PM". These two sub-folders should each
643
+ have results folders that include a grids_info.json and .res files with
644
+ illuminance values for each sensor. If Meshes are found for the sensor
645
+ grids in the HBJSON file, they will be used to compute percentages
646
+ of occupied floor area that pass vs. fail. Otherwise, all sensors will
647
+ be assumed to represent an equal amount of floor area.
648
+ """
649
+ try:
650
+ credit_summary = leed_illuminance_to_folder(
651
+ folder, glare_control, grids_filter, sub_folder)
652
+ output_file.write(json.dumps(credit_summary, indent=4))
653
+ except Exception:
654
+ _logger.exception('Failed to calculate LEED daylight metrics.')
655
+ sys.exit(1)
656
+ else:
657
+ sys.exit(0)
658
+
659
+
660
+ @post_process.command('solar-tracking')
661
+ @click.argument(
662
+ 'folder',
663
+ type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True)
664
+ )
665
+ @click.argument(
666
+ 'sun-up-hours', type=click.Path(exists=True, file_okay=True, resolve_path=True)
667
+ )
668
+ @click.argument(
669
+ 'wea', type=click.Path(exists=True, file_okay=True, resolve_path=True)
670
+ )
671
+ @click.option(
672
+ '--north', default=0, type=float, show_default=True,
673
+ help='Angle to north (0-360). 90 is west and 270 is east'
674
+ )
675
+ @click.option(
676
+ '--tracking-increment', '-t', type=int, default=5, help='An integer for the '
677
+ 'increment angle of each state in degrees. (Default: 5).'
678
+ )
679
+ @click.option(
680
+ '--sub-folder', '-sf', help='Optional relative path for subfolder to write output '
681
+ '.ill files of the dynamic tracking system.', default='final'
682
+ )
683
+ def solar_tracking(folder, sun_up_hours, wea, north, tracking_increment, sub_folder):
684
+ """Postprocess a list of result folders to account for dynamic solar tracking.
685
+
686
+ \b
687
+ This function essentially takes .ill files for each state of a dynamic tracking
688
+ system and produces a single .ill file that models the tracking behavior.
689
+
690
+ \b
691
+ Args:
692
+ folder: Results folder containing sub-folders that each represent a state
693
+ of the dynamic solar tracking system. Each sub-folder should contain .ill
694
+ files for that state and the names of these .ill files should be the
695
+ same across all sub-folders.
696
+ sun_up_hours: The .txt file containing the sun-up hours that were simulated.
697
+ wea: The .wea file that was used in the simulation. This will be used to
698
+ determine the solar positions.
699
+ """
700
+ try:
701
+ # load all of the result sub-folders in the folder and sort them
702
+ models = [f for f in os.listdir(folder)
703
+ if os.path.isdir(os.path.join(folder, f)) and
704
+ os.path.isfile(os.path.join(folder, f, 'grids_info.json'))]
705
+ model_num = [int(''.join([i for i in f if i.isdigit()])) for f in models]
706
+ sorted_models = [x for _, x in sorted(zip(model_num, models))]
707
+ models = [os.path.join(folder, f) for f in sorted_models]
708
+
709
+ dest_folder = os.path.join(folder, sub_folder)
710
+ if len(models) == 1: # not a dynamic system; just copy the files
711
+ if not os.path.isdir(dest_folder):
712
+ os.mkdir(dest_folder)
713
+ for f in os.listdir(models[0]):
714
+ shutil.copyfile(
715
+ os.path.join(models[0], f),
716
+ os.path.join(dest_folder, f))
717
+ else:
718
+ wea_obj = Wea.from_file(wea)
719
+ post_process_solar_tracking(
720
+ models, sun_up_hours, wea_obj.location, north,
721
+ tracking_increment, dest_folder)
722
+ except Exception:
723
+ _logger.exception('Failed to compute irradiance metrics.')
724
+ sys.exit(1)
725
+ else:
726
+ sys.exit(0)
727
+
728
+
729
+ @post_process.command('daylight-factor-vis-metadata')
730
+ @click.option(
731
+ '--output-file', '-o', help='Optional JSON file to output the metadata file.',
732
+ type=click.File('w'), default='-', show_default=True
733
+ )
734
+ def daylight_factor_vis(output_file):
735
+ """Write a visualization metadata file for daylight factor."""
736
+ vm_data = {
737
+ 'type': 'VisualizationMetaData',
738
+ 'data_type': Fraction('Daylight Factor').to_dict(),
739
+ 'unit': '%',
740
+ 'legend_parameters': LegendParameters(colors=Colorset.ecotect()).to_dict()
741
+ }
742
+ try:
743
+ output_file.write(json.dumps(vm_data, indent=4))
744
+ except Exception:
745
+ _logger.exception('Failed to write the visualization metadata file.')
746
+ sys.exit(1)
747
+ else:
748
+ sys.exit(0)
749
+
750
+
751
+ @post_process.command('daylight-factor-config')
752
+ @click.option(
753
+ '--folder', '-f', help='Optional relative path for results folder. This value will '
754
+ 'be set as path inside the config file', default='daylight-factor'
755
+ )
756
+ @click.option(
757
+ '--output-file', '-o', help='Optional JSON file to output the config file.',
758
+ type=click.File('w'), default='-', show_default=True
759
+ )
760
+ def daylight_fatcor_config(folder, output_file):
761
+ """Write a vtk-config file for daylight factor."""
762
+ cfg = {
763
+ 'data': [
764
+ {
765
+ 'identifier': 'Daylight Factor',
766
+ 'object_type': 'grid',
767
+ 'unit': 'Percentage',
768
+ 'path': folder,
769
+ 'hide': False,
770
+ 'legend_parameters': {
771
+ 'hide_legend': False,
772
+ 'min': 0,
773
+ 'max': 2,
774
+ 'color_set': 'original'
775
+ }
776
+ }
777
+ ]
778
+ }
779
+ try:
780
+ output_file.write(json.dumps(cfg, indent=4))
781
+ except Exception:
782
+ _logger.exception('Failed to write the config file.')
783
+ sys.exit(1)
784
+ else:
785
+ sys.exit(0)
786
+
787
+
788
+ @post_process.command('point-in-time-vis-metadata')
789
+ @click.option(
790
+ '--metric', '-m', default='illuminance', show_default=True,
791
+ help='Text for the type of metric to be output from the calculation. Choose from: '
792
+ 'illuminance, irradiance, luminance, radiance.'
793
+ )
794
+ @click.option(
795
+ '--output-file', '-o', help='Optional JSON file to output the config file.',
796
+ type=click.File('w'), default='-', show_default=True
797
+ )
798
+ def point_in_time_vis(metric, output_file):
799
+ """Write a visualization metadata file for point-in-time."""
800
+ unit_map = {
801
+ 'illuminance': ['lux', 0, 3000, Illuminance('Illuminance')],
802
+ 'irradiance': ['W/m2', 0, 300, GenericType('Irradiance', unit='W/m2')],
803
+ 'luminance': ['cd/m2', 0, 3000, Luminance('Luminance')],
804
+ 'radiance': ['W/m2-sr', 0, 300, GenericType('Radiance', unit='W/m2-sr')]
805
+ }
806
+ unit_props = unit_map[metric.lower()]
807
+
808
+ vm_data = {
809
+ 'type': 'VisualizationMetaData',
810
+ 'data_type': unit_props[-1].to_dict(),
811
+ 'unit': unit_props[0],
812
+ 'legend_parameters': LegendParameters(
813
+ colors=Colorset.ecotect(), min=unit_props[1], max=unit_props[2]
814
+ ).to_dict()
815
+ }
816
+ try:
817
+ output_file.write(json.dumps(vm_data, indent=4))
818
+ except Exception:
819
+ _logger.exception('Failed to write the visualization metadata file.')
820
+ sys.exit(1)
821
+ else:
822
+ sys.exit(0)
823
+
824
+
825
+ @post_process.command('point-in-time-config')
826
+ @click.option(
827
+ '--metric', '-m', default='illuminance', show_default=True,
828
+ help='Text for the type of metric to be output from the calculation. Choose from: '
829
+ 'illuminance, irradiance, luminance, radiance.'
830
+ )
831
+ @click.option(
832
+ '--folder', '-f', help='Optional relative path for results folder. This value will '
833
+ 'be set as path inside the config file', default='point-in-time'
834
+ )
835
+ @click.option(
836
+ '--output-file', '-o', help='Optional JSON file to output the config file.',
837
+ type=click.File('w'), default='-', show_default=True
838
+ )
839
+ def point_in_time_config(metric, folder, output_file):
840
+ """Write a vtk-config file for a point-in-time study."""
841
+ unit_map = {
842
+ 'illuminance': ['Lux', 0, 3000],
843
+ 'irradiance': ['W/m2', 0, 300],
844
+ 'luminance': ['cd/m2', 0, 3000],
845
+ 'radiance': ['W/m2-sr', 0, 300]
846
+ }
847
+ unit_props = unit_map[metric.lower()]
848
+ cfg = {
849
+ 'data': [
850
+ {
851
+ 'identifier': 'Point-in-time {}'.format(metric.title()),
852
+ 'object_type': 'grid',
853
+ 'unit': unit_props[0],
854
+ 'path': folder,
855
+ 'hide': False,
856
+ 'legend_parameters': {
857
+ 'hide_legend': False,
858
+ 'min': unit_props[1],
859
+ 'max': unit_props[2],
860
+ 'color_set': 'ecotect'
861
+ }
862
+ }
863
+ ]
864
+ }
865
+ try:
866
+ output_file.write(json.dumps(cfg, indent=4))
867
+ except Exception:
868
+ _logger.exception('Failed to write the config file.')
869
+ sys.exit(1)
870
+ else:
871
+ sys.exit(0)
872
+
873
+
874
+ @post_process.command('cumulative-radiation-vis-metadata')
875
+ @click.option(
876
+ '--output-file', '-o', help='Optional JSON file to output the config file.',
877
+ type=click.File('w'), default='-', show_default=True
878
+ )
879
+ def cumulative_radiation_vis(output_file):
880
+ """Write a visualization metadata file for cumulative radiation."""
881
+ rad_vis_metadata = _annual_irradiance_vis_metadata()
882
+ vm_data = rad_vis_metadata['cumulative_radiation']
883
+
884
+ try:
885
+ output_file.write(json.dumps(vm_data, indent=4))
886
+ except Exception:
887
+ _logger.exception('Failed to write the visualization metadata file.')
888
+ sys.exit(1)
889
+ else:
890
+ sys.exit(0)
891
+
892
+
893
+ @post_process.command('cumulative-radiation-config')
894
+ @click.option(
895
+ '--output-file', '-o', help='Optional JSON file to output the config file.',
896
+ type=click.File('w'), default='-', show_default=True
897
+ )
898
+ def cumulative_radiation_config(output_file):
899
+ """Write a vtk-config file for cumulative radiation."""
900
+ rad_config_dict = _annual_irradiance_config()
901
+ cfg = {
902
+ 'data': [
903
+ rad_config_dict['data'][0],
904
+ rad_config_dict['data'][2]
905
+ ]
906
+ }
907
+ try:
908
+ output_file.write(json.dumps(cfg, indent=4))
909
+ except Exception:
910
+ _logger.exception('Failed to write the config file.')
911
+ sys.exit(1)
912
+ else:
913
+ sys.exit(0)
914
+
915
+
916
+ @post_process.command('average-irradiance-vis-metadata')
917
+ @click.option(
918
+ '--output-file', '-o', help='Optional JSON file to output the config file.',
919
+ type=click.File('w'), default='-', show_default=True
920
+ )
921
+ def average_irradiance_vis(output_file):
922
+ """Write a visualization metadata file for cumulative radiation."""
923
+ rad_vis_metadata = _annual_irradiance_vis_metadata()
924
+ vm_data = rad_vis_metadata['average_irradiance']
925
+ try:
926
+ output_file.write(json.dumps(vm_data, indent=4))
927
+ except Exception:
928
+ _logger.exception('Failed to write the visualization metadata file.')
929
+ sys.exit(1)
930
+ else:
931
+ sys.exit(0)
932
+
933
+
934
+ @post_process.command('direct-sun-hours-vis-metadata')
935
+ @click.option(
936
+ '--output-file', '-o', help='Optional JSON file to output the metadata file.',
937
+ type=click.File('w'), default='-', show_default=True
938
+ )
939
+ def direct_sun_hours_vis(output_file):
940
+ """Write a visualization metadata file for direct sun hours."""
941
+ vm_data = {
942
+ 'type': 'VisualizationMetaData',
943
+ 'data_type': Time('Direct Sun Hours').to_dict(),
944
+ 'unit': 'hr',
945
+ 'legend_parameters': LegendParameters(colors=Colorset.ecotect()).to_dict()
946
+ }
947
+ try:
948
+ output_file.write(json.dumps(vm_data, indent=4))
949
+ except Exception:
950
+ _logger.exception('Failed to write the visualization metadata file.')
951
+ sys.exit(1)
952
+ else:
953
+ sys.exit(0)
954
+
955
+
956
+ @post_process.command('direct-sun-hours-config')
957
+ @click.option(
958
+ '--folder', '-f', help='Optional relative path for results folder. This value will '
959
+ 'be set as path inside the config file', default='direct-sun-hours'
960
+ )
961
+ @click.option(
962
+ '--output-file', '-o', help='Optional JSON file to output the config file.',
963
+ type=click.File('w'), default='-', show_default=True
964
+ )
965
+ def direct_sun_hours_config(folder, output_file):
966
+ """Write a vtk-config file for direct sun hours."""
967
+ cfg = {
968
+ 'data': [
969
+ {
970
+ 'identifier': 'Direct Sun Hours',
971
+ 'object_type': 'grid',
972
+ 'unit': 'Hours',
973
+ 'path': folder,
974
+ 'hide': False,
975
+ 'legend_parameters': {
976
+ 'hide_legend': False,
977
+ 'color_set': 'ecotect'
978
+ }
979
+ }
980
+ ]
981
+ }
982
+ try:
983
+ output_file.write(json.dumps(cfg, indent=4))
984
+ except Exception:
985
+ _logger.exception('Failed to write the config file.')
986
+ sys.exit(1)
987
+ else:
988
+ sys.exit(0)
989
+
990
+
991
+ @post_process.command('sky-view-vis-metadata')
992
+ @click.option(
993
+ '--output-file', '-o', help='Optional JSON file to output the metadata file.',
994
+ type=click.File('w'), default='-', show_default=True
995
+ )
996
+ def sky_view_vis(output_file):
997
+ """Write a visualization metadata file for sky view."""
998
+ sky_view_lpar = LegendParameters(min=0, max=100, colors=Colorset.view_study())
999
+ vm_data = {
1000
+ 'type': 'VisualizationMetaData',
1001
+ 'data_type': Fraction('Sky View').to_dict(),
1002
+ 'unit': '%',
1003
+ 'legend_parameters': sky_view_lpar.to_dict()
1004
+ }
1005
+ try:
1006
+ output_file.write(json.dumps(vm_data, indent=4))
1007
+ except Exception:
1008
+ _logger.exception('Failed to write the visualization metadata file.')
1009
+ sys.exit(1)
1010
+ else:
1011
+ sys.exit(0)
1012
+
1013
+
1014
+ @post_process.command('sky-view-config')
1015
+ @click.option(
1016
+ '--folder', '-f', help='Optional relative path for results folder. This value will '
1017
+ 'be set as path inside the config file', default='sky-view'
1018
+ )
1019
+ @click.option(
1020
+ '--output-file', '-o', help='Optional JSON file to output the config file.',
1021
+ type=click.File('w'), default='-', show_default=True
1022
+ )
1023
+ def sky_view_config(folder, output_file):
1024
+ """Write a vtk-config file for daylight factor. """
1025
+ cfg = {
1026
+ 'data': [
1027
+ {
1028
+ 'identifier': 'Sky View',
1029
+ 'object_type': 'grid',
1030
+ 'unit': 'Percentage',
1031
+ 'path': folder,
1032
+ 'hide': False,
1033
+ 'legend_parameters': {
1034
+ 'hide_legend': False,
1035
+ 'min': 0,
1036
+ 'max': 100,
1037
+ 'color_set': 'view_study'
1038
+ }
1039
+ }
1040
+ ]
1041
+ }
1042
+ try:
1043
+ output_file.write(json.dumps(cfg, indent=4))
1044
+ except Exception:
1045
+ _logger.exception('Failed to write the config file.')
1046
+ sys.exit(1)
1047
+ else:
1048
+ sys.exit(0)
1049
+
1050
+
1051
+ @post_process.command('imageless-annual-glare-vis-metadata')
1052
+ @click.option(
1053
+ '--output-file', '-o', help='Optional JSON file to output the metadata file.',
1054
+ type=click.File('w'), default='-', show_default=True
1055
+ )
1056
+ def imageless_annual_glare_vis(output_file):
1057
+ """Write a visualization metadata file for imageless annual glare."""
1058
+ ga_lpar = LegendParameters(min=0, max=100, colors=reversed(Colorset.glare_study()))
1059
+ vm_data = {
1060
+ 'type': 'VisualizationMetaData',
1061
+ 'data_type': Fraction('Glare Autonomy').to_dict(),
1062
+ 'unit': '%',
1063
+ 'legend_parameters': ga_lpar.to_dict()
1064
+ }
1065
+ try:
1066
+ output_file.write(json.dumps(vm_data, indent=4))
1067
+ except Exception:
1068
+ _logger.exception('Failed to write the visualization metadata file.')
1069
+ sys.exit(1)
1070
+ else:
1071
+ sys.exit(0)
1072
+
1073
+
1074
+ @post_process.command('leed-daylight-option-two-vis-metadata')
1075
+ @click.option(
1076
+ '--output-folder', '-o', help='Output folder for vis metadata files.',
1077
+ type=click.Path(exists=False, file_okay=False, dir_okay=True, resolve_path=True),
1078
+ default='visualization', show_default=True
1079
+ )
1080
+ def leed_daylight_option_two_vis(output_folder):
1081
+ """Write five visualization metadata files for LEED Daylight Option Two."""
1082
+ ill_lpar = LegendParameters(min=300, max=3000, colors=Colorset.ecotect())
1083
+ colors = [Color(220, 0, 0), Color(0, 220, 0)]
1084
+ pass_fail_lpar = \
1085
+ LegendParameters(min=0, max=1, colors=colors, segment_count=2, title='Pass/Fail')
1086
+ pass_fail_lpar.ordinal_dictionary = {0: "Fail", 1: "Pass"}
1087
+
1088
+ metric_info_dict = {
1089
+ 'illuminance-9am': {
1090
+ 'type': 'VisualizationMetaData',
1091
+ 'data_type': Illuminance('Illuminance 9am').to_dict(),
1092
+ 'unit': 'lux',
1093
+ 'legend_parameters': ill_lpar.to_dict()
1094
+ },
1095
+ 'illuminance-3pm': {
1096
+ 'type': 'VisualizationMetaData',
1097
+ 'data_type': Illuminance('Illuminance 3pm').to_dict(),
1098
+ 'unit': 'lux',
1099
+ 'legend_parameters': ill_lpar.to_dict()
1100
+ },
1101
+ 'pass-fail-9am': {
1102
+ 'type': 'VisualizationMetaData',
1103
+ 'data_type': GenericType('Pass/Fail 9am', '').to_dict(),
1104
+ 'unit': '',
1105
+ 'legend_parameters': pass_fail_lpar.to_dict()
1106
+ },
1107
+ 'pass-fail-3pm': {
1108
+ 'type': 'VisualizationMetaData',
1109
+ 'data_type': GenericType('Pass/Fail 3pm', '').to_dict(),
1110
+ 'unit': '',
1111
+ 'legend_parameters': pass_fail_lpar.to_dict()
1112
+ },
1113
+ 'pass-fail-combined': {
1114
+ 'type': 'VisualizationMetaData',
1115
+ 'data_type': GenericType('Pass/Fail', '').to_dict(),
1116
+ 'unit': '',
1117
+ 'legend_parameters': pass_fail_lpar.to_dict()
1118
+ }
1119
+ }
1120
+ try:
1121
+ if not os.path.exists(output_folder):
1122
+ os.mkdir(output_folder)
1123
+ for metric, data in metric_info_dict.items():
1124
+ if not os.path.exists(os.path.join(output_folder, metric)):
1125
+ os.mkdir(os.path.join(output_folder, metric))
1126
+ file_path = os.path.join(output_folder, metric, 'vis_metadata.json')
1127
+ with open(file_path, 'w') as fp:
1128
+ json.dump(data, fp, indent=4)
1129
+ except Exception:
1130
+ _logger.exception('Failed to write the visualization metadata files.')
1131
+ sys.exit(1)
1132
+ else:
1133
+ sys.exit(0)
1134
+
1135
+
1136
+ @post_process.command('abnt-nbr-15575-daylight-vis-metadata')
1137
+ @click.option(
1138
+ '--output-folder', '-o', help='Output folder for vis metadata files.',
1139
+ type=click.Path(exists=False, file_okay=False, dir_okay=True, resolve_path=True),
1140
+ default='visualization', show_default=True
1141
+ )
1142
+ def abnt_nbr_15575_daylight_vis(output_folder):
1143
+ """Write four visualization metadata files for ABNT NBR 15575."""
1144
+ ill_lpar = LegendParameters(min=48, max=1000, colors=Colorset.ecotect(),
1145
+ title='Iluminancia')
1146
+
1147
+ metric_info_dict = {
1148
+ '4_930AM': {
1149
+ 'type': 'VisualizationMetaData',
1150
+ 'data_type': Illuminance('23 de abril 9:30h').to_dict(),
1151
+ 'unit': 'lux',
1152
+ 'legend_parameters': ill_lpar.to_dict()
1153
+ },
1154
+ '4_330PM': {
1155
+ 'type': 'VisualizationMetaData',
1156
+ 'data_type': Illuminance('23 de abril 15:30h').to_dict(),
1157
+ 'unit': 'lux',
1158
+ 'legend_parameters': ill_lpar.to_dict()
1159
+ },
1160
+ '10_930AM': {
1161
+ 'type': 'VisualizationMetaData',
1162
+ 'data_type': Illuminance('23 de outubro 9:30h').to_dict(),
1163
+ 'unit': 'lux',
1164
+ 'legend_parameters': ill_lpar.to_dict()
1165
+ },
1166
+ '10_330PM': {
1167
+ 'type': 'VisualizationMetaData',
1168
+ 'data_type': Illuminance('23 de outubro 15:30h').to_dict(),
1169
+ 'unit': 'lux',
1170
+ 'legend_parameters': ill_lpar.to_dict()
1171
+ }
1172
+ }
1173
+ try:
1174
+ if not os.path.exists(output_folder):
1175
+ os.mkdir(output_folder)
1176
+ for metric, data in metric_info_dict.items():
1177
+ if not os.path.exists(os.path.join(output_folder, metric)):
1178
+ os.mkdir(os.path.join(output_folder, metric))
1179
+ file_path = os.path.join(output_folder, metric, 'vis_metadata.json')
1180
+ with open(file_path, 'w') as fp:
1181
+ json.dump(data, fp, indent=4)
1182
+ except Exception:
1183
+ _logger.exception('Failed to write the visualization metadata files.')
1184
+ sys.exit(1)
1185
+ else:
1186
+ sys.exit(0)