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
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
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))
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)
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