Exemplo n.º 1
0
def fix_vars_unindexed_by(b, time):
    """
    Searches block b for variables not indexed by time
    and fixes them. 

    Args:
        b : Block to search
        time : Set with respect to which to find unindexed variables

    Returns:
        List of variables fixed
    """
    varlist = []

    visited = set()
    for var in b.component_objects(Var):
        if id(var) in visited:
            continue
        visited.add(id(var))

        if (not is_explicitly_indexed_by(var, time)
                and not is_in_block_indexed_by(var, time)):
            for index in var:
                vardata = var[index]
                if (not vardata.fixed and vardata.value is not None):
                    # Can't fix a variable with a value of None, but this
                    # should be called after a solve, so any variable with
                    # value of None is stale and won't be sent to solver,
                    # so it doesn't need to be fixed to maintain correct
                    # degrees of freedom
                    vardata.fix()
                    varlist.append(vardata)

    return varlist
Exemplo n.º 2
0
def deactivate_constraints_unindexed_by(b, time):
    """
    Searches block b for and constraints not indexed by time
    and deactivates them. 

    Args:
        b : Block to search
        time : Set with respect to which to find unindexed constraints

    Returns:
        List of constraints deactivated
    """
    conlist = []

    visited = set()
    for comp in b.component_objects(Constraint, active=True):
        if id(comp) in visited:
            continue
        visited.add(id(comp))

        if (not is_explicitly_indexed_by(comp, time)
                and not is_in_block_indexed_by(comp, time)):
            for index in comp:
                compdata = comp[index]
                if compdata.active:
                    compdata.deactivate()
                    conlist.append(compdata)

    return conlist
Exemplo n.º 3
0
def deactivate_model_at(b, cset, pts, outlvl=idaeslog.NOTSET):
    """
    Finds any block or constraint in block b, indexed explicitly (and not 
    implicitly) by cset, and deactivates it at points specified. 
    Implicitly indexed components are excluded because one of their parent 
    blocks will be deactivated, so deactivating them too would be redundant.

    Args:
        b : Block to search
        cset : ContinuousSet of interest
        pts : Value or list of values, in ContinuousSet, to deactivate at

    Returns:
        A dictionary mapping points in pts to lists of
        component data that have been deactivated there
    """
    if not type(pts) is list:
        pts = [pts]
    for pt in pts:
        if not pt in cset:
            msg = str(pt) + ' is not in ContinuousSet ' + cset.name
            raise ValueError(msg)
    deactivated = {pt: [] for pt in pts}

    visited = set()
    for comp in b.component_objects([Block, Constraint], active=True):
        # Record components that have been visited in case component_objects
        # contains duplicates (due to references)
        if id(comp) in visited:
            continue
        visited.add(id(comp))

        if (is_explicitly_indexed_by(comp, cset)
                and not is_in_block_indexed_by(comp, cset)):
            info = get_index_set_except(comp, cset)
            non_cset_set = info['set_except']
            index_getter = info['index_getter']

            for non_cset_index in non_cset_set:
                for pt in pts:
                    index = index_getter(non_cset_index, pt)
                    try:
                        comp[index].deactivate()
                        deactivated[pt].append(comp[index])
                    except KeyError:
                        # except KeyError to allow Constraint/Block.Skip
                        msg = (comp.name + ' has no index ' + str(index))
                        init_log = idaeslog.getInitLogger(__name__, outlvl)
                        init_log.warning(msg)
                        continue

    return deactivated
