From c4f43a6436ea9f3d24205bc4f27724195eab191f Mon Sep 17 00:00:00 2001 From: Chris Mackey Date: Tue, 5 Nov 2024 13:52:04 -0800 Subject: [PATCH] fix(simulate): Use better error message when OpenStudio fails to start --- .../icon/HB Model to OSM.png | Bin 1515 -> 1515 bytes .../json/HB_Model_to_OSM.json | 4 ++-- .../src/HB Model to OSM.py | 10 ++++++++-- .../user_objects/HB Model to OSM.ghuser | Bin 9885 -> 9975 bytes 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/honeybee_grasshopper_energy/icon/HB Model to OSM.png b/honeybee_grasshopper_energy/icon/HB Model to OSM.png index 4f679bf20b9b87ba977ee30bcb355c1716666349..1ee1d4e692b0b7eb52ad2c6a9d6da6de6f5f131f 100644 GIT binary patch delta 1461 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 P00000NkvXXu0mjf;#0mM diff --git a/honeybee_grasshopper_energy/json/HB_Model_to_OSM.json b/honeybee_grasshopper_energy/json/HB_Model_to_OSM.json index bcc57c2..e30b7cc 100644 --- a/honeybee_grasshopper_energy/json/HB_Model_to_OSM.json +++ b/honeybee_grasshopper_energy/json/HB_Model_to_OSM.json @@ -1,5 +1,5 @@ { - "version": "1.8.0", + "version": "1.8.1", "nickname": "ModelToOSM", "outputs": [ [ @@ -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\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", + "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 try:\n log_osw = OSW(os.path.join(directory, 'out.osw'))\n except FileNotFoundError:\n msg = 'OpenStudio failed to initialize.\\nCheck that you can run ' \\\n 'the OpenStudio executable at: {}'.format(\n energy_folders.openstudio_exe)\n raise Exception(msg)\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-" diff --git a/honeybee_grasshopper_energy/src/HB Model to OSM.py b/honeybee_grasshopper_energy/src/HB Model to OSM.py index df6e758..2673e72 100644 --- a/honeybee_grasshopper_energy/src/HB Model to OSM.py +++ b/honeybee_grasshopper_energy/src/HB Model to OSM.py @@ -74,7 +74,7 @@ ghenv.Component.Name = 'HB Model to OSM' ghenv.Component.NickName = 'ModelToOSM' -ghenv.Component.Message = '1.8.0' +ghenv.Component.Message = '1.8.1' ghenv.Component.Category = 'HB-Energy' ghenv.Component.SubCategory = '5 :: Simulate' ghenv.Component.AdditionalHelpFromDocStrings = '1' @@ -257,7 +257,13 @@ with open(idf, "a") as idf_file: idf_file.write(add_str) if idf is None: # measures failed to run correctly; parse out.osw - log_osw = OSW(os.path.join(directory, 'out.osw')) + try: + log_osw = OSW(os.path.join(directory, 'out.osw')) + except FileNotFoundError: + msg = 'OpenStudio failed to initialize.\nCheck that you can run ' \ + 'the OpenStudio executable at: {}'.format( + energy_folders.openstudio_exe) + raise Exception(msg) errors = [] for error, tb in zip(log_osw.errors, log_osw.error_tracebacks): if 'Cannot create a surface' in error: diff --git a/honeybee_grasshopper_energy/user_objects/HB Model to OSM.ghuser b/honeybee_grasshopper_energy/user_objects/HB Model to OSM.ghuser index 2317dd3eb6ef9c513e86b2a1220ab507161e8c96..44e67c723681599ffff600316201efc84a71872b 100644 GIT binary patch literal 9975 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 P1O&)5?VLRReG2