Skip to content

Commit

Permalink
feat(simulate): Add a component to estimate electricity production
Browse files Browse the repository at this point in the history
  • Loading branch information
chriswmackey authored and Chris Mackey committed Jan 9, 2024
1 parent 2b9fed3 commit d94b71f
Show file tree
Hide file tree
Showing 8 changed files with 345 additions and 5 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
92 changes: 92 additions & 0 deletions honeybee_grasshopper_energy/json/HB_Generation_Loads.json
Original file line number Diff line number Diff line change
@@ -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-"
}
Loading

0 comments on commit d94b71f

Please sign in to comment.