예제 #1
0
def load_costs():
    costs = pd.read_excel(snakemake.input.tech_costs,
                          sheet_name=snakemake.wildcards.cost,
                          index_col=0).T

    discountrate = snakemake.config['costs']['discountrate']
    costs['capital_cost'] = (
        (annuity(costs.pop('Lifetime [a]'), discountrate) +
         costs.pop('FOM [%/a]').fillna(0.) / 100.) *
        costs.pop('Overnight cost [R/kW_el]') * 1e3)

    costs['efficiency'] = costs.pop('Efficiency').fillna(1.)
    costs['marginal_cost'] = (
        costs.pop('VOM [R/MWh_el]').fillna(0.) +
        (costs.pop('Fuel cost [R/MWh_th]') / costs['efficiency']).fillna(0.))

    emissions_cols = costs.columns.to_series(
    ).loc[lambda s: s.str.endswith(' emissions [kg/MWh_th]')]
    costs.loc[:, emissions_cols.index] = (costs.loc[:, emissions_cols.index] /
                                          1e3).fillna(0.)
    costs = costs.rename(columns=emissions_cols.str[:-len(" [kg/MWh_th]")].str.
                         lower().str.replace(' ', '_'))

    for attr in ('marginal_cost', 'capital_cost'):
        overwrites = snakemake.config['costs'].get(attr)
        if overwrites is not None:
            overwrites = pd.Series(overwrites)
            costs.loc[overwrites.index, attr] = overwrites

    return costs
예제 #2
0
def calculate_costs(n, label, costs):

    for c in n.iterate_components(n.branch_components
                                  | n.controllable_one_port_components
                                  ^ {"Load"}):
        capital_costs = c.df.capital_cost * c.df[opt_name.get(c.name, "p") +
                                                 "_nom_opt"]
        capital_costs_grouped = capital_costs.groupby(c.df.group).sum()

        costs = costs.reindex(costs.index | pd.MultiIndex.from_product(
            [[c.list_name], ["capital"], capital_costs_grouped.index]))

        costs.loc[idx[c.list_name, "capital",
                      list(capital_costs_grouped.index)],
                  label] = capital_costs_grouped.values

        if c.name == "Link":
            p = c.pnl.p0.sum()
        elif c.name == "StorageUnit":
            p_all = c.pnl.p.copy()
            p_all[p_all < 0.] = 0.
            p = p_all.sum()
        else:
            p = c.pnl.p.sum()

        marginal_costs = p * c.df.marginal_cost

        marginal_costs_grouped = marginal_costs.groupby(c.df.group).sum()

        costs = costs.reindex(costs.index | pd.MultiIndex.from_product(
            [[c.list_name], ["marginal"], marginal_costs_grouped.index]))

        costs.loc[idx[c.list_name, "marginal",
                      list(marginal_costs_grouped.index)],
                  label] = marginal_costs_grouped.values

    #add back in costs of links if there is a line volume limit
    if label[1] != "opt":
        costs.loc[("links-added", "capital", "transmission lines"), label] = (
            (400 * 1.25 * n.links.length + 150000.) * n.links.p_nom_opt
        )[n.links.group == "transmission lines"].sum() * 1.5 * (
            annuity(40., 0.07) + 0.02)

    #add back in all hydro
    costs.loc[("storage_units", "capital", "hydro"),
              label] = (0.01) * 2e6 * n.storage_units.loc[
                  n.storage_units.group == "hydro", "p_nom"].sum()
    costs.loc[("storage_units", "capital", "PHS"),
              label] = (0.01) * 2e6 * n.storage_units.loc[
                  n.storage_units.group == "PHS", "p_nom"].sum()
    costs.loc[("generators", "capital", "ror"),
              label] = (0.02) * 3e6 * n.generators.loc[n.generators.group ==
                                                       "ror", "p_nom"].sum()

    return costs
def retrofitting_comparison():

    #from Fig 4.2 HPI http://dx.doi.org/10.1016/j.rser.2013.09.012

    hp = np.array([[100, 0], [90, 18], [80, 40], [70, 67], [60, 98], [50, 135],
                   [40, 180], [30, 230], [20, 285]])
    hp = pd.Series(hp[:, 1], 100 - hp[:, 0])

    hp_annual = (annuity(50, 0.04) + 0.01) * hp

    #100 per cent to per unit; 124 kWh/m^2 average for 2011 from HP
    hp_final = (hp_annual / (hp_annual.index.to_series() / 100 * 124)).fillna(
        method="bfill") * 1e3
    hp_final.loc[0] = 76

    #Danes Heatroadmap Fig 5 of http://dx.doi.org/10.1016/j.enpol.2013.10.035

    dane = np.array([[0, 1.05], [10, 1.15], [20, 1.30], [30, 1.45], [40, 1.65],
                     [50, 1.87], [60, 2.13], [70, 2.40], [75, 2.6]])
    dane = pd.Series(dane[:, 1], dane[:, 0])

    dane_final = ((annuity(30, 0.04) + 0.01) * dane) * 1e3

    fig, ax = plt.subplots(1, 1)

    fig.set_size_inches(4.5, 3.5)

    hp_final.plot(ax=ax, label="Germany", linewidth=2)
    dane_final.plot(ax=ax, label="Denmark", linewidth=2)

    ax.set_ylabel("Cost for energy saved [EUR/MWh]")
    ax.set_xlabel("Heat demand reduction [%]")

    ax.grid()

    ax.set_xlim([0, 80])

    ax.legend()

    fig.tight_layout()

    fig.savefig(snakemake.output.retrofitting_comparison, transparent=True)
