def test_assertion_error():
    """Test that the correct assertion error will be raised."""

    mg = RasterModelGrid(10, 10)
    z = mg.add_zeros('topographic__elevation', at='node')
    z += 200 + mg.x_of_node + mg.y_of_node + np.random.randn(mg.size('node'))

    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, z, -9999)
    fa = FlowAccumulator(mg, flow_director='D8', depression_finder=DepressionFinderAndRouter)
    sp = FastscapeEroder(mg, K_sp=.0001, m_sp=.5, n_sp=1)
    ld = LinearDiffuser(mg, linear_diffusivity=0.0001)

    dt = 100
    for i in range(200):
        fa.run_one_step()
        flooded = np.where(fa.depression_finder.flood_status==3)[0]
        sp.run_one_step(dt=dt,  flooded_nodes=flooded)
        ld.run_one_step(dt=dt)
        mg.at_node['topographic__elevation'][0] -= 0.001 # Uplift


    assert_raises(AssertionError,
                  analyze_channel_network_and_plot,
                  mg,
                  threshold = 100,
                  starting_nodes = [0],
                  number_of_channels=2)
Beispiel #2
0
def test_asking_for_too_many_watersheds():
    mg = RasterModelGrid((10, 10))
    z = mg.add_zeros("topographic__elevation", at="node")
    z += 200 + mg.x_of_node + mg.y_of_node
    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, z, -9999)
    fa = FlowAccumulator(mg, flow_director="D8")
    sp = FastscapeEroder(mg, K_sp=0.0001, m_sp=0.5, n_sp=1)

    dt = 100
    for i in range(200):
        fa.run_one_step()
        sp.run_one_step(dt=dt)
        mg.at_node["topographic__elevation"][0] -= 0.001

    with pytest.raises(ValueError):
        ChannelProfiler(mg, number_of_watersheds=3)

    with pytest.raises(ValueError):
        ChannelProfiler(mg,
                        number_of_watersheds=None,
                        minimum_outlet_threshold=200)
Beispiel #3
0
def test_assertion_error():
    """Test that the correct assertion error will be raised."""
    mg = RasterModelGrid((10, 10))
    z = mg.add_zeros("topographic__elevation", at="node")
    z += 200 + mg.x_of_node + mg.y_of_node + np.random.randn(mg.size("node"))

    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, z, -9999)
    fa = FlowAccumulator(mg,
                         flow_director="D8",
                         depression_finder=DepressionFinderAndRouter)
    sp = FastscapeEroder(mg,
                         K_sp=0.0001,
                         m_sp=0.5,
                         n_sp=1,
                         erode_flooded_nodes=True)
    ld = LinearDiffuser(mg, linear_diffusivity=0.0001)

    dt = 100
    for i in range(200):
        fa.run_one_step()
        sp.run_one_step(dt=dt)
        ld.run_one_step(dt=dt)
        mg.at_node["topographic__elevation"][0] -= 0.001  # Uplift

    with pytest.raises(ValueError):
        ChannelProfiler(mg, outlet_nodes=[0], number_of_watersheds=2)
def test_mask_is_stable():
    mg = RasterModelGrid((10, 10))
    mg.add_zeros("node", "topographic__elevation")
    np.random.seed(3542)
    noise = np.random.rand(mg.size("node"))
    mg.at_node["topographic__elevation"] += noise
    fr = FlowAccumulator(mg, flow_director="D8")
    fsc = FastscapeEroder(mg, K_sp=0.01, m_sp=0.5, n_sp=1)
    for x in range(2):
        fr.run_one_step()
        fsc.run_one_step(dt=10.0)
        mg.at_node["topographic__elevation"][mg.core_nodes] += 0.01

    mask = np.zeros(len(mg.at_node["topographic__elevation"]), dtype=np.uint8)
    mask[np.where(mg.at_node["drainage_area"] > 0)] = 1

    mask0 = mask.copy()

    dd = DrainageDensity(mg, channel__mask=mask)
    mask1 = mask.copy()

    dd.calc_drainage_density()
    mask2 = mask.copy()

    assert_array_equal(mask0, mask1)
    assert_array_equal(mask0[mg.core_nodes], mask2[mg.core_nodes])
Beispiel #5
0
def test_assertion_error():
    """Test that the correct assertion error will be raised."""

    mg = RasterModelGrid(10, 10)
    z = mg.add_zeros('topographic__elevation', at='node')
    z += 200 + mg.x_of_node + mg.y_of_node + np.random.randn(mg.size('node'))

    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, z, -9999)
    fa = FlowAccumulator(mg, depression_finder=DepressionFinderAndRouter)
    sp = FastscapeEroder(mg, K_sp=.0001, m_sp=.5, n_sp=1)
    ld = LinearDiffuser(mg, linear_diffusivity=0.0001)

    dt = 100
    for i in range(200):
        fa.run_one_step()
        flooded = np.where(fa.depression_finder.flood_status == 3)[0]
        sp.run_one_step(dt=dt, flooded_nodes=flooded)
        ld.run_one_step(dt=dt)
        mg.at_node['topographic__elevation'][0] -= 0.001  # Uplift

    assert_raises(AssertionError,
                  analyze_channel_network_and_plot,
                  mg,
                  threshold=100,
                  starting_nodes=[0],
                  number_of_channels=2)
Beispiel #6
0
def test_re_calculating_nodes_and_distance():
    mg = RasterModelGrid((20, 20), xy_spacing=100)
    z = mg.add_zeros("topographic__elevation", at="node")
    z += np.random.rand(z.size)
    mg.set_closed_boundaries_at_grid_edges(
        bottom_is_closed=False,
        left_is_closed=True,
        right_is_closed=True,
        top_is_closed=True,
    )

    fa = FlowAccumulator(mg, flow_director="D8")
    sp = FastscapeEroder(mg, K_sp=0.0001, m_sp=0.5, n_sp=1)

    dt = 1000
    uplift_per_step = 0.001 * dt

    for i in range(10):
        z[mg.core_nodes] += uplift_per_step
        fa.run_one_step()
        sp.run_one_step(dt=dt)

    profiler = ChannelProfiler(mg)
    profiler.run_one_step()
    assert len(profiler.distance_along_profile) == 1  # result: 1
    profiler.run_one_step()
    # here nathan originally found result: 2, a bug!
    assert len(profiler.distance_along_profile) == 1

    # make the most complicated profile structure
    profiler = ChannelProfiler(mg,
                               main_channel_only=False,
                               number_of_watersheds=2)
    profiler.run_one_step()
    p1 = list(profiler.nodes)
    d1 = list(profiler.distance_along_profile)

    profiler.run_one_step()
    p2 = list(profiler.nodes)
    d2 = list(profiler.distance_along_profile)

    # assert that these are copies, not pointers to same thing
    assert p1 is not p2
    assert d1 is not d2

    # test that structures are the same.
    for idx_watershed in range(len(p1)):
        p1_w = p1[idx_watershed]
        p2_w = p2[idx_watershed]

        d1_w = d1[idx_watershed]
        d2_w = d2[idx_watershed]

        for idx_segment in range(len(p1_w)):
            np.testing.assert_array_equal(p1_w[idx_segment], p2_w[idx_segment])
            np.testing.assert_array_equal(d1_w[idx_segment], d2_w[idx_segment])
