Beispiel #1
0
def initialize_time_series_data(variables, time, t0=None):
    """
    Initializes time series data from initial conditions
    """
    # TODO: What is the right API for this function?
    if t0 is None:
        # time could be a list, OrderedSet, or ContinuousSet
        t0 = next(iter(time))
    variable_data = {}
    for var in variables:
        if var.is_reference():
            try:
                variable_data[str(ComponentUID(
                    var.referent))] = [value(var[t0])]
            except ValueError as err:
                if "No value for uninitialized NumericValue" in str(err):
                    variable_data[str(ComponentUID(var.referent))] = [None]
                else:
                    raise err

        else:
            try:
                variable_data[str(ComponentUID(var))] = [value(var[t0])]
            except ValueError as err:
                if "No value for uninitialized NumericValue" in str(err):
                    variable_data[str(ComponentUID(var))] = [None]
                else:
                    raise err
    return ([t0], variable_data)
Beispiel #2
0
def get_time_indexed_cuid(var, sets=None, dereference=None):
    """
    Arguments
    ---------
    var:
        Object to process
    time: Set
        Set to use if slicing a vardata object
    dereference: None or int
        Number of times we may access referent attribute to recover a
        "base component" from a reference.

    """
    # TODO: Does this function have a good name?
    # Should this function be generalized beyond a single indexing set?
    # Is allowing dereference to be an integer worth the confusion it might
    # add?
    if dereference is None:
        # Does this branch make sense? If given an unattached component,
        # we dereference, otherwise we don't dereference.
        remaining_dereferences = int(var.parent_block() is None)
    else:
        remaining_dereferences = int(dereference)
    if var.is_indexed():
        if var.is_reference() and remaining_dereferences:
            remaining_dereferences -= 1
            referent = var.referent
            if isinstance(referent, IndexedComponent_slice):
                return ComponentUID(referent)
            else:
                # If dereference is None, we propagate None, dereferencing
                # until we either reach a component attached to a block
                # or reach a non-reference component.
                dereference = dereference if dereference is None else\
                        remaining_dereferences
                # NOTE: Calling this function recursively
                return get_time_indexed_cuid(referent,
                                             time,
                                             dereference=dereference)
        else:
            # Assume that var is indexed only by time
            # TODO: Get appropriate slice for indexing set.
            # TODO: Should we call slice_component_along_sets here as well?
            # To cover the case of b[t0].var, where var is indexed
            # by a set we care about, and we also care about time...
            # But then maybe we should slice only the sets we care about...
            # Don't want to do anything with these sets unless we're
            # presented with a vardata...
            #index = tuple(
            #    get_slice_for_set(s) for s in var.index_set().subsets()
            #)
            index = get_slice_for_set(var.index_set())
            return ComponentUID(var[:])
    else:
        if sets is None:
            raise ValueError(
                "A ComponentData %s was provided but no set. We need to know\n"
                "what set this component should be indexed by." % var.name)
        slice_ = slice_component_along_sets(var, sets)
        return ComponentUID(slice_)
Beispiel #3
0
def extend_time_series_data(
    data,
    variables,
    time,
    offset=None,
    include_first=False,
    include_last=True,
):
    """
    data:
    (
        [t0, t1, ...],
        {
            str(cuid): [value0, value1, ...],
        },
    )
    """
    existing_time, existing_name_map = data
    time = list(time)
    start = None if include_first else 1
    stop = None if include_last else -1
    slice_idx = slice(start, stop, None)
    time = time[slice_idx]
    variable_data = {}
    for var in variables:
        if var.is_reference():
            variable_data[str(ComponentUID(
                var.referent))] = [value(var[t]) for t in time]
        else:
            variable_data[str(
                ComponentUID(var))] = [value(var[t]) for t in time]
    if not set(variable_data.keys()) == set(existing_name_map.keys()):
        raise ValueError(
            "Trying to extend time series with different variables "
            "than were originally present.")
    new_name_map = {
        name: existing_name_map[name] + variable_data[name]
        for name in existing_name_map
    }
    if offset is not None:
        time = [t + offset for t in time]
    if existing_time[-1] >= time[0]:
        raise ValueError(
            "Cannot extend a data series with duplicated or "
            "unsorted time entries. Trying to place %s after %s." %
            (time[0], existing_time[-1]))
    new_time = existing_time + time
    return (new_time, new_name_map)
