Annual global emissions and absolute annual rates of change (Table 2.4)

Notebook sr15_2.3.3_global_emissions_statistics

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).

sr15_2.3.3_global_emissions_statistics

IPCC SR15 scenario assessment

Analysis of global CO2 and Kyoto emissions, (BE)CCS
and year of net-zero

This notebook computes indicators and diagnostics of emissions pathways, the use of carbon capture and sequestration, and the timing of net-zero of different emissions categories in the IPCC's "Special Report on Global Warming of 1.5°C". The notebook generates the data for Table 2.4 in the Special Report.

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 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)

rc = pyam.run_control()
for item in specs.pop('run_control').items():
    rc.update({item[0]: item[1]})
cats = specs.pop('cats')
cats_15_no_lo = specs.pop('cats_15_no_lo')

Downselect scenario ensemble to categories of interest for this assessment

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. SPM Statement C1).

Also, note that we apply the filter by relevant years after computing the year of netzero.

In [5]:
cats.remove('Above 2C')
In [6]:
sr1p5.meta.rename(columns={'Kyoto-GHG|2010 (SAR)': 'kyoto_ghg_2010'}, inplace=True)
In [7]:
filter_args_aim = dict(model='AIM*',
                       scenario=['SFCM*_1p5Degree', 'EMF33_Med2C_nofuel', 'EMF33_Med2C_none'],
                       keep=False)
In [8]:
df = (
    sr1p5
    .filter(kyoto_ghg_2010='in range', category=cats)
    .filter(**filter_args_aim)
)

Initialize a pyam.Statistics instance

In [9]:
stats = pyam.Statistics(df=df,
                        filters=[
                            ('below 1.5', {'category': 'Below 1.5C'}),
                            ('lo os 1.5', {'category': '1.5C low overshoot'}),
                            ('no & lo os 1.5', {'category': cats_15_no_lo}),
                            ('hi os 1.5', {'category': ['1.5C high overshoot']}),
                            ('lower 2.0', {'category': ['Lower 2C']}),
                            ('higher 2.0', {'category': ['Higher 2C']})]
                        , rows=True)
In [10]:
years = [2030, 2050, 2100]
compare_years = [(2010, 2030), (2020, 2030), (2030, 2050)]

Function to compute the year of netzero and add growth statistics to the summary

In [11]:
def year_of_net_zero(data, years, threshold):
    prev_val = 0
    prev_yr = np.nan

    for yr, val in zip(years, data):
        if np.isnan(val):
            continue
        
        if val < threshold:
            x = (val - prev_val) / (yr - prev_yr) # absolute change per year
            return prev_yr + int((threshold - prev_val) / x) + 1 # add one because int() rounds down
        
        prev_val = val
        prev_yr = yr
    return np.inf
In [12]:
header='Annual emissions/sequestration (GtCO2)'
header_change='Absolute annual change (GtCO2)'
header_zero='Timing of global zero'

statistics_settings = dict(
    header=header,
    header_change=header_change,
    header_zero= header_zero,
    years=years,
    compare_years=[(2010, 2030), (2020, 2030), (2030, 2050)],
)
In [13]:
def add_statistics(data, row, years, compare_years,
                  header, header_change, header_zero, add_netzero=False):
    stats.add(data[years], header=header, row=row)
    for i, j in compare_years:
        abs_ann_change = (data[j] - data[i]) / (j - i)
        stats.add(abs_ann_change, header=header_change, row=row,
                       subheader='{}-{}'.format(i,j))
    if add_netzero:
        netzero = data.apply(year_of_net_zero, years=data.columns, threshold=0, axis=1)
        stats.add(netzero, header=header_zero, row=row, subheader='year')

Get timeseries of total CO2 emissions

