NEMED 0.3.3 documentation

Overview

  • Methodology

Examples

  • Total Emissions
  • CDEII Comparison
    • Benchmark Results
    • Benchmark Sourcecode
  • Marginal Emissions
  • Emissions Trends

API Reference

  • NEMED module
  • Process module
  • Downloader module

Development

  • Changelog
  • Contributing
  • Code of Conduct
Theme by the Executable Book Project
  • repository
  • open issue
  • .ipynb
Contents
  • Data Extraction
    • Import Packages
    • Processing Emissions Data
  • Chart 1 - CY2019 Emissions Trends

Emissions Trends

Contents

  • Data Extraction
    • Import Packages
    • Processing Emissions Data
  • Chart 1 - CY2019 Emissions Trends

Emissions Trends#

A snapshot of emissions trends for both marginal and total with respect to regional prices

Data Extraction#

Import Packages#

Show code cell content Hide code cell content
import nemed
import nemosis

# To generate plots shown
import pandas as pd
import numpy as np
import plotly.graph_objects as go
import plotly.express as px
from plotly.subplots import make_subplots

# Open plot in browser (optional)
import plotly.io as pio
pio.renderers.default = "browser"

# Hide Logging output
import logging
logging.getLogger("nemosis").setLevel(logging.WARNING)
logging.getLogger("nemed").setLevel(logging.WARNING)

Processing Emissions Data#

This example serves as a snapshot of trends rather than a detailled tutorial. We recommend you first explore the total emissions example and marginal emissions example for understanding on how NEMED extracts and processes emissions.

start_time = "2019/01/01 00:00"
end_time = "2020/01/01 00:00"
cache = "E:\TEMPCACHE_nemed_demo"

Download market prices using NEMOSIS

prices = nemosis.dynamic_data_compiler(start_time=start_time+":00", 
                                      end_time=end_time+":00", 
                                      table_name='DISPATCHPRICE', 
                                      raw_data_location=cache,
                                      fformat='feather',
                                      select_columns=['SETTLEMENTDATE','REGIONID','RRP','INTERVENTION'],
                                      keep_csv=False)
prices = prices.rename(columns = {'SETTLEMENTDATE': 'Time', 'REGIONID': 'Region', 'RRP': 'Prices'})
prices = prices[prices['INTERVENTION']==0]

Download Total Emissions

total = nemed.get_total_emissions(start_time=start_time,
                                  end_time=end_time,
                                  cache=cache,
                                  by=None)
total
TimeEnding Region Energy Total_Emissions Intensity_Index
0 2019-01-01 00:05:00 NEM 0.000000 0.000000 0.000000
1 2019-01-01 00:05:00 NSW1 0.000000 0.000000 0.000000
2 2019-01-01 00:05:00 QLD1 0.000000 0.000000 0.000000
3 2019-01-01 00:05:00 SA1 0.000000 0.000000 0.000000
4 2019-01-01 00:05:00 TAS1 0.000000 0.000000 0.000000
... ... ... ... ... ...
630715 2020-01-01 00:00:00 NSW1 676.491550 536.609209 0.793224
630716 2020-01-01 00:00:00 QLD1 513.112938 411.923700 0.802793
630717 2020-01-01 00:00:00 SA1 117.760043 49.024213 0.416306
630718 2020-01-01 00:00:00 TAS1 52.408370 0.000000 0.000000
630719 2020-01-01 00:00:00 VIC1 358.095808 353.548618 0.987302

630720 rows × 5 columns

Download Marginal Emissions

marginal = nemed.get_marginal_emissions(start_time=start_time,
                                        end_time=end_time,
                                        cache=cache)
Show code cell output Hide code cell output
WARNING: Warning: Gen_info table only has most recent NEM registration and exemption list. Does not account for retired generators
  0%|          | 0/3 [00:00<?, ?it/s]
WARNING: PriceSetter Download for 2019-04-15 00:00:00 failed. Continuing with remaining dates...
 33%|███▎      | 1/3 [00:02<00:05,  2.58s/it]
WARNING: PriceSetter Download for 2019-10-31 00:00:00 failed. Continuing with remaining dates...
 67%|██████▋   | 2/3 [00:03<00:01,  1.67s/it]
WARNING: PriceSetter Download for 2019-12-11 00:00:00 failed. Continuing with remaining dates...
100%|██████████| 3/3 [00:04<00:00,  1.46s/it]
Reading selected 363 JSON files to pandas, of cached files
100%|██████████| 363/363 [01:28<00:00,  4.09it/s]
marginal
Time Region Intensity_Index DUID CO2E_ENERGY_SOURCE
0 2019-01-01 00:05:00 NSW1 0.421647 PPCCGT Natural Gas (Pipeline)
1 2019-01-01 00:05:00 QLD1 0.401443 PPCCGT Natural Gas (Pipeline)
2 2019-01-01 00:05:00 SA1 0.473708 PPCCGT Natural Gas (Pipeline)
3 2019-01-01 00:05:00 TAS1 0.477227 PPCCGT Natural Gas (Pipeline)
4 2019-01-01 00:05:00 VIC1 0.453253 PPCCGT Natural Gas (Pipeline)
... ... ... ... ... ...
519069 2020-01-01 00:00:00 NSW1 0.908305 VP5 Black coal
519070 2020-01-01 00:00:00 QLD1 0.942021 VP6 Black coal
519071 2020-01-01 00:00:00 SA1 0.681150 TORRB3 Natural Gas (Pipeline)
519072 2020-01-01 00:00:00 TAS1 0.000000 GORDON Hydro
519073 2020-01-01 00:00:00 VIC1 0.672636 TORRB3 Natural Gas (Pipeline)

519074 rows × 5 columns

The following function helps to arrange the above data into a single dataframe and aggregate to average hourly resolution.

def arrange_data_into_hourly(price_df, total_df, marginal_df):
    # Group data by Hour, Region
    total_df.rename(columns={'Intensity_Index': 'Average_EI',
                                        'TimeEnding': 'Time'}, inplace=True)
    data = pd.merge(price_df, total, how='left', on=['Time','Region'])
    data = pd.merge(data, marginal_df.rename(columns={'Intensity_Index': 'Marginal_EI'}),
                    how='left', on=['Time','Region'])
    data.set_index('Time', inplace=True)
    data_hr = data.groupby([data.index.hour, data.Region]).mean()
    data_hr.index.names = ['hour','Region']
    return data_hr.reset_index()
prices
Time Region INTERVENTION Prices
0 2019-01-01 00:05:00 NSW1 0 68.09260
1 2019-01-01 00:05:00 QLD1 0 64.82955
2 2019-01-01 00:05:00 SA1 0 76.50000
3 2019-01-01 00:05:00 TAS1 0 77.06842
4 2019-01-01 00:05:00 VIC1 0 73.19672
... ... ... ... ...
53725 2020-01-01 00:00:00 NSW1 0 48.50000
53726 2020-01-01 00:00:00 QLD1 0 50.30038
53727 2020-01-01 00:00:00 SA1 0 69.00000
53728 2020-01-01 00:00:00 TAS1 0 81.95000
53729 2020-01-01 00:00:00 VIC1 0 68.13710

525600 rows × 4 columns

data_by_hr = arrange_data_into_hourly(prices, total, marginal)
data_by_hr
hour Region INTERVENTION Prices Energy Total_Emissions Average_EI Marginal_EI
0 0 NSW1 0.0 71.563565 597.993603 468.158659 0.780488 0.746162
1 0 QLD1 0.0 64.302266 517.315615 401.527264 0.774328 0.767774
2 0 SA1 0.0 87.138166 127.779344 37.774871 0.302747 0.514765
3 0 TAS1 0.0 86.857243 87.187231 0.545751 0.007020 0.291249
4 0 VIC1 0.0 88.493301 386.696932 354.732110 0.916301 0.497278
... ... ... ... ... ... ... ... ...
115 23 NSW1 0.0 77.933472 628.251938 489.995185 0.779599 0.702943
116 23 QLD1 0.0 68.372989 547.849366 423.348145 0.773068 0.744226
117 23 SA1 0.0 97.778069 132.848236 41.143097 0.315965 0.413469
118 23 TAS1 0.0 93.027470 92.245026 0.716384 0.008509 0.262014
119 23 VIC1 0.0 103.507738 396.581260 357.824731 0.903870 0.389096

120 rows × 8 columns

Chart 1 - CY2019 Emissions Trends#

Toggle to unhide the cell below containing a charting function for plotly

Show code cell content Hide code cell content
def NORD_theme():
    plotly_NORD_theme = pio.templates["simple_white"]
    plotly_NORD_theme.layout.plot_bgcolor = "#f4f4f5" 
    plotly_NORD_theme.layout.paper_bgcolor = "#ffffff"
    plotly_NORD_theme.layout.xaxis.gridcolor = '#d8dee9'
    plotly_NORD_theme.layout.yaxis.gridcolor = '#d8dee9'
    return plotly_NORD_theme

