{
"cells": [
{
"cell_type": "markdown",
"id": "53629219",
"metadata": {},
"source": [
"# Head coefficients\n",
"\n",
"When running Prodrisk for the first time on a system/dataset, Prodrisk will run twice:\n",
"\n",
"- **Initial run:** The purpose is to find reasonable head coefficients and expected reservoir trajectories for the system. These are stored on the files **HKORR.SDDP** and **MAGVOL.SDDP**.\n",
"- **Final run:** Uses the head coefficients and expected reservoir levels available in the files **HKORR.SDDP** and **MAGVOL.SDDP**, as computed in the initial run. Stores results and updated values for head coefficients and expected reservoir levels at **NY_HKORR.SDDP** and **NY_MAGVOL.SDDP**.\n",
"\n",
"The unit for **MAGVOL** is Mm3. The unit for **HKORR** is 10^6 øre/Mm3 if the price is given in øre/kWh, and NOK/Mm3 if the price is given in NOK/MWh.\n",
"\n",
"It is possible to run Prodrisk without head coefficients and without the initial run, if the option **-NOHEAD** is used. If one wants to use the files **NY_HKORR.SDDP** and **NY_MAGVOL.SDDP** in a new Prodrisk run, the option **–NEWHEAD** should be used.\n",
"\n",
"Small reservoirs that are not regulated should not include nominal head nor submersion, to avoid getting head coefficients calculated for this reservoir.\n",
"\n",
"## Head coefficients\n",
"\n",
"Head coefficients represent the expected added value of storing water in the reservoirs due to head. The head coefficients enter the optimization problem formulated for each week, defining an additional reward for storing water. These coefficients are based on pervious Prodrisk computations and vary between weeks and reservoirs. For a detailed description of the mathematical formulation, see {cite:ps}`Gjelsvik_considering_2005`. \n",
"\n",
"The head coefficients are stored as follows:\n",
"\n",
"- If the coefficients are not present from a previous Prodrisk run, Prodrisk starts by setting head coefficients to zero. Once a Prodrisk initial run is completed, estimated coefficients are store in a file named **HKORR.SDDP**.\n",
"- In the final run, head coefficients per week and reservoir are read from **HKORR.SDDP** and used in the computation of new cuts. Updated head coefficients are stored in the file **NY_HKORR.SDDP**.\n",
"\n",
"The coefficients in files **HKORR.SDDP** or **NY_KORR.SDDP** (if **-newhead** is used) are always used (except if **-nohead** is used) by Prodrisk if these files exist and are consistent. These coefficients are used even if the starting reservoir or computation period are changed, as it is believed that previously computed coefficients provide a better estimate than running without coefficients.\n",
"\n",
"The head coefficients are updated as follows:\n",
"\n",
"- **HKORR.SDDP** is updated if an initial Prodrisk run is performed or if the period of analysis (week numbers) is changed compared to a current file.\n",
"- **NY_HKORR.SDDP** is updated if one uses the options **-nohead**, **–newhead** or **–forts**, or if a consistent **HKORR.SDDP** file exists and the period of analysis is the same as specified in this file.\n",
"\n",
"## Expected reservoir trajectories\n",
"\n",
"For valid cut computations, Prodrisk must use a fixed and state-independent head in each week. The following approximate scheme is used to find the expected head:\n",
"\n",
"- In the initial run, Prodrisk assumes that the reservoir trajectories follows the rule curves. The rule curves are either explicitly defined by the user or automatically computed by the EOPS model. The simulated mean reservoir trajectories from the initial run are stored in the file **MAGVOL.SDDP**.\n",
"- In the final run, the mean reservoir level per week is read from the file **MAGVOL.SDDP**, and these are used when computing new cuts. Updated mean reservoir trajectories are stored in the file **NY_MAGVOL.SDDP**.\n",
"- If one wants to use the new mean reservoir trajectories in a later final run, Prodrisk should be run with the option **-newhead**.\n",
"\n",
"The trajectories in files **MAGVOL.SDDP** or **NY_MAGVOL.SDDP** (if **-newhead** is used) are always used by Prodrisk if these files exist and are consistent. These coefficients are used even though one changes the staring reservoir or computation period, as it is believed that previously computed coefficients provide a better estimate than running without coefficients. These coefficients are used even though one changes the starting reservoir or computation period, as it is believed that previously computed trajectories provide a better estimate than the rule curves. To use rule curves instead of the computed trajectories, the MAGVOL.SDDP file must be deleted.\n",
"\n",
"The files files **MAGVOL.SDDP** or **NY_MAGVOL.SDDP** are updated similarly as the files **HKORR.SDDP** or **NY_HKORR.SDDP**.\n",
"\n",
"## Command line interface\n",
"If the command line option **-nohead** is used, Prodrisk will run without head coefficients and run only.\n",
"\n",
"If the command line option **-newhead** is used, Prodrisk will use the head coefficients from the file **NY_HKORR.SDDP**.\n",
"\n",
"## API\n",
"Running the API without head coefficients can be done by setting \n",
"\n",
"**prodrisk-command_line_option=\"-NOHEAD\"**.\n",
"\n",
"Running the API with head coefficients from the file **NY_HKORR.SDDP** can be done by setting \n",
"\n",
"**prodrisk-command_line_option=\"-NEWHEAD\"**.\n",
"\n",
"## Example of use with head coefficients\n",
"\n",
"This example illustrates how the head coefficients and mean reservoir trajectories may be set as input to a Prodrisk API run based on a previous run.\n",
"\n",
"Each prodrisk session in this example is a copy of the session created in the basic example."
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "5ce28f90",
"metadata": {
"Collapsed": "false"
},
"outputs": [],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"import plotly.express as px\n",
"\n",
"from pyprodrisk import ProdriskSession\n",
"from head_coefficients import build_model, set_all_head_coeffs_and_mean_reservoir_trajectories"
]
},
{
"cell_type": "markdown",
"id": "098577b8",
"metadata": {},
"source": [
"### First session\n",
"The first session runs two main iterations, as no head coefficients are set, and command line option **-NOHEAD** is not used.\n",
"\n",
"The F- and K-costs from the two main iterations are added to a dictionary."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "377ac48f",
"metadata": {
"Collapsed": "false"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Expected objective value: 21009.885 kkr\n"
]
}
],
"source": [
"prodrisk = ProdriskSession(license_path='/prodrisk', solver_path='/pyprodrisk', silent=False, log_file='')\n",
"build_model(prodrisk)\n",
"\n",
"# --- run prodrisk session ---\n",
"status = prodrisk.run()\n",
"\n",
"my_area = prodrisk.model.area[\"my_area\"]\n",
"expected_objective_val_kkr = my_area.expected_objective_value.get()\n",
"print(f\"Expected objective value: {expected_objective_val_kkr} kkr\")\n",
"\n",
"#fcost_first_run = my_area.forward_cost_first_run.get()\n",
"#kcost_first_run = my_area.backward_cost_first_run.get()\n",
"#iteration_numbers_first_run = range(1, len(fcost_first_run)+1)\n",
"\n",
"#fcost = my_area.forward_cost.get()\n",
"#kcost = my_area.backward_cost.get()\n",
"#iteration_numbers = range(1, len(fcost)+1)\n",
"\n",
"#cost_dict = { \"F-cost, first session\": pd.Series(data=fcost, index=iteration_numbers),\n",
"# \"K-cost, first session\": pd.Series(data=kcost, index=iteration_numbers),\n",
"# \"F-cost first run, first session\": pd.Series(data=fcost_first_run, index=iteration_numbers_first_run),\n",
"# \"K-cost first run, first session\": pd.Series(data=kcost_first_run, index=iteration_numbers_first_run),\n",
"# }"
]
},
{
"cell_type": "markdown",
"id": "ed97ac30",
"metadata": {},
"source": [
"### Second session\n",
"\n",
"The second session is a copy of the first one, but the head coefficients and mean reservoir trajectories are set equal to those used in the second main iteration of the first session.\n",
"\n",
"This run should thus give identical results as the first session, but take approximately half the time to run, as the head coefficients are set as input, so only one main iteration is run.\n",
"\n",
"The F- and K-costs from this run are also added to the cost dictionary."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "9a2efe59",
"metadata": {
"Collapsed": "false"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Expected objective value second run: 21009.885 kkr\n"
]
}
],
"source": [
"prodrisk_second_run = ProdriskSession(license_path='/prodrisk', solver_path='/pyprodrisk', silent=False, log_file='')\n",
"build_model(prodrisk_second_run)\n",
"prodrisk_second_run.command_line_option = \"\"\n",
"#set_all_head_coeffs_and_mean_reservoir_trajectories(prodrisk, prodrisk_second_run, new_head=False)\n",
"\n",
"status = prodrisk_second_run.run()\n",
"\n",
"my_area = prodrisk_second_run.model.area[\"my_area\"]\n",
"expected_objective_val_kkr = my_area.expected_objective_value.get()\n",
"print(f\"Expected objective value second run: {expected_objective_val_kkr} kkr\")\n",
"\n",
"my_area = prodrisk_second_run.model.area[\"my_area\"]\n",
"#fcost = my_area.forward_cost.get()\n",
"#kcost = my_area.backward_cost.get()\n",
"#iteration_numbers = range(1, len(fcost)+1)\n",
"#cost_dict[\"F-cost, second session\"] = pd.Series(data=fcost, index=iteration_numbers)\n",
"#cost_dict[\"K-cost, second session\"] = pd.Series(data=kcost, index=iteration_numbers)"
]
},
{
"cell_type": "markdown",
"id": "f4627f65",
"metadata": {},
"source": [
"### Third session\n",
"\n",
"The third session is a copy of the first one, but the head coefficients and mean reservoir trajectories are set equal to those created by the second main iteration of the first session. This would correspond to running with option -NEWHEAD in \"manual\" Prodrisk runs.\n",
"\n",
"This run should thus not give identical results as the first and second session, as new head coefficients are set as input, so only one main iteration is run."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "6f87d089",
"metadata": {
"Collapsed": "false"
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Expected objective value second run: 21009.885 kkr\n"
]
}
],
"source": [
"prodrisk_third_run = ProdriskSession(license_path='/prodrisk', solver_path='/pyprodrisk', silent=False, log_file='')\n",
"build_model(prodrisk_third_run)\n",
"prodrisk_third_run.command_line_option = \"\"\n",
"#set_all_head_coeffs_and_mean_reservoir_trajectories(prodrisk, prodrisk_third_run, new_head=True)\n",
"\n",
"status = prodrisk_third_run.run()\n",
"\n",
"my_area = prodrisk_second_run.model.area[\"my_area\"]\n",
"expected_objective_val_kkr = my_area.expected_objective_value.get()\n",
"print(f\"Expected objective value second run: {expected_objective_val_kkr} kkr\")\n",
"\n",
"my_area = prodrisk_third_run.model.area[\"my_area\"]\n",
"#fcost = my_area.forward_cost.get()\n",
"#kcost = my_area.backward_cost.get()\n",
"#iteration_numbers = range(1, len(fcost)+1)\n",
"#cost_dict[\"F-cost, third session\"] = pd.Series(data=fcost, index=iteration_numbers)\n",
"#cost_dict[\"K-cost, third session\"] = pd.Series(data=kcost, index=iteration_numbers)"
]
},
{
"cell_type": "markdown",
"id": "bb6a46cd",
"metadata": {},
"source": [
"### Plot F/K-costs from the different runs."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "634c2d11",
"metadata": {
"Collapsed": "false"
},
"outputs": [],
"source": [
"#df = pd.DataFrame(cost_dict)\n",
"\n",
"#fig = px.line(df, labels={\n",
"# \"index\": \"Iteration number\",\n",
"# \"value\": \"Cost\"\n",
"# })\n",
"#fig.show()"
]
},
{
"cell_type": "markdown",
"id": "30e1336b",
"metadata": {},
"source": [
"\n",
"
Relative head: (head/nominal head)=%{x}
Discharge capacity [m3/s]=%{y}