ttransport sediment for more than one cell - granular-channel-hydro - subglacial hydrology model for sedimentary channels
git clone git://src.adamsgaard.dk/granular-channel-hydro
Log
Files
Refs
README
LICENSE
---
commit 400da52ca6dea9a381f902ba653ca1c267ac286f
parent 26bc00c665916c00d8fc195170f6df2062d77332
Author: Anders Damsgaard Christensen 
Date:   Thu,  2 Feb 2017 16:22:16 -0800

ttransport sediment for more than one cell

Diffstat:
  A 1d-channel-flux.py                  |     292 +++++++++++++++++++++++++++++++
  M 1d-channel.py                       |     153 +++++++++++++++++--------------

2 files changed, 377 insertions(+), 68 deletions(-)
---
diff --git a/1d-channel-flux.py b/1d-channel-flux.py
t@@ -0,0 +1,292 @@
+#!/usr/bin/env python
+
+# # ABOUT THIS FILE
+# The following script uses basic Python and Numpy functionality to solve the
+# coupled systems of equations describing subglacial channel development in
+# soft beds as presented in `Damsgaard et al. "Sediment plasticity controls
+# channelization of subglacial meltwater in soft beds"`, submitted to Journal
+# of Glaciology.
+#
+# High performance is not the goal for this implementation, which is instead
+# intended as a heavily annotated example on the solution procedure without
+# relying on solver libraries, suitable for low-level languages like C, Fortran
+# or CUDA.
+#
+# License: Gnu Public License v3
+# Author: Anders Damsgaard, adamsgaard@ucsd.edu, https://adamsgaard.dk
+
+import numpy
+import matplotlib.pyplot as plt
+import sys
+
+
+# # Model parameters
+Ns = 25               # Number of nodes [-]
+Ls = 100e3            # Model length [m]
+t_end = 24.*60.*60.*120.  # Total simulation time [s]
+tol_Q = 1e-3       # Tolerance criteria for the normalized max. residual for Q
+tol_P_c = 1e-3     # Tolerance criteria for the normalized max residual for P_c
+max_iter = 1e2*Ns  # Maximum number of solver iterations before failure
+output_convergence = False  # Display convergence statistics during run
+safety = 0.1     # Safety factor ]0;1] for adaptive timestepping
+
+# Physical parameters
+rho_w = 1000.  # Water density [kg/m^3]
+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]
+
+# Water source term [m/s]
+# m_dot = 7.93e-11
+m_dot = 4.5e-8
+# m_dot = 5.79e-5
+
+# Hewitt 2011 channel flux parameters
+manning = 0.1  # Manning roughness coefficient [m^{-1/3} s]
+F = rho_w*g*manning*(2.*(numpy.pi + 2.)**2./numpy.pi)**(2./3.)
+
+# Channel growth-limit parameters
+c_1 = -0.118  # [m/kPa]
+c_2 = 4.60    # [m]
+
+# Minimum channel size [m^2], must be bigger than 0
+# S_min = 1e-1
+S_min = 1.5e-2
+
+
+# # Initialize model arrays
+# Node positions, terminus at Ls
+s = numpy.linspace(0., Ls, Ns)
+ds = s[1:] - s[:-1]
+
+# Ice thickness and bed topography
+H = 6.*(numpy.sqrt(Ls - s + 5e3) - numpy.sqrt(5e3)) + 1.0  # max: 1.5 km
+# H = 1.*(numpy.sqrt(Ls - s + 5e3) - numpy.sqrt(5e3)) + 1.0  # max: 255 m
+# H = 0.6*(numpy.sqrt(Ls - s + 5e3) - numpy.sqrt(5e3)) + 1.0
+b = numpy.zeros_like(H)
+
+N = H*0.1*rho_i*g            # Initial effective stress [Pa]
+p_w = rho_i*g*H - N          # Initial guess of water pressure [Pa]
+hydro_pot = rho_w*g*b + p_w  # Initial guess of hydraulic potential [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_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
+Q = numpy.zeros_like(S)      # Water flux in channel segments [m^3/s]
+Q_s = numpy.zeros_like(S)    # Sediment flux in channel segments [m^3/s]
+dQ_s_ds = numpy.empty_like(S)  # Transient in channel cross-sect. area [m^2/s]
+N_c = numpy.zeros_like(S)    # Effective pressure in channel segments [Pa]
+P_c = numpy.zeros_like(S)    # Water pressure in channel segments [Pa]
+res = numpy.zeros_like(S)    # Solution residual during solver iterations
+
+
+# # Helper functions
+def gradient(arr, arr_x):
+    # Central difference gradient of an array ``arr`` with node positions at
+    # ``arr_x``.
+    return (arr[:-1] - arr[1:])/(arr_x[:-1] - arr_x[1:])
+
+def avg_midpoint(arr):
+    # Averaged value of neighboring array elements
+    return (arr[:-1] + arr[1:])/2.
+
+def channel_water_flux(S, hydro_pot_grad):
+    # Hewitt 2011
+    return numpy.sqrt(1./F*S**(8./3.)*-hydro_pot_grad)
+
+def update_channel_size_with_limit(S, dSdt, dt, N):
+    # Damsgaard et al, in prep
+    S_max = ((c_1*N.clip(min=0.)/1000. + c_2)*\
+             numpy.tan(numpy.deg2rad(theta))).clip(min=S_min)
+    S = numpy.minimum(S + dSdt*dt, S_max).clip(min=S_min)
+    W = S/numpy.tan(numpy.deg2rad(theta))  # Assume no channel floor wedge
+    return S, W, S_max
+
+def flux_solver(m_dot, ds):
+    # Iteratively find new fluxes
+    it = 0
+    max_res = 1e9  # arbitrary large value
+
+    # Iteratively find solution, do not settle for less iterations than the
+    # number of nodes
+    while max_res > tol_Q or it < Ns:
+
+        Q_old = Q.copy()
+        # dQ/ds = m_dot  ->  Q_out = m*delta(s) + Q_in
+        # Upwind information propagation (upwind)
+        Q[0] = 1e-2  # Ng 2000
+        Q[1:] = m_dot*ds[1:] + Q[:-1]
+        max_res = numpy.max(numpy.abs((Q - Q_old)/(Q + 1e-16)))
+
+        if output_convergence:
+            print('it = {}: max_res = {}'.format(it, max_res))
+
+        #import ipdb; ipdb.set_trace()
+        if it >= max_iter:
+            raise Exception('t = {}, step = {}:'.format(time, step) +
+                            'Iterative solution not found for Q')
+        it += 1
+
+    return Q
+
+def sediment_flux(Q):
+    #return Q**(3./2.)
+    return Q/2.
+
+def sediment_flux_divergence(Q_s, ds):
+    # Damsgaard et al, in prep
+    return (Q_s[1:] - Q_s[:-1])/ds[1:]
+
+def pressure_solver(psi, F, Q, S):
+    # Iteratively find new water pressures
+    # dP_c/ds = psi - FQ^2/S^{8/3}
+
+    it = 0
+    max_res = 1e9  # arbitrary large value
+    while max_res > tol_P_c or it < Ns*40:
+
+        P_c_old = P_c.copy()
+
+        # Upwind finite differences
+        P_c[:-1] = -psi[:-1]*ds[:-1] \
+            + F*Q[:-1]**2./(S[:-1]**(8./3.))*ds[:-1] \
+            + P_c[1:]  # Upstream
+
+        # Dirichlet BC (fixed pressure) at terminus
+        P_c[-1] = 0.
+
+        # von Neumann BC (no gradient = no flux) at s=0
+        P_c[0] = P_c[1]
+
+        max_res = numpy.max(numpy.abs((P_c - P_c_old)/(P_c + 1e-16)))
+
+        if output_convergence:
+            print('it = {}: max_res = {}'.format(it, max_res))
+
+        if it >= max_iter:
+            raise Exception('t = {}, step = {}:'.format(time, step) +
+                            'Iterative solution not found for P_c')
+        it += 1
+
+    return P_c
+
+def plot_state(step, time):
+    # Plot parameters along profile
+    fig = plt.gcf()
+    fig.set_size_inches(3.3, 3.3)
+
+    ax_Pa = plt.subplot(2, 1, 1)  # axis with Pascals as y-axis unit
+    ax_Pa.plot(s_c/1000., P_c/1000., '--r', label='$P_c$')
+
+    ax_m3s = ax_Pa.twinx()  # axis with m3/s as y-axis unit
+    ax_m3s.plot(s_c/1000., Q, '-b', label='$Q$')
+    ax_m3s.plot(s_c/1000., Q_s, ':b', label='$Q_s$')
+
+    plt.title('Day: {:.3}'.format(time/(60.*60.*24.)))
+    ax_Pa.legend(loc=2)
+    ax_m3s.legend(loc=1)
+    ax_Pa.set_ylabel('[kPa]')
+    ax_m3s.set_ylabel('[m$^3$/s]')
+
+    ax_m2 = plt.subplot(2, 1, 2, sharex=ax_Pa)
+    ax_m2.plot(s_c/1000., S, '-k', label='$S$')
+    ax_m2.plot(s_c/1000., S_max, '--k', label='$S_{max}$')
+    #ax_m.semilogy(s_c/1000., S, '-k', label='$S$')
+    #ax_m.semilogy(s_c/1000., S_max, '--k', label='$S_{max}$')
+
+    ax_m2s = ax_m2.twinx()
+    ax_m2s.plot(s_c/1000., dSdt, ':b', label='$dS/dt$')
+
+    ax_m2.legend(loc=2)
+    ax_m2s.legend(loc=1)
+    ax_m2.set_xlabel('$s$ [km]')
+    ax_m2.set_ylabel('[m$^2$]')
+    ax_m2s.set_ylabel('[m$^2$/s]')
+
+    plt.setp(ax_Pa.get_xticklabels(), visible=False)
+    plt.tight_layout()
+    if step == -1:
+        plt.savefig('chan-0.init.pdf')
+    else:
+        plt.savefig('chan-' + str(step) + '.pdf')
+    plt.clf()
+
+def find_new_timestep(ds, Q, S):
+    # Determine the timestep using the Courant-Friedrichs-Lewy condition
+    dt = safety*numpy.minimum(60.*60.*24., numpy.min(numpy.abs(ds/(Q*S))))
+
+    if dt < 1.0:
+        raise Exception('Error: Time step less than 1 second at step '
+                        + '{}, time '.format(step)
+                        + '{:.3} s/{:.3} d'.format(time, time/(60.*60.*24.)))
+
+    return dt
+
+def print_status_to_stdout(time, dt):
+    sys.stdout.write('\rt = {:.2} s or {:.4} d, dt = {:.2} s         '\
+                     .format(time, time/(60.*60.*24.), dt))
+    sys.stdout.flush()
+
+s_c = avg_midpoint(s) # Channel section midpoint coordinates [m]
+
+# Find gradient in hydraulic potential between the nodes
+hydro_pot_grad = gradient(hydro_pot, s)
+
+# Find field values at the middle of channel segments
+N_c = avg_midpoint(N)
+H_c = avg_midpoint(N)
+
+# Find fluxes in channel segments [m^3/s]
+Q = channel_water_flux(S, hydro_pot_grad)
+
+# Water-pressure gradient from geometry [Pa/m]
+psi = -rho_i*g*gradient(H, s) - (rho_w - rho_i)*g*gradient(b, s)
+
+# Prepare figure object for plotting during the simulation
+fig = plt.figure('channel')
+plot_state(-1, 0.0)
+
+
+# # Time loop
+time = 0.; step = 0
+while time <= t_end:
+
+    #print('@ @ @ step ' + str(step))
+
+    dt = find_new_timestep(ds, Q, S)
+
+    print_status_to_stdout(time, dt)
+
+    # Find the sediment flux
+    Q_s = sediment_flux(Q)
+
+    # Find sediment flux divergence which determines channel growth, no growth
+    # in first segment
+    dSdt[1:] = sediment_flux_divergence(Q_s, ds)
+
+    # Update channel cross-sectional area and width according to growth rate
+    # and size limit for each channel segment
+    S, W, S_max = update_channel_size_with_limit(S, dSdt, dt, N_c)
+
+    # Find new water fluxes consistent with mass conservation and local
+    # meltwater production (m_dot)
+    Q = flux_solver(m_dot, ds)
+
+    # Find new water pressures consistent with the flow law
+    P_c = pressure_solver(psi, F, Q, S)
+
+    # Find new effective pressure in channel segments
+    N_c = rho_i*g*H_c - P_c
+
+    plot_state(step, time)
+
+    #import ipdb; ipdb.set_trace()
+    #if step > 0:
+        #break
+
+    # Update time
+    time += dt
+    step += 1
diff --git a/1d-channel.py b/1d-channel.py
t@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 
-## ABOUT THIS FILE
+# # ABOUT THIS FILE
 # The following script uses basic Python and Numpy functionality to solve the
 # coupled systems of equations describing subglacial channel development in
 # soft beds as presented in `Damsgaard et al. "Sediment plasticity controls
