Example #1
0
class AnnualDaylightEntryPoint(DAG):
    """Annual daylight entry point."""

    # inputs
    north = Inputs.float(default=0,
                         description='A number for rotation from north.',
                         spec={
                             'type': 'number',
                             'minimum': 0,
                             'maximum': 360
                         },
                         alias=north_input)

    sensor_count = Inputs.int(
        default=200,
        description='The maximum number of grid points per parallel execution.',
        spec={
            'type': 'integer',
            'minimum': 1
        },
        alias=sensor_count_input)

    radiance_parameters = Inputs.str(
        description='The radiance parameters for ray tracing.',
        default='-ab 2 -ad 5000 -lw 2e-05',
        alias=rad_par_annual_input)

    grid_filter = Inputs.str(
        description=
        'Text for a grid identifer or a pattern to filter the sensor grids '
        'of the model that are simulated. For instance, first_floor_* will simulate '
        'only the sensor grids that have an identifier that starts with '
        'first_floor_. By default, all grids in the model will be simulated.',
        default='*',
        alias=grid_filter_input)

    model = Inputs.file(description='A Honeybee model in HBJSON file format.',
                        extensions=['json', 'hbjson'],
                        alias=hbjson_model_input)

    wea = Inputs.file(description='Wea file.',
                      extensions=['wea'],
                      alias=wea_input)

    schedule = Inputs.file(
        description=
        '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.',
        extensions=['txt', 'csv'],
        optional=True,
        alias=schedule_csv_input)

    thresholds = Inputs.str(
        description=
        'A string to change the threshold for daylight autonomy and useful '
        'daylight illuminance. Valid keys are -t for daylight autonomy threshold, -lt '
        'for the lower threshold for useful daylight illuminance and -ut for the upper '
        'threshold. The default is -t 300 -lt 100 -ut 3000. The order of the keys is not '
        'important and you can include one or all of them. For instance if you only '
        'want to change the upper threshold to 2000 lux you should use -ut 2000 as '
        'the input.',
        default='-t 300 -lt 100 -ut 3000',
        alias=daylight_thresholds_input)

    @task(template=CreateSunMatrix)
    def generate_sunpath(self, north=north, wea=wea):
        """Create sunpath for sun-up-hours."""
        return [{
            'from': CreateSunMatrix()._outputs.sunpath,
            'to': 'resources/sunpath.mtx'
        }, {
            'from': CreateSunMatrix()._outputs.sun_modifiers,
            'to': 'resources/suns.mod'
        }]

    @task(template=CreateRadianceFolderGrid)
    def create_rad_folder(self, input_model=model, grid_filter=grid_filter):
        """Translate the input model to a radiance folder."""
        return [{
            'from': CreateRadianceFolderGrid()._outputs.model_folder,
            'to': 'model'
        }, {
            'from': CreateRadianceFolderGrid()._outputs.sensor_grids_file,
            'to': 'results/grids_info.json'
        }, {
            'from': CreateRadianceFolderGrid()._outputs.sensor_grids,
            'description': 'Sensor grids information.'
        }]

    @task(template=CreateOctree, needs=[create_rad_folder])
    def create_octree(self, model=create_rad_folder._outputs.model_folder):
        """Create octree from radiance folder."""
        return [{
            'from': CreateOctreeWithSky()._outputs.scene_file,
            'to': 'resources/scene.oct'
        }]

    @task(template=CreateOctreeWithSky,
          needs=[generate_sunpath, create_rad_folder])
    def create_octree_with_suns(self,
                                model=create_rad_folder._outputs.model_folder,
                                sky=generate_sunpath._outputs.sunpath):
        """Create octree from radiance folder and sunpath for direct studies."""
        return [{
            'from': CreateOctreeWithSky()._outputs.scene_file,
            'to': 'resources/scene_with_suns.oct'
        }]

    @task(template=CreateSkyDome)
    def create_sky_dome(self):
        """Create sky dome for daylight coefficient studies."""
        return [{
            'from': CreateSkyDome()._outputs.sky_dome,
            'to': 'resources/sky.dome'
        }]

    @task(template=CreateSkyMatrix)
    def create_total_sky(self,
                         north=north,
                         wea=wea,
                         sun_up_hours='sun-up-hours'):
        return [{
            'from': CreateSkyMatrix()._outputs.sky_matrix,
            'to': 'resources/sky.mtx'
        }]

    @task(template=CreateSkyMatrix)
    def create_direct_sky(self,
                          north=north,
                          wea=wea,
                          sky_type='sun-only',
                          sun_up_hours='sun-up-hours'):
        return [{
            'from': CreateSkyMatrix()._outputs.sky_matrix,
            'to': 'resources/sky_direct.mtx'
        }]

    @task(template=ParseSunUpHours, needs=[generate_sunpath])
    def parse_sun_up_hours(
            self, sun_modifiers=generate_sunpath._outputs.sun_modifiers):
        return [{
            'from': ParseSunUpHours()._outputs.sun_up_hours,
            'to': 'results/sun-up-hours.txt'
        }]

    @task(
        template=AnnualDaylightRayTracing,
        needs=[
            create_sky_dome, create_octree_with_suns, create_octree,
            generate_sunpath, create_total_sky, create_direct_sky,
            create_rad_folder
        ],
        loop=create_rad_folder._outputs.sensor_grids,
        sub_folder=
        'initial_results/{{item.name}}',  # create a subfolder for each grid
        sub_paths={'sensor_grid':
                   'grid/{{item.full_id}}.pts'}  # sub_path for sensor_grid arg
    )
    def annual_daylight_raytracing(
            self,
            sensor_count=sensor_count,
            radiance_parameters=radiance_parameters,
            octree_file_with_suns=create_octree_with_suns._outputs.scene_file,
            octree_file=create_octree._outputs.scene_file,
            grid_name='{{item.full_id}}',
            sensor_grid=create_rad_folder._outputs.model_folder,
            sky_matrix=create_total_sky._outputs.sky_matrix,
            sky_dome=create_sky_dome._outputs.sky_dome,
            sky_matrix_direct=create_direct_sky._outputs.sky_matrix,
            sunpath=generate_sunpath._outputs.sunpath,
            sun_modifiers=generate_sunpath._outputs.sun_modifiers):
        pass

    @task(template=AnnualDaylightMetrics,
          needs=[parse_sun_up_hours, annual_daylight_raytracing])
    def calculate_annual_metrics(self,
                                 folder='results',
                                 schedule=schedule,
                                 thresholds=thresholds):
        return [{
            'from': AnnualDaylightMetrics()._outputs.annual_metrics,
            'to': 'metrics'
        }]

    results = Outputs.folder(
        source='results',
        description='Folder with raw result files (.ill) that '
        'contain illuminance matrices.',
        alias=annual_daylight_results)

    metrics = Outputs.folder(source='metrics',
                             description='Annual metrics folder.')

    da = Outputs.folder(source='metrics/da',
                        description='Daylight autonomy results.',
                        alias=daylight_autonomy_results)

    cda = Outputs.folder(source='metrics/cda',
                         description='Continuous daylight autonomy results.',
                         alias=continuous_daylight_autonomy_results)

    udi = Outputs.folder(source='metrics/udi',
                         description='Useful daylight illuminance results.',
                         alias=udi_results)

    udi_lower = Outputs.folder(
        source='metrics/udi_lower',
        description='Results for the percent of time that '
        'is below the lower threshold of useful daylight illuminance.',
        alias=udi_lower_results)

    udi_upper = Outputs.folder(
        source='metrics/udi_upper',
        description='Results for the percent of time that '
        'is above the upper threshold of useful daylight illuminance.',
        alias=udi_upper_results)
