Skip to content

Commit 506b3f1

Browse files
jameshalgrenjhrehanoaajdhondia-noaa
authored
Adding level-pool reservoir simulation capability to v01 (#90)
* add folder to start reservoir work * set debuglevel/verbosity to maximum for test * Add reservoir demo script * attribute module * Add initial dummy reservoir calculation * reorganize functional inputs * created globals and reran df from utilities * minor changes to flow/resevoir code * moved waterbodies key values to supernetwork_data * Sequential Network Execution Works for Parallel, no waterbodies; serial no waterbodies; serial with waterbodies. NOT YET WORKING for parallel with waterbodies (which may indicate deeper issue...) * fix referencing in flowveldepth -- full execution now works * remove never-used file Reservoir.f90 * move reservoir parameters to nested dict * black nhd_reach_utilities * split qlateral Also moved flowveldepth into a purely functional call * revert nhd_network_utilities_v02 (it should not have changed...) * Update nhd_network_utilities_v02.py * minor updates * blackened * blackened * force single precision in reservoir calculation * added route and replace function for testing of Mainstems_CONUS input DF * Merge Jdhondia branch + many fixes to parallelization, etc. More info: Added qlateral-time-step subdivision capability + "--qlateral-time-step", + "--qts_subdivisions", Detect and handle WSL compile (WSL solution appears to lose fidelity if compiled with optimizations) fixing logic for network head segments downstream of other networks add cumulative qlat and cumulative total volume in flowveldepth and modify write functions to handle gather Qlat from all segments of the waterbody Outstanding issues: Head segments is never hit for quc Tuple (False,) is unexplained * remove tuple-wrapped False * Use ts for flow backreference using flowarr[-1] works within the loop but obtains the wrong value for the arrays references upstream tributaries -- the upstreams have completed calculation and will have a complete time series to choose from. * Add more usage examples * Use empty dict for flowveldepth_connect * Reduce volume of functional return to avoid memory error on process pickle https://bugs.python.org/issue17560 Reviewing that link suggests that we may be following an anti-pattern in pusing *pushing so much data into a multi-process. Hence, we are looking at the v02 method using threads... * Add TODO about network sorting * Add RnR test * fix rnr name * fix return value to have key * close pool only if parallel also blackened file * make networks global * use static tuple inside temporary ordering list * added sort to ordered_networks * updated code for writing nc file for waterbodies * added custom input arg, added init_waterbody_states and q_initial, changed json test file columns * addes q_initial_states * minor changes and added functions to utilities file * separated functions to remove cascading effect * sort waterbodies_df and correct search method This change cut execution time in half or better * renamed folder to file * working custom json input and functions * renamed inputs and removed for loop * added waterbody_init and renamed a few things * formatted with black * added Waterbody_ID_crosswalks to def get_reservoir_restart_from_wrf_hydro( * simplify references for short-time-step assumption also fixed qlaterals. * make placeholders to read warm/initial states * update download link to latest version of route-link file * symlink varPrecision * blackend * reorganize arguments * add default output text and nc folders * make channel initial states work for RnR * set default initial state dataframe (with all zeros) * cleanup and blacken * cleanup and blacken * Add Waterbody Initial State Parsing * Blackend; add variable for xs in waterbody crosswalk * use initial states in actual calculation * Set custom file to read Pocono LakeParm * add custom json example usage * Add Pocono1 Test case * finalize water body capability * blackend * Move All Inputs to Json * add empty dict for empty custom input frames * fix levelpool height assignment Anyuse prior to this fix is completely wrong; the input variable was being dropped without any assignment * Cleanup reservoir test script and add docstring * blackened * remove old method * write qlateral to flowveldepth to allow time-step adjustment * Add sort networks argument Also clean up and add more useful comments * read waterbody parameters from separate utility function * BUGFIX accumulate qup in initial states from all upstreams The calculation previously was only re-assigning with "=" instead of "+=" * add timing statements for full program and main loop * multiply by dt for correct qlat accumulation * lowercase for uname.release Co-authored-by: Jacob Hreha <jacob.hreha@noaa.gov> Co-authored-by: jdhondia <juzer.dhondia@noaa.gov> Former-commit-id: ee21efdbc669631a267a356f08769a8e8ce1c870
1 parent 5b95e96 commit 506b3f1

39 files changed

+1821
-273
lines changed
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
! This module derived from the NWM repo found here:
2+
! https://github.com/NCAR/wrf_hydro_nwm_public/blob
3+
! /master/trunk/NDHMS/Routing/Reservoirs/Level_Pool/module_levelpool.F
4+
5+
! This module defines and instantiates objects
6+
! for a level pool type reservoir. The level
7+
! pool reservoir type inherits input and
8+
! output types from the reservoir base
9+
! module and calls instantiation of these into
10+
! sub-objects. The level pool reservoir type
11+
! also points to types for level pool properties
12+
! and state and calls instantiation of these into
13+
! sub-objects. This module also contains the
14+
! subroutine to run level pool reservoir that is
15+
! derived from the reservoir base type interface
16+
! to run reservoir. Running level pool will
17+
! then call the LEVELPOOL_PHYSICS subroutine, which
18+
! processes the given inputs, properties, and
19+
! state for a particular level pool reservoir and
20+
! returns the output/outflow.
21+
22+
module module_levelpool
23+
24+
use precis
25+
implicit none
26+
27+
contains
28+
29+
! ------------------------------------------------
30+
! SUBROUTINE LEVELPOOL
31+
! ------------------------------------------------
32+
33+
subroutine levelpool_physics(dt,qi0,qi1,ql,ar,we,maxh,wc,wl,dl,oe,oc,oa,H0,H1,qo1)
34+
35+
!! ---------------------------- argument variables
36+
!! All elevations should be relative to a common base (often belev(k))
37+
38+
! integer, intent(IN) :: ln ! lake number
39+
real(prec), intent(IN) :: dt ! routing period [s]
40+
real(prec), intent(IN) :: qi0 ! inflow at previous timestep (cms)
41+
real(prec), intent(IN) :: qi1 ! inflow at current timestep (cms)
42+
real(prec), intent(IN) :: ql ! lateral inflow
43+
real(prec), intent(IN) :: ar ! area of reservoir (km^2)
44+
real(prec), intent(IN) :: we ! bottom of weir elevation
45+
real(prec), intent(IN) :: wc ! weir coeff.
46+
real(prec), intent(IN) :: wl ! weir length (m)
47+
real(prec), intent(IN) :: dl ! dam length(m)
48+
real(prec), intent(IN) :: oe ! orifice elevation
49+
real(prec), intent(IN) :: oc ! orifice coeff.
50+
real(prec), intent(IN) :: oa ! orifice area (m^2)
51+
real(prec), intent(IN) :: maxh ! max depth of reservoir before overtop (m)
52+
real(prec), intent(IN) :: H0 ! water elevation height (m)
53+
real(prec), intent(OUT) :: H1 ! water elevation height (m)
54+
real(prec), intent(OUT) :: qo1 ! outflow at current timestep
55+
56+
!!DJG Add lake option switch here...move up to namelist in future versions...
57+
integer :: LAKE_OPT ! Lake model option (move to namelist later)
58+
real(prec) :: H ! Temporary assign of incoming lake el. (m)
59+
60+
!! ---------------------------- local variables
61+
real(prec) :: sap ! local surface area values
62+
real(prec) :: discharge ! storage discharge m^3/s
63+
real(prec) :: tmp1, tmp2
64+
real(prec) :: dh, dh1, dh2, dh3 ! Depth in weir, and height function for 3 order RK
65+
real(prec) :: It, Itdt_3, Itdt_2_3 ! inflow hydrographs
66+
real(prec) :: maxWeirDepth !maximum capacity of weir
67+
real(prec) :: exp32, quot23, zero, gravity ! internal variables
68+
!real :: hdiff_vol, qdiff_vol ! water balance check variables
69+
!! ---------------------------- subroutine body: from chow, mad mays. pg. 252
70+
!! -- determine from inflow hydrograph
71+
72+
73+
!!DJG Set hardwire for LAKE_OPT...move specification of this to namelist in
74+
!future versions...
75+
LAKE_OPT = 2
76+
exp32 = 3._prec/2._prec
77+
quot23 = 0.667_prec
78+
zero = 0.0_prec
79+
gravity = 9.81_prec
80+
!hdiff_vol = zero
81+
!qdiff_vol = zero
82+
83+
!!DJG IF-block for lake model option 1 - outflow=inflow, 2 - Chow et al level
84+
!pool, .....
85+
if (LAKE_OPT == 1) then ! If-block for simple pass through scheme....
86+
87+
qo1 = qi1 ! Set outflow equal to inflow at current time
88+
H = H0 ! Set new lake water elevation to incoming lake el.
89+
90+
else if (LAKE_OPT == 2) then ! If-block for Chow et al level pool scheme
91+
92+
H = H0
93+
It = qi0
94+
Itdt_3 = qi0 + ((qi1 + ql - qi0) * 0.33_prec)
95+
Itdt_2_3 = qi0 + ((qi1 + ql - qi0) * 0.67_prec)
96+
maxWeirDepth = maxh - we
97+
98+
!assume vertically walled reservoir
99+
!remove this when moving to a variable head area volume
100+
sap = ar * 1.0E6_prec
101+
102+
!-- determine Q(dh) from elevation-discharge relationship
103+
!-- and dh1
104+
dh = H - we
105+
if (dh > maxWeirDepth) then
106+
dh = maxWeirDepth
107+
endif
108+
109+
tmp1 = oc * oa * sqrt(2._prec * gravity * ( H - oe )) !orifice at capacity
110+
tmp2 = wc * wl * (dh ** (exp32)) !weir flows at capacity
111+
112+
!determine the discharge based on current height
113+
if(H > maxh) then
114+
discharge = tmp1 + tmp2 + (wc* (wl*dl) * (H-maxh)**(exp32)) !overtop
115+
else if (dh > zero) then !! orifice and weir discharge
116+
discharge = tmp1 + tmp2
117+
else if ( H > oe ) then !! only orifice flow
118+
discharge = oc * oa * sqrt(2._prec * gravity * ( H - oe ) )
119+
else
120+
discharge = zero !in the dead pool
121+
endif
122+
123+
if (sap > zero) then
124+
dh1 = ((It - discharge)/sap)*dt
125+
else
126+
dh1 = zero
127+
endif
128+
129+
!-- determine Q(H + dh1/3) from elevation-discharge relationship
130+
!-- dh2
131+
dh = (H+dh1/3._prec) - we
132+
if (dh > maxWeirDepth) then
133+
dh = maxWeirDepth
134+
endif
135+
136+
tmp1 = oc * oa * sqrt(2._prec * gravity * ( (H+dh1/3._prec) - oe ) )
137+
tmp2 = wc * wl * (dh ** (exp32))
138+
139+
!determine the discharge based on current height
140+
if(H > maxh) then
141+
discharge = tmp1 + tmp2 + (wc* (wl*dl) * (H-maxh)**(exp32)) !overtop
142+
else if (dh > zero) then !! orifice and weir discharge
143+
discharge = tmp1 + tmp2
144+
else if ( (H+dh1/3._prec) > oe ) then !! only orifice flow,not full
145+
discharge = oc * oa * sqrt(2._prec * gravity * ( (H+dh1/3._prec) - oe ) )
146+
else
147+
discharge = zero
148+
endif
149+
150+
151+
if (sap > zero) then
152+
dh2 = ((Itdt_3 - discharge)/sap)*dt
153+
else
154+
dh2 = zero
155+
endif
156+
157+
!-- determine Q(H + 2/3 dh2) from elevation-discharge relationship
158+
!-- dh3
159+
dh = (H + (quot23*dh2)) - we
160+
if (dh > maxWeirDepth) then
161+
dh = maxWeirDepth
162+
endif
163+
164+
tmp1 = oc * oa * sqrt(2._prec * gravity * ( (H+dh2*quot23) - oe ) )
165+
tmp2 = wc * wl * (dh ** (exp32))
166+
167+
!determine the discharge based on current height
168+
if(H > maxh) then ! overtop condition, not good!
169+
discharge = tmp1 + tmp2 + (wc* (wl*dl) * (H-maxh)**(exp32)) !overtop
170+
else if (dh > zero) then !! orifice and weir discharge
171+
discharge = tmp1 + tmp2
172+
else if ( (H+dh2*quot23) > oe ) then !! only orifice flow,not full
173+
discharge = oc * oa * sqrt(2._prec * gravity * ( (H+dh2*quot23) - oe ) )
174+
else
175+
discharge = zero
176+
endif
177+
178+
if (sap > zero) then
179+
dh3 = ((Itdt_2_3 - discharge)/sap)*dt
180+
else
181+
dh3 = zero
182+
endif
183+
184+
!-- determine dh and H
185+
dh = (dh1/4._prec) + (0.75_prec*dh3)
186+
H = H + dh
187+
188+
!-- compute final discharge
189+
dh = H - we
190+
if (dh > maxWeirDepth) then
191+
dh = maxWeirDepth
192+
endif
193+
194+
tmp1 = oc * oa * sqrt(2._prec * gravity * ( H - oe ) )
195+
tmp2 = wc * wl * (dh ** (exp32))
196+
197+
!determine the discharge based on current height
198+
if(H > maxh) then ! overtop condition, not good!
199+
discharge = tmp1 + tmp2 + (wc* (wl*dl) * (H-maxh)**(exp32)) !overtop
200+
else if (dh > zero) then !! orifice and overtop discharge
201+
discharge = tmp1 + tmp2
202+
else if ( H > oe ) then !! only orifice flow,not full
203+
discharge = oc * oa * sqrt(2._prec * gravity * ( H - oe ) )
204+
else
205+
discharge = zero
206+
endif
207+
208+
H1 = H
209+
qo1 = discharge ! return the flow rate from reservoir
210+
211+
!#ifdef HYDRO_D
212+
!#ifndef NCEP_WCOSS
213+
! ! Water balance check
214+
! qdiff_vol = (qi1+ql-qo1)*dt !m3
215+
! hdiff_vol = (H-H0)*sap !m3
216+
!22 format(f8.4,2x,f8.4,2x,f8.4,2x,f8.4,2x,f8.4,2x,f6.0,2x,f20.1,2x,f20.1)
217+
! open (unit=67, &
218+
! file='lake_massbalance_out.txt', status='unknown',position='append')
219+
! write(67,22) H0, H, qi1, ql, qo1, dt, qdiff_vol, hdiff_vol
220+
! close(67)
221+
!#endif
222+
!#endif
223+
224+
!23 format('botof H dh orf wr Q',f8.4,2x,f8.4,2x,f8.3,2x,f8.3,2x,f8.2)
225+
!24 format('ofonl H dh sap Q ',f8.4,2x,f8.4,2x,f8.0,2x,f8.2)
226+
227+
228+
else ! ELSE for LAKE_OPT....
229+
endif ! ENDIF for LAKE_OPT....
230+
231+
return
232+
233+
! ----------------------------------------------------------------
234+
end subroutine levelpool_physics
235+
! ----------------------------------------------------------------
236+
237+
end module module_levelpool
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import traceback
2+
3+
debuglevel = 0
4+
COMPILE = True
5+
if COMPILE:
6+
try:
7+
import subprocess
8+
9+
fortran_compile_call = []
10+
fortran_compile_call.append(r"f2py3")
11+
fortran_compile_call.append(r"-c")
12+
fortran_compile_call.append(r"varPrecision.f90")
13+
fortran_compile_call.append(r"module_levelpool.f90")
14+
fortran_compile_call.append(r"-m")
15+
fortran_compile_call.append(r"levelpools")
16+
# fortran_compile_call.append(r"--opt='-fdefault-real-8'")
17+
18+
if debuglevel <= -1:
19+
print(fortran_compile_call)
20+
if debuglevel <= -2:
21+
subprocess.run(fortran_compile_call)
22+
else:
23+
subprocess.run(
24+
fortran_compile_call,
25+
stdout=subprocess.DEVNULL,
26+
stderr=subprocess.DEVNULL,
27+
)
28+
from levelpools import *
29+
30+
# import mc_sseg_stime as muskingcunge_module
31+
except Exception as e:
32+
print(e)
33+
if debuglevel <= -1:
34+
traceback.print_exc()
35+
else:
36+
from levelpools import *
37+
38+
# import mc_sseg_stime as muskingcunge_module
39+
40+
41+
def reservoirs_calc(
42+
dt=None,
43+
qi0=None,
44+
qi1=None,
45+
ql=None,
46+
ar=None,
47+
we=None,
48+
maxh=None,
49+
wc=None,
50+
wl=None,
51+
dl=None,
52+
oe=None,
53+
oc=None,
54+
oa=None,
55+
h0=None,
56+
):
57+
"""This test function demonstrates a simple connection between
58+
the module_levelpool routing function and python via f2py
59+
inputs are described by the fortran header:
60+
real(prec), intent(IN) :: qi0 ! inflow at previous timestep (cms)
61+
real(prec), intent(IN) :: qi1 ! inflow at current timestep (cms)
62+
real(prec), intent(IN) :: ql ! lateral inflow
63+
real(prec), intent(IN) :: ar ! area of reservoir (km^2)
64+
real(prec), intent(IN) :: we ! bottom of weir elevation
65+
real(prec), intent(IN) :: wc ! weir coeff.
66+
real(prec), intent(IN) :: wl ! weir length (m)
67+
real(prec), intent(IN) :: dl ! dam length(m)
68+
real(prec), intent(IN) :: oe ! orifice elevation
69+
real(prec), intent(IN) :: oc ! orifice coeff.
70+
real(prec), intent(IN) :: oa ! orifice area (m^2)
71+
real(prec), intent(IN) :: maxh ! max depth of reservoir before overtop (m)
72+
real(prec), intent(IN) :: H0 ! water elevation height (m)
73+
74+
75+
outputs are, in order the new water depth and new outflow.
76+
real(prec), intent(OUT) :: H1 ! water elevation height (m)
77+
real(prec), intent(OUT) :: qo1 ! outflow at current timestep
78+
"""
79+
80+
# call Fortran routine
81+
return module_levelpool.levelpool_physics(
82+
dt, qi0, qi1, ql, ar, we, maxh, wc, wl, dl, oe, oc, oa, h0,
83+
)
84+
# return hc, qdc
85+
86+
87+
def main():
88+
89+
# dummy execution of reservoirs_calc()
90+
91+
dt = 300
92+
qi0 = 500
93+
qi1 = 600
94+
ql = 400
95+
ar = 22
96+
we = 20
97+
maxh = 30
98+
wc = 1.6
99+
wl = 20
100+
dl = 200
101+
oe = 1
102+
oc = 0.6
103+
oa = 0.2
104+
h0 = 22
105+
106+
print(reservoirs_calc(dt, qi0, qi1, ql, ar, we, maxh, wc, wl, dl, oe, oc, oa, h0,))
107+
108+
109+
if __name__ == "__main__":
110+
main()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../MC_singleSeg_singleTS/varPrecision.f90

0 commit comments

Comments
 (0)