Merge branch 'master' into scenario-management
This commit is contained in:
commit
3f3f752e8f
@ -16,6 +16,8 @@ Upcoming Release
|
|||||||
|
|
||||||
* For industry distribution, use EPRTR as fallback if ETS data is not available.
|
* For industry distribution, use EPRTR as fallback if ETS data is not available.
|
||||||
|
|
||||||
|
* The minimum capacity for renewable generators when using the myopic option has been fixed.
|
||||||
|
|
||||||
PyPSA-Eur 0.8.1 (27th July 2023)
|
PyPSA-Eur 0.8.1 (27th July 2023)
|
||||||
================================
|
================================
|
||||||
|
|
||||||
|
@ -107,6 +107,8 @@ rule plot_summary:
|
|||||||
countries=config_provider("countries"),
|
countries=config_provider("countries"),
|
||||||
planning_horizons=config_provider("scenario", "planning_horizons"),
|
planning_horizons=config_provider("scenario", "planning_horizons"),
|
||||||
sector_opts=config_provider("scenario", "sector_opts"),
|
sector_opts=config_provider("scenario", "sector_opts"),
|
||||||
|
emissions_scope=config_provider("energy", "emissions"),
|
||||||
|
eurostat_report_year=config_provider("energy", "eurostat_report_year"),
|
||||||
plotting=config_provider("plotting"),
|
plotting=config_provider("plotting"),
|
||||||
RDIR=RDIR,
|
RDIR=RDIR,
|
||||||
input:
|
input:
|
||||||
@ -114,6 +116,7 @@ rule plot_summary:
|
|||||||
energy=RESULTS + "csvs/energy.csv",
|
energy=RESULTS + "csvs/energy.csv",
|
||||||
balances=RESULTS + "csvs/supply_energy.csv",
|
balances=RESULTS + "csvs/supply_energy.csv",
|
||||||
eurostat=input_eurostat,
|
eurostat=input_eurostat,
|
||||||
|
co2="data/eea/UNFCCC_v23.csv",
|
||||||
output:
|
output:
|
||||||
costs=RESULTS + "graphs/costs.pdf",
|
costs=RESULTS + "graphs/costs.pdf",
|
||||||
energy=RESULTS + "graphs/energy.pdf",
|
energy=RESULTS + "graphs/energy.pdf",
|
||||||
|
@ -165,7 +165,7 @@ def sanitize_carriers(n, config):
|
|||||||
nice_names = (
|
nice_names = (
|
||||||
pd.Series(config["plotting"]["nice_names"])
|
pd.Series(config["plotting"]["nice_names"])
|
||||||
.reindex(carrier_i)
|
.reindex(carrier_i)
|
||||||
.fillna(carrier_i.to_series().str.title())
|
.fillna(carrier_i.to_series())
|
||||||
)
|
)
|
||||||
n.carriers["nice_name"] = n.carriers.nice_name.where(
|
n.carriers["nice_name"] = n.carriers.nice_name.where(
|
||||||
n.carriers.nice_name != "", nice_names
|
n.carriers.nice_name != "", nice_names
|
||||||
|
@ -435,15 +435,20 @@ def add_heating_capacities_installed_before_baseyear(
|
|||||||
|
|
||||||
# split existing capacities between residential and services
|
# split existing capacities between residential and services
|
||||||
# proportional to energy demand
|
# proportional to energy demand
|
||||||
|
p_set_sum = n.loads_t.p_set.sum()
|
||||||
ratio_residential = pd.Series(
|
ratio_residential = pd.Series(
|
||||||
[
|
[
|
||||||
(
|
(
|
||||||
n.loads_t.p_set.sum()[f"{node} residential rural heat"]
|
p_set_sum[f"{node} residential rural heat"]
|
||||||
/ (
|
/ (
|
||||||
n.loads_t.p_set.sum()[f"{node} residential rural heat"]
|
p_set_sum[f"{node} residential rural heat"]
|
||||||
+ n.loads_t.p_set.sum()[f"{node} services rural heat"]
|
+ p_set_sum[f"{node} services rural heat"]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
# if rural heating demand for one of the nodes doesn't exist,
|
||||||
|
# then columns were dropped before and heating demand share should be 0.0
|
||||||
|
if all(f"{node} {service} rural heat" in p_set_sum.index for service in ["residential", "services"])
|
||||||
|
else 0.
|
||||||
for node in nodal_df.index
|
for node in nodal_df.index
|
||||||
],
|
],
|
||||||
index=nodal_df.index,
|
index=nodal_df.index,
|
||||||
|
@ -17,6 +17,8 @@ import geopandas as gpd
|
|||||||
import pandas as pd
|
import pandas as pd
|
||||||
from packaging.version import Version, parse
|
from packaging.version import Version, parse
|
||||||
|
|
||||||
|
import country_converter as coco
|
||||||
|
cc = coco.CountryConverter()
|
||||||
|
|
||||||
def locate_missing_industrial_sites(df):
|
def locate_missing_industrial_sites(df):
|
||||||
"""
|
"""
|
||||||
@ -93,6 +95,17 @@ def prepare_hotmaps_database(regions):
|
|||||||
gdf.rename(columns={"index_right": "bus"}, inplace=True)
|
gdf.rename(columns={"index_right": "bus"}, inplace=True)
|
||||||
gdf["country"] = gdf.bus.str[:2]
|
gdf["country"] = gdf.bus.str[:2]
|
||||||
|
|
||||||
|
# the .sjoin can lead to duplicates if a geom is in two overlapping regions
|
||||||
|
if gdf.index.duplicated().any():
|
||||||
|
# get all duplicated entries
|
||||||
|
duplicated_i = gdf.index[gdf.index.duplicated()]
|
||||||
|
# convert from raw data country name to iso-2-code
|
||||||
|
code = cc.convert(gdf.loc[duplicated_i, "Country"], to="iso2")
|
||||||
|
# screen out malformed country allocation
|
||||||
|
gdf_filtered = gdf.loc[duplicated_i].query("country == @code")
|
||||||
|
# concat not duplicated and filtered gdf
|
||||||
|
gdf = pd.concat([gdf.drop(duplicated_i), gdf_filtered])
|
||||||
|
|
||||||
return gdf
|
return gdf
|
||||||
|
|
||||||
|
|
||||||
|
@ -711,5 +711,5 @@ if __name__ == "__main__":
|
|||||||
if snakemake.params.foresight == "myopic":
|
if snakemake.params.foresight == "myopic":
|
||||||
cumulative_cost = calculate_cumulative_cost()
|
cumulative_cost = calculate_cumulative_cost()
|
||||||
cumulative_cost.to_csv(
|
cumulative_cost.to_csv(
|
||||||
"results/" + snakemake.params.RDIR + "/csvs/cumulative_cost.csv"
|
"results/" + snakemake.params.RDIR + "csvs/cumulative_cost.csv"
|
||||||
)
|
)
|
||||||
|
@ -387,6 +387,9 @@ def historical_emissions(countries):
|
|||||||
countries.remove("GB")
|
countries.remove("GB")
|
||||||
countries.append("UK")
|
countries.append("UK")
|
||||||
|
|
||||||
|
# remove countries which are not included in eea historical emission dataset
|
||||||
|
countries_to_remove = {"AL", "BA", "ME", "MK", "RS"}
|
||||||
|
countries = list(set(countries) - countries_to_remove)
|
||||||
year = np.arange(1990, 2018).tolist()
|
year = np.arange(1990, 2018).tolist()
|
||||||
|
|
||||||
idx = pd.IndexSlice
|
idx = pd.IndexSlice
|
||||||
@ -457,9 +460,20 @@ def plot_carbon_budget_distribution(input_eurostat):
|
|||||||
ax1.set_ylim([0, 5])
|
ax1.set_ylim([0, 5])
|
||||||
ax1.set_xlim([1990, snakemake.params.planning_horizons[-1] + 1])
|
ax1.set_xlim([1990, snakemake.params.planning_horizons[-1] + 1])
|
||||||
|
|
||||||
path_cb = "results/" + snakemake.params.RDIR + "/csvs/"
|
path_cb = "results/" + snakemake.params.RDIR + "csvs/"
|
||||||
countries = snakemake.params.countries
|
countries = snakemake.params.countries
|
||||||
e_1990 = co2_emissions_year(countries, input_eurostat, opts, year=1990)
|
emissions_scope = snakemake.params.emissions_scope
|
||||||
|
report_year = snakemake.params.eurostat_report_year
|
||||||
|
input_co2 = snakemake.input.co2
|
||||||
|
e_1990 = co2_emissions_year(
|
||||||
|
countries,
|
||||||
|
input_eurostat,
|
||||||
|
opts,
|
||||||
|
emissions_scope,
|
||||||
|
report_year,
|
||||||
|
input_co2,
|
||||||
|
year=1990,
|
||||||
|
)
|
||||||
CO2_CAP = pd.read_csv(path_cb + "carbon_budget_distribution.csv", index_col=0)
|
CO2_CAP = pd.read_csv(path_cb + "carbon_budget_distribution.csv", index_col=0)
|
||||||
|
|
||||||
ax1.plot(e_1990 * CO2_CAP[o], linewidth=3, color="dodgerblue", label=None)
|
ax1.plot(e_1990 * CO2_CAP[o], linewidth=3, color="dodgerblue", label=None)
|
||||||
@ -535,7 +549,7 @@ def plot_carbon_budget_distribution(input_eurostat):
|
|||||||
fancybox=True, fontsize=18, loc=(0.01, 0.01), facecolor="white", frameon=True
|
fancybox=True, fontsize=18, loc=(0.01, 0.01), facecolor="white", frameon=True
|
||||||
)
|
)
|
||||||
|
|
||||||
path_cb_plot = "results/" + snakemake.params.RDIR + "/graphs/"
|
path_cb_plot = "results/" + snakemake.params.RDIR + "graphs/"
|
||||||
plt.savefig(path_cb_plot + "carbon_budget_plot.pdf", dpi=300)
|
plt.savefig(path_cb_plot + "carbon_budget_plot.pdf", dpi=300)
|
||||||
|
|
||||||
|
|
||||||
|
@ -196,17 +196,15 @@ def get(item, investment_year=None):
|
|||||||
|
|
||||||
|
|
||||||
def co2_emissions_year(
|
def co2_emissions_year(
|
||||||
countries, input_eurostat, opts, emissions_scope, report_year, year
|
countries, input_eurostat, opts, emissions_scope, report_year, input_co2, year
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Calculate CO2 emissions in one specific year (e.g. 1990 or 2018).
|
Calculate CO2 emissions in one specific year (e.g. 1990 or 2018).
|
||||||
"""
|
"""
|
||||||
emissions_scope = snakemake.params.energy["emissions"]
|
eea_co2 = build_eea_co2(input_co2, year, emissions_scope)
|
||||||
eea_co2 = build_eea_co2(snakemake.input.co2, year, emissions_scope)
|
|
||||||
|
|
||||||
# TODO: read Eurostat data from year > 2014
|
# TODO: read Eurostat data from year > 2014
|
||||||
# this only affects the estimation of CO2 emissions for BA, RS, AL, ME, MK
|
# this only affects the estimation of CO2 emissions for BA, RS, AL, ME, MK
|
||||||
report_year = snakemake.params.energy["eurostat_report_year"]
|
|
||||||
if year > 2014:
|
if year > 2014:
|
||||||
eurostat_co2 = build_eurostat_co2(
|
eurostat_co2 = build_eurostat_co2(
|
||||||
input_eurostat, countries, report_year, year=2014
|
input_eurostat, countries, report_year, year=2014
|
||||||
@ -227,7 +225,7 @@ def co2_emissions_year(
|
|||||||
|
|
||||||
|
|
||||||
# TODO: move to own rule with sector-opts wildcard?
|
# TODO: move to own rule with sector-opts wildcard?
|
||||||
def build_carbon_budget(o, input_eurostat, fn, emissions_scope, report_year):
|
def build_carbon_budget(o, input_eurostat, fn, emissions_scope, report_year, input_co2):
|
||||||
"""
|
"""
|
||||||
Distribute carbon budget following beta or exponential transition path.
|
Distribute carbon budget following beta or exponential transition path.
|
||||||
"""
|
"""
|
||||||
@ -245,12 +243,24 @@ def build_carbon_budget(o, input_eurostat, fn, emissions_scope, report_year):
|
|||||||
countries = snakemake.params.countries
|
countries = snakemake.params.countries
|
||||||
|
|
||||||
e_1990 = co2_emissions_year(
|
e_1990 = co2_emissions_year(
|
||||||
countries, input_eurostat, opts, emissions_scope, report_year, year=1990
|
countries,
|
||||||
|
input_eurostat,
|
||||||
|
opts,
|
||||||
|
emissions_scope,
|
||||||
|
report_year,
|
||||||
|
input_co2,
|
||||||
|
year=1990,
|
||||||
)
|
)
|
||||||
|
|
||||||
# emissions at the beginning of the path (last year available 2018)
|
# emissions at the beginning of the path (last year available 2018)
|
||||||
e_0 = co2_emissions_year(
|
e_0 = co2_emissions_year(
|
||||||
countries, input_eurostat, opts, emissions_scope, report_year, year=2018
|
countries,
|
||||||
|
input_eurostat,
|
||||||
|
opts,
|
||||||
|
emissions_scope,
|
||||||
|
report_year,
|
||||||
|
input_co2,
|
||||||
|
year=2018,
|
||||||
)
|
)
|
||||||
|
|
||||||
planning_horizons = snakemake.params.planning_horizons
|
planning_horizons = snakemake.params.planning_horizons
|
||||||
@ -3398,12 +3408,18 @@ if __name__ == "__main__":
|
|||||||
if "cb" not in o:
|
if "cb" not in o:
|
||||||
continue
|
continue
|
||||||
limit_type = "carbon budget"
|
limit_type = "carbon budget"
|
||||||
fn = "results/" + snakemake.params.RDIR + "/csvs/carbon_budget_distribution.csv"
|
fn = "results/" + snakemake.params.RDIR + "csvs/carbon_budget_distribution.csv"
|
||||||
if not os.path.exists(fn):
|
if not os.path.exists(fn):
|
||||||
emissions_scope = snakemake.params.emissions_scope
|
emissions_scope = snakemake.params.emissions_scope
|
||||||
report_year = snakemake.params.eurostat_report_year
|
report_year = snakemake.params.eurostat_report_year
|
||||||
|
input_co2 = snakemake.input.co2
|
||||||
build_carbon_budget(
|
build_carbon_budget(
|
||||||
o, snakemake.input.eurostat, fn, emissions_scope, report_year
|
o,
|
||||||
|
snakemake.input.eurostat,
|
||||||
|
fn,
|
||||||
|
emissions_scope,
|
||||||
|
report_year,
|
||||||
|
input_co2,
|
||||||
)
|
)
|
||||||
co2_cap = pd.read_csv(fn, index_col=0).squeeze()
|
co2_cap = pd.read_csv(fn, index_col=0).squeeze()
|
||||||
limit = co2_cap.loc[investment_year]
|
limit = co2_cap.loc[investment_year]
|
||||||
|
@ -55,6 +55,9 @@ def _add_land_use_constraint(n):
|
|||||||
# warning: this will miss existing offwind which is not classed AC-DC and has carrier 'offwind'
|
# warning: this will miss existing offwind which is not classed AC-DC and has carrier 'offwind'
|
||||||
|
|
||||||
for carrier in ["solar", "onwind", "offwind-ac", "offwind-dc"]:
|
for carrier in ["solar", "onwind", "offwind-ac", "offwind-dc"]:
|
||||||
|
extendable_i = (n.generators.carrier == carrier) & n.generators.p_nom_extendable
|
||||||
|
n.generators.loc[extendable_i, "p_nom_min"] = 0
|
||||||
|
|
||||||
ext_i = (n.generators.carrier == carrier) & ~n.generators.p_nom_extendable
|
ext_i = (n.generators.carrier == carrier) & ~n.generators.p_nom_extendable
|
||||||
existing = (
|
existing = (
|
||||||
n.generators.loc[ext_i, "p_nom"]
|
n.generators.loc[ext_i, "p_nom"]
|
||||||
@ -71,7 +74,7 @@ def _add_land_use_constraint(n):
|
|||||||
if len(existing_large):
|
if len(existing_large):
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"Existing capacities larger than technical potential for {existing_large},\
|
f"Existing capacities larger than technical potential for {existing_large},\
|
||||||
adjust technical potential to existing capacities"
|
adjust technical potential to existing capacities"
|
||||||
)
|
)
|
||||||
n.generators.loc[existing_large, "p_nom_max"] = n.generators.loc[
|
n.generators.loc[existing_large, "p_nom_max"] = n.generators.loc[
|
||||||
existing_large, "p_nom_min"
|
existing_large, "p_nom_min"
|
||||||
|
Loading…
Reference in New Issue
Block a user