Exemplo n.º 4
0
def get_inconsistent_initial_conditions(model,
                                        time,
                                        tol=1e-8,
                                        t0=None,
                                        allow_skip=True,
                                        suppress_warnings=False):
    """Finds constraints of the model that are implicitly or explicitly
    indexed by time and checks if they are consistent to within a tolerance
    at the initial value of time.

    Args:
        model: Model whose constraints to check
        time: Set whose initial condition will be checked
        tol: Maximum constraint violation
        t0: Point in time at which to check constraints

    Returns:
        List of constraint data objects that were found to be inconsistent.
    """
    if t0 is None:
        t0 = time.first()

    inconsistent = ComponentSet()
    for con in model.component_objects(Constraint, active=True):
        if not is_explicitly_indexed_by(con, time):
            continue
        if is_in_block_indexed_by(con, time):
            continue
        info = get_index_set_except(con, time)
        non_time_set = info['set_except']
        index_getter = info['index_getter']
        for non_time_index in non_time_set:
            index = index_getter(non_time_index, t0)
            try:
                condata = con[index]
            except KeyError:
                # To allow Constraint.Skip
                if not suppress_warnings:
                    print(index_warning(con.name, index))
                if not allow_skip:
                    raise
                continue
            if (value(condata.body) - value(condata.upper) > tol
                    or value(condata.lower) - value(condata.body) > tol):
                inconsistent.add(condata)

    for blk in model.component_objects(Block, active=True):
        # What if there are time-indexed blocks at multiple levels
        # of a hierarchy?
        # My preferred convention is to only check the first (highest-
        # level) time index, but distinguishing between different-level
        # time indices is an expensive operation.
        if not is_explicitly_indexed_by(blk, time):
            continue
        if is_in_block_indexed_by(blk, time):
            continue
        info = get_index_set_except(blk, time)
        non_time_set = info['set_except']
        index_getter = info['index_getter']
        for non_time_index in non_time_set:
            index = index_getter(non_time_index, t0)
            blkdata = blk[index]
            for condata in blkdata.component_data_objects(Constraint,
                                                          active=True):
                if (value(condata.body) - value(condata.upper) > tol
                        or value(condata.lower) - value(condata.body) > tol):
                    if condata in inconsistent:
                        raise ValueError(
                            '%s has already been visited. The only way this '
                            'should happen is if the model has nested time-'
                            'indexed blocks, which is not supported.')
                    inconsistent.add(condata)

    return list(inconsistent)
Exemplo n.º 5
0
def copy_non_time_indexed_values(
    fs_tgt,
    fs_src,
    copy_fixed=True,
    outlvl=idaeslog.NOTSET,
):
    """
    Function to set the values of all variables that are not (implicitly
    or explicitly) indexed by time to their values in a different flowsheet.

    Args:
        fs_tgt : Flowsheet into which values will be copied.
        fs_src : Flowsheet from which values will be copied.
        copy_fixed : Bool marking whether or not to copy over fixed variables
                     in the target flowsheet.
        outlvl : Outlevel for the IDAES logger.

    Returns:
        None
    """
    time_tgt = fs_tgt.time

    var_visited = set()
    for var_tgt in fs_tgt.component_objects(Var, descend_into=False):
        if id(var_tgt) in var_visited:
            continue
        var_visited.add(id(var_tgt))

        if is_explicitly_indexed_by(var_tgt, time_tgt):
            continue
        var_src = fs_src.find_component(var_tgt.local_name)
        # ^ this find_component is fine because var_tgt is a Var not VarData
        # and its local_name is used. Assumes that there are no other decimal
        # indices in between fs_src and var_src

        if var_src is None:
            # Log a warning
            msg = ('Warning copying values: ' + var_src.name +
                   ' does not exist in source block ' + fs_src.name)
            init_log = idaeslog.getInitLogger(__name__, outlvl)
            init_log.warning(msg)
            continue

        for index in var_tgt:
            if not copy_fixed and var_tgt[index].fixed:
                continue
            var_tgt[index].set_value(var_src.value)

    blk_visited = set()
    for blk_tgt in fs_tgt.component_objects(Block):

        if id(blk_tgt) in blk_visited:
            continue
        blk_visited.add(id(blk_tgt))

        if (is_in_block_indexed_by(blk_tgt, time_tgt)
                or is_explicitly_indexed_by(blk_tgt, time_tgt)):
            continue
        # block is not even implicitly indexed by time
        for b_index in blk_tgt:

            var_visited = set()
            for var_tgt in blk_tgt[b_index].component_objects(
                    Var, descend_into=False):
                if id(var_tgt) in var_visited:
                    continue
                var_visited.add(id(var_tgt))

                if is_explicitly_indexed_by(var_tgt, time_tgt):
                    continue

                # can't used find_component(local_name) here because I might
                # have decimal indices
                try:
                    local_parent = fs_src
                    for r in path_from_block(var_tgt, fs_tgt):
                        local_parent = getattr(local_parent, r[0])[r[1]]
                except AttributeError:
                    # log warning
                    msg = ('Warning copying values: ' + r[0] +
                           ' does not exist in source' + local_parent.name)
                    init_log = idaeslog.getInitLogger(__name__, outlvl)
                    init_log.warning(msg)
                    continue
                except KeyError:
                    msg = ('Warning copying values: ' + str(r[1]) +
                           ' is not a valid index for' +
                           getattr(local_parent, r[0]).name)
                    init_log = idaeslog.getInitLogger(__name__, outlvl)
                    init_log.warning(msg)
                    continue

                var_src = getattr(local_parent, var_tgt.local_name)

                for index in var_tgt:
                    if not copy_fixed and var_tgt[index].fixed:
                        continue
                    var_tgt[index].set_value(var_src[index].value)
