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,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