wolfhece 2.1.99__py3-none-any.whl → 2.1.101__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. wolfhece/PyDraw.py +220 -29
  2. wolfhece/PyGui.py +1039 -53
  3. wolfhece/PyVertexvectors.py +2 -2
  4. wolfhece/Results2DGPU.py +37 -13
  5. wolfhece/acceptability/Parallels.py +2 -2
  6. wolfhece/acceptability/_add_path.py +23 -0
  7. wolfhece/acceptability/acceptability.py +594 -563
  8. wolfhece/acceptability/acceptability_gui.py +564 -331
  9. wolfhece/acceptability/cli.py +307 -120
  10. wolfhece/acceptability/func.py +1754 -1597
  11. wolfhece/apps/version.py +1 -1
  12. wolfhece/bernoulli/losses.py +76 -23
  13. wolfhece/bernoulli/losses_jax.py +143 -0
  14. wolfhece/bernoulli/pipe.py +7 -2
  15. wolfhece/gpuview.py +4 -1
  16. wolfhece/libs/__init__.py +11 -10
  17. wolfhece/libs/wolfogl.cp310-win_amd64.pyd +0 -0
  18. wolfhece/math_parser/__init__.py +4 -4
  19. wolfhece/math_parser/calculator.py +51 -9
  20. wolfhece/mesh2d/bc_manager.py +25 -2
  21. wolfhece/mesh2d/gpu_2d.py +644 -0
  22. wolfhece/mesh2d/simple_2d.py +2817 -0
  23. wolfhece/mesh2d/wolf2dprev.py +5 -2
  24. wolfhece/pidcontroller.py +131 -0
  25. wolfhece/pywalous.py +7 -7
  26. wolfhece/scenario/config_manager.py +98 -21
  27. wolfhece/wolf_array.py +391 -176
  28. wolfhece/wolf_vrt.py +108 -7
  29. wolfhece/wolfresults_2D.py +113 -6
  30. wolfhece/xyz_file.py +91 -51
  31. {wolfhece-2.1.99.dist-info → wolfhece-2.1.101.dist-info}/METADATA +3 -1
  32. {wolfhece-2.1.99.dist-info → wolfhece-2.1.101.dist-info}/RECORD +35 -30
  33. {wolfhece-2.1.99.dist-info → wolfhece-2.1.101.dist-info}/WHEEL +1 -1
  34. {wolfhece-2.1.99.dist-info → wolfhece-2.1.101.dist-info}/entry_points.txt +0 -0
  35. {wolfhece-2.1.99.dist-info → wolfhece-2.1.101.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,2817 @@
1
+ import numpy as np
2
+ from matplotlib import pyplot as plt
3
+ from tqdm import tqdm
4
+ from numba import jit
5
+ from scipy.optimize import root_scalar
6
+ from typing import Literal
7
+ from enum import Enum
8
+ import logging
9
+
10
+ """
11
+ Calcul des equations shallow water en 1D section rectangulaire avec frottement selon Manning
12
+
13
+ Continuity : dh/dt + d(q)/dx = 0
14
+ Momentum : dq/dt + d(q^2/h + 1/2 g h^2)/dx + g h dz/dx = -g h J
15
+ Friction Slope : J = n^2 (q/h)^2 / h^(4/3)
16
+
17
+ Discretisation :
18
+
19
+ J = n^2 q_t2 q_t1 / h^(10/3)
20
+ ghJ = q_t2 (g n^2 q_t1 / h^(7/3))
21
+
22
+ h_t2 = h_t1 - dt/dx (q_t,r - q_t,l)
23
+ q_t2 = 1/(1 + dt g n^2 q_t1 / h^(7/3)) * (q_t1 - dt/dx (q_t,r^2/h_t,r - q_t,l^2/h_t,l + 1/2 g (h_t,r^2 - h_t,l^2) + g h_center_mean (z_t,r - z_t,l))
24
+
25
+ """
26
+
27
+ class Scenarios(Enum):
28
+ unsteady_downstream_bc = 0
29
+ hydrograph = 1
30
+ hydrograph_2steps = 2
31
+ Gauss = 3
32
+ water_lines = 4
33
+
34
+ def domain(length:float, dx:float, slope:float) -> np.ndarray:
35
+ """ Create the domain
36
+
37
+ :param length: Length of the domain
38
+ :param dx: Space step
39
+ :param slope: Slope of the domain
40
+ """
41
+
42
+ nb = int(length/dx)
43
+ dom = np.zeros(nb+2)
44
+ x = [-dx/2.] + [(float(i)+.5)*dx for i in range(0,nb)] + [length + dx/2.]
45
+ z = -slope * np.array(x)
46
+ return dom, x, z
47
+
48
+ def _init_conditions(dom:np.ndarray, h0:float, q0:float) -> np.ndarray:
49
+ """ Initial conditions
50
+
51
+ :param dom: Domain
52
+ :param h0: Initial water depth [m]
53
+ :param q0: Initial discharge [m^2/s]
54
+ """
55
+
56
+ h = np.zeros_like(dom)
57
+ h[1:-1] = h0
58
+ q = np.zeros_like(dom)
59
+ q[1:-1] = q0
60
+ return h, q
61
+
62
+ def get_friction_slope_2D_Manning(q:float, h:float, n:float) -> float:
63
+ """ Friction slope based on Manning formula
64
+
65
+ :param q: Discharge [m^2/s]
66
+ :param h: Water depth [m]
67
+ :param n: Manning coefficient [m^(1/3)/s]
68
+ """
69
+ return n**2 * q**2 / h**(10/3)
70
+
71
+ def compute_dt(dx:float, h:np.ndarray, q:np.ndarray, CN:float) -> float:
72
+ """ Compute the time step according to the Courant number anf the maximum velocity
73
+
74
+ :param dx: Space step
75
+ :param h: Water depth
76
+ :param q: Discharge
77
+ :param CN: Courant number
78
+ """
79
+
80
+ h_pos = np.where(h > 0)[0]
81
+ dt = CN * dx / (np.max(np.abs(q[h_pos]/h[h_pos]) + np.sqrt(h[h_pos]*9.81)))
82
+ return dt
83
+
84
+ def all_unk_border(dom:np.ndarray, h0:float, q0:float) -> tuple[np.ndarray]:
85
+ """ Initialize all arrays storing unknowns at center and borders
86
+
87
+ :param dom: Domain
88
+ :param h0: Initial water depth
89
+ :param q0: Initial discharge
90
+ """
91
+
92
+ h, q = _init_conditions(dom, h0, q0)
93
+ h_pred = np.zeros_like(dom)
94
+ h_corr = np.zeros_like(dom)
95
+ q_pred = np.zeros_like(dom)
96
+ q_corr = np.zeros_like(dom)
97
+
98
+ q_border = np.zeros((len(dom), 2))
99
+ h_border = np.zeros((len(dom), 2))
100
+ z_border = np.zeros((len(dom), 2))
101
+
102
+ u_border = np.zeros(len(dom))
103
+ h_center = np.zeros(len(dom)-2)
104
+ u_center = np.zeros(len(dom)-2)
105
+
106
+ return h, q, h_pred, q_pred, h_corr, q_corr, q_border, h_border, z_border, u_border, h_center, u_center
107
+
108
+ def uniform_waterdepth(slope:float, q:float, n:float):
109
+ """ Compute the uniform water depth for a given slope, discharge and Manning coefficient
110
+
111
+ :param slope: Slope
112
+ :param q: Discharge [m^2/s]
113
+ :param n: Manning coefficient
114
+ """
115
+
116
+ if n==0. or slope==0.:
117
+ logging.error("Manning coefficient or slope cannot be null")
118
+ logging.warning("Return 99999.")
119
+ return 99999.
120
+
121
+ return root_scalar(lambda h: slope - get_friction_slope_2D_Manning(q, h, n), bracket=[0.1, 100.]).root
122
+
123
+ # -----------------
124
+ # LOSS Coefficients
125
+ # -----------------
126
+
127
+ def k_abrupt_enlargment(asmall:float, alarge:float) -> float:
128
+ """ Compute the local head loss coefficient of the abrupt enlargment
129
+
130
+ :params asmall: float, area of the section 1 -- smaller section
131
+ :params alarge: float, area of the section 2 -- larger section
132
+ """
133
+
134
+ return (1 - asmall/alarge)**2
135
+
136
+ def k_abrupt_contraction(alarge:float, asmall:float) -> float:
137
+ """ Compute the local head loss coefficient of the abrupt contraction
138
+
139
+ :params alarge: float, area of the section 1 -- larger section
140
+ :params asmall: float, area of the section 2 -- smaller section
141
+ """
142
+
143
+ return .5*(1 - asmall/alarge)
144
+
145
+ def head_loss_enlargment(q:float, asmall:float, alarge:float) -> float:
146
+ """ Compute the head loss of the enlargment.
147
+
148
+ Reference velocity is the velocity in the smaller section.
149
+
150
+ :params q: float, discharge
151
+ :params asmall: float, area of the section 1 -- smaller section
152
+ :params alarge: float, area of the section 2 -- larger section
153
+ """
154
+
155
+ return k_abrupt_enlargment(asmall, alarge) * (q/asmall)**2 / 2 / 9.81
156
+
157
+ def head_loss_contraction(q:float, alarge:float, asmall:float) -> float:
158
+ """ Compute the head loss of the contraction.
159
+
160
+ Reference velocity is the velocity in the smaller section.
161
+
162
+ :params q: float, discharge
163
+ :params alarge: float, area of the section 1 -- larger section
164
+ :params asmall: float, area of the section 2 -- smaller section
165
+ """
166
+
167
+ return k_abrupt_contraction(alarge, asmall) * (q/asmall)**2 / 2 / 9.81
168
+
169
+ def head_loss_contract_enlarge(q:float, a_up:float, asmall:float, a_down:float) -> float:
170
+ """ Compute the head loss of the contraction/enlargment.
171
+
172
+ Reference velocity is the velocity in the smaller section.
173
+
174
+ :params q: float, discharge
175
+ :params a_up: float, area of the section 1 -- larger section
176
+ :params asmall: float, area of the section 2 -- smaller section
177
+ :params a_down: float, area of the section 3 -- larger section
178
+ """
179
+
180
+ return (k_abrupt_enlargment(asmall, a_down) + k_abrupt_contraction(a_up, asmall)) * (q/asmall)**2 / 2 / 9.81
181
+
182
+ # ----------------------------
183
+ # START JIT compiled functions
184
+ # ----------------------------
185
+
186
+ @jit(nopython=True)
187
+ def get_friction_slope_2D_Manning_semi_implicit(u:np.ndarray, h:np.ndarray, n:float) -> np.ndarray:
188
+ """ Friction slope based on Manning formula -- Only semi-implicit formulea for the friction slope
189
+
190
+ :param u: Velocity [m/s]
191
+ :param h: Water depth [m]
192
+ :param n: Manning coefficient [m^(1/3)/s]
193
+ """
194
+
195
+ return n**2 * np.abs(u) / h**(7/3)
196
+
197
+ @jit(nopython=True)
198
+ def Euler_RK(h_t1:np.ndarray, h_t2:np.ndarray,
199
+ q_t1:np.ndarray, q_t2:np.ndarray,
200
+ h:np.ndarray, q:np.ndarray,
201
+ h_border:np.ndarray, q_border:np.ndarray,
202
+ z:np.ndarray, z_border:np.ndarray,
203
+ dt:float, dx:float,
204
+ CL_h:float, CL_q:float,
205
+ n:float, u_border:np.ndarray,
206
+ h_center:np.ndarray, u_center:np.ndarray) -> None:
207
+ """ Solve the mass and momentum equations using a explicit Euler/Runge-Kutta scheme (only 1 step)
208
+
209
+ :param h_t1: Water depth at time t
210
+ :param h_t2: Water depth at time t+dt (or t_star or t_doublestar if RK)
211
+ :param q_t1: Discharge at time t
212
+ :param q_t2: Discharge at time t+dt (or t_star or t_doublestar if RK)
213
+ :param h: Water depth at the mesh center
214
+ :param q: Discharge at the mesh center
215
+ :param h_border: Water depth at the mesh border
216
+ :param q_border: Discharge at the mesh border
217
+ :param z: Bed elevation
218
+ :param z_border: Bed elevation at the mesh border
219
+ :param dt: Time step
220
+ :param dx: Space step
221
+ :param CL_h: Downstream boudary condition for water depth
222
+ :param CL_q: Upstream boundary condition for discharge
223
+ :param n: Manning coefficient
224
+ :param u_border: Velocity at the mesh border
225
+ :param h_center: Water depth at the mesh center
226
+ :param u_center: Velocity at the mesh center
227
+ """
228
+
229
+ g = 9.81
230
+
231
+ slice_mesh = slice(1,-1)
232
+ slice_right_border = slice(2,None)
233
+ slice_left_border = slice(1,-1)
234
+
235
+ up = 0
236
+ do = 1
237
+
238
+ # valeur à gauche du bord
239
+ q_border[slice_right_border, up] = q[1:-1]
240
+ q_border[1,up] = CL_q
241
+
242
+ h_border[slice_right_border, up] = h[1:-1]
243
+ h_border[1,up] = h[1]
244
+
245
+ z_border[slice_right_border, up] = z[1:-1]
246
+ z_border[1,up] = z[1]
247
+
248
+ # valeur à droite du bord
249
+ q_border[slice_left_border, do] = q[1:-1]
250
+ q_border[-1,do] = q[-2]
251
+
252
+ h_border[slice_left_border, do] = h[1:-1]
253
+ h_border[-1,do] = CL_h
254
+
255
+ z_border[slice_left_border, do] = z[1:-1]
256
+ z_border[-1,do] = z[-2]
257
+
258
+ u_border[1:] = q_border[1:,up]/h_border[1:,up]
259
+
260
+ h_center = (h_border[slice_right_border,do] + h_border[slice_left_border,do])/2.
261
+ u_center = q[slice_mesh]/h[slice_mesh]
262
+
263
+ #Continuity
264
+ h_t2[slice_mesh] = h_t1[slice_mesh] - dt/dx * (q_border[slice_right_border,up] - q_border[slice_left_border,up])
265
+
266
+ # Momentum
267
+
268
+ J = get_friction_slope_2D_Manning_semi_implicit(u_center, h[slice_mesh], n)
269
+ qm_right = u_border[slice_right_border]*q_border[slice_right_border,up]
270
+ qm_left = u_border[slice_left_border]*q_border[slice_left_border,up]
271
+
272
+ press_right = 0.5 * g * h_border[slice_right_border,do]**2
273
+ press_left = 0.5 * g * h_border[slice_left_border,do]**2
274
+
275
+ bed_right = g * h_center * z_border[slice_right_border,do]
276
+ bed_left = g * h_center * z_border[slice_left_border,do]
277
+
278
+ q_t2[slice_mesh] = 1./(1. + dt * g *h[slice_mesh] * J) * (q_t1[slice_mesh] - dt/dx * (qm_right - qm_left + press_right - press_left + bed_right - bed_left))
279
+
280
+ limit_h_q(h_t2, q_t2, hmin=1e-3, Froudemax=3.)
281
+
282
+ @jit(nopython=True)
283
+ def Euler_RK_hedge(h_t1:np.ndarray, h_t2:np.ndarray,
284
+ q_t1:np.ndarray, q_t2:np.ndarray,
285
+ h:np.ndarray, q:np.ndarray,
286
+ h_border:np.ndarray, q_border:np.ndarray,
287
+ z:np.ndarray, z_border:np.ndarray,
288
+ dt:float, dx:float,
289
+ CL_h:float, CL_q:float,
290
+ n:float, u_border:np.ndarray,
291
+ h_center:np.ndarray, u_center:np.ndarray,
292
+ theta:np.ndarray, theta_border:np.ndarray) -> None:
293
+ """ Solve the mass and momentum equations using a explicit Euler/Runge-Kutta scheme (only 1 step)
294
+
295
+ :param h_t1: Water depth at time t
296
+ :param h_t2: Water depth at time t+dt (or t_star or t_doublestar if RK)
297
+ :param q_t1: Discharge at time t
298
+ :param q_t2: Discharge at time t+dt (or t_star or t_doublestar if RK)
299
+ :param h: Water depth at the mesh center
300
+ :param q: Discharge at the mesh center
301
+ :param h_border: Water depth at the mesh border
302
+ :param q_border: Discharge at the mesh border
303
+ :param z: Bed elevation
304
+ :param z_border: Bed elevation at the mesh border
305
+ :param dt: Time step
306
+ :param dx: Space step
307
+ :param CL_h: Downstream boudary condition for water depth
308
+ :param CL_q: Upstream boundary condition for discharge
309
+ :param n: Manning coefficient
310
+ :param u_border: Velocity at the mesh border
311
+ :param h_center: Water depth at the mesh center
312
+ :param u_center: Velocity at the mesh center
313
+ """
314
+
315
+ g = 9.81
316
+
317
+ slice_mesh = slice(1,-1)
318
+ slice_right_border = slice(2,None)
319
+ slice_left_border = slice(1,-1)
320
+
321
+ up = 0
322
+ do = 1
323
+
324
+ # valeur à gauche du bord
325
+ q_border[slice_right_border, up] = q[1:-1]
326
+ q_border[1,up] = CL_q
327
+
328
+ h_border[slice_right_border, up] = h[1:-1]
329
+ h_border[1,up] = h[1]
330
+
331
+ z_border[slice_right_border, up] = z[1:-1]
332
+ z_border[1,up] = z[1]
333
+
334
+ theta_border[slice_right_border, up] = theta[1:-1]
335
+ theta_border[1,up] = theta[1]
336
+
337
+ # valeur à droite du bord
338
+ q_border[slice_left_border, do] = q[1:-1]
339
+ q_border[-1,do] = q[-2]
340
+
341
+ h_border[slice_left_border, do] = h[1:-1]
342
+ h_border[-1,do] = CL_h
343
+
344
+ z_border[slice_left_border, do] = z[1:-1]
345
+ z_border[-1,do] = z[-2]
346
+
347
+ theta_border[slice_left_border, do] = theta[1:-1]
348
+ theta_border[-1,do] = theta[-2]
349
+
350
+
351
+ u_border[1:] = q_border[1:,up]/(h_border[1:,up] * theta_border[1:,up])
352
+
353
+ h_center = (theta_border[slice_right_border,do] * h_border[slice_right_border,do] + theta_border[slice_left_border,do] * h_border[slice_left_border,do])/2.
354
+ u_center = q[slice_mesh]/(h[slice_mesh]*theta[slice_mesh])
355
+
356
+ #Continuity
357
+ h_t2[slice_mesh] = h_t1[slice_mesh] - dt/dx * (q_border[slice_right_border,up] - q_border[slice_left_border,up])
358
+
359
+ # Momentum
360
+
361
+ J = get_friction_slope_2D_Manning_semi_implicit(u_center, h[slice_mesh], n)
362
+ qm_right = u_border[slice_right_border]*q_border[slice_right_border,up]
363
+ qm_left = u_border[slice_left_border ]*q_border[slice_left_border,up]
364
+
365
+ press_right = 0.5 * g * (theta_border[slice_right_border,do] * h_border[slice_right_border,do])**2
366
+ press_left = 0.5 * g * (theta_border[slice_left_border ,do] * h_border[slice_left_border ,do])**2
367
+
368
+ bed_right = g * h_center * (z_border[slice_right_border,do] + (1-theta_border[slice_right_border,do]) * h_border[slice_right_border,do])
369
+ bed_left = g * h_center * (z_border[slice_left_border,do] + (1-theta_border[slice_left_border,do]) * h_border[slice_left_border,do])
370
+
371
+ q_t2[slice_mesh] = 1./(1. + dt * g *h[slice_mesh] * J) * (q_t1[slice_mesh] - dt/dx * (qm_right - qm_left + press_right - press_left + bed_right - bed_left))
372
+
373
+ limit_h_q(h_t2, q_t2, hmin=1e-3, Froudemax=3.)
374
+
375
+
376
+ @jit(nopython=True)
377
+ def splitting(q_left:np.float64, q_right:np.float64,
378
+ h_left:np.float64, h_right:np.float64,
379
+ z_left:np.float64, z_right:np.float64,
380
+ z_bridge_left:np.float64, z_bridge_right:np.float64) -> np.ndarray:
381
+ """ Splitting of the unknowns at border between two nodes
382
+ -- Based on the WOLF HECE original scheme
383
+ """
384
+
385
+ prod_q = q_left * q_right
386
+ sum_q = q_left + q_right
387
+
388
+ if prod_q > 0.:
389
+ if q_left > 0.:
390
+ return np.asarray([q_left, min(h_left, z_bridge_left-z_left), h_right, z_right, z_bridge_right], dtype=np.float64)
391
+ else:
392
+ return np.asarray([q_right, min(h_right, z_bridge_right-z_right), h_left, z_left, z_bridge_left], dtype=np.float64)
393
+ elif prod_q < 0.:
394
+ if sum_q > 0.:
395
+ return np.asarray([q_left, min(h_left, z_bridge_left-z_left), h_right, z_right, z_bridge_right], dtype=np.float64)
396
+ elif sum_q < 0.:
397
+ return np.asarray([q_right, min(h_right, z_bridge_right-z_right), h_left, z_left, z_bridge_left], dtype=np.float64)
398
+ else:
399
+ return np.asarray([0., 1., (h_left + h_right) / 2., (z_left + z_right) / 2., (z_bridge_left + z_bridge_right) / 2.], dtype=np.float64)
400
+ else:
401
+ if q_left<0.:
402
+ return np.asarray([np.float64(0.), np.float64(1.), h_left, z_left, z_bridge_left], dtype=np.float64)
403
+ elif q_right<0.:
404
+ return np.asarray([np.float64(0.), np.float64(1.), h_right, z_right, z_bridge_right], dtype=np.float64)
405
+ else:
406
+ return np.asarray([sum_q / 2., # q
407
+ (min(h_left, z_bridge_left-z_left) + min(h_right, z_bridge_right-z_right)) / 2., # h_vel
408
+ (h_left + h_right) / 2., # h
409
+ (z_left + z_right) / 2., # z
410
+ (z_bridge_left + z_bridge_right) / 2.], dtype=np.float64) # z_bridge
411
+
412
+
413
+ @jit(nopython=True)
414
+ def Euler_RK_bridge(h_t1:np.ndarray, h_t2:np.ndarray,
415
+ q_t1:np.ndarray, q_t2:np.ndarray,
416
+ h:np.ndarray, q:np.ndarray,
417
+ h_border:np.ndarray, q_border:np.ndarray,
418
+ z:np.ndarray, z_border:np.ndarray,
419
+ dt:float, dx:float,
420
+ CL_h:float, CL_q:float,
421
+ n:float, u_border:np.ndarray,
422
+ h_center:np.ndarray, u_center:np.ndarray,
423
+ z_bridge:np.ndarray, z_bridge_border:np.ndarray,
424
+ press_mode:int=0,
425
+ infil_exfil=None) -> None:
426
+ """
427
+ Solve the mass and momentum equations using a explicit Euler/Runge-Kutta scheme (only 1 step)
428
+ applying source terms for infiltration/exfiltration and pressure at the roof.
429
+ """
430
+ g = 9.81
431
+
432
+ slice_mesh = slice(1,-1)
433
+ slice_right_border = slice(2,None)
434
+ slice_left_border = slice(1,-1)
435
+
436
+ up = 0
437
+ do = 1
438
+
439
+ # valeur à gauche du bord
440
+ z_border[slice_right_border, up] = z[1:-1]
441
+ z_border[1,up] = z[1]
442
+
443
+ z_bridge_copy = z_bridge.copy()
444
+
445
+ fs_cells = np.where(h <= z_bridge - z)[0]
446
+ press_cells = np.where(h > z_bridge - z)[0]
447
+
448
+ z_bridge_copy[fs_cells] = z[fs_cells] + h[fs_cells]
449
+
450
+ h_border[slice_right_border, up] = h[1:-1]
451
+ h_border[1,up] = h[1]
452
+
453
+ q_border[slice_right_border, up] = q[1:-1]
454
+ q_border[1,up] = CL_q
455
+
456
+ # valeur à droite du bord
457
+ z_border[slice_left_border, do] = z[1:-1]
458
+ z_border[-1,do] = z[-2]
459
+
460
+ z_bridge_border[slice_right_border, up] = z_bridge_copy[1:-1]
461
+ z_bridge_border[1,up] = z_bridge_copy[1]
462
+
463
+ z_bridge_border[slice_left_border, do] = z_bridge_copy[1:-1]
464
+ z_bridge_border[-1,do] = z_bridge_copy[-2]
465
+
466
+ h_border[slice_left_border, do] = h[1:-1]
467
+ h_border[-1,do] = CL_h
468
+
469
+ q_border[slice_left_border, do] = q[1:-1]
470
+ q_border[-1,do] = q[-2]
471
+
472
+
473
+
474
+ for i in range(1, len(h)-1):
475
+
476
+ qc_right, h4u_right, hc_right, zc_right, zbc_right = splitting(q_border[i+1,up], q_border[i+1, do], h_border[i+1,up], h_border[i+1,do], z_border[i+1,up], z_border[i+1,do], z_bridge_border[i+1,up], z_bridge_border[i+1,do])
477
+ qc_left, h4u_left, hc_left, zc_left, zbc_left = splitting(q_border[i,up], q_border[i, do], h_border[i,up], h_border[i,do], z_border[i,up], z_border[i,do], z_bridge_border[i,up], z_bridge_border[i,do])
478
+
479
+ # Continuity
480
+ # ++++++++++
481
+
482
+ h_t2[i] = h_t1[i] - dt/dx * (qc_right - qc_left)
483
+
484
+ # Momentum
485
+ # ++++++++
486
+
487
+ # Limited section at the right border and at the left border -- decentred downstream
488
+ d_right = zbc_right - zc_right
489
+ d_left = zbc_left - zc_left
490
+
491
+ # Pressure on the roof -- 0. if free surface
492
+ press_roof_right = hc_right - d_right
493
+ press_roof_left = hc_left - d_left
494
+
495
+ # Pressure integral at the right border and at the left border
496
+ press_right = 0.5 * g * (hc_right**2. - press_roof_right**2)
497
+ press_left = 0.5 * g * (hc_left**2. - press_roof_left**2)
498
+
499
+ # Friction slope based on center values
500
+ u_center = q[i] / (z_bridge_copy[i] - z[i])
501
+ # Number of surfaces
502
+ nb_frott = 2. if h[i] > z_bridge[i] - z[i] else 1.
503
+ # Integration water depth
504
+ h_frott = min(h[i], z_bridge[i] - z[i])
505
+ # Slope
506
+ J = get_friction_slope_2D_Manning_semi_implicit(u_center, h_frott/nb_frott, n)
507
+
508
+ # Velocity at the right border and at the left border -- decentred upstream
509
+ u_right = qc_right / h4u_right
510
+ u_left = qc_left / h4u_left
511
+
512
+ # Momentum at the right border and at the left border
513
+ qm_right = u_right * qc_right
514
+ qm_left = u_left * qc_left
515
+
516
+ # Mean pressure impacting bed reaction
517
+ h_mean = (hc_right + hc_left)/2.
518
+ bed_right = g * h_mean * zc_right
519
+ bed_left = g * h_mean * zc_left
520
+
521
+ # Mean pressure impacting roof reaction
522
+ h_roof_right = max(hc_right + zc_right - zbc_right, 0.)
523
+ h_roof_left = max(hc_left + zc_left - zbc_left , 0.)
524
+ h_roof_mean = (h_roof_right + h_roof_left) / 2.
525
+
526
+ roof_right = g * h_roof_mean * zbc_right
527
+ roof_left = g * h_roof_mean * zbc_left
528
+
529
+ rhs = (qm_right - qm_left + press_right - press_left + bed_right - bed_left - roof_right + roof_left)
530
+
531
+ if rhs !=0.:
532
+ pass
533
+
534
+ q_t2[i] = 1./(1. + dt * g * h_frott * J) * (q_t1[i] - dt/dx * rhs)
535
+
536
+ if infil_exfil is not None:
537
+ idx_up, idx_do, q_infil_exfil, u_infil_exfil, pond, k = infil_exfil
538
+
539
+ i = idx_up
540
+
541
+ qc_right, h4u_right, hc_right, zc_right, zbc_right = splitting(q_border[i+1,up], q_border[i+1, do], h_border[i+1,up], h_border[i+1,do], z_border[i+1,up], z_border[i+1,do], z_bridge_border[i+1,up], z_bridge_border[i+1,do])
542
+ qc_left, h4u_left, hc_left, zc_left, zbc_left = splitting(q_border[i,up], q_border[i, do], h_border[i,up], h_border[i,do], z_border[i,up], z_border[i,do], z_bridge_border[i,up], z_bridge_border[i,do])
543
+
544
+ # Limited section at the right border and at the left border -- decentred downstream
545
+ d_right = zbc_right - zc_right
546
+ d_left = zbc_left - zc_left
547
+
548
+ # Pressure on the roof -- 0. if free surface
549
+ press_roof_right = hc_right - d_right
550
+ press_roof_left = hc_left - d_left
551
+
552
+ # Pressure integral at the right border and at the left border
553
+ press_right = 0.5 * g * (hc_right**2. - press_roof_right**2)
554
+ press_left = 0.5 * g * (hc_left**2. - press_roof_left**2)
555
+
556
+ # Friction slope based on center values
557
+ u_center = q[i] / (z_bridge_copy[i] - z[i])
558
+ # Number of surfaces
559
+ nb_frott = 2. if h[i] > z_bridge[i] - z[i] else 1.
560
+ # Integration water depth
561
+ h_frott = min(h[i], z_bridge[i] - z[i])
562
+ # Slope
563
+ J = get_friction_slope_2D_Manning_semi_implicit(u_center, h_frott/nb_frott, n)
564
+
565
+ # Velocity at the right border and at the left border -- decentred upstream
566
+ u_right = qc_right / h4u_right
567
+ u_left = qc_left / h4u_left
568
+
569
+ # Momentum at the right border and at the left border
570
+ qm_right = u_right * qc_right
571
+ qm_left = u_left * qc_left
572
+
573
+ # Mean pressure impacting bed reaction
574
+ h_mean = (hc_right + hc_left)/2.
575
+ bed_right = g * h_mean * zc_right
576
+ bed_left = g * h_mean * zc_left
577
+
578
+ # Mean pressure impacting roof reaction
579
+ h_roof_right = max(hc_right + zc_right - zbc_right, 0.)
580
+ h_roof_left = max(hc_left + zc_left - zbc_left , 0.)
581
+ h_roof_mean = (h_roof_right + h_roof_left) / 2.
582
+
583
+ roof_right = g * h_roof_mean * zbc_right
584
+ roof_left = g * h_roof_mean * zbc_left
585
+
586
+ h_t2[i] = h_t1[i] - dt/dx * (qc_right - qc_left + q_infil_exfil)
587
+ q_t2[i] = 1./(1. + dt * g * h_frott * J) * (q_t1[i] - dt/dx * (qm_right - qm_left + \
588
+ press_right - press_left + \
589
+ bed_right - bed_left - \
590
+ roof_right + roof_left + \
591
+ u_infil_exfil * q_infil_exfil))
592
+
593
+ i = idx_do
594
+
595
+ qc_right, h4u_right, hc_right, zc_right, zbc_right = splitting(q_border[i+1,up], q_border[i+1, do], h_border[i+1,up], h_border[i+1,do], z_border[i+1,up], z_border[i+1,do], z_bridge_border[i+1,up], z_bridge_border[i+1,do])
596
+ qc_left, h4u_left, hc_left, zc_left, zbc_left = splitting(q_border[i,up], q_border[i, do], h_border[i,up], h_border[i,do], z_border[i,up], z_border[i,do], z_bridge_border[i,up], z_bridge_border[i,do])
597
+
598
+ # Limited section at the right border and at the left border -- decentred downstream
599
+ d_right = zbc_right - zc_right
600
+ d_left = zbc_left - zc_left
601
+
602
+ # Pressure on the roof -- 0. if free surface
603
+ press_roof_right = hc_right - d_right
604
+ press_roof_left = hc_left - d_left
605
+
606
+ # Pressure integral at the right border and at the left border
607
+ press_right = 0.5 * g * (hc_right**2. - press_roof_right**2)
608
+ press_left = 0.5 * g * (hc_left**2. - press_roof_left**2)
609
+
610
+ # Friction slope based on center values
611
+ u_center = q[i] / (z_bridge_copy[i] - z[i])
612
+ # Number of surfaces
613
+ nb_frott = 2. if h[i] > z_bridge[i] - z[i] else 1.
614
+ # Integration water depth
615
+ h_frott = min(h[i], z_bridge[i] - z[i])
616
+ # Slope
617
+ J = get_friction_slope_2D_Manning_semi_implicit(u_center, h_frott/nb_frott, n)
618
+
619
+ # Velocity at the right border and at the left border -- decentred upstream
620
+ u_right = qc_right / h4u_right
621
+ u_left = qc_left / h4u_left
622
+
623
+ # Momentum at the right border and at the left border
624
+ qm_right = u_right * qc_right
625
+ qm_left = u_left * qc_left
626
+
627
+ # Mean pressure impacting bed reaction
628
+ h_mean = (hc_right + hc_left)/2.
629
+ bed_right = g * h_mean * zc_right
630
+ bed_left = g * h_mean * zc_left
631
+
632
+ # Mean pressure impacting roof reaction
633
+ h_roof_right = max(hc_right + zc_right - zbc_right, 0.)
634
+ h_roof_left = max(hc_left + zc_left - zbc_left , 0.)
635
+ h_roof_mean = (h_roof_right + h_roof_left) / 2.
636
+
637
+ roof_right = g * h_roof_mean * zbc_right
638
+ roof_left = g * h_roof_mean * zbc_left
639
+
640
+ h_t2[i] = h_t1[i] - dt/dx * (qc_right - qc_left - q_infil_exfil)
641
+ q_t2[i] = 1./(1. + dt * g * h_frott * J) * (q_t1[i] - dt/dx * (qm_right - qm_left + \
642
+ press_right - press_left + \
643
+ bed_right - bed_left - \
644
+ roof_right + roof_left -\
645
+ u_infil_exfil * q_infil_exfil))
646
+
647
+ limit_h_q(h_t2, q_t2, hmin=1e-3, Froudemax=3.)
648
+
649
+ @ jit(nopython=True)
650
+ def limit_h_q(h:np.ndarray, q:np.ndarray, hmin:float = 0., Froudemax:float = 3.) -> None:
651
+ """ Limit the water depth and the discharge
652
+
653
+ :param h: Water depth [m]
654
+ :param q: Discharge [m^2/s]
655
+ :param hmin: Minimum water depth [m]
656
+ :param Froudemax: Maximum Froude number [-]
657
+ """
658
+
659
+ # retrieve positive and negative values
660
+ hpos = np.where(h > hmin)
661
+ hneg = np.where(h <= hmin)
662
+
663
+ # limit water depth
664
+ h[hneg] = hmin
665
+ q[hneg] = 0.
666
+
667
+ # limit discharge based on Froude number
668
+ Fr = np.zeros_like(h)
669
+ Fr[hpos] = np.abs(q[hpos]) / h[hpos] / np.sqrt(9.81 * h[hpos])
670
+ q[Fr > Froudemax] = Froudemax * np.sqrt(9.81 * h[Fr > Froudemax]) * h[Fr > Froudemax] * np.sign(q[Fr > Froudemax])
671
+
672
+ # --------------------------
673
+ # END JIT compiled functions
674
+ # --------------------------
675
+
676
+ # ----------------------
677
+ # START Problems section
678
+ # ----------------------
679
+
680
+ def problem(dom:np.ndarray, z:np.ndarray, h0:float, q0:float, dx:float, CN:float, n:float):
681
+ """ Solve the mass and momentum equations using a explicit Runge-Kutta scheme (2 steps - 2nd order)
682
+
683
+ **NO BRIDGE**
684
+ """
685
+
686
+ h, q, h_pred, q_pred, h_corr, q_corr, q_border, h_border, z_border, u_border, h_center, u_center = all_unk_border(dom, h0, q0)
687
+
688
+ totaltime = 4*3600
689
+ eps=1.
690
+
691
+ with tqdm(total=totaltime) as pbar:
692
+ t = 0.
693
+ while eps > 1e-12:
694
+ dt = compute_dt(dx, h, q, CN)
695
+ # Predictor step
696
+ Euler_RK(h, h_pred, q, q_pred, h, q, h_border, q_border, z, z_border, dt, dx, h0, q0, n, u_border, h_center, u_center)
697
+ # Corrector step
698
+ Euler_RK(h, h_corr, q, q_corr, h_pred, q_pred, h_border, q_border, z, z_border, dt, dx, h0, q0, n, u_border, h_center, u_center)
699
+
700
+ # Update -- Mean for second order in time
701
+ h = (h_pred + h_corr)/2.
702
+ q = (q_pred + q_corr)/2.
703
+ t+=dt
704
+
705
+ eps = np.sum(np.abs(q - q_pred))
706
+
707
+ pbar.update(dt)
708
+
709
+ print("Total time : ", t)
710
+ print("Residual : ", eps)
711
+
712
+ return h, q
713
+
714
+ def problem_hedge(dom:np.ndarray, z:np.ndarray, h0:float, q0:float, dx:float, CN:float, n:float):
715
+ """ Solve the mass and momentum equations using a explicit Runge-Kutta scheme (2 steps - 2nd order)
716
+
717
+ **NO BRIDGE bur HEDGE in the middle**
718
+ """
719
+
720
+ h, q, h_pred, q_pred, h_corr, q_corr, q_border, h_border, z_border, u_border, h_center, u_center = all_unk_border(dom, h0, q0)
721
+
722
+ theta = np.ones_like(h)
723
+ theta_border = np.ones_like(h_border)
724
+
725
+ slice_hedge = slice(len(h) // 2-1, len(h) // 2+2)
726
+ theta_val = 0.5
727
+ theta[slice_hedge] = theta_val
728
+
729
+ totaltime = 4*3600
730
+ eps=1.
731
+
732
+ with tqdm(total=totaltime) as pbar:
733
+ t = 0.
734
+ while eps > 1e-12:
735
+ dt = compute_dt(dx, h, q, CN)
736
+ # Predictor step
737
+ Euler_RK_hedge(h, h_pred, q, q_pred, h, q, h_border, q_border, z, z_border, dt, dx, h0, q0, n, u_border, h_center, u_center, theta, theta_border)
738
+ # Corrector step
739
+ Euler_RK_hedge(h, h_corr, q, q_corr, h_pred, q_pred, h_border, q_border, z, z_border, dt, dx, h0, q0, n, u_border, h_center, u_center, theta, theta_border)
740
+
741
+ # Update -- Mean for second order in time
742
+ h = (h_pred + h_corr)/2.
743
+ q = (q_pred + q_corr)/2.
744
+ t+=dt
745
+
746
+ eps = np.sum(np.abs(q - q_pred))
747
+
748
+ pbar.update(dt)
749
+
750
+ print("Total time : ", t)
751
+ print("Residual : ", eps)
752
+
753
+ return h, q, theta
754
+
755
+ def problem_bridge(dom:np.ndarray, z:np.ndarray, z_bridge:np.ndarray,
756
+ h0:float, q0:float,
757
+ dx:float, CN:float, n:float,
758
+ press_mode:int = 4,
759
+ h_ini:np.ndarray = None, q_ini:np.ndarray = None) -> tuple[np.ndarray]:
760
+ """ Solve the mass and momentum equations using a explicit Rung-Kutta scheme (2 steps - 2nd order)
761
+
762
+ **WITH BRIDGE and NO OVERFLOW**
763
+ """
764
+
765
+ h, q, h_pred, q_pred, h_corr, q_corr, q_border, h_border, z_border, u_border, h_center, u_center = all_unk_border(dom, h0, q0)
766
+ z_bridge_border = np.zeros_like(z_border)
767
+
768
+ if h_ini is not None:
769
+ h[:] = h_ini[:]
770
+ if q_ini is not None:
771
+ q[:] = q_ini[:]
772
+
773
+ totaltime = 4*3600
774
+ eps=1.
775
+
776
+ with tqdm(total=totaltime) as pbar:
777
+ t = 0.
778
+ while eps > 1e-7:
779
+ dt = compute_dt(dx, h, q, CN)
780
+ # Predictor step
781
+ Euler_RK_bridge(h, h_pred, q, q_pred, h, q, h_border, q_border, z, z_border, dt, dx, h0, q0, n, u_border, h_center, u_center, z_bridge, z_bridge_border, press_mode)
782
+ # Corrector step
783
+ Euler_RK_bridge(h, h_corr, q, q_corr, h_pred, q_pred, h_border, q_border, z, z_border, dt, dx, h0, q0, n, u_border, h_center, u_center, z_bridge, z_bridge_border, press_mode)
784
+
785
+ # Update -- Mean for second order in time
786
+ h = (h_pred + h_corr)/2.
787
+ q = (q_pred + q_corr)/2.
788
+ t+=dt
789
+
790
+ eps = np.sum(np.abs(q - q_pred))
791
+
792
+ pbar.update(dt)
793
+
794
+ print("Total time : ", t)
795
+ print("Residual : ", eps)
796
+
797
+ return h, q
798
+
799
+ def problem_bridge_multiple_steadystates(dom:np.ndarray, z:np.ndarray, z_bridge:np.ndarray,
800
+ h0:float, qmin:float, qmax:float,
801
+ dx:float, CN:float, n:float,
802
+ press_mode:int = 4) -> list[tuple[float, np.ndarray, np.ndarray]]:
803
+ """ Solve multiple steady states for a given discharge range """
804
+
805
+ all_q = np.arange(qmin, qmax+.1, (qmax-qmin)/10)
806
+
807
+ ret = []
808
+ h = None
809
+ for curq in all_q:
810
+ h, q = problem_bridge(dom, z, z_bridge, h0, curq, dx, CN, n, press_mode, h_ini=h, q_ini=None)
811
+ ret.append((0., h.copy(), q.copy()))
812
+
813
+ return ret
814
+
815
+ def problem_bridge_unsteady(dom:np.ndarray, z:np.ndarray, z_bridge:np.ndarray,
816
+ h0:float, q0:float,
817
+ dx:float, CN:float, n:float,
818
+ press_mode:int = 4):
819
+ """ Solve the mass and momentum equations using a explicit Runge-Kutta scheme (2 steps - 2nd order).
820
+
821
+ **WITH BRIDGE and NO OVERFLOW**
822
+
823
+ The downstream boundary condition rises temporarily.
824
+
825
+ Firstly, we stabilize the flow with a constant downstream boundary condition.
826
+ Then, we increase the downstream boundary condition.
827
+
828
+ """
829
+
830
+ h, q, h_pred, q_pred, h_corr, q_corr, q_border, h_border, z_border, u_border, h_center, u_center = all_unk_border(dom, h0, q0)
831
+ z_bridge_border = np.zeros_like(z_border)
832
+
833
+ totaltime = 4*3600
834
+ eps=1.
835
+
836
+ # Compute steady state
837
+ with tqdm(total=totaltime) as pbar:
838
+ t = 0.
839
+ while eps > 1e-7:
840
+ dt = compute_dt(dx, h, q, CN)
841
+ # Predictor step
842
+ Euler_RK_bridge(h, h_pred, q, q_pred, h, q, h_border, q_border, z, z_border, dt, dx, h0, q0, n, u_border, h_center, u_center, z_bridge, z_bridge_border, press_mode)
843
+ # Corrector step
844
+ Euler_RK_bridge(h, h_corr, q, q_corr, h_pred, q_pred, h_border, q_border, z, z_border, dt, dx, h0, q0, n, u_border, h_center, u_center, z_bridge, z_bridge_border, press_mode)
845
+
846
+ # Update -- Mean for second order in time
847
+ h = (h_pred + h_corr)/2.
848
+ q = (q_pred + q_corr)/2.
849
+ t+=dt
850
+
851
+ eps = np.sum(np.abs(q - q_pred))
852
+
853
+ pbar.update(dt)
854
+
855
+ print("Total time : ", t)
856
+ print("Residual : ", eps)
857
+
858
+ res = []
859
+
860
+ totaltime = 2*3600
861
+ k=0
862
+ with tqdm(total=totaltime) as pbar:
863
+ t = 0.
864
+ while t < totaltime:
865
+
866
+ dt = compute_dt(dx, h, q, CN)
867
+
868
+ if t < totaltime/2:
869
+ h_cl = h0 + 7. * t/totaltime*2
870
+ else:
871
+ h_cl = h0 + 7. - 7. * (t - totaltime/2)/totaltime*2
872
+
873
+ # Predictor step
874
+ Euler_RK_bridge(h, h_pred, q, q_pred, h, q, h_border, q_border, z, z_border, dt, dx, h_cl, q0, n, u_border, h_center, u_center, z_bridge, z_bridge_border, press_mode)
875
+ # Corrector step
876
+ Euler_RK_bridge(h, h_corr, q, q_corr, h_pred, q_pred, h_border, q_border, z, z_border, dt, dx, h_cl, q0, n, u_border, h_center, u_center, z_bridge, z_bridge_border, press_mode)
877
+
878
+ # Update -- Mean for second order in time
879
+ h = (h_pred + h_corr)/2.
880
+ q = (q_pred + q_corr)/2.
881
+
882
+ if t > 300*k:
883
+ res.append((t, h.copy(), q.copy()))
884
+ k+=1
885
+
886
+ t+=dt
887
+ pbar.update(dt)
888
+
889
+
890
+ return res
891
+
892
+ def problem_bridge_unsteady_topo(dom:np.ndarray, z:np.ndarray,
893
+ z_roof:np.ndarray, z_deck:np.ndarray, z_roof_null:float,
894
+ h0:float, q0:float,
895
+ dx:float, CN:float, n:float,
896
+ press_mode:int = 0,
897
+ motion_duration:float = 300.,
898
+ scenario_bc:Literal['unsteady_downstream_bc',
899
+ 'hydrograph',
900
+ 'hydrograph_2steps',
901
+ 'Gauss'] = 'unsteady_downstream_bc',
902
+ min_overflow:float = 0.05,
903
+ updating_time_interval:float = 0.
904
+ ):
905
+ """ Solve the mass and momentum equations using a explicit Rung-Kutta scheme (2 steps - 2nd order).
906
+
907
+ **WITH BRIDGE and OVERFLOW**
908
+ """
909
+
910
+ h, q, h_pred, q_pred, h_corr, q_corr, q_border, h_border, z_border, u_border, h_center, u_center = all_unk_border(dom, h0, q0)
911
+ z_roof_border = np.zeros_like(z_border)
912
+
913
+ totaltime = 3600 #4*3600
914
+ eps=1.
915
+
916
+ # Compute steady state
917
+ with tqdm(total=totaltime) as pbar:
918
+ t = 0.
919
+ while eps > 1e-7:
920
+ dt = compute_dt(dx, h, q, CN)
921
+ # Predictor step
922
+ Euler_RK_bridge(h, h_pred, q, q_pred, h, q, h_border, q_border, z, z_border, dt, dx, h0, q0, n, u_border, h_center, u_center, z_roof, z_roof_border, press_mode)
923
+ # Corrector step
924
+ Euler_RK_bridge(h, h_corr, q, q_corr, h_pred, q_pred, h_border, q_border, z, z_border, dt, dx, h0, q0, n, u_border, h_center, u_center, z_roof, z_roof_border, press_mode)
925
+
926
+ # Update -- Mean for second order in time
927
+ h = (h_pred + h_corr)/2.
928
+ q = (q_pred + q_corr)/2.
929
+ t+=dt
930
+
931
+ eps = np.sum(np.abs(q - q_pred))
932
+
933
+ pbar.update(dt)
934
+
935
+ print("Total time : ", t)
936
+ print("Residual : ", eps)
937
+
938
+ g = 9.81
939
+
940
+ res = []
941
+
942
+ # Functions to evaluate the head and the local head loss coefficient
943
+ def compute_head(z,h,q):
944
+ return z+h+(q/h)**2/2/g
945
+
946
+ def compute_delta_head(zup,hup,qup,zdo,hdo,qdo):
947
+ return compute_head(zup,hup,qup) - compute_head(zdo,hdo,qdo)
948
+
949
+ def compute_k_mean(zup,hup,qup,zdo,hdo,qdo,A_bridge):
950
+ delta = compute_delta_head(zup,hup,qup,zdo,hdo,qdo)
951
+ q_mean = (qup+qdo)/2
952
+ return delta/((q_mean/A_bridge)**2/2/g)
953
+
954
+ def compute_losses(q, A_bridge, k):
955
+ """ Compute the losses based on the flow rate and the local head loss coefficient
956
+
957
+ :return: k * |Q| * Q / A^2 / 2 / g
958
+ :unit: [m]
959
+ """
960
+ return k * q * np.abs(q) / A_bridge**2. /2. / g
961
+
962
+ def compute_losses_semi_implicit(q, A_bridge, k):
963
+ """ Compute part of the losses based on the flow rate and the local head loss coefficient
964
+ to use in the semi-implicit scheme.
965
+
966
+ :return: k * |Q| / A / 2.
967
+ :unit: [s/m²]
968
+ """
969
+ return k * np.abs(q) / A_bridge /2.
970
+
971
+ def compute_q_wo_inertia(zup,hup,qup,zdo,hdo,qdo,A_bridge,k):
972
+ """ Compute the flow rate based on Bernoulli equation
973
+ without inertia term """
974
+
975
+ delta = compute_delta_head(zup,hup,qup,zdo,hdo,qdo)
976
+ return np.sqrt(2*g*np.abs(delta)/k)*A_bridge*np.sign(delta), delta
977
+
978
+ def compute_q_w_inertia(zup:float, hup:float, qup:float,
979
+ zdo:float, hdo:float, qdo:float,
980
+ A_bridge:float, k:float, q_prev:float,
981
+ dt:float, length:float):
982
+ """ Compute the flow rate based on Bernoulli equation
983
+ with inertia term
984
+
985
+ $ \frac{\partial Q}{\partial t} + \frac {g A}{L} (Head_do - Head_up + Losses) = 0 $
986
+
987
+ $ Losses = k \frac {U^2}{2 g} = k \frac {Q |Q|}{A^2 2 g} $
988
+ """
989
+
990
+ # new_q, delta = compute_q_wo_inertia(zup,hup,qup,zdo,hdo,qdo,A_bridge,k)
991
+
992
+ delta = compute_delta_head(zup,hup,qup,zdo,hdo,qdo)
993
+
994
+ if delta > 1.:
995
+ pass
996
+
997
+ inv_dt = 1./ dt
998
+ return (inv_dt * q_prev + g * A_bridge * delta / length) / (inv_dt + compute_losses_semi_implicit(q_prev, A_bridge, k) / length)
999
+
1000
+ def _update_top(z_start:np.ndarray, z_end:np.ndarray, move_time:float, move_totaltime:float, reverse:bool=False):
1001
+
1002
+ if reverse:
1003
+ pond = (move_totaltime - move_time) / move_totaltime
1004
+ else:
1005
+ pond = move_time / move_totaltime
1006
+
1007
+ loc_z = z_end * pond + z_start * (1.-pond)
1008
+
1009
+ return pond, loc_z
1010
+
1011
+ def update_top_q(z:np.ndarray, h:np.ndarray, q:np.ndarray, z_bridge:np.ndarray,
1012
+ idx_up:int, idx_do:int,
1013
+ survey_up:int, survey_do:int,
1014
+ A:float, k:float, q_prev:float, dt:float,
1015
+ move_time:float, move_totaltime:float, move_restore:bool,
1016
+ z_roof_start:np.ndarray, z_roof_end:np.ndarray,
1017
+ z_bath_start:np.ndarray, z_bath_end:np.ndarray,
1018
+ update_top:bool=True, stop_motion:bool = False):
1019
+
1020
+ if stop_motion:
1021
+ move_time = move_totaltime
1022
+
1023
+ zup = z[survey_up]
1024
+ zdo = z[survey_do]
1025
+
1026
+ hup = h[survey_up]
1027
+ hdo = h[survey_do]
1028
+
1029
+ qup = q[survey_up]
1030
+ qdo = q[survey_do]
1031
+
1032
+ # Update the flow rate considering inertia (generalized Bernoulli)
1033
+ qtot_infil_exfil = compute_q_w_inertia(zup, hup, qup,
1034
+ zdo, hdo, qdo,
1035
+ A, k, q_prev, dt,
1036
+ length=dx *(idx_do-idx_up-1))
1037
+
1038
+ if update_top:
1039
+
1040
+ pond, z_bridge[bridge] = _update_top(z_roof_start, z_roof_end, move_time, move_totaltime, move_restore)
1041
+ pond, z[bridge] = _update_top(z_bath_start, z_bath_end, move_time, move_totaltime, move_restore)
1042
+
1043
+ # if move_restore:
1044
+ # zref_up = zup + hup - .15
1045
+ # z[bridge] = np.minimum(z[bridge], zref_up)
1046
+
1047
+ else:
1048
+ pond = 0. if move_restore else 1.
1049
+
1050
+
1051
+ if stop_motion and move_restore:
1052
+ infil_exfil = None
1053
+ else:
1054
+ # Applying the infiltration/exfiltration linearly according to the topo-bathymetry evolution
1055
+ q_infil_exfil = qtot_infil_exfil * pond
1056
+ infil_exfil = (idx_up, idx_do, q_infil_exfil, qup/hup, pond, k)
1057
+
1058
+ return infil_exfil, z_bridge, z, qtot_infil_exfil
1059
+
1060
+ bridge = np.where(z_roof != z_roof_null)[0]
1061
+
1062
+ idx_up = bridge[0]-1
1063
+ idx_do = bridge[-1]+1
1064
+
1065
+ survey_up = idx_up-1
1066
+ survey_do = idx_do+1
1067
+
1068
+ z_overflow = z_deck[bridge].max() + min_overflow
1069
+
1070
+ totaltime = 2*3600
1071
+ total_compute = totaltime + 1800 + 2.
1072
+
1073
+ infil_exfil = None
1074
+
1075
+ bridge_in_motion = False
1076
+ filled_bridge = False
1077
+ emptying_bridge = False
1078
+ motion_completed = True
1079
+ local_motion_time = 0.
1080
+ total_motion_time = motion_duration
1081
+
1082
+ q_infil_t_current = 0.
1083
+
1084
+ delta_res_time_def = 30.
1085
+
1086
+ if scenario_bc == 'unsteady_downstream_bc':
1087
+ dh_cl = 7.
1088
+ dq_cl = 0.
1089
+
1090
+ elif scenario_bc == 'unsteady_downstream_bc_culvert':
1091
+ dh_cl = 2.
1092
+ dq_cl = 0.5
1093
+
1094
+ elif scenario_bc == 'hydrograph_culvert':
1095
+ dh_cl = 1.
1096
+ dq_cl = 3.
1097
+
1098
+ elif scenario_bc == 'hydrograph':
1099
+ dh_cl = 2.
1100
+ dq_cl = 8.
1101
+
1102
+ elif scenario_bc == 'hydrograph_2steps':
1103
+ dh_cl = 2.
1104
+ dq_cl = 8.
1105
+
1106
+ elif scenario_bc == 'hydrograph_2steps_culvert':
1107
+ dh_cl = 1.
1108
+ dq_cl = 10.
1109
+
1110
+ elif scenario_bc == 'Gauss':
1111
+ dh_cl = 2.
1112
+ dq_cl = 8.
1113
+
1114
+ with tqdm(total=total_compute, desc=scenario_bc) as pbar:
1115
+ t = 0.
1116
+
1117
+ res_time = 0.
1118
+ update_time = 0.
1119
+
1120
+ delta_res_time = delta_res_time_def
1121
+
1122
+ z_roof_start = None
1123
+ z_roof_end = None
1124
+ z_bath_start = None
1125
+ z_bath_end = None
1126
+
1127
+ while t < total_compute:
1128
+
1129
+ dt = compute_dt(dx, h, q, CN)
1130
+
1131
+ if scenario_bc == 'unsteady_downstream_bc' or scenario_bc == 'unsteady_downstream_bc_culvert':
1132
+ # The downstream boundary condition evolves linearly
1133
+ # from h0 to h0 + dh_cl in totaltime/2 seconds
1134
+ # keeps the value h0 + dh_cl during 1000 seconds
1135
+ # and then from h0 + dh_cl to h0 in totaltime/2 seconds
1136
+ #
1137
+ # ____
1138
+ # / \
1139
+ # / \
1140
+ # ------ / \
1141
+ # _/ \____
1142
+
1143
+ if t < totaltime/2:
1144
+ h_cl = h0 + dh_cl * t/totaltime*2
1145
+ q_cl = q0 + dq_cl * t/totaltime*2
1146
+ else:
1147
+ if t < totaltime/2 + 1000.:
1148
+ h_cl = h0 + dh_cl
1149
+ q_cl = q0 + dq_cl
1150
+ else:
1151
+ h_cl = h0 + dh_cl - dh_cl * (t - (totaltime/2+1000.))/(totaltime/2 - 1000.)
1152
+ q_cl = q0 + dq_cl - dq_cl * (t - (totaltime/2+1000.))/(totaltime/2 - 1000.)
1153
+
1154
+ elif scenario_bc == 'hydrograph' or scenario_bc == 'hydrograph_culvert':
1155
+ # The downstream boundary condition evolves linearly
1156
+ # from h0 to h0 + dh_cl in totaltime/2 seconds
1157
+ # keeps the value h0 + dh_cl during 1000 seconds
1158
+ # and then from h0 + dh_cl to h0 - dh_cl/2 in totaltime/2 seconds
1159
+ #
1160
+ # The upstream boundary condition evolves linearly
1161
+ # from q0 to q0 + dq_cl in totaltime/2 seconds
1162
+ # keeps the value q0 + dq_cl during 1000 seconds
1163
+ # and then from q0 + dq_cl to q0 - dq_cl/2 in totaltime/2 seconds
1164
+ #
1165
+ # _____ ____
1166
+ # / \ / \
1167
+ # / \ / \
1168
+ # / \ / \
1169
+ # _/ \ __/ \
1170
+ # \ \
1171
+ # \______ \____
1172
+
1173
+ if t < totaltime/2:
1174
+ h_cl = h0 + dh_cl * t/totaltime*2
1175
+ q_cl = q0 + dq_cl * t/totaltime*2
1176
+ else:
1177
+ if t < totaltime/2 + 1000.:
1178
+ h_cl = h0 + dh_cl
1179
+ q_cl = q0 + dq_cl
1180
+ elif t < totaltime:
1181
+ h_cl = h0 + dh_cl - dh_cl * (t - (totaltime/2+1000.))/(totaltime/2 - 1000.) * 1.5
1182
+ q_cl = q0 + dq_cl - dq_cl * (t - (totaltime/2+1000.))/(totaltime/2 - 1000.) * 1.5
1183
+ else:
1184
+ h_cl = h0 - dh_cl / 2.
1185
+ q_cl = q0 - dq_cl / 2.
1186
+
1187
+ elif scenario_bc == 'hydrograph_2steps' or scenario_bc == 'hydrograph_2steps_culvert':
1188
+ # Same as hydrograph but the downstream boundary condition
1189
+ # evolves linearly a second time during peek flow
1190
+
1191
+ if t < totaltime/2:
1192
+ h_cl = h0 + dh_cl * t/totaltime*2
1193
+ q_cl = q0 + dq_cl * t/totaltime*2
1194
+ else:
1195
+ if t < totaltime/2 + 500.:
1196
+ h_cl = h0 + dh_cl + dh_cl * (t - (totaltime/2))/(500.)
1197
+ q_cl = q0 + dq_cl
1198
+ elif t < totaltime/2 + 1000.:
1199
+ h_cl = h0 + 2* dh_cl - dh_cl * (t - (totaltime/2+500.))/(500.)
1200
+ q_cl = q0 + dq_cl
1201
+ elif t < totaltime:
1202
+ h_cl = h0 + dh_cl - dh_cl * (t - (totaltime/2+1000.))/(totaltime/2 - 1000.) * 1.5
1203
+ q_cl = q0 + dq_cl - dq_cl * (t - (totaltime/2+1000.))/(totaltime/2 - 1000.) * 1.5
1204
+ else:
1205
+ h_cl = h0 - dh_cl / 2.
1206
+ q_cl = q0 - dq_cl / 2.
1207
+
1208
+ elif scenario_bc == 'Gauss':
1209
+ # The downstream and upstream boundary conditions evolve
1210
+ # according to a Gaussian function
1211
+
1212
+ h_cl = h0 + dh_cl * np.exp(-((t-totaltime/2)**.5)/3600)
1213
+ q_cl = q0 + dq_cl * np.exp(-((t-totaltime/2)**.5)/3600)
1214
+
1215
+ # Predictor step
1216
+ Euler_RK_bridge(h, h_pred, q, q_pred, h, q, h_border, q_border, z, z_border,
1217
+ dt, dx, h_cl, q_cl,
1218
+ n, u_border, h_center, u_center, z_roof, z_roof_border,
1219
+ press_mode, infil_exfil)
1220
+
1221
+ # Corrector step
1222
+ Euler_RK_bridge(h, h_corr, q, q_corr, h_pred, q_pred, h_border, q_border, z, z_border,
1223
+ dt, dx, h_cl, q_cl,
1224
+ n, u_border, h_center, u_center, z_roof, z_roof_border,
1225
+ press_mode, infil_exfil)
1226
+
1227
+ # Update -- Mean for second order in time
1228
+ h = (h_pred + h_corr)/2.
1229
+ q = (q_pred + q_corr)/2.
1230
+
1231
+ if t >= res_time:
1232
+ res.append((t, h.copy(), q.copy(), z.copy(), z_roof.copy(), infil_exfil if infil_exfil is not None else (idx_up, idx_do, 0., 0., 0., 0.)))
1233
+ res_time += delta_res_time
1234
+
1235
+ if updating_time_interval == 0. or t> update_time:
1236
+ update_time += updating_time_interval
1237
+
1238
+ if z[survey_up] + h[survey_up] > z_overflow:
1239
+ # Overflow
1240
+
1241
+ # Convert Bridge into topography
1242
+ # add infiltration/exfiltration
1243
+ if not bridge_in_motion and motion_completed and not filled_bridge:
1244
+
1245
+ # Movement must be initiated...
1246
+
1247
+ # Decreasing the interval of time for the
1248
+ # saving to be more precise when plotting
1249
+ delta_res_time = delta_res_time_def / 5.
1250
+
1251
+ bridge_in_motion = True
1252
+ emptying_bridge = False
1253
+ motion_completed = False
1254
+
1255
+ local_motion_time = 0.
1256
+ starting_time = t
1257
+
1258
+ # Keeping the old values -- Can be useful when restoring
1259
+ old_z_roof = z_roof.copy()
1260
+ old_z = z.copy()
1261
+
1262
+ # Reference section of the bridge...
1263
+ # Keeping the minimum distance between the bridge and the floor
1264
+ A_bridge = np.min(z_roof[bridge] - z[bridge])
1265
+
1266
+
1267
+ # Computing global head loss coefficient associated to the bridge and the reference section
1268
+ k_bridge = compute_k_mean(z[survey_up], h[survey_up], q[survey_up],
1269
+ z[survey_do], h[survey_do], q[survey_do],
1270
+ A_bridge)
1271
+
1272
+ # Starting the motion...
1273
+ # - the bridge's roof is going up
1274
+ # - the bridge's floor is going up
1275
+
1276
+ z_roof_start = old_z_roof[bridge]
1277
+ z_roof_end = z_roof_null
1278
+ z_bath_start = old_z[bridge]
1279
+ z_bath_end = z_deck[bridge]
1280
+
1281
+ # Mean Flow rate under the bridge
1282
+ q_infil_t_current = np.mean(q[bridge])
1283
+
1284
+ infil_exfil, z_roof, z, q_infil_t_current = update_top_q(z, h, q, z_roof,
1285
+ idx_up, idx_do,
1286
+ survey_up, survey_do,
1287
+ A_bridge, k_bridge, q_infil_t_current,
1288
+ dt,
1289
+ local_motion_time, total_motion_time,
1290
+ emptying_bridge,
1291
+ z_roof_start, z_roof_end, z_bath_start, z_bath_end)
1292
+
1293
+ else:
1294
+ if not motion_completed:
1295
+ # Movement is initiated but not finished...
1296
+
1297
+ # Updating the local time
1298
+ # local_motion_time += dt
1299
+ local_motion_time = t - starting_time
1300
+
1301
+ if local_motion_time > total_motion_time:
1302
+ # Total time is reached...
1303
+ # ... so terminate the movement
1304
+
1305
+ delta_res_time = delta_res_time_def
1306
+
1307
+ bridge_in_motion = False
1308
+ motion_completed = True
1309
+ filled_bridge = not filled_bridge
1310
+
1311
+ infil_exfil, z_roof, z, q_infil_t_current = update_top_q(z, h, q, z_roof,
1312
+ idx_up, idx_do,
1313
+ survey_up, survey_do,
1314
+ A_bridge, k_bridge, q_infil_t_current, dt,
1315
+ local_motion_time, total_motion_time, emptying_bridge,
1316
+ z_roof_start, z_roof_end, z_bath_start, z_bath_end,
1317
+ stop_motion= True)
1318
+
1319
+ local_motion_time = 0.
1320
+ else:
1321
+ # Total time is not reached...
1322
+ # ... so continue the movement
1323
+
1324
+ infil_exfil, z_roof, z, q_infil_t_current = update_top_q(z, h, q, z_roof,
1325
+ idx_up, idx_do,
1326
+ survey_up, survey_do,
1327
+ A_bridge, k_bridge, q_infil_t_current, dt,
1328
+ local_motion_time, total_motion_time, emptying_bridge,
1329
+ z_roof_start, z_roof_end, z_bath_start, z_bath_end)
1330
+ else:
1331
+ # Movement is done...
1332
+
1333
+ if infil_exfil is not None:
1334
+
1335
+ # Updating the infiltration discharge according to head difference
1336
+ infil_exfil, z_roof, z, q_infil_t_current = update_top_q(z, h, q, z_roof,
1337
+ idx_up, idx_do,
1338
+ survey_up, survey_do,
1339
+ A_bridge, k_bridge, q_infil_t_current, dt,
1340
+ local_motion_time, total_motion_time, emptying_bridge,
1341
+ z_roof_start, z_roof_end, z_bath_start, z_bath_end,
1342
+ update_top=False)
1343
+
1344
+
1345
+ else:
1346
+ # No overflow
1347
+
1348
+ if bridge_in_motion:
1349
+ # But movement is initiated...
1350
+ # local_motion_time += dt
1351
+ local_motion_time = t - starting_time
1352
+
1353
+ if local_motion_time > total_motion_time:
1354
+
1355
+ delta_res_time = delta_res_time_def
1356
+
1357
+ # Total time is reached...
1358
+ # ... so terminate the movement
1359
+
1360
+ bridge_in_motion = False
1361
+ motion_completed = True
1362
+ filled_bridge = not filled_bridge
1363
+
1364
+ infil_exfil, z_roof, z, q_infil_t_current = update_top_q(z, h, q, z_roof,
1365
+ idx_up, idx_do,
1366
+ survey_up, survey_do,
1367
+ A_bridge, k_bridge, q_infil_t_current, dt,
1368
+ local_motion_time, total_motion_time, emptying_bridge,
1369
+ z_roof_start, z_roof_end, z_bath_start, z_bath_end,
1370
+ stop_motion= True)
1371
+
1372
+ local_motion_time = 0.
1373
+
1374
+ else:
1375
+ # Total time is not reached...
1376
+ # ... so continue the movement
1377
+
1378
+ infil_exfil, z_roof, z, q_infil_t_current = update_top_q(z, h, q, z_roof,
1379
+ idx_up, idx_do,
1380
+ survey_up, survey_do,
1381
+ A_bridge, k_bridge, q_infil_t_current, dt,
1382
+ local_motion_time, total_motion_time, emptying_bridge,
1383
+ z_roof_start, z_roof_end, z_bath_start, z_bath_end)
1384
+
1385
+ else:
1386
+
1387
+ if infil_exfil is not None:
1388
+
1389
+ if motion_completed:
1390
+ # The bridge is not moving and the infiltration/exfiltration exists
1391
+
1392
+ # We can start to restore the bridge as it was before the overflow...
1393
+
1394
+ delta_res_time = delta_res_time_def / 5.
1395
+
1396
+ bridge_in_motion = True
1397
+ local_motion_time = 0.
1398
+ motion_completed = False
1399
+
1400
+ emptying_bridge = True
1401
+ starting_time = t
1402
+
1403
+ infil_exfil, z_roof, z, q_infil_t_current = update_top_q(z, h, q, z_roof,
1404
+ idx_up, idx_do,
1405
+ survey_up, survey_do,
1406
+ A_bridge, k_bridge, q_infil_t_current, dt,
1407
+ local_motion_time, total_motion_time, emptying_bridge,
1408
+ z_roof_start, z_roof_end, z_bath_start, z_bath_end,
1409
+ )
1410
+
1411
+ t+=dt
1412
+ pbar.update(dt)
1413
+
1414
+
1415
+ return res
1416
+
1417
+
1418
+ # --------------------
1419
+ # END Problems section
1420
+ # --------------------
1421
+
1422
+
1423
+ # ----------------
1424
+ # PLOTTING SECTION
1425
+ # ----------------
1426
+ import matplotlib.animation as animation
1427
+
1428
+ def plot_bridge(ax:plt.Axes, x:np.ndarray,
1429
+ h1:np.ndarray, h2:np.ndarray,
1430
+ q1:np.ndarray, q2:np.ndarray,
1431
+ z:np.ndarray, z_bridge:np.ndarray,
1432
+ hu:float):
1433
+
1434
+ u1=np.zeros_like(q1)
1435
+ u2=np.zeros_like(q1)
1436
+
1437
+ u1[1:-1] = q1[1:-1]/h1[1:-1]
1438
+ u2[1:-1] = q2[1:-1]/np.minimum(h2[1:-1],z_bridge[1:-1]-z[1:-1])
1439
+
1440
+ ax.plot(x[1:-1], z[1:-1], label = 'z')
1441
+ ax.plot(x[1:-1], z_bridge[1:-1], label = 'bridge')
1442
+
1443
+ ax.plot(x[1:-1], z[1:-1] + h1[1:-1], label = 'z + h1')
1444
+ ax.plot(x[1:-1], z[1:-1] + h2[1:-1], label = 'z + h2')
1445
+
1446
+ ax.plot(x[1:-1], q1[1:-1], label = 'q')
1447
+
1448
+ under_bridge = np.where(h2 > z_bridge - z)[0]
1449
+ free_surface = np.where(h2 <= z_bridge - z)[0]
1450
+
1451
+ ax.plot(x[1:-1], z[1:-1] + h1[1:-1] + u1[1:-1]**2/2/9.81, label = 'head1')
1452
+
1453
+ ax.plot(x[1:-1], z[1:-1] + h2[1:-1] + u2[1:-1]**2/2/9.81, label = 'head2')
1454
+ # ax.plot(x[free_surface], (z[free_surface] + h2[free_surface] + u2[free_surface]**2/2/9.81) * h2[free_surface], label = 'head2 free surface')
1455
+
1456
+ if hu != 99999.:
1457
+ ax.plot(x[1:-1], z[1:-1]+hu, linestyle='--', label = 'h uniform')
1458
+ ax.legend()
1459
+
1460
+ def plot_hedge(ax:plt.Axes, x:np.ndarray,
1461
+ h1:np.ndarray, h2:np.ndarray,
1462
+ q1:np.ndarray, q2:np.ndarray,
1463
+ z:np.ndarray, hu:float,
1464
+ theta:np.ndarray):
1465
+
1466
+ u1=np.zeros_like(q1)
1467
+ u2=np.zeros_like(q1)
1468
+
1469
+ u1[1:-1] = q1[1:-1]/h1[1:-1]
1470
+ u2[1:-1] = q2[1:-1]/(h2[1:-1]*theta[1:-1])
1471
+
1472
+ ax.plot(x[1:-1], z[1:-1], label = 'z')
1473
+
1474
+ ax.plot(x[1:-1], z[1:-1] + h1[1:-1], label = 'z + h1')
1475
+ ax.plot(x[1:-1], z[1:-1] + h2[1:-1], label = 'z + h2')
1476
+
1477
+ ax.plot(x[1:-1], u1[1:-1], label = 'u1')
1478
+ ax.plot(x[1:-1], u2[1:-1], label = 'u2')
1479
+
1480
+ # ax.plot(x[1:-1], z[1:-1] + h1[1:-1] + u1[1:-1]**2/2/9.81, label = 'head1')
1481
+ # ax.plot(x[1:-1], z[1:-1] + h2[1:-1] + u2[1:-1]**2/2/9.81, label = 'head2')
1482
+
1483
+ ax.plot(x[1:-1], z[1:-1]+hu, linestyle='--', label = 'h uniform')
1484
+ ax.legend()
1485
+
1486
+
1487
+ def animate_bridge_unsteady_topo(dom, poly_bridge_x, poly_bridge_y, res:list[float, np.ndarray, np.ndarray, np.ndarray, np.ndarray, tuple[int,int,float,float,float]],
1488
+ x:np.ndarray, z_ini:np.ndarray, hu:float, z_null:float, length:float, title:str= "Bridge", motion_duration=300.):
1489
+ fig, axes = plt.subplots(2,1)
1490
+
1491
+ all_q_middle = [cur[2][len(dom)//2] for cur in res]
1492
+ all_q_inf_ex = [cur[5][2] for cur in res]
1493
+
1494
+ all_q_up = [cur[2][1] for cur in res]
1495
+ all_q_down = [cur[2][-2] for cur in res]
1496
+
1497
+ all_times = [cur[0] for cur in res]
1498
+ idx_Froude = [int(len(dom)*cur) for cur in np.linspace(0,100,11,True)/100]
1499
+ idx_Froude[0] += 5
1500
+ idx_Froude[-1] -= 5
1501
+ x_Froude = x[idx_Froude]
1502
+ Froude = np.zeros_like(x_Froude)
1503
+
1504
+ def update(frame):
1505
+ ax:plt.Axes
1506
+ ax = axes[0]
1507
+ ax.clear()
1508
+
1509
+ z:np.ndarray
1510
+ t, h, q, z, z_roof, inf_ex = res[frame]
1511
+
1512
+ for i, idx in enumerate(idx_Froude):
1513
+ if h[idx]==0.:
1514
+ Froude[i] = 0.
1515
+ else:
1516
+ Froude[i] = q[idx] / h[idx] /np.sqrt(9.81*h[idx])
1517
+
1518
+ ax.fill_between(x[1:-1], z[1:-1], z[1:-1] + h[1:-1], color='blue', alpha=0.5)
1519
+ ax.plot(x[1:-1], z[1:-1] + h[1:-1], label='water level [m]', color='blue')
1520
+
1521
+ ax.fill_between(x[1:-1], np.ones(z.shape)[1:-1] * z.min(), z[1:-1], color='brown', alpha=0.5)
1522
+ ax.plot(x[1:-1], z[1:-1], label='bottom level [m]', color='brown')
1523
+
1524
+ slice_roof = z_roof != z_null
1525
+ ax.fill_between(x[slice_roof], z_roof[slice_roof], np.ones(z.shape)[slice_roof] * z_null, color='grey', alpha=0.5)
1526
+ ax.plot(x[slice_roof], z_roof[slice_roof], label='roof level [m]', color='grey')
1527
+
1528
+ # ax.plot(x[1:-1], z_ini[1:-1] + hu, linestyle='--', label='h uniform')
1529
+ ax.plot(x[1:-1], q[1:-1], linestyle='-', label='flow rate [$m^2/s$]', color='black', linewidth=1.5)
1530
+ ax.legend(loc='upper right')
1531
+
1532
+ ax.fill(poly_bridge_x, poly_bridge_y, color='black', alpha=0.8)
1533
+
1534
+ q_middle = q[len(dom)//2]
1535
+ q_inf_ex = inf_ex[2]
1536
+
1537
+ txt = f'Total flow rate {q_middle+q_inf_ex:.2f} $m^2/s$'
1538
+ txt += f'\nOverflow = {q_middle:.2f} $m^2/s$ - {q_middle/(q_inf_ex+q_middle)*100:.2f} %'
1539
+ txt += f'\nUnderflow = {q_inf_ex:.2f} $m^2/s$ - {q_inf_ex/(q_inf_ex+q_middle)*100:.2f} %'
1540
+
1541
+ in_txt = '$k_{loss}$ ='
1542
+ txt += f'\n\nMotion time = {motion_duration:.1f} s -- {in_txt} {inf_ex[-1]:.2f}'
1543
+
1544
+ ax.text(x[len(dom)//2+8], 11., txt, fontsize=9)
1545
+
1546
+ for posFroude, curFroude in zip(x_Froude, Froude):
1547
+ ax.text(posFroude, 8., f'Fr = {curFroude:.2f}', fontsize=9, horizontalalignment='center')
1548
+
1549
+ ax.set_xlim(0, 500)
1550
+ ax.set_xticks(np.arange(0, 501, 50))
1551
+
1552
+ ax.grid(axis='x')
1553
+
1554
+ ax.set_ylim(0, 20)
1555
+ ax.set_title(f'{title} - Length = {length:.1f} m - Time = {t:.1f} s')
1556
+
1557
+ ax = axes[1]
1558
+ ax.clear()
1559
+
1560
+ ax.plot(all_times[:frame], all_q_middle[:frame], label='Overflow', color='blue')
1561
+ ax.plot(all_times[:frame], all_q_inf_ex[:frame], label='Underflow', color='red')
1562
+ ax.plot(all_times[:frame], [qinf + qmiddle for qinf, qmiddle in zip(all_q_inf_ex[:frame], all_q_middle[:frame])], label='Total', color='black')
1563
+
1564
+ ax.plot(all_times[:frame], all_q_up[:frame], label='Upstream', color='black', linestyle='--', linewidth=1.)
1565
+ ax.plot(all_times[:frame], all_q_down[:frame], label='Downstream', color='black', linestyle='-.', linewidth=1.)
1566
+
1567
+ ax.legend(loc='upper right')
1568
+ ax.set_title('Flow rate')
1569
+ ax.set_xlabel('Time [s]')
1570
+ ax.set_ylabel('Flow rate [$m^2/s$]')
1571
+
1572
+ ax.set_ylim(0, 20)
1573
+ ax.set_xlim(0, all_times[-1])
1574
+ ax.set_xticks(np.arange(0, all_times[-1]+10, 900))
1575
+ ax.grid()
1576
+
1577
+ fig.set_size_inches(20,8)
1578
+ update(0)
1579
+ fig.tight_layout()
1580
+
1581
+ ani = animation.FuncAnimation(fig, update, frames=len(res), repeat=True)
1582
+
1583
+ return ani
1584
+
1585
+
1586
+ # -------------
1587
+ # REAL PROBLEMS
1588
+ # -------------
1589
+
1590
+ def lake_at_rest():
1591
+ """ Compute Lake at rest problem
1592
+
1593
+ The problem is a simple steady state problem with a bridge in the middle of the domain.
1594
+ The bridge is a simple flat bridge with a height of 2 m.
1595
+
1596
+ No discharge and no water movement is expected.
1597
+ """
1598
+
1599
+ length = 500.
1600
+ dx = 1.
1601
+ CN = 0.4
1602
+ h0 = 4. # Initial water depth
1603
+ q0 = 0. # Initial discharge
1604
+ n = 0.025
1605
+ slope = 0 #1e-4
1606
+ press_mode = 4
1607
+
1608
+ hu = 0
1609
+
1610
+ dom, x, z = domain(length, dx, slope)
1611
+ x = np.array(x)
1612
+
1613
+ # Bridge roof level is 10 m everywhere except in the middle of the domain
1614
+ z_bridge = np.ones_like(z) * 10.
1615
+ z_bridge[len(dom)//2-5:len(dom)//2+5] = z[len(dom)//2-5:len(dom)//2+5] + 2.
1616
+
1617
+ fig, axes = plt.subplots(3,1)
1618
+
1619
+ # free surface flow
1620
+ h1, q1 = problem(dom, z, h0, q0, dx, CN, n)
1621
+ # partially pressurized flow
1622
+ h2, q2 = problem_bridge(dom, z, z_bridge, h0, q0, dx, CN, n, press_mode=press_mode)
1623
+
1624
+ assert np.allclose(h1[1:-1], h0), 'Free surface flow is not steady state'
1625
+ assert np.allclose(q1[1:-1], q0), 'Free surface flow is not steady state'
1626
+ assert np.allclose(h2[1:-1], h0), 'Partially pressurized flow is not steady state'
1627
+ assert np.allclose(q2[1:-1], q0), 'Partially pressurized flow is not steady state'
1628
+
1629
+ plot_bridge(axes[0], x, h1, h2, q1, q2, z, z_bridge, hu)
1630
+
1631
+ # increasing water depth
1632
+ h0 += 1.
1633
+
1634
+ # free surface flow
1635
+ h1, q1 = problem(dom, z, h0+1., q0, dx, CN, n)
1636
+ # partially pressurized flow
1637
+ h2, q2 = problem_bridge(dom, z, z_bridge, h0, q0, dx, CN, n, press_mode=press_mode)
1638
+
1639
+ assert np.allclose(h1[1:-1], h0+1.), 'Free surface flow is not steady state'
1640
+ assert np.allclose(q1[1:-1], q0), 'Free surface flow is not steady state'
1641
+ assert np.allclose(h2[1:-1], h0), 'Partially pressurized flow is not steady state'
1642
+ assert np.allclose(q2[1:-1], q0), 'Partially pressurized flow is not steady state'
1643
+
1644
+ plot_bridge(axes[1], x, h1, h2, q1, q2, z, z_bridge, hu)
1645
+
1646
+ # increasing water depth
1647
+ h0 += 1.
1648
+
1649
+ # free surface flow
1650
+ h1, q1 = problem(dom, z, h0+2., q0, dx, CN, n)
1651
+ # partially pressurized flow
1652
+ h2, q2 = problem_bridge(dom, z, z_bridge, h0, q0, dx, CN, n, press_mode=press_mode)
1653
+
1654
+ assert np.allclose(h1[1:-1], h0+2.), 'Free surface flow is not steady state'
1655
+ assert np.allclose(q1[1:-1], q0), 'Free surface flow is not steady state'
1656
+ assert np.allclose(h2[1:-1], h0), 'Partially pressurized flow is not steady state'
1657
+ assert np.allclose(q2[1:-1], q0), 'Partially pressurized flow is not steady state'
1658
+
1659
+ plot_bridge(axes[2], x, h1, h2, q1, q2, z, z_bridge, hu)
1660
+
1661
+ fig.set_size_inches(15, 10)
1662
+ fig.tight_layout()
1663
+
1664
+ return fig, axes
1665
+
1666
+
1667
+ def water_line():
1668
+ """ Compute Water line problems
1669
+
1670
+ Length = 500 m
1671
+ dx = 1 m
1672
+ CN = 0.4
1673
+ h0 = 4 m
1674
+ q0 = 7 m^2/s
1675
+ n = 0.025
1676
+ slope = 1e-4
1677
+ """
1678
+
1679
+ length = 500.
1680
+ dx = 1.
1681
+ CN = 0.4
1682
+ h0 = 4.
1683
+ q0 = 7.
1684
+ n = 0.025
1685
+ slope = 1e-4
1686
+ press_mode = 4
1687
+
1688
+ dom, x, z = domain(length, dx, slope)
1689
+ x = np.array(x)
1690
+
1691
+ hu = uniform_waterdepth(slope, q0, n)
1692
+
1693
+ # bridge roof level is 10 m everywhere except in the middle of the domain
1694
+ # where the bridge is located.
1695
+ # The bridge is a flat bridge with a height of 2 m.
1696
+ z_bridge = np.ones_like(z) * 10.
1697
+ z_bridge[len(dom)//2-5:len(dom)//2+5] = z[len(dom)//2-5:len(dom)//2+5] + 2.
1698
+
1699
+ fig, axes = plt.subplots(3,1)
1700
+
1701
+ h1, q1 = problem(dom, z, h0, q0, dx, CN, n)
1702
+ h2, q2 = problem_bridge(dom, z, z_bridge, h0, q0, dx, CN, n, press_mode=press_mode)
1703
+
1704
+ plot_bridge(axes[0], x, h1, h2, q1, q2, z, z_bridge, hu)
1705
+
1706
+ h1, q1 = problem(dom, z, h0+1., q0, dx, CN, n)
1707
+ h2, q2 = problem_bridge(dom, z, z_bridge, h0+1., q0, dx, CN, n, press_mode=press_mode)
1708
+
1709
+ plot_bridge(axes[1], x, h1, h2, q1, q2, z, z_bridge, hu)
1710
+
1711
+ h1, q1 = problem(dom, z, h0+2., q0, dx, CN, n)
1712
+ h2, q2 = problem_bridge(dom, z, z_bridge, h0+2., q0, dx, CN, n, press_mode=press_mode)
1713
+
1714
+ plot_bridge(axes[2], x, h1, h2, q1, q2, z, z_bridge, hu)
1715
+
1716
+ fig.set_size_inches(20, 10)
1717
+ fig.tight_layout()
1718
+
1719
+ return fig, axes
1720
+
1721
+ def water_line_noloss_noslope():
1722
+ """ Compute Water line problems
1723
+
1724
+ Length = 500 m
1725
+ dx = 1 m
1726
+ CN = 0.4
1727
+ h0 = 4 m
1728
+ q0 = 7 m^2/s
1729
+ n = 0.0
1730
+ slope = 0.0
1731
+ """
1732
+
1733
+ length = 500.
1734
+ dx = 1.
1735
+ CN = 0.4
1736
+ h0 = 4.
1737
+ q0 = 7.
1738
+ n = 0.
1739
+ slope = 0.
1740
+ press_mode = 4
1741
+
1742
+ dom, x, z = domain(length, dx, slope)
1743
+ x = np.array(x)
1744
+
1745
+ hu = uniform_waterdepth(slope, q0, n)
1746
+
1747
+ # bridge roof level is 10 m everywhere except in the middle of the domain
1748
+ # where the bridge is located.
1749
+ # The bridge is a flat bridge with a height of 2 m.
1750
+ z_bridge = np.ones_like(z) * 10.
1751
+ z_bridge[len(dom)//2-5:len(dom)//2+5] = z[len(dom)//2-5:len(dom)//2+5] + 2.
1752
+
1753
+ fig, axes = plt.subplots(3,1)
1754
+
1755
+ h1, q1 = problem(dom, z, h0, q0, dx, CN, n)
1756
+ h2, q2 = problem_bridge(dom, z, z_bridge, h0, q0, dx, CN, n, press_mode=press_mode)
1757
+
1758
+ plot_bridge(axes[0], x, h1, h2, q1, q2, z, z_bridge, hu)
1759
+
1760
+ h1, q1 = problem(dom, z, h0+1., q0, dx, CN, n)
1761
+ h2, q2 = problem_bridge(dom, z, z_bridge, h0+1., q0, dx, CN, n, press_mode=press_mode)
1762
+
1763
+ plot_bridge(axes[1], x, h1, h2, q1, q2, z, z_bridge, hu)
1764
+
1765
+ h1, q1 = problem(dom, z, h0+2., q0, dx, CN, n)
1766
+ h2, q2 = problem_bridge(dom, z, z_bridge, h0+2., q0, dx, CN, n, press_mode=press_mode)
1767
+
1768
+ plot_bridge(axes[2], x, h1, h2, q1, q2, z, z_bridge, hu)
1769
+
1770
+ fig.set_size_inches(20, 10)
1771
+ fig.tight_layout()
1772
+
1773
+ return fig, axes
1774
+
1775
+ def water_lines():
1776
+ """ Compute multiple water lines problems.
1777
+
1778
+ Evaluate the head loss due to the bridge and compare
1779
+ to theoretical fomula.
1780
+ """
1781
+
1782
+ length = 500.
1783
+ dx = 1.
1784
+ CN = 0.4
1785
+ h0 = 4.
1786
+ q0 = 7.
1787
+ n = 0. #0.025
1788
+ slope = 0 #1e-4
1789
+ press_mode = 4
1790
+
1791
+ dom, x, z = domain(length, dx, slope)
1792
+ x = np.array(x)
1793
+
1794
+ hu = 0. #uniform_waterdepth(slope, q0, n)
1795
+
1796
+ b_bridge = 2.
1797
+
1798
+ z_bridge = np.ones_like(z) * 10.
1799
+ z_bridge[len(dom)//2-5:len(dom)//2+5] = z[len(dom)//2-5:len(dom)//2+5] + b_bridge
1800
+
1801
+ idx_before_bridge = len(dom)//2-5 -2
1802
+ idx_after_bridge = len(dom)//2+5 +2
1803
+ idx_bridge = len(dom)//2
1804
+
1805
+ h0 = 4.5
1806
+ res = problem_bridge_multiple_steadystates(dom, z, z_bridge, h0, 3.5, 20., dx, CN, n, press_mode=press_mode)
1807
+
1808
+ fig, axes = plt.subplots(2,1)
1809
+
1810
+ ax:plt.Axes
1811
+
1812
+ ax = axes[0]
1813
+ for cur in res:
1814
+ ax.plot(x[1:-1], z[1:-1]+cur[1][1:-1], label = f't = {cur[0]:.1f}')
1815
+ ax.plot(x[1:-1], z[1:-1]+hu, linestyle='--', label = 'h uniform')
1816
+ ax.plot(x[1:-1], z_bridge[1:-1], label = 'bridge')
1817
+ ax.plot(x[1:-1], z[1:-1], label = 'z')
1818
+ ax.legend()
1819
+
1820
+
1821
+ # Head losses
1822
+
1823
+ # pressure before, after and at the bridge
1824
+ press_before = [cur[1][idx_before_bridge] for cur in res]
1825
+ press_after = [cur[1][idx_after_bridge] for cur in res]
1826
+ press_bridge = [cur[1][idx_bridge] for cur in res]
1827
+
1828
+ h_bridge = np.minimum(press_bridge, z_bridge[idx_bridge] - z[idx_bridge])
1829
+
1830
+ # flow rate before, after and at the bridge
1831
+ q_before = [cur[2][idx_before_bridge] for cur in res]
1832
+ q_after = [cur[2][idx_after_bridge] for cur in res]
1833
+ q_bridge = [cur[2][idx_bridge] for cur in res]
1834
+
1835
+ # velocity before, after and at the bridge
1836
+ u_before = [q/h for q,h in zip(q_before, press_before)]
1837
+ u_after = [q/h for q,h in zip(q_after, press_after)]
1838
+ u_bridge = [q/h for q,h in zip(q_bridge, h_bridge)]
1839
+
1840
+ # head before, after and at the bridge
1841
+ head_before = [z[idx_before_bridge] + press_before[i] + u_before[i]**2/2/9.81 for i in range(len(res))]
1842
+ head_after = [z[idx_after_bridge] + press_after[i] + u_after[i]**2/2/9.81 for i in range(len(res))]
1843
+ head_bridge = [z[idx_bridge] + press_bridge[i] + u_bridge[i]**2/2/9.81 for i in range(len(res))]
1844
+
1845
+ # head losses
1846
+ delta_head_total = [head_before[i] - head_after[i] for i in range(len(res))]
1847
+ delta_head_up = [head_before[i] - head_bridge[i] for i in range(len(res))]
1848
+ delta_head_do = [head_bridge[i] - head_after[i] for i in range(len(res))]
1849
+
1850
+ ax = axes[1]
1851
+
1852
+ ax.plot(delta_head_up, [head_loss_contraction(q, h1, h2) for q,h1,h2 in zip(q_bridge, press_before, h_bridge)], marker='*', label = 'Contraction Loss')
1853
+ ax.plot(delta_head_do, [head_loss_enlargment(q, h1, h2) for q,h1,h2 in zip(q_bridge, h_bridge, press_after)], marker='o', label = 'Enlargment Loss')
1854
+ ax.plot(delta_head_total, [head_loss_contract_enlarge(q,h1,h2,h3) for q,h1,h2,h3 in zip(q_bridge, press_before, h_bridge, press_after)], marker='x', label = 'Total Loss')
1855
+
1856
+ ax.set_xlabel('Computed $\Delta H$ [m]')
1857
+ ax.set_ylabel('Theoretical $\Delta H$ [m]')
1858
+
1859
+ ax.plot([0,1],[0,1], linestyle='-', linewidth=2, color='black')
1860
+ ax.set_aspect('equal')
1861
+ ax.legend()
1862
+
1863
+ fig.set_size_inches(20, 10)
1864
+ fig.tight_layout()
1865
+
1866
+ return fig, axes
1867
+
1868
+
1869
+ def unsteady_without_bedmotion():
1870
+ """
1871
+ Compute unsteady problem without bed motion.
1872
+
1873
+ The downstream boundary condition rises and decreases.
1874
+ """
1875
+
1876
+ length = 500.
1877
+ dx = 1.
1878
+ CN = 0.4
1879
+ h0 = 4.
1880
+ q0 = 7.
1881
+ n = 0. #0.025
1882
+ slope = 0 #1e-4
1883
+ press_mode = 4
1884
+
1885
+ dom, x, z = domain(length, dx, slope)
1886
+ x = np.array(x)
1887
+
1888
+ hu = 0. #uniform_waterdepth(slope, q0, n)
1889
+
1890
+ b_bridge = 2.
1891
+
1892
+ z_bridge = np.ones_like(z) * 10.
1893
+ z_bridge[len(dom)//2-5:len(dom)//2+5] = z[len(dom)//2-5:len(dom)//2+5] + b_bridge
1894
+
1895
+ idx_before_bridge = len(dom)//2-5 -2
1896
+ idx_after_bridge = len(dom)//2+5 +2
1897
+ idx_bridge = len(dom)//2
1898
+
1899
+ h0 = 1.5
1900
+ res = problem_bridge_unsteady(dom, z, z_bridge, h0, q0, dx, CN, n, press_mode=press_mode)
1901
+
1902
+ fig, axes = plt.subplots(2,1)
1903
+
1904
+ ax:plt.Axes
1905
+
1906
+ ax = axes[0]
1907
+ for cur in res:
1908
+ ax.plot(x[1:-1], z[1:-1]+cur[1][1:-1], label = f't = {cur[0]:.1f}')
1909
+ ax.plot(x[1:-1], z[1:-1]+hu, linestyle='--', label = 'h uniform')
1910
+ ax.plot(x[1:-1], z_bridge[1:-1], label = 'bridge')
1911
+ ax.plot(x[1:-1], z[1:-1], label = 'z')
1912
+ ax.legend()
1913
+
1914
+ press_before = [cur[1][idx_before_bridge] for cur in res]
1915
+ press_after = [cur[1][idx_after_bridge] for cur in res]
1916
+ press_bridge = [cur[1][idx_bridge] for cur in res]
1917
+
1918
+ h_bridge = np.minimum(press_bridge, z_bridge[idx_bridge] - z[idx_bridge])
1919
+
1920
+ q_before = [cur[2][idx_before_bridge] for cur in res]
1921
+ q_after = [cur[2][idx_after_bridge] for cur in res]
1922
+ q_bridge = [cur[2][idx_bridge] for cur in res]
1923
+
1924
+ u_before = [q/h for q,h in zip(q_before, press_before)]
1925
+ u_after = [q/h for q,h in zip(q_after, press_after)]
1926
+ u_bridge = [q/h for q,h in zip(q_bridge, h_bridge)]
1927
+
1928
+ head_before = [z[idx_before_bridge] + press_before[i] + u_before[i]**2/2/9.81 for i in range(len(res))]
1929
+ head_after = [z[idx_after_bridge] + press_after[i] + u_after[i]**2/2/9.81 for i in range(len(res))]
1930
+ head_bridge = [z[idx_bridge] + press_bridge[i] + u_bridge[i]**2/2/9.81 for i in range(len(res))]
1931
+
1932
+ delta_head_total = [head_before[i] - head_after[i] for i in range(len(res))]
1933
+ delta_head_up = [head_before[i] - head_bridge[i] for i in range(len(res))]
1934
+ delta_head_do = [head_bridge[i] - head_after[i] for i in range(len(res))]
1935
+
1936
+ ax = axes[1]
1937
+
1938
+ ax.plot(delta_head_up, [head_loss_contraction(q, h1, h2) for q,h1,h2 in zip(q_bridge, press_before, h_bridge)], marker='*', label = 'Contraction Loss')
1939
+ ax.plot(delta_head_do, [head_loss_enlargment(q, h1, h2) for q,h1,h2 in zip(q_bridge, h_bridge, press_after)], marker='o', label = 'Enlargment Loss')
1940
+ ax.plot(delta_head_total, [head_loss_contract_enlarge(q,h1,h2,h3) for q,h1,h2,h3 in zip(q_bridge, press_before, h_bridge, press_after)], marker='x', label = 'Total Loss')
1941
+
1942
+ ax.set_xlabel('Computed $\Delta H$ [m]')
1943
+ ax.set_ylabel('Theoretical $\Delta H$ [m]')
1944
+
1945
+ ax.plot([0,1],[0,1], linestyle='-', linewidth=2, color='black')
1946
+ ax.set_aspect('equal')
1947
+ ax.legend()
1948
+
1949
+ fig.set_size_inches(20, 10)
1950
+ fig.tight_layout()
1951
+
1952
+ return fig, axes
1953
+
1954
+
1955
+ def unsteady_with_bedmotion(problems:list[int], save_video:bool = False) -> list[animation.FuncAnimation]:
1956
+ """
1957
+ Unsteady problem with bed motion if overflowing occurs.
1958
+
1959
+ :param problems: list of problems to solve
1960
+
1961
+ Problems :
1962
+ 2 - Rectangular bridge - Length = 20 m (will compute 21, 22 and 23)
1963
+ 6 - Rectangular bridge - Length = 60 m (will compute 61, 62 and 63)
1964
+ 7 - V-shape bridge - Length = 20 m (will compute 71, 72 and 73)
1965
+ 8 - U-shape bridge - Length = 20 m (will compute 81, 82 and 83)
1966
+ 9 - Culvert - Length = 100 m (will compute 91, 92 and 93)
1967
+
1968
+ 21 - Rectangular bridge - Length = 20 m - Unsteady downstream bc
1969
+ 22 - Rectangular bridge - Length = 20 m - Hydrograph
1970
+ 23 - Rectangular bridge - Length = 20 m - Hydrograph 2 steps
1971
+
1972
+ 61 - Rectangular bridge - Length = 60 m - Unsteady downstream bc
1973
+ 62 - Rectangular bridge - Length = 60 m - Hydrograph
1974
+ 63 - Rectangular bridge - Length = 60 m - Hydrograph 2 steps
1975
+
1976
+ 71 - V-shape bridge - Length = 20 m - Unsteady downstream bc
1977
+ 72 - V-shape bridge - Length = 20 m - Hydrograph
1978
+ 73 - V-shape bridge - Length = 20 m - Hydrograph 2 steps
1979
+
1980
+ 81 - U-shape bridge - Length = 20 m - Unsteady downstream bc
1981
+ 82 - U-shape bridge - Length = 20 m - Hydrograph
1982
+ 83 - U-shape bridge - Length = 20 m - Hydrograph 2 steps
1983
+
1984
+ 91 - Culvert - Length = 100 m - Unsteady downstream bc
1985
+ 92 - Culvert - Length = 100 m - Hydrograph
1986
+ 93 - Culvert - Length = 100 m - Hydrograph 2 steps
1987
+
1988
+ """
1989
+ length = 500.
1990
+ dx = 1.
1991
+ CN = 0.4
1992
+ h0 = 4.
1993
+ q0 = 7.
1994
+ n = 0.025
1995
+ slope = 0 #1e-4
1996
+ press_mode = 4
1997
+
1998
+ dom, x, z = domain(length, dx, slope)
1999
+ x = np.array(x)
2000
+
2001
+ hu = uniform_waterdepth(slope, q0, n)
2002
+
2003
+ anims=[]
2004
+
2005
+ if 2 in problems or 21 in problems or 22 in problems or 23 in problems or 24 in problems:
2006
+ # Rectangular bridge - Lenght = 20 m
2007
+
2008
+ CN = 0.4
2009
+
2010
+ if 2 in problems:
2011
+ scenarios = ['unsteady_downstream_bc',
2012
+ 'hydrograph',
2013
+ 'hydrograph_2steps',
2014
+ # 'Gauss',
2015
+ ]
2016
+ elif 21 in problems:
2017
+ scenarios = ['unsteady_downstream_bc']
2018
+ elif 22 in problems:
2019
+ scenarios = ['hydrograph']
2020
+ elif 23 in problems:
2021
+ scenarios = ['hydrograph_2steps']
2022
+ elif 24 in problems:
2023
+ scenarios = ['Gauss']
2024
+
2025
+ for scenario in scenarios:
2026
+
2027
+ # UNSTEADY WITH TOPOGRAPHY ADAPTATION
2028
+ motion_duration = 300.
2029
+ len_bridge = 20
2030
+ z_roof_null = 10.
2031
+ min_overflow = 0.25
2032
+
2033
+ h0 = 1.5
2034
+
2035
+ h_under_bridge = 3.5
2036
+ h_deck_bridge = 0.75
2037
+
2038
+ slice_bridge = slice(int(len(dom)//2-len_bridge//2),int(len(dom)//2+len_bridge//2))
2039
+ slice_bridge_up = slice(int(len(dom)//2+(len_bridge//2-1)),int(len(dom)//2-(len_bridge//2+1)),-1)
2040
+
2041
+ z_bridge = np.ones_like(z) * z_roof_null
2042
+ z_deck = np.ones_like(z) * z_roof_null
2043
+
2044
+ for idx in range(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2):
2045
+ z_bridge[idx] = z[idx] + h_under_bridge
2046
+ z_deck[idx] = z_bridge[idx] + h_deck_bridge
2047
+
2048
+ poly_bridge_x = np.concatenate((x[slice_bridge], x[slice_bridge_up]))
2049
+ poly_bridge_y = np.concatenate((z_bridge[slice_bridge], z_deck[slice_bridge_up]))
2050
+
2051
+ z_ini = z.copy()
2052
+
2053
+ res = problem_bridge_unsteady_topo(dom, z,
2054
+ z_bridge, z_deck, z_roof_null,
2055
+ h0, q0, dx, CN, n,
2056
+ press_mode= press_mode,
2057
+ motion_duration= motion_duration,
2058
+ scenario_bc= scenario,
2059
+ min_overflow= min_overflow)
2060
+
2061
+ ani = animate_bridge_unsteady_topo(dom, poly_bridge_x, poly_bridge_y, res, x, z_ini, hu, z_roof_null, len_bridge, motion_duration=motion_duration)
2062
+
2063
+ if save_video:
2064
+ update_func = lambda _i, _n: progress_bar.update(1)
2065
+ with tqdm(total=len(res), desc='Saving video') as progress_bar:
2066
+ ani.save(f'bridge_L20_{scenario}.mp4',
2067
+ writer='ffmpeg', fps=5,
2068
+ progress_callback=update_func)
2069
+
2070
+ anims.append(ani)
2071
+
2072
+ if 6 in problems or 61 in problems or 62 in problems or 63 in problems or 64 in problems:
2073
+ # Rectangular bridge - Lenght = 60 m
2074
+
2075
+ CN = 0.2
2076
+
2077
+ if 6 in problems:
2078
+ scenarios = ['unsteady_downstream_bc',
2079
+ 'hydrograph',
2080
+ 'hydrograph_2steps',
2081
+ # 'Gauss',
2082
+ ]
2083
+
2084
+ elif 61 in problems:
2085
+ scenarios = ['unsteady_downstream_bc']
2086
+ elif 62 in problems:
2087
+ scenarios = ['hydrograph']
2088
+ elif 63 in problems:
2089
+ scenarios = ['hydrograph_2steps']
2090
+ elif 64 in problems:
2091
+ scenarios = ['Gauss']
2092
+
2093
+ for scenario in scenarios:
2094
+
2095
+ # UNSTEADY WITH TOPOGRAPHY ADAPTATION
2096
+ motion_duration = 300.
2097
+ z_roof_null = 10.
2098
+ min_overflow = 0.25
2099
+
2100
+ h_under_bridge = 3.5
2101
+ h_deck_bridge = 0.75
2102
+ len_bridge = 60
2103
+ q0 = 6.
2104
+ h0 = 1.5
2105
+
2106
+ slice_bridge = slice(int(len(dom)//2-len_bridge//2),int(len(dom)//2+len_bridge//2))
2107
+ slice_bridge_up = slice(int(len(dom)//2+(len_bridge//2-1)),int(len(dom)//2-(len_bridge//2+1)),-1)
2108
+
2109
+ z_bridge = np.ones_like(z) * z_roof_null
2110
+ z_deck = np.ones_like(z) * z_roof_null
2111
+
2112
+ for idx in range(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2):
2113
+ z_bridge[idx] = z[idx] + h_under_bridge
2114
+ z_deck[idx] = z_bridge[idx] + h_deck_bridge
2115
+
2116
+ poly_bridge_x = np.concatenate((x[slice_bridge], x[slice_bridge_up]))
2117
+ poly_bridge_y = np.concatenate((z_bridge[slice_bridge], z_deck[slice_bridge_up]))
2118
+
2119
+ z_ini = z.copy()
2120
+
2121
+ res = problem_bridge_unsteady_topo(dom, z, z_bridge,
2122
+ z_deck, z_roof_null,
2123
+ h0, q0, dx, CN, n,
2124
+ press_mode=press_mode,
2125
+ motion_duration=motion_duration,
2126
+ scenario_bc=scenario,
2127
+ min_overflow=min_overflow)
2128
+
2129
+ ani = animate_bridge_unsteady_topo(dom, res, x, z_ini, hu, z_roof_null, len_bridge, motion_duration=motion_duration)
2130
+
2131
+ if save_video:
2132
+ update_func = lambda _i, _n: progress_bar.update(1)
2133
+ with tqdm(total=len(res), desc='Saving video') as progress_bar:
2134
+ ani.save(f'bridge_L60{scenario}.mp4',
2135
+ writer='ffmpeg', fps=5,
2136
+ progress_callback=update_func)
2137
+
2138
+ anims.append(ani)
2139
+
2140
+ if 9 in problems or 91 in problems or 92 in problems or 93 in problems or 94 in problems:
2141
+ # Culvert
2142
+
2143
+ CN = 0.4
2144
+
2145
+ if 9 in problems:
2146
+ scenarios = ['unsteady_downstream_bc_culvert',
2147
+ 'hydrograph_culvert',
2148
+ 'hydrograph_2steps_culvert',
2149
+ # 'Gauss',
2150
+ ]
2151
+
2152
+ elif 91 in problems:
2153
+ scenarios = ['unsteady_downstream_bc_culvert']
2154
+ elif 92 in problems:
2155
+ scenarios = ['hydrograph_culvert']
2156
+ elif 93 in problems:
2157
+ scenarios = ['hydrograph_2steps_culvert']
2158
+
2159
+ for scenario in scenarios:
2160
+
2161
+ # UNSTEADY WITH TOPOGRAPHY ADAPTATION
2162
+ motion_duration = 300.
2163
+ z_roof_null = 10.
2164
+ min_overflow = 0.25
2165
+
2166
+ h_under_bridge = 1.5
2167
+ h_deck_bridge = 4.0
2168
+ len_bridge = 100
2169
+ h0 = 0.8
2170
+ q0 = 1.
2171
+
2172
+ slice_bridge = slice(int(len(dom)//2-len_bridge//2),int(len(dom)//2+len_bridge//2))
2173
+ slice_bridge_up = slice(int(len(dom)//2+(len_bridge//2-1)),int(len(dom)//2-(len_bridge//2+1)),-1)
2174
+
2175
+ z_bridge = np.ones_like(z) * z_roof_null
2176
+ z_deck = np.ones_like(z) * z_roof_null
2177
+
2178
+ for idx in range(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2):
2179
+ z_bridge[idx] = z[idx] + h_under_bridge
2180
+ z_deck[idx] = z_bridge[idx] + h_deck_bridge
2181
+
2182
+ poly_bridge_x = np.concatenate((x[slice_bridge], x[slice_bridge_up]))
2183
+ poly_bridge_y = np.concatenate((z_bridge[slice_bridge], z_deck[slice_bridge_up]))
2184
+
2185
+ z_ini = z.copy()
2186
+
2187
+ res = problem_bridge_unsteady_topo(dom, z, z_bridge,
2188
+ z_deck, z_roof_null,
2189
+ h0, q0, dx, CN, n,
2190
+ press_mode=press_mode,
2191
+ motion_duration=motion_duration,
2192
+ scenario_bc=scenario,
2193
+ min_overflow=min_overflow)
2194
+
2195
+ ani = animate_bridge_unsteady_topo(dom, poly_bridge_x, poly_bridge_y, res, x, z_ini, hu, z_roof_null, len_bridge, title='Culvert', motion_duration=motion_duration)
2196
+
2197
+ if save_video:
2198
+ update_func = lambda _i, _n: progress_bar.update(1)
2199
+ with tqdm(total=len(res), desc='Saving video') as progress_bar:
2200
+ ani.save(f'culvert_{scenario}.mp4',
2201
+ writer='ffmpeg', fps=5,
2202
+ progress_callback=update_func)
2203
+
2204
+ anims.append(ani)
2205
+
2206
+ if 7 in problems or 71 in problems or 72 in problems or 73 in problems or 74 in problems:
2207
+ # V-shape Bridge
2208
+
2209
+ CN = 0.4
2210
+
2211
+ if 7 in problems:
2212
+ scenarios = ['unsteady_downstream_bc',
2213
+ 'hydrograph',
2214
+ 'hydrograph_2steps',
2215
+ # 'Gauss',
2216
+ ]
2217
+ elif 71 in problems:
2218
+ scenarios = ['unsteady_downstream_bc']
2219
+ elif 72 in problems:
2220
+ scenarios = ['hydrograph']
2221
+ elif 73 in problems:
2222
+ scenarios = ['hydrograph_2steps']
2223
+ elif 74 in problems:
2224
+ scenarios = ['Gauss']
2225
+
2226
+ for scenario in scenarios:
2227
+
2228
+ # UNSTEADY WITH TOPOGRAPHY ADAPTATION
2229
+ motion_duration = 300.
2230
+ z_roof_null = 10.
2231
+ min_overflow = 0.25
2232
+
2233
+ h_under_bridge = 3.5
2234
+ h_deck_bridge = 0.75
2235
+ len_bridge = 20
2236
+
2237
+ h0 = 1.5
2238
+
2239
+ z_bridge = np.ones_like(z) * z_roof_null
2240
+ z_deck = np.ones_like(z) * z_roof_null
2241
+
2242
+ slice_bridge = slice(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2)
2243
+ slice_bridge_up = slice(len(dom)//2+(len_bridge//2-1),len(dom)//2-(len_bridge//2+1),-1)
2244
+
2245
+ for idx in range(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2):
2246
+ decal = abs(idx - (len(dom)//2))
2247
+ z_bridge[idx] = z[idx] + h_under_bridge + 0.05 * decal
2248
+ z_deck[idx] = h_under_bridge + h_deck_bridge
2249
+
2250
+ poly_bridge_x = np.concatenate((x[slice_bridge], x[slice_bridge_up]))
2251
+ poly_bridge_y = np.concatenate((z_bridge[slice_bridge], z_deck[slice_bridge_up]))
2252
+
2253
+ z_ini = z.copy()
2254
+
2255
+ res = problem_bridge_unsteady_topo(dom, z,
2256
+ z_bridge, z_deck, z_roof_null,
2257
+ h0, q0, dx, CN, n,
2258
+ press_mode=press_mode,
2259
+ motion_duration=motion_duration,
2260
+ scenario_bc=scenario,
2261
+ min_overflow= min_overflow)
2262
+
2263
+ ani = animate_bridge_unsteady_topo(dom, poly_bridge_x, poly_bridge_y, res, x, z_ini, hu, z_roof_null, len_bridge, motion_duration=motion_duration)
2264
+
2265
+ if save_video:
2266
+ update_func = lambda _i, _n: progress_bar.update(1)
2267
+ with tqdm(total=len(res), desc='Saving video') as progress_bar:
2268
+ ani.save(f'bridge_Vshape{scenario}.mp4',
2269
+ writer='ffmpeg', fps=5,
2270
+ progress_callback=update_func)
2271
+
2272
+ anims.append(ani)
2273
+
2274
+ if 8 in problems or 81 in problems or 82 in problems or 83 in problems or 84 in problems:
2275
+ # U-shape Bridge
2276
+
2277
+ CN = 0.4
2278
+
2279
+ if 8 in problems:
2280
+ scenarios = ['unsteady_downstream_bc',
2281
+ 'hydrograph',
2282
+ 'hydrograph_2steps',
2283
+ # 'Gauss',
2284
+ ]
2285
+ elif 81 in problems:
2286
+ scenarios = ['unsteady_downstream_bc']
2287
+ elif 82 in problems:
2288
+ scenarios = ['hydrograph']
2289
+ elif 83 in problems:
2290
+ scenarios = ['hydrograph_2steps']
2291
+ elif 84 in problems:
2292
+ scenarios = ['Gauss']
2293
+
2294
+ for scenario in scenarios:
2295
+
2296
+ # UNSTEADY WITH TOPOGRAPHY ADAPTATION
2297
+ motion_duration = 300.
2298
+ z_roof_null = 10.
2299
+ min_overflow = 0.25
2300
+
2301
+ h_under_bridge = 3.5
2302
+ h_deck_bridge = 0.4
2303
+ len_bridge = 20
2304
+
2305
+ h0 = 1.5
2306
+
2307
+ z_bridge = np.ones_like(z) * z_roof_null
2308
+ z_deck = np.ones_like(z) * z_roof_null
2309
+
2310
+ slice_bridge = slice(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2)
2311
+ slice_bridge_up = slice(len(dom)//2+(len_bridge//2-1),len(dom)//2-(len_bridge//2+1),-1)
2312
+
2313
+ z_bridge[slice_bridge] = z[slice_bridge] + h_under_bridge
2314
+ z_deck[slice_bridge] = z_bridge[slice_bridge] + h_deck_bridge
2315
+
2316
+ idx_up = len(dom)//2-len_bridge//2
2317
+ idx_down = len(dom)//2+len_bridge//2-1
2318
+
2319
+ z_bridge[idx_up] -= .4
2320
+ z_bridge[idx_up+1] -= .4
2321
+
2322
+ z_bridge[idx_down] -= .4
2323
+ z_bridge[idx_down-1] -= .4
2324
+
2325
+ poly_bridge_x = np.concatenate((x[slice_bridge], x[slice_bridge_up]))
2326
+ poly_bridge_y = np.concatenate((z_bridge[slice_bridge], z_deck[slice_bridge_up]))
2327
+
2328
+ z_ini = z.copy()
2329
+
2330
+ res = problem_bridge_unsteady_topo(dom, z,
2331
+ z_bridge, z_deck, z_roof_null,
2332
+ h0, q0, dx, CN, n,
2333
+ press_mode=press_mode,
2334
+ motion_duration=motion_duration,
2335
+ scenario_bc=scenario,
2336
+ min_overflow= min_overflow)
2337
+
2338
+ ani = animate_bridge_unsteady_topo(dom, poly_bridge_x, poly_bridge_y, res, x, z_ini, hu, z_roof_null, len_bridge, motion_duration=motion_duration)
2339
+
2340
+ if save_video:
2341
+ update_func = lambda _i, _n: progress_bar.update(1)
2342
+ with tqdm(total=len(res), desc='Saving video') as progress_bar:
2343
+ ani.save(f'bridge_Ushape{scenario}.mp4',
2344
+ writer='ffmpeg', fps=5,
2345
+ progress_callback=update_func)
2346
+
2347
+ anims.append(ani)
2348
+
2349
+ return anims
2350
+
2351
+ def unsteady_with_bedmotion_interval(problems:list[int], save_video:bool = False, update_interval:float = 0., motion_duration:float = 300.) -> list[animation.FuncAnimation]:
2352
+ """
2353
+ Unsteady problem with bed motion if overflowing occurs.
2354
+
2355
+ :param problems: list of problems to solve
2356
+
2357
+ Problems :
2358
+ 2 - Rectangular bridge - Length = 20 m (will compute 21, 22 and 23)
2359
+ 6 - Rectangular bridge - Length = 60 m (will compute 61, 62 and 63)
2360
+ 7 - V-shape bridge - Length = 20 m (will compute 71, 72 and 73)
2361
+ 8 - U-shape bridge - Length = 20 m (will compute 81, 82 and 83)
2362
+ 9 - Culvert - Length = 100 m (will compute 91, 92 and 93)
2363
+
2364
+ 21 - Rectangular bridge - Length = 20 m - Unsteady downstream bc
2365
+ 22 - Rectangular bridge - Length = 20 m - Hydrograph
2366
+ 23 - Rectangular bridge - Length = 20 m - Hydrograph 2 steps
2367
+
2368
+ 61 - Rectangular bridge - Length = 60 m - Unsteady downstream bc
2369
+ 62 - Rectangular bridge - Length = 60 m - Hydrograph
2370
+ 63 - Rectangular bridge - Length = 60 m - Hydrograph 2 steps
2371
+
2372
+ 71 - V-shape bridge - Length = 20 m - Unsteady downstream bc
2373
+ 72 - V-shape bridge - Length = 20 m - Hydrograph
2374
+ 73 - V-shape bridge - Length = 20 m - Hydrograph 2 steps
2375
+
2376
+ 81 - U-shape bridge - Length = 20 m - Unsteady downstream bc
2377
+ 82 - U-shape bridge - Length = 20 m - Hydrograph
2378
+ 83 - U-shape bridge - Length = 20 m - Hydrograph 2 steps
2379
+
2380
+ 91 - Culvert - Length = 100 m - Unsteady downstream bc
2381
+ 92 - Culvert - Length = 100 m - Hydrograph
2382
+ 93 - Culvert - Length = 100 m - Hydrograph 2 steps
2383
+
2384
+ """
2385
+ length = 500.
2386
+ dx = 1.
2387
+ CN = 0.4
2388
+ h0 = 4.
2389
+ q0 = 7.
2390
+ n = 0.025
2391
+ slope = 0 #1e-4
2392
+ press_mode = 4
2393
+
2394
+ dom, x, z = domain(length, dx, slope)
2395
+ x = np.array(x)
2396
+
2397
+ hu = uniform_waterdepth(slope, q0, n)
2398
+
2399
+ anims=[]
2400
+
2401
+ if 2 in problems or 21 in problems or 22 in problems or 23 in problems or 24 in problems:
2402
+ # Rectangular bridge - Lenght = 20 m
2403
+
2404
+ CN = 0.4
2405
+
2406
+ if 2 in problems:
2407
+ scenarios = ['unsteady_downstream_bc',
2408
+ 'hydrograph',
2409
+ 'hydrograph_2steps',
2410
+ # 'Gauss',
2411
+ ]
2412
+ elif 21 in problems:
2413
+ scenarios = ['unsteady_downstream_bc']
2414
+ elif 22 in problems:
2415
+ scenarios = ['hydrograph']
2416
+ elif 23 in problems:
2417
+ scenarios = ['hydrograph_2steps']
2418
+ elif 24 in problems:
2419
+ scenarios = ['Gauss']
2420
+
2421
+ for scenario in scenarios:
2422
+
2423
+ # UNSTEADY WITH TOPOGRAPHY ADAPTATION
2424
+ len_bridge = 20
2425
+ z_roof_null = 10.
2426
+ min_overflow = 0.25
2427
+
2428
+ h0 = 1.5
2429
+
2430
+ h_under_bridge = 3.5
2431
+ h_deck_bridge = 0.75
2432
+
2433
+ slice_bridge = slice(int(len(dom)//2-len_bridge//2),int(len(dom)//2+len_bridge//2))
2434
+ slice_bridge_up = slice(int(len(dom)//2+(len_bridge//2-1)),int(len(dom)//2-(len_bridge//2+1)),-1)
2435
+
2436
+ z_bridge = np.ones_like(z) * z_roof_null
2437
+ z_deck = np.ones_like(z) * z_roof_null
2438
+
2439
+ for idx in range(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2):
2440
+ z_bridge[idx] = z[idx] + h_under_bridge
2441
+ z_deck[idx] = z_bridge[idx] + h_deck_bridge
2442
+
2443
+ poly_bridge_x = np.concatenate((x[slice_bridge], x[slice_bridge_up]))
2444
+ poly_bridge_y = np.concatenate((z_bridge[slice_bridge], z_deck[slice_bridge_up]))
2445
+
2446
+ z_ini = z.copy()
2447
+
2448
+ res = problem_bridge_unsteady_topo(dom, z,
2449
+ z_bridge, z_deck, z_roof_null,
2450
+ h0, q0, dx, CN, n,
2451
+ press_mode= press_mode,
2452
+ motion_duration= motion_duration,
2453
+ scenario_bc= scenario,
2454
+ min_overflow= min_overflow,
2455
+ updating_time_interval=update_interval)
2456
+
2457
+ ani = animate_bridge_unsteady_topo(dom, poly_bridge_x, poly_bridge_y, res, x, z_ini, hu, z_roof_null, len_bridge, motion_duration=motion_duration)
2458
+
2459
+ if save_video:
2460
+ update_func = lambda _i, _n: progress_bar.update(1)
2461
+ with tqdm(total=len(res), desc='Saving video') as progress_bar:
2462
+ ani.save(f'bridge_L20_{scenario}.mp4',
2463
+ writer='ffmpeg', fps=5,
2464
+ progress_callback=update_func)
2465
+
2466
+ anims.append(ani)
2467
+
2468
+ if 6 in problems or 61 in problems or 62 in problems or 63 in problems or 64 in problems:
2469
+ # Rectangular bridge - Lenght = 60 m
2470
+
2471
+ CN = 0.2
2472
+
2473
+ if 6 in problems:
2474
+ scenarios = ['unsteady_downstream_bc',
2475
+ 'hydrograph',
2476
+ 'hydrograph_2steps',
2477
+ # 'Gauss',
2478
+ ]
2479
+
2480
+ elif 61 in problems:
2481
+ scenarios = ['unsteady_downstream_bc']
2482
+ elif 62 in problems:
2483
+ scenarios = ['hydrograph']
2484
+ elif 63 in problems:
2485
+ scenarios = ['hydrograph_2steps']
2486
+ elif 64 in problems:
2487
+ scenarios = ['Gauss']
2488
+
2489
+ for scenario in scenarios:
2490
+
2491
+ # UNSTEADY WITH TOPOGRAPHY ADAPTATION
2492
+ z_roof_null = 10.
2493
+ min_overflow = 0.25
2494
+
2495
+ h_under_bridge = 3.5
2496
+ h_deck_bridge = 0.75
2497
+ len_bridge = 60
2498
+ q0 = 6.
2499
+ h0 = 1.5
2500
+
2501
+ slice_bridge = slice(int(len(dom)//2-len_bridge//2),int(len(dom)//2+len_bridge//2))
2502
+ slice_bridge_up = slice(int(len(dom)//2+(len_bridge//2-1)),int(len(dom)//2-(len_bridge//2+1)),-1)
2503
+
2504
+ z_bridge = np.ones_like(z) * z_roof_null
2505
+ z_deck = np.ones_like(z) * z_roof_null
2506
+
2507
+ for idx in range(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2):
2508
+ z_bridge[idx] = z[idx] + h_under_bridge
2509
+ z_deck[idx] = z_bridge[idx] + h_deck_bridge
2510
+
2511
+ poly_bridge_x = np.concatenate((x[slice_bridge], x[slice_bridge_up]))
2512
+ poly_bridge_y = np.concatenate((z_bridge[slice_bridge], z_deck[slice_bridge_up]))
2513
+
2514
+ z_ini = z.copy()
2515
+
2516
+ res = problem_bridge_unsteady_topo(dom, z, z_bridge,
2517
+ z_deck, z_roof_null,
2518
+ h0, q0, dx, CN, n,
2519
+ press_mode=press_mode,
2520
+ motion_duration=motion_duration,
2521
+ scenario_bc=scenario,
2522
+ min_overflow=min_overflow,
2523
+ updating_time_interval=update_interval)
2524
+
2525
+ ani = animate_bridge_unsteady_topo(dom, res, x, z_ini, hu, z_roof_null, len_bridge, motion_duration=motion_duration)
2526
+
2527
+ if save_video:
2528
+ update_func = lambda _i, _n: progress_bar.update(1)
2529
+ with tqdm(total=len(res), desc='Saving video') as progress_bar:
2530
+ ani.save(f'bridge_L60{scenario}.mp4',
2531
+ writer='ffmpeg', fps=5,
2532
+ progress_callback=update_func)
2533
+
2534
+ anims.append(ani)
2535
+
2536
+ if 9 in problems or 91 in problems or 92 in problems or 93 in problems or 94 in problems:
2537
+ # Culvert
2538
+
2539
+ CN = 0.4
2540
+
2541
+ if 9 in problems:
2542
+ scenarios = ['unsteady_downstream_bc_culvert',
2543
+ 'hydrograph_culvert',
2544
+ 'hydrograph_2steps_culvert',
2545
+ # 'Gauss',
2546
+ ]
2547
+
2548
+ elif 91 in problems:
2549
+ scenarios = ['unsteady_downstream_bc_culvert']
2550
+ elif 92 in problems:
2551
+ scenarios = ['hydrograph_culvert']
2552
+ elif 93 in problems:
2553
+ scenarios = ['hydrograph_2steps_culvert']
2554
+
2555
+ for scenario in scenarios:
2556
+
2557
+ # UNSTEADY WITH TOPOGRAPHY ADAPTATION
2558
+ z_roof_null = 10.
2559
+ min_overflow = 0.25
2560
+
2561
+ h_under_bridge = 1.5
2562
+ h_deck_bridge = 4.0
2563
+ len_bridge = 100
2564
+ h0 = 0.8
2565
+ q0 = 1.
2566
+
2567
+ slice_bridge = slice(int(len(dom)//2-len_bridge//2),int(len(dom)//2+len_bridge//2))
2568
+ slice_bridge_up = slice(int(len(dom)//2+(len_bridge//2-1)),int(len(dom)//2-(len_bridge//2+1)),-1)
2569
+
2570
+ z_bridge = np.ones_like(z) * z_roof_null
2571
+ z_deck = np.ones_like(z) * z_roof_null
2572
+
2573
+ for idx in range(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2):
2574
+ z_bridge[idx] = z[idx] + h_under_bridge
2575
+ z_deck[idx] = z_bridge[idx] + h_deck_bridge
2576
+
2577
+ poly_bridge_x = np.concatenate((x[slice_bridge], x[slice_bridge_up]))
2578
+ poly_bridge_y = np.concatenate((z_bridge[slice_bridge], z_deck[slice_bridge_up]))
2579
+
2580
+ z_ini = z.copy()
2581
+
2582
+ res = problem_bridge_unsteady_topo(dom, z, z_bridge,
2583
+ z_deck, z_roof_null,
2584
+ h0, q0, dx, CN, n,
2585
+ press_mode=press_mode,
2586
+ motion_duration=motion_duration,
2587
+ scenario_bc=scenario,
2588
+ min_overflow=min_overflow,
2589
+ updating_time_interval=update_interval)
2590
+
2591
+ ani = animate_bridge_unsteady_topo(dom, poly_bridge_x, poly_bridge_y, res, x, z_ini, hu, z_roof_null, len_bridge, title='Culvert', motion_duration=motion_duration)
2592
+
2593
+ if save_video:
2594
+ update_func = lambda _i, _n: progress_bar.update(1)
2595
+ with tqdm(total=len(res), desc='Saving video') as progress_bar:
2596
+ ani.save(f'culvert_{scenario}.mp4',
2597
+ writer='ffmpeg', fps=5,
2598
+ progress_callback=update_func)
2599
+
2600
+ anims.append(ani)
2601
+
2602
+ if 7 in problems or 71 in problems or 72 in problems or 73 in problems or 74 in problems:
2603
+ # V-shape Bridge
2604
+
2605
+ CN = 0.4
2606
+
2607
+ if 7 in problems:
2608
+ scenarios = ['unsteady_downstream_bc',
2609
+ 'hydrograph',
2610
+ 'hydrograph_2steps',
2611
+ # 'Gauss',
2612
+ ]
2613
+ elif 71 in problems:
2614
+ scenarios = ['unsteady_downstream_bc']
2615
+ elif 72 in problems:
2616
+ scenarios = ['hydrograph']
2617
+ elif 73 in problems:
2618
+ scenarios = ['hydrograph_2steps']
2619
+ elif 74 in problems:
2620
+ scenarios = ['Gauss']
2621
+
2622
+ for scenario in scenarios:
2623
+
2624
+ # UNSTEADY WITH TOPOGRAPHY ADAPTATION
2625
+ z_roof_null = 10.
2626
+ min_overflow = 0.25
2627
+
2628
+ h_under_bridge = 3.5
2629
+ h_deck_bridge = 0.75
2630
+ len_bridge = 20
2631
+
2632
+ h0 = 1.5
2633
+
2634
+ z_bridge = np.ones_like(z) * z_roof_null
2635
+ z_deck = np.ones_like(z) * z_roof_null
2636
+
2637
+ slice_bridge = slice(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2)
2638
+ slice_bridge_up = slice(len(dom)//2+(len_bridge//2-1),len(dom)//2-(len_bridge//2+1),-1)
2639
+
2640
+ for idx in range(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2):
2641
+ decal = abs(idx - (len(dom)//2))
2642
+ z_bridge[idx] = z[idx] + h_under_bridge + 0.05 * decal
2643
+ z_deck[idx] = h_under_bridge + h_deck_bridge
2644
+
2645
+ poly_bridge_x = np.concatenate((x[slice_bridge], x[slice_bridge_up]))
2646
+ poly_bridge_y = np.concatenate((z_bridge[slice_bridge], z_deck[slice_bridge_up]))
2647
+
2648
+ z_ini = z.copy()
2649
+
2650
+ res = problem_bridge_unsteady_topo(dom, z,
2651
+ z_bridge, z_deck, z_roof_null,
2652
+ h0, q0, dx, CN, n,
2653
+ press_mode=press_mode,
2654
+ motion_duration=motion_duration,
2655
+ scenario_bc=scenario,
2656
+ min_overflow= min_overflow,
2657
+ updating_time_interval=update_interval)
2658
+
2659
+ ani = animate_bridge_unsteady_topo(dom, poly_bridge_x, poly_bridge_y, res, x, z_ini, hu, z_roof_null, len_bridge, motion_duration=motion_duration)
2660
+
2661
+ if save_video:
2662
+ update_func = lambda _i, _n: progress_bar.update(1)
2663
+ with tqdm(total=len(res), desc='Saving video') as progress_bar:
2664
+ ani.save(f'bridge_Vshape{scenario}.mp4',
2665
+ writer='ffmpeg', fps=5,
2666
+ progress_callback=update_func)
2667
+
2668
+ anims.append(ani)
2669
+
2670
+ if 8 in problems or 81 in problems or 82 in problems or 83 in problems or 84 in problems:
2671
+ # U-shape Bridge
2672
+
2673
+ CN = 0.4
2674
+
2675
+ if 8 in problems:
2676
+ scenarios = ['unsteady_downstream_bc',
2677
+ 'hydrograph',
2678
+ 'hydrograph_2steps',
2679
+ # 'Gauss',
2680
+ ]
2681
+ elif 81 in problems:
2682
+ scenarios = ['unsteady_downstream_bc']
2683
+ elif 82 in problems:
2684
+ scenarios = ['hydrograph']
2685
+ elif 83 in problems:
2686
+ scenarios = ['hydrograph_2steps']
2687
+ elif 84 in problems:
2688
+ scenarios = ['Gauss']
2689
+
2690
+ for scenario in scenarios:
2691
+
2692
+ # UNSTEADY WITH TOPOGRAPHY ADAPTATION
2693
+ z_roof_null = 10.
2694
+ min_overflow = 0.25
2695
+
2696
+ h_under_bridge = 3.5
2697
+ h_deck_bridge = 0.4
2698
+ len_bridge = 20
2699
+
2700
+ h0 = 1.5
2701
+
2702
+ z_bridge = np.ones_like(z) * z_roof_null
2703
+ z_deck = np.ones_like(z) * z_roof_null
2704
+
2705
+ slice_bridge = slice(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2)
2706
+ slice_bridge_up = slice(len(dom)//2+(len_bridge//2-1),len(dom)//2-(len_bridge//2+1),-1)
2707
+
2708
+ z_bridge[slice_bridge] = z[slice_bridge] + h_under_bridge
2709
+ z_deck[slice_bridge] = z_bridge[slice_bridge] + h_deck_bridge
2710
+
2711
+ idx_up = len(dom)//2-len_bridge//2
2712
+ idx_down = len(dom)//2+len_bridge//2-1
2713
+
2714
+ z_bridge[idx_up] -= .4
2715
+ z_bridge[idx_up+1] -= .4
2716
+
2717
+ z_bridge[idx_down] -= .4
2718
+ z_bridge[idx_down-1] -= .4
2719
+
2720
+ poly_bridge_x = np.concatenate((x[slice_bridge], x[slice_bridge_up]))
2721
+ poly_bridge_y = np.concatenate((z_bridge[slice_bridge], z_deck[slice_bridge_up]))
2722
+
2723
+ z_ini = z.copy()
2724
+
2725
+ res = problem_bridge_unsteady_topo(dom, z,
2726
+ z_bridge, z_deck, z_roof_null,
2727
+ h0, q0, dx, CN, n,
2728
+ press_mode=press_mode,
2729
+ motion_duration=motion_duration,
2730
+ scenario_bc=scenario,
2731
+ min_overflow= min_overflow,
2732
+ updating_time_interval=update_interval)
2733
+
2734
+ ani = animate_bridge_unsteady_topo(dom, poly_bridge_x, poly_bridge_y, res, x, z_ini, hu, z_roof_null, len_bridge, motion_duration=motion_duration)
2735
+
2736
+ if save_video:
2737
+ update_func = lambda _i, _n: progress_bar.update(1)
2738
+ with tqdm(total=len(res), desc='Saving video') as progress_bar:
2739
+ ani.save(f'bridge_Ushape{scenario}.mp4',
2740
+ writer='ffmpeg', fps=5,
2741
+ progress_callback=update_func)
2742
+
2743
+ anims.append(ani)
2744
+
2745
+ return anims
2746
+
2747
+
2748
+
2749
+ def hedge():
2750
+ """ Compute Water line problems with hedge
2751
+
2752
+ Length = 500 m
2753
+ dx = 1 m
2754
+ CN = 0.4
2755
+ h0 = 4 m
2756
+ q0 = 7 m^2/s
2757
+ n = 0.025
2758
+ slope = 1e-4
2759
+ """
2760
+
2761
+ length = 500.
2762
+ dx = 1.
2763
+ CN = 0.4
2764
+ h0 = 4.
2765
+ q0 = 7.
2766
+ n = 0.025
2767
+ slope = 1e-4
2768
+
2769
+ dom, x, z = domain(length, dx, slope)
2770
+ x = np.array(x)
2771
+
2772
+ hu = uniform_waterdepth(slope, q0, n)
2773
+
2774
+ # bridge roof level is 10 m everywhere except in the middle of the domain
2775
+ # where the bridge is located.
2776
+ # The bridge is a flat bridge with a height of 2 m.
2777
+ z_bridge = np.ones_like(z) * 10.
2778
+ z_bridge[len(dom)//2-5:len(dom)//2+5] = z[len(dom)//2-5:len(dom)//2+5] + 2.
2779
+
2780
+ fig, axes = plt.subplots(3,1)
2781
+
2782
+ h1, q1 = problem(dom, z, h0, q0, dx, CN, n)
2783
+ h2, q2, theta = problem_hedge(dom, z, h0, q0, dx, CN, n)
2784
+
2785
+ plot_hedge(axes[0], x, h1, h2, q1, q2, z, hu, theta)
2786
+
2787
+ h1, q1 = problem(dom, z, h0+1., q0, dx, CN, n)
2788
+ h2, q2, theta = problem_hedge(dom, z, h0+1., q0, dx, CN, n)
2789
+
2790
+ plot_hedge(axes[1], x, h1, h2, q1, q2, z, hu, theta)
2791
+
2792
+ h1, q1 = problem(dom, z, h0+2., q0, dx, CN, n)
2793
+ h2, q2, theta = problem_hedge(dom, z, h0+2., q0, dx, CN, n)
2794
+
2795
+ plot_hedge(axes[2], x, h1, h2, q1, q2, z, hu, theta)
2796
+
2797
+ fig.set_size_inches(20, 10)
2798
+ fig.tight_layout()
2799
+
2800
+ return fig, axes
2801
+
2802
+ if __name__ == '__main__':
2803
+
2804
+ # anim = steady()
2805
+ # anim = water_line()
2806
+ # anim = water_lines()
2807
+ # anim = unsteady_without_bedmotion()
2808
+ # anim = unteaady_with_bedmotion([2, 6, 7, 8, 9])
2809
+ anim = hedge()
2810
+ # anim = water_line_noloss_noslope()
2811
+
2812
+ # anim1 = unsteady_with_bedmotion([2])
2813
+ # anim2 = unsteady_with_bedmotion_interval([21], update_interval=2., motion_duration=300.)
2814
+
2815
+ plt.show()
2816
+
2817
+ pass