t@@ -20,38 +20,37 @@ import matplotlib.pyplot as plt
 import sys
 
 
-## Model parameters
-Ns = 25               # Number of nodes [-]
-Ls = 100e3            # Model length [m]
-t_end = 24.*60.*60.*2 # Total simulation time [s]
-tol_Q = 1e-3      # Tolerance criteria for the normalized max. residual for Q
-tol_P_c = 1e-3    # Tolerance criteria for the normalized max. residual for P_c
-max_iter = 1e2*Ns # Maximum number of solver iterations before failure
+# # Model parameters
+Ns = 25                # Number of nodes [-]
+Ls = 100e3             # Model length [m]
+t_end = 24.*60.*60.*2  # Total simulation time [s]
+tol_Q = 1e-3       # Tolerance criteria for the normalized max. residual for Q
+tol_P_c = 1e-3     # Tolerance criteria for the normalized max residual for P_c
+max_iter = 1e2*Ns  # Maximum number of solver iterations before failure
 output_convergence = False  # Display convergence statistics during run
 safety = 0.1     # Safety factor ]0;1] for adaptive timestepping
 
 # Physical parameters
-rho_w = 1000. # Water density [kg/m^3]
-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]
+rho_w = 1000.  # Water density [kg/m^3]
+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]
 
 # Water source term [m/s]