Example #2
0
class AnnualDaylightEntryPoint(DAG):
    """Annual daylight entry point."""

    # inputs
    north = Inputs.float(
        default=0,
        description='A number for rotation from north.',
        spec={'type': 'number', 'minimum': 0, 'maximum': 360},
        alias=north_input
    )

    cpu_count = Inputs.int(
        default=50,
        description='The maximum number of CPUs for parallel execution. This will be '
        'used to determine the number of sensors run by each worker.',
        spec={'type': 'integer', 'minimum': 1},
        alias=cpu_count
    )

    min_sensor_count = Inputs.int(
        description='The minimum number of sensors in each sensor grid after '
        'redistributing the sensors based on cpu_count. This value takes '
        'precedence over the cpu_count and can be used to ensure that '
        'the parallelization does not result in generating unnecessarily small '
        'sensor grids. The default value is set to 1, which means that the '
        'cpu_count is always respected.', default=1,
        spec={'type': 'integer', 'minimum': 1},
        alias=min_sensor_count_input
    )

    radiance_parameters = Inputs.str(
        description='The radiance parameters for ray tracing.',
        default='-ab 2 -ad 5000 -lw 2e-05',
        alias=rad_par_annual_input
    )

    grid_filter = Inputs.str(
        description='Text for a grid identifier or a pattern to filter the sensor grids '
        'of the model that are simulated. For instance, first_floor_* will simulate '
        'only the sensor grids that have an identifier that starts with '
        'first_floor_. By default, all grids in the model will be simulated.',
        default='*',
        alias=grid_filter_input
    )

    model = Inputs.file(
        description='A Honeybee model in HBJSON file format.',
        extensions=['json', 'hbjson'],
        alias=hbjson_model_grid_input
    )

    wea = Inputs.file(
        description='Wea file.',
        extensions=['wea'],
        alias=wea_input_timestep_check
    )

    schedule = Inputs.file(
        description='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.',
        extensions=['txt', 'csv'], optional=True, alias=schedule_csv_input
    )

    thresholds = Inputs.str(
        description='A string to change the threshold for daylight autonomy and useful '
        'daylight illuminance. Valid keys are -t for daylight autonomy threshold, -lt '
        'for the lower threshold for useful daylight illuminance and -ut for the upper '
        'threshold. The default is -t 300 -lt 100 -ut 3000. The order of the keys is '
        'not important and you can include one or all of them. For instance if you only '
        'want to change the upper threshold to 2000 lux you should use -ut 2000 as '
        'the input.', default='-t 300 -lt 100 -ut 3000',
        alias=daylight_thresholds_input
    )

    @task(template=CreateSunMatrix)
    def generate_sunpath(self, north=north, wea=wea):
        """Create sunpath for sun-up-hours."""
        return [
            {'from': CreateSunMatrix()._outputs.sunpath,
             'to': 'resources/sunpath.mtx'},
            {
                'from': CreateSunMatrix()._outputs.sun_modifiers,
                'to': 'resources/suns.mod'
            }
        ]

    @task(template=CreateRadianceFolderGrid)
    def create_rad_folder(self, input_model=model, grid_filter=grid_filter):
        """Translate the input model to a radiance folder."""
        return [
            {
                'from': CreateRadianceFolderGrid()._outputs.model_folder,
                'to': 'model'
            },
            {
                'from': CreateRadianceFolderGrid()._outputs.bsdf_folder,
                'to': 'model/bsdf'
            },
            {
                'from': CreateRadianceFolderGrid()._outputs.sensor_grids_file,
                'to': 'results/total/grids_info.json'
            },
            {
                'from': CreateRadianceFolderGrid()._outputs.sensor_grids,
                'description': 'Sensor grids information.'
            }
        ]

    @task(template=Copy, needs=[create_rad_folder])
    def copy_grid_info(self, src=create_rad_folder._outputs.sensor_grids_file):
        return [
            {
                'from': Copy()._outputs.dst,
                'to': 'results/direct/grids_info.json'
            }
        ]

    @task(template=CreateOctree, needs=[create_rad_folder])
    def create_octree(self, model=create_rad_folder._outputs.model_folder):
        """Create octree from radiance folder."""
        return [
            {
                'from': CreateOctreeWithSky()._outputs.scene_file,
                'to': 'resources/scene.oct'
            }
        ]

    @task(
        template=SplitGridFolder, needs=[create_rad_folder],
        sub_paths={'input_folder': 'grid'}
    )
    def split_grid_folder(
        self, input_folder=create_rad_folder._outputs.model_folder,
        cpu_count=cpu_count, cpus_per_grid=3, min_sensor_count=min_sensor_count
    ):
        """Split sensor grid folder based on the number of CPUs"""
        return [
            {
                'from': SplitGridFolder()._outputs.output_folder,
                'to': 'resources/grid'
            },
            {
                'from': SplitGridFolder()._outputs.dist_info,
                'to': 'initial_results/final/total/_redist_info.json'
            },
            {
                'from': SplitGridFolder()._outputs.sensor_grids,
                'description': 'Sensor grids information.'
            }
        ]

    @task(template=Copy, needs=[split_grid_folder])
    def copy_redist_info(self, src=split_grid_folder._outputs.dist_info):
        return [
            {
                'from': Copy()._outputs.dst,
                'to': 'initial_results/final/direct/_redist_info.json'
            }
        ]

    @task(
        template=CreateOctreeWithSky, needs=[
            generate_sunpath, create_rad_folder]
    )
    def create_octree_with_suns(
        self, model=create_rad_folder._outputs.model_folder,
        sky=generate_sunpath._outputs.sunpath
    ):
        """Create octree from radiance folder and sunpath for direct studies."""
        return [
            {
                'from': CreateOctreeWithSky()._outputs.scene_file,
                'to': 'resources/scene_with_suns.oct'
            }
        ]

    @task(template=CreateSkyDome)
    def create_sky_dome(self):
        """Create sky dome for daylight coefficient studies."""
        return [
            {'from': CreateSkyDome()._outputs.sky_dome, 'to': 'resources/sky.dome'}
        ]

    @task(template=CreateSkyMatrix)
    def create_total_sky(self, north=north, wea=wea, sun_up_hours='sun-up-hours'):
        return [
            {'from': CreateSkyMatrix()._outputs.sky_matrix,
             'to': 'resources/sky.mtx'}
        ]

    @task(template=CreateSkyMatrix)
    def create_direct_sky(
        self, north=north, wea=wea, sky_type='sun-only', sun_up_hours='sun-up-hours'
    ):
        return [
            {
                'from': CreateSkyMatrix()._outputs.sky_matrix,
                'to': 'resources/sky_direct.mtx'
            }
        ]

    @task(template=ParseSunUpHours, needs=[generate_sunpath])
    def parse_sun_up_hours(self, sun_modifiers=generate_sunpath._outputs.sun_modifiers):
        return [
            {
                'from': ParseSunUpHours()._outputs.sun_up_hours,
                'to': 'results/total/sun-up-hours.txt'
            }
        ]

    @task(template=Copy, needs=[parse_sun_up_hours])
    def copy_sun_up_hours(self, src=parse_sun_up_hours._outputs.sun_up_hours):
        return [
            {
                'from': Copy()._outputs.dst,
                'to': 'results/direct/sun-up-hours.txt'
            }
        ]

    @task(
        template=AnnualDaylightRayTracing,
        needs=[
            create_sky_dome, create_octree_with_suns, create_octree, generate_sunpath,
            create_total_sky, create_direct_sky, create_rad_folder, split_grid_folder
        ],
        loop=split_grid_folder._outputs.sensor_grids,
        # create a subfolder for each grid
        sub_folder='initial_results/{{item.full_id}}',
        # sensor_grid sub_path
        sub_paths={'sensor_grid': '{{item.full_id}}.pts'}
    )
    def annual_daylight_raytracing(
        self,
        radiance_parameters=radiance_parameters,
        octree_file_with_suns=create_octree_with_suns._outputs.scene_file,
        octree_file=create_octree._outputs.scene_file,
        grid_name='{{item.full_id}}',
        sensor_grid=split_grid_folder._outputs.output_folder,
        sensor_count='{{item.count}}',
        sky_matrix=create_total_sky._outputs.sky_matrix,
        sky_dome=create_sky_dome._outputs.sky_dome,
        sky_matrix_direct=create_direct_sky._outputs.sky_matrix,
        sunpath=generate_sunpath._outputs.sunpath,
        sun_modifiers=generate_sunpath._outputs.sun_modifiers,
        bsdfs=create_rad_folder._outputs.bsdf_folder
    ):
        pass

    @task(
        template=MergeFolderData,
        needs=[annual_daylight_raytracing]
    )
    def restructure_results(
        self, input_folder='initial_results/final/total', extension='ill'
    ):
        return [
            {
                'from': MergeFolderData()._outputs.output_folder,
                'to': 'results/total'
            }
        ]

    @task(
        template=MergeFolderData,
        needs=[annual_daylight_raytracing]
    )
    def restructure_direct_results(
        self, input_folder='initial_results/final/direct',
        extension='ill'
    ):
        return [
            {
                'from': MergeFolderData()._outputs.output_folder,
                'to': 'results/direct'
            }
        ]

    @task(
        template=AnnualDaylightMetrics,
        needs=[parse_sun_up_hours, annual_daylight_raytracing, restructure_results]
    )
    def calculate_annual_metrics(
        self, folder='results/total', schedule=schedule, thresholds=thresholds
    ):
        return [
            {
                'from': AnnualDaylightMetrics()._outputs.annual_metrics,
                'to': 'metrics'
            }
        ]

    results = Outputs.folder(
        source='results/total', description='Folder with raw result files (.ill) that '
        'contain illuminance matrices for each sensor at each timestep of the analysis.',
        alias=annual_daylight_results
    )

    results_direct = Outputs.folder(
        source='results/direct', description='Folder with raw result files (.ill) that '
        'contain matrices for just the direct illuminance.',
        alias=annual_daylight_direct_results
    )

    metrics = Outputs.folder(
        source='metrics', description='Annual metrics folder.'
    )

    da = Outputs.folder(
        source='metrics/da', description='Daylight autonomy results.',
        alias=daylight_autonomy_results
    )

    cda = Outputs.folder(
        source='metrics/cda', description='Continuous daylight autonomy results.',
        alias=continuous_daylight_autonomy_results
    )

    udi = Outputs.folder(
        source='metrics/udi', description='Useful daylight illuminance results.',
        alias=udi_results
    )

    udi_lower = Outputs.folder(
        source='metrics/udi_lower', description='Results for the percent of time that '
        'is below the lower threshold of useful daylight illuminance.',
        alias=udi_lower_results
    )

    udi_upper = Outputs.folder(
        source='metrics/udi_upper', description='Results for the percent of time that '
        'is above the upper threshold of useful daylight illuminance.',
        alias=udi_upper_results
    )
Example #3
0
class DaylightFactorEntryPoint(DAG):
    """Daylight factor entry point."""

    # inputs
    model = Inputs.file(description='A Honeybee model in HBJSON file format.',
                        extensions=['json', 'hbjson'],
                        alias=hbjson_model_grid_input)

    cpu_count = Inputs.int(
        default=50,
        description=
        'The maximum number of CPUs for parallel execution. This will be '
        'used to determine the number of sensors run by each worker.',
        spec={
            'type': 'integer',
            'minimum': 1
        },
        alias=cpu_count)

    min_sensor_count = Inputs.int(
        description='The minimum number of sensors in each sensor grid after '
        'redistributing the sensors based on cpu_count. This value takes '
        'precedence over the cpu_count and can be used to ensure that '
        'the parallelization does not result in generating unnecessarily small '
        'sensor grids. The default value is set to 1, which means that the '
        'cpu_count is always respected.',
        default=1,
        spec={
            'type': 'integer',
            'minimum': 1
        },
        alias=min_sensor_count_input)

    radiance_parameters = Inputs.str(
        description='The radiance parameters for ray tracing',
        default='-ab 2 -aa 0.1 -ad 2048 -ar 64',
        alias=rad_par_daylight_factor_input)

    grid_filter = Inputs.str(
        description=
        'Text for a grid identifier or a pattern to filter the sensor grids '
        'of the model that are simulated. For instance, first_floor_* will simulate '
        'only the sensor grids that have an identifier that starts with '
        'first_floor_. By default, all grids in the model will be simulated.',
        default='*',
        alias=grid_filter_input)

    @task(template=GenSkyWithCertainIllum)
    def generate_sky(self):
        return [{
            'from': GenSkyWithCertainIllum()._outputs.sky,
            'to': 'resources/100000_lux.sky'
        }]

    @task(template=CreateRadianceFolderGrid)
    def create_rad_folder(self, input_model=model, grid_filter=grid_filter):
        """Translate the input model to a radiance folder."""
        return [{
            'from': CreateRadianceFolderGrid()._outputs.model_folder,
            'to': 'model'
        }, {
            'from': CreateRadianceFolderGrid()._outputs.bsdf_folder,
            'to': 'model/bsdf'
        }, {
            'from':
            CreateRadianceFolderGrid()._outputs.model_sensor_grids_file,
            'to': 'results/daylight-factor/grids_info.json'
        }, {
            'from': CreateRadianceFolderGrid()._outputs.sensor_grids,
            'description': 'Sensor grids information.'
        }]

    @task(template=CreateOctreeWithSky,
          needs=[generate_sky, create_rad_folder])
    def create_octree(self,
                      model=create_rad_folder._outputs.model_folder,
                      sky=generate_sky._outputs.sky):
        """Create octree from radiance folder and sky."""
        return [{
            'from': CreateOctreeWithSky()._outputs.scene_file,
            'to': 'resources/scene.oct'
        }]

    @task(template=SplitGridFolder,
          needs=[create_rad_folder],
          sub_paths={'input_folder': 'grid'})
    def split_grid_folder(self,
                          input_folder=create_rad_folder._outputs.model_folder,
                          cpu_count=cpu_count,
                          cpus_per_grid=1,
                          min_sensor_count=min_sensor_count):
        """Split sensor grid folder based on the number of CPUs"""
        return [{
            'from': SplitGridFolder()._outputs.output_folder,
            'to': 'resources/grid'
        }, {
            'from': SplitGridFolder()._outputs.dist_info,
            'to': 'initial_results/_redist_info.json'
        }, {
            'from': SplitGridFolder()._outputs.sensor_grids,
            'description': 'Sensor grids information.'
        }]

    @task(
        template=RayTracingDaylightFactor,
        needs=[create_rad_folder, split_grid_folder, create_octree],
        loop=split_grid_folder._outputs.sensor_grids,
        sub_folder='initial_results/{{item.full_id}}',  # subfolder for each grid
        sub_paths={'grid': '{{item.full_id}}.pts'}  # sensor_grid sub_path
    )
    def daylight_factor_ray_tracing(
            self,
            radiance_parameters=radiance_parameters,
            scene_file=create_octree._outputs.scene_file,
            grid=split_grid_folder._outputs.output_folder,
            bsdf_folder=create_rad_folder._outputs.bsdf_folder):
        return [{
            'from': RayTracingDaylightFactor()._outputs.result,
            'to': '../{{item.name}}.res'
        }]

    @task(template=MergeFolderData, needs=[daylight_factor_ray_tracing])
    def restructure_results(self,
                            input_folder='initial_results',
                            extension='res'):
        return [{
            'from': MergeFolderData()._outputs.output_folder,
            'to': 'results/daylight-factor'
        }]

    results = Outputs.folder(
        source='results/daylight-factor',
        description='Folder with raw result files '
        '(.res) that contain daylight factor values for each sensor.',
        alias=daylight_factor_results)
Example #4
0
class CustomEnergySimEntryPoint(DAG):
    """Custom energy sim entry point."""

    # inputs
    model = Inputs.file(
        description='A Honeybee model in HBJSON file format.',
        extensions=['json', 'hbjson'],
        alias=hbjson_model_input
    )

    epw = Inputs.file(
        description='EPW weather file to be used for the energy simulation.',
        extensions=['epw']
    )

    sim_par = Inputs.file(
        description='SimulationParameter JSON that describes the settings for the '
        'simulation. Note that this SimulationParameter should usually contain '
        'design days. If it does not, the annual EPW data be used to generate '
        'default design days, which may not be as representative of the climate as '
        'those from a DDY file.', extensions=['json'], optional=True,
        alias=energy_simulation_parameter_input
    )

    additional_string = Inputs.str(
        description='An additional text string to be appended to the IDF before '
        'simulation. The input should include complete EnergyPlus objects as a '
        'single string following the IDF format. This input can be used to include '
        'EnergyPlus objects that are not currently supported by honeybee.',
        default='', alias=idf_additional_strings_input
    )

    # tasks
    @task(template=SimulateModel)
    def run_simulation(
        self, model=model, epw=epw, sim_par=sim_par,
        additional_string=additional_string
    ) -> List[Dict]:
        return [
            {'from': SimulateModel()._outputs.idf, 'to': 'model.idf'},
            {'from': SimulateModel()._outputs.sql, 'to': 'eplusout.sql'},
            {'from': SimulateModel()._outputs.zsz, 'to': 'epluszsz.csv'},
            {'from': SimulateModel()._outputs.html, 'to': 'eplustbl.htm'},
            {'from': SimulateModel()._outputs.err, 'to': 'eplusout.err'}
        ]

    # outputs
    idf = Outputs.file(
        source='model.idf', description='The IDF model used in the simulation.'
    )

    sql = Outputs.file(
        source='eplusout.sql',
        description='The result SQL file output by the simulation.'
    )

    zsz = Outputs.file(
        source='epluszsz.csv', description='The result CSV with the zone loads '
        'over the design day output by the simulation.', optional=True
    )

    html = Outputs.file(
        source='eplustbl.htm',
        description='The result HTML page with summary reports output by the simulation.'
    )

    err = Outputs.file(
        source='eplusout.err',
        description='The error report output by the simulation.'
    )
