Reservoirs store water between timesteps. They are nodes in the water network, and receive upstream flows and precipitation, and provide withdrawals to canals. However, their outflows are determined by an optimization.

Selecting Reservoirs

The reservoirs that should be modeled as storing water across period, rather than acting like stream nodes, differ depending on the timestep. The function that provides a list of reservoirs is getreservoirs(config).


In [1]:
include("../src/lib/readconfig.jl")
include("../src/lib/reservoirs.jl")

config = readconfig("../configs/standard.yml")


Out[1]:
Dict{Any,Any} with 11 entries:
  "urbandemand-index"     => "FIPS_county"
  "filterstate"           => nothing
  "urbandemand-transform" => "repcap"
  "startweather"          => 612
  "timestep"              => 6
  "urbandemand-column"    => "per_capita"
  "startmonth"            => "10/2000"
  "rescap"                => "full"
  "urbandemand-path"      => "demand/urbandemand_May11.csv"
  "netset"                => "usa"
  "endmonth"              => "9/2010"

In [2]:
@doc getreservoirs


Out[2]:

Return a DataFrame containing collection and colid fields matching those in the Water Network.

Any additional columns can be provided, to be used by other components.

Rows may be excluded, to represent that a given reservoir should be modeled as a stream at the specified timestep (in months).


In [3]:
getreservoirs(config)


Out[3]:
collectioncolidarealatlonelevMAXCAP
1reservoir1NA48.8733-122.688NA6.907488e6
2reservoir2145.0394448.7583-122.422NA9.497796e7
3reservoir3556.8478548.6583-121.687NA2.185529203e8
4reservoir4556.8478548.6483-121.69NA3.89903028e8
5reservoir52587.4000148.7317-121.067NA2.014766232e9
6reservoir62913.7387548.7133-121.13NA1.12986768e8
7reservoir73001.7984148.6983-121.207NA1.32969144e7
8reservoir8769.2270348.5483-121.74NA1.991700156e8
9reservoir9NA48.9317-119.418NA4.93392e7
10reservoir10797.7169248.095-123.555NA9.991188e6
11reservoir11NA48.8116-119.533NA1.73057244e7
12reservoir12634.5475548.0017-123.598NA4.8229068e7
13reservoir13NA48.9866-117.347NA1.5048456e8
14reservoir14NA48.5583-119.745NA2.13145344e7
15reservoir15NA48.4683-120.25NA7.0493382e6
16reservoir16313.3887948.5416-119.747NA2.0475768e7
17reservoir17132.60748848.84-117.288NA3.849567732e7
18reservoir18NA48.78-117.41NA1.23348e8
19reservoir19NA48.3616-119.697NA8.32599e6
20reservoir20176.1193247.975-121.687NA5.920704e7
21reservoir21NA47.945-121.83NA1.6281936e7
22reservoir22NA47.665-122.397NA5.6493384e8
23reservoir23106.1895947.385-123.605NA9.374448e7
24reservoir2433.6698748.2217-118.893NA7.894272e6
25reservoir25243.4590647.4216-123.222NA5.8960344e8
26reservoir26NA47.3983-123.2NA1.079295e7
27reservoir27NA47.6933-121.688NA8.2889856e7
28reservoir2895.8296348.275-118.375NA2.3374446e7
29reservoir291481.4742848.49-116.902NA1.0114536e8
30reservoir30NA47.925-120.177NA1.167365472e7

Optimization example

To understand the functioning of reservoirs, consider how they work in a very simple three gauge example. The three guages example has three counties, with a river running through them. The middle county has a reservoir. Water is supplied only upstream (in counties 1 and 2) and consumed only downstream (in counties 2 and 3).

Note that the reservoir appears to be outside of the river system. While it is spatially synonymous with the middle gauge, reservoirs get all of their inflows from "captured" water. Any water that is not captured is allowed to run through the reservoir just like a stream. Reservoir captures can also be negative, providing releases.

The optimize-surface.jl script models the constraints to satisfy surface water demands, using reservoirs to store water between periods.

In the three counties example, the first period has more rainfall than the second period, so that storage is optimal.


In [25]:
config = readconfig("../configs/dummy3.yml")
include("../src/optimize-surface.jl")


Loading from saved region network...
Loading from saved water network...
WARNING: replacing module _mimi_implementation_WaterDemand
WARNING: replacing module _mimi_implementation_WaterNetwork
Optimize a model with 33 rows, 30 columns and 78 nonzeros
Coefficient statistics:
  Matrix range    [1e+00, 1e+00]
  Objective range [1e+03, 1e+03]
  Bounds range    [0e+00, 0e+00]
  RHS range       [8e-01, 7e+00]
