Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(create): Preserve order in Rejoin to Building #262

Merged
merged 2 commits into from
Sep 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified dragonfly_grasshopper/icon/DF Rejoin to Building.png
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.
43 changes: 43 additions & 0 deletions dragonfly_grasshopper/json/DF_Fourth_Generation_Thermal_Loop.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"version": "1.6.0",
"nickname": "Gen4Loop",
"outputs": [
[
{
"access": "None",
"name": "des_loop",
"description": "A Dragonfly Thermal Loop object possessing all infrastructure for a\nDistrict Energy Simulation (DES) simulation. This should be connected\nto the loop_ input of the \"DF Model to GeoJSON\" component.",
"type": null,
"default": null
}
]
],
"inputs": [
{
"access": "item",
"name": "_chilled_temp_",
"description": "A number for the temperature of chilled water in the DES\nin degrees Celsius. (Default: 6).",
"type": "double",
"default": null
},
{
"access": "item",
"name": "_hot_temp_",
"description": "A number for the temperature of hot water in the DES in degrees\nCelsius. (Default: 54).",
"type": "double",
"default": null
},
{
"access": "item",
"name": "_name_",
"description": "Text to be used for the name and identifier of the Thermal Loop.\nIf no name is provided, it will be \"unnamed\".",
"type": "string",
"default": null
}
],
"subcategory": "3 :: Energy",
"code": "\ntry: # import the core honeybee dependencies\n from honeybee.typing import clean_ep_string\nexcept ImportError as e:\n raise ImportError('\\nFailed to import honeybee:\\n\\t{}'.format(e))\n\ntry: # import the core dragonfly_energy dependencies\n from dragonfly_energy.des.loop import FourthGenThermalLoop\nexcept ImportError as e:\n raise ImportError('\\nFailed to import dragonfly_energy:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs, 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 defaults\n name = clean_ep_string(_name_) if _name_ is not None else 'unnamed'\n cwt = 6 if _chilled_temp_ is None else _chilled_temp_\n hwt = 54 if _hot_temp_ is None else _hot_temp_\n\n # create the loop\n des_loop = FourthGenThermalLoop(name, cwt, hwt)\n if _name_ is not None:\n des_loop.display_name = _name_\n",
"category": "Dragonfly",
"name": "DF Fourth Generation Thermal Loop",
"description": "Create an Fourth Generation Thermal Loop, which represents all infrastructure\nfor a District Energy Simulation (DES) simulation.\n_\nThis includes a central hot and chilled water plant for the district.\n-"
}
4 changes: 2 additions & 2 deletions dragonfly_grasshopper/json/DF_Rejoin_to_Building.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "1.6.0",
"version": "1.6.1",
"nickname": "Rejoin",
"outputs": [
[
Expand All @@ -22,7 +22,7 @@
}
],
"subcategory": "0 :: Create",
"code": "\ntry: # import the core dragonfly dependencies\n from dragonfly.building import Building\n from dragonfly.story import Story\n from dragonfly.room2d import Room2D\n from dragonfly.colorobj import ColorRoom2D\nexcept ImportError as e:\n raise ImportError('\\nFailed to import dragonfly:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs, give_warning, \\\n document_counter\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 # duplicate the initial objects\n room2ds = []\n for rm in _room2ds:\n assert isinstance(rm, Room2D), 'Expected Room2D. Got {}.'.format(type(rm))\n room2ds.append(rm.duplicate())\n\n # organize the rooms into a nested dictionary by story/building\n orphaned_rooms = []\n org_dict, bldg_dict = {}, {}\n for rm in room2ds:\n if rm.has_parent and rm.parent.has_parent:\n story = rm.parent\n bldg = story.parent\n if bldg.identifier not in bldg_dict:\n bldg_dict[bldg.identifier] = bldg\n org_dict[bldg.identifier] = {}\n try:\n org_dict[bldg.identifier][story.identifier].append(rm)\n except KeyError:\n org_dict[bldg.identifier][story.identifier] = [rm]\n else:\n orphaned_rooms.append(rm)\n\n # re-generate the Buildings and add the new Room2Ds\n buildings = []\n for bldg in bldg_dict.values():\n new_bldg = bldg.duplicate()\n for story in new_bldg:\n try:\n rm_2ds = org_dict[bldg.identifier][story.identifier]\n story._room_2ds = ()\n story.add_room_2ds(rm_2ds)\n except KeyError: # story missing from the input and we'll use the old ones\n pass\n buildings.append(new_bldg)\n\n # if there were orphaned Room2Ds, add them to their own building\n if len(orphaned_rooms) != 0:\n # give a warning about the orphaned Room2Ds\n display_name = 'Building_{}'.format(document_counter('bldg_count'))\n name = clean_and_id_string(display_name)\n msg = '{} of the input Room2Ds were not a part of an original Dragonfly ' \\\n 'Building.\\nThey have been added to a new Building with the auto-generated ' \\\n 'name \"{}\"\\nBetter practice is to add these Room2Ds to new Stories and ' \\\n 'then a Building.'.format(len(orphaned_rooms), display_name)\n give_warning(ghenv.Component, msg)\n # create the stories and the building\n color_obj = ColorRoom2D(orphaned_rooms, 'floor_height')\n story_groups = [[] for val in values]\n values = color_obj.attributes_unique\n for atr, room in zip(color_obj.attributes, in_rooms):\n atr_i = values.index(atr)\n story_groups[atr_i].append(room)\n stories = [Story('{}_Story{}'.format(name, i), r_group)\n for i, r_group in enumerate(story_groups)]\n o_building = Building(name, stories)\n o_building.display_name = display_name\n buildings.append(o_building)\n",
"code": "\nfrom collections import OrderedDict\n\ntry: # import the core dragonfly dependencies\n from dragonfly.building import Building\n from dragonfly.story import Story\n from dragonfly.room2d import Room2D\n from dragonfly.colorobj import ColorRoom2D\nexcept ImportError as e:\n raise ImportError('\\nFailed to import dragonfly:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs, give_warning, \\\n document_counter\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 # duplicate the initial objects\n room2ds = []\n for rm in _room2ds:\n assert isinstance(rm, Room2D), 'Expected Room2D. Got {}.'.format(type(rm))\n room2ds.append(rm.duplicate())\n\n # organize the rooms into a nested dictionary by story/building\n orphaned_rooms = []\n org_dict, bldg_dict = OrderedDict(), OrderedDict()\n for rm in room2ds:\n if rm.has_parent and rm.parent.has_parent:\n story = rm.parent\n bldg = story.parent\n if bldg.identifier not in bldg_dict:\n bldg_dict[bldg.identifier] = bldg\n org_dict[bldg.identifier] = OrderedDict()\n try:\n org_dict[bldg.identifier][story.identifier].append(rm)\n except KeyError:\n org_dict[bldg.identifier][story.identifier] = [rm]\n else:\n orphaned_rooms.append(rm)\n\n # re-generate the Buildings and add the new Room2Ds\n buildings = []\n for bldg in bldg_dict.values():\n new_bldg = bldg.duplicate()\n for story in new_bldg:\n try:\n rm_2ds = org_dict[bldg.identifier][story.identifier]\n story._room_2ds = ()\n story.add_room_2ds(rm_2ds)\n except KeyError: # story missing from the input and we'll use the old ones\n pass\n buildings.append(new_bldg)\n\n # if there were orphaned Room2Ds, add them to their own building\n if len(orphaned_rooms) != 0:\n # give a warning about the orphaned Room2Ds\n display_name = 'Building_{}'.format(document_counter('bldg_count'))\n name = clean_and_id_string(display_name)\n msg = '{} of the input Room2Ds were not a part of an original Dragonfly ' \\\n 'Building.\\nThey have been added to a new Building with the auto-generated ' \\\n 'name \"{}\"\\nBetter practice is to add these Room2Ds to new Stories and ' \\\n 'then a Building.'.format(len(orphaned_rooms), display_name)\n give_warning(ghenv.Component, msg)\n # create the stories and the building\n color_obj = ColorRoom2D(orphaned_rooms, 'floor_height')\n story_groups = [[] for val in values]\n values = color_obj.attributes_unique\n for atr, room in zip(color_obj.attributes, in_rooms):\n atr_i = values.index(atr)\n story_groups[atr_i].append(room)\n stories = [Story('{}_Story{}'.format(name, i), r_group)\n for i, r_group in enumerate(story_groups)]\n o_building = Building(name, stories)\n o_building.display_name = display_name\n buildings.append(o_building)\n",
"category": "Dragonfly",
"name": "DF Rejoin to Building",
"description": "Rejoin a list of Room2Ds that were originally a part of a Building back to a new\nBuilding with updated Room2Ds.\n_\nIn the event that the input contains Room2Ds that were not a part of an original\nBuilding, this component can still be used but the stories will be regenerated\nbased on the Room2D floor elevations and a warning will be given.\n-"
Expand Down
43 changes: 43 additions & 0 deletions dragonfly_grasshopper/json/DF_Run_Modelica_DES.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"version": "1.6.0",
"nickname": "RunDES",
"outputs": [
[
{
"access": "None",
"name": "modelica",
"description": "A folder where all of the Modelica files of the District Energy\nSystem (DES) are written.",
"type": null,
"default": null
}
]
],
"inputs": [
{
"access": "item",
"name": "_geojson",
"description": "The path to an URBANopt-compatible geoJSON file. This geoJSON\nfile can be obtained form the \"DF Model to geoJSON\" component.\nThe geoJSON must have a valid District Energy System (DES) Loop\nassigned to it in order to run correctly through the DES simulation.",
"type": "string",
"default": null
},
{
"access": "item",
"name": "_scenario",
"description": "The path to an URBANopt .csv file for the scenario. This CSV\nfile can be obtained form the \"DF Run URBANopt\" component.",
"type": "string",
"default": null
},
{
"access": "item",
"name": "_run",
"description": "Set to \"True\" to simulate the Distric Energy System.",
"type": "bool",
"default": null
}
],
"subcategory": "3 :: Energy",
"code": "\nimport os\nimport subprocess\n\ntry:\n from ladybug.futil import nukedir\n from ladybug.config import folders as lb_folders\nexcept ImportError as e:\n raise ImportError('\\nFailed to import honeybee:\\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: # import the dragonfly_energy dependencies\n from dragonfly_energy.run import run_default_report\nexcept ImportError as e:\n raise ImportError('\\nFailed to import dragonfly_energy:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.download import download_file_by_name\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_{{cad}}:\\n\\t{}'.format(e))\n\nUO_GMT_VERSION = '0.6.0rc1'\nUO_TN_VERSION = '0.2.1'\nMBL_VERSION = '9.1.1'\n\n\nif all_required_inputs(ghenv.Component) and _run:\n # set clobal values\n ext = '.exe' if os.name == 'nt' else ''\n executor_path = os.path.join(\n lb_folders.ladybug_tools_folder, '{{plugin}}',\n 'ladybug_{{plugin}}_dotnet', 'Ladybug.Executor.exe')\n\n # check to see if the geojson-modelica-translator is installed\n uo_gmt = '{}/uo_des{}'.format(folders.python_scripts_path, ext)\n uo_gmt_pack = '{}/geojson_modelica_translator-{}.dist-info'.format(\n folders.python_package_path, UO_GMT_VERSION)\n if not os.path.isfile(uo_gmt) or not os.path.isdir(uo_gmt_pack):\n #install_cmd = 'pip install geojson-modelica-translator=={}'.format(UO_GMT_VERSION)\n install_cmd = 'pip install git+https://github.com/urbanopt/geojson-modelica-translator@develop'\n if os.name == 'nt' and os.path.isfile(executor_path) and \\\n 'Program Files' in executor_path:\n pip_cmd = [\n executor_path, folders.python_exe_path, '-m {}'.format(install_cmd)\n ]\n else:\n pip_cmd = '\"{py_exe}\" -m {uo_cmd}'.format(\n py_exe=folders.python_exe_path, uo_cmd=install_cmd)\n shell = True if os.name == 'nt' else False\n process = subprocess.Popen(pip_cmd, stderr=subprocess.PIPE, shell=shell)\n stderr = process.communicate()\n\n # check to see if the ThermalNetwork package is installed\n uo_tn = '{}/thermalnetwork{}'.format(folders.python_scripts_path, ext)\n uo_tn_pack = '{}/ThermalNetwork-{}.dist-info'.format(\n folders.python_package_path, UO_TN_VERSION)\n if not os.path.isfile(uo_tn) or not os.path.isdir(uo_tn_pack):\n install_cmd = 'pip install thermalnetwork=={}'.format(UO_TN_VERSION)\n if os.name == 'nt' and os.path.isfile(executor_path) and \\\n 'Program Files' in executor_path:\n pip_cmd = [\n executor_path, folders.python_exe_path, '-m {}'.format(install_cmd)\n ]\n else:\n pip_cmd = '\"{py_exe}\" -m {uo_cmd}'.format(\n py_exe=folders.python_exe_path, uo_cmd=install_cmd)\n shell = True if os.name == 'nt' else False\n process = subprocess.Popen(pip_cmd, stderr=subprocess.PIPE, shell=shell)\n stderr = process.communicate()\n\n # check to see if the Modelica Buildings Library is installed\n install_directory = os.path.join(lb_folders.ladybug_tools_folder, 'resources')\n final_dir = os.path.join(install_directory, 'mbl')\n version_file = os.path.join(final_dir, 'version.txt')\n already_installed = False\n if os.path.isdir(final_dir) and os.path.isfile(version_file):\n with open(version_file, 'r') as vf:\n install_version = vf.read()\n if install_version == MBL_VERSION:\n already_installed = True\n else:\n nukedir(final_dir, True)\n if not already_installed:\n install_cmd = 'dragonfly_energy install mbl --version {}'.format(MBL_VERSION)\n if os.name == 'nt' and os.path.isfile(executor_path) and \\\n 'Program Files' in executor_path:\n pip_cmd = [\n executor_path, folders.python_exe_path, '-m {}'.format(install_cmd)\n ]\n else:\n pip_cmd = '\"{py_exe}\" -m {uo_cmd}'.format(\n py_exe=folders.python_exe_path, uo_cmd=install_cmd)\n shell = True if os.name == 'nt' else False\n process = subprocess.Popen(pip_cmd, stderr=subprocess.PIPE, shell=shell)\n stderr = process.communicate()\n\n \"\"\"\n # delete any existing files in the result folder\n scen_name = os.path.basename(_scenario).replace('.csv', '')\n run_folder = os.path.join(os.path.dirname(_geojson), 'run', scen_name)\n result_folder = os.path.join(run_folder, 'modelica')\n nukedir(result_folder)\n\n # prepare the Modelica-running command\n command = '\"{uo_ditto}\" run-opendss -f \"{feature_file}\" ' \\\n '-s \"{scenario_file}\"'.format(\n uo_ditto=uo_ditto, feature_file=_geojson, scenario_file=_scenario)\n\n # execute the command to run everything through Modelica\n shell = False if os.name == 'nt' else True\n process = subprocess.Popen(command, stderr=subprocess.PIPE, shell=shell)\n stderr = process.communicate()\n\n # gather together all of the result files\n bldg_folder = os.path.join(result_folder, 'results', 'Features')\n \"\"\"\n",
"category": "Dragonfly",
"name": "DF Run Modelica DES",
"description": "Run a an URBANopt geoJSON and scenario through Modelica DES simulation.\n_\nThe geoJSON must have a valid DES Loop assigned to it in order to run correctly\nthrough Modelica DES simulation.\n-"
}
64 changes: 64 additions & 0 deletions dragonfly_grasshopper/src/DF Fourth Generation Thermal Loop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Dragonfly: A Plugin for Environmental Analysis (GPL)
# This file is part of Dragonfly.
#
# Copyright (c) 2023, Ladybug Tools.
# You should have received a copy of the GNU Affero General Public License
# along with Dragonfly; If not, see <http://www.gnu.org/licenses/>.
#
# @license AGPL-3.0-or-later <https://spdx.org/licenses/AGPL-3.0-or-later>

