Beispiel #1
0
def test_mass_conserve_all_closed_ErosionDeposition(grid, solver, v_s, dt):
    z_init = grid.at_node["topographic__elevation"].copy()

    fa = FlowAccumulator(grid)
    fa.run_one_step()

    ed = ErosionDeposition(grid, solver=solver, v_s=v_s)
    ed.run_one_step(dt)

    dz = z_init - grid.at_node["topographic__elevation"]

    # For Erosion Deposition, porosity should not have any effect, because
    # the component operates in terms of bulk-equivalent sediment flux,
    # erosion, and deposition.

    assert_array_almost_equal(dz.sum(), 0.0, decimal=10)
Beispiel #2
0
def test_erodep_slope_area_with_threshold():
    """Test steady state run with Vs = 1 and wc = 0.00001."""

    # Set up a 5x5 grid with open boundaries and low initial elevations.
    rg = RasterModelGrid((5, 5))
    z = rg.add_zeros('node', 'topographic__elevation')
    z[:] = 0.01 * rg.x_of_node

    # Create a D8 flow handler
    fa = FlowAccumulator(rg, flow_director='FlowDirectorD8')

    # test: Vs = 1
    K = 0.002
    vs = 1.0
    U = 0.001
    dt = 10.0
    wc = 0.0001

    # Create the ErosionDeposition component...
    ed = ErosionDeposition(rg,
                           K=K,
                           phi=0.0,
                           v_s=vs,
                           m_sp=0.5,
                           n_sp=1.0,
                           sp_crit=wc,
                           method='threshold_stream_power',
                           discharge_method='drainage_area',
                           area_field='drainage_area',
                           solver='adaptive')

    # ... and run it to steady state.
    for i in range(1000):
        fa.run_one_step()
        ed.run_one_step(dt=dt)
        z[rg.core_nodes] += U * dt

    # Test the results
    s = rg.at_node['topographic__steepest_slope']
    sa_factor = ((1.0 + vs) * U + wc) / K  # approximate sol'n
    a11 = 2.0
    a12 = 1.0
    s11 = sa_factor * (a11**-0.5)
    s12 = sa_factor * (a12**-0.5)
    assert_equal(np.round(s[11], 2), np.round(s11, 2))
    assert_equal(np.round(s[12], 2), np.round(s12, 2))
Beispiel #3
0
def test_can_run_with_hex():
    """Test that model can run with hex model grid."""

    # Set up a 5x5 grid with open boundaries and low initial elevations.
    mg = HexModelGrid(7, 7)
    z = mg.add_zeros('node', 'topographic__elevation')
    z[:] = 0.01 * mg.x_of_node

    # Create a D8 flow handler
    fa = FlowAccumulator(mg, flow_director='FlowDirectorSteepest')

    # Parameter values for test 1
    K = 0.001
    vs = 0.0001
    U = 0.001
    dt = 10.0

    # Create the ErosionDeposition component...
    ed = ErosionDeposition(mg,
                           K=K,
                           phi=0.0,
                           v_s=vs,
                           m_sp=0.5,
                           n_sp=1.0,
                           method='simple_stream_power',
                           discharge_method='drainage_area',
                           area_field='drainage_area',
                           solver='adaptive')

    # ... and run it to steady state.
    for i in range(2000):
        fa.run_one_step()
        ed.run_one_step(dt=dt)
        z[mg.core_nodes] += U * dt

    # Test the results
    s = mg.at_node['topographic__steepest_slope']
    sa_factor = (1.0 + vs) * U / K
    a18 = mg.at_node['drainage_area'][18]
    a28 = mg.at_node['drainage_area'][28]
    s = mg.at_node['topographic__steepest_slope']
    s18 = sa_factor * (a18**-0.5)
    s28 = sa_factor * (a28**-0.5)
    assert_equal(np.round(s[18], 3), np.round(s18, 3))
    assert_equal(np.round(s[28], 3), np.round(s28, 3))
Beispiel #4
0
def test_erodep_slope_area_shear_stress_scaling():
    """Test steady state run with m_sp = 0.33, n_sp=0.67, Vs = 1."""

    # Set up a 5x5 grid with open boundaries and low initial elevations.
    rg = RasterModelGrid((5, 5))
    rg.set_closed_boundaries_at_grid_edges(True, True, True, False)
    z = rg.add_zeros('node', 'topographic__elevation')
    z[:] = 0.01 * rg.x_of_node

    # Create a D8 flow handler
    fa = FlowAccumulator(rg, flow_director='FlowDirectorD8')

    # test: Vs = 1
    K = 0.002
    vs = 1.0
    U = 0.001
    dt = 10.0

    # Create the ErosionDeposition component...
    ed = ErosionDeposition(rg,
                           K=K,
                           phi=0.0,
                           v_s=vs,
                           m_sp=0.33,
                           n_sp=0.67,
                           method='simple_stream_power',
                           discharge_method='drainage_area',
                           area_field='drainage_area',
                           solver='adaptive')

    # ... and run it to steady state.
    for i in range(1500):
        fa.run_one_step()
        ed.run_one_step(dt=dt)
        z[rg.core_nodes] += U * dt

    # Test the results
    s = rg.at_node['topographic__steepest_slope']
    sa_factor = ((1.0 + vs) * U / K)**(1.0 / 0.67)
    a6 = 5.0
    a8 = 3.0
    s6 = sa_factor * (a6**-(0.33 / 0.67))
    s8 = sa_factor * (a8**-(0.33 / 0.67))
    assert_equal(np.round(s[6], 2), np.round(s6, 2))
    assert_equal(np.round(s[8], 2), np.round(s8, 2))
Beispiel #5
0
def test_erodep_slope_area_small_vs():
    """Test steady state run with Vs << 1."""

    # Set up a 5x5 grid with open boundaries and low initial elevations.
    rg = RasterModelGrid((5, 5))
    z = rg.add_zeros('node', 'topographic__elevation')
    z[:] = 0.01 * rg.x_of_node

    # Create a D8 flow handler
    fa = FlowAccumulator(rg, flow_director='FlowDirectorD8')

    # Parameter values for test 1
    K = 0.001
    vs = 0.0001
    U = 0.001
    dt = 10.0

    # Create the ErosionDeposition component...
    ed = ErosionDeposition(rg,
                           K=K,
                           phi=0.0,
                           v_s=vs,
                           m_sp=0.5,
                           n_sp=1.0,
                           method='simple_stream_power',
                           discharge_method='drainage_area',
                           area_field='drainage_area',
                           solver='adaptive')

    # ... and run it to steady state.
    for i in range(1000):
        fa.run_one_step()
        ed.run_one_step(dt=dt)
        z[rg.core_nodes] += U * dt

    # Test the results
    s = rg.at_node['topographic__steepest_slope']
    sa_factor = (1.0 + vs) * U / K
    a11 = 2.0
    a12 = 1.0
    s = rg.at_node['topographic__steepest_slope']
    s11 = sa_factor * (a11**-0.5)
    s12 = sa_factor * (a12**-0.5)
    assert_equal(np.round(s[11], 3), np.round(s11, 3))
    assert_equal(np.round(s[12], 3), np.round(s12, 3))
def test_erodep_slope_area_shear_stress_scaling():
    """Test steady state run with m_sp = 0.33, n_sp=0.67, Vs = 1."""

    # Set up a 5x5 grid with open boundaries and low initial elevations.
    rg = RasterModelGrid((5, 5))
    rg.set_closed_boundaries_at_grid_edges(True, True, True, False)
    z = rg.add_zeros("node", "topographic__elevation")
    z[:] = 0.01 * rg.x_of_node

    # Create a D8 flow handler
    fa = FlowAccumulator(rg, flow_director="FlowDirectorD8")

    # test: Vs = 1
    K = 0.002
    vs = 1.0
    U = 0.001
    dt = 10.0
    m_sp = 0.33
    n_sp = 0.67
    # Create the ErosionDeposition component...
    ed = ErosionDeposition(rg,
                           K=K,
                           phi=0.0,
                           v_s=vs,
                           m_sp=m_sp,
                           n_sp=n_sp,
                           solver="adaptive")

    # ... and run it to steady state.
    for i in range(1500):
        fa.run_one_step()
        ed.run_one_step(dt=dt)
        z[rg.core_nodes] += U * dt

    # Test the results
    s = rg.at_node["topographic__steepest_slope"]
    sa_factor = ((1.0 + vs) * U / K)**(1.0 / n_sp)
    a6 = rg.at_node["drainage_area"][6]
    a8 = rg.at_node["drainage_area"][8]
    s6 = sa_factor * (a6**-(m_sp / n_sp))
    s8 = sa_factor * (a8**-(m_sp / n_sp))
    assert_equal(np.round(s[6], 2), np.round(s6, 2))
    assert_equal(np.round(s[8], 2), np.round(s8, 2))
