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.
- honeybee_radiance/__init__.py +11 -0
- honeybee_radiance/__main__.py +4 -0
- honeybee_radiance/_extend_honeybee.py +93 -0
- honeybee_radiance/cli/__init__.py +88 -0
- honeybee_radiance/cli/dc.py +400 -0
- honeybee_radiance/cli/edit.py +529 -0
- honeybee_radiance/cli/glare.py +118 -0
- honeybee_radiance/cli/grid.py +859 -0
- honeybee_radiance/cli/lib.py +458 -0
- honeybee_radiance/cli/modifier.py +133 -0
- honeybee_radiance/cli/mtx.py +226 -0
- honeybee_radiance/cli/multiphase.py +1034 -0
- honeybee_radiance/cli/octree.py +640 -0
- honeybee_radiance/cli/postprocess.py +1186 -0
- honeybee_radiance/cli/raytrace.py +219 -0
- honeybee_radiance/cli/rpict.py +125 -0
- honeybee_radiance/cli/schedule.py +56 -0
- honeybee_radiance/cli/setconfig.py +63 -0
- honeybee_radiance/cli/sky.py +545 -0
- honeybee_radiance/cli/study.py +66 -0
- honeybee_radiance/cli/sunpath.py +331 -0
- honeybee_radiance/cli/threephase.py +255 -0
- honeybee_radiance/cli/translate.py +400 -0
- honeybee_radiance/cli/util.py +121 -0
- honeybee_radiance/cli/view.py +261 -0
- honeybee_radiance/cli/viewfactor.py +347 -0
- honeybee_radiance/config.json +6 -0
- honeybee_radiance/config.py +427 -0
- honeybee_radiance/dictutil.py +50 -0
- honeybee_radiance/dynamic/__init__.py +5 -0
- honeybee_radiance/dynamic/group.py +479 -0
- honeybee_radiance/dynamic/multiphase.py +557 -0
- honeybee_radiance/dynamic/state.py +718 -0
- honeybee_radiance/dynamic/stategeo.py +352 -0
- honeybee_radiance/geometry/__init__.py +13 -0
- honeybee_radiance/geometry/bubble.py +42 -0
- honeybee_radiance/geometry/cone.py +215 -0
- honeybee_radiance/geometry/cup.py +54 -0
- honeybee_radiance/geometry/cylinder.py +197 -0
- honeybee_radiance/geometry/geometrybase.py +37 -0
- honeybee_radiance/geometry/instance.py +40 -0
- honeybee_radiance/geometry/mesh.py +38 -0
- honeybee_radiance/geometry/polygon.py +174 -0
- honeybee_radiance/geometry/ring.py +214 -0
- honeybee_radiance/geometry/source.py +182 -0
- honeybee_radiance/geometry/sphere.py +178 -0
- honeybee_radiance/geometry/tube.py +46 -0
- honeybee_radiance/lib/__init__.py +1 -0
- honeybee_radiance/lib/_loadmodifiers.py +72 -0
- honeybee_radiance/lib/_loadmodifiersets.py +69 -0
- honeybee_radiance/lib/modifiers.py +58 -0
- honeybee_radiance/lib/modifiersets.py +63 -0
- honeybee_radiance/lightpath.py +204 -0
- honeybee_radiance/lightsource/__init__.py +1 -0
- honeybee_radiance/lightsource/_gendaylit.py +479 -0
- honeybee_radiance/lightsource/dictutil.py +49 -0
- honeybee_radiance/lightsource/ground.py +160 -0
- honeybee_radiance/lightsource/sky/__init__.py +7 -0
- honeybee_radiance/lightsource/sky/_skybase.py +177 -0
- honeybee_radiance/lightsource/sky/certainirradiance.py +232 -0
- honeybee_radiance/lightsource/sky/cie.py +378 -0
- honeybee_radiance/lightsource/sky/climatebased.py +501 -0
- honeybee_radiance/lightsource/sky/hemisphere.py +160 -0
- honeybee_radiance/lightsource/sky/skydome.py +113 -0
- honeybee_radiance/lightsource/sky/skymatrix.py +163 -0
- honeybee_radiance/lightsource/sky/strutil.py +34 -0
- honeybee_radiance/lightsource/sky/sunmatrix.py +212 -0
- honeybee_radiance/lightsource/sunpath.py +247 -0
- honeybee_radiance/modifier/__init__.py +3 -0
- honeybee_radiance/modifier/material/__init__.py +30 -0
- honeybee_radiance/modifier/material/absdf.py +477 -0
- honeybee_radiance/modifier/material/antimatter.py +54 -0
- honeybee_radiance/modifier/material/ashik2.py +51 -0
- honeybee_radiance/modifier/material/brtdfunc.py +81 -0
- honeybee_radiance/modifier/material/bsdf.py +292 -0
- honeybee_radiance/modifier/material/dielectric.py +53 -0
- honeybee_radiance/modifier/material/glass.py +431 -0
- honeybee_radiance/modifier/material/glow.py +246 -0
- honeybee_radiance/modifier/material/illum.py +51 -0
- honeybee_radiance/modifier/material/interface.py +49 -0
- honeybee_radiance/modifier/material/light.py +206 -0
- honeybee_radiance/modifier/material/materialbase.py +36 -0
- honeybee_radiance/modifier/material/metal.py +167 -0
- honeybee_radiance/modifier/material/metal2.py +41 -0
- honeybee_radiance/modifier/material/metdata.py +41 -0
- honeybee_radiance/modifier/material/metfunc.py +41 -0
- honeybee_radiance/modifier/material/mirror.py +340 -0
- honeybee_radiance/modifier/material/mist.py +86 -0
- honeybee_radiance/modifier/material/plasdata.py +58 -0
- honeybee_radiance/modifier/material/plasfunc.py +59 -0
- honeybee_radiance/modifier/material/plastic.py +354 -0
- honeybee_radiance/modifier/material/plastic2.py +58 -0
- honeybee_radiance/modifier/material/prism1.py +57 -0
- honeybee_radiance/modifier/material/prism2.py +48 -0
- honeybee_radiance/modifier/material/spotlight.py +50 -0
- honeybee_radiance/modifier/material/trans.py +518 -0
- honeybee_radiance/modifier/material/trans2.py +49 -0
- honeybee_radiance/modifier/material/transdata.py +50 -0
- honeybee_radiance/modifier/material/transfunc.py +53 -0
- honeybee_radiance/modifier/mixture/__init__.py +6 -0
- honeybee_radiance/modifier/mixture/mixdata.py +49 -0
- honeybee_radiance/modifier/mixture/mixfunc.py +54 -0
- honeybee_radiance/modifier/mixture/mixpict.py +52 -0
- honeybee_radiance/modifier/mixture/mixtext.py +66 -0
- honeybee_radiance/modifier/mixture/mixturebase.py +28 -0
- honeybee_radiance/modifier/modifierbase.py +40 -0
- honeybee_radiance/modifier/pattern/__init__.py +9 -0
- honeybee_radiance/modifier/pattern/brightdata.py +49 -0
- honeybee_radiance/modifier/pattern/brightfunc.py +47 -0
- honeybee_radiance/modifier/pattern/brighttext.py +81 -0
- honeybee_radiance/modifier/pattern/colordata.py +56 -0
- honeybee_radiance/modifier/pattern/colorfunc.py +47 -0
- honeybee_radiance/modifier/pattern/colorpict.py +54 -0
- honeybee_radiance/modifier/pattern/colortext.py +73 -0
- honeybee_radiance/modifier/pattern/patternbase.py +34 -0
- honeybee_radiance/modifier/texture/__init__.py +4 -0
- honeybee_radiance/modifier/texture/texdata.py +29 -0
- honeybee_radiance/modifier/texture/texfunc.py +26 -0
- honeybee_radiance/modifier/texture/texturebase.py +27 -0
- honeybee_radiance/modifierset.py +1091 -0
- honeybee_radiance/mutil.py +60 -0
- honeybee_radiance/postprocess/__init__.py +1 -0
- honeybee_radiance/postprocess/annual.py +108 -0
- honeybee_radiance/postprocess/annualdaylight.py +425 -0
- honeybee_radiance/postprocess/annualglare.py +201 -0
- honeybee_radiance/postprocess/annualirradiance.py +187 -0
- honeybee_radiance/postprocess/electriclight.py +119 -0
- honeybee_radiance/postprocess/en17037.py +261 -0
- honeybee_radiance/postprocess/leed.py +304 -0
- honeybee_radiance/postprocess/solartracking.py +90 -0
- honeybee_radiance/primitive.py +554 -0
- honeybee_radiance/properties/__init__.py +1 -0
- honeybee_radiance/properties/_base.py +390 -0
- honeybee_radiance/properties/aperture.py +197 -0
- honeybee_radiance/properties/door.py +198 -0
- honeybee_radiance/properties/face.py +123 -0
- honeybee_radiance/properties/model.py +1291 -0
- honeybee_radiance/properties/room.py +490 -0
- honeybee_radiance/properties/shade.py +186 -0
- honeybee_radiance/properties/shademesh.py +116 -0
- honeybee_radiance/putil.py +44 -0
- honeybee_radiance/reader.py +214 -0
- honeybee_radiance/sensor.py +166 -0
- honeybee_radiance/sensorgrid.py +1008 -0
- honeybee_radiance/view.py +1101 -0
- honeybee_radiance/writer.py +951 -0
- honeybee_radiance-1.66.190.dist-info/METADATA +89 -0
- honeybee_radiance-1.66.190.dist-info/RECORD +152 -0
- honeybee_radiance-1.66.190.dist-info/WHEEL +5 -0
- honeybee_radiance-1.66.190.dist-info/entry_points.txt +2 -0
- honeybee_radiance-1.66.190.dist-info/licenses/LICENSE +661 -0
- 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)
|