honeybee-radiance-postprocess 0.4.555__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.
- honeybee_radiance_postprocess/__init__.py +1 -0
- honeybee_radiance_postprocess/__main__.py +4 -0
- honeybee_radiance_postprocess/annual.py +73 -0
- honeybee_radiance_postprocess/annualdaylight.py +289 -0
- honeybee_radiance_postprocess/annualirradiance.py +35 -0
- honeybee_radiance_postprocess/breeam/__init__.py +1 -0
- honeybee_radiance_postprocess/breeam/breeam.py +552 -0
- honeybee_radiance_postprocess/cli/__init__.py +33 -0
- honeybee_radiance_postprocess/cli/abnt.py +392 -0
- honeybee_radiance_postprocess/cli/breeam.py +96 -0
- honeybee_radiance_postprocess/cli/datacollection.py +133 -0
- honeybee_radiance_postprocess/cli/grid.py +295 -0
- honeybee_radiance_postprocess/cli/leed.py +143 -0
- honeybee_radiance_postprocess/cli/merge.py +161 -0
- honeybee_radiance_postprocess/cli/mtxop.py +161 -0
- honeybee_radiance_postprocess/cli/postprocess.py +1092 -0
- honeybee_radiance_postprocess/cli/schedule.py +103 -0
- honeybee_radiance_postprocess/cli/translate.py +216 -0
- honeybee_radiance_postprocess/cli/two_phase.py +252 -0
- honeybee_radiance_postprocess/cli/util.py +121 -0
- honeybee_radiance_postprocess/cli/viewfactor.py +157 -0
- honeybee_radiance_postprocess/cli/well.py +110 -0
- honeybee_radiance_postprocess/data_type.py +102 -0
- honeybee_radiance_postprocess/dynamic.py +273 -0
- honeybee_radiance_postprocess/electriclight.py +24 -0
- honeybee_radiance_postprocess/en17037.py +304 -0
- honeybee_radiance_postprocess/helper.py +266 -0
- honeybee_radiance_postprocess/ies/__init__.py +1 -0
- honeybee_radiance_postprocess/ies/lm.py +224 -0
- honeybee_radiance_postprocess/ies/lm_schedule.py +248 -0
- honeybee_radiance_postprocess/leed/__init__.py +1 -0
- honeybee_radiance_postprocess/leed/leed.py +801 -0
- honeybee_radiance_postprocess/leed/leed_schedule.py +256 -0
- honeybee_radiance_postprocess/metrics.py +439 -0
- honeybee_radiance_postprocess/reader.py +80 -0
- honeybee_radiance_postprocess/results/__init__.py +4 -0
- honeybee_radiance_postprocess/results/annual_daylight.py +752 -0
- honeybee_radiance_postprocess/results/annual_irradiance.py +196 -0
- honeybee_radiance_postprocess/results/results.py +1416 -0
- honeybee_radiance_postprocess/type_hints.py +38 -0
- honeybee_radiance_postprocess/util.py +211 -0
- honeybee_radiance_postprocess/vis_metadata.py +49 -0
- honeybee_radiance_postprocess/well/__init__.py +1 -0
- honeybee_radiance_postprocess/well/well.py +509 -0
- honeybee_radiance_postprocess-0.4.555.dist-info/METADATA +79 -0
- honeybee_radiance_postprocess-0.4.555.dist-info/RECORD +50 -0
- honeybee_radiance_postprocess-0.4.555.dist-info/WHEEL +5 -0
- honeybee_radiance_postprocess-0.4.555.dist-info/entry_points.txt +2 -0
- honeybee_radiance_postprocess-0.4.555.dist-info/licenses/LICENSE +661 -0
- honeybee_radiance_postprocess-0.4.555.dist-info/top_level.txt +1 -0
@@ -0,0 +1,224 @@
|
|
1
|
+
"""Functions for IES LM post-processing."""
|
2
|
+
from typing import Tuple, Union
|
3
|
+
from collections import defaultdict
|
4
|
+
import itertools
|
5
|
+
try:
|
6
|
+
import cupy as np
|
7
|
+
is_gpu = True
|
8
|
+
except ImportError:
|
9
|
+
is_gpu = False
|
10
|
+
import numpy as np
|
11
|
+
|
12
|
+
from honeybee_radiance.postprocess.annual import filter_schedule_by_hours
|
13
|
+
|
14
|
+
from ..annual import schedule_to_hoys, occupancy_schedule_8_to_6
|
15
|
+
from ..results.annual_daylight import AnnualDaylight
|
16
|
+
from ..util import filter_array
|
17
|
+
from ..dynamic import DynamicSchedule, ApertureGroupSchedule
|
18
|
+
from .lm_schedule import shd_trans_schedule_descending, states_schedule_descending
|
19
|
+
|
20
|
+
|
21
|
+
def shade_transmittance_per_light_path(
|
22
|
+
light_paths: list, shade_transmittance: Union[float, dict],
|
23
|
+
shd_trans_dict: dict) -> dict:
|
24
|
+
"""Filter shade_transmittance by light paths and add default multiplier.
|
25
|
+
|
26
|
+
Args:
|
27
|
+
light_paths: A list of light paths.
|
28
|
+
shade_transmittance: A value to use as a multiplier in place of solar
|
29
|
+
shading. This input can be either a single value that will be used
|
30
|
+
for all aperture groups, or a dictionary where aperture groups are
|
31
|
+
keys, and the value for each key is the shade transmittance. Values
|
32
|
+
for shade transmittance must be 1 > value > 0.
|
33
|
+
shd_trans_dict: A dictionary used to store shade transmittance value
|
34
|
+
for each aperture group.
|
35
|
+
|
36
|
+
Returns:
|
37
|
+
A dictionary with filtered light paths.
|
38
|
+
"""
|
39
|
+
shade_transmittances = {}
|
40
|
+
if isinstance(shade_transmittance, dict):
|
41
|
+
for light_path in light_paths:
|
42
|
+
# default multiplier
|
43
|
+
shade_transmittances[light_path] = [1]
|
44
|
+
# add custom shade transmittance
|
45
|
+
if light_path in shade_transmittance:
|
46
|
+
shade_transmittances[light_path].append(
|
47
|
+
shade_transmittance[light_path])
|
48
|
+
shd_trans_dict[light_path] = shade_transmittance[light_path]
|
49
|
+
# add default shade transmittance (0.02)
|
50
|
+
elif light_path != '__static_apertures__':
|
51
|
+
shade_transmittances[light_path].append(0.02)
|
52
|
+
shd_trans_dict[light_path] = 0.02
|
53
|
+
else:
|
54
|
+
shade_transmittances[light_path].append(1)
|
55
|
+
shd_trans_dict[light_path] = 1
|
56
|
+
else:
|
57
|
+
shd_trans = float(shade_transmittance)
|
58
|
+
for light_path in light_paths:
|
59
|
+
# default multiplier
|
60
|
+
shade_transmittances[light_path] = [1]
|
61
|
+
# add custom shade transmittance
|
62
|
+
if light_path != '__static_apertures__':
|
63
|
+
shade_transmittances[light_path].append(shd_trans)
|
64
|
+
shd_trans_dict[light_path] = shd_trans
|
65
|
+
else:
|
66
|
+
shade_transmittances[light_path].append(1)
|
67
|
+
shd_trans_dict[light_path] = 1
|
68
|
+
|
69
|
+
return shade_transmittances, shd_trans_dict
|
70
|
+
|
71
|
+
|
72
|
+
def dynamic_schedule_direct_illuminance(
|
73
|
+
results: Union[str, AnnualDaylight], grids_filter: str = '*',
|
74
|
+
shade_transmittance: Union[float, dict] = 0.02,
|
75
|
+
use_states: bool = False
|
76
|
+
) -> Tuple[dict, dict]:
|
77
|
+
"""Calculate a schedule of each aperture group.
|
78
|
+
|
79
|
+
This function calculates an annual shading schedule of each aperture
|
80
|
+
group. Hour by hour it will select the least shaded aperture group
|
81
|
+
configuration, so that no more than 2% of the sensors points receive
|
82
|
+
direct illuminance of 1000 lux or more.
|
83
|
+
|
84
|
+
Args:
|
85
|
+
results: Path to results folder or a Results class object.
|
86
|
+
grids_filter: The name of a grid or a pattern to filter the grids.
|
87
|
+
Defaults to '*'.
|
88
|
+
shade_transmittance: A value to use as a multiplier in place of solar
|
89
|
+
shading. This input can be either a single value that will be used
|
90
|
+
for all aperture groups, or a dictionary where aperture groups are
|
91
|
+
keys, and the value for each key is the shade transmittance. Values
|
92
|
+
for shade transmittance must be 1 > value > 0.
|
93
|
+
Defaults to 0.02.
|
94
|
+
use_states: A boolean to note whether to use the simulated states. Set
|
95
|
+
to True to use the simulated states. The default is False which will
|
96
|
+
use the shade transmittance instead.
|
97
|
+
|
98
|
+
Returns:
|
99
|
+
Tuple: A tuple with a dictionary of the annual schedule and a
|
100
|
+
dictionary of hours where no shading configuration comply with the
|
101
|
+
2% rule.
|
102
|
+
"""
|
103
|
+
if not isinstance(results, AnnualDaylight):
|
104
|
+
results = AnnualDaylight(results)
|
105
|
+
|
106
|
+
grids_info = results._filter_grids(grids_filter=grids_filter)
|
107
|
+
schedule = occupancy_schedule_8_to_6(as_list=True)
|
108
|
+
occ_pattern = \
|
109
|
+
filter_schedule_by_hours(results.sun_up_hours, schedule=schedule)[0]
|
110
|
+
occ_mask = np.array(occ_pattern)
|
111
|
+
|
112
|
+
states_schedule = defaultdict(list)
|
113
|
+
fail_to_comply = {}
|
114
|
+
shd_trans_dict = {}
|
115
|
+
|
116
|
+
for grid_info in grids_info:
|
117
|
+
grid_states_schedule = defaultdict(list)
|
118
|
+
|
119
|
+
grid_count = grid_info['count']
|
120
|
+
light_paths = []
|
121
|
+
for lp in grid_info['light_path']:
|
122
|
+
for _lp in lp:
|
123
|
+
if _lp == '__static_apertures__' and len(lp) > 1:
|
124
|
+
pass
|
125
|
+
else:
|
126
|
+
light_paths.append(_lp)
|
127
|
+
|
128
|
+
shade_transmittances, shd_trans_dict = (
|
129
|
+
shade_transmittance_per_light_path(
|
130
|
+
light_paths, shade_transmittance, shd_trans_dict
|
131
|
+
)
|
132
|
+
)
|
133
|
+
|
134
|
+
if len(light_paths) > 6:
|
135
|
+
if use_states:
|
136
|
+
grid_states_schedule, fail_to_comply = states_schedule_descending(
|
137
|
+
results, grid_info, light_paths, occ_mask,
|
138
|
+
grid_states_schedule, fail_to_comply)
|
139
|
+
else:
|
140
|
+
grid_states_schedule, fail_to_comply = shd_trans_schedule_descending(
|
141
|
+
results, grid_info, light_paths, shade_transmittances, occ_mask,
|
142
|
+
grid_states_schedule, fail_to_comply)
|
143
|
+
else:
|
144
|
+
if use_states:
|
145
|
+
combinations = results._get_state_combinations(grid_info)
|
146
|
+
else:
|
147
|
+
shade_transmittances, shd_trans_dict = shade_transmittance_per_light_path(
|
148
|
+
light_paths, shade_transmittance, shd_trans_dict)
|
149
|
+
keys, values = zip(*shade_transmittances.items())
|
150
|
+
combinations = [dict(zip(keys, v)) for v in itertools.product(*values)]
|
151
|
+
|
152
|
+
array_list_combinations = []
|
153
|
+
for combination in combinations:
|
154
|
+
combination_arrays = []
|
155
|
+
for light_path, value in combination.items():
|
156
|
+
if use_states:
|
157
|
+
combination_arrays.append(
|
158
|
+
results._get_array(grid_info, light_path, state=value,
|
159
|
+
res_type='direct')
|
160
|
+
)
|
161
|
+
else:
|
162
|
+
array = results._get_array(
|
163
|
+
grid_info, light_path, res_type='direct')
|
164
|
+
if value == 1:
|
165
|
+
combination_arrays.append(array)
|
166
|
+
else:
|
167
|
+
combination_arrays.append(array * value)
|
168
|
+
combination_array = sum(combination_arrays)
|
169
|
+
|
170
|
+
combination_percentage = \
|
171
|
+
(combination_array >= 1000).sum(axis=0) / grid_count
|
172
|
+
array_list_combinations.append(combination_percentage)
|
173
|
+
array_combinations = np.array(array_list_combinations)
|
174
|
+
array_combinations[array_combinations > 0.02] = -np.inf
|
175
|
+
|
176
|
+
grid_comply = np.where(np.all(array_combinations==-np.inf, axis=0))[0]
|
177
|
+
if grid_comply.size != 0:
|
178
|
+
grid_comply = np.array(results.sun_up_hours)[grid_comply]
|
179
|
+
fail_to_comply[grid_info['name']] = \
|
180
|
+
[int(hoy) for hoy in grid_comply]
|
181
|
+
|
182
|
+
array_combinations_filter = np.apply_along_axis(
|
183
|
+
filter_array, 1, array_combinations, occ_mask
|
184
|
+
)
|
185
|
+
max_indices = array_combinations_filter.argmax(axis=0)
|
186
|
+
# select the combination for each hour
|
187
|
+
combinations = [combinations[idx] for idx in max_indices]
|
188
|
+
|
189
|
+
# merge the combinations of dicts
|
190
|
+
for combination in combinations:
|
191
|
+
for light_path, value in combination.items():
|
192
|
+
if light_path != '__static_apertures__':
|
193
|
+
grid_states_schedule[light_path].append(value)
|
194
|
+
|
195
|
+
for key, value in grid_states_schedule.items():
|
196
|
+
if key not in states_schedule:
|
197
|
+
states_schedule[key] = value
|
198
|
+
else:
|
199
|
+
if use_states:
|
200
|
+
merged_array = np.logical_or(states_schedule[key], value).astype(int)
|
201
|
+
else:
|
202
|
+
merged_array = np.minimum(states_schedule[key], value)
|
203
|
+
states_schedule[key] = merged_array
|
204
|
+
|
205
|
+
occupancy_hoys = schedule_to_hoys(schedule, results.sun_up_hours)
|
206
|
+
|
207
|
+
# map states to 8760 values
|
208
|
+
if use_states:
|
209
|
+
aperture_group_schedules = []
|
210
|
+
for identifier, values in states_schedule.items():
|
211
|
+
mapped_states = results.values_to_annual(
|
212
|
+
occupancy_hoys, values, results.timestep, dtype=np.int32)
|
213
|
+
aperture_group_schedules.append(
|
214
|
+
ApertureGroupSchedule(identifier, mapped_states.tolist())
|
215
|
+
)
|
216
|
+
states_schedule = \
|
217
|
+
DynamicSchedule.from_group_schedules(aperture_group_schedules)
|
218
|
+
else:
|
219
|
+
for light_path, shd_trans in states_schedule.items():
|
220
|
+
mapped_states = results.values_to_annual(
|
221
|
+
occupancy_hoys, shd_trans, results.timestep)
|
222
|
+
states_schedule[light_path] = mapped_states
|
223
|
+
|
224
|
+
return states_schedule, fail_to_comply, shd_trans_dict
|
@@ -0,0 +1,248 @@
|
|
1
|
+
"""Module for dynamic LM schedules."""
|
2
|
+
from typing import Tuple
|
3
|
+
try:
|
4
|
+
import cupy as np
|
5
|
+
is_gpu = True
|
6
|
+
except ImportError:
|
7
|
+
is_gpu = False
|
8
|
+
import numpy as np
|
9
|
+
|
10
|
+
from ..results.annual_daylight import AnnualDaylight
|
11
|
+
from ..util import filter_array
|
12
|
+
|
13
|
+
|
14
|
+
def shd_trans_schedule_descending(
|
15
|
+
results: AnnualDaylight, grid_info, light_paths, shade_transmittances, occ_mask,
|
16
|
+
states_schedule, fail_to_comply
|
17
|
+
) -> Tuple[dict, dict]:
|
18
|
+
grid_count = grid_info['count']
|
19
|
+
full_direct = []
|
20
|
+
full_thresh = []
|
21
|
+
full_shd_trans_array = []
|
22
|
+
for light_path in light_paths:
|
23
|
+
array = results._get_array(grid_info, light_path, res_type="direct")
|
24
|
+
array = np.apply_along_axis(filter_array, 1, array, occ_mask)
|
25
|
+
full_direct.append(array)
|
26
|
+
full_thresh.append((array >= 1000).sum(axis=0))
|
27
|
+
full_shd_trans_array.append(shade_transmittances[light_path][1])
|
28
|
+
|
29
|
+
# Sum the array element-wise.
|
30
|
+
# This array is the sum of all direct illuminance without shade
|
31
|
+
# transmittance.
|
32
|
+
full_direct_sum = sum(full_direct)
|
33
|
+
|
34
|
+
# Create base list of shading combinations (all set to 1).
|
35
|
+
# We will replace the 1s later.
|
36
|
+
combinations = [
|
37
|
+
{light_path: 1 for light_path in light_paths}
|
38
|
+
for i in range(full_direct_sum.shape[1])
|
39
|
+
]
|
40
|
+
|
41
|
+
# Find the percentage of floor area >= 1000 lux.
|
42
|
+
# This array is the percentage for each hour (axis=0).
|
43
|
+
direct_pct_above = (full_direct_sum >= 1000).sum(axis=0) / grid_count
|
44
|
+
|
45
|
+
# Find the indices where the percentage of floor area is > 2%.
|
46
|
+
# This array is the problematic hours.
|
47
|
+
above_2_indices = np.where(direct_pct_above > 0.02)[0]
|
48
|
+
|
49
|
+
# Use the indices to get the relevant hours.
|
50
|
+
direct_sum = np.take(full_direct_sum, above_2_indices, axis=1)
|
51
|
+
|
52
|
+
# Use the indices to get the relevant hours.
|
53
|
+
direct = np.take(full_direct, above_2_indices, axis=2)
|
54
|
+
|
55
|
+
# Use the indices to get the relevant hours.
|
56
|
+
thresh = np.take(full_thresh, above_2_indices, axis=1)
|
57
|
+
|
58
|
+
# Sort and get indices. Negate the array to get descending order.
|
59
|
+
# Descending order puts the "highest offender" light path first.
|
60
|
+
sort_thresh = np.argsort(-thresh, axis=0).transpose()
|
61
|
+
|
62
|
+
_combinations = []
|
63
|
+
_combinations.insert(
|
64
|
+
0, (np.arange(full_direct_sum.shape[1]), combinations)
|
65
|
+
)
|
66
|
+
|
67
|
+
if np.any(above_2_indices):
|
68
|
+
# There are hours where the percentage of floor area is > 2%.
|
69
|
+
for idx, lp in enumerate(light_paths):
|
70
|
+
# Take column. For each iteration it will take the next column
|
71
|
+
# in descending order, i.e., the "highest offender" is the first
|
72
|
+
# column.
|
73
|
+
sort_indices = np.take(sort_thresh, idx, axis=1)
|
74
|
+
|
75
|
+
# Map light path identifiers to indices.
|
76
|
+
light_path_ids = np.take(light_paths, sort_indices)
|
77
|
+
|
78
|
+
# Map shade transmittance to indices.
|
79
|
+
shd_trans_array = np.take(full_shd_trans_array, sort_indices)
|
80
|
+
|
81
|
+
# Create combination for the subset.
|
82
|
+
_subset_combination = [
|
83
|
+
{light_path: _shd_trans} for light_path, _shd_trans in
|
84
|
+
zip(light_path_ids, shd_trans_array)
|
85
|
+
]
|
86
|
+
_combinations.insert(0, (above_2_indices, _subset_combination))
|
87
|
+
|
88
|
+
# Take the values from each array by indexing.
|
89
|
+
direct_array = \
|
90
|
+
direct[sort_indices, :, range(len(sort_indices))].transpose()
|
91
|
+
|
92
|
+
# Subtract the illuminance values.
|
93
|
+
direct_sum = direct_sum - (direct_array * (1 - shd_trans_array))
|
94
|
+
|
95
|
+
# Find the percentage of floor area >= 1000 lux.
|
96
|
+
direct_pct_above = (direct_sum >= 1000).sum(axis=0) / grid_count
|
97
|
+
|
98
|
+
# Find the indices where the percentage of floor area is > 2%.
|
99
|
+
above_2_indices = np.where(direct_pct_above > 0.02)[0]
|
100
|
+
|
101
|
+
# Break if there are no hours above 2%.
|
102
|
+
if not np.any(above_2_indices):
|
103
|
+
break
|
104
|
+
|
105
|
+
# Update variables for the next iteration.
|
106
|
+
direct_sum = np.take(direct_sum, above_2_indices, axis=1)
|
107
|
+
direct = np.take(direct, above_2_indices, axis=2)
|
108
|
+
thresh = np.take(thresh, above_2_indices, axis=1)
|
109
|
+
sort_thresh = np.take(sort_thresh, above_2_indices, axis=0)
|
110
|
+
|
111
|
+
if np.any(above_2_indices):
|
112
|
+
# There are hours not complying with the 2% rule.
|
113
|
+
previous_indices = []
|
114
|
+
previous_combination = []
|
115
|
+
grid_comply = []
|
116
|
+
# Merge the combinations from the iterations of the subsets.
|
117
|
+
for i, subset in enumerate(_combinations):
|
118
|
+
if i == 0:
|
119
|
+
previous_indices = subset[0]
|
120
|
+
else:
|
121
|
+
_indices = subset[0]
|
122
|
+
grid_comply = []
|
123
|
+
for _pr_idx in previous_indices:
|
124
|
+
grid_comply.append(_indices[_pr_idx])
|
125
|
+
previous_indices = grid_comply
|
126
|
+
# Convert indices to sun up hours indices.
|
127
|
+
filter_indices = np.where(occ_mask.astype(bool))[0]
|
128
|
+
grid_comply = [filter_indices[_gc] for _gc in grid_comply]
|
129
|
+
grid_comply = np.array(results.sun_up_hours)[grid_comply]
|
130
|
+
fail_to_comply[grid_info['name']] = \
|
131
|
+
[int(hoy) for hoy in grid_comply]
|
132
|
+
|
133
|
+
previous_indices = None
|
134
|
+
previous_combination = None
|
135
|
+
# Merge the combinations from the iterations of the subsets.
|
136
|
+
for i, subset in enumerate(_combinations):
|
137
|
+
if i == 0:
|
138
|
+
previous_indices, previous_combination = subset
|
139
|
+
else:
|
140
|
+
_indices, _combination = subset
|
141
|
+
for _pr_idx, _pr_comb in \
|
142
|
+
zip(previous_indices, previous_combination):
|
143
|
+
for light_path, _shd_trans in _pr_comb.items():
|
144
|
+
_combination[_pr_idx][light_path] = _shd_trans
|
145
|
+
previous_indices = _indices
|
146
|
+
previous_combination = _combination
|
147
|
+
|
148
|
+
combinations = _combination
|
149
|
+
|
150
|
+
# Merge the combinations of dicts.
|
151
|
+
for combination in combinations:
|
152
|
+
for light_path, shd_trans in combination.items():
|
153
|
+
if light_path != "__static_apertures__":
|
154
|
+
states_schedule[light_path].append(shd_trans)
|
155
|
+
|
156
|
+
return states_schedule, fail_to_comply
|
157
|
+
|
158
|
+
|
159
|
+
def states_schedule_descending(
|
160
|
+
results: AnnualDaylight, grid_info, light_paths, occ_mask,
|
161
|
+
states_schedule, fail_to_comply
|
162
|
+
) -> Tuple[dict, dict]:
|
163
|
+
grid_count = grid_info['count']
|
164
|
+
full_direct = []
|
165
|
+
full_thresh = []
|
166
|
+
full_direct_blinds = []
|
167
|
+
for light_path in light_paths:
|
168
|
+
array = results._get_array(
|
169
|
+
grid_info, light_path, state=0, res_type="direct")
|
170
|
+
array = np.apply_along_axis(filter_array, 1, array, occ_mask)
|
171
|
+
full_direct.append(array)
|
172
|
+
full_thresh.append((array >= 1000).sum(axis=0))
|
173
|
+
|
174
|
+
array = results._get_array(
|
175
|
+
grid_info, light_path, state=1, res_type="direct")
|
176
|
+
array = np.apply_along_axis(filter_array, 1, array, occ_mask)
|
177
|
+
full_direct_blinds.append(array)
|
178
|
+
|
179
|
+
full_direct = np.array(full_direct)
|
180
|
+
full_direct_blinds = np.array(full_direct_blinds)
|
181
|
+
full_direct_sum = full_direct.sum(axis=0)
|
182
|
+
|
183
|
+
new_array = full_direct.copy()
|
184
|
+
|
185
|
+
percentage_sensors = (full_direct_sum >= 1000).sum(axis=0) / grid_count
|
186
|
+
if not np.any(percentage_sensors > 0.02):
|
187
|
+
combinations = [
|
188
|
+
{light_path: 0 for light_path in light_paths}
|
189
|
+
for i in range(full_direct_sum.shape[1])]
|
190
|
+
else:
|
191
|
+
tracking_array = np.zeros(
|
192
|
+
(new_array.shape[0], new_array.shape[2]), dtype=int)
|
193
|
+
|
194
|
+
percentage_sensors = (full_direct >= 1000).sum(axis=1) / grid_count
|
195
|
+
|
196
|
+
ranking_indices = np.argsort(-percentage_sensors, axis=0)
|
197
|
+
|
198
|
+
for rank in range(ranking_indices.shape[0]):
|
199
|
+
# Calculate the percentage of sensors with values >= 1000 for the current new_array
|
200
|
+
summed_array = np.sum(new_array, axis=0)
|
201
|
+
percentage_sensors_summed = np.sum(
|
202
|
+
summed_array >= 1000, axis=0) / grid_count
|
203
|
+
indices_above_2_percent = np.where(
|
204
|
+
percentage_sensors_summed > 0.02)[0]
|
205
|
+
|
206
|
+
# Exit if there are no more hours exceeding the threshold
|
207
|
+
if len(indices_above_2_percent) == 0:
|
208
|
+
break
|
209
|
+
|
210
|
+
# Array indices to use for replacement for these hours
|
211
|
+
replace_indices = indices_above_2_percent
|
212
|
+
array_indices = ranking_indices[rank, replace_indices]
|
213
|
+
|
214
|
+
# Use advanced indexing to replace values in new_array for these hours
|
215
|
+
for hour_idx, array_idx in zip(replace_indices, array_indices):
|
216
|
+
new_array[array_idx, :, hour_idx] = full_direct_blinds[
|
217
|
+
array_idx, :, hour_idx
|
218
|
+
]
|
219
|
+
|
220
|
+
# Update the tracking array
|
221
|
+
tracking_array[array_indices, replace_indices] = 1
|
222
|
+
|
223
|
+
combinations = []
|
224
|
+
for hour in range(new_array.shape[2]):
|
225
|
+
hour_dict = {
|
226
|
+
light_paths[i]: tracking_array[i, hour]
|
227
|
+
for i in range(tracking_array.shape[0])}
|
228
|
+
combinations.append(hour_dict)
|
229
|
+
|
230
|
+
final_summed_array = np.sum(new_array, axis=0)
|
231
|
+
final_percentage_sensors_summed = (
|
232
|
+
final_summed_array >= 1000).sum(
|
233
|
+
axis=0) / grid_count
|
234
|
+
final_indices_above_2_percent = np.where(
|
235
|
+
final_percentage_sensors_summed > 0.02)[0]
|
236
|
+
if np.any(final_indices_above_2_percent):
|
237
|
+
sun_up_hours_indices = np.where(occ_mask == 1)[0][
|
238
|
+
final_indices_above_2_percent]
|
239
|
+
grid_comply = np.array(results.sun_up_hours)[sun_up_hours_indices]
|
240
|
+
fail_to_comply[grid_info['name']] = [
|
241
|
+
int(hoy) for hoy in grid_comply]
|
242
|
+
|
243
|
+
for combination in combinations:
|
244
|
+
for light_path, value in combination.items():
|
245
|
+
if light_path != '__static_apertures__':
|
246
|
+
states_schedule[light_path].append(value)
|
247
|
+
|
248
|
+
return states_schedule, fail_to_comply
|
@@ -0,0 +1 @@
|
|
1
|
+
"""honeybee-radiance-postprocess library."""
|