def test_erodep_slope_area_with_threshold():
    """Test steady state run with Vs = 1 and wc = 0.00001."""

    # Set up a 5x5 grid with open boundaries and low initial elevations.
    rg = RasterModelGrid((5, 5))
    z = rg.add_zeros('node', 'topographic__elevation')
    z[:] = 0.01 * rg.x_of_node

    # Create a D8 flow handler
    fa = FlowAccumulator(rg, flow_director='FlowDirectorD8')

    # test: Vs = 1
    K = 0.002
    vs = 1.0
    U = 0.001
    dt = 10.0
    wc = 0.0001

    # Create the ErosionDeposition component...
    ed = ErosionDeposition(rg, K=K, phi=0.0, v_s=vs, m_sp=0.5, n_sp=1.0,
                           sp_crit=wc,
                           method='threshold_stream_power',
                           discharge_method='drainage_area', 
                           area_field='drainage_area',
                           solver='adaptive')

    # ... and run it to steady state.
    for i in range(1000):
        fa.run_one_step()
        ed.run_one_step(dt=dt)
        z[rg.core_nodes] += U * dt

    # Test the results
    s = rg.at_node['topographic__steepest_slope']
    sa_factor = ((1.0 + vs) * U + wc) / K  # approximate sol'n
    a11 = 2.0
    a12 = 1.0
    s11 = sa_factor * (a11 ** -0.5)
    s12 = sa_factor * (a12 ** -0.5)
    assert_equal(np.round(s[11], 2), np.round(s11, 2))
    assert_equal(np.round(s[12], 2), np.round(s12, 2))
def test_erodep_slope_area_shear_stress_scaling():
    """Test steady state run with m_sp = 0.33, n_sp=0.67, Vs = 1."""

    # Set up a 5x5 grid with open boundaries and low initial elevations.
    rg = RasterModelGrid((5, 5))
    rg.set_closed_boundaries_at_grid_edges(True, True, True, False)
    z = rg.add_zeros('node', 'topographic__elevation')
    z[:] = 0.01 * rg.x_of_node

    # Create a D8 flow handler
    fa = FlowAccumulator(rg, flow_director='FlowDirectorD8')

    # test: Vs = 1
    K = 0.002
    vs = 1.0
    U = 0.001
    dt = 10.0
    m_sp = 0.33
    n_sp = 0.67
    # Create the ErosionDeposition component...
    ed = ErosionDeposition(rg, K=K, phi=0.0, v_s=vs, m_sp=m_sp, n_sp=n_sp,
                           method='simple_stream_power',
                           discharge_method='drainage_area',
                           area_field='drainage_area',
                           solver='adaptive')

    # ... and run it to steady state.
    for i in range(1500):
        fa.run_one_step()
        ed.run_one_step(dt=dt)
        z[rg.core_nodes] += U * dt

    # Test the results
    s = rg.at_node['topographic__steepest_slope']
    sa_factor = ((1.0 + vs) * U / K) ** (1.0 / n_sp)
    a6 = rg.at_node['drainage_area'][6]
    a8 = rg.at_node['drainage_area'][8]
    s6 = sa_factor * (a6 ** -(m_sp / n_sp))
    s8 = sa_factor * (a8 ** -(m_sp / n_sp))
    assert_equal(np.round(s[6], 2), np.round(s6, 2))
    assert_equal(np.round(s[8], 2), np.round(s8, 2))
Beispiel #9
0
def test_can_run_with_hex():
    """Test that model can run with hex model grid."""

    # Set up a 5x5 grid with open boundaries and low initial elevations.
    mg = HexModelGrid((7, 7))
    z = mg.add_zeros("topographic__elevation", at="node")
    z[:] = 0.01 * mg.x_of_node

    # Create a D8 flow handler
    fa = FlowAccumulator(mg, flow_director="FlowDirectorSteepest")

    # Parameter values for test 1
    K = 0.001
    vs = 0.0001
    U = 0.001
    dt = 10.0

    # Create the ErosionDeposition component...
    ed = ErosionDeposition(mg,
                           K=K,
                           v_s=vs,
                           m_sp=0.5,
                           n_sp=1.0,
                           solver="adaptive")

    # ... and run it to steady state.
    for _ in range(2000):
        fa.run_one_step()
        ed.run_one_step(dt=dt)
        z[mg.core_nodes] += U * dt

    # Test the results
    s = mg.at_node["topographic__steepest_slope"]
    sa_factor = (1.0 + vs) * U / K
    a18 = mg.at_node["drainage_area"][18]
    a28 = mg.at_node["drainage_area"][28]
    s = mg.at_node["topographic__steepest_slope"]
    s18 = sa_factor * (a18**-0.5)
    s28 = sa_factor * (a28**-0.5)
    testing.assert_equal(np.round(s[18], 3), np.round(s18, 3))
    testing.assert_equal(np.round(s[28], 3), np.round(s28, 3))
def test_erodep_slope_area_big_vs():
    """Test steady state run with Vs >> 1."""

    # Set up a 5x5 grid with open boundaries and low initial elevations.
    rg = RasterModelGrid((5, 5))
    z = rg.add_zeros("node", "topographic__elevation")
    z[:] = 0.01 * rg.x_of_node

    # Create a D8 flow handler
    fa = FlowAccumulator(rg, flow_director="FlowDirectorD8")

    # Next test: big Vs
    K = 1.0
    vs = 1000.0
    U = 0.001
    dt = 10.0

    # Create the ErosionDeposition component...
    ed = ErosionDeposition(rg,
                           K=K,
                           phi=0.0,
                           v_s=vs,
                           m_sp=0.5,
                           n_sp=1.0,
                           solver="adaptive")

    # ... and run it to steady state.
    for i in range(1000):
        fa.run_one_step()
        ed.run_one_step(dt=dt)
        z[rg.core_nodes] += U * dt

    # Test the results
    s = rg.at_node["topographic__steepest_slope"]
    sa_factor = (1.0 + vs) * U / K
    a11 = 2.0
    a12 = 1.0
    s11 = sa_factor * (a11**-0.5)
    s12 = sa_factor * (a12**-0.5)
    assert_equal(np.round(s[11], 2), np.round(s11, 2))
    assert_equal(np.round(s[12], 2), np.round(s12, 2))
def test_erodep_slope_area_small_vs():
    """Test steady state run with Vs << 1."""

    # Set up a 5x5 grid with open boundaries and low initial elevations.
    rg = RasterModelGrid((5, 5))
    z = rg.add_zeros('node', 'topographic__elevation')
    z[:] = 0.01 * rg.x_of_node

    # Create a D8 flow handler
    fa = FlowAccumulator(rg, flow_director='FlowDirectorD8')

    # Parameter values for test 1
    K = 0.001
    vs = 0.0001
    U = 0.001
    dt = 10.0

    # Create the ErosionDeposition component...
    ed = ErosionDeposition(rg, K=K, phi=0.0, v_s=vs, m_sp=0.5, n_sp=1.0,
                           method='simple_stream_power',
                           discharge_method='drainage_area', 
                           area_field='drainage_area',
                           solver='adaptive')

    # ... and run it to steady state.
    for i in range(1000):
        fa.run_one_step()
        ed.run_one_step(dt=dt)
        z[rg.core_nodes] += U * dt

    # Test the results
    s = rg.at_node['topographic__steepest_slope']
    sa_factor = (1.0 + vs) * U / K
    a11 = 2.0
    a12 = 1.0
    s = rg.at_node['topographic__steepest_slope']    
    s11 = sa_factor * (a11 ** -0.5)
    s12 = sa_factor * (a12 ** -0.5)
    assert_equal(np.round(s[11], 3), np.round(s11, 3))
    assert_equal(np.round(s[12], 3), np.round(s12, 3))
Beispiel #12
0
def test_can_run_with_hex():
    """Test that model can run with hex model grid."""

    # Set up a 5x5 grid with open boundaries and low initial elevations.
    mg = HexModelGrid(7, 7)
    z = mg.add_zeros('node', 'topographic__elevation')
    z[:] = 0.01 * mg.x_of_node

    # Create a D8 flow handler
    fa = FlowAccumulator(mg, flow_director='FlowDirectorSteepest')

    # Parameter values for test 1
    K = 0.001
    vs = 0.0001
    U = 0.001
    dt = 10.0

    # Create the ErosionDeposition component...
    ed = ErosionDeposition(mg, K=K, phi=0.0, v_s=vs, m_sp=0.5, n_sp=1.0,
                           method='simple_stream_power',
                           discharge_method='drainage_area',
                           area_field='drainage_area',
                           solver='adaptive')

    # ... and run it to steady state.
    for i in range(2000):
        fa.run_one_step()
        ed.run_one_step(dt=dt)
        z[mg.core_nodes] += U * dt

    # Test the results
    s = mg.at_node['topographic__steepest_slope']
    sa_factor = (1.0 + vs) * U / K
    a18 = mg.at_node['drainage_area'][18]
    a28 = mg.at_node['drainage_area'][28]
    s = mg.at_node['topographic__steepest_slope']
    s18 = sa_factor * (a18 ** -0.5)
    s28 = sa_factor * (a28 ** -0.5)
    assert_equal(np.round(s[18], 3), np.round(s18, 3))
    assert_equal(np.round(s[28], 3), np.round(s28, 3))
