Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Reservoir Operating Rules

This example shows an example of a simple network with a reservoir and how it impacts the downstream flows.

First let us generate a random network

net.random_tree(8, seed=1232434)
nodes.reservoir = false
# define two reservoirs
nodes[7,1].reservoir = true

# a function to generate flows
func gen_flow(seed, ind){
	random(seed+ind) * int(random_bool(0.3, seed+ind)) * (1 + int(random_bool(0.1, seed+ind)))
}

N = 40
# define flows at the leaves
nodes.noleaves = true;
leaves.noleaves = false;
leaves$flows = [gen_flow(INDEX, i) for i in 1:N]
nodes(noleaves)$local_flows = [{gen_flow(INDEX,i)/2} for i in 1:N]
nodes<inp>(noleaves)$flows = $local_flows + (inputs$flows -> @sum)

nodes.mean_flow = sr_mean("flows")
nodes.max_flow = $flows ->+ func(a, LAST=0) {max([a, LAST], 0)}

# rudimentary visualization of the network with flow series
nodes(reservoir).visual = {nodeshape="box", nodecolor="black"}
net.cairo.plot("flows", "./output/ro-reservoir-table.svg", normalize=true)

Results:

nm<inv> {sr_min("flows")}

Results:

{
  "3" = 0.0,
  "7" = 0.0,
  "2" = 0.0,
  "4" = 0.0,
  "1" = 0.0,
  "0" = 0.0,
  "5" = 0.08524753881832114,
  "6" = 0.08524753881832114
}

Now let us have a simple rule, that reservoirs operate by providing flows at a constant if it is less than mean flow at that location

nodes[7].low_flow_limit = 0.05
nodes[1].low_flow_limit = 0.1
leaves$c_flows = $flows
nodes<inp>(noleaves)$c_flows = if (reservoir) {
	($local_flows, inputs$c_flows) -> func(l, i) {
		x = l + sum(i);
		# we can use node variables because it is a node context
		max([x], node.low_flow_limit)
	}
} else {
	$local_flows + (inputs$c_flows -> @sum)
}

net.cairo.plot("c_flows", "./output/ro-reservoir-table-2.svg", normalize=true)

Results:


If we look at the minimum flows, we can see how it has maintained the provided minimum flows from the reservoirs onwards.

nm<inv> {[sr_min("flows"), sr_min("c_flows")]}

Results:

{
  "3" = [0.0, 0.0],
  "7" = [0.0, 0.05],
  "2" = [0.0, 0.0],
  "4" = [0.0, 0.05],
  "1" = [0.0, 0.1],
  "0" = [0.0, 0.0],
  "5" = [0.08524753881832114, 0.1244055736719738],
  "6" = [0.08524753881832114, 0.13524753881832113]
}

Of course we did not put any mass balance into the equation. We can’t simply provide the extra flow without taking it from somewhere, so we should make a storage value and save it.

Let’s provide both reservoirs with storage values of 5 unit. And see how it changes when we provide the minimum flow by augmenting the flow using storage.

nodes(reservoir).storage = 5.0

leaves$c_flows = $flows
nodes<inp>(noleaves)$c_flows = if (reservoir) {
	($local_flows, inputs$c_flows) -> func(l, i) {
		x = l + sum(i);
	    if (x < node.low_flow_limit) {
			aug = node.low_flow_limit - x
			node.storage = node.storage - aug
			node.low_flow_limit
		} else {x}
	}
} else {
	$local_flows + (inputs$c_flows -> @sum)
}
nm<inv>.storage

Results:

{
  "3" = <None>,
  "7" = 4.090680944257121,
  "2" = <None>,
  "4" = <None>,
  "1" = 4.902766460431184,
  "0" = <None>,
  "5" = <None>,
  "6" = <None>
}

The storage at the end shows how much of the water was used up to provide the minimum flows.

Now, let’s try to maintain the storage at a certain level by taking excess values from the flows when it has more than the minimum.

nodes(reservoir).storage = 5.0

leaves$c_flows = $flows
nodes<inp>(noleaves)$c_flows = if (reservoir) {
	($local_flows, inputs$c_flows) -> func(l, i) {
		x = l + sum(i);
	    if (x < node.low_flow_limit) {
			aug = node.low_flow_limit - x
			node.storage = node.storage - aug
			node.low_flow_limit
		} else {
			if (node.storage >= 5.0) { x } else {
				excess = x - node.low_flow_limit
				take = excess * 0.8
				node.storage = storage + take
				x - take
			}
		}
	}
} else {
	$local_flows + (inputs$c_flows -> @sum)
}
nm<inv>.storage

Results:

{
  "3" = <None>,
  "7" = 4.87491229927272,
  "2" = <None>,
  "4" = <None>,
  "1" = 5.142603270575256,
  "0" = <None>,
  "5" = <None>,
  "6" = <None>
}

While we have more water in storage here compared to the previous, we still did not recover the storage loss, this is because our minimum flow was arbritary

net.cairo.plot("c_flows", "./output/ro-reservoirs-final.svg", normalize=true, config={delta_y=40.0})

Results:


Let’s look at the mean, minimum and maximum flows at the nodes before and after the reservoir operation rules.

nm<inv> {[sr_mean('flows'), sr_mean('c_flows')]}

Results:

{
  "3" = [0.05201108811624298, 0.05201108811624298],
  "7" = [0.07940064308632276, 0.08252783560450483],
  "2" = [0.054779109940159644, 0.054779109940159644],
  "4" = [0.16521959389081808, 0.16834678640900014],
  "1" = [0.19622891913238585, 0.19579102988618646],
  "0" = [0.06038987573525525, 0.06038987573525525],
  "5" = [0.2868137327352687, 0.2863758434890693],
  "6" = [0.31700867060289634, 0.31657078135669703]
}
nm<inv> {[sr_min('flows'), sr_min('c_flows')]}

Results:

{
  "3" = [0.0, 0.0],
  "7" = [0.0, 0.05],
  "2" = [0.0, 0.0],
  "4" = [0.0, 0.05],
  "1" = [0.0, 0.1],
  "0" = [0.0, 0.0],
  "5" = [0.08524753881832114, 0.1],
  "6" = [0.08524753881832114, 0.13524753881832113]
}
nm<inv> {[sr_max('flows'), sr_max('c_flows')]}

Results:

{
  "3" = [0.26922029023541016, 0.26922029023541016],
  "7" = [0.26922029023541016, 0.26922029023541016],
  "2" = [0.26922029023541016, 0.26922029023541016],
  "4" = [0.4076741577386759, 0.4076741577386759],
  "1" = [0.4076741577386759, 0.4076741577386759],
  "0" = [0.29202287154046747, 0.29202287154046747],
  "5" = [0.676894447974086, 0.676894447974086],
  "6" = [0.676894447974086, 0.676894447974086]
}

Conclusion

In this chapter we generated a sample network, assigned two nodes as reservoirs and defined rules to provide minimum flows as well as algorithms to recover storage volume.

Bonus HEC-DSS

For bonus, let’s save the data into HEC-DSS format.

nodes do dss.save_series($flows, "./output/ro-reservoir-test.dss", r"/{NAME}/Flow/1910Jan01//1Day/F/")
nodes do dss.save_series($flows, "./output/ro-reservoir-test.dss", r"/{NAME}/ModFlow/1910Jan01//1Day/F/")

Results:


You can download the dss and test it here