class DirectSunHoursEntryPoint(DAG):
    """Direct sun hours entry point."""

    # inputs
    north = Inputs.float(default=0,
                         description='A number for rotation from north.',
                         spec={
                             'type': 'number',
                             'minimum': 0,
                             'maximum': 360
                         },
                         alias=north_input)

    sensor_count = Inputs.int(
        default=200,
        description='The maximum number of grid points per parallel execution.',
        spec={
            'type': 'integer',
            'minimum': 1
        })

    sensor_grid = Inputs.str(
        description=
        'A grid name or a pattern to filter the sensor grids. By default '
        'all the grids in HBJSON model will be exported.',
        default='*')

    model = Inputs.file(description='A Honeybee model in HBJSON file format.',
                        extensions=['json', 'hbjson'],
                        alias=hbjson_model_input)

    wea = Inputs.file(description='Wea file.',
                      extensions=['wea'],
                      alias=wea_input)

    @task(template=CreateSunMatrix)
    def generate_sunpath(self, north=north, wea=wea, output_type=1):
        """Create sunpath for sun-up-hours."""
        return [{
            'from': CreateSunMatrix()._outputs.sunpath,
            'to': 'resources/sunpath.mtx'
        }, {
            'from': CreateSunMatrix()._outputs.sun_modifiers,
            'to': 'resources/suns.mod'
        }]

    @task(template=CreateRadianceFolder)
    def create_rad_folder(self, input_model=model, sensor_grid=sensor_grid):
        """Translate the input model to a radiance folder."""
        return [{
            'from': CreateRadianceFolder()._outputs.model_folder,
            'to': 'model'
        }, {
            'from': CreateRadianceFolder()._outputs.sensor_grids_file,
            'to': 'results/direct_sun_hours/grids_info.json'
        }, {
            'from': CreateRadianceFolder()._outputs.sensor_grids_file,
            'to': 'results/cumulative/grids_info.json'
        }, {
            'from': CreateRadianceFolder()._outputs.sensor_grids_file,
            'to': 'results/direct_radiation/grids_info.json'
        }, {
            'from': CreateRadianceFolder()._outputs.sensor_grids,
            'description': 'Sensor grids information.'
        }]

    @task(template=CreateOctreeWithSky,
          needs=[generate_sunpath, create_rad_folder])
    def create_octree(self,
                      model=create_rad_folder._outputs.model_folder,
                      sky=generate_sunpath._outputs.sunpath):
        """Create octree from radiance folder and sunpath for direct studies."""
        return [{
            'from': CreateOctreeWithSky()._outputs.scene_file,
            'to': 'resources/scene_with_suns.oct'
        }]

    @task(template=ParseSunUpHours, needs=[generate_sunpath])
    def parse_sun_up_hours(
            self, sun_modifiers=generate_sunpath._outputs.sun_modifiers):
        return [{
            'from': ParseSunUpHours()._outputs.sun_up_hours,
            'to': 'results/direct_sun_hours/sun-up-hours.txt'
        }]

    @task(
        template=DirectSunHoursEntryLoop,
        needs=[create_octree, generate_sunpath, create_rad_folder],
        loop=create_rad_folder._outputs.sensor_grids,
        sub_folder=
        'initial_results/{{item.name}}',  # create a subfolder for each grid
        sub_paths={'sensor_grid':
                   'grid/{{item.full_id}}.pts'}  # sub_path for sensor_grid arg
    )
    def direct_sun_hours_raytracing(
            self,
            sensor_count=sensor_count,
            octree_file=create_octree._outputs.scene_file,
            grid_name='{{item.full_id}}',
            sensor_grid=create_rad_folder._outputs.model_folder,
            sunpath=generate_sunpath._outputs.sunpath,
            sun_modifiers=generate_sunpath._outputs.sun_modifiers):
        pass

    results = Outputs.folder(
        source='results',
        description=
        'Results folder. There are 3 subfolders under results folder: '
        'direct_sun_hours, cumulative and direct_radiation.')

    direct_sun_hours = Outputs.folder(
        source='results/direct_sun_hours',
        description='Hourly results for direct sun hours.',
        # alias=sort_direct_results/direct_sun_hours
    )

    cumulative_sun_hours = Outputs.folder(
        source='results/cumulative',
        description=
        'Cumulative results for direct sun hours for all the input hours.',
        # alias=sort_direct_results/direct_sun_hours
    )

    direct_radiation = Outputs.folder(
        source='results/direct_radiation',
        description=
        'Hourly direct radiation results. These results only includes the '
        'direct radiation from sun disk.',
        # alias=sort_direct_results/direct_sun_hours
    )
