diff --git a/honeybee_grasshopper_energy/icon/HB Generation Loads.png b/honeybee_grasshopper_energy/icon/HB Generation Loads.png new file mode 100644 index 0000000..93c5bd0 Binary files /dev/null and b/honeybee_grasshopper_energy/icon/HB Generation Loads.png differ diff --git a/honeybee_grasshopper_energy/json/HB_Generation_Loads.json b/honeybee_grasshopper_energy/json/HB_Generation_Loads.json new file mode 100644 index 0000000..533abac --- /dev/null +++ b/honeybee_grasshopper_energy/json/HB_Generation_Loads.json @@ -0,0 +1,92 @@ +{ + "version": "1.7.0", + "nickname": "GenLoads", + "outputs": [ + [ + { + "access": "None", + "name": "total_ac", + "description": "A number for the total on-site produced alternating current (AC)\nelectricity in kWh.", + "type": null, + "default": null + }, + { + "access": "None", + "name": "ac_power", + "description": "A data collection of all on-site produced electricity (kWh). This\nrepresents the alternating current (AC) electricity coming out of\nthe inverter that processes all on-site power production.", + "type": null, + "default": null + }, + { + "access": "None", + "name": "generators", + "description": "A list of names for each of the electricity generation objects that\nwere found among the connected _hb_objs. These names align with the\ntota_dc output below as well as the dc_power data collections.", + "type": null, + "default": null + }, + { + "access": "None", + "name": "total_dc", + "description": "A list of numbers for the direct current (DC) electricity produced\nby each generator object in kWh.", + "type": null, + "default": null + }, + { + "access": "None", + "name": "dc_power", + "description": "A list of data collections for the direct current (DC) electricity\nproduced by each on-site electricity generator (kWh). Each\nphotovoltaic object will have a separate data collection.", + "type": null, + "default": null + } + ] + ], + "inputs": [ + { + "access": "list", + "name": "_hb_objs", + "description": "An array of honeybee Rooms, Faces, Apertures, Doors or Shades to be\nincluded in the simulation of electricity production. This can also\nbe an entire Model to be simulated. Any number of Honeybee Rooms or\nnon-generating objects can be connected but they will only be\nsimulated as context shade that casts shadows on the\ngenerator objects.", + "type": "System.Object", + "default": null + }, + { + "access": "item", + "name": "_epw_file", + "description": "Path to an .epw file on your system as a text string.", + "type": "string", + "default": null + }, + { + "access": "item", + "name": "_north_", + "description": "A number between -360 and 360 for the counterclockwise difference\nbetween the North and the positive Y-axis in degrees.\n90 is West and 270 is East. (Default: 0).", + "type": "System.Object", + "default": null + }, + { + "access": "item", + "name": "_inverter_eff_", + "description": "A number between 0 and 1 for the load centers's inverter nominal\nrated DC-to-AC conversion efficiency. An inverter converts DC power,\nsuch as that output by photovoltaic panels, to AC power, such as\nthat distributed by the electrical grid and is available from\nstandard electrical outlets. Inverter efficiency is defined as the\ninverter's rated AC power output divided by its rated DC power\noutput. (Default: 0.96).", + "type": "double", + "default": null + }, + { + "access": "item", + "name": "_dc_to_ac_size_", + "description": "A positive number (typically greater than 1) for the ratio of the\ninverter's DC rated size to its AC rated size. Typically, inverters\nare not sized to convert the full DC output under standard test\nconditions (STC) as such conditions rarely occur in reality and\ntherefore unnecessarily add to the size/cost of the inverter. For a\nsystem with a high DC to AC size ratio, during times when the\nDC power output exceeds the inverter's rated DC input size, the inverter\nlimits the array's power output by increasing the DC operating voltage,\nwhich moves the arrays operating point down its current-voltage (I-V)\ncurve. The default value of 1.1 is reasonable for most systems. A\ntypical range is 1.1 to 1.25, although some large-scale systems have\nratios of as high as 1.5. The optimal value depends on the system's\nlocation, array orientation, and module cost. (Default: 1.1).", + "type": "double", + "default": null + }, + { + "access": "item", + "name": "_run", + "description": "Set to \"True\" to run the simulation to obtain annual loads. This can\nalso be the integer 2 to run the simulation while being able to see\nthe simulation process (with a batch window).", + "type": "int", + "default": null + } + ], + "subcategory": "5 :: Simulate", + "code": "\nimport os\nimport subprocess\nimport json\nfrom collections import OrderedDict\n\ntry:\n from ladybug.futil import write_to_file_by_name, nukedir\n from ladybug.sql import SQLiteResult\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug:\\n\\t{}'.format(e))\n\ntry:\n from honeybee.config import folders\n from honeybee.model import Model\nexcept ImportError as e:\n raise ImportError('\\nFailed to import honeybee:\\n\\t{}'.format(e))\n\ntry:\n from honeybee_energy.simulation.parameter import SimulationParameter\n from honeybee_energy.run import run_idf\n from honeybee_energy.result.err import Err\n from honeybee_energy.writer import energyplus_idf_version\n from honeybee_energy.config import folders as energy_folders\n from honeybee_energy.lib.constructions import opaque_construction_by_identifier\nexcept ImportError as e:\n raise ImportError('\\nFailed to import honeybee_energy:\\n\\t{}'.format(e))\n\ntry:\n from lbt_recipes.version import check_energyplus_version\nexcept ImportError as e:\n raise ImportError('\\nFailed to import lbt_recipes:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.togeometry import to_vector2d\n from ladybug_{{cad}}.config import units_system, tolerance, angle_tolerance\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\ndef serialize_data(data_dicts):\n \"\"\"Reserialize a list of HourlyCollection dictionaries.\"\"\"\n return [HourlyCollection.from_dict(data) for data in data_dicts]\n\n\n# List of all the output strings that will be requested\nac_out = 'Facility Total Produced Electricity Energy'\ndc_out = 'Generator Produced DC Electricity Energy'\nenergy_output = (ac_out, dc_out)\n\n\nif all_required_inputs(ghenv.Component) and _run:\n # check the presence of energyplus and check that the version is compatible\n check_energyplus_version()\n\n # create the Model from the _hb_objs\n models = [obj.duplicate() for obj in _hb_objs if isinstance(obj, Model)]\n other_objs = [obj.duplicate() for obj in _hb_objs if not isinstance(obj, Model)]\n model = Model.from_objects('Generation_Loads', other_objs, units_system(),\n tolerance, angle_tolerance)\n for m in models:\n model.add_model(m)\n model.rooms_to_orphaned()\n soil_constr = opaque_construction_by_identifier('Mud')\n model.properties.energy.generate_ground_room(soil_constr)\n\n # set the inverter efficiency and size\n if _inverter_eff_ is not None:\n model.properties.energy.electric_load_center.inverter_efficiency = _inverter_eff_\n if _dc_to_ac_size_ is not None:\n model.properties.energy.electric_load_center.inverter_dc_to_ac_size_ratio = _dc_to_ac_size_\n\n # process the simulation folder name and the directory\n directory = os.path.join(folders.default_simulation_folder, model.identifier)\n sch_directory = os.path.join(directory, 'schedules')\n nukedir(directory) # delete any existing files in the directory\n\n # create simulation parameters for the coarsest/fastest E+ sim possible\n _sim_par_ = SimulationParameter()\n _sim_par_.timestep = 6\n _sim_par_.shadow_calculation.solar_distribution = 'FullExteriorWithReflections'\n _sim_par_.output.reporting_frequency = 'Hourly'\n _sim_par_.output.add_electricity_generation()\n _sim_par_.output.include_html = False\n _sim_par_.simulation_control.do_zone_sizing = False\n _sim_par_.simulation_control.do_system_sizing = False\n _sim_par_.simulation_control.do_plant_sizing = False\n\n # set the north if it is not defaulted\n if _north_ is not None:\n try:\n _sim_par_.north_vector = to_vector2d(_north_)\n except AttributeError: # north angle instead of vector\n _sim_par_.north_angle = float(_north_)\n\n # create the strings for simulation paramters and model\n ver_str = energyplus_idf_version() if energy_folders.energyplus_version \\\n is not None else energyplus_idf_version(compatibe_ep_version)\n sim_par_str = _sim_par_.to_idf()\n model_str = model.to.idf(\n model, schedule_directory=sch_directory, patch_missing_adjacencies=True)\n idf_str = '\\n\\n'.join([ver_str, sim_par_str, model_str])\n\n # write the final string into an IDF\n idf = os.path.join(directory, 'in.idf')\n write_to_file_by_name(directory, 'in.idf', idf_str, True)\n\n # run the IDF through EnergyPlus\n silent = True if _run == 1 else False\n sql, zsz, rdd, html, err = run_idf(idf, _epw_file, silent=silent)\n if sql is None and err is not None: # something went wrong; parse the errors\n err_obj = Err(err)\n print(err_obj.file_contents)\n for error in err_obj.fatal_errors:\n raise Exception(error)\n\n # parse the result sql and get the monthly data collections\n if os.name == 'nt': # we are on windows; use IronPython like usual\n sql_obj = SQLiteResult(sql)\n ac_power = sql_obj.data_collections_by_output_name(ac_out)\n dc_power = sql_obj.data_collections_by_output_name(dc_out)\n else: # we are on Mac; sqlite3 module doesn't work in Mac IronPython\n # Execute the honybee CLI to obtain the results via CPython\n cmds = [folders.python_exe_path, '-m', 'honeybee_energy', 'result',\n 'data-by-outputs', sql]\n for outp in energy_output:\n cmds.append('[\"{}\"]'.format(outp))\n custom_env = os.environ.copy()\n custom_env['PYTHONHOME'] = ''\n process = subprocess.Popen(cmds, stdout=subprocess.PIPE, env=custom_env)\n stdout = process.communicate()\n data_coll_dicts = json.loads(stdout[0])\n ac_power = serialize_data(data_coll_dicts[0])\n dc_power = serialize_data(data_coll_dicts[1])\n\n # give a warning if no generators were found in the results\n if len(ac_power) == 0:\n msg = 'No electricity generation objects were found in the connected _hb_objs.\\n' \\\n 'Try applying PV properties to some of the connected Shade geometries.'\n print(msg)\n give_warning(ghenv.Component, msg)\n\n # group the generator results by identifier\n dc_dict = OrderedDict()\n for g_data in dc_power:\n gen_id = g_data.header.metadata['System'].split('..')[0]\n g_data.header.metadata['System'] = gen_id\n try:\n dc_dict[gen_id] += g_data\n except KeyError:\n dc_dict[gen_id] = g_data\n\n # sum the results and ouput them\n total_ac = [acp.total for acp in ac_power]\n generators = list(dc_dict.keys())\n total_dc = [dcp.total for dcp in dc_dict.values()]\n dc_power = [dcp for dcp in dc_dict.values()]\n", + "category": "HB-Energy", + "name": "HB Generation Loads", + "description": "Run Honeybee objects capable of generating electricity (such as Shades with PV\nproperties) through a quick energy simulation to obtain an estimate of\nelectricity production.\n_\nNote that this component only evaluates electricity production and not energy\nconsumption. Any number of Honeybee Rooms or other objects can be connected\nbut they will only be simulated as context shade that casts shadows on the\ngenerator objects.\n-" +} \ No newline at end of file diff --git a/honeybee_grasshopper_energy/json/HB_Read_Generation_Result.json b/honeybee_grasshopper_energy/json/HB_Read_Generation_Result.json index ac7b583..4fe2e2d 100644 --- a/honeybee_grasshopper_energy/json/HB_Read_Generation_Result.json +++ b/honeybee_grasshopper_energy/json/HB_Read_Generation_Result.json @@ -1,5 +1,5 @@ { - "version": "1.7.0", + "version": "1.7.1", "nickname": "GenerationResult", "outputs": [ [ @@ -50,8 +50,8 @@ } ], "subcategory": "6 :: Result", - "code": "\nimport os\nimport subprocess\nimport json\n\ntry:\n from ladybug.datacollection import HourlyContinuousCollection, \\\n MonthlyCollection, DailyCollection\n from ladybug.sql import SQLiteResult\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_energy.result.generation import generation_summary_from_sql, \\\n generation_data_from_sql\nexcept ImportError as e:\n raise ImportError('\\nFailed to import honeybee_energy:\\n\\t{}'.format(e))\n\ntry:\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\n\n# The SQLite3 module doesn't work in IronPython on Mac, so we must make a call\n# to the Honeybee CLI (which runs on CPython) to get the results.\ndef get_results_mac(sql_files):\n from collections import OrderedDict\n cmds = [folders.python_exe_path, '-m', 'honeybee_energy', 'result',\n 'generation-summary']\n cmds.extend(sql_files)\n process = subprocess.Popen(cmds, stdout=subprocess.PIPE, env=custom_env)\n stdout = process.communicate()\n results = json.loads(stdout[0])\n return results\n\n\ndef serialize_data(data_dicts):\n \"\"\"Reserialize a list of collection dictionaries.\"\"\"\n if len(data_dicts) == 0 or data_dicts[0] is None:\n return [None] * len(data_dicts)\n elif data_dicts[0]['type'] == 'HourlyContinuous':\n return [HourlyContinuousCollection.from_dict(data) for data in data_dicts]\n elif data_dicts[0]['type'] == 'Monthly':\n return [MonthlyCollection.from_dict(data) for data in data_dicts]\n elif data_dicts[0]['type'] == 'Daily':\n return [DailyCollection.from_dict(data) for data in data_dicts]\n\nDC_OUTPUT = 'Generator Produced DC Electricity Energy'\ncustom_env = os.environ.copy()\ncustom_env['PYTHONHOME'] = ''\n\n\nif all_required_inputs(ghenv.Component):\n dc_power = []\n if os.name == 'nt': # we are on windows; use IronPython like usual\n result_dict = generation_summary_from_sql(_sql)\n production, consumption = generation_data_from_sql(_sql)\n for sql_f in _sql:\n sql_obj = SQLiteResult(sql_f)\n dc_data = sql_obj.data_collections_by_output_name(DC_OUTPUT)\n dc_power.extend(dc_data)\n\n else: # we are on Mac; sqlite3 module doesn't work in Mac IronPython\n # Execute the honybee CLI to obtain the results via CPython\n result_dict = get_results_mac(_sql)\n cmds = [folders.python_exe_path, '-m', 'honeybee_energy', 'result',\n 'generation-data']\n cmds.extend(_sql)\n process = subprocess.Popen(cmds, stdout=subprocess.PIPE, env=custom_env)\n stdout = process.communicate()\n data_dicts = json.loads(stdout[0])\n production, consumption = serialize_data(data_dicts)\n\n for sql_f in _sql:\n cmds = [folders.python_exe_path, '-m', 'honeybee_energy', 'result',\n 'data-by-outputs', sql_f, DC_OUTPUT]\n process = subprocess.Popen(cmds, stdout=subprocess.PIPE, env=custom_env)\n stdout = process.communicate()\n data_dicts = json.loads(stdout[0])\n dc_data = serialize_data(data_dicts[0])\n dc_power.extend(dc_data)\n\n # output the separate summary results\n site_totals = (\n result_dict['total_production'],\n result_dict['total_consumption']\n )\n utility_totals = (\n result_dict['production_used_on_site'],\n result_dict['production_surplus_sold'],\n result_dict['consumption_purchased']\n )\n", + "code": "\nimport os\nimport subprocess\nimport json\nfrom collections import OrderedDict\n\ntry:\n from ladybug.datacollection import HourlyContinuousCollection, \\\n MonthlyCollection, DailyCollection\n from ladybug.sql import SQLiteResult\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_energy.result.generation import generation_summary_from_sql, \\\n generation_data_from_sql\nexcept ImportError as e:\n raise ImportError('\\nFailed to import honeybee_energy:\\n\\t{}'.format(e))\n\ntry:\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\n\n# The SQLite3 module doesn't work in IronPython on Mac, so we must make a call\n# to the Honeybee CLI (which runs on CPython) to get the results.\ndef get_results_mac(sql_files):\n from collections import OrderedDict\n cmds = [folders.python_exe_path, '-m', 'honeybee_energy', 'result',\n 'generation-summary']\n cmds.extend(sql_files)\n process = subprocess.Popen(cmds, stdout=subprocess.PIPE, env=custom_env)\n stdout = process.communicate()\n results = json.loads(stdout[0])\n return results\n\n\ndef serialize_data(data_dicts):\n \"\"\"Reserialize a list of collection dictionaries.\"\"\"\n if len(data_dicts) == 0 or data_dicts[0] is None:\n return [None] * len(data_dicts)\n elif data_dicts[0]['type'] == 'HourlyContinuous':\n return [HourlyContinuousCollection.from_dict(data) for data in data_dicts]\n elif data_dicts[0]['type'] == 'Monthly':\n return [MonthlyCollection.from_dict(data) for data in data_dicts]\n elif data_dicts[0]['type'] == 'Daily':\n return [DailyCollection.from_dict(data) for data in data_dicts]\n\nDC_OUTPUT = 'Generator Produced DC Electricity Energy'\ncustom_env = os.environ.copy()\ncustom_env['PYTHONHOME'] = ''\n\n\nif all_required_inputs(ghenv.Component):\n dc_power = []\n if os.name == 'nt': # we are on windows; use IronPython like usual\n result_dict = generation_summary_from_sql(_sql)\n production, consumption = generation_data_from_sql(_sql)\n for sql_f in _sql:\n sql_obj = SQLiteResult(sql_f)\n dc_data = sql_obj.data_collections_by_output_name(DC_OUTPUT)\n dc_power.extend(dc_data)\n\n else: # we are on Mac; sqlite3 module doesn't work in Mac IronPython\n # Execute the honybee CLI to obtain the results via CPython\n result_dict = get_results_mac(_sql)\n cmds = [folders.python_exe_path, '-m', 'honeybee_energy', 'result',\n 'generation-data']\n cmds.extend(_sql)\n process = subprocess.Popen(cmds, stdout=subprocess.PIPE, env=custom_env)\n stdout = process.communicate()\n data_dicts = json.loads(stdout[0])\n production, consumption = serialize_data(data_dicts)\n\n for sql_f in _sql:\n cmds = [folders.python_exe_path, '-m', 'honeybee_energy', 'result',\n 'data-by-outputs', sql_f, DC_OUTPUT]\n process = subprocess.Popen(cmds, stdout=subprocess.PIPE, env=custom_env)\n stdout = process.communicate()\n data_dicts = json.loads(stdout[0])\n dc_data = serialize_data(data_dicts[0])\n dc_power.extend(dc_data)\n\n # output the separate summary results\n site_totals = (\n result_dict['total_production'],\n result_dict['total_consumption']\n )\n utility_totals = (\n result_dict['production_used_on_site'],\n result_dict['production_surplus_sold'],\n result_dict['consumption_purchased']\n )\n\n # group the generator results by identifier\n dc_dict = OrderedDict()\n for g_data in dc_power:\n gen_id = g_data.header.metadata['System'].split('..')[0]\n g_data.header.metadata['System'] = gen_id\n try:\n dc_dict[gen_id] += g_data\n except KeyError:\n dc_dict[gen_id] = g_data\n dc_power = [dcp for dcp in dc_dict.values()]\n", "category": "HB-Energy", "name": "HB Read Generation Result", - "description": "Parse any time series data from an energy simulation SQL result file.\n-" + "description": "Parse electricity generation results from an energy simulation SQL result file.\n-" } \ No newline at end of file diff --git a/honeybee_grasshopper_energy/src/HB Generation Loads.py b/honeybee_grasshopper_energy/src/HB Generation Loads.py new file mode 100644 index 0000000..6f3d15e --- /dev/null +++ b/honeybee_grasshopper_energy/src/HB Generation Loads.py @@ -0,0 +1,236 @@ +# Honeybee: A Plugin for Environmental Analysis (GPL) +# This file is part of Honeybee. +# +# Copyright (c) 2023, Ladybug Tools. +# You should have received a copy of the GNU Affero General Public License +# along with Honeybee; If not, see . +# +# @license AGPL-3.0-or-later + +""" +Run Honeybee objects capable of generating electricity (such as Shades with PV +properties) through a quick energy simulation to obtain an estimate of +electricity production. +_ +Note that this component only evaluates electricity production and not energy +consumption. Any number of Honeybee Rooms or other objects can be connected +but they will only be simulated as context shade that casts shadows on the +generator objects. +- + + Args: + _hb_objs: An array of honeybee Rooms, Faces, Apertures, Doors or Shades to be + included in the simulation of electricity production. This can also + be an entire Model to be simulated. Any number of Honeybee Rooms or + non-generating objects can be connected but they will only be + simulated as context shade that casts shadows on the + generator objects. + _epw_file: Path to an .epw file on your system as a text string. + _north_: A number between -360 and 360 for the counterclockwise difference + between the North and the positive Y-axis in degrees. + 90 is West and 270 is East. (Default: 0). + _inverter_eff_: A number between 0 and 1 for the load centers's inverter nominal + rated DC-to-AC conversion efficiency. An inverter converts DC power, + such as that output by photovoltaic panels, to AC power, such as + that distributed by the electrical grid and is available from + standard electrical outlets. Inverter efficiency is defined as the + inverter's rated AC power output divided by its rated DC power + output. (Default: 0.96). + _dc_to_ac_size_: A positive number (typically greater than 1) for the ratio of the + inverter's DC rated size to its AC rated size. Typically, inverters + are not sized to convert the full DC output under standard test + conditions (STC) as such conditions rarely occur in reality and + therefore unnecessarily add to the size/cost of the inverter. For a + system with a high DC to AC size ratio, during times when the + DC power output exceeds the inverter's rated DC input size, the inverter + limits the array's power output by increasing the DC operating voltage, + which moves the arrays operating point down its current-voltage (I-V) + curve. The default value of 1.1 is reasonable for most systems. A + typical range is 1.1 to 1.25, although some large-scale systems have + ratios of as high as 1.5. The optimal value depends on the system's + location, array orientation, and module cost. (Default: 1.1). + _run: Set to "True" to run the simulation to obtain annual loads. This can + also be the integer 2 to run the simulation while being able to see + the simulation process (with a batch window). + + Returns: + report: A report of the energy simulation run. + total_ac: A number for the total on-site produced alternating current (AC) + electricity in kWh. + ac_power: A data collection of all on-site produced electricity (kWh). This + represents the alternating current (AC) electricity coming out of + the inverter that processes all on-site power production. + generators: A list of names for each of the electricity generation objects that + were found among the connected _hb_objs. These names align with the + tota_dc output below as well as the dc_power data collections. + total_dc: A list of numbers for the direct current (DC) electricity produced + by each generator object in kWh. + dc_power: A list of data collections for the direct current (DC) electricity + produced by each on-site electricity generator (kWh). Each + photovoltaic object will have a separate data collection. +""" + +ghenv.Component.Name = 'HB Generation Loads' +ghenv.Component.NickName = 'GenLoads' +ghenv.Component.Message = '1.7.0' +ghenv.Component.Category = 'HB-Energy' +ghenv.Component.SubCategory = '5 :: Simulate' +ghenv.Component.AdditionalHelpFromDocStrings = '2' + +import os +import subprocess +import json +from collections import OrderedDict + +try: + from ladybug.futil import write_to_file_by_name, nukedir + from ladybug.sql import SQLiteResult +except ImportError as e: + raise ImportError('\nFailed to import ladybug:\n\t{}'.format(e)) + +try: + from honeybee.config import folders + from honeybee.model import Model +except ImportError as e: + raise ImportError('\nFailed to import honeybee:\n\t{}'.format(e)) + +try: + from honeybee_energy.simulation.parameter import SimulationParameter + from honeybee_energy.run import run_idf + from honeybee_energy.result.err import Err + from honeybee_energy.writer import energyplus_idf_version + from honeybee_energy.config import folders as energy_folders + from honeybee_energy.lib.constructions import opaque_construction_by_identifier +except ImportError as e: + raise ImportError('\nFailed to import honeybee_energy:\n\t{}'.format(e)) + +try: + from lbt_recipes.version import check_energyplus_version +except ImportError as e: + raise ImportError('\nFailed to import lbt_recipes:\n\t{}'.format(e)) + +try: + from ladybug_rhino.togeometry import to_vector2d + from ladybug_rhino.config import units_system, tolerance, angle_tolerance + 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)) + + +def serialize_data(data_dicts): + """Reserialize a list of HourlyCollection dictionaries.""" + return [HourlyCollection.from_dict(data) for data in data_dicts] + + +# List of all the output strings that will be requested +ac_out = 'Facility Total Produced Electricity Energy' +dc_out = 'Generator Produced DC Electricity Energy' +energy_output = (ac_out, dc_out) + + +if all_required_inputs(ghenv.Component) and _run: + # check the presence of energyplus and check that the version is compatible + check_energyplus_version() + + # create the Model from the _hb_objs + models = [obj.duplicate() for obj in _hb_objs if isinstance(obj, Model)] + other_objs = [obj.duplicate() for obj in _hb_objs if not isinstance(obj, Model)] + model = Model.from_objects('Generation_Loads', other_objs, units_system(), + tolerance, angle_tolerance) + for m in models: + model.add_model(m) + model.rooms_to_orphaned() + soil_constr = opaque_construction_by_identifier('Mud') + model.properties.energy.generate_ground_room(soil_constr) + + # set the inverter efficiency and size + if _inverter_eff_ is not None: + model.properties.energy.electric_load_center.inverter_efficiency = _inverter_eff_ + if _dc_to_ac_size_ is not None: + model.properties.energy.electric_load_center.inverter_dc_to_ac_size_ratio = _dc_to_ac_size_ + + # process the simulation folder name and the directory + directory = os.path.join(folders.default_simulation_folder, model.identifier) + sch_directory = os.path.join(directory, 'schedules') + nukedir(directory) # delete any existing files in the directory + + # create simulation parameters for the coarsest/fastest E+ sim possible + _sim_par_ = SimulationParameter() + _sim_par_.timestep = 6 + _sim_par_.shadow_calculation.solar_distribution = 'FullExteriorWithReflections' + _sim_par_.output.reporting_frequency = 'Hourly' + _sim_par_.output.add_electricity_generation() + _sim_par_.output.include_html = False + _sim_par_.simulation_control.do_zone_sizing = False + _sim_par_.simulation_control.do_system_sizing = False + _sim_par_.simulation_control.do_plant_sizing = False + + # set the north if it is not defaulted + if _north_ is not None: + try: + _sim_par_.north_vector = to_vector2d(_north_) + except AttributeError: # north angle instead of vector + _sim_par_.north_angle = float(_north_) + + # create the strings for simulation paramters and model + ver_str = energyplus_idf_version() if energy_folders.energyplus_version \ + is not None else energyplus_idf_version(compatibe_ep_version) + sim_par_str = _sim_par_.to_idf() + model_str = model.to.idf( + model, schedule_directory=sch_directory, patch_missing_adjacencies=True) + idf_str = '\n\n'.join([ver_str, sim_par_str, model_str]) + + # write the final string into an IDF + idf = os.path.join(directory, 'in.idf') + write_to_file_by_name(directory, 'in.idf', idf_str, True) + + # run the IDF through EnergyPlus + silent = True if _run == 1 else False + sql, zsz, rdd, html, err = run_idf(idf, _epw_file, silent=silent) + if sql is None and err is not None: # something went wrong; parse the errors + err_obj = Err(err) + print(err_obj.file_contents) + for error in err_obj.fatal_errors: + raise Exception(error) + + # parse the result sql and get the monthly data collections + if os.name == 'nt': # we are on windows; use IronPython like usual + sql_obj = SQLiteResult(sql) + ac_power = sql_obj.data_collections_by_output_name(ac_out) + dc_power = sql_obj.data_collections_by_output_name(dc_out) + else: # we are on Mac; sqlite3 module doesn't work in Mac IronPython + # Execute the honybee CLI to obtain the results via CPython + cmds = [folders.python_exe_path, '-m', 'honeybee_energy', 'result', + 'data-by-outputs', sql] + for outp in energy_output: + cmds.append('["{}"]'.format(outp)) + custom_env = os.environ.copy() + custom_env['PYTHONHOME'] = '' + process = subprocess.Popen(cmds, stdout=subprocess.PIPE, env=custom_env) + stdout = process.communicate() + data_coll_dicts = json.loads(stdout[0]) + ac_power = serialize_data(data_coll_dicts[0]) + dc_power = serialize_data(data_coll_dicts[1]) + + # give a warning if no generators were found in the results + if len(ac_power) == 0: + msg = 'No electricity generation objects were found in the connected _hb_objs.\n' \ + 'Try applying PV properties to some of the connected Shade geometries.' + print(msg) + give_warning(ghenv.Component, msg) + + # group the generator results by identifier + dc_dict = OrderedDict() + for g_data in dc_power: + gen_id = g_data.header.metadata['System'].split('..')[0] + g_data.header.metadata['System'] = gen_id + try: + dc_dict[gen_id] += g_data + except KeyError: + dc_dict[gen_id] = g_data + + # sum the results and ouput them + total_ac = [acp.total for acp in ac_power] + generators = list(dc_dict.keys()) + total_dc = [dcp.total for dcp in dc_dict.values()] + dc_power = [dcp for dcp in dc_dict.values()] diff --git a/honeybee_grasshopper_energy/src/HB Read Generation Result.py b/honeybee_grasshopper_energy/src/HB Read Generation Result.py index b662711..37c8eee 100644 --- a/honeybee_grasshopper_energy/src/HB Read Generation Result.py +++ b/honeybee_grasshopper_energy/src/HB Read Generation Result.py @@ -8,7 +8,7 @@ # @license AGPL-3.0-or-later """ -Parse any time series data from an energy simulation SQL result file. +Parse electricity generation results from an energy simulation SQL result file. - Args: @@ -39,7 +39,7 @@ ghenv.Component.Name = 'HB Read Generation Result' ghenv.Component.NickName = 'GenerationResult' -ghenv.Component.Message = '1.7.0' +ghenv.Component.Message = '1.7.1' ghenv.Component.Category = 'HB-Energy' ghenv.Component.SubCategory = '6 :: Result' ghenv.Component.AdditionalHelpFromDocStrings = '0' @@ -47,6 +47,7 @@ import os import subprocess import json +from collections import OrderedDict try: from ladybug.datacollection import HourlyContinuousCollection, \ @@ -141,3 +142,14 @@ def serialize_data(data_dicts): result_dict['production_surplus_sold'], result_dict['consumption_purchased'] ) + + # group the generator results by identifier + dc_dict = OrderedDict() + for g_data in dc_power: + gen_id = g_data.header.metadata['System'].split('..')[0] + g_data.header.metadata['System'] = gen_id + try: + dc_dict[gen_id] += g_data + except KeyError: + dc_dict[gen_id] = g_data + dc_power = [dcp for dcp in dc_dict.values()] diff --git a/honeybee_grasshopper_energy/user_objects/HB Generation Loads.ghuser b/honeybee_grasshopper_energy/user_objects/HB Generation Loads.ghuser new file mode 100644 index 0000000..d22d3cc Binary files /dev/null and b/honeybee_grasshopper_energy/user_objects/HB Generation Loads.ghuser differ diff --git a/honeybee_grasshopper_energy/user_objects/HB Read Generation Result.ghuser b/honeybee_grasshopper_energy/user_objects/HB Read Generation Result.ghuser index 2248bb2..a3ed9da 100644 Binary files a/honeybee_grasshopper_energy/user_objects/HB Read Generation Result.ghuser and b/honeybee_grasshopper_energy/user_objects/HB Read Generation Result.ghuser differ diff --git a/samples/photovoltaics.gh b/samples/photovoltaics.gh index 0c662ee..78d42a8 100644 Binary files a/samples/photovoltaics.gh and b/samples/photovoltaics.gh differ