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)
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
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
Ejemplo n.º 5
0
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)
Ejemplo n.º 8
0
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)
Ejemplo n.º 9
0
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)
Ejemplo n.º 11
0
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)
Ejemplo n.º 12
0
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)
Ejemplo n.º 16
0
        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')
Ejemplo n.º 17
0
        # 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_
]
Ejemplo n.º 20
0
    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_
]
Ejemplo n.º 21
0
    # 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
Ejemplo n.º 22
0
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