-#m_dot = 7.93e-11
+# m_dot = 7.93e-11
 m_dot = 4.5e-7
-#m_dot = 5.79e-5
+# m_dot = 5.79e-5
 
 # Walder and Fowler 1994 sediment transport parameters
 K_e = 0.1   # Erosion constant [-], disabled when 0.0
-#K_d = 6.0   # Deposition constant [-], disabled when 0.0
-K_d = 1e-1   # Deposition constant [-], disabled when 0.0
+K_d = 6.0   # Deposition constant [-], disabled when 0.0
 alpha = 2e5  # Geometric correction factor (Carter et al 2017)
-#D50 = 1e-3 # Median grain size [m]
-#tau_c = 0.5*g*(rho_s - rho_i)*D50  # Critical shear stress for transport
+# D50 = 1e-3 # Median grain size [m]
+# tau_c = 0.5*g*(rho_s - rho_i)*D50  # Critical shear stress for transport
 d15 = 1e-3  # Characteristic grain size [m]
 tau_c = 0.025*d15*g*(rho_s - rho_i)  # Critical shear stress (Carter 2017)
-#tau_c = 0.
+# tau_c = 0.
 mu_w = 1.787e-3  # Water viscosity [Pa*s]
 froude = 0.1     # Friction factor [-]
 v_s = d15**2.*g*2.*(rho_s - rho_i)/(9.*mu_w)  # Settling velocity (Carter 2017)
