def run_osw_and_report_errors(i): """Run an OSW through OpenStudio CLI.""" osw = _osw[i] osm_i, idf_i = run_osw(osw, silent=silent) # process the additional strings if add_str_ != [] and add_str_[0] is not None and idf is not None: add_str = '/n'.join(add_str_) with open(idf, "a") as idf_file: idf_file.write(add_str) osm.append(osm_i) idf.append(idf_i) # run the IDF through EnergyPlus if run_: sql_i, zsz_i, rdd_i, html_i, err_i = run_idf(idf_i, _epw_file, silent=silent) # report any errors on this component if err_i is not None: err_obj = Err(err_i) err_objs.append(err_obj) for warn in err_obj.severe_errors: give_warning(ghenv.Component, warn) for error in err_obj.fatal_errors: print err_obj.file_contents # print before raising the error raise Exception(error) # append everything to the global lists sql.append(sql_i) zsz.append(zsz_i) rdd.append(rdd_i) html.append(html_i) err.append(err_i)
def strategy_warning(polygon_name): """Give a warning about a polygon not fitting on the chart.""" msg = 'Polygon "{}" could not fit on the chart given the current location of ' \ 'the comfort polygon(s).\nTry moving the comfort polygon(s) by changing ' \ 'its criteria to see the missing polygon.'.format(polygon_name) give_warning(ghenv.Component, msg) print(msg)
def check_hdr_dimensions(hdr_path): """Check that a given HDR file has dimensions suitable for evalglare. A warning is raised if the image is not 1000x1000 pixels and a ValueError is raised if the image is completely outside the accptable ragne from 800x800 to 1500x1500 pixels. Args: hdr_path: The path to an HDR image file. """ # get the path the the getinfo command getinfo_exe = os.path.join(rad_folders.radbin_path, 'getinfo.exe') if \ os.name == 'nt' else os.path.join(rad_folders.radbin_path, 'getinfo') # run the getinfo command in a manner that lets us obtain the result cmds = [getinfo_exe, '-d', hdr_path] use_shell = True if os.name == 'nt' else False process = subprocess.Popen(cmds, stdout=subprocess.PIPE, shell=use_shell) stdout = process.communicate() img_dim = stdout[0] # check the X and Y dimensions of the image x = int(img_dim.split(' ')[-1].strip()) y = int(img_dim.split(' ')[-3].strip()) msg = 'Input _hdr image dimensions must be {} {} x {} pixels. Got "{} x {}"' if x < 800 or y < 800: raise ValueError(msg.format('at least', 800, 800, x, y)) elif x > 1500 or y > 1500: raise ValueError(msg.format('no greater than', 1500, 1500, x, y)) elif not (999 <= x <=1001 and 999 <= y <=1001): msg = 'Input _hdr image dimensions are not the recommended values of 1000 x 1000.\n' \ 'Got {} x {}. Glare analysis is still performed, though you might ' \ 'consider revising the image size.'.format(x, y) give_warning(ghenv.Component, msg) return x, y
def run_idf_and_report_errors(i): """Run an IDF file through EnergyPlus and report errors/warnings on this component.""" # process the additional strings idf_i = idfs[i] if add_str_ != [] and add_str_[0] is not None: a_str = '/n'.join(add_str_) with open(idf_i, "a") as idf_file: idf_file.write(a_str) sql_i, zsz_i, rdd_i, html_i, err_i = run_idf(idf_i, _epw_file, silent=silent) # report any errors on this component if err_i is not None: err_obj = Err(err_i) err_objs[i] = err_obj for warn in err_obj.severe_errors: give_warning(ghenv.Component, warn) for error in err_obj.fatal_errors: print err_obj.file_contents # print before raising the error raise Exception(error) # append everything to the global lists sql[i] = sql_i zsz[i] = zsz_i rdd[i] = rdd_i html[i] = html_i err[i] = err_i
def check_symmetric(constr_type, construction): """Give a warning on the component that a construction is asymmetric.""" if not construction.is_symmetric: # check whether the construction is symmetric. message = 'Input {} is asymmetric (materials in reversed order ' \ 'do not equal those in the current order).\nThis can cause issues if the ' \ 'resulting constr_set is applied across multiple Rooms.\nMaterials: {}'.format( constr_type, construction.layers) give_warning(ghenv.Component, message)
def run_osm_and_report_errors(i): """Run an OSW through OpenStudio CLI.""" # create a blank osw for the translation osw_dict = {'seed_file': _osm[i], 'weather_file': _epw_file} osw_directory = os.path.dirname(_osm[i]) sch_directory1 = os.path.join(os.path.dirname(osw_directory), 'schedules') sch_directory2 = os.path.join(osw_directory, 'schedules') if os.path.isdir(sch_directory1): osw_dict['file_paths'] = [sch_directory1] elif os.path.isdir(sch_directory2): osw_dict['file_paths'] = [sch_directory2] osw = os.path.join(osw_directory, 'workflow.osw') with open(osw, 'w') as fp: json.dump(osw_dict, fp, indent=4) # get an IDF from the OSM using the OpenStudio CLI osm_i, idf_i = run_osw(osw, silent=silent) if idf_i is None: log_osw = OSW(os.path.join(osw_directory, 'out.osw')) errors = [] print(log_osw.stdout) for error, tb in zip(log_osw.errors, log_osw.error_tracebacks): print(tb) errors.append(error) raise Exception('Failed to run OpenStudio CLI:\n{}'.format( '\n'.join(errors))) # process the additional strings if add_str_ != [] and add_str_[0] is not None and idf is not None: a_str = '/n'.join(add_str_) with open(idf_i, "a") as idf_file: idf_file.write(a_str) osm[i] = osm_i idf[i] = idf_i # run the IDF through EnergyPlus if run_: sql_i, zsz_i, rdd_i, html_i, err_i = run_idf(idf_i, _epw_file, silent=silent) # report any errors on this component if err_i is not None: err_obj = Err(err_i) err_objs[i] = err_obj for warn in err_obj.severe_errors: give_warning(ghenv.Component, warn) for error in err_obj.fatal_errors: print err_obj.file_contents # print before raising the error raise Exception(error) # append everything to the global lists sql[i] = sql_i zsz[i] = zsz_i rdd[i] = rdd_i html[i] = html_i err[i] = err_i
def check_window_vent(rooms): """Check a rooms to make sure there's no opening of windows as coarse timestep.""" for room in rooms: if room.properties.energy.window_vent_control is not None: msg = 'Window ventilation was detected but your timestep is too low ' \ 'to model window opening correctly.\nIt is recommended that you ' \ 'increase your timestep to at least 4 to get loads for this case.' print msg give_warning(ghenv.Component, msg)
def check_strategy(value, name, default, max, min): """Check a strategy parameter to ensure it is correct.""" if value is None: strategy_par.append(default) elif value <= max and value >= min: strategy_par.append(value) else: strategy_par.append(default) msg = '"{}" must be between {} and {}. Got {}.\nReverting to default ' \ 'value of {}'.format(name, min, max, value, default) print(msg) give_warning(ghenv.Component, msg)
def is_measure_input(): """Check if a measure path is input to this component. This is needed because we don't know if there are default values for all required inputs until we load the measure. """ if _measure_path is None: msg = 'Input parameter _measure_path failed to collect data!' print(msg) give_warning(ghenv.Component, msg) return False return True
def run_idf_and_report_errors(i): """Run an IDF file through EnergyPlus and report errors/warnings on this component.""" idf = _idf[i] sql_i, zsz_i, rdd_i, html_i, err_i = run_idf(idf, _epw_file) # report any errors on this component if err_i is not None: err_obj = Err(err_i) err_objs.append(err_obj) for warn in err_obj.severe_errors: give_warning(ghenv.Component, warn) for error in err_obj.fatal_errors: raise Exception(error) # append everything to the global lists sql.append(sql_i) zsz.append(zsz_i) rdd.append(rdd_i) html.append(html_i) err.append(err_i)
def model_units_tolerance_check(model): """Convert a model to the current Rhino units and check the tolerance. Args: model: A honeybee Model, which will have its units checked. """ # check the model units if model.units != units_system(): print('Imported model units "{}" do not match that of the current Rhino ' 'model units "{}"\nThe model is being automatically converted ' 'to the Rhino doc units.'.format(model.units, units_system())) model.convert_to_units(units_system()) # check that the model tolerance is not too far from the Rhino tolerance if model.tolerance / tolerance >= 100: msg = 'Imported Model tolerance "{}" is significantly coarser than the ' \ 'current Rhino model tolerance "{}".\nIt is recommended that the ' \ 'Rhino document tolerance be changed to be coarser and this ' \ 'component is re-run.'.format(model.tolerance, tolerance) print msg give_warning(ghenv.Component, msg)
def version_check(data): """Check the version of the object if it was included in the dictionary. This is most useful in cases of importing entire Models to make sure the Model isn't newer than the currently installed Honeybee. Args: data: Dictionary of the object, which optionally has the "version" key. """ if 'version' in data and data['version'] is not None: model_ver = tuple(int(d) for d in data['version'].split('.')) hb_ver = folders.honeybee_schema_version if model_ver > hb_ver: msg = 'Imported Model schema version "{}" is newer than that with the ' \ 'currently installed Honeybee "{}".\nThe Model may fail to import ' \ 'or (worse) some newer features of the Model might not be imported ' \ 'without detection.'.format(data['version'], folders.honeybee_schema_version_str) print msg give_warning(ghenv.Component, msg) elif model_ver != hb_ver: msg = 'Imported Model schema version "{}" is older than that with the ' \ 'currently installed Honeybee "{}".\nThe Model will be upgraded upon ' \ 'import.'.format(data['version'], folders.honeybee_schema_version_str) print msg
# generate a default name if _name_ is None: # create a default Room name display_name = 'Room_{}'.format(document_counter('room_count')) else: display_name = _name_ name = clean_and_id_string(display_name) # create the Room room = Room(name, faces, tolerance, angle_tolerance) room.display_name = display_name # check that the Room geometry is closed. if room.check_solid(tolerance, angle_tolerance, False) != '': give_warning( ghenv.Component, 'Input _faces do not form a closed volume.\n' 'Room volume must be closed to access most honeybee features.\n' 'Preview the output Room to see the holes in your model.') # try to assign the modifier set if _mod_set_ is not None: if isinstance(_mod_set_, str): _mod_set_ = modifier_set_by_identifier(_mod_set_) room.properties.radiance.modifier_set = _mod_set_ # try to assign the construction set if _constr_set_ is not None: if isinstance(_constr_set_, str): _constr_set_ = construction_set_by_identifier(_constr_set_) room.properties.energy.construction_set = _constr_set_ # try to assign the program
try: _sim_par_.sizing_parameter.add_from_ddy_996_004(ddy_file) except AssertionError: msg = 'No design days were found in the .ddy file next to the _epw_file.' else: msg = 'No .ddy file was found next to the _epw_file.' if msg is not None: 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 msg = msg + '\nDesign days were generated from the input _epw_file but this ' \ '\nis not as accurate as design days from DDYs distributed with the EPW.' give_warning(ghenv.Component, msg) print msg # create the strings for simulation paramters and model ver_str = energyplus_idf_version() if energy_folders.energyplus_version \ is not None else energyplus_idf_version(compatibe_ep_version) sim_par_str = _sim_par_.to_idf() model_str = _model.to.idf(_model, schedule_directory=sch_directory, patch_missing_adjacencies=True) idf_str = '\n\n'.join([ver_str, sim_par_str, model_str]) # write the final string into an IDF idf = os.path.join(directory, 'in.idf') write_to_file_by_name(directory, 'in.idf', idf_str, True)
except ImportError as e: raise ImportError('\nFailed to import ladybug_rhino:\n\t{}'.format(e)) if all_required_inputs(ghenv.Component) and _update: # set default variables version = version_ if version_ is not None else folders.honeybee_schema_version_str name = _name_ if _name_ is not None else \ os.path.basename(_hbjson).lower().replace('.hbjson', '_UPDATED').replace('.json', '_UPDATED') if not (name.endswith('.hbjson') or name.endswith('.json')): name = '{}.hbjson'.format(name) folder = _folder_ if _folder_ is not None else os.path.dirname(_hbjson) hbjson = os.path.join(folder, name) # execute the update command and update the HBJSON shell = True if os.name == 'nt' else False cmds = [ folders.python_exe_path, '-m', 'honeybee_schema', 'update-model', _hbjson, '--version', version, '--output-file', hbjson ] process = subprocess.Popen(cmds, stderr=subprocess.PIPE, shell=shell) stderr = process.communicate() print(stderr[-1]) # validate the model if validation was requested if validate_: parsed_model = Model.from_hbjson(hbjson) valid_report = parsed_model.check_all(raise_exception=False) if valid_report != '': print(valid_report) give_warning(ghenv.Component, valid_report)
if _weather_URL.endswith('/all'): repl_section = '{0}/all'.format(_folder_name) new_section = '{0}/{0}.zip'.format(_folder_name) _weather_URL = _weather_URL.replace(repl_section, new_section) _weather_URL = _weather_URL.replace( 'www.energyplus.net/weather-download', 'energyplus-weather.s3.amazonaws.com') _weather_URL = _weather_URL.replace( 'energyplus.net/weather-download', 'energyplus-weather.s3.amazonaws.com') _weather_URL = _weather_URL[:8] + _weather_URL[8:].replace('//', '/') msg = 'The weather file URL is out of date.\nThis component ' \ 'is automatically updating it to the newer version:' print(msg) print(_weather_URL) give_warning(ghenv.Component, msg) give_warning(ghenv.Component, _weather_URL) # create default working_dir if _folder_ is None: _folder_ = folders.default_epw_folder print('Files will be downloaded to: {}'.format(_folder_)) # default file names epw = os.path.join(_folder_, _folder_name, _folder_name + '.epw') stat = os.path.join(_folder_, _folder_name, _folder_name + '.stat') ddy = os.path.join(_folder_, _folder_name, _folder_name + '.ddy') # download and unzip the files if they do not exist if not os.path.isfile(epw) or not os.path.isfile(stat) or not os.path.isfile(ddy): zip_file_path = os.path.join(_folder_, _folder_name, _folder_name + '.zip')
# assign the cross ventilation cross_vent = longest_list(_wind_cross_vent_, i) if \ len(_wind_cross_vent_) != 0 else None if cross_vent is None: # analyze normals of room's apertures to test if cross vent is possible orient_angles = [] for face in room.faces: for ap in face.apertures: if ap.is_operable: try: orient_angles.append(ap.horizontal_orientation()) except ZeroDivisionError: orient_angles.append(0) if len(orient_angles) != 0: orient_angles.sort() vent_open.wind_cross_vent = \ True if orient_angles[-1] - orient_angles[0] >= 90 else False else: vent_open.wind_cross_vent = False else: vent_open.wind_cross_vent = cross_vent vent_aps = room.properties.energy.assign_ventilation_opening(vent_open) rooms.append(room) op_count += len(vent_aps) # give a warning if no operable windows were found among the connected rooms if op_count == 0: give_warning( ghenv.Component, 'No operable Apertures were found among the connected _rooms.\n' 'Make sure that you have set the is_operable property of Apertures to True.')
# divide the individual data collections by floor area total_area = 0 room_data = [] for tup in match_tups: total_flr_area = tup[0].floor_area * tup[2] # includes effect of multiplier total_area += total_flr_area try: room_data.append(tup[1].normalize_by_area(total_flr_area, 'm2')) except ZeroDivisionError: # no floor area; not normalizable pass # sum all collections together and normalize them by the total if len(match_tups) != 0: summed_vals = [val for val in match_tups[0][1]] for data_i in match_tups[1:]: for i, val in enumerate(data_i[1]): summed_vals[i] += val else: # just assume all of the data corresponds with all input rooms summed_vals = [0 for val in _data[0]] total_area = sum(room.floor_area * room.multiplier for room in rooms) for d in _data: for i, val in enumerate(d): summed_vals[i] += val try: total_data = _data[0].duplicate() total_data.values = summed_vals total_data = total_data.normalize_by_area(total_area, 'm2') total_data.header.metadata = {'type': _data[0].header.metadata['type']} except ZeroDivisionError: # no floors in the model give_warning(ghenv.Component, 'No floors were found in the input _model.')
_interior_wall_ = opaque_constr(_interior_wall_, '_interior_wall_') check_symmetric('_interior_wall_', _interior_wall_) if _ceiling_ is not None: _ceiling_ = opaque_constr(_ceiling_, '_ceiling_') if _interior_floor_ is not None: _interior_floor_ = opaque_constr(_interior_floor_, '_interior_floor_') if _interior_window_ is not None: _interior_window_ = window_constr(_interior_window_, '_interior_window_') check_symmetric('_interior_window_', _interior_window_) if _interior_door_ is not None: _interior_door_ = opaque_constr(_interior_door_, '_interior_door_') check_symmetric('_interior_door_', _interior_door_) if _int_glass_door_ is not None: _int_glass_door_ = window_constr(_int_glass_door_, '_int_glass_door_') check_symmetric('_int_glass_door_', _int_glass_door_) # check whether the ceiling has the revered materials of the floor if _ceiling_ is not None or _interior_floor_ is not None: if constr_set.roof_ceiling_set.interior_construction != \ tuple(reversed(constr_set.floor_set.interior_construction)): give_warning( ghenv.Component, '_ceiling_ does not have materials in reversed ' \ ' order of the _interior_floor_.\nThis can cause issues if the ' \ 'resulting constr_set is applied across multiple Rooms.') # return the final list from the component interior_set = [ _interior_wall_, _ceiling_, _interior_floor_, _interior_window_, _interior_door_, _int_glass_door_ ]
check_symmetric('_interior_wall_', _interior_wall_) if _ceiling_ is not None: _ceiling_ = opaque_constr(_ceiling_, '_ceiling_') if _interior_floor_ is not None: _interior_floor_ = opaque_constr(_interior_floor_, '_interior_floor_') if _interior_window_ is not None: _interior_window_ = window_constr(_interior_window_, '_interior_window_') check_symmetric('_interior_window_', _interior_window_) if _interior_door_ is not None: _interior_door_ = opaque_constr(_interior_door_, '_interior_door_') check_symmetric('_interior_door_', _interior_door_) if _int_glass_door_ is not None: _int_glass_door_ = window_constr(_int_glass_door_, '_int_glass_door_') check_symmetric('_int_glass_door_', _int_glass_door_) # check whether the ceiling has the revered materials of the floor if _ceiling_ is not None or _interior_floor_ is not None: if _ceiling_ is None or _interior_floor_ is None or \ _ceiling_.layers != list(reversed(_interior_floor_.layers)): warn = '_ceiling_ does not have materials in reversed ' \ ' order of the _interior_floor_.\nThis can cause issues if the ' \ 'resulting constr_set is applied across multiple Rooms.' give_warning(ghenv.Component, warn) print(warn) # return the final list from the component interior_set = [ _interior_wall_, _ceiling_, _interior_floor_, _interior_window_, _interior_door_, _int_glass_door_ ]
# get the compatiable versions of all the dependencies temp_folder = os.path.join(folders.ladybug_tools_folder, 'temp') lbt_gh_folder = download_repo_github('lbt-grasshopper', temp_folder, version_) ver_dict = parse_lbt_gh_versions(lbt_gh_folder) ver_dict['lbt-grasshopper'] = version_ # install the core libraries print 'Installing Ladybug Tools core Python libraries.' df_ver = ver_dict['lbt-dragonfly'] stderr = update_libraries_pip(py_exe, 'lbt-dragonfly[cli]', df_ver) if os.path.isdir( os.path.join(py_lib, 'lbt_dragonfly-{}.dist-info'.format(df_ver))): print 'Ladybug Tools core Python libraries successfully installed!\n ' else: give_warning(ghenv.Component, stderr) print stderr # install the queenbee core libraries print 'Installing Queenbee core Python libraries.' qb_ver = ver_dict['queenbee-luigi'] stderr = update_libraries_pip(py_exe, 'queenbee-luigi[cli]', qb_ver) if os.path.isdir( os.path.join(py_lib, 'queenbee_luigi-{}.dist-info'.format(qb_ver))): print 'Queenbee core Python libraries successfully installed!\n ' else: give_warning(stderr) print stderr # install the library needed for interaction with Rhino
def update_component(component, uofolder): """Update a component using its version in the user object folder.""" # identify the correct user object sub-folder to which the component belongs if str(component.Name).startswith('LB'): # ladybug [+] fp = os.path.join(uofolder, 'ladybug_grasshopper', 'user_objects', '%s.ghuser' % component.Name) elif str(component.Name).startswith('HB'): # honeybee[+] if str(component.Category) == 'Honeybee': fp = os.path.join(uofolder, 'honeybee_grasshopper_core', 'user_objects', '%s.ghuser' % component.Name) elif str(component.Category) == 'HB-Energy': fp = os.path.join(uofolder, 'honeybee_grasshopper_energy', 'user_objects', '%s.ghuser' % component.Name) elif str(component.Category) == 'HB-Radiance': fp = os.path.join(uofolder, 'honeybee_grasshopper_radiance', 'user_objects', '%s.ghuser' % component.Name) elif str(component.Name).startswith('DF'): # dragonfly [+] fp = os.path.join(uofolder, 'dragonfly_grasshopper', 'user_objects', '%s.ghuser' % component.Name) elif str(component.Name).startswith('Ladybug'): # ladybug legacy fp = os.path.join(uofolder, 'Ladybug', '%s.ghuser' % component.Name) elif str(component.Name).startswith('Honeybee'): # honeybee legacy fp = os.path.join(uofolder, 'Honeybee', '%s.ghuser' % component.Name) elif str(component.Name).startswith('HoneybeePlus'): # old honeybee [+] fp = os.path.join(uofolder, 'HoneybeePlus', '%s.ghuser' % component.Name) else: # unidentified plugin; see if we can find it in the root fp = os.path.join(uofolder, '%s.ghuser' % component.Name) if not os.path.isfile(fp): category = str(component.Name).split('_')[0] fp = os.path.join(uofolder, category, '%s.ghuser' % component.Name) if not os.path.isfile(fp): warning = 'Failed to find the userobject for %s' % component.Name give_warning(ghenv.Component, warning) return False # the the instance of the user object uo = gh.GH_UserObject(fp).InstantiateObject() # check to see if the version of the userobject has changed if not has_version_changed(uo, component): return False # the version has changed component.Code = uo.Code # define the callback function def call_back(document): component.ExpireSolution(False) # update the solution doc.ScheduleSolution(2, gh.GH_Document.GH_ScheduleDelegate(call_back)) # check if the inputs or outputs have changed if input_output_changed(uo, component): insert_new_uo(uo, component, doc) mark_component(doc, component) # mark component with a warning to the user return 'Cannot update %s. Replace manually.' % component.Name return 'Updated %s' % component.Name