Commit 3dc54e74 authored by Jorn Baayen's avatar Jorn Baayen

Merge branch 'master' into casadi3

parents 4c46d015 0fae3cd5
......@@ -58,7 +58,7 @@ master_doc = 'index'
# General information about the project.
project = 'RTC-Tools'
copyright = '2016 Deltares'
copyright = '2016 - 2017 Deltares'
author = 'Jorn Baayen, Matthijs den Toom, et al.'
# The version info for the project you're documenting, acts as replacement for
......@@ -68,7 +68,7 @@ author = 'Jorn Baayen, Matthijs den Toom, et al.'
# The short X.Y version.
version = '2.0'
# The full version, including alpha/beta/rc tags.
release = '2.0.0-beta1'
release = '2.0.0-beta7'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
......@@ -350,7 +350,7 @@ texinfo_documents = [
# -- Hack to strip annotations from Modelica files ------------------------
MODELICA_EXAMPLE_BASE_FOLDER = '../examples'
MODELICA_EXAMPLES = ['basic', 'ensemble', 'goal_programming', 'mixed_integer', 'lookup_table']
MODELICA_EXAMPLES = ['basic', 'ensemble', 'goal_programming', 'mixed_integer', 'lookup_table', 'simulation']
MODELICA_STRIPPED_EXAMPLE_FOLDER = '_build/mo'
......
......@@ -6,8 +6,5 @@ This section provides examples demonstrating key features of RTC-Tools.
.. toctree::
:maxdepth: 2
examples/basic
examples/mixed_integer
examples/goal_programming
examples/lookup_table
examples/ensemble
examples/optimization
examples/simulation
Optimization examples
=====================
This section provides examples demonstrating key features of RTC-Tools optimization.
.. toctree::
:maxdepth: 2
optimization/basic
optimization/mixed_integer
optimization/goal_programming
optimization/lookup_table
optimization/ensemble
Filling a Reservoir
~~~~~~~~~~~~~~~~~~~
.. insert image here? e.g.
https://commons.wikimedia.org/wiki/File:Badgernet_Clywedog_reservoir_3.JPG
.. image:: ../../images/graig-coch-2117306_640.jpg
.. :href: https://pixabay.com/en/graig-coch-dam-wales-reservoir-uk-2117306/
.. pixabay content is released under a CC0 Public Domain licence - no attribution needed
Overview
--------
......@@ -52,7 +55,7 @@ Make sure to load the Deltares library before loading the example:
Once loaded, we have an OpenModelica Connection Editor window that looks like
this:
.. image:: ../images/simple_storage_openmodelica.png
.. image:: ../../images/simple_storage_openmodelica.png
The model ``Example.mo`` represents a simple system with the following
......@@ -75,7 +78,7 @@ and drag between the ports on the elements.
In text mode, the Modelica model looks as follows (with
annotation statements removed):
.. literalinclude:: ../_build/mo/basic.mo
.. literalinclude:: ../../_build/mo/basic.mo
:language: modelica
:lineno-match:
......@@ -95,7 +98,7 @@ the library elements must also be defined in the ``equation`` section.
In addition to elements, the input variables ``Q_in`` and ``Q_release`` are also
defined. ``Q_in`` is determined by the forecast and the operator cannot control
it, so we set ``Q_in(fixed = true)``. The actual values of ``Q_in`` are stored
in ``timeseries_input.csv``. In the ``equation`` section, equations are defined
in ``timeseries_import.csv``. In the ``equation`` section, equations are defined
to relate the inputs to the appropriate water system elements.
Because we want to view the water volume in the storage element in the output
......@@ -126,7 +129,7 @@ package ``run_optimization_problem`` form the ``rtctools.util`` package, and
any extra packages we want to use. For this example, the import block looks
like:
.. literalinclude:: ../../examples/basic/src/example.py
.. literalinclude:: ../../../examples/basic/src/example.py
:language: python
:lines: 1-5
:lineno-match:
......@@ -140,16 +143,16 @@ parent classes each perform different tasks related to importing and exporting
data and solving the optimization problem. Each imported class makes a set of
methods available to the our optimization class.
.. literalinclude:: ../../examples/basic/src/example.py
.. literalinclude:: ../../../examples/basic/src/example.py
:language: python
:pyobject: Example
:lineno-match:
:end-before: """
The next, we define an objective function. This is a class method that returns
Next, we define an objective function. This is a class method that returns
the value that needs to be minimized.
.. literalinclude:: ../../examples/basic/src/example.py
.. literalinclude:: ../../../examples/basic/src/example.py
:language: python
:pyobject: Example.objective
:lineno-match:
......@@ -161,7 +164,7 @@ at an individual timestep, we could define it inside the ``constraints()`` metho
Other parent classes also declare this method, so we call the ``super()`` method
so that we don't overwrite their behaviour.
.. literalinclude:: ../../examples/basic/src/example.py
.. literalinclude:: ../../../examples/basic/src/example.py
:language: python
:pyobject: Example.path_constraints
:lineno-match:
......@@ -173,7 +176,7 @@ To make our script run, at the bottom of our file we just have to call
the ``run_optimization_problem()`` method we imported on the optimization
problem class we just created.
.. literalinclude:: ../../examples/basic/src/example.py
.. literalinclude:: ../../../examples/basic/src/example.py
:language: python
:lineno-match:
:start-after: # Run
......@@ -183,7 +186,7 @@ The Whole Script
All together, the whole example script is as follows:
.. literalinclude:: ../../examples/basic/src/example.py
.. literalinclude:: ../../../examples/basic/src/example.py
:language: python
:lineno-match:
......@@ -202,7 +205,7 @@ The results from the run are found in ``output\timeseries_export.csv``. Any
CSV-reading software can import it, but this is what the results look like when
plotted in Microsoft Excel:
.. image:: ../images/basic_resultplot.png
.. image:: ../../images/basic_resultplot.png
This plot shows that the operator is able to keep the water level within the
bounds over the entire time horizon and end with a full reservoir.
......
......@@ -58,12 +58,12 @@ The Model
In OpenModelica Connection Editor, the model looks like this:
.. image:: ../images/simple_storage_openmodelica.png
.. image:: ../../images/simple_storage_openmodelica.png
In text mode, the Modelica model is as follows (with annotation statements
removed):
.. literalinclude:: ../_build/mo/ensemble.mo
.. literalinclude:: ../../_build/mo/ensemble.mo
:language: modelica
:lineno-match:
......@@ -91,7 +91,7 @@ Importing Packages
For this example, the import block is as follows:
.. literalinclude:: ../../examples/ensemble/src/example.py
.. literalinclude:: ../../../examples/ensemble/src/example.py
:language: python
:lines: 1-10
:lineno-match:
......@@ -102,7 +102,7 @@ Declaring Goals
First, we have a high priority goal to keep the water volume within a minimum
and maximum.
.. literalinclude:: ../../examples/ensemble/src/example.py
.. literalinclude:: ../../../examples/ensemble/src/example.py
:language: python
:pyobject: WaterVolumeRangeGoal
:lineno-match:
......@@ -110,7 +110,7 @@ and maximum.
We also want to save energy, so we define a goal to minimize ``Q_release``. This
goal has a lower priority.
.. literalinclude:: ../../examples/ensemble/src/example.py
.. literalinclude:: ../../../examples/ensemble/src/example.py
:language: python
:pyobject: MinimizeQreleaseGoal
:lineno-match:
......@@ -121,7 +121,7 @@ Optimization Problem
Next, we construct the class by declaring it and inheriting the desired parent
classes.
.. literalinclude:: ../../examples/ensemble/src/example.py
.. literalinclude:: ../../../examples/ensemble/src/example.py
:language: python
:pyobject: Example
:lineno-match:
......@@ -129,7 +129,7 @@ classes.
We turn on ensemble mode by setting ``csv_ensemble_mode = True``:
.. literalinclude:: ../../examples/ensemble/src/example.py
.. literalinclude:: ../../../examples/ensemble/src/example.py
:language: python
:lines: 49-50
:lineno-match:
......@@ -145,14 +145,14 @@ Because the timeseries we set will be the same for both ensemble members, we
also make sure that the timeseries we set are set for both ensemble members
using for loops.
.. literalinclude:: ../../examples/ensemble/src/example.py
.. literalinclude:: ../../../examples/ensemble/src/example.py
:language: python
:pyobject: Example.pre
:lineno-match:
Now we pass in the goals:
.. literalinclude:: ../../examples/ensemble/src/example.py
.. literalinclude:: ../../../examples/ensemble/src/example.py
:language: python
:pyobject: Example.path_goals
:lineno-match:
......@@ -163,7 +163,7 @@ allows the control tree to split at every time, but we override this method
and force it to split at a single timestep. See :ref:`ensemble-results` at
the bottom of the page for more information.
.. literalinclude:: ../../examples/ensemble/src/example.py
.. literalinclude:: ../../../examples/ensemble/src/example.py
:language: python
:pyobject: Example.control_tree_options
:lineno-match:
......@@ -171,14 +171,14 @@ the bottom of the page for more information.
We define the ``priority_completed()`` method. We ensure that it stores the
results from both ensemble members.
.. literalinclude:: ../../examples/ensemble/src/example.py
.. literalinclude:: ../../../examples/ensemble/src/example.py
:language: python
:pyobject: Example.priority_completed
:lineno-match:
We output our intermediate results using the ``post()`` method:
.. literalinclude:: ../../examples/ensemble/src/example.py
.. literalinclude:: ../../../examples/ensemble/src/example.py
:language: python
:pyobject: Example.post
:lineno-match:
......@@ -186,7 +186,7 @@ We output our intermediate results using the ``post()`` method:
Finally, we want to apply some additional configuration, reducing the amount of
information the solver outputs:
.. literalinclude:: ../../examples/ensemble/src/example.py
.. literalinclude:: ../../../examples/ensemble/src/example.py
:language: python
:pyobject: Example.solver_options
:lineno-match:
......@@ -198,7 +198,7 @@ To make our script run, at the bottom of our file we just have to call
the ``run_optimization_problem()`` method we imported on the optimization
problem class we just created.
.. literalinclude:: ../../examples/ensemble/src/example.py
.. literalinclude:: ../../../examples/ensemble/src/example.py
:language: python
:lineno-match:
:start-after: # Run
......@@ -208,7 +208,7 @@ The Whole Script
All together, the whole example script is as follows:
.. literalinclude:: ../../examples/ensemble/src/example.py
.. literalinclude:: ../../../examples/ensemble/src/example.py
:language: python
:lineno-match:
......
......@@ -47,12 +47,12 @@ weir). If the sea water level is higher than in the canal, water must be pumped.
In OpenModelica Connection Editor, the model looks like this:
.. image:: ../images/orifice_vs_pump_openmodelica.png
.. image:: ../../images/orifice_vs_pump_openmodelica.png
In text mode, the Modelica model looks as follows (with annotation statements
removed):
.. literalinclude:: ../_build/mo/goal_programming.mo
.. literalinclude:: ../../_build/mo/goal_programming.mo
:language: modelica
:lineno-match:
......@@ -80,7 +80,7 @@ Importing Packages
For this example, the import block is as follows:
.. literalinclude:: ../../examples/goal_programming/src/example.py
.. literalinclude:: ../../../examples/goal_programming/src/example.py
:language: python
:lines: 1-8
:lineno-match:
......@@ -99,7 +99,7 @@ maximum. Since we are applying this goal to a specific state (model variable) in
our model at every time step, we can inherit a special helper class to define
this goal, called a ``StateGoal``:
.. literalinclude:: ../../examples/goal_programming/src/example.py
.. literalinclude:: ../../../examples/goal_programming/src/example.py
:language: python
:pyobject: WaterLevelRangeGoal
:lineno-match:
......@@ -109,7 +109,7 @@ We also want to save energy, so we define a goal to minimize the integral of
non-path goals, the function range must be large enough to enclose the integral
of the variable over all the timesteps. This goal does not use a helper class:
.. literalinclude:: ../../examples/goal_programming/src/example.py
.. literalinclude:: ../../../examples/goal_programming/src/example.py
:language: python
:pyobject: MinimizeQpumpGoal
:lineno-match:
......@@ -122,7 +122,7 @@ The order of this goal must be 2, so that it penalizes both positive and
negative derivatives. Order of 2 is the default, but we include it here
explicitly for the sake of clarity.
.. literalinclude:: ../../examples/goal_programming/src/example.py
.. literalinclude:: ../../../examples/goal_programming/src/example.py
:language: python
:pyobject: MinimizeChangeInQpumpGoal
:lineno-match:
......@@ -133,7 +133,7 @@ Optimization Problem
Next, we construct the class by declaring it and inheriting the desired parent
classes.
.. literalinclude:: ../../examples/goal_programming/src/example.py
.. literalinclude:: ../../../examples/goal_programming/src/example.py
:language: python
:pyobject: Example
:lineno-match:
......@@ -149,7 +149,7 @@ implemented below in the ``path_constraints()`` method. Other parent classes
also declare this method, so we call the ``super()`` method so that we don't
overwrite their behaviour.
.. literalinclude:: ../../examples/goal_programming/src/example.py
.. literalinclude:: ../../../examples/goal_programming/src/example.py
:language: python
:pyobject: Example.path_constraints
:lineno-match:
......@@ -163,7 +163,7 @@ Non-path goals are more general goals that are not iteratively applied at every
timestep. We use the ``goals()`` method to pass a list of these goals to the
optimizer.
.. literalinclude:: ../../examples/goal_programming/src/example.py
.. literalinclude:: ../../../examples/goal_programming/src/example.py
:language: python
:pyobject: Example.goals
:lineno-match:
......@@ -176,7 +176,7 @@ affect our desire to satisfy the goal at time step B. Goals that inherit
``StateGoal`` are always path goals and must always be initialized with the
parameter ``self``.
.. literalinclude:: ../../examples/goal_programming/src/example.py
.. literalinclude:: ../../../examples/goal_programming/src/example.py
:language: python
:pyobject: Example.path_goals
:lineno-match:
......@@ -192,7 +192,7 @@ we declare a new ``pre()`` method, call ``super().pre()`` to ensure
that the original method runs unmodified, and add in a variable declaration to
store our list of intermediate results:
.. literalinclude:: ../../examples/goal_programming/src/example.py
.. literalinclude:: ../../../examples/goal_programming/src/example.py
:language: python
:pyobject: Example.pre
:lineno-match:
......@@ -201,7 +201,7 @@ Next, we define the ``priority_completed()`` method to inspect and summarize the
results. These are appended to our intermediate results variable after each
priority is completed.
.. literalinclude:: ../../examples/goal_programming/src/example.py
.. literalinclude:: ../../../examples/goal_programming/src/example.py
:language: python
:pyobject: Example.priority_completed
:lineno-match:
......@@ -210,7 +210,7 @@ We want some way to output our intermediate results. This is accomplished using
the ``post()`` method. Again, we nedd to call the ``super()`` method to avoid
overwiting the internal method.
.. literalinclude:: ../../examples/goal_programming/src/example.py
.. literalinclude:: ../../../examples/goal_programming/src/example.py
:language: python
:pyobject: Example.post
:lineno-match:
......@@ -218,7 +218,7 @@ overwiting the internal method.
Finally, we want to apply some additional configuration, reducing the amount of
information the solver outputs:
.. literalinclude:: ../../examples/goal_programming/src/example.py
.. literalinclude:: ../../../examples/goal_programming/src/example.py
:language: python
:pyobject: Example.solver_options
:lineno-match:
......@@ -230,7 +230,7 @@ To make our script run, at the bottom of our file we just have to call
the ``run_optimization_problem()`` method we imported on the optimization
problem class we just created.
.. literalinclude:: ../../examples/goal_programming/src/example.py
.. literalinclude:: ../../../examples/goal_programming/src/example.py
:language: python
:lineno-match:
:start-after: # Run
......@@ -240,7 +240,7 @@ The Whole Script
All together, the whole example script is as follows:
.. literalinclude:: ../../examples/goal_programming/src/example.py
.. literalinclude:: ../../../examples/goal_programming/src/example.py
:language: python
:lineno-match:
......
......@@ -24,12 +24,12 @@ The Model
In OpenModelica Connection Editor, the model looks like this:
.. image:: ../images/simple_storage_openmodelica.png
.. image:: ../../images/simple_storage_openmodelica.png
In text mode, the Modelica model is as follows (with annotation statements
removed):
.. literalinclude:: ../_build/mo/lookup_table.mo
.. literalinclude:: ../../_build/mo/lookup_table.mo
:language: modelica
:lineno-match:
......@@ -56,7 +56,7 @@ Importing Packages
For this example, the import block is as follows:
.. literalinclude:: ../../examples/lookup_table/src/example.py
.. literalinclude:: ../../../examples/lookup_table/src/example.py
:language: python
:lines: 1-9
:lineno-match:
......@@ -79,7 +79,7 @@ problem class, so we define the ``__init__()`` method so we can pass the
values of the goals in later. We call the ``super()`` method to avoid
overwriting the ``__init__()`` method of the parent class.
.. literalinclude:: ../../examples/lookup_table/src/example.py
.. literalinclude:: ../../../examples/lookup_table/src/example.py
:language: python
:pyobject: WaterVolumeRangeGoal
:lineno-match:
......@@ -87,7 +87,7 @@ overwriting the ``__init__()`` method of the parent class.
We also want to save energy, so we define a goal to minimize ``Q_release``. This
goal has a lower priority.
.. literalinclude:: ../../examples/lookup_table/src/example.py
.. literalinclude:: ../../../examples/lookup_table/src/example.py
:language: python
:pyobject: MinimizeQreleaseGoal
:lineno-match:
......@@ -98,7 +98,7 @@ Optimization Problem
Next, we construct the class by declaring it and inheriting the desired parent
classes.
.. literalinclude:: ../../examples/lookup_table/src/example.py
.. literalinclude:: ../../../examples/lookup_table/src/example.py
:language: python
:pyobject: Example
:lineno-match:
......@@ -117,7 +117,7 @@ relation using the ``lookup_table()`` method. We cache the functions for
convenience. The ``lookup_storage_V()`` method can convert timeseries objects,
and we save the water volume goal bounds as timeseries.
.. literalinclude:: ../../examples/lookup_table/src/example.py
.. literalinclude:: ../../../examples/lookup_table/src/example.py
:language: python
:pyobject: Example.pre
:lineno-match:
......@@ -133,7 +133,7 @@ use the ``path_goals()`` method. This is a method that returns a list of the
goals we defined above. The ``WaterVolumeRangeGoal`` needs to be instantiated
with the new water volume timeseries we just defined.
.. literalinclude:: ../../examples/lookup_table/src/example.py
.. literalinclude:: ../../../examples/lookup_table/src/example.py
:language: python
:pyobject: Example.path_goals
:lineno-match:
......@@ -147,7 +147,7 @@ We define the ``priority_completed()`` method to inspect and summerize the
results. These are appended to our intermediate results variable after each
priority is completed.
.. literalinclude:: ../../examples/lookup_table/src/example.py
.. literalinclude:: ../../../examples/lookup_table/src/example.py
:language: python
:pyobject: Example.priority_completed
:lineno-match:
......@@ -155,7 +155,7 @@ priority is completed.
We output our intermediate results using the ``post()`` method. Again, we nedd
to call the ``super()`` method to avoid overwiting the internal method.
.. literalinclude:: ../../examples/lookup_table/src/example.py
.. literalinclude:: ../../../examples/lookup_table/src/example.py
:language: python
:pyobject: Example.post
:lineno-match:
......@@ -163,7 +163,7 @@ to call the ``super()`` method to avoid overwiting the internal method.
Finally, we want to apply some additional configuration, reducing the amount of
information the solver outputs:
.. literalinclude:: ../../examples/lookup_table/src/example.py
.. literalinclude:: ../../../examples/lookup_table/src/example.py
:language: python
:pyobject: Example.solver_options
:lineno-match:
......@@ -175,7 +175,7 @@ To make our script run, at the bottom of our file we just have to call
the ``run_optimization_problem()`` method we imported on the optimization
problem class we just created.
.. literalinclude:: ../../examples/lookup_table/src/example.py
.. literalinclude:: ../../../examples/lookup_table/src/example.py
:language: python
:lineno-match:
:start-after: # Run
......@@ -185,7 +185,7 @@ The Whole Script
All together, the whole example script is as follows:
.. literalinclude:: ../../examples/lookup_table/src/example.py
.. literalinclude:: ../../../examples/lookup_table/src/example.py
:language: python
:lineno-match:
......
Mixed Integer Optimization: Pumps and Orifices
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. image:: ../../images/Woudagemaal.jpg
.. :href: https://commons.wikimedia.org/wiki/File:Woudagemaal.jpg
.. content is released under a CC0 Public Domain licence - no attribution needed
.. note::
This example focuses on how to incorporate mixed integer components into a
......@@ -43,12 +48,12 @@ represents a simple water system with the following elements:
* an orifice
``Deltares.ChannelFlow.Hydraulic.Structures.BooleanSubmergedOrifice``
.. image:: ../images/orifice_vs_pump_openmodelica.png
.. image:: ../../images/orifice_vs_pump_openmodelica.png
In text mode, the Modelica model looks as follows (with annotation statements
removed):
.. literalinclude:: ../_build/mo/mixed_integer.mo
.. literalinclude:: ../../_build/mo/mixed_integer.mo
:language: modelica
:lineno-match:
......@@ -94,7 +99,7 @@ Importing Packages
For this example, the import block is as follows:
.. literalinclude:: ../../examples/mixed_integer/src/example.py
.. literalinclude:: ../../../examples/mixed_integer/src/example.py
:language: python
:lines: 1-6
:lineno-match:
......@@ -108,7 +113,7 @@ Optimization Problem
Next, we construct the class by declaring it and inheriting the desired parent
classes.
.. literalinclude:: ../../examples/mixed_integer/src/example.py
.. literalinclude:: ../../../examples/mixed_integer/src/example.py
:language: python
:pyobject: Example
:lineno-match:
......@@ -118,7 +123,7 @@ Now we define an objective function. This is a class method that returns the
value that needs to be minimized. Here we specify that we want to minimize the
volume pumped:
.. literalinclude:: ../../examples/mixed_integer/src/example.py
.. literalinclude:: ../../../examples/mixed_integer/src/example.py
:language: python
:pyobject: Example.objective
:lineno-match:
......@@ -133,7 +138,7 @@ in order to work. They are implemented below in the ``path_constraints()``
method. their parent classes also declare this method, so we call the
``super()`` method so that we don't overwrite their behaviour.
.. literalinclude:: ../../examples/mixed_integer/src/example.py
.. literalinclude:: ../../../examples/mixed_integer/src/example.py
:language: python
:pyobject: Example.path_constraints
:lineno-match:
......@@ -141,7 +146,7 @@ method. their parent classes also declare this method, so we call the
Finally, we want to apply some additional configuration, reducing the amount of
information the solver outputs:
.. literalinclude:: ../../examples/mixed_integer/src/example.py
.. literalinclude:: ../../../examples/mixed_integer/src/example.py
:language: python
:pyobject: Example.solver_options
:lineno-match:
......@@ -153,7 +158,7 @@ To make our script run, at the bottom of our file we just have to call
the ``run_optimization_problem()`` method we imported on the optimization
problem class we just created.
.. literalinclude:: ../../examples/mixed_integer/src/example.py
.. literalinclude:: ../../../examples/mixed_integer/src/example.py
:language: python
:lineno-match:
:start-after: # Run
......@@ -163,7 +168,7 @@ The Whole Script
All together, the whole example script is as follows:
.. literalinclude:: ../../examples/mixed_integer/src/example.py
.. literalinclude:: ../../../examples/mixed_integer/src/example.py
:language: python
:lineno-match:
......
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from datetime import datetime
data_path = '../../../examples/simulation/output/timeseries_export.csv'
delimiter = ','
# Import Data
ncols = len(np.genfromtxt(data_path, max_rows=1, delimiter=delimiter))
datefunc = lambda x: datetime.strptime(x, '%Y-%m-%d %H:%M:%S')
results = np.genfromtxt(data_path, converters={0: datefunc}, delimiter=delimiter,
dtype='object' + ',float' * (ncols - 1), names=True)
# Generate Plot
n_subplots = 2
f, axarr = plt.subplots(n_subplots, sharex=True, figsize=(8, 3 * n_subplots))
axarr[0].set_title('Water Volume and Discharge')
f.autofmt_xdate()
# Upper subplot
axarr[0].set_ylabel(u'Water Volume [m\u00B3]')
axarr[0].ticklabel_format(style='sci', axis='y', scilimits=(0,0))
axarr[0].plot(results['time'], results['storage_V'], label='Storage',
linewidth=2, color='b')
axarr[0].plot(results['time'], [420000]*len(results['time']), label='Storage Max',
linewidth=2, color='r', linestyle='--')
axarr[0].plot(results['time'], [380000]*len(results['time']), label='Storage Min',
linewidth=2, color='g', linestyle='--')
# Lower Subplot
axarr[1].set_ylabel(u'Flow Rate [m\u00B3/s]')
# axarr[1].plot(results['time'], results['Q_in'], label='Inflow',
# linewidth=2, color='g')
axarr[1].plot(results['time'], results['Q_release'], label='Release',
linewidth=2, color='r')
# Shrink each axis by 20% and put a legend to the right of the axis
for i in range(n_subplots):
box = axarr[i].get_position()
axarr[i].set_position([box.x0, box.y0, box.width * 0.8, box.height])
axarr[i].legend(loc='center left', bbox_to_anchor=(1, 0.5), frameon=False)
# Output Plot
plt.show()
Simulation examples
=====================
This section provides examples demonstrating key features of RTC-Tools simulation.
.. toctree::
:maxdepth: 2
simulation/setpoint
Tracking a Setpoint
~~~~~~~~~~~~~~~~~~~
.. image:: ../../images/graig-coch-2117306_640.jpg
.. :href: https://pixabay.com/en/graig-coch-dam-wales-reservoir-uk-2117306/
.. pixabay content is released under a CC0 Public Domain licence - no attribution needed
Overview
--------
The purpose of this example is to understand the technical setup of an RTC-
Tools simulation model, how to run the model, and how to access the
results.
The scenario is the following: A reservoir operator is trying to keep the
reservoir's volume close to a given target volume. They are given a six-day
forecast of inflows given in 12-hour increments. To keep things simple, we
ignore the waterlevel-storage relation of the reservoir and head-discharge
relationships in this example. To make things interesting, the reservoir
operator is only able to release water at a few discrete flow rates, and only
change the discrete flow rate every 12 hours. They have chosen to use the RTC-
Tools simulator to see if a simple proportional controller will be able to
keep the system close enough to the target water volume.
The folder ``<installation directory>\RTCTools2\examples\simulation``
contains a complete RTC-Tools simulation problem. An RTC-Tools
directory has the following structure:
* ``input``: This folder contains the model input data. These are several files
in comma separated value format, ``csv``.
* ``model``: This folder contains the Modelica model. The Modelica model
contains the physics of the RTC-Tools model.
* ``output``: The folder where the output is saved in the file
``timeseries_export.csv``.
* ``src``: This folder contains a Python file. This file contains the
configuration of the model and is used to run the model .
The Model
---------
The first step is to develop a physical model of the system. The model can be
viewed and edited using the OpenModelica Connection Editor (OMEdit) program.
For how to download and start up OMEdit, see :ref:`getting-started-omedit`.
Make sure to load the Deltares library before loading the example:
#. Load the Deltares library into OMEdit
* Using the menu bar: *File -> Open Model/Library File(s)*
* Select ``<installation directory>\RTCTools2\mo\Deltares\package.mo``
#. Load the example model into OMEdit
* Using the menu bar: *File -> Open Model/Library File(s)*
* Select ``<installation directory>\RTCTools2\examples\simulation\model\Example.mo``
Once loaded, we have an OpenModelica Connection Editor window that looks like
this:
.. image:: ../../images/simple_storage_openmodelica.png
The model ``Example.mo`` represents a simple system with the following
elements:
* a reservoir, modeled as storage element
``Deltares.ChannelFlow.SimpleRouting.Storage.Storage``,
* an inflow boundary condition
``Deltares.ChannelFlow.SimpleRouting.BoundaryConditions.Inflow``,
* an outfall boundary condition
``Deltares.ChannelFlow.SimpleRouting.BoundaryConditions.Terminal``,
* connectors (black lines) connecting the elements.
You can use the mouse-over feature help to identify the predefined models from
the Deltares library. You can also drag the elements around- the connectors will
move with the elements. Adding new elements is easy- just drag them in from the
Deltares Library on the sidebar. Connecting the elements is just as easy- click
and drag between the ports on the elements.
In text mode, the Modelica model looks as follows (with
annotation statements removed):
.. literalinclude:: ../../_build/mo/simulation.mo
:language: modelica
:lineno-match:
The three water system elements (storage, inflow, and outfall) appear under
the ``model Example`` statement. The ``equation`` part connects these three
elements with the help of connections. Note that ``storage`` extends the partial
model ``QSISO`` which contains the connectors ``QIn`` and ``QOut``.