def generate_model(model_data, uc_formulation, relax_binaries=False, ptdf_options=None): """ returns a UC uc_formulation as an abstract model with the components specified in a UCFormulation, with the option to relax the binary variables. Parameters ---------- model_data : egret.data.ModelData uc_formulation : egret.model_components.model_generator.UCFormulation The named tuple with the specified formulation relax_binaries : bool, optional Relaxes all binary variables in the constructed model, resulting in a continuous problem. Default is False. ptdf_options : dict, optional Dictionary of options for ptdf transmission model Returns ------- pyomo.environ.ConcreteModel : The unit commitment formulation specified with the data from model_data """ md = model_data.clone_in_service() scale_ModelData_to_pu(md, inplace=True) return _generate_model( md, *_get_formulation_from_UCFormulation(uc_formulation), relax_binaries, ptdf_options)
def test_scale_unscale(): md = ModelData.read(scuc_fn) ## do type conversions original_base_MVA = md.data['system']['baseMVA'] md.data['system']['baseMVA'] = 1. scale_ModelData_to_pu(md, inplace=True) md.data['system']['baseMVA'] = original_base_MVA md_transformed = scale_ModelData_to_pu(md, inplace=False) # test inplace flag assert id(md.data) != id(md_transformed.data) unscale_ModelData_to_pu(md_transformed, inplace=True) assert md.data['system'] == md_transformed.data['system'] for esn, esd in md.data['elements'].items(): for en, ed in esd.items(): assert ed == md_transformed.data['elements'][esn][en] for esn, esd in md_transformed.data['elements'].items(): for en, ed in esd.items(): assert ed == md.data['elements'][esn][en]
def reset_unit_commitment_penalties(m): scale_ModelData_to_pu(m.model_data, inplace=True) _reconstruct_pyomo_component(m.LoadMismatchPenalty) for param in m.component_objects(Param): if param.mutable and isinstance(param._rule, (ScalarCallInitializer, IndexedCallInitializer)) \ and (param._rule._fcn.__name__ == 'penalty_rule'): _reconstruct_pyomo_component(param) unscale_ModelData_to_pu(m.model_data, inplace=True)
def create_economic_dispatch_approx_model(model_data): md = tx_utils.scale_ModelData_to_pu(model_data) 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')) gen_attrs = md.attributes(element_type='generator') bus_attrs = md.attributes(element_type='bus') branch_attrs = md.attributes(element_type='branch') load_attrs = md.attributes(element_type='load') shunt_attrs = md.attributes(element_type='shunt') inlet_branches_by_bus, outlet_branches_by_bus = \ tx_utils.inlet_outlet_branches_by_bus(branches, buses) gens_by_bus = tx_utils.gens_in_service_by_bus(buses, gens) model = pe.ConcreteModel() ### declare (and fix) the loads at the buses bus_p_loads, _ = tx_utils.dict_of_bus_loads(buses, loads) libbus.declare_var_pl(model, bus_attrs['names'], initialize=bus_p_loads) model.pl.fix() ### declare the fixed shunts at the buses _, bus_gs_fixed_shunts = tx_utils.dict_of_bus_fixed_shunts(buses, shunts) ### 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(model, gen_attrs['names'], initialize=pg_init, bounds=zip_items(gen_attrs['p_min'], gen_attrs['p_max'])) ### declare the p balance libbus.declare_eq_p_balance_ed(model=model, 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) ### declare the generator cost objective libgen.declare_expression_pgqg_operating_cost(model=model, index_set=gen_attrs['names'], p_costs=gen_attrs['p_cost']) obj_expr = sum(model.pg_operating_cost[gen_name] for gen_name in model.pg_operating_cost) model.obj = pe.Objective(expr=obj_expr) return model
def generate_model(model_data, uc_formulation, relax_binaries=False, ptdf_options=None, PTDF_matrix_dict=None): """ returns a UC uc_formulation as an abstract model with the components specified in a UCFormulation, with the option to relax the binary variables. Parameters ---------- model_data : egret.data.ModelData uc_formulation : egret.model_components.model_generator.UCFormulation The named tuple with the specified formulation relax_binaries : bool, optional Relaxes all binary variables in the constructed model, resulting in a continuous problem. Default is False. ptdf_options : dict, optional Dictionary of options for ptdf transmission model PTDF_matrix_dict : dict, optional Dictionary of egret.data.ptdf_utils.PTDFMatrix objects for use in model construction. WARNING: Nearly zero checking is done on the consistency of this object with the model_data. Use with extreme caution! Returns ------- pyomo.environ.ConcreteModel : The unit commitment formulation specified with the data from model_data """ md = model_data.clone_in_service() scale_ModelData_to_pu(md, inplace=True) return _generate_model( md, *_get_formulation_from_UCFormulation(uc_formulation), relax_binaries, ptdf_options, PTDF_matrix_dict)
def solve_stochastic_bilevel_nk(model_data, solver, solver_tee=True, return_model=False, return_results=False, **kwargs): ''' Create and solve a new worst-case attacker defender as a stochastic bilevel interdiction problem. Parameters ---------- model_data : egret.data.ModelData An egret ModelData object with the appropriate data loaded. solver : str or pyomo.opt.base.solvers.OptSolver Either a string specifying a pyomo solver name, or an instantiated pyomo solver solver_tee : bool (optional) Display solver log. Default is True. return_model : bool (optional) If True, returns the pyomo model object return_results : bool (optional) If True, returns the pyomo results object kwargs : dictionary (optional) Additional arguments for building model ''' import random import math import pyomo.environ as pe from pyomo.environ import value from egret.model_library.transmission.tx_utils import \ scale_ModelData_to_pu, unscale_ModelData_to_pu seed = 23 random.seed(seed) # repeatable md = model_data.clone_in_service() scale_ModelData_to_pu(md, inplace=True) ### pop from kwargs the number k for N-k contingency of relay IPs attack_budget_k = kwargs.pop('attack_budget_k', 1) omega = kwargs.pop('omega', None) if not omega: raise Exception( 'User must specify a dictionary of scenario name <key>, probability <value> pairs.' ) ### create upper-level of the bilevel problem m, md = create_master(md, omega, attack_budget_k) m.OmegaSet = pe.Set(initialize=omega.keys()) m.Scenarios = pe.Block(m.OmegaSet) for p in m.OmegaSet: _md_uncertain = md.clone() per_l, per_u = omega[p]['percentage_bounds'] loads = dict(_md_uncertain.elements(element_type='load')) for _, load_dict in loads.items(): _variation_fraction = random.uniform(per_l, per_u) load_dict['p_load'] = _variation_fraction * load_dict['p_load'] ### declare lower-level as a PAO (Pyomo-extension) submodel; ### be explicit in specifying upper-level variables that appear in this model subproblem = bi.SubModel(fixed=(m.u, m.v, m.w)) ### create lower-level of the bilevel problem m.Scenarios[p].sub = subproblem m, _ = create_explicit_subproblem(m, subproblem, _md_uncertain, p, include_bigm=False) ### use PAO (Pyomo-extension) to do the following: ### 1. Transform the lower-level primal problem into it's corresponding dual problem ### 2. Apply Pyomo.GDP transformations to handle bilinear terms (Big-M) ### 3. Solve formulation (upper-level primal with lower-level dual) as a single level MILP ### 4. Take optimal solution from MILP, fix upper-level variables that appear in the ### lower-level problem, and resolve to determine primal variable solution for the lower-level weights = dict() for p in m.OmegaSet: name = m.Scenarios[p].name + '.sub' weights[name] = omega[p]['probability'] kwargs = {'subproblem_objective_weights': weights} opt = pe.SolverFactory('pao.bilevel.stochastic_ld', solver=solver) ## need to fine-tune bigM and mipgap -- make sure that both the solve and resolve result in the same ## best objective opt.options.setdefault('bigM', 100) opt.options.setdefault('mipgap', 0.001) results = opt.solve(m, **kwargs, tee=solver_tee) objective = md.data['system']['baseMVA'] * value(m.obj) print('~~~~~~~~~~ solution stats ~~~~~~~~~~~') print('objective: {} MW expected load shed'.format(objective)) _relay_list = '' for name, val in m.delta.items(): if val == 1: _relay_list += name + " " print(' relay(s) compromised: {}'.format(_relay_list)) unscale_ModelData_to_pu(md, inplace=True) ### return model_data (md), model (m), and/or results (results) objects if return_model and return_results: return md, m, results elif return_model: return md, m elif return_results: return md, results return md
def create_hot_start_lpac_model(model_data, voltages, lower_bound=-pi / 3, upper_bound=pi / 3, cosine_segment_count=20, include_feasibility_slack=False, mode="uniform"): """ The hot start LPAC model assumes that voltages are known, e.g. from an AC base point solution. """ ###Grid data md = model_data.clone_in_service() tx_utils.scale_ModelData_to_pu(md, inplace=True) 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')) 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) model = pe.ConcreteModel() ###declare (and fix) the voltage magnitudes and squares of voltage magnitudes bus_voltage_magnitudes = voltages #Assumes voltages is given as a dictionary libbus.declare_var_vm(model, bus_attrs['names'], initialize=bus_voltage_magnitudes) model.vm.fix() libbus.declare_var_vmsq( model=model, index_set=bus_attrs['names'], initialize={k: v**2 for k, v in bus_attrs['vm'].items()}, bounds=zip_items({k: v**2 for k, v in bus_attrs['v_min'].items()}, {k: v**2 for k, v in bus_attrs['v_max'].items()})) ### declare the polar voltages libbus.declare_var_va(model, bus_attrs['names'], initialize=bus_attrs['va']) ### declare the cosine approximation variables cos_hat_bounds = {k: (0, 1) for k in branch_attrs['names']} decl.declare_var('cos_hat', model, branch_attrs['names'], bounds=cos_hat_bounds) ### fix the reference bus ref_bus = md.data['system']['reference_bus'] #ref_angle = md.data['system']['reference_bus_angle'] model.va[ref_bus].fix(radians(0.0)) ### declare the fixed shunts at the buses bus_bs_fixed_shunts, bus_gs_fixed_shunts = tx_utils.dict_of_bus_fixed_shunts( buses, shunts) ### declare (and fix) the loads at the buses bus_p_loads, bus_q_loads = tx_utils.dict_of_bus_loads(buses, loads) libbus.declare_var_pl(model, bus_attrs['names'], initialize=bus_p_loads) libbus.declare_var_ql(model, bus_attrs['names'], initialize=bus_q_loads) model.pl.fix() model.ql.fix() ### include the feasibility slack for the bus balances p_rhs_kwargs = {} q_rhs_kwargs = {} if include_feasibility_slack: p_rhs_kwargs, q_rhs_kwargs, penalty_expr = _include_feasibility_slack( model, bus_attrs, gen_attrs, bus_p_loads, bus_q_loads) ### declare the generator real and reactive 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(model, gen_attrs['names'], initialize=pg_init, bounds=zip_items(gen_attrs['p_min'], gen_attrs['p_max'])) qg_init = { k: (gen_attrs['q_min'][k] + gen_attrs['q_max'][k]) / 2.0 for k in gen_attrs['qg'] } libgen.declare_var_qg(model, gen_attrs['names'], initialize=qg_init, bounds=zip_items(gen_attrs['q_min'], gen_attrs['q_max'])) ### 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'] } s_max = {k: branches[k]['rating_long_term'] for k in branches.keys()} s_lbub = dict() for k in branches.keys(): if s_max[k] is None: s_lbub[k] = (None, None) else: s_lbub[k] = (-s_max[k], s_max[k]) pf_bounds = s_lbub pt_bounds = s_lbub qf_bounds = s_lbub qt_bounds = s_lbub pf_init = dict() pt_init = dict() qf_init = dict() qt_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) itr_init = tx_calc.calculate_itr(vr_init[from_bus], vj_init[from_bus], vr_init[to_bus], vj_init[to_bus], y_matrix) itj_init = tx_calc.calculate_itj(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]) pt_init[branch_name] = tx_calc.calculate_p(itr_init, itj_init, vr_init[to_bus], vj_init[to_bus]) qf_init[branch_name] = tx_calc.calculate_q(ifr_init, ifj_init, vr_init[from_bus], vj_init[from_bus]) qt_init[branch_name] = tx_calc.calculate_q(itr_init, itj_init, vr_init[to_bus], vj_init[to_bus]) libbranch.declare_var_pf(model=model, index_set=branch_attrs['names'], initialize=pf_init, bounds=pf_bounds) libbranch.declare_var_pt(model=model, index_set=branch_attrs['names'], initialize=pt_init, bounds=pt_bounds) libbranch.declare_var_qf(model=model, index_set=branch_attrs['names'], initialize=qf_init, bounds=qf_bounds) libbranch.declare_var_qt(model=model, index_set=branch_attrs['names'], initialize=qt_init, bounds=qt_bounds) #################### #Constraints #################### ###Balance equations in a bus #p balance libbus.declare_eq_p_balance(model=model, 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, **p_rhs_kwargs) #q balance libbus.declare_eq_q_balance(model=model, index_set=bus_attrs['names'], bus_q_loads=bus_q_loads, gens_by_bus=gens_by_bus, bus_bs_fixed_shunts=bus_bs_fixed_shunts, inlet_branches_by_bus=inlet_branches_by_bus, outlet_branches_by_bus=outlet_branches_by_bus, **q_rhs_kwargs) ### Power in a branch branch_con_set = decl.declare_set('_con_eq_p_q_lpac_branch_power', model, branch_attrs['names']) model.eq_pf_branch_t = pe.Constraint(branch_con_set) model.eq_pt_branch_t = pe.Constraint(branch_con_set) model.eq_qf_branch_t = pe.Constraint(branch_con_set) model.eq_qt_branch_t = pe.Constraint(branch_con_set) for branch_name in branch_con_set: branch = branches[branch_name] from_bus = branch['from_bus'] to_bus = branch['to_bus'] g = tx_calc.calculate_conductance(branch) b = tx_calc.calculate_susceptance(branch) model.eq_pf_branch_t[branch_name] = \ model.pf[branch_name] == \ g*model.vmsq[from_bus] - model.vm[from_bus]*model.vm[to_bus]*(g * model.cos_hat[branch_name] + b * (model.va[from_bus] - model.va[to_bus])) model.eq_pt_branch_t[branch_name] = \ model.pt[branch_name] == \ g*model.vmsq[to_bus] - model.vm[from_bus]*model.vm[to_bus]*(g * model.cos_hat[branch_name] + b * (model.va[to_bus] - model.va[from_bus])) model.eq_qf_branch_t[branch_name] = \ model.qf[branch_name] == \ -b*model.vmsq[from_bus] - model.vm[from_bus]*model.vm[to_bus]*(g*(model.va[from_bus] - model.va[to_bus]) - b*model.cos_hat[branch_name]) model.eq_qt_branch_t[branch_name] = \ model.qt[branch_name] == \ -b*model.vmsq[to_bus] - model.vm[from_bus]*model.vm[to_bus]*(g*(model.va[to_bus] - model.va[from_bus]) - b*model.cos_hat[branch_name]) ### Piecewise linear cosine constraints model.N = pe.Set(initialize=list(range(cosine_segment_count + 1))) declare_pwl_cosine_bounds(model=model, index_set=branch_attrs['names'], branches=branches, lower_bound=lower_bound, upper_bound=upper_bound, cosine_segment_count=cosine_segment_count, mode=mode) ### Objective is to maximize cosine hat variables obj_expr = sum(model.cos_hat[branch_name] for branch_name in branch_attrs['names']) if include_feasibility_slack: obj_expr += penalty_expr model.obj = pe.Objective(expr=obj_expr) return model, md
def create_cold_start_lpac_model(model_data, cosine_segment_count=20, lower_bound=-pi / 3, upper_bound=pi / 3, include_feasibility_slack=False, mode="uniform"): """ The cold start LPAC model assumes that no target voltages are available and that all voltages are initially approximated as 1 pu. """ ###Grid data md = model_data.clone_in_service() tx_utils.scale_ModelData_to_pu(md, inplace=True) 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')) 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) model = pe.ConcreteModel() ### declare the polar voltages libbus.declare_var_va(model, bus_attrs['names'], initialize=bus_attrs['va']) libbus.declare_var_vmsq( model=model, index_set=bus_attrs['names'], initialize={k: v**2 for k, v in bus_attrs['vm'].items()}, bounds=zip_items({k: v**2 for k, v in bus_attrs['v_min'].items()}, {k: v**2 for k, v in bus_attrs['v_max'].items()})) ### declare the voltage change variables decl.declare_var('phi', model, bus_attrs['names']) ### declare the cosine approximation variables cos_hat_bounds = {k: (0, 1) for k in branch_attrs['names']} decl.declare_var('cos_hat', model, branch_attrs['names'], bounds=cos_hat_bounds) ### fix the reference bus ref_bus = md.data['system']['reference_bus'] #ref_angle = md.data['system']['reference_bus_angle'] model.va[ref_bus].fix(radians(0.0)) ### declare (and fix) the loads at the buses bus_p_loads, bus_q_loads = tx_utils.dict_of_bus_loads(buses, loads) libbus.declare_var_pl(model, bus_attrs['names'], initialize=bus_p_loads) libbus.declare_var_ql(model, bus_attrs['names'], initialize=bus_q_loads) model.pl.fix() model.ql.fix() ### declare the fixed shunts at the buses bus_bs_fixed_shunts, bus_gs_fixed_shunts = tx_utils.dict_of_bus_fixed_shunts( buses, shunts) ### include the feasibility slack for the bus balances p_rhs_kwargs = {} q_rhs_kwargs = {} if include_feasibility_slack: p_rhs_kwargs, q_rhs_kwargs, penalty_expr = _include_feasibility_slack( model, bus_attrs, gen_attrs, bus_p_loads, bus_q_loads) ### declare the generator real and reactive 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(model, gen_attrs['names'], initialize=pg_init, bounds=zip_items(gen_attrs['p_min'], gen_attrs['p_max'])) qg_init = { k: (gen_attrs['q_min'][k] + gen_attrs['q_max'][k]) / 2.0 for k in gen_attrs['qg'] } libgen.declare_var_qg(model, gen_attrs['names'], initialize=qg_init, bounds=zip_items(gen_attrs['q_min'], gen_attrs['q_max'])) ### 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'] } s_max = {k: branches[k]['rating_long_term'] for k in branches.keys()} s_lbub = dict() for k in branches.keys(): if s_max[k] is None: s_lbub[k] = (None, None) else: s_lbub[k] = (-s_max[k], s_max[k]) pf_bounds = s_lbub pt_bounds = s_lbub qf_bounds = s_lbub qt_bounds = s_lbub pf_init = dict() pt_init = dict() qf_init = dict() qt_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) itr_init = tx_calc.calculate_itr(vr_init[from_bus], vj_init[from_bus], vr_init[to_bus], vj_init[to_bus], y_matrix) itj_init = tx_calc.calculate_itj(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]) pt_init[branch_name] = tx_calc.calculate_p(itr_init, itj_init, vr_init[to_bus], vj_init[to_bus]) qf_init[branch_name] = tx_calc.calculate_q(ifr_init, ifj_init, vr_init[from_bus], vj_init[from_bus]) qt_init[branch_name] = tx_calc.calculate_q(itr_init, itj_init, vr_init[to_bus], vj_init[to_bus]) libbranch.declare_var_pf(model=model, index_set=branch_attrs['names'], initialize=pf_init, bounds=pf_bounds) libbranch.declare_var_pt(model=model, index_set=branch_attrs['names'], initialize=pt_init, bounds=pt_bounds) libbranch.declare_var_qf(model=model, index_set=branch_attrs['names'], initialize=qf_init, bounds=qf_bounds) libbranch.declare_var_qt(model=model, index_set=branch_attrs['names'], initialize=qt_init, bounds=qt_bounds) ################################ #Constraints ################################ ### Balance equations at a bus (based on Kirchhoff Current Law) #Should be able to just use DC OPF approximation of B-theta type? ### declare the p balance libbus.declare_eq_p_balance(model=model, 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, **p_rhs_kwargs) #Need one also for q balance libbus.declare_eq_q_balance(model=model, index_set=bus_attrs['names'], bus_q_loads=bus_q_loads, gens_by_bus=gens_by_bus, bus_bs_fixed_shunts=bus_bs_fixed_shunts, inlet_branches_by_bus=inlet_branches_by_bus, outlet_branches_by_bus=outlet_branches_by_bus, **q_rhs_kwargs) ### Constraints for power in a branch branch_con_set = decl.declare_set('_con_eq_p_q_lpac_branch_power', model, branch_attrs['names']) model.eq_pf_branch_t = pe.Constraint(branch_con_set) model.eq_pt_branch_t = pe.Constraint(branch_con_set) model.eq_qf_branch_t = pe.Constraint(branch_con_set) model.eq_qt_branch_t = pe.Constraint(branch_con_set) for branch_name in branch_con_set: branch = branches[branch_name] from_bus = branch['from_bus'] to_bus = branch['to_bus'] g = tx_calc.calculate_conductance(branch) b = tx_calc.calculate_susceptance(branch) model.eq_pf_branch_t[branch_name] = \ model.pf[branch_name] == \ g - g * model.cos_hat[branch_name] - b * (model.va[from_bus] - model.va[to_bus]) model.eq_pt_branch_t[branch_name] = \ model.pt[branch_name] == \ g - g * model.cos_hat[branch_name] - b * (model.va[to_bus] - model.va[from_bus]) model.eq_qf_branch_t[branch_name] = \ model.qf[branch_name] == \ -b - g*(model.va[from_bus] - model.va[to_bus]) + b*model.cos_hat[branch_name] - b*(model.phi[from_bus] - model.phi[to_bus]) model.eq_qt_branch_t[branch_name] = \ model.qt[branch_name] == \ -b - g*(model.va[to_bus] - model.va[from_bus]) +b*model.cos_hat[branch_name] - b*(model.phi[to_bus] - model.phi[from_bus]) ### Piecewise linear cosine constraints model.N = pe.Set(initialize=list(range(cosine_segment_count + 1))) declare_pwl_cosine_bounds(model=model, index_set=branch_attrs['names'], branches=branches, lower_bound=lower_bound, upper_bound=upper_bound, cosine_segment_count=cosine_segment_count, mode=mode) ### Objective is to maximize cosine hat variables # obj_expr = sum(model.cos_hat[branch_name] for branch_name in branch_attrs['names']) # if include_feasibility_slack: # obj_expr += penalty_expr # model.obj = pe.Objective(expr=obj_expr) ###Objective to match with acopf.py ### declare the generator cost objective libgen.declare_expression_pgqg_operating_cost(model=model, index_set=gen_attrs['names'], p_costs=gen_attrs['p_cost'], q_costs=gen_attrs.get( 'q_cost', None)) obj_expr = sum(model.pg_operating_cost[gen_name] for gen_name in model.pg_operating_cost) if include_feasibility_slack: obj_expr += penalty_expr if hasattr(model, 'qg_operating_cost'): obj_expr += sum(model.qg_operating_cost[gen_name] for gen_name in model.qg_operating_cost) model.obj = pe.Objective(expr=obj_expr) return model, md
def create_psv_acopf_model(model_data, include_feasibility_slack=False): md = model_data.clone_in_service() tx_utils.scale_ModelData_to_pu(md, inplace=True) 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')) gen_attrs = md.attributes(element_type='generator') bus_attrs = md.attributes(element_type='bus') branch_attrs = md.attributes(element_type='branch') load_attrs = md.attributes(element_type='load') shunt_attrs = md.attributes(element_type='shunt') 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) model = pe.ConcreteModel() ### declare (and fix) the loads at the buses bus_p_loads, bus_q_loads = tx_utils.dict_of_bus_loads(buses, loads) libbus.declare_var_pl(model, bus_attrs['names'], initialize=bus_p_loads) libbus.declare_var_ql(model, bus_attrs['names'], initialize=bus_q_loads) model.pl.fix() model.ql.fix() ### declare the fixed shunts at the buses bus_bs_fixed_shunts, bus_gs_fixed_shunts = tx_utils.dict_of_bus_fixed_shunts( buses, shunts) ### declare the polar voltages libbus.declare_var_vm(model, bus_attrs['names'], initialize=bus_attrs['vm'], bounds=zip_items(bus_attrs['v_min'], bus_attrs['v_max'])) libbus.declare_expr_vmsq(model=model, index_set=bus_attrs['names'], coordinate_type=CoordinateType.POLAR) va_bounds = {k: (-pi, pi) for k in bus_attrs['va']} libbus.declare_var_va(model, bus_attrs['names'], initialize=bus_attrs['va'], bounds=va_bounds) ### include the feasibility slack for the bus balances p_rhs_kwargs = {} q_rhs_kwargs = {} if include_feasibility_slack: p_rhs_kwargs, q_rhs_kwargs, penalty_expr = _include_feasibility_slack( model, bus_attrs, gen_attrs, bus_p_loads, bus_q_loads) ### fix the reference bus ref_bus = md.data['system']['reference_bus'] ref_angle = md.data['system']['reference_bus_angle'] model.va[ref_bus].fix(radians(ref_angle)) ### declare the generator real and reactive 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(model, gen_attrs['names'], initialize=pg_init, bounds=zip_items(gen_attrs['p_min'], gen_attrs['p_max'])) qg_init = { k: (gen_attrs['q_min'][k] + gen_attrs['q_max'][k]) / 2.0 for k in gen_attrs['qg'] } libgen.declare_var_qg(model, gen_attrs['names'], initialize=qg_init, bounds=zip_items(gen_attrs['q_min'], gen_attrs['q_max'])) ### 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'] } s_max = {k: branches[k]['rating_long_term'] for k in branches.keys()} s_lbub = dict() for k in branches.keys(): if s_max[k] is None: s_lbub[k] = (None, None) else: s_lbub[k] = (-s_max[k], s_max[k]) pf_bounds = s_lbub pt_bounds = s_lbub qf_bounds = s_lbub qt_bounds = s_lbub pf_init = dict() pt_init = dict() qf_init = dict() qt_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) itr_init = tx_calc.calculate_itr(vr_init[from_bus], vj_init[from_bus], vr_init[to_bus], vj_init[to_bus], y_matrix) itj_init = tx_calc.calculate_itj(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]) pt_init[branch_name] = tx_calc.calculate_p(itr_init, itj_init, vr_init[to_bus], vj_init[to_bus]) qf_init[branch_name] = tx_calc.calculate_q(ifr_init, ifj_init, vr_init[from_bus], vj_init[from_bus]) qt_init[branch_name] = tx_calc.calculate_q(itr_init, itj_init, vr_init[to_bus], vj_init[to_bus]) libbranch.declare_var_pf(model=model, index_set=branch_attrs['names'], initialize=pf_init, bounds=pf_bounds) libbranch.declare_var_pt(model=model, index_set=branch_attrs['names'], initialize=pt_init, bounds=pt_bounds) libbranch.declare_var_qf(model=model, index_set=branch_attrs['names'], initialize=qf_init, bounds=qf_bounds) libbranch.declare_var_qt(model=model, index_set=branch_attrs['names'], initialize=qt_init, bounds=qt_bounds) ### declare the branch power flow constraints bus_pairs = zip_items(branch_attrs['from_bus'], branch_attrs['to_bus']) unique_bus_pairs = list( OrderedDict((val, None) for idx, val in bus_pairs.items()).keys()) libbranch.declare_expr_c(model=model, index_set=unique_bus_pairs, coordinate_type=CoordinateType.POLAR) libbranch.declare_expr_s(model=model, index_set=unique_bus_pairs, coordinate_type=CoordinateType.POLAR) libbranch.declare_eq_branch_power(model=model, index_set=branch_attrs['names'], branches=branches) ### declare the pq balances libbus.declare_eq_p_balance(model=model, 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, **p_rhs_kwargs) libbus.declare_eq_q_balance(model=model, index_set=bus_attrs['names'], bus_q_loads=bus_q_loads, gens_by_bus=gens_by_bus, bus_bs_fixed_shunts=bus_bs_fixed_shunts, inlet_branches_by_bus=inlet_branches_by_bus, outlet_branches_by_bus=outlet_branches_by_bus, **q_rhs_kwargs) ### declare the thermal limits libbranch.declare_ineq_s_branch_thermal_limit( model=model, index_set=branch_attrs['names'], branches=branches, s_thermal_limits=s_max, flow_type=FlowType.POWER) ### declare the voltage min and max inequalities libbus.declare_ineq_vm_bus_lbub(model=model, index_set=bus_attrs['names'], buses=buses, coordinate_type=CoordinateType.POLAR) ### declare angle difference limits on interconnected buses libbranch.declare_ineq_angle_diff_branch_lbub( model=model, index_set=branch_attrs['names'], branches=branches, coordinate_type=CoordinateType.POLAR) ### declare the generator cost objective libgen.declare_expression_pgqg_operating_cost(model=model, index_set=gen_attrs['names'], p_costs=gen_attrs['p_cost'], q_costs=gen_attrs.get( 'q_cost', None)) obj_expr = sum(model.pg_operating_cost[gen_name] for gen_name in model.pg_operating_cost) if include_feasibility_slack: obj_expr += penalty_expr if hasattr(model, 'qg_operating_cost'): obj_expr += sum(model.qg_operating_cost[gen_name] for gen_name in model.qg_operating_cost) model.obj = pe.Objective(expr=obj_expr) return model, md
def create_ptdf_losses_dcopf_model(model_data, include_feasibility_slack=False, ptdf_options=None): ptdf_options = lpu.populate_default_ptdf_options(ptdf_options) baseMVA = model_data.data['system']['baseMVA'] lpu.check_and_scale_ptdf_options(ptdf_options, baseMVA) md = model_data.clone_in_service() tx_utils.scale_ModelData_to_pu(md, inplace = True) 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')) gen_attrs = md.attributes(element_type='generator') bus_attrs = md.attributes(element_type='bus') branch_attrs = md.attributes(element_type='branch') load_attrs = md.attributes(element_type='load') shunt_attrs = md.attributes(element_type='shunt') 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) model = pe.ConcreteModel() ### declare (and fix) the loads at the buses bus_p_loads, _ = tx_utils.dict_of_bus_loads(buses, loads) libbus.declare_var_pl(model, bus_attrs['names'], initialize=bus_p_loads) model.pl.fix() ### declare the fixed shunts at the buses _, bus_gs_fixed_shunts = tx_utils.dict_of_bus_fixed_shunts(buses, shunts) ### 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(model, gen_attrs['names'], initialize=pg_init, bounds=zip_items(gen_attrs['p_min'], gen_attrs['p_max']) ) ### include the feasibility slack for the system balance p_rhs_kwargs = {} if include_feasibility_slack: p_rhs_kwargs, penalty_expr = _include_system_feasibility_slack(model, gen_attrs, bus_p_loads) ### declare net withdraw expression for use in PTDF power flows libbus.declare_expr_p_net_withdraw_at_bus(model=model, 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, ) ### declare the current flows in the branches p_max = {k: branches[k]['rating_long_term'] for k in branches.keys()} pfl_bounds = {k: (-p_max[k]**2,p_max[k]**2) for k in branches.keys()} pfl_init = {k: 0 for k in branches.keys()} ## Do and store PTDF calculation reference_bus = md.data['system']['reference_bus'] ## We'll assume we have a solution to initialize from base_point = BasePointType.SOLUTION PTDF = ptdf_utils.PTDFLossesMatrix(branches, buses, reference_bus, base_point, ptdf_options) model._PTDF = PTDF model._ptdf_options = ptdf_options libbranch.declare_expr_pf(model=model, index_set=branch_attrs['names'], ) libbranch.declare_var_pfl(model=model, index_set=branch_attrs['names'], initialize=pfl_init, bounds=pfl_bounds ) ### declare the branch power flow approximation constraints libbranch.declare_eq_branch_power_ptdf_approx(model=model, index_set=branch_attrs['names'], PTDF=PTDF, abs_ptdf_tol=ptdf_options['abs_ptdf_tol'], rel_ptdf_tol=ptdf_options['rel_ptdf_tol'], ) ### declare the branch power loss approximation constraints libbranch.declare_eq_branch_loss_ptdf_approx(model=model, index_set=branch_attrs['names'], PTDF=PTDF, abs_ptdf_tol=ptdf_options['abs_ptdf_tol'], rel_ptdf_tol=ptdf_options['rel_ptdf_tol'], ) ### declare the p balance libbus.declare_eq_p_balance_ed(model=model, 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, include_losses=branch_attrs['names'], **p_rhs_kwargs ) ### declare the real power flow limits libbranch.declare_ineq_p_branch_thermal_lbub(model=model, index_set=branch_attrs['names'], branches=branches, p_thermal_limits=p_max, approximation_type=ApproximationType.PTDF ) ### declare the generator cost objective libgen.declare_expression_pgqg_operating_cost(model=model, index_set=gen_attrs['names'], p_costs=gen_attrs['p_cost'] ) obj_expr = sum(model.pg_operating_cost[gen_name] for gen_name in model.pg_operating_cost) if include_feasibility_slack: obj_expr += penalty_expr model.obj = pe.Objective(expr=obj_expr) return model, md
def create_gdp_subproblem(model, model_data, include_angle_diff_limits=False): md = model_data tx_utils.scale_ModelData_to_pu(md, inplace=True) 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')) 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) model.subproblem = bi.SubModel(fixed=(model.u, model.v, model.w)) ### 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.) libbus.declare_var_pl(model.subproblem, bus_attrs['names'], initialize=bus_p_loads) model.subproblem.pl.fix() ### 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']} libbus.declare_var_va(model.subproblem, bus_attrs['names'], initialize=tx_utils.radians_from_degrees_dict( 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'] model.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(model.subproblem, gen_attrs['names'], initialize=pg_init, bounds=zip_items(gen_attrs['p_min'], gen_attrs['p_max'])) ### declare the current flows in the branches vr_init = { k: bus_attrs['vm'][k] * pe.cos(radians(bus_attrs['va'][k])) for k in bus_attrs['vm'] } vj_init = { k: bus_attrs['vm'][k] * pe.sin(radians(bus_attrs['va'][k])) for k in bus_attrs['vm'] } p_max = {k: branches[k]['rating_long_term'] for k in branches.keys()} p_lbub = {k: (-p_max[k], p_max[k]) for k in branches.keys()} pf_bounds = p_lbub 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=model.subproblem, index_set=branch_attrs['names'], initialize=pf_init, bounds=pf_bounds) # need to include variable references on subproblem to variables, which exist on the master block bi.components.varref(model.subproblem) ### declare the branch power flow disjuncts (LHS is status quo, RHS is compromised) libbranch.declare_eq_branch_power_btheta_approx( model=model.subproblem, index_set=branch_attrs['names'], branches=branches) subcons.declare_eq_branch_power_off(model=model.subproblem, index_set=branch_attrs['names'], branches=branches) subcons.disjunctify(model=model.subproblem, indicator_name='pf_branch_indicator', disjunct_name='pf_branch_disjunct', LHS_disjunct_set=model.subproblem.eq_pf_branch, RHS_disjunct_set=model.subproblem.eq_pf_branch_off) ### declare the load shed disjuncts (LHS is status quo, RHS is compromised) subcons.declare_ineq_load_shed_ub(model=model.subproblem, index_set=buses_with_loads) subcons.declare_ineq_load_shed_lb(model=model.subproblem, index_set=buses_with_loads) subcons.declare_ineq_load_shed_lb_off(model=model.subproblem, index_set=buses_with_loads) subcons.disjunctify( model=model.subproblem, indicator_name='load_shed_indicator', disjunct_name='load_shed_disjunct', LHS_disjunct_set=model.subproblem.ineq_load_shed_lb, RHS_disjunct_set=model.subproblem.ineq_load_shed_lb_off) ### declare the generator disjuncts (LHS is status quo, RHS is compromised) subcons.declare_ineq_gen_on(model=model.subproblem, index_set=gen_attrs['names'], gens=gens) subcons.declare_ineq_gen_off(model=model.subproblem, index_set=gen_attrs['names'], gens=gens) subcons.disjunctify(model=model.subproblem, indicator_name='gen_indicator', disjunct_name='gen_disjunct', LHS_disjunct_set=model.subproblem.ineq_gen, RHS_disjunct_set=model.subproblem.ineq_gen_off) ### declare the p balance rhs_kwargs = {'include_feasibility_slack_neg': 'load_shed'} libbus.declare_eq_p_balance_dc_approx( model=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 the real power flow limits libbranch.declare_ineq_p_branch_thermal_lbub( model=model.subproblem, index_set=branch_attrs['names'], branches=branches, p_thermal_limits=p_max, approximation_type=ApproximationType.BTHETA) ### declare angle difference limits on interconnected buses if include_angle_diff_limits: libbranch.declare_ineq_angle_diff_branch_lbub( model=model.subproblem, index_set=branch_attrs['names'], branches=branches, coordinate_type=CoordinateType.POLAR) model.subproblem.obj = pe.Objective(expr=sum(model.load_shed[l] for l in buses_with_loads), sense=pe.minimize) return model, md
def create_ptdf_dcopf_model(model_data, include_feasibility_slack=False,base_point=BasePointType.FLATSTART): md = model_data.clone_in_service() tx_utils.scale_ModelData_to_pu(md, inplace = True) data_utils.create_dicts_of_ptdf(md,base_point=base_point) 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')) gen_attrs = md.attributes(element_type='generator') bus_attrs = md.attributes(element_type='bus') branch_attrs = md.attributes(element_type='branch') load_attrs = md.attributes(element_type='load') shunt_attrs = md.attributes(element_type='shunt') 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) model = pe.ConcreteModel() ### declare (and fix) the loads at the buses bus_p_loads, _ = tx_utils.dict_of_bus_loads(buses, loads) libbus.declare_var_pl(model, bus_attrs['names'], initialize=bus_p_loads) model.pl.fix() ### declare the fixed shunts at the buses _, bus_gs_fixed_shunts = tx_utils.dict_of_bus_fixed_shunts(buses, shunts) ### 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(model, gen_attrs['names'], initialize=pg_init, bounds=zip_items(gen_attrs['p_min'], gen_attrs['p_max']) ) ### include the feasibility slack for the system balance p_rhs_kwargs = {} if include_feasibility_slack: p_rhs_kwargs, penalty_expr = _include_system_feasibility_slack(model, gen_attrs, bus_p_loads) ### 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']} p_max = {k: branches[k]['rating_long_term'] for k in branches.keys()} p_lbub = {k: (-p_max[k],p_max[k]) for k in branches.keys()} pf_bounds = p_lbub 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=model, index_set=branch_attrs['names'], initialize=pf_init, bounds=pf_bounds ) ### declare the branch power flow approximation constraints libbranch.declare_eq_branch_power_ptdf_approx(model=model, index_set=branch_attrs['names'], branches=branches, buses=buses, bus_p_loads=bus_p_loads, gens_by_bus=gens_by_bus, bus_gs_fixed_shunts=bus_gs_fixed_shunts ) ### declare the p balance libbus.declare_eq_p_balance_ed(model=model, 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, **p_rhs_kwargs ) ### declare the real power flow limits libbranch.declare_ineq_p_branch_thermal_lbub(model=model, index_set=branch_attrs['names'], branches=branches, p_thermal_limits=p_max, approximation_type=ApproximationType.PTDF ) ### declare the generator cost objective libgen.declare_expression_pgqg_operating_cost(model=model, index_set=gen_attrs['names'], p_costs=gen_attrs['p_cost'] ) obj_expr = sum(model.pg_operating_cost[gen_name] for gen_name in model.pg_operating_cost) if include_feasibility_slack: obj_expr += penalty_expr model.obj = pe.Objective(expr=obj_expr) return model, md
def _create_base_power_ac_model(model_data, include_feasibility_slack=False, pw_cost_model='delta'): md = model_data.clone_in_service() tx_utils.scale_ModelData_to_pu(md, inplace=True) 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')) 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) bus_pairs = zip_items(branch_attrs['from_bus'], branch_attrs['to_bus']) unique_bus_pairs = list(OrderedDict((val, None) for idx, val in bus_pairs.items())) model = pe.ConcreteModel() ### declare (and fix) the loads at the buses bus_p_loads, bus_q_loads = tx_utils.dict_of_bus_loads(buses, loads) libbus.declare_var_pl(model, bus_attrs['names'], initialize=bus_p_loads) libbus.declare_var_ql(model, bus_attrs['names'], initialize=bus_q_loads) model.pl.fix() model.ql.fix() ### declare the fixed shunts at the buses bus_bs_fixed_shunts, bus_gs_fixed_shunts = tx_utils.dict_of_bus_fixed_shunts(buses, shunts) libbus.declare_var_vmsq(model=model, index_set=bus_attrs['names'], initialize={k: v**2 for k, v in bus_attrs['vm'].items()}, bounds=zip_items({k: v**2 for k, v in bus_attrs['v_min'].items()}, {k: v**2 for k, v in bus_attrs['v_max'].items()})) libbranch.declare_var_c(model=model, index_set=unique_bus_pairs, initialize=1) libbranch.declare_var_s(model=model, index_set=unique_bus_pairs, initialize=0) ### include the feasibility slack for the bus balances p_rhs_kwargs = {} q_rhs_kwargs = {} if include_feasibility_slack: p_marginal_slack_penalty, q_marginal_slack_penalty = _validate_and_extract_slack_penalties(md) p_rhs_kwargs, q_rhs_kwargs, penalty_expr = _include_feasibility_slack(model, bus_attrs['names'], bus_p_loads, bus_q_loads, gens_by_bus, gen_attrs, p_marginal_slack_penalty, q_marginal_slack_penalty) ### declare the generator real and reactive 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(model, gen_attrs['names'], initialize=pg_init, bounds=zip_items(gen_attrs['p_min'], gen_attrs['p_max']) ) qg_init = {k: (gen_attrs['q_min'][k] + gen_attrs['q_max'][k]) / 2.0 for k in gen_attrs['qg']} libgen.declare_var_qg(model, gen_attrs['names'], initialize=qg_init, bounds=zip_items(gen_attrs['q_min'], gen_attrs['q_max']) ) ### declare the current flows in the branches vr_init = {k: bus_attrs['vm'][k] * pe.cos(radians(bus_attrs['va'][k])) for k in bus_attrs['vm']} vj_init = {k: bus_attrs['vm'][k] * pe.sin(radians(bus_attrs['va'][k])) for k in bus_attrs['vm']} s_max = {k: branches[k]['rating_long_term'] for k in branches.keys()} s_lbub = dict() for k in branches.keys(): if s_max[k] is None: s_lbub[k] = (None, None) else: s_lbub[k] = (-s_max[k],s_max[k]) pf_bounds = s_lbub pt_bounds = s_lbub qf_bounds = s_lbub qt_bounds = s_lbub pf_init = dict() pt_init = dict() qf_init = dict() qt_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) itr_init = tx_calc.calculate_itr(vr_init[from_bus], vj_init[from_bus], vr_init[to_bus], vj_init[to_bus], y_matrix) itj_init = tx_calc.calculate_itj(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]) pt_init[branch_name] = tx_calc.calculate_p(itr_init, itj_init, vr_init[to_bus], vj_init[to_bus]) qf_init[branch_name] = tx_calc.calculate_q(ifr_init, ifj_init, vr_init[from_bus], vj_init[from_bus]) qt_init[branch_name] = tx_calc.calculate_q(itr_init, itj_init, vr_init[to_bus], vj_init[to_bus]) libbranch.declare_var_pf(model=model, index_set=branch_attrs['names'], initialize=pf_init, bounds=pf_bounds ) libbranch.declare_var_pt(model=model, index_set=branch_attrs['names'], initialize=pt_init, bounds=pt_bounds ) libbranch.declare_var_qf(model=model, index_set=branch_attrs['names'], initialize=qf_init, bounds=qf_bounds ) libbranch.declare_var_qt(model=model, index_set=branch_attrs['names'], initialize=qt_init, bounds=qt_bounds ) ### declare the branch power flow constraints libbranch.declare_eq_branch_power(model=model, index_set=branch_attrs['names'], branches=branches ) ### declare the pq balances libbus.declare_eq_p_balance(model=model, 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, **p_rhs_kwargs ) libbus.declare_eq_q_balance(model=model, index_set=bus_attrs['names'], bus_q_loads=bus_q_loads, gens_by_bus=gens_by_bus, bus_bs_fixed_shunts=bus_bs_fixed_shunts, inlet_branches_by_bus=inlet_branches_by_bus, outlet_branches_by_bus=outlet_branches_by_bus, **q_rhs_kwargs ) ### declare the thermal limits libbranch.declare_ineq_s_branch_thermal_limit(model=model, index_set=branch_attrs['names'], branches=branches, s_thermal_limits=s_max, flow_type=FlowType.POWER ) # declare angle difference limits on interconnected buses libbranch.declare_ineq_angle_diff_branch_lbub_c_s(model=model, index_set=branch_attrs['names'], branches=branches ) # declare the generator cost objective p_costs = gen_attrs['p_cost'] pw_pg_cost_gens = list(libgen.pw_gen_generator(gen_attrs['names'], costs=p_costs)) if len(pw_pg_cost_gens) > 0: if pw_cost_model == 'delta': libgen.declare_var_delta_pg(model=model, index_set=pw_pg_cost_gens, p_costs=p_costs) libgen.declare_pg_delta_pg_con(model=model, index_set=pw_pg_cost_gens, p_costs=p_costs) else: libgen.declare_var_pg_cost(model=model, index_set=pw_pg_cost_gens, p_costs=p_costs) libgen.declare_piecewise_pg_cost_cons(model=model, index_set=pw_pg_cost_gens, p_costs=p_costs) libgen.declare_expression_pg_operating_cost(model=model, index_set=gen_attrs['names'], p_costs=p_costs, pw_formulation=pw_cost_model) obj_expr = sum(model.pg_operating_cost[gen_name] for gen_name in model.pg_operating_cost) q_costs = gen_attrs.get('q_cost', None) if q_costs is not None: pw_qg_cost_gens = list(libgen.pw_gen_generator(gen_attrs['names'], costs=q_costs)) if len(pw_qg_cost_gens) > 0: if pw_cost_model == 'delta': libgen.declare_var_delta_qg(model=model, index_set=pw_qg_cost_gens, q_costs=q_costs) libgen.declare_qg_delta_qg_con(model=model, index_set=pw_qg_cost_gens, q_costs=q_costs) else: libgen.declare_var_qg_cost(model=model, index_set=pw_qg_cost_gens, q_costs=q_costs) libgen.declare_piecewise_qg_cost_cons(model=model, index_set=pw_qg_cost_gens, q_costs=q_costs) libgen.declare_expression_qg_operating_cost(model=model, index_set=gen_attrs['names'], q_costs=q_costs, pw_formulation=pw_cost_model) obj_expr += sum(model.qg_operating_cost[gen_name] for gen_name in model.qg_operating_cost) if include_feasibility_slack: obj_expr += penalty_expr model.obj = pe.Objective(expr=obj_expr) return model, md
def solve_bilevel_physical_nk(model_data, solver, solver_tee=True, return_model=False, return_results=False, **kwargs): ''' Create and solve a new worst-case attacker defender Parameters ---------- model_data : egret.data.ModelData An egret ModelData object with the appropriate data loaded. solver : str or pyomo.opt.base.solvers.OptSolver Either a string specifying a pyomo solver name, or an instantiated pyomo solver solver_tee : bool (optional) Display solver log. Default is True. return_model : bool (optional) If True, returns the pyomo model object return_results : bool (optional) If True, returns the pyomo results object kwargs : dictionary (optional) Additional arguments for building model ''' import pyomo.environ as pe from pyomo.environ import value from egret.model_library.transmission.tx_utils import \ scale_ModelData_to_pu, unscale_ModelData_to_pu md = model_data.clone_in_service() scale_ModelData_to_pu(md, inplace=True) ### pop from kwargs the number k for N-k contingency of relay IPs attack_budget_k = kwargs.pop('attack_budget_k', 1) ### create upper-level of the bilevel problem m, md = create_master(md, attack_budget_k) ### create lower-level of the bilevel problem m, md = create_explicit_subproblem(m, md, include_bigm=False) ### use PAO (Pyomo-extension) to do the following: ### 1. Transform the lower-level primal problem into it's corresponding dual problem ### 2. Apply Pyomo.GDP transformations to handle bilinear terms (Big-M) ### 3. Solve formulation (upper-level primal with lower-level dual) as a single level MILP ### 4. Take optimal solution from MILP, fix upper-level variables that appear in the ### lower-level problem, and resolve to determine primal variable solution for the lower-level opt = pe.SolverFactory('pao.bilevel.ld', solver=solver) ## need to fine-tune bigM and mipgap -- make sure that both the solve and resolve result in the same ## best objective opt.options.setdefault('bigM', 100) opt.options.setdefault('mipgap', 0.001) results = opt.solve(m, tee=solver_tee) ### save results data to ModelData object gens = dict(md.elements(element_type='generator')) buses = dict(md.elements(element_type='bus')) branches = dict(md.elements(element_type='branch')) md.data['system']['total_cost'] = value(m.obj) m = m.subproblem for g, g_dict in gens.items(): g_dict['pg'] = value(m.pg[g]) for k, k_dict in branches.items(): k_dict['pf'] = value(m.pf[k]) for b, b_dict in buses.items(): b_dict['pl'] = value(m.pl[b]) b_dict['va'] = value(m.va[b]) unscale_ModelData_to_pu(md, inplace=True) ### return model_data (md), model (m), and/or results (results) objects if return_model and return_results: return md, m, results elif return_model: return md, m elif return_results: return md, results return md
def create_socp_acopf_model(model_data, include_feasibility_slack=False): md = model_data.clone_in_service() tx_utils.scale_ModelData_to_pu(md, inplace = True) 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')) gen_attrs = md.attributes(element_type='generator') bus_attrs = md.attributes(element_type='bus') branch_attrs = md.attributes(element_type='branch') load_attrs = md.attributes(element_type='load') shunt_attrs = md.attributes(element_type='shunt') 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) model = pe.ConcreteModel() ### declare (and fix) the loads at the buses bus_p_loads, bus_q_loads = tx_utils.dict_of_bus_loads(buses, loads) libbus.declare_var_pl(model, bus_attrs['names'], initialize=bus_p_loads) libbus.declare_var_ql(model, bus_attrs['names'], initialize=bus_q_loads) model.pl.fix() model.ql.fix() ### declare the fixed shunts at the buses bus_bs_fixed_shunts, bus_gs_fixed_shunts = tx_utils.dict_of_bus_fixed_shunts(buses, shunts) ### declare the rectangular voltages neg_v_max = map_items(op.neg, bus_attrs['v_max']) vr_init = {k: bus_attrs['vm'][k] * pe.cos(bus_attrs['va'][k]) for k in bus_attrs['vm']} # libbus.declare_var_vr(model, bus_attrs['names'], initialize=vr_init, # bounds=zip_items(neg_v_max, bus_attrs['v_max']) # ) vj_init = {k: bus_attrs['vm'][k] * pe.sin(bus_attrs['va'][k]) for k in bus_attrs['vm']} # libbus.declare_var_vj(model, bus_attrs['names'], initialize=vj_init, # bounds=zip_items(neg_v_max, bus_attrs['v_max']) # ) # w variable for socp vj2_init = {k: (bus_attrs['vm'][k] * pe.sin(bus_attrs['va'][k]))**2 for k in bus_attrs['vm']} vr2_init = {k: (bus_attrs['vm'][k] * pe.cos(bus_attrs['va'][k]))**2 for k in bus_attrs['vm']} w_init = {k: vr2_init[k]+vj2_init[k] for k in vj2_init} wub = {k:bus_attrs['v_max'][k]**2 for k in bus_attrs['v_max']} wlb = {k:bus_attrs['v_min'][k]**2 for k in bus_attrs['v_min']} ## v_min**2 <= w <= v_max**2 libbus.declare_var_w(model, bus_attrs['names'], initialize = w_init, bounds =zip_items(wlb,wub)) ### include the feasibility slack for the bus balances p_rhs_kwargs = {} q_rhs_kwargs = {} if include_feasibility_slack: p_rhs_kwargs, q_rhs_kwargs, penalty_expr = _include_feasibility_slack(model, bus_attrs, gen_attrs, bus_p_loads, bus_q_loads) ### fix the reference bus # ref_bus = md.data['system']['reference_bus'] # ref_angle = md.data['system']['reference_bus_angle'] # if ref_angle != 0.0: # libbus.declare_eq_ref_bus_nonzero(model, ref_angle, ref_bus) # else: # model.vj[ref_bus].fix(0.0) # model.vr[ref_bus].setlb(0.0) ### declare the generator real and reactive 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(model, gen_attrs['names'], initialize=pg_init, bounds=zip_items(gen_attrs['p_min'], gen_attrs['p_max']) ) qg_init = {k: (gen_attrs['q_min'][k] + gen_attrs['q_max'][k]) / 2.0 for k in gen_attrs['qg']} libgen.declare_var_qg(model, gen_attrs['names'], initialize=qg_init, bounds=zip_items(gen_attrs['q_min'], gen_attrs['q_max']) ) ### declare the current flows in the branches s_max = {k: branches[k]['rating_long_term'] for k in branches.keys()} s_lbub = dict() for k in branches.keys(): if s_max[k] is None: s_lbub[k] = (None, None) else: s_lbub[k] = (-s_max[k],s_max[k]) pf_bounds = s_lbub pt_bounds = s_lbub qf_bounds = s_lbub qt_bounds = s_lbub pf_init = dict() pt_init = dict() qf_init = dict() qt_init = dict() cbk_init = dict() sbk_init = dict() cbk_bounds = dict() sbk_bounds = dict() for branch_name, branch in branches.items(): from_bus = branch['from_bus'] to_bus = branch['to_bus'] ba_max = branch['angle_diff_max'] ba_min = branch['angle_diff_min'] 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) itr_init = tx_calc.calculate_itr(vr_init[from_bus], vj_init[from_bus], vr_init[to_bus], vj_init[to_bus], y_matrix) itj_init = tx_calc.calculate_itj(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]) pt_init[branch_name] = tx_calc.calculate_p(itr_init, itj_init, vr_init[to_bus], vj_init[to_bus]) qf_init[branch_name] = tx_calc.calculate_q(ifr_init, ifj_init, vr_init[from_bus], vj_init[from_bus]) qt_init[branch_name] = tx_calc.calculate_q(itr_init, itj_init, vr_init[to_bus], vj_init[to_bus]) #SOCP related variable bounds and init cbk_init[from_bus,to_bus] = vr_init[from_bus]*vr_init[to_bus] + vj_init[from_bus]*vj_init[to_bus] sbk_init[from_bus,to_bus] = vr_init[from_bus]*vj_init[to_bus] - vr_init[to_bus]*vj_init[from_bus] if ba_max is None and ba_min is None: cbk_bounds[from_bus,to_bus] = ( bus_attrs['v_min'][from_bus]*bus_attrs['v_min'][to_bus]*min(pe.cos(-math.pi/2),pe.cos(math.pi/2)), bus_attrs['v_max'][from_bus]*bus_attrs['v_max'][to_bus]*1.0) sbk_bounds[from_bus,to_bus] = ( bus_attrs['v_max'][from_bus]*bus_attrs['v_max'][to_bus]*pe.sin(-math.pi/2), bus_attrs['v_max'][from_bus]*bus_attrs['v_max'][to_bus]*pe.sin(math.pi/2)) if ba_max > 0 and ba_min < 0: cbk_bounds[from_bus,to_bus] = (bus_attrs['v_min'][from_bus]*bus_attrs['v_min'][to_bus]*min(pe.cos(ba_max * math.pi/180),pe.cos(ba_min * math.pi/180)), bus_attrs['v_max'][from_bus]*bus_attrs['v_max'][to_bus]*1.0) sbk_bounds[from_bus,to_bus] = ( bus_attrs['v_max'][from_bus]*bus_attrs['v_max'][to_bus]*pe.sin(ba_min * math.pi/180), bus_attrs['v_max'][from_bus]*bus_attrs['v_max'][to_bus]*pe.sin(math.pi/2)) if ba_max <= 0: cbk_bounds[from_bus,to_bus] = (bus_attrs['v_min'][from_bus]*bus_attrs['v_min'][to_bus]*pe.cos(ba_min * math.pi/180), bus_attrs['v_max'][from_bus]*bus_attrs['v_max'][to_bus]*pe.cos(ba_max * math.pi/180)) sbk_bounds[from_bus,to_bus] = ( bus_attrs['v_max'][from_bus]*bus_attrs['v_max'][to_bus]*pe.sin(ba_min * math.pi/180), bus_attrs['v_max'][from_bus]*bus_attrs['v_max'][to_bus]*pe.sin(math.pi/2)) if ba_min >= 0: cbk_bounds[from_bus,to_bus] = (bus_attrs['v_min'][from_bus]*bus_attrs['v_min'][to_bus]*pe.cos(ba_max * math.pi/180), bus_attrs['v_max'][from_bus]*bus_attrs['v_max'][to_bus]*pe.cos(ba_min * math.pi/180)) sbk_bounds[from_bus,to_bus] = ( bus_attrs['v_max'][from_bus]*bus_attrs['v_max'][to_bus]*pe.sin(ba_min * math.pi/180), bus_attrs['v_max'][from_bus]*bus_attrs['v_max'][to_bus]*pe.sin(math.pi/2)) #print(bus_attrs) #print(branch_attrs) libbranch.declare_var_pf(model=model, index_set=branch_attrs['names'], initialize=pf_init, bounds=pf_bounds ) libbranch.declare_var_pt(model=model, index_set=branch_attrs['names'], initialize=pt_init, bounds=pt_bounds ) libbranch.declare_var_qf(model=model, index_set=branch_attrs['names'], initialize=qf_init, bounds=qf_bounds ) libbranch.declare_var_qt(model=model, index_set=branch_attrs['names'], initialize=qt_init, bounds=qt_bounds ) bus_pairs = zip_items(branch_attrs['from_bus'],branch_attrs['to_bus']) unique_bus_pairs = list(set([val for idx,val in bus_pairs.items()])) libbranch.declare_var_c(model = model, index_set = unique_bus_pairs, initialize = cbk_init, bounds = cbk_bounds ) libbranch.declare_var_s(model = model, index_set = unique_bus_pairs, initialize = sbk_init, bounds = sbk_bounds ) ### declare the branch power flow constraints libbranch.declare_eq_branch_power_socp(model=model, index_set=branch_attrs['names'], branches=branches, branch_attrs=branch_attrs, coordinate_type=CoordinateType.RECTANGULAR ) ### declare the pq balances libbus.declare_eq_p_balance_socp(model=model, 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, coordinate_type=CoordinateType.RECTANGULAR, **p_rhs_kwargs ) libbus.declare_eq_q_balance_socp(model=model, index_set=bus_attrs['names'], bus_q_loads=bus_q_loads, gens_by_bus=gens_by_bus, bus_bs_fixed_shunts=bus_bs_fixed_shunts, inlet_branches_by_bus=inlet_branches_by_bus, outlet_branches_by_bus=outlet_branches_by_bus, coordinate_type=CoordinateType.RECTANGULAR, **q_rhs_kwargs ) ### declare the thermal limits libbranch.declare_ineq_s_branch_thermal_limit(model=model, index_set=branch_attrs['names'], branches=branches, s_thermal_limits=s_max, flow_type=FlowType.POWER ) ### declare the voltage min and max inequalities # libbus.declare_ineq_vm_bus_lbub(model=model, # index_set=bus_attrs['names'], # buses=buses, # coordinate_type=CoordinateType.RECTANGULAR # ) ### declare angle difference limits on interconnected buses # libbranch.declare_ineq_angle_diff_branch_lbub(model=model, # index_set=branch_attrs['names'], # branches=branches, # coordinate_type=CoordinateType.RECTANGULAR # ) libbranch.declare_socp_scw(model = model, index_set = branch_attrs['names'], branches = branches, branch_attrs = branch_attrs) ### declare the generator cost objective libgen.declare_expression_pgqg_operating_cost(model=model, index_set=gen_attrs['names'], p_costs=gen_attrs['p_cost'], q_costs=gen_attrs.get('q_cost', None) ) obj_expr = sum(model.pg_operating_cost[gen_name] for gen_name in model.pg_operating_cost) if include_feasibility_slack: obj_expr += penalty_expr if hasattr(model, 'qg_operating_cost'): obj_expr += sum(model.qg_operating_cost[gen_name] for gen_name in model.qg_operating_cost) model.obj = pe.Objective(expr=obj_expr) return model, md
def create_rsv_acopf_model(model_data): md = tx_utils.scale_ModelData_to_pu(model_data) 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')) gen_attrs = md.attributes(element_type='generator') bus_attrs = md.attributes(element_type='bus') branch_attrs = md.attributes(element_type='branch') load_attrs = md.attributes(element_type='load') shunt_attrs = md.attributes(element_type='shunt') inlet_branches_by_bus, outlet_branches_by_bus = \ tx_utils.inlet_outlet_branches_by_bus(branches, buses) gens_by_bus = tx_utils.gens_in_service_by_bus(buses, gens) model = pe.ConcreteModel() ### declare (and fix) the loads at the buses bus_p_loads, bus_q_loads = tx_utils.dict_of_bus_loads(buses, loads) libbus.declare_var_pl(model, bus_attrs['names'], initialize=bus_p_loads) libbus.declare_var_ql(model, bus_attrs['names'], initialize=bus_q_loads) model.pl.fix() model.ql.fix() ### declare the fixed shunts at the buses bus_bs_fixed_shunts, bus_gs_fixed_shunts = tx_utils.dict_of_bus_fixed_shunts( buses, shunts) ### declare the rectangular voltages neg_v_max = map_items(op.neg, bus_attrs['v_max']) vr_init = { k: bus_attrs['vm'][k] * pe.cos(bus_attrs['va'][k]) for k in bus_attrs['vm'] } libbus.declare_var_vr(model, bus_attrs['names'], initialize=vr_init, bounds=zip_items(neg_v_max, bus_attrs['v_max'])) vj_init = { k: bus_attrs['vm'][k] * pe.sin(bus_attrs['va'][k]) for k in bus_attrs['vm'] } libbus.declare_var_vj(model, bus_attrs['names'], initialize=vj_init, bounds=zip_items(neg_v_max, bus_attrs['v_max'])) ### fix the reference bus ref_bus = md.data['system']['reference_bus'] model.vj[ref_bus].fix(0.0) model.vr[ref_bus].setlb(0.0) ref_angle = md.data['system']['reference_bus_angle'] if ref_angle != 0.0: raise ValueError('The RSV ACOPF formulation currently only supports' ' a reference bus angle of 0 degrees, but an angle' ' of {} degrees was found.'.format(ref_angle)) ### declare the generator real and reactive 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(model, gen_attrs['names'], initialize=pg_init, bounds=zip_items(gen_attrs['p_min'], gen_attrs['p_max'])) qg_init = { k: (gen_attrs['q_min'][k] + gen_attrs['q_max'][k]) / 2.0 for k in gen_attrs['qg'] } libgen.declare_var_qg(model, gen_attrs['names'], initialize=qg_init, bounds=zip_items(gen_attrs['q_min'], gen_attrs['q_max'])) ### declare the current flows in the branches s_max = {k: branches[k]['rating_long_term'] for k in branches.keys()} s_lbub = {k: (-s_max[k], s_max[k]) for k in branches.keys()} pf_bounds = s_lbub pt_bounds = s_lbub qf_bounds = s_lbub qt_bounds = s_lbub pf_init = dict() pt_init = dict() qf_init = dict() qt_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) itr_init = tx_calc.calculate_itr(vr_init[from_bus], vj_init[from_bus], vr_init[to_bus], vj_init[to_bus], y_matrix) itj_init = tx_calc.calculate_itj(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]) pt_init[branch_name] = tx_calc.calculate_p(itr_init, itj_init, vr_init[to_bus], vj_init[to_bus]) qf_init[branch_name] = tx_calc.calculate_q(ifr_init, ifj_init, vr_init[from_bus], vj_init[from_bus]) qt_init[branch_name] = tx_calc.calculate_q(itr_init, itj_init, vr_init[to_bus], vj_init[to_bus]) libbranch.declare_var_pf(model=model, index_set=branch_attrs['names'], initialize=pf_init, bounds=pf_bounds) libbranch.declare_var_pt(model=model, index_set=branch_attrs['names'], initialize=pt_init, bounds=pt_bounds) libbranch.declare_var_qf(model=model, index_set=branch_attrs['names'], initialize=qf_init, bounds=qf_bounds) libbranch.declare_var_qt(model=model, index_set=branch_attrs['names'], initialize=qt_init, bounds=qt_bounds) ### declare the branch power flow constraints libbranch.declare_eq_branch_power( model=model, index_set=branch_attrs['names'], branches=branches, branch_attrs=branch_attrs, coordinate_type=CoordinateType.RECTANGULAR) ### declare the pq balances libbus.declare_eq_p_balance(model=model, 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, coordinate_type=CoordinateType.RECTANGULAR) libbus.declare_eq_q_balance(model=model, index_set=bus_attrs['names'], bus_q_loads=bus_q_loads, gens_by_bus=gens_by_bus, bus_bs_fixed_shunts=bus_bs_fixed_shunts, inlet_branches_by_bus=inlet_branches_by_bus, outlet_branches_by_bus=outlet_branches_by_bus, coordinate_type=CoordinateType.RECTANGULAR) ### declare the thermal limits libbranch.declare_ineq_s_branch_thermal_limit( model=model, index_set=branch_attrs['names'], branches=branches, s_thermal_limits=s_max, flow_type=FlowType.POWER) ### declare the voltage min and max inequalities libbus.declare_ineq_vm_bus_lbub(model=model, index_set=bus_attrs['names'], buses=buses, coordinate_type=CoordinateType.RECTANGULAR) ### declare angle difference limits on interconnected buses libbranch.declare_ineq_angle_diff_branch_lbub( model=model, index_set=branch_attrs['names'], branches=branches, coordinate_type=CoordinateType.RECTANGULAR) ### declare the generator cost objective libgen.declare_expression_pgqg_operating_cost(model=model, index_set=gen_attrs['names'], p_costs=gen_attrs['p_cost'], q_costs=gen_attrs.get( 'q_cost', None)) obj_expr = sum(model.pg_operating_cost[gen_name] for gen_name in model.pg_operating_cost) if hasattr(model, 'qg_operating_cost'): obj_expr += sum(model.qg_operating_cost[gen_name] for gen_name in model.qg_operating_cost) model.obj = pe.Objective(expr=obj_expr) return model
def create_btheta_dcopf_model(model_data, include_angle_diff_limits=False, include_feasibility_slack=False, pw_cost_model='delta'): md = model_data.clone_in_service() tx_utils.scale_ModelData_to_pu(md, inplace = True) 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')) dc_branches = dict(md.elements(element_type='dc_branch')) 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) model = pe.ConcreteModel() ### declare (and fix) the loads at the buses bus_p_loads, _ = tx_utils.dict_of_bus_loads(buses, loads) libbus.declare_var_pl(model, bus_attrs['names'], initialize=bus_p_loads) model.pl.fix() ### 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']} libbus.declare_var_va(model, bus_attrs['names'], initialize=tx_utils.radians_from_degrees_dict(bus_attrs['va']), bounds=va_bounds ) ### include the feasibility slack for the bus balances p_rhs_kwargs = {} penalty_expr = None if include_feasibility_slack: p_marginal_slack_penalty = _validate_and_extract_slack_penalty(md) p_rhs_kwargs, penalty_expr = _include_feasibility_slack(model, bus_attrs['names'], bus_p_loads, gens_by_bus, gen_attrs, p_marginal_slack_penalty) ### fix the reference bus ref_bus = md.data['system']['reference_bus'] ref_angle = md.data['system']['reference_bus_angle'] model.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(model, gen_attrs['names'], initialize=pg_init, bounds=zip_items(gen_attrs['p_min'], gen_attrs['p_max']) ) ### declare the current flows in the branches vr_init = {k: bus_attrs['vm'][k] * pe.cos(radians(bus_attrs['va'][k])) for k in bus_attrs['vm']} vj_init = {k: bus_attrs['vm'][k] * pe.sin(radians(bus_attrs['va'][k])) for k in bus_attrs['vm']} p_max = {k: branches[k]['rating_long_term'] for k in branches.keys()} p_lbub = dict() for k in branches.keys(): k_pmax = p_max[k] if k_pmax is None: p_lbub[k] = (None, None) else: p_lbub[k] = (-k_pmax,k_pmax) pf_bounds = p_lbub 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=model, index_set=branch_attrs['names'], initialize=pf_init, bounds=pf_bounds ) if dc_branches: dcpf_bounds = dict() for k, k_dict in dc_branches.items(): kp_max = k_dict['rating_long_term'] if kp_max is None: dcpf_bounds[k] = (None, None) else: dcpf_bounds[k] = (-kp_max, kp_max) libbranch.declare_var_dcpf(model=model, index_set=dc_branches.keys(), initialize=0., bounds=dcpf_bounds, ) dc_inlet_branches_by_bus, dc_outlet_branches_by_bus = \ tx_utils.inlet_outlet_branches_by_bus(dc_branches, buses) else: dc_inlet_branches_by_bus = None dc_outlet_branches_by_bus = None ### declare the branch power flow approximation constraints libbranch.declare_eq_branch_power_btheta_approx(model=model, index_set=branch_attrs['names'], branches=branches ) ### declare the p balance libbus.declare_eq_p_balance_dc_approx(model=model, 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, dc_inlet_branches_by_bus=dc_inlet_branches_by_bus, dc_outlet_branches_by_bus=dc_outlet_branches_by_bus, **p_rhs_kwargs ) ### declare the real power flow limits libbranch.declare_ineq_p_branch_thermal_lbub(model=model, index_set=branch_attrs['names'], branches=branches, p_thermal_limits=p_max, approximation_type=ApproximationType.BTHETA ) ### declare angle difference limits on interconnected buses if include_angle_diff_limits: libbranch.declare_ineq_angle_diff_branch_lbub(model=model, index_set=branch_attrs['names'], branches=branches, coordinate_type=CoordinateType.POLAR ) # declare the generator cost objective p_costs = gen_attrs['p_cost'] pw_pg_cost_gens = list(libgen.pw_gen_generator(gen_attrs['names'], costs=p_costs)) if len(pw_pg_cost_gens) > 0: if pw_cost_model == 'delta': libgen.declare_var_delta_pg(model=model, index_set=pw_pg_cost_gens, p_costs=p_costs) libgen.declare_pg_delta_pg_con(model=model, index_set=pw_pg_cost_gens, p_costs=p_costs) else: libgen.declare_var_pg_cost(model=model, index_set=pw_pg_cost_gens, p_costs=p_costs) libgen.declare_piecewise_pg_cost_cons(model=model, index_set=pw_pg_cost_gens, p_costs=p_costs) libgen.declare_expression_pg_operating_cost(model=model, index_set=gen_attrs['names'], p_costs=p_costs, pw_formulation=pw_cost_model) obj_expr = sum(model.pg_operating_cost[gen_name] for gen_name in model.pg_operating_cost) if include_feasibility_slack: obj_expr += penalty_expr model.obj = pe.Objective(expr=obj_expr) return model, md
def _create_base_acpf_model(model_data): md = model_data.clone_in_service() tx_utils.scale_ModelData_to_pu(md, inplace = True) 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')) 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) bus_pairs = zip_items(branch_attrs['from_bus'], branch_attrs['to_bus']) unique_bus_pairs = list(OrderedDict((val, None) for idx, val in bus_pairs.items())) model = pe.ConcreteModel() ### declare (and fix) the loads at the buses bus_p_loads, bus_q_loads = tx_utils.dict_of_bus_loads(buses, loads) libbus.declare_var_pl(model, bus_attrs['names'], initialize=bus_p_loads) libbus.declare_var_ql(model, bus_attrs['names'], initialize=bus_q_loads) model.pl.fix() model.ql.fix() ### declare the fixed shunts at the buses bus_bs_fixed_shunts, bus_gs_fixed_shunts = tx_utils.dict_of_bus_fixed_shunts(buses, shunts) libbus.declare_var_vmsq(model=model, index_set=bus_attrs['names'], initialize={k: v**2 for k, v in bus_attrs['vm'].items()} ) libbranch.declare_var_c(model=model, index_set=unique_bus_pairs) libbranch.declare_var_s(model=model, index_set=unique_bus_pairs) ### declare the generator real and reactive 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(model, gen_attrs['names'], initialize=gen_attrs['pg']) #qg_init = {k: (gen_attrs['q_min'][k] + gen_attrs['q_max'][k]) / 2.0 for k in gen_attrs['qg']} libgen.declare_var_qg(model, gen_attrs['names'], initialize=gen_attrs['qg']) ### declare the current flows in the branches vr_init = {k: bus_attrs['vm'][k] * pe.cos(radians(bus_attrs['va'][k])) for k in bus_attrs['vm']} vj_init = {k: bus_attrs['vm'][k] * pe.sin(radians(bus_attrs['va'][k])) for k in bus_attrs['vm']} pf_init = dict() pt_init = dict() qf_init = dict() qt_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) itr_init = tx_calc.calculate_itr(vr_init[from_bus], vj_init[from_bus], vr_init[to_bus], vj_init[to_bus], y_matrix) itj_init = tx_calc.calculate_itj(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]) pt_init[branch_name] = tx_calc.calculate_p(itr_init, itj_init, vr_init[to_bus], vj_init[to_bus]) qf_init[branch_name] = tx_calc.calculate_q(ifr_init, ifj_init, vr_init[from_bus], vj_init[from_bus]) qt_init[branch_name] = tx_calc.calculate_q(itr_init, itj_init, vr_init[to_bus], vj_init[to_bus]) libbranch.declare_var_pf(model=model, index_set=branch_attrs['names'], initialize=pf_init ) libbranch.declare_var_pt(model=model, index_set=branch_attrs['names'], initialize=pt_init ) libbranch.declare_var_qf(model=model, index_set=branch_attrs['names'], initialize=qf_init ) libbranch.declare_var_qt(model=model, index_set=branch_attrs['names'], initialize=qt_init ) ### declare the branch power flow constraints libbranch.declare_eq_branch_power(model=model, index_set=branch_attrs['names'], branches=branches ) ### declare the pq balances libbus.declare_eq_p_balance(model=model, 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 ) libbus.declare_eq_q_balance(model=model, index_set=bus_attrs['names'], bus_q_loads=bus_q_loads, gens_by_bus=gens_by_bus, bus_bs_fixed_shunts=bus_bs_fixed_shunts, inlet_branches_by_bus=inlet_branches_by_bus, outlet_branches_by_bus=outlet_branches_by_bus ) # if there are multiple generators at the same bus, we will # have unwanted degrees of freedom in qg # therefore, we add a constraint making them equal # if the reference bus has multiple generators, we will also # have unwanted degrees of freedom in pg #ref_bus = md.data['system']['reference_bus'] qg_equality_tuples = list() #pg_equality_tuples = list() for b, genlist in gens_by_bus.items(): if len(genlist) > 1: # we have more than one generator at this bus for i in range(1,len(genlist)): qg_equality_tuples.append((genlist[0], genlist[i])) # if b == ref_bus: # pg_equality_tuples.append((genlist[0], genlist[i])) def _qg_equalities(m,i,j): return m.qg[i] == m.qg[j] model.qg_equalities = pe.Constraint(qg_equality_tuples, rule=_qg_equalities) #def _pg_equalities(m,i,j): # return m.pg[i] == m.pg[j] #model.pg_equalities = pe.Constraint(pg_equality_tuples, rule=_pg_equalities) model.obj = pe.Objective(expr=0.0) return model, md
def _get_uc_model(model_data, formulation_list, relax_binaries): formulation = UCFormulation(*formulation_list) md = scale_ModelData_to_pu(model_data) return generate_model(md, formulation, relax_binaries)
def _get_uc_model(model_data, formulation_list, relax_binaries): formulation = UCFormulation(*formulation_list) md = model_data.clone_in_service() scale_ModelData_to_pu(md, inplace=True) return generate_model(md, formulation, relax_binaries)
def create_btheta_dcopf_model(model_data): md = tx_utils.scale_ModelData_to_pu(model_data) 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')) gen_attrs = md.attributes(element_type='generator') bus_attrs = md.attributes(element_type='bus') branch_attrs = md.attributes(element_type='branch') load_attrs = md.attributes(element_type='load') shunt_attrs = md.attributes(element_type='shunt') inlet_branches_by_bus, outlet_branches_by_bus = \ tx_utils.inlet_outlet_branches_by_bus(branches, buses) gens_by_bus = tx_utils.gens_in_service_by_bus(buses, gens) model = pe.ConcreteModel() ### declare (and fix) the loads at the buses bus_p_loads, _ = tx_utils.dict_of_bus_loads(buses, loads) libbus.declare_var_pl(model, bus_attrs['names'], initialize=bus_p_loads) model.pl.fix() ### 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']} libbus.declare_var_va(model, bus_attrs['names'], initialize=bus_attrs['va'], bounds=va_bounds) ### fix the reference bus ref_bus = md.data['system']['reference_bus'] model.va[ref_bus].fix(0.0) ref_angle = md.data['system']['reference_bus_angle'] if ref_angle != 0.0: raise ValueError('The BTHETA DCOPF formulation currently only supports' ' a reference bus angle of 0 degrees, but an angle' ' of {} degrees was found.'.format(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(model, gen_attrs['names'], initialize=pg_init, bounds=zip_items(gen_attrs['p_min'], gen_attrs['p_max'])) ### 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'] } p_max = {k: branches[k]['rating_long_term'] for k in branches.keys()} p_lbub = {k: (-p_max[k], p_max[k]) for k in branches.keys()} pf_bounds = p_lbub 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=model, index_set=branch_attrs['names'], initialize=pf_init, bounds=pf_bounds) ### declare the branch power flow approximation constraints libbranch.declare_eq_branch_power_dc_approx( model=model, index_set=branch_attrs['names'], branches=branches, approximation_type=ApproximationType.BTHETA) ### declare the p balance libbus.declare_eq_p_balance_dc_approx( model=model, 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) ### declare the real power flow limits libbranch.declare_ineq_p_branch_thermal_lbub( model=model, index_set=branch_attrs['names'], branches=branches, p_thermal_limits=p_max, approximation_type=ApproximationType.BTHETA) ### declare angle difference limits on interconnected buses libbranch.declare_ineq_angle_diff_branch_lbub( model=model, index_set=branch_attrs['names'], branches=branches, coordinate_type=CoordinateType.POLAR) ### declare the generator cost objective libgen.declare_expression_pgqg_operating_cost(model=model, index_set=gen_attrs['names'], p_costs=gen_attrs['p_cost']) obj_expr = sum(model.pg_operating_cost[gen_name] for gen_name in model.pg_operating_cost) model.obj = pe.Objective(expr=obj_expr) return model
def create_scopf_model(model_data, include_feasibility_slack=False, base_point=BasePointType.FLATSTART, ptdf_options=None): ptdf_options = lpu.populate_default_ptdf_options(ptdf_options) baseMVA = model_data.data['system']['baseMVA'] lpu.check_and_scale_ptdf_options(ptdf_options, baseMVA) md = model_data.clone_in_service() tx_utils.scale_ModelData_to_pu(md, inplace=True) 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')) dc_branches = dict(md.elements(element_type='dc_branch')) contingencies = dict(md.elements(element_type='contingency')) gen_attrs = md.attributes(element_type='generator') ## to keep things in order buses_idx = tuple(buses.keys()) branches_idx = tuple(branches.keys()) 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) model = pyo.ConcreteModel() ### declare (and fix) the loads at the buses bus_p_loads, _ = tx_utils.dict_of_bus_loads(buses, loads) libbus.declare_var_pl(model, buses_idx, initialize=bus_p_loads) model.pl.fix() ### declare the fixed shunts at the buses _, bus_gs_fixed_shunts = tx_utils.dict_of_bus_fixed_shunts(buses, shunts) ### 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(model, gen_attrs['names'], initialize=pg_init, bounds=zip_items(gen_attrs['p_min'], gen_attrs['p_max'])) ### include the feasibility slack for the system balance p_rhs_kwargs = {} if include_feasibility_slack: p_marginal_slack_penalty = _validate_and_extract_slack_penalty(md) p_rhs_kwargs, penalty_expr = _include_system_feasibility_slack( model, bus_p_loads, gen_attrs, p_marginal_slack_penalty) if dc_branches: dcpf_bounds = dict() for k, k_dict in dc_branches.items(): kp_max = k_dict['rating_long_term'] if kp_max is None: dcpf_bounds[k] = (None, None) else: dcpf_bounds[k] = (-kp_max, kp_max) libbranch.declare_var_dcpf( model=model, index_set=dc_branches.keys(), initialize=0., bounds=dcpf_bounds, ) dc_inlet_branches_by_bus, dc_outlet_branches_by_bus = \ tx_utils.inlet_outlet_branches_by_bus(dc_branches, buses) else: dc_inlet_branches_by_bus = None dc_outlet_branches_by_bus = None ### declare the p balance libbus.declare_eq_p_balance_ed(model=model, index_set=buses_idx, bus_p_loads=bus_p_loads, gens_by_bus=gens_by_bus, bus_gs_fixed_shunts=bus_gs_fixed_shunts, **p_rhs_kwargs) ### declare net withdraw expression for use in PTDF power flows libbus.declare_expr_p_net_withdraw_at_bus( model=model, index_set=buses_idx, bus_p_loads=bus_p_loads, gens_by_bus=gens_by_bus, bus_gs_fixed_shunts=bus_gs_fixed_shunts, dc_inlet_branches_by_bus=dc_inlet_branches_by_bus, dc_outlet_branches_by_bus=dc_outlet_branches_by_bus, ) ### add "blank" power flow expressions libbranch.declare_expr_pf( model=model, index_set=branches_idx, ) ### add "blank" power flow expressions model._contingencies = pyo.Set(initialize=contingencies.keys()) model._branches = pyo.Set(initialize=branches_idx) ### NOTE: important that this not be dense, we'll add elements ### as we find violations model._contingency_set = pyo.Set(within=model._contingencies * model._branches) model.pfc = pyo.Expression(model._contingency_set) ## Do and store PTDF calculation reference_bus = md.data['system']['reference_bus'] PTDF = ptdf_utils.VirtualPTDFMatrix(branches, buses, reference_bus, base_point, ptdf_options,\ contingencies=contingencies, branches_keys=branches_idx, buses_keys=buses_idx) model._PTDF = PTDF model._ptdf_options = ptdf_options if not ptdf_options['lazy']: raise RuntimeError("scopf only supports lazy constraint generation") ### add "blank" real power flow limits libbranch.declare_ineq_p_branch_thermal_bounds( model=model, index_set=branches_idx, branches=branches, p_thermal_limits=None, approximation_type=None, ) ### add "blank" real power flow limits libbranch.declare_ineq_p_contingency_branch_thermal_bounds( model=model, index_set=model._contingency_set, pc_thermal_limits=None, approximation_type=None, ) ### add helpers for tracking monitored branches lpu.add_monitored_flow_tracker(model) ### add initial branches to monitored set lpu.add_initial_monitored_branches(model, branches, branches_idx, ptdf_options, PTDF) ### declare the generator cost objective libgen.declare_expression_pgqg_operating_cost(model=model, index_set=gen_attrs['names'], p_costs=gen_attrs['p_cost']) obj_expr = sum(model.pg_operating_cost[gen_name] for gen_name in model.pg_operating_cost) if include_feasibility_slack: obj_expr += penalty_expr model.obj = pyo.Objective(expr=obj_expr) return model, md
def test_scaling_spot_check(): md = ModelData.read(scuc_fn) baseMVA = md.data['system']['baseMVA'] md_scaled = scale_ModelData_to_pu(md, inplace=False) md_scaled_unscaled = unscale_ModelData_to_pu(md_scaled, inplace=False) ## commitment should be unchanged assert md.data['elements']['generator']['101_STEAM_3_t']['commitment']['values'][10] == \ md_scaled.data['elements']['generator']['101_STEAM_3_t']['commitment']['values'][10] == \ md_scaled_unscaled.data['elements']['generator']['101_STEAM_3_t']['commitment']['values'][10] ## as should production cost assert md.data['elements']['generator']['101_STEAM_3_t']['production_cost']['values'][10] == \ md_scaled.data['elements']['generator']['101_STEAM_3_t']['production_cost']['values'][10] == \ md_scaled_unscaled.data['elements']['generator']['101_STEAM_3_t']['production_cost']['values'][10] ## as should voltage angle assert md.data['elements']['bus']['Alber']['va']['values'][10] == \ md_scaled.data['elements']['bus']['Alber']['va']['values'][10] == \ md_scaled_unscaled.data['elements']['bus']['Alber']['va']['values'][10] ## pg should be scaled assert md.data['elements']['generator']['101_STEAM_3_t']['pg']['values'][10] == \ md_scaled.data['elements']['generator']['101_STEAM_3_t']['pg']['values'][10]/baseMVA == \ md_scaled_unscaled.data['elements']['generator']['101_STEAM_3_t']['pg']['values'][10] ## load should be scaled assert md.data['elements']['bus']['Alber']['pl']['values'][10] == \ md_scaled.data['elements']['bus']['Alber']['pl']['values'][10]/baseMVA == \ md_scaled_unscaled.data['elements']['bus']['Alber']['pl']['values'][10] ## load should be scaled assert md.data['elements']['load']['Alber']['p_load']['values'][10] == \ md_scaled.data['elements']['load']['Alber']['p_load']['values'][10]/baseMVA == \ md_scaled_unscaled.data['elements']['load']['Alber']['p_load']['values'][10] ## flows should be scaled assert md.data['elements']['branch']['A22']['pf']['values'][20] == \ md_scaled.data['elements']['branch']['A22']['pf']['values'][20]/baseMVA == \ md_scaled_unscaled.data['elements']['branch']['A22']['pf']['values'][20] ## contingency flows should also be scaled assert md.data['elements']['contingency']['A1']['monitored_branches']['values'][10]['A11']['pf'] == \ md_scaled.data['elements']['contingency']['A1']['monitored_branches']['values'][10]['A11']['pf']/baseMVA == \ md_scaled_unscaled.data['elements']['contingency']['A1']['monitored_branches']['values'][10]['A11']['pf'] ## lmp should be inversly scaled assert md.data['elements']['bus']['Alber']['lmp']['values'][10] == \ md_scaled.data['elements']['bus']['Alber']['lmp']['values'][10]*baseMVA == \ md_scaled_unscaled.data['elements']['bus']['Alber']['lmp']['values'][10] ## reserve prices should be inversly scaled assert md.data['system']['reserve_price']['values'][18] == \ md_scaled.data['system']['reserve_price']['values'][18]*baseMVA == \ md_scaled_unscaled.data['system']['reserve_price']['values'][18] ## shortfall price should be inversly scaled assert md.data['system']['reserve_shortfall_cost'] == \ md_scaled.data['system']['reserve_shortfall_cost']*baseMVA == \ md_scaled_unscaled.data['system']['reserve_shortfall_cost']
def create_riv_acopf_model(model_data, include_feasibility_slack=False): md = model_data.clone_in_service() tx_utils.scale_ModelData_to_pu(md, inplace=True) 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')) gen_attrs = md.attributes(element_type='generator') bus_attrs = md.attributes(element_type='bus') branch_attrs = md.attributes(element_type='branch') load_attrs = md.attributes(element_type='load') shunt_attrs = md.attributes(element_type='shunt') 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) model = pe.ConcreteModel() ### declare (and fix) the loads at the buses bus_p_loads, bus_q_loads = tx_utils.dict_of_bus_loads(buses, loads) libbus.declare_var_pl(model, bus_attrs['names'], initialize=bus_p_loads) libbus.declare_var_ql(model, bus_attrs['names'], initialize=bus_q_loads) model.pl.fix() model.ql.fix() ### declare the fixed shunts at the buses bus_bs_fixed_shunts, bus_gs_fixed_shunts = tx_utils.dict_of_bus_fixed_shunts( buses, shunts) ### declare the rectangular voltages neg_v_max = map_items(op.neg, bus_attrs['v_max']) vr_init = { k: bus_attrs['vm'][k] * pe.cos(bus_attrs['va'][k]) for k in bus_attrs['vm'] } libbus.declare_var_vr(model, bus_attrs['names'], initialize=vr_init, bounds=zip_items(neg_v_max, bus_attrs['v_max'])) vj_init = { k: bus_attrs['vm'][k] * pe.sin(bus_attrs['va'][k]) for k in bus_attrs['vm'] } libbus.declare_var_vj(model, bus_attrs['names'], initialize=vj_init, bounds=zip_items(neg_v_max, bus_attrs['v_max'])) ### include the feasibility slack for the bus balances p_rhs_kwargs = {} q_rhs_kwargs = {} if include_feasibility_slack: p_rhs_kwargs, q_rhs_kwargs, penalty_expr = _include_feasibility_slack( model, bus_attrs, gen_attrs, bus_p_loads, bus_q_loads) ### fix the reference bus ref_bus = md.data['system']['reference_bus'] ref_angle = md.data['system']['reference_bus_angle'] if ref_angle != 0.0: libbus.declare_eq_ref_bus_nonzero(model, ref_angle, ref_bus) else: model.vj[ref_bus].fix(0.0) model.vr[ref_bus].setlb(0.0) ### declare the generator real and reactive 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(model, gen_attrs['names'], initialize=pg_init, bounds=zip_items(gen_attrs['p_min'], gen_attrs['p_max'])) qg_init = { k: (gen_attrs['q_min'][k] + gen_attrs['q_max'][k]) / 2.0 for k in gen_attrs['qg'] } libgen.declare_var_qg(model, gen_attrs['names'], initialize=qg_init, bounds=zip_items(gen_attrs['q_min'], gen_attrs['q_max'])) ### declare the current flows in the branches branch_currents = tx_utils.dict_of_branch_currents(branches, buses) s_max = {k: branches[k]['rating_long_term'] for k in branches.keys()} if_bounds = dict() it_bounds = dict() ifr_init = dict() ifj_init = dict() itr_init = dict() itj_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[branch_name] = tx_calc.calculate_ifr(vr_init[from_bus], vj_init[from_bus], vr_init[to_bus], vj_init[to_bus], y_matrix) ifj_init[branch_name] = tx_calc.calculate_ifj(vr_init[from_bus], vj_init[from_bus], vr_init[to_bus], vj_init[to_bus], y_matrix) itr_init[branch_name] = tx_calc.calculate_itr(vr_init[from_bus], vj_init[from_bus], vr_init[to_bus], vj_init[to_bus], y_matrix) itj_init[branch_name] = tx_calc.calculate_itj(vr_init[from_bus], vj_init[from_bus], vr_init[to_bus], vj_init[to_bus], y_matrix) if s_max[branch_name] is None: if_bounds[branch_name] = (None, None) it_bounds[branch_name] = (None, None) else: if_max = s_max[branch_name] / buses[branches[branch_name] ['from_bus']]['v_min'] it_max = s_max[branch_name] / buses[branches[branch_name] ['to_bus']]['v_min'] if_bounds[branch_name] = (-if_max, if_max) it_bounds[branch_name] = (-it_max, it_max) libbranch.declare_var_ifr(model=model, index_set=branch_attrs['names'], initialize=ifr_init, bounds=if_bounds) libbranch.declare_var_ifj(model=model, index_set=branch_attrs['names'], initialize=ifj_init, bounds=if_bounds) libbranch.declare_var_itr(model=model, index_set=branch_attrs['names'], initialize=itr_init, bounds=it_bounds) libbranch.declare_var_itj(model=model, index_set=branch_attrs['names'], initialize=itj_init, bounds=it_bounds) ir_init = dict() ij_init = dict() for bus_name, bus in buses.items(): ir_expr = sum([ ifr_init[branch_name] for branch_name in outlet_branches_by_bus[bus_name] ]) ir_expr += sum([ itr_init[branch_name] for branch_name in inlet_branches_by_bus[bus_name] ]) ij_expr = sum([ ifj_init[branch_name] for branch_name in outlet_branches_by_bus[bus_name] ]) ij_expr += sum([ itj_init[branch_name] for branch_name in inlet_branches_by_bus[bus_name] ]) if bus_gs_fixed_shunts[bus_name] != 0.0: ir_expr += bus_gs_fixed_shunts[bus_name] * vr_init[bus_name] ij_expr += bus_gs_fixed_shunts[bus_name] * vj_init[bus_name] if bus_bs_fixed_shunts[bus_name] != 0.0: ir_expr += bus_bs_fixed_shunts[bus_name] * vj_init[bus_name] ij_expr += bus_bs_fixed_shunts[bus_name] * vr_init[bus_name] ir_init[bus_name] = ir_expr ij_init[bus_name] = ij_expr # TODO: Implement better bounds (?) for these aggregated variables -- note, these are unbounded in old Egret libbus.declare_var_ir_aggregation_at_bus(model=model, index_set=bus_attrs['names'], initialize=ir_init, bounds=(None, None)) libbus.declare_var_ij_aggregation_at_bus(model=model, index_set=bus_attrs['names'], initialize=ij_init, bounds=(None, None)) ### declare the branch current flow constraints libbranch.declare_eq_branch_current(model=model, index_set=branch_attrs['names'], branches=branches) ### declare the ir/ij_aggregation constraints libbus.declare_eq_i_aggregation_at_bus( model=model, index_set=bus_attrs['names'], bus_bs_fixed_shunts=bus_bs_fixed_shunts, bus_gs_fixed_shunts=bus_gs_fixed_shunts, inlet_branches_by_bus=inlet_branches_by_bus, outlet_branches_by_bus=outlet_branches_by_bus) ### declare the pq balances libbus.declare_eq_p_balance_with_i_aggregation( model=model, index_set=bus_attrs['names'], bus_p_loads=bus_p_loads, gens_by_bus=gens_by_bus, **p_rhs_kwargs) libbus.declare_eq_q_balance_with_i_aggregation( model=model, index_set=bus_attrs['names'], bus_q_loads=bus_q_loads, gens_by_bus=gens_by_bus, **q_rhs_kwargs) ### declare the thermal limits libbranch.declare_ineq_s_branch_thermal_limit( model=model, index_set=branch_attrs['names'], branches=branches, s_thermal_limits=s_max, flow_type=FlowType.CURRENT) ### declare the voltage min and max inequalities libbus.declare_ineq_vm_bus_lbub(model=model, index_set=bus_attrs['names'], buses=buses, coordinate_type=CoordinateType.RECTANGULAR) ### declare angle difference limits on interconnected buses libbranch.declare_ineq_angle_diff_branch_lbub( model=model, index_set=branch_attrs['names'], branches=branches, coordinate_type=CoordinateType.RECTANGULAR) ### declare the generator cost objective libgen.declare_expression_pgqg_operating_cost(model=model, index_set=gen_attrs['names'], p_costs=gen_attrs['p_cost'], q_costs=gen_attrs.get( 'q_cost', None)) obj_expr = sum(model.pg_operating_cost[gen_name] for gen_name in model.pg_operating_cost) if include_feasibility_slack: obj_expr += penalty_expr if hasattr(model, 'qg_operating_cost'): obj_expr += sum(model.qg_operating_cost[gen_name] for gen_name in model.qg_operating_cost) model.obj = pe.Objective(expr=obj_expr) return model, md
def create_ptdf_dcopf_model(model_data, include_feasibility_slack=False, base_point=BasePointType.FLATSTART, ptdf_options=None): ptdf_options = lpu.populate_default_ptdf_options(ptdf_options) baseMVA = model_data.data['system']['baseMVA'] lpu.check_and_scale_ptdf_options(ptdf_options, baseMVA) md = model_data.clone_in_service() tx_utils.scale_ModelData_to_pu(md, inplace=True) 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')) gen_attrs = md.attributes(element_type='generator') ## to keep things in order buses_idx = tuple(buses.keys()) branches_idx = tuple(branches.keys()) 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) model = pe.ConcreteModel() ### declare (and fix) the loads at the buses bus_p_loads, _ = tx_utils.dict_of_bus_loads(buses, loads) libbus.declare_var_pl(model, buses_idx, initialize=bus_p_loads) model.pl.fix() ### declare the fixed shunts at the buses _, bus_gs_fixed_shunts = tx_utils.dict_of_bus_fixed_shunts(buses, shunts) ### 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(model, gen_attrs['names'], initialize=pg_init, bounds=zip_items(gen_attrs['p_min'], gen_attrs['p_max'])) ### include the feasibility slack for the system balance p_rhs_kwargs = {} if include_feasibility_slack: p_rhs_kwargs, penalty_expr = _include_system_feasibility_slack( model, gen_attrs, bus_p_loads) ### declare the p balance libbus.declare_eq_p_balance_ed(model=model, index_set=buses_idx, bus_p_loads=bus_p_loads, gens_by_bus=gens_by_bus, bus_gs_fixed_shunts=bus_gs_fixed_shunts, **p_rhs_kwargs) ### declare net withdraw expression for use in PTDF power flows libbus.declare_expr_p_net_withdraw_at_bus( model=model, index_set=buses_idx, bus_p_loads=bus_p_loads, gens_by_bus=gens_by_bus, bus_gs_fixed_shunts=bus_gs_fixed_shunts, ) ### add "blank" power flow expressions libbranch.declare_expr_pf( model=model, index_set=branches_idx, ) ## Do and store PTDF calculation reference_bus = md.data['system']['reference_bus'] PTDF = ptdf_utils.get_ptdf_potentially_from_file(ptdf_options, branches_idx, buses_idx) if PTDF is None: PTDF = ptdf_utils.PTDFMatrix(branches, buses, reference_bus, base_point, ptdf_options, branches_keys=branches_idx, buses_keys=buses_idx) model._PTDF = PTDF model._ptdf_options = ptdf_options ptdf_utils.write_ptdf_potentially_to_file(ptdf_options, PTDF) if ptdf_options['lazy']: ### add "blank" real power flow limits libbranch.declare_ineq_p_branch_thermal_bounds( model=model, index_set=branches_idx, branches=branches, p_thermal_limits=None, approximation_type=None, ) ### add helpers for tracking monitored branches lpu.add_monitored_flow_tracker(model) ### add initial branches to monitored set lpu.add_initial_monitored_branches(model, branches, branches_idx, ptdf_options, PTDF) else: p_max = {k: branches[k]['rating_long_term'] for k in branches.keys()} ## add all the constraints ### declare the branch power flow approximation constraints libbranch.declare_eq_branch_power_ptdf_approx( model=model, index_set=branches_idx, PTDF=PTDF, abs_ptdf_tol=ptdf_options['abs_ptdf_tol'], rel_ptdf_tol=ptdf_options['rel_ptdf_tol'], ) ### add all the limits libbranch.declare_ineq_p_branch_thermal_lbub( model=model, index_set=branches_idx, branches=branches, p_thermal_limits=p_max, approximation_type=ApproximationType.PTDF, ) ### declare the generator cost objective libgen.declare_expression_pgqg_operating_cost(model=model, index_set=gen_attrs['names'], p_costs=gen_attrs['p_cost']) obj_expr = sum(model.pg_operating_cost[gen_name] for gen_name in model.pg_operating_cost) if include_feasibility_slack: obj_expr += penalty_expr model.obj = pe.Objective(expr=obj_expr) return model, md
def _create_base_relaxation(model_data): md = model_data.clone_in_service() tx_utils.scale_ModelData_to_pu(md, inplace=True) 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')) 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) bus_pairs = zip_items(branch_attrs['from_bus'], branch_attrs['to_bus']) unique_bus_pairs = list( OrderedDict((val, None) for idx, val in bus_pairs.items())) model = pe.ConcreteModel() ### declare (and fix) the loads at the buses bus_p_loads, bus_q_loads = tx_utils.dict_of_bus_loads(buses, loads) libbus.declare_var_pl(model, bus_attrs['names'], initialize=bus_p_loads) libbus.declare_var_ql(model, bus_attrs['names'], initialize=bus_q_loads) model.pl.fix() model.ql.fix() ### declare the fixed shunts at the buses bus_bs_fixed_shunts, bus_gs_fixed_shunts = tx_utils.dict_of_bus_fixed_shunts( buses, shunts) libbus.declare_var_vmsq( model=model, index_set=bus_attrs['names'], initialize={k: v**2 for k, v in bus_attrs['vm'].items()}, bounds=zip_items({k: v**2 for k, v in bus_attrs['v_min'].items()}, {k: v**2 for k, v in bus_attrs['v_max'].items()})) libbranch.declare_var_c(model=model, index_set=unique_bus_pairs) libbranch.declare_var_s(model=model, index_set=unique_bus_pairs) ### declare the generator real and reactive 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(model, gen_attrs['names'], initialize=pg_init, bounds=zip_items(gen_attrs['p_min'], gen_attrs['p_max'])) qg_init = { k: (gen_attrs['q_min'][k] + gen_attrs['q_max'][k]) / 2.0 for k in gen_attrs['qg'] } libgen.declare_var_qg(model, gen_attrs['names'], initialize=qg_init, bounds=zip_items(gen_attrs['q_min'], gen_attrs['q_max'])) ### 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'] } s_max = {k: branches[k]['rating_long_term'] for k in branches.keys()} s_lbub = dict() for k in branches.keys(): if s_max[k] is None: s_lbub[k] = (None, None) else: s_lbub[k] = (-s_max[k], s_max[k]) pf_bounds = s_lbub pt_bounds = s_lbub qf_bounds = s_lbub qt_bounds = s_lbub pf_init = dict() pt_init = dict() qf_init = dict() qt_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) itr_init = tx_calc.calculate_itr(vr_init[from_bus], vj_init[from_bus], vr_init[to_bus], vj_init[to_bus], y_matrix) itj_init = tx_calc.calculate_itj(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]) pt_init[branch_name] = tx_calc.calculate_p(itr_init, itj_init, vr_init[to_bus], vj_init[to_bus]) qf_init[branch_name] = tx_calc.calculate_q(ifr_init, ifj_init, vr_init[from_bus], vj_init[from_bus]) qt_init[branch_name] = tx_calc.calculate_q(itr_init, itj_init, vr_init[to_bus], vj_init[to_bus]) libbranch.declare_var_pf(model=model, index_set=branch_attrs['names'], initialize=pf_init, bounds=pf_bounds) libbranch.declare_var_pt(model=model, index_set=branch_attrs['names'], initialize=pt_init, bounds=pt_bounds) libbranch.declare_var_qf(model=model, index_set=branch_attrs['names'], initialize=qf_init, bounds=qf_bounds) libbranch.declare_var_qt(model=model, index_set=branch_attrs['names'], initialize=qt_init, bounds=qt_bounds) ### declare the branch power flow constraints libbranch.declare_eq_branch_power(model=model, index_set=branch_attrs['names'], branches=branches) ### declare the pq balances libbus.declare_eq_p_balance(model=model, 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) libbus.declare_eq_q_balance(model=model, index_set=bus_attrs['names'], bus_q_loads=bus_q_loads, gens_by_bus=gens_by_bus, bus_bs_fixed_shunts=bus_bs_fixed_shunts, inlet_branches_by_bus=inlet_branches_by_bus, outlet_branches_by_bus=outlet_branches_by_bus) ### declare the thermal limits libbranch.declare_ineq_s_branch_thermal_limit( model=model, index_set=branch_attrs['names'], branches=branches, s_thermal_limits=s_max, flow_type=FlowType.POWER) ### declare the generator cost objective libgen.declare_expression_pgqg_operating_cost(model=model, index_set=gen_attrs['names'], p_costs=gen_attrs['p_cost'], q_costs=gen_attrs.get( 'q_cost', None)) obj_expr = sum(model.pg_operating_cost[gen_name] for gen_name in model.pg_operating_cost) if hasattr(model, 'qg_operating_cost'): obj_expr += sum(model.qg_operating_cost[gen_name] for gen_name in model.qg_operating_cost) model.obj = pe.Objective(expr=obj_expr) return model, md
def create_copperplate_dispatch_approx_model(model_data, include_feasibility_slack=False): md = model_data.clone_in_service() tx_utils.scale_ModelData_to_pu(md, inplace=True) 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')) gen_attrs = md.attributes(element_type='generator') bus_attrs = md.attributes(element_type='bus') 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) model = pe.ConcreteModel() ### declare (and fix) the loads at the buses bus_p_loads, _ = tx_utils.dict_of_bus_loads(buses, loads) libbus.declare_var_pl(model, bus_attrs['names'], initialize=bus_p_loads) model.pl.fix() ### declare the fixed shunts at the buses _, bus_gs_fixed_shunts = tx_utils.dict_of_bus_fixed_shunts(buses, shunts) ### 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(model, gen_attrs['names'], initialize=pg_init, bounds=zip_items(gen_attrs['p_min'], gen_attrs['p_max'])) ### include the feasibility slack for the system balance p_rhs_kwargs = {} if include_feasibility_slack: p_marginal_slack_penalty = _validate_and_extract_slack_penalty( model_data) p_rhs_kwargs, penalty_expr = _include_system_feasibility_slack( model, bus_p_loads, gen_attrs, p_marginal_slack_penalty) ### declare the p balance libbus.declare_eq_p_balance_ed(model=model, 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, **p_rhs_kwargs) ### declare the generator cost objective libgen.declare_expression_pgqg_operating_cost(model=model, index_set=gen_attrs['names'], p_costs=gen_attrs['p_cost']) obj_expr = sum(model.pg_operating_cost[gen_name] for gen_name in model.pg_operating_cost) if include_feasibility_slack: obj_expr += penalty_expr model.obj = pe.Objective(expr=obj_expr) return model, md
def create_btheta_losses_dcopf_model(model_data, relaxation_type=RelaxationType.SOC, include_angle_diff_limits=False, include_feasibility_slack=False): md = model_data.clone_in_service() tx_utils.scale_ModelData_to_pu(md, inplace = True) 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')) gen_attrs = md.attributes(element_type='generator') bus_attrs = md.attributes(element_type='bus') branch_attrs = md.attributes(element_type='branch') load_attrs = md.attributes(element_type='load') shunt_attrs = md.attributes(element_type='shunt') 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) model = pe.ConcreteModel() ### declare (and fix) the loads at the buses bus_p_loads, _ = tx_utils.dict_of_bus_loads(buses, loads) libbus.declare_var_pl(model, bus_attrs['names'], initialize=bus_p_loads) model.pl.fix() ### 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']} libbus.declare_var_va(model, bus_attrs['names'], initialize=bus_attrs['va'], bounds=va_bounds ) dva_initialize = {k: 0.0 for k in branch_attrs['names']} libbranch.declare_var_dva(model, branch_attrs['names'], initialize=dva_initialize ) ### include the feasibility slack for the bus balances p_rhs_kwargs = {} penalty_expr = None if include_feasibility_slack: p_rhs_kwargs, penalty_expr = _include_feasibility_slack(model, bus_attrs, gen_attrs, bus_p_loads) ### fix the reference bus ref_bus = md.data['system']['reference_bus'] ref_angle = md.data['system']['reference_bus_angle'] model.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(model, gen_attrs['names'], initialize=pg_init, bounds=zip_items(gen_attrs['p_min'], gen_attrs['p_max']) ) ### 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']} p_max = {k: branches[k]['rating_long_term'] for k in branches.keys()} pf_bounds = {k: (-p_max[k],p_max[k]) for k in branches.keys()} 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]) pfl_bounds = {k: (0,p_max[k]**2) for k in branches.keys()} pfl_init = {k: 0 for k in branches.keys()} libbranch.declare_var_pf(model=model, index_set=branch_attrs['names'], initialize=pf_init, bounds=pf_bounds ) libbranch.declare_var_pfl(model=model, index_set=branch_attrs['names'], initialize=pfl_init, bounds=pfl_bounds ) ### declare the angle difference constraint libbranch.declare_eq_branch_dva(model=model, index_set=branch_attrs['names'], branches=branches ) ### declare the branch power flow approximation constraints libbranch.declare_eq_branch_power_btheta_approx(model=model, index_set=branch_attrs['names'], branches=branches, approximation_type=ApproximationType.BTHETA_LOSSES ) ### declare the branch power loss approximation constraints libbranch.declare_eq_branch_loss_btheta_approx(model=model, index_set=branch_attrs['names'], branches=branches, relaxation_type=relaxation_type ) ### declare the p balance libbus.declare_eq_p_balance_dc_approx(model=model, 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_LOSSES, **p_rhs_kwargs ) ### declare the real power flow limits libbranch.declare_ineq_p_branch_thermal_lbub(model=model, index_set=branch_attrs['names'], branches=branches, p_thermal_limits=p_max, approximation_type=ApproximationType.BTHETA ) ### declare angle difference limits on interconnected buses if include_angle_diff_limits: libbranch.declare_ineq_angle_diff_branch_lbub(model=model, index_set=branch_attrs['names'], branches=branches, coordinate_type=CoordinateType.POLAR ) ### declare the generator cost objective libgen.declare_expression_pgqg_operating_cost(model=model, index_set=gen_attrs['names'], p_costs=gen_attrs['p_cost'] ) obj_expr = sum(model.pg_operating_cost[gen_name] for gen_name in model.pg_operating_cost) if include_feasibility_slack: obj_expr += penalty_expr model.obj = pe.Objective(expr=obj_expr) return model, md
def _create_base_ac_with_pwl_approx_model(model_data, branch_dict, Q, include_feasibility_slack=False): md = model_data.clone_in_service() tx_utils.scale_ModelData_to_pu(md, inplace = True) 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')) 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) bus_pairs = zip_items(branch_attrs['from_bus'], branch_attrs['to_bus']) unique_bus_pairs = list(OrderedDict((val, None) for idx, val in bus_pairs.items())) model = pe.ConcreteModel() ### declare (and fix) the loads at the buses bus_p_loads, bus_q_loads = tx_utils.dict_of_bus_loads(buses, loads) libbus.declare_var_pl(model, bus_attrs['names'], initialize=bus_p_loads) libbus.declare_var_ql(model, bus_attrs['names'], initialize=bus_q_loads) model.pl.fix() model.ql.fix() ### declare the fixed shunts at the buses bus_bs_fixed_shunts, bus_gs_fixed_shunts = tx_utils.dict_of_bus_fixed_shunts(buses, shunts) libbus.declare_var_vm(model=model, index_set=bus_attrs['names'], initialize=bus_attrs['vm'], bounds=zip_items(bus_attrs['v_min'], bus_attrs['v_max'])) libbus.declare_var_vmsq(model=model, index_set=bus_attrs['names'], initialize={k: v**2 for k, v in bus_attrs['vm'].items()}, bounds=zip_items({k: v**2 for k, v in bus_attrs['v_min'].items()}, {k: v**2 for k, v in bus_attrs['v_max'].items()})) # libbranch.declare_var_c(model=model, index_set=unique_bus_pairs) # libbranch.declare_var_s(model=model, index_set=unique_bus_pairs) ### declare the polar voltages va_bounds = {k: (-math.pi, math.pi) for k in bus_attrs['va']} libbus.declare_var_va(model, bus_attrs['names'], initialize=bus_attrs['va'], bounds=va_bounds ) ###declare the phase angle differences in each branch libbranch.declare_var_dva(model, index_set=unique_bus_pairs) libbranch.declare_eq_delta_va(model, index_set=unique_bus_pairs) ### include the feasibility slack for the bus balances p_rhs_kwargs = {} q_rhs_kwargs = {} if include_feasibility_slack: p_rhs_kwargs, q_rhs_kwargs, penalty_expr = _include_feasibility_slack(model, bus_attrs, gen_attrs, bus_p_loads, bus_q_loads) ### declare the generator real and reactive 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(model, gen_attrs['names'], initialize=pg_init, bounds=zip_items(gen_attrs['p_min'], gen_attrs['p_max']) ) qg_init = {k: (gen_attrs['q_min'][k] + gen_attrs['q_max'][k]) / 2.0 for k in gen_attrs['qg']} libgen.declare_var_qg(model, gen_attrs['names'], initialize=qg_init, bounds=zip_items(gen_attrs['q_min'], gen_attrs['q_max']) ) ### 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']} s_max = {k: branches[k]['rating_long_term'] for k in branches.keys()} s_lbub = dict() for k in branches.keys(): if s_max[k] is None: s_lbub[k] = (None, None) else: s_lbub[k] = (-s_max[k],s_max[k]) pf_bounds = s_lbub pt_bounds = s_lbub qf_bounds = s_lbub qt_bounds = s_lbub pf_init = dict() pt_init = dict() qf_init = dict() qt_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) itr_init = tx_calc.calculate_itr(vr_init[from_bus], vj_init[from_bus], vr_init[to_bus], vj_init[to_bus], y_matrix) itj_init = tx_calc.calculate_itj(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]) pt_init[branch_name] = tx_calc.calculate_p(itr_init, itj_init, vr_init[to_bus], vj_init[to_bus]) qf_init[branch_name] = tx_calc.calculate_q(ifr_init, ifj_init, vr_init[from_bus], vj_init[from_bus]) qt_init[branch_name] = tx_calc.calculate_q(itr_init, itj_init, vr_init[to_bus], vj_init[to_bus]) libbranch.declare_var_pf(model=model, index_set=branch_attrs['names'], initialize=pf_init, bounds=pf_bounds ) libbranch.declare_var_pt(model=model, index_set=branch_attrs['names'], initialize=pt_init, bounds=pt_bounds ) libbranch.declare_var_qf(model=model, index_set=branch_attrs['names'], initialize=qf_init, bounds=qf_bounds ) libbranch.declare_var_qt(model=model, index_set=branch_attrs['names'], initialize=qt_init, bounds=qt_bounds ) ### declare the branch power flow constraints ### declare a binary on/off variable for deenergizing a given branch decl.declare_var('u', model=model, index_set=branch_attrs['names'], within=pe.Binary) model.u.fix(1) branch_name_set = decl.declare_set('branch_name', model=model, index_set=branch_attrs['names']) model.box_index_set = pe.RangeSet(Q) model.power_type_set = pe.Set(initialize=[0,1]) #Note: 0 is for power_type == "Active"; 1 is for power_type=="Reactive" #For active power energization/deenergization model.u_branch = pe.Var(branch_name_set, model.box_index_set, model.power_type_set, within=pe.Binary) #For selecting the appropriate interval of the PWL approximation model.dva_branch = pe.Var(branch_name_set, model.box_index_set, model.power_type_set) #(5) - Constraints for the on/off variable u def u_sum_rule(model, branch_name, j): return model.u[branch_name] == sum(model.u_branch[branch_name, i, j] for i in model.box_index_set) model.u_sum_Constr = pe.Constraint(branch_name_set, model.power_type_set, rule=u_sum_rule) #(6) - Constraints that sum of dva variables should be equal to total dva #Upper bound constraints def delta_branch_ub_rule(model, branch_name, j): branch = branches[branch_name] from_bus = branch['from_bus'] to_bus = branch['to_bus'] return -model.dva[(from_bus, to_bus)] + sum(model.dva_branch[branch_name, i, j] for i in model.box_index_set) <= math.pi*(1-model.u[branch_name]) model.delta_branch_ub_Constr = pe.Constraint(branch_name_set, model.power_type_set, rule=delta_branch_ub_rule) #Lower bound constraints def delta_branch_lb_rule(model, branch_name, j): branch = branches[branch_name] from_bus = branch['from_bus'] to_bus = branch['to_bus'] return -model.dva[(from_bus, to_bus)] + sum(model.dva_branch[branch_name, i, j] for i in model.box_index_set) >= -math.pi*(1-model.u[branch_name]) model.delta_branch_lb_Constr = pe.Constraint(branch_name_set, model.power_type_set, rule=delta_branch_lb_rule) #(7) - Constraints that force dva variable to be in only one interval #Upper bound def delta_branch_box_ub_rule(model, branch_name, i, j): if j==0: delta_ub = branch_dict["Active_from_bus"][branch_name]['boxes']['coords'][i-1][7][2] else: delta_ub = branch_dict["Reactive_from_bus"][branch_name]['boxes']['coords'][i-1][7][2] return model.dva_branch[branch_name, i, j] <= delta_ub*model.u_branch[branch_name, i, j] model.delta_branch_box_ub_Constr = pe.Constraint(branch_name_set, model.box_index_set, model.power_type_set, rule=delta_branch_box_ub_rule) def delta_branch_box_lb_rule(model, branch_name, i, j): if j==0: delta_lb = branch_dict["Active_from_bus"][branch_name]['boxes']['coords'][i-1][0][2] else: delta_lb = branch_dict["Reactive_from_bus"][branch_name]['boxes']['coords'][i-1][0][2] return model.dva_branch[branch_name, i, j] >= delta_lb*model.u_branch[branch_name, i, j] model.delta_branch_box_lb_Constr = pe.Constraint(branch_name_set, model.box_index_set, model.power_type_set, rule=delta_branch_box_lb_rule) #(8) - Approximating power flow equation by PWL approximation #Active_from_bus def pwl_active_from_ub_rule(model, branch_name, i): branch = branches[branch_name] from_bus = branch['from_bus'] to_bus = branch['to_bus'] g = tx_calc.calculate_conductance(branch) b = tx_calc.calculate_susceptance(branch) coeffs = branch_dict["Active_from_bus"][branch_name]['boxes']['coefficients'][i-1] #M = 10*(g+b) + 4*(coeffs[0]+coeffs[1]+coeffs[2]+coeffs[3]) M = 2*s_max[branch_name] + 10*(np.abs(coeffs[0])+np.abs(coeffs[1])+np.abs(coeffs[2])+np.abs(coeffs[3])) return -model.pf[branch_name] + coeffs[0]*model.vm[from_bus] + coeffs[1]*model.vm[to_bus] + coeffs[2]*model.dva_branch[branch_name, i, 0] + coeffs[3] <= M*(1-model.u_branch[branch_name, i, 0]) model.pwl_active_from_ub_Constr = pe.Constraint(branch_name_set, model.box_index_set, rule=pwl_active_from_ub_rule) def pwl_active_from_lb_rule(model, branch_name, i): branch = branches[branch_name] from_bus = branch['from_bus'] to_bus = branch['to_bus'] g = tx_calc.calculate_conductance(branch) b = tx_calc.calculate_susceptance(branch) coeffs = branch_dict["Active_from_bus"][branch_name]['boxes']['coefficients'][i-1] #M = -(10*(g+b) + 4*(coeffs[0]+coeffs[1]+coeffs[2]+coeffs[3])) M = -2*s_max[branch_name] - 10*(np.abs(coeffs[0])+np.abs(coeffs[1])+np.abs(coeffs[2])+np.abs(coeffs[3])) return -model.pf[branch_name] + coeffs[0]*model.vm[from_bus] + coeffs[1]*model.vm[to_bus] + coeffs[2]*model.dva_branch[branch_name, i, 0] + coeffs[3] >= M*(1-model.u_branch[branch_name, i, 0]) model.pwl_active_from_lb_Constr = pe.Constraint(branch_name_set, model.box_index_set, rule=pwl_active_from_lb_rule) #Active_to_bus def pwl_active_to_ub_rule(model, branch_name, i): branch = branches[branch_name] from_bus = branch['from_bus'] to_bus = branch['to_bus'] g = tx_calc.calculate_conductance(branch) b = tx_calc.calculate_susceptance(branch) coeffs = branch_dict["Active_to_bus"][branch_name]['boxes']['coefficients'][i-1] #M = 10*(g+b) + 4*(coeffs[0]+coeffs[1]+coeffs[2]+coeffs[3]) M = 2*s_max[branch_name] + 10*(np.abs(coeffs[0])+np.abs(coeffs[1])+np.abs(coeffs[2])+np.abs(coeffs[3])) return -model.pt[branch_name] + coeffs[0]*model.vm[from_bus] + coeffs[1]*model.vm[to_bus] + coeffs[2]*model.dva_branch[branch_name, i, 0] + coeffs[3] <= M*(1-model.u_branch[branch_name, i, 0]) model.pwl_active_to_ub_Constr = pe.Constraint(branch_name_set, model.box_index_set, rule=pwl_active_to_ub_rule) def pwl_active_to_lb_rule(model, branch_name, i): branch = branches[branch_name] from_bus = branch['from_bus'] to_bus = branch['to_bus'] g = tx_calc.calculate_conductance(branch) b = tx_calc.calculate_susceptance(branch) coeffs = branch_dict["Active_to_bus"][branch_name]['boxes']['coefficients'][i-1] #M = -(10*(g+b) + 4*(coeffs[0]+coeffs[1]+coeffs[2]+coeffs[3])) M = -2*s_max[branch_name] - 10*(np.abs(coeffs[0])+np.abs(coeffs[1])+np.abs(coeffs[2])+np.abs(coeffs[3])) return -model.pt[branch_name] + coeffs[0]*model.vm[from_bus] + coeffs[1]*model.vm[to_bus] + coeffs[2]*model.dva_branch[branch_name, i, 0] + coeffs[3] >= M*(1-model.u_branch[branch_name, i, 0]) model.pwl_active_to_lb_Constr = pe.Constraint(branch_name_set, model.box_index_set, rule=pwl_active_to_lb_rule) #Reactive_from_bus def pwl_reactive_from_ub_rule(model, branch_name, i): branch = branches[branch_name] from_bus = branch['from_bus'] to_bus = branch['to_bus'] g = tx_calc.calculate_conductance(branch) b = tx_calc.calculate_susceptance(branch) coeffs = branch_dict["Reactive_from_bus"][branch_name]['boxes']['coefficients'][i-1] #M = 10*(g+b) + 4*(coeffs[0]+coeffs[1]+coeffs[2]+coeffs[3]) M = 2*s_max[branch_name] + 10*(np.abs(coeffs[0])+np.abs(coeffs[1])+np.abs(coeffs[2])+np.abs(coeffs[3])) return -model.qf[branch_name] + coeffs[0]*model.vm[from_bus] + coeffs[1]*model.vm[to_bus] + coeffs[2]*model.dva_branch[branch_name, i, 1] + coeffs[3] <= M*(1-model.u_branch[branch_name, i, 1]) model.pwl_reactive_from_ub_Constr = pe.Constraint(branch_name_set, model.box_index_set, rule=pwl_reactive_from_ub_rule) def pwl_reactive_from_lb_rule(model, branch_name, i): branch = branches[branch_name] from_bus = branch['from_bus'] to_bus = branch['to_bus'] g = tx_calc.calculate_conductance(branch) b = tx_calc.calculate_susceptance(branch) coeffs = branch_dict["Reactive_from_bus"][branch_name]['boxes']['coefficients'][i-1] #M = -(10*(g+b) + 4*(coeffs[0]+coeffs[1]+coeffs[2]+coeffs[3])) M = -2*s_max[branch_name] - 10*(np.abs(coeffs[0])+np.abs(coeffs[1])+np.abs(coeffs[2])+np.abs(coeffs[3])) return -model.qf[branch_name] + coeffs[0]*model.vm[from_bus] + coeffs[1]*model.vm[to_bus] + coeffs[2]*model.dva_branch[branch_name, i, 1] + coeffs[3] >= M*(1-model.u_branch[branch_name, i, 1]) model.pwl_reactive_from_lb_Constr = pe.Constraint(branch_name_set, model.box_index_set, rule=pwl_reactive_from_lb_rule) #Reactive_to_bus def pwl_reactive_to_ub_rule(model, branch_name, i): branch = branches[branch_name] from_bus = branch['from_bus'] to_bus = branch['to_bus'] g = tx_calc.calculate_conductance(branch) b = tx_calc.calculate_susceptance(branch) coeffs = branch_dict["Reactive_to_bus"][branch_name]['boxes']['coefficients'][i-1] #M = 10*(g+b) + 4*(coeffs[0]+coeffs[1]+coeffs[2]+coeffs[3]) M = 2*s_max[branch_name] + 10*(np.abs(coeffs[0])+np.abs(coeffs[1])+np.abs(coeffs[2])+np.abs(coeffs[3])) return -model.qt[branch_name] + coeffs[0]*model.vm[from_bus] + coeffs[1]*model.vm[to_bus] + coeffs[2]*model.dva_branch[branch_name, i, 1] + coeffs[3] <= M*(1-model.u_branch[branch_name, i, 1]) model.pwl_reactive_to_ub_Constr = pe.Constraint(branch_name_set, model.box_index_set, rule=pwl_reactive_to_ub_rule) def pwl_reactive_to_lb_rule(model, branch_name, i): branch = branches[branch_name] from_bus = branch['from_bus'] to_bus = branch['to_bus'] g = tx_calc.calculate_conductance(branch) b = tx_calc.calculate_susceptance(branch) coeffs = branch_dict["Reactive_to_bus"][branch_name]['boxes']['coefficients'][i-1] #M = -(10*(g+b) + 4*(coeffs[0]+coeffs[1]+coeffs[2]+coeffs[3])) M = -2*s_max[branch_name] - 10*(np.abs(coeffs[0])+np.abs(coeffs[1])+np.abs(coeffs[2])+np.abs(coeffs[3])) return -model.qt[branch_name] + coeffs[0]*model.vm[from_bus] + coeffs[1]*model.vm[to_bus] + coeffs[2]*model.dva_branch[branch_name, i, 1] + coeffs[3] >= M*(1-model.u_branch[branch_name, i, 1]) model.pwl_reactive_to_lb_Constr = pe.Constraint(branch_name_set, model.box_index_set, rule=pwl_reactive_to_lb_rule) ### declare the pq balances libbus.declare_eq_p_balance(model=model, 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, **p_rhs_kwargs ) libbus.declare_eq_q_balance(model=model, index_set=bus_attrs['names'], bus_q_loads=bus_q_loads, gens_by_bus=gens_by_bus, bus_bs_fixed_shunts=bus_bs_fixed_shunts, inlet_branches_by_bus=inlet_branches_by_bus, outlet_branches_by_bus=outlet_branches_by_bus, **q_rhs_kwargs ) ### declare the thermal limits libbranch.declare_ineq_s_branch_thermal_limit(model=model, index_set=branch_attrs['names'], branches=branches, s_thermal_limits=s_max, flow_type=FlowType.POWER ) # declare angle difference limits on interconnected buses # libbranch.declare_ineq_angle_diff_branch_lbub_c_s(model=model, # index_set=branch_attrs['names'], # branches=branches # ) ### declare the generator cost objective libgen.declare_expression_pgqg_operating_cost(model=model, index_set=gen_attrs['names'], p_costs=gen_attrs['p_cost'], q_costs=gen_attrs.get('q_cost', None) ) obj_expr = sum(model.pg_operating_cost[gen_name] for gen_name in model.pg_operating_cost) if include_feasibility_slack: obj_expr += penalty_expr if hasattr(model, 'qg_operating_cost'): obj_expr += sum(model.qg_operating_cost[gen_name] for gen_name in model.qg_operating_cost) model.obj = pe.Objective(expr=obj_expr) return model, md