Beispiel #7
0
def test_route_to_multiple_error_raised_run_FastscapeEroder():
    mg = RasterModelGrid((10, 10))
    z = mg.add_zeros('node', 'topographic__elevation')
    z += mg.x_of_node + mg.y_of_node
    sp = FastscapeEroder(mg, K_sp=0.1)

    fa = FlowAccumulator(mg, flow_director='MFD')
    fa.run_one_step()

    with pytest.raises(NotImplementedError):
        sp.run_one_step(10)
def test_route_to_multiple_error_raised_run_FastscapeEroder():
    mg = RasterModelGrid((10, 10))
    z = mg.add_zeros("node", "topographic__elevation")
    z += mg.x_of_node + mg.y_of_node
    sp = FastscapeEroder(mg, K_sp=0.1)

    fa = FlowAccumulator(mg, flow_director="MFD")
    fa.run_one_step()

    with pytest.raises(NotImplementedError):
        sp.run_one_step(10)
Beispiel #9
0
def profile_example_grid():
    mg = RasterModelGrid((40, 60))
    z = mg.add_zeros("topographic__elevation", at="node")
    z += 200 + mg.x_of_node + mg.y_of_node
    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, z, -9999)
    fa = FlowAccumulator(mg, flow_director="D8")
    sp = FastscapeEroder(mg, K_sp=0.0001, m_sp=0.5, n_sp=1)

    dt = 100
    for i in range(200):
        fa.run_one_step()
        sp.run_one_step(dt=dt)
        mg.at_node["topographic__elevation"][0] -= 0.001
    return mg
Beispiel #10
0
def test_getting_all_the_way_to_the_divide(main, nshed):
    np.random.seed(42)
    mg = RasterModelGrid((10, 12))
    z = mg.add_zeros("topographic__elevation", at="node")
    z += np.random.rand(z.size)

    fa = FlowAccumulator(mg, flow_director="D8")
    sp = FastscapeEroder(mg, K_sp=0.0001, m_sp=0.5, n_sp=1)

    dt = 1000
    uplift_per_step = 0.001 * dt

    for i in range(100):
        z[mg.core_nodes] += uplift_per_step
        fa.run_one_step()
        sp.run_one_step(dt=dt)

    profiler = ChannelProfiler(
        mg,
        number_of_watersheds=nshed,
        minimum_outlet_threshold=0,
        main_channel_only=main,
        minimum_channel_threshold=0,
    )
    profiler.run_one_step()

    # assert that with minimum_channel_threshold set to zero, we get all the way to the top of the divide.
    for outlet_id in profiler._data_struct:
        seg_tuples = profiler._data_struct[outlet_id].keys()

        wshd_ids = [
            profiler._data_struct[outlet_id][seg]["ids"] for seg in seg_tuples
        ]

        nodes = np.concatenate(wshd_ids).ravel()
        da = mg.at_node["drainage_area"][nodes]

        # if "profile" is just bits of the edge, then da is 0.
        assert (mg.area_of_cell.min() in da) or (0.0 in da)
Beispiel #11
0
class BasicStreamPowerErosionModel(_ErosionModel):
    """
    A BasicStreamPowerErosionModel computes erosion using the simplest form of
    the unit stream power model.
    """
    def __init__(self, input_file=None, params=None):
        """Initialize the BasicStreamPowerErosionModel."""

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

        # Instantiate a FlowRouter and DepressionFinderAndRouter components
        self.flow_router = FlowRouter(self.grid, **self.params)
        self.lake_filler = DepressionFinderAndRouter(self.grid, **self.params)

        # Instantiate a FastscapeEroder component
        self.eroder = FastscapeEroder(self.grid,
                                      K_sp=self.params['K_sp'],
                                      m_sp=self.params['m_sp'],
                                      n_sp=self.params['n_sp'])

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

        # Route flow
        self.flow_router.run_one_step()
        self.lake_filler.map_depressions()

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

        # Do some erosion (but not on the flooded nodes)
        self.eroder.run_one_step(dt, flooded_nodes=flooded)
Beispiel #12
0
class BasicCv(ErosionModel):
    """
    A BasicCV computes erosion using linear diffusion, basic stream
    power, and Q~A.

    It also has basic climate change
    """
    def __init__(self,
                 input_file=None,
                 params=None,
                 BaselevelHandlerClass=None):
        """Initialize the BasicCv model."""
        # Call ErosionModel's init
        super(BasicCv,
              self).__init__(input_file=input_file,
                             params=params,
                             BaselevelHandlerClass=BaselevelHandlerClass)

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

        self.climate_factor = self.params['climate_factor']
        self.climate_constant_date = self.params['climate_constant_date']

        time = [0, self.climate_constant_date, self.params['run_duration']]
        K = [K_sp * self.climate_factor, K_sp, K_sp]
        self.K_through_time = interp1d(time, K)

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

        # Instantiate a FastscapeEroder component
        self.eroder = FastscapeEroder(self.grid,
                                      K_sp=K[0],
                                      m_sp=self.params['m_sp'],
                                      n_sp=self.params['n_sp'])

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

        # Update erosion based on climate
        self.eroder.K = float(self.K_through_time(self.model_time))

        # 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()