예제 #4
0
def add_gas_infrastructure(n, costs):
    buses = n.buses.index[n.buses.population > 0.]
    discountrate = snakemake.config['costs']['discountrate']

    n.add("Carrier", "H2")
    buses_h2 = n.madd("Bus", buses, suffix=" H2", carrier="H2")
    n.madd(
        "Link",
        buses,
        suffix=" H2 Electrolysis",
        bus0=buses,
        bus1=buses_h2,
        p_nom_extendable=True,
        efficiency=0.75,
        #Cost from http://www.nrel.gov/docs/fy09osti/45873.pdf "Future Timeframe"
        #(same source as Budishak)
        capital_cost=(annuity(20., discountrate) + 0.017) * 300. * 1000. *
        USD2013_to_EUR2013)
    n.madd(
        "Link",
        buses,
        suffix=" H2 Fuel Cell",
        bus0=buses_h2,
        bus1=buses,
        p_nom_extendable=True,
        efficiency=0.58,
        #Cost from http://www.nrel.gov/docs/fy09osti/45873.pdf "Future Timeframe"
        #(same source as Budishak)
        #NB: Costs refer to electrical side, so must multiply by efficiency
        capital_cost=(annuity(20., discountrate) + 0.017) * 437. * 0.58 *
        1000. * USD2013_to_EUR2013)
    n.madd("Store",
           buses_h2,
           suffix=" Store",
           bus=buses_h2,
           e_nom_extendable=True,
           e_cyclic=True,
           capital_cost=annuity(20., discountrate) * 11.2 * 1000. *
           USD2013_to_EUR2013)
예제 #5
0
def load_costs(Nyears=1., tech_costs=None, config=None, elec_config=None):
    if tech_costs is None:
        tech_costs = snakemake.input.tech_costs

    if config is None:
        config = snakemake.config['costs']

    # set all asset costs and other parameters
    costs = pd.read_csv(tech_costs, index_col=list(range(3))).sort_index()

    # correct units to MW and EUR
    costs.loc[costs.unit.str.contains("/kW"),"value"] *= 1e3
    costs.loc[costs.unit.str.contains("USD"),"value"] *= config['USD2013_to_EUR2013']

    costs = (costs.loc[idx[:,config['year'],:], "value"]
             .unstack(level=2).groupby("technology").sum(min_count=1))

    costs = costs.fillna({"CO2 intensity" : 0,
                          "FOM" : 0,
                          "VOM" : 0,
                          "discount rate" : config['discountrate'],
                          "efficiency" : 1,
                          "fuel" : 0,
                          "investment" : 0,
                          "lifetime" : 25})

    costs["capital_cost"] = ((annuity(costs["lifetime"], costs["discount rate"]) +
                             costs["FOM"]/100.) *
                             costs["investment"] * Nyears)

    costs.at['OCGT', 'fuel'] = costs.at['gas', 'fuel']
    costs.at['CCGT', 'fuel'] = costs.at['gas', 'fuel']

    costs['marginal_cost'] = costs['VOM'] + costs['fuel'] / costs['efficiency']

    costs = costs.rename(columns={"CO2 intensity": "co2_emissions"})

    costs.at['OCGT', 'co2_emissions'] = costs.at['gas', 'co2_emissions']
    costs.at['CCGT', 'co2_emissions'] = costs.at['gas', 'co2_emissions']

    costs.at['solar', 'capital_cost'] = 0.5*(costs.at['solar-rooftop', 'capital_cost'] +
                                             costs.at['solar-utility', 'capital_cost'])

    def costs_for_storage(store, link1, link2=None, max_hours=1.):
        capital_cost = link1['capital_cost'] + max_hours * store['capital_cost']
        if link2 is not None:
            capital_cost += link2['capital_cost']
        return pd.Series(dict(capital_cost=capital_cost,
                              marginal_cost=0.,
                              co2_emissions=0.))

    if elec_config is None:
        elec_config = snakemake.config['electricity']
    max_hours = elec_config['max_hours']
    costs.loc["battery"] = \
        costs_for_storage(costs.loc["battery storage"], costs.loc["battery inverter"],
                          max_hours=max_hours['battery'])
    costs.loc["H2"] = \
        costs_for_storage(costs.loc["hydrogen storage"], costs.loc["fuel cell"],
                          costs.loc["electrolysis"], max_hours=max_hours['H2'])

    for attr in ('marginal_cost', 'capital_cost'):
        overwrites = config.get(attr)
        if overwrites is not None:
            overwrites = pd.Series(overwrites)
            costs.loc[overwrites.index, attr] = overwrites

    return costs