Example #6
0
class PmvComfortMapEntryPoint(DAG):
    """PMV comfort map entry point."""

    # inputs
    model = Inputs.file(
        description='A Honeybee model in HBJSON file format.',
        extensions=['json', 'hbjson'],
        alias=hbjson_model_grid_room_input
    )

    epw = Inputs.file(
        description='EPW weather file to be used for the comfort map simulation.',
        extensions=['epw']
    )

    ddy = Inputs.file(
        description='A DDY file with design days to be used for the initial '
        'sizing calculation.', extensions=['ddy'],
        alias=ddy_input
    )

    north = Inputs.float(
        default=0,
        description='A a number between -360 and 360 for the counterclockwise '
        'difference between the North and the positive Y-axis in degrees.',
        spec={'type': 'number', 'minimum': -360, 'maximum': 360},
        alias=north_input
    )

    run_period = Inputs.str(
        description='An AnalysisPeriod string to set the start and end dates of '
        'the simulation (eg. "6/21 to 9/21 between 0 and 23 @1"). If None, '
        'the simulation will be annual.', default='', alias=run_period_input
    )

    additional_idf = Inputs.file(
        description='An IDF file with text to be appended before simulation. This '
        'input can be used to include EnergyPlus objects that are not '
        'currently supported by honeybee.', extensions=['idf'],
        optional=True, alias=additional_idf_input
    )

    cpu_count = Inputs.int(
        default=50,
        description='The maximum number of CPUs for parallel execution. This will be '
        'used to determine the number of sensors run by each worker.',
        spec={'type': 'integer', 'minimum': 1},
        alias=cpu_count
    )

    min_sensor_count = Inputs.int(
        description='The minimum number of sensors in each sensor grid after '
        'redistributing the sensors based on cpu_count. This value takes '
        'precedence over the cpu_count and can be used to ensure that '
        'the parallelization does not result in generating unnecessarily small '
        'sensor grids. The default value is set to 1, which means that the '
        'cpu_count is always respected.', default=1,
        spec={'type': 'integer', 'minimum': 1},
        alias=min_sensor_count_input
    )

    write_set_map = Inputs.str(
        description='A switch to note whether the output temperature CSV should '
        'record Operative Temperature or Standard Effective Temperature (SET). '
        'SET is relatively intense to compute and so only recording Operative '
        'Temperature can greatly reduce run time, particularly when air speeds '
        'are low. However, SET accounts for all 6 PMV model inputs and so is a '
        'more representative "feels-like" temperature for the PMV model.',
        default='write-op-map', alias=write_set_map_input,
        spec={'type': 'string', 'enum': ['write-op-map', 'write-set-map']}
    )

    air_speed = Inputs.file(
        description='A CSV file containing a single number for air speed in m/s or '
        'several rows of air speeds that align with the length of the run period. This '
        'will be used for all indoor comfort evaluation.', extensions=['txt', 'csv'],
        optional=True, alias=air_speed_input
    )

    met_rate = Inputs.file(
        description='A CSV file containing a single number for metabolic rate in met '
        'or several rows of met values that align with the length of the run period.',
        extensions=['txt', 'csv'], optional=True, alias=met_rate_input
    )

    clo_value = Inputs.file(
        description='A CSV file containing a single number for clothing level in clo '
        'or several rows of clo values that align with the length of the run period.',
        extensions=['txt', 'csv'], optional=True, alias=clo_value_input
    )

    solarcal_parameters = Inputs.str(
        description='A SolarCalParameter string to customize the assumptions of '
        'the SolarCal model.', default='--posture seated --sharp 135 '
        '--absorptivity 0.7 --emissivity 0.95',
        alias=solar_body_par_indoor_input
    )

    comfort_parameters = Inputs.str(
        description='An PMVParameter string to customize the assumptions of '
        'the PMV comfort model.', default='--ppd-threshold 10',
        alias=pmv_comfort_par_input
    )

    radiance_parameters = Inputs.str(
        description='Radiance parameters for ray tracing.',
        default='-ab 2 -ad 5000 -lw 2e-05',
        alias=rad_par_annual_input
    )

    # tasks
    @task(template=SimParComfort)
    def create_sim_par(self, ddy=ddy, run_period=run_period, north=north) -> List[Dict]:
        return [
            {
                'from': SimParComfort()._outputs.sim_par_json,
                'to': 'energy/simulation_parameter.json'
            }
        ]

    @task(template=DynamicOutputs)
    def dynamic_construction_outputs(
        self, model=model, base_idf=additional_idf
    ) -> List[Dict]:
        return [
            {
                'from': DynamicOutputs()._outputs.dynamic_out_idf,
                'to': 'energy/additional.idf'
            }
        ]

    @task(template=SimulateModel, needs=[create_sim_par, dynamic_construction_outputs])
    def run_energy_simulation(
        self, model=model, epw=epw, sim_par=create_sim_par._outputs.sim_par_json,
        additional_idf=dynamic_construction_outputs._outputs.dynamic_out_idf
    ) -> List[Dict]:
        return [
            {'from': SimulateModel()._outputs.sql, 'to': 'energy/eplusout.sql'},
            {'from': SimulateModel()._outputs.idf, 'to': 'energy/in.idf'}
        ]

    @task(template=EpwToWea)
    def create_wea(self, epw=epw, period=run_period) -> List[Dict]:
        return [
            {
                'from': EpwToWea()._outputs.wea,
                'to': 'radiance/shortwave/in.wea'
            }
        ]

    @task(template=CreateSunMatrix, needs=[create_wea])
    def generate_sunpath(self, north=north, wea=create_wea._outputs.wea, output_type=1):
        """Create sunpath for sun-up-hours."""
        return [
            {
                'from': CreateSunMatrix()._outputs.sunpath,
                'to': 'radiance/shortwave/resources/sunpath.mtx'
            },
            {
                'from': CreateSunMatrix()._outputs.sun_modifiers,
                'to': 'radiance/shortwave/resources/suns.mod'
            }
        ]

    @task(template=CreateSkyDome)
    def create_sky_dome(self):
        """Create sky dome for daylight coefficient studies."""
        return [
            {
                'from': CreateSkyDome()._outputs.sky_dome,
                'to': 'radiance/shortwave/resources/sky.dome'
            }
        ]

    @task(template=CreateSkyMatrix, needs=[create_wea])
    def create_total_sky(
        self, north=north, wea=create_wea._outputs.wea,
        sky_type='total', output_type='solar', sun_up_hours='sun-up-hours'
    ):
        return [
            {
                'from': CreateSkyMatrix()._outputs.sky_matrix,
                'to': 'radiance/shortwave/resources/sky.mtx'
            }
        ]

    @task(template=CreateSkyMatrix, needs=[create_wea])
    def create_direct_sky(
        self, north=north, wea=create_wea._outputs.wea,
        sky_type='sun-only', output_type='solar', sun_up_hours='sun-up-hours'
    ):
        return [
            {
                'from': CreateSkyMatrix()._outputs.sky_matrix,
                'to': 'radiance/shortwave/resources/sky_direct.mtx'
            }
        ]

    @task(template=ParseSunUpHours, needs=[generate_sunpath])
    def parse_sun_up_hours(self, sun_modifiers=generate_sunpath._outputs.sun_modifiers):
        return [
            {
                'from': ParseSunUpHours()._outputs.sun_up_hours,
                'to': 'radiance/shortwave/sun-up-hours.txt'
            }
        ]

    @task(template=ModelModifiersFromConstructions)
    def set_modifiers_from_constructions(
        self, model=model, use_visible='solar', exterior_offset=0.02
    ) -> List[Dict]:
        return [
            {
                'from': ModelModifiersFromConstructions()._outputs.new_model,
                'to': 'radiance/shortwave/model.hbjson'
            }
        ]

    @task(template=CreateRadianceFolderGrid, needs=[set_modifiers_from_constructions])
    def create_rad_folder(
        self, input_model=set_modifiers_from_constructions._outputs.new_model
    ):
        """Translate the input model to a radiance folder."""
        return [
            {
                'from': CreateRadianceFolderGrid()._outputs.model_folder,
                'to': 'radiance/shortwave/model'
            },
            {
                'from': CreateRadianceFolderGrid()._outputs.sensor_grids_file,
                'to': 'results/temperature/grids_info.json'
            },
            {
                'from': CreateRadianceFolderGrid()._outputs.sensor_grids,
                'description': 'Sensor grids information.'
            }
        ]

    @task(template=CopyMultiple, needs=[create_rad_folder])
    def copy_grid_info(self, src=create_rad_folder._outputs.sensor_grids_file):
        return [
            {
                'from': CopyMultiple()._outputs.dst_1,
                'to': 'results/condition/grids_info.json'
            },
            {
                'from': CopyMultiple()._outputs.dst_2,
                'to': 'results/condition_intensity/grids_info.json'
            },
            {
                'from': CopyMultiple()._outputs.dst_3,
                'to': 'metrics/TCP/grids_info.json'
            },
            {
                'from': CopyMultiple()._outputs.dst_4,
                'to': 'metrics/HSP/grids_info.json'
            },
            {
                'from': CopyMultiple()._outputs.dst_5,
                'to': 'metrics/CSP/grids_info.json'
            },
            {
                'from': CopyMultiple()._outputs.dst_6,
                'to': 'initial_results/conditions/grids_info.json'
            }
        ]

    @task(
        template=SplitGridFolder, needs=[create_rad_folder],
        sub_paths={'input_folder': 'grid'}
    )
    def split_grid_folder(
        self, input_folder=create_rad_folder._outputs.model_folder,
        cpu_count=cpu_count, cpus_per_grid=3, min_sensor_count=min_sensor_count
    ):
        """Split sensor grid folder based on the number of CPUs"""
        return [
            {
                'from': SplitGridFolder()._outputs.output_folder,
                'to': 'radiance/grid'
            },
            {
                'from': SplitGridFolder()._outputs.dist_info,
                'to': 'initial_results/results/temperature/_redist_info.json'
            },
            {
                'from': SplitGridFolder()._outputs.sensor_grids_file,
                'to': 'radiance/grid/_split_info.json'
            },
            {
                'from': SplitGridFolder()._outputs.sensor_grids,
                'description': 'Sensor grids information.'
            }
        ]

    @task(template=CopyMultiple, needs=[split_grid_folder])
    def copy_redist_info(self, src=split_grid_folder._outputs.dist_info):
        return [
            {
                'from': CopyMultiple()._outputs.dst_1,
                'to': 'initial_results/results/condition/_redist_info.json'
            },
            {
                'from': CopyMultiple()._outputs.dst_2,
                'to': 'initial_results/results/condition_intensity/_redist_info.json'
            },
            {
                'from': CopyMultiple()._outputs.dst_3,
                'to': 'initial_results/metrics/TCP/_redist_info.json'
            },
            {
                'from': CopyMultiple()._outputs.dst_4,
                'to': 'initial_results/metrics/HSP/_redist_info.json'
            },
            {
                'from': CopyMultiple()._outputs.dst_5,
                'to': 'initial_results/metrics/CSP/_redist_info.json'
            },
            {
                'from': CopyMultiple()._outputs.dst_6,
                'to': 'initial_results/conditions/_redist_info.json'
            }
        ]

    @task(template=CreateOctree, needs=[create_rad_folder])
    def create_octree(self, model=create_rad_folder._outputs.model_folder):
        """Create octree from radiance folder."""
        return [
            {
                'from': CreateOctree()._outputs.scene_file,
                'to': 'radiance/shortwave/resources/scene.oct'
            }
        ]

    @task(
        template=CreateOctreeWithSky, needs=[generate_sunpath, create_rad_folder]
    )
    def create_octree_with_suns(
        self, model=create_rad_folder._outputs.model_folder,
        sky=generate_sunpath._outputs.sunpath
    ):
        """Create octree from radiance folder and sunpath for direct studies."""
        return [
            {
                'from': CreateOctreeWithSky()._outputs.scene_file,
                'to': 'radiance/shortwave/resources/scene_with_suns.oct'
            }
        ]

    @task(
        template=CreateOctreeAbstractedGroups,
        needs=[generate_sunpath, create_rad_folder]
    )
    def create_dynamic_octrees(
        self, model=create_rad_folder._outputs.model_folder,
        sunpath=generate_sunpath._outputs.sunpath
    ):
        """Create a set of octrees for each dynamic window construction."""
        return [
            {
                'from': CreateOctreeAbstractedGroups()._outputs.scene_folder,
                'to': 'radiance/shortwave/resources/dynamic_groups'
            },
            {
                'from': CreateOctreeAbstractedGroups()._outputs.scene_info,
                'description': 'List of octrees to iterate over.'
            }
        ]

    @task(
        template=CreateOctreeShadeTransmittance,
        needs=[generate_sunpath, create_rad_folder]
    )
    def create_shade_trans_octrees(
        self, model=create_rad_folder._outputs.model_folder,
        sunpath=generate_sunpath._outputs.sunpath
    ):
        """Create a set of octrees for each dynamic shade."""
        return [
            {
                'from': CreateOctreeShadeTransmittance()._outputs.scene_folder,
                'to': 'radiance/shortwave/resources/dynamic_shades'
            },
            {
                'from': CreateOctreeShadeTransmittance()._outputs.scene_info,
                'description': 'List of octrees to iterate over.'
            }
        ]

    @task(template=ModelTransSchedules)
    def create_model_trans_schedules(self, model=model, period=run_period) -> List[Dict]:
        return [
            {
                'from': ModelTransSchedules()._outputs.trans_schedule_json,
                'to': 'radiance/shortwave/resources/trans_schedules.json'
            }
        ]

    @task(template=ViewFactorModifiers)
    def create_view_factor_modifiers(
        self, model=model, include_sky='include', include_ground='include',
        grouped_shades='grouped'
    ):
        """Create octree from radiance folder and sunpath for direct studies."""
        return [
            {
                'from': ViewFactorModifiers()._outputs.modifiers_file,
                'to': 'radiance/longwave/resources/scene.mod'
            },
            {
                'from': ViewFactorModifiers()._outputs.scene_file,
                'to': 'radiance/longwave/resources/scene.oct'
            }
        ]

    @task(template=ModelOccSchedules)
    def create_model_occ_schedules(self, model=model, period=run_period) -> List[Dict]:
        return [
            {
                'from': ModelOccSchedules()._outputs.occ_schedule_json,
                'to': 'metrics/occupancy_schedules.json'
            }
        ]

    @task(
        template=RadianceMappingEntryPoint,
        needs=[
            create_sky_dome, create_octree_with_suns, create_octree, generate_sunpath,
            create_total_sky, create_direct_sky, create_rad_folder, split_grid_folder,
            create_view_factor_modifiers
        ],
        loop=split_grid_folder._outputs.sensor_grids,
        sub_folder='radiance',
        sub_paths={'sensor_grid': '{{item.full_id}}.pts'}
    )
    def run_radiance_simulation(
        self,
        radiance_parameters=radiance_parameters,
        model=model,
        octree_file_with_suns=create_octree_with_suns._outputs.scene_file,
        octree_file=create_octree._outputs.scene_file,
        octree_file_view_factor=create_view_factor_modifiers._outputs.scene_file,
        grid_name='{{item.full_id}}',
        sensor_grid=split_grid_folder._outputs.output_folder,
        sensor_count='{{item.count}}',
        sky_dome=create_sky_dome._outputs.sky_dome,
        sky_matrix=create_total_sky._outputs.sky_matrix,
        sky_matrix_direct=create_direct_sky._outputs.sky_matrix,
        sun_modifiers=generate_sunpath._outputs.sun_modifiers,
        view_factor_modifiers=create_view_factor_modifiers._outputs.modifiers_file
    ) -> List[Dict]:
        pass

    @task(
        template=DynamicShadeContribEntryPoint,
        needs=[
            create_sky_dome, generate_sunpath, parse_sun_up_hours,
            create_total_sky, create_direct_sky,
            split_grid_folder, create_shade_trans_octrees, run_radiance_simulation
        ],
        loop=create_shade_trans_octrees._outputs.scene_info,
        sub_folder='radiance',
        sub_paths={
            'octree_file': '{{item.default}}',
            'octree_file_with_suns': '{{item.sun}}'
        }
    )
    def run_radiance_shade_contribution(
        self,
        radiance_parameters=radiance_parameters,
        octree_file=create_shade_trans_octrees._outputs.scene_folder,
        octree_file_with_suns=create_shade_trans_octrees._outputs.scene_folder,
        group_name='{{item.identifier}}',
        sensor_grid_folder='radiance/shortwave/grids',
        sensor_grids=split_grid_folder._outputs.sensor_grids_file,
        sky_dome=create_sky_dome._outputs.sky_dome,
        sky_matrix=create_total_sky._outputs.sky_matrix,
        sky_matrix_direct=create_direct_sky._outputs.sky_matrix,
        sun_modifiers=generate_sunpath._outputs.sun_modifiers,
        sun_up_hours=parse_sun_up_hours._outputs.sun_up_hours
    ) -> List[Dict]:
        pass

    @task(
        template=DynamicContributionEntryPoint,
        needs=[
            create_sky_dome, generate_sunpath, parse_sun_up_hours,
            create_total_sky, create_direct_sky,
            split_grid_folder, create_dynamic_octrees,
            run_energy_simulation, run_radiance_simulation
        ],
        loop=create_dynamic_octrees._outputs.scene_info,
        sub_folder='radiance',
        sub_paths={
            'octree_file_spec': '{{item.identifier}}/{{item.spec}}',
            'octree_file_diff': '{{item.identifier}}/{{item.diff}}',
            'octree_file_with_suns': '{{item.identifier}}/{{item.sun}}'
        },
    )
    def run_radiance_dynamic_contribution(
        self,
        radiance_parameters=radiance_parameters,
        result_sql=run_energy_simulation._outputs.sql,
        octree_file_spec=create_dynamic_octrees._outputs.scene_folder,
        octree_file_diff=create_dynamic_octrees._outputs.scene_folder,
        octree_file_with_suns=create_dynamic_octrees._outputs.scene_folder,
        group_name='{{item.identifier}}',
        sensor_grid_folder='radiance/shortwave/grids',
        sensor_grids=split_grid_folder._outputs.sensor_grids_file,
        sky_dome=create_sky_dome._outputs.sky_dome,
        sky_matrix=create_total_sky._outputs.sky_matrix,
        sky_matrix_direct=create_direct_sky._outputs.sky_matrix,
        sun_modifiers=generate_sunpath._outputs.sun_modifiers,
        sun_up_hours=parse_sun_up_hours._outputs.sun_up_hours,
    ) -> List[Dict]:
        pass

    @task(
        template=ComfortMappingEntryPoint,
        needs=[
            parse_sun_up_hours, create_view_factor_modifiers,
            create_model_occ_schedules, create_model_trans_schedules,
            run_energy_simulation, run_radiance_simulation, split_grid_folder,
            run_radiance_dynamic_contribution, run_radiance_shade_contribution
        ],
        loop=split_grid_folder._outputs.sensor_grids,
        sub_folder='initial_results',
        sub_paths={
            'enclosure_info': '{{item.full_id}}.json',
            'view_factors': '{{item.full_id}}.csv',
            'indirect_irradiance': '{{item.full_id}}.ill',
            'direct_irradiance': '{{item.full_id}}.ill',
            'ref_irradiance': '{{item.full_id}}.ill'
        }
    )
    def run_comfort_map(
        self,
        epw=epw,
        result_sql=run_energy_simulation._outputs.sql,
        grid_name='{{item.full_id}}',
        enclosure_info='radiance/enclosures',
        view_factors='radiance/longwave/view_factors',
        modifiers=create_view_factor_modifiers._outputs.modifiers_file,
        indirect_irradiance='radiance/shortwave/results/indirect',
        direct_irradiance='radiance/shortwave/results/direct',
        ref_irradiance='radiance/shortwave/results/reflected',
        sun_up_hours=parse_sun_up_hours._outputs.sun_up_hours,
        contributions='radiance/shortwave/dynamic/final/{{item.full_id}}',
        transmittance_contribs='radiance/shortwave/shd_trans/final/{{item.full_id}}',
        trans_schedules=create_model_trans_schedules._outputs.trans_schedule_json,
        occ_schedules=create_model_occ_schedules._outputs.occ_schedule_json,
        run_period=run_period,
        air_speed=air_speed,
        met_rate=met_rate,
        clo_value=clo_value,
        solarcal_par=solarcal_parameters,
        comfort_par=comfort_parameters,
        write_set_map=write_set_map
    ) -> List[Dict]:
        pass

    @task(template=MergeFolderData, needs=[run_comfort_map])
    def restructure_temperature_results(
        self, input_folder='initial_results/results/temperature', extension='csv'
    ):
        return [
            {
                'from': MergeFolderData()._outputs.output_folder,
                'to': 'results/temperature'
            }
        ]

    @task(template=MergeFolderData, needs=[run_comfort_map])
    def restructure_condition_results(
        self, input_folder='initial_results/results/condition', extension='csv'
    ):
        return [
            {
                'from': MergeFolderData()._outputs.output_folder,
                'to': 'results/condition'
            }
        ]

    @task(template=MergeFolderData, needs=[run_comfort_map])
    def restructure_condition_intensity_results(
        self, input_folder='initial_results/results/condition_intensity', extension='csv'
    ):
        return [
            {
                'from': MergeFolderData()._outputs.output_folder,
                'to': 'results/condition_intensity'
            }
        ]

    @task(template=MergeFolderData, needs=[run_comfort_map])
    def restructure_tcp_results(
        self, input_folder='initial_results/metrics/TCP', extension='csv'
    ):
        return [
            {
                'from': MergeFolderData()._outputs.output_folder,
                'to': 'metrics/TCP'
            }
        ]

    @task(template=MergeFolderData, needs=[run_comfort_map])
    def restructure_hsp_results(
        self, input_folder='initial_results/metrics/HSP', extension='csv'
    ):
        return [
            {
                'from': MergeFolderData()._outputs.output_folder,
                'to': 'metrics/HSP'
            }
        ]

    @task(template=MergeFolderData, needs=[run_comfort_map])
    def restructure_csp_results(
        self, input_folder='initial_results/metrics/CSP', extension='csv'
    ):
        return [
            {
                'from': MergeFolderData()._outputs.output_folder,
                'to': 'metrics/CSP'
            }
        ]

    @task(template=MapResultInfo)
    def create_result_info(
        self, comfort_model='pmv', run_period=run_period, qualifier=write_set_map
    ) -> List[Dict]:
        return [
            {
                'from': MapResultInfo()._outputs.viz_config_file,
                'to': 'metrics/config.json'
            },
            {
                'from': MapResultInfo()._outputs.temperature_info,
                'to': 'results/temperature/results_info.json'
            },
            {
                'from': MapResultInfo()._outputs.condition_info,
                'to': 'results/condition/results_info.json'
            },
            {
                'from': MapResultInfo()._outputs.condition_intensity_info,
                'to': 'results/condition_intensity/results_info.json'
            }
        ]

    @task(template=Copy, needs=[create_result_info])
    def copy_result_info(
        self, src=create_result_info._outputs.temperature_info
    ) -> List[Dict]:
        return [
            {
                'from': Copy()._outputs.dst,
                'to': 'initial_results/conditions/results_info.json'
            }
        ]

    # outputs
    environmental_conditions = Outputs.folder(
        source='initial_results/conditions',
        description='A folder containing the environmental conditions that were input '
        'to the thermal comfort model. This include the MRT, air temperature, longwave '
        'MRT, shortwave MRT delta and relative humidity.', alias=env_conditions_output
    )

    temperature = Outputs.folder(
        source='results/temperature', description='A folder containing CSV maps of '
        'Operative Temperature for each sensor grid. Alternatively, if the '
        'write-set-map option is used, the CSV maps here will contain Standard '
        'Effective Temperature (SET). Values are in Celsius.',
        alias=operative_or_set_output
    )

    condition = Outputs.folder(
        source='results/condition', description='A folder containing CSV maps of '
        'comfort conditions for each sensor grid. -1 indicates unacceptably cold '
        'conditions. +1 indicates unacceptably hot conditions. 0 indicates neutral '
        '(comfortable) conditions.', alias=thermal_condition_output
    )

    pmv = Outputs.folder(
        source='results/condition_intensity', description='A folder containing CSV maps '
        'of the Predicted Mean Vote (PMV) for each sensor grid. This can be used '
        'to understand not just whether conditions are acceptable but how '
        'uncomfortably hot or cold they are.', alias=pmv_output
    )

    tcp = Outputs.folder(
        source='metrics/TCP', description='A folder containing CSV values for Thermal '
        'Comfort Percent (TCP). TCP is the percentage of occupied time where '
        'thermal conditions are acceptable/comfortable.', alias=tcp_output
    )

    hsp = Outputs.folder(
        source='metrics/HSP', description='A folder containing CSV values for Heat '
        'Sensation Percent (HSP). HSP is the percentage of occupied time where '
        'thermal conditions are hotter than what is considered acceptable/comfortable.',
        alias=hsp_output
    )

    csp = Outputs.folder(
        source='metrics/CSP', description='A folder containing CSV values for Cold '
        'Sensation Percent (CSP). CSP is the percentage of occupied time where '
        'thermal conditions are colder than what is considered acceptable/comfortable.',
        alias=csp_output
    )
