Assessment for statements in the SPM

Notebook spm_sr15_statements

This notebook is based on the Release 1.1 of the IAMC 1.5C Scenario Explorer and Data and refers to the published version of the IPCC Special Report on Global Warming of 1.5C (SR15).

The notebook is run with pyam release 0.5.0.

The source code of this notebook is available on GitHub (release 2.0.2).

spm_sr15_statements

IPCC SR15 scenario assessment

Statements of the Summary for Policymakers
derived from the scenario assessment

This notebook computes the summary statistics highlighted in the Summary for Policymakers of the IPCC's "Special Report on Global Warming of 1.5°C".

The scenario data used in this analysis can be accessed and downloaded at https://data.ene.iiasa.ac.at/iamc-1.5c-explorer.

Load pyam package and other dependencies

In [1]:
import pandas as pd
import numpy as np
import io
import itertools
import yaml
import math
import matplotlib.pyplot as plt
plt.style.use('style_sr15.mplstyle')
%matplotlib inline
import pyam
pyam - INFO: Running in a notebook, setting `pyam` logging level to `logging.INFO` and adding stderr handler

Import scenario data, categorization and specifications files

The metadata file with scenario categorisation and quantitative indicators can be downloaded at https://data.ene.iiasa.ac.at/iamc-1.5c-explorer.
Alternatively, it can be re-created using the notebook sr15_2.0_categories_indicators.

The last cell of this section loads and assigns a number of auxiliary lists as defined in the categorization notebook.

In [2]:
sr1p5 = pyam.IamDataFrame(data='../data/iamc15_scenario_data_world_r2.0.xlsx')
pyam.utils - INFO: Reading `../data/iamc15_scenario_data_world_r2.0.xlsx`
In [3]:
sr1p5.load_meta('sr15_metadata_indicators.xlsx')
pyam.core - INFO: Importing metadata for 416 scenarios (for total of 416)
In [4]:
with open("sr15_specs.yaml", 'r') as stream:
    specs = yaml.load(stream, Loader=yaml.FullLoader)

cats = specs.pop('cats')
cats_15 = specs.pop('cats_15')
cats_15_no_lo = specs.pop('cats_15_no_lo')
cats_2 = specs.pop('cats_2')

Downselect scenario ensemble to categories of interest for this assessment

In [5]:
cats.remove('Above 2C')
In [6]:
compare_year = 2010
In [7]:
sr1p5.meta.rename(columns={'Kyoto-GHG|2010 (SAR)': 'kyoto_ghg_2010'}, inplace=True)
In [8]:
df = sr1p5.filter(category=cats)

Define filters for pyam.Statistics instance

In [9]:
filters_15_no_lo = [('no & lo os 1.5', {'category': cats_15_no_lo})]
filters_compare = filters_15_no_lo + [('2.0 lo', {'category': 'Lower 2C'})]

Statement C1: Net CO2 emissions by warming categories and year of crossing the net-zero threshold

To reduce potential bias by many scenarios from the same modelling framework, 13 scenarios submitted by the 'AIM' model are excluded from the assessment underpinning this statement (cf. Table 2.4).

In [10]:
filter_args_aim = dict(model='AIM*',
                       scenario=['SFCM*_1p5Degree', 'EMF33_Med2C_nofuel', 'EMF33_Med2C_none'],
                       keep=False)
In [11]:
stats_c1 = pyam.Statistics(df=df, filters=filters_compare, rows=True)
In [12]:
co2 = (
    df.filter(kyoto_ghg_2010='in range', variable='Emissions|CO2')
    .filter(**filter_args_aim)
    .convert_unit('Mt CO2/yr', 'Gt CO2/yr')
    .timeseries()
)
In [13]:
for y in [2030]:
    stats_c1.add((1 - co2[y] / co2[compare_year]) * 100,
                 header='Reduction in emissions by {}'.format(y),
                 subheader='relative to {} (%)'.format(compare_year),
                 row='Net CO2 emissions')

Only include scenarios in this statistic that reach net-zero CO2 emissions before the end of the century

In [14]:
net_zero = (
    df.filter(kyoto_ghg_2010='in range')
    .filter(**filter_args_aim)
).meta['year of netzero CO2 emissions']
netzero = net_zero[net_zero < 2100]
In [15]:
stats_c1.add(net_zero,
             header='Year of net-zero'.format(y),
             subheader=''.format(compare_year),
             row='Net CO2 emissions')
In [16]:
stats_c1.summarize(center='median', interquartile=True, custom_format='{:.0f}')
Out[16]:
count Reduction in emissions by 2030 Year of net-zero
median (interquartile range) relative to 2010 (%)
no & lo os 1.5 Net CO2 emissions 42 46 (58, 40) 2050 (2055, 2046)
2.0 lo Net CO2 emissions 54 24 (32, 9) 2070 (2079, 2063)

