Exemple #1
0
def create_submodel_hp_block(instance):
    """
    Creates highpoint relaxation with the given specified model; does
    not include any submodel or block that is deactivated
    """
    block = Block(concrete=True)

    # get the objective for the master problem
    for c in instance.component_objects(Objective, descend_into=False):
        block.add_component(c.name, Reference(c))

    # get the variables of the model (if there are more submodels, then
    # extraneous variables may be added to the block)
    for c in instance.component_objects(Var,
                                        sort=True,
                                        descend_into=True,
                                        active=True):
        block.add_component(c.name, Reference(c))

    # get the constraints from the main model
    for c in instance.component_objects(Constraint,
                                        sort=True,
                                        descend_into=True,
                                        active=True):
        block.add_component(c.name, Reference(c))

    # deactivate the highpoint relaxation
    block.deactivate()

    return block
Exemple #2
0
def create_submodel_hp_block(instance):
    """
    Creates highpoint relaxation with the given specified model; does
    not include any submodel or block that is deactivated
    """
    block = Block(concrete=True)

    # create a component map to keep track of id() for the
    # original object and the referenced object
    block._map = ComponentMap()

    # get the objective for the master problem
    for c in instance.component_objects(Objective, descend_into=False):
        ref = Reference(c)
        block.add_component(c.name, ref)
        block._map[c] = ref[None]

    # get the variables of the model (if there are more submodels, then
    # extraneous variables may be added to the block)
    for c in instance.component_objects(Var,
                                        sort=True,
                                        descend_into=True,
                                        active=True):
        if c.is_indexed():
            ref = Reference(c[...])
            block.add_component(c.name, ref)
            block._map[c] = ref
        else:
            ref = Reference(c)
            block.add_component(c.name, ref)
            block._map[c] = ref[None]

    # get the constraints from the main model
    for c in instance.component_objects(Constraint,
                                        sort=True,
                                        descend_into=True,
                                        active=True):
        if c.is_indexed():
            ref = Reference(c[...])
            block.add_component(c.name, ref)
            block._map[c] = ref
        else:
            ref = Reference(c)
            block.add_component(c.name, ref)
            block._map[c] = ref[None]

    # deactivate the highpoint relaxation
    # block.deactivate()

    return block
Exemple #3
0
def dataref(model, origin=None):
    """
    This helper function enables params and sets to be locally referenced
    from within a given block on the model, since all variables on
    a bilevel model exist only on the parent_block() for ConcreteModel()
    """
    # default to parent block
    if not origin:
        origin = model.root_block()

    for p in origin.component_objects(Param, descend_into=False):
        model.add_component(p.name, Reference(p))

    for s in origin.component_objects(Set, descend_into=False):
        model.add_component(s.name, Reference(s))
Exemple #4
0
def varref(model, origin=None, vars=None):
    """
    This helper function enables variables to be locally referenced
    from within a given block on the model, since all variables on
    a bilevel model exist only on the parent_block() for ConcreteModel()
    """

    # default to root block
    if not origin:
        origin = model.root_block()

    # intersection of 2 Pyomo var lists
    def _intersection(list1, list2):
        list3 = list()
        for l1 in list1:
            for l2 in list2:
                if l1.name == l2.name:
                    list3.append(l1)
        return list3

    # list of variables to add references for to the nested block; if vars is specified,
    # vars must exist on the origin block
    var_list = [
        var for var in origin.component_objects(Var, descend_into=False)
    ]
    if vars:
        var_list = _intersection(vars, var_list)

    # if variable name already exists on nested block, raise a RuntimeError (in Pyomo); otherwise add reference
    # for variable onto the nested block
    for _var in var_list:
        model.add_component(_var.name, Reference(_var))
    def _apply_to_impl(self, instance):
        self.variable_partitions = self._config.variable_partitions if \
                                   self._config.variable_partitions is not \
                                   None else {}
        self.partitioning_method = self._config.variable_partitioning_method

        # create a model to store the global constraints on that we will pass to
        # the compute_bounds_method, for if it wants them. We're making it a
        # separate model because we don't need it again
        global_constraints = ConcreteModel()
        for cons in instance.component_objects(
                Constraint,
                active=True,
                descend_into=Block,
                sort=SortComponents.deterministic):
            global_constraints.add_component(
                unique_component_name(
                    global_constraints,
                    cons.getname(fully_qualified=True,
                                 name_buffer=NAME_BUFFER)), Reference(cons))
        for var in instance.component_objects(
                Var,
                descend_into=(Block, Disjunct),
                sort=SortComponents.deterministic):
            global_constraints.add_component(
                unique_component_name(
                    global_constraints,
                    var.getname(fully_qualified=True,
                                name_buffer=NAME_BUFFER)), Reference(var))
        self._global_constraints = global_constraints

        # we can support targets as usual.
        targets = self._config.targets
        knownBlocks = {}
        if targets is None:
            targets = (instance, )
        # Disjunctions in targets will transform their Disjuncts which will in
        # turn transform all the GDP components declared on themselves. So we
        # only need to list root nodes of the GDP tree as targets, and
        # everything will be transformed (and in the correct order)
        targets = self._preprocess_targets(targets, instance, knownBlocks)
        for t in targets:
            if t.ctype is Disjunction:
                # After preprocessing, we know that this is not indexed.
                self._transform_disjunctionData(t, t.index())
            else:  # We know this is a DisjunctData after preprocessing
                self._transform_blockData(t)