def test_erodep_slope_area_with_vs_unity():
    """Test steady state run with Vs = 1."""

    # Set up a 5x5 grid with open boundaries and low initial elevations.
    rg = RasterModelGrid((5, 5))
    z = rg.add_zeros("node", "topographic__elevation")
    z[:] = 0.01 * rg.x_of_node

    # Create a D8 flow handler
    fa = FlowAccumulator(rg, flow_director="FlowDirectorD8")

    # test: Vs = 1
    K = 0.002
    vs = 1.0
    U = 0.001
    dt = 10.0

    # Create the ErosionDeposition component...
    ed = ErosionDeposition(
        rg, K=K, phi=0.0, v_s=vs, m_sp=0.5, n_sp=1.0, solver="adaptive"
    )

    # ... and run it to steady state.
    for i in range(1000):
        fa.run_one_step()
        ed.run_one_step(dt=dt)
        z[rg.core_nodes] += U * dt

    # Test the results
    s = rg.at_node["topographic__steepest_slope"]
    sa_factor = (1.0 + vs) * U / K
    a11 = 2.0
    a12 = 1.0
    s11 = sa_factor * (a11 ** -0.5)
    s12 = sa_factor * (a12 ** -0.5)
    assert_equal(np.round(s[11], 2), np.round(s11, 2))
    assert_equal(np.round(s[12], 2), np.round(s12, 2))
Beispiel #14
0
def test_mass_conserve_with_depression_finder_ErosionDeposition(
        grid2, solver, depression_finder, v_s, dt):
    assert grid2.status_at_node[1] == grid2.BC_NODE_IS_FIXED_VALUE

    z_init = grid2.at_node["topographic__elevation"].copy()

    if depression_finder is None:
        fa = FlowAccumulator(grid2)
    else:
        fa = FlowAccumulator(grid2,
                             depression_finder=depression_finder,
                             routing="D4")
    fa.run_one_step()

    ed = ErosionDeposition(grid2, solver=solver, v_s=v_s)
    ed.run_one_step(dt)

    dz = grid2.at_node["topographic__elevation"] - z_init

    # assert that the mass loss over the surface is exported through the one
    # outlet.
    net_change = dz[grid2.core_nodes].sum() + (ed.sediment_influx[1] * dt /
                                               grid2.cell_area_at_node[11])
    assert_array_almost_equal(net_change, 0.0, decimal=10)
                     threshold_sp=0.0)  #k eh erodibilidade, usar 0.0004
#FastscapeEroder(grid, K_sp=0.001, m_sp=0.5, n_sp=1.0, threshold_sp=0.0, discharge_field='drainage_area', erode_flooded_nodes=True)
ed = ErosionDeposition(
    mg,
    K=0.0004,  # Erodibility for substrate (units vary)valor anterior = 0.00001
    v_s=0.001,  # Effective settling velocity for chosen grain size metric [L/T].
    m_sp=
    0.5,  # Discharge exponent (units vary) usar valores do fast scape (valor anterior = 0.5)
    n_sp=1.0,  #Slope exponent (units vary) usar valores do fast scape
    sp_crit=0
)  #Critical stream power to erode substrate [E/(TL^2)] usar valores do fast scape
lin_diffuse = LinearDiffuser(mg, linear_diffusivity=0.01)

uplift_rate = 0.001
time_step = 1000
fr.run_one_step()
sp.run_one_step(time_step)
for i in range(101):
    print(i)
    fr.run_one_step()
    df.map_depressions()
    flooded = np.where(df.flood_status == 3)[0]  # ver pra que serve
    ed.run_one_step(time_step)
    mg.at_node['topographic__elevation'] += time_step * uplift_rate
    #no artigo ele usa em anos mas esta de acordo com todos os outros parametros em anos tbm
    #lin_diffuse.run_one_step(time_step)
    css.run_one_step()
    if i == 1 or i == 10 or i == 20 or i == 40 or i == 60 or i == 80 or i == 100:
        files = write_esri_ascii(
            "../testes_dispersao/" + test_name + "/" + str(i) + ".asc", mg)
class BasicHy(_ErosionModel):
    """
    A BasicHy model computes erosion of sediment and bedrock
    using dual mass conservation on the bed and in the water column. It
    applies exponential entrainment rules to account for bed cover.
    """
    def __init__(self,
                 input_file=None,
                 params=None,
                 BaselevelHandlerClass=None):
        """Initialize the HybridAlluviumModel."""

        # Call ErosionModel's init
        super(BasicHy,
              self).__init__(input_file=input_file,
                             params=params,
                             BaselevelHandlerClass=BaselevelHandlerClass)

        # Get Parameters and convert units if necessary:
        K_sp = self.get_parameter_from_exponent('K_sp', raise_error=False)
        K_ss = self.get_parameter_from_exponent('K_ss', raise_error=False)

        # check that a stream power and a shear stress parameter have not both been given
        if K_sp != None and K_ss != None:
            raise ValueError(
                'A parameter for both K_rock_sp and K_rock_ss has been'
                'provided. Only one of these may be provided')
        elif K_sp != None or K_ss != None:
            if K_sp != None:
                self.K = K_sp
            else:
                self.K = (self._length_factor**(
                    1. / 3.)) * K_ss  # K_ss has units Lengtg^(1/3) per Time
        else:
            raise ValueError(
                'A value for K_rock_sp or K_rock_ss  must be provided.')

        # Unit conversion for linear_diffusivity, with units L^2/T
        linear_diffusivity = (
            (self._length_factor**2.) *
            self.get_parameter_from_exponent('linear_diffusivity'))

        # Normalized settling velocity (dimensionless)
        v_sc = self.get_parameter_from_exponent('v_sc')

        # Instantiate a FlowAccumulator with DepressionFinderAndRouter using D8 method
        self.flow_router = FlowAccumulator(
            self.grid,
            flow_director='D8',
            depression_finder=DepressionFinderAndRouter)

        #make area_field and/or discharge_field depending on discharge_method
        #        area_field = self.grid.at_node['drainage_area']
        #        discharge_field = None

        # Handle solver option
        try:
            solver = self.params['solver']
        except:
            solver = 'original'

        # Instantiate a Space component
        self.eroder = ErosionDeposition(self.grid,
                                        K=self.K,
                                        phi=self.params['phi'],
                                        F_f=self.params['F_f'],
                                        v_s=v_sc,
                                        m_sp=self.params['m_sp'],
                                        n_sp=self.params['n_sp'],
                                        method='simple_stream_power',
                                        discharge_method='drainage_area',
                                        area_field='drainage_area',
                                        solver=solver)

        # Instantiate a LinearDiffuser component
        self.diffuser = LinearDiffuser(self.grid,
                                       linear_diffusivity=linear_diffusivity)

    def run_one_step(self, dt):
        """
        Advance model for one time-step of duration dt.
        """

        # Route flow
        self.flow_router.run_one_step()

        # Get IDs of flooded nodes, if any
        flooded = np.where(
            self.flow_router.depression_finder.flood_status == 3)[0]

        # Do some erosion (but not on the flooded nodes)
        # (if we're varying K through time, update that first)
        if self.opt_var_precip:
            self.eroder.K = (
                self.K *
                self.pc.get_erodibility_adjustment_factor(self.model_time))
        self.eroder.run_one_step(dt,
                                 flooded_nodes=flooded,
                                 dynamic_dt=True,
                                 flow_director=self.flow_router.flow_director)

        # Do some soil creep
        self.diffuser.run_one_step(dt)

        # calculate model time
        self.model_time += dt

        # Lower outlet
        self.update_outlet(dt)

        # Check walltime
        self.check_walltime()
Beispiel #17
0
def test_steady_state_with_basic_solver_option():
    """
    Test that model matches the transport-limited analytical solution
    for slope/area relationship at steady state: S=((U * v_s) / (K * A^m)
    + U / (K * A^m))^(1/n).

    Also test that model matches the analytical solution for steady-state
    sediment flux: Qs = U * A * (1 - phi).
    """

    #set up a 5x5 grid with one open outlet node and low initial elevations.
    nr = 5
    nc = 5
    mg = RasterModelGrid((nr, nc), 10.0)

    z = mg.add_zeros('node', 'topographic__elevation')

    mg['node']['topographic__elevation'] += mg.node_y / 100000 \
        + mg.node_x / 100000 \
        + np.random.rand(len(mg.node_y)) / 10000
    mg.set_closed_boundaries_at_grid_edges(bottom_is_closed=True,
                                           left_is_closed=True,
                                           right_is_closed=True,
                                           top_is_closed=True)
    mg.set_watershed_boundary_condition_outlet_id(0,
                                                  mg['node']['topographic__elevation'],
                                                  -9999.)

    #Instantiate DepressionFinderAndRouter
    df = DepressionFinderAndRouter(mg)

    # Create a D8 flow handler
    fa = FlowAccumulator(mg, flow_director='D8',
                         depression_finder='DepressionFinderAndRouter')

    # Parameter values for detachment-limited test
    K = 0.01
    U = 0.0001
    dt = 1.0
    F_f = 0.0 #all sediment is considered coarse bedload
    m_sp = 0.5
    n_sp = 1.0
    v_s = 0.5
    phi=0.5

    # Instantiate the ErosionDeposition component...
    ed = ErosionDeposition(mg, K=K, F_f=F_f, phi=phi, v_s=v_s, m_sp=m_sp,
                           n_sp=n_sp, sp_crit=0, solver='basic')

    # ... and run it to steady state (5000x1-year timesteps).
    for i in range(5000):
        fa.run_one_step()
        flooded = np.where(df.flood_status==3)[0]
        ed.run_one_step(dt=dt, flooded_nodes=flooded)
        z[mg.core_nodes] += U * dt #m

    #compare numerical and analytical slope solutions
    num_slope = mg.at_node['topographic__steepest_slope'][mg.core_nodes]
    analytical_slope = (np.power(((U * v_s) / (K
        * np.power(mg.at_node['drainage_area'][mg.core_nodes], m_sp)))
        + (U / (K * np.power(mg.at_node['drainage_area'][mg.core_nodes],
        m_sp))), 1./n_sp))

    #test for match with analytical slope-area relationship
    testing.assert_array_almost_equal(num_slope, analytical_slope,
                                      decimal=8,
                                      err_msg='E/D slope-area test failed',
                                      verbose=True)

    #compare numerical and analytical sediment flux solutions
    num_sedflux = mg.at_node['sediment__flux'][mg.core_nodes]
    analytical_sedflux = (U * mg.at_node['drainage_area'][mg.core_nodes]
        * (1 - phi))

    #test for match with anakytical sediment flux
    testing.assert_array_almost_equal(num_sedflux, analytical_sedflux,
                                      decimal=8,
                                      err_msg='E/D sediment flux test failed',
                                      verbose=True)
