def create_psv_acpf_model(model_data): model, md = _create_base_acpf_model(model_data) gens = dict(md.elements(element_type='generator')) buses = dict(md.elements(element_type='bus')) bus_attrs = md.attributes(element_type='bus') branch_attrs = md.attributes(element_type='branch') gens_by_bus = tx_utils.gens_by_bus(buses, gens) buses_with_gens = _buses_with_gens(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()).keys()) # declare the polar voltages libbranch.declare_var_dva(model=model, index_set=unique_bus_pairs, initialize=0 ) libbus.declare_var_vm(model, bus_attrs['names'], initialize=bus_attrs['vm'] ) libbus.declare_var_va(model, bus_attrs['names'], initialize=tx_utils.radians_from_degrees_dict(bus_attrs['va']) ) ### In a system with N buses and G generators, there are then 2(N-1)-(G-1) unknowns. ### 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)) model.vm[ref_bus].fixed = True # if there is more than one generator at the reference # bus, then we fix the pg for all but one for i,g in enumerate(gens_by_bus[ref_bus]): if i > 0: model.pg[g].fixed = True for bus_name in bus_attrs['names']: if bus_name != ref_bus and bus_name in buses_with_gens: model.vm[bus_name].fixed = True for gen_name in gens_by_bus[bus_name]: model.pg[gen_name].fixed = True # relate c, s, and vmsq to vm and va libbranch.declare_eq_delta_va(model=model, index_set=unique_bus_pairs) libbus.declare_eq_vmsq(model=model, index_set=bus_attrs['names'], coordinate_type=CoordinateType.POLAR) libbranch.declare_eq_c(model=model, index_set=unique_bus_pairs, coordinate_type=CoordinateType.POLAR) libbranch.declare_eq_s(model=model, index_set=unique_bus_pairs, coordinate_type=CoordinateType.POLAR) return model, md
def create_psv_acopf_model(model_data, include_feasibility_slack=False, pw_cost_model='delta'): model, md = _create_base_power_ac_model( model_data, include_feasibility_slack=include_feasibility_slack, pw_cost_model=pw_cost_model) bus_attrs = md.attributes(element_type='bus') branch_attrs = md.attributes(element_type='branch') 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()) # declare the polar voltages libbranch.declare_var_dva(model=model, index_set=unique_bus_pairs, initialize=0, bounds=(-pi / 2, pi / 2)) libbus.declare_var_vm(model, bus_attrs['names'], initialize=bus_attrs['vm'], bounds=zip_items(bus_attrs['v_min'], bus_attrs['v_max'])) 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) # 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)) # relate c, s, and vmsq to vm and va libbranch.declare_eq_delta_va(model=model, index_set=unique_bus_pairs) libbus.declare_eq_vmsq(model=model, index_set=bus_attrs['names'], coordinate_type=CoordinateType.POLAR) libbranch.declare_eq_c(model=model, index_set=unique_bus_pairs, coordinate_type=CoordinateType.POLAR) libbranch.declare_eq_s(model=model, index_set=unique_bus_pairs, coordinate_type=CoordinateType.POLAR) return model, md
def create_atan_acopf_model(model_data, include_feasibility_slack=False): model, md = _create_base_power_ac_model( model_data, include_feasibility_slack=include_feasibility_slack) branch_attrs = md.attributes(element_type='branch') bus_pairs = zip_items(branch_attrs['from_bus'], branch_attrs['to_bus']) unique_bus_pairs = OrderedSet(val for val in bus_pairs.values()) for fb, tb in unique_bus_pairs: assert (tb, fb) not in unique_bus_pairs graph = get_networkx_graph(md) ref_bus = md.data['system']['reference_bus'] cycle_basis = networkx.algorithms.cycle_basis(graph, root=ref_bus) cycle_basis_bus_pairs = OrderedSet() for cycle in cycle_basis: for ndx in range(len(cycle) - 1): b1 = cycle[ndx] b2 = cycle[ndx + 1] assert (b1, b2) in unique_bus_pairs or (b2, b1) in unique_bus_pairs if (b1, b2) in unique_bus_pairs: cycle_basis_bus_pairs.add((b1, b2)) else: cycle_basis_bus_pairs.add((b2, b1)) b1 = cycle[-1] b2 = cycle[0] assert (b1, b2) in unique_bus_pairs or (b2, b1) in unique_bus_pairs if (b1, b2) in unique_bus_pairs: cycle_basis_bus_pairs.add((b1, b2)) else: cycle_basis_bus_pairs.add((b2, b1)) libbranch.declare_var_dva(model=model, index_set=list(cycle_basis_bus_pairs), initialize=0, bounds=(-pi / 2, pi / 2)) libbranch.declare_eq_dva_arctan(model=model, index_set=list(cycle_basis_bus_pairs)) libbranch.declare_eq_dva_cycle_sum(model=model, cycle_basis=cycle_basis, valid_bus_pairs=cycle_basis_bus_pairs) libbranch.declare_ineq_soc(model=model, index_set=list(unique_bus_pairs), use_outer_approximation=False) libbranch.declare_ineq_soc_ub(model=model, index_set=list(unique_bus_pairs)) 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