Example #1
0
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
Example #2
0
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_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_explicit_subproblem(model,
                               subproblem,
                               model_data,
                               omega_key,
                               include_angle_diff_limits=False,
                               include_bigm=False):
    ### power system data
    md = model_data

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

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

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

    ### declare (and fix) the loads at the buses
    bus_p_loads, _ = tx_utils.dict_of_bus_loads(buses, loads)
    buses_with_loads = list(k for k in bus_p_loads.keys()
                            if bus_p_loads[k] != 0.)
    #libbus.declare_var_pl(model.subproblem, bus_attrs['names'], initialize=bus_p_loads)
    #model.subproblem.pl.fix()
    subproblem.pl = bus_p_loads

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

    ### declare the polar voltages
    va_bounds = {k: (-pi, pi) for k in bus_attrs['va']}
    libbus.declare_var_va(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']
    subproblem.va[ref_bus].fix(radians(ref_angle))

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

    ### declare the current flows in the branches
    vr_init = {
        k: bus_attrs['vm'][k] * pe.cos(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()
    for branch_name, branch in branches.items():
        from_bus = branch['from_bus']
        to_bus = branch['to_bus']
        y_matrix = tx_calc.calculate_y_matrix_from_branch(branch)
        ifr_init = tx_calc.calculate_ifr(vr_init[from_bus], vj_init[from_bus],
                                         vr_init[to_bus], vj_init[to_bus],
                                         y_matrix)
        ifj_init = tx_calc.calculate_ifj(vr_init[from_bus], vj_init[from_bus],
                                         vr_init[to_bus], vj_init[to_bus],
                                         y_matrix)
        pf_init[branch_name] = tx_calc.calculate_p(ifr_init, ifj_init,
                                                   vr_init[from_bus],
                                                   vj_init[from_bus])

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

    # need to include variable references on subproblem to variables, which exist on the master block
    bi.components.varref(subproblem)

    if include_bigm:
        # create big-M
        _create_bigm(subproblem, md)
        ### declare the branch power flow disjuncts
        subcons.declare_eq_branch_power_btheta_approx_bigM(
            model=subproblem,
            index_set=branch_attrs['names'],
            branches=branches)

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

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

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

    ### declare the load shed
    subcons.declare_ineq_load_shed_stochastic(model=subproblem,
                                              index_set=buses_with_loads,
                                              scenario=omega_key)

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

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

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

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

    return model, md
Example #5
0
def create_btheta_losses_dcopf_model(model_data,
                                     relaxation_type=RelaxationType.SOC,
                                     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'))

    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=tx_utils.radians_from_degrees_dict(
                              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_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()}
    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
    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