In [14]:
co2 = (
    df.filter(variable='Emissions|CO2')
    .convert_unit('Mt CO2/yr', 'Gt CO2/yr')
    .timeseries()
)
In [15]:
add_statistics(co2, 'Total CO2 (net)', **statistics_settings, add_netzero=True)
In [16]:
co2_gross_seq_variables = [
    'Carbon Sequestration|CCS|Biomass',
    'Carbon Sequestration|Land Use',
    'Carbon Sequestration|Direct Air Capture',
    'Carbon Sequestration|Enhanced Weathering'
]
agg_sequestration = (
    df.filter(variable=co2_gross_seq_variables)
    .convert_unit('Mt CO2/yr', 'Gt CO2/yr')
    .timeseries()
)
agg_sequestration = agg_sequestration.groupby(pyam.META_IDX).sum()
In [17]:
co2_ene_ind = (
    df.filter(variable='Emissions|CO2|Energy and Industrial Processes')
    .convert_unit('Mt CO2/yr', 'Gt CO2/yr')
    .timeseries()
)
co2_ene_ind.index = co2_ene_ind.index.droplevel([2, 3, 4])
In [18]:
co2_ene_ind_gross = (co2_ene_ind + agg_sequestration).combine_first(co2_ene_ind)
In [19]:
add_statistics(co2_ene_ind_gross, 'CO2 from fossil fuels and industry (gross)', **statistics_settings)
In [20]:
add_statistics(co2_ene_ind, 'CO2 from fossil fuels and industry (net)', **statistics_settings)
In [21]:
co2_afolu = (
    df.filter(variable='Emissions|CO2|AFOLU')
    .convert_unit('Mt CO2/yr', 'Gt CO2/yr')
    .timeseries()
)
In [22]:
add_statistics(co2_afolu, 'CO2 from AFOLU', **statistics_settings)

CCS from bioenergy

In [23]:
ccs_bio = (
    df.filter(variable='Carbon Sequestration|CCS|Biomass')
    .convert_unit('Mt CO2/yr', 'Gt CO2/yr')
    .timeseries()
)
In [24]:
add_statistics(ccs_bio, 'Bioenergy combined with carbon capture and storage (BECCS)',**statistics_settings)

Total greenhouse gases according to the Kyoto protocol

