Skip to content

Commit

Permalink
fix(simulate): Add extra removal of vertices at model tolerance
Browse files Browse the repository at this point in the history
It seems that this is needed for certain models that were built at fine tolerances and have lots of duplicated vertices.
  • Loading branch information
chriswmackey authored and Chris Mackey committed Feb 9, 2023
1 parent c9f8f1e commit 5ef9fa1
Show file tree
Hide file tree
Showing 5 changed files with 11 additions and 3 deletions.
Binary file modified honeybee_grasshopper_energy/icon/HB Model to OSM.png
Loading
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "1.6.0",
"version": "1.6.1",
"nickname": "ModelToOSM",
"outputs": [
[
Expand Down Expand Up @@ -120,7 +120,7 @@
}
],
"subcategory": "5 :: Simulate",
"code": "\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 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\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 try:\n with open(model_json, 'w') as fp:\n json.dump(model_dict, fp)\n except UnicodeDecodeError: # non-unicode character in display_name\n with open(model_json, 'w') as fp:\n 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 osm, idf = run_osw(osw, measures_only=False, silent=silent)\n sql, zsz, rdd, html, err = output_energyplus_files(os.path.dirname(idf))\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 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 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 if _model.tolerance < 0.01:\n for room in _model.rooms:\n try:\n room.remove_colinear_vertices_envelope(\n tolerance=_model.tolerance, delete_degenerate=True)\n except AssertionError as e: # room removed; likely wrong units\n error = 'Failed to remove degenerate geometry.\\n{}'.format(e)\n raise ValueError(error)\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\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 try:\n with open(model_json, 'w') as fp:\n json.dump(model_dict, fp)\n except UnicodeDecodeError: # non-unicode character in display_name\n with open(model_json, 'w') as fp:\n 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 osm, idf = run_osw(osw, measures_only=False, silent=silent)\n sql, zsz, rdd, html, err = output_energyplus_files(os.path.dirname(idf))\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
10 changes: 9 additions & 1 deletion honeybee_grasshopper_energy/src/HB Model to OSM.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@

ghenv.Component.Name = 'HB Model to OSM'
ghenv.Component.NickName = 'ModelToOSM'
ghenv.Component.Message = '1.6.0'
ghenv.Component.Message = '1.6.1'
ghenv.Component.Category = 'HB-Energy'
ghenv.Component.SubCategory = '5 :: Simulate'
ghenv.Component.AdditionalHelpFromDocStrings = '1'
Expand Down Expand Up @@ -165,6 +165,14 @@
_model.convert_to_units('Meters')
# remove degenerate geometry within native E+ tolerance of 0.01 meters
try:
if _model.tolerance < 0.01:
for room in _model.rooms:
try:
room.remove_colinear_vertices_envelope(
tolerance=_model.tolerance, delete_degenerate=True)
except AssertionError as e: # room removed; likely wrong units
error = 'Failed to remove degenerate geometry.\n{}'.format(e)
raise ValueError(error)
_model.remove_degenerate_geometry(0.01)
except ValueError:
error = 'Failed to remove degenerate Rooms.\nYour Model units system is: {}. ' \
Expand Down
Binary file modified honeybee_grasshopper_energy/user_objects/HB Model to OSM.ghuser
Binary file not shown.
Binary file modified samples/shoe_box_energy_model.gh
Binary file not shown.

0 comments on commit 5ef9fa1

Please sign in to comment.