예제 #1
0
def netzbooster_constraints(n, snapshots):

    # Define indices & parameters
    n.determine_network_topology()
    sub = n.sub_networks.at["0", "obj"]
    sub.calculate_PTDF()
    sub.calculate_BODF()

    snapshots = snapshots  #n.snapshots
    buses = sub.buses_i()
    lines = sub.lines_i()
    outages = [l for l in lines if "outage" in l]

    ptdf = pd.DataFrame(sub.PTDF, index=lines, columns=buses)
    lodf = pd.DataFrame(sub.BODF, index=lines, columns=lines)

    logger.info("define variables")
    # ......
    # something like
    # define_variables(n, lower_limit, upper_limit, component (e.g. "Bus"), attr (e.g. "p"))

    logger.info("define constraints")
    # in general you define a constraint as
    lhs = linexpr(
        (1, get_var(n, "Bus", "p_pos").loc[(buses, outages, snapshots)]),
        (-1, get_var(n, "Bus", "P_pos").loc[buses]))
    define_constraints(n, lhs, "<=", 0., "Bus", "UpLimitPos")
예제 #2
0
def mga_objective(network, snapshots, direction, options):
    mga_variables = options['mga_variables']
    expr_list = []
    for i, variable in enumerate(mga_variables):
        if variable == 'transmission':
            expr_list.append(
                linexpr((direction[i], get_var(network, 'Link',
                                               'p_nom'))).sum())
        if variable == 'co2_emission':
            expr_list.append(
                linexpr((direction[i], get_var(
                    network, 'Generator', 'p').filter(network.generators.index[
                        network.generators.type == 'ocgt']))).sum().sum())
        elif variable == 'H2' or variable == 'battery':
            expr_list.append(
                linexpr((direction[i], get_var(
                    network, 'StorageUnit',
                    'p_nom').filter(network.storage_units.index[
                        network.storage_units.carrier == variable]))).sum())
        else:
            expr_list.append(
                linexpr(
                    (direction[i],
                     get_var(network, 'Generator',
                             'p_nom').filter(network.generators.index[
                                 network.generators.type == variable]))).sum())

    mga_obj = join_exprs(np.array(expr_list))
    write_objective(network, mga_obj)
예제 #3
0
def add_chp_constraints(n):

    electric_bool = (n.links.index.str.contains("urban central")
                     & n.links.index.str.contains("CHP")
                     & n.links.index.str.contains("electric"))
    heat_bool = (n.links.index.str.contains("urban central")
                 & n.links.index.str.contains("CHP")
                 & n.links.index.str.contains("heat"))

    electric = n.links.index[electric_bool]
    heat = n.links.index[heat_bool]

    electric_ext = n.links.index[electric_bool & n.links.p_nom_extendable]
    heat_ext = n.links.index[heat_bool & n.links.p_nom_extendable]

    electric_fix = n.links.index[electric_bool & ~n.links.p_nom_extendable]
    heat_fix = n.links.index[heat_bool & ~n.links.p_nom_extendable]

    link_p = get_var(n, "Link", "p")

    if not electric_ext.empty:

        link_p_nom = get_var(n, "Link", "p_nom")

        #ratio of output heat to electricity set by p_nom_ratio
        lhs = linexpr((n.links.loc[electric_ext, "efficiency"]
                       *n.links.loc[electric_ext, "p_nom_ratio"],
                       link_p_nom[electric_ext]),
                      (-n.links.loc[heat_ext, "efficiency"].values,
                       link_p_nom[heat_ext].values))

        define_constraints(n, lhs, "=", 0, 'chplink', 'fix_p_nom_ratio')

        #top_iso_fuel_line for extendable
        lhs = linexpr((1,link_p[heat_ext]),
                      (1,link_p[electric_ext].values),
                      (-1,link_p_nom[electric_ext].values))

        define_constraints(n, lhs, "<=", 0, 'chplink', 'top_iso_fuel_line_ext')

    if not electric_fix.empty:

        #top_iso_fuel_line for fixed
        lhs = linexpr((1,link_p[heat_fix]),
                      (1,link_p[electric_fix].values))

        rhs = n.links.loc[electric_fix, "p_nom"].values

        define_constraints(n, lhs, "<=", rhs, 'chplink', 'top_iso_fuel_line_fix')

    if not electric.empty:

        #backpressure
        lhs = linexpr((n.links.loc[electric, "c_b"].values
                       *n.links.loc[heat, "efficiency"],
                       link_p[heat]),
                      (-n.links.loc[electric, "efficiency"].values,
                       link_p[electric].values))

        define_constraints(n, lhs, "<=", 0, 'chplink', 'backpressure')
예제 #4
0
def mga_constraint(network, snapshots, options):
    scale = 1e-6
    # This function creates the MGA constraint
    gen_capital_cost = linexpr((scale * network.generators.capital_cost,
                                get_var(network, 'Generator', 'p_nom'))).sum()
    gen_marginal_cost = linexpr((scale * network.generators.marginal_cost,
                                 get_var(network, 'Generator',
                                         'p'))).sum().sum()
    store_capital_cost = linexpr((scale * network.storage_units.capital_cost,
                                  get_var(network, 'StorageUnit',
                                          'p_nom'))).sum()
    link_capital_cost = linexpr(
        (scale * network.links.capital_cost, get_var(network, 'Link',
                                                     'p_nom'))).sum()
    # total system cost
    cost_scaled = join_exprs(
        np.array([
            gen_capital_cost, gen_marginal_cost, store_capital_cost,
            link_capital_cost
        ]))
    # MGA slack
    if options['mga_slack_type'] == 'percent':
        slack = network.old_objective * options[
            'mga_slack'] + network.old_objective
    elif options['mga_slack_type'] == 'fixed':
        slack = options['baseline_cost'] * options['mga_slack'] + options[
            'baseline_cost']

    define_constraints(network, cost_scaled, '<=', slack * scale,
                       'GlobalConstraint', 'MGA_constraint')
