Skip to content

Commit

Permalink
feat(dynamic): Add components for dynamic group schedules
Browse files Browse the repository at this point in the history
  • Loading branch information
chriswmackey committed Oct 18, 2023
1 parent 7cfbf7d commit c596d56
Show file tree
Hide file tree
Showing 18 changed files with 267 additions and 8 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "1.6.0",
"version": "1.6.1",
"nickname": "DaylightMetrics",
"outputs": [
[
Expand Down Expand Up @@ -48,6 +48,13 @@
"type": "string",
"default": null
},
{
"access": "list",
"name": "dyn_sch_",
"description": "Optional dynamic Aperture Group Schedules from the \"HB Aperture Group\nSchedule\" component, which will be used to customize the behavior\nof any dyanmic aperture geometry in the output metrics. If unsupplied,\nall dynamic aperture groups will be in their default state in for\nthe output metrics.",
"type": "System.Object",
"default": null
},
{
"access": "item",
"name": "_occ_sch_",
Expand Down Expand Up @@ -78,7 +85,7 @@
}
],
"subcategory": "4 :: Results",
"code": "\nimport os\nimport subprocess\n\ntry:\n from ladybug.datacollection import BaseCollection\n from ladybug.futil import write_to_file\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug:\\n\\t{}'.format(e))\n\ntry:\n from honeybee.config import folders\nexcept ImportError as e:\n raise ImportError('\\nFailed to import honeybee:\\n\\t{}'.format(e))\n\ntry:\n from honeybee_radiance.postprocess.annualdaylight import metrics_from_folder\nexcept ImportError as e:\n raise ImportError('\\nFailed to import honeybee_radiance:\\n\\t{}'.format(e))\n\ntry:\n from honeybee_energy.lib.schedules import schedule_by_identifier\nexcept ImportError as e: # honeybee schedule library is not available\n schedule_by_identifier = None\n\ntry:\n from pollination_handlers.outputs.daylight import read_da_from_folder, \\\n read_cda_from_folder, read_udi_from_folder\nexcept ImportError as e:\n raise ImportError('\\nFailed to import pollination_handlers:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs, list_to_data_tree\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_{{cad}}:\\n\\t{}'.format(e))\n\n\nif all_required_inputs(ghenv.Component):\n # set default values for the thresholds and the grid filter\n grid_filter_ = '*' if grid_filter_ is None else grid_filter_\n _threshold_ = _threshold_ if _threshold_ else 300\n if len(_min_max_) != 0:\n assert len(_min_max_), 'Expected two values for _min_max_.'\n min_t = _min_max_[0]\n max_t = _min_max_[1]\n else:\n min_t = 100\n max_t = 3000\n\n # process the schedule\n if _occ_sch_ is None:\n schedule = None\n elif isinstance(_occ_sch_, BaseCollection):\n schedule = _occ_sch_.values\n elif isinstance(_occ_sch_, str):\n if schedule_by_identifier is not None:\n try:\n schedule = schedule_by_identifier(_occ_sch_).values()\n except TypeError: # it's probably a ScheduleFixedInterval\n schedule = schedule_by_identifier(_occ_sch_).values\n else:\n raise ValueError('honeybee-energy must be installed to reference '\n 'occupancy schedules by identifier.')\n else: # assume that it is a honeybee schedule object\n try:\n schedule = _occ_sch_.values()\n except TypeError: # it's probably a ScheduleFixedInterval\n schedule = _occ_sch_.values\n if schedule is not None:\n bin_schedule = []\n for val in schedule:\n bin_val = 1 if val >= 0.1 else 0\n bin_schedule.append(bin_val)\n schedule = bin_schedule\n\n # compute the annual metrics\n res_folder = os.path.dirname(_results[0]) if os.path.isfile(_results[0]) \\\n else _results[0]\n if os.path.isdir(os.path.join(res_folder, '__static_apertures__')):\n cmds = [\n folders.python_exe_path, '-m', 'honeybee_radiance_postprocess',\n 'post-process', 'annual-daylight', res_folder, '-sf', 'metrics',\n '-t', str(_threshold_), '-lt', str(min_t), '-ut', str(max_t)\n ]\n if grid_filter_ != '*':\n cmds.extend(['--grids-filter', grid_filter_])\n if schedule is not None:\n sch_str = '\\n'.join(str(h) for h in schedule)\n sch_file = os.path.join(res_folder, 'schedule.txt')\n write_to_file(sch_file, sch_str)\n cmds.extend(['--schedule', sch_file])\n use_shell = True if os.name == 'nt' else False\n process = subprocess.Popen(\n cmds, cwd=res_folder, shell=use_shell,\n stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n stdout = process.communicate() # wait for the process to finish\n if stdout[-1] != '':\n print(stdout[-1])\n raise ValueError('Failed to compute annual daylight metrics.')\n metric_dir = os.path.join(res_folder, 'metrics')\n DA = list_to_data_tree(read_da_from_folder(os.path.join(metric_dir, 'da')))\n cDA = list_to_data_tree(read_cda_from_folder(os.path.join(metric_dir, 'cda')))\n UDI = list_to_data_tree(read_udi_from_folder(os.path.join(metric_dir, 'udi')))\n UDI_low = list_to_data_tree(read_udi_from_folder(os.path.join(metric_dir, 'udi_lower')))\n UDI_up = list_to_data_tree(read_udi_from_folder(os.path.join(metric_dir, 'udi_upper')))\n else:\n DA, cDA, UDI_low, UDI, UDI_up = metrics_from_folder(\n res_folder, schedule, _threshold_, min_t, max_t, grid_filter_)\n DA = list_to_data_tree(DA)\n cDA = list_to_data_tree(cDA)\n UDI = list_to_data_tree(UDI)\n UDI_low = list_to_data_tree(UDI_low)\n UDI_up = list_to_data_tree(UDI_up)\n",
"code": "\nimport os\nimport subprocess\n\ntry:\n from ladybug.datacollection import BaseCollection\n from ladybug.futil import write_to_file\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug:\\n\\t{}'.format(e))\n\ntry:\n from honeybee.config import folders\nexcept ImportError as e:\n raise ImportError('\\nFailed to import honeybee:\\n\\t{}'.format(e))\n\ntry:\n from honeybee_radiance.postprocess.annualdaylight import metrics_from_folder\nexcept ImportError as e:\n raise ImportError('\\nFailed to import honeybee_radiance:\\n\\t{}'.format(e))\n\ntry:\n from honeybee_radiance_postprocess.dynamic import DynamicSchedule\nexcept ImportError as e:\n raise ImportError('\\nFailed to import honeybee_radiance:\\n\\t{}'.format(e))\n\ntry:\n from honeybee_energy.lib.schedules import schedule_by_identifier\nexcept ImportError as e: # honeybee schedule library is not available\n schedule_by_identifier = None\n\ntry:\n from pollination_handlers.outputs.daylight import read_da_from_folder, \\\n read_cda_from_folder, read_udi_from_folder\nexcept ImportError as e:\n raise ImportError('\\nFailed to import pollination_handlers:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs, list_to_data_tree, \\\n give_warning\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_{{cad}}:\\n\\t{}'.format(e))\n\n\nif all_required_inputs(ghenv.Component):\n # set default values for the thresholds and the grid filter\n grid_filter_ = '*' if grid_filter_ is None else grid_filter_\n _threshold_ = _threshold_ if _threshold_ else 300\n if len(_min_max_) != 0:\n assert len(_min_max_), 'Expected two values for _min_max_.'\n min_t = _min_max_[0]\n max_t = _min_max_[1]\n else:\n min_t = 100\n max_t = 3000\n\n # process the schedule\n if _occ_sch_ is None:\n schedule = None\n elif isinstance(_occ_sch_, BaseCollection):\n schedule = _occ_sch_.values\n elif isinstance(_occ_sch_, str):\n if schedule_by_identifier is not None:\n try:\n schedule = schedule_by_identifier(_occ_sch_).values()\n except TypeError: # it's probably a ScheduleFixedInterval\n schedule = schedule_by_identifier(_occ_sch_).values\n else:\n raise ValueError('honeybee-energy must be installed to reference '\n 'occupancy schedules by identifier.')\n else: # assume that it is a honeybee schedule object\n try:\n schedule = _occ_sch_.values()\n except TypeError: # it's probably a ScheduleFixedInterval\n schedule = _occ_sch_.values\n if schedule is not None:\n bin_schedule = []\n for val in schedule:\n bin_val = 1 if val >= 0.1 else 0\n bin_schedule.append(bin_val)\n schedule = bin_schedule\n\n # compute the annual metrics\n res_folder = os.path.dirname(_results[0]) if os.path.isfile(_results[0]) \\\n else _results[0]\n if os.path.isdir(os.path.join(res_folder, '__static_apertures__')) or \\\n os.path.isfile(os.path.join(res_folder, 'grid_states.json')):\n cmds = [\n folders.python_exe_path, '-m', 'honeybee_radiance_postprocess',\n 'post-process', 'annual-daylight', res_folder, '-sf', 'metrics',\n '-t', str(_threshold_), '-lt', str(min_t), '-ut', str(max_t)\n ]\n if grid_filter_ != '*':\n cmds.extend(['--grids-filter', grid_filter_])\n if len(dyn_sch_) != 0:\n dyn_sch = dyn_sch_[0] if isinstance(dyn_sch_[0], DynamicSchedule) else \\\n DynamicSchedule.from_group_schedules(dyn_sch_)\n dyn_sch_file = dyn_sch.to_json(folder=res_folder)\n cmds.extend(['--states', dyn_sch_file])\n if schedule is not None:\n sch_str = '\\n'.join(str(h) for h in schedule)\n sch_file = os.path.join(res_folder, 'schedule.txt')\n write_to_file(sch_file, sch_str)\n cmds.extend(['--schedule', sch_file])\n use_shell = True if os.name == 'nt' else False\n process = subprocess.Popen(\n cmds, cwd=res_folder, shell=use_shell,\n stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n stdout = process.communicate() # wait for the process to finish\n if stdout[-1] != '':\n print(stdout[-1])\n raise ValueError('Failed to compute annual daylight metrics.')\n metric_dir = os.path.join(res_folder, 'metrics')\n DA = list_to_data_tree(read_da_from_folder(os.path.join(metric_dir, 'da')))\n cDA = list_to_data_tree(read_cda_from_folder(os.path.join(metric_dir, 'cda')))\n UDI = list_to_data_tree(read_udi_from_folder(os.path.join(metric_dir, 'udi')))\n UDI_low = list_to_data_tree(read_udi_from_folder(os.path.join(metric_dir, 'udi_lower')))\n UDI_up = list_to_data_tree(read_udi_from_folder(os.path.join(metric_dir, 'udi_upper')))\n else:\n if len(dyn_sch_) != 0:\n msg = 'Dynamic Schedules are currently only supported for Annual Daylight ' \\\n 'simulations.\\nThe input schedules will be ignored.'\n print(msg)\n give_warning(ghenv.Component, msg)\n DA, cDA, UDI_low, UDI, UDI_up = metrics_from_folder(\n res_folder, schedule, _threshold_, min_t, max_t, grid_filter_)\n DA = list_to_data_tree(DA)\n cDA = list_to_data_tree(cDA)\n UDI = list_to_data_tree(UDI)\n UDI_low = list_to_data_tree(UDI_low)\n UDI_up = list_to_data_tree(UDI_up)\n",
"category": "HB-Radiance",
"name": "HB Annual Daylight Metrics",
"description": "Calculate Annual Daylight Metrics from a result (.ill) files.\n-"
Expand Down
36 changes: 36 additions & 0 deletions honeybee_grasshopper_radiance/json/HB_Aperture_Group_Schedule.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"version": "1.6.0",
"nickname": "GroupSch",
"outputs": [
[
{
"access": "None",
"name": "dyn_sch",
"description": "A dynamic schedule object for the input aperture group, which can be plugged\ninto any of the Results components with a syn_sch input.",
"type": null,
"default": null
}
]
],
"inputs": [
{
"access": "list",
"name": "_group_aps",
"description": "Honeybee Apertures that are a part of the same dynamic group and will\nbe assigned the same schedule for postprocessing. Typically, this is\nthe output of the \"HB Dynamic Aperture Group\" component but it can\nalso be the output of the \"HB Get Dynamic Groups\" component, which\nreturns all of the dynamic groups on a particular Model.",
"type": "System.Object",
"default": null
},
{
"access": "list",
"name": "_schedule",
"description": "A list of 8760 integers refering to the index of the aperture group state\nto be used at each hour of the simulation. This can also be a single integer\nfor a static state to be used for the entire period of the simulation\nor a pattern of integers that is less than 8760 in length and will be\nrepeated until the 8760 is reached. Note that 0 refers to the first\nstate, 1 refers to the second state, and so on. -1 can be used to\ncompletely discout the aperture from the simulation for a given hour.",
"type": "int",
"default": null
}
],
"subcategory": "4 :: Results",
"code": "\ntry:\n from honeybee.aperture import Aperture\nexcept ImportError as e:\n raise ImportError('\\nFailed to import honeybee:\\n\\t{}'.format(e))\n\ntry:\n from honeybee_radiance_postprocess.dynamic import ApertureGroupSchedule\nexcept ImportError as e:\n raise ImportError('\\nFailed to import honeybee_radiance:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs, recipe_result\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_{{cad}}:\\n\\t{}'.format(e))\n\n\nif all_required_inputs(ghenv.Component):\n dyn_sch = []\n dyn_ids = set()\n for ap in _group_aps:\n assert isinstance(ap, Aperture), 'Expected Aperture. Got {}.'.format(type(ap))\n dyn_grp_id = ap.properties.radiance.dynamic_group_identifier\n if dyn_grp_id is None:\n raise ValueError(\n 'Input Aperture \"{}\" is not a part of a dynamic group.'.format(ap.display_name))\n if dyn_grp_id not in dyn_ids:\n dyn_ids.add(dyn_grp_id)\n _ap_group_sch = ApertureGroupSchedule(dyn_grp_id, _schedule)\n dyn_sch.append(_ap_group_sch)\n",
"category": "HB-Radiance",
"name": "HB Aperture Group Schedule",
"description": "Create a Dynamic Aperture Group Schedule, which can be used to process any dynamic\naperture geometry that was run in an annual simulation.\n-"
}
Loading

0 comments on commit c596d56

Please sign in to comment.