In [25]:
ghg = (
    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 [26]:
add_statistics(ghg, 'Kyoto GHG (AR4, GtCO2e)', **statistics_settings, add_netzero=True)
/Users/dh/anaconda3/lib/python3.7/site-packages/numpy/lib/function_base.py:3942: RuntimeWarning: invalid value encountered in multiply
  x2 = take(ap, indices_above, axis=axis) * weights_above

Display and export summary statistics to xlsx

Note that in Table 2.4 as printed in the Special Report, the full range is shown for any cells with less than 7 scenarios, and interquartile ranges are shown otherwise. This formatting was implemented manually ex-post, as it is currently not supported by the pyam.Statistics module.

In [27]:
summary = stats.summarize(center='median', interquartile=True, custom_format='{:.1f}')
index = summary.index.get_level_values(1).unique()
summary = (
    summary
    .swaplevel(0, 1, axis=0)
    .reindex(index=index, level=0)
)
summary
Out[27]:
count Annual emissions/sequestration (GtCO2) Absolute annual change (GtCO2) Timing of global zero
median (interquartile range) 2030 2050 2100 2010-2030 2020-2030 2030-2050 year
Total CO2 (net) below 1.5 5 13.4 (14.1, 13.2) -3.0 (-0.3, -4.3) -8.0 (-3.6, -9.2) -1.2 (-1.2, -1.3) -2.5 (-1.9, -2.6) -0.8 (-0.7, -0.9) 2044.0 (2050.0, 2038.0)
lo os 1.5 37 20.8 (22.2, 18.0) -0.4 (2.7, -2.0) -10.8 (-8.1, -14.3) -0.8 (-0.7, -1.0) -1.7 (-1.4, -2.3) -1.0 (-0.8, -1.2) 2050.0 (2055.0, 2047.0)
no & lo os 1.5 42 20.3 (22.0, 15.9) -0.5 (2.2, -2.8) -10.2 (-7.6, -14.2) -0.9 (-0.7, -1.1) -1.8 (-1.5, -2.3) -1.0 (-0.8, -1.2) 2050.0 (2055.0, 2046.0)
hi os 1.5 36 29.1 (36.4, 26.0) 1.0 (6.3, -1.2) -13.8 (-11.1, -16.4) -0.4 (0.0, -0.6) -1.1 (-0.5, -1.5) -1.3 (-1.1, -1.8) 2052.0 (2059.2, 2049.0)
lower 2.0 54 28.9 (33.7, 24.5) 9.9 (13.1, 6.5) -5.1 (-2.6, -10.3) -0.4 (-0.2, -0.6) -1.1 (-0.8, -1.6) -0.9 (-0.8, -1.2) 2069.5 (2078.8, 2063.2)
higher 2.0 54 33.5 (35.0, 31.0) 17.9 (19.1, 12.2) -3.3 (0.6, -11.5) -0.2 (-0.0, -0.4) -0.7 (-0.5, -0.9) -0.8 (-0.6, -1.0) 2084.5 (inf, 2070.0)
CO2 from fossil fuels and industry (gross) below 1.5 5 18.0 (20.2, 16.8) 10.5 (11.7, 7.7) 8.3 (8.5, 1.0) -0.7 (-0.7, -0.8) -1.5 (-1.2, -2.0) -0.4 (-0.3, -0.7)
lo os 1.5 37 22.1 (24.4, 18.7) 10.3 (14.1, 7.8) 5.6 (8.1, 2.6) -0.5 (-0.4, -0.6) -1.3 (-0.9, -1.7) -0.6 (-0.5, -0.7)
no & lo os 1.5 42 21.6 (24.2, 18.0) 10.3 (13.8, 7.7) 6.1 (8.4, 2.6) -0.5 (-0.4, -0.7) -1.3 (-0.9, -1.8) -0.6 (-0.4, -0.7)
hi os 1.5 36 27.8 (37.1, 25.6) 13.1 (17.0, 11.6) 6.6 (8.8, 2.8) -0.2 (0.2, -0.3) -0.8 (-0.2, -1.1) -0.7 (-0.6, -1.0)
lower 2.0 54 27.7 (31.5, 23.5) 15.4 (19.0, 11.1) 7.2 (10.4, 3.7) -0.2 (-0.0, -0.4) -0.8 (-0.5, -1.2) -0.6 (-0.5, -0.8)
higher 2.0 54 31.3 (33.4, 28.7) 19.2 (22.6, 17.1) 8.1 (10.9, 5.0) -0.1 (0.1, -0.2) -0.5 (-0.2, -0.7) -0.6 (-0.5, -0.7)
CO2 from fossil fuels and industry (net) below 1.5 5 16.4 (16.7, 13.7) 1.0 (4.0, 0.5) -2.7 (0.0, -4.3) -0.8 (-0.8, -1.0) -1.8 (-1.4, -2.1) -0.6 (-0.6, -0.7)
lo os 1.5 37 20.6 (22.2, 17.5) 3.2 (5.6, -0.6) -8.5 (-4.1, -11.6) -0.6 (-0.5, -0.7) -1.4 (-1.1, -1.8) -0.8 (-0.7, -1.1)
no & lo os 1.5 42 20.1 (22.1, 16.8) 3.0 (5.6, 0.0) -8.3 (-3.5, -10.8) -0.6 (-0.5, -0.8) -1.4 (-1.1, -1.9) -0.8 (-0.7, -1.1)
hi os 1.5 36 26.9 (34.7, 25.3) 4.2 (10.0, 1.2) -10.7 (-6.9, -13.2) -0.3 (0.1, -0.3) -0.9 (-0.3, -1.2) -1.2 (-0.9, -1.5)
lower 2.0 54 28.2 (31.0, 23.1) 11.8 (14.1, 6.2) -3.1 (-0.7, -6.4) -0.2 (-0.1, -0.4) -0.8 (-0.5, -1.2) -0.8 (-0.7, -1.0)
higher 2.0 54 31.0 (33.0, 28.7) 17.0 (19.3, 13.1) -2.9 (3.3, -8.0) -0.1 (0.1, -0.2) -0.5 (-0.2, -0.7) -0.7 (-0.5, -1.0)
CO2 from AFOLU below 1.5 5 -2.2 (-1.3, -2.3) -4.4 (-2.3, -10.0) -4.4 (-3.6, -4.9) -0.3 (-0.3, -0.4) -0.5 (-0.4, -0.6) -0.1 (-0.0, -0.3)
lo os 1.5 37 -0.1 (0.8, -1.0) -2.3 (-0.6, -4.1) -2.4 (-1.2, -4.2) -0.2 (-0.2, -0.3) -0.4 (-0.3, -0.5) -0.1 (-0.1, -0.2)
no & lo os 1.5 42 -0.1 (0.7, -1.3) -2.6 (-0.6, -4.5) -2.6 (-1.3, -4.2) -0.2 (-0.2, -0.3) -0.4 (-0.3, -0.5) -0.1 (-0.1, -0.2)
hi os 1.5 36 1.2 (2.7, 0.1) -2.1 (-0.3, -5.4) -2.4 (-1.5, -5.0) -0.1 (-0.1, -0.3) -0.2 (-0.1, -0.5) -0.2 (-0.0, -0.3)
lower 2.0 54 1.4 (2.8, 0.3) -1.4 (-0.5, -2.7) -2.4 (-1.3, -4.2) -0.2 (-0.1, -0.2) -0.3 (-0.2, -0.4) -0.1 (-0.1, -0.2)
higher 2.0 54 1.5 (2.7, 0.8) -0.0 (1.9, -1.6) -1.3 (0.1, -3.9) -0.2 (-0.1, -0.2) -0.2 (-0.1, -0.4) -0.1 (-0.0, -0.1)
Bioenergy combined with carbon capture and storage (BECCS) below 1.5 5 0.4 (0.6, 0.0) 3.4 (4.2, 0.0) 5.7 (10.7, 0.0) 0.0 (0.0, 0.0) 0.0 (0.1, 0.0) 0.2 (0.2, 0.0)
lo os 1.5 36 0.3 (1.1, 0.0) 4.6 (6.4, 3.8) 12.4 (15.6, 7.6) 0.0 (0.1, 0.0) 0.0 (0.1, 0.0) 0.2 (0.3, 0.2)
no & lo os 1.5 41 0.4 (1.0, 0.0) 4.5 (6.3, 3.4) 12.4 (15.0, 6.4) 0.0 (0.1, 0.0) 0.0 (0.1, 0.0) 0.2 (0.3, 0.2)
hi os 1.5 36 0.1 (0.4, 0.0) 6.8 (9.5, 3.7) 14.9 (16.3, 12.1) 0.0 (0.0, 0.0) 0.0 (0.0, 0.0) 0.3 (0.4, 0.2)
lower 2.0 54 0.1 (0.3, 0.0) 3.6 (4.6, 1.8) 9.5 (12.1, 6.9) 0.0 (0.0, 0.0) 0.0 (0.0, 0.0) 0.2 (0.2, 0.1)
higher 2.0 47 0.1 (0.2, 0.0) 3.0 (4.9, 1.6) 10.8 (15.3, 8.2) [46] 0.0 (0.0, 0.0) 0.0 (0.0, 0.0) 0.1 (0.2, 0.1)
Kyoto GHG (AR4, GtCO2e) below 1.5 5 22.1 (22.7, 21.3) 2.7 (6.4, 2.4) -2.6 (1.7, -4.4) -1.4 (-1.4, -1.5) -2.9 (-2.3, -3.1) -0.9 (-0.7, -1.0) 2066.0 (nan, 2065.0)
lo os 1.5 31 27.9 (31.1, 26.0) 7.0 (9.9, 4.5) -3.8 (-2.1, -7.9) -1.1 (-0.9, -1.2) -2.3 (-1.8, -2.8) -1.1 (-0.9, -1.2) 2068.0 (2079.5, 2061.0)
no & lo os 1.5 36 27.4 (30.9, 24.7) 6.5 (9.6, 4.2) -3.7 (-1.8, -7.8) -1.1 (-1.0, -1.3) -2.4 (-1.9, -2.9) -1.1 (-0.9, -1.2) 2067.0 (2083.5, 2061.0)
hi os 1.5 32 40.4 (48.9, 36.3) 8.4 (12.3, 6.2) -8.5 (-5.7, -11.2) -0.5 (-0.0, -0.7) -1.3 (-0.6, -1.8) -1.5 (-1.3, -2.1) 2062.5 (2067.0, 2058.0)
lower 2.0 46 39.6 (45.1, 35.7) 18.3 (20.4, 15.2) 2.1 (4.2, -2.4) -0.5 (-0.1, -0.7) -1.5 (-0.9, -2.2) -1.1 (-0.9, -1.2) inf (inf, 2090.0)
higher 2.0 42 45.3 (48.5, 39.3) 25.9 (27.9, 23.3) 5.2 (11.5, -4.8) -0.2 (-0.0, -0.6) -1.0 (-0.6, -1.2) -1.0 (-0.7, -1.2) inf (inf, 2085.0)
In [28]:
summary.to_excel('output/table_2.4_emission_statistics.xlsx')
In [ ]: