Source code for honeybee_radiance_postprocess.helper

"""Helper functions."""
import json
import numpy as np
from pathlib import Path

from honeybee.model import Model


[docs]def model_grid_areas(model, grids_info): if isinstance(model, Model): hb_model = model else: hb_model = Model.from_file(model) full_ids = [grid_info['full_id'] for grid_info in grids_info] sensor_grids = hb_model.properties.radiance.sensor_grids grid_areas = [] for s_grid in sensor_grids: if s_grid.identifier in full_ids: if s_grid.mesh is not None: grid_areas.append(s_grid.mesh.face_areas) grid_areas = [np.array(grid) for grid in grid_areas] if not grid_areas: grid_areas = [None] * len(full_ids) return grid_areas
[docs]def grid_summary( folder: Path, grid_areas: list = None, grids_info: list = None, name: str = 'grid_summary', grid_metrics: list = None, sub_folder: bool = True ): """Calculate a grid summary for a single metric. Args: folder: A folder with results. grid_areas: A list of area of each sensor. grids_info: Grid information as a dictionary. name: Optional filename of grid summary. grid_metrics: Additional customized metrics to calculate. sub_folder: If set to True it will look for results in all sub-folders in the folder input. Else it look for results directly in the folder input. """ if sub_folder: sub_folders = [sf for sf in folder.iterdir() if sf.is_dir()] else: sub_folders = [folder] # set up the default data types dtype_sensor_grid = ('Sensor Grid', 'O') dtype_sensor_grid_id = ('Sensor Grid ID', 'O') dtype_base = [ ('Mean', np.float32), ('Minimum', np.float32), ('Maximum', np.float32), ('Uniformity Ratio', np.float32) ] dtype = [] # set up default format (for first two columns: str) fmt = ['%s', '%s'] if grids_info is None: for sf in sub_folders: gi_file = sf.joinpath('grids_info.json') if gi_file.exists(): with open(gi_file) as gi: grids_info = json.load(gi) break if grids_info is None: # if it did not find grids_info.json in any folder raise FileNotFoundError( f'The file grids_info.json was not found in any folder.') if grid_areas is None: grid_areas = [None] * len(grids_info) dtype.append(dtype_sensor_grid) dtype.append(dtype_sensor_grid_id) for sf in sub_folders: _dtype = [] _fmt = [] for dt_b in dtype_base: col_name = dt_b[0] if sub_folder: col_name = '-'.join([sf.stem.upper(), col_name]) _dtype.append((col_name, np.float32)) _fmt.append('%.2f') dtype.extend(_dtype) fmt.extend(_fmt) if grid_metrics is not None: for grid_metric in grid_metrics: if len(grid_metric) == 1: if 'allOf' in grid_metric: _mname = [] for gr_m in grid_metric['allOf']: _mname.append(_get_grid_metric_name(gr_m)) mname = ' and '.join(_mname) elif 'anyOf' in grid_metric: _mname = [] for gr_m in grid_metric['anyOf']: _mname.append(_get_grid_metric_name(gr_m)) mname = ' or '.join(_mname) else: mname = _get_grid_metric_name(grid_metric) elif len(grid_metric) == 2: _mname = [] for k, v in grid_metric.items(): _mname.append(_get_grid_metric_name({k: v})) mname = ' and '.join(_mname) col_name = mname if sub_folder: col_name = '-'.join([sf.stem.upper(), col_name]) dtype.append((col_name, np.float32)) fmt.append('%.2f') arrays = [] for grid_info, grid_area in zip(grids_info, grid_areas): full_id = grid_info['full_id'] grid_name = grid_info['name'] data = [grid_name, full_id] for sf in sub_folders: grid_files = list(sf.glob(f'{full_id}.*')) assert len(grid_files) == 1 array = np.loadtxt(grid_files[0]) _mean = array.mean() _min = array.min() _max = array.max() _uniformity_ratio = _min / _mean * 100 data.extend([_mean, _min, _max, _uniformity_ratio]) if grid_metrics is not None: # get grid metrics grid_metrics_data = \ _get_grid_metrics(array, grid_metrics, grid_info, grid_area) data.extend(grid_metrics_data) arrays.append(tuple(data)) # create structured array struct_array = np.array(arrays, dtype=dtype) header = [dt[0] for dt in dtype] # write header to file with open(folder.joinpath(f'{name}.csv'), 'w') as grid_summary_file: grid_summary_file.write(','.join(header)) # write structured array to grid_summary_file with open(folder.joinpath(f'{name}.csv'), 'a') as grid_summary_file: grid_summary_file.write('\n') np.savetxt(grid_summary_file, struct_array, delimiter=',', fmt=fmt) return grid_summary_file
def _calculate_percentage(gr_metric_bool, grid_info, grid_area=None): """Calculate percentage of floor area where True. Args: gr_metric_bool: A NumPy array of booleans. grid_info: Grid information. grid_area: A NumPy array of area for each sensor. (Default: None). Returns: The percentage of floor area where gr_metric_bool is True. """ if grid_area is not None: gr_metric_pct = \ grid_area[gr_metric_bool].sum() / grid_area.sum() * 100 else: gr_metric_pct = \ gr_metric_bool.sum() / grid_info['count'] * 100 return gr_metric_pct def _logical_operator(keyword): lg = { 'minimum': '>', 'exclusiveMinimum': '>=', 'maximum': '<', 'exclusiveMaximum': '<=' } return lg[keyword] def _get_grid_metric_name(grid_metric): if 'minimum' in grid_metric: return f'{_logical_operator("minimum")}{grid_metric["minimum"]}' elif 'exclusiveMinimum' in grid_metric: return f'{_logical_operator("exclusiveMinimum")}{grid_metric["exclusiveMinimum"]}' elif 'maximum' in grid_metric: return f'{_logical_operator("maximum")}{grid_metric["maximum"]}' elif 'exclusiveMaximum' in grid_metric: return f'{_logical_operator("exclusiveMaximum")}{grid_metric["exclusiveMaximum"]}' def _numeric_type(array, gr_metric): if 'minimum' in gr_metric: gr_metric_bool = array > gr_metric['minimum'] elif 'exclusiveMinimum' in gr_metric: gr_metric_bool = array >= gr_metric['minimum'] elif 'maximum' in gr_metric: gr_metric_bool = array < gr_metric['maximum'] elif 'exclusiveMaximum' in gr_metric: gr_metric_bool = array <= gr_metric['exclusiveMaximum'] return gr_metric_bool def _grid_summary_all_any(array, gr_metric, grid_info, grid_area, keyword): gr_metric_arrays = [] for gr_m in gr_metric[keyword]: assert len(gr_m) == 1 gr_metric_arrays.append(_numeric_type(array, gr_m)) if keyword == 'allOf': gr_metric_bool = np.all(gr_metric_arrays, axis=0) else: gr_metric_bool = np.any(gr_metric_arrays, axis=0) gr_metric_pct = \ _calculate_percentage(gr_metric_bool, grid_info, grid_area) return gr_metric_pct def _get_grid_metrics(array, grid_metrics, grid_info, grid_area): grid_metrics_data = [] for gr_metric in grid_metrics: if len(gr_metric) == 1: if 'allOf' in gr_metric: gr_metric_pct = \ _grid_summary_all_any( array, gr_metric, grid_info, grid_area, 'allOf') elif 'anyOf' in gr_metric: gr_metric_pct = \ _grid_summary_all_any( array, gr_metric, grid_info, grid_area, 'anyOf') else: gr_metric_bool = _numeric_type(array, gr_metric) gr_metric_pct = \ _calculate_percentage(gr_metric_bool, grid_info, grid_area) elif len(gr_metric) == 2: gr_metric_arrays = [] for k, threshold in gr_metric.items(): gr_metric_arrays.append(_numeric_type(array, {k: threshold})) gr_metric_bool = np.all(gr_metric_arrays, axis=0) gr_metric_pct = \ _calculate_percentage(gr_metric_bool, grid_info, grid_area) grid_metrics_data.append(gr_metric_pct) return grid_metrics_data