Emissions Trends
Contents
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
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
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
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