Source code for honeybee_radiance_postprocess.ies.lm_schedule

"""Module for dynamic LM schedules."""
from typing import Tuple
import numpy as np

from ..results.annual_daylight import AnnualDaylight
from ..util import filter_array


[docs] def shd_trans_schedule_descending( results: AnnualDaylight, grid_info, light_paths, shade_transmittances, occ_mask, states_schedule, fail_to_comply ) -> Tuple[dict, dict]: grid_count = grid_info['count'] full_direct = [] full_thresh = [] full_shd_trans_array = [] for light_path in light_paths: array = results._get_array(grid_info, light_path, res_type="direct") array = np.apply_along_axis(filter_array, 1, array, occ_mask) full_direct.append(array) full_thresh.append((array >= 1000).sum(axis=0)) full_shd_trans_array.append(shade_transmittances[light_path][1]) # Sum the array element-wise. # This array is the sum of all direct illuminance without shade # transmittance. full_direct_sum = sum(full_direct) # Create base list of shading combinations (all set to 1). # We will replace the 1s later. combinations = [ {light_path: 1 for light_path in light_paths} for i in range(full_direct_sum.shape[1]) ] # Find the percentage of floor area >= 1000 lux. # This array is the percentage for each hour (axis=0). direct_pct_above = (full_direct_sum >= 1000).sum(axis=0) / grid_count # Find the indices where the percentage of floor area is > 2%. # This array is the problematic hours. above_2_indices = np.where(direct_pct_above > 0.02)[0] # Use the indices to get the relevant hours. direct_sum = np.take(full_direct_sum, above_2_indices, axis=1) # Use the indices to get the relevant hours. direct = np.take(full_direct, above_2_indices, axis=2) # Use the indices to get the relevant hours. thresh = np.take(full_thresh, above_2_indices, axis=1) # Sort and get indices. Negate the array to get descending order. # Descending order puts the "highest offender" light path first. sort_thresh = np.argsort(-thresh, axis=0).transpose() _combinations = [] _combinations.insert( 0, (np.arange(full_direct_sum.shape[1]), combinations) ) if np.any(above_2_indices): # There are hours where the percentage of floor area is > 2%. for idx, lp in enumerate(light_paths): # Take column. For each iteration it will take the next column # in descending order, i.e., the "highest offender" is the first # column. sort_indices = np.take(sort_thresh, idx, axis=1) # Map light path identifiers to indices. light_path_ids = np.take(light_paths, sort_indices) # Map shade transmittance to indices. shd_trans_array = np.take(full_shd_trans_array, sort_indices) # Create combination for the subset. _subset_combination = [ {light_path: _shd_trans} for light_path, _shd_trans in zip(light_path_ids, shd_trans_array) ] _combinations.insert(0, (above_2_indices, _subset_combination)) # Take the values from each array by indexing. direct_array = \ direct[sort_indices, :, range(len(sort_indices))].transpose() # Subtract the illuminance values. direct_sum = direct_sum - (direct_array * (1 - shd_trans_array)) # Find the percentage of floor area >= 1000 lux. direct_pct_above = (direct_sum >= 1000).sum(axis=0) / grid_count # Find the indices where the percentage of floor area is > 2%. above_2_indices = np.where(direct_pct_above > 0.02)[0] # Break if there are no hours above 2%. if not np.any(above_2_indices): break # Update variables for the next iteration. direct_sum = np.take(direct_sum, above_2_indices, axis=1) direct = np.take(direct, above_2_indices, axis=2) thresh = np.take(thresh, above_2_indices, axis=1) sort_thresh = np.take(sort_thresh, above_2_indices, axis=0) if np.any(above_2_indices): # There are hours not complying with the 2% rule. previous_indices = [] previous_combination = [] grid_comply = [] # Merge the combinations from the iterations of the subsets. for i, subset in enumerate(_combinations): if i == 0: previous_indices = subset[0] else: _indices = subset[0] grid_comply = [] for _pr_idx in previous_indices: grid_comply.append(_indices[_pr_idx]) previous_indices = grid_comply # Convert indices to sun up hours indices. filter_indices = np.where(occ_mask.astype(bool))[0] grid_comply = [filter_indices[_gc] for _gc in grid_comply] grid_comply = np.array(results.sun_up_hours)[grid_comply] fail_to_comply[grid_info['name']] = \ [int(hoy) for hoy in grid_comply] previous_indices = None previous_combination = None # Merge the combinations from the iterations of the subsets. for i, subset in enumerate(_combinations): if i == 0: previous_indices, previous_combination = subset else: _indices, _combination = subset for _pr_idx, _pr_comb in \ zip(previous_indices, previous_combination): for light_path, _shd_trans in _pr_comb.items(): _combination[_pr_idx][light_path] = _shd_trans previous_indices = _indices previous_combination = _combination combinations = _combination # Merge the combinations of dicts. for combination in combinations: for light_path, shd_trans in combination.items(): if light_path != "__static_apertures__": states_schedule[light_path].append(shd_trans) return states_schedule, fail_to_comply
[docs] def states_schedule_descending( results: AnnualDaylight, grid_info, light_paths, occ_mask, states_schedule, fail_to_comply ) -> Tuple[dict, dict]: grid_count = grid_info['count'] full_direct = [] full_thresh = [] full_direct_blinds = [] for light_path in light_paths: array = results._get_array( grid_info, light_path, state=0, res_type="direct") array = np.apply_along_axis(filter_array, 1, array, occ_mask) full_direct.append(array) full_thresh.append((array >= 1000).sum(axis=0)) array = results._get_array( grid_info, light_path, state=1, res_type="direct") array = np.apply_along_axis(filter_array, 1, array, occ_mask) full_direct_blinds.append(array) full_direct = np.array(full_direct) full_direct_blinds = np.array(full_direct_blinds) full_direct_sum = full_direct.sum(axis=0) new_array = full_direct.copy() percentage_sensors = (full_direct_sum >= 1000).sum(axis=0) / grid_count if not np.any(percentage_sensors > 0.02): combinations = [ {light_path: 0 for light_path in light_paths} for i in range(full_direct_sum.shape[1])] else: tracking_array = np.zeros( (new_array.shape[0], new_array.shape[2]), dtype=int) percentage_sensors = (full_direct >= 1000).sum(axis=1) / grid_count ranking_indices = np.argsort(-percentage_sensors, axis=0) for rank in range(ranking_indices.shape[0]): # Calculate the percentage of sensors with values >= 1000 for the current new_array summed_array = np.sum(new_array, axis=0) percentage_sensors_summed = np.sum( summed_array >= 1000, axis=0) / grid_count indices_above_2_percent = np.where( percentage_sensors_summed > 0.02)[0] # Exit if there are no more hours exceeding the threshold if len(indices_above_2_percent) == 0: break # Array indices to use for replacement for these hours replace_indices = indices_above_2_percent array_indices = ranking_indices[rank, replace_indices] # Use advanced indexing to replace values in new_array for these hours for hour_idx, array_idx in zip(replace_indices, array_indices): new_array[array_idx, :, hour_idx] = full_direct_blinds[ array_idx, :, hour_idx ] # Update the tracking array tracking_array[array_indices, replace_indices] = 1 combinations = [] for hour in range(new_array.shape[2]): hour_dict = { light_paths[i]: tracking_array[i, hour] for i in range(tracking_array.shape[0])} combinations.append(hour_dict) final_summed_array = np.sum(new_array, axis=0) final_percentage_sensors_summed = ( final_summed_array >= 1000).sum( axis=0) / grid_count final_indices_above_2_percent = np.where( final_percentage_sensors_summed > 0.02)[0] if np.any(final_indices_above_2_percent): sun_up_hours_indices = np.where(occ_mask == 1)[0][ final_indices_above_2_percent] grid_comply = np.array(results.sun_up_hours)[sun_up_hours_indices] fail_to_comply[grid_info['name']] = [ int(hoy) for hoy in grid_comply] for combination in combinations: for light_path, value in combination.items(): if light_path != '__static_apertures__': states_schedule[light_path].append(value) return states_schedule, fail_to_comply