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,201 @@
1
+ """Functions for post-processing imageless annual glare outputs.
2
+
3
+ Note: These functions will most likely be moved to a separate package in the near future.
4
+ """
5
+ import json
6
+ import os
7
+
8
+ from .annual import filter_schedule_by_hours, _process_input_folder
9
+
10
+
11
+ def glare_autonomy_to_file(dgp_file, occ_pattern, output_folder, glare_threshold=0.4,
12
+ grid_name=None, total_hours=None):
13
+ """Compute glare autonomy for an dgp file and write the results to a folder.
14
+
15
+ This function generates 1 file for glare autonomy.
16
+
17
+ Args:
18
+ dgp_file: Path to an dgp file generated by Radiance. The dgp file should be
19
+ tab separated and shot NOT have a header. The results for each sensor point
20
+ should be available in a row and and each column should be the daylight
21
+ glare probability value for a sun_up_hour. The number of columns should
22
+ match the number of sun up hours.
23
+ occ_pattern: A list of 0 and 1 values for hours of occupancy.
24
+ output_folder: An output folder where the results will be written to. The folder
25
+ will be created if not exist.
26
+ glare_threshold: A fractional number for the threshold of DGP above which
27
+ conditions are considered to induce glare. Default: 0.4.
28
+ grid_name: An optional name for grid name which will be used to name the output
29
+ files. If None the name of the input file will be used.
30
+ total_hours: An integer for the total number of occupied hours in the
31
+ occupancy schedule. If None, it will be assumed that all of the
32
+ occupied hours are sun-up hours and are already accounted for
33
+ in the the occ_pattern.
34
+
35
+ Returns:
36
+ file.ga
37
+
38
+ """
39
+ if not os.path.isdir(output_folder):
40
+ os.makedirs(output_folder)
41
+
42
+ grid_name = grid_name or os.path.split(dgp_file)[-1][-4:]
43
+ ga = os.path.join(output_folder, 'ga', '%s.ga' % grid_name).replace('\\', '/')
44
+
45
+ folder = os.path.dirname(ga)
46
+ if not os.path.isdir(folder):
47
+ os.makedirs(folder)
48
+
49
+ with open(dgp_file) as results, open(ga, 'w') as gaf:
50
+ for pt_res in results:
51
+ values = (float(res) for res in pt_res.split())
52
+ gar = _glare_autonomy(values, occ_pattern, glare_threshold, total_hours)
53
+ gaf.write(str(gar) + '\n')
54
+
55
+ return ga
56
+
57
+
58
+ def glare_autonomy(dgp_file, occ_pattern, glare_threshold=0.4, total_hours=None):
59
+ """Compute glare autonomy for a given result file.
60
+
61
+ Args:
62
+ dgp_file: Path to a dgp file generated by Radiance. The dgp file should be
63
+ tab separated and shot NOT have a header. The results for each sensor point
64
+ should be available in a row and and each column should be the DGP value for
65
+ a sun_up_hour. The number of columns should match the number of sun up hours.
66
+ occ_pattern: A list of 0 and 1 values for hours of occupancy.
67
+ glare_threshold: Threshold DGP level for glare autonomy. Default: 0.4.
68
+ total_hours: An integer for the total number of occupied hours in the
69
+ occupancy schedule. If None, it will be assumed that all of the
70
+ occupied hours are sun-up hours and are already accounted for
71
+ in the the occ_pattern.
72
+
73
+ Returns:
74
+ A list of glare autonomy values. Number of results in each list matches the
75
+ number of lines in dgp input file.
76
+
77
+ """
78
+ ga = []
79
+ total_occupied_hours = sum(occ_pattern) if total_hours is None else total_hours
80
+ with open(dgp_file) as results:
81
+ for pt_res in results:
82
+ values = (float(res) for res in pt_res.split())
83
+ ga_v = _glare_autonomy(
84
+ values, occ_pattern, glare_threshold, total_occupied_hours
85
+ )
86
+ ga.append(ga_v)
87
+
88
+ return ga
89
+
90
+
91
+ # TODO - support a list of schedules/schedule folder to match the input grids
92
+ def glare_autonomy_from_folder(results_folder, schedule=None, glare_threshold=0.4,
93
+ grids_filter='*'):
94
+ """Compute glare autonomy for a folder.
95
+
96
+ This folder is an output folder of imageless annual glare recipe. Folder should
97
+ include grids_info.json and sun-up-hours.txt - the script uses the list in
98
+ grids_info.json to find the result files for each sensor grid.
99
+
100
+ Args:
101
+ results_folder: Results folder.
102
+ schedule: An annual schedule for 8760 hours of the year as a list of values.
103
+ glare_threshold: Threshold DGP level for glare autonomy. Default: 0.4.
104
+ grids_filter: A pattern to filter the grids. By default all the grids will be
105
+ processed.
106
+
107
+ Returns:
108
+ Tuple[List] - There will be a list for each input sensor grid. Number of results
109
+ in each list matches the number of lines in ill input file.
110
+
111
+ """
112
+ ga = []
113
+
114
+ grids, sun_up_hours = _process_input_folder(results_folder, grids_filter)
115
+ occ_pattern, total_occ, sun_down_occ_hours = \
116
+ filter_schedule_by_hours(sun_up_hours=sun_up_hours, schedule=schedule)
117
+
118
+ for grid in grids:
119
+ dgp_file = os.path.join(results_folder, '%s.dgp' % grid['full_id'])
120
+ ga_r = glare_autonomy(dgp_file, occ_pattern, glare_threshold, total_occ)
121
+ ga.append(ga_r)
122
+
123
+ return ga
124
+
125
+
126
+ def glare_autonomy_to_folder(
127
+ results_folder, schedule=None, glare_threshold=0.4, grids_filter='*',
128
+ sub_folder='metrics'
129
+ ):
130
+ """Compute annual glare autonomy in a folder and write them in a subfolder.
131
+
132
+ This folder is an output folder of imageless annual glare recipe. Folder should
133
+ include grids_info.json and sun-up-hours.txt - the script uses the list in
134
+ grids_info.json to find the result files for each sensor grid.
135
+
136
+ Args:
137
+ results_folder: Results folder.
138
+ schedule: An annual schedule for 8760 hours of the year as a list of values.
139
+ glare_threshold: A fractional number for the threshold of DGP above which
140
+ conditions are considered to induce glare. Default: 0.4.
141
+ grids_filter: A pattern to filter the grids. By default all the grids will be
142
+ processed.
143
+ sub_folder: An optional relative path for subfolder to copy results files.
144
+ Default: metrics
145
+
146
+ Returns:
147
+ str -- Path to results folder.
148
+
149
+ """
150
+ grids, sun_up_hours = _process_input_folder(results_folder, grids_filter)
151
+ occ_pattern, total_occ, sun_down_occ_hours = \
152
+ filter_schedule_by_hours(sun_up_hours=sun_up_hours, schedule=schedule)
153
+
154
+ metrics_folder = os.path.join(results_folder, sub_folder)
155
+ if not os.path.isdir(metrics_folder):
156
+ os.makedirs(metrics_folder)
157
+
158
+ for grid in grids:
159
+ dgp_file = os.path.join(results_folder, '%s.dgp' % grid['full_id'])
160
+ glare_autonomy_to_file(
161
+ dgp_file, occ_pattern, metrics_folder, glare_threshold,
162
+ grid['full_id'], total_occ
163
+ )
164
+
165
+ # copy info.json to all results folders
166
+ grid_info = os.path.join(metrics_folder, 'ga', 'grids_info.json')
167
+ with open(grid_info, 'w') as outf:
168
+ json.dump(grids, outf)
169
+
170
+ return metrics_folder
171
+
172
+
173
+ def _glare_autonomy(values, occ_pattern, glare_threshold, total_hours):
174
+ """Calculate annual glare autonomy for a sensor.
175
+
176
+ Args:
177
+ values: Hourly illuminance values as numbers.
178
+ occ_pattern: A list of 0 and 1 values for hours of occupancy.
179
+ glare_threshold: A fractional number for the threshold of DGP above which
180
+ conditions are considered to induce glare. Default: 0.4.
181
+ total_hours: An integer for the total number of occupied hours, which can be used
182
+ to avoid having to sum occ pattern each time.
183
+
184
+ Returns:
185
+ glare autonomy
186
+ """
187
+ def _percentage(in_v, occ_hours):
188
+ return round(100.0 * in_v / occ_hours, 2)
189
+
190
+ ga_above = 0
191
+ # count hours above glare threshold
192
+ for is_occ, value in zip(occ_pattern, values):
193
+ if is_occ == 0:
194
+ continue
195
+ if value > glare_threshold:
196
+ ga_above += 1
197
+
198
+ # get the number of glare free hours
199
+ ga = total_hours - ga_above
200
+
201
+ return _percentage(ga, total_hours)
@@ -0,0 +1,187 @@
1
+ """Functions for post-processing annual irradiance outputs.
2
+
3
+ Note: These functions will most likely be moved to a separate package in the near future.
4
+ """
5
+ import os
6
+ import shutil
7
+ import json
8
+
9
+ from ladybug.wea import Wea
10
+ from ladybug.datatype.energyflux import EnergyFlux
11
+ from ladybug.datatype.energyintensity import EnergyIntensity
12
+ from ladybug.legend import LegendParameters
13
+
14
+ from .annual import remove_header
15
+
16
+
17
+ def annual_irradiance_to_folder(folder, wea, timestep=1, sub_folder='metrics'):
18
+ """Compute irradiance metrics in a folder and write them in a subfolder.
19
+
20
+ This command generates 3 files for each input grid.
21
+
22
+ * average_irradiance/{grid-name}.res -- Average Irradiance (W/m2)
23
+ * peak_irradiance/{grid-name}.res -- Peak Irradiance (W/m2)
24
+ * cumulative_radiation/{grid-name}.res -- Cumulative Radiation (kWh/m2)
25
+
26
+ Args:
27
+ folder: Results folder from an annual irradiance recipe.
28
+ wea: The .wea file that was used in the annual irradiance simulation. This
29
+ will be used to determine the duration of the analysis for computing
30
+ cumulative radiation.
31
+ timestep: The timestep of the Wea file, which is used to ensure the summed
32
+ row of irradiance yields cumulative radiation over the time period
33
+ of the Wea. (Default: 1).
34
+ sub_folder: An optional relative path for subfolder to copy results
35
+ files. (Default: metrics).
36
+
37
+ Returns:
38
+ str -- Path to results folder.
39
+ """
40
+ # get the time length of the Wea and the list of grids
41
+ wea_len = Wea.count_timesteps(wea) * timestep
42
+ grids = [g.replace('.ill', '') for g in os.listdir(folder) if g.endswith('.ill')]
43
+ grid_info = os.path.join(folder, 'grids_info.json')
44
+
45
+ # write a record of the timestep into the result folder for result processing
46
+ t_step_f = os.path.join(folder, 'timestep.txt')
47
+ with open(t_step_f, 'w') as t_f:
48
+ t_f.write(str(timestep))
49
+
50
+ # setup the folder into which the metrics will be written
51
+ metrics_folder = os.path.join(folder, sub_folder)
52
+ metrics_folders = []
53
+ for sub_f in ('average_irradiance', 'peak_irradiance', 'cumulative_radiation'):
54
+ m_path = os.path.join(metrics_folder, sub_f)
55
+ metrics_folders.append(m_path)
56
+ if not os.path.isdir(m_path):
57
+ os.makedirs(m_path)
58
+ grid_info_copy = os.path.join(m_path, 'grids_info.json')
59
+ shutil.copyfile(grid_info, grid_info_copy)
60
+
61
+ # loop through the grids and compute metrics
62
+ for grid in grids:
63
+ input_matrix = os.path.join(folder, '{}.ill'.format(grid))
64
+ first_line, input_file = remove_header(input_matrix)
65
+ avg = os.path.join(metrics_folders[0], '{}.res'.format(grid))
66
+ pk = os.path.join(metrics_folders[1], '{}.res'.format(grid))
67
+ cml = os.path.join(metrics_folders[2], '{}.res'.format(grid))
68
+ with open(avg, 'w') as avg_i, open(pk, 'w') as pk_i, open(cml, 'w') as cml_r:
69
+ # calculate the values for the first line
70
+ values = [float(v) for v in first_line.split()]
71
+ total_val = sum(values)
72
+ avg_i.write('{}\n'.format(total_val / wea_len))
73
+ pk_i.write('{}\n'.format(max(values)))
74
+ cml_r.write('{}\n'.format(total_val / (timestep * 1000)))
75
+
76
+ # write rest of the lines
77
+ for line in input_file:
78
+ try:
79
+ values = [float(v) for v in line.split()]
80
+ total_val = sum(values)
81
+ pk_i.write('{}\n'.format(max(values)))
82
+ avg_i.write('{}\n'.format(total_val / wea_len))
83
+ cml_r.write('{}\n'.format(total_val / (timestep * 1000)))
84
+ except ValueError:
85
+ pass # last line of the file
86
+
87
+ metric_info_dict = _annual_irradiance_vis_metadata()
88
+ for metric, data in metric_info_dict.items():
89
+ file_path = os.path.join(metrics_folder, metric, 'vis_metadata.json')
90
+ with open(file_path, 'w') as fp:
91
+ json.dump(data, fp, indent=4)
92
+
93
+ return metrics_folder
94
+
95
+
96
+ def _annual_irradiance_vis_metadata():
97
+ """Return visualization metadata for annual irradiance."""
98
+ cumulative_radiation_lpar = LegendParameters(min=0)
99
+ peak_irradiance_lpar = LegendParameters(min=0)
100
+ average_irradiance_lpar = LegendParameters(min=0)
101
+
102
+ metric_info_dict = {
103
+ 'cumulative_radiation': {
104
+ 'type': 'VisualizationMetaData',
105
+ 'data_type': EnergyIntensity('Cumulative Radiance').to_dict(),
106
+ 'unit': 'kWh/m2',
107
+ 'legend_parameters': cumulative_radiation_lpar.to_dict()
108
+ },
109
+ 'peak_irradiance': {
110
+ 'type': 'VisualizationMetaData',
111
+ 'data_type': EnergyFlux('Peak Irradiance').to_dict(),
112
+ 'unit': 'W/m2',
113
+ 'legend_parameters': peak_irradiance_lpar.to_dict()
114
+ },
115
+ 'average_irradiance': {
116
+ 'type': 'VisualizationMetaData',
117
+ 'data_type': EnergyFlux('Average Irradiance').to_dict(),
118
+ 'unit': 'W/m2',
119
+ 'legend_parameters': average_irradiance_lpar.to_dict()
120
+ }
121
+ }
122
+
123
+ return metric_info_dict
124
+
125
+
126
+ def _annual_irradiance_config():
127
+ """Return vtk-config for annual irradiance. """
128
+ cfg = {
129
+ "data": [
130
+ {
131
+ "identifier": "Cumulative Radiation",
132
+ "object_type": "grid",
133
+ "unit": "kW/m2",
134
+ "path": 'cumulative_radiation',
135
+ "hide": False,
136
+ "legend_parameters": {
137
+ "hide_legend": False,
138
+ "color_set": "original",
139
+ "min": 0,
140
+ "max": 1400,
141
+ "label_parameters": {
142
+ "color": [34, 247, 10],
143
+ "size": 0,
144
+ "bold": True
145
+ }
146
+ }
147
+ },
148
+ {
149
+ "identifier": "Peak Irradiance",
150
+ "object_type": "grid",
151
+ "unit": "W/m2",
152
+ "path": 'peak_irradiance',
153
+ "hide": False,
154
+ "legend_parameters": {
155
+ "hide_legend": False,
156
+ "color_set": "original",
157
+ "min": 0,
158
+ "max": 200,
159
+ "label_parameters": {
160
+ "size": 0,
161
+ "color": [34, 247, 10],
162
+ "bold": True
163
+ }
164
+ }
165
+ },
166
+ {
167
+ "identifier": "Average Irradiance",
168
+ "object_type": "grid",
169
+ "unit": "W/m2",
170
+ "path": 'average_irradiance',
171
+ "hide": False,
172
+ "legend_parameters": {
173
+ "hide_legend": False,
174
+ "color_set": "original",
175
+ "min": 0,
176
+ "max": 200,
177
+ "label_parameters": {
178
+ "color": [34, 247, 10],
179
+ "size": 0,
180
+ "bold": True
181
+ }
182
+ }
183
+ }
184
+ ]
185
+ }
186
+
187
+ return cfg
@@ -0,0 +1,119 @@
1
+ """Functions for post-processing daylight outputs into electric lighting schedules."""
2
+ import os
3
+
4
+ from .annual import generate_default_schedule, _process_input_folder
5
+
6
+
7
+ def daylight_control_schedules(
8
+ results_folder, base_schedule=None, ill_setpoint=300,
9
+ min_power_in=0.3, min_light_out=0.2, off_at_min=False
10
+ ):
11
+ """Generate electric lighting schedules from annual daylight results.
12
+
13
+ Such controls will dim the lights according to whether the illuminance values
14
+ at the sensor locations are at a target illuminance setpoint. The results can be
15
+ used to account for daylight controls in energy simulations.
16
+
17
+ This function will generate one schedule per sensor grid in the simulation. Each
18
+ grid should have sensors at the locations in space where daylight dimming sensors
19
+ are located. Grids with one, two, or more sensors can be used to model setups
20
+ where fractions of each room are controlled by different sensors. If the sensor
21
+ grids are distributed over the entire floor of the rooms, the resulting schedules
22
+ will be idealized, where light dimming has been optimized to supply the minimum
23
+ illuminance setpoint everywhere in the room.
24
+
25
+ Args:
26
+ results_folder: The path to the folder containing the annual daylight
27
+ result files.
28
+ base_schedule: A list of 8760 fractional values for the lighting schedule
29
+ representing the usage of lights without any daylight controls. The
30
+ values of this schedule will be multiplied by the hourly dimming
31
+ fraction to yield the output lighting schedules. If None, a schedule
32
+ from 9AM to 5PM on weekdays will be used. (Default: None).
33
+ ill_setpoint: A number for the illuminance setpoint in lux beyond which
34
+ electric lights are dimmed if there is sufficient daylight.
35
+ Some common setpoints are listed below. (Default: 300 lux).
36
+
37
+ * 50 lux - Corridors and hallways.
38
+ * 150 lux - Computer work spaces (screens provide illumination).
39
+ * 300 lux - Paper work spaces (reading from surfaces that need illumination).
40
+ * 500 lux - Retail spaces or museums illuminating merchandise/artifacts.
41
+ * 1000 lux - Operating rooms and workshops where light is needed for safety.
42
+
43
+ min_power_in: A number between 0 and 1 for the the lowest power the lighting
44
+ system can dim down to, expressed as a fraction of maximum
45
+ input power. (Default: 0.3).
46
+ min_light_out: A number between 0 and 1 the lowest lighting output the lighting
47
+ system can dim down to, expressed as a fraction of maximum light
48
+ output. Note that setting this to 1 means lights aren't dimmed at
49
+ all until the illuminance setpoint is reached. This can be used to
50
+ approximate manual light-switching behavior when used in conjunction
51
+ with the off_at_min input below. (Default: 0.2).
52
+ off_at_min: Boolean to note whether lights should switch off completely when
53
+ they get to the minimum power input. (Default: False).
54
+
55
+ Returns:
56
+ A tuple with two values.
57
+
58
+ - schedules: A list of lists where each sub-list represents an electric
59
+ lighting dimming schedule for a sensor grid.
60
+
61
+ - schedule_ids: A list of text strings for the recommended names of the
62
+ electric lighting schedules.
63
+ """
64
+ # process the base schedule input into a list of values
65
+ if base_schedule is None:
66
+ base_schedule = generate_default_schedule()
67
+
68
+ # get the relevant .ill files
69
+ grids, sun_up_hours = _process_input_folder(results_folder, '*')
70
+ sun_up_hours = [int(h) for h in sun_up_hours]
71
+
72
+ # get the dimming fractions for each sensor grid from the .ill files
73
+ dim_fracts = []
74
+ for grid_info in grids:
75
+ ill_file = os.path.join(results_folder, '%s.ill' % grid_info['full_id'])
76
+ fract_list = _file_to_dimming_fraction(
77
+ ill_file, sun_up_hours, ill_setpoint, min_power_in,
78
+ min_light_out, off_at_min
79
+ )
80
+ dim_fracts.append(fract_list)
81
+
82
+ # create the schedule by combining the base schedule with the dimming fraction
83
+ schedules, schedule_ids = [], []
84
+ for grid_info, dim_fract in zip(grids, dim_fracts):
85
+ sch_vals = [b_val * d_val for b_val, d_val in zip(base_schedule, dim_fract)]
86
+ sch_id = '{} Daylight Control'.format(grid_info['full_id'])
87
+ schedules.append(sch_vals)
88
+ schedule_ids.append(sch_id)
89
+ return schedules, schedule_ids
90
+
91
+
92
+ def _file_to_dimming_fraction(ill_file, su_pattern, setpt, m_pow, m_lgt, off_m):
93
+ """Compute hourly dimming fractions for a given result file."""
94
+ # get a base schedule of dimming fractions for the sun-up hours
95
+ su_values = [0] * len(su_pattern)
96
+ sensor_count = 0
97
+ with open(ill_file) as results:
98
+ for pt_res in results:
99
+ sensor_count += 1
100
+ for i, val in enumerate(pt_res.split()):
101
+ su_values[i] += _dimming_from_ill(float(val), setpt, m_pow, m_lgt, off_m)
102
+ su_values = [val / sensor_count for val in su_values]
103
+
104
+ # account for the hours where the sun is not up
105
+ dim_fract = [1] * 8760
106
+ for val, hr in zip(su_values, su_pattern):
107
+ dim_fract[hr] = float(val)
108
+ return dim_fract
109
+
110
+
111
+ def _dimming_from_ill(ill_val, ill_setpt, min_pow, min_light, off_at_min):
112
+ """Compute the dimming fraction from an illuminance value."""
113
+ if ill_val > ill_setpt: # dimmed all of the way
114
+ return 0 if off_at_min else min_pow
115
+ elif ill_val <= min_light: # not dimmed at all
116
+ return 1
117
+ else: # partially dimmed
118
+ fract_dim = (ill_setpt - ill_val) / (ill_setpt - min_light)
119
+ return fract_dim + ((1 - fract_dim) * min_pow)