class BasicVs(ErosionModel):
    r"""**BasicVs** model program.

    This model program evolves a topographic surface, :math:`\eta`,  with the
    following governing equations:

    .. math::

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

        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, and :math:`D` is the
    regolith transport parameter.

    :math:`\alpha` is the saturation area scale used for transforming area into
    effective area :math:`A_{eff}`. 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,
                 hydraulic_conductivity=0.1,
                 **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.
        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
        -------
        BasicVs : model object

        Examples
        --------
        This is a minimal example to demonstrate how to construct an instance
        of model **BasicVs**. 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, BasicVs
        >>> clock = Clock(start=0, stop=100, step=1)
        >>> grid = RasterModelGrid((5,5))
        >>> _ = random(grid, "topographic__elevation")
        >>> _ = random(grid, "soil__depth")

        Construct the model.

        >>> model = BasicVs(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(BasicVs, self).__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)

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

        # Add a field for effective drainage area
        self.grid.at_node["surface_water__discharge"] = self.grid.add_zeros(
            "node", "effective_drainage_area")

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

        # Instantiate a FastscapeEroder component
        self.eroder = FastscapeEroder(
            self.grid,
            discharge_name="surface_water__discharge",
            K_sp=self.K,
            m_sp=self.m,
            n_sp=self.n,
        )

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

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

        # Zero out effective area in flooded nodes
        self.grid.at_node["surface_water__discharge"][flooded] = 0.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)

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

        # Finalize the run_one_step_method
        self.finalize__run_one_step(step)
Beispiel #14
0
dt = 0.1  # Timestep in Ma
cell_area = mg.dx * mg.dy

######################################
### run erosion model for one step ###
######################################

print("Running flow router")
print(datetime.datetime.now().time())
frr.run_one_step()  # flow routing
zrold = zr[mg.nodes]  # pre-incision elevations

print("Running fastscaper")
print(datetime.datetime.now().time())
spr.run_one_step(dt)  # erosion: stream power
zrnew = zr[mg.nodes]  # post-incision elevations
incise = zrold - zrnew  # incision per cell
qs = incise * cell_area  # Volume sediment produced per cell
qsflat = qs.ravel()  # flatten qs for flow routing calculation

# extract cumulative flux (q) as function of flow length.
a, q = find_drainage_area_and_discharge(
    mg.at_node['flow__upstream_node_order'],
    mg.at_node['flow__receiver_node'],
    runoff=qsflat)  # a is number of nodes

area = mg.at_node['drainage_area']
mg.add_field('node', 'flux', q, noclobber=False)
area_threshold = 25  #float(sys.argv[1]) #km2
is_drainage = area > (area_threshold * 1000000)  #km2 to m2
Beispiel #15
0
class Basic(ErosionModel):
    r"""**Basic** model program.

    This model program evolves a topographic surface, :math:`\eta`, with the
    following governing equation:

    .. math::

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

    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, 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,
        **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.
        **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
        -------
        Basic : model object

        Examples
        --------
        This is a minimal example to demonstrate how to construct an instance
        of model **Basic**. 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, Basic
        >>> clock = Clock(start=0, stop=100, step=1)
        >>> grid = RasterModelGrid((5,5))
        >>> _ = random(grid, "topographic__elevation")

        Construct the model.

        >>> model = Basic(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().__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

        self.regolith_transport_parameter = regolith_transport_parameter

        # Instantiate a FastscapeEroder component
        self.eroder = FastscapeEroder(
            self.grid,
            K_sp=self.K,
            m_sp=self.m,
            n_sp=self.n,
            discharge_field="surface_water__discharge",
            erode_flooded_nodes=self._erode_flooded_nodes,
        )

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

    def run_one_step(self, step):
        """Advance model **Basic** 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 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
        """
        # create and move water
        self.create_and_move_water(step)

        # If a PrecipChanger is being used, update the eroder"s K value.
        if "PrecipChanger" in self.boundary_handlers:
            self.eroder.K = (
                self.K
                * self.boundary_handlers[
                    "PrecipChanger"
                ].get_erodibility_adjustment_factor()
            )

        # Do some water 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 #16
0
fr = FlowRouter(mg, method='D8')
sp = FastscapeEroder(mg,
                     K_sp=0.00005,
                     m_sp=0.5,
                     n_sp=1.0,
                     threshold_sp=0.,
                     rainfall_intensity=1.)

k_d = 0.5
lin_diffuse = LinearDiffuser(mg, linear_diffusivity=k_d)

# * The calculations are all done in the time loop below.

for i in range(nt):
    fr.run_one_step()  # route flow
    sp.run_one_step(dt)  # fluvial incision
    lin_diffuse.run_one_step(dt)  # linear diffusion
    mg.at_node['topographic__elevation'][
        mg.core_nodes] += uplift_per_step  # add the uplift

    if i % 20 == 0:
        print("Completed loop ", i, " out of ", nt)

# ### Visualize the results.
#   * First we plot the topography after the time loop.
#   * Second we plot the slope-area relationship, which is often used to
#     identify hillslopes, channels, and quantify drainage density.

plt.figure(1)
imshow_grid(mg,
            'topographic__elevation',
DHDTLowLim = upliftRate - (upliftRate * 1)
DHDTHighLim = upliftRate + (upliftRate * 1)

while elapsed_time < totalT:

    #create copy of "old" topography
    z0 = mg.at_node['topographic__elevation'].copy()

    #Call the erosion routines.
    #expw.run_one_step(dt=dt)
    #dld.run_one_step(dt=dt)
    ld.run_one_step(dt=dt)
    fr.run_one_step()
    lm.map_depressions()
    floodedNodes = np.where(lm.flood_status==3)[0]
    fc.run_one_step(dt=dt, flooded_nodes = floodedNodes)
    mg.at_node['topographic__elevation'][mg.core_nodes] += uplift_per_step #add uplift
    mg.at_node['bedrock__elevation'][mg.core_nodes] += uplift_per_step #add uplift

    #look for nodes where river incises below current soil thickness
    bad_nodes = mg.at_node['topographic__elevation'] < mg.at_node['bedrock__elevation']
    #redefine bedrock to current channel elevation
    mg.at_node['bedrock__elevation'][bad_nodes] = mg.at_node['topographic__elevation'][bad_nodes]

    #calculate drainage_density
    channel_mask = mg.at_node['drainage_area'] > critArea
    dd = drainage_density.DrainageDensity(mg, channel__mask = channel_mask)
    mean_dd.append(dd.calc_drainage_density())

    #Calculate dhdt and E
    dh = (mg.at_node['topographic__elevation'] - z0)
Beispiel #18
0
    # Now that we have performed the tectonic deformation, lets apply our
    # landscape evolution and watch the landscape change as a result.

    # Uplift the landscape
    rmg['node']['topographic__elevation'][rmg.core_nodes] += uplift_rate * dt

    # set the lower boundary as fixed elevation
    rmg['node']['topographic__elevation'][rmg.node_y == 0] = 0

    # Diffuse the landscape simulating hillslope sediment transport
    lin_diffuse.run_one_step(dt)

    # Accumulate and route flow, fill any lakes, and erode under the rivers
    fr.run_one_step()  # route flow
    DepressionFinderAndRouter.map_depressions(fill)  # fill lakes
    sp.run_one_step(dt)  # fastscape stream power eroder

    ## Calculate the geomorphic metric ##

    # In the paper, we use a geomorphic metric, BR, to quantify the
    # reorientation of the channels as time goes on. The code to calculate this
    # value is below but turned off as it can slow the model. Set the
    # 'calculate_BR' variable to 'True' if you want to calculate it.

    if calculate_BR:

        aspects = rmg.calc_aspect_at_node()  # measure pixel aspect

        # classify and count the number of pixels with certain directions
        asp_0_45 = float(np.logical_and(aspects >= 0, aspects <= 45).sum())
        asp_45_135 = float(np.logical_and(aspects > 45, aspects <= 135).sum())
Beispiel #19
0
class BasicCh(_ErosionModel):
    """
    A BasicCh computes erosion using cubic diffusion, basic stream
    power, and Q~A.
    """

    def __init__(self, input_file=None, params=None,
                 BaselevelHandlerClass=None):
        """Initialize the BasicCh."""

        # Call ErosionModel's init
        super(BasicCh, 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.)*self.get_parameter_from_exponent('linear_diffusivity') # has units length^2/time

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

        # Instantiate a FastscapeEroder component
        self.eroder = FastscapeEroder(self.grid,
                                      K_sp=self.K_sp,
                                      m_sp=self.params['m_sp'],
                                      n_sp=self.params['n_sp'])

        # Instantiate a LinearDiffuser component
        self.diffuser = TaylorNonLinearDiffuser(self.grid,
                                               linear_diffusivity=linear_diffusivity,
                                               slope_crit=self.params['slope_crit'],
                                               nterms=11)

    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_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, 
                                   dynamic_dt=True,
                                   if_unstable='raise', 
                                   courant_factor=0.1)

        # calculate model time
        self.model_time += dt

        # Lower outlet
        self.update_outlet(dt)

        # Check walltime
        self.check_walltime()
Beispiel #20
0
#Create boundary conditions of the model grid (either closed or fixed-head)
for edge in (mg.nodes_at_left_edge,mg.nodes_at_right_edge, mg.nodes_at_top_edge):
    mg.status_at_node[edge] = CLOSED_BOUNDARY
for edge in (mg.nodes_at_bottom_edge):
    mg.status_at_node[edge] = FIXED_VALUE_BOUNDARY

#Initialize Fastscape
fc = FastscapeEroder(mg,
                    K_sp = ksp ,
                    m_sp = msp,
                    n_sp = nsp,
                    rainfall_intensity = 1)
fr = FlowRouter(mg)
lm = DepressionFinderAndRouter(mg)

for i in range(nSteps):
    fr.run_one_step(dt=1)
    lm.map_depressions()
    fc.run_one_step(dt=1)
    mg.at_node['topographic__elevation'][mg.core_nodes] += 0.0002

z = mg.at_node['topographic__elevation']

plt.figure()
imshow_grid(mg,z)
plt.savefig('test.png')
plt.close()

np.save('iniTopo',z)
    surface="topographic__elevation",
    fill_surface="topographic__elevation",
    redirect_flow_steepest_descent=False,
    reaccumulate_flow=False,
    track_lakes=False,
    ignore_overfill=True,
)
dfr = DepressionFinderAndRouter(grid)

ld = LinearDiffuser(grid, D)
sp = FastscapeEroder(grid, K_sp=Ksp, m_sp=0.5, n_sp=1.0, threshold_sp=E0)

for i in range(N):

    dfr._find_pits()
    if dfr._number_of_pits > 0:
        lmb.run_one_step()

    z[grid.core_nodes] += U * dt

    ld.run_one_step(dt)
    fa.run_one_step()
    sp.run_one_step(dt)

    print('completed loop %d' % i)

    if i % output_interval == 0:
        print('finished iteration %d' % i)
        filename = base_path + '%d_grid_%d.nc' % (ID, i)
        to_netcdf(grid, filename, include="at_node:topographic__elevation")
Beispiel #22
0
x, y, z = gaussian_hill_elevation(n)
z = z * 100

mg = RasterModelGrid((n, n), node_spacing)
gh_org = mg.add_field('node',
                      'topographic__elevation',
                      z,
                      units='meters',
                      copy=True,
                      clobber=False)

fr = FlowAccumulator(mg, flow_director='D8')
fse = FastscapeEroder(mg, K_sp=1e-3, m_sp=0.5, n_sp=1.)
fr.run_one_step()
fse.run_one_step(dt=1000.)

fg7 = pl.figure()
imshow_grid(
    mg,
    'topographic__elevation',
    plot_name=
    'Gaussian Hill after one time step with K_sp=1e-5, m_sp=0.5, n_sp=1 (FastScape)',
    allow_colorbar=True)

pl.figure()
pl.imshow(np.log10(np.reshape(fr.node_drainage_area, (n, n))))
cb = pl.colorbar()
cb.set_label('Log10 Flowaccumulation D8')