예제 #5
0
def add_chp_constraints(n):

    electric = n.links.index[n.links.index.str.contains("urban central")
                             & n.links.index.str.contains("CHP")
                             & n.links.index.str.contains("electric")]
    heat = n.links.index[n.links.index.str.contains("urban central")
                         & n.links.index.str.contains("CHP")
                         & n.links.index.str.contains("heat")]

    if not electric.empty:

        link_p_nom = get_var(n, "Link", "p_nom")

        # ratio of output heat to electricity set by p_nom_ratio
        lhs = linexpr(
            (n.links.loc[electric, "efficiency"] *
             n.links.loc[electric, 'p_nom_ratio'], link_p_nom[electric]),
            (-n.links.loc[heat, "efficiency"].values, link_p_nom[heat].values))
        define_constraints(n, lhs, "=", 0, 'chplink', 'fix_p_nom_ratio')

        link_p = get_var(n, "Link", "p")

        # backpressure
        lhs = linexpr((n.links.loc[electric, 'c_b'].values *
                       n.links.loc[heat, "efficiency"], link_p[heat]),
                      (-n.links.loc[electric, "efficiency"].values,
                       link_p[electric].values))

        define_constraints(n, lhs, "<=", 0, 'chplink', 'backpressure')

        # top_iso_fuel_line
        lhs = linexpr((1, link_p[heat]), (1, link_p[electric].values),
                      (-1, link_p_nom[electric].values))

        define_constraints(n, lhs, "<=", 0, 'chplink', 'top_iso_fuel_line')
예제 #6
0
def add_mga_objective(net: pypsa.Network, mga_type: str):

    if mga_type == 'link':
        # Minimize transmission capacity (in TWkm)
        p_nom = get_var(net, 'Link', 'p_nom')[net.components_to_minimize]
        capacity_expr = linexpr(
            (net.links.length[net.components_to_minimize], p_nom)).sum()
        write_objective(net, capacity_expr)

    elif mga_type == 'storage':
        # Minimize storage energy/power capacity
        p_nom = get_var(net, 'StorageUnit',
                        'p_nom')[net.components_to_minimize]
        capacity_expr = linexpr((1, p_nom)).sum()
        write_objective(net, capacity_expr)

    elif mga_type == 'generator-cap':
        # Minimize generators power capacity
        p_nom = get_var(net, 'Generator', 'p_nom')[net.components_to_minimize]
        capacity_expr = linexpr((1, p_nom)).sum()
        write_objective(net, capacity_expr)

    elif mga_type == 'generator-power':
        # Minimize generators power generation
        p = get_var(net, 'Generator', 'p')[net.components_to_minimize]
        capacity_expr = linexpr((1, p)).sum().sum()
        write_objective(net, capacity_expr)
예제 #7
0
def add_planning_reserve_constraint(net: pypsa.Network, prm: float):
    """
    Constraint that ensures a minimum dispatchable installed capacity.

    Parameters
    ----------
    net: pypsa.Network
        A PyPSA Network instance with buses associated to regions
    prm: float
        Planning reserve margin.
    """
    cc_ds = net.cc_ds
    dispatchable_technologies = ['ocgt', 'ccgt', 'ccgt_ccs', 'nuclear', 'sto']
    res_technologies = [
        'wind_onshore', 'wind_offshore', 'pv_utility', 'pv_residential'
    ]

    for bus in net.loads.bus:

        lhs = ''
        legacy_at_bus = 0

        gens = net.generators[(net.generators.bus == bus) & (
            net.generators.type.isin(dispatchable_technologies))]
        for gen in gens.index:
            if gens.loc[gen].p_nom_extendable:
                lhs += linexpr((1., get_var(net, 'Generator', 'p_nom')[gen]))
            else:
                legacy_at_bus += gens.loc[gen].p_nom_min

        stos = net.storage_units[(net.storage_units.bus == bus) & (
            net.storage_units.type.isin(dispatchable_technologies))]
        for sto in stos.index:
            if stos.loc[sto].p_nom_extendable:
                lhs += linexpr((1., get_var(net, 'StorageUnit', 'p_nom')[sto]))
            else:
                legacy_at_bus += stos.loc[sto].p_nom_min

        res_gens = net.generators[(net.generators.bus == bus) & (
            net.generators.type.str.contains('|'.join(res_technologies)))]
        for gen in res_gens.index:
            lhs += linexpr((cc_ds.loc[' '.join(gen.split(' ')[1:])],
                            get_var(net, 'Generator', 'p_nom')[gen]))

        # Get load for country
        load_idx = net.loads[net.loads.bus == bus].index
        load_peak = net.loads_t.p_set[load_idx].max()

        load_corrected_with_margin = load_peak * (1 + prm)
        rhs = load_corrected_with_margin.values[0] - legacy_at_bus

        define_constraints(net, lhs.sum(), '>=', rhs,
                           'planning_reserve_margin', bus)