Example #7
0
class PointInTimeGridEntryPoint(DAG):
    """Point-in-time grid-based entry point."""

    # inputs
    model = Inputs.file(
        description='A Honeybee model in HBJSON file format.',
        extensions=['json', 'hbjson'],
        alias=hbjson_model_input
    )

    sky = Inputs.str(
        description='Sky string for any type of sky (cie, climate-based, irradiance, '
        'illuminance). This can be a minimal representation of the sky through '
        'altitude and azimuth (eg. "cie -alt 71.6 -az 185.2 -type 0"). Or it can be '
        'a detailed specification of time and location (eg. "climate-based 21 Jun 12:00 '
        '-lat 41.78 -lon -87.75 -tz 5 -dni 800 -dhi 120"). Both the altitude and '
        'azimuth must be specified for the minimal representation to be used. See the '
        'honeybee-radiance sky CLI group for a full list of options '
        '(https://www.ladybug.tools/honeybee-radiance/docs/cli/sky.html).'
    )

    metric = Inputs.str(
        description='Text for the type of metric to be output from the calculation. '
        'Choose from: illuminance, irradiance, luminance, radiance.',
        default='illuminance', alias=point_in_time_metric_input,
        spec={'type': 'string',
              'enum': ['illuminance', 'irradiance', 'luminance', 'radiance']},
    )

    grid_filter = Inputs.str(
        description='Text for a grid identifer or a pattern to filter the sensor grids '
        'of the model that are simulated. For instance, first_floor_* will simulate '
        'only the sensor grids that have an identifier that starts with '
        'first_floor_. By default, all grids in the model will be simulated.',
        default='*',
        alias=grid_filter_input
    )

    sensor_count = Inputs.int(
        default=200,
        description='The maximum number of grid points per parallel execution',
        spec={'type': 'integer', 'minimum': 1},
        alias=sensor_count_input
    )

    radiance_parameters = Inputs.str(
        description='The radiance parameters for ray tracing',
        default='-ab 2 -aa 0.1 -ad 2048 -ar 64',
        alias=rad_par_daylight_factor_input
    )

    @task(template=GenSky)
    def generate_sky(self, sky_string=sky):
        return [
            {
                'from': GenSky()._outputs.sky,
                'to': 'resources/weather.sky'
            }
        ]

    @task(
        template=AdjustSkyForMetric,
        needs=[generate_sky]
    )
    def adjust_sky(self, sky=generate_sky._outputs.sky, metric=metric):
        return [
            {
                'from': AdjustSkyForMetric()._outputs.adjusted_sky,
                'to': 'resources/weather.sky'
            }
        ]

    @task(template=CreateRadianceFolderGrid)
    def create_rad_folder(
        self, input_model=model, grid_filter=grid_filter
            ):
        """Translate the input model to a radiance folder."""
        return [
            {
                'from': CreateRadianceFolderGrid()._outputs.model_folder,
                'to': 'model'
            },
            {
                'from': CreateRadianceFolderGrid()._outputs.bsdf_folder,
                'to': 'model/bsdf'
            },
            {
                'from': CreateRadianceFolderGrid()._outputs.model_sensor_grids_file,
                'to': 'results/grids_info.json'
            },
            {
                'from': CreateRadianceFolderGrid()._outputs.sensor_grids,
                'description': 'Sensor grids information.'
            }
        ]

    @task(
        template=CreateOctreeWithSky, needs=[adjust_sky, create_rad_folder]
    )
    def create_octree(
        self, model=create_rad_folder._outputs.model_folder,
        sky=adjust_sky._outputs.adjusted_sky
    ):
        """Create octree from radiance folder and sky."""
        return [
            {
                'from': CreateOctreeWithSky()._outputs.scene_file,
                'to': 'resources/scene.oct'
            }
        ]

    @task(
        template=PointInTimeGridRayTracing,
        needs=[create_rad_folder, create_octree],
        loop=create_rad_folder._outputs.sensor_grids,
        sub_folder='initial_results/{{item.name}}',  # create a subfolder for each grid
        sub_paths={'sensor_grid': 'grid/{{item.full_id}}.pts'}  # subpath for sensor_grid
    )
    def point_in_time_grid_ray_tracing(
        self,
        sensor_count=sensor_count,
        radiance_parameters=radiance_parameters,
        metric=metric,
        octree_file=create_octree._outputs.scene_file,
        grid_name='{{item.full_id}}',
        sensor_grid=create_rad_folder._outputs.model_folder,
        bsdfs=create_rad_folder._outputs.bsdf_folder
    ):
        # this task doesn't return a file for each loop.
        # instead we access the results folder directly as an output
        pass

    results = Outputs.folder(
        source='results', description='Folder with raw result files (.res) that contain '
        'numerical values for each sensor. Values are in standard SI units of the input '
        'metric (lux, W/m2, cd/m2, W/m2-sr).', alias=point_in_time_grid_results
    )
Example #8
0
class DirectSunHoursEntryPoint(DAG):
    """Direct sun hours entry point."""

    # inputs
    timestep = Inputs.int(
        description='Input wea timestep. This value will be used to divide the '
        'cumulative results to ensure the units of the output are in hours.',
        default=1,
        spec={
            'type': 'integer',
            'minimum': 1,
            'maximum': 60
        })

    north = Inputs.float(default=0,
                         description='A number for rotation from north.',
                         spec={
                             'type': 'number',
                             'minimum': 0,
                             'maximum': 360
                         },
                         alias=north_input)

    cpu_count = Inputs.int(
        default=50,
        description=
        'The maximum number of CPUs for parallel execution. This will be '
        'used to determine the number of sensors run by each worker.',
        spec={
            'type': 'integer',
            'minimum': 1
        },
        alias=cpu_count)

    min_sensor_count = Inputs.int(
        description='The minimum number of sensors in each sensor grid after '
        'redistributing the sensors based on cpu_count. This value takes '
        'precedence over the cpu_count and can be used to ensure that '
        'the parallelization does not result in generating unnecessarily small '
        'sensor grids. The default value is set to 1, which means that the '
        'cpu_count is always respected.',
        default=1,
        spec={
            'type': 'integer',
            'minimum': 1
        },
        alias=min_sensor_count_input)

    grid_filter = Inputs.str(
        description=
        'Text for a grid identifier or a pattern to filter the sensor grids '
        'of the model that are simulated. For instance, first_floor_* will simulate '
        'only the sensor grids that have an identifier that starts with '
        'first_floor_. By default, all grids in the model will be simulated.',
        default='*',
        alias=grid_filter_input)

    model = Inputs.file(description='A Honeybee model in HBJSON file format.',
                        extensions=['json', 'hbjson'],
                        alias=hbjson_model_grid_input)

    wea = Inputs.file(description='Wea file.',
                      extensions=['wea'],
                      alias=wea_input)

    @task(template=WeaToConstant)
    def convert_wea_to_constant(self, wea=wea):
        """Convert a wea to have constant irradiance values."""
        return [{
            'from': WeaToConstant()._outputs.constant_wea,
            'to': 'resources/constant.wea'
        }]

    @task(template=CreateSunMatrix, needs=[convert_wea_to_constant])
    def generate_sunpath(self,
                         wea=convert_wea_to_constant._outputs.constant_wea,
                         north=north,
                         output_type=1):
        """Create sunpath for sun-up-hours."""
        return [{
            'from': CreateSunMatrix()._outputs.sunpath,
            'to': 'resources/sunpath.mtx'
        }, {
            'from': CreateSunMatrix()._outputs.sun_modifiers,
            'to': 'resources/suns.mod'
        }]

    @task(template=ParseSunUpHours, needs=[generate_sunpath])
    def parse_sun_up_hours(
            self, sun_modifiers=generate_sunpath._outputs.sun_modifiers):
        return [{
            'from': ParseSunUpHours()._outputs.sun_up_hours,
            'to': 'results/direct_sun_hours/sun-up-hours.txt'
        }]

    @task(template=WriteInt)
    def write_timestep(self, src=timestep):
        return [{
            'from': WriteInt()._outputs.dst,
            'to': 'results/direct_sun_hours/timestep.txt'
        }]

    @task(template=CreateRadianceFolderGrid)
    def create_rad_folder(self, input_model=model, grid_filter=grid_filter):
        """Translate the input model to a radiance folder."""
        return [{
            'from': CreateRadianceFolderGrid()._outputs.model_folder,
            'to': 'model'
        }, {
            'from': CreateRadianceFolderGrid()._outputs.bsdf_folder,
            'to': 'model/bsdf'
        }, {
            'from': CreateRadianceFolderGrid()._outputs.sensor_grids_file,
            'to': 'results/direct_sun_hours/grids_info.json'
        }, {
            'from': CreateRadianceFolderGrid()._outputs.sensor_grids,
            'description': 'Sensor grids information.'
        }]

    @task(template=Copy, needs=[create_rad_folder])
    def copy_grid_info(self, src=create_rad_folder._outputs.sensor_grids_file):
        return [{
            'from': Copy()._outputs.dst,
            'to': 'results/cumulative/grids_info.json'
        }]

    @task(template=CreateOctreeWithSky,
          needs=[generate_sunpath, create_rad_folder])
    def create_octree(self,
                      model=create_rad_folder._outputs.model_folder,
                      sky=generate_sunpath._outputs.sunpath):
        """Create octree from radiance folder and sunpath for direct studies."""
        return [{
            'from': CreateOctreeWithSky()._outputs.scene_file,
            'to': 'resources/scene_with_suns.oct'
        }]

    @task(template=SplitGridFolder,
          needs=[create_rad_folder],
          sub_paths={'input_folder': 'grid'})
    def split_grid_folder(self,
                          input_folder=create_rad_folder._outputs.model_folder,
                          cpu_count=cpu_count,
                          cpus_per_grid=1,
                          min_sensor_count=min_sensor_count):
        """Split sensor grid folder based on the number of CPUs"""
        return [{
            'from': SplitGridFolder()._outputs.output_folder,
            'to': 'resources/grid'
        }, {
            'from': SplitGridFolder()._outputs.dist_info,
            'to': 'initial_results/direct_sun_hours/_redist_info.json'
        }, {
            'from': SplitGridFolder()._outputs.sensor_grids,
            'description': 'Sensor grids information.'
        }]

    @task(template=Copy, needs=[split_grid_folder])
    def copy_redist_info(self, src=split_grid_folder._outputs.dist_info):
        return [{
            'from': Copy()._outputs.dst,
            'to': 'initial_results/cumulative/_redist_info.json'
        }]

    @task(
        template=DirectSunHoursCalculation,
        needs=[
            create_octree, generate_sunpath, create_rad_folder,
            split_grid_folder
        ],
        loop=split_grid_folder._outputs.sensor_grids,
        sub_folder=
        'initial_results/{{item.full_id}}',  # create a subfolder for each grid
        sub_paths={'sensor_grid':
                   '{{item.full_id}}.pts'}  # sensor_grid sub_path
    )
    def direct_sun_hours_raytracing(
            self,
            timestep=timestep,
            sensor_count='{{item.count}}',
            octree_file=create_octree._outputs.scene_file,
            grid_name='{{item.full_id}}',
            sensor_grid=split_grid_folder._outputs.output_folder,
            sunpath=generate_sunpath._outputs.sunpath,
            sun_modifiers=generate_sunpath._outputs.sun_modifiers,
            bsdfs=create_rad_folder._outputs.bsdf_folder):
        pass

    @task(template=MergeFolderData, needs=[direct_sun_hours_raytracing])
    def restructure_timestep_results(
            self,
            input_folder='initial_results/direct_sun_hours',
            extension='ill'):
        return [{
            'from': MergeFolderData()._outputs.output_folder,
            'to': 'results/direct_sun_hours'
        }]

    @task(template=MergeFolderData, needs=[direct_sun_hours_raytracing])
    def restructure_cumulative_results(
            self, input_folder='initial_results/cumulative', extension='res'):
        return [{
            'from': MergeFolderData()._outputs.output_folder,
            'to': 'results/cumulative'
        }]

    direct_sun_hours = Outputs.folder(
        source='results/direct_sun_hours',
        description='Hourly results for direct sun hours.',
        alias=direct_sun_hours_results)

    cumulative_sun_hours = Outputs.folder(
        source='results/cumulative',
        description='Cumulative direct sun hours for all the input hours.',
        alias=cumulative_sun_hour_results)