Beispiel #18
0
class BasicDdHy(_ErosionModel):
    """
    A BasicDdHy computes erosion using 1) the hybrid alluvium component
    with a threshold that varies with cumulative incision depth, the linear
    diffusion component.
    """
    def __init__(self,
                 input_file=None,
                 params=None,
                 BaselevelHandlerClass=None):
        """
        Initialize the BasicDdHy
        """

        # Call ErosionModel's init
        super(BasicDdHy,
              self).__init__(input_file=input_file,
                             params=params,
                             BaselevelHandlerClass=BaselevelHandlerClass)

        # Get Parameters and convert units if necessary:
        self.K_sp = self.get_parameter_from_exponent('K_sp')
        linear_diffusivity = (
            (self._length_factor**2)  # L2/T
            * self.get_parameter_from_exponent('linear_diffusivity'))
        v_s = self.get_parameter_from_exponent('v_sc')  # unitless
        self.sp_crit = (
            self._length_factor  # L/T
            * self.get_parameter_from_exponent('erosion__threshold'))

        # Instantiate a FlowAccumulator with DepressionFinderAndRouter using D8 method
        self.flow_router = FlowAccumulator(
            self.grid,
            flow_director='D8',
            depression_finder=DepressionFinderAndRouter)

        # Create a field for the (initial) erosion threshold
        self.threshold = self.grid.add_zeros('node', 'erosion__threshold')
        self.threshold[:] = self.sp_crit  #starting value

        # Handle solver option
        try:
            solver = self.params['solver']
        except:
            solver = 'original'

        # Instantiate an ErosionDeposition component
        self.eroder = ErosionDeposition(self.grid,
                                        K=self.K_sp,
                                        F_f=self.params['F_f'],
                                        phi=self.params['phi'],
                                        v_s=v_s,
                                        m_sp=self.params['m_sp'],
                                        n_sp=self.params['n_sp'],
                                        sp_crit='erosion__threshold',
                                        method='threshold_stream_power',
                                        discharge_method='drainage_area',
                                        area_field='drainage_area',
                                        solver=solver)

        # Get the parameter for rate of threshold increase with erosion depth
        self.thresh_change_per_depth = self.params['thresh_change_per_depth']

        # Instantiate a LinearDiffuser component
        self.diffuser = LinearDiffuser(self.grid,
                                       linear_diffusivity=linear_diffusivity)

    def run_one_step(self, dt):
        """
        Advance model for one time-step of duration dt.
        """

        # Route flow
        self.flow_router.run_one_step()

        # Get IDs of flooded nodes, if any
        flooded = np.where(
            self.flow_router.depression_finder.flood_status == 3)[0]

        # Calculate cumulative erosion and update threshold
        cum_ero = self.grid.at_node['cumulative_erosion__depth']
        cum_ero[:] = (self.z -
                      self.grid.at_node['initial_topographic__elevation'])
        self.threshold[:] = (self.sp_crit -
                             (self.thresh_change_per_depth * cum_ero))
        self.threshold[self.threshold < self.sp_crit] = self.sp_crit

        # Do some erosion (but not on the flooded nodes)
        # (if we're varying K through time, update that first)
        if self.opt_var_precip:
            self.eroder.K = (
                self.K_sp *
                self.pc.get_erodibility_adjustment_factor(self.model_time))
        self.eroder.run_one_step(dt, flooded_nodes=flooded)

        # Do some soil creep
        self.diffuser.run_one_step(dt)

        # calculate model time
        self.model_time += dt

        # Lower outlet
        self.update_outlet(dt)

        # Check walltime
        self.check_walltime()
class BasicHyVs(_ErosionModel):
    """
    A BasicHyVs computes erosion using linear diffusion,
    hybrid alluvium fluvial erosion, and Q ~ A exp( -b S / A).

    "VSA" stands for "variable source area".
    """
    def __init__(self,
                 input_file=None,
                 params=None,
                 BaselevelHandlerClass=None):
        """Initialize the BasicHyVs."""

        # Call ErosionModel's init
        super(BasicHyVs,
              self).__init__(input_file=input_file,
                             params=params,
                             BaselevelHandlerClass=BaselevelHandlerClass)

        self.K_sp = self.get_parameter_from_exponent('K_sp')
        linear_diffusivity = (
            (self._length_factor**2) *
            self.get_parameter_from_exponent('linear_diffusivity')
        )  # has units length^2/time
        recharge_rate = (self._length_factor * self.params['recharge_rate']
                         )  # L/T
        soil_thickness = (self._length_factor *
                          self.params['initial_soil_thickness'])  # L
        K_hydraulic_conductivity = (self._length_factor *
                                    self.params['K_hydraulic_conductivity']
                                    )  # has units length per time

        v_sc = self.get_parameter_from_exponent(
            'v_sc')  # normalized settling velocity. Unitless.

        # Instantiate a FlowAccumulator with DepressionFinderAndRouter using D8 method
        self.flow_router = FlowAccumulator(
            self.grid,
            flow_director='D8',
            depression_finder=DepressionFinderAndRouter)

        # set methods and fields. K's and sp_crits need to be field names
        method = 'simple_stream_power'
        discharge_method = 'drainage_area'
        area_field = 'effective_drainage_area'
        discharge_field = None

        # Add a field for effective drainage area
        if 'effective_drainage_area' in self.grid.at_node:
            self.eff_area = self.grid.at_node['effective_drainage_area']
        else:
            self.eff_area = self.grid.add_zeros('node',
                                                'effective_drainage_area')

        # Get the effective-area parameter
        self.sat_param = (
            (K_hydraulic_conductivity * soil_thickness * self.grid.dx) /
            recharge_rate)

        # Handle solver option
        try:
            solver = self.params['solver']
        except KeyError:
            solver = 'original'

        # Instantiate a SPACE component
        self.eroder = ErosionDeposition(self.grid,
                                        K=self.K_sp,
                                        F_f=self.params['F_f'],
                                        phi=self.params['phi'],
                                        v_s=v_sc,
                                        m_sp=self.params['m_sp'],
                                        n_sp=self.params['n_sp'],
                                        method=method,
                                        discharge_method=discharge_method,
                                        area_field=area_field,
                                        discharge_field=discharge_field,
                                        solver=solver)

        # Instantiate a LinearDiffuser component
        self.diffuser = LinearDiffuser(self.grid,
                                       linear_diffusivity=linear_diffusivity)

    def calc_effective_drainage_area(self):
        """Calculate and store effective drainage area.

        Effective drainage area is defined as:

        $A_{eff} = A \exp ( \alpha S / A) = A R_r$

        where $S$ is downslope-positive steepest gradient, $A$ is drainage
        area, $R_r$ is the runoff ratio, and $\alpha$ is the saturation
        parameter.
        """

        area = self.grid.at_node['drainage_area']
        slope = self.grid.at_node['topographic__steepest_slope']
        cores = self.grid.core_nodes
        self.eff_area[cores] = (
            area[cores] *
            (np.exp(-self.sat_param * slope[cores] / area[cores])))

    def run_one_step(self, dt):
        """
        Advance model for one time-step of duration dt.
        """

        # Route flow
        self.flow_router.run_one_step()

        # Update effective runoff ratio
        self.calc_effective_drainage_area()

        # Zero out effective area in flooded nodes
        self.eff_area[self.flow_router.depression_finder.flood_status ==
                      3] = 0.0

        # Do some erosion
        # (if we're varying K through time, update that first)
        if self.opt_var_precip:
            self.eroder.K = (
                self.K_sp *
                self.pc.get_erodibility_adjustment_factor(self.model_time))
        self.eroder.run_one_step(dt)

        # Do some soil creep
        self.diffuser.run_one_step(dt)

        # calculate model time
        self.model_time += dt

        # Lower outlet
        self.update_outlet(dt)

        # Check walltime
        self.check_walltime()
