commit
1b205b9e4a
@ -26,7 +26,7 @@ the energy system and includes all greenhouse gas emitters except
|
||||
waste management, agriculture, forestry and land use.
|
||||
|
||||
Please see the [documentation](https://pypsa-eur-sec.readthedocs.io/)
|
||||
for installation instructions and other useful information.
|
||||
for installation instructions and other useful information about the snakemake workflow.
|
||||
|
||||
This diagram gives an overview of the sectors and the links between
|
||||
them:
|
||||
|
@ -15,19 +15,21 @@ scenario:
|
||||
lv: [1.0,1.5] # allowed transmission line volume expansion, can be any float >= 1.0 (today) or "opt"
|
||||
clusters: [45,50] # number of nodes in Europe, any integer between 37 (1 node per country-zone) and several hundred
|
||||
opts: [''] # only relevant for PyPSA-Eur
|
||||
sector_opts: [Co2L0-3H-T-H-B-I-solar3-dist1] # this is where the main scenario settings are
|
||||
sector_opts: [Co2L0-3H-T-H-B-I-solar+p3-dist1] # this is where the main scenario settings are
|
||||
# to really understand the options here, look in scripts/prepare_sector_network.py
|
||||
# Co2Lx specifies the CO2 target in x% of the 1990 values; default will give default (5%);
|
||||
# Co2L0p25 will give 25% CO2 emissions; Co2Lm0p05 will give 5% negative emissions
|
||||
# xH is the temporal resolution; 3H is 3-hourly, i.e. one snapshot every 3 hours
|
||||
# single letters are sectors: T for land transport, H for building heating,
|
||||
# B for biomass supply, I for industry, shipping and aviation
|
||||
# solarx or onwindx changes the available installable potential by factor x
|
||||
# dist{n} includes distribution grids with investment cost of n times cost in data/costs.csv
|
||||
# solar+c0.5 reduces the capital cost of solar to 50\% of reference value
|
||||
# solar+p3 multiplies the available installable potential by factor 3
|
||||
# dist{n} includes distribution grids with investment cost of n times cost in data/costs.csv
|
||||
# for myopic/perfect foresight cb states the carbon budget in GtCO2 (cumulative
|
||||
# emissions throughout the transition path in the timeframe determined by the
|
||||
# planning_horizons), be:beta decay; ex:exponential decay
|
||||
# cb40ex0 distributes a carbon budget of 40 GtCO2 following an exponential
|
||||
# decay with initial growth rate 0
|
||||
planning_horizons : [2030] # investment years for myopic and perfect; or costs year for overnight
|
||||
# for example, set to [2020, 2030, 2040, 2050] for myopic foresight
|
||||
|
||||
@ -69,6 +71,7 @@ pypsa_eur:
|
||||
"Link": ["DC"]
|
||||
"Generator": ["onwind", "offwind-ac", "offwind-dc", "solar", "ror"]
|
||||
"StorageUnit": ["PHS","hydro"]
|
||||
"Store": []
|
||||
|
||||
biomass:
|
||||
year: 2030
|
||||
@ -99,7 +102,7 @@ sector:
|
||||
'bev_dsm' : True #turns on EV battery
|
||||
'bev_availability' : 0.5 #How many cars do smart charging
|
||||
'v2g' : True #allows feed-in to grid from EV battery
|
||||
#what is not EV or FCEV is fossil-fuelled
|
||||
#what is not EV or FCEV is oil-fuelled ICE
|
||||
'land_transport_fuel_cell_share': # 1 means all FCEVs
|
||||
2020: 0
|
||||
2030: 0.05
|
||||
@ -331,7 +334,7 @@ plotting:
|
||||
"Fischer-Tropsch" : "#44DD33"
|
||||
"kerosene for aviation": "#44BB11"
|
||||
"naphtha for industry" : "#44FF55"
|
||||
"land transport fossil" : "#44DD33"
|
||||
"land transport oil" : "#44DD33"
|
||||
"water tanks" : "#BBBBBB"
|
||||
"hot water storage" : "#BBBBBB"
|
||||
"hot water charging" : "#BBBBBB"
|
||||
@ -366,6 +369,7 @@ plotting:
|
||||
"process emissions to stored" : "#444444"
|
||||
"process emissions to atmosphere" : "#888888"
|
||||
"process emissions" : "#222222"
|
||||
"oil emissions" : "#666666"
|
||||
"land transport fuel cell" : "#AAAAAA"
|
||||
"biogas" : "#800000"
|
||||
"solid biomass" : "#DAA520"
|
||||
|
@ -2,7 +2,7 @@ description,file/folder,licence,source
|
||||
JRC IDEES database,jrc-idees-2015/,CC BY 4.0,https://ec.europa.eu/jrc/en/potencia/jrc-idees
|
||||
urban/rural fraction,urban_percent.csv,unknown,unknown
|
||||
JRC biomass potentials,biomass/,unknown,https://doi.org/10.2790/39014
|
||||
EEA emission statistics,eea/,unknown,https://www.eea.europa.eu/data-and-maps/data/national-emissions-reported-to-the-unfccc-and-to-the-eu-greenhouse-gas-monitoring-mechanism-14
|
||||
EEA emission statistics,eea/UNFCCC_v23.csv,EEA standard re-use policy,https://www.eea.europa.eu/data-and-maps/data/national-emissions-reported-to-the-unfccc-and-to-the-eu-greenhouse-gas-monitoring-mechanism-16
|
||||
Eurostat Energy Balances,eurostat-energy_balances-*/,Eurostat,https://ec.europa.eu/eurostat/web/energy/data/energy-balances
|
||||
Swiss energy statistics from Swiss Federal Office of Energy,switzerland-sfoe/,unknown,http://www.bfe.admin.ch/themen/00526/00541/00542/02167/index.html?dossier_id=02169
|
||||
BASt emobility statistics,emobility/,unknown,http://www.bast.de/DE/Verkehrstechnik/Fachthemen/v2-verkehrszaehlung/Stundenwerte.html?nn=626916
|
||||
|
|
@ -73,8 +73,8 @@ To download and extract the data bundle on the command line:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
projects/pypsa-eur-sec/data % wget "https://nworbmot.org/pypsa-eur-sec-data-bundle-201012.tar.gz"
|
||||
projects/pypsa-eur-sec/data % tar xvzf pypsa-eur-sec-data-bundle-201012.tar.gz
|
||||
projects/pypsa-eur-sec/data % wget "https://nworbmot.org/pypsa-eur-sec-data-bundle-210125.tar.gz"
|
||||
projects/pypsa-eur-sec/data % tar xvzf pypsa-eur-sec-data-bundle-210125.tar.gz
|
||||
|
||||
|
||||
The data licences and sources are given in the following table.
|
||||
|
@ -4,8 +4,13 @@ Release Notes
|
||||
|
||||
Future release
|
||||
===================
|
||||
*For the myopic option, a carbon budget and a type of decay (exponential or beta) can be selected in the config file to distribute the budget across the planning_horizons.
|
||||
*Added an option to alter the capital cost of carriers by a factor via ``carrier+factor`` in the ``{opts}`` wildcard. This can be useful for exploring uncertain cost parameters. Example: ``solar+0.5`` reduces the capital cost of solar to 50\% of original values.
|
||||
|
||||
* For the myopic investment option, a carbon budget and a type of decay (exponential or beta) can be selected in the ``config.yaml`` file to distribute the budget across the ``planning_horizons``. For example, ``cb40ex0`` in the ``{sector_opts}`` wildcard will distribute a carbon budget of 40 GtCO2 following an exponential decay with initial growth rate 0.
|
||||
* Added an option to alter the capital cost or maximum capacity of carriers by a factor via ``carrier+factor`` in the ``{sector_opts}`` wildcard. This can be useful for exploring uncertain cost parameters. Example: ``solar+c0.5`` reduces the ``capital_cost`` of solar to 50\% of original values. Similarly ``solar+p3`` multiplies the ``p_nom_max`` by 3.
|
||||
* Rename the bus for European liquid hydrocarbons from ``Fischer-Tropsch`` to ``EU oil``, since it can be supplied not just with the Fischer-Tropsch process, but also with fossil oil.
|
||||
* Bugfix: Fix reading in of ``pypsa-eur/resources/powerplants.csv`` to PyPSA-Eur Version 0.3.0 (use column attribute name ``DateIn`` instead of old ``YearDecommissioned``).
|
||||
* Bugfix: Make sure that ``Store`` components (battery and H2) are also removed from PyPSA-Eur, so they can be added later by PyPSA-Eur-Sec.
|
||||
|
||||
|
||||
PyPSA-Eur-Sec 0.4.0 (11th December 2020)
|
||||
=========================================
|
||||
@ -131,4 +136,4 @@ To make a new release of the data bundle, make an archive of the files in ``data
|
||||
|
||||
.. code:: bash
|
||||
|
||||
data % tar pczf pypsa-eur-sec-data-bundle-date.tar.gz eea switzerland-sfoe biomass eurostat-energy_balances-* jrc-idees-2015 emobility urban_percent.csv timezone_mappings.csv heat_load_profile_DK_AdamJensen.csv WindWaveWEC_GLTB.xlsx myb1-2017-nitro.xls Industrial_Database.csv
|
||||
data % tar pczf pypsa-eur-sec-data-bundle-YYMMDD.tar.gz eea/UNFCCC_v23.csv switzerland-sfoe biomass eurostat-energy_balances-* jrc-idees-2015 emobility urban_percent.csv timezone_mappings.csv heat_load_profile_DK_AdamJensen.csv WindWaveWEC_GLTB.xlsx myb1-2017-nitro.xls Industrial_Database.csv
|
||||
|
@ -62,6 +62,9 @@ if __name__ == "__main__":
|
||||
with open('config.yaml', encoding='utf8') as f:
|
||||
snakemake.config = yaml.safe_load(f)
|
||||
|
||||
|
||||
# This is a hack, to be replaced once snakemake is unicode-conform
|
||||
|
||||
if 'Secondary Forestry residues sawdust' in snakemake.config['biomass']['classes']['solid biomass']:
|
||||
snakemake.config['biomass']['classes']['solid biomass'].remove('Secondary Forestry residues sawdust')
|
||||
snakemake.config['biomass']['classes']['solid biomass'].append('Secondary Forestry residues – sawdust')
|
||||
|
@ -1,6 +1,6 @@
|
||||
|
||||
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
#allow plotting without Xwindows
|
||||
@ -9,7 +9,7 @@ matplotlib.use('Agg')
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
from prepare_sector_network import co2_emissions_year
|
||||
|
||||
#consolidate and rename
|
||||
def rename_techs(label):
|
||||
@ -237,7 +237,137 @@ def plot_balances():
|
||||
|
||||
fig.savefig(snakemake.output.balances[:-10] + k + ".pdf",transparent=True)
|
||||
|
||||
def historical_emissions(cts):
|
||||
"""
|
||||
read historical emissions to add them to the carbon budget plot
|
||||
"""
|
||||
#https://www.eea.europa.eu/data-and-maps/data/national-emissions-reported-to-the-unfccc-and-to-the-eu-greenhouse-gas-monitoring-mechanism-16
|
||||
#downloaded 201228 (modified by EEA last on 201221)
|
||||
fn = "data/eea/UNFCCC_v23.csv"
|
||||
df = pd.read_csv(fn, encoding="latin-1")
|
||||
df.loc[df["Year"] == "1985-1987","Year"] = 1986
|
||||
df["Year"] = df["Year"].astype(int)
|
||||
df = df.set_index(['Year', 'Sector_name', 'Country_code', 'Pollutant_name']).sort_index()
|
||||
|
||||
e = pd.Series()
|
||||
e["electricity"] = '1.A.1.a - Public Electricity and Heat Production'
|
||||
e['residential non-elec'] = '1.A.4.b - Residential'
|
||||
e['services non-elec'] = '1.A.4.a - Commercial/Institutional'
|
||||
e['rail non-elec'] = "1.A.3.c - Railways"
|
||||
e["road non-elec"] = '1.A.3.b - Road Transportation'
|
||||
e["domestic navigation"] = "1.A.3.d - Domestic Navigation"
|
||||
e['international navigation'] = '1.D.1.b - International Navigation'
|
||||
e["domestic aviation"] = '1.A.3.a - Domestic Aviation'
|
||||
e["international aviation"] = '1.D.1.a - International Aviation'
|
||||
e['total energy'] = '1 - Energy'
|
||||
e['industrial processes'] = '2 - Industrial Processes and Product Use'
|
||||
e['agriculture'] = '3 - Agriculture'
|
||||
e['LULUCF'] = '4 - Land Use, Land-Use Change and Forestry'
|
||||
e['waste management'] = '5 - Waste management'
|
||||
e['other'] = '6 - Other Sector'
|
||||
e['indirect'] = 'ind_CO2 - Indirect CO2'
|
||||
e["total wL"] = "Total (with LULUCF)"
|
||||
e["total woL"] = "Total (without LULUCF)"
|
||||
|
||||
pol = ["CO2"] # ["All greenhouse gases - (CO2 equivalent)"]
|
||||
cts
|
||||
if "GB" in cts:
|
||||
cts.remove("GB")
|
||||
cts.append("UK")
|
||||
|
||||
year = np.arange(1990,2018).tolist()
|
||||
|
||||
idx = pd.IndexSlice
|
||||
co2_totals = df.loc[idx[year,e.values,cts,pol],"emissions"].unstack("Year").rename(index=pd.Series(e.index,e.values))
|
||||
|
||||
co2_totals = (1/1e6)*co2_totals.groupby(level=0, axis=0).sum() #Gton CO2
|
||||
|
||||
co2_totals.loc['industrial non-elec'] = co2_totals.loc['total energy'] - co2_totals.loc[['electricity', 'services non-elec','residential non-elec', 'road non-elec',
|
||||
'rail non-elec', 'domestic aviation', 'international aviation', 'domestic navigation',
|
||||
'international navigation']].sum()
|
||||
|
||||
emissions = co2_totals.loc["electricity"]
|
||||
if "T" in opts:
|
||||
emissions += co2_totals.loc[[i+ " non-elec" for i in ["rail","road"]]].sum()
|
||||
if "H" in opts:
|
||||
emissions += co2_totals.loc[[i+ " non-elec" for i in ["residential","services"]]].sum()
|
||||
if "I" in opts:
|
||||
emissions += co2_totals.loc[["industrial non-elec","industrial processes",
|
||||
"domestic aviation","international aviation",
|
||||
"domestic navigation","international navigation"]].sum()
|
||||
return emissions
|
||||
|
||||
|
||||
|
||||
def plot_carbon_budget_distribution():
|
||||
"""
|
||||
Plot historical carbon emissions in the EU and decarbonization path
|
||||
"""
|
||||
|
||||
import matplotlib.gridspec as gridspec
|
||||
import seaborn as sns; sns.set()
|
||||
sns.set_style('ticks')
|
||||
plt.style.use('seaborn-ticks')
|
||||
plt.rcParams['xtick.direction'] = 'in'
|
||||
plt.rcParams['ytick.direction'] = 'in'
|
||||
plt.rcParams['xtick.labelsize'] = 20
|
||||
plt.rcParams['ytick.labelsize'] = 20
|
||||
|
||||
plt.figure(figsize=(10, 7))
|
||||
gs1 = gridspec.GridSpec(1, 1)
|
||||
ax1 = plt.subplot(gs1[0,0])
|
||||
ax1.set_ylabel('CO$_2$ emissions (Gt per year)',fontsize=22)
|
||||
ax1.set_ylim([0,5])
|
||||
ax1.set_xlim([1990,snakemake.config['scenario']['planning_horizons'][-1]+1])
|
||||
|
||||
path_cb = snakemake.config['results_dir'] + snakemake.config['run'] + '/csvs/'
|
||||
countries=pd.read_csv(path_cb + 'countries.csv', index_col=1)
|
||||
cts=countries.index.to_list()
|
||||
e_1990 = co2_emissions_year(cts, opts, year=1990)
|
||||
CO2_CAP=pd.read_csv(path_cb + 'carbon_budget_distribution.csv',
|
||||
index_col=0)
|
||||
|
||||
|
||||
ax1.plot(e_1990*CO2_CAP[o],linewidth=3,
|
||||
color='dodgerblue', label=None)
|
||||
|
||||
emissions = historical_emissions(cts)
|
||||
|
||||
ax1.plot(emissions, color='black', linewidth=3, label=None)
|
||||
|
||||
#plot commited and uder-discussion targets
|
||||
#(notice that historical emissions include all countries in the
|
||||
# network, but targets refer to EU)
|
||||
ax1.plot([2020],[0.8*emissions[1990]],
|
||||
marker='*', markersize=12, markerfacecolor='black',
|
||||
markeredgecolor='black')
|
||||
|
||||
ax1.plot([2030],[0.45*emissions[1990]],
|
||||
marker='*', markersize=12, markerfacecolor='white',
|
||||
markeredgecolor='black')
|
||||
|
||||
ax1.plot([2030],[0.6*emissions[1990]],
|
||||
marker='*', markersize=12, markerfacecolor='black',
|
||||
markeredgecolor='black')
|
||||
|
||||
ax1.plot([2050, 2050],[x*emissions[1990] for x in [0.2, 0.05]],
|
||||
color='gray', linewidth=2, marker='_', alpha=0.5)
|
||||
|
||||
ax1.plot([2050],[0.01*emissions[1990]],
|
||||
marker='*', markersize=12, markerfacecolor='white',
|
||||
linewidth=0, markeredgecolor='black',
|
||||
label='EU under-discussion target', zorder=10,
|
||||
clip_on=False)
|
||||
|
||||
ax1.plot([2050],[0.125*emissions[1990]],'ro',
|
||||
marker='*', markersize=12, markerfacecolor='black',
|
||||
markeredgecolor='black', label='EU commited target')
|
||||
|
||||
ax1.legend(fancybox=True, fontsize=18, loc=(0.01,0.01),
|
||||
facecolor='white', frameon=True)
|
||||
|
||||
path_cb_plot = snakemake.config['results_dir'] + snakemake.config['run'] + '/graphs/'
|
||||
plt.savefig(path_cb_plot+'carbon_budget_plot.pdf', dpi=300)
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Detect running outside of snakemake and mock snakemake for testing
|
||||
@ -249,12 +379,15 @@ if __name__ == "__main__":
|
||||
snakemake.config = yaml.safe_load(f)
|
||||
snakemake.input = Dict()
|
||||
snakemake.output = Dict()
|
||||
snakemake.wildcards = Dict()
|
||||
#snakemake.wildcards['sector_opts']='3H-T-H-B-I-solar3-dist1-cb48be3'
|
||||
|
||||
for item in ["costs", "energy"]:
|
||||
snakemake.input[item] = snakemake.config['summary_dir'] + '/{name}/csvs/{item}.csv'.format(name=snakemake.config['run'],item=item)
|
||||
snakemake.output[item] = snakemake.config['summary_dir'] + '/{name}/graphs/{item}.pdf'.format(name=snakemake.config['run'],item=item)
|
||||
snakemake.input["balances"] = snakemake.config['summary_dir'] + '/test/csvs/supply_energy.csv'
|
||||
snakemake.output["balances"] = snakemake.config['summary_dir'] + '/test/graphs/balances-energy.csv'
|
||||
snakemake.input["balances"] = snakemake.config['summary_dir'] + '/{name}/csvs/supply_energy.csv'.format(name=snakemake.config['run'],item=item)
|
||||
snakemake.output["balances"] = snakemake.config['summary_dir'] + '/{name}/graphs/balances-energy.csv'.format(name=snakemake.config['run'],item=item)
|
||||
|
||||
|
||||
n_header = 4
|
||||
|
||||
@ -263,3 +396,9 @@ if __name__ == "__main__":
|
||||
plot_energy()
|
||||
|
||||
plot_balances()
|
||||
|
||||
for sector_opts in snakemake.config['scenario']['sector_opts']:
|
||||
opts=sector_opts.split('-')
|
||||
for o in opts:
|
||||
if "cb" in o:
|
||||
plot_carbon_budget_distribution()
|
@ -48,13 +48,16 @@ override_component_attrs["Store"].loc["build_year"] = ["integer","year",np.nan,"
|
||||
override_component_attrs["Store"].loc["lifetime"] = ["float","years",np.nan,"lifetime","Input (optional)"]
|
||||
|
||||
|
||||
def co2_emissions_year(year):
|
||||
|
||||
def co2_emissions_year(cts, opts, year):
|
||||
|
||||
"""
|
||||
calculate co2 emissions in one specific year (e.g. 1990 or 2018).
|
||||
"""
|
||||
eea_co2 = build_eea_co2(year)
|
||||
|
||||
#TODO: read Eurostat data from year>2014, this only affects the estimation of
|
||||
|
||||
# CO2 emissions for "BA","RS","AL","ME","MK"
|
||||
if year > 2014:
|
||||
eurostat_co2 = build_eurostat_co2(year=2014)
|
||||
@ -63,9 +66,6 @@ def co2_emissions_year(year):
|
||||
|
||||
co2_totals=build_co2_totals(eea_co2, eurostat_co2, year)
|
||||
|
||||
pop_layout = pd.read_csv(snakemake.input.clustered_pop_layout, index_col=0)
|
||||
pop_layout["ct"] = pop_layout.index.str[:2]
|
||||
cts = pop_layout.ct.value_counts().index
|
||||
|
||||
co2_emissions = co2_totals.loc[cts, "electricity"].sum()
|
||||
|
||||
@ -80,69 +80,6 @@ def co2_emissions_year(year):
|
||||
co2_emissions *=0.001 #MtCO2 to GtCO2
|
||||
return co2_emissions
|
||||
|
||||
def historical_emissions():
|
||||
"""
|
||||
read historical emissions to add them to the carbon budget plot
|
||||
"""
|
||||
#https://www.eea.europa.eu/data-and-maps/data/national-emissions-reported-to-the-unfccc-and-to-the-eu-greenhouse-gas-monitoring-mechanism-16
|
||||
#downloaded 201228 (modified by EEA last on 201221)
|
||||
fn = "data/eea/UNFCCC_v23.csv"
|
||||
df = pd.read_csv(fn, encoding="latin-1")
|
||||
df.loc[df["Year"] == "1985-1987","Year"] = 1986
|
||||
df["Year"] = df["Year"].astype(int)
|
||||
df = df.set_index(['Year', 'Sector_name', 'Country_code', 'Pollutant_name']).sort_index()
|
||||
|
||||
e = pd.Series()
|
||||
e["electricity"] = '1.A.1.a - Public Electricity and Heat Production'
|
||||
e['residential non-elec'] = '1.A.4.b - Residential'
|
||||
e['services non-elec'] = '1.A.4.a - Commercial/Institutional'
|
||||
e['rail non-elec'] = "1.A.3.c - Railways"
|
||||
e["road non-elec"] = '1.A.3.b - Road Transportation'
|
||||
e["domestic navigation"] = "1.A.3.d - Domestic Navigation"
|
||||
e['international navigation'] = '1.D.1.b - International Navigation'
|
||||
e["domestic aviation"] = '1.A.3.a - Domestic Aviation'
|
||||
e["international aviation"] = '1.D.1.a - International Aviation'
|
||||
e['total energy'] = '1 - Energy'
|
||||
e['industrial processes'] = '2 - Industrial Processes and Product Use'
|
||||
e['agriculture'] = '3 - Agriculture'
|
||||
e['LULUCF'] = '4 - Land Use, Land-Use Change and Forestry'
|
||||
e['waste management'] = '5 - Waste management'
|
||||
e['other'] = '6 - Other Sector'
|
||||
e['indirect'] = 'ind_CO2 - Indirect CO2'
|
||||
e["total wL"] = "Total (with LULUCF)"
|
||||
e["total woL"] = "Total (without LULUCF)"
|
||||
|
||||
pol = ["CO2"] # ["All greenhouse gases - (CO2 equivalent)"]
|
||||
|
||||
|
||||
pop_layout = pd.read_csv(snakemake.input.clustered_pop_layout, index_col=0)
|
||||
pop_layout["ct"] = pop_layout.index.str[:2]
|
||||
cts = pop_layout.ct.value_counts().index.to_list()
|
||||
if "GB" in cts:
|
||||
cts.remove("GB")
|
||||
cts.append("UK")
|
||||
|
||||
year = np.arange(1990,2018).tolist()
|
||||
|
||||
idx = pd.IndexSlice
|
||||
co2_totals = df.loc[idx[year,e.values,cts,pol],"emissions"].unstack("Year").rename(index=pd.Series(e.index,e.values))
|
||||
|
||||
co2_totals = (1/1e6)*co2_totals.groupby(level=0, axis=0).sum() #Gton CO2
|
||||
|
||||
co2_totals.loc['industrial non-elec'] = co2_totals.loc['total energy'] - co2_totals.loc[['electricity', 'services non-elec','residential non-elec', 'road non-elec',
|
||||
'rail non-elec', 'domestic aviation', 'international aviation', 'domestic navigation',
|
||||
'international navigation']].sum()
|
||||
|
||||
emissions = co2_totals.loc["electricity"]
|
||||
if "T" in opts:
|
||||
emissions += co2_totals.loc[[i+ " non-elec" for i in ["rail","road"]]].sum()
|
||||
if "H" in opts:
|
||||
emissions += co2_totals.loc[[i+ " non-elec" for i in ["residential","services"]]].sum()
|
||||
if "I" in opts:
|
||||
emissions += co2_totals.loc[["industrial non-elec","industrial processes",
|
||||
"domestic aviation","international aviation",
|
||||
"domestic navigation","international navigation"]].sum()
|
||||
return emissions
|
||||
|
||||
|
||||
def build_carbon_budget(o):
|
||||
@ -156,10 +93,15 @@ def build_carbon_budget(o):
|
||||
carbon_budget = float(o[o.find("cb")+2:o.find("ex")])
|
||||
r=float(o[o.find("ex")+2:])
|
||||
|
||||
e_1990 = co2_emissions_year(year=1990)
|
||||
|
||||
pop_layout = pd.read_csv(snakemake.input.clustered_pop_layout, index_col=0)
|
||||
pop_layout["ct"] = pop_layout.index.str[:2]
|
||||
cts = pop_layout.ct.value_counts().index
|
||||
|
||||
e_1990 = co2_emissions_year(cts, opts, year=1990)
|
||||
|
||||
#emissions at the beginning of the path (last year available 2018)
|
||||
e_0 = co2_emissions_year(year=2018)
|
||||
e_0 = co2_emissions_year(cts, opts, year=2018)
|
||||
#emissions in 2019 and 2020 assumed equal to 2018 and substracted
|
||||
carbon_budget -= 2*e_0
|
||||
planning_horizons = snakemake.config['scenario']['planning_horizons']
|
||||
@ -181,71 +123,13 @@ def build_carbon_budget(o):
|
||||
m=(1+np.sqrt(1+r*T))/T
|
||||
CO2_CAP[o] = [(e_0/e_1990)*(1+(m+r)*(t-t_0))*np.exp(-m*(t-t_0)) for t in planning_horizons]
|
||||
|
||||
|
||||
CO2_CAP.to_csv(path_cb + 'carbon_budget_distribution.csv', sep=',',
|
||||
line_terminator='\n', float_format='%.3f')
|
||||
countries=pd.Series(data=cts)
|
||||
countries.to_csv(path_cb + 'countries.csv', sep=',',
|
||||
line_terminator='\n', float_format='%.3f')
|
||||
|
||||
"""
|
||||
Plot historical carbon emissions in the EU and decarbonization path
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.gridspec as gridspec
|
||||
import seaborn as sns; sns.set()
|
||||
sns.set_style('ticks')
|
||||
plt.style.use('seaborn-ticks')
|
||||
plt.rcParams['xtick.direction'] = 'in'
|
||||
plt.rcParams['ytick.direction'] = 'in'
|
||||
plt.rcParams['xtick.labelsize'] = 20
|
||||
plt.rcParams['ytick.labelsize'] = 20
|
||||
|
||||
plt.figure(figsize=(10, 7))
|
||||
gs1 = gridspec.GridSpec(1, 1)
|
||||
ax1 = plt.subplot(gs1[0,0])
|
||||
ax1.set_ylabel('CO$_2$ emissions (Gt per year)',fontsize=22)
|
||||
ax1.set_ylim([0,5])
|
||||
ax1.set_xlim([1990,planning_horizons[-1]+1])
|
||||
ax1.plot(e_1990*CO2_CAP[o],linewidth=3,
|
||||
color='dodgerblue', label=None)
|
||||
|
||||
emissions = historical_emissions()
|
||||
|
||||
ax1.plot(emissions, color='black', linewidth=3, label=None)
|
||||
|
||||
#plot commited and uder-discussion targets
|
||||
#(notice that historical emissions include all countries in the
|
||||
# network, but targets refer to EU)
|
||||
ax1.plot([2020],[0.8*emissions[1990]],
|
||||
marker='*', markersize=12, markerfacecolor='black',
|
||||
markeredgecolor='black')
|
||||
|
||||
ax1.plot([2030],[0.45*emissions[1990]],
|
||||
marker='*', markersize=12, markerfacecolor='white',
|
||||
markeredgecolor='black')
|
||||
|
||||
ax1.plot([2030],[0.6*emissions[1990]],
|
||||
marker='*', markersize=12, markerfacecolor='black',
|
||||
markeredgecolor='black')
|
||||
|
||||
ax1.plot([2050, 2050],[x*emissions[1990] for x in [0.2, 0.05]],
|
||||
color='gray', linewidth=2, marker='_', alpha=0.5)
|
||||
|
||||
ax1.plot([2050],[0.01*emissions[1990]],
|
||||
marker='*', markersize=12, markerfacecolor='white',
|
||||
linewidth=0, markeredgecolor='black',
|
||||
label='EU under-discussion target', zorder=10,
|
||||
clip_on=False)
|
||||
|
||||
ax1.plot([2050],[0.125*emissions[1990]],'ro',
|
||||
marker='*', markersize=12, markerfacecolor='black',
|
||||
markeredgecolor='black', label='EU commited target')
|
||||
|
||||
ax1.legend(fancybox=True, fontsize=18, loc=(0.01,0.01),
|
||||
facecolor='white', frameon=True)
|
||||
|
||||
path_cb_plot = snakemake.config['results_dir'] + snakemake.config['run'] + '/graphs/'
|
||||
if not os.path.exists(path_cb_plot):
|
||||
os.makedirs(path_cb_plot)
|
||||
print('carbon budget distribution saved to ' + path_cb_plot + 'carbon_budget_plot.pdf')
|
||||
plt.savefig(path_cb_plot+'carbon_budget_plot.pdf', dpi=300)
|
||||
|
||||
def add_lifetime_wind_solar(n):
|
||||
"""
|
||||
@ -799,7 +683,7 @@ def add_generation(network):
|
||||
capital_cost=0.) #could correct to e.g. 0.2 EUR/kWh * annuity and O&M
|
||||
|
||||
network.add("Generator",
|
||||
"EU fossil " + carrier,
|
||||
"EU " + carrier,
|
||||
bus="EU " + carrier,
|
||||
p_nom_extendable=True,
|
||||
carrier=carrier,
|
||||
@ -1179,14 +1063,14 @@ def add_land_transport(network):
|
||||
|
||||
fuel_cell_share = get_parameter(options["land_transport_fuel_cell_share"])
|
||||
electric_share = get_parameter(options["land_transport_electric_share"])
|
||||
fossil_share = 1 - fuel_cell_share - electric_share
|
||||
ice_share = 1 - fuel_cell_share - electric_share
|
||||
|
||||
print("shares of FCEV, EV and ICEV are",
|
||||
fuel_cell_share,
|
||||
electric_share,
|
||||
fossil_share)
|
||||
ice_share)
|
||||
|
||||
if fossil_share < 0:
|
||||
if ice_share < 0:
|
||||
print("Error, more FCEV and EV share than 1.")
|
||||
sys.exit()
|
||||
|
||||
@ -1264,14 +1148,14 @@ def add_land_transport(network):
|
||||
p_set=fuel_cell_share/options['transport_fuel_cell_efficiency']*transport[nodes])
|
||||
|
||||
|
||||
if fossil_share > 0:
|
||||
if ice_share > 0:
|
||||
|
||||
network.madd("Load",
|
||||
nodes,
|
||||
suffix=" land transport fossil",
|
||||
bus="Fischer-Tropsch",
|
||||
carrier="land transport fossil",
|
||||
p_set=fossil_share/options['transport_internal_combustion_efficiency']*transport[nodes])
|
||||
suffix=" land transport oil",
|
||||
bus="EU oil",
|
||||
carrier="land transport oil",
|
||||
p_set=ice_share/options['transport_internal_combustion_efficiency']*transport[nodes])
|
||||
|
||||
|
||||
|
||||
@ -1463,7 +1347,7 @@ def add_heat(network):
|
||||
capital_cost=costs.at['central gas CHP','fixed']*costs.at['central gas CHP','efficiency'] + costs.at['biomass CHP capture','fixed']*costs.at['gas','CO2 intensity'],
|
||||
marginal_cost=costs.at['central gas CHP','VOM'],
|
||||
efficiency=costs.at['central gas CHP','efficiency'] - costs.at['gas','CO2 intensity']*(costs.at['biomass CHP capture','electricity-input'] + costs.at['biomass CHP capture','compression-electricity-input']),
|
||||
efficiency2=costs.at['central gas CHP','efficiency']/costs.at['central gas CHP','c_b'] + costs.at['gas','CO2 intensity']*(costs.at['biomass CHP capture','heat-output'] + costs.at['biomass CHP capture','compression-heat-output'] - costs.at['biomass CHP capture','heat-output']),
|
||||
efficiency2=costs.at['central gas CHP','efficiency']/costs.at['central gas CHP','c_b'] + costs.at['gas','CO2 intensity']*(costs.at['biomass CHP capture','heat-output'] + costs.at['biomass CHP capture','compression-heat-output'] - costs.at['biomass CHP capture','heat-input']),
|
||||
efficiency3=costs.at['gas','CO2 intensity']*(1-costs.at['biomass CHP capture','capture_rate']),
|
||||
efficiency4=costs.at['gas','CO2 intensity']*costs.at['biomass CHP capture','capture_rate'],
|
||||
lifetime=costs.at['central gas CHP','lifetime'])
|
||||
@ -1682,7 +1566,7 @@ def add_biomass(network):
|
||||
capital_cost=costs.at['central solid biomass CHP','fixed']*costs.at['central solid biomass CHP','efficiency'] + costs.at['biomass CHP capture','fixed']*costs.at['solid biomass','CO2 intensity'],
|
||||
marginal_cost=costs.at['central solid biomass CHP','VOM'],
|
||||
efficiency=costs.at['central solid biomass CHP','efficiency'] - costs.at['solid biomass','CO2 intensity']*(costs.at['biomass CHP capture','electricity-input'] + costs.at['biomass CHP capture','compression-electricity-input']),
|
||||
efficiency2=costs.at['central solid biomass CHP','efficiency-heat'] + costs.at['solid biomass','CO2 intensity']*(costs.at['biomass CHP capture','heat-output'] + costs.at['biomass CHP capture','compression-heat-output'] - costs.at['biomass CHP capture','heat-output']),
|
||||
efficiency2=costs.at['central solid biomass CHP','efficiency-heat'] + costs.at['solid biomass','CO2 intensity']*(costs.at['biomass CHP capture','heat-output'] + costs.at['biomass CHP capture','compression-heat-output'] - costs.at['biomass CHP capture','heat-input']),
|
||||
efficiency3=-costs.at['solid biomass','CO2 intensity']*costs.at['biomass CHP capture','capture_rate'],
|
||||
efficiency4=costs.at['solid biomass','CO2 intensity']*costs.at['biomass CHP capture','capture_rate'],
|
||||
lifetime=costs.at['central solid biomass CHP','lifetime'])
|
||||
@ -1787,23 +1671,26 @@ def add_industry(network):
|
||||
carrier="H2 for shipping",
|
||||
p_set = nodal_energy_totals.loc[nodes,["total international navigation","total domestic navigation"]].sum(axis=1)*1e6*options['shipping_average_efficiency']/costs.at["fuel cell","efficiency"]/8760.)
|
||||
|
||||
if "EU oil" not in network.buses.index:
|
||||
network.madd("Bus",
|
||||
["Fischer-Tropsch"],
|
||||
["EU oil"],
|
||||
location="EU",
|
||||
carrier="Fischer-Tropsch")
|
||||
carrier="oil")
|
||||
|
||||
#use madd to get carrier inserted
|
||||
if "EU oil Store" not in network.stores.index:
|
||||
network.madd("Store",
|
||||
["Fischer-Tropsch Store"],
|
||||
bus="Fischer-Tropsch",
|
||||
["EU oil Store"],
|
||||
bus="EU oil",
|
||||
e_nom_extendable=True,
|
||||
e_cyclic=True,
|
||||
carrier="Fischer-Tropsch",
|
||||
carrier="oil",
|
||||
capital_cost=0.) #could correct to e.g. 0.001 EUR/kWh * annuity and O&M
|
||||
|
||||
if "EU oil" not in network.generators.index:
|
||||
network.add("Generator",
|
||||
"fossil oil",
|
||||
bus="Fischer-Tropsch",
|
||||
"EU oil",
|
||||
bus="EU oil",
|
||||
p_nom_extendable=True,
|
||||
carrier="oil",
|
||||
capital_cost=0.,
|
||||
@ -1817,7 +1704,7 @@ def add_industry(network):
|
||||
network.madd("Link",
|
||||
nodes_heat[name] + " " + name + " oil boiler",
|
||||
p_nom_extendable=True,
|
||||
bus0=["Fischer-Tropsch"] * len(nodes_heat[name]),
|
||||
bus0="EU oil",
|
||||
bus1=nodes_heat[name] + " " + name + " heat",
|
||||
bus2="co2 atmosphere",
|
||||
carrier=name + " oil boiler",
|
||||
@ -1830,7 +1717,7 @@ def add_industry(network):
|
||||
network.madd("Link",
|
||||
nodes + " Fischer-Tropsch",
|
||||
bus0=nodes + " H2",
|
||||
bus1="Fischer-Tropsch",
|
||||
bus1="EU oil",
|
||||
bus2="co2 stored",
|
||||
carrier="Fischer-Tropsch",
|
||||
efficiency=costs.at["Fischer-Tropsch",'efficiency'],
|
||||
@ -1841,13 +1728,13 @@ def add_industry(network):
|
||||
|
||||
network.madd("Load",
|
||||
["naphtha for industry"],
|
||||
bus="Fischer-Tropsch",
|
||||
bus="EU oil",
|
||||
carrier="naphtha for industry",
|
||||
p_set = industrial_demand.loc[nodes,"naphtha"].sum()/8760.)
|
||||
|
||||
network.madd("Load",
|
||||
["kerosene for aviation"],
|
||||
bus="Fischer-Tropsch",
|
||||
bus="EU oil",
|
||||
carrier="kerosene for aviation",
|
||||
p_set = nodal_energy_totals.loc[nodes,["total international aviation","total domestic aviation"]].sum(axis=1).sum()*1e6/8760.)
|
||||
|
||||
@ -1857,9 +1744,9 @@ def add_industry(network):
|
||||
co2 = network.loads.loc[["naphtha for industry","kerosene for aviation"],"p_set"].sum()*costs.at["oil",'CO2 intensity'] - industrial_demand.loc[nodes,"process emission from feedstock"].sum()/8760.
|
||||
|
||||
network.madd("Load",
|
||||
["Fischer-Tropsch emissions"],
|
||||
["oil emissions"],
|
||||
bus="co2 atmosphere",
|
||||
carrier="Fischer-Tropsch emissions",
|
||||
carrier="oil emissions",
|
||||
p_set=-co2)
|
||||
|
||||
network.madd("Load",
|
||||
@ -1935,13 +1822,6 @@ def add_waste_heat(network):
|
||||
network.links.loc[urban_central + " H2 Fuel Cell","bus2"] = urban_central + " urban central heat"
|
||||
network.links.loc[urban_central + " H2 Fuel Cell","efficiency2"] = 0.95 - network.links.loc[urban_central + " H2 Fuel Cell","efficiency"]
|
||||
|
||||
|
||||
def restrict_technology_potential(n,tech,limit):
|
||||
print("restricting potentials (p_nom_max) for {} to {} of technical potential".format(tech,limit))
|
||||
gens = n.generators.index[n.generators.carrier.str.contains(tech)]
|
||||
#beware if limit is 0 and p_nom_max is np.inf, 0*np.inf is nan
|
||||
n.generators.loc[gens,"p_nom_max"] *=limit
|
||||
|
||||
def decentral(n):
|
||||
n.lines.drop(n.lines.index,inplace=True)
|
||||
n.links.drop(n.links.index[n.links.carrier.isin(["DC","B2B"])],inplace=True)
|
||||
@ -1984,8 +1864,9 @@ if __name__ == "__main__":
|
||||
from vresutils.snakemake import MockSnakemake
|
||||
snakemake = MockSnakemake(
|
||||
wildcards=dict(network='elec', simpl='', clusters='37', lv='1.0',
|
||||
opts='', planning_horizons='2050',
|
||||
sector_opts='Co2L0-180H-T-H-B-I-solar3-dist1-solar+c0.5-Sabatier+c0.5'),
|
||||
opts='', planning_horizons='2020',
|
||||
sector_opts='120H-T-H-B-I-onwind+p3-dist1-cb48be3'),
|
||||
|
||||
input=dict( network='../pypsa-eur/networks/{network}_s{simpl}_{clusters}_ec_lv{lv}_{opts}.nc',
|
||||
energy_totals_name='resources/energy_totals.csv',
|
||||
co2_totals_name='resources/co2_totals.csv',
|
||||
@ -2024,7 +1905,8 @@ if __name__ == "__main__":
|
||||
retro_cost_energy = "resources/retro_cost_{network}_s{simpl}_{clusters}.csv",
|
||||
floor_area = "resources/floor_area_{network}_s{simpl}_{clusters}.csv"
|
||||
),
|
||||
output=['results/version-0/prenetworks/{network}_s{simpl}_{clusters}_lv{lv}__{sector_opts}_{planning_horizons}.nc']
|
||||
output=['results/version-cb48be3/prenetworks/{network}_s{simpl}_{clusters}_lv{lv}__{sector_opts}_{planning_horizons}.nc']
|
||||
|
||||
)
|
||||
import yaml
|
||||
with open('config.yaml', encoding='utf8') as f:
|
||||
@ -2129,6 +2011,7 @@ if __name__ == "__main__":
|
||||
print("CO2 limit set to",limit)
|
||||
|
||||
for o in opts:
|
||||
|
||||
if "cb" in o:
|
||||
path_cb = snakemake.config['results_dir'] + snakemake.config['run'] + '/csvs/'
|
||||
if not os.path.exists(path_cb):
|
||||
@ -2142,6 +2025,7 @@ if __name__ == "__main__":
|
||||
limit=CO2_CAP.loc[investment_year]
|
||||
print("overriding CO2 limit with scenario limit",limit)
|
||||
|
||||
|
||||
for o in opts:
|
||||
if "Co2L" in o:
|
||||
limit = o[o.find("Co2L")+4:]
|
||||
@ -2152,12 +2036,6 @@ if __name__ == "__main__":
|
||||
add_co2limit(n, Nyears, limit)
|
||||
|
||||
for o in opts:
|
||||
for tech in ["solar","onwind","offwind"]:
|
||||
if tech in o and "+" not in o:
|
||||
limit = o[o.find(tech)+len(tech):]
|
||||
limit = float(limit.replace("p",".").replace("m","-"))
|
||||
print("changing potential for",tech,"by factor",limit)
|
||||
restrict_technology_potential(n,tech,limit)
|
||||
|
||||
if o[:10] == 'linemaxext':
|
||||
maxext = float(o[10:])*1e3
|
||||
@ -2169,7 +2047,6 @@ if __name__ == "__main__":
|
||||
|
||||
if snakemake.config["sector"]['electricity_distribution_grid']:
|
||||
insert_electricity_distribution_grid(n)
|
||||
|
||||
for o in opts:
|
||||
if "+" in o:
|
||||
oo = o.split("+")
|
||||
@ -2178,19 +2055,23 @@ if __name__ == "__main__":
|
||||
suptechs = map(lambda c: c.split("-", 2)[0], carrier_list)
|
||||
if oo[0].startswith(tuple(suptechs)):
|
||||
carrier = oo[0]
|
||||
# handles only p_nom_max as stores and lines have no potentials
|
||||
attr_lookup = {"p": "p_nom_max", "c": "capital_cost"}
|
||||
attr = attr_lookup[oo[1][0]]
|
||||
factor = float(oo[1][1:])
|
||||
#beware if factor is 0 and p_nom_max is np.inf, 0*np.inf is nan
|
||||
if carrier == "AC": # lines do not have carrier
|
||||
n.lines[attr] *= factor
|
||||
else:
|
||||
comps = {"Generator", "Link", "StorageUnit", "Store"}
|
||||
comps = {"Generator", "Link", "StorageUnit"} if attr=='p_nom_max' else {"Generator", "Link", "StorageUnit", "Store"}
|
||||
for c in n.iterate_components(comps):
|
||||
if carrier=='solar':
|
||||
sel = c.df.carrier.str.contains(carrier) & ~c.df.carrier.str.contains("solar rooftop")
|
||||
else:
|
||||
sel = c.df.carrier.str.contains(carrier)
|
||||
c.df.loc[sel,attr] *= factor
|
||||
print("changing", attr ,"for",carrier,"by factor",factor)
|
||||
|
||||
|
||||
if snakemake.config["sector"]['gas_distribution_grid']:
|
||||
insert_gas_distribution_costs(n)
|
||||
if snakemake.config["sector"]['electricity_grid_connection']:
|
||||
|
Loading…
Reference in New Issue
Block a user