Example #9
0
class DaylightFactorEntryPoint(DAG):
    """Daylight factor entry point."""

    # inputs
    sensor_count = Inputs.int(
        default=200,
        description='The maximum number of grid points per parallel execution',
        spec={'type': 'integer', 'minimum': 1},
        alias=sensor_count_input
    )

    radiance_parameters = Inputs.str(
        description='The radiance parameters for ray tracing',
        default='-ab 2 -aa 0.1 -ad 2048 -ar 64',
        alias=rad_par_daylight_factor_input
    )

    grid_filter = Inputs.str(
        description='Text for a grid identifer or a pattern to filter the sensor grids '
        'of the model that are simulated. For instance, first_floor_* will simulate '
        'only the sensor grids that have an identifier that starts with '
        'first_floor_. By default, all grids in the model will be simulated.',
        default='*',
        alias=grid_filter_input
    )

    model = Inputs.file(
        description='A Honeybee model in HBJSON file format.',
        extensions=['json', 'hbjson'],
        alias=hbjson_model_input
    )

    @task(template=GenSkyWithCertainIllum)
    def generate_sky(self):
        return [
            {
                'from': GenSkyWithCertainIllum()._outputs.sky,
                'to': 'resources/100000_lux.sky'
            }
        ]

    @task(template=CreateRadianceFolderGrid)
    def create_rad_folder(
        self, input_model=model, grid_filter=grid_filter
            ):
        """Translate the input model to a radiance folder."""
        return [
            {'from': CreateRadianceFolderGrid()._outputs.model_folder, 'to': 'model'},
            {
                'from': CreateRadianceFolderGrid()._outputs.model_sensor_grids_file,
                'to': 'results/grids_info.json'
            },
            {
                'from': CreateRadianceFolderGrid()._outputs.sensor_grids,
                'description': 'Sensor grids information.'
            }
        ]

    @task(
        template=CreateOctreeWithSky, needs=[generate_sky, create_rad_folder]
    )
    def create_octree(
        self, model=create_rad_folder._outputs.model_folder,
        sky=generate_sky._outputs.sky
    ):
        """Create octree from radiance folder and sky."""
        return [
            {
                'from': CreateOctreeWithSky()._outputs.scene_file,
                'to': 'resources/scene.oct'
            }
        ]

    @task(
        template=DaylightFactorRayTracing,
        needs=[create_rad_folder, create_octree],
        loop=create_rad_folder._outputs.sensor_grids,
        sub_folder='initial_results/{{item.name}}',  # create a subfolder for each grid
        sub_paths={'sensor_grid': 'grid/{{item.full_id}}.pts'}  # sub_path for sensor_grid arg
    )
    def daylight_factor_ray_tracing(
        self,
        sensor_count=sensor_count,
        radiance_parameters=radiance_parameters,
        octree_file=create_octree._outputs.scene_file,
        grid_name='{{item.full_id}}',
        sensor_grid=create_rad_folder._outputs.model_folder
    ):
        # this task doesn't return a file for each loop.
        # instead we access the results folder directly as an output
        pass

    results = Outputs.folder(
        source='results', description='Folder with raw result files (.res) that contain '
        'daylight factor values for each sensor.', alias=daylight_factor_results
    )
class AnnualSkyRadiationEntryPoint(DAG):
    """Annual Sky Radiation entry point."""

    # inputs
    north = Inputs.float(default=0,
                         description='A number for rotation from north.',
                         spec={
                             'type': 'number',
                             'minimum': -360,
                             'maximum': 360
                         },
                         alias=north_input)

    sensor_count = Inputs.int(
        default=200,
        description='The maximum number of grid points per parallel execution.',
        spec={
            'type': 'integer',
            'minimum': 1
        })

    radiance_parameters = Inputs.str(
        description='Radiance parameters for ray tracing.', default='-ab 1')

    sensor_grid = Inputs.str(
        description=
        'A grid name or a pattern to filter the sensor grids. By default '
        'all the grids in HBJSON model will be exported.',
        default='*')

    sky_density = Inputs.int(
        default=1,
        description=
        'The density of generated sky. This input corresponds to gendaymtx '
        '-m option. -m 1 generates 146 patch starting with 0 for the ground and '
        'continuing to 145 for the zenith. Increasing the -m parameter yields a higher '
        'resolution sky using the Reinhart patch subdivision. For example, setting -m 4 '
        'yields a sky with 2305 patches plus one patch for the ground.',
        spec={
            'type': 'integer',
            'minimum': 1
        })

    cumulative = Inputs.str(
        description=
        'An option to generate a cumulative sky instead of an hourly sky',
        default='hourly',
        spec={
            'type': 'string',
            'enum': ['hourly', 'cumulative']
        })

    order_by = Inputs.str(
        description=
        'Order of the output results. By default the results are ordered '
        'to include the results for a single sensor in each row.',
        default='sensor',
        spec={
            'type': 'string',
            'enum': ['sensor', 'datetime']
        })

    model = Inputs.file(description='A Honeybee model in HBJSON file format.',
                        extensions=['json', 'hbjson'],
                        alias=hbjson_model_input)

    wea = Inputs.file(description='Wea file.',
                      extensions=['wea'],
                      alias=wea_input)

    timestep = Inputs.int(
        description='Input wea timestep. This value will be used to divide the '
        'cumulative results.',
        default=1,
        spec={
            'type': 'integer',
            'minimum': 1,
            'maximum': 60
        })

    leap_year = Inputs.str(
        description=
        'A flag to indicate if datetimes in the wea file are for a leap '
        'year.',
        default='full-year',
        spec={
            'type': 'string',
            'enum': ['full-year', 'leap-year']
        })

    black_out = Inputs.str(
        default='default',
        description=
        'A value to indicate if the black material should be used for . '
        'the calculation. Valid values are default and black. Default value is default.',
        spec={
            'type': 'string',
            'enum': ['black', 'default']
        })

    @task(template=CreateSunMatrix)
    def generate_sunpath(self, north=north, wea=wea, output_type=1):
        """Create sunpath for sun-up-hours.

        The sunpath is not used to calculate radiation values.
        """
        return [{
            'from': CreateSunMatrix()._outputs.sunpath,
            'to': 'resources/sunpath.mtx'
        }, {
            'from': CreateSunMatrix()._outputs.sun_modifiers,
            'to': 'resources/suns.mod'
        }]

    @task(template=ParseSunUpHours, needs=[generate_sunpath])
    def parse_sun_up_hours(
            self,
            sun_modifiers=generate_sunpath._outputs.sun_modifiers,
            leap_year=leap_year,
            timestep=timestep):
        return [{
            'from': ParseSunUpHours()._outputs.sun_up_hours,
            'to': 'results/sun-up-hours.txt'
        }]

    @task(template=CreateRadianceFolder)
    def create_rad_folder(self, input_model=model, sensor_grid=sensor_grid):
        """Translate the input model to a radiance folder."""
        return [{
            'from': CreateRadianceFolder()._outputs.model_folder,
            'to': 'model'
        }, {
            'from': CreateRadianceFolder()._outputs.sensor_grids_file,
            'to': 'results/grids_info.json'
        }, {
            'from': CreateRadianceFolder()._outputs.sensor_grids,
            'description': 'Sensor grids information.'
        }]

    @task(template=CreateOctree, needs=[create_rad_folder])
    def create_octree(self,
                      model=create_rad_folder._outputs.model_folder,
                      black_out=black_out):
        """Create octree from radiance folder."""
        return [{
            'from': CreateOctree()._outputs.scene_file,
            'to': 'resources/scene.oct'
        }]

    @task(template=CreateSkyDome)
    def create_sky_dome(self, sky_density=sky_density):
        """Create sky dome for daylight coefficient studies."""
        return [{
            'from': CreateSkyDome()._outputs.sky_dome,
            'to': 'resources/sky.dome'
        }]

    @task(template=CreateSkyMatrix)
    def create_sky(self,
                   north=north,
                   wea=wea,
                   sky_type='total',
                   output_type='solar',
                   output_format='ASCII',
                   sky_density=sky_density,
                   cumulative=cumulative,
                   sun_up_hours='sun-up-hours'):
        return [{
            'from': CreateSkyMatrix()._outputs.sky_matrix,
            'to': 'resources/sky.mtx'
        }]

    @task(
        template=AnnualSkyRadiationRayTracing,
        needs=[create_sky_dome, create_octree, create_sky, create_rad_folder],
        loop=create_rad_folder._outputs.sensor_grids,
        sub_folder=
        'initial_results/{{item.name}}',  # create a subfolder for each grid
        sub_paths={'sensor_grid': 'grid/{{item.full_id}}.pts'})
    def annual_sky_radiation_raytracing(
            self,
            sensor_count=sensor_count,
            radiance_parameters=radiance_parameters,
            octree_file=create_octree._outputs.scene_file,
            grid_name='{{item.full_id}}',
            sensor_grid=create_rad_folder._outputs.model_folder,
            sky_dome=create_sky_dome._outputs.sky_dome,
            sky_matrix=create_sky._outputs.sky_matrix,
            order_by=order_by):
        pass

    results = Outputs.folder(description='Total radiation results.',
                             source='results',
                             alias=annual_daylight_results)