Presolve removed 21 rows and 14 columns
Presolve time: 0.01s
Presolved: 12 rows, 16 columns, 33 nonzeros

Iteration    Objective       Primal Inf.    Dual Inf.      Time
       0    9.9800000e+02   1.646210e+01   0.000000e+00      0s
      11    4.2100000e+03   0.000000e+00   0.000000e+00      0s

Solved in 11 iterations and 0.01 seconds
Optimal objective  4.210000000e+03
  5.013757 seconds (3.15 M allocations: 125.856 MB, 4.76% gc time)
WARNING: replacing module _mimi_implementation_Allocation
WARNING: replacing module _mimi_implementation_ReturnFlows
WARNING: replacing module _mimi_implementation_Reservoir
WARNING: replacing module _mimi_implementation_Aquifer
waterfromsupersource
[0.0,0.0,0.20000000000000018,0.0,1.0050000000000001,2.0,0.0,1.0049999999999994,0.0]
Sum: 4.209999999999999
withdrawals
[0.0,2.0,1.7999999999999998,0.0,0.9949999999999999,0.0,0.0,0.9950000000000006,2.0]
Sum: 7.790000000000001
returns
All zero.
captures
[-0.8,0.0050000000000001155,0.0049999999999999]
Sum: -0.79
Out[25]:
24

First, look at the runoff values, with rows for the three gauges and columns for the two time periods.


In [26]:
runoff


Out[26]:
3x3 Array{Float64,2}:
 2.0  1.0  2.2
 1.0  0.0  0.7
 0.0  0.0  0.1

The requirements are for one unit of water each period for each of the lower two gauges.


In [28]:
reshape(constraintoffset_allocation_recordedbalance(m).f, 3, 3)


Out[28]:
3x3 Array{Float64,2}:
 0.0  0.0  0.0
 2.0  2.0  2.0
 2.0  2.0  2.0

The order of the parameters in the LP problem is:


In [29]:
parameters


Out[29]:
4-element Array{Symbol,1}:
 :waterfromsupersource
 :withdrawals         
 :returns             
 :captures            

And the order of the constraint variables is:


In [30]:
constraints


Out[30]:
5-element Array{Symbol,1}:
 :outflows     
 :balance      
 :returnbalance
 :storagemin   
 :storagemax   

Consider the constraint matrix one parameter at a time. The first parameter is the water drawn from the supersource, which only affects the second constraint, :balance, the difference between water demand and water supply. The objective function is such that supersource withdrawals are avoided.


In [33]:
full(house.A)[:, 1:9]


Out[33]:
33x9 Array{Float64,2}:
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
 -1.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0  -1.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0  -1.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0  -1.0  0.0  0.0  0.0  0.0  0.0
  ⋮                           ⋮                 
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0

Normal withdrawals affect :balance as well, but they also affect the level of streamflow, which is constrained to be above 0. See the top six rows in two blocks, in the upper left and lower right, corresponding to the two periods. The gauges are ordered, by chance, such that the most upstream gauge is second, so a withdrawal from that gauge causes all three gauges to inch closer to empty.


In [34]:
full(house.A)[:, 10:18]


Out[34]:
33x9 Array{Float64,2}:
  1.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  1.0   1.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  1.0   1.0   1.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   1.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   1.0  1.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   1.0  1.0  1.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  1.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  1.0  1.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  1.0  1.0  1.0
 -1.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0  -1.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0  -1.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0  -1.0  0.0  0.0  0.0  0.0  0.0
  ⋮                           ⋮                 
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0
  0.0   0.0   0.0   0.0  0.0  0.0  0.0  0.0  0.0

We'll ignore the :returns constraint. The last is the reservoir in the two periods. Reservoir captures have the same affect on streamflows, at least downstream. They have no direct effect on :balance. Finally, they affect the last two constraints on the upper and lower bounds of the reservoir, where capture (or release) in period 1 affects both periods, while capture (or release) in period 2 only affects one.


In [35]:
full(house.A)[:, end-1:end]


Out[35]:
33x2 Array{Float64,2}:
  0.0    0.0
  0.0    0.0
  0.0    0.0
  0.0    0.0
  0.0    0.0
  1.0    0.0
  0.0    0.0
  0.0    0.0
  0.0    1.0
  0.0    0.0
  0.0    0.0
  0.0    0.0
  0.0    0.0
  ⋮         
  0.0    0.0
  0.0    0.0
  0.0    0.0
  0.0    0.0
  0.0    0.0
  0.0    0.0
  0.0    0.0
 -1.0    0.0
 -0.99  -1.0
  0.0    0.0
  1.0    0.0
  0.99   1.0

