Introduction
Introduction Statistics Contact Development Disclaimer Help
timplement Meyer-Peter and Muller 1948 relationship for sediment transport - gr…
git clone git://src.adamsgaard.dk/granular-channel-hydro
Log
Files
Refs
README
LICENSE
---
commit 6afcc6591b23c355ae2cae50953afbba1de9e0d6
parent 4bb1e340b01ce37312e12942c05715d30529e2b0
Author: Anders Damsgaard <[email protected]>
Date: Mon, 15 May 2017 16:11:43 -0400
implement Meyer-Peter and Muller 1948 relationship for sediment transport
Diffstat:
M 1d-channel.py | 141 ++++++++++-------------------…
1 file changed, 43 insertions(+), 98 deletions(-)
---
diff --git a/1d-channel.py b/1d-channel.py
t@@ -13,7 +13,7 @@
# or CUDA.
#
# License: Gnu Public License v3
-# Author: Anders Damsgaard, [email protected], https://adamsgaard.dk
+# Author: Anders Damsgaard, [email protected], https://adamsgaard.dk
import numpy
import matplotlib.pyplot as plt
t@@ -22,9 +22,10 @@ import sys
# # Model parameters
Ns = 25 # Number of nodes [-]
-Ls = 10e3 # Model length [m]
+Ls = 1e3 # Model length [m]
total_days = 60. # Total simulation time [d]
t_end = 24.*60.*60.*total_days # Total simulation time [s]
+tol_S = 1e-3 # Tolerance criteria for the norm. max. residual for Q
tol_Q = 1e-3 # Tolerance criteria for the norm. max. residual for Q
tol_N_c = 1e-3 # Tolerance criteria for the norm. max. residual for N_c
max_iter = 1e2*Ns # Maximum number of solver iterations before failure
t@@ -32,7 +33,9 @@ print_output_convergence = False # Display convergence …
print_output_convergence_main = True # Display convergence in main loop
safety = 0.01 # Safety factor ]0;1] for adaptive timestepping
plot_interval = 20 # Time steps between plots
+plot_during_iterations = False # Generate plots for intermediate results
speedup_factor = 1. # Speed up channel growth to reach steady state faster
+# relax = 0.05 # Relaxation parameter for effective pressure ]0;1]
# Physical parameters
rho_w = 1000. # Water density [kg/m^3]
t@@ -40,15 +43,12 @@ rho_i = 910. # Ice density [kg/m^3]
rho_s = 2600. # Sediment density [kg/m^3]
g = 9.8 # Gravitational acceleration [m/s^2]
theta = 30. # Angle of internal friction in sediment [deg]
-sand_fraction = 0.5 # Initial volumetric fraction of sand relative to gravel
-D_g = 5e-3 # Mean grain size in gravel fraction (> 2 mm) [m]
-D_s = 5e-4 # Mean grain size in sand fraction (<= 2 mm) [m]
-#D_g = 1
-#D_g = 0.1
+D = 1.15e-3 # Mean grain size [m], Lajeuness et al 2010, series 1
+tau_c = 0.016 # Critical Shields stress, Lajeunesse et al 2010, series 1
# Boundary conditions
P_terminus = 0. # Water pressure at terminus [Pa]
-m_dot = 1e-6 # Water source term [m/s]
+m_dot = numpy.linspace(0., 1e-5, Ns-1) # Water source term [m/s]
Q_upstream = 1e-5 # Water influx upstream (must be larger than 0) [m^3/s]
# Channel hydraulic properties
t@@ -61,8 +61,6 @@ c_2 = 4.60 # [m]
# Minimum channel size [m^2], must be bigger than 0
S_min = 1e-2
-# S_min = 1e-1
-# S_min = 1.
# # Initialize model arrays
t@@ -81,7 +79,7 @@ b = numpy.zeros_like(H)
N = H*0.1*rho_i*g # Initial effective stress [Pa]
# Initialize arrays for channel segments between nodes
-S = numpy.ones(len(s) - 1)*S_min # Cross-sect. area of channel segments[m^2]
+S = numpy.ones(len(s) - 1)*0.1 # Cross-sect. area of channel segments [m^2]
S_max = numpy.zeros_like(S) # Max. channel size [m^2]
dSdt = numpy.zeros_like(S) # Transient in channel cross-sect. area [m^2/s]
W = S/numpy.tan(numpy.deg2rad(theta)) # Assuming no channel floor wedge
t@@ -92,10 +90,6 @@ P_c = numpy.zeros_like(S) # Water pressure in channel se…
tau = numpy.zeros_like(S) # Avg. shear stress from current [Pa]
porosity = numpy.ones_like(S)*0.3 # Sediment porosity [-]
res = numpy.zeros_like(S) # Solution residual during solver iterations
-Q_t = numpy.zeros_like(S) # Total sediment flux [m3/s]
-Q_s = numpy.zeros_like(S) # Sediment flux where D <= 2 mm [m3/s]
-Q_g = numpy.zeros_like(S) # Sediment flux where D > 2 mm [m3/s]
-f_s = numpy.ones_like(S)*sand_fraction # Initial sediment fraction of sand [-]
# # Helper functions
t@@ -123,75 +117,23 @@ def channel_shear_stress(Q, S):
return 1./8.*friction_factor*rho_w*u_bar**2.
-def channel_sediment_flux_sand(tau, W, f_s, D_s):
- # Parker 1979, Wilcock 1997, 2001, Egholm 2013
+def channel_sediment_flux(tau, W):
+ # Meyer-Peter and Mueller 1948
# tau: Shear stress by water flow
# W: Channel width
- # f_s: Sand volume fraction
- # D_s: Mean sand fraction grain size
-
- # Piecewise linear functions for nondimensional critical shear stresses
- # dependent on sand fraction from Gasparini et al 1999 of Wilcock 1997
- # data.
- ref_shear_stress = numpy.ones_like(f_s)*0.04
- ref_shear_stress[numpy.nonzero(f_s <= 0.1)] = 0.88
- I = numpy.nonzero((0.1 < f_s) & (f_s <= 0.4))
- ref_shear_stress[I] = 0.88 - 2.8*(f_s[I] - 0.1)
-
- # Non-dimensionalize shear stress
- shields_stress = tau/((rho_s - rho_w)*g*D_s)
-
- # import ipdb; ipdb.set_trace()
- Q_c = 11.2*f_s*W/((rho_s - rho_w)/rho_w*g) \
- * (tau/rho_w)**1.5 \
- * numpy.maximum(0.0,
- (1.0 - 0.846*numpy.sqrt(ref_shear_stress/shields_stress))
- )**4.5
-
- return Q_c
-
-
-def channel_sediment_flux_gravel(tau, W, f_g, D_g):
- # Parker 1979, Wilcock 1997, 2001, Egholm 2013
- # tau: Shear stress by water flow
- # W: Channel width
- # f_g: Gravel volume fraction
- # D_g: Mean gravel fraction grain size
-
- # Piecewise linear functions for nondimensional critical shear stresses
- # dependent on sand fraction from Gasparini et al 1999 of Wilcock 1997
- # data.
- ref_shear_stress = numpy.ones_like(f_g)*0.01
- ref_shear_stress[numpy.nonzero(f_g <= 0.1)] = 0.04
- I = numpy.nonzero((0.1 < f_g) & (f_g <= 0.4))
- ref_shear_stress[I] = 0.04 - 0.1*(f_g[I] - 0.1)
# Non-dimensionalize shear stress
- shields_stress = tau/((rho_s - rho_w)*g*D_g)
-
- # From Wilcock 2001, eq. 3
- Q_g = 11.2*f_g*W/((rho_s - rho_w)/rho_w*g) \
- * (tau/rho_w)**1.5 \
- * numpy.maximum(0.0,
- (1.0 - 0.846*ref_shear_stress/shields_stress))**4.5
-
- # From Wilcock 2001, eq. 4
- I = numpy.nonzero(ref_shear_stress/shields_stress < 1.)
- Q_g[I] = f_g[I]*W[I]/((rho_s - rho_w)/rho_w*g) \
- * (tau[I]/rho_w)**1.5 \
- * 0.0025*(shields_stress[I]/ref_shear_stress[I])**14.2
+ shields_stress = tau/((rho_s - rho_w)*g*D)
- return Q_g
+ stress_excess = shields_stress - tau_c
+ stress_excess[stress_excess < 0.] = 0.
+ return 8.*stress_excess**(3./2.)*W \
+ * numpy.sqrt((rho_s - rho_w)/rho_w*g*D**3.)
-def channel_growth_rate(e_dot, d_dot, W):
+def channel_growth_rate_sedflux(Q_s, porosity, s_c):
# Damsgaard et al, in prep
- return (e_dot - d_dot)*W
-
-
-def channel_growth_rate_sedflux(Q_t, porosity, s_c):
- # Damsgaard et al, in prep
- return 1./porosity[1:] * gradient(Q_t, s_c)
+ return 1./porosity[1:] * gradient(Q_s, s_c)
def update_channel_size_with_limit(S, S_old, dSdt, dt, N_c):
t@@ -213,13 +155,13 @@ def flux_solver(m_dot, ds):
# Iteratively find solution, do not settle for less iterations than the
# number of nodes
- while max_res > tol_Q or it < Ns:
+ while max_res > tol_Q:
Q_old = Q.copy()
# dQ/ds = m_dot -> Q_out = m*delta(s) + Q_in
# Upwind information propagation (upwind)
Q[0] = Q_upstream
- Q[1:] = m_dot*ds[1:] + Q[:-1]
+ Q[1:] = m_dot[1:]*ds[1:] + Q[:-1]
max_res = numpy.max(numpy.abs((Q - Q_old)/(Q + 1e-16)))
if print_output_convergence:
t@@ -240,7 +182,7 @@ def pressure_solver(psi, f, Q, S):
it = 0
max_res = 1e9 # arbitrary large value
- while max_res > tol_N_c or it < Ns:
+ while max_res > tol_N_c:
N_c_old = N_c.copy()
t@@ -250,7 +192,7 @@ def pressure_solver(psi, f, Q, S):
N_c[:-1] = N_c[1:] \
+ psi[:-1]*ds[:-1] \
- f[:-1]*rho_w*g*Q[:-1]*numpy.abs(Q[:-1]) \
- /(S[:-1]**(8./3.))*ds[:-1]
+ / (S[:-1]**(8./3.))*ds[:-1]
max_res = numpy.max(numpy.abs((N_c - N_c_old)/(N_c + 1e-16)))
t@@ -263,6 +205,7 @@ def pressure_solver(psi, f, Q, S):
it += 1
return N_c
+ # return N_c_old*(1 - relax_N_c) + N_c*relax_N_c
def plot_state(step, time, S_, S_max_, title=True):
t@@ -286,9 +229,7 @@ def plot_state(step, time, S_, S_max_, title=True):
ax_m3s.set_ylabel('[m$^3$/s]')
ax_m3s_sed = plt.subplot(3, 1, 2, sharex=ax_Pa)
- ax_m3s_sed.plot(s_c/1000., Q_g, ':', label='$Q_{gravel}$')
- ax_m3s_sed.plot(s_c/1000., Q_s, '-', label='$Q_{sand}$')
- ax_m3s_sed.plot(s_c/1000., Q_t, '--', label='$Q_{total}$')
+ ax_m3s_sed.plot(s_c/1000., Q_s, '-', label='$Q_{s}$')
ax_m3s_sed.set_ylabel('[m$^3$/s]')
ax_m3s_sed.legend(loc=2)
t@@ -314,11 +255,14 @@ def plot_state(step, time, S_, S_max_, title=True):
else:
plt.savefig('chan-' + str(step) + '.pdf')
plt.clf()
+ plt.close()
-def find_new_timestep(ds, Q, S):
+def find_new_timestep(ds, Q, Q_s, S):
# Determine the timestep using the Courant-Friedrichs-Lewy condition
- dt = safety*numpy.minimum(60.*60.*24., numpy.min(numpy.abs(ds/(Q*S))))
+ dt = safety*numpy.minimum(60.*60.*24.,
+ numpy.min(numpy.abs(ds/(Q*S),
+ ds/(Q_s*S)+1e-16)))
if dt < 1.0:
raise Exception('Error: Time step less than 1 second at step '
t@@ -334,6 +278,8 @@ def print_status_to_stdout(step, time, dt):
.format(time, time/(60.*60.*24.), dt))
sys.stdout.flush()
+
+# Initialize remaining values before entering time loop
s_c = avg_midpoint(s) # Channel section midpoint coordinates [m]
H_c = avg_midpoint(H)
t@@ -351,7 +297,7 @@ step = 0
while time <= t_end:
# Determine time step length from water flux
- dt = find_new_timestep(ds, Q, S)
+ dt = find_new_timestep(ds, Q, Q_s, S)
# Display current simulation status
print_status_to_stdout(step, time, dt)
t@@ -363,10 +309,8 @@ while time <= t_end:
max_res = 1e9
S_old = S.copy()
- # Iteratively find solution, do not settle for less iterations than the
- # number of nodes to make sure information has had a chance to pass through
- # the system
- while max_res > tol_Q or it < Ns:
+ # Iteratively find solution with the Jacobi relaxation method
+ while max_res > tol_S:
S_prev_it = S.copy()
t@@ -377,23 +321,21 @@ while time <= t_end:
# Find average shear stress from water flux for each channel segment
tau = channel_shear_stress(Q, S)
- # Determine sediment fluxes for each size fraction
- f_g = 1./f_s # gravel volume fraction is reciprocal to sand
- Q_s = channel_sediment_flux_sand(tau, W, f_s, D_s)
- Q_g = channel_sediment_flux_gravel(tau, W, f_g, D_g)
- Q_t = Q_s + Q_g
+ # Determine sediment flux
+ Q_s = channel_sediment_flux(tau, W)
# Determine change in channel size for each channel segment.
# Use backward differences and assume dS/dt=0 in first segment.
- #dSdt[1:] = channel_growth_rate_sedflux(Q_t, porosity, s_c)
- #dSdt *= speedup_factor
+ dSdt[1:] = channel_growth_rate_sedflux(Q_s, porosity, s_c)
+ # dSdt *= speedup_factor * relax
# Update channel cross-sectional area and width according to growth
# rate and size limit for each channel segment
+ # S_prev = S.copy()
S, W, S_max, dSdt = \
update_channel_size_with_limit(S, S_old, dSdt, dt, N_c)
+ # S = S_prev*(1.0 - relax) + S*relax
- # Find hydraulic roughness
f = channel_hydraulic_roughness(manning, S, W, theta)
# Find new water pressures consistent with the flow law
t@@ -402,6 +344,9 @@ while time <= t_end:
# Find new effective pressure in channel segments
P_c = rho_i*g*H_c - N_c
+ if plot_during_iterations:
+ plot_state(step + it/1e4, time, S, S_max)
+
# Find new maximum normalized residual value
max_res = numpy.max(numpy.abs((S - S_prev_it)/(S + 1e-16)))
if print_output_convergence_main:
You are viewing proxied material from mx1.adamsgaard.dk. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.