Merge branch 'master' into scenario-management
This commit is contained in:
commit
d2a66879de
2
.gitignore
vendored
2
.gitignore
vendored
@ -30,6 +30,8 @@ doc/_build
|
|||||||
config/config.yaml
|
config/config.yaml
|
||||||
config/scenarios.yaml
|
config/scenarios.yaml
|
||||||
|
|
||||||
|
config.yaml
|
||||||
|
config/config.yaml
|
||||||
|
|
||||||
dconf
|
dconf
|
||||||
/data/links_p_nom.csv
|
/data/links_p_nom.csv
|
||||||
|
@ -317,7 +317,7 @@ pypsa_eur:
|
|||||||
|
|
||||||
# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#energy
|
# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#energy
|
||||||
energy:
|
energy:
|
||||||
energy_totals_year: 2011
|
energy_totals_year: 2013
|
||||||
base_emissions_year: 1990
|
base_emissions_year: 1990
|
||||||
eurostat_report_year: 2016
|
eurostat_report_year: 2016
|
||||||
emissions: CO2
|
emissions: CO2
|
||||||
@ -504,6 +504,7 @@ sector:
|
|||||||
SMR_cc: true
|
SMR_cc: true
|
||||||
regional_methanol_demand: false
|
regional_methanol_demand: false
|
||||||
regional_oil_demand: false
|
regional_oil_demand: false
|
||||||
|
regional_coal_demand: false
|
||||||
regional_co2_sequestration_potential:
|
regional_co2_sequestration_potential:
|
||||||
enable: false
|
enable: false
|
||||||
attribute: 'conservative estimate Mt'
|
attribute: 'conservative estimate Mt'
|
||||||
@ -541,8 +542,8 @@ sector:
|
|||||||
efficiency_static: 0.98
|
efficiency_static: 0.98
|
||||||
efficiency_per_1000km: 0.977
|
efficiency_per_1000km: 0.977
|
||||||
H2 pipeline:
|
H2 pipeline:
|
||||||
efficiency_per_1000km: 1 # 0.979
|
efficiency_per_1000km: 1 # 0.982
|
||||||
compression_per_1000km: 0.019
|
compression_per_1000km: 0.018
|
||||||
gas pipeline:
|
gas pipeline:
|
||||||
efficiency_per_1000km: 1 #0.977
|
efficiency_per_1000km: 1 #0.977
|
||||||
compression_per_1000km: 0.01
|
compression_per_1000km: 0.01
|
||||||
@ -639,6 +640,15 @@ industry:
|
|||||||
2040: 0.12
|
2040: 0.12
|
||||||
2045: 0.16
|
2045: 0.16
|
||||||
2050: 0.20
|
2050: 0.20
|
||||||
|
sector_ratios_fraction_future:
|
||||||
|
2020: 0.0
|
||||||
|
2025: 0.1
|
||||||
|
2030: 0.3
|
||||||
|
2035: 0.5
|
||||||
|
2040: 0.7
|
||||||
|
2045: 0.9
|
||||||
|
2050: 1.0
|
||||||
|
basic_chemicals_without_NH3_production_today: 69. #Mt/a, = 86 Mtethylene-equiv - 17 MtNH3
|
||||||
HVC_production_today: 52.
|
HVC_production_today: 52.
|
||||||
MWh_elec_per_tHVC_mechanical_recycling: 0.547
|
MWh_elec_per_tHVC_mechanical_recycling: 0.547
|
||||||
MWh_elec_per_tHVC_chemical_recycling: 6.9
|
MWh_elec_per_tHVC_chemical_recycling: 6.9
|
||||||
|
25
data/switzerland-new_format-all_years.csv
Normal file
25
data/switzerland-new_format-all_years.csv
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
country,item,2010,2011,2012,2013,2014,2015
|
||||||
|
CH,total residential,268.2,223.4,243.4,261.3,214.2,229.1
|
||||||
|
CH,total residential space,192.2,149.0,168.1,185.5,139.7,154.4
|
||||||
|
CH,total residential water,32.2,31.6,31.9,32.2,31.7,31.9
|
||||||
|
CH,total residential cooking,9.3,9.3,9.3,9.4,9.5,9.6
|
||||||
|
CH,electricity residential,67.9,63.7,65.7,67.6,63.0,64.4
|
||||||
|
CH,electricity residential space,15.9,12.8,14.3,15.8,12.3,13.5
|
||||||
|
CH,electricity residential water,8.8,8.5,8.5,8.6,8.5,8.6
|
||||||
|
CH,electricity residential cooking,4.9,4.9,4.9,4.9,5.0,5.0
|
||||||
|
CH,total services,145.9,127.4,136.7,144.0,124.5,132.5
|
||||||
|
CH,total services space,80.0,62.2,70.8,77.4,58.3,64.3
|
||||||
|
CH,total services water,10.1,10.0,10.1,10.1,10.0,10.0
|
||||||
|
CH,total services cooking,2.5,2.4,2.3,2.3,2.4,2.3
|
||||||
|
CH,electricity services,60.5,59.2,60.3,61.4,60.3,62.6
|
||||||
|
CH,electricity services space,4.0,3.2,3.8,4.2,3.3,3.6
|
||||||
|
CH,electricity services water,0.7,0.7,0.7,0.7,0.7,0.7
|
||||||
|
CH,electricity services cooking,2.5,2.4,2.3,2.3,2.4,2.3
|
||||||
|
CH,total rail,11.5,11.1,11.2,11.4,11.1,11.4
|
||||||
|
CH,total road,199.4,200.4,200.4,201.2,202.0,203.1
|
||||||
|
CH,electricity road,0.,0.,0.,0.,0.,0.
|
||||||
|
CH,electricity rail,11.5,11.1,11.2,11.4,11.1,11.4
|
||||||
|
CH,total domestic aviation,3.3,3.2,3.4,3.4,3.5,3.5
|
||||||
|
CH,total international aviation,58.0,62.0,63.5,64.2,64.5,66.8
|
||||||
|
CH,total domestic navigation,1.6,1.6,1.6,1.6,1.6,1.6
|
||||||
|
CH,total international navigation,0.,0.,0.,0.,0.,0.
|
|
@ -17,6 +17,8 @@ HVC_primary_fraction,--,float,The fraction of high value chemicals (HVC) produce
|
|||||||
HVC_mechanical_recycling _fraction,--,float,The fraction of high value chemicals (HVC) produced using mechanical recycling
|
HVC_mechanical_recycling _fraction,--,float,The fraction of high value chemicals (HVC) produced using mechanical recycling
|
||||||
HVC_chemical_recycling _fraction,--,float,The fraction of high value chemicals (HVC) produced using chemical recycling
|
HVC_chemical_recycling _fraction,--,float,The fraction of high value chemicals (HVC) produced using chemical recycling
|
||||||
,,,
|
,,,
|
||||||
|
sector_ratios_fraction_future,--,Dictionary with planning horizons as keys.,The fraction of total progress in fuel and process switching achieved in the industry sector.
|
||||||
|
basic_chemicals_without_NH3_production_today,Mt/a,float,"The amount of basic chemicals produced without ammonia (= 86 Mtethylene-equiv - 17 MtNH3)."
|
||||||
HVC_production_today,MtHVC/a,float,"The amount of high value chemicals (HVC) produced. This includes ethylene, propylene and BTX. From `DECHEMA (2017) <https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf>`_, Figure 16, page 107"
|
HVC_production_today,MtHVC/a,float,"The amount of high value chemicals (HVC) produced. This includes ethylene, propylene and BTX. From `DECHEMA (2017) <https://dechema.de/dechema_media/Downloads/Positionspapiere/Technology_study_Low_carbon_energy_and_feedstock_for_the_European_chemical_industry-p-20002750.pdf>`_, Figure 16, page 107"
|
||||||
Mwh_elec_per_tHVC _mechanical_recycling,MWh/tHVC,float,"The energy amount of electricity needed to produce a ton of high value chemical (HVC) using mechanical recycling. From SI of `Meys et al (2020) <https://doi.org/10.1016/j.resconrec.2020.105010>`_, Table S5, for HDPE, PP, PS, PET. LDPE would be 0.756."
|
Mwh_elec_per_tHVC _mechanical_recycling,MWh/tHVC,float,"The energy amount of electricity needed to produce a ton of high value chemical (HVC) using mechanical recycling. From SI of `Meys et al (2020) <https://doi.org/10.1016/j.resconrec.2020.105010>`_, Table S5, for HDPE, PP, PS, PET. LDPE would be 0.756."
|
||||||
Mwh_elec_per_tHVC _chemical_recycling,MWh/tHVC,float,"The energy amount of electricity needed to produce a ton of high value chemical (HVC) using chemical recycling. The default value is based on pyrolysis and electric steam cracking. From `Material Economics (2019) <https://materialeconomics.com/latest-updates/industrial-transformation-2050>`_, page 125"
|
Mwh_elec_per_tHVC _chemical_recycling,MWh/tHVC,float,"The energy amount of electricity needed to produce a ton of high value chemical (HVC) using chemical recycling. The default value is based on pyrolysis and electric steam cracking. From `Material Economics (2019) <https://materialeconomics.com/latest-updates/industrial-transformation-2050>`_, page 125"
|
||||||
|
|
@ -17,6 +17,29 @@ Upcoming Release
|
|||||||
names to run under ``run: name:`` in the configuration file. The latter must
|
names to run under ``run: name:`` in the configuration file. The latter must
|
||||||
be a subset of toplevel keys in the scenario file.
|
be a subset of toplevel keys in the scenario file.
|
||||||
|
|
||||||
|
* Improved representation of industry transition pathways. A new script was
|
||||||
|
added to interpolate industry sector ratios from today's status quo to future
|
||||||
|
systems (i.e. specific emissions and demands for energy and feedstocks). For
|
||||||
|
each country we gradually switch industry processes from today's specific
|
||||||
|
energy carrier usage per ton material output to the best-in-class energy
|
||||||
|
consumption of tomorrow. This is done on a per-country basis. The ratio of
|
||||||
|
today to tomorrow's energy consumption is set with the ``industry:
|
||||||
|
sector_ratios_fraction_future:`` parameter.
|
||||||
|
|
||||||
|
* Fix plotting of retrofitted hydrogen pipelines with pathway optimisation.
|
||||||
|
|
||||||
|
* Bugfix: Correct units of subtracted chlorine and methanol demand in
|
||||||
|
:mod:`build_industry_sector_ratios`.
|
||||||
|
|
||||||
|
* Include all countries in ammonia production resource. This is so that the full
|
||||||
|
EU28 ammonia demand can be correctly subtracted in the rule
|
||||||
|
:mod:`build_industry_sector_ratios`.
|
||||||
|
|
||||||
|
* Regions are assigned to all buses with unique coordinates in the network with
|
||||||
|
a preference given to substations. Previously, only substations had assigned
|
||||||
|
regions, but this could lead to issues when a high spatial resolution was
|
||||||
|
applied.
|
||||||
|
|
||||||
* The default configuration ``config/config.default.yaml`` is now automatically
|
* The default configuration ``config/config.default.yaml`` is now automatically
|
||||||
used as a base configuration file and no longer copied to
|
used as a base configuration file and no longer copied to
|
||||||
``config/config.yaml`` on first use. The file ``config/config.yaml`` should be
|
``config/config.yaml`` on first use. The file ``config/config.yaml`` should be
|
||||||
@ -24,6 +47,10 @@ Upcoming Release
|
|||||||
|
|
||||||
* Merged two OPSD time series data versions into such that the option ``load:
|
* Merged two OPSD time series data versions into such that the option ``load:
|
||||||
power_statistics:`` becomes superfluous and was hence removed.
|
power_statistics:`` becomes superfluous and was hence removed.
|
||||||
|
* Bugfix: The industry coal emissions for industry were not properly tracked.
|
||||||
|
|
||||||
|
* Allow industrial coal demand to be regional so its emissions can be included
|
||||||
|
in regional emission limits.
|
||||||
|
|
||||||
* Add new default to overdimension heating in individual buildings. This allows
|
* Add new default to overdimension heating in individual buildings. This allows
|
||||||
them to cover heat demand peaks e.g. 10% higher than those in the data. The
|
them to cover heat demand peaks e.g. 10% higher than those in the data. The
|
||||||
@ -120,6 +147,8 @@ Upcoming Release
|
|||||||
workflows with foresight "myopic" and still needs to be added foresight option
|
workflows with foresight "myopic" and still needs to be added foresight option
|
||||||
"perfect".
|
"perfect".
|
||||||
|
|
||||||
|
* Switched the energy totals year from 2011 to 2013 to comply with the assumed default weather year.
|
||||||
|
|
||||||
|
|
||||||
PyPSA-Eur 0.9.0 (5th January 2024)
|
PyPSA-Eur 0.9.0 (5th January 2024)
|
||||||
==================================
|
==================================
|
||||||
|
@ -279,7 +279,7 @@ rule build_energy_totals:
|
|||||||
input:
|
input:
|
||||||
nuts3_shapes=resources("nuts3_shapes.geojson"),
|
nuts3_shapes=resources("nuts3_shapes.geojson"),
|
||||||
co2="data/bundle-sector/eea/UNFCCC_v23.csv",
|
co2="data/bundle-sector/eea/UNFCCC_v23.csv",
|
||||||
swiss="data/bundle-sector/switzerland-sfoe/switzerland-new_format.csv",
|
swiss="data/switzerland-new_format-all_years.csv",
|
||||||
idees="data/bundle-sector/jrc-idees-2015",
|
idees="data/bundle-sector/jrc-idees-2015",
|
||||||
district_heat_share="data/district_heat_share.csv",
|
district_heat_share="data/district_heat_share.csv",
|
||||||
eurostat=input_eurostat,
|
eurostat=input_eurostat,
|
||||||
@ -406,8 +406,6 @@ rule build_salt_cavern_potentials:
|
|||||||
|
|
||||||
|
|
||||||
rule build_ammonia_production:
|
rule build_ammonia_production:
|
||||||
params:
|
|
||||||
countries=config_provider("countries"),
|
|
||||||
input:
|
input:
|
||||||
usgs="data/bundle-sector/myb1-2017-nitro.xls",
|
usgs="data/bundle-sector/myb1-2017-nitro.xls",
|
||||||
output:
|
output:
|
||||||
@ -447,6 +445,31 @@ rule build_industry_sector_ratios:
|
|||||||
"../scripts/build_industry_sector_ratios.py"
|
"../scripts/build_industry_sector_ratios.py"
|
||||||
|
|
||||||
|
|
||||||
|
rule build_industry_sector_ratios_intermediate:
|
||||||
|
params:
|
||||||
|
industry=config["industry"],
|
||||||
|
input:
|
||||||
|
industry_sector_ratios=RESOURCES + "industry_sector_ratios.csv",
|
||||||
|
industrial_energy_demand_per_country_today=RESOURCES
|
||||||
|
+ "industrial_energy_demand_per_country_today.csv",
|
||||||
|
industrial_production_per_country=RESOURCES
|
||||||
|
+ "industrial_production_per_country.csv",
|
||||||
|
output:
|
||||||
|
industry_sector_ratios=RESOURCES
|
||||||
|
+ "industry_sector_ratios_{planning_horizons}.csv",
|
||||||
|
threads: 1
|
||||||
|
resources:
|
||||||
|
mem_mb=1000,
|
||||||
|
log:
|
||||||
|
LOGS + "build_industry_sector_ratios_{planning_horizons}.log",
|
||||||
|
benchmark:
|
||||||
|
BENCHMARKS + "build_industry_sector_ratios_{planning_horizons}"
|
||||||
|
conda:
|
||||||
|
"../envs/environment.yaml"
|
||||||
|
script:
|
||||||
|
"../scripts/build_industry_sector_ratios_intermediate.py"
|
||||||
|
|
||||||
|
|
||||||
rule build_industrial_production_per_country:
|
rule build_industrial_production_per_country:
|
||||||
params:
|
params:
|
||||||
industry=config_provider("industry"),
|
industry=config_provider("industry"),
|
||||||
@ -560,7 +583,7 @@ rule build_industrial_production_per_node:
|
|||||||
|
|
||||||
rule build_industrial_energy_demand_per_node:
|
rule build_industrial_energy_demand_per_node:
|
||||||
input:
|
input:
|
||||||
industry_sector_ratios=resources("industry_sector_ratios.csv"),
|
industry_sector_ratios=resources("industry_sector_ratios_{planning_horizons}.csv"),
|
||||||
industrial_production_per_node=resources(
|
industrial_production_per_node=resources(
|
||||||
"industrial_production_elec_s{simpl}_{clusters}_{planning_horizons}.csv"
|
"industrial_production_elec_s{simpl}_{clusters}_{planning_horizons}.csv"
|
||||||
),
|
),
|
||||||
@ -596,7 +619,6 @@ rule build_industrial_energy_demand_per_country_today:
|
|||||||
industry=config_provider("industry"),
|
industry=config_provider("industry"),
|
||||||
input:
|
input:
|
||||||
jrc="data/bundle-sector/jrc-idees-2015",
|
jrc="data/bundle-sector/jrc-idees-2015",
|
||||||
ammonia_production=resources("ammonia_production.csv"),
|
|
||||||
industrial_production_per_country=resources(
|
industrial_production_per_country=resources(
|
||||||
"industrial_production_per_country.csv"
|
"industrial_production_per_country.csv"
|
||||||
),
|
),
|
||||||
|
@ -559,6 +559,7 @@ def _set_countries_and_substations(n, config, country_shapes, offshore_shapes):
|
|||||||
for b, df in product(("bus0", "bus1"), (n.lines, n.links)):
|
for b, df in product(("bus0", "bus1"), (n.lines, n.links)):
|
||||||
has_connections_b |= ~df.groupby(b).under_construction.min()
|
has_connections_b |= ~df.groupby(b).under_construction.min()
|
||||||
|
|
||||||
|
buses["onshore_bus"] = onshore_b
|
||||||
buses["substation_lv"] = (
|
buses["substation_lv"] = (
|
||||||
lv_b & onshore_b & (~buses["under_construction"]) & has_connections_b
|
lv_b & onshore_b & (~buses["under_construction"]) & has_connections_b
|
||||||
)
|
)
|
||||||
|
@ -28,13 +28,14 @@ if __name__ == "__main__":
|
|||||||
header=0,
|
header=0,
|
||||||
index_col=0,
|
index_col=0,
|
||||||
skipfooter=19,
|
skipfooter=19,
|
||||||
|
na_values=["--"],
|
||||||
)
|
)
|
||||||
|
|
||||||
ammonia.index = cc.convert(ammonia.index, to="iso2")
|
ammonia.index = cc.convert(ammonia.index, to="iso2")
|
||||||
|
|
||||||
years = [str(i) for i in range(2013, 2018)]
|
years = [str(i) for i in range(2013, 2018)]
|
||||||
countries = ammonia.index.intersection(snakemake.params.countries)
|
|
||||||
ammonia = ammonia.loc[countries, years].astype(float)
|
ammonia = ammonia[years]
|
||||||
|
|
||||||
# convert from ktonN to ktonNH3
|
# convert from ktonN to ktonNH3
|
||||||
ammonia *= 17 / 14
|
ammonia *= 17 / 14
|
||||||
|
@ -136,7 +136,13 @@ if __name__ == "__main__":
|
|||||||
c_b = n.buses.country == country
|
c_b = n.buses.country == country
|
||||||
|
|
||||||
onshore_shape = country_shapes[country]
|
onshore_shape = country_shapes[country]
|
||||||
onshore_locs = n.buses.loc[c_b & n.buses.substation_lv, ["x", "y"]]
|
onshore_locs = (
|
||||||
|
n.buses.loc[c_b & n.buses.onshore_bus]
|
||||||
|
.sort_values(
|
||||||
|
by="substation_lv", ascending=False
|
||||||
|
) # preference for substations
|
||||||
|
.drop_duplicates(subset=["x", "y"], keep="first")[["x", "y"]]
|
||||||
|
)
|
||||||
onshore_regions.append(
|
onshore_regions.append(
|
||||||
gpd.GeoDataFrame(
|
gpd.GeoDataFrame(
|
||||||
{
|
{
|
||||||
|
@ -74,7 +74,7 @@ def industrial_energy_demand_per_country(country, year, jrc_dir):
|
|||||||
def get_subsector_data(sheet):
|
def get_subsector_data(sheet):
|
||||||
df = df_dict[sheet][year].groupby(fuels).sum()
|
df = df_dict[sheet][year].groupby(fuels).sum()
|
||||||
|
|
||||||
df["ammonia"] = 0.0
|
df["hydrogen"] = 0.0
|
||||||
|
|
||||||
df["other"] = df["all"] - df.loc[df.index != "all"].sum()
|
df["other"] = df["all"] - df.loc[df.index != "all"].sum()
|
||||||
|
|
||||||
@ -95,51 +95,50 @@ def industrial_energy_demand_per_country(country, year, jrc_dir):
|
|||||||
return df
|
return df
|
||||||
|
|
||||||
|
|
||||||
def add_ammonia_energy_demand(demand):
|
def separate_basic_chemicals(demand, production):
|
||||||
# MtNH3/a
|
|
||||||
fn = snakemake.input.ammonia_production
|
|
||||||
ammonia = pd.read_csv(fn, index_col=0)[str(year)] / 1e3
|
|
||||||
|
|
||||||
def get_ammonia_by_fuel(x):
|
ammonia = pd.DataFrame(
|
||||||
fuels = {
|
{
|
||||||
"gas": params["MWh_CH4_per_tNH3_SMR"],
|
"hydrogen": production["Ammonia"] * params["MWh_H2_per_tNH3_electrolysis"],
|
||||||
"electricity": params["MWh_elec_per_tNH3_SMR"],
|
"electricity": production["Ammonia"]
|
||||||
|
* params["MWh_elec_per_tNH3_electrolysis"],
|
||||||
}
|
}
|
||||||
|
).T
|
||||||
return pd.Series({k: x * v for k, v in fuels.items()})
|
chlorine = pd.DataFrame(
|
||||||
|
{
|
||||||
ammonia_by_fuel = ammonia.apply(get_ammonia_by_fuel).T
|
"hydrogen": production["Chlorine"] * params["MWh_H2_per_tCl"],
|
||||||
ammonia_by_fuel = ammonia_by_fuel.unstack().reindex(
|
"electricity": production["Chlorine"] * params["MWh_elec_per_tCl"],
|
||||||
index=demand.index, fill_value=0.0
|
}
|
||||||
)
|
).T
|
||||||
|
methanol = pd.DataFrame(
|
||||||
ammonia = pd.DataFrame({"ammonia": ammonia * params["MWh_NH3_per_tNH3"]}).T
|
{
|
||||||
|
"gas": production["Methanol"] * params["MWh_CH4_per_tMeOH"],
|
||||||
|
"electricity": production["Methanol"] * params["MWh_elec_per_tMeOH"],
|
||||||
|
}
|
||||||
|
).T
|
||||||
|
|
||||||
demand["Ammonia"] = ammonia.unstack().reindex(index=demand.index, fill_value=0.0)
|
demand["Ammonia"] = ammonia.unstack().reindex(index=demand.index, fill_value=0.0)
|
||||||
|
demand["Chlorine"] = chlorine.unstack().reindex(index=demand.index, fill_value=0.0)
|
||||||
|
demand["Methanol"] = methanol.unstack().reindex(index=demand.index, fill_value=0.0)
|
||||||
|
|
||||||
demand["Basic chemicals (without ammonia)"] = (
|
demand["HVC"] = (
|
||||||
demand["Basic chemicals"] - ammonia_by_fuel
|
demand["Basic chemicals"]
|
||||||
|
- demand["Ammonia"]
|
||||||
|
- demand["Methanol"]
|
||||||
|
- demand["Chlorine"]
|
||||||
)
|
)
|
||||||
|
|
||||||
demand["Basic chemicals (without ammonia)"].clip(lower=0, inplace=True)
|
|
||||||
|
|
||||||
demand.drop(columns="Basic chemicals", inplace=True)
|
demand.drop(columns="Basic chemicals", inplace=True)
|
||||||
|
|
||||||
|
demand["HVC"].clip(lower=0, inplace=True)
|
||||||
|
|
||||||
return demand
|
return demand
|
||||||
|
|
||||||
|
|
||||||
def add_non_eu28_industrial_energy_demand(countries, demand):
|
def add_non_eu28_industrial_energy_demand(countries, demand, production):
|
||||||
non_eu28 = countries.difference(eu28)
|
non_eu28 = countries.difference(eu28)
|
||||||
if non_eu28.empty:
|
if non_eu28.empty:
|
||||||
return demand
|
return demand
|
||||||
# output in MtMaterial/a
|
|
||||||
fn = snakemake.input.industrial_production_per_country
|
|
||||||
production = pd.read_csv(fn, index_col=0) / 1e3
|
|
||||||
|
|
||||||
# recombine HVC, Chlorine and Methanol to Basic chemicals (without ammonia)
|
|
||||||
chemicals = ["HVC", "Chlorine", "Methanol"]
|
|
||||||
production["Basic chemicals (without ammonia)"] = production[chemicals].sum(axis=1)
|
|
||||||
production.drop(columns=chemicals, inplace=True)
|
|
||||||
|
|
||||||
eu28_production = production.loc[countries.intersection(eu28)].sum()
|
eu28_production = production.loc[countries.intersection(eu28)].sum()
|
||||||
eu28_energy = demand.groupby(level=1).sum()
|
eu28_energy = demand.groupby(level=1).sum()
|
||||||
@ -184,9 +183,15 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
demand = industrial_energy_demand(countries.intersection(eu28), year)
|
demand = industrial_energy_demand(countries.intersection(eu28), year)
|
||||||
|
|
||||||
demand = add_ammonia_energy_demand(demand)
|
# output in MtMaterial/a
|
||||||
|
production = (
|
||||||
|
pd.read_csv(snakemake.input.industrial_production_per_country, index_col=0)
|
||||||
|
/ 1e3
|
||||||
|
)
|
||||||
|
|
||||||
demand = add_non_eu28_industrial_energy_demand(countries, demand)
|
demand = separate_basic_chemicals(demand, production)
|
||||||
|
|
||||||
|
demand = add_non_eu28_industrial_energy_demand(countries, demand, production)
|
||||||
|
|
||||||
# for format compatibility
|
# for format compatibility
|
||||||
demand = demand.stack(dropna=False).unstack(level=[0, 2])
|
demand = demand.stack(dropna=False).unstack(level=[0, 2])
|
||||||
|
@ -21,23 +21,31 @@ if __name__ == "__main__":
|
|||||||
)
|
)
|
||||||
set_scenario_config(snakemake)
|
set_scenario_config(snakemake)
|
||||||
|
|
||||||
# import EU ratios df as csv
|
# import ratios
|
||||||
fn = snakemake.input.industry_sector_ratios
|
fn = snakemake.input.industry_sector_ratios
|
||||||
industry_sector_ratios = pd.read_csv(fn, index_col=0)
|
sector_ratios = pd.read_csv(fn, header=[0, 1], index_col=0)
|
||||||
|
|
||||||
# material demand per node and industry (kton/a)
|
# material demand per node and industry (Mton/a)
|
||||||
fn = snakemake.input.industrial_production_per_node
|
fn = snakemake.input.industrial_production_per_node
|
||||||
nodal_production = pd.read_csv(fn, index_col=0)
|
nodal_production = pd.read_csv(fn, index_col=0) / 1e3
|
||||||
|
|
||||||
# energy demand today to get current electricity
|
# energy demand today to get current electricity
|
||||||
fn = snakemake.input.industrial_energy_demand_per_node_today
|
fn = snakemake.input.industrial_energy_demand_per_node_today
|
||||||
nodal_today = pd.read_csv(fn, index_col=0)
|
nodal_today = pd.read_csv(fn, index_col=0)
|
||||||
|
|
||||||
# final energy consumption per node and industry (TWh/a)
|
nodal_sector_ratios = pd.concat(
|
||||||
nodal_df = nodal_production.dot(industry_sector_ratios.T)
|
{node: sector_ratios[node[:2]] for node in nodal_production.index}, axis=1
|
||||||
|
)
|
||||||
|
|
||||||
# convert GWh to TWh and ktCO2 to MtCO2
|
nodal_production_stacked = nodal_production.stack()
|
||||||
nodal_df *= 0.001
|
nodal_production_stacked.index.names = [None, None]
|
||||||
|
|
||||||
|
# final energy consumption per node and industry (TWh/a)
|
||||||
|
nodal_df = (
|
||||||
|
(nodal_sector_ratios.multiply(nodal_production_stacked))
|
||||||
|
.T.groupby(level=0)
|
||||||
|
.sum()
|
||||||
|
)
|
||||||
|
|
||||||
rename_sectors = {
|
rename_sectors = {
|
||||||
"elec": "electricity",
|
"elec": "electricity",
|
||||||
|
@ -261,7 +261,11 @@ def separate_basic_chemicals(demand, year):
|
|||||||
demand["Basic chemicals"].clip(lower=0.0, inplace=True)
|
demand["Basic chemicals"].clip(lower=0.0, inplace=True)
|
||||||
|
|
||||||
# assume HVC, methanol, chlorine production proportional to non-ammonia basic chemicals
|
# assume HVC, methanol, chlorine production proportional to non-ammonia basic chemicals
|
||||||
distribution_key = demand["Basic chemicals"] / demand["Basic chemicals"].sum()
|
distribution_key = (
|
||||||
|
demand["Basic chemicals"]
|
||||||
|
/ params["basic_chemicals_without_NH3_production_today"]
|
||||||
|
/ 1e3
|
||||||
|
)
|
||||||
demand["HVC"] = params["HVC_production_today"] * 1e3 * distribution_key
|
demand["HVC"] = params["HVC_production_today"] * 1e3 * distribution_key
|
||||||
demand["Chlorine"] = params["chlorine_production_today"] * 1e3 * distribution_key
|
demand["Chlorine"] = params["chlorine_production_today"] * 1e3 * distribution_key
|
||||||
demand["Methanol"] = params["methanol_production_today"] * 1e3 * distribution_key
|
demand["Methanol"] = params["methanol_production_today"] * 1e3 * distribution_key
|
||||||
|
@ -408,15 +408,15 @@ def chemicals_industry():
|
|||||||
df.loc["methane", sector] -= ammonia_total * params["MWh_CH4_per_tNH3_SMR"]
|
df.loc["methane", sector] -= ammonia_total * params["MWh_CH4_per_tNH3_SMR"]
|
||||||
df.loc["elec", sector] -= ammonia_total * params["MWh_elec_per_tNH3_SMR"]
|
df.loc["elec", sector] -= ammonia_total * params["MWh_elec_per_tNH3_SMR"]
|
||||||
|
|
||||||
# subtract chlorine demand
|
# subtract chlorine demand (in MtCl/a)
|
||||||
chlorine_total = params["chlorine_production_today"]
|
chlorine_total = params["chlorine_production_today"]
|
||||||
df.loc["hydrogen", sector] -= chlorine_total * params["MWh_H2_per_tCl"]
|
df.loc["hydrogen", sector] -= chlorine_total * params["MWh_H2_per_tCl"] * 1e3
|
||||||
df.loc["elec", sector] -= chlorine_total * params["MWh_elec_per_tCl"]
|
df.loc["elec", sector] -= chlorine_total * params["MWh_elec_per_tCl"] * 1e3
|
||||||
|
|
||||||
# subtract methanol demand
|
# subtract methanol demand (in MtMeOH/a)
|
||||||
methanol_total = params["methanol_production_today"]
|
methanol_total = params["methanol_production_today"]
|
||||||
df.loc["methane", sector] -= methanol_total * params["MWh_CH4_per_tMeOH"]
|
df.loc["methane", sector] -= methanol_total * params["MWh_CH4_per_tMeOH"] * 1e3
|
||||||
df.loc["elec", sector] -= methanol_total * params["MWh_elec_per_tMeOH"]
|
df.loc["elec", sector] -= methanol_total * params["MWh_elec_per_tMeOH"] * 1e3
|
||||||
|
|
||||||
# MWh/t material
|
# MWh/t material
|
||||||
df.loc[sources, sector] = df.loc[sources, sector] / s_out
|
df.loc[sources, sector] = df.loc[sources, sector] / s_out
|
||||||
|
81
scripts/build_industry_sector_ratios_intermediate.py
Normal file
81
scripts/build_industry_sector_ratios_intermediate.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# SPDX-FileCopyrightText: : 2020-2024 The PyPSA-Eur Authors
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
"""
|
||||||
|
Build specific energy consumption by carrier and industries and by country,
|
||||||
|
that interpolates between the current average energy consumption (from
|
||||||
|
2015-2020) and the ideal future best-in-class consumption.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pandas as pd
|
||||||
|
from prepare_sector_network import get
|
||||||
|
|
||||||
|
|
||||||
|
def build_industry_sector_ratios_intermediate():
|
||||||
|
|
||||||
|
# in TWh/a
|
||||||
|
demand = pd.read_csv(
|
||||||
|
snakemake.input.industrial_energy_demand_per_country_today,
|
||||||
|
header=[0, 1],
|
||||||
|
index_col=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
# in Mt/a
|
||||||
|
production = (
|
||||||
|
pd.read_csv(snakemake.input.industrial_production_per_country, index_col=0)
|
||||||
|
/ 1e3
|
||||||
|
).stack()
|
||||||
|
production.index.names = [None, None]
|
||||||
|
|
||||||
|
# in MWh/t
|
||||||
|
future_sector_ratios = pd.read_csv(
|
||||||
|
snakemake.input.industry_sector_ratios, index_col=0
|
||||||
|
)
|
||||||
|
|
||||||
|
today_sector_ratios = demand.div(production, axis=1)
|
||||||
|
|
||||||
|
today_sector_ratios.dropna(how="all", axis=1, inplace=True)
|
||||||
|
|
||||||
|
rename = {
|
||||||
|
"waste": "biomass",
|
||||||
|
"electricity": "elec",
|
||||||
|
"solid": "coke",
|
||||||
|
"gas": "methane",
|
||||||
|
"other": "biomass",
|
||||||
|
"liquid": "naphtha",
|
||||||
|
}
|
||||||
|
today_sector_ratios = today_sector_ratios.rename(rename).groupby(level=0).sum()
|
||||||
|
|
||||||
|
fraction_future = get(params["sector_ratios_fraction_future"], year)
|
||||||
|
|
||||||
|
intermediate_sector_ratios = {}
|
||||||
|
for ct, group in today_sector_ratios.T.groupby(level=0):
|
||||||
|
today_sector_ratios_ct = (
|
||||||
|
group.droplevel(0)
|
||||||
|
.T.reindex_like(future_sector_ratios)
|
||||||
|
.fillna(future_sector_ratios)
|
||||||
|
)
|
||||||
|
intermediate_sector_ratios[ct] = (
|
||||||
|
today_sector_ratios_ct * (1 - fraction_future)
|
||||||
|
+ future_sector_ratios * fraction_future
|
||||||
|
)
|
||||||
|
intermediate_sector_ratios = pd.concat(intermediate_sector_ratios, axis=1)
|
||||||
|
|
||||||
|
intermediate_sector_ratios.to_csv(snakemake.output.industry_sector_ratios)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if "snakemake" not in globals():
|
||||||
|
from _helpers import mock_snakemake
|
||||||
|
|
||||||
|
snakemake = mock_snakemake(
|
||||||
|
"build_industry_sector_ratios_intermediate",
|
||||||
|
planning_horizons="2030",
|
||||||
|
)
|
||||||
|
|
||||||
|
year = int(snakemake.wildcards.planning_horizons[-4:])
|
||||||
|
|
||||||
|
params = snakemake.params.industry
|
||||||
|
|
||||||
|
build_industry_sector_ratios_intermediate()
|
@ -24,6 +24,7 @@ def group_pipes(df, drop_direction=False):
|
|||||||
"""
|
"""
|
||||||
Group pipes which connect same buses and return overall capacity.
|
Group pipes which connect same buses and return overall capacity.
|
||||||
"""
|
"""
|
||||||
|
df = df.copy()
|
||||||
if drop_direction:
|
if drop_direction:
|
||||||
positive_order = df.bus0 < df.bus1
|
positive_order = df.bus0 < df.bus1
|
||||||
df_p = df[positive_order]
|
df_p = df[positive_order]
|
||||||
@ -32,12 +33,13 @@ def group_pipes(df, drop_direction=False):
|
|||||||
df = pd.concat([df_p, df_n])
|
df = pd.concat([df_p, df_n])
|
||||||
|
|
||||||
# there are pipes for each investment period rename to AC buses name for plotting
|
# there are pipes for each investment period rename to AC buses name for plotting
|
||||||
|
df["index_orig"] = df.index
|
||||||
df.index = df.apply(
|
df.index = df.apply(
|
||||||
lambda x: f"H2 pipeline {x.bus0.replace(' H2', '')} -> {x.bus1.replace(' H2', '')}",
|
lambda x: f"H2 pipeline {x.bus0.replace(' H2', '')} -> {x.bus1.replace(' H2', '')}",
|
||||||
axis=1,
|
axis=1,
|
||||||
)
|
)
|
||||||
return df.groupby(level=0).agg(
|
return df.groupby(level=0).agg(
|
||||||
{"p_nom_opt": "sum", "bus0": "first", "bus1": "first"}
|
{"p_nom_opt": "sum", "bus0": "first", "bus1": "first", "index_orig": "first"}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -95,6 +97,7 @@ def plot_h2_map(n, regions):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not h2_retro.empty:
|
if not h2_retro.empty:
|
||||||
|
if snakemake.params.foresight != "myopic":
|
||||||
positive_order = h2_retro.bus0 < h2_retro.bus1
|
positive_order = h2_retro.bus0 < h2_retro.bus1
|
||||||
h2_retro_p = h2_retro[positive_order]
|
h2_retro_p = h2_retro[positive_order]
|
||||||
swap_buses = {"bus0": "bus1", "bus1": "bus0"}
|
swap_buses = {"bus0": "bus1", "bus1": "bus0"}
|
||||||
|
@ -176,6 +176,13 @@ def define_spatial(nodes, options):
|
|||||||
spatial.coal.nodes = ["EU coal"]
|
spatial.coal.nodes = ["EU coal"]
|
||||||
spatial.coal.locations = ["EU"]
|
spatial.coal.locations = ["EU"]
|
||||||
|
|
||||||
|
if options["regional_coal_demand"]:
|
||||||
|
spatial.coal.demand_locations = nodes
|
||||||
|
spatial.coal.industry = nodes + " coal for industry"
|
||||||
|
else:
|
||||||
|
spatial.coal.demand_locations = ["EU"]
|
||||||
|
spatial.coal.industry = ["EU coal for industry"]
|
||||||
|
|
||||||
# lignite
|
# lignite
|
||||||
spatial.lignite = SimpleNamespace()
|
spatial.lignite = SimpleNamespace()
|
||||||
spatial.lignite.nodes = ["EU lignite"]
|
spatial.lignite.nodes = ["EU lignite"]
|
||||||
@ -3052,19 +3059,40 @@ def add_industry(n, costs):
|
|||||||
|
|
||||||
mwh_coal_per_mwh_coke = 1.366 # from eurostat energy balance
|
mwh_coal_per_mwh_coke = 1.366 # from eurostat energy balance
|
||||||
p_set = (
|
p_set = (
|
||||||
industrial_demand["coal"].sum()
|
industrial_demand["coal"]
|
||||||
+ mwh_coal_per_mwh_coke * industrial_demand["coke"].sum()
|
+ mwh_coal_per_mwh_coke * industrial_demand["coke"]
|
||||||
) / nhours
|
) / nhours
|
||||||
|
|
||||||
|
if not options["regional_coal_demand"]:
|
||||||
|
p_set = p_set.sum()
|
||||||
|
|
||||||
|
n.madd(
|
||||||
|
"Bus",
|
||||||
|
spatial.coal.industry,
|
||||||
|
location=spatial.coal.demand_locations,
|
||||||
|
carrier="coal for industry",
|
||||||
|
unit="MWh_LHV",
|
||||||
|
)
|
||||||
|
|
||||||
n.madd(
|
n.madd(
|
||||||
"Load",
|
"Load",
|
||||||
spatial.coal.nodes,
|
spatial.coal.industry,
|
||||||
suffix=" for industry",
|
bus=spatial.coal.industry,
|
||||||
bus=spatial.coal.nodes,
|
|
||||||
carrier="coal for industry",
|
carrier="coal for industry",
|
||||||
p_set=p_set,
|
p_set=p_set,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
n.madd(
|
||||||
|
"Link",
|
||||||
|
spatial.coal.industry,
|
||||||
|
bus0=spatial.coal.nodes,
|
||||||
|
bus1=spatial.coal.industry,
|
||||||
|
bus2="co2 atmosphere",
|
||||||
|
carrier="coal for industry",
|
||||||
|
p_nom_extendable=True,
|
||||||
|
efficiency2=costs.at["coal", "CO2 intensity"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def add_waste_heat(n):
|
def add_waste_heat(n):
|
||||||
# TODO options?
|
# TODO options?
|
||||||
|
@ -612,6 +612,7 @@ if __name__ == "__main__":
|
|||||||
"symbol",
|
"symbol",
|
||||||
"tags",
|
"tags",
|
||||||
"under_construction",
|
"under_construction",
|
||||||
|
"onshore_bus",
|
||||||
"substation_lv",
|
"substation_lv",
|
||||||
"substation_off",
|
"substation_off",
|
||||||
"geometry",
|
"geometry",
|
||||||
|
Loading…
Reference in New Issue
Block a user