diff --git a/lbt_recipes/imageless_annual_glare/flow/dependencies/imageless_annual_glare.py b/lbt_recipes/imageless_annual_glare/flow/dependencies/imageless_annual_glare.py index fae61fc1..1d2c681c 100644 --- a/lbt_recipes/imageless_annual_glare/flow/dependencies/imageless_annual_glare.py +++ b/lbt_recipes/imageless_annual_glare/flow/dependencies/imageless_annual_glare.py @@ -20,12 +20,10 @@ _default_inputs = { 'bsdfs': None, - 'glare_limit': 0.4, 'grid_name': None, 'octree_file': None, 'params_folder': '__params', 'radiance_parameters': '-ab 2 -ad 5000 -lw 2e-05', - 'schedule': None, 'sensor_count': None, 'sensor_grid': None, 'simulation_folder': '.', @@ -33,105 +31,6 @@ 'sky_matrix': None} -class DaylightGlareAutonomy(QueenbeeTask): - """Calculates daylight glare autonomy. The daylight glare autonomy is the fraction - of (occupied) hours without any detected glare. The detection of glare is controlled - by glare_limit.""" - - # DAG Input parameters - _input_params = luigi.DictParameter() - - # Task inputs - @property - def name(self): - return self._input_params['grid_name'] - - @property - def glare_limit(self): - return self._input_params['glare_limit'] - - threshold_factor = luigi.Parameter(default='2000.0') - - @property - def dc_direct(self): - value = pathlib.Path(self.input()['DirectSky']['result_file'].path) - return value.as_posix() if value.is_absolute() \ - else pathlib.Path(self.initiation_folder, value).resolve().as_posix() - - @property - def dc_total(self): - value = pathlib.Path(self.input()['TotalSky']['result_file'].path) - return value.as_posix() if value.is_absolute() \ - else pathlib.Path(self.initiation_folder, value).resolve().as_posix() - - @property - def sky_vector(self): - value = pathlib.Path(self._input_params['sky_matrix']) - return value.as_posix() if value.is_absolute() \ - else pathlib.Path(self.initiation_folder, value).resolve().as_posix() - - @property - def view_rays(self): - value = pathlib.Path(self._input_params['sensor_grid']) - return value.as_posix() if value.is_absolute() \ - else pathlib.Path(self.initiation_folder, value).resolve().as_posix() - - @property - def schedule(self): - try: - pathlib.Path(self._input_params['schedule']) - except TypeError: - # optional artifact - return None - value = pathlib.Path(self._input_params['schedule']) - return value.as_posix() if value.is_absolute() \ - else pathlib.Path(self.initiation_folder, value).resolve().as_posix() - - @property - def execution_folder(self): - return pathlib.Path(self._input_params['simulation_folder']).as_posix() - - @property - def initiation_folder(self): - return pathlib.Path(self._input_params['simulation_folder']).as_posix() - - @property - def params_folder(self): - return pathlib.Path(self.execution_folder, self._input_params['params_folder']).resolve().as_posix() - - def command(self): - return 'honeybee-radiance dcglare two-phase dc_direct.mtx dc_total.mtx sky.smx view_rays.ray --glare-limit {glare_limit} --threshold-factor {threshold_factor} --occupancy-schedule schedule.txt --output view_rays.ga'.format(glare_limit=self.glare_limit, threshold_factor=self.threshold_factor) - - def requires(self): - return {'TotalSky': TotalSky(_input_params=self._input_params), 'DirectSky': DirectSky(_input_params=self._input_params)} - - def output(self): - return { - 'view_rays_glare_autonomy': luigi.LocalTarget( - pathlib.Path(self.execution_folder, '../ga/{name}.ga'.format(name=self.name)).resolve().as_posix() - ) - } - - @property - def input_artifacts(self): - return [ - {'name': 'dc_direct', 'to': 'dc_direct.mtx', 'from': self.dc_direct, 'optional': False}, - {'name': 'dc_total', 'to': 'dc_total.mtx', 'from': self.dc_total, 'optional': False}, - {'name': 'sky_vector', 'to': 'sky.smx', 'from': self.sky_vector, 'optional': False}, - {'name': 'view_rays', 'to': 'view_rays.ray', 'from': self.view_rays, 'optional': False}, - {'name': 'schedule', 'to': 'schedule.txt', 'from': self.schedule, 'optional': True}] - - @property - def output_artifacts(self): - return [ - { - 'name': 'view-rays-glare-autonomy', 'from': 'view_rays.ga', - 'to': pathlib.Path(self.execution_folder, '../ga/{name}.ga'.format(name=self.name)).resolve().as_posix(), - 'optional': False, - 'type': 'file' - }] - - class DaylightGlareProbability(QueenbeeTask): """Calculates DGP for all sky conditions in the sky matrix, but filtered by an occupancy schedule. This means that unoccupied hours will be zero DGP. If the @@ -172,17 +71,6 @@ def view_rays(self): return value.as_posix() if value.is_absolute() \ else pathlib.Path(self.initiation_folder, value).resolve().as_posix() - @property - def schedule(self): - try: - pathlib.Path(self._input_params['schedule']) - except TypeError: - # optional artifact - return None - value = pathlib.Path(self._input_params['schedule']) - return value.as_posix() if value.is_absolute() \ - else pathlib.Path(self.initiation_folder, value).resolve().as_posix() - @property def execution_folder(self): return pathlib.Path(self._input_params['simulation_folder']).as_posix() @@ -214,8 +102,7 @@ def input_artifacts(self): {'name': 'dc_direct', 'to': 'dc_direct.mtx', 'from': self.dc_direct, 'optional': False}, {'name': 'dc_total', 'to': 'dc_total.mtx', 'from': self.dc_total, 'optional': False}, {'name': 'sky_vector', 'to': 'sky.smx', 'from': self.sky_vector, 'optional': False}, - {'name': 'view_rays', 'to': 'view_rays.ray', 'from': self.view_rays, 'optional': False}, - {'name': 'schedule', 'to': 'schedule.txt', 'from': self.schedule, 'optional': True}] + {'name': 'view_rays', 'to': 'view_rays.ray', 'from': self.view_rays, 'optional': False}] @property def output_artifacts(self): @@ -422,7 +309,7 @@ def output_artifacts(self): }] -class _ImagelessAnnualGlare_6c61a333Orchestrator(luigi.WrapperTask): +class _ImagelessAnnualGlare_bafe869fOrchestrator(luigi.WrapperTask): """Runs all the tasks in this module.""" # user input for this module _input_params = luigi.DictParameter() @@ -434,4 +321,4 @@ def input_values(self): return params def requires(self): - yield [DaylightGlareAutonomy(_input_params=self.input_values), DaylightGlareProbability(_input_params=self.input_values)] + yield [DaylightGlareProbability(_input_params=self.input_values)] diff --git a/lbt_recipes/imageless_annual_glare/flow/main.py b/lbt_recipes/imageless_annual_glare/flow/main.py index a09b09a4..3459b145 100644 --- a/lbt_recipes/imageless_annual_glare/flow/main.py +++ b/lbt_recipes/imageless_annual_glare/flow/main.py @@ -17,7 +17,7 @@ import pathlib from queenbee_local import QueenbeeTask from queenbee_local import load_input_param as qb_load_input_param -from .dependencies.imageless_annual_glare import _ImagelessAnnualGlare_6c61a333Orchestrator as ImagelessAnnualGlare_6c61a333Workerbee +from .dependencies.imageless_annual_glare import _ImagelessAnnualGlare_bafe869fOrchestrator as ImagelessAnnualGlare_bafe869fWorkerbee _default_inputs = { 'cpu_count': 50, @@ -52,10 +52,6 @@ def grid_name(self): def sensor_count(self): return self.item['count'] - @property - def glare_limit(self): - return self._input_params['glare_threshold'] - @property def octree_file(self): value = pathlib.Path(self.input()['CreateOctree']['scene_file'].path) @@ -91,17 +87,6 @@ def bsdfs(self): return value.as_posix() if value.is_absolute() \ else pathlib.Path(self.initiation_folder, value).resolve().as_posix() - @property - def schedule(self): - try: - pathlib.Path(self._input_params['schedule']) - except TypeError: - # optional artifact - return None - value = pathlib.Path(self._input_params['schedule']) - return value.as_posix() if value.is_absolute() \ - else pathlib.Path(self.initiation_folder, value).resolve().as_posix() - # get item for loop try: item = luigi.DictParameter() @@ -132,9 +117,7 @@ def map_dag_inputs(self): 'sensor_count': self.sensor_count, 'sky_matrix': self.sky_matrix, 'sky_dome': self.sky_dome, - 'bsdfs': self.bsdfs, - 'schedule': self.schedule, - 'glare_limit': self.glare_limit + 'bsdfs': self.bsdfs } try: inputs['__debug__'] = self._input_params['__debug__'] @@ -145,7 +128,7 @@ def map_dag_inputs(self): return inputs def run(self): - yield [ImagelessAnnualGlare_6c61a333Workerbee(_input_params=self.map_dag_inputs)] + yield [ImagelessAnnualGlare_bafe869fWorkerbee(_input_params=self.map_dag_inputs)] done_file = pathlib.Path(self.execution_folder, 'annual_imageless_glare.done') done_file.parent.mkdir(parents=True, exist_ok=True) done_file.write_text('done!') @@ -569,22 +552,31 @@ def output_artifacts(self): }] -class GenerateSunpath(QueenbeeTask): - """Generate a Radiance sun matrix (AKA sun-path).""" +class DaylightGlareAutonomy(QueenbeeTask): + """Calculate annual glare autonomy for imageless annual glare simulation.""" # DAG Input parameters _input_params = luigi.DictParameter() # Task inputs @property - def north(self): - return self._input_params['north'] + def glare_threshold(self): + return self._input_params['glare_threshold'] - output_type = luigi.Parameter(default='0') + @property + def folder(self): + value = pathlib.Path(self.input()['RestructureDaylightGlareProbabilityResults']['output_folder'].path) + return value.as_posix() if value.is_absolute() \ + else pathlib.Path(self.initiation_folder, value).resolve().as_posix() @property - def wea(self): - value = pathlib.Path(self._input_params['wea']) + def schedule(self): + try: + pathlib.Path(self._input_params['schedule']) + except TypeError: + # optional artifact + return None + value = pathlib.Path(self._input_params['schedule']) return value.as_posix() if value.is_absolute() \ else pathlib.Path(self.initiation_folder, value).resolve().as_posix() @@ -601,41 +593,51 @@ def params_folder(self): return pathlib.Path(self.execution_folder, self._input_params['params_folder']).resolve().as_posix() def command(self): - return 'gendaymtx -n -D sunpath.mtx -M suns.mod -O{output_type} -r {north} -v sky.wea'.format(output_type=self.output_type, north=self.north) + return 'honeybee-radiance post-process annual-glare raw_results --schedule schedule.txt --glare-threshold {glare_threshold} --sub_folder ../metrics'.format(glare_threshold=self.glare_threshold) + + def requires(self): + return {'RestructureDaylightGlareProbabilityResults': RestructureDaylightGlareProbabilityResults(_input_params=self._input_params)} def output(self): return { - 'sun_modifiers': luigi.LocalTarget( - pathlib.Path(self.execution_folder, 'resources/suns.mod').resolve().as_posix() + 'annual_metrics': luigi.LocalTarget( + pathlib.Path(self.execution_folder, 'metrics').resolve().as_posix() ) } @property def input_artifacts(self): return [ - {'name': 'wea', 'to': 'sky.wea', 'from': self.wea, 'optional': False}] + {'name': 'folder', 'to': 'raw_results', 'from': self.folder, 'optional': False}, + {'name': 'schedule', 'to': 'schedule.txt', 'from': self.schedule, 'optional': True}] @property def output_artifacts(self): return [ { - 'name': 'sun-modifiers', 'from': 'suns.mod', - 'to': pathlib.Path(self.execution_folder, 'resources/suns.mod').resolve().as_posix(), + 'name': 'annual-metrics', 'from': 'metrics', + 'to': pathlib.Path(self.execution_folder, 'metrics').resolve().as_posix(), 'optional': False, - 'type': 'file' + 'type': 'folder' }] -class ParseSunUpHours(QueenbeeTask): - """Parse sun up hours from sun modifiers file.""" +class GenerateSunpath(QueenbeeTask): + """Generate a Radiance sun matrix (AKA sun-path).""" # DAG Input parameters _input_params = luigi.DictParameter() # Task inputs @property - def sun_modifiers(self): - value = pathlib.Path(self.input()['GenerateSunpath']['sun_modifiers'].path) + def north(self): + return self._input_params['north'] + + output_type = luigi.Parameter(default='0') + + @property + def wea(self): + value = pathlib.Path(self._input_params['wea']) return value.as_posix() if value.is_absolute() \ else pathlib.Path(self.initiation_folder, value).resolve().as_posix() @@ -652,48 +654,41 @@ def params_folder(self): return pathlib.Path(self.execution_folder, self._input_params['params_folder']).resolve().as_posix() def command(self): - return 'honeybee-radiance sunpath parse-hours suns.mod --name sun-up-hours.txt' - - def requires(self): - return {'GenerateSunpath': GenerateSunpath(_input_params=self._input_params)} + return 'gendaymtx -n -D sunpath.mtx -M suns.mod -O{output_type} -r {north} -v sky.wea'.format(output_type=self.output_type, north=self.north) def output(self): return { - 'sun_up_hours': luigi.LocalTarget( - pathlib.Path(self.execution_folder, 'results/sun-up-hours.txt').resolve().as_posix() + 'sun_modifiers': luigi.LocalTarget( + pathlib.Path(self.execution_folder, 'resources/suns.mod').resolve().as_posix() ) } @property def input_artifacts(self): return [ - {'name': 'sun_modifiers', 'to': 'suns.mod', 'from': self.sun_modifiers, 'optional': False}] + {'name': 'wea', 'to': 'sky.wea', 'from': self.wea, 'optional': False}] @property def output_artifacts(self): return [ { - 'name': 'sun-up-hours', 'from': 'sun-up-hours.txt', - 'to': pathlib.Path(self.execution_folder, 'results/sun-up-hours.txt').resolve().as_posix(), + 'name': 'sun-modifiers', 'from': 'suns.mod', + 'to': pathlib.Path(self.execution_folder, 'resources/suns.mod').resolve().as_posix(), 'optional': False, 'type': 'file' }] -class RestructureDaylightGlareProbabilityResults(QueenbeeTask): - """Restructure files in a distributed folder.""" +class ParseSunUpHours(QueenbeeTask): + """Parse sun up hours from sun modifiers file.""" # DAG Input parameters _input_params = luigi.DictParameter() # Task inputs @property - def extension(self): - return 'dgp' - - @property - def input_folder(self): - value = pathlib.Path('initial_results/dgp') + def sun_modifiers(self): + value = pathlib.Path(self.input()['GenerateSunpath']['sun_modifiers'].path) return value.as_posix() if value.is_absolute() \ else pathlib.Path(self.initiation_folder, value).resolve().as_posix() @@ -710,35 +705,35 @@ def params_folder(self): return pathlib.Path(self.execution_folder, self._input_params['params_folder']).resolve().as_posix() def command(self): - return 'honeybee-radiance grid merge-folder ./input_folder ./output_folder {extension} --dist-info dist_info.json'.format(extension=self.extension) + return 'honeybee-radiance sunpath parse-hours suns.mod --name sun-up-hours.txt' def requires(self): - return {'AnnualImagelessGlare': AnnualImagelessGlare(_input_params=self._input_params)} + return {'GenerateSunpath': GenerateSunpath(_input_params=self._input_params)} def output(self): return { - 'output_folder': luigi.LocalTarget( - pathlib.Path(self.execution_folder, 'results').resolve().as_posix() + 'sun_up_hours': luigi.LocalTarget( + pathlib.Path(self.execution_folder, 'results/sun-up-hours.txt').resolve().as_posix() ) } @property def input_artifacts(self): return [ - {'name': 'input_folder', 'to': 'input_folder', 'from': self.input_folder, 'optional': False}] + {'name': 'sun_modifiers', 'to': 'suns.mod', 'from': self.sun_modifiers, 'optional': False}] @property def output_artifacts(self): return [ { - 'name': 'output-folder', 'from': 'output_folder', - 'to': pathlib.Path(self.execution_folder, 'results').resolve().as_posix(), + 'name': 'sun-up-hours', 'from': 'sun-up-hours.txt', + 'to': pathlib.Path(self.execution_folder, 'results/sun-up-hours.txt').resolve().as_posix(), 'optional': False, - 'type': 'folder' + 'type': 'file' }] -class RestructureGlareAutonomyResults(QueenbeeTask): +class RestructureDaylightGlareProbabilityResults(QueenbeeTask): """Restructure files in a distributed folder.""" # DAG Input parameters @@ -747,11 +742,11 @@ class RestructureGlareAutonomyResults(QueenbeeTask): # Task inputs @property def extension(self): - return 'ga' + return 'dgp' @property def input_folder(self): - value = pathlib.Path('initial_results/ga') + value = pathlib.Path('initial_results/dgp') return value.as_posix() if value.is_absolute() \ else pathlib.Path(self.initiation_folder, value).resolve().as_posix() @@ -776,7 +771,7 @@ def requires(self): def output(self): return { 'output_folder': luigi.LocalTarget( - pathlib.Path(self.execution_folder, 'metrics/ga').resolve().as_posix() + pathlib.Path(self.execution_folder, 'results').resolve().as_posix() ) } @@ -790,7 +785,7 @@ def output_artifacts(self): return [ { 'name': 'output-folder', 'from': 'output_folder', - 'to': pathlib.Path(self.execution_folder, 'metrics/ga').resolve().as_posix(), + 'to': pathlib.Path(self.execution_folder, 'results').resolve().as_posix(), 'optional': False, 'type': 'folder' }] @@ -888,7 +883,7 @@ def output_parameters(self): return [{'name': 'sensor-grids', 'from': 'output_folder/_info.json', 'to': pathlib.Path(self.params_folder, 'output_folder/_info.json').resolve().as_posix()}] -class _Main_6c61a333Orchestrator(luigi.WrapperTask): +class _Main_bafe869fOrchestrator(luigi.WrapperTask): """Runs all the tasks in this module.""" # user input for this module _input_params = luigi.DictParameter() @@ -900,4 +895,4 @@ def input_values(self): return params def requires(self): - yield [CopyGridInfo(_input_params=self.input_values), CopyRedistInfo(_input_params=self.input_values), ParseSunUpHours(_input_params=self.input_values), RestructureDaylightGlareProbabilityResults(_input_params=self.input_values), RestructureGlareAutonomyResults(_input_params=self.input_values)] + yield [CopyGridInfo(_input_params=self.input_values), CopyRedistInfo(_input_params=self.input_values), DaylightGlareAutonomy(_input_params=self.input_values), ParseSunUpHours(_input_params=self.input_values)] diff --git a/lbt_recipes/imageless_annual_glare/package.json b/lbt_recipes/imageless_annual_glare/package.json index 0df33f9e..01a73000 100644 --- a/lbt_recipes/imageless_annual_glare/package.json +++ b/lbt_recipes/imageless_annual_glare/package.json @@ -6,7 +6,7 @@ "type": "MetaData", "annotations": {}, "name": "imageless-annual-glare", - "tag": "0.0.5", + "tag": "0.0.6", "app_version": null, "keywords": [ "honeybee", diff --git a/lbt_recipes/imageless_annual_glare/run.py b/lbt_recipes/imageless_annual_glare/run.py index 6c601585..4744397e 100644 --- a/lbt_recipes/imageless_annual_glare/run.py +++ b/lbt_recipes/imageless_annual_glare/run.py @@ -39,7 +39,7 @@ class LetImagelessAnnualGlareFly(luigi.WrapperTask): _input_params = luigi.DictParameter() def requires(self): - yield [imageless_annual_glare_workerbee._Main_6c61a333Orchestrator(_input_params=self._input_params)] + yield [imageless_annual_glare_workerbee._Main_bafe869fOrchestrator(_input_params=self._input_params)] def start(project_folder, user_values, workers):