def emissions_trend_chart(hourly_df):
    # Mainland Only
    plt_df = hourly_df.copy()[hourly_df['Region']!='TAS1']

    from nemglo import defaults_plot
    fig = make_subplots(rows=2, cols=2,
                        specs=2*[2*[{'secondary_y': True}]],
                        vertical_spacing=0.15, horizontal_spacing=0.2,
                        subplot_titles=("<b>NSW</b>","<b>QLD</b>","<b>SA</b>","<b>VIC</b>"),
                        shared_xaxes=True, shared_yaxes=True)

    # Data
    for idx, reg in enumerate(plt_df['Region'].unique()):
        sub_df = plt_df[plt_df['Region'] == reg]
        fig.add_trace(go.Scatter(x=sub_df.hour,
                                y=sub_df['Average_EI'].round(3),
                                name=f"Average EI (tCO2-e/MWh)",
                                showlegend=False,
                                line={'color': '#5BC8C5'}), row=(idx)//2+1, col=(idx)%2+1)

        fig.add_trace(go.Scatter(x=sub_df.hour,
                                y=sub_df['Marginal_EI'].round(3),
                                name='Marginal EI (tCO2-e/MWh)',
                                showlegend=False,
                                line={'color': '#4f6980'}), row=(idx)//2+1, col=(idx)%2+1)

        fig.add_trace(go.Scatter(x=sub_df.hour,
                                y=sub_df['Prices'].round(2),
                                name='Energy Price ($/MWh)',
                                showlegend=False,
                                line={'color': '#AD134C'},
                                ), secondary_y=True, row=(idx)//2+1, col=(idx)%2+1)

    # Axis definitions
    ax_time = dict(title="Hour of Day", showgrid=True, mirror=True, showticklabels=True,
                tickvals=[i for i in range(0,25,2)])

    ax_price = dict(title=dict(text="Price ($/MWh)",), showticklabels=True,
                    showgrid=False, autorange=False, automargin=False,
                    range=[0,250], tickvals=[i for i in range(0,251,50)],
                    mirror=True,
                    side="right",
                    color='#AD134C')

    ax_emissions = dict(title="Emissions Intensity (tCO2-e/MWh)", showticklabels=True,
                        showgrid=True, autorange=False,
                        range=[0,1], tickvals=[i*10**-2 for i in range(0, 121, 20)],
                    mirror=True, rangemode='tozero', constraintoward='bottom')

    # Layout
    fig.update_layout(xaxis=ax_time, xaxis2=ax_time, xaxis3=ax_time, xaxis4=ax_time,
                    yaxis=ax_emissions, yaxis2=ax_price,
                    yaxis3=ax_emissions, yaxis4=ax_price,
                    yaxis5=ax_emissions, yaxis6=ax_price,
                    yaxis7=ax_emissions, yaxis8=ax_price)
    fig._data_objs[0].showlegend = True
    fig._data_objs[1].showlegend = True
    fig._data_objs[2].showlegend = True

    # Fonts
    FONT_SIZE = 16
    FONT_STYLE = "Raleway"
    fonts = dict(tickfont=dict(size=FONT_SIZE, family=FONT_STYLE),
                titlefont=dict(size=FONT_SIZE, family=FONT_STYLE))
    fig.update_annotations(font=dict(size=FONT_SIZE, family=FONT_STYLE))  
    fig.update_layout(xaxis=fonts, xaxis2=fonts, xaxis3=fonts, xaxis4=fonts,
                    yaxis=fonts, yaxis2=fonts, yaxis3=fonts, yaxis4=fonts,
                    yaxis5=fonts, yaxis6=fonts, yaxis7=fonts, yaxis8=fonts, yaxis9=fonts,
                    legend=dict(font=dict(size=FONT_SIZE-2, family=FONT_STYLE)),
                    title_font_family=FONT_STYLE,
                    title_font_size=22)

    # Other Formatting
    fig.update_layout(
        title=dict(text=f"Historical CY2019 Hourly Average & Marginal Emissions " + \
                         "Intensities against Price<br>" + \
                         "<sub>NEMED | Average/Marginal Emissions Methodology | " + \
                         "Mainland Regional Emissions Trends</sub>",
                   y=0.95),
        margin=dict(l=80, r=60, t=120, b=20),
        legend=dict(xanchor='center', x=0.5, y=-0.15, orientation='h'),
        hovermode="x",
        width=1000,
        height=800,
        template=NORD_theme())
    return fig
emissions_trend_chart(data_by_hr)
INFO: Using Python-MIP package version 1.13.0

Interactive Plot

Click the image to open the plot as an interactive plotly

../_images/emissions_trends_chart_1.png

previous

Marginal Emissions

next

NEMED module

By Declan Heim, Shayan Naderi
© Copyright 2022, Declan Heim, Shayan Naderi.