Skip to content

Commit

Permalink
explain model in doc
Browse files Browse the repository at this point in the history
  • Loading branch information
Jingru923 committed Mar 26, 2024
1 parent 686e5d2 commit 8c4415f
Showing 1 changed file with 307 additions and 15 deletions.
322 changes: 307 additions & 15 deletions docs/python/examples.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
"import matplotlib.pyplot as plt\n",
"import numpy as np\n",
"import pandas as pd\n",
"from ribasim import Allocation, Model, Node\n",
"import ribasim\n",
"from ribasim import Model, Node\n",
"from ribasim.nodes import (\n",
" basin,\n",
" discrete_control,\n",
Expand Down Expand Up @@ -712,12 +713,8 @@
")\n",
"\n",
"y_min, y_max = ax.get_ybound()\n",
"ax.fill_between(\n",
" df_control.time[:2].to_numpy(), 2 * [y_min], 2 * [y_max], alpha=0.2, color=\"C0\"\n",
")\n",
"ax.fill_between(\n",
" df_control.time[2:4].to_numpy(), 2 * [y_min], 2 * [y_max], alpha=0.2, color=\"C0\"\n",
")\n",
"ax.fill_between(df_control.time[:2], 2 * [y_min], 2 * [y_max], alpha=0.2, color=\"C0\")\n",
"ax.fill_between(df_control.time[2:4], 2 * [y_min], 2 * [y_max], alpha=0.2, color=\"C0\")\n",
"\n",
"ax.set_xticks(\n",
" date2num(df_control.time).tolist(),\n",
Expand Down Expand Up @@ -1241,7 +1238,7 @@
"metadata": {},
"outputs": [],
"source": [
"model.allocation = Allocation(use_allocation=True, timestep=86400)"
"model.allocation = ribasim.Allocation(use_allocation=True, timestep=86400)"
]
},
{
Expand Down Expand Up @@ -1416,7 +1413,7 @@
"metadata": {},
"outputs": [],
"source": [
"model = Model(\n",
"model = ribasim.Model(\n",
" starttime=\"2020-01-01\",\n",
" endtime=\"2020-02-01\",\n",
")"
Expand Down Expand Up @@ -1544,7 +1541,7 @@
"metadata": {},
"outputs": [],
"source": [
"model.allocation = Allocation(use_allocation=True, timestep=1e5)"
"model.allocation = ribasim.Allocation(use_allocation=True, timestep=1e5)"
]
},
{
Expand Down Expand Up @@ -1638,14 +1635,23 @@
"df_basin_wide = df_basin.pivot_table(\n",
" index=\"time\", columns=\"node_id\", values=[\"storage\", \"level\"]\n",
")\n",
"ax = df_basin_wide[\"level\"].plot(ylabel=\"level [m]\")"
"ax = df_basin_wide[\"level\"].plot()\n",
"where_allocation = (\n",
" df_basin_wide.index - df_basin_wide.index[0]\n",
").total_seconds() % model.allocation.timestep == 0\n",
"where_allocation[0] = False\n",
"df_basin_wide[where_allocation][\"level\"].plot(\n",
" style=\"o\",\n",
" ax=ax,\n",
")\n",
"ax.set_ylabel(\"level [m]\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In the plot above, the line denotes the level of Basin #2 over time.\n",
"In the plot above, the line denotes the level of Basin #2 over time and the dots denote the times at which allocation optimization was run, with intervals of $\\Delta t_{\\text{alloc}}$.\n",
"The Basin level is a piecewise linear function of time, with several stages explained below.\n",
"\n",
"Constants:\n",
Expand Down Expand Up @@ -1687,7 +1693,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"![alternative text](image/cascade-polder.jpg)"
"![](image/cascade-polder.jpg)"
]
},
{
Expand All @@ -1697,6 +1703,15 @@
"In this polder system, there are give concatinating polder basins and level control system with weirs inbetween each basin and inlet and outlet to main system"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To begin with, all the polder basins are experiencing precipitation excess, shortage and seepage. These are time properties of a basin. So the polder basin are basins.\n",
"\n",
"As for the main system, it gives water to the polder basins and collect water back from polder basins. It is modelled as a basin. However, it is served as a water source, so it is considered a basin with a very large profil area."
]
},
{
"cell_type": "code",
"execution_count": null,
Expand All @@ -1709,7 +1724,284 @@
{
"cell_type": "markdown",
"metadata": {},
"source": []
"source": [
"Setting up the basins:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"time = pd.date_range(model.starttime, model.endtime)\n",
"day_of_year = time.day_of_year.to_numpy()\n",
"\n",
"precipitation = np.zeros(day_of_year.size)\n",
"precipitation[0:90] = 1.72e-8\n",
"precipitation[330:366] = 1.72e-8\n",
"\n",
"evaporation = np.zeros(day_of_year.size)\n",
"evaporation[130:270] = 2.87e-8\n",
"\n",
"drainage = np.zeros(day_of_year.size)\n",
"drainage[120:270] = 0.4 * 2.87e-8\n",
"drainage3 = drainage.copy()\n",
"drainage3[210:240] = 17 * 2.87e-8\n",
"drainage4 = drainage.copy()\n",
"drainage4[160:240] = 13 * 2.87e-8\n",
"\n",
"infiltration = np.zeros(day_of_year.size)\n",
"infiltration[0:90] = 5e-8\n",
"\n",
"polder_profile = basin.Profile(area=[100, 100], level=[0.0, 3.0])\n",
"\n",
"basin_time = [\n",
" basin.Time(\n",
" time=pd.date_range(model.starttime, model.endtime),\n",
" drainage=drainage,\n",
" potential_evaporation=evaporation,\n",
" infiltration=0.0,\n",
" precipitation=precipitation,\n",
" urban_runoff=0.0,\n",
" ),\n",
"]\n",
"\n",
"basin_time4 = [\n",
" basin.Time(\n",
" time=pd.date_range(model.starttime, model.endtime),\n",
" drainage=drainage4,\n",
" potential_evaporation=evaporation,\n",
" infiltration=0.0,\n",
" precipitation=precipitation,\n",
" urban_runoff=0.0,\n",
" ),\n",
"]\n",
"basin_time3 = [\n",
" basin.Time(\n",
" time=pd.date_range(model.starttime, model.endtime),\n",
" drainage=drainage3,\n",
" potential_evaporation=evaporation,\n",
" infiltration=0.0,\n",
" precipitation=precipitation,\n",
" urban_runoff=0.0,\n",
" ),\n",
"]\n",
"\n",
"model.basin.add(\n",
" Node(1, Point(2.0, 0.0)),\n",
" [\n",
" basin.State(level=[2.5]),\n",
" basin.Profile(area=[1000, 1000], level=[0.0, 3.0]),\n",
" basin.Time(\n",
" time=pd.date_range(model.starttime, model.endtime),\n",
" drainage=0.0,\n",
" potential_evaporation=0.0,\n",
" infiltration=0.0,\n",
" precipitation=0.0,\n",
" urban_runoff=0.0,\n",
" ),\n",
" ],\n",
")\n",
"model.basin.add(\n",
" Node(4, Point(0.0, -2.0)),\n",
" [basin.State(level=[1.5]), polder_profile, *basin_time],\n",
")\n",
"model.basin.add(\n",
" Node(6, Point(0.0, -4.0)),\n",
" [basin.State(level=[1.0]), polder_profile, *basin_time],\n",
")\n",
"model.basin.add(\n",
" Node(8, Point(2.0, -4.0)),\n",
" [basin.State(level=[1.5]), polder_profile, *basin_time3],\n",
")\n",
"model.basin.add(\n",
" Node(10, Point(4.0, -4.0)),\n",
" [basin.State(level=[1.3]), polder_profile, *basin_time4],\n",
")\n",
"model.basin.add(\n",
" Node(12, Point(4.0, -2.0)),\n",
" [basin.State(level=[0.1]), polder_profile, *basin_time],\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"After all the basins are set. The component inbetween the basins needs to be determined. For polder basin 5 (node 12), it needs to be maintain at level 0.0 meter. This means that either there should be no water in this basin, or the basin bottom is lower than sea level, and the water level should be maintained at sea level. \n",
"\n",
"If it is the first case, a pump is needed to make sure there is no water left in the basin. If it is the second case, a normal outlet with minimum crest level of 0.0 is needed. In this example, the first case is assumed. Therefore, a pump is set."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Setup the pumps:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"model.pump.add(\n",
" Node(13, Point(4.0, -1.0)),\n",
" [pump.Static(flow_rate=[0.5 / 3600])],\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"According to the description of situation 1 and 2, the water in one polder basin need to be able to flow to the downstream basin if the current basin has too much water that is higher than the setpoint or if the downstream basin requires more water. The functionality of the outlet with a minimum crest satisfy this feature. Take polder basin 1 (node 4) as an example. If the downstream polder has water shortage, polder basin 1 need to flow water as long as the water level is above 1.95 meter. So the minimum crest level for the outlet would be 1.95 meter."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Setup the outlets:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Set up outlet\n",
"model.outlet.add(\n",
" Node(2, Point(0.0, -1.0)),\n",
" [outlet.Static(flow_rate=[2 * 0.5 / 3600], min_crest_level=[0.0])],\n",
")\n",
"model.outlet.add(\n",
" Node(5, Point(0.0, -3.0)),\n",
" [outlet.Static(flow_rate=[0.5 / 3600], min_crest_level=[1.95])],\n",
")\n",
"model.outlet.add(\n",
" Node(7, Point(1.0, -4.0)),\n",
" [outlet.Static(flow_rate=[0.5 / 3600], min_crest_level=[1.45])],\n",
")\n",
"model.outlet.add(\n",
" Node(9, Point(3.0, -4.0)),\n",
" [outlet.Static(flow_rate=[0.5 / 3600], min_crest_level=[0.95])],\n",
")\n",
"model.outlet.add(\n",
" Node(11, Point(4.0, -3.0)),\n",
" [outlet.Static(flow_rate=[0.5 / 3600], min_crest_level=[0.45])],\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"However, to maintain the water level to be at the setpoint, PID controllers are needed. The targets of the PID controllers are set to the setpoints"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Setup the PID control:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pid_control_data = {\n",
" \"listen_node_type\": \"Basin\",\n",
" \"proportional\": [0.05],\n",
" \"integral\": [0.00],\n",
" \"derivative\": [0.0],\n",
"}\n",
"model.pid_control.add(\n",
" Node(3, Point(-1.0, -1.0)),\n",
" [pid_control.Static(listen_node_id=[4], target=[2.0], **pid_control_data)],\n",
")\n",
"model.pid_control.add(\n",
" Node(14, Point(-1.0, -3.0)),\n",
" [pid_control.Static(listen_node_id=[6], target=[1.5], **pid_control_data)],\n",
")\n",
"model.pid_control.add(\n",
" Node(15, Point(1.0, -3.0)),\n",
" [pid_control.Static(listen_node_id=[8], target=[1.0], **pid_control_data)],\n",
")\n",
"model.pid_control.add(\n",
" Node(16, Point(3.0, -3.0)),\n",
" [pid_control.Static(listen_node_id=[10], target=[0.5], **pid_control_data)],\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Setup the edges:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"model.edge.add(model.basin[1], model.outlet[2])\n",
"model.edge.add(model.pid_control[3], model.outlet[2])\n",
"model.edge.add(model.outlet[2], model.basin[4])\n",
"model.edge.add(model.basin[4], model.outlet[5])\n",
"model.edge.add(model.outlet[5], model.basin[6])\n",
"model.edge.add(model.basin[6], model.outlet[7])\n",
"model.edge.add(model.outlet[7], model.basin[8])\n",
"model.edge.add(model.basin[8], model.outlet[9])\n",
"model.edge.add(model.outlet[9], model.basin[10])\n",
"model.edge.add(model.basin[10], model.outlet[11])\n",
"model.edge.add(model.outlet[11], model.basin[12])\n",
"model.edge.add(model.basin[12], model.pump[13])\n",
"model.edge.add(model.pump[13], model.basin[1])\n",
"model.edge.add(model.pid_control[14], model.outlet[5])\n",
"model.edge.add(model.pid_control[15], model.outlet[7])\n",
"model.edge.add(model.pid_control[16], model.outlet[9])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To plot the model"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"model.plot()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Write the model to a TOML file"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# model.write(datadir / \"doc_example/ribasim.toml\")"
]
}
],
"metadata": {
Expand All @@ -1728,7 +2020,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.2"
"version": "3.11.8"
}
},
"nbformat": 4,
Expand Down

0 comments on commit 8c4415f

Please sign in to comment.