{
"cells": [
{
"cell_type": "markdown",
"id": "9a5f105d",
"metadata": {},
"source": [
"# Basic example\n",
"\n",
"This example contains the most commonly used objects, attributes, functions and commands for a basic SHOP model, in addition to examples of how to review and plot input data and results.\n",
"\n",
"It is not dependent on any other data sources such as ASCII files or spreadsheets, as all input data remain intact in the notebook (or Python code) itself, and is fully dependent on the API and pyshop for interacting with SHOP.\n",
"\n",
"The example watercourse consist of a simple SHOP model with two reservoirs and two plants which is optimized over the span of three days.\n",
"\n",
"We will show how to import all necessary packages, settings and data, create a SHOP instance and add all needed objects and populate them with data, before we run SHOP with a selection of commands and review the results in the end.\n",
"\n",
"While the model in this example is quite simple, it can serve as a good reference when designing your own models. It illustrates an intuitive way of designing models in pyshop by incrementally initializing new objects like reservoirs, plants, generators and markets, setting their key attributes before connecting them together into the a desired topology. The model is then optimized, before the results are retrieved and presented through plots.\n",
"\n",
"## Imports and settings\n",
"\n",
"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 backend for dynamic graph plotting"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "4a5ae9a9",
"metadata": {
"Collapsed": "false"
},
"outputs": [],
"source": [
"import pandas as pd\n",
"from pyshop import ShopSession\n",
"import plotly.graph_objs as go\n",
"import plotly.express as px\n",
"from plotly.subplots import make_subplots\n",
"pd.options.plotting.backend = \"plotly\""
]
},
{
"cell_type": "markdown",
"id": "a93474c1",
"metadata": {},
"source": [
"## Instancing SHOP\n",
"\n",
"In order to have SHOP receive our inputs, run the model we create and give us results, we need to create an active, running SHOP session.\n",
"\n",
"You may create multiple SHOP sessions simultaneously if needed."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "f32efc86",
"metadata": {
"Collapsed": "false"
},
"outputs": [],
"source": [
"# Creating a new SHOP session to the instance 'shop'\n",
"shop = ShopSession()"
]
},
{
"cell_type": "markdown",
"id": "7197c890",
"metadata": {},
"source": [
"We can also check the current versions of SHOP and its solvers."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "ff6208c9",
"metadata": {
"Collapsed": "false"
},
"outputs": [
{
"data": {
"text/plain": [
"'16.7.2 Cplex 20.1.0 Gurobi 7.5 OSI/CBC 2.9 2025-04-11'"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Writing out the current version of SHOP and its solvers\n",
"shop.shop_api.GetVersionString()"
]
},
{
"cell_type": "markdown",
"id": "253535ae",
"metadata": {},
"source": [
"## Setting time resolutions for the model\n",
"\n",
"We set the time resolution for the model ourselves, as we generate all input as we go. The start and end time are important, in addition to the resolution of the time steps."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "b8baa4fd",
"metadata": {
"Collapsed": "false"
},
"outputs": [],
"source": [
"# Setting the start time of the model\n",
"starttime = pd.Timestamp('2018-01-23 00:00:00')\n",
"\n",
"# Setting the end time of the model\n",
"endtime = pd.Timestamp('2018-01-26')\n",
"\n",
"# Setting the start and end time to the shop instance, and defining the time unit\n",
"shop.set_time_resolution(starttime=starttime, endtime=endtime, timeunit=\"hour\", timeresolution=pd.Series(index=[starttime],data=[1]))"
]
},
{
"cell_type": "markdown",
"id": "6d641581",
"metadata": {},
"source": [
"## Adding topology parameters\n",
"\n",
"In this example we define the entire topology and generate all its parameters via Python and the API. Below are simple examples on how to define the most common topology objects needed in order to run SHOP.\n",
"\n",
"### Reservoirs\n",
"\n",
"First, we start of by creating our reservoir objects. It is possible to just execute the function, or we can instance them. The latter is preferred, as the syntax for adding attributes to objects become shorter, in addition to it being handy."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "75cc8774",
"metadata": {
"Collapsed": "false"
},
"outputs": [],
"source": [
"# Add reservoir objects to the model and instancing them\n",
"rsv1 = shop.model.reservoir.add_object('Reservoir1')\n",
"rsv2 = shop.model.reservoir.add_object('Reservoir2')\n",
"# Alternatively, only the right hand side can be executed, however it is often nice to refer to instances later on"
]
},
{
"cell_type": "markdown",
"id": "2018a499",
"metadata": {},
"source": [
"We can then verify that all reservoir objects are correctly added to the model instance:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "50b625b1",
"metadata": {
"Collapsed": "false"
},
"outputs": [
{
"data": {
"text/plain": [
"['Reservoir1', 'Reservoir2']"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"shop.model.reservoir.get_object_names()"
]
},
{
"cell_type": "markdown",
"id": "cc4450d7",
"metadata": {},
"source": [
"Next, it is time to set the different parameters to the newly created objects, either via the instance, or directly through the function. Below are examples of both."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "20388ab3",
"metadata": {
"Collapsed": "false"
},
"outputs": [],
"source": [
"# Setting the maximum volume in Mm3 via the instance\n",
"rsv1.max_vol.set(39)\n",
"rsv2.max_vol.set(97.5)\n",
"\n",
"# Setting the LRL (Lowest Regulated Level) of the reservoir in masl via the instance\n",
"rsv1.lrl.set(860)\n",
"rsv2.lrl.set(650)\n",
"\n",
"# Setting the HRL (Highest Regulated Level) of the reservoir in masl by command\n",
"shop.model.reservoir.Reservoir1.hrl.set(905)\n",
"shop.model.reservoir.Reservoir2.hrl.set(679)"
]
},
{
"cell_type": "markdown",
"id": "990f49e2",
"metadata": {},
"source": [
"We can then verify that these values have been stored correctly onto the object. We can either make a call to the instance, or execute the function directly."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "e3eb4b6f",
"metadata": {
"Collapsed": "false"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"HRL of Reservoir 1: 905.0\n",
"HRL of Reservoir 2: 679.0\n"
]
}
],
"source": [
"# Using the instanced rsv1 to retrieve the HRL of the reservoir objects\n",
"print(\"HRL of Reservoir 1: \",rsv1.hrl.get())\n",
"print(\"HRL of Reservoir 2: \",rsv2.hrl.get())"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "00d22c74",
"metadata": {
"Collapsed": "false"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"LRL of Reservoir 1: 860.0\n",
"LRL of Reservoir 2: 650.0\n"
]
}
],
"source": [
"# Using the function directly to retrieve the LRL of Reservoir1\n",
"print(\"LRL of Reservoir 1: \",shop.model.reservoir.Reservoir1.lrl.get())\n",
"print(\"LRL of Reservoir 2: \",shop.model.reservoir.Reservoir2.lrl.get())"
]
},
{
"cell_type": "markdown",
"id": "4a3db058",
"metadata": {},
"source": [
"We continue the process of setting all relevant attributes to the {ref}`reservoir` objects.\n",
"!!!Ref. til I/O."
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "bdf96e3d",
"metadata": {
"Collapsed": "false"
},
"outputs": [],
"source": [
"# Setting the stage storage curve, the relation between the reservoir volume (in Mm3) and the height of the reservoir (in masl)\n",
"rsv1.vol_head.set(\n",
" pd.Series([860, 862, 864, 866, 870, 872, 874, 876, 878, 880, 882, 884, 886, 888, 890, 894, 896, 898, 902, 904, 905, 907],\n",
" index=[0, 0.91, 1.87, 2.88, 5.07, 6.27, 7.56, 8.91, 10.34, 11.87, 13.53, 15.27, 17.11, 19.05, 21.1, 25.65, 27.96, 30.36, 35.18, 37.68, 39, 41.66], name=0))\n",
"rsv2.vol_head.set(\n",
" pd.Series([650, 651.28, 652.55, 653.83, 656.38, 657.66, 658.94, 660.21, 661.49, 662.77, 664.04, 665.32, 666.6, 667.87, 669.15, 671.70, 672.98, 674.26, 676.81, 678.09, 679, 680],\n",
" index=[0, 2.275, 4.675, 7.2, 12.675, 15.675, 18.9, 22.275, 25.85, 29.675, 33.825, 38.175, 42.775, 47.625, 52.75, 64.125, 69.9, 75.9, 87.95, 94.2, 97.5, 104.15], name=0))"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "0709e232",
"metadata": {
"Collapsed": "false"
},
"outputs": [],
"source": [
"# Setting the overflow description, the (linear) relation between spillage (in m3/s) and masl.\n",
"rsv1.flow_descr.set(pd.Series([0, 132], index=[906, 907], name=0))\n",
"rsv2.flow_descr.set(pd.Series([0, 132], index=[679, 680], name=0))\n",
"\n",
"# Inflow to the reservoir is an example of an attribute that can be set as a time series,using indexes that relate values to points in time\n",
"rsv2.inflow.set(pd.Series([60], [starttime]))"
]
},
{
"cell_type": "markdown",
"id": "23c7dfa2",
"metadata": {},
"source": [
"In order to let the model know which initial conditions are set, i.e. the initial start reservoir levels, we need to define them."
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "20ba1b46",
"metadata": {
"Collapsed": "false"
},
"outputs": [],
"source": [
"# Setting the initial start reservoir height in masl\n",
"rsv1.start_head.set(900)\n",
"rsv2.start_head.set(670)"
]
},
{
"cell_type": "markdown",
"id": "2695aa66",
"metadata": {},
"source": [
"We also define the endpoint descriptions (water values) for the reservoirs. The model needs these in order to compare the value of the water remaining in the reservoirs at the end of the period compared to the value of selling the water in the market given the market price."
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "94e784c4",
"metadata": {
"Collapsed": "false"
},
"outputs": [],
"source": [
"# Setting the endpoint description (water value) to 'rsv1' in NOK/MWh\n",
"rsv1.energy_value_input.set(30)\n",
"# Setting the endpoint description (water value) to 'rsv2' in NOK/MWh\n",
"rsv2.energy_value_input.set(10)"
]
},
{
"cell_type": "markdown",
"id": "441cb17e",
"metadata": {},
"source": [
"Now let's review some of the input graphically. Data such as time series, XY-curves and DataFrames can be plotted with a single line of code using .plot()."
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "95afb45f",
"metadata": {
"Collapsed": "false"
},
"outputs": [
{
"data": {
"text/html": [
" \n",
" "
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"application/vnd.plotly.v1+json": {
"config": {
"plotlyServerURL": "https://plot.ly"
},
"data": [
{
"hovertemplate": "=0.0
Mm3=%{x}
masl=%{y}