Statement C1.2: Reductions of methane and black carbon

In [17]:
stats_c12 = pyam.Statistics(df=df, filters=filters_15_no_lo, rows=True)
In [18]:
ch4 = df.filter(kyoto_ghg_2010='in range', variable='Emissions|CH4').timeseries()
In [19]:
for y in [2050]:
    stats_c12.add((1 - ch4[y] / ch4[compare_year]) * 100,
                  header='Reduction in emissions by {}'.format(y),
                  subheader='relative to {} (%)'.format(compare_year),
                  row='Methane (CH4)')
In [20]:
bc = df.filter(variable='Emissions|BC').timeseries()
In [21]:
for y in [2050]:
    stats_c12.add((1 - bc[y] / bc[compare_year]) * 100,
                  header='Reduction in emissions by {}'.format(y),
                  subheader='relative to {} (%)'.format(compare_year),
                  row='Black carbon')
In [22]:
stats_c12.summarize(center='mean', fullrange=True, custom_format='{:.0f}')
Out[22]:
count Reduction in emissions by 2050
mean (max, min) relative to 2010 (%)
no & lo os 1.5 Methane (CH4) 42 53 (80, 37)
Black carbon 51 59 (87, 42)

Statement C2.2: Energy system transformation

In [23]:
def add_stats_share(stats, var_list, name, total, total_name, years, df=df):

    _df = df.filter(variable=var_list)
    for v in var_list:
        _df.require_variable(v, exclude_on_fail=True)
    _df.filter(exclude=False, inplace=True)

    component = (
        _df.timeseries()
        .groupby(['model', 'scenario']).sum()
    )
    share = component / total * 100
    
    for y in years:
        stats.add(share[y], header='Share of {} in {}'.format(total_name, y),
                  subheader='(%)', row=name)
In [24]:
stats_c22 = pyam.Statistics(df=df, filters=filters_15_no_lo, rows=True)
In [25]:
ele = df.filter(variable='Secondary Energy|Electricity').timeseries()
ele.index = ele.index.droplevel([2, 3, 4])
In [26]:
ele_re_vars = [
   'Secondary Energy|Electricity|Biomass',
   'Secondary Energy|Electricity|Non-Biomass Renewables'
]

add_stats_share(stats_c22, ele_re_vars, 'renewables', ele, 'electricity generation', [2050])
pyam.core - INFO: All scenarios have the required variable `Secondary Energy|Electricity|Biomass`
pyam.core - INFO: All scenarios have the required variable `Secondary Energy|Electricity|Non-Biomass Renewables`
In [27]:
ele_gas = ['Secondary Energy|Electricity|Gas']
add_stats_share(stats_c22, ele_gas, 'natural gas', ele, 'electricity generation', [2050])
pyam.core - INFO: All scenarios have the required variable `Secondary Energy|Electricity|Gas`
In [28]:
ele_coal = ['Secondary Energy|Electricity|Coal']
add_stats_share(stats_c22, ele_coal, 'coal', ele, 'electricity generation', [2050])
pyam.core - INFO: All scenarios have the required variable `Secondary Energy|Electricity|Coal`
In [29]:
stats_c22.summarize(center='mean', interquartile=True, custom_format='{:.0f}')
Out[29]:
count Share of electricity generation in 2050
mean (interquartile range) (%)
no & lo os 1.5 renewables 50 78 (86, 69)
natural gas 50 8 (11, 3)
coal 50 1 (2, 0)

Statement C2.3: CO2 emissions from industry

In [30]:
stats_c23 = pyam.Statistics(df=df, filters=filters_compare)
In [31]:
co2_ind = df.filter(variable='Emissions|CO2|Energy|Demand|Industry').timeseries()
In [32]:
for y in [2050]:
    stats_c23.add((1 - co2_ind[y] / co2_ind[compare_year]) * 100,
                   header='Industrial emissions reductions relative to {} (%)'.format(compare_year),
                   subheader=y)
In [33]:
stats_c23.summarize(center='median', interquartile=True, custom_format='{:.0f}')
Out[33]:
count Industrial emissions reductions relative to 2010 (%)
median (interquartile range) 2050
no & lo os 1.5 42 79 (91, 67)
2.0 lo 59 60 (81, 48)

Statement C2.4: Urban infrastructure and transport