예제 #8
0
def define_mga_constraint(n, sns, epsilon=None, with_fix=None):
    """Build constraint defining near-optimal feasible space

    Parameters
    ----------
    n : pypsa.Network
    sns : Series|list-like
        snapshots
    epsilon : float, optional
        Allowed added cost compared to least-cost solution, by default None
    with_fix : bool, optional
        Calculation of allowed cost penalty should include cost of non-extendable components, by default None
    """

    if epsilon is None:
        epsilon = float(snakemake.wildcards.epsilon)

    if with_fix is None:
        with_fix = snakemake.config.get("include_non_extendable", True)

    expr = []

    # operation
    for c, attr in lookup.query("marginal_cost").index:
        cost = (get_as_dense(n, c, "marginal_cost",
                             sns).loc[:, lambda ds: (ds != 0).all()].mul(
                                 n.snapshot_weightings[sns], axis=0))
        if cost.empty:
            continue
        expr.append(
            linexpr((cost, get_var(n, c, attr).loc[sns,
                                                   cost.columns])).stack())

    # investment
    for c, attr in nominal_attrs.items():
        cost = n.df(c)["capital_cost"][get_extendable_i(n, c)]
        if cost.empty:
            continue
        expr.append(linexpr((cost, get_var(n, c, attr)[cost.index])))

    lhs = pd.concat(expr).sum()

    if with_fix:
        ext_const = objective_constant(n, ext=True, nonext=False)
        nonext_const = objective_constant(n, ext=False, nonext=True)
        rhs = (1 + epsilon) * (n.objective + ext_const +
                               nonext_const) - nonext_const
    else:
        ext_const = objective_constant(n)
        rhs = (1 + epsilon) * (n.objective + ext_const)

    define_constraints(n, lhs, "<=", rhs, "GlobalConstraint", "mu_epsilon")
예제 #9
0
def define_mga_objective(n):

    components, pattern, sense = n.mga_obj

    if isinstance(components, str):
        components = [components]

    terms = []
    for c in components:

        variables = get_var(n, c,
                            nominal_attrs[c]).filter(regex=to_regex(pattern))

        if c in ["Link", "Line"] and pattern in ["", "LN|LK", "LK|LN"]:
            coeffs = sense * n.df(c).loc[variables.index, "length"]
        else:
            coeffs = sense

        terms.append(linexpr((coeffs, variables)))

    joint_terms = pd.concat(terms)

    write_objective(n, joint_terms)

    # print objective to console
    print(joint_terms)
예제 #10
0
def add_ccl_constraints(n):

    agg_p_nom_limits = n.config['existing_capacities'].get('agg_p_nom_limits')

    agg_p_nom_minmax = pd.read_csv(agg_p_nom_limits, index_col=list(range(2)))
    print(agg_p_nom_minmax)

    logger.info(
        "Adding per carrier generation capacity constraints for individual countries"
    )
    print(
        'adding per carrier generation capacity constraints for individual countries'
    )
    gen_country = n.generators.bus.map(n.buses.country)

    # cc means country and carrier
    p_nom_per_cc = (pd.DataFrame({
        'p_nom':
        linexpr((1, get_var(n, 'Generator', 'p_nom'))),
        'country':
        gen_country,
        'carrier':
        n.generators.carrier
    }).dropna(subset=['p_nom']).groupby(['country',
                                         'carrier']).p_nom.apply(join_exprs))

    minimum = agg_p_nom_minmax['min'].dropna()
    if not minimum.empty:
        define_constraints(n, p_nom_per_cc[minimum.index], '>=', minimum,
                           'agg_p_nom', 'min')

    maximum = agg_p_nom_minmax['max'].dropna()
    if not maximum.empty:
        define_constraints(n, p_nom_per_cc[maximum.index], '<=', maximum,
                           'agg_p_nom', 'max')
예제 #11
0
def add_biofuel_constraint(n):

    options = snakemake.wildcards.sector_opts.split('-')
    print('options: ', options)
    liquid_biofuel_limit = 0
    for o in options:
        if "B" in o:
            liquid_biofuel_limit = o[o.find("B") + 1:o.find("B") + 4]
            liquid_biofuel_limit = float(liquid_biofuel_limit.replace(
                "p", "."))

    print('Liq biofuel minimum constraint: ', liquid_biofuel_limit, ' ',
          type(liquid_biofuel_limit))

    biofuel_i = n.links.query('carrier == "biomass to liquid"').index
    biofuel_vars = get_var(n, "Link", "p").loc[:, biofuel_i]
    biofuel_vars_eta = n.links.query(
        'carrier == "biomass to liquid"').efficiency

    napkership = n.loads.p_set.filter(
        regex='naphtha for industry|kerosene for aviation|shipping oil').sum(
        ) * len(n.snapshots)
    landtrans = n.loads_t.p_set.filter(regex='land transport oil$').sum().sum()
    total_oil_load = napkership + landtrans
    liqfuelloadlimit = liquid_biofuel_limit * total_oil_load

    lhs = linexpr((biofuel_vars_eta, biofuel_vars)).sum().sum()
    define_constraints(n, lhs, ">=", liqfuelloadlimit, 'Link',
                       'liquid_biofuel_min')
예제 #12
0
파일: store.py 프로젝트: montefesp/replan
def store_links_constraint(net: pypsa.Network, ctd_ratio: float):
    """
    Constraint that links the charging and discharging ratings of store units.

    Parameters
    ----------
    net: pypsa.Network
        A PyPSA Network instance with buses associated to regions
        and containing a functionality configuration dictionary
    ctd_ratio: float
        Pre-defined charge-to-discharge ratio for such units.

    """

    links_p_nom = get_var(net, 'Link', 'p_nom')

    links_to_bus = links_p_nom[links_p_nom.index.str.contains('to AC')].index
    links_from_bus = links_p_nom[links_p_nom.index.str.contains('AC to')].index

    for pair in list(zip(links_to_bus, links_from_bus)):

        discharge_link = links_p_nom.loc[pair[0]]
        charge_link = links_p_nom.loc[pair[1]]
        lhs = linexpr((ctd_ratio, discharge_link), (-1., charge_link))

        define_constraints(net, lhs, '==', 0., 'store_links_constraint')
예제 #13
0
 def map_solution(c, attr):
     variables = get_var(n, c, attr, pop=pop)
     predefined = True
     if (c, attr) not in lookup.index:
         predefined = False
         n.sols[c] = n.sols[c] if c in n.sols else Dict(df=pd.DataFrame(), pnl={})
     n.solutions.at[(c, attr), 'in_comp'] = predefined
     if isinstance(variables, pd.DataFrame):
         # case that variables are timedependent
         n.solutions.at[(c, attr), 'pnl'] = True
         pnl = n.pnl(c) if predefined else n.sols[c].pnl
         values = variables.apply(lambda x: x.map(variables_sol))
         # values = variables.stack().map(variables_sol).unstack()
         if c in n.passive_branch_components and attr == "s":
             set_from_frame(pnl, 'p0', values)
             set_from_frame(pnl, 'p1', - values)
         elif c == 'Link' and attr == "p":
             set_from_frame(pnl, 'p0', values)
             for i in ['1'] + additional_linkports(n):
                 i_eff = '' if i == '1' else i
                 eff = get_as_dense(n, 'Link', f'efficiency{i_eff}', sns)
                 set_from_frame(pnl, f'p{i}', - values * eff)
                 pnl[f'p{i}'].loc[sns, n.links.index[n.links[f'bus{i}'] == ""]] = \
                     n.component_attrs['Link'].loc[f'p{i}','default']
         else:
             set_from_frame(pnl, attr, values)
     else:
         # case that variables are static
         n.solutions.at[(c, attr), 'pnl'] = False
         sol = variables.map(variables_sol)
         if predefined:
             non_ext = n.df(c)[attr]
             n.df(c)[attr + '_opt'] = sol.reindex(non_ext.index).fillna(non_ext)
         else:
             n.sols[c].df[attr] = sol
예제 #14
0
def add_co2_sequestration_limit(n, sns):

    co2_stores = n.stores.loc[n.stores.carrier == 'co2 stored'].index

    if co2_stores.empty or ('Store', 'e') not in n.variables.index:
        return

    vars_final_co2_stored = get_var(n, 'Store', 'e').loc[sns[-1], co2_stores]

    lhs = linexpr((1, vars_final_co2_stored)).sum()

    limit = n.config["sector"].get("co2_sequestration_potential", 200) * 1e6
    for o in opts:
        if not "seq" in o: continue
        limit = float(o[o.find("seq") + 3:])
        break

    name = 'co2_sequestration_limit'
    sense = "<="

    n.add("GlobalConstraint",
          name,
          sense=sense,
          constant=limit,
          type=np.nan,
          carrier_attribute=np.nan)

    define_constraints(n,
                       lhs,
                       sense,
                       limit,
                       'GlobalConstraint',
                       'mu',
                       axes=pd.Index([name]),
                       spec=name)
예제 #15
0
def dispatchable_capacity_lower_bound(net: pypsa.Network, thresholds: Dict):
    """
    Constraint that ensures a minimum dispatchable installed capacity.

    Parameters
    ----------
    net: pypsa.Network
        A PyPSA Network instance with buses associated to regions

    thresholds: Dict
        Dict containing scalar thresholds for disp_capacity/peak_load for each bus
    """
    dispatchable_technologies = ['ocgt', 'ccgt', 'ccgt_ccs', 'nuclear', 'sto']
    for bus in net.loads.bus:

        if bus in thresholds.keys():

            lhs = ''
            legacy_at_bus = 0

            gens = net.generators[(net.generators.bus == bus) & (
                net.generators.type.isin(dispatchable_technologies))]
            for gen in gens.index:
                if gens.loc[gen].p_nom_extendable:
                    lhs += linexpr((1., get_var(net, 'Generator',
                                                'p_nom')[gen]))
                else:
                    legacy_at_bus += gens.loc[gen].p_nom_min

            stos = net.storage_units[(net.storage_units.bus == bus) & (
                net.storage_units.type.isin(dispatchable_technologies))]
            for sto in stos.index:
                if stos.loc[sto].p_nom_extendable:
                    lhs += linexpr((1., get_var(net, 'StorageUnit',
                                                'p_nom')[sto]))
                else:
                    legacy_at_bus += stos.loc[sto].p_nom_min

            # Get load for country
            load_idx = net.loads[net.loads.bus == bus].index
            load_peak = net.loads_t.p_set[load_idx].max()

            load_peak_threshold = load_peak * thresholds[bus]
            rhs = max(0, load_peak_threshold.values[0] - legacy_at_bus)

            define_constraints(net, lhs.sum(), '>=', rhs,
                               'disp_capacity_lower_bound', bus)