t@@ -61,22 +60,22 @@ manning = 0.1  # Manning roughness coefficient [m^{-1/3} s]
 F = rho_w*g*manning*(2.*(numpy.pi + 2)**2./numpy.pi)**(2./3.)
 
 # Channel growth-limit parameters
-c_1 = -0.118 # [m/kPa]
-c_2 = 4.60   # [m]
+c_1 = -0.118  # [m/kPa]
+c_2 = 4.60    # [m]
 
 # Minimum channel size [m^2], must be bigger than 0
 S_min = 1e-1
 
 
-## Initialize model arrays
+# # Initialize model arrays
 # Node positions, terminus at Ls
 s = numpy.linspace(0., Ls, Ns)
 ds = s[1:] - s[:-1]
 
 # Ice thickness and bed topography
 H = 6.*(numpy.sqrt(Ls - s + 5e3) - numpy.sqrt(5e3)) + 1.0  # max: 1.5 km
-#H = 1.*(numpy.sqrt(Ls - s + 5e3) - numpy.sqrt(5e3)) + 1.0  # max: 255 m
-#H = 0.6*(numpy.sqrt(Ls - s + 5e3) - numpy.sqrt(5e3)) + 1.0
+# H = 1.*(numpy.sqrt(Ls - s + 5e3) - numpy.sqrt(5e3)) + 1.0  # max: 255 m
+# H = 0.6*(numpy.sqrt(Ls - s + 5e3) - numpy.sqrt(5e3)) + 1.0
 b = numpy.zeros_like(H)
 
 N = H*0.1*rho_i*g            # Initial effective stress [Pa]
