commit
d4a82a2a41
@ -1,3 +1,4 @@
|
||||
attribute,type,unit,default,description,status
|
||||
build_year,integer,year,n/a,build year,Input (optional)
|
||||
lifetime,float,years,n/a,lifetime,Input (optional)
|
||||
carrier,string,n/a,n/a,carrier,Input (optional)
|
||||
lifetime,float,years,inf,lifetime,Input (optional)
|
||||
build_year,int,year ,0,build year,Input (optional)
|
||||
|
|
@ -2,12 +2,12 @@ attribute,type,unit,default,description,status
|
||||
bus2,string,n/a,n/a,2nd bus,Input (optional)
|
||||
bus3,string,n/a,n/a,3rd bus,Input (optional)
|
||||
bus4,string,n/a,n/a,4th bus,Input (optional)
|
||||
efficiency2,static or series,per unit,1.,2nd bus efficiency,Input (optional)
|
||||
efficiency3,static or series,per unit,1.,3rd bus efficiency,Input (optional)
|
||||
efficiency4,static or series,per unit,1.,4th bus efficiency,Input (optional)
|
||||
p2,series,MW,0.,2nd bus output,Output
|
||||
p3,series,MW,0.,3rd bus output,Output
|
||||
p4,series,MW,0.,4th bus output,Output
|
||||
build_year,integer,year,n/a,build year,Input (optional)
|
||||
lifetime,float,years,n/a,lifetime,Input (optional)
|
||||
efficiency2,static or series,per unit,1,2nd bus efficiency,Input (optional)
|
||||
efficiency3,static or series,per unit,1,3rd bus efficiency,Input (optional)
|
||||
efficiency4,static or series,per unit,1,4th bus efficiency,Input (optional)
|
||||
p2,series,MW,0,2nd bus output,Output
|
||||
p3,series,MW,0,3rd bus output,Output
|
||||
p4,series,MW,0,4th bus output,Output
|
||||
carrier,string,n/a,n/a,carrier,Input (optional)
|
||||
lifetime,float,years,inf,lifetime,Input (optional)
|
||||
build_year,int,year ,0,build year,Input (optional)
|
||||
|
|
@ -1,4 +1,4 @@
|
||||
attribute,type,unit,default,description,status
|
||||
build_year,integer,year,n/a,build year,Input (optional)
|
||||
lifetime,float,years,n/a,lifetime,Input (optional)
|
||||
carrier,string,n/a,n/a,carrier,Input (optional)
|
||||
lifetime,float,years,inf,lifetime,Input (optional)
|
||||
build_year,int,year ,0,build year,Input (optional)
|
||||
|
|
@ -8,15 +8,22 @@ idx = pd.IndexSlice
|
||||
|
||||
import pypsa
|
||||
import yaml
|
||||
import numpy as np
|
||||
|
||||
from add_existing_baseyear import add_build_year_to_new_assets
|
||||
from helper import override_component_attrs
|
||||
from solve_network import basename
|
||||
|
||||
|
||||
def add_brownfield(n, n_p, year):
|
||||
|
||||
print("adding brownfield")
|
||||
|
||||
# electric transmission grid set optimised capacities of previous as minimum
|
||||
n.lines.s_nom_min = n_p.lines.s_nom_opt
|
||||
dc_i = n.links[n.links.carrier=="DC"].index
|
||||
n.links.loc[dc_i, "p_nom_min"] = n_p.links.loc[dc_i, "p_nom_opt"]
|
||||
|
||||
for c in n_p.iterate_components(["Link", "Generator", "Store"]):
|
||||
|
||||
attr = "e" if c.name == "Store" else "p"
|
||||
@ -25,7 +32,7 @@ def add_brownfield(n, n_p, year):
|
||||
# CO2 or global EU values since these are already in n
|
||||
n_p.mremove(
|
||||
c.name,
|
||||
c.df.index[c.df.lifetime.isna()]
|
||||
c.df.index[c.df.lifetime==np.inf]
|
||||
)
|
||||
|
||||
# remove assets whose build_year + lifetime < year
|
||||
@ -44,7 +51,7 @@ def add_brownfield(n, n_p, year):
|
||||
)]
|
||||
|
||||
threshold = snakemake.config['existing_capacities']['threshold_capacity']
|
||||
|
||||
|
||||
if not chp_heat.empty:
|
||||
threshold_chp_heat = (threshold
|
||||
* c.df.efficiency[chp_heat.str.replace("heat", "electric")].values
|
||||
@ -55,7 +62,7 @@ def add_brownfield(n, n_p, year):
|
||||
c.name,
|
||||
chp_heat[c.df.loc[chp_heat, attr + "_nom_opt"] < threshold_chp_heat]
|
||||
)
|
||||
|
||||
|
||||
n_p.mremove(
|
||||
c.name,
|
||||
c.df.index[c.df[attr + "_nom_extendable"] & ~c.df.index.isin(chp_heat) & (c.df[attr + "_nom_opt"] < threshold)]
|
||||
@ -75,16 +82,44 @@ def add_brownfield(n, n_p, year):
|
||||
for tattr in n.component_attrs[c.name].index[selection]:
|
||||
n.import_series_from_dataframe(c.pnl[tattr], c.name, tattr)
|
||||
|
||||
# deal with gas network
|
||||
pipe_carrier = ['gas pipeline']
|
||||
if snakemake.config["sector"]['H2_retrofit']:
|
||||
# drop capacities of previous year to avoid duplicating
|
||||
to_drop = n.links.carrier.isin(pipe_carrier) & (n.links.build_year!=year)
|
||||
n.mremove("Link", n.links.loc[to_drop].index)
|
||||
|
||||
# subtract the already retrofitted from today's gas grid capacity
|
||||
h2_retrofitted_fixed_i = n.links[(n.links.carrier=='H2 pipeline retrofitted') & (n.links.build_year!=year)].index
|
||||
gas_pipes_i = n.links[n.links.carrier.isin(pipe_carrier)].index
|
||||
CH4_per_H2 = 1 / snakemake.config["sector"]["H2_retrofit_capacity_per_CH4"]
|
||||
fr = "H2 pipeline retrofitted"
|
||||
to = "gas pipeline"
|
||||
# today's pipe capacity
|
||||
pipe_capacity = n.links.loc[gas_pipes_i, 'p_nom']
|
||||
# already retrofitted capacity from gas -> H2
|
||||
already_retrofitted = (n.links.loc[h2_retrofitted_fixed_i, 'p_nom']
|
||||
.rename(lambda x: basename(x).replace(fr, to)).groupby(level=0).sum())
|
||||
remaining_capacity = pipe_capacity - CH4_per_H2 * already_retrofitted.reindex(index=pipe_capacity.index).fillna(0)
|
||||
n.links.loc[gas_pipes_i, "p_nom"] = remaining_capacity
|
||||
else:
|
||||
new_pipes = n.links.carrier.isin(pipe_carrier) & (n.links.build_year==year)
|
||||
n.links.loc[new_pipes, "p_nom"] = 0.
|
||||
n.links.loc[new_pipes, "p_nom_min"] = 0.
|
||||
|
||||
|
||||
|
||||
#%%
|
||||
if __name__ == "__main__":
|
||||
if 'snakemake' not in globals():
|
||||
from helper import mock_snakemake
|
||||
snakemake = mock_snakemake(
|
||||
'add_brownfield',
|
||||
simpl='',
|
||||
clusters=48,
|
||||
clusters="37",
|
||||
opts="",
|
||||
lv=1.0,
|
||||
sector_opts='Co2L0-168H-T-H-B-I-solar3-dist1',
|
||||
sector_opts='168H-T-H-B-I-solar+p3-dist1',
|
||||
planning_horizons=2030,
|
||||
)
|
||||
|
||||
|
@ -12,9 +12,11 @@ import xarray as xr
|
||||
import pypsa
|
||||
import yaml
|
||||
|
||||
from prepare_sector_network import prepare_costs
|
||||
from prepare_sector_network import prepare_costs, define_spatial
|
||||
from helper import override_component_attrs
|
||||
|
||||
from types import SimpleNamespace
|
||||
spatial = SimpleNamespace()
|
||||
|
||||
def add_build_year_to_new_assets(n, baseyear):
|
||||
"""
|
||||
@ -28,7 +30,7 @@ def add_build_year_to_new_assets(n, baseyear):
|
||||
# Give assets with lifetimes and no build year the build year baseyear
|
||||
for c in n.iterate_components(["Link", "Generator", "Store"]):
|
||||
|
||||
assets = c.df.index[~c.df.lifetime.isna() & c.df.build_year==0]
|
||||
assets = c.df.index[(c.df.lifetime!=np.inf) & (c.df.build_year==0)]
|
||||
c.df.loc[assets, "build_year"] = baseyear
|
||||
|
||||
# add -baseyear to name
|
||||
@ -159,7 +161,7 @@ def add_power_capacities_installed_before_baseyear(n, grouping_years, costs, bas
|
||||
inv_busmap = {}
|
||||
for k, v in busmap.iteritems():
|
||||
inv_busmap[v] = inv_busmap.get(v, []) + [k]
|
||||
|
||||
|
||||
clustermaps = busmap_s.map(busmap)
|
||||
clustermaps.index = clustermaps.index.astype(int)
|
||||
|
||||
@ -197,10 +199,15 @@ def add_power_capacities_installed_before_baseyear(n, grouping_years, costs, bas
|
||||
capacity = capacity[capacity > snakemake.config['existing_capacities']['threshold_capacity']]
|
||||
|
||||
if generator in ['solar', 'onwind', 'offwind']:
|
||||
|
||||
|
||||
suffix = '-ac' if generator == 'offwind' else ''
|
||||
name_suffix = f' {generator}{suffix}-{baseyear}'
|
||||
|
||||
# to consider electricity grid connection costs or a split between
|
||||
# solar utility and rooftop as well, rather take cost assumptions
|
||||
# from existing network than from the cost database
|
||||
capital_cost = n.generators.loc[n.generators.carrier==generator+suffix, "capital_cost"].mean()
|
||||
|
||||
if 'm' in snakemake.wildcards.clusters:
|
||||
|
||||
for ind in capacity.index:
|
||||
@ -213,14 +220,14 @@ def add_power_capacities_installed_before_baseyear(n, grouping_years, costs, bas
|
||||
|
||||
p_max_pu = n.generators_t.p_max_pu[[i + name_suffix for i in inv_ind]]
|
||||
p_max_pu.columns=[i + name_suffix for i in inv_ind ]
|
||||
|
||||
|
||||
n.madd("Generator",
|
||||
[i + name_suffix for i in inv_ind],
|
||||
bus=ind,
|
||||
carrier=generator,
|
||||
p_nom=capacity[ind] / len(inv_ind), # split among regions in a country
|
||||
marginal_cost=costs.at[generator,'VOM'],
|
||||
capital_cost=costs.at[generator,'fixed'],
|
||||
capital_cost=capital_cost,
|
||||
efficiency=costs.at[generator, 'efficiency'],
|
||||
p_max_pu=p_max_pu,
|
||||
build_year=grouping_year,
|
||||
@ -238,7 +245,7 @@ def add_power_capacities_installed_before_baseyear(n, grouping_years, costs, bas
|
||||
carrier=generator,
|
||||
p_nom=capacity,
|
||||
marginal_cost=costs.at[generator, 'VOM'],
|
||||
capital_cost=costs.at[generator, 'fixed'],
|
||||
capital_cost=capital_cost,
|
||||
efficiency=costs.at[generator, 'efficiency'],
|
||||
p_max_pu=p_max_pu.rename(columns=n.generators.bus),
|
||||
build_year=grouping_year,
|
||||
@ -246,11 +253,14 @@ def add_power_capacities_installed_before_baseyear(n, grouping_years, costs, bas
|
||||
)
|
||||
|
||||
else:
|
||||
bus0 = vars(spatial)[carrier[generator]].nodes
|
||||
if "EU" not in vars(spatial)[carrier[generator]].locations:
|
||||
bus0 = bus0.intersection(capacity.index + " gas")
|
||||
|
||||
n.madd("Link",
|
||||
capacity.index,
|
||||
suffix= " " + generator +"-" + str(grouping_year),
|
||||
bus0="EU " + carrier[generator],
|
||||
bus0=bus0,
|
||||
bus1=capacity.index,
|
||||
bus2="co2 atmosphere",
|
||||
carrier=generator,
|
||||
@ -399,10 +409,11 @@ def add_heating_capacities_installed_before_baseyear(n, baseyear, grouping_years
|
||||
lifetime=costs.at[costs_name, 'lifetime']
|
||||
)
|
||||
|
||||
|
||||
n.madd("Link",
|
||||
nodes[name],
|
||||
suffix= f" {name} gas boiler-{grouping_year}",
|
||||
bus0="EU gas",
|
||||
bus0=spatial.gas.nodes,
|
||||
bus1=nodes[name] + " " + name + " heat",
|
||||
bus2="co2 atmosphere",
|
||||
carrier=name + " gas boiler",
|
||||
@ -417,7 +428,7 @@ def add_heating_capacities_installed_before_baseyear(n, baseyear, grouping_years
|
||||
n.madd("Link",
|
||||
nodes[name],
|
||||
suffix=f" {name} oil boiler-{grouping_year}",
|
||||
bus0="EU oil",
|
||||
bus0=spatial.oil.nodes,
|
||||
bus1=nodes[name] + " " + name + " heat",
|
||||
bus2="co2 atmosphere",
|
||||
carrier=name + " oil boiler",
|
||||
@ -436,17 +447,17 @@ def add_heating_capacities_installed_before_baseyear(n, baseyear, grouping_years
|
||||
threshold = snakemake.config['existing_capacities']['threshold_capacity']
|
||||
n.mremove("Link", [index for index in n.links.index.to_list() if str(grouping_year) in index and n.links.p_nom[index] < threshold])
|
||||
|
||||
|
||||
#%%
|
||||
if __name__ == "__main__":
|
||||
if 'snakemake' not in globals():
|
||||
from helper import mock_snakemake
|
||||
snakemake = mock_snakemake(
|
||||
'add_existing_baseyear',
|
||||
simpl='',
|
||||
clusters=45,
|
||||
clusters="37",
|
||||
lv=1.0,
|
||||
opts='',
|
||||
sector_opts='Co2L0-168H-T-H-B-I-solar+p3-dist1',
|
||||
sector_opts='168H-T-H-B-I-solar+p3-dist1',
|
||||
planning_horizons=2020,
|
||||
)
|
||||
|
||||
@ -459,7 +470,8 @@ if __name__ == "__main__":
|
||||
|
||||
overrides = override_component_attrs(snakemake.input.overrides)
|
||||
n = pypsa.Network(snakemake.input.network, override_component_attrs=overrides)
|
||||
|
||||
# define spatial resolution of carriers
|
||||
spatial = define_spatial(n.buses[n.buses.carrier=="AC"].index, options)
|
||||
add_build_year_to_new_assets(n, baseyear)
|
||||
|
||||
Nyears = n.snapshot_weightings.generators.sum() / 8760.
|
||||
@ -471,7 +483,7 @@ if __name__ == "__main__":
|
||||
snakemake.config['costs']['lifetime']
|
||||
)
|
||||
|
||||
grouping_years=snakemake.config['existing_capacities']['grouping_years']
|
||||
grouping_years = snakemake.config['existing_capacities']['grouping_years']
|
||||
add_power_capacities_installed_before_baseyear(n, grouping_years, costs, baseyear)
|
||||
|
||||
if "H" in opts:
|
||||
|
@ -223,6 +223,26 @@ def plot_map(network, components=["links", "stores", "storage_units", "generator
|
||||
bbox_inches="tight"
|
||||
)
|
||||
|
||||
def group_pipes(df, drop_direction=False):
|
||||
"""Group pipes which connect same buses and return overall capacity.
|
||||
"""
|
||||
if drop_direction:
|
||||
positive_order = df.bus0 < df.bus1
|
||||
df_p = df[positive_order]
|
||||
swap_buses = {"bus0": "bus1", "bus1": "bus0"}
|
||||
df_n = df[~positive_order].rename(columns=swap_buses)
|
||||
df = pd.concat([df_p, df_n])
|
||||
|
||||
# there are pipes for each investment period rename to AC buses name for plotting
|
||||
df.index = df.apply(
|
||||
lambda x: f"H2 pipeline {x.bus0.replace(' H2', '')} -> {x.bus1.replace(' H2', '')}",
|
||||
axis=1
|
||||
)
|
||||
# group pipe lines connecting the same buses and rename them for plotting
|
||||
pipe_capacity = df["p_nom_opt"].groupby(level=0).sum()
|
||||
|
||||
return pipe_capacity
|
||||
|
||||
|
||||
def plot_h2_map(network):
|
||||
|
||||
@ -235,7 +255,7 @@ def plot_h2_map(network):
|
||||
bus_size_factor = 1e5
|
||||
linewidth_factor = 1e4
|
||||
# MW below which not drawn
|
||||
line_lower_threshold = 1e3
|
||||
line_lower_threshold = 1e2
|
||||
|
||||
# Drop non-electric buses so they don't clutter the plot
|
||||
n.buses.drop(n.buses.index[n.buses.carrier != "AC"], inplace=True)
|
||||
@ -246,28 +266,20 @@ def plot_h2_map(network):
|
||||
|
||||
# make a fake MultiIndex so that area is correct for legend
|
||||
bus_sizes.rename(index=lambda x: x.replace(" H2", ""), level=0, inplace=True)
|
||||
|
||||
# drop all links which are not H2 pipelines
|
||||
n.links.drop(n.links.index[~n.links.carrier.str.contains("H2 pipeline")], inplace=True)
|
||||
|
||||
h2_new = n.links.loc[n.links.carrier=="H2 pipeline", "p_nom_opt"]
|
||||
|
||||
h2_new = n.links.loc[n.links.carrier=="H2 pipeline"]
|
||||
h2_retro = n.links.loc[n.links.carrier=='H2 pipeline retrofitted']
|
||||
# sum capacitiy for pipelines from different investment periods
|
||||
h2_new = group_pipes(h2_new)
|
||||
h2_retro = group_pipes(h2_retro, drop_direction=True).reindex(h2_new.index).fillna(0)
|
||||
|
||||
positive_order = h2_retro.bus0 < h2_retro.bus1
|
||||
h2_retro_p = h2_retro[positive_order]
|
||||
swap_buses = {"bus0": "bus1", "bus1": "bus0"}
|
||||
h2_retro_n = h2_retro[~positive_order].rename(columns=swap_buses)
|
||||
h2_retro = pd.concat([h2_retro_p, h2_retro_n])
|
||||
|
||||
h2_retro.index = h2_retro.apply(
|
||||
lambda x: f"H2 pipeline {x.bus0.replace(' H2', '')} -> {x.bus1.replace(' H2', '')}",
|
||||
axis=1
|
||||
)
|
||||
|
||||
h2_retro = h2_retro["p_nom_opt"]
|
||||
|
||||
n.links.rename(index=lambda x: x.split("-2")[0], inplace=True)
|
||||
n.links = n.links.groupby(level=0).first()
|
||||
link_widths_total = (h2_new + h2_retro) / linewidth_factor
|
||||
link_widths_total = link_widths_total.groupby(level=0).sum().reindex(n.links.index).fillna(0.)
|
||||
link_widths_total = link_widths_total.reindex(n.links.index).fillna(0.)
|
||||
link_widths_total[n.links.p_nom_opt < line_lower_threshold] = 0.
|
||||
|
||||
retro = n.links.p_nom_opt.where(n.links.carrier=='H2 pipeline retrofitted', other=0.)
|
||||
@ -281,7 +293,7 @@ def plot_h2_map(network):
|
||||
figsize=(7, 6),
|
||||
subplot_kw={"projection": ccrs.PlateCarree()}
|
||||
)
|
||||
|
||||
|
||||
n.plot(
|
||||
bus_sizes=bus_sizes,
|
||||
bus_colors=snakemake.config['plotting']['tech_colors'],
|
||||
@ -365,7 +377,7 @@ def plot_ch4_map(network):
|
||||
# Drop non-electric buses so they don't clutter the plot
|
||||
n.buses.drop(n.buses.index[n.buses.carrier != "AC"], inplace=True)
|
||||
|
||||
fossil_gas_i = n.generators[n.generators.carrier=="gas"].index
|
||||
fossil_gas_i = n.generators[n.generators.carrier=="gas"].index
|
||||
fossil_gas = n.generators_t.p.loc[:,fossil_gas_i].mul(n.snapshot_weightings.generators, axis=0).sum().groupby(n.generators.loc[fossil_gas_i,"bus"]).sum() / bus_size_factor
|
||||
fossil_gas.rename(index=lambda x: x.replace(" gas", ""), inplace=True)
|
||||
fossil_gas = fossil_gas.reindex(n.buses.index).fillna(0)
|
||||
@ -390,10 +402,10 @@ def plot_ch4_map(network):
|
||||
to_remove = n.links.index[~n.links.carrier.str.contains("gas pipeline")]
|
||||
n.links.drop(to_remove, inplace=True)
|
||||
|
||||
link_widths_rem = n.links.p_nom_opt / linewidth_factor
|
||||
link_widths_rem = n.links.p_nom_opt / linewidth_factor
|
||||
link_widths_rem[n.links.p_nom_opt < line_lower_threshold] = 0.
|
||||
|
||||
link_widths_orig = n.links.p_nom / linewidth_factor
|
||||
link_widths_orig = n.links.p_nom / linewidth_factor
|
||||
link_widths_orig[n.links.p_nom < line_lower_threshold] = 0.
|
||||
|
||||
max_usage = n.links_t.p0.abs().max(axis=0)
|
||||
@ -422,7 +434,7 @@ def plot_ch4_map(network):
|
||||
link_colors='lightgrey',
|
||||
link_widths=link_widths_orig,
|
||||
branch_components=["Link"],
|
||||
ax=ax,
|
||||
ax=ax,
|
||||
**map_opts
|
||||
)
|
||||
|
||||
@ -452,7 +464,7 @@ def plot_ch4_map(network):
|
||||
facecolor='grey'
|
||||
)
|
||||
labels = ["{} TWh".format(s) for s in (10, 100)]
|
||||
|
||||
|
||||
l2 = ax.legend(
|
||||
handles, labels,
|
||||
loc="upper left",
|
||||
@ -462,7 +474,7 @@ def plot_ch4_map(network):
|
||||
title='gas generation',
|
||||
handler_map=make_handler_map_to_scale_circles_as_in(ax)
|
||||
)
|
||||
|
||||
|
||||
ax.add_artist(l2)
|
||||
|
||||
handles = []
|
||||
@ -471,7 +483,7 @@ def plot_ch4_map(network):
|
||||
for s in (50, 10):
|
||||
handles.append(plt.Line2D([0], [0], color="grey", linewidth=s * 1e3 / linewidth_factor))
|
||||
labels.append("{} GW".format(s))
|
||||
|
||||
|
||||
l1_1 = ax.legend(
|
||||
handles, labels,
|
||||
loc="upper left",
|
||||
@ -481,7 +493,7 @@ def plot_ch4_map(network):
|
||||
handletextpad=1.5,
|
||||
title='gas pipeline used capacity'
|
||||
)
|
||||
|
||||
|
||||
ax.add_artist(l1_1)
|
||||
|
||||
fig.savefig(
|
||||
@ -695,11 +707,11 @@ if __name__ == "__main__":
|
||||
snakemake = mock_snakemake(
|
||||
'plot_network',
|
||||
simpl='',
|
||||
clusters=45,
|
||||
lv=1.5,
|
||||
clusters="45",
|
||||
lv=1.0,
|
||||
opts='',
|
||||
sector_opts='Co2L0-168H-T-H-B-I-solar+p3-dist1',
|
||||
planning_horizons=2030,
|
||||
sector_opts='168H-T-H-B-I-A-solar+p3-dist1',
|
||||
planning_horizons="2050",
|
||||
)
|
||||
|
||||
overrides = override_component_attrs(snakemake.input.overrides)
|
||||
|
@ -28,7 +28,7 @@ from types import SimpleNamespace
|
||||
spatial = SimpleNamespace()
|
||||
|
||||
|
||||
def define_spatial(nodes):
|
||||
def define_spatial(nodes, options):
|
||||
"""
|
||||
Namespace for spatial
|
||||
|
||||
@ -38,7 +38,6 @@ def define_spatial(nodes):
|
||||
"""
|
||||
|
||||
global spatial
|
||||
global options
|
||||
|
||||
spatial.nodes = nodes
|
||||
|
||||
@ -73,7 +72,7 @@ def define_spatial(nodes):
|
||||
spatial.co2.vents = ["co2 vent"]
|
||||
|
||||
spatial.co2.df = pd.DataFrame(vars(spatial.co2), index=nodes)
|
||||
|
||||
|
||||
# gas
|
||||
|
||||
spatial.gas = SimpleNamespace()
|
||||
@ -95,6 +94,28 @@ def define_spatial(nodes):
|
||||
|
||||
spatial.gas.df = pd.DataFrame(vars(spatial.gas), index=nodes)
|
||||
|
||||
# oil
|
||||
spatial.oil = SimpleNamespace()
|
||||
spatial.oil.nodes = ["EU oil"]
|
||||
spatial.oil.locations = ["EU"]
|
||||
|
||||
# uranium
|
||||
spatial.uranium = SimpleNamespace()
|
||||
spatial.uranium.nodes = ["EU uranium"]
|
||||
spatial.uranium.locations = ["EU"]
|
||||
|
||||
# coal
|
||||
spatial.coal = SimpleNamespace()
|
||||
spatial.coal.nodes = ["EU coal"]
|
||||
spatial.coal.locations = ["EU"]
|
||||
|
||||
# lignite
|
||||
spatial.lignite = SimpleNamespace()
|
||||
spatial.lignite.nodes = ["EU lignite"]
|
||||
spatial.lignite.locations = ["EU"]
|
||||
|
||||
return spatial
|
||||
|
||||
|
||||
from types import SimpleNamespace
|
||||
spatial = SimpleNamespace()
|
||||
@ -353,7 +374,8 @@ def add_carrier_buses(n, carrier, nodes=None):
|
||||
"""
|
||||
|
||||
if nodes is None:
|
||||
nodes = ["EU " + carrier]
|
||||
nodes = vars(spatial)[carrier].nodes
|
||||
location = vars(spatial)[carrier].locations
|
||||
|
||||
# skip if carrier already exists
|
||||
if carrier in n.carriers.index:
|
||||
@ -366,7 +388,7 @@ def add_carrier_buses(n, carrier, nodes=None):
|
||||
|
||||
n.madd("Bus",
|
||||
nodes,
|
||||
location=nodes.str.replace(" " + carrier, ""),
|
||||
location=location,
|
||||
carrier=carrier
|
||||
)
|
||||
|
||||
@ -807,10 +829,8 @@ def add_generation(n, costs):
|
||||
|
||||
for generator, carrier in conventionals.items():
|
||||
|
||||
if carrier == 'gas':
|
||||
carrier_nodes = spatial.gas.nodes
|
||||
else:
|
||||
carrier_nodes = ["EU " + carrier]
|
||||
|
||||
carrier_nodes = vars(spatial)[carrier].nodes
|
||||
|
||||
add_carrier_buses(n, carrier, carrier_nodes)
|
||||
|
||||
@ -1047,14 +1067,14 @@ def add_storage_and_grids(n, costs):
|
||||
|
||||
cavern_types = snakemake.config["sector"]["hydrogen_underground_storage_locations"]
|
||||
h2_caverns = pd.read_csv(snakemake.input.h2_cavern, index_col=0)
|
||||
|
||||
|
||||
if not h2_caverns.empty and options['hydrogen_underground_storage']:
|
||||
|
||||
h2_caverns = h2_caverns[cavern_types].sum(axis=1)
|
||||
|
||||
# only use sites with at least 2 TWh potential
|
||||
h2_caverns = h2_caverns[h2_caverns > 2]
|
||||
|
||||
|
||||
# convert TWh to MWh
|
||||
h2_caverns = h2_caverns * 1e6
|
||||
|
||||
@ -1072,7 +1092,8 @@ def add_storage_and_grids(n, costs):
|
||||
e_nom_max=h2_caverns.values,
|
||||
e_cyclic=True,
|
||||
carrier="H2 Store",
|
||||
capital_cost=h2_capital_cost
|
||||
capital_cost=h2_capital_cost,
|
||||
lifetime=costs.at["hydrogen storage underground", "lifetime"]
|
||||
)
|
||||
|
||||
# hydrogen stored overground (where not already underground)
|
||||
@ -1122,7 +1143,7 @@ def add_storage_and_grids(n, costs):
|
||||
carrier="gas pipeline",
|
||||
lifetime=costs.at['CH4 (g) pipeline', 'lifetime']
|
||||
)
|
||||
|
||||
|
||||
# remove fossil generators where there is neither
|
||||
# production, LNG terminal, nor entry-point beyond system scope
|
||||
|
||||
@ -1157,9 +1178,9 @@ def add_storage_and_grids(n, costs):
|
||||
|
||||
# apply k_edge_augmentation weighted by length of complement edges
|
||||
k_edge = options.get("gas_network_connectivity_upgrade", 3)
|
||||
augmentation = k_edge_augmentation(G, k_edge, avail=complement_edges.values)
|
||||
augmentation = list(k_edge_augmentation(G, k_edge, avail=complement_edges.values))
|
||||
|
||||
if list(augmentation):
|
||||
if augmentation:
|
||||
|
||||
new_gas_pipes = pd.DataFrame(augmentation, columns=["bus0", "bus1"])
|
||||
new_gas_pipes["length"] = new_gas_pipes.apply(haversine, axis=1)
|
||||
@ -1421,10 +1442,10 @@ def add_land_transport(n, costs):
|
||||
|
||||
if ice_share > 0:
|
||||
|
||||
if "EU oil" not in n.buses.index:
|
||||
n.add("Bus",
|
||||
"EU oil",
|
||||
location="EU",
|
||||
if "oil" not in n.buses.carrier.unique():
|
||||
n.madd("Bus",
|
||||
spatial.oil.nodes,
|
||||
location=spatial.oil.locations,
|
||||
carrier="oil"
|
||||
)
|
||||
|
||||
@ -1433,7 +1454,7 @@ def add_land_transport(n, costs):
|
||||
n.madd("Load",
|
||||
nodes,
|
||||
suffix=" land transport oil",
|
||||
bus="EU oil",
|
||||
bus=spatial.oil.nodes,
|
||||
carrier="land transport oil",
|
||||
p_set=ice_share / ice_efficiency * transport[nodes]
|
||||
)
|
||||
@ -2099,7 +2120,7 @@ def add_industry(n, costs):
|
||||
n.madd("Load",
|
||||
nodes,
|
||||
suffix=" shipping oil",
|
||||
bus="EU oil",
|
||||
bus=spatial.oil.nodes,
|
||||
carrier="shipping oil",
|
||||
p_set=p_set
|
||||
)
|
||||
@ -2113,30 +2134,29 @@ def add_industry(n, costs):
|
||||
p_set=-co2
|
||||
)
|
||||
|
||||
if "EU oil" not in n.buses.index:
|
||||
|
||||
n.add("Bus",
|
||||
"EU oil",
|
||||
location="EU",
|
||||
if "oil" not in n.buses.carrier.unique():
|
||||
n.madd("Bus",
|
||||
spatial.oil.nodes,
|
||||
location=spatial.oil.locations,
|
||||
carrier="oil"
|
||||
)
|
||||
|
||||
if "EU oil Store" not in n.stores.index:
|
||||
if "oil" not in n.stores.carrier.unique():
|
||||
|
||||
#could correct to e.g. 0.001 EUR/kWh * annuity and O&M
|
||||
n.add("Store",
|
||||
"EU oil Store",
|
||||
bus="EU oil",
|
||||
n.madd("Store",
|
||||
[oil_bus + " Store" for oil_bus in spatial.oil.nodes],
|
||||
bus=spatial.oil.nodes,
|
||||
e_nom_extendable=True,
|
||||
e_cyclic=True,
|
||||
carrier="oil",
|
||||
)
|
||||
|
||||
if "EU oil" not in n.generators.index:
|
||||
if "oil" not in n.generators.carrier.unique():
|
||||
|
||||
n.add("Generator",
|
||||
"EU oil",
|
||||
bus="EU oil",
|
||||
n.madd("Generator",
|
||||
spatial.oil.nodes,
|
||||
bus=spatial.oil.nodes,
|
||||
p_nom_extendable=True,
|
||||
carrier="oil",
|
||||
marginal_cost=costs.at["oil", 'fuel']
|
||||
@ -2151,7 +2171,7 @@ def add_industry(n, costs):
|
||||
n.madd("Link",
|
||||
nodes_heat[name] + f" {name} oil boiler",
|
||||
p_nom_extendable=True,
|
||||
bus0="EU oil",
|
||||
bus0=spatial.oil.nodes,
|
||||
bus1=nodes_heat[name] + f" {name} heat",
|
||||
bus2="co2 atmosphere",
|
||||
carrier=f"{name} oil boiler",
|
||||
@ -2164,7 +2184,7 @@ def add_industry(n, costs):
|
||||
n.madd("Link",
|
||||
nodes + " Fischer-Tropsch",
|
||||
bus0=nodes + " H2",
|
||||
bus1="EU oil",
|
||||
bus1=spatial.oil.nodes,
|
||||
bus2=spatial.co2.nodes,
|
||||
carrier="Fischer-Tropsch",
|
||||
efficiency=costs.at["Fischer-Tropsch", 'efficiency'],
|
||||
@ -2174,9 +2194,9 @@ def add_industry(n, costs):
|
||||
lifetime=costs.at['Fischer-Tropsch', 'lifetime']
|
||||
)
|
||||
|
||||
n.add("Load",
|
||||
"naphtha for industry",
|
||||
bus="EU oil",
|
||||
n.madd("Load",
|
||||
["naphtha for industry"],
|
||||
bus=spatial.oil.nodes,
|
||||
carrier="naphtha for industry",
|
||||
p_set=industrial_demand.loc[nodes, "naphtha"].sum() / 8760
|
||||
)
|
||||
@ -2184,9 +2204,9 @@ def add_industry(n, costs):
|
||||
all_aviation = ["total international aviation", "total domestic aviation"]
|
||||
p_set = nodal_energy_totals.loc[nodes, all_aviation].sum(axis=1).sum() * 1e6 / 8760
|
||||
|
||||
n.add("Load",
|
||||
"kerosene for aviation",
|
||||
bus="EU oil",
|
||||
n.madd("Load",
|
||||
["kerosene for aviation"],
|
||||
bus=spatial.oil.nodes,
|
||||
carrier="kerosene for aviation",
|
||||
p_set=p_set
|
||||
)
|
||||
@ -2339,7 +2359,7 @@ def add_agriculture(n, costs):
|
||||
|
||||
n.add("Load",
|
||||
"agriculture machinery oil",
|
||||
bus="EU oil",
|
||||
bus=spatial.oil.nodes,
|
||||
carrier="agriculture machinery oil",
|
||||
p_set=ice_share * machinery_nodal_energy.sum() * 1e6 / 8760
|
||||
)
|
||||
@ -2443,7 +2463,7 @@ if __name__ == "__main__":
|
||||
|
||||
patch_electricity_network(n)
|
||||
|
||||
define_spatial(pop_layout.index)
|
||||
spatial = define_spatial(pop_layout.index, options)
|
||||
|
||||
if snakemake.config["foresight"] == 'myopic':
|
||||
|
||||
|
@ -33,14 +33,14 @@ def _add_land_use_constraint(n):
|
||||
existing = n.generators.loc[n.generators.carrier==carrier,"p_nom"].groupby(n.generators.bus.map(n.buses.location)).sum()
|
||||
existing.index += " " + carrier + "-" + snakemake.wildcards.planning_horizons
|
||||
n.generators.loc[existing.index,"p_nom_max"] -= existing
|
||||
|
||||
|
||||
n.generators.p_nom_max.clip(lower=0, inplace=True)
|
||||
|
||||
|
||||
def _add_land_use_constraint_m(n):
|
||||
# if generators clustering is lower than network clustering, land_use accounting is at generators clusters
|
||||
|
||||
planning_horizons = snakemake.config["scenario"]["planning_horizons"]
|
||||
planning_horizons = snakemake.config["scenario"]["planning_horizons"]
|
||||
grouping_years = snakemake.config["existing_capacities"]["grouping_years"]
|
||||
current_horizon = snakemake.wildcards.planning_horizons
|
||||
|
||||
@ -48,9 +48,9 @@ def _add_land_use_constraint_m(n):
|
||||
|
||||
existing = n.generators.loc[n.generators.carrier==carrier,"p_nom"]
|
||||
ind = list(set([i.split(sep=" ")[0] + ' ' + i.split(sep=" ")[1] for i in existing.index]))
|
||||
|
||||
|
||||
previous_years = [
|
||||
str(y) for y in
|
||||
str(y) for y in
|
||||
planning_horizons + grouping_years
|
||||
if y < int(snakemake.wildcards.planning_horizons)
|
||||
]
|
||||
@ -59,13 +59,13 @@ def _add_land_use_constraint_m(n):
|
||||
ind2 = [i for i in ind if i + " " + carrier + "-" + p_year in existing.index]
|
||||
sel_current = [i + " " + carrier + "-" + current_horizon for i in ind2]
|
||||
sel_p_year = [i + " " + carrier + "-" + p_year for i in ind2]
|
||||
n.generators.loc[sel_current, "p_nom_max"] -= existing.loc[sel_p_year].rename(lambda x: x[:-4] + current_horizon)
|
||||
|
||||
n.generators.loc[sel_current, "p_nom_max"] -= existing.loc[sel_p_year].rename(lambda x: x[:-4] + current_horizon)
|
||||
|
||||
n.generators.p_nom_max.clip(lower=0, inplace=True)
|
||||
|
||||
|
||||
def prepare_network(n, solve_opts=None):
|
||||
|
||||
|
||||
if 'clip_p_max_pu' in solve_opts:
|
||||
for df in (n.generators_t.p_max_pu, n.generators_t.p_min_pu, n.storage_units_t.inflow):
|
||||
df.where(df>solve_opts['clip_p_max_pu'], other=0., inplace=True)
|
||||
@ -185,40 +185,43 @@ def add_chp_constraints(n):
|
||||
|
||||
define_constraints(n, lhs, "<=", 0, 'chplink', 'backpressure')
|
||||
|
||||
def basename(x):
|
||||
return x.split("-2")[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
|
||||
gas_pipes_i = n.links.query("carrier == 'gas pipeline' and p_nom_extendable").index
|
||||
h2_retrofitted_i = n.links.query("carrier == 'H2 pipeline retrofitted' and p_nom_extendable").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"
|
||||
|
||||
pipe_capacity = n.links.loc[gas_pipes_i, 'p_nom'].rename(basename)
|
||||
|
||||
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])
|
||||
)
|
||||
|
||||
lhs.rename(basename, inplace=True)
|
||||
define_constraints(n, lhs, "=", pipe_capacity, 'Link', 'pipe_retrofit')
|
||||
|
||||
|
||||
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
|
||||
@ -226,7 +229,7 @@ def add_co2_sequestration_limit(n, sns):
|
||||
if not "seq" in o: continue
|
||||
limit = float(o[o.find("seq")+3:])
|
||||
break
|
||||
|
||||
|
||||
name = 'co2_sequestration_limit'
|
||||
sense = "<="
|
||||
|
||||
@ -258,7 +261,7 @@ def solve_network(n, config, opts='', **kwargs):
|
||||
|
||||
if cf_solving.get('skip_iterations', False):
|
||||
network_lopf(n, solver_name=solver_name, solver_options=solver_options,
|
||||
extra_functionality=extra_functionality,
|
||||
extra_functionality=extra_functionality,
|
||||
keep_shadowprices=keep_shadowprices, **kwargs)
|
||||
else:
|
||||
ilopf(n, solver_name=solver_name, solver_options=solver_options,
|
||||
@ -277,10 +280,11 @@ if __name__ == "__main__":
|
||||
snakemake = mock_snakemake(
|
||||
'solve_network',
|
||||
simpl='',
|
||||
clusters=48,
|
||||
opts="",
|
||||
clusters="37",
|
||||
lv=1.0,
|
||||
sector_opts='Co2L0-168H-T-H-B-I-solar3-dist1',
|
||||
planning_horizons=2050,
|
||||
sector_opts='168H-T-H-B-I-A-solar+p3-dist1',
|
||||
planning_horizons="2030",
|
||||
)
|
||||
|
||||
logging.basicConfig(filename=snakemake.log.python,
|
||||
|
Loading…
Reference in New Issue
Block a user