gh_fse = fse.grid.node_vector_to_raster(gh_org, flip_vertically=True)
Beispiel #23
0
class BasicCh(ErosionModel):
    r"""**BasicCh** model program.

    This model program evolves a topographic surface, :math:`\eta`, with the
    following governing equation:

    .. math::

        \frac{\partial \eta}{\partial t} = -KQ^{m}S^{n} + \nabla^2 q_h

        q_h = -DS \left[ 1 + \left( \frac{S}{S_c} \right)^2
              + \left( \frac{S}{S_c} \right)^4
              + ... \left( \frac{S}{S_c} \right)^{2(N-1)} \right]

    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:`D` is the
    regolith transport efficiency, and :math:`S_c` is the critical slope.
    :math:`q_h` represents the hillslope sediment flux per unit width.
    :math:`N` is the number of terms in the Taylor Series expansion.

    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,
        critical_slope=0.3,
        number_of_taylor_terms=11,
        **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.
        critical_slope : float, optional
            Critical slope (:math:`S_c`, unitless). Default is 0.3.
        number_of_taylor_terms : int, optional
            Number of terms in the Taylor Series Expansion (:math:`N`). Default
            is 11.
        **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
        -------
        BasicCh : model object

        Examples
        --------
        This is a minimal example to demonstrate how to construct an instance
        of model **BasicCh**. 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, BasicCh
        >>> clock = Clock(start=0, stop=100, step=1)
        >>> grid = RasterModelGrid((5,5))
        >>> _ = random(grid, "topographic__elevation")

        Construct the model.

        >>> model = BasicCh(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(BasicCh, self).__init__(clock, grid, **kwargs)

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

        # Get Parameters and convert units if necessary:
        self.m = m_sp
        self.n = n_sp
        self.K = water_erodibility

        regolith_transport_parameter = regolith_transport_parameter

        # Instantiate a FastscapeEroder component
        self.eroder = FastscapeEroder(
            self.grid,
            K_sp=self.K,
            m_sp=self.m,
            n_sp=self.n,
            discharge_name="surface_water__discharge",
        )

        # Instantiate a NonLinearDiffuser component
        self.diffuser = TaylorNonLinearDiffuser(
            self.grid,
            linear_diffusivity=regolith_transport_parameter,
            slope_crit=critical_slope,
            nterms=number_of_taylor_terms,
        )

    def run_one_step(self, step):
        """Advance model **BasicCh** 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 detachment-limited erosion by water.

        5. Calculates topographic change by nonlinear 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)

        # Do some soil creep
        self.diffuser.run_one_step(
            step, dynamic_dt=True, if_unstable="raise", courant_factor=0.1
        )

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

    This model program evolves a topographic surface, :math:`\eta`, with the
    following governing equation:

    .. math::

        \frac{\partial \eta}{\partial t} = -KQ^{m}S^{n} + D\nabla^2 \eta

    where :math:`K` is the fluviel erodibility coefficient, :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, and :math:`D` is
    the regolith transport parameter.

    This model also has a basic parameterization of climate change such that
    :math:`K` varies through time. Between model run onset and a time at
    which the climate becomes constant, the value of :math:`K` linearly
    changes from :math:`fK` to :math:`K`, at which point it remains at
    :math:`K` for the remainder of the modeling time period.

    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,
                 climate_factor=0.5,
                 climate_constant_date=0.0,
                 **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.
        climate_factor : float, optional.
             Default is 0.5.(:math:`f` )
        climate_constant_date : float, optional.
            Model time at which climate becomes constant (:math:`T_s`) and
            water erodibility stabilizes at a  value of :math:`K`. Default
            is 0.0.
        **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
        -------
        Basic : model object

        Examples
        --------
        This is a minimal example to demonstrate how to construct an instance
        of model ``Basic``. 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, BasicCv
        >>> clock = Clock(start=0, stop=100, step=1)
        >>> grid = RasterModelGrid((5,5))
        >>> _ = random(grid, "topographic__elevation")

        Construct the model.

        >>> model = BasicCv(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().__init__(clock, grid, **kwargs)

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

        self.m = m_sp
        self.n = n_sp
        self.climate_factor = climate_factor
        self.climate_constant_date = climate_constant_date

        time = [
            0,
            self.climate_constant_date,
            self.clock.stop + self.clock.step,
        ]
        K = [
            water_erodibility * self.climate_factor,
            water_erodibility,
            water_erodibility,
        ]
        self.K_through_time = interp1d(time, K)

        # Instantiate a FastscapeEroder component
        self.eroder = FastscapeEroder(
            self.grid,
            K_sp=K[0],
            m_sp=self.m,
            n_sp=self.n,
            discharge_field="surface_water__discharge",
            erode_flooded_nodes=self._erode_flooded_nodes,
        )

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

    def run_one_step(self, step):
        """Advance model ``Basic`` 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. Updates detachment-limited erosion based on climate.

        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 erosion based on climate
        self.eroder.K = float(self.K_through_time(self.model_time))

        # 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 #25
0
class Basic(_ErosionModel):
    """
    A Basic computes erosion using linear diffusion, basic stream
    power, and Q~A.
    """
    def __init__(self,
                 input_file=None,
                 params=None,
                 BaselevelHandlerClass=None):
        """Initialize the Basic model."""
        # Call ErosionModel's init
        super(Basic,
              self).__init__(input_file=input_file,
                             params=params,
                             BaselevelHandlerClass=BaselevelHandlerClass)

        # Get Parameters:
        K_sp = self.get_parameter_from_exponent('K_sp', raise_error=False)
        K_ss = self.get_parameter_from_exponent('K_ss', raise_error=False)
        linear_diffusivity = (
            self._length_factor**2.) * self.get_parameter_from_exponent(
                'linear_diffusivity')  # has units length^2/time

        # 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_sp and K_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_sp or K_ss  must be provided.')

        # run the sink filler, only on initiation.
        sink_filler = SinkFiller(self.grid, apply_slope=True, fill_slope=1e-3)
        sink_filler.run_one_step()

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

        # Instantiate a FastscapeEroder component
        self.eroder = FastscapeEroder(self.grid,
                                      K_sp=self.K,
                                      m_sp=self.params['m_sp'],
                                      n_sp=self.params['n_sp'])

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

        # 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()
from matplotlib import pyplot as plt

## Make a grid that is 100 by 100 with dx=dy=100. m
rmg1 = RasterModelGrid((100, 100), 100.)
## Add elevation field to the grid.
z1 = rmg1.add_ones('node', 'topographic__elevation')

## Instantiate process components
ld1 = LinearDiffuser(rmg1, linear_diffusivity=0.1)
fr1 = FlowRouter(rmg1, method='D8')
fse1 = FastscapeEroder(rmg1, K_sp=1e-5, m_sp=0.5, n_sp=1.)

## Set some variables
rock_up_rate = 1e-3  #m/yr
dt = 1000  # yr
rock_up_len = dt * rock_up_rate  # m

## Time loop where evolution happens
for i in range(500):
    z1[rmg1.core_nodes] += rock_up_len  #uplift only the core nodes
    ld1.run_one_step(dt)  #linear diffusion happens.
    fr1.run_one_step()  #flow routing happens, time step not needed
    fse1.run_one_step(dt)  #fluvial incision happens
    ## optional print statement
    print('i', i)

## Plotting the topography
plt.figure(1)
imshow_grid(rmg1, 'topographic__elevation')

#need to run for about 4000 time steps, or 4,000,000 years to reach SS
Beispiel #27
0
class FloodWorld(ActiveCellWorld):
    """
    FloodWorld class. This ActiveCellWorld implementation uses Landlab to simulate floods.

    :param world_size:
    :param grid_size:
    :param torus:
    :param agent_dynamic:
    """

    def __init__(self, world_size, grid_size, torus, agent_dynamic):
        super(FloodWorld, self).__init__(world_size, grid_size, torus, agent_dynamic)

        # World generation parameters
        self.vertical_scale = 200  # (m)

        self.inputs = {'nrows': 100, 'ncols': 100, 'dx': 0.02, 'dt': 0.5, 'total_time': 50.0, 'uplift_rate': 0.001,
                       'K_sp': 0.3, 'm_sp': 0.5, 'n_sp': 1.0, 'rock_density': 2.7, 'sed_density': 2.7,
                       'linear_diffusivity': 0.0001}

        # Flood parameters
        self.h_init = 5  # initial thin layer of water (m)
        self.n = 0.01  # roughness coefficient, (s/m^(1/3))
        self.alpha = 0.7  # time-step factor (nondimensional; from Bates et al., 2010)
        self.u = 0.4  # constant velocity (m/s, de Almeida et al., 2012)

        self.cell_update_period = 10  # (s)

        self.mg = None
        self.fr = None
        self.sp = None
        self.of = None
        self.swd = None

        self.z = None
        self.flood_threshold = 0.05  # minimum water depth detected as flood (m)

    def reset(self):
        """
        Reset function. Resets the world to its initial state.
        """

        super(FloodWorld, self).reset()

        # Terrain generation
        shape = (self.grid_size,) * 2
        size = self.grid_size * self.grid_size
        # Set initial simple topography
        z = self.vertical_scale * self.simple_topography(shape)

        # Create raster model if it does not exist
        if self.mg is None:
            self.mg = RasterModelGrid(shape, int(round(self.world_size/self.grid_size)))
            self.mg.set_closed_boundaries_at_grid_edges(True, True, True, True)
            self.z = self.mg.add_field('node', 'topographic__elevation', z)
            self.swd = self.mg.add_zeros('node', 'surface_water__depth')
        else:
            self.mg.at_node['topographic__elevation'] = z
            self.swd[:] = np.zeros(size)

        import matplotlib.pyplot as plt
        plt.figure()
        from landlab.plot import imshow_grid
        imshow_grid(self.mg, 'topographic__elevation',
                    colorbar_label='m')
        plt.draw()

        # Set evolution parameters
        uplift_rate = self.inputs['uplift_rate']
        total_t = self.inputs['total_time']
        dt = self.inputs['dt']

        nt = int(total_t // dt)  # Loops
        uplift_per_step = uplift_rate * dt

        self.fr = FlowAccumulator(self.mg, **self.inputs)
        self.sp = FastscapeEroder(self.mg, **self.inputs)

        # Erode terrain
        for i in range(nt):
            self.fr.run_one_step() # Not time sensitive
            self.sp.run_one_step(dt)
            self.mg.at_node['topographic__elevation'][self.mg.core_nodes] += uplift_per_step  # add the uplift
            if i % 10 == 0:
                print('Erode: Completed loop %d' % i)

        plt.figure()
        imshow_grid(self.mg, 'topographic__elevation',
                    colorbar_label='m')
        plt.draw()
        plt.show()

        # Setup surface water flow
        self.of = OverlandFlow(self.mg, steep_slopes=True, mannings_n=0.01)

        # Setup initial flood
        self.swd[[5050, 5051, 5150, 5151]] += self.h_init
        self.active = np.greater(np.flip(np.reshape(self.swd, shape), axis=0), self.flood_threshold)

    def update_cells(self):
        """
        Update cells function. Updates water level and cell status.
        """

        self.of.overland_flow(dt=self.cell_update_period)
        shape = (self.grid_size,) * 2
        self.active = np.greater(np.flip(np.reshape(self.swd, shape), axis=0), self.flood_threshold)

    @staticmethod
    def noise_octave(shape, f):
        """
        Noise octave function. Generates a noise map.

        :param shape: (array) shape of the map.
        :param f: (float) frequency bounds parameter.
        """

        return te3w_util.fbm(shape, -1, lower=f, upper=(2 * f))

    def simple_topography(self, shape):
        """
        Simple topography function. Generates an elevation map.

        :param shape: (array) shape of the map.
        """

        values = np.zeros(shape)
        for p in range(1, 10):
            a = 2 ** p
            values += np.abs(self.noise_octave(shape, a) - 0.5) / a
        result = (1.0 - te3w_util.normalize(values)) ** 2
        return result
Beispiel #28
0
class BasicRtVs(TwoLithologyErosionModel):
    r"""**BasicRtVs** model program.

    This model program combines the :py:class:`BasicRt` and :py:class:`BasicVs`
    programs by allowing for two lithologies, an "upper" layer and a "lower"
    layer, and using discharge proportional to effective drainage area based on
    variable source area hydrology. Given a spatially varying contact zone
    elevation, :math:`\eta_C(x,y))`, model **BasicRtVs** evolves a topographic
    surface described by :math:`\eta` with the following governing equations:

    .. math::

        \frac{\partial \eta}{\partial t} = - K(\eta,\eta_C) A_{eff}^{m}S^{n}
                                           + D\nabla^2 \eta

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

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

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

        \alpha = \frac{K_{sat} 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:`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:`\alpha` is the
    saturation area scale used for transforming area into effective area and 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`. :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``
        - ``soil__depth``
    """

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

    def __init__(self, clock, grid, hydraulic_conductivity=0.1, **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.
        hydraulic_conductivity : float, optional
            Hydraulic conductivity (:math:`K_{sat}`). Default is 0.1.
        **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
        -------
        BasicRtVs : model object

        Examples
        --------
        This is a minimal example to demonstrate how to construct an instance
        of model **BasicRtVs**. 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, BasicRtVs
        >>> clock = Clock(start=0, stop=100, step=1)
        >>> grid = RasterModelGrid((5,5))
        >>> _ = random(grid, "topographic__elevation")
        >>> _ = random(grid, "soil__depth")
        >>> _ = constant(grid, "lithology_contact__elevation", value=-10.)

        Construct the model.

        >>> model = BasicRtVs(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(BasicRtVs, self).__init__(clock, grid, **kwargs)

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

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

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

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

        # Instantiate a FastscapeEroder component
        self.eroder = FastscapeEroder(
            self.grid,
            K_sp=self.erody,
            m_sp=self.m,
            n_sp=self.n,
            discharge_name="surface_water__discharge",
        )

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

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

        Effective drainage area is defined as:

        .. math::

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

        where :math:`S` is downslope-positive steepest gradient, :math:`A` is
        drainage area, :math:`R_r` is the runoff ratio, and :math:`\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

        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 **BasicRtVs** 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. 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 effective runoff ratio
        self._calc_effective_drainage_area()

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

        # Zero out effective area in flooded nodes
        self.grid.at_node["surface_water__discharge"][flooded] = 0.0

        # Update the erodibility field
        self._update_erodibility_field()

        # 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 #29
0
class BasicChSa(_ErosionModel):
    """
    A BasicChSa model computes erosion using depth-dependent cubic diffusion
    with a soil layer, basic stream power, and Q~A.
    """

    def __init__(self, input_file=None, params=None,
                 BaselevelHandlerClass=None):
        """Initialize the BasicChSa model."""

        # Call ErosionModel's init
        super(BasicChSa, 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
        try:
            initial_soil_thickness = (self._length_factor)*self.params['initial_soil_thickness'] # has units length
        except KeyError:
            initial_soil_thickness = 1.0  # default value
        soil_transport_decay_depth = (self._length_factor)*self.params['soil_transport_decay_depth']  # has units length
        max_soil_production_rate = (self._length_factor)*self.params['max_soil_production_rate'] # has units length per time
        soil_production_decay_depth = (self._length_factor)*self.params['soil_production_decay_depth']   # has units length

        # Create soil thickness (a.k.a. depth) field
        if 'soil__depth' in self.grid.at_node:
            soil_thickness = self.grid.at_node['soil__depth']
        else:
            soil_thickness = self.grid.add_zeros('node', 'soil__depth')

        # Create bedrock elevation field
        if 'bedrock__elevation' in self.grid.at_node:
            bedrock_elev = self.grid.at_node['bedrock__elevation']
        else:
            bedrock_elev = self.grid.add_zeros('node', 'bedrock__elevation')

        soil_thickness[:] = initial_soil_thickness
        bedrock_elev[:] = self.z - initial_soil_thickness

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

        # Instantiate a FastscapeEroder component
        self.eroder = FastscapeEroder(self.grid,
                                      K_sp=self.K_sp,
                                      m_sp=self.params['m_sp'],
                                      n_sp=self.params['n_sp'])

        # Instantiate a weathering component
        self.weatherer = ExponentialWeatherer(self.grid,
                                              max_soil_production_rate=max_soil_production_rate,
                                              soil_production_decay_depth=soil_production_decay_depth)

        # Instantiate a soil-transport component
        self.diffuser = DepthDependentTaylorDiffuser(self.grid,
                                                    linear_diffusivity=linear_diffusivity,
                                                    slope_crit=self.params['slope_crit'],
                                                    soil_transport_decay_depth=soil_transport_decay_depth,
                                                    nterms=11)

    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_sp
                             * self.pc.get_erodibility_adjustment_factor(self.model_time))

        self.eroder.run_one_step(dt, flooded_nodes=flooded)

        # We must also now erode the bedrock where relevant. If water erosion
        # into bedrock has occurred, the bedrock elevation will be higher than
        # the actual elevation, so we simply re-set bedrock elevation to the
        # lower of itself or the current elevation.
        b = self.grid.at_node['bedrock__elevation']
        b[:] = np.minimum(b, self.grid.at_node['topographic__elevation'])

        # Calculate regolith-production rate
        self.weatherer.calc_soil_prod_rate()

        # Do some soil creep
        self.diffuser.run_one_step(dt,
                                   dynamic_dt=True,
                                   if_unstable='raise',
                                   courant_factor=0.1)

        # calculate model time
        self.model_time += dt

        # Lower outlet
        self.update_outlet(dt)

        # Check walltime
        self.check_walltime()
class BasicChSa(ErosionModel):
    r"""**BasicChSa** model program.

    This model program combines models :py:class:`BasicCh` and
    :py:class:`BasicSa`. A soil layer is produced by weathering that decays
    exponentially with soil thickness and hillslope transport is soil-depth
    dependent. Given a spatially varying soil thickness :math:`H` and a
    spatially varying bedrock elevation :math:`\eta_b`, model **BasicChSa**
    evolves a topographic surface described by :math:`\eta` with the following
    governing equations:

    .. math::

        \eta = \eta_b + H

        \frac{\partial H}{\partial t} = P_0 \exp (-H/H_s)
                                        - \delta (H) K Q^{m} S^{n}
                                        -\nabla q_h

        \frac{\partial \eta_b}{\partial t} = -P_0 \exp (-H/H_s)
                                             - (1 - \delta (H) ) K Q^{m} S^{n}

        q_h = -D S H^* \left[ 1 + \left( \frac{S}{S_c} \right)^2
              + \left( \frac{S}{S_c} \right)^4
              + ... \left( \frac{S}{S_c} \right)^{2(N-1)} \right]

    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:`D` is the
    regolith transport parameter, :math:`H_s` is the sediment production decay
    depth, :math:`H_s` is the sediment production decay depth, :math:`P_0` is
    the maximum sediment production rate, and :math:`H_0` is the sediment
    transport decay depth. :math:`q_h` is the hillslope sediment flux per unit
    width. :math:`S_c` is the critical slope parameter and :math:`N` is the
    number of terms in the Taylor Series expansion.

    The function :math:`\delta (H)` is used to indicate that water erosion will
    act on soil where it exists, and on the underlying lithology where soil is
    absent. To achieve this, :math:`\delta (H)` is defined to equal 1 when
    :math:`H > 0` (meaning soil is present), and 0 if :math:`H = 0` (meaning
    the underlying parent material is exposed).

    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,
        critical_slope=0.3,
        number_of_taylor_terms=11,
        soil_production__maximum_rate=0.001,
        soil_production__decay_depth=0.5,
        soil_transport_decay_depth=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 : float, optional
            Water erodibility (:math:`K`). Default is 0.0001.
        regolith_transport_parameter : float, optional
            Regolith transport efficiency (:math:`D`). Default is 0.1.
        critical_slope : float, optional
            Critical slope (:math:`S_c`, unitless). Default is 0.3.
        number_of_taylor_terms : int, optional
            Number of terms in the Taylor Series Expansion (:math:`N`). Default
            is 11.
        soil_production__maximum_rate : float, optional
            Maximum rate of soil production (:math:`P_{0}`). Default is 0.001.
        soil_production__decay_depth : float, optional
            Decay depth for soil production (:math:`H_{s}`). Default is 0.5.
        soil_transport_decay_depth : float, optional
            Decay depth for soil transport (:math:`H_{0}`). Default is 0.5.
        **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
        -------
        BasicChSa : model object

        Examples
        --------
        This is a minimal example to demonstrate how to construct an instance
        of model **BasicChSa**. 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 constant
        >>> from terrainbento import Clock, BasicChSa
        >>> clock = Clock(start=0, stop=100, step=1)
        >>> grid = RasterModelGrid((5,5))
        >>> _ = constant(grid, "topographic__elevation", value=1.0)
        >>> _ = constant(grid, "soil__depth", value=1.0)

        Construct the model.

        >>> model = BasicChSa(clock, grid)

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

        >>> model.run_one_step(10)
        >>> model.model_time
        10.0

        """

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

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

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

        # Create bedrock elevation field
        soil_thickness = self.grid.at_node["soil__depth"]
        bedrock_elev = self.grid.add_zeros("node", "bedrock__elevation")
        bedrock_elev[:] = self.z - soil_thickness

        # Instantiate a FastscapeEroder component
        self.eroder = FastscapeEroder(
            self.grid,
            K_sp=self.K,
            m_sp=self.m,
            n_sp=self.n,
            discharge_name="surface_water__discharge",
        )

        # Instantiate a weathering component
        self.weatherer = ExponentialWeatherer(
            self.grid,
            soil_production__maximum_rate=soil_production__maximum_rate,
            soil_production__decay_depth=soil_production__decay_depth,
        )

        # Instantiate a soil-transport component
        self.diffuser = DepthDependentTaylorDiffuser(
            self.grid,
            linear_diffusivity=regolith_transport_parameter,
            slope_crit=critical_slope,
            soil_transport_decay_depth=soil_transport_decay_depth,
            nterms=number_of_taylor_terms,
        )

    def run_one_step(self, step):
        """Advance model **BasicChSa** 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 detachment-limited erosion by water.

        5. Produces soil and calculates soil depth with exponential weathering.

        6. Calculates topographic change by depth-dependent nonlinear
           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)

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

        # We must also now erode the bedrock where relevant. If water erosion
        # into bedrock has occurred, the bedrock elevation will be higher than
        # the actual elevation, so we simply re-set bedrock elevation to the
        # lower of itself or the current elevation.
        b = self.grid.at_node["bedrock__elevation"]
        b[:] = np.minimum(b, self.grid.at_node["topographic__elevation"])

        # Calculate regolith-production rate
        self.weatherer.calc_soil_prod_rate()

        # Do some soil creep
        self.diffuser.run_one_step(
            step, dynamic_dt=True, if_unstable="raise", courant_factor=0.1
        )

        # Finalize the run_one_step_method
        self.finalize__run_one_step(step)
Beispiel #31
0
y_uplift = (-1)*np.power(x_uplift-50, 2.)
y_uplift = y_uplift - np.min(y_uplift)
y_uplift = y_uplift / np.max(y_uplift)
y_uplift = y_uplift / 1000
y_uplift2d = np.empty((100,100))
y_uplift2d = np.reshape(np.repeat(y_uplift,100), (100, 100) )
pl.figure()
pl.imshow(y_uplift2d)
pl.colorbar()

fr = FlowAccumulator(mg, flow_director='D8')
fse = FastscapeEroder(mg, K_sp = 1e-5, m_sp=0.5, n_sp=1.)
dt = 10000.
time_steps = 50
for i in range(time_steps):
    z += np.reshape(y_uplift2d, n*n) * dt #uplift the block nodes
    fr.run_one_step()
    fse.run_one_step(dt)

pl.figure()
imshow_grid(mg, 'topographic__elevation', 
            plot_name='Uplifted block after ts=50,dt=100000 eroded by Stream Power Erosion law with K_sp=1e-4, m_sp=0.5, n_sp=1 (FastScape)', 
            allow_colorbar=True, vmin=np.percentile(z,10), 
            vmax=np.percentile(z,90))

pl.figure()
pl.imshow(np.flipud(np.log10(np.reshape(fr.node_drainage_area, (n,n)))))
cb=pl.colorbar()
cb.set_label('Log10 Flowaccumulation D8 at final step')

Beispiel #32
0
class BasicChRt(ErosionModel):
    """
    A BasicChRt model computes erosion using cubic diffusion, basic stream
    power with two rock units, and Q~A.
    """

    def __init__(self, input_file=None, params=None,
                 BaselevelHandlerClass=None):
        """Initialize the BasicChRt model."""

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

        contact_zone__width = (self._length_factor)*self.params['contact_zone__width'] # has units length
        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')

        # Set up rock-till
        self.setup_rock_and_till(self.params['rock_till_file__name'],
                                 self.K_rock_sp,
                                 self.K_till_sp,
                                 contact_zone__width)

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

        # Instantiate a FastscapeEroder component
        self.eroder = FastscapeEroder(self.grid,
                                      m_sp=self.params['m_sp'],
                                      n_sp=self.params['n_sp'],
                                      K_sp=self.erody)

        # Instantiate a LinearDiffuser component
        self.diffuser = TaylorNonLinearDiffuser(self.grid,
                                               linear_diffusivity=linear_diffusivity,
                                               slope_crit=self.params['slope_crit'],
                                               nterms=7)

    def setup_rock_and_till(self, file_name, rock_erody, till_erody,
                            contact_width):
        """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)

        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.

        Some considerations here:
            1. We could represent the contact between two layers either as a
               depth below present land surface, or as an altitude. Using a
               depth would allow for vertical motion, because for a fixed
               surface, the depth remains constant while the altitude changes.
               But the depth must be updated every time the surface is eroded
               or aggrades. Using an altitude avoids having to update the
               contact position every time the surface erodes or aggrades, but
               any tectonic motion would need to be applied to the contact
               position as well. Here we'll use the altitude approach because
               this model was originally written for an application with lots
               of erosion expected but no tectonics.
        """
        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 erodibility
        if 'substrate__erodibility' in self.grid.at_node:
            self.erody = self.grid.at_node['substrate__erodibility']
        else:
            self.erody = self.grid.add_zeros('node', 'substrate__erodibility')

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

        # Read the erodibility value of rock and till
        self.rock_erody = rock_erody
        self.till_erody = till_erody

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

    def update_erodibility_field(self):
        """Update erodibility 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.

        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")
        D_over_D_star = ((self.z[self.data_nodes] - self.rock_till_contact[self.data_nodes])
                                         / self.contact_width)

        # truncate D_over_D star to remove potential for overflow in exponent
        D_over_D_star[D_over_D_star < -100.0] = -100.0
        D_over_D_star[D_over_D_star > 100.0] = 100.0

        self.erody_wt[self.data_nodes] = (1.0 / (1.0 + np.exp(-D_over_D_star)))

        # (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 = self.K_till_sp * erode_factor
            self.rock_erody = self.K_rock_sp * erode_factor

        # Calculate the effective erodibilities using weighted averaging
        self.erody[:] = (self.erody_wt * self.till_erody
                         + (1.0 - self.erody_wt) * self.rock_erody)

    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 field
        self.update_erodibility_field()

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

        # Do some soil creep
        self.diffuser.run_one_step(dt,
                                   dynamic_dt=True,
                                   if_unstable='raise',
                                   courant_factor=0.1)

        # calculate model time
        self.model_time += dt

        # Lower outlet
        self.update_outlet(dt)

        # Check walltime
        self.check_walltime()
Beispiel #33
0
DHDTLowLim = upliftRate - (upliftRate * 1)
DHDTHighLim = upliftRate + (upliftRate * 1)

while elapsed_time < totalT:

    #create copy of "old" topography
    z0 = mg.at_node['topographic__elevation'].copy()

    #Call the erosion routines.
    #expw.run_one_step(dt=dt)
    #dld.run_one_step(dt=dt)
    ld.run_one_step(dt=dt)
    fr.run_one_step()
    lm.map_depressions()
    floodedNodes = np.where(lm.flood_status == 3)[0]
    fc.run_one_step(dt=dt, flooded_nodes=floodedNodes)
    mg.at_node['topographic__elevation'][
        mg.core_nodes] += uplift_per_step  #add uplift
    mg.at_node['bedrock__elevation'][
        mg.core_nodes] += uplift_per_step  #add uplift

    #look for nodes where river incises below current soil thickness
    bad_nodes = mg.at_node['topographic__elevation'] < mg.at_node[
        'bedrock__elevation']
    #redefine bedrock to current channel elevation
    mg.at_node['bedrock__elevation'][bad_nodes] = mg.at_node[
        'topographic__elevation'][bad_nodes]

    #calculate drainage_density
    channel_mask = mg.at_node['drainage_area'] > critArea
    dd = drainage_density.DrainageDensity(mg, channel__mask=channel_mask)