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 )
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)
class DirectSunHoursCalculation(DAG): sun_modifiers = Inputs.file(description='A file with sun modifiers.') sensor_grid = Inputs.file(description='Sensor grid file.', extensions=['pts']) octree_file = Inputs.file(description='A Radiance octree file with suns.', extensions=['oct']) sensor_count = Inputs.int( default=200, description='The maximum number of grid points per parallel execution', spec={ 'type': 'integer', 'minimum': 1 }) grid_name = Inputs.str( description= 'Sensor grid file name. This is useful to rename the final result ' 'file to {grid_name}.ill') @task(template=DaylightContribution, sub_folder='direct-radiation') def direct_radiation_calculation( self, fixed_radiance_parameters='-aa 0.0 -I -faa -ab 0 -dc 1.0 -dt 0.0 -dj 0.0 -dr 0', conversion='0.265 0.670 0.065', sensor_count=sensor_count, modifiers=sun_modifiers, sensor_grid=sensor_grid, grid_name=grid_name, scene_file=octree_file): return [{ 'from': DaylightContribution()._outputs.result_file, 'to': '{{self.grid_name}}.ill' }] @task(template=ConvertToBinary, needs=[direct_radiation_calculation], sub_folder='direct-sun-hours') def convert_to_sun_hours( self, input_mtx=direct_radiation_calculation._outputs.result_file, grid_name=grid_name): return [{ 'from': ConvertToBinary()._outputs.output_mtx, 'to': '{{self.grid_name}}.ill' }] @task(template=SumRow, needs=[convert_to_sun_hours], sub_folder='cumulative-sun-hours') def calculate_cumulative_hours( self, input_mtx=convert_to_sun_hours._outputs.output_mtx, grid_name=grid_name): return [{ 'from': SumRow()._outputs.output_mtx, 'to': '{{self.grid_name}}.res' }]
class DirectSunHoursEntryLoop(DAG): # inputs sensor_count = Inputs.int( default=200, description='The maximum number of grid points per parallel execution', spec={ 'type': 'integer', 'minimum': 1 }) octree_file = Inputs.file(description='A Radiance octree file with suns.', extensions=['oct']) grid_name = Inputs.str( description= 'Sensor grid file name. This is useful to rename the final result ' 'file to {grid_name}.ill') sensor_grid = Inputs.file(description='Sensor grid file.', extensions=['pts']) sun_modifiers = Inputs.file(description='A file with sun modifiers.') @task(template=SplitGrid) def split_grid(self, sensor_count=sensor_count, input_grid=sensor_grid): return [{ 'from': SplitGrid()._outputs.grids_list }, { 'from': SplitGrid()._outputs.output_folder, 'to': 'sub_grids' }] @task(template=DirectSunHoursCalculation, needs=[split_grid], loop=split_grid._outputs.grids_list, sub_paths={'sensor_grid': '{{item.path}}'}) def direct_sunlight(self, octree_file=octree_file, sensor_count='{{item.count}}', grid_name='{{item.name}}', sun_modifiers=sun_modifiers, sensor_grid=split_grid._outputs.output_folder, scene_file=octree_file): # the results of the loop will be collected under direct_sunlight subfolder. pass @task(template=MergeFiles, needs=[direct_sunlight]) def merge_direct_sun_hours(self, name=grid_name, extension='.ill', folder='direct-sun-hours'): """Merge direct sun hour results from several grids into a single file.""" return [{ 'from': MergeFiles()._outputs.result_file, 'to': '../../results/direct_sun_hours/{{self.name}}.ill' }] @task(template=MergeFiles, needs=[direct_sunlight]) def merge_cumulative_sun_hours(self, name=grid_name, extension='.res', folder='cumulative-sun-hours'): """Merge cumulative sun hour results from several grids into a single file.""" return [{ 'from': MergeFiles()._outputs.result_file, 'to': '../../results/cumulative/{{self.name}}.res' }]
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 )
class RadianceContribEntryPoint(DAG): """Entry point for Radiance calculations for comfort mapping.""" # inputs radiance_parameters = Inputs.str( description='Radiance parameters for ray tracing.', default='-ab 2 -ad 5000 -lw 2e-05', ) octree_file_spec = Inputs.file( description='A Radiance octree file with a specular version of the ' 'window group.', extensions=['oct'] ) octree_file_diff = Inputs.file( description='A Radiance octree file with a diffuse version of the window group.', extensions=['oct'] ) octree_file_with_suns = Inputs.file( description='A Radiance octree file with sun modifiers.', extensions=['oct'] ) group_name = Inputs.str( description='Name for the dynamic aperture group being simulated.' ) grid_name = Inputs.str( description='Sensor grid file name (used to name the final result files).' ) sensor_grid = Inputs.file( description='Sensor grid file.', extensions=['pts'] ) ref_sensor_grid = Inputs.file( description='Reflected Sensor grid file.', extensions=['pts'] ) sensor_count = Inputs.int( description='Number of sensors in the input sensor grid.' ) sky_dome = Inputs.file( description='Path to sky dome file.' ) sky_matrix = Inputs.file( description='Path to total sky matrix file.' ) sky_matrix_direct = Inputs.file( description='Path to direct skymtx file (gendaymtx -d).' ) sun_modifiers = Inputs.file( description='A file with sun modifiers.' ) result_sql = Inputs.file( description='A SQLite file that was generated by EnergyPlus and contains ' 'window transmittance results.', extensions=['sql', 'db', 'sqlite'] ) sun_up_hours = Inputs.file( description='A sun-up-hours.txt file output by Radiance and aligns with the ' 'input irradiance files.' ) @task(template=DaylightContribution) def direct_sun_group( self, grid=grid_name, group=group_name, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -ab 0 -dc 1.0 -dt 0.0 -dj 0.0 -dr 0', sensor_count=sensor_count, modifiers=sun_modifiers, sensor_grid=sensor_grid, conversion='0.265 0.670 0.065', output_format='a', # make it ascii so we expose the file as a separate output header='remove', # remove header to make it process-able later scene_file=octree_file_with_suns ): return [ { 'from': DaylightContribution()._outputs.result_file, 'to': 'dynamic/initial/{{self.group}}/direct_spec/{{self.grid}}.ill' } ] @task(template=DaylightCoefficient) def direct_sky_group( self, grid=grid_name, group=group_name, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -ab 1 -c 1 -faf', sensor_count=sensor_count, sky_matrix=sky_matrix_direct, sky_dome=sky_dome, sensor_grid=sensor_grid, conversion='0.265 0.670 0.065', # divide by 179 scene_file=octree_file_spec ): return [ { 'from': DaylightCoefficient()._outputs.result_file, 'to': 'dynamic/initial/{{self.group}}/direct_sky/{{self.grid}}.ill' } ] @task(template=DaylightCoefficient) def total_sky_spec_group( self, grid=grid_name, group=group_name, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -c 1 -faf', sensor_count=sensor_count, sky_matrix=sky_matrix, sky_dome=sky_dome, sensor_grid=sensor_grid, conversion='0.265 0.670 0.065', # divide by 179 scene_file=octree_file_spec ): return [ { 'from': DaylightCoefficient()._outputs.result_file, 'to': 'dynamic/initial/{{self.group}}/total_sky/{{self.grid}}.ill' } ] @task(template=SubtractSkyMatrix, needs=[total_sky_spec_group, direct_sky_group]) def output_matrix_math_group( self, grid=grid_name, group=group_name, total_sky_matrix=total_sky_spec_group._outputs.result_file, direct_sky_matrix=direct_sky_group._outputs.result_file ): return [ { 'from': SubtractSkyMatrix()._outputs.results_file, 'to': 'dynamic/initial/{{self.group}}/indirect_spec/{{self.grid}}.ill' } ] @task(template=DaylightCoefficient) def ground_reflected_sky_spec_group( self, grid=grid_name, group=group_name, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -c 1', sensor_count=sensor_count, sky_matrix=sky_matrix, sky_dome=sky_dome, sensor_grid=ref_sensor_grid, conversion='0.265 0.670 0.065', # divide by 179 output_format='a', # make it ascii so we expose the file as a separate output header='remove', # remove header to make it process-able later scene_file=octree_file_spec ): return [ { 'from': DaylightCoefficient()._outputs.result_file, 'to': 'dynamic/initial/{{self.group}}/reflected_spec/{{self.grid}}.ill' } ] @task(template=DaylightCoefficient) def total_sky_diff_group( self, grid=grid_name, group=group_name, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -c 1', sensor_count=sensor_count, sky_matrix=sky_matrix, sky_dome=sky_dome, sensor_grid=sensor_grid, conversion='0.265 0.670 0.065', # divide by 179 output_format='a', # make it ascii so we expose the file as a separate output header='remove', # remove header to make it process-able later scene_file=octree_file_diff ): return [ { 'from': DaylightCoefficient()._outputs.result_file, 'to': 'dynamic/initial/{{self.group}}/total_diff/{{self.grid}}.ill' } ] @task(template=DaylightCoefficient) def ground_reflected_sky_diff_group( self, grid=grid_name, group=group_name, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -c 1', sensor_count=sensor_count, sky_matrix=sky_matrix, sky_dome=sky_dome, sensor_grid=ref_sensor_grid, conversion='0.265 0.670 0.065', # divide by 179 output_format='a', # make it ascii so we expose the file as a separate output header='remove', # remove header to make it process-able later scene_file=octree_file_diff ): return [ { 'from': DaylightCoefficient()._outputs.result_file, 'to': 'dynamic/initial/{{self.group}}/reflected_diff/{{self.grid}}.ill' } ] @task( template=IrradianceContribMap, needs=[ direct_sun_group, output_matrix_math_group, ground_reflected_sky_spec_group, total_sky_diff_group, ground_reflected_sky_diff_group ] ) def create_irradiance_contrib_map( self, result_sql=result_sql, direct_specular=direct_sun_group._outputs.result_file, indirect_specular=output_matrix_math_group._outputs.results_file, ref_specular=ground_reflected_sky_spec_group._outputs.result_file, indirect_diffuse=total_sky_diff_group._outputs.result_file, ref_diffuse=ground_reflected_sky_diff_group._outputs.result_file, sun_up_hours=sun_up_hours, aperture_id=group_name, grid=grid_name ) -> List[Dict]: return [ { 'from': IrradianceContribMap()._outputs.result_folder, 'to': 'dynamic/final/{{self.grid}}/{{self.aperture_id}}' } ]
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)
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)
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)
class AnnualDaylightRayTracing(DAG): # inputs 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' ) octree_file_with_suns = Inputs.file( description='A Radiance octree file with suns.', extensions=['oct'] ) octree_file = Inputs.file( description='A Radiance octree file.', extensions=['oct'] ) grid_name = Inputs.str( description='Sensor grid file name. This is useful to rename the final result ' 'file to {grid_name}.res' ) sensor_grid = Inputs.file( description='Sensor grid file.', extensions=['pts'] ) sun_modifiers = Inputs.file( description='A file with sun modifiers.' ) sky_matrix = Inputs.file( description='Path to total sky matrix file.' ) sky_matrix_direct = Inputs.file( description='Path to direct skymtx file (i.e. gendaymtx -d).' ) sky_dome = Inputs.file( description='Path to sky dome file.' ) @task(template=SplitGrid) def split_grid(self, sensor_count=sensor_count, input_grid=sensor_grid): return [ {'from': SplitGrid()._outputs.grids_list}, {'from': SplitGrid()._outputs.output_folder, 'to': 'sub_grids'} ] @task( template=DaylightContribution, needs=[split_grid], loop=split_grid._outputs.grids_list, sub_folder='direct_sunlight', sub_paths={'sensor_grid': '{{item.path}}'} ) def direct_sunlight( self, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -faf -ab 0 -dc 1.0 -dt 0.0 -dj 0.0 -dr 0', sensor_count='{{item.count}}', modifiers=sun_modifiers, sensor_grid=split_grid._outputs.output_folder, scene_file=octree_file_with_suns ): return [ { 'from': DaylightContribution()._outputs.result_file, 'to': '{{item.name}}.ill' } ] @task( template=DaylightCoefficient, needs=[split_grid], loop=split_grid._outputs.grids_list, sub_folder='direct_sky', sub_paths={'sensor_grid': '{{item.path}}'} ) def direct_sky( self, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -ab 1 -c 1 -faf', sensor_count='{{item.count}}', sky_matrix=sky_matrix_direct, sky_dome=sky_dome, sensor_grid=split_grid._outputs.output_folder, scene_file=octree_file ): return [ { 'from': DaylightContribution()._outputs.result_file, 'to': '{{item.name}}.ill' } ] @task( template=DaylightCoefficient, needs=[split_grid], loop=split_grid._outputs.grids_list, sub_folder='total_sky', sub_paths={'sensor_grid': '{{item.path}}'} ) def total_sky( self, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -c 1 -faf', sensor_count='{{item.count}}', sky_matrix=sky_matrix, sky_dome=sky_dome, sensor_grid=split_grid._outputs.output_folder, scene_file=octree_file ): return [ { 'from': DaylightContribution()._outputs.result_file, 'to': '{{item.name}}.ill' } ] @task( template=AddRemoveSkyMatrix, needs=[split_grid, direct_sunlight, total_sky, direct_sky], loop=split_grid._outputs.grids_list, sub_folder='final' ) def output_matrix_math( self, direct_sky_matrix='direct_sky/{{item.name}}.ill', total_sky_matrix='total_sky/{{item.name}}.ill', sunlight_matrix='direct_sunlight/{{item.name}}.ill', conversion='47.4 119.9 11.6' ): return [ { 'from': AddRemoveSkyMatrix()._outputs.results_file, 'to': '{{item.name}}.ill' } ] @task( template=MergeFiles, needs=[output_matrix_math] ) def merge_raw_results(self, name=grid_name, extension='.ill', folder='final'): return [ { 'from': MergeFiles()._outputs.result_file, 'to': '../../results/{{self.name}}.ill' } ]
class AnnualRadiationRayTracing(DAG): # inputs 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') octree_file_with_suns = Inputs.file( description='A Radiance octree file with suns.', extensions=['oct']) octree_file = Inputs.file(description='A Radiance octree file.', extensions=['oct']) grid_name = Inputs.str( description= 'Sensor grid file name. This is useful to rename the final result ' 'file to {grid_name}.res') sensor_grid = Inputs.file(description='Sensor grid file.', extensions=['pts']) sun_modifiers = Inputs.file(description='A file with sun modifiers.') sky_matrix_indirect = Inputs.file( description='Path to indirect skymtx file (i.e. gendaymtx -s).') sky_dome = Inputs.file(description='Path to sky dome file.') @task(template=SplitGrid) def split_grid(self, sensor_count=sensor_count, input_grid=sensor_grid): return [{ 'from': SplitGrid()._outputs.grids_list }, { 'from': SplitGrid()._outputs.output_folder, 'to': '00_sub_grids' }] @task(template=DaylightContribution, needs=[split_grid], loop=split_grid._outputs.grids_list, sub_folder='01_direct', sub_paths={'sensor_grid': '{{item.path}}'}) def direct_sunlight( self, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -ab 0 -dc 1.0 -dt 0.0 -dj 0.0 -dr 0', sensor_count='{{item.count}}', modifiers=sun_modifiers, sensor_grid=split_grid._outputs.output_folder, conversion='0.265 0.670 0.065', output_format='a', # make it ascii so we can expose the file as a separate output scene_file=octree_file_with_suns): return [{ 'from': DaylightContribution()._outputs.result_file, 'to': '{{item.name}}.ill' }] @task(template=DaylightCoefficient, needs=[split_grid], loop=split_grid._outputs.grids_list, sub_folder='02_indirect', sub_paths={'sensor_grid': '{{item.path}}'}) def indirect_sky( self, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -c 1', sensor_count='{{item.count}}', sky_matrix=sky_matrix_indirect, sky_dome=sky_dome, sensor_grid=split_grid._outputs.output_folder, conversion='0.265 0.670 0.065', # divide by 179 scene_file=octree_file): return [{ 'from': DaylightContribution()._outputs.result_file, 'to': '{{item.name}}.ill' }] @task(template=AddSkyMatrix, needs=[split_grid, direct_sunlight, indirect_sky], loop=split_grid._outputs.grids_list, sub_folder='03_total') def output_matrix_math( self, indirect_sky_matrix='02_indirect/{{item.name}}.ill', sunlight_matrix='01_direct/{{item.name}}.ill', ): return [{ 'from': AddSkyMatrix()._outputs.results_file, 'to': '{{item.name}}.ill' }] @task(template=MergeFiles, needs=[output_matrix_math]) def merge_total_results(self, name=grid_name, extension='.ill', folder='03_total'): return [{ 'from': MergeFiles()._outputs.result_file, 'to': '../../results/total/{{self.name}}.ill' }] @task(template=MergeFiles, needs=[output_matrix_math]) def merge_direct_results(self, name=grid_name, extension='.ill', folder='01_direct'): return [{ 'from': MergeFiles()._outputs.result_file, 'to': '../../results/direct/{{self.name}}.ill' }]
class AnnualSkyRadiationRayTracing(DAG): # inputs 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' ) octree_file = Inputs.file( description='A Radiance octree file without suns or sky.', extensions=['oct'] ) grid_name = Inputs.str( description='Sensor grid file name. This is useful to rename the final result ' 'file to {grid_name}.res' ) sensor_grid = Inputs.file( description='Sensor grid file.', extensions=['pts'] ) sky_matrix = Inputs.file( description='Path to skymtx file.' ) sky_dome = Inputs.file( description='Path to sky dome file.' ) 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']} ) @task(template=SplitGrid) def split_grid(self, sensor_count=sensor_count, input_grid=sensor_grid): return [ {'from': SplitGrid()._outputs.grids_list}, {'from': SplitGrid()._outputs.output_folder, 'to': '00_sub_grids'} ] # TODO: add a step to set divide_by to 1/timestep if sky is cumulative. @task( template=DaylightCoefficient, needs=[split_grid], loop=split_grid._outputs.grids_list, sub_folder='01_radiation', sub_paths={'sensor_grid': '{{item.path}}'} ) def total_sky( self, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -c 1', sensor_count='{{item.count}}', sky_matrix=sky_matrix, sky_dome=sky_dome, sensor_grid=split_grid._outputs.output_folder, conversion='0.265 0.670 0.065', # divide by 179 scene_file=octree_file, output_format='a', order_by=order_by ): return [ { 'from': DaylightContribution()._outputs.result_file, 'to': '{{item.name}}.ill' } ] @task( template=MergeFiles, needs=[total_sky] ) def merge_direct_results( self, name=grid_name, extension='.ill', folder='01_radiation'): return [ { 'from': MergeFiles()._outputs.result_file, 'to': '../../results/{{self.name}}.ill' } ]
class AnnualDaylightRayTracing(DAG): # inputs radiance_parameters = Inputs.str( description='The radiance parameters for ray tracing', default='-ab 2 -ad 5000 -lw 2e-05') octree_file_with_suns = Inputs.file( description='A Radiance octree file with suns.', extensions=['oct']) octree_file = Inputs.file(description='A Radiance octree file.', extensions=['oct']) grid_name = Inputs.str( description= 'Sensor grid file name. This is useful to rename the final result ' 'file to {grid_name}.ill') sensor_grid = Inputs.file(description='Sensor grid file.', extensions=['pts']) sensor_count = Inputs.int( description='Number of sensors in the input sensor grid.') sun_modifiers = Inputs.file(description='A file with sun modifiers.') sky_matrix = Inputs.file(description='Path to total sky matrix file.') sky_matrix_direct = Inputs.file( description='Path to direct skymtx file (i.e. gendaymtx -d).') sky_dome = Inputs.file(description='Path to sky dome file.') bsdfs = Inputs.folder( description='Folder containing any BSDF files needed for ray tracing.', optional=True) @task(template=DaylightContribution) def direct_sunlight( self, name=grid_name, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -ab 0 -dc 1.0 -dt 0.0 -dj 0.0 -dr 0', sensor_count=sensor_count, modifiers=sun_modifiers, sensor_grid=sensor_grid, output_format='a', # make it ascii so we expose the file as a separate output scene_file=octree_file_with_suns, bsdf_folder=bsdfs): return [{ 'from': DaylightContribution()._outputs.result_file, 'to': '../final/direct/{{self.name}}.ill' }] @task(template=DaylightCoefficient) def direct_sky(self, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -ab 1 -c 1 -faf', sensor_count=sensor_count, sky_matrix=sky_matrix_direct, sky_dome=sky_dome, sensor_grid=sensor_grid, scene_file=octree_file, bsdf_folder=bsdfs): return [{ 'from': DaylightCoefficient()._outputs.result_file, 'to': 'direct_sky.ill' }] @task(template=DaylightCoefficient) def total_sky(self, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -c 1 -faf', sensor_count=sensor_count, sky_matrix=sky_matrix, sky_dome=sky_dome, sensor_grid=sensor_grid, scene_file=octree_file, bsdf_folder=bsdfs): return [{ 'from': DaylightCoefficient()._outputs.result_file, 'to': 'total_sky.ill' }] @task(template=AddRemoveSkyMatrix, needs=[direct_sunlight, total_sky, direct_sky]) def output_matrix_math( self, name=grid_name, direct_sky_matrix=direct_sky._outputs.result_file, total_sky_matrix=total_sky._outputs.result_file, sunlight_matrix=direct_sunlight._outputs.result_file, conversion='47.4 119.9 11.6'): return [{ 'from': AddRemoveSkyMatrix()._outputs.results_file, 'to': '../final/total/{{self.name}}.ill' }]
class ShadeContribEntryPoint(DAG): """Entry point for Radiance calculations for comfort mapping.""" # inputs radiance_parameters = Inputs.str( description='Radiance parameters for ray tracing.', default='-ab 2 -ad 5000 -lw 2e-05', ) octree_file = Inputs.file( description= 'A Radiance octree file with a completely transparent version ' 'of the dynamic shade group.', extensions=['oct']) octree_file_with_suns = Inputs.file( description='A Radiance octree file with sun modifiers.', extensions=['oct']) group_name = Inputs.str( description='Name for the dynamic aperture group being simulated.') grid_name = Inputs.str( description= 'Sensor grid file name (used to name the final result files).') sensor_grid = Inputs.file(description='Sensor grid file.', extensions=['pts']) ref_sensor_grid = Inputs.file(description='Reflected Sensor grid file.', extensions=['pts']) sensor_count = Inputs.int( description='Number of sensors in the input sensor grid.') sky_dome = Inputs.file(description='Path to sky dome file.') sky_matrix = Inputs.file(description='Path to total sky matrix file.') sky_matrix_direct = Inputs.file( description='Path to direct skymtx file (gendaymtx -d).') sun_modifiers = Inputs.file(description='A file with sun modifiers.') sun_up_hours = Inputs.file( description= 'A sun-up-hours.txt file output by Radiance and aligns with the ' 'input irradiance files.') @task(template=DaylightContribution) def direct_sun_shade_group( self, grid=grid_name, group=group_name, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -ab 0 -dc 1.0 -dt 0.0 -dj 0.0 -dr 0', sensor_count=sensor_count, modifiers=sun_modifiers, sensor_grid=sensor_grid, conversion='0.265 0.670 0.065', output_format='a', # make it ascii so we expose the file as a separate output header='remove', # remove header to make it process-able later scene_file=octree_file_with_suns): return [{ 'from': DaylightContribution()._outputs.result_file, 'to': 'shd_trans/final/{{self.grid}}/{{self.group}}/direct.ill' }] @task(template=DaylightCoefficient) def direct_sky_shade_group( self, grid=grid_name, group=group_name, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -ab 1 -c 1 -faf', sensor_count=sensor_count, sky_matrix=sky_matrix_direct, sky_dome=sky_dome, sensor_grid=sensor_grid, conversion='0.265 0.670 0.065', # divide by 179 scene_file=octree_file): return [{ 'from': DaylightCoefficient()._outputs.result_file, 'to': 'shd_trans/initial/{{self.group}}/direct_sky/{{self.grid}}.ill' }] @task(template=DaylightCoefficient) def total_sky_spec_shade_group( self, grid=grid_name, group=group_name, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -c 1 -faf', sensor_count=sensor_count, sky_matrix=sky_matrix, sky_dome=sky_dome, sensor_grid=sensor_grid, conversion='0.265 0.670 0.065', # divide by 179 scene_file=octree_file): return [{ 'from': DaylightCoefficient()._outputs.result_file, 'to': 'shd_trans/initial/{{self.group}}/total_sky/{{self.grid}}.ill' }] @task(template=SubtractSkyMatrix, needs=[total_sky_spec_shade_group, direct_sky_shade_group]) def output_matrix_math_shade_group( self, grid=grid_name, group=group_name, total_sky_matrix=total_sky_spec_shade_group._outputs.result_file, direct_sky_matrix=direct_sky_shade_group._outputs.result_file): return [{ 'from': SubtractSkyMatrix()._outputs.results_file, 'to': 'shd_trans/final/{{self.grid}}/{{self.group}}/indirect.ill' }] @task(template=DaylightCoefficient) def ground_reflected_sky_shade_group( self, grid=grid_name, group=group_name, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -c 1', sensor_count=sensor_count, sky_matrix=sky_matrix, sky_dome=sky_dome, sensor_grid=ref_sensor_grid, conversion='0.265 0.670 0.065', # divide by 179 output_format='a', # make it ascii so we expose the file as a separate output header='remove', # remove header to make it process-able later scene_file=octree_file): return [{ 'from': DaylightCoefficient()._outputs.result_file, 'to': 'shd_trans/final/{{self.grid}}/{{self.group}}/reflected.ill' }]
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 )
class RadianceMappingEntryPoint(DAG): """Entry point for Radiance calculations for comfort mapping.""" # inputs radiance_parameters = Inputs.str( description='Radiance parameters for ray tracing.', default='-ab 2 -ad 5000 -lw 2e-05', ) model = Inputs.file(description='A Honeybee model in HBJSON file format.', extensions=['json', 'hbjson']) octree_file_with_suns = Inputs.file( description='A Radiance octree file with sun modifiers.', extensions=['oct']) octree_file = Inputs.file( description='A Radiance octree file with a sky dome.', extensions=['oct']) octree_file_view_factor = Inputs.file( description= 'A Radiance octree file with surface view factor modifiers.', extensions=['oct']) grid_name = Inputs.str( description= 'Sensor grid file name (used to name the final result files).') sensor_grid = Inputs.file(description='Sensor grid file.', extensions=['pts']) sensor_count = Inputs.int( description='Number of sensors in the input sensor grid.') sky_dome = Inputs.file(description='Path to sky dome file.') sky_matrix = Inputs.file(description='Path to total sky matrix file.') sky_matrix_direct = Inputs.file( description='Path to direct skymtx file (gendaymtx -d).') sun_modifiers = Inputs.file(description='A file with sun modifiers.') view_factor_modifiers = Inputs.file( description='A file with surface modifiers.') @task(template=RadiantEnclosureInfo) def get_enclosure_info(self, model=model, input_grid=sensor_grid, name=grid_name): return [{ 'from': RadiantEnclosureInfo()._outputs.enclosure_file, 'to': 'enclosures/{{self.name}}.json' }] @task(template=SphericalViewFactorContribution) def compute_spherical_view_factors( self, name=grid_name, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -ab 1 -c 1 -faf', modifiers=view_factor_modifiers, sensor_grid=sensor_grid, scene_file=octree_file_view_factor): return [{ 'from': SphericalViewFactorContribution()._outputs.view_factor_file, 'to': 'longwave/view_factors/{{self.name}}.csv' }] @task(template=MirrorGrid) def mirror_the_grid(self, input_grid=sensor_grid, name=grid_name, vector='0 0 1'): return [{ 'from': MirrorGrid()._outputs.base_file, 'to': 'shortwave/grids/{{self.name}}.pts' }, { 'from': MirrorGrid()._outputs.mirrored_file, 'to': 'shortwave/grids/{{self.name}}_ref.pts' }] @task(template=DaylightContribution, needs=[mirror_the_grid]) def direct_sun( self, name=grid_name, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -ab 0 -dc 1.0 -dt 0.0 -dj 0.0 -dr 0', sensor_count=sensor_count, modifiers=sun_modifiers, sensor_grid=mirror_the_grid._outputs.base_file, conversion='0.265 0.670 0.065', output_format='a', # make it ascii so we expose the file as a separate output header='remove', # remove header to make it process-able later scene_file=octree_file_with_suns): return [{ 'from': DaylightContribution()._outputs.result_file, 'to': 'shortwave/results/direct/{{self.name}}.ill' }] @task(template=DaylightCoefficient, needs=[mirror_the_grid]) def direct_sky( self, name=grid_name, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -ab 1 -c 1 -faf', sensor_count=sensor_count, sky_matrix=sky_matrix_direct, sky_dome=sky_dome, sensor_grid=mirror_the_grid._outputs.base_file, conversion='0.265 0.670 0.065', # divide by 179 scene_file=octree_file): return [{ 'from': DaylightCoefficient()._outputs.result_file, 'to': 'shortwave/initial_results/direct_sky/{{self.name}}.ill' }] @task(template=DaylightCoefficient, needs=[mirror_the_grid]) def total_sky( self, name=grid_name, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -c 1 -faf', sensor_count=sensor_count, sky_matrix=sky_matrix, sky_dome=sky_dome, sensor_grid=mirror_the_grid._outputs.base_file, conversion='0.265 0.670 0.065', # divide by 179 scene_file=octree_file): return [{ 'from': DaylightCoefficient()._outputs.result_file, 'to': 'shortwave/initial_results/total_sky/{{self.name}}.ill' }] @task(template=SubtractSkyMatrix, needs=[total_sky, direct_sky]) def output_matrix_math(self, name=grid_name, total_sky_matrix=total_sky._outputs.result_file, direct_sky_matrix=direct_sky._outputs.result_file): return [{ 'from': SubtractSkyMatrix()._outputs.results_file, 'to': 'shortwave/results/indirect/{{self.name}}.ill' }] @task(template=DaylightCoefficient, needs=[mirror_the_grid]) def ground_reflected_sky( self, name=grid_name, radiance_parameters=radiance_parameters, fixed_radiance_parameters='-aa 0.0 -I -c 1', sensor_count=sensor_count, sky_matrix=sky_matrix, sky_dome=sky_dome, sensor_grid=mirror_the_grid._outputs.mirrored_file, conversion='0.265 0.670 0.065', # divide by 179 output_format='a', # make it ascii so we expose the file as a separate output header='remove', # remove header to make it process-able later scene_file=octree_file): return [{ 'from': DaylightCoefficient()._outputs.result_file, 'to': 'shortwave/results/reflected/{{self.name}}.ill' }]
class PointInTimeGridRayTracing(DAG): """Point-in-time grid-based ray tracing.""" # inputs 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 -aa 0.1 -ad 2048 -ar 64') metric = Inputs.str( description= 'Text for the type of metric to be output from the calculation. ' 'Choose from: illuminance, irradiance, luminance, radiance.', default='illuminance', spec={ 'type': 'string', 'enum': ['illuminance', 'irradiance', 'luminance', 'radiance'] }) octree_file = Inputs.file(description='A Radiance octree file.', extensions=['oct']) grid_name = Inputs.str( description= 'Sensor grid file name. This is useful to rename the final result ' 'file to {grid_name}.res') sensor_grid = Inputs.file(description='Sensor grid file.', extensions=['pts']) bsdfs = Inputs.folder( description='Folder containing any BSDF files needed for ray tracing.') @task(template=SplitGrid) def split_grid(self, sensor_count=sensor_count, input_grid=sensor_grid): return [{ 'from': SplitGrid()._outputs.grids_list }, { 'from': SplitGrid()._outputs.output_folder, 'to': 'sub_grids' }] @task(template=RayTracingPointInTime, needs=[split_grid], loop=split_grid._outputs.grids_list, sub_folder='results', sub_paths={'grid': '{{item.path}}'}) def ray_tracing(self, radiance_parameters=radiance_parameters, metric=metric, grid=split_grid._outputs.output_folder, scene_file=octree_file, bsdf_folder=bsdfs): return [{ 'from': RayTracingPointInTime()._outputs.result, 'to': '{{item.name}}.res' }] @task(template=MergeFiles, needs=[ray_tracing]) def merge_results(self, name=grid_name, extension='.res', folder='results'): return [{ 'from': MergeFiles()._outputs.result_file, 'to': '../../results/{{self.name}}.res' }]
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 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)
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)
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 )
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 )