Example #11
0
class GeojsonAnnualEnergyUseEntryPoint(DAG):
    """GeoJSON annual energy use entry point."""

    # inputs
    geojson = Inputs.file(
        description='A geoJSON file with building footprints as Polygons or '
        'MultiPolygons.',
        path='model.geojson',
        extensions=['geojson', 'json'])

    epw = Inputs.file(
        description=
        'EPW weather file to be used for the annual energy simulation.',
        extensions=['epw'])

    ddy = Inputs.file(
        description='A DDY file with design days to be used for the initial '
        'sizing calculation.',
        extensions=['ddy'],
        alias=ddy_input)

    window_ratio = Inputs.str(
        description=
        'A number between 0 and 1 (but not perfectly equal to 1) for the '
        'desired ratio between window area and wall area. If multiple values are '
        'input here (each separated by a space), different WindowParameters will be '
        'assigned based on cardinal direction, starting with north and moving '
        'clockwise.',
        default='0.4')

    all_to_buildings = Inputs.str(
        description=
        'A switch to indicate if all geometries in the geojson file should '
        'be considered buildings. If buildings-only, this method will only generate '
        'footprints from geometries that are defined as a "Building" in the type '
        'field of its corresponding properties.',
        default='buildings-only',
        spec={
            'type': 'string',
            'enum': ['buildings-only', 'all-to-buildings']
        })

    existing_to_context = Inputs.str(
        description=
        'A switch to indicate whether polygons possessing a building_status '
        'of "Existing" under their properties should be imported as ContextShade '
        'instead of Building objects.',
        default='existing-to-context',
        spec={
            'type': 'string',
            'enum': ['no-context', 'existing-to-context']
        })

    separate_top_bottom = Inputs.str(
        description=
        'A switch to indicate whether top/bottom stories of the buildings '
        'should not be separated in order to account for different boundary conditions '
        'of the roof and ground floor.',
        default='separate-top-bottom',
        spec={
            'type': 'string',
            'enum': ['separate-top-bottom', 'no-separation']
        })

    use_multiplier = Inputs.str(
        description=
        'A switch to note whether the multipliers on each Building story '
        'should be passed along to the generated Honeybee Room objects or if full '
        'geometry objects should be written for each story of each dragonfly building.',
        default='full-geometry',
        spec={
            'type': 'string',
            'enum': ['full-geometry', 'multiplier']
        },
        alias=use_multiplier_input)

    shade_dist = Inputs.str(
        description=
        'A number to note the distance beyond which other buildings shade '
        'should be excluded from a given Honeybee Model. This can include the units of '
        'the distance (eg. 100ft) or, if no units are provided, the value will be '
        'interpreted in the dragonfly model units. If 0, shade from all neighboring '
        'buildings will be excluded from the resulting models.',
        default='50m')

    filter_des_days = Inputs.str(
        description=
        'A switch for whether the ddy-file should be filtered to only '
        'include 99.6 and 0.4 design days',
        default='filter-des-days',
        spec={
            'type': 'string',
            'enum': ['filter-des-days', 'all-des-days']
        },
        alias=filter_des_days_input)

    units = Inputs.str(
        description=
        'A switch to indicate whether the data in the final EUI file '
        'should be in SI (kWh/m2) or IP (kBtu/ft2). Valid values are "si" and "ip".',
        default='si',
        spec={
            'type': 'string',
            'enum': ['si', 'ip']
        })

    # tasks
    @task(template=ModelFromGeojson)
    def convert_from_geojson(
            self,
            geojson=geojson,
            all_to_buildings=all_to_buildings,
            existing_to_context=existing_to_context,
            separate_top_bottom=separate_top_bottom) -> List[Dict]:
        return [{
            'from': ModelFromGeojson()._outputs.model,
            'to': 'model_init.dfjson'
        }]

    @task(template=WindowsByRatio, needs=[convert_from_geojson])
    def assign_windows(self,
                       model=convert_from_geojson._outputs.model,
                       ratio=window_ratio) -> List[Dict]:
        return [{
            'from': WindowsByRatio()._outputs.new_model,
            'to': 'model.dfjson'
        }]

    @task(template=ModelToHoneybee, needs=[assign_windows])
    def convert_to_honeybee(self,
                            model=assign_windows._outputs.new_model,
                            obj_per_model='Story',
                            use_multiplier=use_multiplier,
                            shade_dist=shade_dist) -> List[Dict]:
        return [{
            'from': ModelToHoneybee()._outputs.output_folder,
            'to': 'models'
        }, {
            'from': ModelToHoneybee()._outputs.hbjson_list,
            'description': 'Information about exported HBJSONs.'
        }]

    @task(template=SimParDefault)
    def create_sim_par(self,
                       ddy=ddy,
                       filter_des_days=filter_des_days) -> List[Dict]:
        return [{
            'from': SimParDefault()._outputs.sim_par_json,
            'to': 'simulation_parameter.json'
        }]

    @task(
        template=SimulateModel,
        needs=[create_sim_par, convert_to_honeybee],
        loop=convert_to_honeybee._outputs.hbjson_list,
        sub_folder='results',  # create a subfolder for results
        sub_paths={'model': '{{item.path}}'}  # sub_path for sim_par arg
    )
    def run_simulation(
            self,
            model=convert_to_honeybee._outputs.output_folder,
            epw=epw,
            sim_par=create_sim_par._outputs.sim_par_json) -> List[Dict]:
        return [{
            'from': SimulateModel()._outputs.sql,
            'to': 'sql/{{item.id}}.sql'
        }, {
            'from': SimulateModel()._outputs.zsz,
            'to': 'zsz/{{item.id}}_zsz.csv'
        }, {
            'from': SimulateModel()._outputs.html,
            'to': 'html/{{item.id}}.htm'
        }, {
            'from': SimulateModel()._outputs.err,
            'to': 'err/{{item.id}}.err'
        }]

    @task(template=EnergyUseIntensity, needs=[run_simulation])
    def compute_eui(self,
                    result_folder='results/sql',
                    units=units) -> List[Dict]:
        return [{
            'from': EnergyUseIntensity()._outputs.eui_json,
            'to': 'eui.json'
        }]

    # outputs
    eui = Outputs.file(
        source='eui.json',
        description='A JSON containing energy use intensity '
        'information across the total model floor area. Values are either kWh/m2 '
        'or kBtu/ft2 depending upon the units input.',
        alias=parse_eui_results)

    dfjson = Outputs.file(source='model.dfjson',
                          description='The DFJSON model used for simulation.')

    hbjson = Outputs.folder(
        source='models',
        description='Folder containing the HBJSON models used for simulation.')

    sql = Outputs.folder(
        source='results/sql',
        description=
        'Folder containing the result SQL files output by the simulation.')

    zsz = Outputs.folder(source='results/zsz',
                         description='Folder containing the CSV files with '
                         'the zone loads over the design day.',
                         optional=True)

    html = Outputs.folder(
        source='results/html',
        description=
        'Folder containing the result HTML pages with summary reports.')

    err = Outputs.folder(
        source='results/err',
        description=
        'Folder containing the error reports output by the simulation.')
Example #12
0
class AnnualRadiationEntryPoint(DAG):
    """Annual radiation entry point."""

    # inputs
    north = Inputs.float(default=0,
                         description='A number for rotation from north.',
                         spec={
                             'type': 'number',
                             'minimum': -360,
                             'maximum': 360
                         },
                         alias=north_input)

    sensor_count = Inputs.int(
        default=200,
        description='The maximum number of grid points per parallel execution.',
        spec={
            'type': 'integer',
            'minimum': 1
        },
        alias=sensor_count_input)

    radiance_parameters = Inputs.str(
        description='Radiance parameters for ray tracing.',
        default='-ab 2 -ad 5000 -lw 2e-05',
        alias=rad_par_annual_input)

    grid_filter = Inputs.str(
        description=
        'Text for a grid identifer or a pattern to filter the sensor grids '
        'of the model that are simulated. For instance, first_floor_* will simulate '
        'only the sensor grids that have an identifier that starts with '
        'first_floor_. By default, all grids in the model will be simulated.',
        default='*',
        alias=grid_filter_input)

    model = Inputs.file(description='A Honeybee model in HBJSON file format.',
                        extensions=['json', 'hbjson'],
                        alias=hbjson_model_input)

    wea = Inputs.file(description='Wea file.',
                      extensions=['wea'],
                      alias=wea_input)

    @task(template=CreateSunMatrix)
    def generate_sunpath(self, north=north, wea=wea, output_type=1):
        """Create sunpath for sun-up-hours."""
        return [{
            'from': CreateSunMatrix()._outputs.sunpath,
            'to': 'resources/sunpath.mtx'
        }, {
            'from': CreateSunMatrix()._outputs.sun_modifiers,
            'to': 'resources/suns.mod'
        }]

    @task(template=CreateRadianceFolderGrid)
    def create_rad_folder(self, input_model=model, grid_filter=grid_filter):
        """Translate the input model to a radiance folder."""
        return [{
            'from': CreateRadianceFolderGrid()._outputs.model_folder,
            'to': 'model'
        }, {
            'from': CreateRadianceFolderGrid()._outputs.sensor_grids_file,
            'to': 'results/direct/grids_info.json'
        }, {
            'from': CreateRadianceFolderGrid()._outputs.sensor_grids,
            'description': 'Sensor grids information.'
        }]

    @task(template=Copy, needs=[create_rad_folder])
    def copy_grid_info(self, src=create_rad_folder._outputs.sensor_grids_file):
        return [{
            'from': Copy()._outputs.dst,
            'to': 'results/total/grids_info.json'
        }]

    @task(template=CreateOctree, needs=[create_rad_folder])
    def create_octree(self, model=create_rad_folder._outputs.model_folder):
        """Create octree from radiance folder."""
        return [{
            'from': CreateOctreeWithSky()._outputs.scene_file,
            'to': 'resources/scene.oct'
        }]

    @task(template=CreateOctreeWithSky,
          needs=[generate_sunpath, create_rad_folder])
    def create_octree_with_suns(self,
                                model=create_rad_folder._outputs.model_folder,
                                sky=generate_sunpath._outputs.sunpath):
        """Create octree from radiance folder and sunpath for direct studies."""
        return [{
            'from': CreateOctreeWithSky()._outputs.scene_file,
            'to': 'resources/scene_with_suns.oct'
        }]

    @task(template=CreateSkyDome)
    def create_sky_dome(self):
        """Create sky dome for daylight coefficient studies."""
        return [{
            'from': CreateSkyDome()._outputs.sky_dome,
            'to': 'resources/sky.dome'
        }]

    @task(template=CreateSkyMatrix)
    def create_indirect_sky(self,
                            north=north,
                            wea=wea,
                            sky_type='no-sun',
                            output_type='solar',
                            output_format='ASCII',
                            sun_up_hours='sun-up-hours'):
        return [{
            'from': CreateSkyMatrix()._outputs.sky_matrix,
            'to': 'resources/sky_direct.mtx'
        }]

    @task(template=ParseSunUpHours, needs=[generate_sunpath])
    def parse_sun_up_hours(
            self, sun_modifiers=generate_sunpath._outputs.sun_modifiers):
        return [{
            'from': ParseSunUpHours()._outputs.sun_up_hours,
            'to': 'results/total/sun-up-hours.txt'
        }]

    @task(template=Copy, needs=[parse_sun_up_hours])
    def copy_sun_up_hours(self, src=parse_sun_up_hours._outputs.sun_up_hours):
        return [{
            'from': Copy()._outputs.dst,
            'to': 'results/direct/sun-up-hours.txt'
        }]

    @task(
        template=AnnualRadiationRayTracing,
        needs=[
            create_sky_dome, create_octree_with_suns, create_octree,
            generate_sunpath, create_indirect_sky, create_rad_folder
        ],
        loop=create_rad_folder._outputs.sensor_grids,
        sub_folder=
        'initial_results/{{item.name}}',  # create a subfolder for each grid
        sub_paths={'sensor_grid':
                   'grid/{{item.full_id}}.pts'}  # sub_path for sensor_grid arg
    )
    def annual_radiation_raytracing(
            self,
            sensor_count=sensor_count,
            radiance_parameters=radiance_parameters,
            octree_file_with_suns=create_octree_with_suns._outputs.scene_file,
            octree_file=create_octree._outputs.scene_file,
            grid_name='{{item.full_id}}',
            sensor_grid=create_rad_folder._outputs.model_folder,
            sky_dome=create_sky_dome._outputs.sky_dome,
            sky_matrix_indirect=create_indirect_sky._outputs.sky_matrix,
            sunpath=generate_sunpath._outputs.sunpath,
            sun_modifiers=generate_sunpath._outputs.sun_modifiers):
        pass

    total_radiation = Outputs.folder(
        source='results/total',
        description='Folder with raw result files (.ill) that '
        'contain matrices of total irradiance.',
        alias=total_radiation_results)

    direct_radiation = Outputs.folder(
        source='results/direct',
        description='Folder with raw result files (.ill) that '
        'contain matrices of direct irradiance.',
        alias=direct_radiation_results)