Beispiel #20
0
def test_phi_affects_transience():
    """Test that different porosity values affect the transient case."""

    # Set up one 5x5 grid with open boundaries and low initial elevations.
    mg1 = HexModelGrid((7, 7))
    z1 = mg1.add_zeros("topographic__elevation", at="node")
    z1[:] = 0.01 * mg1.x_of_node

    # Create a D8 flow handler
    fa1 = FlowAccumulator(mg1, flow_director="FlowDirectorSteepest")

    # Parameter values for test 1
    K1 = 0.001
    vs1 = 0.0001
    U1 = 0.001
    dt1 = 10.0
    phi1 = 0.1

    # Create the ErosionDeposition component...
    ed1 = ErosionDeposition(mg1,
                            K=K1,
                            phi=phi1,
                            v_s=vs1,
                            m_sp=0.5,
                            n_sp=1.0,
                            solver="basic")

    # ... and run it to steady state.
    for i in range(200):
        fa1.run_one_step()
        ed1.run_one_step(dt=dt1)
        z1[mg1.core_nodes] += U1 * dt1

    # Set up a second 5x5 grid with open boundaries and low initial elevations.
    mg2 = HexModelGrid((7, 7))
    z2 = mg2.add_zeros("topographic__elevation", at="node")
    z2[:] = 0.01 * mg2.x_of_node

    # Create a D8 flow handler
    fa2 = FlowAccumulator(mg2, flow_director="FlowDirectorSteepest")

    # Parameter values for test 1
    K2 = 0.001
    vs2 = 0.0001
    U2 = 0.001
    dt2 = 10.0
    phi2 = 0.9

    # Create the ErosionDeposition component...
    ed2 = ErosionDeposition(mg2,
                            K=K2,
                            phi=phi2,
                            v_s=vs2,
                            m_sp=0.5,
                            n_sp=1.0,
                            solver="basic")

    # ... and run it to steady state.
    for i in range(200):
        fa2.run_one_step()
        ed2.run_one_step(dt=dt2)
        z2[mg2.core_nodes] += U2 * dt2

    # Test the results: higher phi should be lower slope
    s1 = mg1.at_node["topographic__steepest_slope"][mg1.core_nodes]
    s2 = mg2.at_node["topographic__steepest_slope"][mg2.core_nodes]
    testing.assert_array_less(s2, s1)
class BasicHy(ErosionModel):
    r"""**BasicHy** model program.

    **BasicHy** is a model program that evolves a topographic surface described
    by :math:`\eta` with the following governing equation:

    .. math::

        \frac{\partial \eta}{\partial t} = \frac{V Q_s}
                                                {Q\left(1 - \phi \right)}
                                           - KQ^{m}S^{n}
                                           + D\nabla^2 \eta

        Q_s = \int_0^A \left((1-F_f)KQ(A)^{m}S^{n}
                             - \frac{V Q_s}{Q(A)} \right) dA

    where :math:`Q` is the local stream discharge, :math:`A` is the local
    upstream drainage area,:math:`S` is the local slope, :math:`m` and
    :math:`n` are the discharge and slope exponent parameters, :math:`K` is the
    erodibility by water, :math:`V` is effective sediment settling velocity,
    :math:`Q_s` is volumetric sediment flux, :math:`r` is a runoff rate,
    :math:`\phi` is sediment porosity, and :math:`D` is the regolith transport
    efficiency.

    Refer to
    `Barnhart et al. (2019) <https://doi.org/10.5194/gmd-12-1267-2019>`_
    Table 5 for full list of parameter symbols, names, and dimensions.

    The following at-node fields must be specified in the grid:
        - ``topographic__elevation``
    """

    _required_fields = ["topographic__elevation"]

    def __init__(self,
                 clock,
                 grid,
                 m_sp=0.5,
                 n_sp=1.0,
                 water_erodibility=0.0001,
                 regolith_transport_parameter=0.1,
                 settling_velocity=0.001,
                 sediment_porosity=0.3,
                 fraction_fines=0.5,
                 solver="basic",
                 **kwargs):
        """
        Parameters
        ----------
        clock : terrainbento Clock instance
        grid : landlab model grid instance
            The grid must have all required fields.
        m_sp : float, optional
            Drainage area exponent (:math:`m`). Default is 0.5.
        n_sp : float, optional
            Slope exponent (:math:`n`). Default is 1.0.
        water_erodibility : float, optional
            Water erodibility (:math:`K`). Default is 0.0001.
        regolith_transport_parameter : float, optional
            Regolith transport efficiency (:math:`D`). Default is 0.1.
        settling_velocity : float, optional
            Settling velocity of entrained sediment (:math:`V`). Default
            is 0.001.
        sediment_porosity : float, optional
            Sediment porosity (:math:`\phi`). Default is 0.3.
        fraction_fines : float, optional
            Fraction of fine sediment that is permanently detached
            (:math:`F_f`). Default is 0.5.
        solver : str, optional
            Solver option to pass to the Landlab
            `ErosionDeposition <https://landlab.readthedocs.io/en/latest/landlab.components.erosion_deposition.html>`__
            component. Default is "basic".
        **kwargs :
            Keyword arguments to pass to :py:class:`ErosionModel`. Importantly
            these arguments specify the precipitator and the runoff generator
            that control the generation of surface water discharge (:math:`Q`).

        Returns
        -------
        BasicHy : model object

        Examples
        --------
        This is a minimal example to demonstrate how to construct an instance
        of model **BasicHy**. For more detailed examples, including
        steady-state test examples, see the terrainbento tutorials.

        To begin, import the model class.

        >>> from landlab import RasterModelGrid
        >>> from landlab.values import random
        >>> from terrainbento import Clock, BasicHy
        >>> clock = Clock(start=0, stop=100, step=1)
        >>> grid = RasterModelGrid((5,5))
        >>> _ = random(grid, "topographic__elevation")

        Construct the model.

        >>> model = BasicHy(clock, grid)

        Running the model with ``model.run()`` would create output, so here we
        will just run it one step.

        >>> model.run_one_step(1.)
        >>> model.model_time
        1.0

        """

        # Call ErosionModel"s init
        super(BasicHy, self).__init__(clock, grid, **kwargs)

        # verify correct fields are present.
        self._verify_fields(self._required_fields)

        # Get Parameters
        self.m = m_sp
        self.n = n_sp
        self.K = water_erodibility

        # Instantiate a Space component
        self.eroder = ErosionDeposition(
            self.grid,
            K=self.K,
            phi=sediment_porosity,
            F_f=fraction_fines,
            v_s=settling_velocity,
            m_sp=self.m,
            n_sp=self.n,
            discharge_field="surface_water__discharge",
            solver=solver,
        )

        # Instantiate a LinearDiffuser component
        self.diffuser = LinearDiffuser(
            self.grid, linear_diffusivity=regolith_transport_parameter)

    def run_one_step(self, step):
        """Advance model **BasicHy** for one time-step of duration step.

        The **run_one_step** method does the following:

        1. Creates rain and runoff, then directs and accumulates flow.

        2. Assesses the location, if any, of flooded nodes where erosion should
           not occur.

        3. Assesses if a :py:mod:`PrecipChanger` is an active boundary handler
           and if so, uses it to modify the erodibility by water.

        4. Calculates erosion and deposition by water.

        5. Calculates topographic change by linear diffusion.

        6. Finalizes the step using the :py:mod:`ErosionModel` base class
           function **finalize__run_one_step**. This function updates all
           boundary handlers handlers by ``step`` and increments model time by
           ``step``.

        Parameters
        ----------
        step : float
            Increment of time for which the model is run.
        """
        # create and move water
        self.create_and_move_water(step)

        # Get IDs of flooded nodes, if any
        if self.flow_accumulator.depression_finder is None:
            flooded = []
        else:
            flooded = np.where(
                self.flow_accumulator.depression_finder.flood_status == 3)[0]

        # Do some erosion (but not on the flooded nodes)
        # (if we're varying K through time, update that first)
        if "PrecipChanger" in self.boundary_handlers:
            self.eroder.K = (self.K * self.boundary_handlers["PrecipChanger"].
                             get_erodibility_adjustment_factor())
        self.eroder.run_one_step(
            step,
            flooded_nodes=flooded,
            dynamic_dt=True,
            flow_director=self.flow_accumulator.flow_director,
        )

        # Do some soil creep
        self.diffuser.run_one_step(step)

        # Finalize the run_one_step_method
        self.finalize__run_one_step(step)