In [34]:
stats_c24 = pyam.Statistics(df=df, filters=filters_compare, rows=True)
In [35]:
bld = df.filter(variable='Final Energy|Residential and Commercial').timeseries()
bld.index = bld.index.droplevel([2, 3, 4])
In [36]:
bld_ele_vars = ['Final Energy|Residential and Commercial|Electricity']
add_stats_share(stats_c24, bld_ele_vars, 'electricity', bld, 'energy demand in buildings', [2050])
pyam.core - INFO: All scenarios have the required variable `Final Energy|Residential and Commercial|Electricity`
In [37]:
trp = df.filter(variable='Final Energy|Transportation').timeseries()
trp.index = trp.index.droplevel([2, 3, 4])
In [38]:
var_trp_low = [
    'Final Energy|Transportation|Electricity',
    'Final Energy|Transportation|Hydrogen',
    'Final Energy|Transportation|Liquids|Biomass'
]

add_stats_share(stats_c24, var_trp_low, 'low-emission energy', trp, 'energy demand in transport', [2050])
pyam.core - INFO: All scenarios have the required variable `Final Energy|Transportation|Electricity`
pyam.core - INFO: 50 scenarios do not include required variable `Final Energy|Transportation|Hydrogen`, marked as `exclude: True` in metadata
pyam.core - INFO: 28 scenarios do not include required variable `Final Energy|Transportation|Liquids|Biomass`, marked as `exclude: True` in metadata
In [39]:
stats_c24.summarize(center='median', interquartile=True, custom_format='{:.0f}')
Out[39]:
count Share of energy demand in buildings in 2050 Share of energy demand in transport in 2050
median (interquartile range) (%) (%)
no & lo os 1.5 electricity 30 58 (76, 53)
low-emission energy 29 55 (66, 35)
2.0 lo electricity 40 65 (69, 51)
low-emission energy 34 33 (47, 27)

Statement C2.5: Transitions of land use

Please refer to Figure 2.24 in Chapter 2 of the SR15 for details.

Please refer to Figure 2.27 in Chapter 2 of the SR15 for details.

Statement C2.7: Marginal abatement costs

Please refer to the notebook sr15_2.5_carbon_price_analysis in this folder for the detailed assessment.

Statement C3: Cumulative carbon dioxide removal

Please refer to the notebook sr15_2.3.4_carbon_dioxide_removal in this folder for the detailed assessment.

Statement C3.2: Carbon capture and sequestration

Please refer to the notebook sr15_2.3.4_carbon_dioxide_removal in this folder for the detailed assessment.

Statement D1.1: CO2 emissions reductions

In [40]:
stats_d11 = pyam.Statistics(df=df, filters=filters_15_no_lo)
In [41]:
ghg_ar4_sar = (
    df.filter(kyoto_ghg_2010='in range', variable='Emissions|Kyoto Gases (AR4-GWP100)')
    .rename(unit={'Mt CO2-equiv/yr': 'Mt CO2e/yr'})
    .convert_unit('Mt CO2e/yr','Gt CO2e/yr')
    .timeseries()
)
In [42]:
stats_d11.add(ghg_ar4_sar[[compare_year, 2030]], header='Kyoto GHG emissions (Gt CO2-eq/yr)')
In [43]:
stats_d11.add((ghg_ar4_sar[2030] / ghg_ar4_sar[compare_year] - 1) * 100,
              header='Reduction of Kyoto GHG emissions',
              subheader='2030 relative to {} (%)'.format(compare_year))
In [44]:
stats_d11.summarize(interquartile=True, custom_format='{:.1f}')
Out[44]:
count Kyoto GHG emissions (Gt CO2-eq/yr) Reduction of Kyoto GHG emissions
mean (interquartile range) 2010 2030 2030 relative to 2010 (%)
no & lo os 1.5 36 49.7 (51.8, 48.3) 27.3 (30.9, 24.7) -45.1 (-38.8, -50.5)

Checking 1.5°C pathways with limited overshoot that do not reduce CO2 emissions to below 35Gt in 2030

In [45]:
ghg_ar4 = (
    df.filter(variable='Emissions|Kyoto Gases (AR4-GWP100)')
    .rename(unit={'Mt CO2-equiv/yr': 'Mt CO2e/yr'})
    .convert_unit('Mt CO2e/yr','Gt CO2e/yr')
    .timeseries()
)
In [46]:
ghg_ar4_15 = pyam.filter_by_meta(ghg_ar4, df, category=cats_15_no_lo)
ghg_ar4_15[ghg_ar4_15[2030] > 35].dropna(axis=1)
Out[46]:
2000 2005 2010 2015 2020 2030 2040 2050 2060 2070 2080 2090 2100
model scenario region variable unit
MERGE-ETL 6.0 DAC15_50 World Emissions|Kyoto Gases (AR4-GWP100) Gt CO2e/yr 51.441214 54.672769 58.393493 55.40835 52.423206 40.408918 33.560128 24.173999 16.470141 8.540068 -0.805529 -14.15674 -30.334473