예제 #16
0
        def extra_functionality(network,snapshots):

            link_p_nom = get_var(network, "Link", "p_nom")

            lhs = linexpr((1,link_p_nom["battery_power"]),
                          (-network.links.loc["battery_discharge", "efficiency"],
                           link_p_nom["battery_discharge"]))
            define_constraints(network, lhs, "=", 0, 'Link', 'charger_ratio')
예제 #17
0
def add_mga_objective(net, sense='min'):

    # Minimize transmission capacity
    link_p_nom = get_var(net, 'Link', 'p_nom')
    sense = 1 if sense == 'min' else -1
    link_capacity_expr = linexpr((sense, link_p_nom)).sum()

    write_objective(net, link_capacity_expr)
예제 #18
0
def add_to_objective(n, snapshots):
    """
    adds to the pypsa objective function the costs for the Netzbooster,
    these consists of:
        (a) costs for the capacities of the Netzbooster
        (b) costs for compensation dispatch (e.g. paid to DSM consumers, for storage)

    i => bus
    t => snapshot
    k => outage
    """

    logger.info("define objective")

    n.determine_network_topology()
    sub = n.sub_networks.at["0", "obj"]
    sub.calculate_PTDF()
    sub.calculate_BODF()

    buses = sub.buses_i()
    lines = sub.lines_i()
    outages = [l for l in lines if "outage" in l]

    coefficient1 = float(snakemake.wildcards.flex_cost)  #18000
    coefficient2 = 1  #float(snakemake.wildcards.flex_cost)*0.001 #1

    #  (a) costs for Netzbooster capacities
    # sum_i ( c_pos * P(i)_pos + c_neg * P(i)_neg)
    netzbooster_cap_cost = linexpr(
        (coefficient1, get_var(n, "Bus", "P_pos").loc[buses]),
        (coefficient1, get_var(n, "Bus", "P_neg").loc[buses])).sum()

    write_objective(n, netzbooster_cap_cost)

    # (b) costs for compensation dispatch (paid to DSM consumers/running costs for storage)
    # sum_(i, t, k) ( o_pos * p(i, t, k)_pos + o_neg * p(i, t, k)_pos)

    compensate_p_cost = linexpr(
        (coefficient2, get_var(n, "Bus", "p_pos").loc[snapshots, buses]),
        (coefficient2, get_var(n, "Bus", "p_neg").loc[snapshots, buses]),
    ).sum().sum()

    write_objective(n, compensate_p_cost)
예제 #19
0
def add_mga_constraint(net: pypsa.Network, epsilon: float):

    # Add generation costs
    # Capital cost
    gen_p_nom = get_var(net, 'Generator', 'p_nom')
    gens = net.generators.loc[gen_p_nom.index]
    gen_capex_expr = linexpr((gens.capital_cost, gen_p_nom)).sum()
    gen_exist_cap_cost = (gens.p_nom * gens.capital_cost).sum()
    # Marginal cost
    gen_p = get_var(net, 'Generator', 'p')
    gens = net.generators.loc[gen_p.columns]
    gen_opex_expr = linexpr((net.snapshot_weightings['objective'].apply(
        lambda r: r * gens.marginal_cost), gen_p)).sum().sum()
    gen_cost_expr = gen_capex_expr + gen_opex_expr

    # Add storage cost
    # Capital cost
    su_p_nom = get_var(net, 'StorageUnit', 'p_nom')
    sus = net.storage_units.loc[su_p_nom.index]
    su_capex_expr = linexpr((sus.capital_cost, su_p_nom)).sum()
    su_exist_cap_cost = (sus.p_nom * sus.capital_cost).sum()
    # Marginal cost
    su_p_dispatch = get_var(net, 'StorageUnit', 'p_dispatch')
    sus = net.storage_units.loc[su_p_dispatch.columns]
    su_opex_expr = linexpr((net.snapshot_weightings['objective'].apply(
        lambda r: r * sus.marginal_cost), su_p_dispatch)).sum().sum()
    su_cost_expr = su_capex_expr + su_opex_expr

    # Add transmission cost
    # Capital cost
    link_p_nom = get_var(net, 'Link', 'p_nom')
    links = net.links.loc[link_p_nom.index]
    link_exist_cap_cost = (links.p_nom * links.capital_cost).sum()
    link_capex_expr = linexpr((links.capital_cost, link_p_nom)).sum()
    link_cost_expr = link_capex_expr

    obj_expr = gen_cost_expr + su_cost_expr + link_cost_expr

    exist_cap_cost = gen_exist_cap_cost + su_exist_cap_cost + link_exist_cap_cost
    obj = net.objective * (1 + epsilon) + exist_cap_cost

    define_constraints(net, obj_expr, '<=', obj, 'mga', 'obl')
예제 #20
0
def add_battery_constraints(n):

    nodes = n.buses.index[n.buses.carrier.isin(["battery", "home battery"])]

    link_p_nom = get_var(n, "Link", "p_nom")

    lhs = linexpr((1, link_p_nom[nodes + " charger"]),
                  (-n.links.loc[nodes + " discharger", "efficiency"].values,
                   link_p_nom[nodes + " discharger"].values))

    define_constraints(n, lhs, "=", 0, 'Link', 'charger_ratio')
예제 #21
0
def add_battery_constraints(n):

    chargers = n.links.index[n.links.carrier.str.contains("battery charger") & n.links.p_nom_extendable]
    dischargers = chargers.str.replace("charger","discharger")

    link_p_nom = get_var(n, "Link", "p_nom")

    lhs = linexpr((1,link_p_nom[chargers]),
                  (-n.links.loc[dischargers, "efficiency"].values,
                   link_p_nom[dischargers].values))

    define_constraints(n, lhs, "=", 0, 'Link', 'charger_ratio')