Exemple #6
0
def create_explicit_subproblem(model,
                               subproblem,
                               model_data,
                               include_angle_diff_limits=False,
                               include_bigm=False):
    ### power system data
    md = model_data

    ### create dictionaries of object sets
    gens = dict(md.elements(element_type='generator'))
    buses = dict(md.elements(element_type='bus'))
    branches = dict(md.elements(element_type='branch'))
    loads = dict(md.elements(element_type='load'))
    shunts = dict(md.elements(element_type='shunt'))

    ### create dictionaries across object attributes for an object of the same set type
    gen_attrs = md.attributes(element_type='generator')
    bus_attrs = md.attributes(element_type='bus')
    branch_attrs = md.attributes(element_type='branch')

    inlet_branches_by_bus, outlet_branches_by_bus = \
        tx_utils.inlet_outlet_branches_by_bus(branches, buses)
    gens_by_bus = tx_utils.gens_by_bus(buses, gens)

    ### declare (and fix) the loads at the buses
    bus_p_loads, _ = tx_utils.dict_of_bus_loads(buses, loads)
    buses_with_loads = list(k for k in bus_p_loads.keys()
                            if bus_p_loads[k] != 0.)

    ### declare load shed variables
    decl.declare_var('load_shed',
                     subproblem,
                     buses_with_loads,
                     initialize=0.0,
                     domain=pe.NonNegativeReals)

    #libbus.declare_var_pl(model.subproblem, bus_attrs['names'], initialize=bus_p_loads)
    #model.subproblem.pl.fix()
    subproblem.pl = bus_p_loads

    ### declare the fixed shunts at the buses
    _, bus_gs_fixed_shunts = tx_utils.dict_of_bus_fixed_shunts(buses, shunts)

    ### declare the polar voltages
    va_bounds = {k: (-pi, pi) for k in bus_attrs['va']}
    va_init = {k: bus_attrs['va'][k] * (pi / 180) for k in bus_attrs['va']}
    libbus.declare_var_va(subproblem,
                          bus_attrs['names'],
                          initialize=bus_attrs['va'],
                          bounds=va_bounds)

    ### fix the reference bus
    ref_bus = md.data['system']['reference_bus']
    ref_angle = md.data['system']['reference_bus_angle']
    subproblem.va[ref_bus].fix(radians(ref_angle))

    ### declare the generator real power
    pg_init = {
        k: (gen_attrs['p_min'][k] + gen_attrs['p_max'][k]) / 2.0
        for k in gen_attrs['pg']
    }
    libgen.declare_var_pg(subproblem, gen_attrs['names'], initialize=pg_init)

    ### declare the current flows in the branches
    vr_init = {
        k: bus_attrs['vm'][k] * pe.cos(bus_attrs['va'][k])
        for k in bus_attrs['vm']
    }
    vj_init = {
        k: bus_attrs['vm'][k] * pe.sin(bus_attrs['va'][k])
        for k in bus_attrs['vm']
    }
    pf_init = dict()
    for branch_name, branch in branches.items():
        from_bus = branch['from_bus']
        to_bus = branch['to_bus']
        y_matrix = tx_calc.calculate_y_matrix_from_branch(branch)
        ifr_init = tx_calc.calculate_ifr(vr_init[from_bus], vj_init[from_bus],
                                         vr_init[to_bus], vj_init[to_bus],
                                         y_matrix)
        ifj_init = tx_calc.calculate_ifj(vr_init[from_bus], vj_init[from_bus],
                                         vr_init[to_bus], vj_init[to_bus],
                                         y_matrix)
        pf_init[branch_name] = tx_calc.calculate_p(ifr_init, ifj_init,
                                                   vr_init[from_bus],
                                                   vj_init[from_bus])

    libbranch.declare_var_pf(model=subproblem,
                             index_set=branch_attrs['names'],
                             initialize=pf_init)

    # need to include variable references on subproblem to variables, which exist on the master block
    #bi.components.varref(subproblem, origin = model)
    subproblem.add_component("u", Reference(model.u))
    subproblem.add_component("v", Reference(model.v))
    subproblem.add_component("w", Reference(model.w))
    if include_bigm:
        # create big-M
        _create_bigm(subproblem, md)
        ### declare the branch power flow disjuncts
        subcons.declare_eq_branch_power_btheta_approx_bigM(
            model=subproblem,
            index_set=branch_attrs['names'],
            branches=branches)

        ### declare the real power flow limits
        p_max = {k: branches[k]['rating_long_term'] for k in branches.keys()}
        subcons.declare_ineq_p_branch_thermal_lbub_switch(
            model=subproblem,
            index_set=branch_attrs['names'],
            p_thermal_limits=p_max)

    else:
        ### declare the branch power flow with indicator variable in the bilinear term
        subcons.declare_eq_branch_power_btheta_approx_nonlin(
            model=subproblem,
            index_set=branch_attrs['names'],
            branches=branches)

        ### declare the real power flow limits
        p_max = {k: branches[k]['rating_long_term'] for k in branches.keys()}
        libbranch.declare_ineq_p_branch_thermal_lbub(
            model=subproblem,
            index_set=branch_attrs['names'],
            branches=branches,
            p_thermal_limits=p_max,
            approximation_type=ApproximationType.BTHETA)

    ### declare the load shed
    subcons.declare_ineq_load_shed(model=subproblem,
                                   index_set=buses_with_loads)

    ### declare the generator compromised
    subcons.declare_ineq_gen(model=subproblem,
                             index_set=gen_attrs['names'],
                             gens=gens)

    ### declare the p balance
    rhs_kwargs = {'include_feasibility_slack_neg': 'load_shed'}
    libbus.declare_eq_p_balance_dc_approx(
        model=subproblem,
        index_set=bus_attrs['names'],
        bus_p_loads=bus_p_loads,
        gens_by_bus=gens_by_bus,
        bus_gs_fixed_shunts=bus_gs_fixed_shunts,
        inlet_branches_by_bus=inlet_branches_by_bus,
        outlet_branches_by_bus=outlet_branches_by_bus,
        approximation_type=ApproximationType.BTHETA,
        **rhs_kwargs)

    ### declare angle difference limits on interconnected buses
    if include_angle_diff_limits:
        libbranch.declare_ineq_angle_diff_branch_lbub(
            model=subproblem,
            index_set=branch_attrs['names'],
            branches=branches,
            coordinate_type=CoordinateType.POLAR)

    ### lower-level objective for interdiction problem (opposite to upper-level objective)
    subproblem.obj = pe.Objective(expr=sum(subproblem.load_shed[l]
                                           for l in buses_with_loads),
                                  sense=pe.minimize)

    return model, md
    def _transform_disjunct(self, disjunct, partition, transBlock):
        # deactivated -> either we've already transformed or user deactivated
        if not disjunct.active:
            if disjunct.indicator_var.is_fixed():
                if not value(disjunct.indicator_var):
                    # The user cleanly deactivated the disjunct: there
                    # is nothing for us to do here.
                    return
                else:
                    raise GDP_Error(
                        "The disjunct '%s' is deactivated, but the "
                        "indicator_var is fixed to %s. This makes no sense." %
                        (disjunct.name, value(disjunct.indicator_var)))
            if disjunct._transformation_block is None:
                raise GDP_Error(
                    "The disjunct '%s' is deactivated, but the "
                    "indicator_var is not fixed and the disjunct does not "
                    "appear to have been relaxed. This makes no sense. "
                    "(If the intent is to deactivate the disjunct, fix its "
                    "indicator_var to False.)" % (disjunct.name, ))

        if disjunct._transformation_block is not None:
            # we've transformed it, which means this is the second time it's
            # appearing in a Disjunction
            raise GDP_Error(
                "The disjunct '%s' has been transformed, but a disjunction "
                "it appears in has not. Putting the same disjunct in "
                "multiple disjunctions is not supported." % disjunct.name)

        transformed_disjunct = Disjunct()
        disjunct._transformation_block = weakref_ref(transformed_disjunct)
        transBlock.add_component(
            unique_component_name(
                transBlock,
                disjunct.getname(fully_qualified=True,
                                 name_buffer=NAME_BUFFER)),
            transformed_disjunct)
        # If the original has an indicator_var fixed to something, fix this one
        # too.
        if disjunct.indicator_var.fixed:
            transformed_disjunct.indicator_var.fix(
                value(disjunct.indicator_var))

        # need to transform inner Disjunctions first (before we complain about
        # active Disjuncts)
        for disjunction in disjunct.component_data_objects(
                Disjunction,
                active=True,
                sort=SortComponents.deterministic,
                descend_into=Block):
            self._transform_disjunctionData(disjunction, disjunction.index(),
                                            None, transformed_disjunct)

        # create references to any variables declared here on the transformed
        # Disjunct (this will include the indicator_var) NOTE that we will not
        # have to do this when #1032 is implemented for the writers. But right
        # now, we are going to deactivate this and hide it from the active
        # subtree, so we need to be safe.
        for var in disjunct.component_objects(Var,
                                              descend_into=Block,
                                              active=None):
            transformed_disjunct.add_component(
                unique_component_name(
                    transformed_disjunct,
                    var.getname(fully_qualified=True,
                                name_buffer=NAME_BUFFER)), Reference(var))

        # Since this transformation is GDP -> GDP and it is based on
        # partitioning algebraic expressions, we will copy over
        # LogicalConstraints that may be on the Disjuncts, without transforming
        # them. This is consistent with our handling of nested Disjunctions,
        # which also remain nested, though their algebraic constraints may be
        # transformed. Note that we are not using References because when asked
        # who their parent block is, we would like these constraints to answer
        # that it is the transformed Disjunct.
        logical_constraints = LogicalConstraintList()
        transformed_disjunct.add_component(
            unique_component_name(transformed_disjunct, 'logical_constraints'),
            logical_constraints)
        for cons in disjunct.component_data_objects(LogicalConstraint,
                                                    descend_into=Block,
                                                    active=None):
            # Add a copy of it on the new Disjunct
            logical_constraints.add(cons.expr)

            # deactivate to mark as transformed (so we don't hit it in the loop
            # below)
            cons.deactivate()

        # transform everything else
        for obj in disjunct.component_data_objects(
                active=True, sort=SortComponents.deterministic,
                descend_into=Block):
            handler = self.handlers.get(obj.ctype, None)
            if not handler:
                if handler is None:
                    raise GDP_Error(
                        "No partition_disjuncts transformation handler "
                        "registered "
                        "for modeling components of type %s. If your "
                        "disjuncts contain non-GDP Pyomo components that "
                        "require transformation, please transform them first."
                        % obj.ctype)
                continue
            # we are really only transforming constraints and checking for
            # anything nutty (active Disjuncts, etc) here, so pass through what
            # is necessary for transforming Constraints
            handler(obj, disjunct, transformed_disjunct, transBlock, partition)

        disjunct._deactivate_without_fixing_indicator()
        return transformed_disjunct