commit
07fe3ca9e2
@ -748,7 +748,7 @@ industry:
|
||||
MWh_CH4_per_tMeOH: 10.25
|
||||
MWh_MeOH_per_tMeOH: 5.528
|
||||
hotmaps_locate_missing: false
|
||||
reference_year: 2015
|
||||
reference_year: 2019
|
||||
|
||||
|
||||
# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#costs
|
||||
|
16
data/ch_industrial_production_per_subsector.csv
Normal file
16
data/ch_industrial_production_per_subsector.csv
Normal file
@ -0,0 +1,16 @@
|
||||
sector,2014,2015,2016,2017,2018,2019,2020,2021,2022,2023
|
||||
Nahrung,19035,17728,16486,16782,16261,17062,17132,17335,15625,15713
|
||||
Textil / Leder,1845,1742,1450,1497,1433,1488,1331,1496,1371,1112
|
||||
Papier / Druck,12407,12257,11100,11086,9530,9436,8482,9186,8850,7657
|
||||
Chemie / Pharma,27253,27050,27501,26953,26506,26055,24889,25595,25028,23240
|
||||
Zement / Beton,15513,13307,13576,13429,13263,13221,12612,12843,12919,11400
|
||||
Andere NE-Mineralien,4029,3820,3884,4254,3546,3577,3475,3602,3603,3178
|
||||
Metall / Eisen,7841,7889,7638,8049,8053,7188,7051,6872,6782,5531
|
||||
NE-Metall,3386,3037,2833,2813,2743,2931,2719,2992,2896,2653
|
||||
Metall / Geräte,14652,14993,14272,14689,14300,15037,13946,15042,13862,12578
|
||||
Maschinen,4561,4724,4861,4957,4365,4364,4061,4290,3698,3545
|
||||
Andere Industrien,10750,10825,10225,9833,9689,9545,8696,9371,8823,8157
|
||||
current electricity,55142,53760,51302,52173,51604,51389,48933,51730,50926,46969
|
||||
,,,,,,,,,,
|
||||
,,,,,,,,,,
|
||||
"source: https://pubdb.bfe.admin.ch/de/publication/download/11817, accessed August 2024",,,,,,,,,,
|
|
@ -22,7 +22,7 @@ RS,25,5821
|
||||
SI,8.86,1739
|
||||
ES,0.251589260787732,1273
|
||||
SE,50.4,
|
||||
UK,2,
|
||||
GB,2,
|
||||
BY,70,
|
||||
EE,52,5406
|
||||
KO,3,207
|
||||
|
|
@ -9,6 +9,7 @@ Release Notes
|
||||
|
||||
Upcoming Release
|
||||
================
|
||||
* Update JRC-IDEES-2015 to `JRC-IDEES-2021 <https://publications.jrc.ec.europa.eu/repository/handle/JRC137809>`__. The reference year is changed from 2015 to 2019.
|
||||
|
||||
* Added unsustainable biomass potentials for solid, gaseous, and liquid biomass. The potentials can be phased-out and/or
|
||||
substituted by the phase-in of sustainable biomass types using the config parameters
|
||||
|
@ -296,11 +296,12 @@ rule build_energy_totals:
|
||||
co2="data/bundle/eea/UNFCCC_v23.csv",
|
||||
swiss="data/switzerland-new_format-all_years.csv",
|
||||
swiss_transport="data/gr-e-11.03.02.01.01-cc.csv",
|
||||
idees="data/bundle/jrc-idees-2015",
|
||||
idees="data/jrc-idees-2021",
|
||||
district_heat_share="data/district_heat_share.csv",
|
||||
eurostat="data/eurostat/Balances-April2023",
|
||||
eurostat_households="data/eurostat/eurostat-household_energy_balances-february_2024.csv",
|
||||
output:
|
||||
transformation_output_coke=resources("transformation_output_coke.csv"),
|
||||
energy_name=resources("energy_totals.csv"),
|
||||
co2_name=resources("co2_totals.csv"),
|
||||
transport_name=resources("transport_data.csv"),
|
||||
@ -444,7 +445,9 @@ rule build_salt_cavern_potentials:
|
||||
|
||||
rule build_ammonia_production:
|
||||
input:
|
||||
usgs="data/bundle/myb1-2017-nitro.xls",
|
||||
usgs=storage(
|
||||
"https://d9-wret.s3.us-west-2.amazonaws.com/assets/palladium/production/s3fs-public/media/files/myb1-2022-nitro-ert.xlsx"
|
||||
),
|
||||
output:
|
||||
ammonia_production=resources("ammonia_production.csv"),
|
||||
threads: 1
|
||||
@ -466,7 +469,7 @@ rule build_industry_sector_ratios:
|
||||
ammonia=config_provider("sector", "ammonia", default=False),
|
||||
input:
|
||||
ammonia_production=resources("ammonia_production.csv"),
|
||||
idees="data/bundle/jrc-idees-2015",
|
||||
idees="data/jrc-idees-2021",
|
||||
output:
|
||||
industry_sector_ratios=resources("industry_sector_ratios.csv"),
|
||||
threads: 1
|
||||
@ -515,8 +518,9 @@ rule build_industrial_production_per_country:
|
||||
industry=config_provider("industry"),
|
||||
countries=config_provider("countries"),
|
||||
input:
|
||||
ch_industrial_production="data/ch_industrial_production_per_subsector.csv",
|
||||
ammonia_production=resources("ammonia_production.csv"),
|
||||
jrc="data/bundle/jrc-idees-2015",
|
||||
jrc="data/jrc-idees-2021",
|
||||
eurostat="data/eurostat/Balances-April2023",
|
||||
output:
|
||||
industrial_production_per_country=resources(
|
||||
@ -663,7 +667,8 @@ rule build_industrial_energy_demand_per_country_today:
|
||||
countries=config_provider("countries"),
|
||||
industry=config_provider("industry"),
|
||||
input:
|
||||
jrc="data/bundle/jrc-idees-2015",
|
||||
transformation_output_coke=resources("transformation_output_coke.csv"),
|
||||
jrc="data/jrc-idees-2021",
|
||||
industrial_production_per_country=resources(
|
||||
"industrial_production_per_country.csv"
|
||||
),
|
||||
|
@ -22,7 +22,6 @@ if config["enable"]["retrieve"] and config["enable"].get("retrieve_databundle",
|
||||
"corine/g250_clc06_V18_5.tif",
|
||||
"eea/UNFCCC_v23.csv",
|
||||
"nuts/NUTS_RG_10M_2013_4326_LEVL_2.geojson",
|
||||
"myb1-2017-nitro.xls",
|
||||
"emobility/KFZ__count",
|
||||
"emobility/Pkw__count",
|
||||
"h2_salt_caverns_GWh_per_sqkm.geojson",
|
||||
@ -57,6 +56,15 @@ if config["enable"]["retrieve"] and config["enable"].get("retrieve_databundle",
|
||||
script:
|
||||
"../scripts/retrieve_eurostat_data.py"
|
||||
|
||||
rule retrieve_jrc_idees:
|
||||
output:
|
||||
directory("data/jrc-idees-2021"),
|
||||
log:
|
||||
"logs/retrieve_jrc_idees.log",
|
||||
retries: 2
|
||||
script:
|
||||
"../scripts/retrieve_jrc_idees.py"
|
||||
|
||||
rule retrieve_eurostat_household_data:
|
||||
output:
|
||||
"data/eurostat/eurostat-household_energy_balances-february_2024.csv",
|
||||
|
@ -18,7 +18,8 @@ Outputs
|
||||
Description
|
||||
-------
|
||||
|
||||
This functions takes data from the `Minerals Yearbook <https://www.usgs.gov/centers/national-minerals-information-center/nitrogen-statistics-and-information>`_ (June 2024) published by the US Geological Survey (USGS) and the National Minerals Information Center and extracts the annual ammonia production per country in ktonN/a. The data is converted to ktonNH3/a.
|
||||
This functions takes data from the `Minerals Yearbook <https://www.usgs.gov/centers/national-minerals-information-center/nitrogen-statistics-and-information>`_
|
||||
(July 2024) published by the US Geological Survey (USGS) and the National Minerals Information Center and extracts the annual ammonia production per country in ktonN/a. The data is converted to ktonNH3/a.
|
||||
"""
|
||||
|
||||
import country_converter as coco
|
||||
@ -42,15 +43,15 @@ if __name__ == "__main__":
|
||||
skiprows=5,
|
||||
header=0,
|
||||
index_col=0,
|
||||
skipfooter=19,
|
||||
skipfooter=7,
|
||||
na_values=["--"],
|
||||
)
|
||||
|
||||
ammonia.index = cc.convert(ammonia.index, to="iso2")
|
||||
|
||||
years = [str(i) for i in range(2013, 2018)]
|
||||
years = [str(i) for i in range(2018, 2023)]
|
||||
|
||||
ammonia = ammonia[years]
|
||||
ammonia = ammonia.rename(columns=lambda x: str(x))[years]
|
||||
|
||||
# convert from ktonN to ktonNH3
|
||||
ammonia *= 17 / 14
|
||||
|
@ -25,7 +25,7 @@ Inputs
|
||||
- `data/bundle/eea_UNFCCC_v23.csv`: CO2 emissions data from EEA.
|
||||
- `data/switzerland-new_format-all_years.csv`: Swiss energy data.
|
||||
- `data/gr-e-11.03.02.01.01-cc.csv`: Swiss transport data
|
||||
- `data/bundle/jrc-idees`: JRC IDEES data.
|
||||
- `data/jrc-idees`: JRC IDEES data.
|
||||
- `data/district_heat_share.csv`: District heating shares.
|
||||
- `data/eurostat/Balances-April2023`: Eurostat energy balances.
|
||||
- `data/eurostat/eurostat-household_energy_balances-february_2024.csv`: Eurostat household energy balances.
|
||||
@ -110,7 +110,7 @@ def reverse(dictionary: dict) -> dict:
|
||||
idees_rename = {"GR": "EL", "GB": "UK"}
|
||||
|
||||
eu28 = cc.EU28as("ISO2").ISO2.tolist()
|
||||
|
||||
eu27 = cc.EU27as("ISO2").ISO2.tolist()
|
||||
eu28_eea = eu28.copy()
|
||||
eu28_eea.remove("GB")
|
||||
eu28_eea.append("UK")
|
||||
@ -170,6 +170,7 @@ def eurostat_per_country(input_eurostat: str, country: str) -> pd.DataFrame:
|
||||
sheet_name=None,
|
||||
skiprows=4,
|
||||
index_col=list(range(4)),
|
||||
na_values=":",
|
||||
)
|
||||
sheet.pop("Cover")
|
||||
return pd.concat(sheet)
|
||||
@ -329,9 +330,9 @@ def idees_per_country(ct: str, base_dir: str) -> pd.DataFrame:
|
||||
"""
|
||||
|
||||
ct_idees = idees_rename.get(ct, ct)
|
||||
fn_residential = f"{base_dir}/JRC-IDEES-2015_Residential_{ct_idees}.xlsx"
|
||||
fn_tertiary = f"{base_dir}/JRC-IDEES-2015_Tertiary_{ct_idees}.xlsx"
|
||||
fn_transport = f"{base_dir}/JRC-IDEES-2015_Transport_{ct_idees}.xlsx"
|
||||
fn_residential = f"{base_dir}/{ct_idees}/JRC-IDEES-2021_Residential_{ct_idees}.xlsx"
|
||||
fn_tertiary = f"{base_dir}/{ct_idees}/JRC-IDEES-2021_Tertiary_{ct_idees}.xlsx"
|
||||
fn_transport = f"{base_dir}/{ct_idees}/JRC-IDEES-2021_Transport_{ct_idees}.xlsx"
|
||||
|
||||
ct_totals = {}
|
||||
|
||||
@ -357,14 +358,14 @@ def idees_per_country(ct: str, base_dir: str) -> pd.DataFrame:
|
||||
row = "Energy consumption by fuel - Eurostat structure (ktoe)"
|
||||
ct_totals["total residential"] = df.loc[row]
|
||||
|
||||
assert df.index[47] == "Electricity"
|
||||
ct_totals["electricity residential"] = df.iloc[47]
|
||||
assert df.index[40] == "Electricity"
|
||||
ct_totals["electricity residential"] = df.iloc[40]
|
||||
|
||||
assert df.index[46] == "Derived heat"
|
||||
ct_totals["derived heat residential"] = df.iloc[46]
|
||||
assert df.index[39] == "Distributed heat"
|
||||
ct_totals["distributed heat residential"] = df.iloc[39]
|
||||
|
||||
assert df.index[50] == "Thermal uses"
|
||||
ct_totals["thermal uses residential"] = df.iloc[50]
|
||||
assert df.index[43] == "Thermal uses"
|
||||
ct_totals["thermal uses residential"] = df.iloc[43]
|
||||
|
||||
# services
|
||||
|
||||
@ -390,14 +391,14 @@ def idees_per_country(ct: str, base_dir: str) -> pd.DataFrame:
|
||||
row = "Energy consumption by fuel - Eurostat structure (ktoe)"
|
||||
ct_totals["total services"] = df.loc[row]
|
||||
|
||||
assert df.index[50] == "Electricity"
|
||||
ct_totals["electricity services"] = df.iloc[50]
|
||||
assert df.index[43] == "Electricity"
|
||||
ct_totals["electricity services"] = df.iloc[43]
|
||||
|
||||
assert df.index[49] == "Derived heat"
|
||||
ct_totals["derived heat services"] = df.iloc[49]
|
||||
assert df.index[42] == "Distributed heat"
|
||||
ct_totals["distributed heat services"] = df.iloc[42]
|
||||
|
||||
assert df.index[53] == "Thermal uses"
|
||||
ct_totals["thermal uses services"] = df.iloc[53]
|
||||
assert df.index[46] == "Thermal uses"
|
||||
ct_totals["thermal uses services"] = df.iloc[46]
|
||||
|
||||
# agriculture, forestry and fishing
|
||||
|
||||
@ -410,7 +411,7 @@ def idees_per_country(ct: str, base_dir: str) -> pd.DataFrame:
|
||||
"Lighting",
|
||||
"Ventilation",
|
||||
"Specific electricity uses",
|
||||
"Pumping devices (electric)",
|
||||
"Pumping devices (electricity)",
|
||||
]
|
||||
ct_totals["total agriculture electricity"] = df.loc[rows].sum()
|
||||
|
||||
@ -419,8 +420,8 @@ def idees_per_country(ct: str, base_dir: str) -> pd.DataFrame:
|
||||
|
||||
rows = [
|
||||
"Motor drives",
|
||||
"Farming machine drives (diesel oil incl. biofuels)",
|
||||
"Pumping devices (diesel oil incl. biofuels)",
|
||||
"Farming machine drives (diesel oil and liquid biofuels)",
|
||||
"Pumping devices (diesel oil and liquid biofuels)",
|
||||
]
|
||||
ct_totals["total agriculture machinery"] = df.loc[rows].sum()
|
||||
|
||||
@ -435,7 +436,7 @@ def idees_per_country(ct: str, base_dir: str) -> pd.DataFrame:
|
||||
|
||||
ct_totals["electricity road"] = df.loc["Electricity"]
|
||||
|
||||
ct_totals["total two-wheel"] = df.loc["Powered 2-wheelers (Gasoline)"]
|
||||
ct_totals["total two-wheel"] = df.loc["Powered two-wheelers (Gasoline)"]
|
||||
|
||||
assert df.index[19] == "Passenger cars"
|
||||
ct_totals["total passenger cars"] = df.iloc[19]
|
||||
@ -449,13 +450,13 @@ def idees_per_country(ct: str, base_dir: str) -> pd.DataFrame:
|
||||
assert df.index[39] == "Battery electric vehicles"
|
||||
ct_totals["electricity other road passenger"] = df.iloc[39]
|
||||
|
||||
assert df.index[41] == "Light duty vehicles"
|
||||
assert df.index[41] == "Light commercial vehicles"
|
||||
ct_totals["total light duty road freight"] = df.iloc[41]
|
||||
|
||||
assert df.index[49] == "Battery electric vehicles"
|
||||
ct_totals["electricity light duty road freight"] = df.iloc[49]
|
||||
|
||||
row = "Heavy duty vehicles (Diesel oil incl. biofuels)"
|
||||
row = "Heavy goods vehicles (Diesel oil incl. biofuels)"
|
||||
ct_totals["total heavy duty road freight"] = df.loc[row]
|
||||
|
||||
assert df.index[61] == "Passenger cars"
|
||||
@ -463,44 +464,45 @@ def idees_per_country(ct: str, base_dir: str) -> pd.DataFrame:
|
||||
|
||||
df = pd.read_excel(fn_transport, "TrRail_ene", index_col=0)
|
||||
|
||||
ct_totals["total rail"] = df.loc["by fuel (EUROSTAT DATA)"]
|
||||
ct_totals["total rail"] = df.loc["by fuel"]
|
||||
|
||||
ct_totals["electricity rail"] = df.loc["Electricity"]
|
||||
|
||||
assert df.index[15] == "Passenger transport"
|
||||
ct_totals["total rail passenger"] = df.iloc[15]
|
||||
assert df.index[9] == "Passenger transport"
|
||||
ct_totals["total rail passenger"] = df.iloc[9]
|
||||
|
||||
assert df.index[16] == "Metro and tram, urban light rail"
|
||||
assert df.index[19] == "Electric"
|
||||
assert df.index[20] == "High speed passenger trains"
|
||||
ct_totals["electricity rail passenger"] = df.iloc[[16, 19, 20]].sum()
|
||||
assert df.index[10] == "Metro and tram, urban light rail"
|
||||
assert df.index[13] == "Electric"
|
||||
assert df.index[14] == "High speed passenger trains"
|
||||
ct_totals["electricity rail passenger"] = df.iloc[[10, 13, 14]].sum()
|
||||
|
||||
assert df.index[21] == "Freight transport"
|
||||
ct_totals["total rail freight"] = df.iloc[21]
|
||||
assert df.index[15] == "Freight transport"
|
||||
ct_totals["total rail freight"] = df.iloc[15]
|
||||
|
||||
assert df.index[23] == "Electric"
|
||||
ct_totals["electricity rail freight"] = df.iloc[23]
|
||||
assert df.index[17] == "Electric"
|
||||
ct_totals["electricity rail freight"] = df.iloc[17]
|
||||
|
||||
df = pd.read_excel(fn_transport, "TrAvia_ene", index_col=0)
|
||||
|
||||
assert df.index[6] == "Passenger transport"
|
||||
ct_totals["total aviation passenger"] = df.iloc[6]
|
||||
assert df.index[4] == "Passenger transport"
|
||||
ct_totals["total aviation passenger"] = df.iloc[4]
|
||||
|
||||
assert df.index[10] == "Freight transport"
|
||||
ct_totals["total aviation freight"] = df.iloc[10]
|
||||
assert df.index[8] == "Freight transport"
|
||||
ct_totals["total aviation freight"] = df.iloc[8]
|
||||
|
||||
assert df.index[7] == "Domestic"
|
||||
ct_totals["total domestic aviation passenger"] = df.iloc[7]
|
||||
assert df.index[2] == "Domestic"
|
||||
ct_totals["total domestic aviation passenger"] = df.iloc[2]
|
||||
|
||||
assert df.index[8] == "International - Intra-EU"
|
||||
assert df.index[9] == "International - Extra-EU"
|
||||
ct_totals["total international aviation passenger"] = df.iloc[[8, 9]].sum()
|
||||
assert df.index[6] == "International - Intra-EEAwUK"
|
||||
assert df.index[7] == "International - Extra-EEAwUK"
|
||||
ct_totals["total international aviation passenger"] = df.iloc[[6, 7]].sum()
|
||||
|
||||
assert df.index[11] == "Domestic and International - Intra-EU"
|
||||
ct_totals["total domestic aviation freight"] = df.iloc[11]
|
||||
assert df.index[9] == "Domestic"
|
||||
assert df.index[10] == "International - Intra-EEAwUK"
|
||||
ct_totals["total domestic aviation freight"] = df.iloc[[9, 10]].sum()
|
||||
|
||||
assert df.index[12] == "International - Extra-EU"
|
||||
ct_totals["total international aviation freight"] = df.iloc[12]
|
||||
assert df.index[11] == "International - Extra-EEAwUK"
|
||||
ct_totals["total international aviation freight"] = df.iloc[11]
|
||||
|
||||
ct_totals["total domestic aviation"] = (
|
||||
ct_totals["total domestic aviation freight"]
|
||||
@ -515,7 +517,7 @@ def idees_per_country(ct: str, base_dir: str) -> pd.DataFrame:
|
||||
df = pd.read_excel(fn_transport, "TrNavi_ene", index_col=0)
|
||||
|
||||
# coastal and inland
|
||||
ct_totals["total domestic navigation"] = df.loc["by fuel (EUROSTAT DATA)"]
|
||||
ct_totals["total domestic navigation"] = df.loc["Energy consumption (ktoe)"]
|
||||
|
||||
df = pd.read_excel(fn_transport, "TrRoad_act", index_col=0)
|
||||
|
||||
@ -567,15 +569,51 @@ def build_idees(countries: List[str]) -> pd.DataFrame:
|
||||
names=["country", "year"],
|
||||
)
|
||||
|
||||
# clean up dataframe
|
||||
years = np.arange(2000, 2022)
|
||||
totals = totals[totals.index.get_level_values(1).isin(years)]
|
||||
|
||||
# efficiency kgoe/100km -> ktoe/100km so that after conversion TWh/100km
|
||||
totals.loc[:, "passenger car efficiency"] /= 1e6
|
||||
# convert ktoe to TWh
|
||||
exclude = totals.columns.str.fullmatch("passenger cars")
|
||||
totals = totals.copy()
|
||||
totals.loc[:, ~exclude] *= 11.63 / 1e3
|
||||
|
||||
return totals
|
||||
|
||||
|
||||
def fill_missing_years(fill_values: pd.Series) -> pd.Series:
|
||||
"""
|
||||
Fill missing years for some countries by first using forward fill (ffill)
|
||||
and then backward fill (bfill).
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fill_values : pd.Series
|
||||
A pandas Series with a MultiIndex (levels: country and year) representing
|
||||
energy values, where some values may be zero and need to be filled.
|
||||
|
||||
Returns
|
||||
-------
|
||||
pd.Series
|
||||
A pandas Series with zero values replaced by the forward-filled and
|
||||
backward-filled values of the corresponding country.
|
||||
|
||||
Notes
|
||||
-----
|
||||
- The function groups the data by the 'country' level and performs forward fill
|
||||
and backward fill to fill zero values.
|
||||
- Zero values in the original Series are replaced by the ffilled and bfilled
|
||||
value of their respective country group.
|
||||
"""
|
||||
|
||||
# Forward fill and then backward fill within each country group
|
||||
fill_values = fill_values.groupby(level="country").ffill().bfill()
|
||||
|
||||
return fill_values
|
||||
|
||||
|
||||
def build_energy_totals(
|
||||
countries: List[str],
|
||||
eurostat: pd.DataFrame,
|
||||
@ -629,6 +667,8 @@ def build_energy_totals(
|
||||
|
||||
slicer = idx[in_eurostat, :, :, "Bunkers", :]
|
||||
fill_values = eurostat.loc[slicer, "Total all products"].groupby(level=[0, 1]).sum()
|
||||
# fill missing years for some countries by mean over the other years
|
||||
fill_values = fill_missing_years(fill_values)
|
||||
df.loc[in_eurostat, "total international navigation"] = fill_values
|
||||
|
||||
# add swiss energy data
|
||||
@ -636,6 +676,36 @@ def build_energy_totals(
|
||||
df = pd.concat([df.drop("CH", errors="ignore"), swiss]).sort_index()
|
||||
|
||||
# get values for missing countries based on Eurostat EnergyBalances
|
||||
|
||||
# agriculture
|
||||
|
||||
to_fill = df.index[
|
||||
df["total agriculture"].isna()
|
||||
& df.index.get_level_values("country").isin(eurostat_countries)
|
||||
]
|
||||
c = to_fill.get_level_values("country")
|
||||
y = to_fill.get_level_values("year")
|
||||
|
||||
# take total final energy consumption from Eurostat
|
||||
eurostat_sector = "Agriculture & forestry"
|
||||
slicer = idx[c, y, :, :, eurostat_sector]
|
||||
|
||||
fill_values = eurostat.loc[slicer]["Total all products"].groupby(level=[0, 1]).sum()
|
||||
# fill missing years for some countries by mean over the other years
|
||||
fill_values = fill_missing_years(fill_values)
|
||||
df.loc[to_fill, "total agriculture"] = fill_values
|
||||
|
||||
# split into end uses by average EU data from IDEES
|
||||
uses = ["electricity", "heat", "machinery"]
|
||||
|
||||
for use in uses:
|
||||
avg = (
|
||||
idees["total agriculture electricity"] / idees["total agriculture"]
|
||||
).mean()
|
||||
df.loc[to_fill, f"total agriculture {use}"] = (
|
||||
df.loc[to_fill, "total agriculture"] * avg
|
||||
)
|
||||
|
||||
# divide cooking/space/water according to averages in EU28
|
||||
|
||||
uses = ["space", "cooking", "water"]
|
||||
@ -657,6 +727,8 @@ def build_energy_totals(
|
||||
fill_values = (
|
||||
eurostat.loc[slicer, eurostat_fuels[fuel]].groupby(level=[0, 1]).sum()
|
||||
)
|
||||
# fill missing years for some countries by mean over the other years
|
||||
fill_values = fill_missing_years(fill_values)
|
||||
df.loc[to_fill, f"{fuel} {sector}"] = fill_values
|
||||
|
||||
for sector in ["residential", "services"]:
|
||||
@ -664,7 +736,9 @@ def build_energy_totals(
|
||||
|
||||
for use in uses:
|
||||
fuel_use = df[f"electricity {sector} {use}"]
|
||||
fuel = df[f"electricity {sector}"]
|
||||
fuel = (
|
||||
df[f"electricity {sector}"].replace(0, np.nan).infer_objects(copy=False)
|
||||
)
|
||||
avg = fuel_use.div(fuel).mean()
|
||||
logger.debug(
|
||||
f"{sector}: average fraction of electricity for {use} is {avg:.3f}"
|
||||
@ -680,6 +754,7 @@ def build_energy_totals(
|
||||
df[f"total {sector} {use}"] - df[f"electricity {sector} {use}"]
|
||||
)
|
||||
nonelectric = df[f"total {sector}"] - df[f"electricity {sector}"]
|
||||
nonelectric = nonelectric.copy().replace(0, np.nan)
|
||||
avg = nonelectric_use.div(nonelectric).mean()
|
||||
logger.debug(
|
||||
f"{sector}: average fraction of non-electric for {use} is {avg:.3f}"
|
||||
@ -716,6 +791,7 @@ def build_energy_totals(
|
||||
nonelectric = (
|
||||
no_norway[f"total {sector}"] - no_norway[f"electricity {sector}"]
|
||||
)
|
||||
nonelectric = nonelectric.copy().replace(0, np.nan)
|
||||
fraction = nonelectric_use.div(nonelectric).mean()
|
||||
df.loc["NO", f"total {sector} {use}"] = (
|
||||
total_heating * fraction
|
||||
@ -728,16 +804,22 @@ def build_energy_totals(
|
||||
|
||||
slicer = idx[c, y, :, :, "Domestic aviation"]
|
||||
fill_values = eurostat.loc[slicer, "Total all products"].groupby(level=[0, 1]).sum()
|
||||
# fill missing years for some countries by mean over the other years
|
||||
fill_values = fill_missing_years(fill_values)
|
||||
df.loc[to_fill, "total domestic aviation"] = fill_values
|
||||
|
||||
slicer = idx[c, y, :, :, "International aviation"]
|
||||
fill_values = eurostat.loc[slicer, "Total all products"].groupby(level=[0, 1]).sum()
|
||||
# fill missing years for some countries by mean over the other years
|
||||
fill_values = fill_missing_years(fill_values)
|
||||
df.loc[to_fill, "total international aviation"] = fill_values
|
||||
|
||||
# missing domestic navigation
|
||||
|
||||
slicer = idx[c, y, :, :, "Domestic Navigation"]
|
||||
fill_values = eurostat.loc[slicer, "Total all products"].groupby(level=[0, 1]).sum()
|
||||
# fill missing years for some countries by mean over the other years
|
||||
fill_values = fill_missing_years(fill_values)
|
||||
df.loc[to_fill, "total domestic navigation"] = fill_values
|
||||
|
||||
# split road traffic for non-IDEES
|
||||
@ -793,7 +875,9 @@ def build_energy_totals(
|
||||
mean_BA = df.loc["BA"].loc[2014:2021, "total residential"].mean()
|
||||
mean_RS = df.loc["RS"].loc[2014:2021, "total residential"].mean()
|
||||
ratio = mean_BA / mean_RS
|
||||
df.loc["BA"] = df.loc["BA"].replace(0.0, np.nan).values
|
||||
df.loc["BA"] = (
|
||||
df.loc["BA"].replace(0.0, np.nan).infer_objects(copy=False).values
|
||||
)
|
||||
df.loc["BA"] = df.loc["BA"].combine_first(ratio * df.loc["RS"]).values
|
||||
|
||||
return df
|
||||
@ -817,18 +901,20 @@ def build_district_heat_share(countries: List[str], idees: pd.DataFrame) -> pd.S
|
||||
|
||||
Notes
|
||||
-----
|
||||
- The function calculates the district heating share as the sum of residential and services derived heat, divided by the sum of residential and services thermal uses.
|
||||
- The function calculates the district heating share as the sum of residential and services distributed heat, divided by the sum of residential and services thermal uses.
|
||||
- The district heating share is then reindexed to match the provided list of countries.
|
||||
- Missing district heating shares are filled from `data/district_heat_share.csv`.
|
||||
- The function makes a conservative assumption and takes the minimum district heating share from both the IDEES data and `data/district_heat_share.csv`.
|
||||
"""
|
||||
|
||||
# district heating share
|
||||
district_heat = idees[["derived heat residential", "derived heat services"]].sum(
|
||||
axis=1
|
||||
)
|
||||
total_heat = idees[["thermal uses residential", "thermal uses services"]].sum(
|
||||
axis=1
|
||||
district_heat = idees[
|
||||
["distributed heat residential", "distributed heat services"]
|
||||
].sum(axis=1)
|
||||
total_heat = (
|
||||
idees[["thermal uses residential", "thermal uses services"]]
|
||||
.sum(axis=1)
|
||||
.replace(0, np.nan)
|
||||
)
|
||||
|
||||
district_heat_share = district_heat / total_heat
|
||||
@ -842,10 +928,15 @@ def build_district_heat_share(countries: List[str], idees: pd.DataFrame) -> pd.S
|
||||
.squeeze()
|
||||
)
|
||||
# make conservative assumption and take minimum from both data sets
|
||||
new_index = pd.MultiIndex.from_product(
|
||||
[dh_share.index, district_heat_share.index.get_level_values(1).unique()]
|
||||
)
|
||||
district_heat_share = pd.concat(
|
||||
[district_heat_share, dh_share.reindex_like(district_heat_share)], axis=1
|
||||
[district_heat_share, dh_share.reindex(new_index, level=0)], axis=1
|
||||
).min(axis=1)
|
||||
|
||||
district_heat_share = district_heat_share.reindex(countries, level=0)
|
||||
|
||||
district_heat_share.name = "district heat share"
|
||||
|
||||
# restrict to available years
|
||||
@ -1086,6 +1177,12 @@ def build_transport_data(
|
||||
|
||||
transport_data.rename(columns={"passenger cars": "number cars"}, inplace=True)
|
||||
|
||||
# clean up dataframe
|
||||
years = np.arange(2000, 2022)
|
||||
transport_data = transport_data[
|
||||
transport_data.index.get_level_values(1).isin(years)
|
||||
]
|
||||
|
||||
missing = transport_data.index[transport_data["number cars"].isna()]
|
||||
if not missing.empty:
|
||||
logger.info(
|
||||
@ -1123,8 +1220,8 @@ def rescale_idees_from_eurostat(
|
||||
idees_countries: List[str], energy: pd.DataFrame, eurostat: pd.DataFrame
|
||||
) -> pd.DataFrame:
|
||||
"""
|
||||
Takes JRC IDEES data from 2015 and rescales it by the ratio of the Eurostat
|
||||
data and the 2015 Eurostat data.
|
||||
Takes JRC IDEES data from 2021 and rescales it by the ratio of the Eurostat
|
||||
data and the 2021 Eurostat data.
|
||||
Missing data: ['passenger car efficiency', 'passenger cars']
|
||||
|
||||
Parameters
|
||||
@ -1154,9 +1251,9 @@ def rescale_idees_from_eurostat(
|
||||
|
||||
main_cols = ["Total all products", "Electricity"]
|
||||
# read in the eurostat data for 2015
|
||||
eurostat_2015 = eurostat.xs(2015, level="year")[main_cols]
|
||||
eurostat_2021 = eurostat.xs(2021, level="year")[main_cols]
|
||||
# calculate the ratio of the two data sets
|
||||
ratio = eurostat[main_cols] / eurostat_2015
|
||||
ratio = eurostat[main_cols] / eurostat_2021
|
||||
ratio = ratio.droplevel([2, 5])
|
||||
cols_rename = {"Total all products": "total", "Electricity": "ele"}
|
||||
index_rename = {v: k for k, v in idees_rename.items()}
|
||||
@ -1169,7 +1266,7 @@ def rescale_idees_from_eurostat(
|
||||
"total residential water",
|
||||
"total residential cooking",
|
||||
"total residential",
|
||||
"derived heat residential",
|
||||
"distributed heat residential",
|
||||
"thermal uses residential",
|
||||
],
|
||||
"elec": [
|
||||
@ -1185,7 +1282,7 @@ def rescale_idees_from_eurostat(
|
||||
"total services water",
|
||||
"total services cooking",
|
||||
"total services",
|
||||
"derived heat services",
|
||||
"distributed heat services",
|
||||
"thermal uses services",
|
||||
],
|
||||
"elec": [
|
||||
@ -1376,6 +1473,33 @@ def update_residential_from_eurostat(energy: pd.DataFrame) -> pd.DataFrame:
|
||||
)
|
||||
|
||||
|
||||
def build_transformation_output_coke(eurostat, fn):
|
||||
"""
|
||||
Extracts and builds the transformation output data for coke ovens from the
|
||||
Eurostat dataset.
|
||||
|
||||
This function specifically filters the Eurostat data to extract
|
||||
transformation output related to coke ovens.
|
||||
Since the transformation output for coke ovens
|
||||
is not included in the final energy consumption of the iron and steel sector,
|
||||
it needs to be processed and added separately. The filtered data is saved
|
||||
as a CSV file.
|
||||
|
||||
Parameters:
|
||||
eurostat (pd.DataFrame): A pandas DataFrame containing Eurostat data with
|
||||
a multi-level index
|
||||
fn (str): The file path where the resulting CSV file should be saved.
|
||||
|
||||
Output:
|
||||
The resulting transformation output data for coke ovens is saved as a CSV
|
||||
file at the path specified in fn.
|
||||
"""
|
||||
slicer = pd.IndexSlice[:, :, :, "Coke ovens", "Other sources", :]
|
||||
df = eurostat.loc[slicer, :].droplevel(level=[2, 3, 4, 5])
|
||||
df.to_csv(fn)
|
||||
|
||||
|
||||
# %%
|
||||
if __name__ == "__main__":
|
||||
if "snakemake" not in globals():
|
||||
from _helpers import mock_snakemake
|
||||
@ -1391,7 +1515,7 @@ if __name__ == "__main__":
|
||||
population = nuts3["pop"].groupby(nuts3.country).sum()
|
||||
|
||||
countries = snakemake.params.countries
|
||||
idees_countries = pd.Index(countries).intersection(eu28)
|
||||
idees_countries = pd.Index(countries).intersection(eu27)
|
||||
|
||||
input_eurostat = snakemake.input.eurostat
|
||||
eurostat = build_eurostat(
|
||||
@ -1400,15 +1524,16 @@ if __name__ == "__main__":
|
||||
nprocesses=snakemake.threads,
|
||||
disable_progressbar=snakemake.config["run"].get("disable_progressbar", False),
|
||||
)
|
||||
|
||||
build_transformation_output_coke(
|
||||
eurostat, snakemake.output.transformation_output_coke
|
||||
)
|
||||
|
||||
swiss = build_swiss()
|
||||
idees = build_idees(idees_countries)
|
||||
|
||||
energy = build_energy_totals(countries, eurostat, swiss, idees)
|
||||
|
||||
# Data from IDEES only exists from 2000-2015.
|
||||
logger.info("Extrapolate IDEES data based on eurostat for years 2015-2021.")
|
||||
energy = rescale_idees_from_eurostat(idees_countries, energy, eurostat)
|
||||
|
||||
update_residential_from_eurostat(energy)
|
||||
|
||||
energy.to_csv(snakemake.output.energy_name)
|
||||
|
@ -8,7 +8,7 @@ Build industrial energy demand per country.
|
||||
Inputs
|
||||
-------
|
||||
|
||||
- ``data/bundle/jrc-idees-2015``
|
||||
- ``data/jrc-idees-2021``
|
||||
- ``industrial_production_per_country.csv``
|
||||
|
||||
Outputs
|
||||
@ -34,9 +34,9 @@ For each country and each subcategory of
|
||||
- Glass production
|
||||
- HVC
|
||||
- Integrated steelworks
|
||||
- Machinery Equipment
|
||||
- Machinery equipment
|
||||
- Methanol
|
||||
- Other Industrial Sectors
|
||||
- Other industrial sectors
|
||||
- Other chemicals
|
||||
- Other non-ferrous metals
|
||||
- Paper production
|
||||
@ -44,7 +44,7 @@ For each country and each subcategory of
|
||||
- Printing and media reproduction
|
||||
- Pulp production
|
||||
- Textiles and leather
|
||||
- Transport Equipment
|
||||
- Transport equipment
|
||||
- Wood and wood products
|
||||
|
||||
the output file contains the energy demand in TWh/a for the following carriers
|
||||
@ -74,53 +74,56 @@ ktoe_to_twh = 0.011630
|
||||
|
||||
# name in JRC-IDEES Energy Balances
|
||||
sector_sheets = {
|
||||
"Integrated steelworks": "cisb",
|
||||
"Electric arc": "cise",
|
||||
"Alumina production": "cnfa",
|
||||
"Aluminium - primary production": "cnfp",
|
||||
"Aluminium - secondary production": "cnfs",
|
||||
"Other non-ferrous metals": "cnfo",
|
||||
"Basic chemicals": "cbch",
|
||||
"Other chemicals": "coch",
|
||||
"Pharmaceutical products etc.": "cpha",
|
||||
"Basic chemicals feedstock": "cpch",
|
||||
"Cement": "ccem",
|
||||
"Ceramics & other NMM": "ccer",
|
||||
"Glass production": "cgla",
|
||||
"Pulp production": "cpul",
|
||||
"Paper production": "cpap",
|
||||
"Printing and media reproduction": "cprp",
|
||||
"Food, beverages and tobacco": "cfbt",
|
||||
"Transport Equipment": "ctre",
|
||||
"Machinery Equipment": "cmae",
|
||||
"Textiles and leather": "ctel",
|
||||
"Wood and wood products": "cwwp",
|
||||
"Mining and quarrying": "cmiq",
|
||||
"Construction": "ccon",
|
||||
"Non-specified": "cnsi",
|
||||
"Integrated steelworks": "FC_IND_IS_BF_E",
|
||||
"Electric arc": "FC_IND_IS_EA_E",
|
||||
"Alumina production": "FC_IND_NFM_AM_E",
|
||||
"Aluminium - primary production": "FC_IND_NFM_PA_E",
|
||||
"Aluminium - secondary production": "FC_IND_NFM_SA_E",
|
||||
"Other non-ferrous metals": "FC_IND_NFM_OM_E",
|
||||
"Basic chemicals": "FC_IND_CPC_BC_E",
|
||||
"Other chemicals": "FC_IND_CPC_OC_E",
|
||||
"Pharmaceutical products etc.": "FC_IND_CPC_PH_E",
|
||||
"Basic chemicals feedstock": "FC_IND_CPC_NE",
|
||||
"Cement": "FC_IND_NMM_CM_E",
|
||||
"Ceramics & other NMM": "FC_IND_NMM_CR_E",
|
||||
"Glass production": "FC_IND_NMM_GL_E",
|
||||
"Pulp production": "FC_IND_PPP_PU_E",
|
||||
"Paper production": "FC_IND_PPP_PA_E",
|
||||
"Printing and media reproduction": "FC_IND_PPP_PR_E",
|
||||
"Food, beverages and tobacco": "FC_IND_FBT_E",
|
||||
"Transport equipment": "FC_IND_TE_E",
|
||||
"Machinery equipment": "FC_IND_MAC_E",
|
||||
"Textiles and leather": "FC_IND_TL_E",
|
||||
"Wood and wood products": "FC_IND_WP_E",
|
||||
"Mining and quarrying": "FC_IND_MQ_E",
|
||||
"Construction": "FC_IND_CON_E",
|
||||
"Non-specified": "FC_IND_NSP_E",
|
||||
}
|
||||
|
||||
|
||||
fuels = {
|
||||
"All Products": "all",
|
||||
"Solid Fuels": "solid",
|
||||
"Total petroleum products (without biofuels)": "liquid",
|
||||
"Gases": "gas",
|
||||
"Total": "all",
|
||||
"Solid fossil fuels": "solid",
|
||||
"Peat and peat products": "solid",
|
||||
"Oil shale and oil sands": "solid",
|
||||
"Oil and petroleum products": "liquid",
|
||||
"Manufactured gases": "gas",
|
||||
"Natural gas": "gas",
|
||||
"Nuclear heat": "heat",
|
||||
"Derived heat": "heat",
|
||||
"Biomass and Renewable wastes": "biomass",
|
||||
"Wastes (non-renewable)": "waste",
|
||||
"Heat": "heat",
|
||||
"Renewables and biofuels": "biomass",
|
||||
"Non-renewable waste": "waste",
|
||||
"Electricity": "electricity",
|
||||
}
|
||||
|
||||
eu28 = cc.EU28as("ISO2").ISO2.tolist()
|
||||
eu27 = cc.EU27as("ISO2").ISO2.tolist()
|
||||
|
||||
jrc_names = {"GR": "EL", "GB": "UK"}
|
||||
|
||||
|
||||
def industrial_energy_demand_per_country(country, year, jrc_dir):
|
||||
jrc_country = jrc_names.get(country, country)
|
||||
fn = f"{jrc_dir}/JRC-IDEES-2015_EnergyBalance_{jrc_country}.xlsx"
|
||||
fn = f"{jrc_dir}/{jrc_country}/JRC-IDEES-2021_EnergyBalance_{jrc_country}.xlsx"
|
||||
|
||||
sheets = list(sector_sheets.values())
|
||||
df_dict = pd.read_excel(fn, sheet_name=sheets, index_col=0)
|
||||
@ -139,7 +142,7 @@ def industrial_energy_demand_per_country(country, year, jrc_dir):
|
||||
)
|
||||
|
||||
sel = ["Mining and quarrying", "Construction", "Non-specified"]
|
||||
df["Other Industrial Sectors"] = df[sel].sum(axis=1)
|
||||
df["Other industrial sectors"] = df[sel].sum(axis=1)
|
||||
df["Basic chemicals"] += df["Basic chemicals feedstock"]
|
||||
|
||||
df.drop(columns=sel + ["Basic chemicals feedstock"], index="all", inplace=True)
|
||||
@ -189,20 +192,20 @@ def separate_basic_chemicals(demand, production):
|
||||
return demand
|
||||
|
||||
|
||||
def add_non_eu28_industrial_energy_demand(countries, demand, production):
|
||||
non_eu28 = countries.difference(eu28)
|
||||
if non_eu28.empty:
|
||||
def add_non_eu27_industrial_energy_demand(countries, demand, production):
|
||||
non_eu27 = countries.difference(eu27)
|
||||
if non_eu27.empty:
|
||||
return demand
|
||||
|
||||
eu28_production = production.loc[countries.intersection(eu28)].sum()
|
||||
eu28_energy = demand.groupby(level=1).sum()
|
||||
eu28_averages = eu28_energy / eu28_production
|
||||
eu27_production = production.loc[countries.intersection(eu27)].sum()
|
||||
eu27_energy = demand.groupby(level=1).sum()
|
||||
eu27_averages = eu27_energy / eu27_production
|
||||
|
||||
demand_non_eu28 = pd.concat(
|
||||
{k: v * eu28_averages for k, v in production.loc[non_eu28].iterrows()}
|
||||
demand_non_eu27 = pd.concat(
|
||||
{k: v * eu27_averages for k, v in production.loc[non_eu27].iterrows()}
|
||||
)
|
||||
|
||||
return pd.concat([demand, demand_non_eu28])
|
||||
return pd.concat([demand, demand_non_eu27])
|
||||
|
||||
|
||||
def industrial_energy_demand(countries, year):
|
||||
@ -224,6 +227,48 @@ def industrial_energy_demand(countries, year):
|
||||
return pd.concat(demand_l, keys=countries)
|
||||
|
||||
|
||||
def add_coke_ovens(demand, fn, year, factor=0.75):
|
||||
"""
|
||||
Adds the energy consumption of coke ovens to the energy demand for
|
||||
integrated steelworks.
|
||||
|
||||
This function reads the energy consumption data for coke ovens from a
|
||||
CSV file, processes it to match the structure of the `demand` DataFrame,
|
||||
and then adds a specified share of this energy consumption to the energy
|
||||
demand for integrated steelworks.
|
||||
|
||||
The `factor` parameter controls what proportion of the coke ovens' energy
|
||||
consumption should be attributed to the iron and steel production.
|
||||
The default value of 75% is based on https://doi.org/10.1016/j.erss.2022.102565
|
||||
|
||||
Parameters:
|
||||
demand (pd.DataFrame): A pandas DataFrame containing energy demand data
|
||||
with a multi-level column index where one of the
|
||||
levels corresponds to "Integrated steelworks".
|
||||
fn (str): The file path to the CSV file containing the coke ovens energy
|
||||
consumption data.
|
||||
year (int): The year for which the coke ovens data should be selected.
|
||||
factor (float, optional): The proportion of coke ovens energy consumption to add to the
|
||||
integrated steelworks demand. Defaults to 0.75.
|
||||
|
||||
Returns:
|
||||
pd.DataFrame: The updated `demand` DataFrame with the coke ovens energy
|
||||
consumption added to the integrated steelworks energy demand.
|
||||
"""
|
||||
|
||||
df = pd.read_csv(fn, index_col=[0, 1]).xs(year, level=1)
|
||||
df = df.rename(columns={"Total all products": "Total"})[fuels.keys()]
|
||||
df = df.rename(columns=fuels).T.groupby(level=0).sum().T
|
||||
df["other"] = df["all"] - df.loc[:, df.columns != "all"].sum(axis=1)
|
||||
df = df.T.reindex_like(demand.xs("Integrated steelworks", axis=1, level=1)).fillna(
|
||||
0
|
||||
)
|
||||
sel = demand.columns.get_level_values(1) == "Integrated steelworks"
|
||||
demand.loc[:, sel] += factor * df.values
|
||||
|
||||
return demand
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if "snakemake" not in globals():
|
||||
from _helpers import mock_snakemake
|
||||
@ -232,10 +277,10 @@ if __name__ == "__main__":
|
||||
set_scenario_config(snakemake)
|
||||
|
||||
params = snakemake.params.industry
|
||||
year = params.get("reference_year", 2015)
|
||||
year = params.get("reference_year", 2019)
|
||||
countries = pd.Index(snakemake.params.countries)
|
||||
|
||||
demand = industrial_energy_demand(countries.intersection(eu28), year)
|
||||
demand = industrial_energy_demand(countries.intersection(eu27), year)
|
||||
|
||||
# output in MtMaterial/a
|
||||
production = (
|
||||
@ -245,11 +290,14 @@ if __name__ == "__main__":
|
||||
|
||||
demand = separate_basic_chemicals(demand, production)
|
||||
|
||||
demand = add_non_eu28_industrial_energy_demand(countries, demand, production)
|
||||
demand = add_non_eu27_industrial_energy_demand(countries, demand, production)
|
||||
|
||||
# for format compatibility
|
||||
demand = demand.stack(future_stack=True).unstack(level=[0, 2])
|
||||
|
||||
# add energy consumption of coke ovens
|
||||
demand = add_coke_ovens(demand, snakemake.input.transformation_output_coke, year)
|
||||
|
||||
# style and annotation
|
||||
demand.index.name = "TWh/a"
|
||||
demand.sort_index(axis=1, inplace=True)
|
||||
|
@ -16,7 +16,7 @@ Relevant Settings
|
||||
Inputs
|
||||
-------
|
||||
- ``resources/ammonia_production.csv``
|
||||
- ``data/bundle-sector/jrc-idees-2015``
|
||||
- ``data/bundle-sector/jrc-idees-2021``
|
||||
- ``data/eurostat``
|
||||
|
||||
Outputs
|
||||
@ -31,7 +31,7 @@ The industrial production is taken from the `JRC-IDEES <https://joint-research-c
|
||||
This dataset provides detailed information about the consumption of energy for various processes.
|
||||
If the country is not part of the EU28, the energy consumption in the industrial sectors is taken from the `Eurostat <https://ec.europa.eu/eurostat/de/data/database>` dataset. The industrial production is calculated for the year specified in the config["industry"]["reference_year"].
|
||||
|
||||
The ammonia production is provided by the rule `build_ammonia_production <https://pypsa-eur.readthedocs.io/en/latest/sector.html#module-build_ammonia_production>`. Since Switzerland is not part of the EU28 nor reported by eurostat, the energy consumption in the industrial sectors is taken from the `BFE <https://www.bfe.admin.ch/bfe/de/home/versorgung/statistik-und-geodaten/energiestatistiken/energieverbrauch-nach-verwendungszweck.html> dataset.
|
||||
The ammonia production is provided by the rule `build_ammonia_production <https://pypsa-eur.readthedocs.io/en/latest/sector.html#module-build_ammonia_production>`. Since Switzerland is not part of the EU28 nor reported by eurostat, the energy consumption in the industrial sectors is taken from the `BFE <https://pubdb.bfe.admin.ch/de/publication/download/11817> dataset.
|
||||
After the industrial production is calculated, the basic chemicals are separated into ammonia, chlorine, methanol and HVC. The production of these chemicals is assumed to be proportional to the production of basic chemicals without ammonia.
|
||||
|
||||
The following subcategories [kton/a] are considered:
|
||||
@ -50,11 +50,11 @@ The following subcategories [kton/a] are considered:
|
||||
- Aluminium - primary production
|
||||
- Aluminium - secondary production
|
||||
- Other non-ferrous metals
|
||||
- Transport Equipment
|
||||
- Machinery Equipment
|
||||
- Transport equipment
|
||||
- Machinery equipment
|
||||
- Textiles and leather
|
||||
- Wood and wood products
|
||||
- Other Industrial Sectors
|
||||
- Other industrial sectors
|
||||
- Ammonia
|
||||
- HVC
|
||||
- Chlorine
|
||||
@ -79,25 +79,25 @@ ktoe_to_twh = 0.01163
|
||||
|
||||
sub_sheet_name_dict = {
|
||||
"Iron and steel": "ISI",
|
||||
"Chemicals Industry": "CHI",
|
||||
"Chemical industry": "CHI",
|
||||
"Non-metallic mineral products": "NMM",
|
||||
"Pulp, paper and printing": "PPA",
|
||||
"Food, beverages and tobacco": "FBT",
|
||||
"Non Ferrous Metals": "NFM",
|
||||
"Transport Equipment": "TRE",
|
||||
"Machinery Equipment": "MAE",
|
||||
"Transport equipment": "TRE",
|
||||
"Machinery equipment": "MAE",
|
||||
"Textiles and leather": "TEL",
|
||||
"Wood and wood products": "WWP",
|
||||
"Other Industrial Sectors": "OIS",
|
||||
"Other industrial sectors": "OIS",
|
||||
}
|
||||
|
||||
eu28 = cc.EU28as("ISO2").ISO2.values
|
||||
eu27 = cc.EU27as("ISO2").ISO2.values
|
||||
|
||||
jrc_names = {"GR": "EL", "GB": "UK"}
|
||||
|
||||
sect2sub = {
|
||||
"Iron and steel": ["Electric arc", "Integrated steelworks"],
|
||||
"Chemicals Industry": [
|
||||
"Chemical industry": [
|
||||
"Basic chemicals",
|
||||
"Other chemicals",
|
||||
"Pharmaceutical products etc.",
|
||||
@ -119,11 +119,11 @@ sect2sub = {
|
||||
"Aluminium - secondary production",
|
||||
"Other non-ferrous metals",
|
||||
],
|
||||
"Transport Equipment": ["Transport Equipment"],
|
||||
"Machinery Equipment": ["Machinery Equipment"],
|
||||
"Transport equipment": ["Transport equipment"],
|
||||
"Machinery equipment": ["Machinery equipment"],
|
||||
"Textiles and leather": ["Textiles and leather"],
|
||||
"Wood and wood products": ["Wood and wood products"],
|
||||
"Other Industrial Sectors": ["Other Industrial Sectors"],
|
||||
"Other industrial sectors": ["Other industrial sectors"],
|
||||
}
|
||||
|
||||
sub2sect = {v: k for k, vv in sect2sub.items() for v in vv}
|
||||
@ -145,47 +145,41 @@ fields = {
|
||||
"Aluminium - primary production": "Aluminium - primary production",
|
||||
"Aluminium - secondary production": "Aluminium - secondary production",
|
||||
"Other non-ferrous metals": "Other non-ferrous metals (kt lead eq.)",
|
||||
"Transport Equipment": "Physical output (index)",
|
||||
"Machinery Equipment": "Physical output (index)",
|
||||
"Transport equipment": "Physical output (index)",
|
||||
"Machinery equipment": "Physical output (index)",
|
||||
"Textiles and leather": "Physical output (index)",
|
||||
"Wood and wood products": "Physical output (index)",
|
||||
"Other Industrial Sectors": "Physical output (index)",
|
||||
"Other industrial sectors": "Physical output (index)",
|
||||
}
|
||||
|
||||
eb_sectors = {
|
||||
"Iron & steel": "Iron and steel",
|
||||
"Chemical & petrochemical": "Chemicals Industry",
|
||||
"Chemical & petrochemical": "Chemical industry",
|
||||
"Non-ferrous metals": "Non-metallic mineral products",
|
||||
"Paper, pulp & printing": "Pulp, paper and printing",
|
||||
"Food, beverages & tobacco": "Food, beverages and tobacco",
|
||||
"Non-metallic minerals": "Non Ferrous Metals",
|
||||
"Transport equipment": "Transport Equipment",
|
||||
"Machinery": "Machinery Equipment",
|
||||
"Transport equipment": "Transport equipment",
|
||||
"Machinery": "Machinery equipment",
|
||||
"Textile & leather": "Textiles and leather",
|
||||
"Wood & wood products": "Wood and wood products",
|
||||
"Not elsewhere specified (industry)": "Other Industrial Sectors",
|
||||
"Not elsewhere specified (industry)": "Other industrial sectors",
|
||||
}
|
||||
|
||||
# TODO: this should go in a csv in `data`
|
||||
# Annual energy consumption in Switzerland by sector in 2015 (in TJ)
|
||||
# From: Energieverbrauch in der Industrie und im Dienstleistungssektor, Der Bundesrat
|
||||
# http://www.bfe.admin.ch/themen/00526/00541/00543/index.html?lang=de&dossier_id=00775
|
||||
e_switzerland = pd.Series(
|
||||
{
|
||||
"Iron and steel": 7889.0,
|
||||
"Chemicals Industry": 26871.0,
|
||||
"Non-metallic mineral products": 15513.0 + 3820.0,
|
||||
"Pulp, paper and printing": 12004.0,
|
||||
"Food, beverages and tobacco": 17728.0,
|
||||
"Non Ferrous Metals": 3037.0,
|
||||
"Transport Equipment": 14993.0,
|
||||
"Machinery Equipment": 4724.0,
|
||||
"Textiles and leather": 1742.0,
|
||||
"Wood and wood products": 0.0,
|
||||
"Other Industrial Sectors": 10825.0,
|
||||
"current electricity": 53760.0,
|
||||
}
|
||||
)
|
||||
|
||||
ch_mapping = {
|
||||
"Nahrung": "Food, beverages and tobacco",
|
||||
"Textil / Leder": "Textiles and leather",
|
||||
"Papier / Druck": "Pulp, paper and printing",
|
||||
"Chemie / Pharma": "Chemical industry",
|
||||
"Zement / Beton": "Non-metallic mineral products",
|
||||
"Andere NE-Mineralien": "Other non-ferrous metals",
|
||||
"Metall / Eisen": "Iron and steel",
|
||||
"NE-Metall": "Non Ferrous Metals",
|
||||
"Metall / Geräte": "Transport equipment",
|
||||
"Maschinen": "Machinery equipment",
|
||||
"Andere Industrien": "Other industrial sectors",
|
||||
}
|
||||
|
||||
|
||||
def find_physical_output(df):
|
||||
@ -197,32 +191,39 @@ def find_physical_output(df):
|
||||
|
||||
def get_energy_ratio(country, eurostat_dir, jrc_dir, year):
|
||||
if country == "CH":
|
||||
e_country = e_switzerland * tj_to_ktoe
|
||||
# data ranges between 2014-2023
|
||||
e_country = pd.read_csv(
|
||||
snakemake.input.ch_industrial_production, index_col=0
|
||||
).dropna()
|
||||
e_country = e_country.rename(index=ch_mapping).groupby(level=0).sum()
|
||||
e_country = e_country[str(min(2019, year))]
|
||||
e_country *= tj_to_ktoe
|
||||
else:
|
||||
ct_eurostat = country.replace("GB", "UK")
|
||||
# estimate physical output, energy consumption in the sector and country
|
||||
fn = f"{eurostat_dir}/{country}-Energy-balance-sheets-April-2023-edition.xlsb"
|
||||
fn = f"{eurostat_dir}/{ct_eurostat}-Energy-balance-sheets-April-2023-edition.xlsb"
|
||||
df = pd.read_excel(
|
||||
fn,
|
||||
sheet_name=str(min(2021, year)),
|
||||
sheet_name=str(min(2019, year)),
|
||||
index_col=2,
|
||||
header=0,
|
||||
skiprows=4,
|
||||
)
|
||||
e_country = df.loc[eb_sectors.keys(), "Total"].rename(eb_sectors)
|
||||
|
||||
fn = f"{jrc_dir}/JRC-IDEES-2015_Industry_EU28.xlsx"
|
||||
fn = f"{jrc_dir}/EU27/JRC-IDEES-2021_Industry_EU27.xlsx"
|
||||
|
||||
with mute_print():
|
||||
df = pd.read_excel(fn, sheet_name="Ind_Summary", index_col=0, header=0).squeeze(
|
||||
"columns"
|
||||
)
|
||||
|
||||
assert df.index[48] == "by sector"
|
||||
assert df.index[49] == "by sector"
|
||||
year_i = df.columns.get_loc(year)
|
||||
e_eu28 = df.iloc[49:76, year_i]
|
||||
e_eu28.index = e_eu28.index.str.lstrip()
|
||||
e_eu27 = df.iloc[50:77, year_i]
|
||||
e_eu27.index = e_eu27.index.str.lstrip()
|
||||
|
||||
e_ratio = e_country / e_eu28
|
||||
e_ratio = e_country / e_eu27
|
||||
|
||||
return pd.Series({k: e_ratio[v] for k, v in sub2sect.items()})
|
||||
|
||||
@ -230,7 +231,7 @@ def get_energy_ratio(country, eurostat_dir, jrc_dir, year):
|
||||
def industry_production_per_country(country, year, eurostat_dir, jrc_dir):
|
||||
def get_sector_data(sector, country):
|
||||
jrc_country = jrc_names.get(country, country)
|
||||
fn = f"{jrc_dir}/JRC-IDEES-2015_Industry_{jrc_country}.xlsx"
|
||||
fn = f"{jrc_dir}/{jrc_country}/JRC-IDEES-2021_Industry_{jrc_country}.xlsx"
|
||||
sheet = sub_sheet_name_dict[sector]
|
||||
with mute_print():
|
||||
df = pd.read_excel(fn, sheet_name=sheet, index_col=0, header=0).squeeze(
|
||||
@ -245,10 +246,10 @@ def industry_production_per_country(country, year, eurostat_dir, jrc_dir):
|
||||
|
||||
return df
|
||||
|
||||
ct = "EU28" if country not in eu28 else country
|
||||
ct = "EU27" if country not in eu27 else country
|
||||
demand = pd.concat([get_sector_data(s, ct) for s in sect2sub])
|
||||
|
||||
if country not in eu28:
|
||||
if country not in eu27:
|
||||
demand *= get_energy_ratio(country, eurostat_dir, jrc_dir, year)
|
||||
|
||||
demand.name = country
|
||||
@ -287,6 +288,7 @@ def separate_basic_chemicals(demand, year):
|
||||
"""
|
||||
Separate basic chemicals into ammonia, chlorine, methanol and HVC.
|
||||
"""
|
||||
# ammonia data from 2018-2022
|
||||
ammonia = pd.read_csv(snakemake.input.ammonia_production, index_col=0)
|
||||
|
||||
there = ammonia.index.intersection(demand.index)
|
||||
@ -296,7 +298,12 @@ def separate_basic_chemicals(demand, year):
|
||||
|
||||
demand["Ammonia"] = 0.0
|
||||
|
||||
demand.loc[there, "Ammonia"] = ammonia.loc[there, str(year)]
|
||||
year_to_use = min(max(year, 2018), 2022)
|
||||
if year_to_use != year:
|
||||
logger.info(
|
||||
f"Year {year} outside data range. Using data from {year_to_use} for ammonia production."
|
||||
)
|
||||
demand.loc[there, "Ammonia"] = ammonia.loc[there, str(year_to_use)]
|
||||
|
||||
demand["Basic chemicals"] -= demand["Ammonia"]
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -56,8 +56,8 @@ For each bus, the following industry subcategories
|
||||
- Aluminium - primary production
|
||||
- Aluminium - secondary production
|
||||
- Other non-ferrous metals
|
||||
- Transport Equipment
|
||||
- Machinery Equipment
|
||||
- Transport equipment
|
||||
- Machinery equipment
|
||||
- Textiles and leather
|
||||
- Wood and wood products
|
||||
- Other Industrial Sectors
|
||||
|
@ -26,7 +26,7 @@ logger = logging.getLogger(__name__)
|
||||
def build_nodal_transport_data(fn, pop_layout, year):
|
||||
# get numbers of car and fuel efficiency per country
|
||||
transport_data = pd.read_csv(fn, index_col=[0, 1])
|
||||
transport_data = transport_data.xs(min(2015, year), level="year")
|
||||
transport_data = transport_data.xs(year, level="year")
|
||||
|
||||
# break number of cars down to nodal level based on population density
|
||||
nodal_transport_data = transport_data.loc[pop_layout.ct].fillna(0.0)
|
||||
|
46
scripts/retrieve_jrc_idees.py
Normal file
46
scripts/retrieve_jrc_idees.py
Normal file
@ -0,0 +1,46 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# SPDX-FileCopyrightText: : 2024- The PyPSA-Eur Authors
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
||||
"""
|
||||
Retrieve and extract JRC IDEES 2021 data.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
from _helpers import configure_logging, progress_retrieve, set_scenario_config
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Define the base URL
|
||||
url_jrc = "https://jeodpp.jrc.ec.europa.eu/ftp/jrc-opendata/JRC-IDEES/JRC-IDEES-2021_v1/JRC-IDEES-2021.zip"
|
||||
|
||||
if __name__ == "__main__":
|
||||
if "snakemake" not in globals():
|
||||
from _helpers import mock_snakemake
|
||||
|
||||
snakemake = mock_snakemake("retrieve_jrc_idees")
|
||||
rootpath = ".."
|
||||
else:
|
||||
rootpath = "."
|
||||
|
||||
configure_logging(snakemake)
|
||||
set_scenario_config(snakemake)
|
||||
disable_progress = snakemake.config["run"].get("disable_progressbar", False)
|
||||
|
||||
to_fn = snakemake.output[0]
|
||||
to_fn_zp = to_fn + ".zip"
|
||||
|
||||
# download .zip file
|
||||
logger.info(f"Downloading JRC IDEES from {url_jrc}.")
|
||||
progress_retrieve(url_jrc, to_fn_zp, disable=disable_progress)
|
||||
|
||||
# extract
|
||||
logger.info("Extracting JRC IDEES data.")
|
||||
with zipfile.ZipFile(to_fn_zp, "r") as zip_ref:
|
||||
zip_ref.extractall(to_fn)
|
||||
|
||||
logger.info(f"JRC IDEES data available in '{to_fn}'.")
|
Loading…
Reference in New Issue
Block a user