예제 #22
0
def add_to_objective(n, snapshots):
    """
    adds to the pypsa objective function the costs for the Netzbooster,
    these consists of:
        (a) costs for the capacities of the Netzbooster
        (b) costs for compensation dispatch (e.g. paid to DSM consumers, for storage)

    i => bus
    t => snapshot
    k => outage
    """

    logger.info("define objective")

    n.determine_network_topology()
    sub = n.sub_networks.at["0", "obj"]
    sub.calculate_PTDF()
    sub.calculate_BODF()

    buses = sub.buses_i()
    lines = sub.lines_i()
    outages = [l for l in lines if "outage" in l]

    coefficient1 = 18100
    coefficient2 = 10

    #  (a) costs for Netzbooster capacities
    # sum_i ( c_pos * P(i)_pos + c_neg * P(i)_neg)
    # add noise to the netzbooster capacitiy costs to avoid numerical troubles
    coefficient1_noise = np.random.normal(coefficient1, .01, get_var(n, "Bus", "P_pos").shape)
    coefficient1_noise2 = np.random.normal(coefficient1, .01, get_var(n, "Bus", "P_pos").shape)
    netzbooster_cap_cost = linexpr(
                                (coefficient1_noise, get_var(n, "Bus", "P_pos")),
                                (coefficient1_noise2, get_var(n, "Bus", "P_neg"))
                                ).sum()

    write_objective(n, netzbooster_cap_cost)


    # (b) costs for compensation dispatch (paid to DSM consumers/running costs for storage)
    # sum_(i, t, k) ( o_pos * p(i, t, k)_pos + o_neg * p(i, t, k)_pos)
    # add noise to the marginal costs to avoid numerical troubles
    coefficient2_noise = np.random.normal(coefficient2, .00001, get_var(n, "Bus", "p_pos").shape)
    coefficient2_noise2 = np.random.normal(coefficient2, .00001, get_var(n, "Bus", "p_pos").shape)
    compensate_p_cost = linexpr(
                                (coefficient2_noise, get_var(n, "Bus", "p_pos").loc[snapshots]),
                                (coefficient2_noise2, get_var(n, "Bus", "p_neg").loc[snapshots]),
                                ).sum().sum()

    write_objective(n, compensate_p_cost)
예제 #23
0
def add_to_objective(n, snapshots):

    logger.info("define objective")

    n.determine_network_topology()
    sub = n.sub_networks.at["0", "obj"]
    sub.calculate_PTDF()
    sub.calculate_BODF()

    buses = sub.buses_i()
    lines = sub.lines_i()
    outages = [l for l in lines if "outage" in l]

    coefficient1 = 1
    coefficient2 = 0.0001

    terms = linexpr(
        (coefficient1, get_var(n, "Bus", "P_pos").loc[buses]),
        (coefficient1, get_var(n, "Bus", "P_neg").loc[buses]),
        (coefficient2, get_var(n, "Bus", "p_pos").loc[buses].sum().sum()))

    write_objective(n, terms)
예제 #24
0
def add_co2_sequestration_limit(n, sns):
    
    co2_stores = n.stores.loc[n.stores.carrier=='co2 stored'].index

    if co2_stores.empty or ('Store', 'e') not in n.variables.index:
        return
    
    vars_final_co2_stored = get_var(n, 'Store', 'e').loc[sns[-1], co2_stores]
    
    lhs = linexpr((1, vars_final_co2_stored)).sum()
    rhs = n.config["sector"].get("co2_sequestration_potential", 200) * 1e6
    
    name = 'co2_sequestration_limit'
    define_constraints(n, lhs, "<=", rhs, 'GlobalConstraint',
                       'mu', axes=pd.Index([name]), spec=name)
예제 #25
0
def define_mga_constraint(n, sns):

    epsilon = float(snakemake.wildcards.epsilon)

    expr = []

    # operation
    for c, attr in lookup.query("marginal_cost").index:
        cost = (get_as_dense(n, c, "marginal_cost",
                             sns).loc[:, lambda ds: (ds != 0).all()].mul(
                                 n.snapshot_weightings[sns], axis=0))
        if cost.empty:
            continue
        expr.append(
            linexpr((cost, get_var(n, c, attr).loc[sns,
                                                   cost.columns])).stack())

    # investment
    for c, attr in nominal_attrs.items():
        cost = n.df(c)["capital_cost"][get_extendable_i(n, c)]
        if cost.empty:
            continue
        expr.append(linexpr((cost, get_var(n, c, attr)[cost.index])))

    lhs = pd.concat(expr).sum()

    if snakemake.config["include_non_extendable"]:
        ext_const = objective_constant(n, ext=True, nonext=False)
        nonext_const = objective_constant(n, ext=False, nonext=True)
        rhs = (1 + epsilon) * (n.objective + ext_const +
                               nonext_const) - nonext_const
    else:
        ext_const = objective_constant(n)
        rhs = (1 + epsilon) * (n.objective + ext_const)

    define_constraints(n, lhs, "<=", rhs, "GlobalConstraint", "mu_epsilon")