Beispiel #22
0
def test_steady_state_with_basic_solver_option():
    """
    Test that model matches the transport-limited analytical solution
    for slope/area relationship at steady state: S=((U * v_s) / (K * A^m)
    + U / (K * A^m))^(1/n).

    Also test that model matches the analytical solution for steady-state
    sediment flux: Qs = U * A * (1 - phi).
    """

    # set up a 5x5 grid with one open outlet node and low initial elevations.
    nr = 5
    nc = 5
    mg = RasterModelGrid((nr, nc), xy_spacing=10.0)

    z = mg.add_zeros("topographic__elevation", at="node")

    mg["node"]["topographic__elevation"] += (
        mg.node_y / 100000 + mg.node_x / 100000 +
        np.random.rand(len(mg.node_y)) / 10000)
    mg.set_closed_boundaries_at_grid_edges(
        bottom_is_closed=True,
        left_is_closed=True,
        right_is_closed=True,
        top_is_closed=True,
    )
    mg.set_watershed_boundary_condition_outlet_id(
        0, mg["node"]["topographic__elevation"], -9999.0)

    # Create a D8 flow handler
    fa = FlowAccumulator(mg,
                         flow_director="D8",
                         depression_finder="DepressionFinderAndRouter")

    # Parameter values for detachment-limited test
    K = 0.01
    U = 0.0001
    dt = 1.0
    F_f = 0.0  # all sediment is considered coarse bedload
    m_sp = 0.5
    n_sp = 1.0
    v_s = 0.5

    # Instantiate the ErosionDeposition component...
    ed = ErosionDeposition(
        mg,
        K=K,
        F_f=F_f,
        v_s=v_s,
        m_sp=m_sp,
        n_sp=n_sp,
        sp_crit=0,
        solver="basic",
    )

    # ... and run it to steady state (5000x1-year timesteps).
    for _ in range(5000):
        fa.run_one_step()
        ed.run_one_step(dt=dt)
        z[mg.core_nodes] += U * dt  # m

    # compare numerical and analytical slope solutions
    num_slope = mg.at_node["topographic__steepest_slope"][mg.core_nodes]
    analytical_slope = np.power(
        ((U * v_s) /
         (K * np.power(mg.at_node["drainage_area"][mg.core_nodes], m_sp))) +
        ((U) /
         (K * np.power(mg.at_node["drainage_area"][mg.core_nodes], m_sp))),
        1.0 / n_sp,
    )

    # test for match with analytical slope-area relationship
    testing.assert_array_almost_equal(
        num_slope,
        analytical_slope,
        decimal=8,
        err_msg="E/D slope-area test failed",
        verbose=True,
    )

    # compare numerical and analytical sediment flux solutions
    num_sedflux = mg.at_node["sediment__flux"][mg.core_nodes]
    analytical_sedflux = U * mg.at_node["drainage_area"][mg.core_nodes]

    # test for match with anakytical sediment flux
    testing.assert_array_almost_equal(
        num_sedflux,
        analytical_sedflux,
        decimal=8,
        err_msg="E/D sediment flux test failed",
        verbose=True,
    )
Beispiel #23
0
class BasicHyRt(TwoLithologyErosionModel):
    r"""**BasicHyRt** model program.

    This model program combines the :py:class:`BasicRt` and :py:class:`BasicHy`
    programs by allowing for two lithologies, an "upper" layer and a "lower"
    layer, stream-power-driven sediment erosion and mass conservation. Given a
    spatially varying contact zone elevation, :math:`\eta_C(x,y))`, model
    **BasicHyRt** evolves a topographic surface described by :math:`\eta` with
    the following governing equations:

    .. math::

        \frac{\partial \eta}{\partial t} = \frac{V Q_s}{Q}
                                           - K Q^{m}S^{n}
                                           + D\nabla^2 \eta

        Q_s = \int_0^A \left((1-F_f)KQ(A)^{m}S^{n}
                             - \frac{V Q_s}{Q(A)} \right) dA

        K(\eta, \eta_C ) = w K_1 + (1 - w) K_2

        w = \frac{1}{1+\exp \left( -\frac{(\eta -\eta_C )}{W_c}\right)}

    where :math:`Q` is the local stream discharge, :math:`A` is the local
    upstream drainage area, :math:`S` is the local slope, :math:`m` and
    :math:`n` are the discharge and slope exponen parameters, :math:`W_c` is
    the contact-zone width, :math:`K_1` and :math:`K_2` are the erodabilities
    of the upper and lower lithologies, and :math:`D` is the regolith transport
    parameter. :math:`Q_s` is the volumetric sediment discharge, and
    :math:`V` is the effective settling velocity of the sediment. :math:`w` is
    a weight used to calculate the effective erodibility
    :math:`K(\eta, \eta_C)` based on the depth to the contact zone and the
    width of the contact zone.

    The weight :math:`w` promotes smoothness in the solution of erodibility at
    a given point. When the surface elevation is at the contact elevation, the
    erodibility is the average of :math:`K_1` and :math:`K_2`; above and below
    the contact, the erodibility approaches the value of :math:`K_1` and
    :math:`K_2` at a rate related to the contact zone width. Thus, to make a
    very sharp transition, use a small value for the contact zone width.

    Refer to
    `Barnhart et al. (2019) <https://doi.org/10.5194/gmd-12-1267-2019>`_
    Table 5 for full list of parameter symbols, names, and dimensions.

    The following at-node fields must be specified in the grid:
        - ``topographic__elevation``
        - ``lithology_contact__elevation``
    """

    _required_fields = [
        "topographic__elevation",
        "lithology_contact__elevation",
    ]

    def __init__(self,
                 clock,
                 grid,
                 solver="basic",
                 settling_velocity=0.001,
                 fraction_fines=0.5,
                 **kwargs):
        """
        Parameters
        ----------
        clock : terrainbento Clock instance
        grid : landlab model grid instance
            The grid must have all required fields.
        m_sp : float, optional
            Drainage area exponent (:math:`m`). Default is 0.5.
        n_sp : float, optional
            Slope exponent (:math:`n`). Default is 1.0.
        water_erodibility_upper : float, optional
            Water erodibility of the upper layer (:math:`K_{1}`). Default is
            0.001.
        water_erodibility_lower : float, optional
            Water erodibility of the upper layer (:math:`K_{2}`). Default is
            0.0001.
        contact_zone__width : float, optional
            Thickness of the contact zone (:math:`W_c`). Default is 1.
        regolith_transport_parameter : float, optional
            Regolith transport efficiency (:math:`D`). Default is 0.1.
        settling_velocity : float, optional
            Settling velocity of entrained sediment (:math:`V`). Default
            is 0.001.
        fraction_fines : float, optional
            Fraction of fine sediment that is permanently detached
            (:math:`F_f`). Default is 0.5.
        solver : str, optional
            Solver option to pass to the Landlab
            `ErosionDeposition <https://landlab.readthedocs.io/en/master/reference/components/erosion_deposition.html>`__
            component. Default is "basic".
        **kwargs :
            Keyword arguments to pass to :py:class:`TwoLithologyErosionModel`.
            Importantly these arguments specify the precipitator and the runoff
            generator that control the generation of surface water discharge
            (:math:`Q`).

        Returns
        -------
        BasicHyRt : model object

        Examples
        --------
        This is a minimal example to demonstrate how to construct an instance
        of model **BasicHyRt**. For more detailed examples, including
        steady-state test examples, see the terrainbento tutorials.

        To begin, import the model class.

        >>> from landlab import RasterModelGrid
        >>> from landlab.values import random, constant
        >>> from terrainbento import Clock, BasicHyRt
        >>> clock = Clock(start=0, stop=100, step=1)
        >>> grid = RasterModelGrid((5,5))
        >>> _ = random(grid, "topographic__elevation")
        >>> _ = constant(grid, "lithology_contact__elevation", value=-10.)

        Construct the model.

        >>> model = BasicHyRt(clock, grid)

        Running the model with ``model.run()`` would create output, so here we
        will just run it one step.

        >>> model.run_one_step(1.)
        >>> model.model_time
        1.0
        """
        # If needed, issue warning on porosity
        if "sediment_porosity" in kwargs:
            msg = "sediment_porosity is no longer used by BasicHyRt."
            raise ValueError(msg)

        # Call ErosionModel"s init
        super().__init__(clock, grid, **kwargs)

        # verify correct fields are present.
        self._verify_fields(self._required_fields)

        # Save the threshold values for rock and till
        self.rock_thresh = 0.0
        self.till_thresh = 0.0

        # Set up rock-till boundary and associated grid fields.
        self._setup_rock_and_till_with_threshold()

        # Instantiate an ErosionDeposition ("hybrid") component
        self.eroder = ErosionDeposition(
            self.grid,
            K="substrate__erodibility",
            F_f=fraction_fines,
            v_s=settling_velocity,
            m_sp=self.m,
            n_sp=self.n,
            discharge_field="surface_water__discharge",
            solver=solver,
        )

        # Instantiate a LinearDiffuser component
        self.diffuser = LinearDiffuser(
            self.grid, linear_diffusivity=self.regolith_transport_parameter)

    def run_one_step(self, step):
        """Advance model **BasicHyRt** for one time-step of duration step.

        The **run_one_step** method does the following:

        1. Creates rain and runoff, then directs and accumulates flow.

        2. Assesses the location, if any, of flooded nodes where erosion should
           not occur.

        3. Assesses if a :py:mod:`PrecipChanger` is an active boundary handler
           and if so, uses it to modify the erodibility by water.

        4. Updates the spatially variable erodibility value based on the
           relative distance between the topographic surface and the lithology
           contact.

        5. Calculates detachment-limited erosion by water.

        6. Calculates topographic change by linear diffusion.

        7. Finalizes the step using the :py:mod:`ErosionModel` base class
           function **finalize__run_one_step**. This function updates all
           boundary handlers handlers by ``step`` and increments model time by
           ``step``.

        Parameters
        ----------
        step : float
            Increment of time for which the model is run.
        """
        # create and move water
        self.create_and_move_water(step)

        # Update the erodibility and threshold field
        self._update_erodibility_and_threshold_fields()

        # Do some erosion (but not on the flooded nodes)
        self.eroder.run_one_step(step)

        # Do some soil creep
        self.diffuser.run_one_step(step)

        # Finalize the run_one_step_method
        self.finalize__run_one_step(step)