t@@ -84,79 +83,88 @@ p_w = rho_i*g*H - N          # Initial guess of water pressure [Pa]
 hydro_pot = rho_w*g*b + p_w  # Initial guess of hydraulic potential [Pa]
 
 # Initialize arrays for channel segments between nodes
-S = numpy.ones(len(s) - 1)*S_min # Cross-sectional area of channel segments[m^2]
-S_max = numpy.zeros_like(S) # Max. channel size [m^2]
-dSdt = numpy.empty_like(S)  # Transient in channel cross-sectional area [m^2/s]
+S = numpy.ones(len(s) - 1)*S_min  # Cross-sect. area of channel segments[m^2]
+S_max = numpy.zeros_like(S)  # Max. channel size [m^2]
+dSdt = numpy.empty_like(S)   # Transient in channel cross-sect. area [m^2/s]
 W = S/numpy.tan(numpy.deg2rad(theta))  # Assuming no channel floor wedge
-Q = numpy.zeros_like(S)     # Water flux in channel segments [m^3/s]
-Q_s = numpy.zeros_like(S)   # Sediment flux in channel segments [m^3/s]
-N_c = numpy.zeros_like(S)   # Effective pressure in channel segments [Pa]
-P_c = numpy.zeros_like(S)   # Water pressure in channel segments [Pa]
-e_dot = numpy.zeros_like(S) # Sediment erosion rate in channel segments [m/s]
-d_dot = numpy.zeros_like(S) # Sediment deposition rate in chan. segments [m/s]
-c_bar = numpy.zeros_like(S) # Vertically integrated sediment concentration [-]
-tau = numpy.zeros_like(S)   # Avg. shear stress from current [Pa]
-porosity = numpy.ones_like(S)*0.3 # Sediment porosity [-]
+Q = numpy.zeros_like(S)      # Water flux in channel segments [m^3/s]
+Q_s = numpy.zeros_like(S)    # Sediment flux in channel segments [m^3/s]
+N_c = numpy.zeros_like(S)    # Effective pressure in channel segments [Pa]
+P_c = numpy.zeros_like(S)    # Water pressure in channel segments [Pa]
+e_dot = numpy.zeros_like(S)  # Sediment erosion rate in channel segments [m/s]
+d_dot = numpy.zeros_like(S)  # Sediment deposition rate in chan. segments [m/s]
+c_bar = numpy.zeros_like(S)  # Vertically integrated sediment concentration [-]
+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
 
 
-## Helper functions
+# # Helper functions
 def gradient(arr, arr_x):
     # Central difference gradient of an array ``arr`` with node positions at
     # ``arr_x``.
     return (arr[:-1] - arr[1:])/(arr_x[:-1] - arr_x[1:])
 
+
 def avg_midpoint(arr):
     # Averaged value of neighboring array elements
     return (arr[:-1] + arr[1:])/2.
 
+
 def channel_water_flux(S, hydro_pot_grad):
     # Hewitt 2011
     return numpy.sqrt(1./F*S**(8./3.)*-hydro_pot_grad)
 
+
 def channel_shear_stress(Q, S):
     # Weertman 1972, Walder and Fowler 1994
     u_bar = Q/S
     return 1./8.*froude*rho_w*u_bar**2.
 
+
 def channel_erosion_rate(tau):
     # Parker 1979, Walder and Fowler 1994
-    #return K_e*v_s*(tau - tau_c).clip(min=0.)/(g*(rho_s - rho_w)*d15)
+    # return K_e*v_s*(tau - tau_c).clip(min=0.)/(g*(rho_s - rho_w)*d15)
     # Carter et al 2017
     return K_e*v_s/alpha*(tau - tau_c).clip(min=0.)/(g*(rho_s - rho_w)*d15)
 
+
 def channel_deposition_rate_kernel(tau, c_bar, ix):
     # Parker 1979, Walder and Fowler 1994