The result is that it is optimal to store one unit of water and release it in the second period.


In [36]:
constraining(house, sol.sol)


Ignore:

0.25
0.5
0.75
1.0
Out[36]:
solutioncomponentparameterabovefailbelowfail
10.0Allocationwaterfromsupersourcebalance.1
20.0Allocationwaterfromsupersourcebalance.2
30.20000000000000018Allocationwaterfromsupersourcebalance.3
40.0Allocationwaterfromsupersourcebalance.4
51.0050000000000001Allocationwaterfromsupersourcebalance.5
62.0Allocationwaterfromsupersourcebalance.6
70.0Allocationwaterfromsupersourcebalance.7
81.0049999999999994Allocationwaterfromsupersourcebalance.8
90.0Allocationwaterfromsupersourcebalance.9
100.0Allocationwithdrawalsoutflows.3balance.1
112.0Allocationwithdrawalsoutflows.3balance.2
121.7999999999999998Allocationwithdrawalsoutflows.3balance.3
130.0Allocationwithdrawalsoutflows.6balance.4
140.9949999999999999Allocationwithdrawalsoutflows.6balance.5
150.0Allocationwithdrawalsoutflows.6balance.6
160.0Allocationwithdrawalsoutflows.9balance.7
170.9950000000000006Allocationwithdrawalsoutflows.9balance.8
182.0Allocationwithdrawalsoutflows.9balance.9
190.0Allocationreturnsreturnbalance.1outflows.3
200.0Allocationreturnsreturnbalance.2outflows.3
210.0Allocationreturnsreturnbalance.3outflows.3
220.0Allocationreturnsreturnbalance.4outflows.6
230.0Allocationreturnsreturnbalance.5outflows.6
240.0Allocationreturnsreturnbalance.6outflows.6
250.0Allocationreturnsreturnbalance.7outflows.9
260.0Allocationreturnsreturnbalance.8outflows.9
270.0Allocationreturnsreturnbalance.9outflows.9
28-0.8Reservoircapturesoutflows.3storagemin.1, storagemin.2, storagemin.3
290.0050000000000001155Reservoircapturesoutflows.6storagemin.2, storagemin.3
300.0049999999999999Reservoircapturesoutflows.9storagemin.3

Future work:

  • The optimization does not include evaporation, since this would make the numbers for this toy example inconvenient. Evaporation needs to be added to both the optimization and the example.

Reservoir data

Maximum Reservoir Storage (or Capacity)

Received Maximum capacity for 2641 reservoirs from the US Army Corps of Engineers. However, in the allreservoirs.csv file in the folder awash/data/reservoirs/,there are 2671 reservoirs in total.

The allreservoir.csv database is being used in the reservoir optimization (reservoir.jl). The remaining 30 reservoirs are from the USGS website.

Since we are unable to currently find the maximum capacity for those 30 reservoirs (reservoir codes mentioned below), for this example, the maximum capacity is taken as 0.

usgsres 3170500 usgsres 5113750 usgsres 6886900 usgsres 7233550 usgsres 7332610 usgsres 7342495 usgsres 7343460 usgsres 7344488 usgsres 8025350 usgsres 8051100 usgsres 8064550 usgsres 8072500 usgsres 8073000 usgsres 8079700 usgsres 8092500 usgsres 8095550 usgsres 8118000 usgsres 8123000 usgsres 8123755 usgsres 8123950 usgsres 8131200 usgsres 8134500 usgsres 8136600 usgsres 8180010 usgsres 8202800 usgsres 9125800 usgsres 9129550 usgsres 9143600 usgsres 9147022 usgsres 15225990

Minimum reservoir storage

The minimum reservoir storage is assumed to be 10% of the maximum reservoir storage, i.e.

storagecapacitymin = 0.1 * storagecapacitymax

This data needs improvement -- we need to find the minimum storage for each reservoir rather than taking one assumptions for all reservoirs

Initial Storage

The initial storage is assumed to be the average of minimum and maximum capacity, i.e.

Initial storage = (storagecapacitymax-storagecapacitymin)/2

This data needs improvement -- we need to find the initial storage of each reservoir

Evaporation

Evaporation is currently assumed to be 1% of the storage capacity, i.e.

Evaporation = 0.01 * storagecapacitymax

The data needs improvement -- we need to find a linear relationship between evaporation and reservoir storage.


In [ ]: