Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(results): Add uniformity ratio #142

Merged
merged 4 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions honeybee_radiance_postprocess/cli/postprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
93 changes: 93 additions & 0 deletions honeybee_radiance_postprocess/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -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, \
Expand Down Expand Up @@ -1334,6 +1335,98 @@ 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 annual uniformity ratio, annual
data collections, 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

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

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],
Expand Down
2 changes: 2 additions & 0 deletions honeybee_radiance_postprocess/type_hints.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]]
Loading