예제 #6
0
def prepare_network(options):

    #Build the Network object, which stores all other objects
    network = pypsa.Network()
    network.opf_keep_files = False
    network.options = options
    network.shadow_prices = {}

    #load graph
    nodes = pd.Index(
        pd.read_csv("data/graph/nodes.csv", header=None, squeeze=True).values)
    edges = pd.read_csv("data/graph/edges.csv", header=None)

    #set times
    network.set_snapshots(
        pd.date_range(options['tmin'], options['tmax'], freq='H'))

    represented_hours = network.snapshot_weightings.sum()
    Nyears = represented_hours / 8760.

    #set all asset costs and other parameters
    costs = pd.read_csv(snakemake.input.cost_name,
                        index_col=list(range(2))).sort_index()

    #correct units to MW and EUR
    costs.loc[costs.unit.str.contains("/kW"), "value"] *= 1e3
    costs.loc[costs.unit.str.contains("USD"),
              "value"] *= options['USD2019_to_EUR2019']

    costs = costs.loc[:, "value"].unstack(level=1).groupby("technology").sum()

    costs = costs.fillna({
        "CO2 intensity": 0,
        "FOM": 0,
        "VOM": 0,
        "discount rate": options['discountrate'],
        "efficiency": 1,
        "fuel": 0,
        "investment": 0,
        "lifetime": 25
    })

    costs["fixed"] = [
        (annuity(v["lifetime"], v["discount rate"]) + v["FOM"] / 100.) *
        v["investment"] * Nyears for i, v in costs.iterrows()
    ]

    #load demand data
    df_elec, df_heat, df_cooling, p_max_pu, p_nom_max, ashp_cop, gshp_cop, co2_totals, df_transport, transport_data, avail_profile, dsm_profile = prepare_data(
    )

    #add carriers
    network.add("Carrier",
                "gas",
                co2_emissions=costs.at['gas',
                                       'CO2 intensity'])  # in t_CO2/MWht
    network.add("Carrier",
                "coal",
                co2_emissions=costs.at['coal',
                                       'CO2 intensity'])  # in t_CO2/MWht
    network.add("Carrier",
                "lignite",
                co2_emissions=costs.at['lignite',
                                       'CO2 intensity'])  # in t_CO2/MWht
    network.add("Carrier",
                "oil",
                co2_emissions=costs.at['oil',
                                       'CO2 intensity'])  # in t_CO2/MWht
    network.add("Carrier", "nuclear", co2_emissions=0)
    network.add("Carrier", "solid biomass", co2_emissions=0)
    network.add("Carrier", "onwind")
    network.add("Carrier", "offwind")
    network.add("Carrier", "solar")
    if options['add_PHS']:
        network.add("Carrier", "PHS")
    if options['add_hydro']:
        network.add("Carrier", "hydro")
    if options['add_ror']:
        network.add("Carrier", "ror")
    if options['add_H2_storage']:
        network.add("Carrier", "H2")
    if options['add_battery_storage']:
        network.add("Carrier", "battery")
    if options["heat_coupling"]:
        network.add("Carrier", "heat")
        network.add("Carrier", "water tanks")
    if options['cooling_coupling']:
        network.add("Carrier", "cooling")
    if options["retrofitting"]:
        network.add("Carrier", "retrofitting")
    if options["transport_coupling"]:
        network.add("Carrier", "Li ion")

    if options['co2_reduction'] is not None:
        co2_limit = co2_totals["electricity"].sum() * Nyears

        if options["transport_coupling"]:
            co2_limit += co2_totals[[
                i + " non-elec" for i in ["rail", "road", "transport"]
            ]].sum().sum() * Nyears

        if options["heat_coupling"]:
            co2_limit += co2_totals[[
                i + " non-elec" for i in ["residential", "services"]
            ]].sum().sum() * Nyears

        co2_limit *= options['co2_reduction']

        network.add("GlobalConstraint",
                    "co2_limit",
                    type="primary_energy",
                    carrier_attribute="co2_emissions",
                    sense="<=",
                    constant=co2_limit)

    #load hydro data
    if options['add_PHS'] or options['add_hydro']:
        hydrocapa_df = vhydro.get_hydro_capas(
            fn='data/hydro/emil_hydro_capas.csv')

    if options['add_ror']:
        ror_share = vhydro.get_ror_shares(
            fn='data/hydro/ror_ENTSOe_Restore2050.csv')
    else:
        ror_share = pd.Series(0, index=hydrocapa_df.index)

    if options['add_hydro']:
        inflow_df = vhydro.get_hydro_inflow(
            inflow_dir='data/hydro/inflow/') * 1e3  # GWh/h to MWh/h
        # if Hydro_Inflow from Restore2050 is not available, use alternative dataset:
        #inflow_df = vhydro.get_inflow_NCEP_EIA().to_series().unstack(0) #MWh/h

        # select only nodes that are in the network
        # inflow_df = inflow_df.loc[network.snapshots,nodes].dropna(axis=1)
        inflow_df = inflow_df.loc['2011', nodes].dropna(axis=1)
        inflow_df.index = network.snapshots

    network.madd("Bus",
                 nodes,
                 x=[country_shapes[node].centroid.x for node in nodes],
                 y=[country_shapes[node].centroid.y for node in nodes])

    network.madd("Load", nodes, bus=nodes, p_set=df_elec[nodes])

    #add renewables
    onwinds = pd.Index(
        [i for i in p_nom_max['onwind'].index if i[:2] in nodes])
    network.madd("Generator",
                 onwinds,
                 suffix=' onwind',
                 bus=[i[:2] for i in onwinds],
                 p_nom_extendable=True,
                 carrier="onwind",
                 p_nom_max=p_nom_max['onwind'][onwinds],
                 capital_cost=costs.at['onwind', 'fixed'],
                 marginal_cost=costs.at['onwind', 'VOM'],
                 p_max_pu=p_max_pu['onwind'][onwinds])

    offwinds = p_nom_max['offwind'].index[~p_nom_max['offwind'].isnull(
    )].intersection(nodes)
    network.madd("Generator",
                 offwinds,
                 suffix=' offwind',
                 p_nom_extendable=True,
                 bus=offwinds,
                 carrier="offwind",
                 p_nom_max=p_nom_max['offwind'][offwinds],
                 capital_cost=costs.at['offwind', 'fixed'],
                 p_max_pu=p_max_pu['offwind'][offwinds],
                 marginal_cost=costs.at['offwind', 'VOM'])

    if options['ninja_solar']:
        solar = pd.read_csv('data/renewables/ninja_pv_europe_v1.1_sarah.csv',
                            index_col=0,
                            parse_dates=True)[nodes]
    else:
        solar = p_max_pu['solar'][nodes]

    network.madd(
        "Generator",
        nodes,
        suffix=' solar',
        p_nom_extendable=True,
        bus=nodes,
        carrier="solar",
        p_nom_max=p_nom_max['solar'][nodes],
        capital_cost=0.5 * (costs.at['solar-utility', 'fixed'] +
                            costs.at['solar-rooftop', 'fixed']),
        p_max_pu=solar,
        marginal_cost=0.01)  # RES costs made up to fix curtailment order

    #add conventionals
    for generator, carrier in [("OCGT", "gas"), ("CCGT", "gas"),
                               ("coal", "coal"), ("nuclear", "nuclear"),
                               ("lignite", "lignite"), ("oil", "oil")]:
        network.madd("Bus", nodes + " " + carrier, carrier=carrier)

        p_max_pu = 0.9 if generator == 'nuclear' else 1.0
        network.madd(
            "Link",
            nodes + " " + generator,
            bus0=nodes + " " + carrier,
            bus1=nodes,
            marginal_cost=costs.at[generator, 'efficiency'] *
            costs.at[generator, 'VOM'],  #NB: VOM is per MWel
            capital_cost=costs.at[generator, 'efficiency'] *
            costs.at[generator, 'fixed'],  #NB: fixed cost is per MWel
            p_nom_extendable=True,
            p_max_pu=p_max_pu,
            efficiency=costs.at[generator, 'efficiency'])

        network.madd("Store",
                     nodes + " " + carrier + " store",
                     bus=nodes + " " + carrier,
                     e_nom_extendable=True,
                     e_min_pu=-1.,
                     marginal_cost=costs.at[carrier, 'fuel'] +
                     options['CO2price'] * costs.at[carrier, 'CO2 intensity'])

    if options['add_PHS']:
        # pure pumped hydro storage, fixed, 6h energy by default, no inflow
        phss = hydrocapa_df.index[
            hydrocapa_df['p_nom_store[GW]'] > 0].intersection(nodes)
        if options['hydro_capital_cost']:
            cc = costs.at['PHS', 'fixed']
        else:
            cc = 0.

        network.madd(
            "StorageUnit",
            phss,
            suffix=" PHS",
            bus=phss,
            carrier="PHS",
            p_nom_extendable=False,
            p_nom=hydrocapa_df.loc[phss]['p_nom_store[GW]'] *
            1000.,  #from GW to MW
            max_hours=options['PHS_max_hours'],
            efficiency_store=np.sqrt(costs.at['PHS', 'efficiency']),
            efficiency_dispatch=np.sqrt(costs.at['PHS', 'efficiency']),
            cyclic_state_of_charge=True,
            capital_cost=cc,
            marginal_cost=options['marginal_cost_storage'])

    if options['add_hydro']:
        # inflow hydro:
        #  * run-of-river if E_s=0
        #  * reservoir
        #  * could include mixed pumped, if 0>p_min_pu_fixed=p_pump*p_nom

        #add storage
        pnom = (1 - ror_share
                ) * hydrocapa_df['p_nom_discharge[GW]'] * 1000.  #GW to MW
        hydros = pnom.index[pnom > 0.]

        if options['hydro_max_hours'] is None:
            max_hours = (hydrocapa_df.loc[hydros, 'E_store[TWh]'] * 1e6 / pnom
                         )  #TWh to MWh
        else:
            max_hours = options['hydro_max_hours']

        inflow = inflow_df.multiply((1 - ror_share))[hydros].dropna(axis=1)

        if options['hydro_capital_cost']:
            cc = costs.at['hydro', 'fixed']
        else:
            cc = 0.

        network.madd(
            "StorageUnit",
            hydros,
            suffix=' hydro',
            bus=hydros,
            carrier="hydro",
            p_nom_extendable=False,
            p_nom=pnom[hydros],
            max_hours=max_hours,
            p_max_pu=1,  #dispatch
            p_min_pu=0.,  #store
            efficiency_dispatch=1,
            efficiency_store=0,
            inflow=inflow,
            cyclic_state_of_charge=True,
            capital_cost=cc,
            marginal_cost=options['marginal_cost_storage'])

    if options['add_ror']:
        rors = ror_share.index[ror_share > 0.]
        rors = rors.intersection(nodes)
        rors = rors.intersection(inflow_df.columns)
        pnom = ror_share[rors] * hydrocapa_df.loc[
            rors, 'p_nom_discharge[GW]'] * 1000.  #GW to MW
        inflow_pu = inflow_df[rors].multiply(ror_share[rors] / pnom)
        inflow_pu[
            inflow_pu >
            1] = 1.  #limit inflow per unit to one, i.e, excess inflow is spilled here

        if options['hydro_capital_cost']:
            cc = costs.at['ror', 'fixed']
        else:
            cc = 0.

        network.madd("Generator",
                     rors,
                     suffix=" ror",
                     bus=rors,
                     carrier="ror",
                     p_nom_extendable=False,
                     p_nom=pnom,
                     p_max_pu=inflow_pu,
                     capital_cost=cc,
                     marginal_cost=options['marginal_cost_storage'])

    if options['add_H2_storage']:
        PtGC = 1 - options['PtGC'] / 100.
        network.madd("Bus", nodes + " H2", carrier="H2")

        network.madd("Link",
                     nodes + " H2 Electrolysis",
                     bus1=nodes + " H2",
                     bus0=nodes,
                     p_nom_extendable=True,
                     efficiency=costs.at["electrolysis", "efficiency"],
                     capital_cost=costs.at["electrolysis", "fixed"] * PtGC)

        network.madd(
            "Link",
            nodes + " H2 Fuel Cell",
            bus0=nodes + " H2",
            bus1=nodes,
            p_nom_extendable=True,
            efficiency=costs.at["fuel cell", "efficiency"],
            capital_cost=costs.at["fuel cell", "fixed"] *
            costs.at["fuel cell", "efficiency"])  #NB: fixed cost is per MWel

        network.madd("Store",
                     nodes + " H2 Store tank",
                     bus=nodes + " H2",
                     e_nom_extendable=True,
                     e_cyclic=True,
                     capital_cost=costs.at["hydrogen storage tank", "fixed"])

        # add upper limits for underground H2 storage
        H2_cavern = pd.read_csv('data/hydrogen_salt_cavern_potentials.csv',
                                index_col=0,
                                sep=';')
        network.madd(
            "Store",
            nodes[H2_cavern.iloc[:, 0]] + " H2 Store underground",
            bus=nodes[H2_cavern.iloc[:, 0]] + " H2",
            e_nom_extendable=True,
            e_nom_max=H2_cavern.TWh[H2_cavern.iloc[:, 0]].values *
            1e6,  #from TWh to MWh
            e_cyclic=True,
            capital_cost=costs.at["hydrogen storage underground", "fixed"])

    if options['add_methanation']:
        network.madd("Link",
                     nodes + " Sabatier",
                     bus0=nodes + " H2",
                     bus1=nodes + " gas",
                     p_nom_extendable=True,
                     efficiency=costs.at["methanation", "efficiency"],
                     capital_cost=costs.at["methanation", "fixed"] * PtGC)

    if options['add_battery_storage']:

        network.madd("Bus", nodes + " battery", carrier="battery")

        network.madd("Store",
                     nodes + " battery",
                     bus=nodes + " battery",
                     e_cyclic=True,
                     e_nom_extendable=True,
                     capital_cost=costs.at['battery storage', 'fixed'])

        network.madd("Link",
                     nodes + " battery charger",
                     bus0=nodes,
                     bus1=nodes + " battery",
                     efficiency=costs.at['battery inverter',
                                         'efficiency']**0.5,
                     capital_cost=costs.at['battery inverter', 'fixed'],
                     p_nom_extendable=True)

        network.madd("Link",
                     nodes + " battery discharger",
                     bus0=nodes + " battery",
                     bus1=nodes,
                     efficiency=costs.at['battery inverter',
                                         'efficiency']**0.5,
                     marginal_cost=options['marginal_cost_storage'],
                     p_nom_extendable=True)

    #Sources:
    #[HP]: Henning, Palzer http://www.sciencedirect.com/science/article/pii/S1364032113006710
    #[B]: Budischak et al. http://www.sciencedirect.com/science/article/pii/S0378775312014759

    if options["heat_coupling"]:

        #urban are southern countries, where disctrict heating will not be implemented
        if options["central"]:
            urban = pd.Index(["ES", "GR", "PT", "IT", "BG"])
        else:
            urban = nodes

        #NB: must add costs of central heating afterwards (EUR 400 / kWpeak, 50a, 1% FOM from Fraunhofer ISE)

        #central are urban nodes with district heating
        # district heating shares come from https://www.euroheat.org/knowledge-hub/country-profiles/
        central = nodes ^ urban
        urban_fraction = pd.read_csv(
            'data/existing_2020/district_heating_share.csv',
            index_col=0).loc[nodes, str(2015)]  #options['year']
        network.madd("Bus", nodes + " heat", carrier="heat")

        network.madd("Bus", nodes + " urban heat", carrier="heat")

        network.madd("Load",
                     nodes,
                     suffix=" heat",
                     bus=nodes + " heat",
                     p_set=df_heat[nodes].multiply((1 - urban_fraction)))

        network.madd("Load",
                     nodes,
                     suffix=" urban heat",
                     bus=nodes + " urban heat",
                     p_set=df_heat[nodes].multiply(urban_fraction))

        if options["PTH"]:
            HPC = 1 - options['HPC'] / 100.
            RHC = 1 - options['RHC'] / 100.

            network.madd(
                "Link",
                urban,
                suffix=" central heat pump",
                bus0=urban,
                bus1=urban + " urban heat",
                efficiency=ashp_cop[urban] if options["time_dep_hp_cop"] else
                costs.at['decentral air-sourced heat pump', 'efficiency'],
                capital_cost=costs.at['decentral air-sourced heat pump',
                                      'efficiency'] *
                costs.at['decentral air-sourced heat pump', 'fixed'] * HPC,
                p_nom_extendable=True)

            network.madd(
                "Link",
                central,
                suffix=" central heat pump",
                bus0=central,
                bus1=central + " urban heat",
                efficiency=gshp_cop[central] if options["time_dep_hp_cop"] else
                costs.at['central ground-sourced heat pump', 'efficiency'],
                capital_cost=costs.at['central ground-sourced heat pump',
                                      'efficiency'] *
                costs.at['central ground-sourced heat pump', 'fixed'] * HPC,
                p_nom_extendable=True)

            network.madd(
                "Link",
                nodes,
                suffix=" decentral heat pump",
                bus0=nodes,
                bus1=nodes + " heat",
                efficiency=gshp_cop[nodes] if options["time_dep_hp_cop"] else
                costs.at['decentral ground-sourced heat pump', 'efficiency'],
                capital_cost=costs.at['decentral ground-sourced heat pump',
                                      'efficiency'] *
                costs.at['decentral ground-sourced heat pump', 'fixed'] * HPC,
                p_nom_extendable=True)

            network.madd("Link",
                         nodes + " decentral resistive heater",
                         bus0=nodes,
                         bus1=nodes + " heat",
                         efficiency=costs.at['decentral resistive heater',
                                             'efficiency'],
                         capital_cost=costs.at['decentral resistive heater',
                                               'efficiency'] *
                         costs.at['decentral resistive heater', 'fixed'] * RHC,
                         p_nom_extendable=True)

            network.madd("Link",
                         urban + " central resistive heater",
                         bus0=urban,
                         bus1=urban + " urban heat",
                         efficiency=costs.at['decentral resistive heater',
                                             'efficiency'],
                         capital_cost=costs.at['decentral resistive heater',
                                               'efficiency'] *
                         costs.at['decentral resistive heater', 'fixed'] * RHC,
                         p_nom_extendable=True)

            network.madd(
                "Link",
                central + " central resistive heater",
                bus0=central,
                bus1=central + " urban heat",
                p_nom_extendable=True,
                capital_cost=costs.at['central resistive heater', 'efficiency']
                * costs.at['central resistive heater', 'fixed'] * RHC,
                efficiency=costs.at['central resistive heater', 'efficiency'])

        if options["tes"]:

            network.madd("Bus", nodes + " water tanks", carrier="water tanks")

            network.madd("Link",
                         nodes + " water tanks charger",
                         bus0=nodes + " heat",
                         bus1=nodes + " water tanks",
                         efficiency=costs.at['water tank charger',
                                             'efficiency'],
                         p_nom_extendable=True)

            network.madd("Link",
                         nodes + " water tanks discharger",
                         bus0=nodes + " water tanks",
                         bus1=nodes + " heat",
                         efficiency=costs.at['water tank discharger',
                                             'efficiency'],
                         p_nom_extendable=True)

            network.madd(
                "Store",
                nodes + " water tank",
                bus=nodes + " water tanks",
                e_cyclic=True,
                e_nom_extendable=True,
                standing_loss=1 - np.exp(
                    -1 / (24. * options["tes_tau"])
                ),  # [HP] 180 day time constant for centralised, 3 day for decentralised
                capital_cost=costs.at['decentral water tank storage',
                                      'fixed'] / (1.17e-3 * 40)
            )  #convert EUR/m^3 to EUR/MWh for 40 K diff and 1.17 kWh/m^3/K

            network.madd("Bus",
                         urban + " central water tanks",
                         carrier="water tanks")

            network.madd("Link",
                         urban + " central water tanks charger",
                         bus0=urban + " urban heat",
                         bus1=urban + " central water tanks",
                         efficiency=costs.at['water tank charger',
                                             'efficiency'],
                         p_nom_extendable=True)

            network.madd("Link",
                         urban + " central water tanks discharger",
                         bus0=urban + " central water tanks",
                         bus1=urban + " urban heat",
                         efficiency=costs.at['water tank discharger',
                                             'efficiency'],
                         p_nom_extendable=True)

            network.madd(
                "Store",
                urban + " central water tank",
                bus=urban + " central water tanks",
                e_cyclic=True,
                e_nom_extendable=True,
                standing_loss=1 - np.exp(
                    -1 / (24. * options["tes_tau"])
                ),  # [HP] 180 day time constant for centralised, 3 day for decentralised
                capital_cost=costs.at['decentral water tank storage',
                                      'fixed'] / (1.17e-3 * 40)
            )  #convert EUR/m^3 to EUR/MWh for 40 K diff and 1.17 kWh/m^3/K

            network.madd("Bus",
                         central + " central water tanks",
                         carrier="water tanks")

            network.madd("Link",
                         central + " central water tanks charger",
                         bus0=central + " urban heat",
                         bus1=central + " central water tanks",
                         p_nom_extendable=True,
                         efficiency=costs.at['water tank charger',
                                             'efficiency'])

            network.madd("Link",
                         central + " central water tanks discharger",
                         bus0=central + " central water tanks",
                         bus1=central + " urban heat",
                         p_nom_extendable=True,
                         efficiency=costs.at['water tank discharger',
                                             'efficiency'])

            network.madd(
                "Store",
                central,
                suffix=" central water tank",
                bus=central + " central water tanks",
                e_cyclic=True,
                e_nom_extendable=True,
                standing_loss=1 - np.exp(
                    -1 / (24. * 180.)
                ),  # [HP] 180 day time constant for centralised, 3 day for decentralised
                capital_cost=costs.at[
                    'central water tank storage',
                    'fixed'])  # EUR/MWh now instead of EUR/m^3

            # Heat demand-side management by building thermal inertia
            if options['dsm_heat']:
                max_hours = options['DSM']  # 0,2,4,6,... hours
                network.madd("StorageUnit",
                             nodes,
                             suffix=" DSM",
                             bus=nodes + " heat",
                             cyclic_state_of_charge=True,
                             max_hours=max_hours,
                             standing_loss=1 - np.exp(-1 / max_hours),
                             p_nom_extendable=False,
                             p_nom=df_heat[nodes].multiply(
                                 (1 - urban_fraction)).mean())

                network.madd(
                    "StorageUnit",
                    nodes,
                    suffix=" DSM urban",
                    bus=nodes + " urban heat",
                    cyclic_state_of_charge=True,
                    max_hours=max_hours,
                    standing_loss=1 - np.exp(-1 / max_hours),
                    p_nom_extendable=False,
                    p_nom=df_heat[nodes].multiply(urban_fraction).mean())

        if options["boilers"]:

            network.madd(
                "Link",
                nodes + " decentral gas boiler",
                p_nom_extendable=True,
                bus0=nodes + " gas",
                bus1=nodes + " heat",
                efficiency=costs.at['decentral gas boiler', 'efficiency'],
                capital_cost=costs.at['decentral gas boiler', 'efficiency'] *
                costs.at['decentral gas boiler', 'fixed'])

            network.madd(
                "Link",
                urban + " central gas boiler",
                p_nom_extendable=True,
                bus0=urban + " gas",
                bus1=urban + " urban heat",
                efficiency=costs.at['decentral gas boiler', 'efficiency'],
                capital_cost=costs.at['decentral gas boiler', 'efficiency'] *
                costs.at['decentral gas boiler', 'fixed'])

            network.madd(
                "Link",
                central + " central gas boiler",
                bus0=central + " gas",
                bus1=central + " urban heat",
                p_nom_extendable=True,
                capital_cost=costs.at['central gas boiler', 'efficiency'] *
                costs.at['central gas boiler', 'fixed'],
                efficiency=costs.at['central gas boiler', 'efficiency'])

        if options["chp"]:

            network.madd("Link",
                         central + " central gas CHP electric",
                         bus0=central + " gas",
                         bus1=central,
                         p_nom_extendable=True,
                         capital_cost=costs.at['central gas CHP', 'fixed'] *
                         options['chp_parameters']['eta_elec'],
                         efficiency=options['chp_parameters']['eta_elec'])

            network.madd("Link",
                         central + " central gas CHP heat",
                         bus0=central + " gas",
                         bus1=central + " urban heat",
                         p_nom_extendable=True,
                         efficiency=options['chp_parameters']['eta_elec'] /
                         options['chp_parameters']['c_v'])

            if options["biomass"]:

                network.madd("Bus",
                             nodes + " solid biomass",
                             carrier="solid biomass")

                biomass_potential = pd.read_csv('data/biomass_potentials.csv',
                                                index_col=0)
                network.madd(
                    "Store",
                    nodes + " solid biomass store",
                    bus=nodes + " solid biomass",
                    e_initial=0,
                    e_min_pu=-1.,
                    e_nom_extendable=True,
                    e_nom_max=biomass_potential.loc[nodes,
                                                    'solid biomass'].values,
                    capital_cost=0.01,
                    marginal_cost=costs.at['solid biomass', 'fuel'] +
                    options['CO2price'] *
                    costs.at['solid biomass', 'CO2 intensity'])

                network.madd("Link",
                             central + " central biomass CHP electric",
                             bus0=central + " solid biomass",
                             bus1=central,
                             p_nom_extendable=True,
                             capital_cost=costs.at['biomass CHP', 'fixed'] *
                             options['chp_parameters']['eta_elec'],
                             efficiency=options['chp_parameters']['eta_elec'])

                network.madd("Link",
                             central + " central biomass CHP heat",
                             bus0=central + " solid biomass",
                             bus1=central + " urban heat",
                             p_nom_extendable=True,
                             efficiency=options['chp_parameters']['eta_elec'] /
                             options['chp_parameters']['c_v'])

                network.madd("Link",
                             central + " central biomass HOP",
                             bus0=central + " solid biomass",
                             bus1=central + " urban heat",
                             p_nom_extendable=True,
                             capital_cost=costs.at['biomass HOP', 'fixed'] *
                             costs.at['biomass HOP', 'efficiency'],
                             efficiency=costs.at['biomass HOP', 'efficiency'])

                network.madd("Link",
                             nodes + " biomass EOP",
                             bus0=nodes + " solid biomass",
                             bus1=nodes,
                             p_nom_extendable=True,
                             capital_cost=costs.at['biomass EOP', 'fixed'] *
                             costs.at['biomass EOP', 'efficiency'],
                             efficiency=costs.at['biomass EOP', 'efficiency'])