Beispiel #4
0
def expand_data_over_space(m, time, space, data):
    """
    data maps time/space cuids to values.
    We get a data object from the cuid, then generate slices
    over time only at every point in space.
    """
    sets = (time, )
    t0 = next(iter(time))
    new_data = {}
    for name, value in data.items():
        var = m.find_component(name)
        if var is None:
            # Steady model won't have accumulation variables
            assert "accumulation" in name
            continue
        factor_sets = list(var.index_set().subsets())
        if var.is_indexed() and len(factor_sets) == 2:
            # Assume our var is indexed by time, then space.
            for x in space:
                cuid = ComponentUID(
                    slice_component_along_sets(var[t0, x], sets))
                new_data[str(cuid)] = value
        else:
            new_data[name] = value
    return new_data
Beispiel #5
0
def _get_structured_variable_data(sets, comps, dereference=False):
    mesh = np.meshgrid(*sets, indexing="ij")
    # Without "ij", for two dimensions, meshgrid returns what
    # I would consider the transpose of the proper mesh.

    # TODO: references get tricky. If we have flattened
    # "base components" using references, we would like
    # to deference to get the underlying slices.
    # If we have flattened references, however, we may
    # or may not want to get the underlying slice.
    #
    # For now, we only dereference once, to undo the layer
    # of references added by the flattener. The ability
    # to completely dereference may be valuable in the future...
    set_cuids = [str(ComponentUID(s)) for s in sets]
    indices = [[val for val in s] for s in sets]
    cuid_buffer = {}
    if dereference:
        # TODO: Should we branch on whether each component is a reference
        # rather than use some "dereference" flag?
        variables = {
            str(ComponentUID(comp.referent, cuid_buffer=cuid_buffer)):
            apply_function_elementwise(
                lambda *ind: comp[ind].value,
                # This function, which gives us the data to be stored,
                # will vary with component type, and could possibly
                # by user provided/configurable.
                *mesh,
            )
            for comp in comps
        }
    else:
        variables = {
            str(ComponentUID(comp, cuid_buffer=cuid_buffer)):
            apply_function_elementwise(
                lambda *ind: comp[ind].value,
                *mesh,
            )
            for comp in comps
        }

    return {
        "sets": set_cuids,
        "indices": indices,
        "variables": variables,
    }
Beispiel #6
0
def _get_unstructured_variable_data(comps):
    # TODO: If we decide to support "complete dereferencing,"
    # we will need a dereference flag here as well.
    cuid_buffer = {}
    return {
        "sets": None,
        "indices": None,
        "variables":
        {str(ComponentUID(comp, cuid_buffer)): comp.value
         for comp in comps},
    }
Beispiel #7
0
def get_tracking_cost_expression(
    variables,
    time,
    setpoint_data,
    weight_data=None,
    dereference=True,
):
    """
    Arguments
    ---------
    variables: list
        List of time-indexed variables to include in the tracking cost
        expression.
    setpoint_data: dict
        Maps variable names to setpoint values
    weight_data: dict
        Maps variable names to tracking cost weights

    Returns
    -------
    Pyomo expression. Sum of weighted squared difference between
    variables and setpoint values.

    """
    # TODO: Should setpoints be mutable params? Indexed by variable names?
    # This is tempting as it makes changing the setpoint easy.
    variable_names = [
        # NOTE: passing strings through ComponentUID.
        # This may break things that rely on a particular form of the string.
        # I believe this approach is more consistent, however.
        # This is a temporary measure before I switch to using CUIDs as keys.
        str(ComponentUID(str(get_time_indexed_cuid(var)))) for var in variables
    ]
    #    str(ComponentUID(var))
    #    if not var.is_reference() or not dereference
    #    else str(ComponentUID(var.referent))
    #    for var in variables
    #]
    if weight_data is None:
        weight_data = {name: 1.0 for name in variable_names}

    def tracking_rule(m, t):
        return sum(weight_data[name] * (var[t] - setpoint_data[name])**2
                   for name, var in zip(variable_names, variables))

    # Note that I don't enforce that variables are actually indexed
    # by time. "time" could just be a list of sample points...
    tracking_expr = Expression(time, rule=tracking_rule)
    return tracking_expr
Beispiel #8
0
def _get_structured_variable_data_from_dict(sets, indices, comps_dict):
    """
    Arguments
    ---------
    sets: Tuple of Pyomo Sets
        Sets by which the components are indexed.
    comps_dict: Dict of dicts.
        Maps ComponentUID to a dict mapping indices to values.

    """
    mesh = np.meshgrid(*indices, indexing="ij")
    return {
        # TODO: Why am I storing sets and indices separately instead of one
        # "sets" field that contains a list of (name, indices) tuples?
        "sets": [str(ComponentUID(s)) for s in sets],
        "indices": [list(s) for s in indices],
        "variables": {
            name: apply_function_elementwise(
                lambda *idx: data[idx],
                *mesh,
            )
            for name, data in comps_dict.items()
        },
    }
Beispiel #9
0
    def add_solution(self,
                     solution,
                     smap_id,
                     delete_symbol_map=True,
                     cache=None,
                     ignore_invalid_labels=False,
                     ignore_missing_symbols=True,
                     default_variable_value=None):

        instance = self._instance()

        soln = ModelSolution()
        soln._metadata['status'] = solution.status
        if not type(solution.message) is UndefinedData:
            soln._metadata['message'] = solution.message
        if not type(solution.gap) is UndefinedData:
            soln._metadata['gap'] = solution.gap

        if smap_id is None:
            #
            # Cache symbol names, which might be re-used in subsequent
            # calls to add_solution()
            #
            if cache is None:
                cache = {}
            if solution._cuid:
                #
                # Loading a solution with CUID keys
                #
                if len(cache) == 0:
                    for obj in instance.component_data_objects(Var):
                        cache[ComponentUID(obj)] = obj
                    for obj in instance.component_data_objects(Objective,
                                                               active=True):
                        cache[ComponentUID(obj)] = obj
                    for obj in instance.component_data_objects(Constraint,
                                                               active=True):
                        cache[ComponentUID(obj)] = obj

                for name in ['problem', 'objective', 'variable', 'constraint']:
                    tmp = soln._entry[name]
                    for cuid, val in getattr(solution, name).items():
                        obj = cache.get(cuid, None)
                        if obj is None:
                            if ignore_invalid_labels:
                                continue
                            raise RuntimeError(
                                "CUID %s is missing from model %s" %
                                (str(cuid), instance.name))
                        tmp[id(obj)] = (weakref_ref(obj), val)
            else:
                #
                # Loading a solution with string keys
                #
                if len(cache) == 0:
                    for obj in instance.component_data_objects(Var):
                        cache[obj.name] = obj
                    for obj in instance.component_data_objects(Objective,
                                                               active=True):
                        cache[obj.name] = obj
                    for obj in instance.component_data_objects(Constraint,
                                                               active=True):
                        cache[obj.name] = obj

                for name in ['problem', 'objective', 'variable', 'constraint']:
                    tmp = soln._entry[name]
                    for symb, val in getattr(solution, name).items():
                        obj = cache.get(symb, None)
                        if obj is None:
                            if ignore_invalid_labels:
                                continue
                            raise RuntimeError(
                                "Symbol %s is missing from model %s" %
                                (symb, instance.name))
                        tmp[id(obj)] = (weakref_ref(obj), val)
        else:
            #
            # Map solution
            #
            smap = self.symbol_map[smap_id]
            for name in ['problem', 'objective', 'variable', 'constraint']:
                tmp = soln._entry[name]
                for symb, val in getattr(solution, name).items():
                    if symb in smap.bySymbol:
                        obj = smap.bySymbol[symb]
                    elif symb in smap.aliases:
                        obj = smap.aliases[symb]
                    elif ignore_missing_symbols:
                        continue
                    else:  #pragma:nocover
                        #
                        # This should never happen ...
                        #
                        raise RuntimeError(
                            "ERROR: Symbol %s is missing from "
                            "model %s when loading with a symbol map!" %
                            (symb, instance.name))

                    tmp[id(obj())] = (obj, val)
            #
            # Wrap up
            #
            if delete_symbol_map:
                self.delete_symbol_map(smap_id)

        #
        # Collect fixed variables
        #
        tmp = soln._entry['variable']
        for vdata in instance.component_data_objects(Var):
            id_ = id(vdata)
            if vdata.fixed:
                tmp[id_] = (weakref_ref(vdata), {'Value': value(vdata)})
            elif (default_variable_value is not None) and \
                 (smap_id is not None) and \
                 (id_ in smap.byObject) and \
                 (id_ not in tmp):
                tmp[id_] = (weakref_ref(vdata), {
                    'Value': default_variable_value
                })

        self.solutions.append(soln)
        return len(self.solutions) - 1
Beispiel #10
0
 def __call__(self, obj=None):
     return ComponentUID(obj)