{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 2. Renewable PPAs\n",
"This example demonstrates a the renewable `Generator` class of NEMGLO and the PPA features within. It shows how to extract historical AEMO data of the NEM, define load characteristics (as per Example 1) and further PPA structures, then running the optimiser to find the operational load behaviour.\n",
"\n",
"This example uses plotly == 5.6.0 to plot results. Install with... pip install plotly==5.6.0"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Install Packages\n",
"For standard use of NEMGLO we can use from nemglo import * to import `nemglo` functionality. This example also uses plotly to generate charts, with the optional setting defining where to render charts."
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"# NEMGLO Packages\n",
"from nemglo import *\n",
"\n",
"# Generic Packages\n",
"import pandas as pd\n",
"import plotly.graph_objects as go\n",
"from plotly.subplots import make_subplots"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"# Display plotly chart in a browser (optional)\n",
"import plotly.io as pio\n",
"pio.renderers.default = \"browser\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Load Historical AEMO data\n",
"Create a nemosis_data object to retrieve historical data for the simulation. nemosis_data class requires a defined interval length and cache folder."
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"inputdata = nemosis_data(intlength=30, local_cache=r'E:\\TEMPCACHE')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we define the simulation period by start and end dispatch intervals, as well as the region for which we are modelling the load in. Additionally we specify here one or two generators we wish to extract dispatch traces for."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"start = \"02/01/2020 00:00\"\n",
"end = \"09/01/2020 00:00\"\n",
"region = 'VIC1'\n",
"duid_1 = 'NUMURSF1'\n",
"duid_2 = 'ARWF1'"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here we can optionally check the AEMO defined information about these generators, namely their registered capacity (MW). Although in the load optimisation we can set any desired sizings for these plants."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Retrieving static table Generators and Scheduled Loads.\n"
]
},
{
"data": {
"text/html": [
"
| \n", " | Station Name | \n", "Region | \n", "Fuel Source - Descriptor | \n", "DUID | \n", "Reg Cap (MW) | \n", "
|---|---|---|---|---|---|
| 11 | \n", "Ararat Wind Farm | \n", "VIC1 | \n", "Wind | \n", "ARWF1 | \n", "241.59 | \n", "
| 364 | \n", "Numurkah Solar Farm | \n", "VIC1 | \n", "Solar | \n", "NUMURSF1 | \n", "112.00 | \n", "
set_ of the `nemosis_data` class."
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"inputdata.set_dates(start, end)\n",
"inputdata.set_region(region)\n",
"inputdata.set_unit(duid_1, duid_2)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Price data can now be loaded as per Example 1. Additionally we now have VRE generator trace data, which is the historical dispatch data (MW) scaled by the noted AEMO registered capacities (MW) to get a percentage capacity factor trace as below."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Compiling data for table DISPATCHPRICE.\n",
"Returning DISPATCHPRICE.\n"
]
},
{
"data": {
"text/html": [
"| \n", " | Time | \n", "Prices | \n", "
|---|---|---|
| 0 | \n", "2020-01-02 00:30:00 | \n", "67.094337 | \n", "
| 1 | \n", "2020-01-02 01:00:00 | \n", "65.526907 | \n", "
| 2 | \n", "2020-01-02 01:30:00 | \n", "50.028502 | \n", "
| 3 | \n", "2020-01-02 02:00:00 | \n", "40.664717 | \n", "
| 4 | \n", "2020-01-02 02:30:00 | \n", "43.609028 | \n", "
| ... | \n", "... | \n", "... | \n", "
| 331 | \n", "2020-01-08 22:00:00 | \n", "44.572158 | \n", "
| 332 | \n", "2020-01-08 22:30:00 | \n", "51.387228 | \n", "
| 333 | \n", "2020-01-08 23:00:00 | \n", "44.822428 | \n", "
| 334 | \n", "2020-01-08 23:30:00 | \n", "44.136238 | \n", "
| 335 | \n", "2020-01-09 00:00:00 | \n", "49.485932 | \n", "
336 rows × 2 columns
\n", "| \n", " | Time | \n", "ARWF1 | \n", "NUMURSF1 | \n", "
|---|---|---|---|
| 0 | \n", "2020-01-02 00:30:00 | \n", "0.424686 | \n", "0.0 | \n", "
| 1 | \n", "2020-01-02 01:00:00 | \n", "0.310995 | \n", "0.0 | \n", "
| 2 | \n", "2020-01-02 01:30:00 | \n", "0.264429 | \n", "0.0 | \n", "
| 3 | \n", "2020-01-02 02:00:00 | \n", "0.234626 | \n", "0.0 | \n", "
| 4 | \n", "2020-01-02 02:30:00 | \n", "0.198132 | \n", "0.0 | \n", "
| ... | \n", "... | \n", "... | \n", "... | \n", "
| 331 | \n", "2020-01-08 22:00:00 | \n", "0.265119 | \n", "0.0 | \n", "
| 332 | \n", "2020-01-08 22:30:00 | \n", "0.299888 | \n", "0.0 | \n", "
| 333 | \n", "2020-01-08 23:00:00 | \n", "0.307753 | \n", "0.0 | \n", "
| 334 | \n", "2020-01-08 23:30:00 | \n", "0.348731 | \n", "0.0 | \n", "
| 335 | \n", "2020-01-09 00:00:00 | \n", "0.329484 | \n", "0.0 | \n", "
336 rows × 3 columns
\n", "load_market_prices stores these required values."
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"P2G = Plan(identifier = \"P2G\")\n",
"P2G.load_market_prices(prices)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Create an Electrolyser object + define its operating characteristics\n",
"A load must be defined and linked to the `Plan` object in order to model it's behaviour. For creating components in `NEMGLO`, they must be defined as belonging to a `Plan` class. Similarly to `Plan`, all components must have a unique identifer which has been called **H2E** here for the Electrolyser. Following this we defined the operating characteristics as per Example 1, and add the electrolyser operation to the model."
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
"h2e = Electrolyser(P2G, identifier='H2E')\n",
"\n",
"h2e.load_h2_parameters_preset(capacity = 100.0,\n",
" maxload = 100.0,\n",
" minload = 10.0,\n",
" offload = 0.0,\n",
" electrolyser_type = 'PEM',\n",
" sec_profile = 'fixed',\n",
" h2_price_kg = 5.0)\n",
"\n",
"h2e.add_electrolyser_operation()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Create an Generator object + define the RE trace data & PPA structure\n",
"The `Generator` component is created just like the Electrolyser, parsing the `Plan` object and using a unique identifier for this Generator."
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"solar = Generator(P2G, identifier=\"SF\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To define the parameters of our new Generator **SF**, we call the `load_vre_parameters` function specifying the 'duid', desired 'capacity', 'trace' using the vre dataframe we extracted from the `nemosis_data` module earlier, 'ppa_strike' price and optionally a 'ppa_floor' price. Note we only parse the 'Time' and duid columns of the vre dataframe, we do not want to use the other generator data here for the wind farm!"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"solar.load_vre_parameters(duid = 'NUMURSF1',\n",
" capacity = 100.0,\n",
" trace = vre[['Time', 'NUMURSF1']],\n",
" ppa_strike = 30.0,\n",
" ppa_floor=None)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Finally, we can implement this component in the model by using `add_ppa_contract`"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"solar.add_ppa_contract()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Running the optimisation\n",
"As per Example 1..."
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"OPTIMISATION COMPLETE, Obj Value: -610452.4214854483\n"
]
},
{
"data": {
"text/plain": [
"get_costs are standardized across the pacakge based on the identifiers given for each components and default variable names which can be found in the [Naming Convention](../../naming.md)\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"| \n", " | time | \n", "interval | \n", "total_cost | \n", "H2E-h2_produced | \n", "H2E-mw_load | \n", "SF-ppa_cfd | \n", "
|---|---|---|---|---|---|---|
| 0 | \n", "2020-01-02 00:30:00 | \n", "0 | \n", "-433.161955 | \n", "-3787.878788 | \n", "3354.716833 | \n", "0.0 | \n", "
| 1 | \n", "2020-01-02 01:00:00 | \n", "1 | \n", "-511.533455 | \n", "-3787.878788 | \n", "3276.345333 | \n", "0.0 | \n", "
| 2 | \n", "2020-01-02 01:30:00 | \n", "2 | \n", "-1286.453705 | \n", "-3787.878788 | \n", "2501.425083 | \n", "0.0 | \n", "
| 3 | \n", "2020-01-02 02:00:00 | \n", "3 | \n", "-1754.642955 | \n", "-3787.878788 | \n", "2033.235833 | \n", "0.0 | \n", "
| 4 | \n", "2020-01-02 02:30:00 | \n", "4 | \n", "-1607.427371 | \n", "-3787.878788 | \n", "2180.451417 | \n", "0.0 | \n", "
| ... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "... | \n", "
| 331 | \n", "2020-01-08 22:00:00 | \n", "331 | \n", "-1559.270871 | \n", "-3787.878788 | \n", "2228.607917 | \n", "0.0 | \n", "
| 332 | \n", "2020-01-08 22:30:00 | \n", "332 | \n", "-1218.517371 | \n", "-3787.878788 | \n", "2569.361417 | \n", "0.0 | \n", "
| 333 | \n", "2020-01-08 23:00:00 | \n", "333 | \n", "-1546.757371 | \n", "-3787.878788 | \n", "2241.121417 | \n", "0.0 | \n", "
| 334 | \n", "2020-01-08 23:30:00 | \n", "334 | \n", "-1581.066871 | \n", "-3787.878788 | \n", "2206.811917 | \n", "0.0 | \n", "
| 335 | \n", "2020-01-09 00:00:00 | \n", "335 | \n", "-1313.582205 | \n", "-3787.878788 | \n", "2474.296583 | \n", "0.0 | \n", "
336 rows × 6 columns
\n", "