# add cooling if neccesary
    if options['cooling_coupling']:
        network.madd("Bus", nodes + " cooling", carrier="cooling")

        network.madd("Load",
                     nodes,
                     suffix=" cooling",
                     bus=nodes + " cooling",
                     p_set=df_cooling[nodes])

        # decentral ground heat pump can also provide cooling
        network.madd("Link",
                     nodes,
                     suffix=" cooling pump",
                     bus0=nodes,
                     bus1=nodes + " cooling",
                     efficiency=3,
                     p_nom_extendable=True)

    # transportation sector
    if options["transport_coupling"]:
        EV_pene = options['EV_pene']
        network.madd("Bus", nodes, suffix=" EV battery", carrier="Li ion")

        network.madd("Load",
                     nodes,
                     suffix=" transport",
                     bus=nodes + " EV battery",
                     p_set=EV_pene *
                     (df_transport[nodes] + shift_df(df_transport[nodes], 1) +
                      shift_df(df_transport[nodes], 2)) / 3.)

        p_nom = EV_pene * transport_data[
            "number cars"] * 0.011  #3-phase charger with 11 kW * x% of time grid-connected
        network.madd(
            "Link",
            nodes,
            suffix=" BEV charger",
            bus0=nodes,
            bus1=nodes + " EV battery",
            p_nom=p_nom,
            p_max_pu=avail_profile[nodes],
            efficiency=0.9,  #[B]
            #These were set non-zero to find LU infeasibility when availability = 0.25
            #p_nom_extendable=True,
            #p_nom_min=p_nom,
            #capital_cost=1e6,  #i.e. so high it only gets built where necessary
        )

        if options["v2g"]:
            network.madd("Link",
                         nodes,
                         suffix=" V2G",
                         bus1=nodes,
                         bus0=nodes + " EV battery",
                         p_nom=p_nom * options['v2g_availability'],
                         p_max_pu=avail_profile[nodes],
                         efficiency=0.9)  #[B]

        if options["bev"]:
            network.madd("Store",
                         nodes,
                         suffix=" battery storage",
                         bus=nodes + " EV battery",
                         e_cyclic=True,
                         e_nom=EV_pene * transport_data["number cars"] * 0.05 *
                         options["bev_availability"],
                         e_max_pu=1,
                         e_min_pu=dsm_profile[nodes])

    #add lines
    if not network.options['no_lines']:

        lengths = np.array([
            haversine(
                [network.buses.at[name0, "x"], network.buses.at[name0, "y"]],
                [network.buses.at[name1, "x"], network.buses.at[name1, "y"]])
            for name0, name1 in edges.values
        ])

        if options['line_volume_limit_factor'] is not None:
            cc = Nyears * 0.01  # Set line costs to ~zero because we already restrict the line volume
        else:
            cc = ((options['line_cost_factor']*lengths*costs.at['HVDC overhead','fixed']*1.25+costs.at['HVDC inverter pair','fixed']) \
                    * 1.5)
            # 1.25 because lines are not straight, 150000 is per MW cost of
            # converter pair for DC line,
            # n-1 security is approximated by an overcapacity factor 1.5 ~ 1./0.666667
            #FOM of 2%/a

        network.madd("Link",
                     edges[0] + '-' + edges[1],
                     bus0=edges[0].values,
                     bus1=edges[1].values,
                     p_nom_extendable=True,
                     p_min_pu=-1,
                     length=lengths,
                     capital_cost=cc)

    return network
