Skip to content

Commit

Permalink
Merge pull request #150 from PyPSA/refineries
Browse files Browse the repository at this point in the history
Make unravelling of oil bus consistent with new refineries
  • Loading branch information
lindnemi authored Aug 6, 2024
2 parents f9096c2 + 048c46d commit 8435176
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 62 deletions.
57 changes: 29 additions & 28 deletions config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

# docs in https://pypsa-eur.readthedocs.io/en/latest/configuration.html#run
run:
prefix: 20240801offwindconnectioncosts

prefix: 240806oilrefineries

name:
# - CurrentPolicies
- KN2045_Bal_v4
Expand All @@ -18,7 +20,7 @@ run:
enable: true
manual_file: config/scenarios.manual.yaml
file: config/scenarios.automated.yaml
shared_resources:
shared_resources:
policy: base #stops recalculating
exclude:
- existing_heating.csv # specify files which should not be shared between scenarios
Expand Down Expand Up @@ -148,20 +150,20 @@ clustering:
'SE': 0.0454
# # high spatial resolution: change clusters to 44
# focus_weights:
# # 44 nodes: 30 for Germany, 2 each for Denmark and UK, 1 per each of other 10 "Stromnachbarn" (high spatial)
# 'DE': 0.6818 # 30/44
# 'AT': 0.0227 # 1/44
# 'BE': 0.0227
# 'CH': 0.0227
# 'CZ': 0.0227
# 'DK': 0.0455 # 2/44
# 'FR': 0.0227
# 'GB': 0.0455
# 'LU': 0.0227
# 'NL': 0.0227
# 'NO': 0.0227
# 'PL': 0.0227
# 'SE': 0.0227
# # 44 nodes: 30 for Germany, 2 each for Denmark and UK, 1 per each of other 10 "Stromnachbarn" (high spatial)
# 'DE': 0.6818 # 30/44
# 'AT': 0.0227 # 1/44
# 'BE': 0.0227
# 'CH': 0.0227
# 'CZ': 0.0227
# 'DK': 0.0455 # 2/44
# 'FR': 0.0227
# 'GB': 0.0455
# 'LU': 0.0227
# 'NL': 0.0227
# 'NO': 0.0227
# 'PL': 0.0227
# 'SE': 0.0227
temporal:
resolution_sector: 365H

Expand Down Expand Up @@ -213,17 +215,17 @@ limits_capacity_min:
offwind:
DE:
2030: 30 # Wind-auf-See Law
2035: 30
2035: 30
2040: 30
2045: 30
2045: 30
solar:
DE:
# EEG2023; Ziel for 2024: 88 GW and for 2026: 128 GW,
# EEG2023; Ziel for 2024: 88 GW and for 2026: 128 GW,
# assuming at least 1/3 of difference reached in 2025
2025: 101
2030: 101
2025: 101
2030: 101
2035: 101
2040: 101
2040: 101
2045: 101


Expand All @@ -244,9 +246,9 @@ limits_capacity_min:
# 2045: 70 #70 Wind-auf-See Law
# solar:
# DE:
# # EEG2023; Ziel for 2024: 88 GW and for 2026: 128 GW,
# # EEG2023; Ziel for 2024: 88 GW and for 2026: 128 GW,
# # assuming at least 1/3 of difference reached in 2025
# 2025: 101
# 2025: 101
# 2030: 215 # PV strategy
# 2035: 309
# 2040: 400 # PV strategy
Expand Down Expand Up @@ -484,11 +486,10 @@ pypsa_eur:


co2_price_add_on_fossils:
2020: 25
2020: 25
2025: 60

must_run_biomass:
enable: true
p_min_pu: 0.7
p_min_pu: 0.7
regions: ['DE']

37 changes: 23 additions & 14 deletions workflow/scripts/export_ariadne_variables.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def _get_oil_fossil_fraction(n, region):
total_oil_supply = n.statistics.supply(
bus_carrier="oil", **kwargs
).groupby("name").sum().reindex([
"DE oil",
"DE oil refining",
"DE renewable oil -> DE oil",
"EU renewable oil -> DE oil",
]).dropna() # If links are not used they are dropped here
Expand All @@ -49,7 +49,7 @@ def _get_oil_fossil_fraction(n, region):
total_oil_supply = n.statistics.supply(
bus_carrier="oil", **kwargs
).groupby("name").sum().reindex([
"EU oil",
"EU oil refining",
"DE renewable oil -> EU oil",
"EU renewable oil -> EU oil",
]).dropna()
Expand Down Expand Up @@ -833,7 +833,7 @@ def get_primary_energy(n, region):
var = pd.Series()

oil_fossil_fraction = _get_oil_fossil_fraction(n, region)

