Beispiel #1
0
    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 __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)
Beispiel #3
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 #4
0
def test_bad_solver_name():
    """
    Test that any solver name besides 'basic' and 'adaptive' raises an error.
    """

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

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

    mg['node']['topographic__elevation'] += mg.node_y / 10000 \
        + mg.node_x / 10000 \
        + 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.)

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

    #try to instantiate ErodionDeposition using a wrong solver name
    with pytest.raises(ValueError):
        ErosionDeposition(mg, K=0.01, phi=0.0, v_s=0.001, m_sp=0.5, n_sp=1.0,
                          sp_crit=0, F_f=0.0, solver='something_else')
Beispiel #5
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 #6
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 #7
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))
Beispiel #8
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 #9
0
def test_extra_kwd_error_raised():
    mg = RasterModelGrid((10, 10))
    z = mg.add_zeros("topographic__elevation", at="node")
    z += mg.x_of_node + mg.y_of_node
    fa = FlowAccumulator(mg)
    fa.run_one_step()

    with pytest.raises(ValueError):
        ErosionDeposition(mg, spam=0)
Beispiel #10
0
def test_route_to_multiple_error_raised():
    mg = RasterModelGrid((10, 10))
    z = mg.add_zeros("topographic__elevation", at="node")
    z += mg.x_of_node + mg.y_of_node
    fa = FlowAccumulator(mg, flow_director="MFD")
    fa.run_one_step()

    with pytest.raises(NotImplementedError):
        ErosionDeposition(mg, K=0.01, v_s=0.001, m_sp=0.5, n_sp=1.0, sp_crit=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
    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_q_as_array():
    """
    Test that passing in water discharge as an array results in self.q
    holding correct values
    """

    # 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)

    mg.add_zeros("topographic__elevation", at="node")
    q = np.zeros(mg.number_of_nodes)
    q[:] += 1.0  # add 1.0 m3/yr of water

    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
    FlowAccumulator(
        mg, flow_director="D8", depression_finder="DepressionFinderAndRouter"
    )

    # Instantiate the ErosionDeposition component...
    ed = ErosionDeposition(
        mg,
        K=0.01,
        F_f=0.0,
        phi=0.0,
        v_s=0.001,
        m_sp=0.5,
        n_sp=1.0,
        sp_crit=0.0,
        discharge_field=q,
        solver="basic",
    )

    # ensure that ed._q is everywhere equal to 1.0 m3/yr.
    testing.assert_array_equal(
        np.ones(mg.number_of_nodes),
        ed._q,
        err_msg="E/D discharge array test failed",
        verbose=True,
    )
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))
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 #15
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_sediment__flux_already_created():
    """
    Test that an existing sediment flux grid field is not changed by
    instantiating ErosionDeposition.
    """

    # 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)

    mg.add_zeros("topographic__elevation", at="node")
    qs = mg.add_zeros("sediment__flux", at="node")
    qs[:] += 1.0  # add 1.0 m3/yr of flux

    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
    FlowAccumulator(
        mg, flow_director="D8", depression_finder="DepressionFinderAndRouter"
    )

    # Instantiate the ErosionDeposition component...
    ed = ErosionDeposition(
        mg,
        K=0.01,
        F_f=0.0,
        phi=0.0,
        v_s=0.001,
        m_sp=0.5,
        n_sp=1.0,
        sp_crit=0.0,
        solver="basic",
    )

    # ensure that 'sediment__flux' field is everywhere equal to 1.0 m3/yr.
    testing.assert_array_equal(
        np.ones(mg.number_of_nodes),
        ed._qs,
        err_msg="E/D sediment flux field test failed",
        verbose=True,
    )
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 #19
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 #21
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)
Beispiel #22
0
def test_q_as_field():
    """
    Test that passing in water discharge as a grid field results in self.q
    holding correct values
    """

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

    mg.add_zeros('node', 'topographic__elevation')
    q = mg.add_zeros('node', 'user_imposed_discharge')
    q[:] += 1.0  #add 1.0 m3/yr of water

    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.)

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

    # Instantiate the ErosionDeposition component...
    ed = ErosionDeposition(mg,
                           K=0.01,
                           F_f=0.0,
                           phi=0.0,
                           v_s=0.001,
                           m_sp=0.5,
                           n_sp=1.0,
                           sp_crit=0.0,
                           discharge_field='user_imposed_discharge',
                           solver='basic')

    #ensure that ed.q is everywhere equal to 1.0 m3/yr.
    testing.assert_array_equal(np.ones(mg.number_of_nodes),
                               ed.q,
                               err_msg='E/D discharge field test failed',
                               verbose=True)
def test_Ff_too_high_vals():
    """
    Test that instantiating ErosionDeposition with a F_f value > 1 throws a
    ValueError.
    """

    # 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)

    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
    FlowAccumulator(
        mg, flow_director="D8", depression_finder="DepressionFinderAndRouter"
    )

    # Instantiate the ErosionDeposition component...
    with pytest.raises(ValueError):
        ErosionDeposition(
            mg,
            K=0.01,
            F_f=2.0,
            phi=0.5,
            v_s=0.001,
            m_sp=0.5,
            n_sp=1.0,
            sp_crit=0.0,
            solver="basic",
        )
Beispiel #24
0
def test_Ff_bad_vals():
    """
    Test that instantiating ErosionDeposition with a F_f value > 1 throws a 
    ValueError.
    """

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

    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.)

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

    # Instantiate the ErosionDeposition component...
    with pytest.raises(ValueError):
        ErosionDeposition(mg,
                          K=0.01,
                          F_f=2.0,
                          phi=0.5,
                          v_s=0.001,
                          m_sp=0.5,
                          n_sp=1.0,
                          sp_crit=0.0,
                          solver='basic')
Beispiel #25
0
    def __init__(self,
                 input_file=None,
                 params=None,
                 BaselevelHandlerClass=None):
        """Initialize the BasicHySt."""

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

        # Get Parameters:
        K = ((self._length_factor**0.5)  # K_stochastic [=] L^(1/2)  T^-(1/2)
             * self.get_parameter_from_exponent('K_stochastic_sp'))

        linear_diffusivity = (
            (self._length_factor**2) *
            self.get_parameter_from_exponent('linear_diffusivity'))  # L^2/T

        v_s = (self._length_factor) * self.get_parameter_from_exponent(
            'v_s')  # has units length per time

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

        #set methods and fields.
        method = 'simple_stream_power'
        discharge_method = 'discharge_field'
        area_field = None
        discharge_field = 'surface_water__discharge'

        # instantiate rain generator
        self.instantiate_rain_generator()

        # Add a field for discharge
        if 'surface_water__discharge' not in self.grid.at_node:
            self.grid.add_zeros('node', 'surface_water__discharge')
        self.discharge = self.grid.at_node['surface_water__discharge']

        # Get the infiltration-capacity parameter
        infiltration_capacity = (self._length_factor *
                                 self.params['infiltration_capacity'])  # L/T
        self.infilt = infiltration_capacity

        # Run flow routing and lake filler
        self.flow_router.run_one_step()

        # Keep a reference to drainage area
        self.area = self.grid.at_node['drainage_area']

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

        # Instantiate an ErosionDeposition component
        self.eroder = ErosionDeposition(self.grid,
                                        K=K,
                                        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'],
                                        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)
Beispiel #26
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,
        ],
    )
Beispiel #27
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 #28
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 #29
0
    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)
Beispiel #30
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()
    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)
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 #33
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)
    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)