def validate_sim_par(sim_par_json): """Validate all properties of a SimulationParameter JSON against the Honeybee schema. \b Args: sim_par_json: Full path to a SimulationParameter JSON file. """ try: # first check the JSON against the OpenAPI specification click.echo('Validating SimulationParameter JSON ...') schema_simulation_parameter.SimulationParameter.parse_file( sim_par_json) click.echo('Pydantic validation passed.') # re-serialize to make sure no errors are found in re-serialization with open(sim_par_json) as json_file: data = json.load(json_file) SimulationParameter.from_dict(data) click.echo('Python re-serialization passed.') # if we made it to this point, report that the object is valid click.echo('Congratulations! Your SimulationParameter JSON is valid!') except Exception as e: _logger.exception( 'SimulationParameter validation failed.\n{}'.format(e)) sys.exit(1) else: sys.exit(0)
def dict_to_simulation(sim_dict, raise_exception=True): """Get a Python object of any Simulation object from a dictionary. Args: sim_dict: A dictionary of any Honeybee energy simulation object. Note that this should be a non-abridged dictionary to be valid. raise_exception: Boolean to note whether an excpetion should be raised if the object is not identified as a simulation object. Default: True. Returns: A Python object derived from the input sim_dict. """ try: # get the type key from the dictionary sim_type = sim_dict['type'] except KeyError: raise ValueError('Simulation dictionary lacks required "type" key.') if sim_type == 'SimulationControl': return SimulationControl.from_dict(sim_dict) elif sim_type == 'RunPeriod': return RunPeriod.from_dict(sim_dict) elif sim_type == 'DaylightSavingTime': return DaylightSavingTime.from_dict(sim_dict) elif sim_type == 'ShadowCalculation': return ShadowCalculation.from_dict(sim_dict) elif sim_type == 'SizingParameter': return SizingParameter.from_dict(sim_dict) elif sim_type == 'SimulationOutput': return SimulationOutput.from_dict(sim_dict) elif sim_type == 'SimulationParameter': return SimulationParameter.from_dict(sim_dict) elif raise_exception: raise ValueError( '{} is not a recognized energy Simulation type'.format(sim_type))
def test_comfort_sim_par(): """Test the comfort_sim_par command.""" runner = CliRunner() ddy_path = './tests/ddy/chicago.ddy' result = runner.invoke(comfort_sim_par, [ddy_path]) assert result.exit_code == 0 simpar_dict = json.loads(result.output) sim_par = SimulationParameter.from_dict(simpar_dict) assert 'Zone Mean Air Temperature' in sim_par.output.outputs assert 'Surface Inside Face Temperature' in sim_par.output.outputs
def test_default_sim_par(): """Test the default_sim_par command.""" runner = CliRunner() ddy_path = './tests/ddy/chicago.ddy' result = runner.invoke(default_sim_par, [ddy_path]) assert result.exit_code == 0 simpar_dict = json.loads(result.output) sim_par = SimulationParameter.from_dict(simpar_dict) assert 'Zone Ideal Loads Supply Air Total Cooling Energy' in sim_par.output.outputs assert 'Chiller Electricity Energy' in sim_par.output.outputs
def test_load_balance_sim_par(): """Test the load_balance_sim_par command.""" runner = CliRunner() ddy_path = './tests/ddy/chicago.ddy' result = runner.invoke(load_balance_sim_par, [ddy_path, '--load-type', 'Sensible']) assert result.exit_code == 0 simpar_dict = json.loads(result.output) sim_par = SimulationParameter.from_dict(simpar_dict) assert 'Zone Ideal Loads Supply Air Sensible Cooling Energy' \ in sim_par.output.outputs assert 'Zone People Sensible Heating Energy' in sim_par.output.outputs
def test_custom_sim_par(): """Test the custom_sim_par command.""" runner = CliRunner() ddy_path = './tests/ddy/chicago.ddy' result = runner.invoke( custom_sim_par, [ddy_path, 'Surface Window Transmitted Beam Solar Radiation Energy']) assert result.exit_code == 0 simpar_dict = json.loads(result.output) sim_par = SimulationParameter.from_dict(simpar_dict) assert 'Surface Window Transmitted Beam Solar Radiation Energy' in sim_par.output.outputs
def model_to_idf(model_json, sim_par_json, additional_str, output_file): """Translate a Model JSON file to an IDF using direct-to-idf translators. \n The resulting IDF should be simulate-able but not all Model properties might make it into the IDF given that the direct-to-idf translators are used. \n Args: model_json: Full path to a Model JSON file. """ try: # check that the model JSON is there assert os.path.isfile(model_json), \ 'No Model JSON file found at {}.'.format(model_json) # check that the simulation parameters are there and load them if sim_par_json is not None: assert os.path.isfile(sim_par_json), \ 'No simulation parameter file found at {}.'.format(sim_par_json) with open(sim_par_json) as json_file: data = json.load(json_file) sim_par = SimulationParameter.from_dict(data) else: sim_par = SimulationParameter() sim_par.output.add_zone_energy_use() sim_par.output.add_hvac_energy_use() # re-serialize the Model to Python with open(model_json) as json_file: data = json.load(json_file) model = Model.from_dict(data) # set the schedule directory in case it is needed sch_path = os.path.abspath(model_json) if 'stdout' in str(output_file) \ else os.path.abspath(str(output_file)) sch_directory = os.path.join(os.path.split(sch_path)[0], 'schedules') # create the strings for simulation paramters and model ver_str = energyplus_idf_version() if folders.energyplus_version \ is not None else energyplus_idf_version((9, 2, 0)) sim_par_str = sim_par.to_idf() model_str = model.to.idf(model, schedule_directory=sch_directory) idf_str = '\n\n'.join( [ver_str, sim_par_str, model_str, additional_str]) # write out the IDF file output_file.write(idf_str) except Exception as e: _logger.exception('Model translation failed.\n{}'.format(e)) sys.exit(1) else: sys.exit(0)
def model_to_idf(model_json, sim_par_json, additional_str, output_file): """Translate a Model JSON file to an IDF using direct-to-idf translators. If the model contains a feature that is not translate-able through direct-to-idf translators, an exception will be raised. \b Args: model_json: Full path to a Model JSON file. """ try: # load simulation parameters or generate default ones if sim_par_json is not None: with open(sim_par_json) as json_file: data = json.load(json_file) sim_par = SimulationParameter.from_dict(data) else: sim_par = SimulationParameter() sim_par.output.add_zone_energy_use() sim_par.output.add_hvac_energy_use() # re-serialize the Model to Python with open(model_json) as json_file: data = json.load(json_file) model = Model.from_dict(data) # set the schedule directory in case it is needed sch_path = os.path.abspath(model_json) if 'stdout' in str(output_file) \ else os.path.abspath(str(output_file)) sch_directory = os.path.join(os.path.split(sch_path)[0], 'schedules') # create the strings for simulation paramters and model ver_str = energyplus_idf_version() if folders.energyplus_version \ is not None else '' sim_par_str = sim_par.to_idf() model_str = model.to.idf(model, schedule_directory=sch_directory) idf_str = '\n\n'.join( [ver_str, sim_par_str, model_str, additional_str]) # write out the IDF file output_file.write(idf_str) except Exception as e: _logger.exception('Model translation failed.\n{}'.format(e)) sys.exit(1) else: sys.exit(0)
def test_simulation_parameter_dict_methods(): """Test the to/from dict methods.""" sim_par = SimulationParameter() output = SimulationOutput() output.add_zone_energy_use() sim_par.output = output run_period = RunPeriod(Date(1, 1), Date(6, 21)) sim_par.run_period = run_period sim_par.timestep = 4 sim_control_alt = SimulationControl(run_for_sizing_periods=True, run_for_run_periods=False) sim_par.simulation_control = sim_control_alt shadow_calc_alt = ShadowCalculation(calculation_frequency=20) sim_par.shadow_calculation = shadow_calc_alt sizing_alt = SizingParameter(None, 1, 1) relative_path = './tests/ddy/chicago.ddy' sizing_alt.add_from_ddy_996_004(relative_path) sim_par.sizing_parameter = sizing_alt sim_par_dict = sim_par.to_dict() new_sim_par = SimulationParameter.from_dict(sim_par_dict) new_sim_par.sizing_parameter.apply_location(sizing_alt[0].location) assert new_sim_par == sim_par assert sim_par_dict == new_sim_par.to_dict()
def simulate_model(model_json, epw_file, sim_par_json, base_osw, folder, check_model, log_file): """Simulate a Model JSON file in EnergyPlus. \b Args: model_json: Full path to a Model JSON file. epw_file: Full path to an .epw file. """ try: # get a ddy variable that might get used later epw_folder, epw_file_name = os.path.split(epw_file) ddy_file = os.path.join(epw_folder, epw_file_name.replace('.epw', '.ddy')) # set the default folder to the default if it's not specified if folder is None: proj_name = \ os.path.basename(model_json).replace('.json', '').replace('.hbjson', '') folder = os.path.join(folders.default_simulation_folder, proj_name, 'OpenStudio') preparedir(folder, remove_content=False) # process the simulation parameters and write new ones if necessary def ddy_from_epw(epw_file, sim_par): """Produce a DDY from an EPW file.""" epw_obj = EPW(epw_file) des_days = [ epw_obj.approximate_design_day('WinterDesignDay'), epw_obj.approximate_design_day('SummerDesignDay') ] sim_par.sizing_parameter.design_days = des_days def write_sim_par(sim_par): """Write simulation parameter object to a JSON.""" sim_par_dict = sim_par.to_dict() sp_json = os.path.abspath( os.path.join(folder, 'simulation_parameter.json')) with open(sp_json, 'w') as fp: json.dump(sim_par_dict, fp) return sp_json if sim_par_json is None: # generate some default simulation parameters sim_par = SimulationParameter() sim_par.output.add_zone_energy_use() sim_par.output.add_hvac_energy_use() else: with open(sim_par_json) as json_file: data = json.load(json_file) sim_par = SimulationParameter.from_dict(data) if len(sim_par.sizing_parameter.design_days) == 0 and os.path.isfile( ddy_file): try: sim_par.sizing_parameter.add_from_ddy_996_004(ddy_file) except AssertionError: # no design days within the DDY file ddy_from_epw(epw_file, sim_par) elif len(sim_par.sizing_parameter.design_days) == 0: ddy_from_epw(epw_file, sim_par) sim_par_json = write_sim_par(sim_par) # run the Model re-serialization and check if specified if check_model: model_json = measure_compatible_model_json(model_json, folder) # Write the osw file to translate the model to osm osw = to_openstudio_osw(folder, model_json, sim_par_json, base_osw=base_osw, epw_file=epw_file) # run the measure to translate the model JSON to an openstudio measure if osw is not None and os.path.isfile(osw): gen_files = [osw] if base_osw is None: # separate the OS CLI run from the E+ run osm, idf = run_osw(osw) # run the resulting idf through EnergyPlus if idf is not None and os.path.isfile(idf): gen_files.extend([osm, idf]) sql, eio, rdd, html, err = run_idf(idf, epw_file) if err is not None and os.path.isfile(err): gen_files.extend([sql, eio, rdd, html, err]) else: raise Exception('Running EnergyPlus failed.') else: raise Exception('Running OpenStudio CLI failed.') else: # run the whole simulation with the OpenStudio CLI osm, idf = run_osw(osw, measures_only=False) if idf is not None and os.path.isfile(idf): gen_files.extend([osm, idf]) else: raise Exception('Running OpenStudio CLI failed.') sql, eio, rdd, html, err = output_energyplus_files( os.path.dirname(idf)) if os.path.isfile(err): gen_files.extend([sql, eio, rdd, html, err]) else: raise Exception('Running EnergyPlus failed.') log_file.write(json.dumps(gen_files)) else: raise Exception('Writing OSW file failed.') except Exception as e: _logger.exception('Model simulation failed.\n{}'.format(e)) sys.exit(1) else: sys.exit(0)
def simulate_model(model_json, epw_file, sim_par_json, base_osw, folder, check_model, log_file): """Simulate a Model JSON file in EnergyPlus. \n Args: model_json: Full path to a Model JSON file.\n epw_file: Full path to an .epw file. """ try: # check that the model JSON and the EPW file is there assert os.path.isfile(model_json), \ 'No Model JSON file found at {}.'.format(model_json) assert os.path.isfile(epw_file), \ 'No EPW file found at {}.'.format(epw_file) # ddy variable that might get used later epw_folder, epw_file_name = os.path.split(epw_file) ddy_file = os.path.join(epw_folder, epw_file_name.replace('.epw', '.ddy')) # set the default folder to the default if it's not specified if folder is None: proj_name = os.path.basename(model_json).replace('.json', '') folder = os.path.join(folders.default_simulation_folder, proj_name, 'OpenStudio') preparedir(folder, remove_content=False) # process the simulation parameters and write new ones if necessary def write_sim_par(sim_par): """Write simulation parameter object to a JSON.""" sim_par_dict = sim_par.to_dict() sp_json = os.path.abspath( os.path.join(folder, 'simulation_parameter.json')) with open(sp_json, 'w') as fp: json.dump(sim_par_dict, fp) return sp_json if sim_par_json is None: # generate some default simulation parameters sim_par = SimulationParameter() sim_par.output.add_zone_energy_use() sim_par.output.add_hvac_energy_use() if os.path.isfile(ddy_file): sim_par.sizing_parameter.add_from_ddy_996_004(ddy_file) else: raise ValueError( 'No sim-par-json was input and there is no .ddy file next to ' 'the .epw.\nAt least one of these two cirtieria must be satisfied ' 'for a successful simulation.') sim_par_json = write_sim_par(sim_par) else: assert os.path.isfile(sim_par_json), \ 'No simulation parameter file found at {}.'.format(sim_par_json) with open(sim_par_json) as json_file: data = json.load(json_file) sim_par = SimulationParameter.from_dict(data) if len(sim_par.sizing_parameter.design_days ) == 0 and os.path.isfile(ddy_file): sim_par.sizing_parameter.add_from_ddy_996_004(ddy_file) sim_par_json = write_sim_par(sim_par) elif len(sim_par.sizing_parameter.design_days) == 0: raise ValueError( 'No design days were found in the input sim-par-json and there is ' 'no .ddy file next to the .epw.\nAt least one of these two cirtieria ' 'must be satisfied for a successful simulation.') # run the Model re-serialization and check if specified if check_model: model_json = measure_compatible_model_json(model_json, folder) # Write the osw file to translate the model to osm osw = to_openstudio_osw(folder, model_json, sim_par_json, base_osw=base_osw, epw_file=epw_file) # run the measure to translate the model JSON to an openstudio measure if osw is not None and os.path.isfile(osw): gen_files = [osw] if base_osw is None: # separate the OS CLI run from the E+ run osm, idf = run_osw(osw) # run the resulting idf through EnergyPlus if idf is not None and os.path.isfile(idf): gen_files.extend([osm, idf]) sql, eio, rdd, html, err = run_idf(idf, epw_file) if err is not None and os.path.isfile(err): gen_files.extend([sql, eio, rdd, html, err]) else: raise Exception('Running EnergyPlus failed.') else: raise Exception('Running OpenStudio CLI failed.') else: # run the whole simulation with the OpenStudio CLI osm, idf = run_osw(osw, measures_only=False) if idf is not None and os.path.isfile(idf): gen_files.extend([osm, idf]) else: raise Exception('Running OpenStudio CLI failed.') sql, eio, rdd, html, err = output_energyplus_files( os.path.dirname(idf)) if os.path.isfile(err): gen_files.extend([sql, eio, rdd, html, err]) else: raise Exception('Running EnergyPlus failed.') log_file.write(json.dumps(gen_files)) else: raise Exception('Writing OSW file failed.') except Exception as e: _logger.exception('Model simulation failed.\n{}'.format(e)) sys.exit(1) else: sys.exit(0)
def simulate_model(model_json, epw_file, sim_par_json, obj_per_model, multiplier, no_plenum, no_cap, shade_dist, base_osw, folder, log_file): """Simulate a Dragonfly Model JSON file in EnergyPlus. \b Args: model_json: Full path to a Dragonfly Model JSON file. epw_file: Full path to an .epw file. """ try: # get a ddy variable that might get used later epw_folder, epw_file_name = os.path.split(epw_file) ddy_file = os.path.join(epw_folder, epw_file_name.replace('.epw', '.ddy')) # set the default folder to the default if it's not specified if folder is None: proj_name = \ os.path.basename(model_json).replace('.json', '').replace('.dfjson', '') folder = os.path.join(folders.default_simulation_folder, proj_name, 'OpenStudio') preparedir(folder, remove_content=False) # process the simulation parameters and write new ones if necessary def ddy_from_epw(epw_file, sim_par): """Produce a DDY from an EPW file.""" epw_obj = EPW(epw_file) des_days = [ epw_obj.approximate_design_day('WinterDesignDay'), epw_obj.approximate_design_day('SummerDesignDay') ] sim_par.sizing_parameter.design_days = des_days def write_sim_par(sim_par): """Write simulation parameter object to a JSON.""" sim_par_dict = sim_par.to_dict() sp_json = os.path.abspath( os.path.join(folder, 'simulation_parameter.json')) with open(sp_json, 'w') as fp: json.dump(sim_par_dict, fp) return sp_json if sim_par_json is None: # generate some default simulation parameters sim_par = SimulationParameter() sim_par.output.add_zone_energy_use() sim_par.output.add_hvac_energy_use() else: with open(sim_par_json) as json_file: data = json.load(json_file) sim_par = SimulationParameter.from_dict(data) if len(sim_par.sizing_parameter.design_days) == 0 and os.path.isfile( ddy_file): try: sim_par.sizing_parameter.add_from_ddy_996_004(ddy_file) except AssertionError: # no design days within the DDY file ddy_from_epw(epw_file, sim_par) elif len(sim_par.sizing_parameter.design_days) == 0: ddy_from_epw(epw_file, sim_par) sim_par_json = write_sim_par(sim_par) # re-serialize the Dragonfly Model with open(model_json) as json_file: data = json.load(json_file) model = Model.from_dict(data) model.convert_to_units('Meters') # convert Dragonfly Model to Honeybee add_plenum = not no_plenum cap = not no_cap hb_models = model.to_honeybee(obj_per_model, shade_dist, multiplier, add_plenum, cap) # write out the honeybee JSONs osms = [] idfs = [] sqls = [] for hb_model in hb_models: model_dict = hb_model.to_dict(triangulate_sub_faces=True) directory = os.path.join(folder, hb_model.identifier) file_path = os.path.join(directory, '{}.json'.format(hb_model.identifier)) preparedir(directory, remove_content=False) # create the directory with open(file_path, 'w') as fp: json.dump(model_dict, fp, indent=4) # Write the osw file to translate the model to osm osw = to_openstudio_osw(directory, file_path, sim_par_json, base_osw=base_osw, epw_file=epw_file) # run the measure to translate the model JSON to an openstudio measure if osw is not None and os.path.isfile(osw): if base_osw is None: # separate the OS CLI run from the E+ run osm, idf = run_osw(osw) if idf is not None and os.path.isfile(idf): sql, eio, rdd, html, err = run_idf(idf, epw_file) osms.append(osm) idfs.append(idf) sqls.append(sql) if err is None or not os.path.isfile(err): raise Exception('Running EnergyPlus failed.') else: raise Exception('Running OpenStudio CLI failed.') else: # run the whole simulation with the OpenStudio CLI osm, idf = run_osw(osw, measures_only=False) if idf is None or not os.path.isfile(idf): raise Exception('Running OpenStudio CLI failed.') sql, eio, rdd, html, err = \ output_energyplus_files(os.path.dirname(idf)) if err is None or not os.path.isfile(err): raise Exception('Running EnergyPlus failed.') osms.append(osm) idfs.append(idf) sqls.append(sql) else: raise Exception('Writing OSW file failed.') log_file.write(json.dumps({'osm': osms, 'idf': idfs, 'sql': sqls})) except Exception as e: _logger.exception('Model translation failed.\n{}'.format(e)) sys.exit(1) else: sys.exit(0)
def model_to_idf(model_json, sim_par_json, obj_per_model, multiplier, no_plenum, no_cap, shade_dist, folder, log_file): """Translate a Model JSON file to an IDF using direct-to-idf translators. The resulting IDF should be simulate-able but not all Model properties might make it into the IDF given that the direct-to-idf translators are used. \b Args: model_json: Full path to a Dragonfly Model JSON file. """ try: # set the default folder to the default if it's not specified if folder is None: proj_name = \ os.path.basename(model_json).replace('.json', '').replace('.dfjson', '') folder = os.path.join(hb_folders.default_simulation_folder, proj_name, 'EnergyPlus') preparedir(folder, remove_content=False) # check that the simulation parameters are there and load them if sim_par_json is not None: with open(sim_par_json) as json_file: data = json.load(json_file) sim_par = SimulationParameter.from_dict(data) else: sim_par = SimulationParameter() sim_par.output.add_zone_energy_use() sim_par.output.add_hvac_energy_use() # re-serialize the Dragonfly Model with open(model_json) as json_file: data = json.load(json_file) df_model = Model.from_dict(data) df_model.convert_to_units('Meters') # convert Dragonfly Model to Honeybee add_plenum = not no_plenum cap = not no_cap hb_models = df_model.to_honeybee(obj_per_model, shade_dist, multiplier, add_plenum, cap) # set the schedule directory in case it is needed sch_path = os.path.abspath(model_json) if 'stdout' in str(log_file) \ else os.path.abspath(str(log_file)) sch_directory = os.path.join(os.path.split(sch_path)[0], 'schedules') # write out the honeybee JSONs idfs = [] for hb_model in hb_models: # create the strings for simulation paramters and model ver_str = energyplus_idf_version() if folders.energyplus_version \ is not None else '' sim_par_str = sim_par.to_idf() model_str = hb_model.to.idf(hb_model, schedule_directory=sch_directory) idf_str = '\n\n'.join([ver_str, sim_par_str, model_str]) # write out the IDF files idf_path = os.path.join(folder, '{}.idf'.format(hb_model.identifier)) with open(idf_path, 'w') as idf_file: idf_file.write(idf_str) idfs.append(idf_path) log_file.write('\n'.join(idfs)) except Exception as e: _logger.exception('Model translation failed.\n{}\n'.format(e)) sys.exit(1) else: sys.exit(0)