Example #13
0
class DirectSunHoursEntryPoint(DAG):
    """Direct sun hours entry point."""

    # inputs
    north = Inputs.float(default=0,
                         description='A number for rotation from north.',
                         spec={
                             'type': 'number',
                             'minimum': 0,
                             'maximum': 360
                         },
                         alias=north_input)

    sensor_count = Inputs.int(
        default=200,
        description='The maximum number of grid points per parallel execution.',
        spec={
            'type': 'integer',
            'minimum': 1
        },
        alias=sensor_count_input)

    grid_filter = Inputs.str(
        description=
        'Text for a grid identifer or a pattern to filter the sensor grids '
        'of the model that are simulated. For instance, first_floor_* will simulate '
        'only the sensor grids that have an identifier that starts with '
        'first_floor_. By default, all grids in the model will be simulated.',
        default='*',
        alias=grid_filter_input)

    model = Inputs.file(description='A Honeybee model in HBJSON file format.',
                        extensions=['json', 'hbjson'],
                        alias=hbjson_model_input)

    wea = Inputs.file(description='Wea file.',
                      extensions=['wea'],
                      alias=wea_input)

    @task(template=WeaToConstant)
    def convert_wea_to_constant(self, wea=wea):
        """Convert a wea to have constant irradiance values."""
        return [{
            'from': WeaToConstant()._outputs.constant_wea,
            'to': 'resources/constant.wea'
        }]

    @task(template=CreateSunMatrix, needs=[convert_wea_to_constant])
    def generate_sunpath(self,
                         wea=convert_wea_to_constant._outputs.constant_wea,
                         north=north,
                         output_type=1):
        """Create sunpath for sun-up-hours."""
        return [{
            'from': CreateSunMatrix()._outputs.sunpath,
            'to': 'resources/sunpath.mtx'
        }, {
            'from': CreateSunMatrix()._outputs.sun_modifiers,
            'to': 'resources/suns.mod'
        }]

    @task(template=CreateRadianceFolderGrid)
    def create_rad_folder(self, input_model=model, grid_filter=grid_filter):
        """Translate the input model to a radiance folder."""
        return [{
            'from': CreateRadianceFolderGrid()._outputs.model_folder,
            'to': 'model'
        }, {
            'from': CreateRadianceFolderGrid()._outputs.sensor_grids_file,
            'to': 'results/direct_sun_hours/grids_info.json'
        }, {
            'from': CreateRadianceFolderGrid()._outputs.sensor_grids,
            'description': 'Sensor grids information.'
        }]

    @task(template=CopyMultiple, needs=[create_rad_folder])
    def copy_grid_info(self, src=create_rad_folder._outputs.sensor_grids_file):
        return [{
            'from': CopyMultiple()._outputs.dst_1,
            'to': 'results/cumulative/grids_info.json'
        }, {
            'from': CopyMultiple()._outputs.dst_2,
            'to': 'results/direct_radiation/grids_info.json'
        }]

    @task(template=CreateOctreeWithSky,
          needs=[generate_sunpath, create_rad_folder])
    def create_octree(self,
                      model=create_rad_folder._outputs.model_folder,
                      sky=generate_sunpath._outputs.sunpath):
        """Create octree from radiance folder and sunpath for direct studies."""
        return [{
            'from': CreateOctreeWithSky()._outputs.scene_file,
            'to': 'resources/scene_with_suns.oct'
        }]

    @task(template=ParseSunUpHours, needs=[generate_sunpath])
    def parse_sun_up_hours(
            self, sun_modifiers=generate_sunpath._outputs.sun_modifiers):
        return [{
            'from': ParseSunUpHours()._outputs.sun_up_hours,
            'to': 'results/direct_sun_hours/sun-up-hours.txt'
        }]

    @task(
        template=DirectSunHoursEntryLoop,
        needs=[create_octree, generate_sunpath, create_rad_folder],
        loop=create_rad_folder._outputs.sensor_grids,
        sub_folder=
        'initial_results/{{item.name}}',  # create a subfolder for each grid
        sub_paths={'sensor_grid':
                   'grid/{{item.full_id}}.pts'}  # sub_path for sensor_grid arg
    )
    def direct_sun_hours_raytracing(
            self,
            sensor_count=sensor_count,
            octree_file=create_octree._outputs.scene_file,
            grid_name='{{item.full_id}}',
            sensor_grid=create_rad_folder._outputs.model_folder,
            sunpath=generate_sunpath._outputs.sunpath,
            sun_modifiers=generate_sunpath._outputs.sun_modifiers):
        pass

    direct_sun_hours = Outputs.folder(
        source='results/direct_sun_hours',
        description='Hourly results for direct sun hours.',
        alias=direct_sun_hours_results)

    cumulative_sun_hours = Outputs.folder(
        source='results/cumulative',
        description=
        'Cumulative results for direct sun hours for all the input hours.',
        alias=cumulative_sun_hour_results)
Example #14
0
class AnnualEnergyUseEntryPoint(DAG):
    """Annual energy use entry point."""

    # inputs
    model = Inputs.file(description='A Honeybee model in HBJSON file format.',
                        extensions=['json', 'hbjson'],
                        alias=hbjson_model_input)

    epw = Inputs.file(
        description=
        'EPW weather file to be used for the annual energy simulation.',
        extensions=['epw'])

    ddy = Inputs.file(
        description='A DDY file with design days to be used for the initial '
        'sizing calculation.',
        extensions=['ddy'],
        alias=ddy_input)

    filter_des_days = Inputs.str(
        description=
        'A switch for whether the ddy-file should be filtered to only '
        'include 99.6 and 0.4 design days',
        default='filter-des-days',
        spec={
            'type': 'string',
            'enum': ['filter-des-days', 'all-des-days']
        },
        alias=filter_des_days_input)

    units = Inputs.str(
        description=
        'A switch to indicate whether the data in the final EUI file '
        'should be in SI (kWh/m2) or IP (kBtu/ft2). Valid values are "si" and "ip".',
        default='si',
        spec={
            'type': 'string',
            'enum': ['si', 'ip']
        })

    # tasks
    @task(template=SimParDefault)
    def create_sim_par(self,
                       ddy=ddy,
                       filter_des_days=filter_des_days) -> List[Dict]:
        return [{
            'from': SimParDefault()._outputs.sim_par_json,
            'to': 'simulation_parameter.json'
        }]

    @task(template=SimulateModel, needs=[create_sim_par])
    def run_simulation(
            self,
            model=model,
            epw=epw,
            sim_par=create_sim_par._outputs.sim_par_json) -> List[Dict]:
        return [{
            'from': SimulateModel()._outputs.hbjson,
            'to': 'model.hbjson'
        }, {
            'from': SimulateModel()._outputs.result_folder,
            'to': 'run'
        }]

    @task(template=EnergyUseIntensity, needs=[run_simulation])
    def compute_eui(self,
                    result_folder=run_simulation._outputs.result_folder,
                    units=units) -> List[Dict]:
        return [{
            'from': EnergyUseIntensity()._outputs.eui_json,
            'to': 'eui.json'
        }]

    # outputs
    eui = Outputs.file(
        source='eui.json',
        description='A JSON containing energy use intensity '
        'information across the total model floor area. Values are either kWh/m2 '
        'or kBtu/ft2 depending upon the units input.',
        alias=parse_eui_results)

    sql = Outputs.file(
        source='run/eplusout.sql',
        description='The result SQL file output by the simulation.')

    zsz = Outputs.file(source='run/epluszsz.csv',
                       description='The result CSV with the zone loads '
                       'over the design day output by the simulation.',
                       optional=True)

    html = Outputs.file(
        source='run/eplustbl.htm',
        description=
        'The result HTML page with summary reports output by the simulation.')

    err = Outputs.file(
        source='run/eplusout.err',
        description='The error report output by the simulation.')
Example #15
0
class AnnualDaylightEntryPoint(DAG):
    """Annual daylight entry point."""

    # inputs
    north = Inputs.float(
        default=0,
        description='A number for rotation from north.',
        spec={'type': 'number', 'minimum': 0, 'maximum': 360},
        alias=north_input
    )

    sensor_count = Inputs.int(
        default=200,
        description='The maximum number of grid points per parallel execution.',
        spec={'type': 'integer', 'minimum': 1}
    )

    radiance_parameters = Inputs.str(
        description='The radiance parameters for ray tracing.',
        default='-ab 2 -ad 5000 -lw 2e-05'
    )

    sensor_grid = Inputs.str(
        description='A grid name or a pattern to filter the sensor grids. By default '
        'all the grids in HBJSON model will be exported.', default='*'
    )

    model = Inputs.file(
        description='A Honeybee model in HBJSON file format.',
        extensions=['json', 'hbjson'],
        alias=hbjson_model_input
    )

    wea = Inputs.file(
        description='Wea file.',
        extensions=['wea'],
        alias=wea_input
    )

    @task(template=CreateSunMatrix)
    def generate_sunpath(self, north=north, wea=wea):
        """Create sunpath for sun-up-hours."""
        return [
            {'from': CreateSunMatrix()._outputs.sunpath, 'to': 'resources/sunpath.mtx'},
            {
                'from': CreateSunMatrix()._outputs.sun_modifiers,
                'to': 'resources/suns.mod'
            }
        ]

    @task(template=CreateRadianceFolder)
    def create_rad_folder(self, input_model=model, sensor_grid=sensor_grid):
        """Translate the input model to a radiance folder."""
        return [
            {'from': CreateRadianceFolder()._outputs.model_folder, 'to': 'model'},
            {
                'from': CreateRadianceFolder()._outputs.sensor_grids_file,
                'to': 'results/grids_info.json'
            },
            {
                'from': CreateRadianceFolder()._outputs.sensor_grids,
                'description': 'Sensor grids information.'
            }
        ]

    @task(template=CreateOctree, needs=[create_rad_folder])
    def create_octree(self, model=create_rad_folder._outputs.model_folder):
        """Create octree from radiance folder."""
        return [
            {
                'from': CreateOctreeWithSky()._outputs.scene_file,
                'to': 'resources/scene.oct'
            }
        ]

    @task(
        template=CreateOctreeWithSky, needs=[generate_sunpath, create_rad_folder]
    )
    def create_octree_with_suns(
        self, model=create_rad_folder._outputs.model_folder,
        sky=generate_sunpath._outputs.sunpath
    ):
        """Create octree from radiance folder and sunpath for direct studies."""
        return [
            {
                'from': CreateOctreeWithSky()._outputs.scene_file,
                'to': 'resources/scene_with_suns.oct'
            }
        ]

    @task(template=CreateSkyDome)
    def create_sky_dome(self):
        """Create sky dome for daylight coefficient studies."""
        return [
            {'from': CreateSkyDome()._outputs.sky_dome, 'to': 'resources/sky.dome'}
        ]

    @task(template=CreateSkyMatrix)
    def create_total_sky(self, north=north, wea=wea, sun_up_hours='sun-up-hours'):
        return [
            {'from': CreateSkyMatrix()._outputs.sky_matrix, 'to': 'resources/sky.mtx'}
        ]

    @task(template=CreateSkyMatrix)
    def create_direct_sky(
        self, north=north, wea=wea, sky_type='sun-only', sun_up_hours='sun-up-hours'
            ):
        return [
            {
                'from': CreateSkyMatrix()._outputs.sky_matrix,
                'to': 'resources/sky_direct.mtx'
            }
        ]

    @task(template=ParseSunUpHours, needs=[generate_sunpath])
    def parse_sun_up_hours(self, sun_modifiers=generate_sunpath._outputs.sun_modifiers):
        return [
            {
                'from': ParseSunUpHours()._outputs.sun_up_hours,
                'to': 'results/sun-up-hours.txt'
            }
        ]

    @task(
        template=AnnualDaylightRayTracing,
        needs=[
            create_sky_dome, create_octree_with_suns, create_octree, generate_sunpath,
            create_total_sky, create_direct_sky, create_rad_folder
        ],
        loop=create_rad_folder._outputs.sensor_grids,
        sub_folder='initial_results/{{item.name}}',  # create a subfolder for each grid
        sub_paths={'sensor_grid': 'grid/{{item.full_id}}.pts'}  # sub_path for sensor_grid arg
    )
    def annual_daylight_raytracing(
        self,
        sensor_count=sensor_count,
        radiance_parameters=radiance_parameters,
        octree_file_with_suns=create_octree_with_suns._outputs.scene_file,
        octree_file=create_octree._outputs.scene_file,
        grid_name='{{item.full_id}}',
        sensor_grid=create_rad_folder._outputs.model_folder,
        sky_matrix=create_total_sky._outputs.sky_matrix,
        sky_dome=create_sky_dome._outputs.sky_dome,
        sky_matrix_direct=create_direct_sky._outputs.sky_matrix,
        sunpath=generate_sunpath._outputs.sunpath,
        sun_modifiers=generate_sunpath._outputs.sun_modifiers
    ):
        pass

    results = Outputs.folder(
        source='results',
        alias=sort_annual_daylight_results
    )