primary_oil_factor = n.links.query("carrier=='oil refining'").efficiency.unique().item()
oil_usage = n.statistics.withdrawal(
bus_carrier="oil",
**kwargs
Expand All @@ -850,14 +850,20 @@ def get_primary_energy(n, region):
## Primary Energy

var["Primary Energy|Oil|Heat"] = \
oil_usage.filter(like="urban central oil boiler").sum() + oil_CHP_H_usage
(oil_usage.filter(like="urban central oil boiler").sum() + oil_CHP_H_usage) * primary_oil_factor


var["Primary Energy|Oil|Electricity"] = \
oil_usage.get("oil", 0) + oil_CHP_E_usage
(oil_usage.get("oil", 0) + oil_CHP_E_usage) * primary_oil_factor

var["Primary Energy|Oil"] = oil_usage.sum()
var["Primary Energy|Oil"] = oil_usage.sum() * primary_oil_factor

# At the moment, everyting that is not electricity or heat is counted as liquid fuel
var["Primary Energy|Oil|Liquids"] = (
var["Primary Energy|Oil"]
- var["Primary Energy|Oil|Electricity"]
- var["Primary Energy|Oil|Heat"]
)

gas_fractions = _get_gas_fractions(n)

Expand Down Expand Up @@ -1285,7 +1291,7 @@ def get_secondary_energy(n, region, _industry_demand):
hydrogen_production.get('H2 Electrolysis', 0)

var["Secondary Energy|Hydrogen|Gas"] = \
hydrogen_production.get(["SMR","SMR CC"]).sum()
hydrogen_production.reindex(["SMR","SMR CC"]).sum()

var["Secondary Energy|Hydrogen"] = (
var["Secondary Energy|Hydrogen|Electricity"]
Expand Down Expand Up @@ -1317,7 +1323,10 @@ def get_secondary_energy(n, region, _industry_demand):
"kerosene for aviation",
"land transport oil",
"naphtha for industry",
"shipping oil"
"shipping oil",
# TODO is decentral heating oil considered a fuel?
"rural oil boiler",
"urban decentral oil boiler"
]
)

Expand Down Expand Up @@ -1496,7 +1505,6 @@ def get_final_energy(n, region, _industry_demand, _energy_totals, _sector_ratios
var[f"Final Energy|Non-Energy Use|Gases|{gas_type}"] = \
var["Final Energy|Non-Energy Use|Gases"] * gas_fractions[gas_type]

oil_fossil_fraction = _get_oil_fossil_fraction(n, region)
var["Final Energy|Non-Energy Use|Liquids"] = non_energy.naphtha

var["Final Energy|Non-Energy Use|Liquids|Petroleum"] = non_energy.naphtha * oil_fossil_fraction
Expand Down Expand Up @@ -1726,7 +1734,6 @@ def get_final_energy(n, region, _industry_demand, _energy_totals, _sector_ratios

# var["Final Energy|Residential and Commercial|Hydrogen"] = \
# ! Not implemented
oil_fossil_fraction = _get_oil_fossil_fraction(n, region)
oil_usage = n.statistics.withdrawal(
bus_carrier="oil", **kwargs
).filter(like=region).groupby(
Expand Down Expand Up @@ -2340,15 +2347,17 @@ def get_emissions(n, region, _energy_totals):
"waste CHP CC",
]).sum()

# var["Emissions|CO2|Energy|Supply|Liquids and Gases"] = \
# var["Emissions|CO2|Energy|Supply|Liquids"]
var["Emissions|CO2|Energy|Supply|Liquids and Gases"] = \
var["Emissions|CO2|Energy|Supply|Liquids"] = \
co2_emissions.get("oil refining", 0)

# var["Emissions|CO2|Energy|Supply|Gases"] + \

var["Emissions|CO2|Energy|Supply"] = \
var["Emissions|CO2|Energy|Supply|Gases"] + \
var["Emissions|CO2|Energy|Supply|Hydrogen"] + \
var["Emissions|CO2|Energy|Supply|Electricity and Heat"] # + \
# var["Emissions|CO2|Energy|Supply|Liquids"]
var["Emissions|CO2|Energy|Supply|Electricity and Heat"] + \
var["Emissions|CO2|Energy|Supply|Liquids"]

# var["Emissions|CO2|Energy|Supply|Other Sector"] = \
# var["Emissions|CO2|Energy|Supply|Solids"] = \
Expand Down
53 changes: 34 additions & 19 deletions workflow/scripts/modify_prenetwork.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def first_technology_occurrence(n):
if int(snakemake.wildcards.planning_horizons) < first_year:
logger.info(f"{carrier} not extendable before {first_year}.")
n.df(c).loc[n.df(c).carrier == carrier, "p_nom_extendable"] = False


def fix_new_boiler_profiles(n):

Expand Down Expand Up @@ -100,7 +100,7 @@ def coal_generation_ban(n):
logger.info(f"Dropping {links}")
n.links.drop(links,
inplace=True)

def nuclear_generation_ban(n):

year = int(snakemake.wildcards.planning_horizons)
Expand All @@ -112,7 +112,7 @@ def nuclear_generation_ban(n):
links = n.links.index[(n.links.index.str[:2] == ct) & n.links.carrier.isin(["nuclear"])]
logger.info(f"Dropping {links}")
n.links.drop(links,
inplace=True)
inplace=True)


