import numpy as np
from matplotlib import pyplot as plt
from tqdm import tqdm
from numba import jit
from scipy.optimize import root_scalar
from typing import Literal
from enum import Enum
import logging
"""
Calcul des equations shallow water en 1D section rectangulaire avec frottement selon Manning
Continuity : dh/dt + d(q)/dx = 0
Momentum : dq/dt + d(q^2/h + 1/2 g h^2)/dx + g h dz/dx = -g h J
Friction Slope : J = n^2 (q/h)^2 / h^(4/3)
Discretisation :
J = n^2 q_t2 q_t1 / h^(10/3)
ghJ = q_t2 (g n^2 q_t1 / h^(7/3))
h_t2 = h_t1 - dt/dx (q_t,r - q_t,l)
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))
"""
[docs]
class Scenarios(Enum):
[docs]
unsteady_downstream_bc = 0
[docs]
def domain(length:float, dx:float, slope:float) -> np.ndarray:
""" Create the domain
:param length: Length of the domain
:param dx: Space step
:param slope: Slope of the domain
"""
nb = int(length/dx)
dom = np.zeros(nb+2)
x = [-dx/2.] + [(float(i)+.5)*dx for i in range(0,nb)] + [length + dx/2.]
z = -slope * np.array(x)
return dom, x, z
[docs]
def _init_conditions(dom:np.ndarray, h0:float, q0:float) -> np.ndarray:
""" Initial conditions
:param dom: Domain
:param h0: Initial water depth [m]
:param q0: Initial discharge [m^2/s]
"""
h = np.zeros_like(dom)
h[1:-1] = h0
q = np.zeros_like(dom)
q[1:-1] = q0
return h, q
[docs]
def get_friction_slope_2D_Manning(q:float, h:float, n:float) -> float:
""" Friction slope based on Manning formula
:param q: Discharge [m^2/s]
:param h: Water depth [m]
:param n: Manning coefficient [m^(1/3)/s]
"""
return n**2 * q**2 / h**(10/3)
[docs]
def compute_dt(dx:float, h:np.ndarray, q:np.ndarray, CN:float) -> float:
""" Compute the time step according to the Courant number anf the maximum velocity
:param dx: Space step
:param h: Water depth
:param q: Discharge
:param CN: Courant number
"""
h_pos = np.where(h > 0)[0]
dt = CN * dx / (np.max(np.abs(q[h_pos]/h[h_pos]) + np.sqrt(h[h_pos]*9.81)))
return dt
[docs]
def all_unk_border(dom:np.ndarray, h0:float, q0:float) -> tuple[np.ndarray]:
""" Initialize all arrays storing unknowns at center and borders
:param dom: Domain
:param h0: Initial water depth
:param q0: Initial discharge
"""
h, q = _init_conditions(dom, h0, q0)
h_pred = np.zeros_like(dom)
h_corr = np.zeros_like(dom)
q_pred = np.zeros_like(dom)
q_corr = np.zeros_like(dom)
q_border = np.zeros((len(dom), 2))
h_border = np.zeros((len(dom), 2))
z_border = np.zeros((len(dom), 2))
u_border = np.zeros(len(dom))
h_center = np.zeros(len(dom)-2)
u_center = np.zeros(len(dom)-2)
return h, q, h_pred, q_pred, h_corr, q_corr, q_border, h_border, z_border, u_border, h_center, u_center
# -----------------
# LOSS Coefficients
# -----------------
[docs]
def k_abrupt_enlargment(asmall:float, alarge:float) -> float:
""" Compute the local head loss coefficient of the abrupt enlargment
:params asmall: float, area of the section 1 -- smaller section
:params alarge: float, area of the section 2 -- larger section
"""
return (1 - asmall/alarge)**2
[docs]
def k_abrupt_contraction(alarge:float, asmall:float) -> float:
""" Compute the local head loss coefficient of the abrupt contraction
:params alarge: float, area of the section 1 -- larger section
:params asmall: float, area of the section 2 -- smaller section
"""
return .5*(1 - asmall/alarge)
[docs]
def head_loss_enlargment(q:float, asmall:float, alarge:float) -> float:
""" Compute the head loss of the enlargment.
Reference velocity is the velocity in the smaller section.
:params q: float, discharge
:params asmall: float, area of the section 1 -- smaller section
:params alarge: float, area of the section 2 -- larger section
"""
return k_abrupt_enlargment(asmall, alarge) * (q/asmall)**2 / 2 / 9.81
[docs]
def head_loss_contraction(q:float, alarge:float, asmall:float) -> float:
""" Compute the head loss of the contraction.
Reference velocity is the velocity in the smaller section.
:params q: float, discharge
:params alarge: float, area of the section 1 -- larger section
:params asmall: float, area of the section 2 -- smaller section
"""
return k_abrupt_contraction(alarge, asmall) * (q/asmall)**2 / 2 / 9.81
[docs]
def head_loss_contract_enlarge(q:float, a_up:float, asmall:float, a_down:float) -> float:
""" Compute the head loss of the contraction/enlargment.
Reference velocity is the velocity in the smaller section.
:params q: float, discharge
:params a_up: float, area of the section 1 -- larger section
:params asmall: float, area of the section 2 -- smaller section
:params a_down: float, area of the section 3 -- larger section
"""
return (k_abrupt_enlargment(asmall, a_down) + k_abrupt_contraction(a_up, asmall)) * (q/asmall)**2 / 2 / 9.81
# ----------------------------
# START JIT compiled functions
# ----------------------------
@jit(nopython=True)
[docs]
def get_friction_slope_2D_Manning_semi_implicit(u:np.ndarray, h:np.ndarray, n:float) -> np.ndarray:
""" Friction slope based on Manning formula -- Only semi-implicit formulea for the friction slope
:param u: Velocity [m/s]
:param h: Water depth [m]
:param n: Manning coefficient [m^(1/3)/s]
"""
return n**2 * np.abs(u) / h**(7/3)
@jit(nopython=True)
[docs]
def Euler_RK(h_t1:np.ndarray, h_t2:np.ndarray,
q_t1:np.ndarray, q_t2:np.ndarray,
h:np.ndarray, q:np.ndarray,
h_border:np.ndarray, q_border:np.ndarray,
z:np.ndarray, z_border:np.ndarray,
dt:float, dx:float,
CL_h:float, CL_q:float,
n:float, u_border:np.ndarray,
h_center:np.ndarray, u_center:np.ndarray) -> None:
""" Solve the mass and momentum equations using a explicit Euler/Runge-Kutta scheme (only 1 step)
:param h_t1: Water depth at time t
:param h_t2: Water depth at time t+dt (or t_star or t_doublestar if RK)
:param q_t1: Discharge at time t
:param q_t2: Discharge at time t+dt (or t_star or t_doublestar if RK)
:param h: Water depth at the mesh center
:param q: Discharge at the mesh center
:param h_border: Water depth at the mesh border
:param q_border: Discharge at the mesh border
:param z: Bed elevation
:param z_border: Bed elevation at the mesh border
:param dt: Time step
:param dx: Space step
:param CL_h: Downstream boudary condition for water depth
:param CL_q: Upstream boundary condition for discharge
:param n: Manning coefficient
:param u_border: Velocity at the mesh border
:param h_center: Water depth at the mesh center
:param u_center: Velocity at the mesh center
"""
g = 9.81
slice_mesh = slice(1,-1)
slice_right_border = slice(2,None)
slice_left_border = slice(1,-1)
up = 0
do = 1
# valeur à gauche du bord
q_border[slice_right_border, up] = q[1:-1]
q_border[1,up] = CL_q
h_border[slice_right_border, up] = h[1:-1]
h_border[1,up] = h[1]
z_border[slice_right_border, up] = z[1:-1]
z_border[1,up] = z[1]
# valeur à droite du bord
q_border[slice_left_border, do] = q[1:-1]
q_border[-1,do] = q[-2]
h_border[slice_left_border, do] = h[1:-1]
h_border[-1,do] = CL_h
z_border[slice_left_border, do] = z[1:-1]
z_border[-1,do] = z[-2]
u_border[1:] = q_border[1:,up]/h_border[1:,up]
h_center = (h_border[slice_right_border,do] + h_border[slice_left_border,do])/2.
u_center = q[slice_mesh]/h[slice_mesh]
#Continuity
h_t2[slice_mesh] = h_t1[slice_mesh] - dt/dx * (q_border[slice_right_border,up] - q_border[slice_left_border,up])
# Momentum
J = get_friction_slope_2D_Manning_semi_implicit(u_center, h[slice_mesh], n)
qm_right = u_border[slice_right_border]*q_border[slice_right_border,up]
qm_left = u_border[slice_left_border]*q_border[slice_left_border,up]
press_right = 0.5 * g * h_border[slice_right_border,do]**2
press_left = 0.5 * g * h_border[slice_left_border,do]**2
bed_right = g * h_center * z_border[slice_right_border,do]
bed_left = g * h_center * z_border[slice_left_border,do]
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))
limit_h_q(h_t2, q_t2, hmin=1e-3, Froudemax=3.)
@jit(nopython=True)
[docs]
def Euler_RK_hedge(h_t1:np.ndarray, h_t2:np.ndarray,
q_t1:np.ndarray, q_t2:np.ndarray,
h:np.ndarray, q:np.ndarray,
h_border:np.ndarray, q_border:np.ndarray,
z:np.ndarray, z_border:np.ndarray,
dt:float, dx:float,
CL_h:float, CL_q:float,
n:float, u_border:np.ndarray,
h_center:np.ndarray, u_center:np.ndarray,
theta:np.ndarray, theta_border:np.ndarray) -> None:
""" Solve the mass and momentum equations using a explicit Euler/Runge-Kutta scheme (only 1 step)
:param h_t1: Water depth at time t
:param h_t2: Water depth at time t+dt (or t_star or t_doublestar if RK)
:param q_t1: Discharge at time t
:param q_t2: Discharge at time t+dt (or t_star or t_doublestar if RK)
:param h: Water depth at the mesh center
:param q: Discharge at the mesh center
:param h_border: Water depth at the mesh border
:param q_border: Discharge at the mesh border
:param z: Bed elevation
:param z_border: Bed elevation at the mesh border
:param dt: Time step
:param dx: Space step
:param CL_h: Downstream boudary condition for water depth
:param CL_q: Upstream boundary condition for discharge
:param n: Manning coefficient
:param u_border: Velocity at the mesh border
:param h_center: Water depth at the mesh center
:param u_center: Velocity at the mesh center
"""
g = 9.81
slice_mesh = slice(1,-1)
slice_right_border = slice(2,None)
slice_left_border = slice(1,-1)
up = 0
do = 1
# valeur à gauche du bord
q_border[slice_right_border, up] = q[1:-1]
q_border[1,up] = CL_q
h_border[slice_right_border, up] = h[1:-1]
h_border[1,up] = h[1]
z_border[slice_right_border, up] = z[1:-1]
z_border[1,up] = z[1]
theta_border[slice_right_border, up] = theta[1:-1]
theta_border[1,up] = theta[1]
# valeur à droite du bord
q_border[slice_left_border, do] = q[1:-1]
q_border[-1,do] = q[-2]
h_border[slice_left_border, do] = h[1:-1]
h_border[-1,do] = CL_h
z_border[slice_left_border, do] = z[1:-1]
z_border[-1,do] = z[-2]
theta_border[slice_left_border, do] = theta[1:-1]
theta_border[-1,do] = theta[-2]
u_border[1:] = q_border[1:,up]/(h_border[1:,up] * theta_border[1:,up])
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.
u_center = q[slice_mesh]/(h[slice_mesh]*theta[slice_mesh])
#Continuity
h_t2[slice_mesh] = h_t1[slice_mesh] - dt/dx * (q_border[slice_right_border,up] - q_border[slice_left_border,up])
# Momentum
J = get_friction_slope_2D_Manning_semi_implicit(u_center, h[slice_mesh], n)
qm_right = u_border[slice_right_border]*q_border[slice_right_border,up]
qm_left = u_border[slice_left_border ]*q_border[slice_left_border,up]
press_right = 0.5 * g * (theta_border[slice_right_border,do] * h_border[slice_right_border,do])**2
press_left = 0.5 * g * (theta_border[slice_left_border ,do] * h_border[slice_left_border ,do])**2
bed_right = g * h_center * (z_border[slice_right_border,do] + (1-theta_border[slice_right_border,do]) * h_border[slice_right_border,do])
bed_left = g * h_center * (z_border[slice_left_border,do] + (1-theta_border[slice_left_border,do]) * h_border[slice_left_border,do])
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))
limit_h_q(h_t2, q_t2, hmin=1e-3, Froudemax=3.)
@jit(nopython=True)
[docs]
def splitting(q_left:np.float64, q_right:np.float64,
h_left:np.float64, h_right:np.float64,
z_left:np.float64, z_right:np.float64,
z_bridge_left:np.float64, z_bridge_right:np.float64) -> np.ndarray:
""" Splitting of the unknowns at border between two nodes
-- Based on the WOLF HECE original scheme
"""
prod_q = q_left * q_right
sum_q = q_left + q_right
if prod_q > 0.:
if q_left > 0.:
return np.asarray([q_left, min(h_left, z_bridge_left-z_left), h_right, z_right, z_bridge_right], dtype=np.float64)
else:
return np.asarray([q_right, min(h_right, z_bridge_right-z_right), h_left, z_left, z_bridge_left], dtype=np.float64)
elif prod_q < 0.:
if sum_q > 0.:
return np.asarray([q_left, min(h_left, z_bridge_left-z_left), h_right, z_right, z_bridge_right], dtype=np.float64)
elif sum_q < 0.:
return np.asarray([q_right, min(h_right, z_bridge_right-z_right), h_left, z_left, z_bridge_left], dtype=np.float64)
else:
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)
else:
if q_left<0.:
return np.asarray([np.float64(0.), np.float64(1.), h_left, z_left, z_bridge_left], dtype=np.float64)
elif q_right<0.:
return np.asarray([np.float64(0.), np.float64(1.), h_right, z_right, z_bridge_right], dtype=np.float64)
else:
return np.asarray([sum_q / 2., # q
(min(h_left, z_bridge_left-z_left) + min(h_right, z_bridge_right-z_right)) / 2., # h_vel
(h_left + h_right) / 2., # h
(z_left + z_right) / 2., # z
(z_bridge_left + z_bridge_right) / 2.], dtype=np.float64) # z_bridge
@jit(nopython=True)
[docs]
def Euler_RK_bridge(h_t1:np.ndarray, h_t2:np.ndarray,
q_t1:np.ndarray, q_t2:np.ndarray,
h:np.ndarray, q:np.ndarray,
h_border:np.ndarray, q_border:np.ndarray,
z:np.ndarray, z_border:np.ndarray,
dt:float, dx:float,
CL_h:float, CL_q:float,
n:float, u_border:np.ndarray,
h_center:np.ndarray, u_center:np.ndarray,
z_bridge:np.ndarray, z_bridge_border:np.ndarray,
press_mode:int=0,
infil_exfil=None) -> None:
"""
Solve the mass and momentum equations using a explicit Euler/Runge-Kutta scheme (only 1 step)
applying source terms for infiltration/exfiltration and pressure at the roof.
"""
g = 9.81
slice_mesh = slice(1,-1)
slice_right_border = slice(2,None)
slice_left_border = slice(1,-1)
up = 0
do = 1
# valeur à gauche du bord
z_border[slice_right_border, up] = z[1:-1]
z_border[1,up] = z[1]
z_bridge_copy = z_bridge.copy()
fs_cells = np.where(h <= z_bridge - z)[0]
press_cells = np.where(h > z_bridge - z)[0]
z_bridge_copy[fs_cells] = z[fs_cells] + h[fs_cells]
h_border[slice_right_border, up] = h[1:-1]
h_border[1,up] = h[1]
q_border[slice_right_border, up] = q[1:-1]
q_border[1,up] = CL_q
# valeur à droite du bord
z_border[slice_left_border, do] = z[1:-1]
z_border[-1,do] = z[-2]
z_bridge_border[slice_right_border, up] = z_bridge_copy[1:-1]
z_bridge_border[1,up] = z_bridge_copy[1]
z_bridge_border[slice_left_border, do] = z_bridge_copy[1:-1]
z_bridge_border[-1,do] = z_bridge_copy[-2]
h_border[slice_left_border, do] = h[1:-1]
h_border[-1,do] = CL_h
q_border[slice_left_border, do] = q[1:-1]
q_border[-1,do] = q[-2]
for i in range(1, len(h)-1):
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])
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])
# Continuity
# ++++++++++
h_t2[i] = h_t1[i] - dt/dx * (qc_right - qc_left)
# Momentum
# ++++++++
# Limited section at the right border and at the left border -- decentred downstream
d_right = zbc_right - zc_right
d_left = zbc_left - zc_left
# Pressure on the roof -- 0. if free surface
press_roof_right = hc_right - d_right
press_roof_left = hc_left - d_left
# Pressure integral at the right border and at the left border
press_right = 0.5 * g * (hc_right**2. - press_roof_right**2)
press_left = 0.5 * g * (hc_left**2. - press_roof_left**2)
# Friction slope based on center values
u_center = q[i] / (z_bridge_copy[i] - z[i])
# Number of surfaces
nb_frott = 2. if h[i] > z_bridge[i] - z[i] else 1.
# Integration water depth
h_frott = min(h[i], z_bridge[i] - z[i])
# Slope
J = get_friction_slope_2D_Manning_semi_implicit(u_center, h_frott/nb_frott, n)
# Velocity at the right border and at the left border -- decentred upstream
u_right = qc_right / h4u_right
u_left = qc_left / h4u_left
# Momentum at the right border and at the left border
qm_right = u_right * qc_right
qm_left = u_left * qc_left
# Mean pressure impacting bed reaction
h_mean = (hc_right + hc_left)/2.
bed_right = g * h_mean * zc_right
bed_left = g * h_mean * zc_left
# Mean pressure impacting roof reaction
h_roof_right = max(hc_right + zc_right - zbc_right, 0.)
h_roof_left = max(hc_left + zc_left - zbc_left , 0.)
h_roof_mean = (h_roof_right + h_roof_left) / 2.
roof_right = g * h_roof_mean * zbc_right
roof_left = g * h_roof_mean * zbc_left
rhs = (qm_right - qm_left + press_right - press_left + bed_right - bed_left - roof_right + roof_left)
if rhs !=0.:
pass
q_t2[i] = 1./(1. + dt * g * h_frott * J) * (q_t1[i] - dt/dx * rhs)
if infil_exfil is not None:
idx_up, idx_do, q_infil_exfil, u_infil_exfil, pond, k = infil_exfil
i = idx_up
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])
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])
# Limited section at the right border and at the left border -- decentred downstream
d_right = zbc_right - zc_right
d_left = zbc_left - zc_left
# Pressure on the roof -- 0. if free surface
press_roof_right = hc_right - d_right
press_roof_left = hc_left - d_left
# Pressure integral at the right border and at the left border
press_right = 0.5 * g * (hc_right**2. - press_roof_right**2)
press_left = 0.5 * g * (hc_left**2. - press_roof_left**2)
# Friction slope based on center values
u_center = q[i] / (z_bridge_copy[i] - z[i])
# Number of surfaces
nb_frott = 2. if h[i] > z_bridge[i] - z[i] else 1.
# Integration water depth
h_frott = min(h[i], z_bridge[i] - z[i])
# Slope
J = get_friction_slope_2D_Manning_semi_implicit(u_center, h_frott/nb_frott, n)
# Velocity at the right border and at the left border -- decentred upstream
u_right = qc_right / h4u_right
u_left = qc_left / h4u_left
# Momentum at the right border and at the left border
qm_right = u_right * qc_right
qm_left = u_left * qc_left
# Mean pressure impacting bed reaction
h_mean = (hc_right + hc_left)/2.
bed_right = g * h_mean * zc_right
bed_left = g * h_mean * zc_left
# Mean pressure impacting roof reaction
h_roof_right = max(hc_right + zc_right - zbc_right, 0.)
h_roof_left = max(hc_left + zc_left - zbc_left , 0.)
h_roof_mean = (h_roof_right + h_roof_left) / 2.
roof_right = g * h_roof_mean * zbc_right
roof_left = g * h_roof_mean * zbc_left
h_t2[i] = h_t1[i] - dt/dx * (qc_right - qc_left + q_infil_exfil)
q_t2[i] = 1./(1. + dt * g * h_frott * J) * (q_t1[i] - dt/dx * (qm_right - qm_left + \
press_right - press_left + \
bed_right - bed_left - \
roof_right + roof_left + \
u_infil_exfil * q_infil_exfil))
i = idx_do
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])
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])
# Limited section at the right border and at the left border -- decentred downstream
d_right = zbc_right - zc_right
d_left = zbc_left - zc_left
# Pressure on the roof -- 0. if free surface
press_roof_right = hc_right - d_right
press_roof_left = hc_left - d_left
# Pressure integral at the right border and at the left border
press_right = 0.5 * g * (hc_right**2. - press_roof_right**2)
press_left = 0.5 * g * (hc_left**2. - press_roof_left**2)
# Friction slope based on center values
u_center = q[i] / (z_bridge_copy[i] - z[i])
# Number of surfaces
nb_frott = 2. if h[i] > z_bridge[i] - z[i] else 1.
# Integration water depth
h_frott = min(h[i], z_bridge[i] - z[i])
# Slope
J = get_friction_slope_2D_Manning_semi_implicit(u_center, h_frott/nb_frott, n)
# Velocity at the right border and at the left border -- decentred upstream
u_right = qc_right / h4u_right
u_left = qc_left / h4u_left
# Momentum at the right border and at the left border
qm_right = u_right * qc_right
qm_left = u_left * qc_left
# Mean pressure impacting bed reaction
h_mean = (hc_right + hc_left)/2.
bed_right = g * h_mean * zc_right
bed_left = g * h_mean * zc_left
# Mean pressure impacting roof reaction
h_roof_right = max(hc_right + zc_right - zbc_right, 0.)
h_roof_left = max(hc_left + zc_left - zbc_left , 0.)
h_roof_mean = (h_roof_right + h_roof_left) / 2.
roof_right = g * h_roof_mean * zbc_right
roof_left = g * h_roof_mean * zbc_left
h_t2[i] = h_t1[i] - dt/dx * (qc_right - qc_left - q_infil_exfil)
q_t2[i] = 1./(1. + dt * g * h_frott * J) * (q_t1[i] - dt/dx * (qm_right - qm_left + \
press_right - press_left + \
bed_right - bed_left - \
roof_right + roof_left -\
u_infil_exfil * q_infil_exfil))
limit_h_q(h_t2, q_t2, hmin=1e-3, Froudemax=3.)
@ jit(nopython=True)
[docs]
def limit_h_q(h:np.ndarray, q:np.ndarray, hmin:float = 0., Froudemax:float = 3.) -> None:
""" Limit the water depth and the discharge
:param h: Water depth [m]
:param q: Discharge [m^2/s]
:param hmin: Minimum water depth [m]
:param Froudemax: Maximum Froude number [-]
"""
# retrieve positive and negative values
hpos = np.where(h > hmin)
hneg = np.where(h <= hmin)
# limit water depth
h[hneg] = hmin
q[hneg] = 0.
# limit discharge based on Froude number
Fr = np.zeros_like(h)
Fr[hpos] = np.abs(q[hpos]) / h[hpos] / np.sqrt(9.81 * h[hpos])
q[Fr > Froudemax] = Froudemax * np.sqrt(9.81 * h[Fr > Froudemax]) * h[Fr > Froudemax] * np.sign(q[Fr > Froudemax])
# --------------------------
# END JIT compiled functions
# --------------------------
# ----------------------
# START Problems section
# ----------------------
[docs]
def problem(dom:np.ndarray, z:np.ndarray, h0:float, q0:float, dx:float, CN:float, n:float):
""" Solve the mass and momentum equations using a explicit Runge-Kutta scheme (2 steps - 2nd order)
**NO BRIDGE**
"""
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)
totaltime = 4*3600
eps=1.
with tqdm(total=totaltime) as pbar:
t = 0.
while eps > 1e-12:
dt = compute_dt(dx, h, q, CN)
# Predictor step
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)
# Corrector step
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)
# Update -- Mean for second order in time
h = (h_pred + h_corr)/2.
q = (q_pred + q_corr)/2.
t+=dt
eps = np.sum(np.abs(q - q_pred))
pbar.update(dt)
print("Total time : ", t)
print("Residual : ", eps)
return h, q
[docs]
def problem_hedge(dom:np.ndarray, z:np.ndarray, h0:float, q0:float, dx:float, CN:float, n:float):
""" Solve the mass and momentum equations using a explicit Runge-Kutta scheme (2 steps - 2nd order)
**NO BRIDGE bur HEDGE in the middle**
"""
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)
theta = np.ones_like(h)
theta_border = np.ones_like(h_border)
slice_hedge = slice(len(h) // 2-1, len(h) // 2+2)
theta_val = 0.5
theta[slice_hedge] = theta_val
totaltime = 4*3600
eps=1.
with tqdm(total=totaltime) as pbar:
t = 0.
while eps > 1e-12:
dt = compute_dt(dx, h, q, CN)
# Predictor step
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)
# Corrector step
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)
# Update -- Mean for second order in time
h = (h_pred + h_corr)/2.
q = (q_pred + q_corr)/2.
t+=dt
eps = np.sum(np.abs(q - q_pred))
pbar.update(dt)
print("Total time : ", t)
print("Residual : ", eps)
return h, q, theta
[docs]
def problem_bridge(dom:np.ndarray, z:np.ndarray, z_bridge:np.ndarray,
h0:float, q0:float,
dx:float, CN:float, n:float,
press_mode:int = 4,
h_ini:np.ndarray = None, q_ini:np.ndarray = None) -> tuple[np.ndarray]:
""" Solve the mass and momentum equations using a explicit Rung-Kutta scheme (2 steps - 2nd order)
**WITH BRIDGE and NO OVERFLOW**
"""
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)
z_bridge_border = np.zeros_like(z_border)
if h_ini is not None:
h[:] = h_ini[:]
if q_ini is not None:
q[:] = q_ini[:]
totaltime = 4*3600
eps=1.
with tqdm(total=totaltime) as pbar:
t = 0.
while eps > 1e-7:
dt = compute_dt(dx, h, q, CN)
# Predictor step
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)
# Corrector step
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)
# Update -- Mean for second order in time
h = (h_pred + h_corr)/2.
q = (q_pred + q_corr)/2.
t+=dt
eps = np.sum(np.abs(q - q_pred))
pbar.update(dt)
print("Total time : ", t)
print("Residual : ", eps)
return h, q
[docs]
def problem_bridge_multiple_steadystates(dom:np.ndarray, z:np.ndarray, z_bridge:np.ndarray,
h0:float, qmin:float, qmax:float,
dx:float, CN:float, n:float,
press_mode:int = 4) -> list[tuple[float, np.ndarray, np.ndarray]]:
""" Solve multiple steady states for a given discharge range """
all_q = np.arange(qmin, qmax+.1, (qmax-qmin)/10)
ret = []
h = None
for curq in all_q:
h, q = problem_bridge(dom, z, z_bridge, h0, curq, dx, CN, n, press_mode, h_ini=h, q_ini=None)
ret.append((0., h.copy(), q.copy()))
return ret
[docs]
def problem_bridge_unsteady(dom:np.ndarray, z:np.ndarray, z_bridge:np.ndarray,
h0:float, q0:float,
dx:float, CN:float, n:float,
press_mode:int = 4):
""" Solve the mass and momentum equations using a explicit Runge-Kutta scheme (2 steps - 2nd order).
**WITH BRIDGE and NO OVERFLOW**
The downstream boundary condition rises temporarily.
Firstly, we stabilize the flow with a constant downstream boundary condition.
Then, we increase the downstream boundary condition.
"""
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)
z_bridge_border = np.zeros_like(z_border)
totaltime = 4*3600
eps=1.
# Compute steady state
with tqdm(total=totaltime) as pbar:
t = 0.
while eps > 1e-7:
dt = compute_dt(dx, h, q, CN)
# Predictor step
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)
# Corrector step
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)
# Update -- Mean for second order in time
h = (h_pred + h_corr)/2.
q = (q_pred + q_corr)/2.
t+=dt
eps = np.sum(np.abs(q - q_pred))
pbar.update(dt)
print("Total time : ", t)
print("Residual : ", eps)
res = []
totaltime = 2*3600
k=0
with tqdm(total=totaltime) as pbar:
t = 0.
while t < totaltime:
dt = compute_dt(dx, h, q, CN)
if t < totaltime/2:
h_cl = h0 + 7. * t/totaltime*2
else:
h_cl = h0 + 7. - 7. * (t - totaltime/2)/totaltime*2
# Predictor step
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)
# Corrector step
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)
# Update -- Mean for second order in time
h = (h_pred + h_corr)/2.
q = (q_pred + q_corr)/2.
if t > 300*k:
res.append((t, h.copy(), q.copy()))
k+=1
t+=dt
pbar.update(dt)
return res
[docs]
def problem_bridge_unsteady_topo(dom:np.ndarray, z:np.ndarray,
z_roof:np.ndarray, z_deck:np.ndarray, z_roof_null:float,
h0:float, q0:float,
dx:float, CN:float, n:float,
press_mode:int = 0,
motion_duration:float = 300.,
scenario_bc:Literal['unsteady_downstream_bc',
'hydrograph',
'hydrograph_2steps',
'Gauss'] = 'unsteady_downstream_bc',
min_overflow:float = 0.05,
updating_time_interval:float = 0.
):
""" Solve the mass and momentum equations using a explicit Rung-Kutta scheme (2 steps - 2nd order).
**WITH BRIDGE and OVERFLOW**
"""
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)
z_roof_border = np.zeros_like(z_border)
totaltime = 3600 #4*3600
eps=1.
# Compute steady state
with tqdm(total=totaltime) as pbar:
t = 0.
while eps > 1e-7:
dt = compute_dt(dx, h, q, CN)
# Predictor step
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)
# Corrector step
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)
# Update -- Mean for second order in time
h = (h_pred + h_corr)/2.
q = (q_pred + q_corr)/2.
t+=dt
eps = np.sum(np.abs(q - q_pred))
pbar.update(dt)
print("Total time : ", t)
print("Residual : ", eps)
g = 9.81
res = []
# Functions to evaluate the head and the local head loss coefficient
def compute_head(z,h,q):
return z+h+(q/h)**2/2/g
def compute_delta_head(zup,hup,qup,zdo,hdo,qdo):
return compute_head(zup,hup,qup) - compute_head(zdo,hdo,qdo)
def compute_k_mean(zup,hup,qup,zdo,hdo,qdo,A_bridge):
delta = compute_delta_head(zup,hup,qup,zdo,hdo,qdo)
q_mean = (qup+qdo)/2
return delta/((q_mean/A_bridge)**2/2/g)
def compute_losses(q, A_bridge, k):
""" Compute the losses based on the flow rate and the local head loss coefficient
:return: k * |Q| * Q / A^2 / 2 / g
:unit: [m]
"""
return k * q * np.abs(q) / A_bridge**2. /2. / g
def compute_losses_semi_implicit(q, A_bridge, k):
""" Compute part of the losses based on the flow rate and the local head loss coefficient
to use in the semi-implicit scheme.
:return: k * |Q| / A / 2.
:unit: [s/m²]
"""
return k * np.abs(q) / A_bridge /2.
def compute_q_wo_inertia(zup,hup,qup,zdo,hdo,qdo,A_bridge,k):
""" Compute the flow rate based on Bernoulli equation
without inertia term """
delta = compute_delta_head(zup,hup,qup,zdo,hdo,qdo)
return np.sqrt(2*g*np.abs(delta)/k)*A_bridge*np.sign(delta), delta
def compute_q_w_inertia(zup:float, hup:float, qup:float,
zdo:float, hdo:float, qdo:float,
A_bridge:float, k:float, q_prev:float,
dt:float, length:float):
""" Compute the flow rate based on Bernoulli equation
with inertia term
$ \frac{\partial Q}{\partial t} + \frac {g A}{L} (Head_do - Head_up + Losses) = 0 $
$ Losses = k \frac {U^2}{2 g} = k \frac {Q |Q|}{A^2 2 g} $
"""
# new_q, delta = compute_q_wo_inertia(zup,hup,qup,zdo,hdo,qdo,A_bridge,k)
delta = compute_delta_head(zup,hup,qup,zdo,hdo,qdo)
if delta > 1.:
pass
inv_dt = 1./ dt
return (inv_dt * q_prev + g * A_bridge * delta / length) / (inv_dt + compute_losses_semi_implicit(q_prev, A_bridge, k) / length)
def _update_top(z_start:np.ndarray, z_end:np.ndarray, move_time:float, move_totaltime:float, reverse:bool=False):
if reverse:
pond = (move_totaltime - move_time) / move_totaltime
else:
pond = move_time / move_totaltime
loc_z = z_end * pond + z_start * (1.-pond)
return pond, loc_z
def update_top_q(z:np.ndarray, h:np.ndarray, q:np.ndarray, z_bridge:np.ndarray,
idx_up:int, idx_do:int,
survey_up:int, survey_do:int,
A:float, k:float, q_prev:float, dt:float,
move_time:float, move_totaltime:float, move_restore:bool,
z_roof_start:np.ndarray, z_roof_end:np.ndarray,
z_bath_start:np.ndarray, z_bath_end:np.ndarray,
update_top:bool=True, stop_motion:bool = False):
if stop_motion:
move_time = move_totaltime
zup = z[survey_up]
zdo = z[survey_do]
hup = h[survey_up]
hdo = h[survey_do]
qup = q[survey_up]
qdo = q[survey_do]
# Update the flow rate considering inertia (generalized Bernoulli)
qtot_infil_exfil = compute_q_w_inertia(zup, hup, qup,
zdo, hdo, qdo,
A, k, q_prev, dt,
length=dx *(idx_do-idx_up-1))
if update_top:
pond, z_bridge[bridge] = _update_top(z_roof_start, z_roof_end, move_time, move_totaltime, move_restore)
pond, z[bridge] = _update_top(z_bath_start, z_bath_end, move_time, move_totaltime, move_restore)
# if move_restore:
# zref_up = zup + hup - .15
# z[bridge] = np.minimum(z[bridge], zref_up)
else:
pond = 0. if move_restore else 1.
if stop_motion and move_restore:
infil_exfil = None
else:
# Applying the infiltration/exfiltration linearly according to the topo-bathymetry evolution
q_infil_exfil = qtot_infil_exfil * pond
infil_exfil = (idx_up, idx_do, q_infil_exfil, qup/hup, pond, k)
return infil_exfil, z_bridge, z, qtot_infil_exfil
bridge = np.where(z_roof != z_roof_null)[0]
idx_up = bridge[0]-1
idx_do = bridge[-1]+1
survey_up = idx_up-1
survey_do = idx_do+1
z_overflow = z_deck[bridge].max() + min_overflow
totaltime = 2*3600
total_compute = totaltime + 1800 + 2.
infil_exfil = None
bridge_in_motion = False
filled_bridge = False
emptying_bridge = False
motion_completed = True
local_motion_time = 0.
total_motion_time = motion_duration
q_infil_t_current = 0.
delta_res_time_def = 30.
if scenario_bc == 'unsteady_downstream_bc':
dh_cl = 7.
dq_cl = 0.
elif scenario_bc == 'unsteady_downstream_bc_culvert':
dh_cl = 2.
dq_cl = 0.5
elif scenario_bc == 'hydrograph_culvert':
dh_cl = 1.
dq_cl = 3.
elif scenario_bc == 'hydrograph':
dh_cl = 2.
dq_cl = 8.
elif scenario_bc == 'hydrograph_2steps':
dh_cl = 2.
dq_cl = 8.
elif scenario_bc == 'hydrograph_2steps_culvert':
dh_cl = 1.
dq_cl = 10.
elif scenario_bc == 'Gauss':
dh_cl = 2.
dq_cl = 8.
with tqdm(total=total_compute, desc=scenario_bc) as pbar:
t = 0.
res_time = 0.
update_time = 0.
delta_res_time = delta_res_time_def
z_roof_start = None
z_roof_end = None
z_bath_start = None
z_bath_end = None
while t < total_compute:
dt = compute_dt(dx, h, q, CN)
if scenario_bc == 'unsteady_downstream_bc' or scenario_bc == 'unsteady_downstream_bc_culvert':
# The downstream boundary condition evolves linearly
# from h0 to h0 + dh_cl in totaltime/2 seconds
# keeps the value h0 + dh_cl during 1000 seconds
# and then from h0 + dh_cl to h0 in totaltime/2 seconds
#
# ____
# / \
# / \
# ------ / \
# _/ \____
if t < totaltime/2:
h_cl = h0 + dh_cl * t/totaltime*2
q_cl = q0 + dq_cl * t/totaltime*2
else:
if t < totaltime/2 + 1000.:
h_cl = h0 + dh_cl
q_cl = q0 + dq_cl
else:
h_cl = h0 + dh_cl - dh_cl * (t - (totaltime/2+1000.))/(totaltime/2 - 1000.)
q_cl = q0 + dq_cl - dq_cl * (t - (totaltime/2+1000.))/(totaltime/2 - 1000.)
elif scenario_bc == 'hydrograph' or scenario_bc == 'hydrograph_culvert':
# The downstream boundary condition evolves linearly
# from h0 to h0 + dh_cl in totaltime/2 seconds
# keeps the value h0 + dh_cl during 1000 seconds
# and then from h0 + dh_cl to h0 - dh_cl/2 in totaltime/2 seconds
#
# The upstream boundary condition evolves linearly
# from q0 to q0 + dq_cl in totaltime/2 seconds
# keeps the value q0 + dq_cl during 1000 seconds
# and then from q0 + dq_cl to q0 - dq_cl/2 in totaltime/2 seconds
#
# _____ ____
# / \ / \
# / \ / \
# / \ / \
# _/ \ __/ \
# \ \
# \______ \____
if t < totaltime/2:
h_cl = h0 + dh_cl * t/totaltime*2
q_cl = q0 + dq_cl * t/totaltime*2
else:
if t < totaltime/2 + 1000.:
h_cl = h0 + dh_cl
q_cl = q0 + dq_cl
elif t < totaltime:
h_cl = h0 + dh_cl - dh_cl * (t - (totaltime/2+1000.))/(totaltime/2 - 1000.) * 1.5
q_cl = q0 + dq_cl - dq_cl * (t - (totaltime/2+1000.))/(totaltime/2 - 1000.) * 1.5
else:
h_cl = h0 - dh_cl / 2.
q_cl = q0 - dq_cl / 2.
elif scenario_bc == 'hydrograph_2steps' or scenario_bc == 'hydrograph_2steps_culvert':
# Same as hydrograph but the downstream boundary condition
# evolves linearly a second time during peek flow
if t < totaltime/2:
h_cl = h0 + dh_cl * t/totaltime*2
q_cl = q0 + dq_cl * t/totaltime*2
else:
if t < totaltime/2 + 500.:
h_cl = h0 + dh_cl + dh_cl * (t - (totaltime/2))/(500.)
q_cl = q0 + dq_cl
elif t < totaltime/2 + 1000.:
h_cl = h0 + 2* dh_cl - dh_cl * (t - (totaltime/2+500.))/(500.)
q_cl = q0 + dq_cl
elif t < totaltime:
h_cl = h0 + dh_cl - dh_cl * (t - (totaltime/2+1000.))/(totaltime/2 - 1000.) * 1.5
q_cl = q0 + dq_cl - dq_cl * (t - (totaltime/2+1000.))/(totaltime/2 - 1000.) * 1.5
else:
h_cl = h0 - dh_cl / 2.
q_cl = q0 - dq_cl / 2.
elif scenario_bc == 'Gauss':
# The downstream and upstream boundary conditions evolve
# according to a Gaussian function
h_cl = h0 + dh_cl * np.exp(-((t-totaltime/2)**.5)/3600)
q_cl = q0 + dq_cl * np.exp(-((t-totaltime/2)**.5)/3600)
# Predictor step
Euler_RK_bridge(h, h_pred, q, q_pred, h, q, h_border, q_border, z, z_border,
dt, dx, h_cl, q_cl,
n, u_border, h_center, u_center, z_roof, z_roof_border,
press_mode, infil_exfil)
# Corrector step
Euler_RK_bridge(h, h_corr, q, q_corr, h_pred, q_pred, h_border, q_border, z, z_border,
dt, dx, h_cl, q_cl,
n, u_border, h_center, u_center, z_roof, z_roof_border,
press_mode, infil_exfil)
# Update -- Mean for second order in time
h = (h_pred + h_corr)/2.
q = (q_pred + q_corr)/2.
if t >= res_time:
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.)))
res_time += delta_res_time
if updating_time_interval == 0. or t> update_time:
update_time += updating_time_interval
if z[survey_up] + h[survey_up] > z_overflow:
# Overflow
# Convert Bridge into topography
# add infiltration/exfiltration
if not bridge_in_motion and motion_completed and not filled_bridge:
# Movement must be initiated...
# Decreasing the interval of time for the
# saving to be more precise when plotting
delta_res_time = delta_res_time_def / 5.
bridge_in_motion = True
emptying_bridge = False
motion_completed = False
local_motion_time = 0.
starting_time = t
# Keeping the old values -- Can be useful when restoring
old_z_roof = z_roof.copy()
old_z = z.copy()
# Reference section of the bridge...
# Keeping the minimum distance between the bridge and the floor
A_bridge = np.min(z_roof[bridge] - z[bridge])
# Computing global head loss coefficient associated to the bridge and the reference section
k_bridge = compute_k_mean(z[survey_up], h[survey_up], q[survey_up],
z[survey_do], h[survey_do], q[survey_do],
A_bridge)
# Starting the motion...
# - the bridge's roof is going up
# - the bridge's floor is going up
z_roof_start = old_z_roof[bridge]
z_roof_end = z_roof_null
z_bath_start = old_z[bridge]
z_bath_end = z_deck[bridge]
# Mean Flow rate under the bridge
q_infil_t_current = np.mean(q[bridge])
infil_exfil, z_roof, z, q_infil_t_current = update_top_q(z, h, q, z_roof,
idx_up, idx_do,
survey_up, survey_do,
A_bridge, k_bridge, q_infil_t_current,
dt,
local_motion_time, total_motion_time,
emptying_bridge,
z_roof_start, z_roof_end, z_bath_start, z_bath_end)
else:
if not motion_completed:
# Movement is initiated but not finished...
# Updating the local time
# local_motion_time += dt
local_motion_time = t - starting_time
if local_motion_time > total_motion_time:
# Total time is reached...
# ... so terminate the movement
delta_res_time = delta_res_time_def
bridge_in_motion = False
motion_completed = True
filled_bridge = not filled_bridge
infil_exfil, z_roof, z, q_infil_t_current = update_top_q(z, h, q, z_roof,
idx_up, idx_do,
survey_up, survey_do,
A_bridge, k_bridge, q_infil_t_current, dt,
local_motion_time, total_motion_time, emptying_bridge,
z_roof_start, z_roof_end, z_bath_start, z_bath_end,
stop_motion= True)
local_motion_time = 0.
else:
# Total time is not reached...
# ... so continue the movement
infil_exfil, z_roof, z, q_infil_t_current = update_top_q(z, h, q, z_roof,
idx_up, idx_do,
survey_up, survey_do,
A_bridge, k_bridge, q_infil_t_current, dt,
local_motion_time, total_motion_time, emptying_bridge,
z_roof_start, z_roof_end, z_bath_start, z_bath_end)
else:
# Movement is done...
if infil_exfil is not None:
# Updating the infiltration discharge according to head difference
infil_exfil, z_roof, z, q_infil_t_current = update_top_q(z, h, q, z_roof,
idx_up, idx_do,
survey_up, survey_do,
A_bridge, k_bridge, q_infil_t_current, dt,
local_motion_time, total_motion_time, emptying_bridge,
z_roof_start, z_roof_end, z_bath_start, z_bath_end,
update_top=False)
else:
# No overflow
if bridge_in_motion:
# But movement is initiated...
# local_motion_time += dt
local_motion_time = t - starting_time
if local_motion_time > total_motion_time:
delta_res_time = delta_res_time_def
# Total time is reached...
# ... so terminate the movement
bridge_in_motion = False
motion_completed = True
filled_bridge = not filled_bridge
infil_exfil, z_roof, z, q_infil_t_current = update_top_q(z, h, q, z_roof,
idx_up, idx_do,
survey_up, survey_do,
A_bridge, k_bridge, q_infil_t_current, dt,
local_motion_time, total_motion_time, emptying_bridge,
z_roof_start, z_roof_end, z_bath_start, z_bath_end,
stop_motion= True)
local_motion_time = 0.
else:
# Total time is not reached...
# ... so continue the movement
infil_exfil, z_roof, z, q_infil_t_current = update_top_q(z, h, q, z_roof,
idx_up, idx_do,
survey_up, survey_do,
A_bridge, k_bridge, q_infil_t_current, dt,
local_motion_time, total_motion_time, emptying_bridge,
z_roof_start, z_roof_end, z_bath_start, z_bath_end)
else:
if infil_exfil is not None:
if motion_completed:
# The bridge is not moving and the infiltration/exfiltration exists
# We can start to restore the bridge as it was before the overflow...
delta_res_time = delta_res_time_def / 5.
bridge_in_motion = True
local_motion_time = 0.
motion_completed = False
emptying_bridge = True
starting_time = t
infil_exfil, z_roof, z, q_infil_t_current = update_top_q(z, h, q, z_roof,
idx_up, idx_do,
survey_up, survey_do,
A_bridge, k_bridge, q_infil_t_current, dt,
local_motion_time, total_motion_time, emptying_bridge,
z_roof_start, z_roof_end, z_bath_start, z_bath_end,
)
t+=dt
pbar.update(dt)
return res
# --------------------
# END Problems section
# --------------------
# ----------------
# PLOTTING SECTION
# ----------------
import matplotlib.animation as animation
[docs]
def plot_bridge(ax:plt.Axes, x:np.ndarray,
h1:np.ndarray, h2:np.ndarray,
q1:np.ndarray, q2:np.ndarray,
z:np.ndarray, z_bridge:np.ndarray,
hu:float):
u1=np.zeros_like(q1)
u2=np.zeros_like(q1)
u1[1:-1] = q1[1:-1]/h1[1:-1]
u2[1:-1] = q2[1:-1]/np.minimum(h2[1:-1],z_bridge[1:-1]-z[1:-1])
ax.plot(x[1:-1], z[1:-1], label = 'z')
ax.plot(x[1:-1], z_bridge[1:-1], label = 'bridge')
ax.plot(x[1:-1], z[1:-1] + h1[1:-1], label = 'z + h1')
ax.plot(x[1:-1], z[1:-1] + h2[1:-1], label = 'z + h2')
ax.plot(x[1:-1], q1[1:-1], label = 'q')
under_bridge = np.where(h2 > z_bridge - z)[0]
free_surface = np.where(h2 <= z_bridge - z)[0]
ax.plot(x[1:-1], z[1:-1] + h1[1:-1] + u1[1:-1]**2/2/9.81, label = 'head1')
ax.plot(x[1:-1], z[1:-1] + h2[1:-1] + u2[1:-1]**2/2/9.81, label = 'head2')
# 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')
if hu != 99999.:
ax.plot(x[1:-1], z[1:-1]+hu, linestyle='--', label = 'h uniform')
ax.legend()
[docs]
def plot_hedge(ax:plt.Axes, x:np.ndarray,
h1:np.ndarray, h2:np.ndarray,
q1:np.ndarray, q2:np.ndarray,
z:np.ndarray, hu:float,
theta:np.ndarray):
u1=np.zeros_like(q1)
u2=np.zeros_like(q1)
u1[1:-1] = q1[1:-1]/h1[1:-1]
u2[1:-1] = q2[1:-1]/(h2[1:-1]*theta[1:-1])
ax.plot(x[1:-1], z[1:-1], label = 'z')
ax.plot(x[1:-1], z[1:-1] + h1[1:-1], label = 'z + h1')
ax.plot(x[1:-1], z[1:-1] + h2[1:-1], label = 'z + h2')
ax.plot(x[1:-1], u1[1:-1], label = 'u1')
ax.plot(x[1:-1], u2[1:-1], label = 'u2')
# ax.plot(x[1:-1], z[1:-1] + h1[1:-1] + u1[1:-1]**2/2/9.81, label = 'head1')
# ax.plot(x[1:-1], z[1:-1] + h2[1:-1] + u2[1:-1]**2/2/9.81, label = 'head2')
ax.plot(x[1:-1], z[1:-1]+hu, linestyle='--', label = 'h uniform')
ax.legend()
[docs]
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]],
x:np.ndarray, z_ini:np.ndarray, hu:float, z_null:float, length:float, title:str= "Bridge", motion_duration=300.):
fig, axes = plt.subplots(2,1)
all_q_middle = [cur[2][len(dom)//2] for cur in res]
all_q_inf_ex = [cur[5][2] for cur in res]
all_q_up = [cur[2][1] for cur in res]
all_q_down = [cur[2][-2] for cur in res]
all_times = [cur[0] for cur in res]
idx_Froude = [int(len(dom)*cur) for cur in np.linspace(0,100,11,True)/100]
idx_Froude[0] += 5
idx_Froude[-1] -= 5
x_Froude = x[idx_Froude]
Froude = np.zeros_like(x_Froude)
def update(frame):
ax:plt.Axes
ax = axes[0]
ax.clear()
z:np.ndarray
t, h, q, z, z_roof, inf_ex = res[frame]
for i, idx in enumerate(idx_Froude):
if h[idx]==0.:
Froude[i] = 0.
else:
Froude[i] = q[idx] / h[idx] /np.sqrt(9.81*h[idx])
ax.fill_between(x[1:-1], z[1:-1], z[1:-1] + h[1:-1], color='blue', alpha=0.5)
ax.plot(x[1:-1], z[1:-1] + h[1:-1], label='water level [m]', color='blue')
ax.fill_between(x[1:-1], np.ones(z.shape)[1:-1] * z.min(), z[1:-1], color='brown', alpha=0.5)
ax.plot(x[1:-1], z[1:-1], label='bottom level [m]', color='brown')
slice_roof = z_roof != z_null
ax.fill_between(x[slice_roof], z_roof[slice_roof], np.ones(z.shape)[slice_roof] * z_null, color='grey', alpha=0.5)
ax.plot(x[slice_roof], z_roof[slice_roof], label='roof level [m]', color='grey')
# ax.plot(x[1:-1], z_ini[1:-1] + hu, linestyle='--', label='h uniform')
ax.plot(x[1:-1], q[1:-1], linestyle='-', label='flow rate [$m^2/s$]', color='black', linewidth=1.5)
ax.legend(loc='upper right')
ax.fill(poly_bridge_x, poly_bridge_y, color='black', alpha=0.8)
q_middle = q[len(dom)//2]
q_inf_ex = inf_ex[2]
txt = f'Total flow rate {q_middle+q_inf_ex:.2f} $m^2/s$'
txt += f'\nOverflow = {q_middle:.2f} $m^2/s$ - {q_middle/(q_inf_ex+q_middle)*100:.2f} %'
txt += f'\nUnderflow = {q_inf_ex:.2f} $m^2/s$ - {q_inf_ex/(q_inf_ex+q_middle)*100:.2f} %'
in_txt = '$k_{loss}$ ='
txt += f'\n\nMotion time = {motion_duration:.1f} s -- {in_txt} {inf_ex[-1]:.2f}'
ax.text(x[len(dom)//2+8], 11., txt, fontsize=9)
for posFroude, curFroude in zip(x_Froude, Froude):
ax.text(posFroude, 8., f'Fr = {curFroude:.2f}', fontsize=9, horizontalalignment='center')
ax.set_xlim(0, 500)
ax.set_xticks(np.arange(0, 501, 50))
ax.grid(axis='x')
ax.set_ylim(0, 20)
ax.set_title(f'{title} - Length = {length:.1f} m - Time = {t:.1f} s')
ax = axes[1]
ax.clear()
ax.plot(all_times[:frame], all_q_middle[:frame], label='Overflow', color='blue')
ax.plot(all_times[:frame], all_q_inf_ex[:frame], label='Underflow', color='red')
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')
ax.plot(all_times[:frame], all_q_up[:frame], label='Upstream', color='black', linestyle='--', linewidth=1.)
ax.plot(all_times[:frame], all_q_down[:frame], label='Downstream', color='black', linestyle='-.', linewidth=1.)
ax.legend(loc='upper right')
ax.set_title('Flow rate')
ax.set_xlabel('Time [s]')
ax.set_ylabel('Flow rate [$m^2/s$]')
ax.set_ylim(0, 20)
ax.set_xlim(0, all_times[-1])
ax.set_xticks(np.arange(0, all_times[-1]+10, 900))
ax.grid()
fig.set_size_inches(20,8)
update(0)
fig.tight_layout()
ani = animation.FuncAnimation(fig, update, frames=len(res), repeat=True)
return ani
# -------------
# REAL PROBLEMS
# -------------
[docs]
def lake_at_rest():
""" Compute Lake at rest problem
The problem is a simple steady state problem with a bridge in the middle of the domain.
The bridge is a simple flat bridge with a height of 2 m.
No discharge and no water movement is expected.
"""
length = 500.
dx = 1.
CN = 0.4
h0 = 4. # Initial water depth
q0 = 0. # Initial discharge
n = 0.025
slope = 0 #1e-4
press_mode = 4
hu = 0
dom, x, z = domain(length, dx, slope)
x = np.array(x)
# Bridge roof level is 10 m everywhere except in the middle of the domain
z_bridge = np.ones_like(z) * 10.
z_bridge[len(dom)//2-5:len(dom)//2+5] = z[len(dom)//2-5:len(dom)//2+5] + 2.
fig, axes = plt.subplots(3,1)
# free surface flow
h1, q1 = problem(dom, z, h0, q0, dx, CN, n)
# partially pressurized flow
h2, q2 = problem_bridge(dom, z, z_bridge, h0, q0, dx, CN, n, press_mode=press_mode)
assert np.allclose(h1[1:-1], h0), 'Free surface flow is not steady state'
assert np.allclose(q1[1:-1], q0), 'Free surface flow is not steady state'
assert np.allclose(h2[1:-1], h0), 'Partially pressurized flow is not steady state'
assert np.allclose(q2[1:-1], q0), 'Partially pressurized flow is not steady state'
plot_bridge(axes[0], x, h1, h2, q1, q2, z, z_bridge, hu)
# increasing water depth
h0 += 1.
# free surface flow
h1, q1 = problem(dom, z, h0+1., q0, dx, CN, n)
# partially pressurized flow
h2, q2 = problem_bridge(dom, z, z_bridge, h0, q0, dx, CN, n, press_mode=press_mode)
assert np.allclose(h1[1:-1], h0+1.), 'Free surface flow is not steady state'
assert np.allclose(q1[1:-1], q0), 'Free surface flow is not steady state'
assert np.allclose(h2[1:-1], h0), 'Partially pressurized flow is not steady state'
assert np.allclose(q2[1:-1], q0), 'Partially pressurized flow is not steady state'
plot_bridge(axes[1], x, h1, h2, q1, q2, z, z_bridge, hu)
# increasing water depth
h0 += 1.
# free surface flow
h1, q1 = problem(dom, z, h0+2., q0, dx, CN, n)
# partially pressurized flow
h2, q2 = problem_bridge(dom, z, z_bridge, h0, q0, dx, CN, n, press_mode=press_mode)
assert np.allclose(h1[1:-1], h0+2.), 'Free surface flow is not steady state'
assert np.allclose(q1[1:-1], q0), 'Free surface flow is not steady state'
assert np.allclose(h2[1:-1], h0), 'Partially pressurized flow is not steady state'
assert np.allclose(q2[1:-1], q0), 'Partially pressurized flow is not steady state'
plot_bridge(axes[2], x, h1, h2, q1, q2, z, z_bridge, hu)
fig.set_size_inches(15, 10)
fig.tight_layout()
return fig, axes
[docs]
def water_line():
""" Compute Water line problems
Length = 500 m
dx = 1 m
CN = 0.4
h0 = 4 m
q0 = 7 m^2/s
n = 0.025
slope = 1e-4
"""
length = 500.
dx = 1.
CN = 0.4
h0 = 4.
q0 = 7.
n = 0.025
slope = 1e-4
press_mode = 4
dom, x, z = domain(length, dx, slope)
x = np.array(x)
hu = uniform_waterdepth(slope, q0, n)
# bridge roof level is 10 m everywhere except in the middle of the domain
# where the bridge is located.
# The bridge is a flat bridge with a height of 2 m.
z_bridge = np.ones_like(z) * 10.
z_bridge[len(dom)//2-5:len(dom)//2+5] = z[len(dom)//2-5:len(dom)//2+5] + 2.
fig, axes = plt.subplots(3,1)
h1, q1 = problem(dom, z, h0, q0, dx, CN, n)
h2, q2 = problem_bridge(dom, z, z_bridge, h0, q0, dx, CN, n, press_mode=press_mode)
plot_bridge(axes[0], x, h1, h2, q1, q2, z, z_bridge, hu)
h1, q1 = problem(dom, z, h0+1., q0, dx, CN, n)
h2, q2 = problem_bridge(dom, z, z_bridge, h0+1., q0, dx, CN, n, press_mode=press_mode)
plot_bridge(axes[1], x, h1, h2, q1, q2, z, z_bridge, hu)
h1, q1 = problem(dom, z, h0+2., q0, dx, CN, n)
h2, q2 = problem_bridge(dom, z, z_bridge, h0+2., q0, dx, CN, n, press_mode=press_mode)
plot_bridge(axes[2], x, h1, h2, q1, q2, z, z_bridge, hu)
fig.set_size_inches(20, 10)
fig.tight_layout()
return fig, axes
[docs]
def water_line_noloss_noslope():
""" Compute Water line problems
Length = 500 m
dx = 1 m
CN = 0.4
h0 = 4 m
q0 = 7 m^2/s
n = 0.0
slope = 0.0
"""
length = 500.
dx = 1.
CN = 0.4
h0 = 4.
q0 = 7.
n = 0.
slope = 0.
press_mode = 4
dom, x, z = domain(length, dx, slope)
x = np.array(x)
hu = uniform_waterdepth(slope, q0, n)
# bridge roof level is 10 m everywhere except in the middle of the domain
# where the bridge is located.
# The bridge is a flat bridge with a height of 2 m.
z_bridge = np.ones_like(z) * 10.
z_bridge[len(dom)//2-5:len(dom)//2+5] = z[len(dom)//2-5:len(dom)//2+5] + 2.
fig, axes = plt.subplots(3,1)
h1, q1 = problem(dom, z, h0, q0, dx, CN, n)
h2, q2 = problem_bridge(dom, z, z_bridge, h0, q0, dx, CN, n, press_mode=press_mode)
plot_bridge(axes[0], x, h1, h2, q1, q2, z, z_bridge, hu)
h1, q1 = problem(dom, z, h0+1., q0, dx, CN, n)
h2, q2 = problem_bridge(dom, z, z_bridge, h0+1., q0, dx, CN, n, press_mode=press_mode)
plot_bridge(axes[1], x, h1, h2, q1, q2, z, z_bridge, hu)
h1, q1 = problem(dom, z, h0+2., q0, dx, CN, n)
h2, q2 = problem_bridge(dom, z, z_bridge, h0+2., q0, dx, CN, n, press_mode=press_mode)
plot_bridge(axes[2], x, h1, h2, q1, q2, z, z_bridge, hu)
fig.set_size_inches(20, 10)
fig.tight_layout()
return fig, axes
[docs]
def water_lines():
""" Compute multiple water lines problems.
Evaluate the head loss due to the bridge and compare
to theoretical fomula.
"""
length = 500.
dx = 1.
CN = 0.4
h0 = 4.
q0 = 7.
n = 0. #0.025
slope = 0 #1e-4
press_mode = 4
dom, x, z = domain(length, dx, slope)
x = np.array(x)
hu = 0. #uniform_waterdepth(slope, q0, n)
b_bridge = 2.
z_bridge = np.ones_like(z) * 10.
z_bridge[len(dom)//2-5:len(dom)//2+5] = z[len(dom)//2-5:len(dom)//2+5] + b_bridge
idx_before_bridge = len(dom)//2-5 -2
idx_after_bridge = len(dom)//2+5 +2
idx_bridge = len(dom)//2
h0 = 4.5
res = problem_bridge_multiple_steadystates(dom, z, z_bridge, h0, 3.5, 20., dx, CN, n, press_mode=press_mode)
fig, axes = plt.subplots(2,1)
ax:plt.Axes
ax = axes[0]
for cur in res:
ax.plot(x[1:-1], z[1:-1]+cur[1][1:-1], label = f't = {cur[0]:.1f}')
ax.plot(x[1:-1], z[1:-1]+hu, linestyle='--', label = 'h uniform')
ax.plot(x[1:-1], z_bridge[1:-1], label = 'bridge')
ax.plot(x[1:-1], z[1:-1], label = 'z')
ax.legend()
# Head losses
# pressure before, after and at the bridge
press_before = [cur[1][idx_before_bridge] for cur in res]
press_after = [cur[1][idx_after_bridge] for cur in res]
press_bridge = [cur[1][idx_bridge] for cur in res]
h_bridge = np.minimum(press_bridge, z_bridge[idx_bridge] - z[idx_bridge])
# flow rate before, after and at the bridge
q_before = [cur[2][idx_before_bridge] for cur in res]
q_after = [cur[2][idx_after_bridge] for cur in res]
q_bridge = [cur[2][idx_bridge] for cur in res]
# velocity before, after and at the bridge
u_before = [q/h for q,h in zip(q_before, press_before)]
u_after = [q/h for q,h in zip(q_after, press_after)]
u_bridge = [q/h for q,h in zip(q_bridge, h_bridge)]
# head before, after and at the bridge
head_before = [z[idx_before_bridge] + press_before[i] + u_before[i]**2/2/9.81 for i in range(len(res))]
head_after = [z[idx_after_bridge] + press_after[i] + u_after[i]**2/2/9.81 for i in range(len(res))]
head_bridge = [z[idx_bridge] + press_bridge[i] + u_bridge[i]**2/2/9.81 for i in range(len(res))]
# head losses
delta_head_total = [head_before[i] - head_after[i] for i in range(len(res))]
delta_head_up = [head_before[i] - head_bridge[i] for i in range(len(res))]
delta_head_do = [head_bridge[i] - head_after[i] for i in range(len(res))]
ax = axes[1]
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')
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')
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')
ax.set_xlabel('Computed $\Delta H$ [m]')
ax.set_ylabel('Theoretical $\Delta H$ [m]')
ax.plot([0,1],[0,1], linestyle='-', linewidth=2, color='black')
ax.set_aspect('equal')
ax.legend()
fig.set_size_inches(20, 10)
fig.tight_layout()
return fig, axes
[docs]
def unsteady_without_bedmotion():
"""
Compute unsteady problem without bed motion.
The downstream boundary condition rises and decreases.
"""
length = 500.
dx = 1.
CN = 0.4
h0 = 4.
q0 = 7.
n = 0. #0.025
slope = 0 #1e-4
press_mode = 4
dom, x, z = domain(length, dx, slope)
x = np.array(x)
hu = 0. #uniform_waterdepth(slope, q0, n)
b_bridge = 2.
z_bridge = np.ones_like(z) * 10.
z_bridge[len(dom)//2-5:len(dom)//2+5] = z[len(dom)//2-5:len(dom)//2+5] + b_bridge
idx_before_bridge = len(dom)//2-5 -2
idx_after_bridge = len(dom)//2+5 +2
idx_bridge = len(dom)//2
h0 = 1.5
res = problem_bridge_unsteady(dom, z, z_bridge, h0, q0, dx, CN, n, press_mode=press_mode)
fig, axes = plt.subplots(2,1)
ax:plt.Axes
ax = axes[0]
for cur in res:
ax.plot(x[1:-1], z[1:-1]+cur[1][1:-1], label = f't = {cur[0]:.1f}')
ax.plot(x[1:-1], z[1:-1]+hu, linestyle='--', label = 'h uniform')
ax.plot(x[1:-1], z_bridge[1:-1], label = 'bridge')
ax.plot(x[1:-1], z[1:-1], label = 'z')
ax.legend()
press_before = [cur[1][idx_before_bridge] for cur in res]
press_after = [cur[1][idx_after_bridge] for cur in res]
press_bridge = [cur[1][idx_bridge] for cur in res]
h_bridge = np.minimum(press_bridge, z_bridge[idx_bridge] - z[idx_bridge])
q_before = [cur[2][idx_before_bridge] for cur in res]
q_after = [cur[2][idx_after_bridge] for cur in res]
q_bridge = [cur[2][idx_bridge] for cur in res]
u_before = [q/h for q,h in zip(q_before, press_before)]
u_after = [q/h for q,h in zip(q_after, press_after)]
u_bridge = [q/h for q,h in zip(q_bridge, h_bridge)]
head_before = [z[idx_before_bridge] + press_before[i] + u_before[i]**2/2/9.81 for i in range(len(res))]
head_after = [z[idx_after_bridge] + press_after[i] + u_after[i]**2/2/9.81 for i in range(len(res))]
head_bridge = [z[idx_bridge] + press_bridge[i] + u_bridge[i]**2/2/9.81 for i in range(len(res))]
delta_head_total = [head_before[i] - head_after[i] for i in range(len(res))]
delta_head_up = [head_before[i] - head_bridge[i] for i in range(len(res))]
delta_head_do = [head_bridge[i] - head_after[i] for i in range(len(res))]
ax = axes[1]
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')
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')
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')
ax.set_xlabel('Computed $\Delta H$ [m]')
ax.set_ylabel('Theoretical $\Delta H$ [m]')
ax.plot([0,1],[0,1], linestyle='-', linewidth=2, color='black')
ax.set_aspect('equal')
ax.legend()
fig.set_size_inches(20, 10)
fig.tight_layout()
return fig, axes
[docs]
def unsteady_with_bedmotion(problems:list[int], save_video:bool = False) -> list[animation.FuncAnimation]:
"""
Unsteady problem with bed motion if overflowing occurs.
:param problems: list of problems to solve
Problems :
2 - Rectangular bridge - Length = 20 m (will compute 21, 22 and 23)
6 - Rectangular bridge - Length = 60 m (will compute 61, 62 and 63)
7 - V-shape bridge - Length = 20 m (will compute 71, 72 and 73)
8 - U-shape bridge - Length = 20 m (will compute 81, 82 and 83)
9 - Culvert - Length = 100 m (will compute 91, 92 and 93)
21 - Rectangular bridge - Length = 20 m - Unsteady downstream bc
22 - Rectangular bridge - Length = 20 m - Hydrograph
23 - Rectangular bridge - Length = 20 m - Hydrograph 2 steps
61 - Rectangular bridge - Length = 60 m - Unsteady downstream bc
62 - Rectangular bridge - Length = 60 m - Hydrograph
63 - Rectangular bridge - Length = 60 m - Hydrograph 2 steps
71 - V-shape bridge - Length = 20 m - Unsteady downstream bc
72 - V-shape bridge - Length = 20 m - Hydrograph
73 - V-shape bridge - Length = 20 m - Hydrograph 2 steps
81 - U-shape bridge - Length = 20 m - Unsteady downstream bc
82 - U-shape bridge - Length = 20 m - Hydrograph
83 - U-shape bridge - Length = 20 m - Hydrograph 2 steps
91 - Culvert - Length = 100 m - Unsteady downstream bc
92 - Culvert - Length = 100 m - Hydrograph
93 - Culvert - Length = 100 m - Hydrograph 2 steps
"""
length = 500.
dx = 1.
CN = 0.4
h0 = 4.
q0 = 7.
n = 0.025
slope = 0 #1e-4
press_mode = 4
dom, x, z = domain(length, dx, slope)
x = np.array(x)
hu = uniform_waterdepth(slope, q0, n)
anims=[]
if 2 in problems or 21 in problems or 22 in problems or 23 in problems or 24 in problems:
# Rectangular bridge - Lenght = 20 m
CN = 0.4
if 2 in problems:
scenarios = ['unsteady_downstream_bc',
'hydrograph',
'hydrograph_2steps',
# 'Gauss',
]
elif 21 in problems:
scenarios = ['unsteady_downstream_bc']
elif 22 in problems:
scenarios = ['hydrograph']
elif 23 in problems:
scenarios = ['hydrograph_2steps']
elif 24 in problems:
scenarios = ['Gauss']
for scenario in scenarios:
# UNSTEADY WITH TOPOGRAPHY ADAPTATION
motion_duration = 300.
len_bridge = 20
z_roof_null = 10.
min_overflow = 0.25
h0 = 1.5
h_under_bridge = 3.5
h_deck_bridge = 0.75
slice_bridge = slice(int(len(dom)//2-len_bridge//2),int(len(dom)//2+len_bridge//2))
slice_bridge_up = slice(int(len(dom)//2+(len_bridge//2-1)),int(len(dom)//2-(len_bridge//2+1)),-1)
z_bridge = np.ones_like(z) * z_roof_null
z_deck = np.ones_like(z) * z_roof_null
for idx in range(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2):
z_bridge[idx] = z[idx] + h_under_bridge
z_deck[idx] = z_bridge[idx] + h_deck_bridge
poly_bridge_x = np.concatenate((x[slice_bridge], x[slice_bridge_up]))
poly_bridge_y = np.concatenate((z_bridge[slice_bridge], z_deck[slice_bridge_up]))
z_ini = z.copy()
res = problem_bridge_unsteady_topo(dom, z,
z_bridge, z_deck, z_roof_null,
h0, q0, dx, CN, n,
press_mode= press_mode,
motion_duration= motion_duration,
scenario_bc= scenario,
min_overflow= min_overflow)
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)
if save_video:
update_func = lambda _i, _n: progress_bar.update(1)
with tqdm(total=len(res), desc='Saving video') as progress_bar:
ani.save(f'bridge_L20_{scenario}.mp4',
writer='ffmpeg', fps=5,
progress_callback=update_func)
anims.append(ani)
if 6 in problems or 61 in problems or 62 in problems or 63 in problems or 64 in problems:
# Rectangular bridge - Lenght = 60 m
CN = 0.2
if 6 in problems:
scenarios = ['unsteady_downstream_bc',
'hydrograph',
'hydrograph_2steps',
# 'Gauss',
]
elif 61 in problems:
scenarios = ['unsteady_downstream_bc']
elif 62 in problems:
scenarios = ['hydrograph']
elif 63 in problems:
scenarios = ['hydrograph_2steps']
elif 64 in problems:
scenarios = ['Gauss']
for scenario in scenarios:
# UNSTEADY WITH TOPOGRAPHY ADAPTATION
motion_duration = 300.
z_roof_null = 10.
min_overflow = 0.25
h_under_bridge = 3.5
h_deck_bridge = 0.75
len_bridge = 60
q0 = 6.
h0 = 1.5
slice_bridge = slice(int(len(dom)//2-len_bridge//2),int(len(dom)//2+len_bridge//2))
slice_bridge_up = slice(int(len(dom)//2+(len_bridge//2-1)),int(len(dom)//2-(len_bridge//2+1)),-1)
z_bridge = np.ones_like(z) * z_roof_null
z_deck = np.ones_like(z) * z_roof_null
for idx in range(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2):
z_bridge[idx] = z[idx] + h_under_bridge
z_deck[idx] = z_bridge[idx] + h_deck_bridge
poly_bridge_x = np.concatenate((x[slice_bridge], x[slice_bridge_up]))
poly_bridge_y = np.concatenate((z_bridge[slice_bridge], z_deck[slice_bridge_up]))
z_ini = z.copy()
res = problem_bridge_unsteady_topo(dom, z, z_bridge,
z_deck, z_roof_null,
h0, q0, dx, CN, n,
press_mode=press_mode,
motion_duration=motion_duration,
scenario_bc=scenario,
min_overflow=min_overflow)
ani = animate_bridge_unsteady_topo(dom, res, x, z_ini, hu, z_roof_null, len_bridge, motion_duration=motion_duration)
if save_video:
update_func = lambda _i, _n: progress_bar.update(1)
with tqdm(total=len(res), desc='Saving video') as progress_bar:
ani.save(f'bridge_L60{scenario}.mp4',
writer='ffmpeg', fps=5,
progress_callback=update_func)
anims.append(ani)
if 9 in problems or 91 in problems or 92 in problems or 93 in problems or 94 in problems:
# Culvert
CN = 0.4
if 9 in problems:
scenarios = ['unsteady_downstream_bc_culvert',
'hydrograph_culvert',
'hydrograph_2steps_culvert',
# 'Gauss',
]
elif 91 in problems:
scenarios = ['unsteady_downstream_bc_culvert']
elif 92 in problems:
scenarios = ['hydrograph_culvert']
elif 93 in problems:
scenarios = ['hydrograph_2steps_culvert']
for scenario in scenarios:
# UNSTEADY WITH TOPOGRAPHY ADAPTATION
motion_duration = 300.
z_roof_null = 10.
min_overflow = 0.25
h_under_bridge = 1.5
h_deck_bridge = 4.0
len_bridge = 100
h0 = 0.8
q0 = 1.
slice_bridge = slice(int(len(dom)//2-len_bridge//2),int(len(dom)//2+len_bridge//2))
slice_bridge_up = slice(int(len(dom)//2+(len_bridge//2-1)),int(len(dom)//2-(len_bridge//2+1)),-1)
z_bridge = np.ones_like(z) * z_roof_null
z_deck = np.ones_like(z) * z_roof_null
for idx in range(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2):
z_bridge[idx] = z[idx] + h_under_bridge
z_deck[idx] = z_bridge[idx] + h_deck_bridge
poly_bridge_x = np.concatenate((x[slice_bridge], x[slice_bridge_up]))
poly_bridge_y = np.concatenate((z_bridge[slice_bridge], z_deck[slice_bridge_up]))
z_ini = z.copy()
res = problem_bridge_unsteady_topo(dom, z, z_bridge,
z_deck, z_roof_null,
h0, q0, dx, CN, n,
press_mode=press_mode,
motion_duration=motion_duration,
scenario_bc=scenario,
min_overflow=min_overflow)
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)
if save_video:
update_func = lambda _i, _n: progress_bar.update(1)
with tqdm(total=len(res), desc='Saving video') as progress_bar:
ani.save(f'culvert_{scenario}.mp4',
writer='ffmpeg', fps=5,
progress_callback=update_func)
anims.append(ani)
if 7 in problems or 71 in problems or 72 in problems or 73 in problems or 74 in problems:
# V-shape Bridge
CN = 0.4
if 7 in problems:
scenarios = ['unsteady_downstream_bc',
'hydrograph',
'hydrograph_2steps',
# 'Gauss',
]
elif 71 in problems:
scenarios = ['unsteady_downstream_bc']
elif 72 in problems:
scenarios = ['hydrograph']
elif 73 in problems:
scenarios = ['hydrograph_2steps']
elif 74 in problems:
scenarios = ['Gauss']
for scenario in scenarios:
# UNSTEADY WITH TOPOGRAPHY ADAPTATION
motion_duration = 300.
z_roof_null = 10.
min_overflow = 0.25
h_under_bridge = 3.5
h_deck_bridge = 0.75
len_bridge = 20
h0 = 1.5
z_bridge = np.ones_like(z) * z_roof_null
z_deck = np.ones_like(z) * z_roof_null
slice_bridge = slice(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2)
slice_bridge_up = slice(len(dom)//2+(len_bridge//2-1),len(dom)//2-(len_bridge//2+1),-1)
for idx in range(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2):
decal = abs(idx - (len(dom)//2))
z_bridge[idx] = z[idx] + h_under_bridge + 0.05 * decal
z_deck[idx] = h_under_bridge + h_deck_bridge
poly_bridge_x = np.concatenate((x[slice_bridge], x[slice_bridge_up]))
poly_bridge_y = np.concatenate((z_bridge[slice_bridge], z_deck[slice_bridge_up]))
z_ini = z.copy()
res = problem_bridge_unsteady_topo(dom, z,
z_bridge, z_deck, z_roof_null,
h0, q0, dx, CN, n,
press_mode=press_mode,
motion_duration=motion_duration,
scenario_bc=scenario,
min_overflow= min_overflow)
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)
if save_video:
update_func = lambda _i, _n: progress_bar.update(1)
with tqdm(total=len(res), desc='Saving video') as progress_bar:
ani.save(f'bridge_Vshape{scenario}.mp4',
writer='ffmpeg', fps=5,
progress_callback=update_func)
anims.append(ani)
if 8 in problems or 81 in problems or 82 in problems or 83 in problems or 84 in problems:
# U-shape Bridge
CN = 0.4
if 8 in problems:
scenarios = ['unsteady_downstream_bc',
'hydrograph',
'hydrograph_2steps',
# 'Gauss',
]
elif 81 in problems:
scenarios = ['unsteady_downstream_bc']
elif 82 in problems:
scenarios = ['hydrograph']
elif 83 in problems:
scenarios = ['hydrograph_2steps']
elif 84 in problems:
scenarios = ['Gauss']
for scenario in scenarios:
# UNSTEADY WITH TOPOGRAPHY ADAPTATION
motion_duration = 300.
z_roof_null = 10.
min_overflow = 0.25
h_under_bridge = 3.5
h_deck_bridge = 0.4
len_bridge = 20
h0 = 1.5
z_bridge = np.ones_like(z) * z_roof_null
z_deck = np.ones_like(z) * z_roof_null
slice_bridge = slice(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2)
slice_bridge_up = slice(len(dom)//2+(len_bridge//2-1),len(dom)//2-(len_bridge//2+1),-1)
z_bridge[slice_bridge] = z[slice_bridge] + h_under_bridge
z_deck[slice_bridge] = z_bridge[slice_bridge] + h_deck_bridge
idx_up = len(dom)//2-len_bridge//2
idx_down = len(dom)//2+len_bridge//2-1
z_bridge[idx_up] -= .4
z_bridge[idx_up+1] -= .4
z_bridge[idx_down] -= .4
z_bridge[idx_down-1] -= .4
poly_bridge_x = np.concatenate((x[slice_bridge], x[slice_bridge_up]))
poly_bridge_y = np.concatenate((z_bridge[slice_bridge], z_deck[slice_bridge_up]))
z_ini = z.copy()
res = problem_bridge_unsteady_topo(dom, z,
z_bridge, z_deck, z_roof_null,
h0, q0, dx, CN, n,
press_mode=press_mode,
motion_duration=motion_duration,
scenario_bc=scenario,
min_overflow= min_overflow)
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)
if save_video:
update_func = lambda _i, _n: progress_bar.update(1)
with tqdm(total=len(res), desc='Saving video') as progress_bar:
ani.save(f'bridge_Ushape{scenario}.mp4',
writer='ffmpeg', fps=5,
progress_callback=update_func)
anims.append(ani)
return anims
[docs]
def unsteady_with_bedmotion_interval(problems:list[int], save_video:bool = False, update_interval:float = 0., motion_duration:float = 300.) -> list[animation.FuncAnimation]:
"""
Unsteady problem with bed motion if overflowing occurs.
:param problems: list of problems to solve
Problems :
2 - Rectangular bridge - Length = 20 m (will compute 21, 22 and 23)
6 - Rectangular bridge - Length = 60 m (will compute 61, 62 and 63)
7 - V-shape bridge - Length = 20 m (will compute 71, 72 and 73)
8 - U-shape bridge - Length = 20 m (will compute 81, 82 and 83)
9 - Culvert - Length = 100 m (will compute 91, 92 and 93)
21 - Rectangular bridge - Length = 20 m - Unsteady downstream bc
22 - Rectangular bridge - Length = 20 m - Hydrograph
23 - Rectangular bridge - Length = 20 m - Hydrograph 2 steps
61 - Rectangular bridge - Length = 60 m - Unsteady downstream bc
62 - Rectangular bridge - Length = 60 m - Hydrograph
63 - Rectangular bridge - Length = 60 m - Hydrograph 2 steps
71 - V-shape bridge - Length = 20 m - Unsteady downstream bc
72 - V-shape bridge - Length = 20 m - Hydrograph
73 - V-shape bridge - Length = 20 m - Hydrograph 2 steps
81 - U-shape bridge - Length = 20 m - Unsteady downstream bc
82 - U-shape bridge - Length = 20 m - Hydrograph
83 - U-shape bridge - Length = 20 m - Hydrograph 2 steps
91 - Culvert - Length = 100 m - Unsteady downstream bc
92 - Culvert - Length = 100 m - Hydrograph
93 - Culvert - Length = 100 m - Hydrograph 2 steps
"""
length = 500.
dx = 1.
CN = 0.4
h0 = 4.
q0 = 7.
n = 0.025
slope = 0 #1e-4
press_mode = 4
dom, x, z = domain(length, dx, slope)
x = np.array(x)
hu = uniform_waterdepth(slope, q0, n)
anims=[]
if 2 in problems or 21 in problems or 22 in problems or 23 in problems or 24 in problems:
# Rectangular bridge - Lenght = 20 m
CN = 0.4
if 2 in problems:
scenarios = ['unsteady_downstream_bc',
'hydrograph',
'hydrograph_2steps',
# 'Gauss',
]
elif 21 in problems:
scenarios = ['unsteady_downstream_bc']
elif 22 in problems:
scenarios = ['hydrograph']
elif 23 in problems:
scenarios = ['hydrograph_2steps']
elif 24 in problems:
scenarios = ['Gauss']
for scenario in scenarios:
# UNSTEADY WITH TOPOGRAPHY ADAPTATION
len_bridge = 20
z_roof_null = 10.
min_overflow = 0.25
h0 = 1.5
h_under_bridge = 3.5
h_deck_bridge = 0.75
slice_bridge = slice(int(len(dom)//2-len_bridge//2),int(len(dom)//2+len_bridge//2))
slice_bridge_up = slice(int(len(dom)//2+(len_bridge//2-1)),int(len(dom)//2-(len_bridge//2+1)),-1)
z_bridge = np.ones_like(z) * z_roof_null
z_deck = np.ones_like(z) * z_roof_null
for idx in range(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2):
z_bridge[idx] = z[idx] + h_under_bridge
z_deck[idx] = z_bridge[idx] + h_deck_bridge
poly_bridge_x = np.concatenate((x[slice_bridge], x[slice_bridge_up]))
poly_bridge_y = np.concatenate((z_bridge[slice_bridge], z_deck[slice_bridge_up]))
z_ini = z.copy()
res = problem_bridge_unsteady_topo(dom, z,
z_bridge, z_deck, z_roof_null,
h0, q0, dx, CN, n,
press_mode= press_mode,
motion_duration= motion_duration,
scenario_bc= scenario,
min_overflow= min_overflow,
updating_time_interval=update_interval)
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)
if save_video:
update_func = lambda _i, _n: progress_bar.update(1)
with tqdm(total=len(res), desc='Saving video') as progress_bar:
ani.save(f'bridge_L20_{scenario}.mp4',
writer='ffmpeg', fps=5,
progress_callback=update_func)
anims.append(ani)
if 6 in problems or 61 in problems or 62 in problems or 63 in problems or 64 in problems:
# Rectangular bridge - Lenght = 60 m
CN = 0.2
if 6 in problems:
scenarios = ['unsteady_downstream_bc',
'hydrograph',
'hydrograph_2steps',
# 'Gauss',
]
elif 61 in problems:
scenarios = ['unsteady_downstream_bc']
elif 62 in problems:
scenarios = ['hydrograph']
elif 63 in problems:
scenarios = ['hydrograph_2steps']
elif 64 in problems:
scenarios = ['Gauss']
for scenario in scenarios:
# UNSTEADY WITH TOPOGRAPHY ADAPTATION
z_roof_null = 10.
min_overflow = 0.25
h_under_bridge = 3.5
h_deck_bridge = 0.75
len_bridge = 60
q0 = 6.
h0 = 1.5
slice_bridge = slice(int(len(dom)//2-len_bridge//2),int(len(dom)//2+len_bridge//2))
slice_bridge_up = slice(int(len(dom)//2+(len_bridge//2-1)),int(len(dom)//2-(len_bridge//2+1)),-1)
z_bridge = np.ones_like(z) * z_roof_null
z_deck = np.ones_like(z) * z_roof_null
for idx in range(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2):
z_bridge[idx] = z[idx] + h_under_bridge
z_deck[idx] = z_bridge[idx] + h_deck_bridge
poly_bridge_x = np.concatenate((x[slice_bridge], x[slice_bridge_up]))
poly_bridge_y = np.concatenate((z_bridge[slice_bridge], z_deck[slice_bridge_up]))
z_ini = z.copy()
res = problem_bridge_unsteady_topo(dom, z, z_bridge,
z_deck, z_roof_null,
h0, q0, dx, CN, n,
press_mode=press_mode,
motion_duration=motion_duration,
scenario_bc=scenario,
min_overflow=min_overflow,
updating_time_interval=update_interval)
ani = animate_bridge_unsteady_topo(dom, res, x, z_ini, hu, z_roof_null, len_bridge, motion_duration=motion_duration)
if save_video:
update_func = lambda _i, _n: progress_bar.update(1)
with tqdm(total=len(res), desc='Saving video') as progress_bar:
ani.save(f'bridge_L60{scenario}.mp4',
writer='ffmpeg', fps=5,
progress_callback=update_func)
anims.append(ani)
if 9 in problems or 91 in problems or 92 in problems or 93 in problems or 94 in problems:
# Culvert
CN = 0.4
if 9 in problems:
scenarios = ['unsteady_downstream_bc_culvert',
'hydrograph_culvert',
'hydrograph_2steps_culvert',
# 'Gauss',
]
elif 91 in problems:
scenarios = ['unsteady_downstream_bc_culvert']
elif 92 in problems:
scenarios = ['hydrograph_culvert']
elif 93 in problems:
scenarios = ['hydrograph_2steps_culvert']
for scenario in scenarios:
# UNSTEADY WITH TOPOGRAPHY ADAPTATION
z_roof_null = 10.
min_overflow = 0.25
h_under_bridge = 1.5
h_deck_bridge = 4.0
len_bridge = 100
h0 = 0.8
q0 = 1.
slice_bridge = slice(int(len(dom)//2-len_bridge//2),int(len(dom)//2+len_bridge//2))
slice_bridge_up = slice(int(len(dom)//2+(len_bridge//2-1)),int(len(dom)//2-(len_bridge//2+1)),-1)
z_bridge = np.ones_like(z) * z_roof_null
z_deck = np.ones_like(z) * z_roof_null
for idx in range(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2):
z_bridge[idx] = z[idx] + h_under_bridge
z_deck[idx] = z_bridge[idx] + h_deck_bridge
poly_bridge_x = np.concatenate((x[slice_bridge], x[slice_bridge_up]))
poly_bridge_y = np.concatenate((z_bridge[slice_bridge], z_deck[slice_bridge_up]))
z_ini = z.copy()
res = problem_bridge_unsteady_topo(dom, z, z_bridge,
z_deck, z_roof_null,
h0, q0, dx, CN, n,
press_mode=press_mode,
motion_duration=motion_duration,
scenario_bc=scenario,
min_overflow=min_overflow,
updating_time_interval=update_interval)
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)
if save_video:
update_func = lambda _i, _n: progress_bar.update(1)
with tqdm(total=len(res), desc='Saving video') as progress_bar:
ani.save(f'culvert_{scenario}.mp4',
writer='ffmpeg', fps=5,
progress_callback=update_func)
anims.append(ani)
if 7 in problems or 71 in problems or 72 in problems or 73 in problems or 74 in problems:
# V-shape Bridge
CN = 0.4
if 7 in problems:
scenarios = ['unsteady_downstream_bc',
'hydrograph',
'hydrograph_2steps',
# 'Gauss',
]
elif 71 in problems:
scenarios = ['unsteady_downstream_bc']
elif 72 in problems:
scenarios = ['hydrograph']
elif 73 in problems:
scenarios = ['hydrograph_2steps']
elif 74 in problems:
scenarios = ['Gauss']
for scenario in scenarios:
# UNSTEADY WITH TOPOGRAPHY ADAPTATION
z_roof_null = 10.
min_overflow = 0.25
h_under_bridge = 3.5
h_deck_bridge = 0.75
len_bridge = 20
h0 = 1.5
z_bridge = np.ones_like(z) * z_roof_null
z_deck = np.ones_like(z) * z_roof_null
slice_bridge = slice(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2)
slice_bridge_up = slice(len(dom)//2+(len_bridge//2-1),len(dom)//2-(len_bridge//2+1),-1)
for idx in range(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2):
decal = abs(idx - (len(dom)//2))
z_bridge[idx] = z[idx] + h_under_bridge + 0.05 * decal
z_deck[idx] = h_under_bridge + h_deck_bridge
poly_bridge_x = np.concatenate((x[slice_bridge], x[slice_bridge_up]))
poly_bridge_y = np.concatenate((z_bridge[slice_bridge], z_deck[slice_bridge_up]))
z_ini = z.copy()
res = problem_bridge_unsteady_topo(dom, z,
z_bridge, z_deck, z_roof_null,
h0, q0, dx, CN, n,
press_mode=press_mode,
motion_duration=motion_duration,
scenario_bc=scenario,
min_overflow= min_overflow,
updating_time_interval=update_interval)
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)
if save_video:
update_func = lambda _i, _n: progress_bar.update(1)
with tqdm(total=len(res), desc='Saving video') as progress_bar:
ani.save(f'bridge_Vshape{scenario}.mp4',
writer='ffmpeg', fps=5,
progress_callback=update_func)
anims.append(ani)
if 8 in problems or 81 in problems or 82 in problems or 83 in problems or 84 in problems:
# U-shape Bridge
CN = 0.4
if 8 in problems:
scenarios = ['unsteady_downstream_bc',
'hydrograph',
'hydrograph_2steps',
# 'Gauss',
]
elif 81 in problems:
scenarios = ['unsteady_downstream_bc']
elif 82 in problems:
scenarios = ['hydrograph']
elif 83 in problems:
scenarios = ['hydrograph_2steps']
elif 84 in problems:
scenarios = ['Gauss']
for scenario in scenarios:
# UNSTEADY WITH TOPOGRAPHY ADAPTATION
z_roof_null = 10.
min_overflow = 0.25
h_under_bridge = 3.5
h_deck_bridge = 0.4
len_bridge = 20
h0 = 1.5
z_bridge = np.ones_like(z) * z_roof_null
z_deck = np.ones_like(z) * z_roof_null
slice_bridge = slice(len(dom)//2-len_bridge//2,len(dom)//2+len_bridge//2)
slice_bridge_up = slice(len(dom)//2+(len_bridge//2-1),len(dom)//2-(len_bridge//2+1),-1)
z_bridge[slice_bridge] = z[slice_bridge] + h_under_bridge
z_deck[slice_bridge] = z_bridge[slice_bridge] + h_deck_bridge
idx_up = len(dom)//2-len_bridge//2
idx_down = len(dom)//2+len_bridge//2-1
z_bridge[idx_up] -= .4
z_bridge[idx_up+1] -= .4
z_bridge[idx_down] -= .4
z_bridge[idx_down-1] -= .4
poly_bridge_x = np.concatenate((x[slice_bridge], x[slice_bridge_up]))
poly_bridge_y = np.concatenate((z_bridge[slice_bridge], z_deck[slice_bridge_up]))
z_ini = z.copy()
res = problem_bridge_unsteady_topo(dom, z,
z_bridge, z_deck, z_roof_null,
h0, q0, dx, CN, n,
press_mode=press_mode,
motion_duration=motion_duration,
scenario_bc=scenario,
min_overflow= min_overflow,
updating_time_interval=update_interval)
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)
if save_video:
update_func = lambda _i, _n: progress_bar.update(1)
with tqdm(total=len(res), desc='Saving video') as progress_bar:
ani.save(f'bridge_Ushape{scenario}.mp4',
writer='ffmpeg', fps=5,
progress_callback=update_func)
anims.append(ani)
return anims
[docs]
def hedge():
""" Compute Water line problems with hedge
Length = 500 m
dx = 1 m
CN = 0.4
h0 = 4 m
q0 = 7 m^2/s
n = 0.025
slope = 1e-4
"""
length = 500.
dx = 1.
CN = 0.4
h0 = 4.
q0 = 7.
n = 0.025
slope = 1e-4
dom, x, z = domain(length, dx, slope)
x = np.array(x)
hu = uniform_waterdepth(slope, q0, n)
# bridge roof level is 10 m everywhere except in the middle of the domain
# where the bridge is located.
# The bridge is a flat bridge with a height of 2 m.
z_bridge = np.ones_like(z) * 10.
z_bridge[len(dom)//2-5:len(dom)//2+5] = z[len(dom)//2-5:len(dom)//2+5] + 2.
fig, axes = plt.subplots(3,1)
h1, q1 = problem(dom, z, h0, q0, dx, CN, n)
h2, q2, theta = problem_hedge(dom, z, h0, q0, dx, CN, n)
plot_hedge(axes[0], x, h1, h2, q1, q2, z, hu, theta)
h1, q1 = problem(dom, z, h0+1., q0, dx, CN, n)
h2, q2, theta = problem_hedge(dom, z, h0+1., q0, dx, CN, n)
plot_hedge(axes[1], x, h1, h2, q1, q2, z, hu, theta)
h1, q1 = problem(dom, z, h0+2., q0, dx, CN, n)
h2, q2, theta = problem_hedge(dom, z, h0+2., q0, dx, CN, n)
plot_hedge(axes[2], x, h1, h2, q1, q2, z, hu, theta)
fig.set_size_inches(20, 10)
fig.tight_layout()
return fig, axes
if __name__ == '__main__':
# anim = steady()
# anim = water_line()
# anim = water_lines()
# anim = unsteady_without_bedmotion()
# anim = unteaady_with_bedmotion([2, 6, 7, 8, 9])
# anim = water_line_noloss_noslope()
# anim1 = unsteady_with_bedmotion([2])
# anim2 = unsteady_with_bedmotion_interval([21], update_interval=2., motion_duration=300.)
plt.show()
pass