"""Functions for post-processing annual daylight outputs.
Note: These functions will most likely be moved to a separate package in the near future.
"""
import json
import os
import numpy as np
from ladybug.color import Colorset
from ladybug.datatype.fraction import Fraction
from ladybug.legend import LegendParameters
from honeybee_radiance.postprocess.annual import filter_schedule_by_hours, \
_process_input_folder
from .metrics import da_array2d, cda_array2d, udi_array2d, \
udi_lower_array2d, udi_upper_array2d
from .util import filter_array
[docs]
def metrics_to_files(ill_file, occ_pattern, output_folder, threshold=300,
min_t=100, max_t=3000, grid_name=None, total_hours=None,
sun_down_occ_hours=0):
"""Compute annual metrics for an ill file and write the results to a folder.
This function generates 5 different files or daylight autonomy, continuous daylight
autonomy, lower than useful daylight illuminance, useful daylight illuminance and
higher than useful daylight illuminance.
Args:
ill_file: Path to an ill file generated by Radiance. The ill file should be
tab separated and shot NOT have a header. The results for each sensor point
should be available in a row and and each column should be the illuminance
value for a sun_up_hour. The number of columns should match the number of
sun up hours.
occ_pattern: A list of 0 and 1 values for hours of occupancy.
output_folder: An output folder where the results will be written to. The folder
will be created if not exist.
threshold: Threshold illuminance level for daylight autonomy. Default: 300.
min_t: Minimum threshold for useful daylight illuminance. Default: 100.
max_t: Maximum threshold for useful daylight illuminance. Default: 3000.
grid_name: An optional name for grid name which will be used to name the output
files. If None the name of the input file will be used.
total_hours: An integer for the total number of occupied hours in the
occupancy schedule. If None, it will be assumed that all of the
occupied hours are sun-up hours and are already accounted for
in the the occ_pattern.
Returns:
Tuple(file.da, file.cda, file.udi, file.udi, file.udi)
"""
if not os.path.isdir(output_folder):
os.makedirs(output_folder)
grid_name = grid_name or os.path.split(ill_file)[-1][-4:]
da = os.path.join(output_folder, 'da', '%s.da' % grid_name).replace('\\', '/')
cda = os.path.join(output_folder, 'cda', '%s.cda' % grid_name).replace('\\', '/')
udi = os.path.join(output_folder, 'udi', '%s.udi' % grid_name).replace('\\', '/')
udi_lower = \
os.path.join(output_folder, 'udi_lower', '%s.udi' % grid_name).replace('\\', '/')
udi_upper = \
os.path.join(output_folder, 'udi_upper', '%s.udi' % grid_name).replace('\\', '/')
for file_path in [da, cda, udi, udi_upper, udi_lower]:
folder = os.path.dirname(file_path)
if not os.path.isdir(folder):
os.makedirs(folder)
mask = np.array(occ_pattern)
results = np.load(ill_file)
results_occ = np.apply_along_axis(filter_array, 1, results, mask=mask)
dar = da_array2d(results_occ, total_occ=total_hours, threshold=threshold)
np.savetxt(da, dar, fmt='%.2f')
cdar = cda_array2d(results_occ, total_occ=total_hours, threshold=threshold)
np.savetxt(cda, cdar, fmt='%.2f')
udir = udi_array2d(results_occ, total_occ=total_hours, min_t=min_t, max_t=max_t)
np.savetxt(udi, udir, fmt='%.2f')
udi_lowerr = udi_lower_array2d(results_occ, total_occ=total_hours, min_t=min_t,
sun_down_occ_hours=sun_down_occ_hours)
np.savetxt(udi_lower, udi_lowerr, fmt='%.2f')
udi_upperr = udi_upper_array2d(results_occ, total_occ=total_hours, max_t=max_t)
np.savetxt(udi_upper, udi_upperr, fmt='%.2f')
return da, cda, udi_lower, udi, udi_upper
# TODO - support a list of schedules/schedule folder to match the input grids
[docs]
def metrics_to_folder(
results_folder, schedule=None, threshold=300, min_t=100, max_t=3000,
grids_filter='*', sub_folder='metrics'
):
"""Compute annual metrics in a folder and write them in a subfolder.
This folder is an output folder of annual daylight recipe. Folder should include
grids_info.json and sun-up-hours.txt - the script uses the list in grids_info.json
to find the result files for each sensor grid.
Args:
results_folder: Results folder.
schedule: An annual schedule for 8760 hours of the year as a list of values.
threshold: Threshold illuminance level for daylight autonomy. Default: 300.
min_t: Minimum threshold for useful daylight illuminance. Default: 100.
max_t: Maximum threshold for useful daylight illuminance. Default: 3000.
grids_filter: A pattern to filter the grids. By default all the grids will be
processed.
sub_folder: An optional relative path for subfolder to copy results files.
Default: metrics
Returns:
str -- Path to results folder.
"""
grids, sun_up_hours = _process_input_folder(results_folder, grids_filter)
occ_pattern, total_occ, sun_down_occ_hours = \
filter_schedule_by_hours(sun_up_hours=sun_up_hours, schedule=schedule)
metrics_folder = os.path.join(results_folder, sub_folder)
if not os.path.isdir(metrics_folder):
os.makedirs(metrics_folder)
for grid in grids:
ill_file = os.path.join(results_folder, '%s.npy' % grid['full_id'])
metrics_to_files(
ill_file, occ_pattern, metrics_folder, threshold, min_t,
max_t, grid['full_id'], total_occ, sun_down_occ_hours
)
# copy info.json to all results folders
for folder_name in ['da', 'cda', 'udi_lower', 'udi', 'udi_upper']:
grid_info = os.path.join(metrics_folder, folder_name, 'grids_info.json')
with open(grid_info, 'w') as outf:
json.dump(grids, outf)
metric_info_dict = _annual_daylight_vis_metadata()
for metric, data in metric_info_dict.items():
file_path = os.path.join(metrics_folder, metric, 'vis_metadata.json')
with open(file_path, 'w') as fp:
json.dump(data, fp, indent=4)
return metrics_folder
def _annual_daylight_vis_metadata():
"""Return visualization metadata for annual daylight."""
udi_l_lpar = LegendParameters(min=0, max=100, colors=Colorset.nuanced())
udi_u_lpar = LegendParameters(min=0, max=100, colors=Colorset.glare_study())
udi_lpar = LegendParameters(min=0, max=100, colors=Colorset.annual_comfort())
cda_lpar = LegendParameters(min=0, max=100, colors=Colorset.annual_comfort())
da_lpar = LegendParameters(min=0, max=100, colors=Colorset.annual_comfort())
metric_info_dict = {
'udi_lower': {
'type': 'VisualizationMetaData',
'data_type': Fraction('Useful Daylight Illuminance Lower').to_dict(),
'unit': '%',
'legend_parameters': udi_l_lpar.to_dict()
},
'udi_upper': {
'type': 'VisualizationMetaData',
'data_type': Fraction('Useful Daylight Illuminance Upper').to_dict(),
'unit': '%',
'legend_parameters': udi_u_lpar.to_dict()
},
'udi': {
'type': 'VisualizationMetaData',
'data_type': Fraction('Useful Daylight Illuminance').to_dict(),
'unit': '%',
'legend_parameters': udi_lpar.to_dict()
},
'cda': {
'type': 'VisualizationMetaData',
'data_type': Fraction('Continuous Daylight Autonomy').to_dict(),
'unit': '%',
'legend_parameters': cda_lpar.to_dict()
},
'da': {
'type': 'VisualizationMetaData',
'data_type': Fraction('Daylight Autonomy').to_dict(),
'unit': '%',
'legend_parameters': da_lpar.to_dict()
}
}
return metric_info_dict
def _annual_daylight_config():
"""Return vtk-config for annual daylight. """
cfg = {
"data": [
{
"identifier": "Useful Daylight Illuminance Lower",
"object_type": "grid",
"unit": "Percentage",
"path": "udi_lower",
"hide": False,
"legend_parameters": {
"hide_legend": False,
"min": 0,
"max": 100,
"color_set": "nuanced",
},
},
{
"identifier": "Useful Daylight Illuminance Upper",
"object_type": "grid",
"unit": "Percentage",
"path": "udi_upper",
"hide": False,
"legend_parameters": {
"hide_legend": False,
"min": 0,
"max": 100,
"color_set": "glare_study",
"label_parameters": {
"color": [34, 247, 10],
"size": 0,
"bold": True,
},
},
},
{
"identifier": "Useful Daylight Illuminance",
"object_type": "grid",
"unit": "Percentage",
"path": "udi",
"hide": False,
"legend_parameters": {
"hide_legend": False,
"min": 0,
"max": 100,
"color_set": "annual_comfort",
"label_parameters": {
"color": [34, 247, 10],
"size": 0,
"bold": True,
},
},
},
{
"identifier": "Continuous Daylight Autonomy",
"object_type": "grid",
"unit": "Percentage",
"path": "cda",
"hide": False,
"legend_parameters": {
"hide_legend": False,
"min": 0,
"max": 100,
"color_set": "annual_comfort",
"label_parameters": {
"color": [34, 247, 10],
"size": 0,
"bold": True,
},
},
},
{
"identifier": "Daylight Autonomy",
"object_type": "grid",
"unit": "Percentage",
"path": "da",
"hide": False,
"legend_parameters": {
"hide_legend": False,
"min": 0,
"max": 100,
"color_set": "annual_comfort",
"label_parameters": {
"color": [34, 247, 10],
"size": 0,
"bold": True,
},
},
},
]
}
return cfg