예제 #26
0
파일: co2.py 프로젝트: montefesp/replan
def add_co2_budget_per_country(net: pypsa.Network,
                               co2_reduction_share: Dict[str, float],
                               co2_reduction_refyear: int):
    """
    Add CO2 budget per country.

    Parameters
    ----------
    net: pypsa.Network
        A PyPSA Network instance with buses associated to regions
    co2_reduction_share: float
        Percentage of reduction of emission.
    co2_reduction_refyear: int
        Reference year from which the reduction in emission is computed.

    """

    for bus in net.loads.bus:

        bus_emission_reference = get_co2_emission_level_for_country(
            bus, co2_reduction_refyear)
        co2_budget = (1-co2_reduction_share[bus]) * bus_emission_reference \
            * sum(net.snapshot_weightings['objective']) / 8760.

        # Drop rows (gens) without an associated carrier (i.e., technologies not emitting)
        gens = net.generators[(net.generators.carrier.astype(bool))
                              & (net.generators.bus == bus)]
        gens_p = get_var(net, 'Generator', 'p')[gens.index]

        coefficients = pd.DataFrame(index=gens_p.index,
                                    columns=gens_p.columns,
                                    dtype=float)
        for tech in gens.type.unique():
            fuel, efficiency = get_tech_info(tech, ["fuel", "efficiency_ds"])
            fuel_emissions_el = get_fuel_info(fuel, ['CO2'])
            fuel_emissions_thermal = fuel_emissions_el / efficiency

            gens_with_tech = gens[gens.index.str.contains(tech)]
            coefficients[
                gens_with_tech.index] = fuel_emissions_thermal.values[0]

        lhs = linexpr((coefficients, gens_p)).sum().sum()
        define_constraints(net, lhs, '<=', co2_budget,
                           'generation_emissions_global', bus)
예제 #27
0
파일: co2.py 프로젝트: montefesp/replan
def add_co2_budget_global(net: pypsa.Network, region: str,
                          co2_reduction_share: float,
                          co2_reduction_refyear: int):
    """
    Add global CO2 budget.

    Parameters
    ----------
    region: str
        Region over which the network is defined.
    net: pypsa.Network
        A PyPSA Network instance with buses associated to regions
    co2_reduction_share: float
        Percentage of reduction of emission.
    co2_reduction_refyear: int
        Reference year from which the reduction in emission is computed.

    """

    co2_reference_kt = get_reference_emission_levels_for_region(
        region, co2_reduction_refyear)
    co2_budget = co2_reference_kt * (1 - co2_reduction_share) * sum(
        net.snapshot_weightings['objective']) / 8760.

    gens = net.generators[net.generators.carrier.astype(bool)]
    gens_p = get_var(net, 'Generator', 'p')[gens.index]

    coefficients = pd.DataFrame(index=gens_p.index,
                                columns=gens_p.columns,
                                dtype=float)
    for tech in gens.type.unique():

        fuel, efficiency = get_tech_info(tech, ["fuel", "efficiency_ds"])
        fuel_emissions_el = get_fuel_info(fuel, ['CO2'])
        fuel_emissions_thermal = fuel_emissions_el / efficiency

        gens_with_tech = gens[gens.index.str.contains(tech)]
        coefficients[gens_with_tech.index] = fuel_emissions_thermal.values[0]

    lhs = linexpr((coefficients, gens_p)).sum().sum()
    define_constraints(net, lhs, '<=', co2_budget,
                       'generation_emissions_global')
예제 #28
0
def add_pipe_retrofit_constraint(n):
    """Add constraint for retrofitting existing CH4 pipelines to H2 pipelines."""

    gas_pipes_i = n.links[n.links.carrier == "gas pipeline"].index
    h2_retrofitted_i = n.links[n.links.carrier ==
                               'H2 pipeline retrofitted'].index

    if h2_retrofitted_i.empty or gas_pipes_i.empty: return

    link_p_nom = get_var(n, "Link", "p_nom")

    pipe_capacity = n.links.loc[gas_pipes_i, 'p_nom']

    CH4_per_H2 = 1 / n.config["sector"]["H2_retrofit_capacity_per_CH4"]

    fr = "H2 pipeline retrofitted"
    to = "gas pipeline"
    lhs = linexpr((CH4_per_H2, link_p_nom.loc[h2_retrofitted_i].rename(
        index=lambda x: x.replace(fr, to))), (1, link_p_nom.loc[gas_pipes_i]))

    define_constraints(n, lhs, "=", pipe_capacity, 'Link', 'pipe_retrofit')
예제 #29
0
def add_ccl_constraints_conventional(n):

    agg_p_nom_limits_conventional = n.config['existing_capacities'].get(
        'agg_p_nom_limits_conventional')

    agg_p_nom_minmax_conventional = pd.read_csv(agg_p_nom_limits_conventional,
                                                index_col=list(range(2)))

    logger.info(
        "Adding per carrier conventional link capacity constraints for individual countries"
    )
    print(
        'adding per carrier conventional link capacity constraints for individual countries'
    )
    link_country = n.links.bus1.map(n.buses.country)

    # cc means country and carrier
    p_nom_per_cc_link = (pd.DataFrame({
        'p_nom':
        linexpr((1, get_var(n, 'Link', 'p_nom'))),
        'country':
        link_country,
        'carrier':
        n.links.carrier
    }).dropna(subset=['p_nom']).groupby(['country',
                                         'carrier']).p_nom.apply(join_exprs))

    minimum_conventional = agg_p_nom_minmax_conventional['min'].dropna()
    if not minimum_conventional.empty:
        define_constraints(n, p_nom_per_cc_link[minimum_conventional.index],
                           '>=', minimum_conventional, 'agg_p_nom', 'min')

    maximum_conventional = agg_p_nom_minmax_conventional['max'].dropna()
    if not maximum_conventional.empty:
        define_constraints(n, p_nom_per_cc_link[maximum_conventional.index],
                           '<=', maximum_conventional, 'agg_p_nom', 'max')