"""
Create an Fourth Generation Thermal Loop, which represents all infrastructure
for a District Energy Simulation (DES) simulation.
_
This includes a central hot and chilled water plant for the district.
-

Args:
_chilled_temp_: A number for the temperature of chilled water in the DES
in degrees Celsius. (Default: 6).
_hot_temp_: A number for the temperature of hot water in the DES in degrees
Celsius. (Default: 54).
_name_: Text to be used for the name and identifier of the Thermal Loop.
If no name is provided, it will be "unnamed".

Returns:
report: Reports, errors, warnings, etc.
loop: A Dragonfly Thermal Loop object possessing all infrastructure for a
District Energy Simulation (DES) simulation. This should be connected
to the loop_ input of the "DF Model to GeoJSON" component.
"""

ghenv.Component.Name = 'DF Fourth Generation Thermal Loop'
ghenv.Component.NickName = 'Gen4Loop'
ghenv.Component.Message = '1.6.0'
ghenv.Component.Category = 'Dragonfly'
ghenv.Component.SubCategory = '3 :: Energy'
ghenv.Component.AdditionalHelpFromDocStrings = '0'

try: # import the core honeybee dependencies
from honeybee.typing import clean_ep_string
except ImportError as e:
raise ImportError('\nFailed to import honeybee:\n\t{}'.format(e))

try: # import the core dragonfly_energy dependencies
from dragonfly_energy.des.loop import FourthGenThermalLoop
except ImportError as e:
raise ImportError('\nFailed to import dragonfly_energy:\n\t{}'.format(e))

try:
from ladybug_rhino.grasshopper import all_required_inputs, give_warning
except ImportError as e:
raise ImportError('\nFailed to import ladybug_rhino:\n\t{}'.format(e))


if all_required_inputs(ghenv.Component):
# set defaults
name = clean_ep_string(_name_) if _name_ is not None else 'unnamed'
cwt = 6 if _chilled_temp_ is None else _chilled_temp_
hwt = 54 if _hot_temp_ is None else _hot_temp_

# create the loop
des_loop = FourthGenThermalLoop(name, cwt, hwt)
if _name_ is not None:
des_loop.display_name = _name_
Loading