-    #result = K_d*v_s*c_bar[ix]*(g*(rho_s - rho_w)*d15/tau[ix])**0.5
+    # result = K_d*v_s*c_bar[ix]*(g*(rho_s - rho_w)*d15/tau[ix])**0.5
 
     # Carter et al. 2017
     result = K_d*v_s/alpha*c_bar[ix]*(g*(rho_s - rho_w)*d15/tau[ix])**0.5
 
+    '''
     print('tau[{}] = {}'.format(ix, tau[ix]))
     print('c_bar[{}] = {}'.format(ix, c_bar[ix]))
     print('e_dot[{}] = {}'.format(ix, e_dot[ix]))
     print('d_dot[{}] = {}'.format(ix, result))
     print('')
+    '''
 
     return result
 
+
 def channel_deposition_rate_kernel_ng(c_bar, ix):
     # Ng 2000
     h = W[ix]/2.*numpy.tan(numpy.deg2rad(theta))
-    epsilon = numpy.sqrt((psi[ix] - (P_c[ix] - P_c[ix - 1])/ds[ix])\
-                         /(rho_w*froude))*h**(3./2.)
+    epsilon = numpy.sqrt((psi[ix] - (P_c[ix] - P_c[ix - 1])/ds[ix])
+                         / (rho_w*froude))*h**(3./2.)
     return v_s/epsilon*c_bar[ix]
 
+
 def channel_deposition_rate(tau, c_bar, d_dot, Ns):
     # Parker 1979, Walder and Fowler 1994
     # Find deposition rate from upstream to downstream, margin at is=0
 
-
+    '''
     print("\n## Before loop:")
     print(c_bar)
     print(d_dot)
     print('')
-
+    '''
 
     # No sediment deposition at upstream end
     c_bar[0] = 0.
t@@ -164,36 +172,39 @@ def channel_deposition_rate(tau, c_bar, d_dot, Ns):
     for ix in numpy.arange(1, Ns - 1):
 
         # Net erosion in upstream cell
-        #c_bar[ix] = numpy.maximum((e_dot[ix-1] - d_dot[ix-1])*dt*ds[ix-1], 0.)
-        c_bar[ix] = numpy.maximum(
-            W[ix - 1]*ds[ix - 1]*rho_s/rho_w*
-            (e_dot[ix - 1] - d_dot[ix - 1])/Q[ix - 1]
-            , 0.)
-
-        #d_dot[ix] = channel_deposition_rate_kernel(tau, c_bar, ix)
-        d_dot[ix] = channel_deposition_rate_kernel_ng(c_bar, ix)
+        # c_bar[ix] = numpy.maximum((e_dot[ix-1]-d_dot[ix-1])*dt*ds[ix-1], 0.)
+        c_bar[ix] = c_bar[ix - 1] + \
+            numpy.maximum(
+                W[ix - 1]*ds[ix - 1]*rho_s/rho_w *
+                (e_dot[ix - 1] - d_dot[ix - 1])/Q[ix - 1], 0.)
 
+        d_dot[ix] = channel_deposition_rate_kernel(tau, c_bar, ix)
+        # d_dot[ix] = channel_deposition_rate_kernel_ng(c_bar, ix)
 
+    '''
     print("\n## After loop:")
     print(c_bar)
     print(d_dot)
     print('')
-
+    '''
 
     return d_dot, c_bar
 
+
 def channel_growth_rate(e_dot, d_dot, porosity, W):
     # Damsgaard et al, in prep
     return (e_dot - d_dot)/porosity*W
 
+
 def update_channel_size_with_limit(S, dSdt, dt, N):
     # Damsgaard et al, in prep