Exemplo n.º 6
0
def initialize_by_element_in_range(model,
                                   time,
                                   t_start,
                                   t_end,
                                   time_linking_vars=[],
                                   dae_vars=[],
                                   max_linking_range=0,
                                   **kwargs):
    """Function for solving a square model, time element-by-time element,
    between specified start and end times.

    Args:
        model : Flowsheet model to solve
        t_start : Beginning of timespan over which to solve
        t_end : End of timespan over which to solve

    Kwargs:
        solver : Solver option used to solve portions of the square model
        outlvl : idaes.logger output level
    """
    solver = kwargs.pop('solver', SolverFactory('ipopt'))
    outlvl = kwargs.pop('outlvl', idaeslog.NOTSET)
    init_log = idaeslog.getInitLogger('nmpc', outlvl)
    solver_log = idaeslog.getSolveLogger('nmpc', outlvl)
    solve_initial_conditions = kwargs.pop('solve_initial_conditions', False)

    #TODO: Move to docstring
    # Variables that will be fixed for time points outside the finite element
    # when constraints for a finite element are activated.
    # For a "normal" process, these should just be differential variables
    # (and maybe derivative variables). For a process with a (PID) controller,
    # these should also include variables used by the controller.
    # If these variables are not specified,

    # Timespan over which these variables will be fixed, counting backwards
    # from the first time point in the finite element (which will always be
    # fixed)
    # Should I specify max_linking_range as an integer number of finite
    # elements, an integer number of time points, or a float in actual time
    # units? Go with latter for now.

    assert t_start in time.get_finite_elements()
    assert t_end in time.get_finite_elements()
    #assert degrees_of_freedom(model) == 0
    # No need to check dof here as we will check right before each solve

    #dae_vars = kwargs.pop('dae_vars', [])
    if not dae_vars:
        scalar_vars, dae_vars = flatten_dae_components(model, time, ctype=Var)
        for var in scalar_vars:
            var.fix()
        deactivate_constraints_unindexed_by(model, time)

    ncp = time.get_discretization_info()['ncp']

    fe_in_range = [
        i for i, fe in enumerate(time.get_finite_elements())
        if fe >= t_start and fe <= t_end
    ]
    t_in_range = [t for t in time if t >= t_start and t <= t_end]

    fe_in_range.pop(0)
    n_fe_in_range = len(fe_in_range)

    was_originally_active = get_activity_dict(model)
    was_originally_fixed = get_fixed_dict(model)

    # Deactivate model
    if not solve_initial_conditions:
        time_list = [t for t in time]
        deactivated = deactivate_model_at(model,
                                          time,
                                          time_list,
                                          outlvl=idaeslog.ERROR)
    else:
        time_list = [t for t in time if t != time.first()]
        deactivated = deactivate_model_at(model,
                                          time,
                                          time_list,
                                          outlvl=idaeslog.ERROR)

        assert degrees_of_freedom(model) == 0
        with idaeslog.solver_log(solver_log, level=idaeslog.DEBUG) as slc:
            results = solver.solve(model, tee=slc.tee)
        if results.solver.termination_condition == TerminationCondition.optimal:
            pass
        else:
            raise ValueError(
                'Failed to solve for consistent initial conditions.')

        deactivated[time.first()] = deactivate_model_at(
            model, time, time.first(), outlvl=idaeslog.ERROR)[time.first()]

    # "Integration" loop
    for i in fe_in_range:
        t_prev = time[(i - 1) * ncp + 1]

        fe = [time[k] for k in range((i - 1) * ncp + 2, i * ncp + 2)]

        con_list = []
        for t in fe:
            # These will be fixed vars in constraints at t
            # Probably not necessary to record at what t
            # they occur
            for comp in deactivated[t]:
                if was_originally_active[id(comp)]:
                    comp.activate()
                    if not time_linking_vars:
                        if isinstance(comp, _ConstraintData):
                            con_list.append(comp)
                        elif isinstance(comp, _BlockData):
                            # Active here should be independent of whether block
                            # was active
                            con_list.extend(
                                list(
                                    comp.component_data_objects(Constraint,
                                                                active=True)))

        if not time_linking_vars:
            fixed_vars = []
            for con in con_list:
                for var in identify_variables(con.expr, include_fixed=False):
                    # use var_locator/ComponentMap to get index somehow
                    t_idx = get_implicit_index_of_set(var, time)
                    if t_idx is None:
                        assert not is_in_block_indexed_by(var, time)
                        continue
                    if t_idx <= t_prev:
                        fixed_vars.append(var)
                        var.fix()
        else:
            fixed_vars = []
            time_range = [
                t for t in time
                if t_prev - t <= max_linking_range and t <= t_prev
            ]
            time_range = [t_prev]
            for _slice in time_linking_vars:
                for t in time_range:
                    #if not _slice[t].fixed:
                    _slice[t].fix()
                    fixed_vars.append(_slice[t])

        # Here I assume that the only variables that can appear in
        # constraints at a different (later) time index are derivatives
        # and differential variables (they do so in the discretization
        # equations) and that they only participate at t_prev.
        #
        # This is not the case for, say, PID controllers, in which case
        # I should pass in a list of "complicating variables," then fix
        # them at all time points outside the finite element.
        #
        # Alternative solution is to identify_variables in each constraint
        # that is activated and fix those belonging to a previous finite
        # element. (Should not encounter variables belonging to a future
        # finite element.)
        # ^ This option is easier, less efficient
        #
        # In either case need to record whether variable was previously fixed
        # so I know if I should unfix it or not.

        for t in fe:
            for _slice in dae_vars:
                if not _slice[t].fixed:
                    # Fixed DAE variables are time-dependent disturbances,
                    # whose values should not be altered by this function.
                    _slice[t].set_value(_slice[t_prev].value)

        assert degrees_of_freedom(model) == 0

        with idaeslog.solver_log(solver_log, level=idaeslog.DEBUG) as slc:
            results = solver.solve(model, tee=slc.tee)
        if results.solver.termination_condition == TerminationCondition.optimal:
            pass
        else:
            raise ValueError('Failed to solve for finite element %s' % i)

        for t in fe:
            for comp in deactivated[t]:
                comp.deactivate()

        for var in fixed_vars:
            if not was_originally_fixed[id(var)]:
                var.unfix()

    for t in time:
        for comp in deactivated[t]:
            if was_originally_active[id(comp)]:
                comp.activate()