예제 #7
0
def add_water_heating(n):
    ##### CHP Parameters
    ###### electrical efficiency with no heat output
    eta_elec = 0.468
    ###### loss of fuel for each addition of heat
    c_v = 0.15
    ###### backpressure ratio
    c_m = 0.75
    ###### ratio of max heat output to max electrical output
    p_nom_ratio = 1.

    heat_demand = compute_heat_demand(n)

    network.add("Carrier", "heat")

    network.add("Bus", node + " heat", carrier="heat")
    network.add(
        "Link",
        node + " heat pump",
        bus0=node,
        bus1=node + " heat",
        efficiency=cop[node],  #cop for 2011 time_dep_hp_cop
        capital_cost=(annuity(20, discountrate) + 0.015) * 3. *
        1.050e6,  #20a, 1% FOM, 1050 EUR/kWth from [HP] NB: PyPSA uses bus0 for p_nom restriction, hence factor 3 to get 3150 EUR/kWel
        p_nom_extendable=True)
    network.add("Load",
                node + " heat",
                bus=node + " heat",
                p_set=heat_demand[node])
    network.add(
        "Link",
        node + " resistive heater",
        bus0=node,
        bus1=node + " heat",
        efficiency=0.9,
        capital_cost=(annuity(20, discountrate) + 0.02) * 0.9 *
        1.e5,  #100 EUR/kW_th, 2% FOM from Schaber thesis
        p_nom_extendable=True,
    )

    ##### H2 bus w/methanation
    # methanation
    network.add(
        "Link",
        node + " Sabatier",
        bus0=node + " H2",
        bus1=node + " OCGT",
        p_nom_extendable=True,
        #Efficiency from Katrin Schaber PhD thesis
        efficiency=0.77,
        #Costs from Katrin Schaber PhD thesis; comparable with HP (1.5e6 including H2 electrolysis)
        capital_cost=(annuity(20., discountrate) + 0.02) * 1.1e6 * Nyears)
    # gas boiler
    network.add(
        "Link",
        node + " gas boiler",
        p_nom_extendable=True,
        bus0=node + " OCGT",
        bus1=node + " heat",
        capital_cost=(annuity(20, 0.07) + 0.01) * 0.9 *
        3.e5,  #300 EUR/kW_th, 1% FOM from Schaber thesis, 20a from HP
        efficiency=0.9,
    )
    # chp - standardmäßig rein? Ist es jetzt auf jeden Fall!
    network.add(
        "Link",
        node + " CHP electric",
        bus0=node + " OCGT",
        bus1=node,
        capital_cost=(annuity(25, 0.07) + 0.03) * 1.4e6 *
        eta_elec,  #From HP decentral
        efficiency=eta_elec,
        p_nom_extendable=True)
    network.add("Link",
                node + " CHP heat",
                p_nom_extendable=True,
                bus0=node + " OCGT",
                bus1=node + " heat",
                capital_cost=0.,
                efficiency=eta_elec / c_v)

    ##### heat flexibilities:
    if "TES" in flexibilities:
        network.add("Carrier", "water tanks")
        network.add("Bus", node + " water tanks", carrier="water tanks")
        network.add("Link",
                    node + " water tanks charger",
                    bus0=node + " heat",
                    bus1=node + " water tanks",
                    efficiency=0.9,
                    capital_cost=0.,
                    p_nom_extendable=True)
        network.add("Link",
                    node + " water tanks discharger",
                    bus0=node + " water tanks",
                    bus1=node + " heat",
                    efficiency=0.9,
                    capital_cost=0.,
                    p_nom_extendable=True)
        network.add(
            "Store",
            node + " water tank",
            bus=node + " water tanks",
            e_cyclic=True,
            e_nom_extendable=True,
            standing_loss=1 - np.exp(
                -1 / (24. * 180)
            ),  #options["tes_tau"])),  # [HP] 180 day time constant for centralised, 3 day for decentralised
            capital_cost=(annuity(40, discountrate) + 0.01) * 20 / (
                1.17e-3 * 40
            ),  #[HP] 40 years, 20 EUR/m^3 in EUR/MWh for 40 K diff and 1.17 kWh/m^2, 1% FOM
        )
