Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Addition of unsustainable biomass potentials #1139

Merged
merged 33 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
16301e5
add columns to potential df defined by difference to eurostat
cpschau May 13, 2024
c50fc9f
add network components
cpschau May 16, 2024
f6b2557
add unsustainable bioliquids
cpschau May 17, 2024
4a81993
replaced stores by generators, still infeasible
cpschau Jun 7, 2024
26e771b
remove municipal waste
cpschau Jul 8, 2024
3246d0a
remove separate treatment of waste from biomass potential calculation
cpschau Jul 8, 2024
8f422aa
phase out unsustainble biomass potentials
cpschau Jul 8, 2024
1b761d0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 8, 2024
0b34283
phase-out unsustainable bioliquids
cpschau Jul 9, 2024
2dfe089
remove waste_incineration from build_sector rule
cpschau Jul 9, 2024
275f66c
multiple potential calculation for different planning horizons
cpschau Jul 12, 2024
7fc6fdc
raised costs of unsustainable solid biomass
cpschau Jul 12, 2024
82a9234
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 12, 2024
6d82ada
stores instead of generators
cpschau Jul 12, 2024
517db86
change snakemake inputs
cpschau Jul 23, 2024
5b1603a
add phas-eout to config
cpschau Jul 24, 2024
49d30ce
add techcolor for unsustainable bioliquids
cpschau Jul 24, 2024
e048e3e
add config parameter to disable inclusion of unsustainable bioenergy …
cpschau Jul 25, 2024
62173b6
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 25, 2024
d5b0963
add biomass to params
cpschau Jul 26, 2024
7065903
Merge branch 'master' into unsus_biomass
cpschau Jul 26, 2024
13bb7ad
remove call of snakemake object in define_spatial
cpschau Jul 26, 2024
d4a890d
Quick resolve of review part 1 (config parameters, if-clause-reductio…
cpschau Aug 2, 2024
b900c01
Quick resolve of review part 2 (config table, helper function, fixed …
cpschau Aug 2, 2024
deaa6cc
Cast of planning_horizon parameter to int type after test run
cpschau Aug 2, 2024
49eac35
added JRC fuel costs for solid and liquid biofuels, BtL VOM
cpschau Aug 5, 2024
4fd1ec8
Merge branch 'master' into unsus_biomass
cpschau Aug 6, 2024
9b944d3
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 6, 2024
07d456d
clean-up after master merge
cpschau Aug 6, 2024
01f0318
Merge branch 'master' into unsus_biomass
lisazeyen Aug 7, 2024
d64917f
adressed review (increase threads for build_eurostat, fixed e_max_pu …
cpschau Aug 7, 2024
81ded81
Merge branch 'master' into unsus_biomass
cpschau Aug 7, 2024
fe2975c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions config/config.default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,8 @@ biomass:
biogas:
- Manure solid, liquid
- Sludge
include_unsustainable: true
unsustainable_phase_out: 2035
cpschau marked this conversation as resolved.
Show resolved Hide resolved

# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#solar-thermal
solar_thermal:
Expand Down Expand Up @@ -1028,6 +1030,7 @@ plotting:
services rural biomass boiler: '#c6cf98'
services urban decentral biomass boiler: '#dde5b5'
biomass to liquid: '#32CD32'
unsustainable bioliquids: '#32CD32'
BioSNG: '#123456'
# power transmission
lines: '#6c9459'
Expand Down
15 changes: 5 additions & 10 deletions rules/build_sector.smk
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,8 @@ rule build_biomass_potentials:
"https://zenodo.org/records/10356004/files/ENSPRESO_BIOMASS.xlsx",
keep_local=True,
),
nuts2="data/bundle/nuts/NUTS_RG_10M_2013_4326_LEVL_2.geojson", # https://gisco-services.ec.europa.eu/distribution/v2/nuts/download/#nuts21
eurostat="data/eurostat/Balances-April2023",
nuts2="data/bundle/nuts/NUTS_RG_10M_2013_4326_LEVL_2.geojson",
regions_onshore=resources("regions_onshore_elec_s{simpl}_{clusters}.geojson"),
nuts3_population=ancient("data/bundle/nama_10r_3popgdp.tsv.gz"),
swiss_cantons=ancient("data/ch_cantons.csv"),
Expand Down Expand Up @@ -940,6 +941,7 @@ rule prepare_sector_network:
countries=config_provider("countries"),
adjustments=config_provider("adjustments", "sector"),
emissions_scope=config_provider("energy", "emissions"),
biomass=config_provider("biomass"),
RDIR=RDIR,
input:
unpack(input_profile_offwind),
Expand Down Expand Up @@ -986,15 +988,8 @@ rule prepare_sector_network:
dsm_profile=resources("dsm_profile_s{simpl}_{clusters}.csv"),
co2_totals_name=resources("co2_totals.csv"),
co2="data/bundle/eea/UNFCCC_v23.csv",
biomass_potentials=lambda w: (
resources(
"biomass_potentials_s{simpl}_{clusters}_"
+ "{}.csv".format(config_provider("biomass", "year")(w))
)
if config_provider("foresight")(w) == "overnight"
else resources(
"biomass_potentials_s{simpl}_{clusters}_{planning_horizons}.csv"
)
biomass_potentials=resources(
"biomass_potentials_s{simpl}_{clusters}_{planning_horizons}.csv"
cpschau marked this conversation as resolved.
Show resolved Hide resolved
),
costs=lambda w: (
resources("costs_{}.csv".format(config_provider("costs", "year")(w)))
Expand Down
176 changes: 175 additions & 1 deletion scripts/build_biomass_potentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,14 +211,182 @@ def convert_nuts2_to_regions(bio_nuts2, regions):
return bio_regions


def build_eurostat(input_eurostat, countries, year, idees_rename):
"""
Return multi-index for all countries' energy data in TWh/a.
"""
df = {}
countries = {idees_rename.get(country, country) for country in countries} - {"CH"}
for country in countries:
filename = (
f"{input_eurostat}/{country}-Energy-balance-sheets-April-2023-edition.xlsb"
)
sheet = pd.read_excel(
filename,
engine="pyxlsb",
sheet_name=str(year),
skiprows=4,
index_col=list(range(4)),
)
df[country] = sheet
df = pd.concat(df, axis=0)

# drop columns with all NaNs
unnamed_cols = df.columns[df.columns.astype(str).str.startswith("Unnamed")]
df.drop(unnamed_cols, axis=1, inplace=True)
df.drop(year, axis=1, inplace=True)

# make numeric values where possible
df.replace("Z", 0, inplace=True)
df = df.apply(pd.to_numeric, errors="coerce")
df = df.select_dtypes(include=[np.number])

# write 'International aviation' to the 2nd level of the multiindex
int_avia = df.index.get_level_values(2) == "International aviation"
temp = df.loc[int_avia]
temp.index = pd.MultiIndex.from_frame(
temp.index.to_frame().fillna("International aviation")
)
df = pd.concat([temp, df.loc[~int_avia]])

# Renaming some indices
index_rename = {
"Households": "Residential",
"Commercial & public services": "Services",
"Domestic navigation": "Domestic Navigation",
"International maritime bunkers": "Bunkers",
}
columns_rename = {"Total": "Total all products", "UK": "GB"}
df.rename(index=index_rename, columns=columns_rename, inplace=True)
df.sort_index(inplace=True)
df.index.names = [None] * len(df.index.names)

# convert to MWh/a from ktoe/a
df *= 11.63 * 1e3

return df
cpschau marked this conversation as resolved.
Show resolved Hide resolved


def add_unsustainable_potentials(df):
"""
Add unsustainable biomass potentials to the given dataframe. The difference
between the data of JRC and Eurostat is assumed to be unsustainable
biomass.

Parameters
----------
df : pd.DataFrame
The dataframe with sustainable biomass potentials.
unsustainable_biomass : str
Path to the file with unsustainable biomass potentials.

Returns
-------
pd.DataFrame
The dataframe with added unsustainable biomass potentials.
"""
if "GB" in snakemake.config["countries"]:
latest_year = 2019
else:
latest_year = 2021
idees_rename = {"GR": "EL", "GB": "UK"}
df_unsustainable = (
build_eurostat(
countries=snakemake.config["countries"],
year=max(min(latest_year, investment_year), 1990),
input_eurostat=snakemake.input.eurostat,
idees_rename=idees_rename,
)
.xs("Primary production", level=2)
.droplevel([1, 2, 3])
)

df_unsustainable.index = df_unsustainable.index.str.strip()
df_unsustainable = df_unsustainable.rename(
{v: k for k, v in idees_rename.items()}, axis=0
)

bio_carriers = [
"Primary solid biofuels",
"Biogases",
"Renewable municipal waste",
"Pure biogasoline",
"Blended biogasoline",
"Pure biodiesels",
"Blended biodiesels",
"Pure bio jet kerosene",
"Blended bio jet kerosene",
"Other liquid biofuels",
]

df_unsustainable = df_unsustainable[bio_carriers]

# Phase out unsustainable biomass potentials linearly from 2020 to 2035 while phasing in sustainable potentials
reduction_factor = max(
0,
min(
1,
1
- (investment_year - 2020)
/ (snakemake.config["biomass"]["unsustainable_phase_out"] - 2020),
),
)
cpschau marked this conversation as resolved.
Show resolved Hide resolved

df_wo_ch = df.drop(df.filter(regex="CH\d", axis=0).index)

df_wo_ch["unsustainable solid biomass"] = (
(
df_wo_ch.apply(
lambda c: c.sum()
/ df_wo_ch.loc[df_wo_ch.index.str[:2] == c.name[:2]].sum().sum()
* df_unsustainable.loc[c.name[:2], "Primary solid biofuels"],
axis=1,
)
cpschau marked this conversation as resolved.
Show resolved Hide resolved
)
.mul(reduction_factor)
.clip(lower=0)
)

df_wo_ch["unsustainable biogas"] = (
(
df_wo_ch.apply(
lambda c: c.sum()
/ df_wo_ch.loc[df_wo_ch.index.str[:2] == c.name[:2]].sum().sum()
* df_unsustainable.loc[c.name[:2], "Biogases"],
axis=1,
)
)
.mul(reduction_factor)
.clip(lower=0)
)

df_wo_ch["unsustainable bioliquids"] = df_wo_ch.apply(
lambda c: c.sum()
/ df_wo_ch.loc[df_wo_ch.index.str[:2] == c.name[:2]].sum().sum()
* df_unsustainable.filter(regex="gasoline|diesel|kerosene|liquid")
.sum(axis=1)
.loc[c.name[:2]],
axis=1,
).mul(reduction_factor)

df *= 1 - reduction_factor

df = df.join(df_wo_ch.filter(like="unsustainable")).fillna(0)

return df


if __name__ == "__main__":
if "snakemake" not in globals():
import os

from _helpers import mock_snakemake

os.chdir(os.path.dirname(os.path.realpath(__file__)))
cpschau marked this conversation as resolved.
Show resolved Hide resolved
snakemake = mock_snakemake(
"build_biomass_potentials",
simpl="",
clusters="5",
clusters="37",
planning_horizons=2050,
)

Expand Down Expand Up @@ -272,4 +440,10 @@ def convert_nuts2_to_regions(bio_nuts2, regions):
df *= 1e6 # TWh/a to MWh/a
df.index.name = "MWh/a"

if (
params["include_unsustainable"]
and investment_year < params["unsustainable_phase_out"]
):
cpschau marked this conversation as resolved.
Show resolved Hide resolved
df = add_unsustainable_potentials(df)

df.to_csv(snakemake.output.biomass_potentials)
Loading
Loading