From de1e63e4717020b85923cf947744bf7f920d0e6d Mon Sep 17 00:00:00 2001 From: Mikkel Pedersen Date: Thu, 19 Oct 2023 12:35:02 +0200 Subject: [PATCH 1/4] fix(results): Add uniformity ratio --- honeybee_radiance_postprocess/results.py | 51 +++++++++++++++++++++ honeybee_radiance_postprocess/type_hints.py | 2 + 2 files changed, 53 insertions(+) diff --git a/honeybee_radiance_postprocess/results.py b/honeybee_radiance_postprocess/results.py index ee454336..07dcf6de 100644 --- a/honeybee_radiance_postprocess/results.py +++ b/honeybee_radiance_postprocess/results.py @@ -8,6 +8,7 @@ from ladybug.analysisperiod import AnalysisPeriod from ladybug.datacollection import HourlyContinuousCollection from ladybug.datatype.illuminance import Illuminance +from ladybug.datatype.fraction import Fraction from ladybug.dt import DateTime from ladybug.header import Header from honeybee_radiance.postprocess.annual import _process_input_folder, \ @@ -1334,6 +1335,56 @@ def daylight_control_schedules_to_folder( info_file = schedule_folder.joinpath('grids_info.json') info_file.write_text(json.dumps(grids_info)) + def annual_uniformity_ratio( + self, threshold: float = 0.5, states: DynamicSchedule = None, + grids_filter: str = '*') -> type_hints.annual_uniformity_ratio: + """Calculate annual uniformity ratio. + + Args: + threshold: A threshold for the uniformity ratio. Defaults to 0.5. + states: A dictionary of states. Defaults to None. + grids_filter: The name of a grid or a pattern to filter the grids. + Defaults to '*'. + + Returns: + Tuple: A tuple with the daylight autonomy and grid information. + """ + grids_info = self._filter_grids(grids_filter=grids_filter) + analysis_period = AnalysisPeriod(timestep=self.timestep) + + data_collections = [] + annual_uniformity_ratio = [] + for grid_info in grids_info: + array = self._array_from_states(grid_info, states=states, res_type='total') + if np.any(array): + su_min_array = array.min(axis=0) + su_mean_array = array.mean(axis=0) + su_uniformity_ratio = su_min_array / su_mean_array + + array_filter = np.apply_along_axis( + filter_array, 1, array, mask=self.occ_mask) + min_array = array_filter.min(axis=0) + mean_array = array_filter.mean(axis=0) + uniformity_ratio = min_array / mean_array + annual_uniformity_ratio.append( + np.float64( + (uniformity_ratio >= threshold).sum() / self.total_occ * 100 + ) + ) + else: + su_uniformity_ratio = np.zeros(len(self.sun_up_hours)) + annual_uniformity_ratio.append(np.float64(0)) + + annual_array = \ + self.values_to_annual( + self.sun_up_hours, su_uniformity_ratio, self.timestep) + header = Header(Fraction(), '%', analysis_period) + header.metadata['sensor grid'] = grid_info['full_id'] + data_collections.append( + HourlyContinuousCollection(header, annual_array.tolist())) + + return annual_uniformity_ratio, data_collections, grids_info + @staticmethod def values_to_annual( hours: Union[List[float], np.ndarray], diff --git a/honeybee_radiance_postprocess/type_hints.py b/honeybee_radiance_postprocess/type_hints.py index d5553f87..91cc4e86 100644 --- a/honeybee_radiance_postprocess/type_hints.py +++ b/honeybee_radiance_postprocess/type_hints.py @@ -21,3 +21,5 @@ cumulative_values = Tuple[List[np.ndarray], List[dict]] peak_values = Tuple[List[np.ndarray], List[dict]] annual_data = Tuple[List[List[HourlyContinuousCollection]], List[dict], dict] +annual_uniformity_ratio = \ + Tuple[List[float], List[HourlyContinuousCollection], List[dict]] From f11b1c493f7fd4c0943cf740c51b7e64e78a38ad Mon Sep 17 00:00:00 2001 From: Mikkel Pedersen Date: Fri, 27 Oct 2023 12:30:02 +0200 Subject: [PATCH 2/4] fix(results): Add uniformity ratio to folder --- honeybee_radiance_postprocess/results.py | 41 +++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/honeybee_radiance_postprocess/results.py b/honeybee_radiance_postprocess/results.py index 07dcf6de..650fdcd2 100644 --- a/honeybee_radiance_postprocess/results.py +++ b/honeybee_radiance_postprocess/results.py @@ -1347,7 +1347,8 @@ def annual_uniformity_ratio( Defaults to '*'. Returns: - Tuple: A tuple with the daylight autonomy and grid information. + Tuple: A tuple with the annual uniformity ratio, annual + data collections, and grid information. """ grids_info = self._filter_grids(grids_filter=grids_filter) analysis_period = AnalysisPeriod(timestep=self.timestep) @@ -1385,6 +1386,44 @@ def annual_uniformity_ratio( return annual_uniformity_ratio, data_collections, grids_info + def annual_uniformity_ratio_to_folder( + self, target_folder: str, threshold: float = 0.5, + states: DynamicSchedule = None, grids_filter: str = '*' + ) -> type_hints.annual_uniformity_ratio: + """Calculate annual uniformity ratio and write it to a folder. + + Args: + target_folder: Folder path to write annual uniformity ratio in. + threshold: A threshold for the uniformity ratio. Defaults to 0.5. + states: A dictionary of states. Defaults to None. + grids_filter: The name of a grid or a pattern to filter the grids. + Defaults to '*'. + + Returns: + Tuple: A tuple with the daylight autonomy and grid information. + """ + folder = Path(target_folder) + folder.mkdir(parents=True, exist_ok=True) + + annual_uniformity_ratio, data_collections, grids_info = \ + self.annual_uniformity_ratio(threshold=threshold, states=states, + grids_filter=grids_filter) + + datacollection_folder = folder.joinpath('datacollections') + uniformity_ratio_folder = folder.joinpath('uniformity_ratio') + + for aur, data_collection, grid_info in \ + zip(annual_uniformity_ratio, data_collections, grids_info): + grid_id = grid_info['full_id'] + data_dict = data_collection.to_dict() + data_file = datacollection_folder.joinpath(f'{grid_id}.json') + data_file.parent.mkdir(parents=True, exist_ok=True) + data_file.write_text(json.dumps(data_dict)) + + aur_file = uniformity_ratio_folder.joinpath(f'{grid_id}.ur') + aur_file.parent.mkdir(parents=True, exist_ok=True) + aur_file.write_text(str(round(aur, 2))) + @staticmethod def values_to_annual( hours: Union[List[float], np.ndarray], From a1a36907eba1a3897902d31fb44a542a8c3d875c Mon Sep 17 00:00:00 2001 From: Mikkel Pedersen Date: Fri, 27 Oct 2023 12:30:46 +0200 Subject: [PATCH 3/4] fix(postprocess): Add uniformity ratio CLI command --- .../cli/postprocess.py | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/honeybee_radiance_postprocess/cli/postprocess.py b/honeybee_radiance_postprocess/cli/postprocess.py index e5901965..9d2e514f 100644 --- a/honeybee_radiance_postprocess/cli/postprocess.py +++ b/honeybee_radiance_postprocess/cli/postprocess.py @@ -747,3 +747,65 @@ def grid_summary_metric( sys.exit(1) else: sys.exit(0) + + +@post_process.command('annual-uniformity-ratio') +@click.argument( + 'folder', + type=click.Path(exists=True, file_okay=False, dir_okay=True, resolve_path=True) +) +@click.option( + '--schedule', '-sch', help='Path to an annual schedule file. Values should be 0-1 ' + 'separated by new line. If not provided an 8-5 annual schedule will be created.', + type=click.Path(exists=False, file_okay=True, dir_okay=False, resolve_path=True) +) +@click.option( + '--threshold', '-t', help='A threshold for the uniformity ratio. Defaults ' + 'to 0.5.', + default=0.5, type=click.FloatRange(0, 1), show_default=True +) +@click.option( + '--states', '-st', help='A JSON file with a dictionary of states. If states are not ' + 'provided the default states will be used for any aperture groups.', default=None, + type=click.Path(exists=False, file_okay=True, dir_okay=False, resolve_path=True) +) +@click.option( + '--grids-filter', '-gf', help='A pattern to filter the grids.', default='*', + show_default=True +) +@click.option( + '--sub-folder', '-sf', help='Optional relative path for subfolder to write ' + 'annual uniformity ratio.', default='annual_uniformity_ratio' +) +def annual_uniformity_ratio( + folder, schedule, threshold, states, grids_filter, sub_folder +): + """Calculate annual uniformity ratio and write it to a folder. + + \b + Args: + folder: Results folder. This folder is an output folder of annual + daylight recipe. Folder should include grids_info.json and + sun-up-hours.txt. + """ + # optional input - only check if the file exist otherwise ignore + if schedule and os.path.isfile(schedule): + with open(schedule) as hourly_schedule: + schedule = [int(float(v)) for v in hourly_schedule] + else: + schedule = None + + if states: + states = DynamicSchedule.from_json(states) + + try: + results = Results(folder, schedule=schedule) + results.annual_uniformity_ratio_to_folder( + sub_folder, threshold=threshold, states=states, + grids_filter=grids_filter + ) + except Exception: + _logger.exception('Failed to calculate annual uniformity ratio.') + sys.exit(1) + else: + sys.exit(0) From da75ae2e1a8a5079192b3e4cc5e4553db1489aae Mon Sep 17 00:00:00 2001 From: Mikkel Pedersen Date: Fri, 27 Oct 2023 13:18:54 +0200 Subject: [PATCH 4/4] fix(results): Write grids info in uniformity ratio --- honeybee_radiance_postprocess/results.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/honeybee_radiance_postprocess/results.py b/honeybee_radiance_postprocess/results.py index 650fdcd2..e323075a 100644 --- a/honeybee_radiance_postprocess/results.py +++ b/honeybee_radiance_postprocess/results.py @@ -1424,6 +1424,9 @@ def annual_uniformity_ratio_to_folder( aur_file.parent.mkdir(parents=True, exist_ok=True) aur_file.write_text(str(round(aur, 2))) + info_file = uniformity_ratio_folder.joinpath('grids_info.json') + info_file.write_text(json.dumps(grids_info)) + @staticmethod def values_to_annual( hours: Union[List[float], np.ndarray],