Marginal Emissions#

Exploring the functionality of NEMED to extract regional marginal emitters from price setting and generator emissions data

Data Preparation#

Import Packages#

Hide code cell content
import nemed

import pandas as pd
# To generate plots shown
import plotly.graph_objects as go
import plotly.express as px
import string

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

Processing Marginal Emissions Data#

Regional Marginal Emissions can be extracted in NEMED using get_marginal_emissions. The following inputs must be specified:

  • start_time define the start of the historical period to collect data for. Must be in the format: “yyyy/mm/dd HH:MM”

  • end_time define the end of the historical period to collect data for. Must be in the format: “yyyy/mm/dd HH:MM”

  • cache specify the local file directory to temporarily store downloaded files

The returned dataframe will contain timeseries data with columns:

Column

Type

Description

Time

datetime

Timestamp reported as end of dispatch interval.

Region

string

The NEM region corresponding to the marginal emitter data.

Intensity_Index

float

The intensity index [tCO2e/MWh] (as by weighted contributions) of the price-setting generators.

DUID

string

Unit identifier of the generator with the largest contribution on the margin for that Time-Region.

CO2E_ENERGY_SOURCE

string

Unit energy source with the largest contribution on the margin for that Time-Region.

The simpliest way to collect emissions data is:

Note

Extracting 1 month of marginal emissions data was found to take approx. 2 minutes in testing.
Extracting 1 year of marginal emissions data was found to take approx. 30 minutes. Cached files amount to 3 GB.

result = nemed.get_marginal_emissions(start_time="2021/01/01 00:00",
                                      end_time="2022/01/01 00:00",
                                      cache="E:/TEMPCACHE2/")
Hide code cell output
WARNING: Warning: Gen_info table only has most recent NEM registration and exemption list. Does not account for retired generators
INFO: Processing Price Setter Files...
0it [00:00, ?it/s]
Reading selected 366 JSON files to pandas, of cached files
INFO: Loading Cached Price Setter Files...
100%|██████████| 366/366 [01:42<00:00,  3.56it/s]
result.head()
Time Region Intensity_Index DUID CO2E_ENERGY_SOURCE
0 2021-01-01 00:05:00 NSW1 0.908305 VP6 Black coal
1 2021-01-01 00:05:00 QLD1 0.855969 VP6 Black coal
2 2021-01-01 00:05:00 SA1 0.867177 VP5 Black coal
3 2021-01-01 00:05:00 TAS1 0.000000 GORDON Hydro
4 2021-01-01 00:05:00 VIC1 0.895716 VP6 Black coal

Aggregate/Filter data#

Data retrieve from NEMED can be manually filtered. For example sorting by region we can produce…

result['Date'] = result['Time'].dt.date
result['Hour'] = result['Time'].dt.hour
result['Season'] = result['Time'].dt.month%12 // 3 +1
result['Season'].replace({1:'Summer', 2:'Autumn', 3:'Winter', 4:'Spring'}, inplace=True)

Example Chart 1#

Which fuel (generator) type is most often the marginal emitter?#

The below chart can be reproduced using the code below (toggle the cell to view)

result['Date'] = result['Time'].dt.date
result['Hour'] = result['Time'].dt.hour
result['Season'] = result['Time'].dt.month%12 // 3 +1
result['Season'].replace({1:'Summer', 2:'Autumn', 3:'Winter', 4:'Spring'}, inplace=True)
Hide code cell content
def NORD_theme():
    plotly_NORD_theme = pio.templates["plotly_white"]
    plotly_NORD_theme.layout.plot_bgcolor = "#f4f4f5" 
    plotly_NORD_theme.layout.paper_bgcolor = "#f4f4f5"
    plotly_NORD_theme.layout.xaxis.gridcolor = '#d8dee9'
    plotly_NORD_theme.layout.yaxis.gridcolor = '#d8dee9'
    return plotly_NORD_theme

def set_font_size(layout, font_size=16):
    layout['titlefont']['size'] = font_size + 4
    layout.legend['font']['size'] = font_size

    for ax in [item for item in layout if item.__contains__('xaxis')]:
        layout[ax].titlefont.size = font_size
        layout[ax].tickfont.size = font_size

    for ax in [item for item in layout if item.__contains__('yaxis')]:
        layout[ax].titlefont.size = font_size
        layout[ax].tickfont.size = font_size

def plot_marginal_fuelsrc(plt_df):
    df = plt_df[plt_df['Region']!="TAS1"].copy(deep=True)
    df['Region'] = df['Region'].str.rstrip(string.digits)
    # Chart
    fig = px.histogram(
        df,
        x="Hour",
        color="CO2E_ENERGY_SOURCE",
        facet_col="Season",
        facet_row="Region",
        barnorm="percent", 
        category_orders={"Season":["Spring", "Summer", "Autumn", "Winter"]},
        height=1000,
        width=1200,
        color_discrete_sequence=['#0C0A0C','#5E3F1C','#00527A','#5A9367','#8F6593','#FFB41F','#D2F1E4','#E9724C',
                                 '#6D250D','#F4B8A4'])
    # Layout
    fig.update_layout(title=dict(text=f"Historical CY2021 Marginal Emitter (by Fuel Source)<br>"+\
                             "<sub>NEMED | Marginal Emissions Methodology | Categorisation by Fuel Source (Mainland Regions)</sub>",
                                 y=0.95),
                      template=NORD_theme(),
                      legend={'title':'', 'orientation':'h', 'xanchor': 'center', 'x': 0.5, 'y':-0.1},
                      margin=dict(l=120, r=60, t=140, b=60))
    
    # Axis Formatting
    fig.update_yaxes(title_text=None, mirror=True, showgrid=False)
    fig.update_yaxes(title="% of dispatch intervals", tickvals=[0,25,50,75,100], col=1,)
    fig.update_xaxes(mirror=True, showgrid=False)
    ax_time = dict(title_text="Time of Day [h]")
    fig.update_layout(xaxis=ax_time, xaxis2=ax_time, xaxis3=ax_time, xaxis4=ax_time)
    
    # Font Formatting
    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_layout(xaxis=fonts, xaxis2=fonts, xaxis3=fonts, xaxis4=fonts,
                      yaxis=fonts, yaxis5=fonts, yaxis9=fonts, yaxis13=fonts,
                      legend=dict(font=dict(size=FONT_SIZE-2, family=FONT_STYLE)),
                      title_font_family=FONT_STYLE,
                      title_font_size=22)
    fig.update_annotations(font=dict(size=FONT_SIZE, family=FONT_STYLE))  
    return fig
fig = plot_marginal_fuelsrc(result)
fig.show()

Interactive Plot

Click the image to open the plot as an interactive plotly

../_images/marginal_emissions_chart_1.png