Beispiel #24
0
class BasicHyVs(ErosionModel):
    r"""**BasicHyVs** model program.

    This model program combines :py:class:`BasicHy` and :py:class:`BasicVs` to
    evolves a topographic surface described by :math:`\eta` with the following
    governing equations:

    .. math::

        \frac{\partial \eta}{\partial t} = -\left(KQ(A)^{m}S^{n}
              - \omega_c\left(1-e^{-KQ(A)^{m}S^{n}/\omega_c}\right)\right)
              + V\frac{Q_s}{Q(A)}
              + D\nabla^2 \eta

        Q_s = \int_0^A \left(KQ(A)^{m}S^{n} - \frac{V Q_s}{Q(A)} \right) dA

        Q = A \exp \left( -\frac{-\alpha S}{A}\right)

        \alpha = \frac{K_{sat} H dx}{R_m}

    where :math:`Q` is the local stream discharge, :math:`S` is the local
    slope, :math:`m` and :math:`n` are the discharge and slope exponent
    parameters, :math:`K` is the erodibility by water, :math:`\omega_c` is the
    critical stream power needed for erosion to occur, :math:`V` is effective
    sediment settling velocity, :math:`Q_s` is volumetric sediment flux,
    and :math:`D` is the regolith transport
    efficiency.

    :math:`\alpha` is the saturation area scale used for transforming area into
    effective area :math:`A_{eff}` (used as discharge). It is given as a
    function of the saturated hydraulic conductivity :math:`K_{sat}`, the soil
    thickness :math:`H`, the grid spacing :math:`dx`, and the recharge rate,
    :math:`R_m`.

    Refer to
    `Barnhart et al. (2019) <https://doi.org/10.5194/gmd-12-1267-2019>`_
    Table 5 for full list of parameter symbols, names, and dimensions.

    The following at-node fields must be specified in the grid:
        - ``topographic__elevation``
        - ``soil__depth``
    """

    _required_fields = ["topographic__elevation", "soil__depth"]

    def __init__(self,
                 clock,
                 grid,
                 m_sp=0.5,
                 n_sp=1.0,
                 water_erodibility=0.0001,
                 regolith_transport_parameter=0.1,
                 settling_velocity=0.001,
                 fraction_fines=0.5,
                 hydraulic_conductivity=0.1,
                 solver="basic",
                 **kwargs):
        """
        Parameters
        ----------
        clock : terrainbento Clock instance
        grid : landlab model grid instance
            The grid must have all required fields.
        m_sp : float, optional
            Drainage area exponent (:math:`m`). Default is 0.5.
        n_sp : float, optional
            Slope exponent (:math:`n`). Default is 1.0.
        water_erodibility : float, optional
            Water erodibility (:math:`K`). Default is 0.0001.
        regolith_transport_parameter : float, optional
            Regolith transport efficiency (:math:`D`). Default is 0.1.
        settling_velocity : float, optional
            Settling velocity of entrained sediment (:math:`V`). Default
            is 0.001.
        fraction_fines : float, optional
            Fraction of fine sediment that is permanently detached
            (:math:`F_f`). Default is 0.5.
        solver : str, optional
            Solver option to pass to the Landlab
            `ErosionDeposition <https://landlab.readthedocs.io/en/master/reference/components/erosion_deposition.html>`__
            component. Default is "basic".
        hydraulic_conductivity : float, optional
            Hydraulic conductivity (:math:`K_{sat}`). Default is 0.1.
        **kwargs :
            Keyword arguments to pass to :py:class:`ErosionModel`. Importantly
            these arguments specify the precipitator and the runoff generator
            that control the generation of surface water discharge (:math:`Q`).

        Returns
        -------
        BasicHyVs : model object

        Examples
        --------
        This is a minimal example to demonstrate how to construct an instance
        of model **BasicHy**. For more detailed examples, including
        steady-state test examples, see the terrainbento tutorials.

        To begin, import the model class.

        >>> from landlab import RasterModelGrid
        >>> from landlab.values import random
        >>> from terrainbento import Clock, BasicHyVs
        >>> clock = Clock(start=0, stop=100, step=1)
        >>> grid = RasterModelGrid((5,5))
        >>> _ = random(grid, "topographic__elevation")
        >>> _ = random(grid, "soil__depth")

        Construct the model.

        >>> model = BasicHyVs(clock, grid)

        Running the model with ``model.run()`` would create output, so here we
        will just run it one step.

        >>> model.run_one_step(1.)
        >>> model.model_time
        1.0

        """
        # If needed, issue warning on porosity
        if "sediment_porosity" in kwargs:
            msg = "sediment_porosity is no longer used by BasicHyVs."
            raise ValueError(msg)

        # Call ErosionModel"s init
        super().__init__(clock, grid, **kwargs)

        # ensure Precipitator and RunoffGenerator are vanilla
        self._ensure_precip_runoff_are_vanilla(vsa_precip=True)

        # verify correct fields are present.
        self._verify_fields(self._required_fields)

        self.m = m_sp
        self.n = n_sp
        self.K = water_erodibility

        # Get the effective-area parameter
        self._Kdx = hydraulic_conductivity * self.grid.dx

        # Instantiate a SPACE component
        self.eroder = ErosionDeposition(
            self.grid,
            K=self.K,
            F_f=fraction_fines,
            v_s=settling_velocity,
            m_sp=self.m,
            n_sp=self.n,
            discharge_field="surface_water__discharge",
            solver=solver,
        )

        # Instantiate a LinearDiffuser component
        self.diffuser = LinearDiffuser(
            self.grid, linear_diffusivity=regolith_transport_parameter)

    def _calc_effective_drainage_area(self):
        """Calculate and store effective drainage area."""

        area = self.grid.at_node["drainage_area"]
        slope = self.grid.at_node["topographic__steepest_slope"]
        cores = self.grid.core_nodes

        sat_param = (self._Kdx * self.grid.at_node["soil__depth"] /
                     self.grid.at_node["rainfall__flux"])

        eff_area = area[cores] * (np.exp(
            -sat_param[cores] * slope[cores] / area[cores]))

        self.grid.at_node["surface_water__discharge"][cores] = eff_area

    def run_one_step(self, step):
        """Advance model **BasicVs** for one time-step of duration step.

        The **run_one_step** method does the following:

        1. Directs flow, accumulates drainage area, and calculates effective
           drainage area.

        2. Assesses the location, if any, of flooded nodes where erosion should
           not occur.

        3. Assesses if a :py:mod:`PrecipChanger` is an active boundary handler
           and if so, uses it to modify the erodibility by water.

        4. Calculates detachment-limited erosion by water.

        5. Calculates topographic change by linear diffusion.

        6. Finalizes the step using the :py:mod:`ErosionModel` base class
           function **finalize__run_one_step**. This function updates all
           boundary handlers handlers by ``step`` and increments model time by
           ``step``.

        Parameters
        ----------
        step : float
            Increment of time for which the model is run.
        """
        # create and move water
        self.create_and_move_water(step)

        # Update effective runoff ratio
        self._calc_effective_drainage_area()

        # Do some erosion
        # (if we're varying K through time, update that first)
        if "PrecipChanger" in self.boundary_handlers:
            self.eroder.K = (self.K * self.boundary_handlers["PrecipChanger"].
                             get_erodibility_adjustment_factor())
        self.eroder.run_one_step(step)

        # Do some soil creep
        self.diffuser.run_one_step(step)

        # Finalize the run_one_step_method
        self.finalize__run_one_step(step)
Beispiel #25
0
def test_adaptive_solver_without_depression_handling():

    grid = RasterModelGrid((3, 5), xy_spacing=10.0)
    grid.set_closed_boundaries_at_grid_edges(False, True, False, True)
    z = grid.add_zeros("node", "topographic__elevation")
    z[grid.x_of_node < 15.0] = 10.0

    fa = FlowAccumulator(grid)
    ed = ErosionDeposition(grid, solver="adaptive")

    fa.run_one_step()
    ed.run_one_step(1.0)

    assert_array_equal(
        ed._q,
        [
            0.0,
            0.0,
            0.0,
            0.0,
            0.0,
            0.0,
            100.0,
            200.0,
            100.0,
            0.0,
            0.0,
            0.0,
            0.0,
            0.0,
            0.0,
        ],
    )
    assert_array_equal(
        ed._erosion_term,
        [
            0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.02, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
            0.0, 0.0
        ],
    )
    assert_array_equal(
        ed._depo_rate,
        [
            0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.01, 0.01, 0.0, 0.0, 0.0, 0.0, 0.0,
            0.0, 0.0
        ],
    )
    assert_array_equal(
        ed._qs,
        [
            0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
            0.0, 0.0
        ],
    )
    assert_array_equal(
        ed._qs_in,
        [
            0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0,
            0.0, 0.0
        ],
    )
