{ "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 }