def create_master(model_data, k=1): """ Create the upper-level (master) of the bilevel problem Arguments: model_data: An Egret dict of dict that stores data for the power system k: A positive integer indicating the number of relays that can be attacked Returns: Tuple with the following values: model: A Pyomo model representing the algebraic form of the bilevel problem md: The model_data object associated to the model """ ### 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')) loads = dict(md.elements(element_type='load')) branches = dict(md.elements(element_type='branch')) ### create dictionaries across object attributes for an object of the same set type bus_attrs = md.attributes(element_type='bus') gen_attrs = md.attributes(element_type='generator') branch_attrs = md.attributes(element_type='branch') ### declare new Pyomo model model = pe.ConcreteModel() ### 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.) ### upper-level (attacker) variables decl.declare_var('load_shed', model, buses_with_loads, initialize=0.0, domain=pe.NonNegativeReals) decl.declare_var('delta_k', model, branch_attrs['names'], domain=pe.Binary) # line compromised decl.declare_var('delta_g', model, gen_attrs['names'], domain=pe.Binary) # gen compromised decl.declare_var('delta_b', model, bus_attrs['names'], domain=pe.Binary) # bus compromised decl.declare_var('u', model, buses_with_loads, domain=pe.Binary) # load available decl.declare_var('v', model, gen_attrs['names'], domain=pe.Binary) # generator available decl.declare_var('w', model, branch_attrs['names'], domain=pe.Binary) # line available ### upper-level constraints cons.declare_component_budget(model, k, branch_attrs['name'],\ gen_attrs['name'], bus_attrs['names']) # note that all k are costed equally in current implementation cons.declare_load_compromised(model, relay_load_tuple) cons.declare_load_uncompromised(model, buses_with_loads, load_relays) cons.declare_branch_compromised(model, relay_branch_tuple) cons.declare_branch_uncompromised(model, branch_attrs['names'], branch_relays) cons.declare_gen_compromised(model, relay_gen_tuple) cons.declare_gen_uncompromised(model, gen_attrs['names'], gen_relays) ### upper-level objective for interdiction problem (opposite to lower-level objective) model.obj = pe.Objective(expr=sum(model.load_shed[l] for l in buses_with_loads), sense=pe.maximize) return model, md
def create_master(model_data, omega, k=1): """ Create the upper-level (master) of the stochastic bilevel problem Arguments: model_data: An Egret dict of dict that stores data for the power system omega: A dict of scenario name <key> and probability per scenario <value> where the probabilities add to 1 k: A positive integer indicating the number of relays that can be attacked Returns: Tuple with the following values: model: A Pyomo model representing the algebraic form of the bilevel problem md: The model_data object associated to the model """ ### power system data md = model_data ### create dictionaries of object sets relays = dict(md.elements(element_type='relay')) gens = dict(md.elements(element_type='generator')) buses = dict(md.elements(element_type='bus')) loads = dict(md.elements(element_type='load')) branches = dict(md.elements(element_type='branch')) ### create dictionaries across object attributes for an object of the same set type relay_attrs = md.attributes(element_type='relay') gen_attrs = md.attributes(element_type='generator') branch_attrs = md.attributes(element_type='branch') ### declare new Pyomo model model = pe.ConcreteModel() ### scenarios scenarios = omega.keys() ### 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.) ### relay-to-power-device mappings relay_branches = utils.dict_of_relay_branches(relays, branches) branch_relays = utils.dict_of_branch_relays(relays, branches) relay_branch_tuple = utils.relay_branch_tuple(relay_branches) relay_gens = utils.dict_of_relay_gens(relays, gens) gen_relays = utils.dict_of_gen_relays(relays, gens) relay_gen_tuple = utils.relay_branch_tuple(relay_gens) relay_loads = utils.dict_of_relay_loads(relays, loads, buses_with_loads) load_relays = utils.dict_of_load_relays(relays, buses_with_loads) relay_load_tuple = utils.relay_branch_tuple(relay_loads) ### upper-level (attacker) variables scenarios_loads = pe.Set(initialize=scenarios) * pe.Set( initialize=buses_with_loads) decl.declare_var('load_shed', model, scenarios_loads, initialize=0.0, domain=pe.NonNegativeReals) decl.declare_var('delta', model, relay_attrs['names'], domain=pe.Binary, bounds=(0, 1)) # relays compromised decl.declare_var('u', model, buses_with_loads, domain=pe.Binary, bounds=(0, 1)) # load available decl.declare_var('v', model, gen_attrs['names'], domain=pe.Binary, bounds=(0, 1)) # generator available decl.declare_var('w', model, branch_attrs['names'], domain=pe.Binary, bounds=(0, 1)) # line available ### upper-level constraints cons.declare_budget( model, k, relays) # note that all k are costed equally in current implementation cons.declare_load_compromised(model, relay_load_tuple) cons.declare_load_uncompromised(model, buses_with_loads, load_relays) cons.declare_branch_compromised(model, relay_branch_tuple) cons.declare_branch_uncompromised(model, branch_attrs['names'], branch_relays) cons.declare_gen_compromised(model, relay_gen_tuple) cons.declare_gen_uncompromised(model, gen_attrs['names'], gen_relays) ### upper-level objective for stochastic interdiction problem (opposite to lower-level objective) model.obj = pe.Objective(expr=sum(omega[p]['probability'] * model.load_shed[p, l] for (p, l) in scenarios_loads), sense=pe.maximize) return model, md