def add_reversed_pipes(df):
Expand Down Expand Up @@ -141,7 +141,7 @@ def reduce_capacity(targets, origins, carrier, origin_attr="removed_gas_cap", ta

def apply_cut(row):
match = targets[
(targets.bus0 == row.bus0 + " " + carrier) &
(targets.bus0 == row.bus0 + " " + carrier) &
(targets.bus1 == row.bus1 + " " + carrier)
].sort_index()
cut = row[origin_attr] * conversion_rate
Expand Down Expand Up @@ -249,33 +249,48 @@ def unravel_oilbus(n):
# add buses
n.add("Bus", "DE", location="DE", x=10.5, y=51.2, carrier="none")
n.add("Bus", "DE oil", location="DE", x=10.5, y=51.2, carrier="oil")
n.add("Bus", "DE oil primary", location="DE", x=10.5, y=51.2, carrier="oil primary")
n.add("Bus", "DE renewable oil", location="DE", x=10.5, y=51.2, carrier="renewable oil")
n.add(
"Bus",
"EU renewable oil",
"Bus",
"EU renewable oil",
location="EU",
x=n.buses.loc["EU","x"],
y=n.buses.loc["EU","y"],
carrier="renewable oil"
)

# add one generator for DE oil
# add one generator for DE oil primary
n.add("Generator",
name="DE oil",
bus="DE oil",
carrier="oil",
name="DE oil primary",
bus="DE oil primary",
carrier="oil primary",
p_nom_extendable=True,
marginal_cost=n.generators.loc["EU oil"].marginal_cost,
marginal_cost=n.generators.loc["EU oil primary"].marginal_cost,
)


# add link for DE oil refining
n.add(
"Link",
"DE oil refining",
bus0="DE oil primary",
bus1="DE oil",
bus2="co2 atmosphere",
location="DE",
carrier="oil refining",
p_nom=1e6,
efficiency=1 - (snakemake.config["industry"]["fuel_refining"]["oil"]["emissions"] / costs.at["oil", "CO2 intensity"]),
efficiency2=snakemake.config["industry"]["fuel_refining"]["oil"]["emissions"],
)

# change links from EU oil to DE oil
german_oil_links = n.links[(n.links.bus0=="EU oil") & (n.links.index.str.contains("DE"))].index
german_FT_links = n.links[(n.links.bus1=="EU oil") & (n.links.index.str.contains("DE"))].index
german_FT_links = n.links[(n.links.bus1=="EU oil") & (n.links.index.str.contains("DE")) & (n.links.index.str.contains("Fischer-Tropsch"))].index
n.links.loc[german_oil_links, "bus0"] = "DE oil"
n.links.loc[german_FT_links, "bus1"] = "DE renewable oil"

# change FT links in rest of Europe
europ_FT_links = n.links[n.links.bus1=="EU oil"].index
europ_FT_links = n.links[(n.links.bus1=="EU oil") & (n.links.index.str.contains("Fischer-Tropsch"))].index
n.links.loc[europ_FT_links, "bus1"] = "EU renewable oil"

# add links between oil buses
Expand All @@ -298,19 +313,19 @@ def unravel_oilbus(n):
e_cyclic=True,
capital_cost=0.02,
)

# unravel meoh
logger.info("Unraveling methanol bus")
# add bus
n.add(
"Bus",
"DE methanol",
"Bus",
"DE methanol",
location="DE",
x=n.buses.loc["DE","x"],
y=n.buses.loc["DE","y"],
carrier="methanol"
)

# change links from EU meoh to DE meoh
DE_meoh_out = n.links[(n.links.bus0=="EU methanol") & (n.links.index.str[:2]=="DE")].index
n.links.loc[DE_meoh_out, "bus0"] = "DE methanol"
Expand Down Expand Up @@ -495,7 +510,7 @@ def aladin_mobility_demand(n):
remove_old_boiler_profiles(n)

coal_generation_ban(n)

nuclear_generation_ban(n)

first_technology_occurrence(n)
Expand Down

0 comments on commit 8435176

Please sign in to comment.