Skip to content

Commit 55672ea

Browse files
committed
feat(utils): move cross section utils from modflow-devtools
1 parent bd51afa commit 55672ea

File tree

1 file changed

+276
-0
lines changed

1 file changed

+276
-0
lines changed

flopy/utils/crosssection.py

+276
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
import numpy as np
2+
3+
# power for Manning's hydraulic radius term
4+
_mpow = 2.0 / 3.0
5+
6+
7+
def calculate_rectchan_mannings_discharge(
8+
conversion_factor, roughness, slope, width, depth
9+
):
10+
"""
11+
Calculate Manning's discharge for a rectangular channel.
12+
13+
"""
14+
area = width * depth
15+
return conversion_factor * area * depth**_mpow * slope**0.5 / roughness
16+
17+
18+
# n-point cross-section functions
19+
def get_wetted_station(
20+
x0,
21+
x1,
22+
h0,
23+
h1,
24+
depth,
25+
):
26+
"""Get the wetted length in the x-direction"""
27+
# -- calculate the minimum and maximum depth
28+
hmin = min(h0, h1)
29+
hmax = max(h0, h1)
30+
31+
# -- if depth is less than or equal to the minimum value the
32+
# station length (xlen) is zero
33+
if depth <= hmin:
34+
x1 = x0
35+
# -- if depth is between hmin and hmax, station length is less
36+
# than h1 - h0
37+
elif depth < hmax:
38+
xlen = x1 - x0
39+
dlen = h1 - h0
40+
if abs(dlen) > 0.0:
41+
slope = xlen / dlen
42+
else:
43+
slope = 0.0
44+
if h0 > h1:
45+
dx = (depth - h1) * slope
46+
xt = x1 + dx
47+
xt0 = xt
48+
xt1 = x1
49+
else:
50+
dx = (depth - h0) * slope
51+
xt = x0 + dx
52+
xt0 = x0
53+
xt1 = xt
54+
x0 = xt0
55+
x1 = xt1
56+
return x0, x1
57+
58+
59+
def get_wetted_perimeter(
60+
x0,
61+
x1,
62+
h0,
63+
h1,
64+
depth,
65+
):
66+
# -- calculate the minimum and maximum depth
67+
hmin = min(h0, h1)
68+
hmax = max(h0, h1)
69+
70+
# -- calculate the wetted perimeter for the segment
71+
xlen = x1 - x0
72+
if xlen > 0.0:
73+
if depth > hmax:
74+
dlen = hmax - hmin
75+
else:
76+
dlen = depth - hmin
77+
else:
78+
if depth > hmin:
79+
dlen = min(depth, hmax) - hmin
80+
else:
81+
dlen = 0.0
82+
return np.sqrt(xlen**2.0 + dlen**2.0)
83+
84+
85+
def get_wetted_area(x0, x1, h0, h1, depth):
86+
# -- calculate the minimum and maximum depth
87+
hmin = min(h0, h1)
88+
hmax = max(h0, h1)
89+
90+
# -- calculate the wetted area for the segment
91+
xlen = x1 - x0
92+
area = 0.0
93+
if xlen > 0.0:
94+
# -- add the area above hmax
95+
if depth > hmax:
96+
area = xlen * (depth - hmax)
97+
# -- add the area below zmax
98+
if hmax != hmin and depth > hmin:
99+
area += 0.5 * (depth - hmin)
100+
return area
101+
102+
103+
def wetted_area(
104+
x,
105+
h,
106+
depth,
107+
verbose=False,
108+
):
109+
area = 0.0
110+
if x.shape[0] == 1:
111+
area = x[0] * depth
112+
else:
113+
for idx in range(0, x.shape[0] - 1):
114+
x0, x1 = x[idx], x[idx + 1]
115+
h0, h1 = h[idx], h[idx + 1]
116+
117+
# get station data
118+
x0, x1 = get_wetted_station(x0, x1, h0, h1, depth)
119+
120+
# get wetted area
121+
a = get_wetted_area(x0, x1, h0, h1, depth)
122+
area += a
123+
124+
# write to screen
125+
if verbose:
126+
print(
127+
f"{idx}->{idx + 1} ({x0},{x1}) - "
128+
f"perimeter={x1 - x0} - area={a}"
129+
)
130+
131+
return area
132+
133+
134+
def wetted_perimeter(
135+
x,
136+
h,
137+
depth,
138+
verbose=False,
139+
):
140+
perimeter = 0.0
141+
if x.shape[0] == 1:
142+
perimeter = x[0]
143+
else:
144+
for idx in range(0, x.shape[0] - 1):
145+
x0, x1 = x[idx], x[idx + 1]
146+
h0, h1 = h[idx], h[idx + 1]
147+
148+
# get station data
149+
x0, x1 = get_wetted_station(x0, x1, h0, h1, depth)
150+
151+
# get wetted perimeter
152+
perimeter += get_wetted_perimeter(x0, x1, h0, h1, depth)
153+
154+
# write to screen
155+
if verbose:
156+
print(f"{idx}->{idx + 1} ({x0},{x1}) - perimeter={x1 - x0}")
157+
158+
return perimeter
159+
160+
161+
def manningsq(
162+
x,
163+
h,
164+
depth,
165+
roughness=0.01,
166+
slope=0.001,
167+
conv=1.0,
168+
):
169+
if isinstance(roughness, float):
170+
roughness = np.ones(x.shape, dtype=float) * roughness
171+
if x.shape[0] > 1:
172+
q = 0.0
173+
for i0 in range(x.shape[0] - 1):
174+
i1 = i0 + 1
175+
perimeter = get_wetted_perimeter(x[i0], x[i1], h[i0], h[i1], depth)
176+
area = get_wetted_area(x[i0], x[i1], h[i0], h[i1], depth)
177+
if perimeter > 0.0:
178+
radius = area / perimeter
179+
q += (
180+
conv * area * radius ** _mpow * slope ** 0.5 / roughness[i0]
181+
)
182+
else:
183+
perimeter = wetted_perimeter(x, h, depth)
184+
area = wetted_area(x, h, depth)
185+
radius = 0.0
186+
if perimeter > 0.0:
187+
radius = area / perimeter
188+
q = conv * area * radius ** _mpow * slope ** 0.5 / roughness[0]
189+
return q
190+
191+
192+
def get_depths(
193+
flows,
194+
x,
195+
h,
196+
roughness=0.01,
197+
slope=0.001,
198+
conv=1.0,
199+
dd=1e-4,
200+
verbose=False,
201+
):
202+
if isinstance(flows, float):
203+
flows = np.array([flows], dtype=float)
204+
if isinstance(roughness, float):
205+
roughness = np.ones(x.shape, dtype=float) * roughness
206+
depths = np.zeros(flows.shape, dtype=float)
207+
for idx, q in enumerate(flows):
208+
depths[idx] = qtodepth(
209+
x,
210+
h,
211+
q,
212+
roughness=roughness,
213+
slope=slope,
214+
conv=conv,
215+
dd=dd,
216+
verbose=False,
217+
)
218+
219+
return depths
220+
221+
222+
def qtodepth(
223+
x,
224+
h,
225+
q,
226+
roughness=0.01,
227+
slope=0.001,
228+
conv=1.0,
229+
dd=1e-4,
230+
verbose=False,
231+
):
232+
h0 = 0.0
233+
q0 = manningsq(
234+
x,
235+
h,
236+
h0,
237+
roughness=roughness,
238+
slope=slope,
239+
conv=conv,
240+
)
241+
r = q0 - q
242+
243+
iter = 0
244+
if verbose:
245+
print(f"iteration {iter:>2d} - residual={r}")
246+
while abs(r) > 1e-12:
247+
q1 = manningsq(
248+
x,
249+
h,
250+
h0 + dd,
251+
roughness=roughness,
252+
slope=slope,
253+
conv=conv,
254+
)
255+
dq = q1 - q0
256+
if dq != 0.0:
257+
derv = dd / (q1 - q0)
258+
else:
259+
derv = 0.0
260+
h0 -= derv * r
261+
q0 = manningsq(
262+
x,
263+
h,
264+
h0,
265+
roughness=roughness,
266+
slope=slope,
267+
conv=conv,
268+
)
269+
r = q0 - q
270+
271+
iter += 1
272+
if verbose:
273+
print(f"iteration {iter:>2d} - residual={r}")
274+
if iter > 100:
275+
break
276+
return h0

0 commit comments

Comments
 (0)