Exemplo n.º 7
0
    def test_indexed_by(self):
        m = ConcreteModel()
        m.time = ContinuousSet(bounds=(0, 10))
        m.space = ContinuousSet(bounds=(0, 10))
        m.set = Set(initialize=['a', 'b', 'c'])
        m.set2 = Set(initialize=[('a', 1), ('b', 2)])
        m.v = Var()
        m.v1 = Var(m.time)
        m.v2 = Var(m.time, m.space)
        m.v3 = Var(m.set, m.space, m.time)
        m.v4 = Var(m.time, m.set2)
        m.v5 = Var(m.set2, m.time, m.space)

        @m.Block()
        def b(b):
            b.v = Var()
            b.v1 = Var(m.time)
            b.v2 = Var(m.time, m.space)
            b.v3 = Var(m.set, m.space, m.time)

        @m.Block(m.time)
        def b1(b):
            b.v = Var()
            b.v1 = Var(m.space)
            b.v2 = Var(m.space, m.set)

        @m.Block(m.time, m.space)
        def b2(b):
            b.v = Var()
            b.v1 = Var(m.set)

            @b.Block()
            def b(bl):
                bl.v = Var()
                bl.v1 = Var(m.set)
                bl.v2 = Var(m.time)

        @m.Block(m.set2, m.time)
        def b3(b):
            b.v = Var()
            b.v1 = Var(m.space)

            @b.Block(m.space)
            def b(bb):
                bb.v = Var(m.set)

        disc = TransformationFactory('dae.collocation')
        disc.apply_to(m, wrt=m.time, nfe=5, ncp=2, scheme='LAGRANGE-RADAU')
        disc.apply_to(m, wrt=m.space, nfe=5, ncp=2, scheme='LAGRANGE-RADAU')

        self.assertFalse(is_explicitly_indexed_by(m.v, m.time))
        self.assertTrue(is_explicitly_indexed_by(m.b.v2, m.space))
        self.assertTrue(is_explicitly_indexed_by(m.b.v3, m.time, m.space))

        self.assertFalse(is_in_block_indexed_by(m.v1, m.time))
        self.assertFalse(is_in_block_indexed_by(m.v2, m.set))
        self.assertTrue(is_in_block_indexed_by(m.b1[m.time[1]].v2, m.time))

        self.assertTrue(is_in_block_indexed_by(
            m.b2[m.time[1], m.space[1]].b.v1, m.time))
        self.assertTrue(is_in_block_indexed_by(
            m.b2[m.time[1], m.space[1]].b.v2, m.time))
        self.assertTrue(is_explicitly_indexed_by(
            m.b2[m.time[1], m.space[1]].b.v2, m.time))
        self.assertFalse(is_in_block_indexed_by(
            m.b2[m.time[1], m.space[1]].b.v1, m.set))

        self.assertFalse(is_in_block_indexed_by(
            m.b2[m.time[1], m.space[1]].b.v1, 
            m.space, stop_at=m.b2[m.time[1], m.space[1]]))

        # Explicit indexing with multi-dimensional set:
        self.assertTrue(is_explicitly_indexed_by(m.v4, m.time, m.set2))
        self.assertTrue(is_explicitly_indexed_by(m.v5, m.time, m.set2, m.space))

        # Implicit indexing with multi-dimensional set:
        self.assertTrue(is_in_block_indexed_by(
            m.b3['a', 1, m.time[1]].v, m.set2))
        self.assertTrue(is_in_block_indexed_by(
            m.b3['a', 1, m.time[1]].v, m.time))
        self.assertTrue(is_in_block_indexed_by(
            m.b3['a', 1, m.time[1]].v1[m.space[1]], m.set2))
        self.assertFalse(is_in_block_indexed_by(
            m.b3['a', 1, m.time[1]].v1[m.space[1]], m.space))
        self.assertTrue(is_in_block_indexed_by(
            m.b3['b', 2, m.time[2]].b[m.space[2]].v['b'], m.set2))
        self.assertTrue(is_in_block_indexed_by(
            m.b3['b', 2, m.time[2]].b[m.space[2]].v['b'], m.time))
        self.assertTrue(is_in_block_indexed_by(
            m.b3['b', 2, m.time[2]].b[m.space[2]].v['b'], m.space))
        self.assertFalse(is_in_block_indexed_by(
            m.b3['b', 2, m.time[2]].b[m.space[2]].v['b'], m.set))
        self.assertFalse(is_in_block_indexed_by(
            m.b3['b', 2, m.time[2]].b[m.space[2]].v['b'], m.time,
            stop_at=m.b3['b', 2, m.time[2]]))
        self.assertFalse(is_in_block_indexed_by(
            m.b3['b', 2, m.time[2]].b[m.space[2]].v['b'], m.time,
            stop_at=m.b3))