def netzbooster_constraints(n, snapshots):
    """
    add to the LOPF the additional Netzbooster constraints
    """

    # Define indices & parameters
    n.determine_network_topology()
    sub = n.sub_networks.at["0", "obj"]
    sub.calculate_PTDF()
    sub.calculate_BODF()

    snapshots = snapshots  #n.snapshots
    buses = sub.buses_i()
    lines = sub.lines_i()
    outages = [l for l in lines if "outage" in l]

    ptdf = pd.DataFrame(sub.PTDF, index=lines, columns=buses)
    lodf = pd.DataFrame(sub.BODF, index=lines, columns=lines)

    logger.info("define variables")

    # i = buses
    # t = snapshots
    # k = outages

    # P_i + >= 0
    define_variables(n, 0, inf, "Bus", "P_pos", axes=[buses])
    # P_i - >= 0
    define_variables(n, 0, inf, "Bus", "P_neg", axes=[buses])
    # p_{i,t,k} + >= 0
    define_variables(
        n,
        0,
        inf,
        "Bus",
        "p_pos",
        axes=[snapshots,
              pd.MultiIndex.from_product([buses, outages])])
    # p_{i,t,k} + >= 0
    define_variables(
        n,
        0,
        inf,
        "Bus",
        "p_neg",
        axes=[snapshots,
              pd.MultiIndex.from_product([buses, outages])])

    logger.info("define constraints")

    # # Define constraints

    # ########### p_pos(i,k,t) <= P_pos(i)  ##########
    P_pos_extend = expand_series(
        get_var(n, "Bus", "P_pos").loc[buses],
        snapshots).T.reindex(pd.MultiIndex.from_product([buses, outages]),
                             axis=1,
                             level=0)
    lhs_1 = linexpr((1, P_pos_extend[buses]),
                    (-1, get_var(n, "Bus", "p_pos").loc[snapshots, buses]))
    define_constraints(n, lhs_1, ">=", 0., "Bus", "UpLimitPos")

    # ######### p_neg(i,k,t) <= P_neg(i)  #########
    P_neg_extend = expand_series(
        get_var(n, "Bus", "P_neg").loc[buses],
        snapshots).T.reindex(pd.MultiIndex.from_product([buses, outages]),
                             axis=1,
                             level=0)
    lhs_2 = linexpr((1, P_neg_extend[buses]),
                    (-1, get_var(n, "Bus", "p_pos").loc[snapshots, buses]))

    define_constraints(n, lhs_2, ">=", 0., "Bus", "UpLimitNeg")

    # ######## sum(i, p_pos(i,k,t) - p_neg(i,k,t)) = 0  #########
    lhs_3 = linexpr((1, get_var(n, "Bus", "p_pos").loc[snapshots].groupby(
        level=1, axis=1).sum()), (-1, get_var(
            n, "Bus", "p_neg").loc[snapshots].groupby(level=1, axis=1).sum()))
    define_constraints(n, lhs_3, "==", 0., "Bus", "FlexBal")

    # ## |f(l,t) + LODF(l,k) * f(k,t) + sum_i (PTDF(l,i) * (p_neg(i,k, t) - p_pos(i,k, t)))| <= F(l)  ########
    # f(l,t) + LODF(l,k) * f(k,t) + sum_i (PTDF(l,i) * (p_neg(i,k, t) - p_pos(i,k, t))) <= F(l)

    # f(l,t) here: f pandas DataFrame(index=snapshots, columns=lines)
    f = get_var(n, "Line", "s")
    # F(l) -> line capacity for n.lines.s_nom_extendable=False
    F = n.lines.s_nom
    # p pos (index=snapshots, columns=[bus, outage])
    p_pos = get_var(n, "Bus", "p_pos")
    # p neg (index=snapshots, columns=[bus, outage])
    p_neg = get_var(n, "Bus", "p_neg")

    for k in outages:
        for l in lines.difference(pd.Index(
            [k])):  # all l except for outage line
            for t in snapshots:

                lhs = ''
                # sum_i (PTDF(l,i) * (p_neg(i,k, t) - p_pos(i,k, t)))
                v1 = linexpr(
                    (ptdf.loc[l], p_neg.loc[t].xs(
                        k, level=1)),  # sum_i (PTDF(l,i) * (p_neg(i,k, t))
                    (-1 * ptdf.loc[l], p_pos.loc[t].xs(k, level=1)
                     )  # sum_i (- PTDF(l,i) * (p_pos(i,k, t))
                )
                lhs += '\n' + join_exprs(v1.sum())

                # f(l,t) + LODF(l,k) * f(k,t)
                v = linexpr(
                    (1, f.loc[t, l]),  # f(l,t)
                    (lodf.loc[l, k], f.loc[t, k]))  # LODF(l,k) * f(k,t)

                lhs += '\n' + join_exprs(v)

                rhs = F.loc[l]

                define_constraints(n, lhs, "<=", rhs, "booster1",
                                   "capacity_limit")

                define_constraints(n, lhs, ">=", -rhs, "booster2",
                                   "capacity_limit2")