class BasicHyRt(ErosionModel):
    """
    A BasicHyRt computes erosion using linear diffusion, hybrid alluvium
    stream erosion, Q~A, and two lithologies: rock and till.
    """
    def __init__(self,
                 input_file=None,
                 params=None,
                 BaselevelHandlerClass=None):
        """Initialize the BasicHyRt."""

        # Call ErosionModel's init
        super(BasicHyRt,
              self).__init__(input_file=input_file,
                             params=params,
                             BaselevelHandlerClass=BaselevelHandlerClass)

        contact_zone__width = (self._length_factor *
                               self.params['contact_zone__width'])  # L
        self.K_rock_sp = self.get_parameter_from_exponent('K_rock_sp')
        self.K_till_sp = self.get_parameter_from_exponent('K_till_sp')

        linear_diffusivity = (
            (self._length_factor**2) *
            self.get_parameter_from_exponent('linear_diffusivity'))

        v_sc = self.get_parameter_from_exponent(
            'v_sc')  # normalized settling velocity. Unitless.

        # Set up rock-till
        self.setup_rock_and_till(self.params['rock_till_file__name'],
                                 rock_erody_br=self.K_rock_sp,
                                 till_erody_br=self.K_till_sp,
                                 rock_thresh_br=0.0,
                                 till_thresh_br=0.0,
                                 contact_width=contact_zone__width)

        # Instantiate a FlowAccumulator with DepressionFinderAndRouter using D8 method
        self.flow_router = FlowAccumulator(
            self.grid,
            flow_director='D8',
            depression_finder=DepressionFinderAndRouter)

        # Handle solver option
        try:
            solver = self.params['solver']
        except:
            solver = 'original'

        # Instantiate an ErosionDeposition ("hybrid") component
        self.eroder = ErosionDeposition(self.grid,
                                        K='K_br',
                                        F_f=self.params['F_f'],
                                        phi=self.params['phi'],
                                        v_s=v_sc,
                                        m_sp=self.params['m_sp'],
                                        n_sp=self.params['n_sp'],
                                        method='simple_stream_power',
                                        discharge_method='drainage_area',
                                        area_field='drainage_area',
                                        solver=solver)

        # Instantiate a LinearDiffuser component
        self.diffuser = LinearDiffuser(self.grid,
                                       linear_diffusivity=linear_diffusivity)

    def setup_rock_and_till(self,
                            file_name='file',
                            rock_erody_br=1,
                            till_erody_br=1,
                            rock_thresh_br=0,
                            till_thresh_br=0,
                            contact_width=1):
        """Set up lithology handling for two layers with different erodibility.

        Parameters
        ----------
        file_name : string
            Name of arc-ascii format file containing elevation of contact
            position at each grid node (or NODATA)
        rock_erody : float
            Water erosion coefficient for bedrock
        till_erody : float
            Water erosion coefficient for till
        rock_thresh : float
            Water erosion threshold for bedrock
        till_thresh : float
            Water erosion threshold for till
        contact_width : float [L]
            Characteristic width of the interface zone between rock and till

        Read elevation of rock-till contact from an esri-ascii format file
        containing the basal elevation value at each node, create a field for
        erodibility.
        """
        from landlab.io import read_esri_ascii

        # Read input data on rock-till contact elevation
        read_esri_ascii(file_name,
                        grid=self.grid,
                        name='rock_till_contact__elevation',
                        halo=1)

        # Get a reference to the rock-till field
        self.rock_till_contact = self.grid.at_node[
            'rock_till_contact__elevation']

        # Create field for rock erodability
        if 'K_br' in self.grid.at_node:
            self.erody_br = self.grid.at_node['K_br']
        else:
            self.erody_br = self.grid.add_ones('node', 'K_br')
            self.erody_br[:] = rock_erody_br

        # field for rock threshold values
        if 'sp_crit_br' in self.grid.at_node:
            self.threshold_br = self.grid.at_node['sp_crit_br']
        else:
            self.threshold_br = self.grid.add_ones('node', 'sp_crit_br')
            self.threshold_br[:] = rock_thresh_br

        # Create array for erodibility weighting function for BEDROCK
        self.erody_wt_br = np.zeros(self.grid.number_of_nodes)

        # Read the erodibility value of rock and till
        self.rock_erody_br = rock_erody_br
        self.till_erody_br = till_erody_br

        # Read the threshold values for rock and till
        self.rock_thresh_br = rock_thresh_br
        self.till_thresh_br = till_thresh_br

        # Read and remember the contact zone characteristic width
        self.contact_width = contact_width

    def update_erodibility_and_threshold_fields(self):
        """Update erodibility and threshold at each node based on elevation
        relative to contact elevation.

        To promote smoothness in the solution, the erodibility at a given point
        (x,y) is set as follows:

            1. Take the difference between elevation, z(x,y), and contact
               elevation, b(x,y): D(x,y) = z(x,y) - b(x,y). This number could
               be positive (if land surface is above the contact), negative
               (if we're well within the rock), or zero (meaning the rock-till
               contact is right at the surface).
            2. Define a smoothing function as:
                $F(D) = 1 / (1 + exp(-D/D*))$
               This sigmoidal function has the property that F(0) = 0.5,
               F(D >> D*) = 1, and F(-D << -D*) = 0.
                   Here, D* describes the characteristic width of the "contact
               zone", where the effective erodibility is a mixture of the two.
               If the surface is well above this contact zone, then F = 1. If
               it's well below the contact zone, then F = 0.
            3. Set the erodibility using F:
                $K = F K_till + (1-F) K_rock$
               So, as F => 1, K => K_till, and as F => 0, K => K_rock. In
               between, we have a weighted average.
            4. Threshold values are set similarly.

        Translating these symbols into variable names:

            z = self.elev
            b = self.rock_till_contact
            D* = self.contact_width
            F = self.erody_wt
            K_till = self.till_erody
            K_rock = self.rock_erody
        """

        # Update the erodibility weighting function (this is "F")
        self.erody_wt_br[self.data_nodes] = (1.0 / (1.0 + np.exp(
            -(self.z[self.data_nodes] -
              self.rock_till_contact[self.data_nodes]) / self.contact_width)))

        # (if we're varying K through time, update that first)
        if self.opt_var_precip:
            erode_factor = self.pc.get_erodibility_adjustment_factor(
                self.model_time)
            self.till_erody_br = self.K_till_sp * erode_factor
            self.rock_erody_br = self.K_rock_sp * erode_factor

        # Calculate the effective BEDROCK erodibilities using weighted averaging
        self.erody_br[:] = (self.erody_wt_br * self.till_erody_br +
                            (1.0 - self.erody_wt_br) * self.rock_erody_br)

        # Calculate the effective BEDROCK thresholds using weighted averaging
        self.threshold_br[:] = (self.erody_wt_br * self.till_thresh_br +
                                (1.0 - self.erody_wt_br) * self.rock_thresh_br)

    def run_one_step(self, dt):
        """
        Advance model for one time-step of duration dt.
        """
        # Route flow
        self.flow_router.run_one_step()

        # Get IDs of flooded nodes, if any
        flooded = np.where(
            self.flow_router.depression_finder.flood_status == 3)[0]

        # Update the erodibility and threshold field
        self.update_erodibility_and_threshold_fields()

        # Do some erosion (but not on the flooded nodes)
        self.eroder.run_one_step(dt, flooded_nodes=flooded)

        # Do some soil creep
        self.diffuser.run_one_step(dt)

        # calculate model time
        self.model_time += dt

        # Lower outlet
        self.update_outlet(dt)

        # Check walltime
        self.check_walltime()
Beispiel #27
0
def test_with_depression_handling():

    grid = RasterModelGrid((3, 5), xy_spacing=10.0)
    grid.set_closed_boundaries_at_grid_edges(False, True, False, True)
    z = grid.add_zeros("node", "topographic__elevation")
    z[grid.x_of_node < 15.0] = 10.0

    fa = FlowAccumulator(grid,
                         routing="D4",
                         depression_finder="DepressionFinderAndRouter")
    ed = ErosionDeposition(grid)

    fa.run_one_step()
    ed.run_one_step(1.0)

    assert_array_equal(
        ed._q,
        [
            0.0,
            0.0,
            0.0,
            0.0,
            0.0,
            0.0,
            100.0,
            200.0,
            300.0,
            300.0,
            0.0,
            0.0,
            0.0,
            0.0,
            0.0,
        ],
    )
    assert_array_equal(
        ed._erosion_term,
        [
            0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.02, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
            0.0, 0.0
        ],
    )
    assert_array_almost_equal(
        ed._depo_rate,
        [
            0.0,
            0.0,
            0.0,
            0.0,
            0.0,
            0.0,
            0.01,
            0.00333333,
            0.00166667,
            0.0,
            0.0,
            0.0,
            0.0,
            0.0,
            0.0,
        ],
    )
    assert_array_almost_equal(
        ed._qs,
        [
            0.0,
            0.0,
            0.0,
            0.0,
            0.0,
            0.0,
            1.0,
            0.66666667,
            0.5,
            0.0,
            0.0,
            0.0,
            0.0,
            0.0,
            0.0,
        ],
    )
    assert_array_almost_equal(
        ed._qs_in,
        [
            0.0,
            0.0,
            0.0,
            0.0,
            0.0,
            0.0,
            0.0,
            1.0,
            0.66666667,
            0.5,
            0.0,
            0.0,
            0.0,
            0.0,
            0.0,
        ],
    )