2020-07-07 16:20:51 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
2024-02-19 15:21:48 +00:00
|
|
|
# SPDX-FileCopyrightText: : 2020-2024 The PyPSA-Eur Authors
|
2023-03-06 17:49:23 +00:00
|
|
|
#
|
|
|
|
# SPDX-License-Identifier: MIT
|
2023-03-09 11:45:43 +00:00
|
|
|
"""
|
|
|
|
Adds existing power and heat generation capacities for initial planning
|
|
|
|
horizon.
|
|
|
|
"""
|
|
|
|
|
2020-07-07 16:20:51 +00:00
|
|
|
import logging
|
|
|
|
from types import SimpleNamespace
|
|
|
|
|
2023-03-09 07:36:18 +00:00
|
|
|
import country_converter as coco
|
2020-07-07 16:20:51 +00:00
|
|
|
import numpy as np
|
2024-01-19 09:47:58 +00:00
|
|
|
import pandas as pd
|
2024-04-11 06:41:59 +00:00
|
|
|
import powerplantmatching as pm
|
2020-07-07 16:20:51 +00:00
|
|
|
import pypsa
|
|
|
|
import xarray as xr
|
2024-02-12 10:53:20 +00:00
|
|
|
from _helpers import (
|
|
|
|
configure_logging,
|
|
|
|
set_scenario_config,
|
2024-02-17 16:16:28 +00:00
|
|
|
update_config_from_wildcards,
|
2024-02-12 10:54:13 +00:00
|
|
|
)
|
2023-05-03 11:24:57 +00:00
|
|
|
from add_electricity import sanitize_carriers
|
2024-08-07 15:55:29 +00:00
|
|
|
from definitions.heat_sector import HeatSector
|
|
|
|
from definitions.heat_system import HeatSystem
|
|
|
|
from definitions.heat_system_type import HeatSystemType
|
2024-08-08 14:46:06 +00:00
|
|
|
from prepare_sector_network import cluster_heat_buses, define_spatial, prepare_costs
|
2020-07-18 09:22:30 +00:00
|
|
|
|
2024-01-19 09:47:58 +00:00
|
|
|
logger = logging.getLogger(__name__)
|
2023-03-09 07:36:18 +00:00
|
|
|
cc = coco.CountryConverter()
|
2024-01-19 09:47:58 +00:00
|
|
|
idx = pd.IndexSlice
|
2022-03-18 09:18:24 +00:00
|
|
|
spatial = SimpleNamespace()
|
2020-08-10 18:30:29 +00:00
|
|
|
|
2023-03-06 08:27:45 +00:00
|
|
|
|
2020-08-12 16:08:01 +00:00
|
|
|
def add_build_year_to_new_assets(n, baseyear):
|
|
|
|
"""
|
|
|
|
Parameters
|
|
|
|
----------
|
2021-07-01 18:09:04 +00:00
|
|
|
n : pypsa.Network
|
|
|
|
baseyear : int
|
|
|
|
year in which optimized assets are built
|
2020-08-12 16:08:01 +00:00
|
|
|
"""
|
2021-07-01 18:09:04 +00:00
|
|
|
# Give assets with lifetimes and no build year the build year baseyear
|
2020-08-12 16:08:01 +00:00
|
|
|
for c in n.iterate_components(["Link", "Generator", "Store"]):
|
2022-01-07 10:38:25 +00:00
|
|
|
assets = c.df.index[(c.df.lifetime != np.inf) & (c.df.build_year == 0)]
|
2020-08-12 16:08:01 +00:00
|
|
|
c.df.loc[assets, "build_year"] = baseyear
|
|
|
|
|
2021-07-01 18:09:04 +00:00
|
|
|
# add -baseyear to name
|
2020-08-12 16:08:01 +00:00
|
|
|
rename = pd.Series(c.df.index, c.df.index)
|
2023-10-08 09:20:36 +00:00
|
|
|
rename[assets] += f"-{str(baseyear)}"
|
2020-08-12 16:08:01 +00:00
|
|
|
c.df.rename(index=rename, inplace=True)
|
|
|
|
|
2021-07-01 18:09:04 +00:00
|
|
|
# rename time-dependent
|
|
|
|
selection = n.component_attrs[c.name].type.str.contains(
|
|
|
|
"series"
|
|
|
|
) & n.component_attrs[c.name].status.str.contains("Input")
|
|
|
|
for attr in n.component_attrs[c.name].index[selection]:
|
2024-01-31 16:10:08 +00:00
|
|
|
c.pnl[attr] = c.pnl[attr].rename(columns=rename)
|
2020-08-12 16:08:01 +00:00
|
|
|
|
2021-07-01 18:09:04 +00:00
|
|
|
|
2024-03-06 09:26:23 +00:00
|
|
|
def add_existing_renewables(df_agg, costs):
|
2020-08-11 09:09:39 +00:00
|
|
|
"""
|
|
|
|
Append existing renewables to the df_agg pd.DataFrame with the conventional
|
|
|
|
power plants.
|
|
|
|
"""
|
2024-06-04 15:02:56 +00:00
|
|
|
tech_map = {"solar": "PV", "onwind": "Onshore", "offwind-ac": "Offshore"}
|
2021-09-27 09:16:12 +00:00
|
|
|
|
2024-05-21 13:28:24 +00:00
|
|
|
countries = snakemake.config["countries"] # noqa: F841
|
2024-04-11 06:41:59 +00:00
|
|
|
irena = pm.data.IRENASTAT().powerplant.convert_country_to_alpha2()
|
|
|
|
irena = irena.query("Country in @countries")
|
|
|
|
irena = irena.groupby(["Technology", "Country", "Year"]).Capacity.sum()
|
2021-07-01 18:09:04 +00:00
|
|
|
|
2024-04-11 06:41:59 +00:00
|
|
|
irena = irena.unstack().reset_index()
|
|
|
|
|
|
|
|
for carrier, tech in tech_map.items():
|
|
|
|
df = (
|
|
|
|
irena[irena.Technology.str.contains(tech)]
|
|
|
|
.drop(columns=["Technology"])
|
|
|
|
.set_index("Country")
|
|
|
|
)
|
2020-08-10 18:30:29 +00:00
|
|
|
df.columns = df.columns.astype(int)
|
|
|
|
|
|
|
|
# calculate yearly differences
|
|
|
|
df.insert(loc=0, value=0.0, column="1999")
|
2021-07-01 18:09:04 +00:00
|
|
|
df = df.diff(axis=1).drop("1999", axis=1).clip(lower=0)
|
2020-08-10 18:30:29 +00:00
|
|
|
|
2021-07-01 18:09:04 +00:00
|
|
|
# distribute capacities among nodes according to capacity factor
|
|
|
|
# weighting with nodal_fraction
|
2020-08-17 10:08:49 +00:00
|
|
|
elec_buses = n.buses.index[n.buses.carrier == "AC"].union(
|
|
|
|
n.buses.index[n.buses.carrier == "DC"]
|
|
|
|
)
|
2021-07-01 18:09:04 +00:00
|
|
|
nodal_fraction = pd.Series(0.0, elec_buses)
|
2020-08-10 18:30:29 +00:00
|
|
|
|
2021-07-01 18:09:04 +00:00
|
|
|
for country in n.buses.loc[elec_buses, "country"].unique():
|
2020-08-12 16:08:01 +00:00
|
|
|
gens = n.generators.index[
|
|
|
|
(n.generators.index.str[:2] == country)
|
|
|
|
& (n.generators.carrier == carrier)
|
|
|
|
]
|
2020-08-10 18:30:29 +00:00
|
|
|
cfs = n.generators_t.p_max_pu[gens].mean()
|
2021-07-01 18:09:04 +00:00
|
|
|
cfs_key = cfs / cfs.sum()
|
2023-12-08 09:30:08 +00:00
|
|
|
nodal_fraction.loc[n.generators.loc[gens, "bus"]] = cfs_key.groupby(
|
|
|
|
n.generators.loc[gens, "bus"]
|
|
|
|
).sum()
|
2020-08-10 18:30:29 +00:00
|
|
|
|
2021-07-01 18:09:04 +00:00
|
|
|
nodal_df = df.loc[n.buses.loc[elec_buses, "country"]]
|
2020-08-10 18:30:29 +00:00
|
|
|
nodal_df.index = elec_buses
|
2021-07-01 18:09:04 +00:00
|
|
|
nodal_df = nodal_df.multiply(nodal_fraction, axis=0)
|
2020-08-10 18:30:29 +00:00
|
|
|
|
|
|
|
for year in nodal_df.columns:
|
|
|
|
for node in nodal_df.index:
|
2024-04-11 06:41:59 +00:00
|
|
|
name = f"{node}-{carrier}-{year}"
|
2021-07-01 18:09:04 +00:00
|
|
|
capacity = nodal_df.loc[node, year]
|
2020-08-10 18:30:29 +00:00
|
|
|
if capacity > 0.0:
|
2024-06-04 15:02:56 +00:00
|
|
|
cost_key = carrier.split("-")[0]
|
2024-04-11 06:41:59 +00:00
|
|
|
df_agg.at[name, "Fueltype"] = carrier
|
2021-07-01 18:09:04 +00:00
|
|
|
df_agg.at[name, "Capacity"] = capacity
|
|
|
|
df_agg.at[name, "DateIn"] = year
|
2024-06-04 15:02:56 +00:00
|
|
|
df_agg.at[name, "lifetime"] = costs.at[cost_key, "lifetime"]
|
2024-04-11 06:41:59 +00:00
|
|
|
df_agg.at[name, "DateOut"] = (
|
2024-06-04 15:02:56 +00:00
|
|
|
year + costs.at[cost_key, "lifetime"] - 1
|
2024-04-11 06:41:59 +00:00
|
|
|
)
|
2024-09-13 13:37:01 +00:00
|
|
|
df_agg.at[name, "bus"] = node
|
2021-07-01 18:09:04 +00:00
|
|
|
|
2020-08-10 18:30:29 +00:00
|
|
|
|
2020-08-12 16:08:01 +00:00
|
|
|
def add_power_capacities_installed_before_baseyear(n, grouping_years, costs, baseyear):
|
2020-07-07 16:20:51 +00:00
|
|
|
"""
|
|
|
|
Parameters
|
|
|
|
----------
|
2021-07-01 18:09:04 +00:00
|
|
|
n : pypsa.Network
|
2021-09-27 09:16:12 +00:00
|
|
|
grouping_years :
|
2021-07-01 18:09:04 +00:00
|
|
|
intervals to group existing capacities
|
2021-09-27 09:16:12 +00:00
|
|
|
costs :
|
2021-07-01 18:09:04 +00:00
|
|
|
to read lifetime to estimate YearDecomissioning
|
|
|
|
baseyear : int
|
2020-07-07 16:20:51 +00:00
|
|
|
"""
|
2023-02-24 13:42:51 +00:00
|
|
|
logger.debug(
|
2024-09-13 13:37:01 +00:00
|
|
|
f"Adding power capacities installed before {baseyear} from"
|
|
|
|
" powerplants_s_{clusters}.csv"
|
2023-02-24 13:42:51 +00:00
|
|
|
)
|
2020-07-30 06:22:27 +00:00
|
|
|
|
2020-08-10 18:30:29 +00:00
|
|
|
df_agg = pd.read_csv(snakemake.input.powerplants, index_col=0)
|
2020-07-07 16:20:51 +00:00
|
|
|
|
2021-07-01 18:09:04 +00:00
|
|
|
rename_fuel = {
|
|
|
|
"Hard Coal": "coal",
|
|
|
|
"Lignite": "lignite",
|
|
|
|
"Nuclear": "nuclear",
|
|
|
|
"Oil": "oil",
|
|
|
|
"OCGT": "OCGT",
|
|
|
|
"CCGT": "CCGT",
|
2022-08-08 11:52:41 +00:00
|
|
|
"Bioenergy": "urban central solid biomass CHP",
|
2021-07-01 18:09:04 +00:00
|
|
|
}
|
|
|
|
|
2023-07-26 05:24:12 +00:00
|
|
|
# Replace Fueltype "Natural Gas" with the respective technology (OCGT or CCGT)
|
|
|
|
df_agg.loc[df_agg["Fueltype"] == "Natural Gas", "Fueltype"] = df_agg.loc[
|
|
|
|
df_agg["Fueltype"] == "Natural Gas", "Technology"
|
|
|
|
]
|
|
|
|
|
2021-07-01 18:09:04 +00:00
|
|
|
fueltype_to_drop = [
|
|
|
|
"Hydro",
|
|
|
|
"Wind",
|
|
|
|
"Solar",
|
|
|
|
"Geothermal",
|
|
|
|
"Waste",
|
|
|
|
"Other",
|
|
|
|
"CCGT, Thermal",
|
|
|
|
]
|
|
|
|
|
|
|
|
technology_to_drop = ["Pv", "Storage Technologies"]
|
|
|
|
|
2024-04-11 08:29:30 +00:00
|
|
|
# drop unused fueltypes and technologies
|
2021-07-01 18:09:04 +00:00
|
|
|
df_agg.drop(df_agg.index[df_agg.Fueltype.isin(fueltype_to_drop)], inplace=True)
|
|
|
|
df_agg.drop(df_agg.index[df_agg.Technology.isin(technology_to_drop)], inplace=True)
|
2020-07-07 16:20:51 +00:00
|
|
|
df_agg.Fueltype = df_agg.Fueltype.map(rename_fuel)
|
|
|
|
|
2022-08-09 13:30:58 +00:00
|
|
|
# Intermediate fix for DateIn & DateOut
|
|
|
|
# Fill missing DateIn
|
2022-08-09 19:30:45 +00:00
|
|
|
biomass_i = df_agg.loc[df_agg.Fueltype == "urban central solid biomass CHP"].index
|
|
|
|
mean = df_agg.loc[biomass_i, "DateIn"].mean()
|
|
|
|
df_agg.loc[biomass_i, "DateIn"] = df_agg.loc[biomass_i, "DateIn"].fillna(int(mean))
|
2022-08-09 13:30:58 +00:00
|
|
|
# Fill missing DateOut
|
2023-03-06 16:18:07 +00:00
|
|
|
dateout = (
|
|
|
|
df_agg.loc[biomass_i, "DateIn"]
|
2023-06-15 16:52:25 +00:00
|
|
|
+ snakemake.params.costs["fill_values"]["lifetime"]
|
2023-03-06 16:19:23 +00:00
|
|
|
)
|
2022-08-09 19:30:45 +00:00
|
|
|
df_agg.loc[biomass_i, "DateOut"] = df_agg.loc[biomass_i, "DateOut"].fillna(dateout)
|
2022-08-09 13:30:58 +00:00
|
|
|
|
2021-07-01 18:09:04 +00:00
|
|
|
# include renewables in df_agg
|
2024-03-06 09:26:23 +00:00
|
|
|
add_existing_renewables(df_agg, costs)
|
|
|
|
|
|
|
|
# drop assets which are already phased out / decommissioned
|
|
|
|
phased_out = df_agg[df_agg["DateOut"] < baseyear].index
|
|
|
|
df_agg.drop(phased_out, inplace=True)
|
2020-08-10 18:30:29 +00:00
|
|
|
|
2024-06-13 12:51:32 +00:00
|
|
|
newer_assets = (df_agg.DateIn > max(grouping_years)).sum()
|
|
|
|
if newer_assets:
|
2024-04-11 06:54:15 +00:00
|
|
|
logger.warning(
|
2024-06-13 12:51:32 +00:00
|
|
|
f"There are {newer_assets} assets with build year "
|
|
|
|
f"after last power grouping year {max(grouping_years)}. "
|
2024-04-11 06:54:15 +00:00
|
|
|
"These assets are dropped and not considered."
|
|
|
|
"Consider to redefine the grouping years to keep them."
|
|
|
|
)
|
2024-06-13 12:51:32 +00:00
|
|
|
to_drop = df_agg[df_agg.DateIn > max(grouping_years)].index
|
2024-04-11 06:54:15 +00:00
|
|
|
df_agg.drop(to_drop, inplace=True)
|
2024-04-11 12:21:43 +00:00
|
|
|
|
2021-07-01 18:09:04 +00:00
|
|
|
df_agg["grouping_year"] = np.take(
|
2024-06-13 12:51:32 +00:00
|
|
|
grouping_years, np.digitize(df_agg.DateIn, grouping_years, right=True)
|
2021-07-01 18:09:04 +00:00
|
|
|
)
|
|
|
|
|
2024-03-05 11:00:50 +00:00
|
|
|
# calculate (adjusted) remaining lifetime before phase-out (+1 because assuming
|
|
|
|
# phase out date at the end of the year)
|
|
|
|
df_agg["lifetime"] = df_agg.DateOut - df_agg["grouping_year"] + 1
|
|
|
|
|
2021-07-01 18:09:04 +00:00
|
|
|
df = df_agg.pivot_table(
|
|
|
|
index=["grouping_year", "Fueltype"],
|
2024-09-13 13:37:01 +00:00
|
|
|
columns="bus",
|
2021-07-01 18:09:04 +00:00
|
|
|
values="Capacity",
|
|
|
|
aggfunc="sum",
|
|
|
|
)
|
|
|
|
|
2022-08-08 13:28:04 +00:00
|
|
|
lifetime = df_agg.pivot_table(
|
|
|
|
index=["grouping_year", "Fueltype"],
|
2024-09-13 13:37:01 +00:00
|
|
|
columns="bus",
|
2022-08-08 13:28:04 +00:00
|
|
|
values="lifetime",
|
|
|
|
aggfunc="mean", # currently taken mean for clustering lifetimes
|
|
|
|
)
|
|
|
|
|
2021-07-01 18:09:04 +00:00
|
|
|
carrier = {
|
|
|
|
"OCGT": "gas",
|
|
|
|
"CCGT": "gas",
|
|
|
|
"coal": "coal",
|
|
|
|
"oil": "oil",
|
|
|
|
"lignite": "lignite",
|
2022-08-08 13:28:04 +00:00
|
|
|
"nuclear": "uranium",
|
|
|
|
"urban central solid biomass CHP": "biomass",
|
2021-07-01 18:09:04 +00:00
|
|
|
}
|
2020-07-30 06:22:27 +00:00
|
|
|
|
2020-08-10 18:30:29 +00:00
|
|
|
for grouping_year, generator in df.index:
|
2021-07-01 18:09:04 +00:00
|
|
|
# capacity is the capacity in MW at each node for this
|
2020-08-10 18:30:29 +00:00
|
|
|
capacity = df.loc[grouping_year, generator]
|
|
|
|
capacity = capacity[~capacity.isna()]
|
|
|
|
capacity = capacity[
|
2023-06-15 16:52:25 +00:00
|
|
|
capacity > snakemake.params.existing_capacities["threshold_capacity"]
|
2023-03-06 08:27:45 +00:00
|
|
|
]
|
2022-08-01 13:24:07 +00:00
|
|
|
suffix = "-ac" if generator == "offwind" else ""
|
2022-08-08 13:28:04 +00:00
|
|
|
name_suffix = f" {generator}{suffix}-{grouping_year}"
|
2024-04-11 08:29:30 +00:00
|
|
|
name_suffix_by = f" {generator}{suffix}-{baseyear}"
|
2022-08-01 13:24:07 +00:00
|
|
|
asset_i = capacity.index + name_suffix
|
2024-06-04 15:02:56 +00:00
|
|
|
if generator in ["solar", "onwind", "offwind-ac"]:
|
|
|
|
cost_key = generator.split("-")[0]
|
2022-01-07 11:09:02 +00:00
|
|
|
# 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
|
2022-03-17 17:15:14 +00:00
|
|
|
capital_cost = n.generators.loc[
|
|
|
|
n.generators.carrier == generator + suffix, "capital_cost"
|
|
|
|
].mean()
|
2022-11-28 15:56:42 +00:00
|
|
|
marginal_cost = n.generators.loc[
|
|
|
|
n.generators.carrier == generator + suffix, "marginal_cost"
|
|
|
|
].mean()
|
2022-08-01 13:24:07 +00:00
|
|
|
# check if assets are already in network (e.g. for 2020)
|
|
|
|
already_build = n.generators.index.intersection(asset_i)
|
|
|
|
new_build = asset_i.difference(n.generators.index)
|
|
|
|
|
|
|
|
# this is for the year 2020
|
|
|
|
if not already_build.empty:
|
2024-06-07 15:59:43 +00:00
|
|
|
n.generators.loc[already_build, "p_nom"] = n.generators.loc[
|
|
|
|
already_build, "p_nom_min"
|
|
|
|
] = capacity.loc[already_build.str.replace(name_suffix, "")].values
|
2022-08-01 13:24:07 +00:00
|
|
|
new_capacity = capacity.loc[new_build.str.replace(name_suffix, "")]
|
2022-01-07 11:09:02 +00:00
|
|
|
|
2024-09-13 13:37:01 +00:00
|
|
|
p_max_pu = n.generators_t.p_max_pu[capacity.index + name_suffix_by]
|
2021-07-06 07:55:41 +00:00
|
|
|
|
2024-09-13 13:37:01 +00:00
|
|
|
if not new_build.empty:
|
|
|
|
n.madd(
|
|
|
|
"Generator",
|
|
|
|
new_capacity.index,
|
|
|
|
suffix=name_suffix,
|
|
|
|
bus=new_capacity.index,
|
|
|
|
carrier=generator,
|
|
|
|
p_nom=new_capacity,
|
|
|
|
marginal_cost=marginal_cost,
|
|
|
|
capital_cost=capital_cost,
|
|
|
|
efficiency=costs.at[cost_key, "efficiency"],
|
|
|
|
p_max_pu=p_max_pu.rename(columns=n.generators.bus),
|
|
|
|
build_year=grouping_year,
|
|
|
|
lifetime=costs.at[cost_key, "lifetime"],
|
|
|
|
)
|
2021-07-06 07:55:41 +00:00
|
|
|
|
2020-07-18 09:22:30 +00:00
|
|
|
else:
|
2022-03-18 12:46:40 +00:00
|
|
|
bus0 = vars(spatial)[carrier[generator]].nodes
|
|
|
|
if "EU" not in vars(spatial)[carrier[generator]].locations:
|
2023-10-24 14:46:58 +00:00
|
|
|
bus0 = bus0.intersection(capacity.index + " " + carrier[generator])
|
2021-07-01 18:09:04 +00:00
|
|
|
|
2023-08-23 11:27:40 +00:00
|
|
|
# check for missing bus
|
|
|
|
missing_bus = pd.Index(bus0).difference(n.buses.index)
|
|
|
|
if not missing_bus.empty:
|
|
|
|
logger.info(f"add buses {bus0}")
|
|
|
|
n.madd(
|
|
|
|
"Bus",
|
|
|
|
bus0,
|
|
|
|
carrier=generator,
|
|
|
|
location=vars(spatial)[carrier[generator]].locations,
|
|
|
|
unit="MWh_el",
|
|
|
|
)
|
2021-07-01 18:09:04 +00:00
|
|
|
|
2022-08-01 13:24:07 +00:00
|
|
|
already_build = n.links.index.intersection(asset_i)
|
|
|
|
new_build = asset_i.difference(n.links.index)
|
2022-08-08 13:28:04 +00:00
|
|
|
lifetime_assets = lifetime.loc[grouping_year, generator].dropna()
|
2022-08-01 13:24:07 +00:00
|
|
|
|
|
|
|
# this is for the year 2020
|
|
|
|
if not already_build.empty:
|
|
|
|
n.links.loc[already_build, "p_nom_min"] = capacity.loc[
|
|
|
|
already_build.str.replace(name_suffix, "")
|
|
|
|
].values
|
|
|
|
|
|
|
|
if not new_build.empty:
|
|
|
|
new_capacity = capacity.loc[new_build.str.replace(name_suffix, "")]
|
|
|
|
|
2022-08-08 13:28:04 +00:00
|
|
|
if generator != "urban central solid biomass CHP":
|
|
|
|
n.madd(
|
|
|
|
"Link",
|
|
|
|
new_capacity.index,
|
|
|
|
suffix=name_suffix,
|
|
|
|
bus0=bus0,
|
|
|
|
bus1=new_capacity.index,
|
|
|
|
bus2="co2 atmosphere",
|
|
|
|
carrier=generator,
|
|
|
|
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=new_capacity / costs.at[generator, "efficiency"],
|
|
|
|
efficiency=costs.at[generator, "efficiency"],
|
|
|
|
efficiency2=costs.at[carrier[generator], "CO2 intensity"],
|
|
|
|
build_year=grouping_year,
|
|
|
|
lifetime=lifetime_assets.loc[new_capacity.index],
|
|
|
|
)
|
|
|
|
else:
|
|
|
|
key = "central solid biomass CHP"
|
2024-04-11 17:14:45 +00:00
|
|
|
central_heat = n.buses.query(
|
|
|
|
"carrier == 'urban central heat'"
|
|
|
|
).location.unique()
|
|
|
|
heat_buses = new_capacity.index.map(
|
|
|
|
lambda i: i + " urban central heat" if i in central_heat else ""
|
|
|
|
)
|
|
|
|
|
2022-08-08 13:28:04 +00:00
|
|
|
n.madd(
|
|
|
|
"Link",
|
|
|
|
new_capacity.index,
|
|
|
|
suffix=name_suffix,
|
|
|
|
bus0=spatial.biomass.df.loc[new_capacity.index]["nodes"].values,
|
|
|
|
bus1=new_capacity.index,
|
2024-04-11 17:14:45 +00:00
|
|
|
bus2=heat_buses,
|
2022-08-08 13:28:04 +00:00
|
|
|
carrier=generator,
|
|
|
|
p_nom=new_capacity / costs.at[key, "efficiency"],
|
|
|
|
capital_cost=costs.at[key, "fixed"]
|
|
|
|
* costs.at[key, "efficiency"],
|
|
|
|
marginal_cost=costs.at[key, "VOM"],
|
|
|
|
efficiency=costs.at[key, "efficiency"],
|
|
|
|
build_year=grouping_year,
|
|
|
|
efficiency2=costs.at[key, "efficiency-heat"],
|
|
|
|
lifetime=lifetime_assets.loc[new_capacity.index],
|
|
|
|
)
|
2023-02-22 16:44:05 +00:00
|
|
|
# check if existing capacities are larger than technical potential
|
|
|
|
existing_large = n.generators[
|
|
|
|
n.generators["p_nom_min"] > n.generators["p_nom_max"]
|
|
|
|
].index
|
|
|
|
if len(existing_large):
|
|
|
|
logger.warning(
|
|
|
|
f"Existing capacities larger than technical potential for {existing_large},\
|
|
|
|
adjust technical potential to existing capacities"
|
|
|
|
)
|
|
|
|
n.generators.loc[existing_large, "p_nom_max"] = n.generators.loc[
|
|
|
|
existing_large, "p_nom_min"
|
|
|
|
]
|
2023-03-06 08:27:45 +00:00
|
|
|
|
|
|
|
|
2024-09-09 16:19:18 +00:00
|
|
|
def get_efficiency(heat_system, carrier, nodes, heating_efficiencies, costs):
|
|
|
|
"""
|
2024-09-09 16:28:07 +00:00
|
|
|
Computes the heating system efficiency based on the sector and carrier
|
|
|
|
type.
|
2024-09-09 16:19:18 +00:00
|
|
|
|
|
|
|
Parameters:
|
|
|
|
-----------
|
|
|
|
heat_system : object
|
|
|
|
carrier : str
|
|
|
|
The type of fuel or energy carrier (e.g., 'gas', 'oil').
|
|
|
|
nodes : pandas.Series
|
|
|
|
A pandas Series containing node information used to match the heating efficiency data.
|
|
|
|
heating_efficiencies : dict
|
|
|
|
A dictionary containing efficiency values for different carriers and sectors.
|
|
|
|
costs : pandas.DataFrame
|
|
|
|
A DataFrame containing boiler cost and efficiency data for different heating systems.
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
--------
|
|
|
|
efficiency : pandas.Series or float
|
|
|
|
A pandas Series mapping the efficiencies based on nodes for residential and services sectors, or a single
|
|
|
|
efficiency value for other heating systems (e.g., urban central).
|
|
|
|
|
|
|
|
Notes:
|
|
|
|
------
|
|
|
|
- For residential and services sectors, efficiency is mapped based on the nodes.
|
|
|
|
- For other sectors, the default boiler efficiency is retrieved from the `costs` database.
|
|
|
|
"""
|
2024-09-11 13:09:51 +00:00
|
|
|
|
2024-09-11 13:06:17 +00:00
|
|
|
if heat_system.value == "urban central":
|
|
|
|
boiler_costs_name = getattr(heat_system, f"{carrier}_boiler_costs_name")
|
|
|
|
efficiency = costs.at[boiler_costs_name, "efficiency"]
|
|
|
|
elif heat_system.sector.value == "residential":
|
2024-09-09 16:19:18 +00:00
|
|
|
key = f"{carrier} residential space efficiency"
|
|
|
|
efficiency = nodes.str[:2].map(heating_efficiencies[key])
|
|
|
|
elif heat_system.sector.value == "services":
|
|
|
|
key = f"{carrier} services space efficiency"
|
|
|
|
efficiency = nodes.str[:2].map(heating_efficiencies[key])
|
|
|
|
else:
|
2024-09-11 13:06:17 +00:00
|
|
|
logger.warning(f"{heat_system} not defined.")
|
2024-09-09 16:19:18 +00:00
|
|
|
|
|
|
|
return efficiency
|
|
|
|
|
2024-09-09 16:28:07 +00:00
|
|
|
|
2020-07-18 09:22:30 +00:00
|
|
|
def add_heating_capacities_installed_before_baseyear(
|
2024-08-05 14:11:18 +00:00
|
|
|
n: pypsa.Network,
|
|
|
|
baseyear: int,
|
|
|
|
grouping_years: list,
|
2024-07-25 12:33:14 +00:00
|
|
|
cop: dict,
|
|
|
|
time_dep_hp_cop: bool,
|
2024-08-05 14:11:18 +00:00
|
|
|
costs: pd.DataFrame,
|
|
|
|
default_lifetime: int,
|
2024-07-25 12:33:37 +00:00
|
|
|
existing_heating: pd.DataFrame,
|
2020-07-18 09:22:30 +00:00
|
|
|
):
|
2020-07-07 16:20:51 +00:00
|
|
|
"""
|
|
|
|
Parameters
|
|
|
|
----------
|
2021-07-01 18:09:04 +00:00
|
|
|
n : pypsa.Network
|
|
|
|
baseyear : last year covered in the existing capacities database
|
2020-07-18 09:22:30 +00:00
|
|
|
grouping_years : intervals to group existing capacities
|
2021-07-01 18:09:04 +00:00
|
|
|
linear decommissioning of heating capacities from 2020 to 2045 is
|
|
|
|
currently assumed heating capacities split between residential and
|
|
|
|
services proportional to heating load in both 50% capacities
|
2024-04-11 08:29:30 +00:00
|
|
|
in rural buses 50% in urban buses
|
2024-08-05 09:27:52 +00:00
|
|
|
cop: xr.DataArray
|
|
|
|
DataArray with time-dependent coefficients of performance (COPs) heat pumps. Coordinates are heat sources (see config), heat system types (see :file:`scripts/enums/HeatSystemType.py`), nodes and snapshots.
|
2024-07-25 12:33:14 +00:00
|
|
|
time_dep_hp_cop: bool
|
|
|
|
If True, time-dependent (dynamic) COPs are used for heat pumps
|
2020-07-30 06:22:27 +00:00
|
|
|
"""
|
2023-02-24 13:42:51 +00:00
|
|
|
logger.debug(f"Adding heating capacities installed before {baseyear}")
|
2020-07-30 06:22:27 +00:00
|
|
|
|
2024-07-29 16:29:27 +00:00
|
|
|
for heat_system in existing_heating.columns.get_level_values(0).unique():
|
2024-08-02 15:27:16 +00:00
|
|
|
heat_system = HeatSystem(heat_system)
|
2020-07-30 06:22:27 +00:00
|
|
|
|
2024-07-29 16:33:49 +00:00
|
|
|
nodes = pd.Index(
|
|
|
|
n.buses.location[n.buses.index.str.contains(f"{heat_system} heat")]
|
|
|
|
)
|
2024-02-23 14:27:17 +00:00
|
|
|
|
2024-08-05 09:42:42 +00:00
|
|
|
if (not heat_system == HeatSystem.URBAN_CENTRAL) and options[
|
|
|
|
"electricity_distribution_grid"
|
|
|
|
]:
|
2024-02-23 14:23:55 +00:00
|
|
|
nodes_elec = nodes + " low voltage"
|
2020-07-07 16:20:51 +00:00
|
|
|
else:
|
2024-02-23 14:23:55 +00:00
|
|
|
nodes_elec = nodes
|
2021-07-01 18:09:04 +00:00
|
|
|
|
2024-08-05 09:42:42 +00:00
|
|
|
too_large_grouping_years = [
|
|
|
|
gy for gy in grouping_years if gy >= int(baseyear)
|
|
|
|
]
|
2024-08-05 09:27:52 +00:00
|
|
|
if too_large_grouping_years:
|
|
|
|
logger.warning(
|
|
|
|
f"Grouping years >= baseyear are ignored. Dropping {too_large_grouping_years}."
|
|
|
|
)
|
|
|
|
valid_grouping_years = pd.Series(
|
|
|
|
[
|
|
|
|
int(grouping_year)
|
|
|
|
for grouping_year in grouping_years
|
|
|
|
if int(grouping_year) + default_lifetime > int(baseyear)
|
|
|
|
and int(grouping_year) < int(baseyear)
|
|
|
|
]
|
2024-06-13 12:51:32 +00:00
|
|
|
)
|
2024-04-11 12:21:43 +00:00
|
|
|
|
2024-08-05 09:27:52 +00:00
|
|
|
assert valid_grouping_years.is_monotonic_increasing
|
2024-06-13 12:51:32 +00:00
|
|
|
|
2024-08-05 09:27:52 +00:00
|
|
|
# get number of years of each interval
|
|
|
|
_years = valid_grouping_years.diff()
|
|
|
|
# Fill NA from .diff() with value for the first interval
|
|
|
|
_years[0] = valid_grouping_years[0] - baseyear + default_lifetime
|
|
|
|
# Installation is assumed to be linear for the past
|
|
|
|
ratios = _years / _years.sum()
|
2022-08-09 19:30:45 +00:00
|
|
|
|
2024-03-14 12:44:15 +00:00
|
|
|
for ratio, grouping_year in zip(ratios, valid_grouping_years):
|
2024-08-05 09:27:52 +00:00
|
|
|
# Add heat pumps
|
2024-08-05 09:42:42 +00:00
|
|
|
for heat_source in snakemake.params.heat_pump_sources[
|
|
|
|
heat_system.system_type.value
|
|
|
|
]:
|
2024-08-05 09:27:52 +00:00
|
|
|
costs_name = heat_system.heat_pump_costs_name(heat_source)
|
|
|
|
|
|
|
|
efficiency = (
|
2024-08-05 09:42:42 +00:00
|
|
|
cop.sel(
|
|
|
|
heat_system=heat_system.system_type.value,
|
|
|
|
heat_source=heat_source,
|
|
|
|
name=nodes,
|
|
|
|
)
|
2024-08-05 09:27:52 +00:00
|
|
|
.to_pandas()
|
|
|
|
.reindex(index=n.snapshots)
|
2024-08-05 14:11:18 +00:00
|
|
|
if time_dep_hp_cop
|
2024-08-05 09:27:52 +00:00
|
|
|
else costs.at[costs_name, "efficiency"]
|
|
|
|
)
|
2020-08-11 09:09:39 +00:00
|
|
|
|
2024-08-05 09:27:52 +00:00
|
|
|
n.madd(
|
|
|
|
"Link",
|
|
|
|
nodes,
|
|
|
|
suffix=f" {heat_system} {heat_source} heat pump-{grouping_year}",
|
|
|
|
bus0=nodes_elec,
|
|
|
|
bus1=nodes + " " + heat_system.value + " heat",
|
|
|
|
carrier=f"{heat_system} {heat_source} heat pump",
|
|
|
|
efficiency=efficiency,
|
|
|
|
capital_cost=costs.at[costs_name, "efficiency"]
|
|
|
|
* costs.at[costs_name, "fixed"],
|
|
|
|
p_nom=existing_heating.loc[
|
|
|
|
nodes, (heat_system.value, f"{heat_source} heat pump")
|
|
|
|
]
|
|
|
|
* ratio
|
|
|
|
/ costs.at[costs_name, "efficiency"],
|
|
|
|
build_year=int(grouping_year),
|
|
|
|
lifetime=costs.at[costs_name, "lifetime"],
|
|
|
|
)
|
2020-07-30 06:22:27 +00:00
|
|
|
|
|
|
|
# add resistive heater, gas boilers and oil boilers
|
2020-07-18 09:22:30 +00:00
|
|
|
n.madd(
|
|
|
|
"Link",
|
2024-01-19 17:42:49 +00:00
|
|
|
nodes,
|
2024-07-29 16:29:27 +00:00
|
|
|
suffix=f" {heat_system} resistive heater-{grouping_year}",
|
2024-02-23 14:23:55 +00:00
|
|
|
bus0=nodes_elec,
|
2024-08-02 15:27:16 +00:00
|
|
|
bus1=nodes + " " + heat_system.value + " heat",
|
2024-08-05 09:27:52 +00:00
|
|
|
carrier=heat_system.value + " resistive heater",
|
2024-08-05 09:42:42 +00:00
|
|
|
efficiency=costs.at[
|
|
|
|
heat_system.resistive_heater_costs_name, "efficiency"
|
|
|
|
],
|
2023-10-08 09:20:36 +00:00
|
|
|
capital_cost=(
|
2024-08-05 09:27:52 +00:00
|
|
|
costs.at[heat_system.resistive_heater_costs_name, "efficiency"]
|
|
|
|
* costs.at[heat_system.resistive_heater_costs_name, "fixed"]
|
2023-10-08 09:20:36 +00:00
|
|
|
),
|
|
|
|
p_nom=(
|
2024-08-02 15:27:16 +00:00
|
|
|
existing_heating.loc[nodes, (heat_system.value, "resistive heater")]
|
2023-10-08 09:20:36 +00:00
|
|
|
* ratio
|
2024-08-05 09:27:52 +00:00
|
|
|
/ costs.at[heat_system.resistive_heater_costs_name, "efficiency"]
|
2023-10-08 09:20:36 +00:00
|
|
|
),
|
2021-07-01 18:09:04 +00:00
|
|
|
build_year=int(grouping_year),
|
2024-08-05 09:27:52 +00:00
|
|
|
lifetime=costs.at[heat_system.resistive_heater_costs_name, "lifetime"],
|
2021-07-01 18:09:04 +00:00
|
|
|
)
|
2020-07-30 06:22:27 +00:00
|
|
|
|
2024-09-09 16:28:07 +00:00
|
|
|
efficiency = get_efficiency(
|
|
|
|
heat_system, "gas", nodes, heating_efficiencies, costs
|
|
|
|
)
|
2024-09-02 17:32:13 +00:00
|
|
|
|
2020-07-18 09:22:30 +00:00
|
|
|
n.madd(
|
|
|
|
"Link",
|
2024-01-19 17:42:49 +00:00
|
|
|
nodes,
|
2024-09-12 15:53:25 +00:00
|
|
|
suffix=f" {heat_system} gas boiler-{grouping_year}",
|
2024-02-02 12:19:08 +00:00
|
|
|
bus0="EU gas" if "EU gas" in spatial.gas.nodes else nodes + " gas",
|
2024-08-05 10:07:24 +00:00
|
|
|
bus1=nodes + " " + heat_system.value + " heat",
|
2021-07-01 18:09:04 +00:00
|
|
|
bus2="co2 atmosphere",
|
2024-08-05 09:27:52 +00:00
|
|
|
carrier=heat_system.value + " gas boiler",
|
2024-09-02 17:32:13 +00:00
|
|
|
efficiency=efficiency,
|
2021-07-01 18:09:04 +00:00
|
|
|
efficiency2=costs.at["gas", "CO2 intensity"],
|
2023-10-08 09:20:36 +00:00
|
|
|
capital_cost=(
|
2024-08-05 09:27:52 +00:00
|
|
|
costs.at[heat_system.gas_boiler_costs_name, "efficiency"]
|
|
|
|
* costs.at[heat_system.gas_boiler_costs_name, "fixed"]
|
2023-10-08 09:20:36 +00:00
|
|
|
),
|
|
|
|
p_nom=(
|
2024-08-05 09:27:52 +00:00
|
|
|
existing_heating.loc[nodes, (heat_system.value, "gas boiler")]
|
2023-10-08 09:20:36 +00:00
|
|
|
* ratio
|
2024-08-05 09:27:52 +00:00
|
|
|
/ costs.at[heat_system.gas_boiler_costs_name, "efficiency"]
|
2023-10-08 09:20:36 +00:00
|
|
|
),
|
2021-07-01 18:09:04 +00:00
|
|
|
build_year=int(grouping_year),
|
2024-08-05 09:27:52 +00:00
|
|
|
lifetime=costs.at[heat_system.gas_boiler_costs_name, "lifetime"],
|
2021-07-01 18:09:04 +00:00
|
|
|
)
|
2021-09-27 09:16:12 +00:00
|
|
|
|
2024-09-09 16:28:07 +00:00
|
|
|
efficiency = get_efficiency(
|
|
|
|
heat_system, "oil", nodes, heating_efficiencies, costs
|
|
|
|
)
|
2024-09-02 17:32:13 +00:00
|
|
|
|
2020-07-18 09:22:30 +00:00
|
|
|
n.madd(
|
|
|
|
"Link",
|
2024-01-19 17:42:49 +00:00
|
|
|
nodes,
|
2024-07-29 16:29:27 +00:00
|
|
|
suffix=f" {heat_system} oil boiler-{grouping_year}",
|
2022-03-21 08:14:15 +00:00
|
|
|
bus0=spatial.oil.nodes,
|
2024-08-05 10:07:24 +00:00
|
|
|
bus1=nodes + " " + heat_system.value + " heat",
|
2021-07-01 18:09:04 +00:00
|
|
|
bus2="co2 atmosphere",
|
2024-08-05 09:27:52 +00:00
|
|
|
carrier=heat_system.value + " oil boiler",
|
2024-09-02 17:32:13 +00:00
|
|
|
efficiency=efficiency,
|
2021-07-01 18:09:04 +00:00
|
|
|
efficiency2=costs.at["oil", "CO2 intensity"],
|
2024-08-05 09:27:52 +00:00
|
|
|
capital_cost=costs.at[heat_system.oil_boiler_costs_name, "efficiency"]
|
|
|
|
* costs.at[heat_system.oil_boiler_costs_name, "fixed"],
|
2024-01-19 17:42:49 +00:00
|
|
|
p_nom=(
|
2024-08-05 09:27:52 +00:00
|
|
|
existing_heating.loc[nodes, (heat_system.value, "oil boiler")]
|
2024-01-19 17:42:49 +00:00
|
|
|
* ratio
|
2024-08-05 09:27:52 +00:00
|
|
|
/ costs.at[heat_system.oil_boiler_costs_name, "efficiency"]
|
2024-01-19 17:42:49 +00:00
|
|
|
),
|
2021-07-01 18:09:04 +00:00
|
|
|
build_year=int(grouping_year),
|
2024-08-05 09:42:42 +00:00
|
|
|
lifetime=costs.at[
|
|
|
|
f"{heat_system.central_or_decentral} gas boiler", "lifetime"
|
|
|
|
],
|
2021-07-01 18:09:04 +00:00
|
|
|
)
|
2020-07-30 06:22:27 +00:00
|
|
|
|
2020-07-18 09:22:30 +00:00
|
|
|
# delete links with p_nom=nan corresponding to extra nodes in country
|
2020-08-10 18:30:29 +00:00
|
|
|
n.mremove(
|
|
|
|
"Link",
|
|
|
|
[
|
|
|
|
index
|
|
|
|
for index in n.links.index.to_list()
|
|
|
|
if str(grouping_year) in index and np.isnan(n.links.p_nom[index])
|
2023-03-06 08:27:45 +00:00
|
|
|
],
|
|
|
|
)
|
2020-07-30 06:22:27 +00:00
|
|
|
|
2022-08-09 19:30:45 +00:00
|
|
|
# delete links with capacities below threshold
|
2023-06-15 16:52:25 +00:00
|
|
|
threshold = snakemake.params.existing_capacities["threshold_capacity"]
|
2021-07-01 18:09:04 +00:00
|
|
|
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
|
2023-03-06 08:27:45 +00:00
|
|
|
],
|
2021-07-01 18:09:04 +00:00
|
|
|
)
|
2020-07-07 16:20:51 +00:00
|
|
|
|
2023-03-06 08:27:45 +00:00
|
|
|
|
2024-09-12 15:53:25 +00:00
|
|
|
def set_defaults(n):
|
|
|
|
"""
|
|
|
|
Set default values for missing values in the network.
|
|
|
|
|
|
|
|
Parameters:
|
|
|
|
n (pypsa.Network): The network object.
|
|
|
|
Returns:
|
|
|
|
None
|
|
|
|
"""
|
|
|
|
if "Link" in n.components:
|
|
|
|
if "reversed" in n.links.columns:
|
|
|
|
# Replace NA values with default value False
|
|
|
|
n.links.loc[n.links.reversed.isna(), "reversed"] = False
|
|
|
|
n.links.reversed = n.links.reversed.astype(bool)
|
|
|
|
|
|
|
|
|
2024-02-23 14:23:55 +00:00
|
|
|
# %%
|
2020-07-07 16:20:51 +00:00
|
|
|
if __name__ == "__main__":
|
|
|
|
if "snakemake" not in globals():
|
2023-03-06 18:09:45 +00:00
|
|
|
from _helpers import mock_snakemake
|
2023-03-06 08:27:45 +00:00
|
|
|
|
2021-07-01 18:09:04 +00:00
|
|
|
snakemake = mock_snakemake(
|
|
|
|
"add_existing_baseyear",
|
2024-09-11 13:06:17 +00:00
|
|
|
configfiles="config/test/config.myopic.yaml",
|
|
|
|
clusters="5",
|
|
|
|
ll="v1.5",
|
2021-09-27 09:16:12 +00:00
|
|
|
opts="",
|
2024-09-09 16:10:59 +00:00
|
|
|
sector_opts="",
|
2024-09-11 13:06:17 +00:00
|
|
|
planning_horizons=2030,
|
2020-07-07 16:20:51 +00:00
|
|
|
)
|
|
|
|
|
2024-02-12 10:53:20 +00:00
|
|
|
configure_logging(snakemake)
|
|
|
|
set_scenario_config(snakemake)
|
2020-07-07 16:20:51 +00:00
|
|
|
|
2024-02-17 10:57:16 +00:00
|
|
|
update_config_from_wildcards(snakemake.config, snakemake.wildcards)
|
2022-07-20 09:35:12 +00:00
|
|
|
|
2023-06-15 16:52:25 +00:00
|
|
|
options = snakemake.params.sector
|
2020-07-07 16:20:51 +00:00
|
|
|
|
2023-06-15 16:52:25 +00:00
|
|
|
baseyear = snakemake.params.baseyear
|
2020-07-30 06:22:27 +00:00
|
|
|
|
2023-07-13 20:31:55 +00:00
|
|
|
n = pypsa.Network(snakemake.input.network)
|
2020-07-30 06:22:27 +00:00
|
|
|
|
2022-03-18 09:18:24 +00:00
|
|
|
# define spatial resolution of carriers
|
2022-03-18 12:46:40 +00:00
|
|
|
spatial = define_spatial(n.buses[n.buses.carrier == "AC"].index, options)
|
2020-08-12 16:08:01 +00:00
|
|
|
add_build_year_to_new_assets(n, baseyear)
|
|
|
|
|
2021-07-01 18:09:04 +00:00
|
|
|
Nyears = n.snapshot_weightings.generators.sum() / 8760.0
|
|
|
|
costs = prepare_costs(
|
|
|
|
snakemake.input.costs,
|
2023-06-15 16:52:25 +00:00
|
|
|
snakemake.params.costs,
|
2021-07-01 18:09:04 +00:00
|
|
|
Nyears,
|
|
|
|
)
|
2020-07-07 16:20:51 +00:00
|
|
|
|
2023-06-15 16:52:25 +00:00
|
|
|
grouping_years_power = snakemake.params.existing_capacities["grouping_years_power"]
|
|
|
|
grouping_years_heat = snakemake.params.existing_capacities["grouping_years_heat"]
|
2022-08-02 07:18:06 +00:00
|
|
|
add_power_capacities_installed_before_baseyear(
|
|
|
|
n, grouping_years_power, costs, baseyear
|
|
|
|
)
|
2020-07-30 06:22:27 +00:00
|
|
|
|
2024-02-17 10:57:16 +00:00
|
|
|
if options["heating"]:
|
2024-07-25 12:33:14 +00:00
|
|
|
|
2024-09-09 16:10:59 +00:00
|
|
|
# one could use baseyear here instead (but dangerous if no data)
|
2024-09-11 07:35:50 +00:00
|
|
|
fn = snakemake.input.heating_efficiencies
|
|
|
|
year = int(snakemake.params["energy_totals_year"])
|
|
|
|
heating_efficiencies = pd.read_csv(fn, index_col=[1, 0]).loc[year]
|
2024-09-02 17:32:13 +00:00
|
|
|
|
2022-08-02 07:18:06 +00:00
|
|
|
add_heating_capacities_installed_before_baseyear(
|
2024-07-25 12:33:14 +00:00
|
|
|
n=n,
|
|
|
|
baseyear=baseyear,
|
|
|
|
grouping_years=grouping_years_heat,
|
2024-08-05 09:27:52 +00:00
|
|
|
cop=xr.open_dataarray(snakemake.input.cop_profiles),
|
2024-07-25 12:33:14 +00:00
|
|
|
time_dep_hp_cop=options["time_dep_hp_cop"],
|
|
|
|
costs=costs,
|
|
|
|
default_lifetime=snakemake.params.existing_capacities[
|
|
|
|
"default_heating_lifetime"
|
|
|
|
],
|
|
|
|
existing_heating=pd.read_csv(
|
|
|
|
snakemake.input.existing_heating_distribution,
|
|
|
|
header=[0, 1],
|
|
|
|
index_col=0,
|
|
|
|
),
|
2022-08-02 07:18:06 +00:00
|
|
|
)
|
2020-07-07 16:20:51 +00:00
|
|
|
|
2024-09-12 15:53:25 +00:00
|
|
|
# Set defaults for missing missing values
|
|
|
|
set_defaults(n)
|
|
|
|
|
2023-01-30 10:55:40 +00:00
|
|
|
if options.get("cluster_heat_buses", False):
|
|
|
|
cluster_heat_buses(n)
|
|
|
|
|
2022-06-30 06:42:18 +00:00
|
|
|
n.meta = dict(snakemake.config, **dict(wildcards=dict(snakemake.wildcards)))
|
2022-08-01 16:15:35 +00:00
|
|
|
|
2023-05-03 11:24:57 +00:00
|
|
|
sanitize_carriers(n, snakemake.config)
|
2022-08-01 16:15:35 +00:00
|
|
|
|
2020-07-30 06:22:27 +00:00
|
|
|
n.export_to_netcdf(snakemake.output[0])
|