-    S_max = ((c_1*N/1000. + c_2)*\
+    S_max = ((c_1*N.clip(min=0.)/1000. + c_2) *
              numpy.tan(numpy.deg2rad(theta))).clip(min=S_min)
     S = numpy.minimum(S + dSdt*dt, S_max).clip(min=S_min)
     W = S/numpy.tan(numpy.deg2rad(theta))  # Assume no channel floor wedge
     return S, W, S_max
 
+
 def flux_solver(m_dot, ds):
     # Iteratively find new fluxes
     it = 0
t@@ -213,7 +224,7 @@ def flux_solver(m_dot, ds):
         if output_convergence:
             print('it = {}: max_res = {}'.format(it, max_res))
 
-        #import ipdb; ipdb.set_trace()
+        # import ipdb; ipdb.set_trace()
         if it >= max_iter:
             raise Exception('t = {}, step = {}:'.format(time, step) +
                             'Iterative solution not found for Q')
t@@ -221,11 +232,13 @@ def flux_solver(m_dot, ds):
 
     return Q
 
+
 def suspended_sediment_flux(c_bar, Q, S):
     # Find the fluvial sediment flux through the system
     # Q_s = c_bar * u * S, where u = Q/S
     return c_bar*Q
 
+
 def pressure_solver(psi, F, Q, S):
     # Iteratively find new water pressures
     # dP_c/ds = psi - FQ^2/S^{8/3}
t@@ -259,6 +272,7 @@ def pressure_solver(psi, F, Q, S):
 
     return P_c
 
+
 def plot_state(step, time):
     # Plot parameters along profile
     fig = plt.gcf()
t@@ -277,10 +291,10 @@ def plot_state(step, time):
     ax_m3s.set_ylabel('[m$^3$/s]')
 
     ax_m = plt.subplot(2, 1, 2, sharex=ax_Pa)
-    #ax_m.plot(s_c/1000., S, '-k', label='$S$')
-    #ax_m.plot(s_c/1000., S_max, '--k', label='$S_{max}$')
-    ax_m.semilogy(s_c/1000., S, '-k', label='$S$')
-    ax_m.semilogy(s_c/1000., S_max, '--k', label='$S_{max}$')
+    ax_m.plot(s_c/1000., S, '-k', label='$S$')
+    ax_m.plot(s_c/1000., S_max, '--k', label='$S_{max}$')
+    # ax_m.semilogy(s_c/1000., S, '-k', label='$S$')
+    # ax_m.semilogy(s_c/1000., S_max, '--k', label='$S_{max}$')
 
     ax_ms = ax_m.twinx()
     ax_ms.plot(s_c/1000., e_dot, '--r', label='$\dot{e}$')
t@@ -300,6 +314,7 @@ def plot_state(step, time):
         plt.savefig('chan-' + str(step) + '.pdf')
     plt.clf()
 
+
 def find_new_timestep(ds, Q, S):
     # Determine the timestep using the Courant-Friedrichs-Lewy condition
     dt = safety*numpy.minimum(60.*60.*24., numpy.min(numpy.abs(ds/(Q*S))))
t@@ -311,12 +326,13 @@ def find_new_timestep(ds, Q, S):
 
     return dt
 
+
 def print_status_to_stdout(time, dt):
-    sys.stdout.write('\rt = {:.2} s or {:.4} d, dt = {:.2} s         '\
+    sys.stdout.write('\rt = {:.2} s or {:.4} d, dt = {:.2} s         '
                      .format(time, time/(60.*60.*24.), dt))
     sys.stdout.flush()
 
-s_c = avg_midpoint(s) # Channel section midpoint coordinates [m]
+s_c = avg_midpoint(s)  # Channel section midpoint coordinates [m]
 
 # Find gradient in hydraulic potential between the nodes
 hydro_pot_grad = gradient(hydro_pot, s)
t@@ -336,11 +352,12 @@ fig = plt.figure('channel')
 plot_state(-1, 0.0)
 
 
-## Time loop
-time = 0.; step = 0
+# # Time loop
+time = 0.
+step = 0
 while time <= t_end:
 
-    #print('@ @ @ step ' + str(step))
+    # print('@ @ @ step ' + str(step))
 
     dt = find_new_timestep(ds, Q, S)
 
t@@ -365,7 +382,7 @@ while time <= t_end:
     Q = flux_solver(m_dot, ds)
 
     # Find the corresponding sediment flux
-    #Q_b = bedload_sediment_flux(
+    # Q_b = bedload_sediment_flux(
     Q_s = suspended_sediment_flux(c_bar, Q, S)
 
     # Find new water pressures consistent with the flow law
t@@ -376,9 +393,9 @@ while time <= t_end:
 
     plot_state(step, time)
 
-    #import ipdb; ipdb.set_trace()
-    if step > 0:
-        break
+    # import ipdb; ipdb.set_trace()
+    #if step > 0:
+        #break
 
     # Update time
     time += dt