{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# 1. Electrolyser Characteristics\n",
"**Examples of defining Electrolyser Characteristics in NEMGLO and the influence of such sensitivities.**
\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## 1A. Hydrogen Production Benefit Price\n",
"The **H2 price** feature is optional, yet recommended if your model does not use production targets, in order to incentivise the electrolyser to operate and maximise its load capacity factor. The `h2_price_kg` parameter is set withint the `Electrolyser.load_h2_parameters_preset` function call.\n",
"\n",
"This example demonstrates the impact of changing this parameter on the optimiser results."
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Import Packages"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"hide-cell"
]
},
"outputs": [],
"source": [
"# NEMGLO Packages\n",
"from nemglo import *\n",
"from nemglo import defaults_plot\n",
"\n",
"# Generic Packages\n",
"import pandas as pd\n",
"import plotly.graph_objects as go\n",
"from plotly.subplots import make_subplots\n",
"\n",
"# Display plotly chart in a browser (optional)\n",
"import plotly.io as pio\n",
"pio.renderers.default = \"browser\""
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Load Historical NEM input data"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"data = Market(local_cache=r'E:\\TEMPCACHE',\n",
" intlength=30,\n",
" region='VIC1',\n",
" start_date=\"2020/09/01 00:00\",\n",
" end_date=\"2020/09/08 00:00\",\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"prices = data.get_prices()\n",
"prices.head()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Create Plan"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"h2_price_points = [1.0, 2.0, 3.0, 4.0, 5.0]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"result_load = {}\n",
"for h2_price in h2_price_points:\n",
" p2g = Plan(identifier = \"p2g\")\n",
" p2g.load_market_prices(prices)\n",
"\n",
" h2e = Electrolyser(p2g, identifier='h2e')\n",
" h2e.load_h2_parameters_preset(capacity = 100.0,\n",
" maxload = 100.0,\n",
" minload = 20.0,\n",
" offload = 0.0,\n",
" electrolyser_type = 'PEM',\n",
" sec_profile = 'fixed',\n",
" h2_price_kg = h2_price)\n",
" h2e.add_electrolyser_operation()\n",
"\n",
" p2g.optimise()\n",
"\n",
" result_load.update({h2_price: p2g.get_load()})\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"hide-cell"
]
},
"outputs": [],
"source": [
"fig = make_subplots(rows=2+len(h2_price_points), cols=2,\n",
" subplot_titles=(\"VIC1 Energy Price\",None,None,None,\n",
" \"H2 Price = $1.0/kg\",None,\n",
" \"H2 Price = $2.0/kg\",None,\n",
" \"H2 Price = $3.0/kg\",None,\n",
" \"H2 Price = $4.0/kg\",None,\n",
" \"H2 Price = $5.0/kg\",None),\n",
" specs=[[{'rowspan':2}, {'rowspan':2}],\n",
" [{'colspan':2}, {}],\n",
" [{'colspan':2}, {}],\n",
" [{'colspan':2}, {}],\n",
" [{'colspan':2}, {}],\n",
" [{'colspan':2}, {}],\n",
" [{'colspan':2}, {}],],\n",
" vertical_spacing=0.05, shared_xaxes=True)\n",
"fig.update_annotations(x=0.75)\n",
"ax_price = dict(title=\"Price
($/MWh)\", showgrid=True,\n",
" range=[-60,260], tickvals=[i for i in range(-50,251,50)],\n",
" mirror=True,)\n",
"ax_price_slim = dict(title=None, showgrid=True, showticklabels=False,\n",
" range=[-60,260], tickvals=[i for i in range(-50,251,50)],\n",
" mirror=True)\n",
"ax_load = dict(title=\"Load
(MW)\", showgrid=True, range=[-10,110],\n",
" mirror=True,\n",
" tickvals=[i for i in range(0, 151, 50)])\n",
"ax_time = dict(title=None, mirror=True, showticklabels=False, autorange=False,\n",
" range=[datetime(2020,9,1), prices.iloc[-1,0]+timedelta(minutes=30)],\n",
" domain=[0,0.85])\n",
"ax_time_shown = dict(title=\"Datetime\", mirror=True, showticklabels=True, autorange=False,\n",
" range=[datetime(2020,9,1), prices.iloc[-1,0]+timedelta(minutes=30)],\n",
" domain=[0,0.85]) \n",
"\n",
"# xaxis for price dist\n",
"ax_prc_dist = dict(title=\"Count\",showticklabels=True, mirror=True,\n",
" domain=[0.87,1])\n",
"\n",
"# Price charts\n",
"fig.add_trace(go.Scatter(x=prices['Time'],\n",
" y=prices['Prices'],\n",
" name=\"Price\",\n",
" line={'color':defaults_plot.PAL22['crimson_1']},\n",
" showlegend=False),\n",
" row=1, col=1)\n",
"fig.add_hrect(y0=prices['Prices'].iloc[prices['Prices'].sub(15.15).abs().idxmin()],\n",
" y1=prices['Prices'].iloc[prices['Prices'].sub(45.45).abs().idxmin()],\n",
" fillcolor=defaults_plot.PAL22['salmon_2'], opacity=0.15,\n",
" layer=\"above\", line_width=0, row=[1,2], col=1)\n",
"fig.add_trace(go.Histogram(y=prices['Prices'],\n",
" name=\"Price Dist\",\n",
" marker=dict(color = defaults_plot.PAL22['crimson_1']),\n",
" showlegend=False),\n",
" row=1, col=2)\n",
"\n",
"# Data\n",
"for idx, element in enumerate(h2_price_points):\n",
" if idx < 7:\n",
" fig.add_trace(go.Scatter(x=result_load[element]['time'],\n",
" y=result_load[element]['value'],\n",
" name=\"${}/kg\".format(str(element)),\n",
" line={'color':defaults_plot.PAL22['blue_3']},\n",
" showlegend=False,\n",
" xaxis=\"x2\", yaxis=\"y\"),\n",
" row=idx+3, col=1)\n",
"\n",
" else:\n",
" fig.add_trace(go.Scatter(x=result_load[element]['time'],\n",
" y=result_load[element]['value'],\n",
" name=\"${}/kg\".format(str(element)),\n",
" showlegend=False,\n",
" line={'color':defaults_plot.PAL22['blue_3']}),\n",
" row=idx+3, col=1)\n",
"# Layout\n",
"fig.update_layout(xaxis=ax_time, xaxis2=ax_prc_dist,\n",
" xaxis3=ax_time, xaxis4=None,\n",
" xaxis5=ax_time, xaxis6=None,\n",
" xaxis7=ax_time, xaxis8=None,\n",
" xaxis9=ax_time, xaxis10=None,\n",
" xaxis11=ax_time, xaxis12=None,\n",
" xaxis13=ax_time_shown, xaxis14=None)\n",
"fig.update_layout(yaxis=ax_price, yaxis2=ax_price_slim,\n",
" yaxis3=None, yaxis4=None,\n",
" yaxis5=ax_load, yaxis6=None,\n",
" yaxis7=ax_load, yaxis8=None,\n",
" yaxis9=ax_load, yaxis10=None,\n",
" yaxis11=ax_load , yaxis12=None,\n",
" yaxis13=ax_load, yaxis14=None)\n",
"fig.update_xaxes(showticklabels=True, row=10, col=1)\n",
"\n",
"# Fonts\n",
"FONT_SIZE = 17\n",
"FONT_STYLE = \"Raleway\"\n",
"fonts = dict(tickfont=dict(size=FONT_SIZE, family=FONT_STYLE),\n",
" titlefont=dict(size=FONT_SIZE, family=FONT_STYLE))\n",
"fonts_dist = dict(tickfont=dict(size=FONT_SIZE-6, family=FONT_STYLE),\n",
" titlefont=dict(size=FONT_SIZE-6, family=FONT_STYLE))\n",
"fig.update_layout(xaxis=fonts, xaxis2=fonts,\n",
" xaxis3=fonts, xaxis4=fonts,\n",
" xaxis5=fonts, xaxis6=fonts,\n",
" xaxis7=fonts, xaxis8=fonts,\n",
" xaxis9=fonts, xaxis10=fonts,\n",
" xaxis11=fonts, xaxis12=fonts,\n",
" xaxis13=fonts, xaxis14=fonts,\n",
" yaxis=fonts, yaxis2=fonts,\n",
" yaxis3=fonts, yaxis4=fonts,\n",
" yaxis5=fonts, yaxis6=fonts,\n",
" yaxis7=fonts, yaxis8=fonts,\n",
" yaxis9=fonts,yaxis10=fonts,\n",
" yaxis11=fonts, yaxis12=fonts,\n",
" yaxis13=fonts, yaxis14=fonts)\n",
"\n",
"fig.update_annotations(font=dict(size=FONT_SIZE, family=FONT_STYLE))\n",
"fig.update_layout(legend=dict(font=dict(size=FONT_SIZE, family=FONT_STYLE)))\n",
"# Legend\n",
"fig.update_layout(**defaults_plot.plt_markup.legend_bottom())\n",
"fig.update_layout(legend=dict(y=-0.2))\n",
"# Other formatting\n",
"fig.update_layout(title=f\"Price Responsive Load with H2 Production Benefit Price Sensitivity
\"+\\\n",
" \"NEMGLO | Hydrogen Sensitivities | VIC-2020 Case Study\",\n",
" title_font_family=FONT_STYLE,\n",
" title_font_size=22)\n",
"fig.update_layout(**defaults_plot.plt_size.medium())\n",
"fig.update_layout(template=defaults_plot.NORD_theme())\n",
"fig.show()\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"```{admonition} Interactive Plot\n",
"Click the image to open the plot as an interactive plotly\n",
"```\n",
"\n",
"```{image} h2_pbt.png\n",
":target: ../../_static/html_charts/hydrogen_sensitivity_h2pbt.html\n",
"```"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## 1B. Minimum Stable Loading\n",
"The MSL feature is, by default, considered in NEMGLO by `load_h2_parameters_preset` and `add_electrolyser_operation` if the `minload` value parsed is greater than zero."
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Load Historical NEM input data"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"data = Market(local_cache=r'E:\\TEMPCACHE',\n",
" intlength=30,\n",
" region='VIC1',\n",
" start_date=\"2020/01/01 00:00\",\n",
" end_date=\"2020/01/08 00:00\",\n",
")\n",
"prices = data.get_prices()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"msl_levels = [0.0, 20.0, 40.0]\n",
"result_load, result_product = {}, {}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"hide-output"
]
},
"outputs": [],
"source": [
"for msl in msl_levels:\n",
" # Create the plan object and load price trace\n",
" P2G = Plan(identifier = \"P2G\")\n",
" P2G.load_market_prices(prices)\n",
"\n",
" # Create the Hydrogen Electrolyser and add parameters to model\n",
" h2e = Electrolyser(P2G, identifier='H2E')\n",
" h2_price = 4.0\n",
" h2e.load_h2_parameters_preset(capacity = 100.0,\n",
" maxload = 100.0,\n",
" minload = msl,\n",
" offload = 0.0,\n",
" electrolyser_type = 'PEM',\n",
" sec_profile = 'fixed',\n",
" h2_price_kg = h2_price)\n",
" h2e.add_electrolyser_operation()\n",
"\n",
" # Set the production targets of the Electrolyser\n",
" h2e._set_production_target(target_value=800,bound=\"max\", period=\"hour\")\n",
" h2e._set_production_target(target_value=100,bound=\"min\", period=\"hour\")\n",
"\n",
" # Set a ramping cost\n",
" h2e._set_ramp_variable()\n",
" h2e._set_ramp_cost(cost=10)\n",
"\n",
" # Run Optimisation\n",
" P2G.optimise(solver_name=\"GUROBI\")\n",
" result_load.update({str(msl): P2G.get_load()})\n",
" result_product.update({str(msl) : P2G.get_production()})"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"hide-cell"
]
},
"outputs": [],
"source": [
"# Comparison Chart of varying MSL levels\n",
"fig = make_subplots(rows=4, cols=1, subplot_titles=(\"VIC1 Energy Price\",\"MSL = 0 MW\",\"MSL = 20 MW\", \"MSL = 40 MW\"), \\\n",
" specs=[[{}]] + 3*[[{'secondary_y': True}]], vertical_spacing=0.07, shared_xaxes=True, shared_yaxes=False)\n",
"fig.update_annotations(x=0.85)\n",
"\n",
"\n",
"# Y axis definitions\n",
"price_axis = dict(title=\"Price
($/MWh)\", showgrid=True, autorange=False,\n",
" range=[-260,140], #tickvals=[i for i in range(-50,251,50)],\n",
" mirror=True, overlaying=\"y\", side=\"left\",\n",
" color=defaults_plot.PAL22['crimson_1'])\n",
"\n",
"load_axis = dict(title=\"Load
(MW)\", showgrid=True,\n",
" range=[-10,110], tickvals=[i for i in range(0, 101, 20)],\n",
" mirror=True, rangemode='tozero', constraintoward='bottom',\n",
" color=defaults_plot.PAL22['blue_3'])\n",
"\n",
"h2_axis = dict(title=\"H2 Production
(kg)\", showgrid=False,\n",
" range=[-80,880], tickvals=[i for i in range(0, 801, 160)],\n",
" mirror=True,\n",
" color=defaults_plot.PAL22['gold_3'])\n",
"\n",
"# Data: Price subplot\n",
"fig.add_trace(go.Scatter(x=prices['Time'],\n",
" y=prices['Prices'],\n",
" name='Energy Price ($/MWh)',\n",
" line={'color':defaults_plot.PAL22['crimson_1']},\n",
" showlegend=True), row=1, col=1)\n",
"# Add price production benefit threshold\n",
"fig.add_trace(go.Line(x=prices['Time'], y=[60.6]*len(prices['Time']), line={\"color\": defaults_plot.PAL22['crimson_1'],\\\n",
" \"dash\":\"dot\"}, name=\"PBT ($/MWh)\"), row=1, col=1)\n",
"\n",
"fig.update_xaxes(title=None, showgrid=True, mirror=True, row=1, col=1)\n",
"\n",
"# Data: MSL levels\n",
"for idx, msl in enumerate(msl_levels):\n",
" fig.add_trace(go.Scatter(x=result_load[str(msl)]['time'],\n",
" y=result_load[str(msl)]['value'],\n",
" name=\"Load (MW)\",\n",
" showlegend=False,\n",
" line={'color':defaults_plot.PAL22['blue_3']}\n",
" ), row=2+idx, col=1)\n",
"\n",
" fig.add_trace(go.Scatter(x=result_product[str(msl)]['time'],\n",
" y=result_product[str(msl)]['value'],\n",
" name=\"H2 Production (kg)\",\n",
" showlegend=False,\n",
" line={'color':defaults_plot.PAL22['gold_3'], 'dash':'dot', 'width':2}\n",
" ), row=2+idx, col=1, secondary_y=True)\n",
"\n",
" fig.update_xaxes(showticklabels=False, showgrid=True, mirror=True, row=2+idx, col=1)\n",
"\n",
"# Set x_labels at last row\n",
"fig.update_xaxes(showticklabels=True, showgrid=True, mirror=True, row=2+idx, col=1,\n",
" range=[datetime(2020,1,1),prices['Time'].iloc[-1]])\n",
"\n",
"# Update y axis\n",
"fig.update_layout(yaxis=price_axis,\n",
" yaxis2=load_axis, yaxis3=h2_axis,\n",
" yaxis4=load_axis, yaxis5=h2_axis,\n",
" yaxis6=load_axis, yaxis7=h2_axis)\n",
"\n",
"# Fonts\n",
"FONT_SIZE = 17\n",
"FONT_STYLE = \"Raleway\"\n",
"fonts = dict(tickfont=dict(size=FONT_SIZE, family=FONT_STYLE),\n",
" titlefont=dict(size=FONT_SIZE, family=FONT_STYLE))\n",
"\n",
"fig.update_layout(xaxis=fonts, xaxis2=fonts,\n",
" xaxis3=fonts, xaxis4=fonts,\n",
" yaxis=fonts,\n",
" yaxis2=fonts, yaxis3=fonts,\n",
" yaxis4=fonts, yaxis5=fonts,\n",
" yaxis6=fonts, yaxis7=fonts)\n",
"fig.update_annotations(font=dict(size=FONT_SIZE, family=FONT_STYLE))\n",
"fig.update_layout(legend=dict(font=dict(size=FONT_SIZE, family=FONT_STYLE)))\n",
"\n",
"# Legend\n",
"fig._data_objs[2].showlegend = True\n",
"fig._data_objs[3].showlegend = True\n",
"fig.update_layout(**defaults_plot.plt_markup.legend_bottom())\n",
"\n",
"# Other formatting\n",
"fig.update_layout(title=f\"Load Constrained by Production Targets with MSL Sensitivity
\"+\\\n",
" \"NEMGLO | Hydrogen Sensitivities | VIC-2020 Case Study\",\n",
" title_font_family=FONT_STYLE,\n",
" title_font_size=22)\n",
"fig.update_layout(**defaults_plot.plt_size.medium())\n",
"fig.update_layout(template=defaults_plot.NORD_theme())\n",
"\n",
"# Show/Save\n",
"fig.show()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"```{admonition} Interactive Plot\n",
"Click the image to open the plot as an interactive plotly\n",
"```\n",
"\n",
"```{image} h2_msl.png\n",
":target: ../../_static/html_charts/hydrogen_sensitivity_h2msl.html\n",
"```"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## 1C. Hydrogen Production Targets\n",
"\n",
"### Load Historical NEM input data"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"data = Market(local_cache=r'E:\\TEMPCACHE',\n",
" intlength=30,\n",
" region='NSW1',\n",
" start_date=\"2022/03/01 00:00\",\n",
" end_date=\"2022/04/01 00:00\",\n",
")\n",
"prices = data.get_prices()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"target_max = [(25441, \"day\"), (788641, \"month\")]\n",
"target_min = [(25440, \"day\"), (788640, \"month\")]\n",
"cases = ['Day', 'Month']\n",
"result_load, result_product = {}, {}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"hide-output"
]
},
"outputs": [],
"source": [
"for idx, name in enumerate(cases):\n",
"\n",
" P2G = Plan(identifier = \"P2G\")\n",
" P2G.load_market_prices(prices)\n",
"\n",
" h2e = Electrolyser(P2G, identifier='H2E')\n",
" h2e.load_h2_parameters_preset(capacity = 100.0,\n",
" maxload = 100.0,\n",
" minload = 0.0,\n",
" offload = 0.0,\n",
" electrolyser_type = 'PEM',\n",
" sec_profile = 'fixed',\n",
" h2_price_kg = 4.0)\n",
" h2e.add_electrolyser_operation()\n",
"\n",
" if target_min[idx][1] != 'none':\n",
" h2e._set_production_target(target_value=target_min[idx][0],bound=\"min\", period=target_min[idx][1])\n",
"\n",
" if target_max[idx][1] != 'none':\n",
" h2e._set_production_target(target_value=target_max[idx][0],bound=\"max\", period=target_max[idx][1])\n",
"\n",
" h2e._set_ramp_variable()\n",
" h2e._set_ramp_cost(cost=10)\n",
"\n",
" P2G.optimise(solver_name=\"GUROBI\")\n",
" result_load.update({name: P2G.get_load()})\n",
" result_product.update({name: P2G.get_production()})"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"hide-cell"
]
},
"outputs": [],
"source": [
"# Comparison Chart of varying MSL levels\n",
"fig = make_subplots(rows=4, cols=1, subplot_titles=(\"NSW1 Energy Price\",\"\",\"Daily 70% CF\", \"Monthly 70% CF\"), \\\n",
" specs=[[{}]]*2 + 2*[[{'secondary_y': True}]], vertical_spacing=0.07, shared_xaxes=True, shared_yaxes=False)\n",
"fig.update_annotations(x=0.85)\n",
"p_thres = 200\n",
"\n",
"# Y axis definitions\n",
"ax_price = dict(title=\"Price
($/MWh)\", showgrid=True, autorange=True,domain=[0.56,0.78],\n",
" range=[-100,200], tickvals=[i for i in range(-100,201,100)],\n",
" mirror=True,\n",
" color=defaults_plot.PAL22['crimson_1'])\n",
"\n",
"ax_price_log = dict(type='log', showgrid=True, autorange=True,\n",
" tickvals=[300,500,1000,2000],\n",
" mirror=True, color=defaults_plot.PAL22['crimson_1'])\n",
"\n",
"ax_load = dict(title=\"Load
(MW)\", showgrid=True,\n",
" range=[-10,110], tickvals=[i for i in range(0, 101, 20)],\n",
" mirror=True,\n",
" color=defaults_plot.PAL22['blue_3'])\n",
"\n",
"ax_h2 = dict(title=\"H2 Production
(kg)\", showgrid=False,\n",
" range=[-80,880], tickvals=[i for i in range(0, 801, 160)],\n",
" mirror=True,\n",
" color=defaults_plot.PAL22['gold_3'])\n",
"# fig.update_xaxes(minor=dict(ticks=\"inside\", showgrid=True), row=5, col=1)\n",
"\n",
"ax_time_y1 = dict(showgrid=True, mirror=True, visible=True,\n",
" minor=dict(dtick='M', showgrid=True, gridcolor=defaults_plot.NORD['snow_3'], griddash=\"solid\"))\n",
"\n",
"ax_time_y2 = dict(showgrid=True, mirror=False,\n",
" minor=dict(dtick='M', showgrid=True, gridcolor=defaults_plot.NORD['snow_3'], griddash=\"solid\"))\n",
"\n",
"ax_time = dict(showgrid=True, mirror=True, range=[prices.iloc[0,0],prices.iloc[-1,0]],\n",
" minor=dict(dtick='M', showgrid=True, gridcolor=defaults_plot.NORD['snow_3'], griddash=\"solid\"))\n",
"\n",
"# Data: Price subplot\n",
"prices['log'] = np.where(prices['Prices']>p_thres, prices['Prices'],np.nan)\n",
"prices['normal'] = np.where(prices['Prices']<=p_thres, prices['Prices'],np.nan)\n",
"\n",
"fig.add_trace(go.Scatter(x=prices['Time'],\n",
" y=prices['log'],\n",
" mode=\"markers\", marker_size=4,\n",
" name='Energy Price ($/MWh)',\n",
" line={'color':defaults_plot.PAL22['crimson_1']},\n",
" showlegend=False), row=1, col=1)\n",
"\n",
"fig.add_trace(go.Scatter(x=prices['Time'],\n",
" y=prices['normal'],\n",
" mode=\"markers\", marker_size=4,\n",
" name='Energy Price ($/MWh)',\n",
" line={'color':defaults_plot.PAL22['crimson_1']},\n",
" showlegend=True), row=2, col=1)\n",
"# Add price production benefit threshold\n",
"fig.add_trace(go.Line(x=prices['Time'], y=[60.6]*len(prices['Time']), line={\"color\": defaults_plot.PAL22['crimson_1'],\\\n",
" \"dash\":\"dot\"}, name=\"PBT ($/MWh)\"), row=2, col=1)\n",
"\n",
"\n",
"# Data: MSL levels\n",
"for idx, name in enumerate(cases):\n",
" fig.add_trace(go.Scatter(x=result_load[name]['time'],\n",
" y=result_load[name]['value'],\n",
" name=\"Load (MW)\",\n",
" showlegend=False,\n",
" legendgroup=\"load\",\n",
" line={'color':defaults_plot.PAL22['blue_3']}\n",
" ), row=3+idx, col=1)\n",
"\n",
" fig.add_trace(go.Scatter(x=result_product[name]['time'],\n",
" y=result_product[name]['value'],\n",
" name=\"H2 Production (kg)\",\n",
" showlegend=False,\n",
" legendgroup=\"h2\",\n",
" line={'color':defaults_plot.PAL22['gold_2'], 'dash':'dot', 'width':2}\n",
" ), row=3+idx, col=1, secondary_y=True)\n",
"\n",
"\n",
"# Update y axis\n",
"fig.update_layout(xaxis=ax_time_y1, xaxis2=ax_time_y2, xaxis3=ax_time, xaxis4=ax_time, xaxis5=ax_time,\n",
" yaxis=ax_price_log, yaxis2=ax_price,\n",
" yaxis3=ax_load, yaxis4=ax_h2,\n",
" yaxis5=ax_load, yaxis6=ax_h2,\n",
" yaxis7=ax_load, yaxis8=ax_h2)\n",
"fig.update_xaxes(showticklabels=True, showgrid=True, mirror=True, row=5, col=1)\n",
"\n",
"fig.update_xaxes(minor_dtick='M')\n",
"\n",
"# Fonts\n",
"FONT_SIZE = 17\n",
"FONT_STYLE = \"Raleway\"\n",
"fonts = dict(tickfont=dict(size=FONT_SIZE, family=FONT_STYLE),\n",
" titlefont=dict(size=FONT_SIZE, family=FONT_STYLE))\n",
"\n",
"fig.update_layout(xaxis=fonts, xaxis2=fonts,\n",
" xaxis3=fonts, xaxis4=fonts, xaxis5=fonts,\n",
" yaxis=fonts,\n",
" yaxis2=fonts, yaxis3=fonts,\n",
" yaxis4=fonts, yaxis5=fonts,\n",
" yaxis6=fonts, yaxis7=fonts, yaxis8=fonts)\n",
"fig.update_annotations(font=dict(size=FONT_SIZE, family=FONT_STYLE))\n",
"fig.update_layout(legend=dict(font=dict(size=FONT_SIZE, family=FONT_STYLE)))\n",
"\n",
"# Legend\n",
"fig._data_objs[3].showlegend = True\n",
"fig._data_objs[4].showlegend = True\n",
"fig.update_layout(**defaults_plot.plt_markup.legend_bottom())\n",
"\n",
"# Other formatting\n",
"fig.update_layout(title=f\"Production Targets Sensitivity by varying Target Duration
\"+\\\n",
" \"NEMGLO | Hydrogen Sensitivities | NSW-2022 Case Study\",\n",
" title_font_family=FONT_STYLE,\n",
" title_font_size=22)\n",
"fig.update_layout(**defaults_plot.plt_size.medium())\n",
"fig.update_layout(template=defaults_plot.NORD_theme())\n",
"\n",
"# Show/Save\n",
"fig.show()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"```{admonition} Interactive Plot\n",
"Click the image to open the plot as an interactive plotly\n",
"```\n",
"\n",
"```{image} h2_producttarget.png\n",
":target: ../../_static/html_charts/hydrogen_sensitivity_h2producttarget.html\n",
"```"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## 1D. Hydrogen Storage\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"data = Market(local_cache=r'E:\\TEMPCACHE',\n",
" intlength=30,\n",
" region='VIC1',\n",
" start_date=\"2020/08/01 00:00\",\n",
" end_date=\"2020/09/01 00:00\",\n",
")\n",
"prices = data.get_prices()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"h2_price = 4.0\n",
"result_load, result_product, result_storage = {}, {}, {}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"names = [3, 12, 24, 48]\n",
"s_en = [True]*(len(names))\n",
"storages = [400*2*names[i] for i in range(len(names))]\n",
"\n",
"def special_model(msl, storage_en, storage_max, storage_flow, max_hr_target, min_hr_target, name):\n",
"\n",
" P2G = Plan(identifier = \"P2G\")\n",
" P2G.load_market_prices(prices)\n",
"\n",
" h2e = Electrolyser(P2G, identifier='H2E')\n",
" h2e.load_h2_parameters_preset(capacity = 100.0,\n",
" maxload = 100.0,\n",
" minload = msl,\n",
" offload = 0.0,\n",
" electrolyser_type = 'PEM',\n",
" sec_profile = 'fixed',\n",
" h2_price_kg = h2_price)\n",
" h2e.add_electrolyser_operation()\n",
"\n",
" if storage_en:\n",
" # Storage\n",
" h2e._storage_max = storage_max\n",
" h2e._storage_final = 0.0\n",
" h2e._set_h2_production_tracking()\n",
" h2e._set_h2_storage_max()\n",
" h2e._set_h2_storage_final()\n",
" P2G.relax_and_price_constr_violation('H2E-h2_stored_limit',\"+\",100)\n",
"\n",
" if storage_flow:\n",
" h2e._set_storage_external_flow(external_flow=storage_flow)\n",
"\n",
" h2e._set_ramp_variable()\n",
" h2e._set_ramp_cost(cost=10)\n",
"\n",
" P2G.optimise(solver_name=\"GUROBI\")\n",
" result_load.update({name: P2G.get_load()})\n",
" result_product.update({name: P2G.get_production()})\n",
"\n",
" if storage_en:\n",
" result_storage.update({name: P2G._format_out_vars_timeseries('H2E-h2_stored')})\n",
"\n",
" return P2G"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"hide-output"
]
},
"outputs": [],
"source": [
"outlist = []\n",
"for idx in range(len(storages)):\n",
" model = special_model(msl = 0.0,\n",
" storage_en=s_en[idx],\n",
" storage_max=storages[idx],\n",
" storage_flow=-400,\n",
" max_hr_target=True,\n",
" min_hr_target=True,\n",
" name=names[idx],\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"tags": [
"hide-cell"
]
},
"outputs": [],
"source": [
"fig = make_subplots(rows=len(names)+2, cols=1, subplot_titles=(\"VIC1 Energy Price\",\"\",\n",
" \"No storage\",\n",
" \"3-hour storage\",\n",
" \"12-hour storage\",\n",
" \"24-hour storage\",\n",
" \"48-hour storage\"), \\\n",
" specs=[[{}]]*2 + len(names)*[[{'secondary_y': True}]], vertical_spacing=0.05, shared_xaxes=True, shared_yaxes=False)\n",
"fig.update_annotations(x=0.85)\n",
"\n",
"p_thres = 200\n",
"\n",
"# Y axis definitions\n",
"ax_price = dict(title=\"Price
($/MWh)\", showgrid=True, autorange=False, domain=[0.7,0.86],\n",
" range=[-100,200], tickvals=[i for i in range(-100,201,100)],\n",
" mirror=True,\n",
" color=defaults_plot.PAL22['crimson_1'])\n",
"\n",
"ax_price_log = dict(type='log', #domain=[0.925,1],\n",
" showgrid=True, autorange=True, tickvals=[300, 500, 1000,2000],\n",
" mirror=True, color=defaults_plot.PAL22['crimson_1'])\n",
"\n",
"ax_h2 = dict(title=\"Production
(kg)\", showgrid=True, range=[-80,880], mirror=True,\n",
" rangemode='tozero', constraintoward='bottom',\n",
" color=defaults_plot.PAL22['gold_3'])\n",
"\n",
"ax_stor = dict(title=\"Stored
(%)\", showgrid=False, autorange=False, range=[-10,110], mirror=True,\n",
" color=defaults_plot.PAL22['green_3'])\n",
"\n",
"ax_time_y1 = dict(showgrid=True, mirror=True, visible=True)\n",
"ax_time_y2 = dict(showgrid=True, mirror=False)\n",
"ax_time = dict(showgrid=True, mirror=True, autorange=False,\n",
" range=[prices.iloc[0,0]-timedelta(minutes=30),prices.iloc[-1,0]])\n",
"\n",
"# Data: Price subplot\n",
"prices['log'] = np.where(prices['Prices']>p_thres, prices['Prices'],np.nan)\n",
"prices['normal'] = np.where(prices['Prices']<=p_thres, prices['Prices'],np.nan)\n",
"\n",
"fig.add_trace(go.Scatter(x=prices['Time'],\n",
" y=prices['log'],\n",
" mode=\"markers\", marker_size=4,\n",
" name='Energy Price ($/MWh)',\n",
" line={'color':defaults_plot.PAL22['crimson_1']},\n",
" showlegend=False), row=1, col=1)\n",
"\n",
"fig.add_trace(go.Scatter(x=prices['Time'],\n",
" y=prices['normal'],\n",
" mode=\"markers\", marker_size=4,\n",
" name='Energy Price ($/MWh)',\n",
" line={'color':defaults_plot.PAL22['crimson_1']},\n",
" showlegend=True), row=2, col=1)\n",
"# Add price production benefit threshold\n",
"fig.add_trace(go.Line(x=prices['Time'], y=[60.6]*len(prices['Time']), line={\"color\": 'black',\\\n",
" \"dash\":\"dot\"}, name=\"PBT ($/MWh)\"), row=2, col=1)\n",
"\n",
"# Data H2:\n",
"for idx in range(len(names)):\n",
"\n",
"\n",
" #if (idx <= len(names)-2):\n",
" fig.add_trace(go.Scatter(x=result_storage[names[idx]]['time'],\n",
" y=(result_storage[names[idx]]['value'] / storages[idx])*100,\n",
" name=\"H2 Stored (%)\",\n",
" showlegend=False,\n",
" line={'color': '#A0BBA2'} #defaults_plot.PAL22['green_2']}\n",
" ), row=3+idx, col=1, secondary_y=True)\n",
"\n",
" fig.add_trace(go.Scatter(x=result_product[names[idx]]['time'],\n",
" y=result_product[names[idx]]['value'],\n",
" name=\"H2 Production (kg)\",\n",
" showlegend=False,\n",
" line={'color': defaults_plot.PAL22['gold_3b']}\n",
" ), row=3+idx, col=1)\n",
"\n",
"\n",
"# Update y axis\n",
"fig.update_layout(xaxis=ax_time_y1, xaxis2=ax_time_y2, xaxis3=ax_time, xaxis4=ax_time, xaxis5=ax_time, xaxis6=ax_time, xaxis7=ax_time,\n",
" yaxis=ax_price_log, yaxis2=ax_price,\n",
" yaxis3=ax_h2, yaxis4=ax_stor,\n",
" yaxis5=ax_h2, yaxis6=ax_stor,\n",
" yaxis7=ax_h2, yaxis8=ax_stor,\n",
" yaxis9=ax_h2, yaxis10=ax_stor,\n",
" yaxis11=ax_h2, yaxis12=ax_stor,)\n",
"\n",
"fig.update_xaxes(showticklabels=True, showgrid=True, mirror=True, row=7, col=1)\n",
"\n",
"\n",
"# Fonts\n",
"FONT_SIZE = 17\n",
"FONT_STYLE = \"Raleway\"\n",
"fonts = dict(tickfont=dict(size=FONT_SIZE, family=FONT_STYLE),\n",
" titlefont=dict(size=FONT_SIZE, family=FONT_STYLE))\n",
"\n",
"fig.update_layout(xaxis=fonts, xaxis2=fonts, xaxis3=fonts, xaxis4=fonts, xaxis5=fonts, xaxis6=fonts, xaxis7=fonts,\n",
" yaxis=fonts, yaxis2=fonts, yaxis3=fonts, yaxis4=fonts, yaxis5=fonts, yaxis6=fonts,\n",
" yaxis7=fonts, yaxis8=fonts, yaxis9=fonts, yaxis10=fonts, yaxis11=fonts, yaxis12=fonts)\n",
"\n",
"fig.update_annotations(font=dict(size=FONT_SIZE, family=FONT_STYLE))\n",
"fig.update_layout(legend=dict(font=dict(size=FONT_SIZE, family=FONT_STYLE)))\n",
"\n",
"# Legend\n",
"fig._data_objs[4].showlegend = True\n",
"fig._data_objs[5].showlegend = True\n",
"fig.update_layout(**defaults_plot.plt_markup.legend_bottom())\n",
"fig.update_layout(legend=dict(y=-0.06))\n",
"\n",
"# Other formatting\n",
"fig.update_layout(title=f\"Hydrogen Storage Sizing Sensitivity
\"+\\\n",
" \"NEMGLO | Hydrogen Sensitivities | VIC-2020 Case Study\",\n",
" title_font_family=FONT_STYLE,\n",
" title_font_size=22)\n",
"fig.update_layout(**defaults_plot.plt_size.medium())\n",
"fig.update_layout(template=defaults_plot.NORD_theme())\n",
"\n",
"# Show/Save\n",
"fig.show()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"```{admonition} Interactive Plot\n",
"Click the image to open the plot as an interactive plotly\n",
"```\n",
"\n",
"```{image} h2_storage.png\n",
":target: ../../_static/html_charts/hydrogen_sensitivity_h2storage.html\n",
"```"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## 1E. Specific Energy Consumption\n",
"The SEC functionality in NEMGLO is two-fold; a **fixed** profile, whereby all load (MW) produces an equivalent amount of hydrogen based on the defined SEC (kWh/kg), or a **variable** profile whereby the energy consumption (kWh/kg) varies depending on the load (MW).\n",
"\n",
"To demonstrate these features, we iterate through an arbitrary simulation period and each iteration force the load to a certain MW value using the advanced feature `_set_force_h2_load`. These iterations are then repeated for each combination of `fixed` and `variable` with both `PEM` and `AE` electrolyser types."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"inputdata = nemosis_data(intlength=30, local_cache=r'E:\\TEMPCACHE')\n",
"start = \"02/01/2020 00:00\"\n",
"end = \"02/01/2020 01:00\"\n",
"region = 'VIC1'\n",
"inputdata.set_dates(start, end)\n",
"inputdata.set_region(region)\n",
"prices = inputdata.get_prices()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For a fixed SEC profile using PEM"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pem_xpoints_f, pem_ypoints_f = [], []\n",
"\n",
"for x_val in range(20,101,1):\n",
" P2G = Plan('P2G')\n",
" P2G.load_market_prices(prices)\n",
"\n",
" h2e = Electrolyser(P2G, identifier='H2E')\n",
" h2e.load_h2_parameters_preset(capacity = 100.0,\n",
" maxload = 100.0,\n",
" minload = 20.0,\n",
" offload = 0.0,\n",
" electrolyser_type = 'PEM',\n",
" sec_profile = 'fixed',\n",
" h2_price_kg = 6.0)\n",
" h2e.add_electrolyser_operation()\n",
" h2e._set_force_h2_load(mw_value=x_val)\n",
" P2G.optimise()\n",
"\n",
" result_load = P2G.get_load()\n",
" result_product = P2G.get_production()\n",
" pem_xpoints_f += [float(result_load.loc[result_load['interval']==0,'value'])]\n",
" pem_ypoints_f += [float(result_product.loc[result_product['interval']==0,'value'])]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For a variable SEC profile using PEM"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pem_xpoints, pem_ypoints = [], []\n",
"\n",
"for x_val in range(20,101,1):\n",
" P2G = Plan('P2G')\n",
" P2G.load_market_prices(prices)\n",
"\n",
" h2e = Electrolyser(P2G, identifier='H2E')\n",
" h2e.load_h2_parameters_preset(capacity = 100.0,\n",
" maxload = 100.0,\n",
" minload = 20.0,\n",
" offload = 0.0,\n",
" electrolyser_type = 'PEM',\n",
" sec_profile = 'variable',\n",
" h2_price_kg = 6.0)\n",
" h2e.add_electrolyser_operation()\n",
" h2e._set_force_h2_load(mw_value=x_val)\n",
" P2G.optimise()\n",
"\n",
" result_load = P2G.get_load()\n",
" result_product = P2G.get_production()\n",
" pem_xpoints += [float(result_load.loc[result_load['interval']==0,'value'])]\n",
" pem_ypoints += [float(result_product.loc[result_product['interval']==0,'value'])]\n",
"\n",
"# Save predefined SEC points for plotting\n",
"pem_sec_spec = h2e._sec_variable_points"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For a fixed profile using AE"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ae_xpoints_f, ae_ypoints_f = [], []\n",
"\n",
"for x_val in range(20,101,1):\n",
" P2G = Plan('P2G')\n",
" P2G.load_market_prices(prices)\n",
"\n",
" h2e = Electrolyser(P2G, identifier='H2E')\n",
" h2e.load_h2_parameters_preset(capacity = 100.0,\n",
" maxload = 100.0,\n",
" minload = 20.0,\n",
" offload = 0.0,\n",
" electrolyser_type = 'AE',\n",
" sec_profile = 'fixed',\n",
" h2_price_kg = 6.0)\n",
" h2e.add_electrolyser_operation()\n",
" h2e._set_force_h2_load(mw_value=x_val)\n",
" P2G.optimise()\n",
"\n",
" result_load = P2G.get_load()\n",
" result_product = P2G.get_production()\n",
" ae_xpoints_f += [float(result_load.loc[result_load['interval']==0,'value'])]\n",
" ae_ypoints_f += [float(result_product.loc[result_product['interval']==0,'value'])]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"For a variable profile using AE"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"ae_xpoints, ae_ypoints = [], []\n",
"\n",
"for x_val in range(20,101,1):\n",
" P2G = Plan('P2G')\n",
" P2G.load_market_prices(prices)\n",
"\n",
" h2e = Electrolyser(P2G, identifier='H2E')\n",
" h2e.load_h2_parameters_preset(capacity = 100.0,\n",
" maxload = 100.0,\n",
" minload = 20.0,\n",
" offload = 0.0,\n",
" electrolyser_type = 'AE',\n",
" sec_profile = 'variable',\n",
" h2_price_kg = 6.0)\n",
" h2e.add_electrolyser_operation()\n",
" h2e._set_force_h2_load(mw_value=x_val)\n",
" P2G.optimise()\n",
"\n",
" result_load = P2G.get_load()\n",
" result_product = P2G.get_production()\n",
" ae_xpoints += [float(result_load.loc[result_load['interval']==0,'value'])]\n",
" ae_ypoints += [float(result_product.loc[result_product['interval']==0,'value'])]\n",
"\n",
"# Save predefined SEC points for plotting\n",
"ae_sec_spec = h2e._sec_variable_points"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note that for variable SEC profiles, NEMGLO uses a Special Ordered Set Type 2 which directly converts from load (MW) to hydrogen production (kg). As such there is no variable storing the SEC (kWh/kg) for each interval. We can infer this value by the calculation below."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# PEM\n",
"pem_sec_y_variable = [(pem_xpoints[i] * 0.5 * 1000) / pem_ypoints[i] \\\n",
" for i in range(1,len(pem_xpoints))]\n",
"pem_sec_y_defined = [(pem_sec_spec['h2e_load'].to_list()[i] * 0.5 * 1000) \\\n",
" / pem_sec_spec['h2_volume'].to_list()[i] for i in \\\n",
" range(1,len(pem_sec_spec['h2e_load']))]\n",
"pem_sec_fix_y_variable = [(pem_xpoints_f[i] * 0.5 * 1000) / pem_ypoints_f[i] \\\n",
" for i in range(1,len(pem_xpoints_f))]\n",
"\n",
"## AE\n",
"ae_sec_y_variable = [(ae_xpoints[i] * 0.5 * 1000) / ae_ypoints[i] \\\n",
" for i in range(1,len(ae_xpoints))]\n",
"ae_sec_y_defined = [(ae_sec_spec['h2e_load'].to_list()[i] * 0.5 * 1000) \\\n",
" / ae_sec_spec['h2_volume'].to_list()[i] for i in \\\n",
" range(1,len(ae_sec_spec['h2e_load']))]\n",
"ae_sec_fix_y_variable = [(ae_xpoints_f[i] * 0.5 * 1000) / ae_ypoints_f[i] \\\n",
" for i in range(1,len(ae_xpoints_f))]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Plotting the relationship between the amount of hydrogen produced against load (MW) yields..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig = go.Figure()\n",
"PALETTE = ['#b2d4ee','#849db1','#4f6980',\n",
" '#B4E3BC','#89AE8F','#638b66',\n",
" '#ffb04f','#de9945','#af7635',\n",
" '#ff7371','#d6635f','#b65551',\n",
" '#AD134C','#cc688d','#ff82b0'] \n",
"\n",
"# PEM\n",
"fig.add_trace(go.Scatter(x=pem_sec_spec['h2e_load'][1:], y=pem_sec_spec['h2_volume'][1:], mode='markers', name=\"Input SEC points [PEM]\",\n",
" legendgroup='marker', marker_symbol=\"diamond\", marker_size=14, line={'color':PALETTE[2]}))\n",
"fig.add_trace(go.Scatter(x=pem_xpoints, y=pem_ypoints, mode='lines', name=\"Variable SEC mode [PEM]\", line_width=3,\n",
" legendgroup='var', line={'color':PALETTE[2],'dash': 'dash'}))\n",
"fig.add_trace(go.Scatter(x=pem_xpoints_f, y=pem_ypoints_f, mode='lines', name=\"Fixed SEC mode [PEM]\", line_width=3,\n",
" legendgroup='fix', line={'color':PALETTE[2]}))\n",
"\n",
"# AE\n",
"fig.add_trace(go.Scatter(x=ae_sec_spec['h2e_load'][1:], y=ae_sec_spec['h2_volume'][1:], mode='markers', name=\"Input SEC points [AE]\",\n",
" legendgroup='marker', marker_symbol=\"diamond\", marker_size=14, line={'color':PALETTE[5]}))\n",
"fig.add_trace(go.Scatter(x=ae_xpoints, y=ae_ypoints, mode='lines', name=\"Variable SEC mode [AE]\", line_width=3,\n",
" legendgroup='var', line={'color':PALETTE[5],'dash': 'dash'}))\n",
"fig.add_trace(go.Scatter(x=ae_xpoints_f, y=ae_ypoints_f, mode='lines', name=\"Fixed SEC mode [AE]\", line_width=3,\n",
" legendgroup='fix', line={'color':PALETTE[5]}))\n",
"\n",
"# Layout\n",
"fig.update_layout(title=\"NEMGLO Hydrogen Production vs Load Relationship\", titlefont=dict(size=24),\n",
" margin=dict(l=20, r=20, t=50, b=0),\n",
" xaxis=dict(title=\"Electrolyser Load (MW)\", showgrid=True, mirror=True, titlefont=dict(size=24), tickfont=dict(size=24)),\n",
" yaxis=dict(title=\"Hydrogen Produced (kg)\", showgrid=True, mirror=True, titlefont=dict(size=24), tickfont=dict(size=24)),\n",
" legend=dict(xanchor='center',x=0.5, y=-0.18, orientation='h', font=dict(size=20)),\n",
" template=\"simple_white\",\n",
" width=1000,\n",
" height=600,\n",
" font_family=\"Times New Roman\",\n",
" )\n",
"fig.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```{note} The load region between offload and minload is omitted here since the hydrogen should not operate within that region. The MSL constraint shown prior enforces such behaviour.\n",
"```\n",
"\n",
"```{include} example_electrolyser_h2produced.html \n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Plotting the relationship between Specific Energy Consumption (SEC) and load (MW) yields..."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# PEM\n",
"fig = go.Figure()\n",
"fig.add_trace(go.Scatter(x=pem_sec_spec['h2e_load'][1:], y=pem_sec_y_defined, mode='markers', name=\"Input SEC points [PEM]\",\n",
" legendgroup='marker', marker_symbol=\"diamond\", marker_size=14, line={'color':PALETTE[2]}))\n",
"fig.add_trace(go.Scatter(x=pem_xpoints, y=pem_sec_y_variable, mode='lines', name=\"Variable SEC mode [PEM]\", line_width=3,\n",
" legendgroup='var', line={'color':PALETTE[2], 'dash': 'dash'}))\n",
"fig.add_trace(go.Scatter(x=pem_xpoints_f, y=pem_sec_fix_y_variable, mode='lines', name=\"Fixed SEC mode [PEM]\", line_width=3,\n",
" legendgroup='fix', line={'color':PALETTE[2]}))\n",
"\n",
"# AE\n",
"fig.add_trace(go.Scatter(x=ae_sec_spec['h2e_load'][1:], y=ae_sec_y_defined, mode='markers', name=\"Input SEC points [AE]\",\n",
" legendgroup='marker', marker_symbol=\"diamond\", marker_size=14, line={'color':PALETTE[5]}))\n",
"fig.add_trace(go.Scatter(x=ae_xpoints, y=ae_sec_y_variable, mode='lines', name=\"Variable SEC mode [AE]\", line_width=3,\n",
" legendgroup='var', line={'color':PALETTE[5], 'dash': 'dash'}))\n",
"fig.add_trace(go.Scatter(x=ae_xpoints_f, y=ae_sec_fix_y_variable, mode='lines', name=\"Fixed SEC mode [AE]\", line_width=3,\n",
" legendgroup='fix', line={'color':PALETTE[5]}))\n",
"\n",
"# Layout\n",
"fig.update_layout(title=\"NEMGLO Specific Energy Consumption vs Load Relationship\", titlefont=dict(size=24),\n",
" margin=dict(l=20, r=20, t=50, b=0),\n",
" xaxis=dict(title=\"Electrolyser Load (MW)\", showgrid=True, mirror=True, titlefont=dict(size=24), tickfont=dict(size=24)),\n",
" yaxis=dict(title=\"Specific Energy Consumption
(kWh/kg)\", showgrid=True, mirror=True, titlefont=dict(size=24), tickfont=dict(size=24)),\n",
" legend=dict(xanchor='center',x=0.5, y=-0.18, orientation='h', font=dict(size=20)),\n",
" template=\"simple_white\",\n",
" width=1000,\n",
" height=600,\n",
" font_family=\"Times New Roman\",\n",
" )\n",
"\n",
"fig.show()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.9.12 ('nempy')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.12"
},
"vscode": {
"interpreter": {
"hash": "4aab49ac747d4948ee2428bd46f4ac833ef94a37ecb38233c747c75e4d05fe4b"
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}