{ "cells": [ { "cell_type": "markdown", "id": "04ac5031", "metadata": { "Collapsed": "false" }, "source": [ "(multiple-price-bid-matrix)=\n", "# Bidding under uncertainty\n", "\n", "The model setup is available in the following formats:\n", "\n", "- pyshop\n", " - [](multi_price_bid_matrix_model.py)" ] }, { "cell_type": "markdown", "id": "5d079f8f", "metadata": { "Collapsed": "false" }, "source": [ "In this example we show how to import a spreadsheet containing multiple prices (up to 52 time series) into a dataframe. This price input will be used to create the same amount of scenarios which we in turn feed to SHOP. The output of this multiple price input run from SHOP will result into a joint bid matrix which consider all the stochastic price inputs to SHOP with all its price data intact, ready to make use of in a marked bidding situation with uncertainty regarding the price forecasts." ] }, { "cell_type": "markdown", "id": "91da31ec", "metadata": { "Collapsed": "false" }, "source": [ "## Imports and settings" ] }, { "cell_type": "markdown", "id": "8d44656a", "metadata": { "Collapsed": "false" }, "source": [ "The first thing we do is to import the needed packages. You can import whichever packages you like, however we use the following ones for this example:\n", "\n", "* Pandas for structuring our data into dataframes\n", "* Pyshop in order to create a SHOP session\n", "* Plotly as backend for dynamic graph plotting" ] }, { "cell_type": "code", "execution_count": 1, "id": "82051bc5", "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "import pandas as pd\n", "from pyshop import ShopSession\n", "import plotly.express as px\n", "pd.options.plotting.backend = \"plotly\"" ] }, { "cell_type": "markdown", "id": "95619ca1", "metadata": { "Collapsed": "false" }, "source": [ "Additionally, we import basic SHOP functions and data from a predefined demo dataset, see this section of the documentation:" ] }, { "cell_type": "code", "execution_count": 2, "id": "21b4daa8", "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "from multi_price_bid_matrix_model import build_model, run_model" ] }, { "cell_type": "markdown", "id": "2bb819b8", "metadata": { "Collapsed": "false" }, "source": [ "## Instancing SHOP and building the model" ] }, { "cell_type": "markdown", "id": "595d7f77", "metadata": { "Collapsed": "false" }, "source": [ "In order to have SHOP receive our inputs, run the model we create and give us results, we need to instance a running SHOP session. You may create multiple SHOP sessions simultaneously if needed." ] }, { "cell_type": "code", "execution_count": 3, "id": "c34733bf", "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "#Create a standard ShopSession\n", "shop = ShopSession()" ] }, { "cell_type": "markdown", "id": "a4cd18d2", "metadata": { "Collapsed": "false" }, "source": [ "We then build our model using the existing function imported from bp.py" ] }, { "cell_type": "code", "execution_count": 4, "id": "507da28d", "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "#Build a simple model with one plant with two equal generators and a second plant with one large and three small generators \n", "#by calling function \"build_model\" in bp.py\n", "build_model(shop)" ] }, { "cell_type": "markdown", "id": "b0b7ae71", "metadata": { "Collapsed": "false" }, "source": [ "The imported model can now be visualized:" ] }, { "cell_type": "code", "execution_count": 5, "id": "128e548d", "metadata": { "Collapsed": "false" }, "outputs": [ { "data": { "image/svg+xml": [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "SHOP Model connection tree\n", "SINTEF Energy\n", "\n", "cluster_canvas\n", "\n", "\n", "cluster_wrap_reservoir_Reservoir1\n", "\n", "\n", "cluster_wrap_reservoir_Reservoir2\n", "\n", "\n", "cluster_wrap_plant_Plant1\n", "\n", "\n", "cluster_wrap_plant_Plant2\n", "\n", "\n", "\n", "reservoir_Reservoir1\n", "\n", "\n", " \n", "Reservoir1\n", " \n", "Reservoir\n", "\n", "\n", "\n", "plant_Plant1\n", "\n", "\n", "\n", "\n", "Plant1\n", " \n", "Plant\n", "\n", "\n", "\n", "reservoir_Reservoir1->plant_Plant1\n", "\n", "\n", "\n", "\n", "\n", "reservoir_Reservoir2\n", "\n", "\n", " \n", "Reservoir2\n", " \n", "Reservoir\n", "\n", "\n", "\n", "plant_Plant1->reservoir_Reservoir2\n", "\n", "\n", "\n", "\n", "\n", "plant_Plant2\n", "\n", "\n", "\n", "\n", "Plant2\n", " \n", "Plant\n", "\n", "\n", "\n", "reservoir_Reservoir2->plant_Plant2\n", "\n", "\n", "\n", "\n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "#Display topology to the screen\n", "display(shop.model.build_connection_tree())" ] }, { "cell_type": "markdown", "id": "0ccee33f", "metadata": { "Collapsed": "false" }, "source": [ "## Data preperation" ] }, { "cell_type": "markdown", "id": "2b124e4d", "metadata": { "Collapsed": "false" }, "source": [ "Since we we need to work a bit with time horizons given that we will consider part of a period with stochastic information, we retrive the start time from the imported data" ] }, { "cell_type": "code", "execution_count": 6, "id": "ef455236", "metadata": { "Collapsed": "false" }, "outputs": [ { "data": { "text/plain": [ "{'starttime': Timestamp('2018-01-23 00:00:00'),\n", " 'endtime': Timestamp('2018-01-26 00:00:00'),\n", " 'timeunit': 'hour',\n", " 'timeresolution': 2018-01-23 00:00:00 1.0\n", " 2018-01-23 01:00:00 1.0\n", " 2018-01-23 02:00:00 1.0\n", " 2018-01-23 03:00:00 1.0\n", " 2018-01-23 04:00:00 1.0\n", " ... \n", " 2018-01-25 20:00:00 1.0\n", " 2018-01-25 21:00:00 1.0\n", " 2018-01-25 22:00:00 1.0\n", " 2018-01-25 23:00:00 1.0\n", " 2018-01-26 00:00:00 1.0\n", " Name: data, Length: 73, dtype: float64}" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Retrieve the start time for adding stochastic time series later\n", "timeres=shop.get_time_resolution()\n", "shop.get_time_resolution()" ] }, { "cell_type": "markdown", "id": "ff4e9869", "metadata": { "Collapsed": "false" }, "source": [ "### Importing prices from spreadsheet" ] }, { "cell_type": "markdown", "id": "79650b73", "metadata": { "Collapsed": "false" }, "source": [ "After the model data has been imported and read, we move on to the prices we want to consider. \n", "We first define the number of prices we want to import" ] }, { "cell_type": "code", "execution_count": 7, "id": "fe37e93d", "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# Import number of prices\n", "n_prices = 10" ] }, { "cell_type": "markdown", "id": "761b8206", "metadata": { "Collapsed": "false" }, "source": [ "which will be equal to the scenarios we will create later on. \n", "We then define for how long of a period we want to consider the stochastic price input. This can not be a larger period than what you have data for in your price forecasts." ] }, { "cell_type": "code", "execution_count": 8, "id": "09ef31f8", "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# Set the hours to import stochastic price\n", "stochastic_prices_start = timeres['starttime']\n", "# In this example, we choose 24 hours\n", "stochastic_prices_end = stochastic_prices_start + pd.Timedelta(hours=23)" ] }, { "cell_type": "markdown", "id": "8aa80440", "metadata": { "Collapsed": "false" }, "source": [ "Next we import the stochastic prices accordingly into a dataframe" ] }, { "cell_type": "code", "execution_count": 9, "id": "686d4f37", "metadata": { "Collapsed": "false" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/tmp/ipykernel_2555/2553596855.py:2: Pandas4Warning: The xlrd engine is deprecated and will be removed in a future version. Use the calamine engine instead, by setting engine='calamine'.\n", " stochastic_prices_from_file = pd.read_excel ('52_hourly_prices_random.xls', header=None,usecols=list(range(0,n_prices)))\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
0123456789
038.68892530.84143739.30318343.12222131.39970838.21173842.47948529.83885729.93227946.548664
138.91648428.47852433.84338536.85633340.44714034.47371133.75162933.69934636.98775738.796374
237.13397329.92342531.28058540.11727133.28553834.72807434.09604732.08503336.42659045.003989
328.56714525.73859033.42898439.93746239.57022431.55864838.29084237.52719228.99041040.885543
432.90962930.64244834.66852147.70865636.96327333.74917438.70346831.80226729.55370346.706921
534.43855634.62347033.96960641.13648235.48029141.83473233.78857628.95316439.69066846.564680
635.47086230.47929435.03191041.90296137.54483633.30079441.63094742.22412740.85321838.904588
743.88436351.59123243.36287248.16582343.54943448.46864753.58639643.27692246.69187551.359444
846.48571249.47362744.29502254.17468247.94751053.74083050.93594954.03181853.41606958.500872
951.83157148.73569756.20106050.87969954.29157454.69347458.22480053.48550864.63548454.939225
1042.77078741.49397037.82846242.26399449.96689654.10620844.12832049.39106660.57510445.339350
1137.87060037.42526244.11692641.93284042.41688846.17547545.12199549.68323140.77799045.914877
1238.72012642.77109941.50705142.14680438.80397248.31662040.81178047.99123653.71575441.338486
1336.11937043.06022638.30018035.63193039.99991146.29631245.36328432.37836542.46518134.729646
1438.19404946.10862637.37508736.57564544.01463748.48044750.67242540.07908141.31169249.711962
1538.26022751.17502739.21637538.05367646.20276647.60882844.79515039.29150351.56377837.664475
1638.05804847.07770340.79919645.25095843.69050844.29423050.90564946.36238146.65976144.224564
1756.71036747.71087555.55446449.06669554.54647952.04463756.70139453.91189560.11102253.380406
1859.00434456.79788660.17298158.23750557.51310055.19502254.10929073.96141556.12152855.876500
1955.77624558.24584758.27061754.60177357.31774556.95479253.89801570.85271559.41414553.165951
2043.24421050.68762749.48684149.71405937.70655846.24326647.60151958.95601043.54022949.330592
2148.31710341.46123145.00785743.13839040.50753644.10861041.70081651.10217740.24570739.758460
2238.23718747.59398242.57414136.78084949.54837138.09788933.73190951.82626946.92059542.529905
2342.37140435.49695346.26549938.24747843.22821436.81609242.98743346.24975639.40723141.273543
\n", "
" ], "text/plain": [ " 0 1 2 3 4 5 \\\n", "0 38.688925 30.841437 39.303183 43.122221 31.399708 38.211738 \n", "1 38.916484 28.478524 33.843385 36.856333 40.447140 34.473711 \n", "2 37.133973 29.923425 31.280585 40.117271 33.285538 34.728074 \n", "3 28.567145 25.738590 33.428984 39.937462 39.570224 31.558648 \n", "4 32.909629 30.642448 34.668521 47.708656 36.963273 33.749174 \n", "5 34.438556 34.623470 33.969606 41.136482 35.480291 41.834732 \n", "6 35.470862 30.479294 35.031910 41.902961 37.544836 33.300794 \n", "7 43.884363 51.591232 43.362872 48.165823 43.549434 48.468647 \n", "8 46.485712 49.473627 44.295022 54.174682 47.947510 53.740830 \n", "9 51.831571 48.735697 56.201060 50.879699 54.291574 54.693474 \n", "10 42.770787 41.493970 37.828462 42.263994 49.966896 54.106208 \n", "11 37.870600 37.425262 44.116926 41.932840 42.416888 46.175475 \n", "12 38.720126 42.771099 41.507051 42.146804 38.803972 48.316620 \n", "13 36.119370 43.060226 38.300180 35.631930 39.999911 46.296312 \n", "14 38.194049 46.108626 37.375087 36.575645 44.014637 48.480447 \n", "15 38.260227 51.175027 39.216375 38.053676 46.202766 47.608828 \n", "16 38.058048 47.077703 40.799196 45.250958 43.690508 44.294230 \n", "17 56.710367 47.710875 55.554464 49.066695 54.546479 52.044637 \n", "18 59.004344 56.797886 60.172981 58.237505 57.513100 55.195022 \n", "19 55.776245 58.245847 58.270617 54.601773 57.317745 56.954792 \n", "20 43.244210 50.687627 49.486841 49.714059 37.706558 46.243266 \n", "21 48.317103 41.461231 45.007857 43.138390 40.507536 44.108610 \n", "22 38.237187 47.593982 42.574141 36.780849 49.548371 38.097889 \n", "23 42.371404 35.496953 46.265499 38.247478 43.228214 36.816092 \n", "\n", " 6 7 8 9 \n", "0 42.479485 29.838857 29.932279 46.548664 \n", "1 33.751629 33.699346 36.987757 38.796374 \n", "2 34.096047 32.085033 36.426590 45.003989 \n", "3 38.290842 37.527192 28.990410 40.885543 \n", "4 38.703468 31.802267 29.553703 46.706921 \n", "5 33.788576 28.953164 39.690668 46.564680 \n", "6 41.630947 42.224127 40.853218 38.904588 \n", "7 53.586396 43.276922 46.691875 51.359444 \n", "8 50.935949 54.031818 53.416069 58.500872 \n", "9 58.224800 53.485508 64.635484 54.939225 \n", "10 44.128320 49.391066 60.575104 45.339350 \n", "11 45.121995 49.683231 40.777990 45.914877 \n", "12 40.811780 47.991236 53.715754 41.338486 \n", "13 45.363284 32.378365 42.465181 34.729646 \n", "14 50.672425 40.079081 41.311692 49.711962 \n", "15 44.795150 39.291503 51.563778 37.664475 \n", "16 50.905649 46.362381 46.659761 44.224564 \n", "17 56.701394 53.911895 60.111022 53.380406 \n", "18 54.109290 73.961415 56.121528 55.876500 \n", "19 53.898015 70.852715 59.414145 53.165951 \n", "20 47.601519 58.956010 43.540229 49.330592 \n", "21 41.700816 51.102177 40.245707 39.758460 \n", "22 33.731909 51.826269 46.920595 42.529905 \n", "23 42.987433 46.249756 39.407231 41.273543 " ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Use pandas read_excel function to import n_prices to a dataframe, starting from the first column to the left in the spreadsheet\n", "stochastic_prices_from_file = pd.read_excel ('52_hourly_prices_random.xls', header=None,usecols=list(range(0,n_prices)))\n", "\n", "# Optionally, select which scenarios you want relative to the columns in your spreadsheet. They must then be equal to n_prices;\n", "#stoch_price_from_file = pd.read_excel (r'20200123_scens_edit.xlsx', usecols=[1,2,5,7,9])\n", "\n", "stochastic_prices_from_file" ] }, { "cell_type": "markdown", "id": "4b5c29ce", "metadata": { "Collapsed": "false" }, "source": [ "In order to later combine the stochastic prices with a deterministic price into the future, we make sure the dataframe has the correct timestamp indexing" ] }, { "cell_type": "code", "execution_count": 10, "id": "1cac3341", "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# Add correct indexing to the dataframe\n", "stochastic_prices_from_file.index=pd.date_range(stochastic_prices_start,stochastic_prices_end,freq='h')" ] }, { "cell_type": "markdown", "id": "f7794e8f", "metadata": { "Collapsed": "false" }, "source": [ "Then we can plot and review the newly imported prices" ] }, { "cell_type": "code", "execution_count": 11, "id": "beb8fc4d", "metadata": { "Collapsed": "false" }, "outputs": [ { "data": { "text/html": [ "
\n", "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Plot all stochastic prices imported from Excel\n", "stochastic_prices_from_file.plot(title=\"Stochastic prices imported from spreadsheet\", labels=dict(index=\"Time\", value=\"EUR/MWh\", variable=\"\"))" ] }, { "cell_type": "markdown", "id": "fd2d7025", "metadata": { "Collapsed": "false" }, "source": [ "### Creating scenarios" ] }, { "cell_type": "markdown", "id": "b89cd75c", "metadata": { "Collapsed": "false" }, "source": [ "Now it is time to create scenarios that we can populate with the imported prices. We make sure to create just as many scenarios as we have prices imported." ] }, { "cell_type": "code", "execution_count": 12, "id": "9a6e36eb", "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# Generate as many scenarios as prices\n", "n_scenarios = n_prices\n", "for i in range (1, n_scenarios+1):\n", " scenario_name='S'+str(i)\n", " # The first scenario always exists in SHOP and should not be added again\n", " if i>1:\n", " scenario = shop.model.scenario.add_object(scenario_name)\n", " else:\n", " scenario=shop.model.scenario[scenario_name]\n", " scenario.scenario_id.set(i)\n", " \n", " # Set each scenario equally probable\n", " scenario.probability.set(1.0/n_scenarios)\n", " \n", " # Branch immediately, i.e. at 'starttime'\n", " scenario.common_scenario.set(pd.Series([i], index=[timeres['starttime']]))\n", " \n", " # Optionally set branching to start after given number of hours (all scenarios are set equal to scenario 2 before this) \n", " #scen.common_scenario.set(pd.Series([2, i], index=[timeres['starttime'], branching_start_time]))" ] }, { "cell_type": "markdown", "id": "4f42d896", "metadata": { "Collapsed": "false" }, "source": [ "### Creating the new price array from stochastic and deterministic prices" ] }, { "cell_type": "markdown", "id": "4dbc7bbe", "metadata": { "Collapsed": "false" }, "source": [ "Since we have chosen to only consider the first 24 hours as stochastic when it comes to the price, but have longer total time horizon, we need to combine the stochastic and deterministic prices into a joint price dataframe. We have already defined the start and end time for the stochastic price, but need to make sure that we also define when the deterministic price should be valid and thus overlap each other." ] }, { "cell_type": "code", "execution_count": 13, "id": "8bd1d6e8", "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# Use first (and only) market as index for setting stochastic data and getting results\n", "name_list = shop.model.market.get_object_names()\n", "\n", "# The deterministic model only has one market, so we give stochastic price to that (da is just a name we use to indicate that we regard it to be a day ahead energy market)\n", "day_ahead_market = shop.model.market[name_list[0]]\n", "\n", "# Get the deterministic price from ASCII import for correct indexing \n", "deterministic_price = day_ahead_market.sale_price.get()\n", "\n", "# Create a new array for deterministic price with n_prices columns in order to combine it with the stochastic price\n", "deterministic_price_multi_dimension_array = pd.DataFrame(index=deterministic_price.index, columns=list(range(0,n_prices)))\n", "\n", "# Define when to use deterministic price\n", "deterministic_price_start = timeres['starttime']+pd.Timedelta(hours=24)\n", "deterministic_price_end = timeres['endtime']-pd.Timedelta(hours=1)" ] }, { "cell_type": "markdown", "id": "51ca8913", "metadata": { "Collapsed": "false" }, "source": [ "We print out the start and end times to make sure they are correct." ] }, { "cell_type": "code", "execution_count": 14, "id": "a34ac041", "metadata": { "Collapsed": "false" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Stochastic price(s) start time 2018-01-23 00:00:00\n", "Stochastic price(s) end time 2018-01-23 23:00:00\n", "Deterministic prices start time 2018-01-24 00:00:00\n", "Deterministic prices end time 2018-01-25 23:00:00\n" ] } ], "source": [ "# Print out start and end set point for prices\n", "print(\"Stochastic price(s) start time\",stochastic_prices_start)\n", "print(\"Stochastic price(s) end time\",stochastic_prices_end)\n", "print(\"Deterministic prices start time\",deterministic_price_start)\n", "print(\"Deterministic prices end time\",deterministic_price_end)" ] }, { "cell_type": "markdown", "id": "64f3c65a", "metadata": { "Collapsed": "false" }, "source": [ "Lastly, we need to combine the stochastic and deterministic price inputs. We do this by creating a new dataframe with the right dimensions and indexes, and then populate it according to the start and end times defined above." ] }, { "cell_type": "code", "execution_count": 15, "id": "58c36e0f", "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# Creating a new price dataframe which will combine both stochastic and deterministic price\n", "combined_price = pd.DataFrame(index=deterministic_price.index, columns=list(range(0,n_prices)))\n", "\n", "# Loop over the new price array and assign stochastic and deterministic prices\n", "\n", "for j in range(0,n_prices):\n", " deterministic_price_multi_dimension_array[j]=deterministic_price\n", "for i in pd.date_range(stochastic_prices_start,stochastic_prices_end,freq='h'):\n", " combined_price.loc[i] = stochastic_prices_from_file.loc[i]\n", "for i in pd.date_range(deterministic_price_start,(deterministic_price_end),freq='h'):\n", " combined_price.loc[i] = deterministic_price_multi_dimension_array.loc[i]" ] }, { "cell_type": "markdown", "id": "dee6618c", "metadata": { "Collapsed": "false" }, "source": [ "Then we can plot out the resulting combined price." ] }, { "cell_type": "code", "execution_count": 16, "id": "32df7d06", "metadata": { "Collapsed": "false" }, "outputs": [ { "data": { "text/html": [ "
\n", "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Plot the combined price\n", "combined_price.plot(title=\"Combined price input\", labels=dict(index=\"Time\", value=\"EUR/MWh\", variable=\"\"))" ] }, { "cell_type": "markdown", "id": "275f1af3", "metadata": { "Collapsed": "false" }, "source": [ "This price is then set as the day ahead market price to consider for all scenarios. We also define a slightly higher buy-back price" ] }, { "cell_type": "code", "execution_count": 17, "id": "109a87f6", "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "day_ahead_market.sale_price.set(combined_price)\n", "\n", "# Make sure that the buy price is slightly higher, so no arbitrage occurs\n", "\n", "day_ahead_market.buy_price.set(day_ahead_market.sale_price.get()+0.1)" ] }, { "cell_type": "markdown", "id": "9d61409b", "metadata": { "Collapsed": "false" }, "source": [ "### Create bid groups and configure limits" ] }, { "cell_type": "markdown", "id": "a950696d", "metadata": { "Collapsed": "false" }, "source": [ "Now we need to create a bid group, which is the connection between a set of hydropower plants and their bid matrix." ] }, { "cell_type": "code", "execution_count": 18, "id": "2bca4075", "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# Create a bid group\n", "bg=shop.model.bid_group.add_object('bg')\n", "\n", "# Add all plants in the system to the bid group\n", "for plant in shop.model.plant:\n", " bg.connect_to(plant)\n", "\n", "# Defining which periods the bid curve should consider\n", "day_ahead_market.bid_flag.set(pd.Series([1,0],index=[stochastic_prices_start,stochastic_prices_end]))" ] }, { "cell_type": "markdown", "id": "351886b0", "metadata": { "Collapsed": "false" }, "source": [ "## Running multi price scenarios in SHOP" ] }, { "cell_type": "markdown", "id": "2f006c7d", "metadata": { "Collapsed": "false" }, "source": [ "It is time to optimize. We call the predefined function run_model, which calls for five full and three incremental iterations. Since we have defined scenarios in the SHOP instance earlier, all scenarios will be computed and optimized with a single call." ] }, { "cell_type": "code", "execution_count": 19, "id": "d1d8b5b7", "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "#Optimize model by calling \"run_model\" bp.py\n", "run_model(shop)" ] }, { "cell_type": "markdown", "id": "ab570cf8", "metadata": { "Collapsed": "false" }, "source": [ "## Creating and plotting the bid matrix" ] }, { "cell_type": "markdown", "id": "5ab5452c", "metadata": { "Collapsed": "false" }, "source": [ "Once SHOP has finished optimizing all scenarios, we can retrive and start processing the bid matrix the way we want it." ] }, { "cell_type": "code", "execution_count": 20, "id": "527aec32", "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# Get bid matrix as an array\n", "bid_result=bg.bid_curves.get()\n", "\n", "# Convert bid matrix to a dataframe structure of choice\n", "bid_matrix=pd.DataFrame(index=bid_result[0].index)\n", "for t in range (0, 23):\n", " bid_matrix[t]=bid_result[t].values\n", "\n", "# Transpose the bid matrix if necessary depending on the viewing needs \n", "bid_matrix_transposed=bid_matrix.transpose()" ] }, { "cell_type": "markdown", "id": "d87280a7", "metadata": { "Collapsed": "false" }, "source": [ "When we are satisfied with a data structure that fulfill our needs, we can either print it or plot it to review it" ] }, { "cell_type": "code", "execution_count": 21, "id": "77c07d99", "metadata": { "Collapsed": "false" }, "outputs": [ { "data": { "text/html": [ "
\n", "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Plotting bid matrices with different properties\n", "fig = px.imshow(bid_matrix.transpose(), color_continuous_scale='spectral')\n", "fig.update_layout(title=\"Bid matrix heatmapped (and inverted compared to normal bid matrix)\", xaxis_title =\"Market price\", yaxis_title=\"Hours\")\n", "fig.update_yaxes(autorange=True) \n", "fig.show()" ] }, { "cell_type": "code", "execution_count": 22, "id": "32e0db68", "metadata": { "Collapsed": "false" }, "outputs": [ { "data": { "text/html": [ "
\n", "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Plotting bid matrices with different properties\n", "bid_matrix_transposed.plot.bar(title=\"Bid matrix per hour\", labels=dict(index=\"Hour [h]\", value=\"Market price\", variable=\"\")).update_layout(barmode='group')" ] }, { "cell_type": "markdown", "id": "846590e8", "metadata": { "Collapsed": "false" }, "source": [ "## Plotting other results" ] }, { "cell_type": "markdown", "id": "335cc32e", "metadata": { "Collapsed": "false" }, "source": [ "If we want to see how each scenario suggest to produce, we can of course have a look at that as well" ] }, { "cell_type": "code", "execution_count": 23, "id": "67824a76", "metadata": { "Collapsed": "false" }, "outputs": [], "source": [ "# Get the optimal production value(s)\n", "optimal_production=day_ahead_market.sale.get()" ] }, { "cell_type": "code", "execution_count": 24, "id": "a3fe885f", "metadata": { "Collapsed": "false" }, "outputs": [ { "data": { "text/html": [ "
\n", "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "\n", "# Plot optimal production in different plot types\n", "optimal_production.plot.bar(title=\"Bid volumes per stochastic price #\", labels=dict(index=\"Time\", value=\"Sale/Production [MW]\", variable=\"\"))" ] }, { "cell_type": "code", "execution_count": 25, "id": "797f8900", "metadata": { "Collapsed": "false" }, "outputs": [ { "data": { "text/html": [ "
\n", "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Plot optimal production in different plot types\n", "optimal_production.plot.bar(title=\"Bid volumes per stochastic price #, stacked\", labels=dict(index=\"Time\", value=\"Sale/Production [MW]\", variable=\"\")) # bargap=.1, " ] }, { "cell_type": "markdown", "id": "19104c1c", "metadata": { "Collapsed": "false" }, "source": [ "# Files" ] } ], "metadata": { "jupytext": { "text_representation": { "extension": ".md", "format_name": "myst", "format_version": 0.13, "jupytext_version": "1.13.8" } }, "kernelspec": { "display_name": "Python 3", "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.13.17" }, "source_map": [ 14, 24, 28, 32, 40, 49, 53, 59, 63, 67, 74, 78, 86, 90, 97, 101, 105, 113, 117, 122, 129, 134, 143, 147, 159, 163, 170, 174, 181, 185, 189, 213, 217, 221, 241, 245, 255, 259, 275, 279, 286, 290, 300, 304, 308, 322, 326, 330, 337, 341, 345, 360, 364, 374, 381, 385, 389, 396, 404, 411 ] }, "nbformat": 4, "nbformat_minor": 5 }