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(simulate): Add a better error message when the input is not a Model #408

Merged
merged 1 commit into from Mar 18, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Binary file modified honeybee_grasshopper_energy/icon/HB Model to OSM.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions honeybee_grasshopper_energy/json/HB_Model_to_OSM.json
@@ -1,5 +1,5 @@
{
"version": "1.7.3",
"version": "1.7.4",
"nickname": "ModelToOSM",
"outputs": [
[
Expand Down Expand Up @@ -120,7 +120,7 @@
}
],
"subcategory": "5 :: Simulate",
"code": "\nimport sys\nimport os\nimport re\nimport json\n\ntry:\n from ladybug.futil import preparedir, nukedir\n from ladybug.epw import EPW\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug:\\n\\t{}'.format(e))\n\ntry:\n import honeybee.config as hb_config\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 to_openstudio_osw, run_osw, run_idf, \\\n output_energyplus_files\n from honeybee_energy.result.err import Err\n from honeybee_energy.result.osw import OSW\n from honeybee_energy.config import folders as energy_folders\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_openstudio_version\nexcept ImportError as e:\n raise ImportError('\\nFailed to import lbt_recipes:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs, give_warning\n from ladybug_{{cad}}.config import units_system\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_{{cad}}:\\n\\t{}'.format(e))\n\n\nif all_required_inputs(ghenv.Component) and _write:\n # check the presence of openstudio and check that the version is compatible\n check_openstudio_version()\n\n # process the simulation parameters\n if _sim_par_ is None:\n _sim_par_ = SimulationParameter()\n _sim_par_.output.add_zone_energy_use()\n _sim_par_.output.add_hvac_energy_use()\n _sim_par_.output.add_electricity_generation()\n else:\n _sim_par_ = _sim_par_.duplicate() # ensure input is not edited\n\n # assign design days from the DDY next to the EPW if there are None\n if len(_sim_par_.sizing_parameter.design_days) == 0:\n msg = None\n folder, epw_file_name = os.path.split(_epw_file)\n ddy_file = os.path.join(folder, epw_file_name.replace('.epw', '.ddy'))\n if os.path.isfile(ddy_file):\n try:\n _sim_par_.sizing_parameter.add_from_ddy_996_004(ddy_file)\n except AssertionError:\n msg = 'No ddy_file_ was input into the _sim_par_ sizing ' \\\n 'parameters\\n and no design days were found in the .ddy file '\\\n 'next to the _epw_file.'\n else:\n msg = 'No ddy_file_ was input into the _sim_par_ sizing parameters\\n' \\\n 'and no .ddy file was found next to the _epw_file.'\n if msg is not None:\n epw_obj = EPW(_epw_file)\n des_days = [epw_obj.approximate_design_day('WinterDesignDay'),\n epw_obj.approximate_design_day('SummerDesignDay')]\n _sim_par_.sizing_parameter.design_days = des_days\n msg = msg + '\\nDesign days were generated from the input _epw_file but this ' \\\n '\\nis not as accurate as design days from DDYs distributed with the EPW.'\n give_warning(ghenv.Component, msg)\n print(msg)\n\n # process the simulation folder name and the directory\n _folder_ = hb_config.folders.default_simulation_folder if _folder_ is None else _folder_\n clean_name = re.sub(r'[^.A-Za-z0-9_-]', '_', _model.display_name)\n directory = os.path.join(_folder_, clean_name, 'openstudio')\n\n # duplicate model to avoid mutating it as we edit it for energy simulation\n _model = _model.duplicate()\n # scale the model if the units are not meters\n if _model.units != 'Meters':\n _model.convert_to_units('Meters')\n # remove degenerate geometry within native E+ tolerance of 0.01 meters\n try:\n _model.remove_degenerate_geometry(0.01)\n except ValueError:\n error = 'Failed to remove degenerate Rooms.\\nYour Model units system is: {}. ' \\\n 'Is this correct?'.format(units_system())\n raise ValueError(error)\n # remove the HVAC from any Rooms lacking setpoints\n rem_msgs = _model.properties.energy.remove_hvac_from_no_setpoints()\n if rem_msgs is not None and len(rem_msgs) != 0:\n msg = '\\n'.join(rem_msgs)\n give_warning(ghenv.Component, msg)\n print(msg)\n\n # auto-assign stories if there are none since most OpenStudio measures need these\n if len(_model.stories) == 0 and len(_model.rooms) != 0:\n _model.assign_stories_by_floor_height()\n\n # delete any existing files in the directory and prepare it for simulation\n nukedir(directory, True)\n preparedir(directory)\n sch_directory = os.path.join(directory, 'schedules')\n preparedir(sch_directory)\n\n # write the model parameter JSONs\n model_dict = _model.to_dict(triangulate_sub_faces=True)\n _model.properties.energy.add_autocal_properties_to_dict(model_dict)\n model_json = os.path.join(directory, '{}.hbjson'.format(clean_name))\n if (sys.version_info < (3, 0)): # we need to manually encode it as UTF-8\n with open(model_json, 'wb') as fp:\n model_str = json.dumps(model_dict, ensure_ascii=False)\n fp.write(model_str.encode('utf-8'))\n else:\n with open(model_json, 'w', encoding='utf-8') as fp:\n model_str = json.dump(model_dict, fp, ensure_ascii=False)\n\n # write the simulation parameter JSONs\n sim_par_dict = _sim_par_.to_dict()\n sim_par_json = os.path.join(directory, 'simulation_parameter.json')\n with open(sim_par_json, 'w') as fp:\n json.dump(sim_par_dict, fp)\n\n # process any measures input to the component\n measures = None if len(measures_) == 0 or measures_[0] is None else measures_\n no_report_meas = True if measures is None else \\\n all(meas.type != 'ReportingMeasure' for meas in measures)\n str_inject = None if no_report_meas or add_str_ == [] or add_str_[0] is None \\\n else '\\n'.join(add_str_)\n\n # collect the two jsons for output and write out the osw file\n jsons = [model_json, sim_par_json]\n osw = to_openstudio_osw(\n directory, model_json, sim_par_json, additional_measures=measures,\n epw_file=_epw_file, schedule_directory=sch_directory,\n strings_to_inject=str_inject)\n\n # run the measure to translate the model JSON to an openstudio measure\n silent = True if run_ == 3 else False\n if run_ > 0 and not no_report_meas: # everything must run with OS CLI\n if run_ == 1: # simulate everything at once\n osm, idf = run_osw(osw, measures_only=False, silent=silent)\n sql, zsz, rdd, html, err = output_energyplus_files(os.path.dirname(idf))\n else: # remove reporting measure and give a warning\n m_to_remove = [m.identifier for m in measures if m.type == 'ReportingMeasure']\n with open(osw, 'r') as op:\n osw_data = json.load(op)\n s_to_remove = []\n for i, step in enumerate(osw_data['steps']):\n if step['measure_dir_name'] in m_to_remove:\n s_to_remove.append(i)\n for i in reversed(s_to_remove):\n osw_data['steps'].pop(i)\n with open(osw, 'wb') as fp:\n workflow_str = json.dumps(osw_data, indent=4, ensure_ascii=False)\n fp.write(workflow_str.encode('utf-8'))\n msg = 'The following were reporting measures and were not\\n' \\\n 'included in the OSW to avoid running the simulation:\\n{}'.format(\n '\\n'.join(m_to_remove))\n give_warning(ghenv.Component, msg)\n print(msg)\n osm, idf = run_osw(osw, silent=silent)\n elif run_ > 0: # no reporting measure; simulate separately from measure application\n osm, idf = run_osw(osw, silent=silent)\n # process the additional strings\n if len(add_str_) != 0 and add_str_[0] is not None and idf is not None:\n add_str = '\\n'.join(add_str_)\n with open(idf, \"a\") as idf_file:\n idf_file.write(add_str)\n if idf is None: # measures failed to run correctly; parse out.osw\n log_osw = OSW(os.path.join(directory, 'out.osw'))\n errors = []\n for error, tb in zip(log_osw.errors, log_osw.error_tracebacks):\n if 'Cannot create a surface' in error:\n error = 'Your {{Cad}} Model units system is: {}. ' \\\n 'Is this correct?\\n{}'.format(units_system(), error)\n print(tb)\n errors.append(error)\n raise Exception('Failed to run OpenStudio CLI:\\n{}'.format('\\n'.join(errors)))\n elif run_ in (1, 3): # run the resulting idf throught EnergyPlus\n sql, zsz, rdd, html, err = run_idf(idf, _epw_file, silent=silent)\n\n # parse the error log and report any warnings\n if run_ in (1, 3) and err is not None:\n err_obj = Err(err)\n print(err_obj.file_contents)\n for warn in err_obj.severe_errors:\n give_warning(ghenv.Component, warn)\n for error in err_obj.fatal_errors:\n raise Exception(error)\n",
"code": "\nimport sys\nimport os\nimport re\nimport json\n\ntry:\n from ladybug.futil import preparedir, nukedir\n from ladybug.epw import EPW\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug:\\n\\t{}'.format(e))\n\ntry:\n import honeybee.config as hb_config\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 to_openstudio_osw, run_osw, run_idf, \\\n output_energyplus_files\n from honeybee_energy.result.err import Err\n from honeybee_energy.result.osw import OSW\n from honeybee_energy.config import folders as energy_folders\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_openstudio_version\nexcept ImportError as e:\n raise ImportError('\\nFailed to import lbt_recipes:\\n\\t{}'.format(e))\n\ntry:\n from ladybug_{{cad}}.{{plugin}} import all_required_inputs, give_warning\n from ladybug_{{cad}}.config import units_system\nexcept ImportError as e:\n raise ImportError('\\nFailed to import ladybug_{{cad}}:\\n\\t{}'.format(e))\n\n\nif all_required_inputs(ghenv.Component) and _write:\n # check the presence of openstudio and check that the version is compatible\n check_openstudio_version()\n assert isinstance(_model, Model), \\\n 'Expected Honeybee Model for _model input. Got {}.'.format(type(_model))\n\n # process the simulation parameters\n if _sim_par_ is None:\n _sim_par_ = SimulationParameter()\n _sim_par_.output.add_zone_energy_use()\n _sim_par_.output.add_hvac_energy_use()\n _sim_par_.output.add_electricity_generation()\n else:\n _sim_par_ = _sim_par_.duplicate() # ensure input is not edited\n\n # assign design days from the DDY next to the EPW if there are None\n if len(_sim_par_.sizing_parameter.design_days) == 0:\n msg = None\n folder, epw_file_name = os.path.split(_epw_file)\n ddy_file = os.path.join(folder, epw_file_name.replace('.epw', '.ddy'))\n if os.path.isfile(ddy_file):\n try:\n _sim_par_.sizing_parameter.add_from_ddy_996_004(ddy_file)\n except AssertionError:\n msg = 'No ddy_file_ was input into the _sim_par_ sizing ' \\\n 'parameters\\n and no design days were found in the .ddy file '\\\n 'next to the _epw_file.'\n else:\n msg = 'No ddy_file_ was input into the _sim_par_ sizing parameters\\n' \\\n 'and no .ddy file was found next to the _epw_file.'\n if msg is not None:\n epw_obj = EPW(_epw_file)\n des_days = [epw_obj.approximate_design_day('WinterDesignDay'),\n epw_obj.approximate_design_day('SummerDesignDay')]\n _sim_par_.sizing_parameter.design_days = des_days\n msg = msg + '\\nDesign days were generated from the input _epw_file but this ' \\\n '\\nis not as accurate as design days from DDYs distributed with the EPW.'\n give_warning(ghenv.Component, msg)\n print(msg)\n\n # process the simulation folder name and the directory\n _folder_ = hb_config.folders.default_simulation_folder if _folder_ is None else _folder_\n clean_name = re.sub(r'[^.A-Za-z0-9_-]', '_', _model.display_name)\n directory = os.path.join(_folder_, clean_name, 'openstudio')\n\n # duplicate model to avoid mutating it as we edit it for energy simulation\n _model = _model.duplicate()\n # scale the model if the units are not meters\n if _model.units != 'Meters':\n _model.convert_to_units('Meters')\n # remove degenerate geometry within native E+ tolerance of 0.01 meters\n try:\n _model.remove_degenerate_geometry(0.01)\n except ValueError:\n error = 'Failed to remove degenerate Rooms.\\nYour Model units system is: {}. ' \\\n 'Is this correct?'.format(units_system())\n raise ValueError(error)\n # remove the HVAC from any Rooms lacking setpoints\n rem_msgs = _model.properties.energy.remove_hvac_from_no_setpoints()\n if rem_msgs is not None and len(rem_msgs) != 0:\n msg = '\\n'.join(rem_msgs)\n give_warning(ghenv.Component, msg)\n print(msg)\n\n # auto-assign stories if there are none since most OpenStudio measures need these\n if len(_model.stories) == 0 and len(_model.rooms) != 0:\n _model.assign_stories_by_floor_height()\n\n # delete any existing files in the directory and prepare it for simulation\n nukedir(directory, True)\n preparedir(directory)\n sch_directory = os.path.join(directory, 'schedules')\n preparedir(sch_directory)\n\n # write the model parameter JSONs\n model_dict = _model.to_dict(triangulate_sub_faces=True)\n _model.properties.energy.add_autocal_properties_to_dict(model_dict)\n model_json = os.path.join(directory, '{}.hbjson'.format(clean_name))\n if (sys.version_info < (3, 0)): # we need to manually encode it as UTF-8\n with open(model_json, 'wb') as fp:\n model_str = json.dumps(model_dict, ensure_ascii=False)\n fp.write(model_str.encode('utf-8'))\n else:\n with open(model_json, 'w', encoding='utf-8') as fp:\n model_str = json.dump(model_dict, fp, ensure_ascii=False)\n\n # write the simulation parameter JSONs\n sim_par_dict = _sim_par_.to_dict()\n sim_par_json = os.path.join(directory, 'simulation_parameter.json')\n with open(sim_par_json, 'w') as fp:\n json.dump(sim_par_dict, fp)\n\n # process any measures input to the component\n measures = None if len(measures_) == 0 or measures_[0] is None else measures_\n no_report_meas = True if measures is None else \\\n all(meas.type != 'ReportingMeasure' for meas in measures)\n str_inject = None if no_report_meas or add_str_ == [] or add_str_[0] is None \\\n else '\\n'.join(add_str_)\n\n # collect the two jsons for output and write out the osw file\n jsons = [model_json, sim_par_json]\n osw = to_openstudio_osw(\n directory, model_json, sim_par_json, additional_measures=measures,\n epw_file=_epw_file, schedule_directory=sch_directory,\n strings_to_inject=str_inject)\n\n # run the measure to translate the model JSON to an openstudio measure\n silent = True if run_ == 3 else False\n if run_ > 0 and not no_report_meas: # everything must run with OS CLI\n if run_ == 1: # simulate everything at once\n osm, idf = run_osw(osw, measures_only=False, silent=silent)\n sql, zsz, rdd, html, err = output_energyplus_files(os.path.dirname(idf))\n else: # remove reporting measure and give a warning\n m_to_remove = [m.identifier for m in measures if m.type == 'ReportingMeasure']\n with open(osw, 'r') as op:\n osw_data = json.load(op)\n s_to_remove = []\n for i, step in enumerate(osw_data['steps']):\n if step['measure_dir_name'] in m_to_remove:\n s_to_remove.append(i)\n for i in reversed(s_to_remove):\n osw_data['steps'].pop(i)\n with open(osw, 'wb') as fp:\n workflow_str = json.dumps(osw_data, indent=4, ensure_ascii=False)\n fp.write(workflow_str.encode('utf-8'))\n msg = 'The following were reporting measures and were not\\n' \\\n 'included in the OSW to avoid running the simulation:\\n{}'.format(\n '\\n'.join(m_to_remove))\n give_warning(ghenv.Component, msg)\n print(msg)\n osm, idf = run_osw(osw, silent=silent)\n elif run_ > 0: # no reporting measure; simulate separately from measure application\n osm, idf = run_osw(osw, silent=silent)\n # process the additional strings\n if len(add_str_) != 0 and add_str_[0] is not None and idf is not None:\n add_str = '\\n'.join(add_str_)\n with open(idf, \"a\") as idf_file:\n idf_file.write(add_str)\n if idf is None: # measures failed to run correctly; parse out.osw\n log_osw = OSW(os.path.join(directory, 'out.osw'))\n errors = []\n for error, tb in zip(log_osw.errors, log_osw.error_tracebacks):\n if 'Cannot create a surface' in error:\n error = 'Your {{Cad}} Model units system is: {}. ' \\\n 'Is this correct?\\n{}'.format(units_system(), error)\n print(tb)\n errors.append(error)\n raise Exception('Failed to run OpenStudio CLI:\\n{}'.format('\\n'.join(errors)))\n elif run_ in (1, 3): # run the resulting idf throught EnergyPlus\n sql, zsz, rdd, html, err = run_idf(idf, _epw_file, silent=silent)\n\n # parse the error log and report any warnings\n if run_ in (1, 3) and err is not None:\n err_obj = Err(err)\n print(err_obj.file_contents)\n for warn in err_obj.severe_errors:\n give_warning(ghenv.Component, warn)\n for error in err_obj.fatal_errors:\n raise Exception(error)\n",
"category": "HB-Energy",
"name": "HB Model to OSM",
"description": "Write a honeybee Model to an OSM file (OpenStudio Model), which can then be translated\nto an IDF file and then run through EnergyPlus.\n-"
Expand Down
5 changes: 4 additions & 1 deletion honeybee_grasshopper_energy/src/HB Model to OSM.py
Expand Up @@ -74,7 +74,7 @@

ghenv.Component.Name = 'HB Model to OSM'
ghenv.Component.NickName = 'ModelToOSM'
ghenv.Component.Message = '1.7.3'
ghenv.Component.Message = '1.7.4'
ghenv.Component.Category = 'HB-Energy'
ghenv.Component.SubCategory = '5 :: Simulate'
ghenv.Component.AdditionalHelpFromDocStrings = '1'
Expand All @@ -92,6 +92,7 @@

try:
import honeybee.config as hb_config
from honeybee.model import Model
except ImportError as e:
raise ImportError('\nFailed to import honeybee:\n\t{}'.format(e))

Expand Down Expand Up @@ -120,6 +121,8 @@
if all_required_inputs(ghenv.Component) and _write:
# check the presence of openstudio and check that the version is compatible
check_openstudio_version()
assert isinstance(_model, Model), \
'Expected Honeybee Model for _model input. Got {}.'.format(type(_model))

# process the simulation parameters
if _sim_par_ is None:
Expand Down
Binary file modified honeybee_grasshopper_energy/user_objects/HB Model to OSM.ghuser
Binary file not shown.