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,60 @@
|
|
|
1
|
+
"""Modifier utility functions."""
|
|
2
|
+
import honeybee_radiance.modifier.material as material
|
|
3
|
+
import honeybee_radiance.modifier.mixture as mixture
|
|
4
|
+
import honeybee_radiance.modifier.pattern as pattern
|
|
5
|
+
import honeybee_radiance.modifier.texture as texture
|
|
6
|
+
from honeybee_radiance.primitive import Primitive, Void
|
|
7
|
+
|
|
8
|
+
_MAPPER = {'bsdf': 'BSDF', 'absdf': 'aBSDF', 'brtdfunc': 'BRTDfunc'}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def modifier_class_from_type_string(type_string):
|
|
12
|
+
"""Get the class of any modifier using its 'type' string.
|
|
13
|
+
|
|
14
|
+
This function is equivalent to the primitive_class_from_type_string function
|
|
15
|
+
but it is only for modifiers (not geometry) and is slightly faster when one
|
|
16
|
+
is sure that the type_string is a modifier.
|
|
17
|
+
|
|
18
|
+
Note that this function returns the class itself and not a class instance.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
type_string: Text for the name of a modifier module/class. This should
|
|
22
|
+
be the same as the 'type' key used in the dictionary representation
|
|
23
|
+
of the modifier.
|
|
24
|
+
"""
|
|
25
|
+
lower_str = type_string.lower()
|
|
26
|
+
if lower_str == 'void':
|
|
27
|
+
return Void
|
|
28
|
+
elif lower_str in Primitive.MATERIALTYPES:
|
|
29
|
+
target_module = material
|
|
30
|
+
elif lower_str in Primitive.MIXTURETYPES:
|
|
31
|
+
target_module = mixture
|
|
32
|
+
elif lower_str in Primitive.PATTERNTYPES:
|
|
33
|
+
target_module = pattern
|
|
34
|
+
elif lower_str in Primitive.TEXTURETYPES:
|
|
35
|
+
target_module = texture
|
|
36
|
+
else:
|
|
37
|
+
if lower_str in _MAPPER:
|
|
38
|
+
return getattr(material, _MAPPER[lower_str])
|
|
39
|
+
else:
|
|
40
|
+
raise ValueError('%s is not a Radiance modifier.' % type_string)
|
|
41
|
+
|
|
42
|
+
return getattr(target_module, lower_str.capitalize())
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def dict_to_modifier(mdict):
|
|
46
|
+
"""Convert a dictionary representation of any modifier to a class instance.
|
|
47
|
+
|
|
48
|
+
The returned object will have the correct class type and will not be the
|
|
49
|
+
generic Modifier base class. Note that this function is recursive and will
|
|
50
|
+
re-serialize modifiers of modifiers.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
mdict: A dictionary of any Radiance Modifier.
|
|
54
|
+
"""
|
|
55
|
+
mod_class = modifier_class_from_type_string(mdict['type'])
|
|
56
|
+
|
|
57
|
+
if 'values' in mdict:
|
|
58
|
+
return mod_class.from_primitive_dict(mdict)
|
|
59
|
+
else:
|
|
60
|
+
return mod_class.from_dict(mdict)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Modules for post-processing simulation outputs."""
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""Shared functions for post-processing annual results."""
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
from ..writer import _filter_by_pattern
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def generate_default_schedule(weekday=None, weekend=None):
|
|
9
|
+
"""Create a list of 8760 values based on a daily schedule for weekend and weekday.
|
|
10
|
+
|
|
11
|
+
The default is set to align with IES LM 83 23, which assumes 8am-6pm for all
|
|
12
|
+
365 days of the year.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
weekday: A list of 24 values for each hour of a weekday. The values can
|
|
16
|
+
be 0 or 1.
|
|
17
|
+
weekend: A list of 24 values for each hour of a weekend day. The values can
|
|
18
|
+
be 0 or 1.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
List -- A list of 8760 values for the year.
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
weekend = weekend or \
|
|
25
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0]
|
|
26
|
+
weekday = weekday or \
|
|
27
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0]
|
|
28
|
+
assert len(weekend) == 24, 'Weekend list should be 24 values.'
|
|
29
|
+
assert len(weekday) == 24, 'Weekend list should be 24 values.'
|
|
30
|
+
weekend = [0 if v == 0 else 1 for v in weekend]
|
|
31
|
+
weekday = [0 if v == 0 else 1 for v in weekday]
|
|
32
|
+
all_values = []
|
|
33
|
+
day_counter = 0
|
|
34
|
+
week_counter = 1
|
|
35
|
+
while day_counter < 365:
|
|
36
|
+
day_counter += 1
|
|
37
|
+
if week_counter < 7:
|
|
38
|
+
if week_counter == 1:
|
|
39
|
+
all_values.extend(weekend)
|
|
40
|
+
else:
|
|
41
|
+
all_values.extend(weekday)
|
|
42
|
+
week_counter += 1
|
|
43
|
+
else:
|
|
44
|
+
all_values.extend(weekend)
|
|
45
|
+
week_counter = 1
|
|
46
|
+
return all_values
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# TODO: support smaller timesteps - currently this works only for hourly calculations
|
|
50
|
+
def filter_schedule_by_hours(sun_up_hours, schedule=None):
|
|
51
|
+
"""Filter an annual schedule based on sun up hours.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
sun_up_hours: A list of sun up hours as integers.
|
|
55
|
+
schedule: A list of 8760 values for the occupancy schedule.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
A tuple with two values.
|
|
59
|
+
|
|
60
|
+
occ_pattern -- A filtered version of the annual schedule that only
|
|
61
|
+
includes the sun-up-hours.
|
|
62
|
+
|
|
63
|
+
total_hours -- An integer for the total occupied hours of the schedule.
|
|
64
|
+
|
|
65
|
+
sun_down_occ_hours -- An integer for the number of occupied hours where
|
|
66
|
+
the sun is down and there's no possibility of being daylit or
|
|
67
|
+
experiencing glare.
|
|
68
|
+
"""
|
|
69
|
+
schedule = schedule or generate_default_schedule()
|
|
70
|
+
occ_pattern = [schedule[int(h)] for h in sun_up_hours]
|
|
71
|
+
sun_down_sch = schedule[:] # copy the schedule in place
|
|
72
|
+
for h in reversed(sun_up_hours):
|
|
73
|
+
sun_down_sch.pop(int(h))
|
|
74
|
+
return occ_pattern, sum(schedule), sum(sun_down_sch)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _process_input_folder(folder, filter_pattern):
|
|
78
|
+
"""Process an annual daylight results folder.
|
|
79
|
+
|
|
80
|
+
This returns grids_info and sun-up-hours.
|
|
81
|
+
"""
|
|
82
|
+
suh_fp = os.path.join(folder, 'sun-up-hours.txt')
|
|
83
|
+
with open(suh_fp) as suh_file:
|
|
84
|
+
sun_up_hours = [float(hour) for hour in suh_file.readlines()]
|
|
85
|
+
|
|
86
|
+
info = os.path.join(folder, 'grids_info.json')
|
|
87
|
+
with open(info) as data_f:
|
|
88
|
+
data = json.load(data_f)
|
|
89
|
+
|
|
90
|
+
# filter grids if there is a filtering pattern
|
|
91
|
+
grids = _filter_by_pattern(data, filter=filter_pattern)
|
|
92
|
+
|
|
93
|
+
return grids, sun_up_hours
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def remove_header(input_file):
|
|
97
|
+
"""Remove the header text from a Radiance matrix file."""
|
|
98
|
+
inf = open(input_file)
|
|
99
|
+
first_line = next(inf)
|
|
100
|
+
if first_line[:10] == '#?RADIANCE':
|
|
101
|
+
for line in inf:
|
|
102
|
+
if line[:7] == 'FORMAT=':
|
|
103
|
+
# pass next empty line
|
|
104
|
+
next(inf)
|
|
105
|
+
first_line = next(inf)
|
|
106
|
+
break
|
|
107
|
+
continue
|
|
108
|
+
return first_line, inf
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
"""Functions for post-processing annual daylight 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 ladybug.datatype.fraction import Fraction
|
|
9
|
+
from ladybug.legend import LegendParameters
|
|
10
|
+
|
|
11
|
+
from .annual import filter_schedule_by_hours, _process_input_folder
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _metrics(values, occ_pattern, threshold, min_t, max_t, total_hours,
|
|
15
|
+
sun_down_occ_hours):
|
|
16
|
+
"""Calculate annual metrics for a sensor.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
values: Hourly illuminance values as numbers.
|
|
20
|
+
occ_pattern: A list of 0 and 1 values for hours of occupancy.
|
|
21
|
+
threshold: Threshold value for daylight autonomy. Default: 300.
|
|
22
|
+
min_t: Minimum threshold for useful daylight illuminance. Default: 100.
|
|
23
|
+
max_t: Maximum threshold for useful daylight illuminance. Default: 3000.
|
|
24
|
+
total_hours: An integer for the total number of occupied hours,
|
|
25
|
+
which can be used to avoid having to sum occ pattern each time.
|
|
26
|
+
sun_down_occ_hours: An integer for the total number of occupied hours where
|
|
27
|
+
the sun is down. This is often needed to properly tally all hours that
|
|
28
|
+
have illuminance below the threshold.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Tuple -- daylight autonomy, continuous daylight autonomy, lower useful daylight
|
|
32
|
+
illuminance, useful daylight illuminance, higher useful daylight illuminance
|
|
33
|
+
"""
|
|
34
|
+
def _percentage(in_v, occ_hours):
|
|
35
|
+
return round(100.0 * in_v / occ_hours, 2)
|
|
36
|
+
|
|
37
|
+
da = 0
|
|
38
|
+
cda = 0
|
|
39
|
+
udi_lower = sun_down_occ_hours
|
|
40
|
+
udi = 0
|
|
41
|
+
udi_upper = 0
|
|
42
|
+
|
|
43
|
+
for is_occ, value in zip(occ_pattern, values):
|
|
44
|
+
if is_occ == 0:
|
|
45
|
+
continue
|
|
46
|
+
if value > threshold:
|
|
47
|
+
da += 1
|
|
48
|
+
cda += 1
|
|
49
|
+
else:
|
|
50
|
+
cda += value / threshold
|
|
51
|
+
|
|
52
|
+
if min_t > value:
|
|
53
|
+
udi_lower += 1
|
|
54
|
+
elif value > max_t:
|
|
55
|
+
udi_upper += 1
|
|
56
|
+
else:
|
|
57
|
+
udi += 1
|
|
58
|
+
|
|
59
|
+
return _percentage(da, total_hours), _percentage(cda, total_hours), \
|
|
60
|
+
_percentage(udi_lower, total_hours), _percentage(udi, total_hours), \
|
|
61
|
+
_percentage(udi_upper, total_hours)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def metrics(ill_file, occ_pattern, threshold=300, min_t=100, max_t=3000,
|
|
65
|
+
total_hours=None, sun_down_occ_hours=0):
|
|
66
|
+
"""Compute annual metrics for a given result file.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
ill_file: Path to an ill file generated by Radiance. The ill file should be
|
|
70
|
+
tab separated and shot NOT have a header. The results for each sensor point
|
|
71
|
+
should be available in a row and and each column should be the illuminance
|
|
72
|
+
value for a sun_up_hour. The number of columns should match the number of
|
|
73
|
+
sun up hours.
|
|
74
|
+
occ_pattern: A list of 0 and 1 values for hours of occupancy.
|
|
75
|
+
threshold: Threshold illuminance level for daylight autonomy. Default: 300.
|
|
76
|
+
min_t: Minimum threshold for useful daylight illuminance. Default: 100.
|
|
77
|
+
max_t: Maximum threshold for useful daylight illuminance. Default: 3000.
|
|
78
|
+
total_hours: An integer for the total number of occupied hours in the
|
|
79
|
+
occupancy schedule. If None, it will be assumed that all of the
|
|
80
|
+
occupied hours are sun-up hours and are already accounted for
|
|
81
|
+
in the the occ_pattern.
|
|
82
|
+
sun_down_occ_hours: An integer for the total number of occupied hours where
|
|
83
|
+
the sun is down. This is often needed to properly tally all hours that
|
|
84
|
+
have illuminance below the threshold.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Tuple(List, List, List, List, List) -- 5 lists for daylight autonomy,
|
|
88
|
+
continuous daylight autonomy, lower than useful daylight illuminance,
|
|
89
|
+
useful daylight illuminance and higher than useful daylight illuminance.
|
|
90
|
+
Number of results in each list matches the number of lines in ill input file.
|
|
91
|
+
|
|
92
|
+
"""
|
|
93
|
+
da = []
|
|
94
|
+
cda = []
|
|
95
|
+
udi = []
|
|
96
|
+
udi_lower = []
|
|
97
|
+
udi_upper = []
|
|
98
|
+
total_occupied_hours = sum(occ_pattern) if total_hours is None else total_hours
|
|
99
|
+
with open(ill_file) as results:
|
|
100
|
+
for pt_res in results:
|
|
101
|
+
values = (float(res) for res in pt_res.split())
|
|
102
|
+
da_v, cda_v, udi_lower_v, udi_v, udi_upper_v = _metrics(
|
|
103
|
+
values, occ_pattern, threshold, min_t, max_t,
|
|
104
|
+
total_occupied_hours, sun_down_occ_hours
|
|
105
|
+
)
|
|
106
|
+
da.append(da_v)
|
|
107
|
+
cda.append(cda_v)
|
|
108
|
+
udi_lower.append(udi_lower_v)
|
|
109
|
+
udi.append(udi_v)
|
|
110
|
+
udi_upper.append(udi_upper_v)
|
|
111
|
+
|
|
112
|
+
return da, cda, udi_lower, udi, udi_upper
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def metrics_to_files(ill_file, occ_pattern, output_folder, threshold=300,
|
|
116
|
+
min_t=100, max_t=3000, grid_name=None, total_hours=None,
|
|
117
|
+
sun_down_occ_hours=0):
|
|
118
|
+
"""Compute annual metrics for an ill file and write the results to a folder.
|
|
119
|
+
|
|
120
|
+
This function generates 5 different files or daylight autonomy, continuous daylight
|
|
121
|
+
autonomy, lower than useful daylight illuminance, useful daylight illuminance and
|
|
122
|
+
higher than useful daylight illuminance.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
ill_file: Path to an ill file generated by Radiance. The ill file should be
|
|
126
|
+
tab separated and shot NOT have a header. The results for each sensor point
|
|
127
|
+
should be available in a row and and each column should be the illuminance
|
|
128
|
+
value for a sun_up_hour. The number of columns should match the number of
|
|
129
|
+
sun up hours.
|
|
130
|
+
occ_pattern: A list of 0 and 1 values for hours of occupancy.
|
|
131
|
+
output_folder: An output folder where the results will be written to. The folder
|
|
132
|
+
will be created if not exist.
|
|
133
|
+
threshold: Threshold illuminance level for daylight autonomy. Default: 300.
|
|
134
|
+
min_t: Minimum threshold for useful daylight illuminance. Default: 100.
|
|
135
|
+
max_t: Maximum threshold for useful daylight illuminance. Default: 3000.
|
|
136
|
+
grid_name: An optional name for grid name which will be used to name the output
|
|
137
|
+
files. If None the name of the input file will be used.
|
|
138
|
+
total_hours: An integer for the total number of occupied hours in the
|
|
139
|
+
occupancy schedule. If None, it will be assumed that all of the
|
|
140
|
+
occupied hours are sun-up hours and are already accounted for
|
|
141
|
+
in the the occ_pattern.
|
|
142
|
+
sun_down_occ_hours: An integer for the total number of occupied hours where
|
|
143
|
+
the sun is down. This is often needed to properly tally all hours that
|
|
144
|
+
have illuminance below the threshold.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
Tuple(file.da, file.cda, file.luid, file.uid, file.hudi)
|
|
148
|
+
|
|
149
|
+
"""
|
|
150
|
+
if not os.path.isdir(output_folder):
|
|
151
|
+
os.makedirs(output_folder)
|
|
152
|
+
|
|
153
|
+
grid_name = grid_name or os.path.split(ill_file)[-1][-4:]
|
|
154
|
+
da = os.path.join(output_folder, 'da', '%s.da' % grid_name).replace('\\', '/')
|
|
155
|
+
cda = os.path.join(output_folder, 'cda', '%s.cda' % grid_name).replace('\\', '/')
|
|
156
|
+
udi = os.path.join(output_folder, 'udi', '%s.udi' % grid_name).replace('\\', '/')
|
|
157
|
+
udi_lower = \
|
|
158
|
+
os.path.join(output_folder, 'udi_lower', '%s.udi' % grid_name).replace('\\', '/')
|
|
159
|
+
udi_upper = \
|
|
160
|
+
os.path.join(output_folder, 'udi_upper', '%s.udi' % grid_name).replace('\\', '/')
|
|
161
|
+
|
|
162
|
+
for file_path in [da, cda, udi, udi_upper, udi_lower]:
|
|
163
|
+
folder = os.path.dirname(file_path)
|
|
164
|
+
if not os.path.isdir(folder):
|
|
165
|
+
os.makedirs(folder)
|
|
166
|
+
|
|
167
|
+
total_occupied_hours = sum(occ_pattern) if total_hours is None else total_hours
|
|
168
|
+
|
|
169
|
+
with open(ill_file) as results, open(da, 'w') as daf, open(cda, 'w') as cdaf, \
|
|
170
|
+
open(udi, 'w') as udif, open(udi_lower, 'w') as udi_lowerf, \
|
|
171
|
+
open(udi_upper, 'w') as udi_upperf:
|
|
172
|
+
for pt_res in results:
|
|
173
|
+
values = (float(res) for res in pt_res.split())
|
|
174
|
+
dar, cdar, udi_lowerr, udir, udi_upperr = _metrics(
|
|
175
|
+
values, occ_pattern, threshold, min_t, max_t,
|
|
176
|
+
total_occupied_hours, sun_down_occ_hours
|
|
177
|
+
)
|
|
178
|
+
daf.write(str(dar) + '\n')
|
|
179
|
+
cdaf.write(str(cdar) + '\n')
|
|
180
|
+
udi_lowerf.write(str(udi_lowerr) + '\n')
|
|
181
|
+
udif.write(str(udir) + '\n')
|
|
182
|
+
udi_upperf.write(str(udi_upperr) + '\n')
|
|
183
|
+
|
|
184
|
+
return da, cda, udi_lower, udi, udi_upper
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
# TODO - support a list of schedules/schedule folder to match the input grids
|
|
188
|
+
def metrics_from_folder(results_folder, schedule=None, threshold=300,
|
|
189
|
+
min_t=100, max_t=3000, grids_filter='*'):
|
|
190
|
+
"""Compute annual metrics for a folder.
|
|
191
|
+
|
|
192
|
+
This folder is an output folder of annual daylight recipe. Folder should include
|
|
193
|
+
grids_info.json and sun-up-hours.txt - the script uses the list in grids_info.json
|
|
194
|
+
to find the result files for each sensor grid.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
results_folder: Results folder.
|
|
198
|
+
schedule: An annual schedule for 8760 hours of the year as a list of values.
|
|
199
|
+
threshold: Threshold illuminance level for daylight autonomy. Default: 300.
|
|
200
|
+
min_t: Minimum threshold for useful daylight illuminance. Default: 100.
|
|
201
|
+
max_t: Maximum threshold for useful daylight illuminance. Default: 3000.
|
|
202
|
+
grids_filter: A pattern to filter the grids. By default all the grids will be
|
|
203
|
+
processed.
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
Tuple[Tuple] - There will be a tuple for each input sensor grid which is a
|
|
207
|
+
Tuple(List, List, List, List, List) -- 5 lists for daylight autonomy,
|
|
208
|
+
continuous daylight autonomy, lower than useful daylight illuminance,
|
|
209
|
+
useful daylight illuminance and higher than useful daylight illuminance.
|
|
210
|
+
Number of results in each list matches the number of lines in ill input file.
|
|
211
|
+
|
|
212
|
+
"""
|
|
213
|
+
da = []
|
|
214
|
+
cda = []
|
|
215
|
+
udi = []
|
|
216
|
+
udi_lower = []
|
|
217
|
+
udi_upper = []
|
|
218
|
+
|
|
219
|
+
grids, sun_up_hours = _process_input_folder(results_folder, grids_filter)
|
|
220
|
+
occ_pattern, total_occ, sun_down_occ_hours = \
|
|
221
|
+
filter_schedule_by_hours(sun_up_hours=sun_up_hours, schedule=schedule)
|
|
222
|
+
|
|
223
|
+
for grid in grids:
|
|
224
|
+
ill_file = os.path.join(results_folder, '%s.ill' % grid['full_id'])
|
|
225
|
+
da_r, cda_r, udi_lower_r, udi_r, udi_upper_r = \
|
|
226
|
+
metrics(ill_file, occ_pattern, threshold, min_t, max_t,
|
|
227
|
+
total_occ, sun_down_occ_hours)
|
|
228
|
+
da.append(da_r)
|
|
229
|
+
cda.append(cda_r)
|
|
230
|
+
udi_lower.append(udi_lower_r)
|
|
231
|
+
udi.append(udi_r)
|
|
232
|
+
udi_upper.append(udi_upper_r)
|
|
233
|
+
|
|
234
|
+
return da, cda, udi_lower, udi, udi_upper
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
# TODO - support a list of schedules/schedule folder to match the input grids
|
|
238
|
+
def metrics_to_folder(
|
|
239
|
+
results_folder, schedule=None, threshold=300, min_t=100, max_t=3000,
|
|
240
|
+
grids_filter='*', sub_folder='metrics'
|
|
241
|
+
):
|
|
242
|
+
"""Compute annual metrics in a folder and write them in a subfolder.
|
|
243
|
+
|
|
244
|
+
This folder is an output folder of annual daylight recipe. Folder should include
|
|
245
|
+
grids_info.json and sun-up-hours.txt - the script uses the list in grids_info.json
|
|
246
|
+
to find the result files for each sensor grid.
|
|
247
|
+
|
|
248
|
+
Args:
|
|
249
|
+
results_folder: Results folder.
|
|
250
|
+
schedule: An annual schedule for 8760 hours of the year as a list of values.
|
|
251
|
+
threshold: Threshold illuminance level for daylight autonomy. Default: 300.
|
|
252
|
+
min_t: Minimum threshold for useful daylight illuminance. Default: 100.
|
|
253
|
+
max_t: Maximum threshold for useful daylight illuminance. Default: 3000.
|
|
254
|
+
grids_filter: A pattern to filter the grids. By default all the grids will be
|
|
255
|
+
processed.
|
|
256
|
+
sub_folder: An optional relative path for subfolder to copy results files.
|
|
257
|
+
Default: metrics
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
str -- Path to results folder.
|
|
261
|
+
|
|
262
|
+
"""
|
|
263
|
+
grids, sun_up_hours = _process_input_folder(results_folder, grids_filter)
|
|
264
|
+
occ_pattern, total_occ, sun_down_occ_hours = \
|
|
265
|
+
filter_schedule_by_hours(sun_up_hours=sun_up_hours, schedule=schedule)
|
|
266
|
+
|
|
267
|
+
metrics_folder = os.path.join(results_folder, sub_folder)
|
|
268
|
+
if not os.path.isdir(metrics_folder):
|
|
269
|
+
os.makedirs(metrics_folder)
|
|
270
|
+
|
|
271
|
+
for grid in grids:
|
|
272
|
+
ill_file = os.path.join(results_folder, '%s.ill' % grid['full_id'])
|
|
273
|
+
metrics_to_files(
|
|
274
|
+
ill_file, occ_pattern, metrics_folder, threshold, min_t,
|
|
275
|
+
max_t, grid['full_id'], total_occ, sun_down_occ_hours
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
# copy info.json to all results folders
|
|
279
|
+
for folder_name in ['da', 'cda', 'udi_lower', 'udi', 'udi_upper']:
|
|
280
|
+
grid_info = os.path.join(metrics_folder, folder_name, 'grids_info.json')
|
|
281
|
+
with open(grid_info, 'w') as outf:
|
|
282
|
+
json.dump(grids, outf)
|
|
283
|
+
|
|
284
|
+
metric_info_dict = _annual_daylight_vis_metadata()
|
|
285
|
+
for metric, data in metric_info_dict.items():
|
|
286
|
+
file_path = os.path.join(metrics_folder, metric, 'vis_metadata.json')
|
|
287
|
+
with open(file_path, 'w') as fp:
|
|
288
|
+
json.dump(data, fp, indent=4)
|
|
289
|
+
|
|
290
|
+
return metrics_folder
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def _annual_daylight_vis_metadata():
|
|
294
|
+
"""Return visualization metadata for annual daylight."""
|
|
295
|
+
annual_daylight_lpar = LegendParameters(min=0, max=100)
|
|
296
|
+
|
|
297
|
+
metric_info_dict = {
|
|
298
|
+
'udi_lower': {
|
|
299
|
+
'type': 'VisualizationMetaData',
|
|
300
|
+
'data_type': Fraction('Useful Daylight Illuminance Lower').to_dict(),
|
|
301
|
+
'unit': '%',
|
|
302
|
+
'legend_parameters': annual_daylight_lpar.to_dict()
|
|
303
|
+
},
|
|
304
|
+
'udi_upper': {
|
|
305
|
+
'type': 'VisualizationMetaData',
|
|
306
|
+
'data_type': Fraction('Useful Daylight Illuminance Upper').to_dict(),
|
|
307
|
+
'unit': '%',
|
|
308
|
+
'legend_parameters': annual_daylight_lpar.to_dict()
|
|
309
|
+
},
|
|
310
|
+
'udi': {
|
|
311
|
+
'type': 'VisualizationMetaData',
|
|
312
|
+
'data_type': Fraction('Useful Daylight Illuminance').to_dict(),
|
|
313
|
+
'unit': '%',
|
|
314
|
+
'legend_parameters': annual_daylight_lpar.to_dict()
|
|
315
|
+
},
|
|
316
|
+
'cda': {
|
|
317
|
+
'type': 'VisualizationMetaData',
|
|
318
|
+
'data_type': Fraction('Continuous Daylight Autonomy').to_dict(),
|
|
319
|
+
'unit': '%',
|
|
320
|
+
'legend_parameters': annual_daylight_lpar.to_dict()
|
|
321
|
+
},
|
|
322
|
+
'da': {
|
|
323
|
+
'type': 'VisualizationMetaData',
|
|
324
|
+
'data_type': Fraction('Daylight Autonomy').to_dict(),
|
|
325
|
+
'unit': '%',
|
|
326
|
+
'legend_parameters': annual_daylight_lpar.to_dict()
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return metric_info_dict
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def _annual_daylight_config():
|
|
334
|
+
"""Return vtk-config for annual daylight. """
|
|
335
|
+
cfg = {
|
|
336
|
+
"data": [
|
|
337
|
+
{
|
|
338
|
+
"identifier": "Useful Daylight Illuminance Lower",
|
|
339
|
+
"object_type": "grid",
|
|
340
|
+
"unit": "Percentage",
|
|
341
|
+
"path": "udi_lower",
|
|
342
|
+
"hide": False,
|
|
343
|
+
"legend_parameters": {
|
|
344
|
+
"hide_legend": False,
|
|
345
|
+
"min": 0,
|
|
346
|
+
"max": 100,
|
|
347
|
+
"color_set": "nuanced",
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
"identifier": "Useful Daylight Illuminance Upper",
|
|
352
|
+
"object_type": "grid",
|
|
353
|
+
"unit": "Percentage",
|
|
354
|
+
"path": "udi_upper",
|
|
355
|
+
"hide": False,
|
|
356
|
+
"legend_parameters": {
|
|
357
|
+
"hide_legend": False,
|
|
358
|
+
"min": 0,
|
|
359
|
+
"max": 100,
|
|
360
|
+
"color_set": "glare_study",
|
|
361
|
+
"label_parameters": {
|
|
362
|
+
"color": [34, 247, 10],
|
|
363
|
+
"size": 0,
|
|
364
|
+
"bold": True,
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
"identifier": "Useful Daylight Illuminance",
|
|
370
|
+
"object_type": "grid",
|
|
371
|
+
"unit": "Percentage",
|
|
372
|
+
"path": "udi",
|
|
373
|
+
"hide": False,
|
|
374
|
+
"legend_parameters": {
|
|
375
|
+
"hide_legend": False,
|
|
376
|
+
"min": 0,
|
|
377
|
+
"max": 100,
|
|
378
|
+
"color_set": "annual_comfort",
|
|
379
|
+
"label_parameters": {
|
|
380
|
+
"color": [34, 247, 10],
|
|
381
|
+
"size": 0,
|
|
382
|
+
"bold": True,
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
"identifier": "Continuous Daylight Autonomy",
|
|
388
|
+
"object_type": "grid",
|
|
389
|
+
"unit": "Percentage",
|
|
390
|
+
"path": "cda",
|
|
391
|
+
"hide": False,
|
|
392
|
+
"legend_parameters": {
|
|
393
|
+
"hide_legend": False,
|
|
394
|
+
"min": 0,
|
|
395
|
+
"max": 100,
|
|
396
|
+
"color_set": "annual_comfort",
|
|
397
|
+
"label_parameters": {
|
|
398
|
+
"color": [34, 247, 10],
|
|
399
|
+
"size": 0,
|
|
400
|
+
"bold": True,
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
"identifier": "Daylight Autonomy",
|
|
406
|
+
"object_type": "grid",
|
|
407
|
+
"unit": "Percentage",
|
|
408
|
+
"path": "da",
|
|
409
|
+
"hide": False,
|
|
410
|
+
"legend_parameters": {
|
|
411
|
+
"hide_legend": False,
|
|
412
|
+
"min": 0,
|
|
413
|
+
"max": 100,
|
|
414
|
+
"color_set": "annual_comfort",
|
|
415
|
+
"label_parameters": {
|
|
416
|
+
"color": [34, 247, 10],
|
|
417
|
+
"size": 0,
|
|
418
|
+
"bold": True,
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
]
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
return cfg
|