def export_metrics_to_json(network, power_round=1):
    """power_round refers to GW of power"""

    n = network

    #add missing costs

    n.links.capital_cost = (400 * 1.25 * n.links.length +
                            150000.) * 1.5 * (annuity(40., 0.07) + 0.02)

    hydro = n.storage_units.index[n.storage_units.carrier == "hydro"]
    PHS = n.storage_units.index[n.storage_units.carrier == "PHS"]
    ror = n.generators.index[n.generators.carrier == "ror"]

    n.storage_units.loc[hydro, "p_nom_opt"] = n.storage_units.loc[hydro,
                                                                  "p_nom"]
    n.storage_units.loc[PHS, "p_nom_opt"] = n.storage_units.loc[PHS, "p_nom"]
    n.generators.loc[ror, "p_nom_opt"] = n.generators.loc[ror, "p_nom"]

    n.storage_units.loc[hydro, "capital_cost"] = (0.01) * 2e6
    n.storage_units.loc[PHS, "capital_cost"] = (0.01) * 2e6
    n.generators.loc[ror, "capital_cost"] = (0.02) * 3e6

    #only take AC buses
    carrier = "AC"
    buses = n.buses[n.buses.carrier == carrier]

    generation_carriers = n.generators.carrier.value_counts().index
    generation_carriers = (preferred_order & generation_carriers).append(
        generation_carriers.difference(preferred_order))
    print("generation carriers:", generation_carriers)

    storage_carriers = n.storage_units.carrier.value_counts().index
    storage_carriers = (preferred_order & storage_carriers).append(
        storage_carriers.difference(preferred_order))
    print("storage carriers:", storage_carriers)

    if len(metrics["energy"]) == 0:
        metrics["energy"] = [[] for i in range(len(buses.index) + 1)]
        metrics["power"] = [[] for i in range(len(buses.index) + 1)]
        metrics["cost"] = [[] for i in range(len(buses.index) + 1)]

    carriers = {}
    carriers["energy"] = generation_carriers.append(
        pd.Index(["electricity demand"]).append(storage_carriers))
    carriers["power"] = generation_carriers.append(storage_carriers)
    carriers["cost"] = carriers["power"].append(
        pd.Index(["OCGT marginal", "transmission"]))

    for k, v in carriers.items():
        metrics[k][0].append(pd.Series(index=v).fillna(0.))

    for i, ct in enumerate(buses.index):

        storage = n.storage_units_t.p.loc[:,
                                          n.storage_units.bus == ct].groupby(
                                              n.storage_units.carrier,
                                              axis=1).sum().reindex(
                                                  columns=storage_carriers
                                              ).fillna(0.)
        generation = n.generators_t.p.loc[:, n.generators.bus == ct].groupby(
            n.generators.carrier,
            axis=1).sum().reindex(columns=generation_carriers).fillna(0.)
        load = n.loads_t.p_set.loc[:, n.loads.bus == ct].sum(axis=1)
        load.name = "electricity demand"

        data = {}

        data["energy"] = (pd.concat((generation.sum(), storage.sum(),
                                     pd.Series([-load.sum()], [load.name]))) /
                          factor).round(power_round).reindex(
                              carriers["energy"])

        data["power"] = (pd.concat(
            (n.generators.p_nom_opt[n.generators.bus == ct].groupby(
                n.generators.carrier).sum(),
             n.storage_units.p_nom_opt[n.storage_units.bus == ct].groupby(
                 n.storage_units.carrier).sum())) /
                         factor).round(power_round).reindex(
                             carriers["power"]).fillna(0.)

        generation_cost = (
            n.generators.p_nom_opt *
            n.generators.capital_cost)[n.generators.bus == ct].groupby(
                n.generators.carrier).sum()

        storage_cost = (
            n.storage_units.p_nom_opt *
            n.storage_units.capital_cost)[n.storage_units.bus == ct].groupby(
                n.storage_units.carrier).sum()

        #Each country gets half of transmission lines ending there
        transmission_cost = 0.5 * pd.Series(
            [(n.links.p_nom_opt * n.links.capital_cost)[
                (n.links.bus0 == ct) ^ (n.links.bus1 == ct)].sum()],
            index=["transmission"])

        marginal_cost = pd.Series([
            n.generators.at[ct + " OCGT", "marginal_cost"] *
            n.generators_t.p[ct + " OCGT"].sum()
        ],
                                  index=["OCGT marginal"])

        data["cost"] = pd.concat(
            (generation_cost, storage_cost, transmission_cost,
             marginal_cost)).reindex(carriers["cost"]).fillna(0.)

        for item in carriers:
            metrics[item][0][-1] += data[item]
            metrics[item][i + 1].append(data[item].values.tolist())

    for item in carriers:
        metrics[item][0][-1] = metrics[item][0][-1].values.tolist()
        metrics[item + "_carriers"] = carriers[item].tolist()
        metrics[item + "_colors"] = [colors[i] for i in carriers[item]]

    metrics["price"].append([])

    metrics["price"][-1].append(
        -pd.read_csv(n.folder + "shadow_prices.csv").at[0, "co2_constraint"])