コード例 #1
0
    def _test_water_inputs(self, grid, runoff_rate):
        """Test inputs for runoff_rate and water__unit_flux_in."""
        if "water__unit_flux_in" not in grid.at_node:
            if runoff_rate is None:
                # assume that if runoff rate is not supplied, that the value
                # should be set to one everywhere.
                grid.add_ones("node", "water__unit_flux_in", dtype=float)
            else:
                runoff_rate = return_array_at_node(grid, runoff_rate)
                grid.at_node["water__unit_flux_in"] = runoff_rate
        else:
            if runoff_rate is not None:
                print("FlowAccumulator found both the field " +
                      "'water__unit_flux_in' and a provided float or " +
                      "array for the runoff_rate argument. THE FIELD IS " +
                      "BEING OVERWRITTEN WITH THE SUPPLIED RUNOFF_RATE!")
                runoff_rate = return_array_at_node(grid, runoff_rate)
                grid.at_node["water__unit_flux_in"] = runoff_rate

        # perform a test (for politeness!) that the old name for the water_in
        # field is not present:
        if "water__discharge_in" in grid.at_node:
            warnings.warn(
                "This component formerly took 'water__discharge" +
                "_in' as an input field. However, this field is " +
                "now named 'water__unit_flux_in'. You are still " +
                "using a field with the old name. Please update " +
                "your code if you intended to use that field.",
                DeprecationWarning,
            )
コード例 #2
0
ファイル: flow_accumulator.py プロジェクト: awickert/landlab
    def _test_water_inputs(self, grid, runoff_rate):
        """Test inputs for runoff_rate and water__unit_flux_in."""
        try:
            grid.at_node['water__unit_flux_in']
        except FieldError:
            if runoff_rate is None:
                # assume that if runoff rate is not supplied, that the value
                # should be set to one everywhere.
                grid.add_ones('node', 'water__unit_flux_in', dtype=float)
            else:
                runoff_rate = return_array_at_node(grid, runoff_rate)
                grid.at_node['water__unit_flux_in'] = runoff_rate
        else:
            if runoff_rate is not None:
                print ("FlowAccumulator found both the field " +
                       "'water__unit_flux_in' and a provided float or " +
                       "array for the runoff_rate argument. THE FIELD IS " +
                       "BEING OVERWRITTEN WITH THE SUPPLIED RUNOFF_RATE!")
                runoff_rate = return_array_at_node(grid, runoff_rate)
                grid.at_node['water__unit_flux_in'] = runoff_rate

        # perform a test (for politeness!) that the old name for the water_in
        # field is not present:
        if 'water__discharge_in' in grid.at_node:
            warnings.warn("This component formerly took 'water__discharge" +
                          "_in' as an input field. However, this field is " +
                          "now named 'water__unit_flux_in'. You are still " +
                          "using a field with the old name. Please update " +
                          "your code if you intended the FlowRouter to use " +
                          "that field.", DeprecationWarning)
コード例 #3
0
    def __init__(self, grid, K_sed=None, K_br=None, F_f=None,
                 phi=None, H_star=None, v_s=None,
                 m_sp=None, n_sp=None, sp_crit_sed=None,
                 sp_crit_br=None, discharge_field='surface_water__discharge',
                 solver='basic',
                 dt_min=DEFAULT_MINIMUM_TIME_STEP,
                 **kwds):
        """Initialize the Space model.

        """
        super(Space, self).__init__(grid, m_sp=m_sp, n_sp=n_sp,
                                    phi=phi, F_f=F_f, v_s=v_s,
                                    dt_min=dt_min,
                                    discharge_field=discharge_field)

        self._grid = grid #store grid

        # space specific inits
        self.H_star = H_star

        if 'soil__depth' in grid.at_node:
            self.soil__depth = grid.at_node['soil__depth']
        else:
            self.soil__depth = grid.add_zeros(
                'soil__depth', at='node', dtype=float)

        if 'bedrock__elevation' in grid.at_node:
            self.bedrock__elevation = grid.at_node['bedrock__elevation']
        else:
            self.bedrock__elevation = grid.add_zeros(
                'bedrock__elevation', at='node', dtype=float)
            self.bedrock__elevation[:] = self.topographic__elevation -\
                self.soil__depth

        self.Es = np.zeros(grid.number_of_nodes)
        self.Er = np.zeros(grid.number_of_nodes)

        #K's and critical values can be floats, grid fields, or arrays
        self.K_sed = return_array_at_node(grid, K_sed)
        self.K_br = return_array_at_node(grid, K_br)

        self.sp_crit_sed = return_array_at_node(grid, sp_crit_sed)
        self.sp_crit_br = return_array_at_node(grid, sp_crit_br)

        # Handle option for solver
        if solver == 'basic':
            self.run_one_step = self.run_one_step_basic
        elif solver == 'adaptive':
            self.run_one_step = self.run_with_adaptive_time_step_solver
            self.time_to_flat = np.zeros(grid.number_of_nodes)
            self.porosity_factor = 1.0 / (1.0 - self.phi)
        else:
            raise ValueError("Parameter 'solver' must be one of: "
                             + "'basic', 'adaptive'")
コード例 #4
0
ファイル: flow_director.py プロジェクト: cmshobe/landlab
    def _changed_surface(self):
        """Check if the surface values have changed.

        If the surface values are stored as a field, it is important to
        check if they have changed since the component was instantiated.
        """
        if isinstance(self.surface, six.string_types):
            self.surface_values = return_array_at_node(self._grid, self.surface)
コード例 #5
0
    def _changed_surface(self):
        """Check if the surface values have changed.

        If the surface values are stored as a field, it is important to
        check if they have changed since the component was instantiated.
        """
        if isinstance(self._surface, str):
            self._surface_values = return_array_at_node(self._grid, self._surface)
コード例 #6
0
 def _test_water_inputs(self, grid, runoff_rate):
     """Test inputs for runoff_rate and water__unit_flux_in."""
     if "water__unit_flux_in" not in grid.at_node:
         if runoff_rate is None:
             # assume that if runoff rate is not supplied, that the value
             # should be set to one everywhere.
             grid.add_ones("water__unit_flux_in", at="node", dtype=float)
         else:
             runoff_rate = return_array_at_node(grid, runoff_rate)
             grid.at_node["water__unit_flux_in"] = runoff_rate
     else:
         if runoff_rate is not None:
             print("FlowAccumulator found both the field " +
                   "'water__unit_flux_in' and a provided float or " +
                   "array for the runoff_rate argument. THE FIELD IS " +
                   "BEING OVERWRITTEN WITH THE SUPPLIED RUNOFF_RATE!")
             runoff_rate = return_array_at_node(grid, runoff_rate)
             grid.at_node["water__unit_flux_in"] = runoff_rate
コード例 #7
0
def test_return_array():
    mg = RasterModelGrid((10, 10))
    node_vals = np.arange(mg.number_of_nodes)
    out = return_array_at_node(mg, node_vals)

    np.testing.assert_array_equal(np.arange(mg.number_of_nodes), out)

    link_vals = np.arange(mg.number_of_links)
    out = return_array_at_link(mg, link_vals)

    np.testing.assert_array_equal(np.arange(mg.number_of_links), out)
コード例 #8
0
    def plot_profiles(
        self,
        field="topographic__elevation",
        xlabel="Distance Along Profile",
        ylabel="Plotted Quantity",
        title="Extracted Profiles",
        color=None,
    ):
        """Plot distance-upstream vs at at-node or size (nnodes,) quantity.

        Parameters
        ----------
        field : field name or nnode array
            Array of the at-node-field to plot against distance upstream.
            Default value is the at-node field 'topographic__elevation'.
        xlabel : str, optional
            X-axis label, default is "Distance Along Profile".
        ylabel : str, optional
            Y-axis label, default value is "Plotted Quantity".
        title : str, optional
            Plot title, default value is "Extracted Profiles".
        color : RGBA tuple or color string
            Color to use in order to plot all profiles the same color. Default
            is None, and the colors assigned to each profile are used.
        """
        quantity = return_array_at_node(self._grid, field)

        # create segments the way that line collection likes them.
        segments = []
        qmin = []
        qmax = []
        for idx, nodes in enumerate(self._nodes):
            segments.append(
                list(zip(self._distance_along_profile[idx], quantity[nodes])))
            qmin.append(min(quantity[nodes]))
            qmax.append(max(quantity[nodes]))

        # We need to set the plot limits.
        ax = plt.gca()
        ax.set_xlim(
            _recursive_min(self._distance_along_profile),
            _recursive_max(self._distance_along_profile),
        )
        ax.set_ylim(min(qmin), max(qmax))

        line_segments = LineCollection(segments)
        colors = color or self._colors
        line_segments.set_color(colors)
        ax.add_collection(line_segments)
        ax.set_xlabel(xlabel)
        ax.set_ylabel(ylabel)
        ax.set_title(title)
コード例 #9
0
    def dz_advection(self):
        """Rate of vertical advection.

        Parameters
        ----------
        dz_advection : float, `(n_nodes, )` shape array, or at-node field array optional
            Change in rock elevation due to advection by some external process.
            This can be changed using the property setter. Dimensions are in
            length, not length per time.

        Returns
        -------
        current rate of vertical advection
        """
        return return_array_at_node(self._grid, self._dz_advection)
コード例 #10
0
ファイル: flow_director.py プロジェクト: sequence-dev/landlab
    def __init__(self, grid, surface):
        """Initialize the _FlowDirector class."""
        # We keep a local reference to the grid
        super(_FlowDirector, self).__init__(grid)

        self._grid = grid
        self._bc_set_code = self.grid.bc_set_code

        # set up the grid type testing
        self._is_raster = isinstance(self._grid, RasterModelGrid)
        if not self._is_raster:
            self.method = None

        # save elevations as class properites.
        self.surface = surface
        self.surface_values = return_array_at_node(grid, surface)
コード例 #11
0
ファイル: flow_director.py プロジェクト: awickert/landlab
    def __init__(self, grid, surface):
        """Initialize the _FlowDirector class."""
        # We keep a local reference to the grid
        super(_FlowDirector, self).__init__(grid)

        self._grid = grid
        self._bc_set_code = self.grid.bc_set_code

        # set up the grid type testing
        self._is_raster = isinstance(self._grid, RasterModelGrid)
        if not self._is_raster:
            self.method = None

        # save elevations as class properites.
        self.surface = surface
        self.surface_values = return_array_at_node(grid, surface)
コード例 #12
0
    def __init__(self, grid, surface):
        """Initialize the _FlowDirector class."""
        # We keep a local reference to the grid
        super(_FlowDirector, self).__init__(grid)

        self._bc_set_code = self._grid.bc_set_code

        # set up the grid type testing
        self._is_raster = isinstance(self._grid, RasterModelGrid)
        if not self._is_raster:
            self._method = None

        # save elevations as class properites.
        self._surface = surface
        self._surface_values = return_array_at_node(grid, surface)

        grid.add_zeros("flow__sink_flag", at="node", dtype=bool, clobber=True)
コード例 #13
0
    def rock_id(self):
        """Rock type for deposition.

        Parameters
        ----------
        rock_id : value or `(n_nodes, )` shape array, optional
            Rock type id for new material if deposited.
            This can be changed using the property setter.

        Returns
        -------
        current type of rock being deposited (if deposition occurs)
        """
        if self._rock_id is None:
            return None
        else:
            return return_array_at_node(self._grid, self._rock_id)
コード例 #14
0
ファイル: sink_fill_barnes.py プロジェクト: landlab/landlab
    def __init__(
        self,
        grid,
        surface="topographic__elevation",
        method="D8",
        fill_flat=False,
        ignore_overfill=False,
    ):
        """
        Initialise the component.
        """
        if "flow__receiver_node" in grid.at_node:
            if grid.at_node["flow__receiver_node"].size != grid.size("node"):
                msg = (
                    "A route-to-multiple flow director has been "
                    "run on this grid. The landlab development team has not "
                    "verified that SinkFillerBarnes is compatible with "
                    "route-to-multiple methods. Please open a GitHub Issue "
                    "to start this process."
                )
                raise NotImplementedError(msg)

        # Most of the functionality of this component is directly inherited
        # from SinkFillerBarnes, so
        super(SinkFillerBarnes, self).__init__(
            grid,
            surface=surface,
            method=method,
            fill_flat=fill_flat,
            fill_surface=surface,
            redirect_flow_steepest_descent=False,
            reaccumulate_flow=False,
            ignore_overfill=ignore_overfill,
            track_lakes=True,
        )
        # note we will always track the fills, since we're only doing this
        # once... Likewise, no need for flow routing; this is not going to
        # get used dynamically.
        self._supplied_surface = return_array_at_node(grid, surface).copy()
        # create the only new output field:
        self._sed_fill_depth = self.grid.add_zeros(
            "node", "sediment_fill__depth", noclobber=False
        )
コード例 #15
0
ファイル: sink_fill_barnes.py プロジェクト: sklgp2016/landlab
    def __init__(
        self,
        grid,
        surface="topographic__elevation",
        method="D8",
        fill_flat=False,
        ignore_overfill=False,
    ):
        """
        Initialise the component.
        """
        if "flow__receiver_node" in grid.at_node:
            if grid.at_node["flow__receiver_node"].size != grid.size("node"):
                msg = (
                    "A route-to-multiple flow director has been "
                    "run on this grid. The landlab development team has not "
                    "verified that SinkFillerBarnes is compatible with "
                    "route-to-multiple methods. Please open a GitHub Issue "
                    "to start this process."
                )
                raise NotImplementedError(msg)

        # Most of the functionality of this component is directly inherited
        # from SinkFillerBarnes, so
        super(SinkFillerBarnes, self).__init__(
            grid,
            surface=surface,
            method=method,
            fill_flat=fill_flat,
            fill_surface=surface,
            redirect_flow_steepest_descent=False,
            reaccumulate_flow=False,
            ignore_overfill=ignore_overfill,
            track_lakes=True,
        )
        # note we will always track the fills, since we're only doing this
        # once... Likewise, no need for flow routing; this is not going to
        # get used dynamically.
        self._supplied_surface = return_array_at_node(grid, surface).copy()
        # create the only new output field:
        self._sed_fill_depth = self.grid.add_zeros(
            "node", "sediment_fill__depth", noclobber=False
        )
コード例 #16
0
    def __init__(
        self,
        grid,
        K_sed=0.02,
        K_br=0.02,
        F_f=0.0,
        phi=0.3,
        H_star=0.1,
        v_s=1.0,
        v_s_lake=None,
        m_sp=0.5,
        n_sp=1.0,
        sp_crit_sed=0.0,
        sp_crit_br=0.0,
        discharge_field="surface_water__discharge",
        erode_flooded_nodes=False,
        thickness_lim=100,
    ):
        """Initialize the SpaceLargeScaleEroder model.

        Parameters
        ----------
        grid : ModelGrid
            Landlab ModelGrid object
        K_sed : float, array of float, or str, optional
            Erodibility for sediment (units vary) as either a number or a field name.
        K_br : float, array of float, or str, optional
            Erodibility for bedrock (units vary) as either a number or a field name.
        F_f : float, optional
            Fraction of permanently suspendable fines in bedrock [-].
        phi : float, optional
            Sediment porosity [-].
        H_star : float, optional
            Sediment thickness required for full entrainment [L].
        v_s : float, optional
            Effective settling velocity for chosen grain size metric [L/T].
        v_s_lake : float, optional
            Effective settling velocity in lakes for chosen grain size metric [L/T].
        m_sp : float, optional
            Drainage area exponent (units vary).
        n_sp : float, optional
            Slope exponent (units vary).
        sp_crit_sed : float, array of float, or str, optional
            Critical stream power to erode sediment [E/(TL^2)].
        sp_crit_br : float, array of float, or str, optional
            Critical stream power to erode rock [E/(TL^2)]
        discharge_field : float, array of float, or str, optional
            Discharge [L^2/T]. The default is to use the grid field
            'surface_water__discharge', which is simply drainage area
            multiplied by the default rainfall rate (1 m/yr). To use custom
            spatially/temporally varying rainfall, use 'water__unit_flux_in'
            to specify water input to the FlowAccumulator.
        erode_flooded_nodes : bool, optional
            Whether erosion occurs in flooded nodes identified by a
            depression/lake mapper (e.g., DepressionFinderAndRouter). When set
            to false, the field *flood_status_code* must be present on the grid
            (this is created by the DepressionFinderAndRouter). Default True.
        """
        if grid.at_node["flow__receiver_node"].size != grid.size("node"):
            msg = (
                "A route-to-multiple flow director has been "
                "run on this grid. The landlab development team has not "
                "verified that SpaceLargeScaleEroder is compatible with "
                "route-to-multiple methods. Please open a GitHub Issue "
                "to start this process."
            )
            raise NotImplementedError(msg)

        super(SpaceLargeScaleEroder, self).__init__(grid)

        self._soil__depth = grid.at_node["soil__depth"]
        self._topographic__elevation = grid.at_node["topographic__elevation"]

        if "bedrock__elevation" in grid.at_node:
            self._bedrock__elevation = grid.at_node["bedrock__elevation"]
        else:
            self._bedrock__elevation = grid.add_zeros(
                "bedrock__elevation", at="node", dtype=float
            )

            self._bedrock__elevation[:] = (
                self._topographic__elevation - self._soil__depth
            )

        # Check consistency of bedrock, soil and topogarphic elevation fields
        err_msg = (
            "The sum of bedrock elevation and topographic elevation should be equal"
        )
        np.testing.assert_almost_equal(
            grid.at_node["bedrock__elevation"] + grid.at_node["soil__depth"],
            grid.at_node["topographic__elevation"],
            decimal=5,
            err_msg=err_msg,
        )

        # specific inits
        self._thickness_lim = thickness_lim
        self._H_star = H_star

        self._sed_erosion_term = np.zeros(grid.number_of_nodes)
        self._br_erosion_term = np.zeros(grid.number_of_nodes)
        self._Es = np.zeros(grid.number_of_nodes)
        self._Er = np.zeros(grid.number_of_nodes)

        # K's and critical values can be floats, grid fields, or arrays
        # use setters defined below
        self._K_sed = K_sed
        self._K_br = K_br

        self._sp_crit_sed = return_array_at_node(grid, sp_crit_sed)
        self._sp_crit_br = return_array_at_node(grid, sp_crit_br)

        self._erode_flooded_nodes = erode_flooded_nodes

        self._flow_receivers = grid.at_node["flow__receiver_node"]
        self._stack = grid.at_node["flow__upstream_node_order"]
        self._slope = grid.at_node["topographic__steepest_slope"]

        self.initialize_output_fields()

        self._qs = grid.at_node["sediment__outflux"]
        self._q = return_array_at_node(grid, discharge_field)

        # for backward compatibility (remove in 3.0.0+)
        grid.at_node["sediment__flux"] = grid.at_node["sediment__outflux"]

        self._Q_to_the_m = np.zeros(grid.number_of_nodes)
        self._S_to_the_n = np.zeros(grid.number_of_nodes)

        # store other constants
        self._m_sp = np.float64(m_sp)
        self._n_sp = np.float64(n_sp)
        self._phi = np.float64(phi)
        self._v_s = np.float64(v_s)

        if isinstance(grid, RasterModelGrid):
            self._link_lengths = grid.length_of_d8
        else:
            self._link_lengths = grid.length_of_link

        if v_s_lake is None:
            self._v_s_lake = np.float64(v_s)
        else:
            self._v_s_lake = np.float64(v_s_lake)
        self._F_f = np.float64(F_f)

        if phi >= 1.0:
            raise ValueError("Porosity must be < 1.0")

        if F_f > 1.0:
            raise ValueError("Fraction of fines must be <= 1.0")

        if phi < 0.0:
            raise ValueError("Porosity must be > 0.0")

        if F_f < 0.0:
            raise ValueError("Fraction of fines must be > 0.0")
コード例 #17
0
def flow_directions_dinf(grid, elevs="topographic__elevation", baselevel_nodes=None):
    """
    Find Dinfinity flow directions and proportions on a raster grid.

    Finds and returns flow directions and proportions for a given elevation
    grid by the D infinity method (Tarboton, 1997). Each node is assigned two
    flow directions, toward the two neighboring nodes that are on the steepest
    subtriangle. Partitioning of flow is done based on the aspect of the
    subtriangle.

    This method does not support irregular grids.

    Parameters
    ----------
    grid : ModelGrid
        A grid of type Voroni.
    elevs : field name at node or array of length node
        The surface to direct flow across.
    baselevel_nodes : array_like, optional
        IDs of open boundary (baselevel) nodes.

    Returns
    -------
    receivers : ndarray of size (num nodes, max neighbors at node)
        For each node, the IDs of the nodes that receive its flow. For nodes
        that do not direct flow to all neighbors, BAD_INDEX_VALUE is given as
        a placeholder. The ID of the node itself is given if no other receiver
        is assigned.
    proportions : ndarray of size (num nodes, max neighbors at node)
        For each receiver, the proportion of flow (between 0 and 1) is given.
        A proportion of zero indicates that the link does not have flow along
        it.
    slopes: ndarray of size (num nodes, max neighbors at node)
        For each node in the array ``recievers``, the slope value (positive
        downhill) in the direction of flow. If no flow occurs (value of
        ``recievers`` is -1), then this array is set to 0.
    steepest_slope : ndarray
        The slope value (positive downhill) in the direction of flow.
    steepest_receiver : ndarray
        For each node, the node ID of the node connected by the steepest link.
        BAD_INDEX_VALUE is given if no flow emmanates from the node.
    sink : ndarray
        IDs of nodes that are flow sinks (they are their own receivers)
    receiver_links : ndarray of size (num nodes, max neighbors at node)
        ID of links that leads from each node to its receiver, or
        UNDEFINED_INDEX if no flow occurs on this link.
    steepest_link : ndarray
        For each node, the link ID of the steepest link.
        BAD_INDEX_VALUE is given if no flow emmanates from the node.

    Examples
    --------
    >>> from landlab import RasterModelGrid
    >>> from landlab.components.flow_director.flow_direction_dinf import(
    ...                                                  flow_directions_dinf)

    Dinfinity routes flow based on the relative proportion of flow along the
    triangular facets around a central raster node.

    >>> grid = RasterModelGrid((3,3), spacing=(1, 1))
    >>> _ = grid.add_field('topographic__elevation',
    ...                     2.*grid.node_x+grid.node_y,
    ...                     at = 'node')
    >>> (receivers, proportions, slopes,
    ... steepest_slope, steepest_receiver,
    ... sink, receiver_links, steepest_link) = flow_directions_dinf(grid)
    >>> receivers
    array([[ 0, -1],
           [ 0,  3],
           [ 1,  4],
           [ 0,  1],
           [ 3,  0],
           [ 4,  1],
           [ 3,  4],
           [ 6,  3],
           [ 7,  4]])
    >>> proportions
    array([[ 1.        ,  0.        ],
           [ 1.        , -0.        ],
           [ 1.        , -0.        ],
           [ 1.        ,  0.        ],
           [ 0.40966553,  0.59033447],
           [ 0.40966553,  0.59033447],
           [ 1.        ,  0.        ],
           [ 0.40966553,  0.59033447],
           [ 0.40966553,  0.59033447]])

    This method also works if the elevations are passed as an array instead of
    the (implied) field name 'topographic__elevation'.

    >>> z = grid['node']['topographic__elevation']
    >>> (receivers, proportions, slopes,
    ... steepest_slope, steepest_receiver,
    ... sink, receiver_links, steepest_link) = flow_directions_dinf(grid, z)
    >>> receivers
    array([[ 0, -1],
           [ 0,  3],
           [ 1,  4],
           [ 0,  1],
           [ 3,  0],
           [ 4,  1],
           [ 3,  4],
           [ 6,  3],
           [ 7,  4]])
    >>> slopes
    array([[-2.        , -0.        ],
           [ 2.        ,  0.70710678],
           [ 2.        ,  0.70710678],
           [ 1.        , -0.70710678],
           [ 2.        ,  2.12132034],
           [ 2.        ,  2.12132034],
           [ 1.        , -0.70710678],
           [ 2.        ,  2.12132034],
           [ 2.        ,  2.12132034]])
    >>> proportions
    array([[ 1.        ,  0.        ],
           [ 1.        , -0.        ],
           [ 1.        , -0.        ],
           [ 1.        ,  0.        ],
           [ 0.40966553,  0.59033447],
           [ 0.40966553,  0.59033447],
           [ 1.        ,  0.        ],
           [ 0.40966553,  0.59033447],
           [ 0.40966553,  0.59033447]])
    """
    # grid type testing
    if isinstance(grid, VoronoiDelaunayGrid):
        raise NotImplementedError(
            "Dinfinity is currently implemented for" " Raster grids only"
        )
    # get elevs
    elevs = return_array_at_node(grid, elevs)

    ### Step 1, some basic set-up, gathering information about the grid.

    # Calculate the number of nodes.
    num_nodes = len(elevs)

    # Set the number of receivers and facets.
    num_receivers = 2
    num_facets = 8

    # Create a node array
    node_id = np.arange(num_nodes)

    # find where there are closed nodes.
    closed_nodes = grid.status_at_node == CLOSED_BOUNDARY

    # create an array of the triangle numbers
    tri_numbers = np.arange(num_facets)

    ### Step 3, create some triangle datastructures because landlab (smartly)
    # makes it hard to deal with diagonals.

    # create list of triangle neighbors at node. Use orientation associated
    # with tarboton's 1997 algorithm, orthogonal link first, then diagonal.
    # has shape, (nnodes, 8 triangles, 2 neighbors)
    n_at_node = grid.adjacent_nodes_at_node
    dn_at_node = grid.diagonal_adjacent_nodes_at_node

    triangle_neighbors_at_node = np.stack(
        [
            np.vstack((n_at_node[:, 0], dn_at_node[:, 0])),
            np.vstack((n_at_node[:, 1], dn_at_node[:, 0])),
            np.vstack((n_at_node[:, 1], dn_at_node[:, 1])),
            np.vstack((n_at_node[:, 2], dn_at_node[:, 1])),
            np.vstack((n_at_node[:, 2], dn_at_node[:, 2])),
            np.vstack((n_at_node[:, 3], dn_at_node[:, 2])),
            np.vstack((n_at_node[:, 3], dn_at_node[:, 3])),
            np.vstack((n_at_node[:, 0], dn_at_node[:, 3])),
        ],
        axis=-1,
    )
    triangle_neighbors_at_node = triangle_neighbors_at_node.swapaxes(0, 1)

    # next create, triangle links at node
    l_at_node = grid.d8s_at_node[:, :4]
    dl_at_node = grid.d8s_at_node[:, 4:]
    triangle_links_at_node = np.stack(
        [
            np.vstack((l_at_node[:, 0], dl_at_node[:, 0])),
            np.vstack((l_at_node[:, 1], dl_at_node[:, 0])),
            np.vstack((l_at_node[:, 1], dl_at_node[:, 1])),
            np.vstack((l_at_node[:, 2], dl_at_node[:, 1])),
            np.vstack((l_at_node[:, 2], dl_at_node[:, 2])),
            np.vstack((l_at_node[:, 3], dl_at_node[:, 2])),
            np.vstack((l_at_node[:, 3], dl_at_node[:, 3])),
            np.vstack((l_at_node[:, 0], dl_at_node[:, 3])),
        ],
        axis=-1,
    )
    triangle_links_at_node = triangle_links_at_node.swapaxes(0, 1)

    # next create link directions and active link directions at node
    # link directions
    ld_at_node = grid.link_dirs_at_node
    dld_at_node = grid.diagonal_dirs_at_node
    triangle_link_dirs_at_node = np.stack(
        [
            np.vstack((ld_at_node[:, 0], dld_at_node[:, 0])),
            np.vstack((ld_at_node[:, 1], dld_at_node[:, 0])),
            np.vstack((ld_at_node[:, 1], dld_at_node[:, 1])),
            np.vstack((ld_at_node[:, 2], dld_at_node[:, 1])),
            np.vstack((ld_at_node[:, 2], dld_at_node[:, 2])),
            np.vstack((ld_at_node[:, 3], dld_at_node[:, 2])),
            np.vstack((ld_at_node[:, 3], dld_at_node[:, 3])),
            np.vstack((ld_at_node[:, 0], dld_at_node[:, 3])),
        ],
        axis=-1,
    )
    triangle_link_dirs_at_node = triangle_link_dirs_at_node.swapaxes(0, 1)

    #    # active link directions.
    #    ald_at_node = grid.active_link_dirs_at_node
    #    adld_at_node = grid.active_diagonal_dirs_at_node
    #
    #    triangle_active_link_dirs_at_node = np.stack([np.vstack((ald_at_node[:,0], adld_at_node[:,0])),
    #                                                  np.vstack((ald_at_node[:,1], adld_at_node[:,0])),
    #                                                  np.vstack((ald_at_node[:,1], adld_at_node[:,1])),
    #                                                  np.vstack((ald_at_node[:,2], adld_at_node[:,1])),
    #                                                  np.vstack((ald_at_node[:,2], adld_at_node[:,2])),
    #                                                  np.vstack((ald_at_node[:,3], adld_at_node[:,2])),
    #                                                  np.vstack((ald_at_node[:,3], adld_at_node[:,3])),
    #                                                  np.vstack((ald_at_node[:,0], adld_at_node[:,3]))],
    #                                                 axis=-1)
    #    triangle_active_link_dirs_at_node = triangle_active_link_dirs_at_node.swapaxes(0,1)
    #
    # need to create a list of diagonal links since it doesn't exist.
    diag_links = np.sort(np.unique(grid.d8s_at_node[:, 4:]))
    diag_links = diag_links[diag_links > 0]

    # calculate graidents across diagonals and orthogonals
    diag_grads = grid._calculate_gradients_at_d8_links(elevs)
    ortho_grads = grid.calc_grad_at_link(elevs)

    # finally compile link slopes
    link_slope = np.hstack((ortho_grads, diag_grads))

    # Construct the array of slope to triangles at node. This also will adjust
    # for the slope convention based on the direction of the links.
    # this is a (nnodes, 2, 8) array
    slopes_to_triangles_at_node = (
        link_slope[triangle_links_at_node] * triangle_link_dirs_at_node
    )

    # identify where nodes are closed.
    closed_triangle_neighbors = closed_nodes[triangle_neighbors_at_node]
    # construct some arrays that deal with the distances between points on the
    # grid.

    #### Step 3: make arrays necessary for the specific tarboton algorithm.
    # create a arrays
    ac = np.array([0., 1., 1., 2., 2., 3., 3., 4.])
    af = np.array([1., -1., 1., -1., 1., -1., 1., -1.])

    # construct d1 and d2, we know these because we know where the orthogonal
    # links are
    diag_length = ((grid.dx) ** 2 + (grid.dy) ** 2) ** 0.5

    # for irregular grids, d1 and d2 will need to be matricies
    d1 = np.array(
        [grid.dx, grid.dy, grid.dy, grid.dx, grid.dx, grid.dy, grid.dy, grid.dy]
    )
    d2 = np.array(
        [grid.dx, grid.dx, grid.dy, grid.dy, grid.dx, grid.dx, grid.dy, grid.dy]
    )

    thresh = np.arctan(d2 / d1)

    ##### Step 4, Initialize receiver and proportion arrays
    receivers = UNDEFINED_INDEX * np.ones((num_nodes, num_receivers), dtype=int)
    receiver_closed = UNDEFINED_INDEX * np.ones((num_nodes, num_receivers), dtype=int)
    proportions = np.zeros((num_nodes, num_receivers), dtype=float)
    receiver_links = UNDEFINED_INDEX * np.ones((num_nodes, num_receivers), dtype=int)
    slopes_to_receivers = np.zeros((num_nodes, num_receivers), dtype=float)

    #### Step  5  begin the algorithm in earnest

    # construct e0, e1, e2 for all triangles at all nodes.
    # will be (nnodes, nfacets=8 for raster or nfacets = max number of patches
    # for irregular grids.

    # e0 is origin point of the facet
    e0 = elevs[node_id]

    # e1 is the point on the orthogoanal edges
    e1 = elevs[triangle_neighbors_at_node[:, 0, :]]
    # e2 is the point on the diagonal edges
    e2 = elevs[triangle_neighbors_at_node[:, 1, :]]

    # mask out where nodes do not exits (e.g. triangle_neighbors_at_node == -1)
    e2[triangle_neighbors_at_node[:, 1, :] == -1] = np.nan
    e1[triangle_neighbors_at_node[:, 0, :] == -1] = np.nan

    # loop through and calculate s1 and s2
    # this will only loop nfacets times.
    s1 = np.empty_like(e1)
    s2 = np.empty_like(e2)

    for i in range(num_facets):
        s1[:, i] = (e0 - e1[:, i]) / d1[i]
        s2[:, i] = (e1[:, i] - e2[:, i]) / d2[i]

    # calculate r and s, the direction and magnitude
    r = np.arctan2(s2, s1)
    s = ((s1 ** 2) + (s2 ** 2)) ** 0.5

    r[np.isnan(r)] = 0
    # adjust r if it doesn't sit in the realm of (0, arctan(d2,d1))
    too_small = r < 0
    radj = r.copy()
    radj[too_small] = 0
    s[too_small] = s1[too_small]

    # to consider two big, we need to look by trangle.
    for i in range(num_facets):
        too_big = r[:, i] > thresh[i]
        radj[too_big, i] = thresh[i]
        s[too_big, i] = (e0[too_big] - e2[too_big, i]) / diag_length

    # calculate the geospatial version of r based on radj
    rg = np.empty_like(r)
    for i in range(num_facets):
        rg[:, i] = (af[i] * radj[:, i]) + (ac[i] * np.pi / 2.)

    # set slopes that are nan to zero
    s[np.isnan(s)] = 0

    # sort slopes based on
    steepest_sort = np.argsort(s)

    # determine the steepest triangle
    steepest_triangle = tri_numbers[steepest_sort[:, -1]]

    # initialize arrays for the steepest rg and steepest s
    steepest_rg = np.empty_like(node_id, dtype=float)
    steepest_s = np.empty_like(node_id, dtype=float)

    for n in node_id:
        steepest_rg[n] = rg[n, steepest_sort[n, -1]]
        receiver_closed[n] = closed_triangle_neighbors[n, :, steepest_sort[n, -1]]
        steepest_s[n] = s[n, steepest_sort[n, -1]]
        receivers[n, :] = triangle_neighbors_at_node[n, :, steepest_sort[n, -1]]
        receiver_links[n, :] = triangle_links_at_node[n, :, steepest_sort[n, -1]]
        slopes_to_receivers[n, :] = slopes_to_triangles_at_node[
            n, :, steepest_sort[n, -1]
        ]

    # construct the baseline for proportions
    rg_baseline = np.array([0., 1., 1., 2., 2., 3., 3., 4]) * np.pi / 2.
    # rg_baseline = np.array([0., 0.5, 1., 1.5, 2., 2.5, 3., 3.5])*np.pi/4.

    # calculate alpha1 and alpha 2
    alpha2 = (steepest_rg - rg_baseline[steepest_triangle]) * af[steepest_triangle]
    alpha1 = thresh[steepest_triangle] - alpha2

    # calculate proportions from alpha
    proportions[:, 0] = (alpha1) / (alpha1 + alpha2)
    proportions[:, 1] = (alpha2) / (alpha1 + alpha2)

    ### END OF THE Tarboton algorithm, start of work to make this code mesh
    # with other landlab flow directing algorithms.

    # identify what drains to itself, and set proportion and id values based on
    # that.

    # if proportions is nan, drain to self
    drains_to_self = np.isnan(proportions[:, 0])

    # if all slopes are leading out, drain to self
    drains_to_self[steepest_s <= 0] = True

    # if both receiver nodes are closed, drain to self
    drains_to_two_closed = receiver_closed.sum(axis=1) == num_receivers
    drains_to_self[drains_to_two_closed] = True

    # if drains to one closed receiver, check that the open receiver actually
    # gets flow. If so, route all to the open receiver. If the receiver getting
    # all the flow is closed, then drain to self.
    all_flow_to_closed = np.sum(receiver_closed * proportions, axis=1) == 1
    drains_to_self[all_flow_to_closed] = True

    drains_to_one_closed = receiver_closed.sum(axis=1) == 1
    fix_flow = drains_to_one_closed * (all_flow_to_closed == False)
    first_column_has_closed = np.array(receiver_closed[:, 0] * fix_flow, dtype=bool)
    second_column_has_closed = np.array(receiver_closed[:, 1] * fix_flow, dtype=bool)

    # remove the link to the closed node
    receivers[first_column_has_closed, 0] = -1
    receivers[second_column_has_closed, 1] = -1

    # change the proportions
    proportions[first_column_has_closed, 0] = 0.
    proportions[first_column_has_closed, 1] = 1.

    proportions[second_column_has_closed, 0] = 1.
    proportions[second_column_has_closed, 1] = 0.

    # set properties of drains to self.
    receivers[drains_to_self, 0] = node_id[drains_to_self]
    receivers[drains_to_self, 1] = -1

    proportions[drains_to_self, 0] = 1.
    proportions[drains_to_self, 1] = 0.

    # mask the receiver_links by where flow doesn't occur to return
    receiver_links[drains_to_self, :] = UNDEFINED_INDEX

    # identify the steepest link so that the steepest receiver, link, and slope
    # can be returned.
    slope_sort = np.argsort(np.argsort(slopes_to_receivers, axis=1), axis=1) == (
        num_receivers - 1
    )
    steepest_slope = slopes_to_receivers[slope_sort]
    steepest_slope[drains_to_self] = 0.

    ## identify the steepest link and steepest receiever.
    steepest_link = receiver_links[slope_sort]
    steepest_link[drains_to_self] = UNDEFINED_INDEX

    steepest_receiver = receivers[slope_sort]
    steepest_receiver[drains_to_self] = node_id[drains_to_self]

    # Optionally, handle baselevel nodes: they are their own receivers
    if baselevel_nodes is not None:
        receivers[baselevel_nodes, 0] = node_id[baselevel_nodes]
        receivers[baselevel_nodes, 1:] = -1
        proportions[baselevel_nodes, 0] = 1
        proportions[baselevel_nodes, 1:] = 0
        receiver_links[baselevel_nodes, :] = UNDEFINED_INDEX
        steepest_slope[baselevel_nodes] = 0.

    # The sink nodes are those that are their own receivers (this will normally
    # include boundary nodes as well as interior ones; "pits" would be sink
    # nodes that are also interior nodes).
    (sink,) = np.where(node_id == receivers[:, 0])
    sink = as_id_array(sink)

    return (
        receivers,
        proportions,
        slopes_to_receivers,
        steepest_slope,
        steepest_receiver,
        sink,
        receiver_links,
        steepest_link,
    )
コード例 #18
0
    def __init__(self,
                 grid,
                 K=0.002,
                 v_s=1.0,
                 m_sp=0.5,
                 n_sp=1.0,
                 sp_crit=0.0,
                 F_f=0.0,
                 discharge_field="surface_water__discharge",
                 solver="basic",
                 dt_min=DEFAULT_MINIMUM_TIME_STEP,
                 **kwds):
        """Initialize the ErosionDeposition model.

        Parameters
        ----------
        grid : ModelGrid
            Landlab ModelGrid object
        K : float, field name, or array
            Erodibility for substrate (units vary).
        v_s : float
            Effective settling velocity for chosen grain size metric [L/T].
        m_sp : float
            Discharge exponent (units vary)
        n_sp : float
            Slope exponent (units vary)
        sp_crit : float, field name, or array
            Critical stream power to erode substrate [E/(TL^2)]
        F_f : float
            Fraction of eroded material that turns into "fines" that do not
            contribute to (coarse) sediment load. Defaults to zero.
        discharge_field : float, field name, or array
            Discharge [L^2/T]. The default is to use the grid field
            'surface_water__discharge', which is simply drainage area
            multiplied by the default rainfall rate (1 m/yr). To use custom
            spatially/temporally varying rainfall, use 'water__unit_flux_in'
            to specify water input to the FlowAccumulator.
        solver : string
            Solver to use. Options at present include:
                (1) 'basic' (default): explicit forward-time extrapolation.
                    Simple but will become unstable if time step is too large.
                (2) 'adaptive': adaptive time-step solver that estimates a
                    stable step size based on the shortest time to "flattening"
                    among all upstream-downstream node pairs.

        Examples
        ---------
        >>> import numpy as np
        >>> from landlab import RasterModelGrid
        >>> from landlab.components import FlowAccumulator
        >>> from landlab.components import DepressionFinderAndRouter
        >>> from landlab.components import ErosionDeposition
        >>> from landlab.components import FastscapeEroder
        >>> np.random.seed(seed = 5000)

        Define grid and initial topography:
            -5x5 grid with baselevel in the lower left corner
            -all other boundary nodes closed
            -Initial topography is plane tilted up to the upper right + noise

        >>> nr = 5
        >>> nc = 5
        >>> dx = 10
        >>> mg = RasterModelGrid((nr, nc), xy_spacing=10.0)
        >>> _ = mg.add_zeros('node', 'topographic__elevation')
        >>> mg['node']['topographic__elevation'] += (mg.node_y/10 +
        ...        mg.node_x/10 + np.random.rand(len(mg.node_y)) / 10)
        >>> 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.)
        >>> fsc_dt = 100.
        >>> ed_dt = 1.

        Check initial topography

        >>> mg.at_node['topographic__elevation'] # doctest: +NORMALIZE_WHITESPACE
        array([ 0.02290479,  1.03606698,  2.0727653 ,  3.01126678,  4.06077707,
            1.08157495,  2.09812694,  3.00637448,  4.07999597,  5.00969486,
            2.04008677,  3.06621577,  4.09655859,  5.04809001,  6.02641123,
            3.05874171,  4.00585786,  5.0595697 ,  6.04425233,  7.05334077,
            4.05922478,  5.0409473 ,  6.07035008,  7.0038935 ,  8.01034357])

        Instantiate Fastscape eroder, flow router, and depression finder

        >>> fr = FlowAccumulator(mg, flow_director='D8')
        >>> df = DepressionFinderAndRouter(mg)
        >>> fsc = FastscapeEroder(
        ...     mg,
        ...     K_sp=.001,
        ...     m_sp=.5,
        ...     n_sp=1)

        Burn in an initial drainage network using the Fastscape eroder:

        >>> for x in range(100):
        ...     fr.run_one_step()
        ...     df.map_depressions()
        ...     flooded = np.where(df.flood_status==3)[0]
        ...     fsc.run_one_step(dt = fsc_dt)
        ...     mg.at_node['topographic__elevation'][0] -= 0.001 #uplift

        Instantiate the E/D component:

        >>> ed = ErosionDeposition(
        ...     mg,
        ...     K=0.00001,
        ...     v_s=0.001,
        ...     m_sp=0.5,
        ...     n_sp = 1.0,
        ...     sp_crit=0)

        Now run the E/D component for 2000 short timesteps:

        >>> for x in range(2000): #E/D component loop
        ...     fr.run_one_step()
        ...     df.map_depressions()
        ...     ed.run_one_step(dt = ed_dt)
        ...     mg.at_node['topographic__elevation'][0] -= 2e-4 * ed_dt

        Now we test to see if topography is right:

        >>> np.around(mg.at_node['topographic__elevation'], decimals=3) # doctest: +NORMALIZE_WHITESPACE
        array([-0.477,  1.036,  2.073,  3.011,  4.061,  1.082, -0.08 , -0.065,
           -0.054,  5.01 ,  2.04 , -0.065, -0.065, -0.053,  6.026,  3.059,
           -0.054, -0.053, -0.035,  7.053,  4.059,  5.041,  6.07 ,  7.004,
            8.01 ])
        """
        if grid.at_node["flow__receiver_node"].size != grid.size("node"):
            msg = ("A route-to-multiple flow director has been "
                   "run on this grid. The landlab development team has not "
                   "verified that ErosionDeposition is compatible with "
                   "route-to-multiple methods. Please open a GitHub Issue "
                   "to start this process.")
            raise NotImplementedError(msg)

        if "phi" in kwds:
            msg = "As of Landlab v2 ErosionDeposition no longer takes the keyword argument phi. The sediment flux is considered to represent bulk deposit volume rather than mineral volume, and therefore porosity does not impact the dynamics. The following pull request explains the math behind this: https://github.com/landlab/landlab/pull/1186."
            raise ValueError(msg)
        elif len(kwds) > 0:
            kwdstr = " ".join(list(kwds.keys()))
            raise ValueError(
                "Extra kwds passed to ErosionDeposition:{kwds}".format(
                    kwds=kwdstr))
        super().__init__(
            grid,
            m_sp=m_sp,
            n_sp=n_sp,
            F_f=F_f,
            v_s=v_s,
            dt_min=dt_min,
            discharge_field=discharge_field,
        )

        # E/D specific inits.

        # K's and critical values can be floats, grid fields, or arrays
        # use setter for K defined below
        self.K = K
        self._sp_crit = return_array_at_node(grid, sp_crit)

        # Handle option for solver
        if solver == "basic":
            self.run_one_step = self.run_one_step_basic
        elif solver == "adaptive":
            self.run_one_step = self.run_with_adaptive_time_step_solver
            self._time_to_flat = np.zeros(grid.number_of_nodes)
        else:
            raise ValueError("Parameter 'solver' must be one of: " +
                             "'basic', 'adaptive'")
コード例 #19
0
def test_no_field():
    mg = RasterModelGrid((10, 10))
    with pytest.raises(FieldError):
        return_array_at_node(mg, "spam")
    with pytest.raises(FieldError):
        return_array_at_link(mg, "spam")
コード例 #20
0
ファイル: erosion_deposition.py プロジェクト: cmshobe/landlab
    def __init__(
        self,
        grid,
        K=None,
        phi=None,
        v_s=None,
        m_sp=None,
        n_sp=None,
        sp_crit=0.0,
        F_f=0.0,
        discharge_field="surface_water__discharge",
        solver="basic",
        dt_min=DEFAULT_MINIMUM_TIME_STEP,
        **kwds
    ):
        """Initialize the ErosionDeposition model.

        Parameters
        ----------
        grid : ModelGrid
            Landlab ModelGrid object
        K : float, field name, or array
            Erodibility for substrate (units vary).
        phi : float
            Sediment porosity [-].
        v_s : float
            Effective settling velocity for chosen grain size metric [L/T].
        m_sp : float
            Discharge exponent (units vary)
        n_sp : float
            Slope exponent (units vary)
        sp_crit : float, field name, or array
            Critical stream power to erode substrate [E/(TL^2)]
        F_f : float
            Fraction of eroded material that turns into "fines" that do not
            contribute to (coarse) sediment load. Defaults to zero.
        discharge_field : float, field name, or array
            Discharge [L^2/T].
        solver : string
            Solver to use. Options at present include:
                (1) 'basic' (default): explicit forward-time extrapolation.
                    Simple but will become unstable if time step is too large.
                (2) 'adaptive': adaptive time-step solver that estimates a
                    stable step size based on the shortest time to "flattening"
                    among all upstream-downstream node pairs.

        Examples
        ---------
        >>> import numpy as np
        >>> from landlab import RasterModelGrid
        >>> from landlab.components import FlowAccumulator
        >>> from landlab.components import DepressionFinderAndRouter
        >>> from landlab.components import ErosionDeposition
        >>> from landlab.components import FastscapeEroder
        >>> np.random.seed(seed = 5000)

        Define grid and initial topography:
            -5x5 grid with baselevel in the lower left corner
            -all other boundary nodes closed
            -Initial topography is plane tilted up to the upper right + noise

        >>> nr = 5
        >>> nc = 5
        >>> dx = 10
        >>> mg = RasterModelGrid((nr, nc), xy_spacing=10.0)
        >>> _ = mg.add_zeros('node', 'topographic__elevation')
        >>> mg['node']['topographic__elevation'] += (mg.node_y/10 +
        ...        mg.node_x/10 + np.random.rand(len(mg.node_y)) / 10)
        >>> 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.)
        >>> fsc_dt = 100.
        >>> ed_dt = 1.

        Check initial topography

        >>> mg.at_node['topographic__elevation'] # doctest: +NORMALIZE_WHITESPACE
        array([ 0.02290479,  1.03606698,  2.0727653 ,  3.01126678,  4.06077707,
            1.08157495,  2.09812694,  3.00637448,  4.07999597,  5.00969486,
            2.04008677,  3.06621577,  4.09655859,  5.04809001,  6.02641123,
            3.05874171,  4.00585786,  5.0595697 ,  6.04425233,  7.05334077,
            4.05922478,  5.0409473 ,  6.07035008,  7.0038935 ,  8.01034357])

        Instantiate Fastscape eroder, flow router, and depression finder

        >>> fsc = FastscapeEroder(mg, K_sp=.001, m_sp=.5, n_sp=1)
        >>> fr = FlowAccumulator(mg, flow_director='D8')
        >>> df = DepressionFinderAndRouter(mg)

        Burn in an initial drainage network using the Fastscape eroder:

        >>> for x in range(100):
        ...     fr.run_one_step()
        ...     df.map_depressions()
        ...     flooded = np.where(df.flood_status==3)[0]
        ...     fsc.run_one_step(dt = fsc_dt, flooded_nodes=flooded)
        ...     mg.at_node['topographic__elevation'][0] -= 0.001 #uplift

        Instantiate the E/D component:

        >>> ed = ErosionDeposition(mg, K=0.00001, phi=0.0, v_s=0.001,
        ...                        m_sp=0.5, n_sp = 1.0, sp_crit=0)

        Now run the E/D component for 2000 short timesteps:

        >>> for x in range(2000): #E/D component loop
        ...     fr.run_one_step()
        ...     df.map_depressions()
        ...     flooded = np.where(df.flood_status==3)[0]
        ...     ed.run_one_step(dt = ed_dt, flooded_nodes=flooded)
        ...     mg.at_node['topographic__elevation'][0] -= 2e-4 * ed_dt

        Now we test to see if topography is right:

        >>> np.around(mg.at_node['topographic__elevation'], decimals=3) # doctest: +NORMALIZE_WHITESPACE
        array([-0.477,  1.036,  2.073,  3.011,  4.061,  1.082, -0.08 , -0.065,
           -0.054,  5.01 ,  2.04 , -0.065, -0.065, -0.053,  6.026,  3.059,
           -0.054, -0.053, -0.035,  7.053,  4.059,  5.041,  6.07 ,  7.004,
            8.01 ])
        """
        if grid.at_node["flow__receiver_node"].size != grid.size("node"):
            msg = (
                "A route-to-multiple flow director has been "
                "run on this grid. The landlab development team has not "
                "verified that ErosionDeposition is compatible with "
                "route-to-multiple methods. Please open a GitHub Issue "
                "to start this process."
            )
            raise NotImplementedError(msg)

        super(ErosionDeposition, self).__init__(
            grid,
            m_sp=m_sp,
            n_sp=n_sp,
            phi=phi,
            F_f=F_f,
            v_s=v_s,
            dt_min=dt_min,
            discharge_field=discharge_field,
        )

        self._grid = grid  # store grid

        # E/D specific inits.

        # K's and critical values can be floats, grid fields, or arrays
        self.K = return_array_at_node(grid, K)
        self.sp_crit = return_array_at_node(grid, sp_crit)

        # Handle option for solver
        if solver == "basic":
            self.run_one_step = self.run_one_step_basic
        elif solver == "adaptive":
            self.run_one_step = self.run_with_adaptive_time_step_solver
            self.time_to_flat = np.zeros(grid.number_of_nodes)
        else:
            raise ValueError(
                "Parameter 'solver' must be one of: " + "'basic', 'adaptive'"
            )
コード例 #21
0
    def __init__(self,
                 grid,
                 surface="topographic__elevation",
                 flow_director="FlowDirectorSteepest",
                 runoff_rate=None,
                 depression_finder=None,
                 **kwargs):
        """
        Initialize the FlowAccumulator component.

        Saves the grid, tests grid type, tests imput types and compatability
        for the flow_director and depression_finder keyword arguments, tests
        the argument of runoff_rate, and initializes new fields.
        """
        super(FlowAccumulator, self).__init__(grid)
        # Keep a local reference to the grid
        self._grid = grid

        # Grid type testing
        self._is_raster = isinstance(self._grid, RasterModelGrid)
        self._is_Voroni = isinstance(self._grid, VoronoiDelaunayGrid)

        self.kwargs = kwargs
        # STEP 1: Testing of input values, supplied either in function call or
        # as part of the grid.
        self._test_water_inputs(grid, runoff_rate)

        # save elevations and node_cell_area to class properites.
        self.surface = surface
        self.surface_values = return_array_at_node(grid, surface)

        node_cell_area = self._grid.cell_area_at_node.copy()
        node_cell_area[self._grid.closed_boundary_nodes] = 0.

        self.node_cell_area = node_cell_area

        # STEP 2:
        # identify Flow Director method, save name, import and initialize the correct
        # flow director component if necessary
        self._add_director(flow_director)
        self._add_depression_finder(depression_finder)

        # This component will track of the following variables.
        # Attempt to create each, if they already exist, assign the existing
        # version to the local copy.

        #   - drainage area at each node
        #   - receiver of each node
        #   - D array
        #   - delta array
        #   - missing nodes in stack.
        if "drainage_area" not in grid.at_node:
            self.drainage_area = grid.add_zeros("drainage_area",
                                                at="node",
                                                dtype=float)
        else:
            self.drainage_area = grid.at_node["drainage_area"]

        if "surface_water__discharge" not in grid.at_node:
            self.discharges = grid.add_zeros("surface_water__discharge",
                                             at="node",
                                             dtype=float)
        else:
            self.discharges = grid.at_node["surface_water__discharge"]

        if "flow__upstream_node_order" not in grid.at_node:
            self.upstream_ordered_nodes = grid.add_field(
                "flow__upstream_node_order",
                BAD_INDEX_VALUE * grid.ones(at="node", dtype=int),
                at="node",
                dtype=int,
            )
        else:
            self.upstream_ordered_nodes = grid.at_node[
                "flow__upstream_node_order"]

        if "flow__data_structure_delta" not in grid.at_node:
            self.delta_structure = grid.add_field(
                "flow__data_structure_delta",
                BAD_INDEX_VALUE * grid.ones(at="node", dtype=int),
                at="node",
                dtype=int,
            )
        else:
            self.delta_structure = grid.at_node["flow__data_structure_delta"]

        if "flow__data_structure_D" not in grid.at_link:
            if self.flow_director.to_n_receivers == "many" and self._is_raster:
                # needs to be BAD_INDEX_VALUE
                self.D_structure = grid.add_field(
                    "flow__data_structure_D",
                    BAD_INDEX_VALUE * np.ones(
                        (self._grid.number_of_links, 2), dtype=int),
                    at="link",
                    dtype=int,
                    noclobber=False,
                )
            else:
                # needs to be BAD_INDEX_VALUE
                self.D_structure = grid.add_field(
                    "flow__data_structure_D",
                    BAD_INDEX_VALUE * grid.ones(at="link"),
                    at="link",
                    dtype=int,
                )
        else:
            self.D_structure = grid.at_link["flow__data_structure_D"]

        self.nodes_not_in_stack = True
コード例 #22
0
    def __init__(
        self,
        grid,
        surface="topographic__elevation",
        flow_director="FlowDirectorSteepest",
        runoff_rate=None,
        depression_finder=None,
        **kwargs
    ):
        """Initialize the FlowAccumulator component.

        Saves the grid, tests grid type, tests imput types and
        compatability for the flow_director and depression_finder
        keyword arguments, tests the argument of runoff_rate, and
        initializes new fields.
        """
        super(FlowAccumulator, self).__init__(grid)
        # Keep a local reference to the grid

        # Grid type testing
        self._is_raster = isinstance(self._grid, RasterModelGrid)
        self._is_Voroni = isinstance(self._grid, VoronoiDelaunayGrid)
        self._is_Network = isinstance(self._grid, NetworkModelGrid)
        self._kwargs = kwargs

        # STEP 1: Testing of input values, supplied either in function call or
        # as part of the grid.
        self._test_water_inputs(grid, runoff_rate)

        # save elevations and node_cell_area to class properites.
        self._surface = surface
        self._surface_values = return_array_at_node(grid, surface)

        if self._is_Network:
            try:
                node_cell_area = self._grid.at_node["cell_area_at_node"]
            except FieldError:
                raise FieldError(
                    "In order for the FlowAccumulator to work, the "
                    "grid must have an at-node field called "
                    "cell_area_at_node."
                )
        else:
            node_cell_area = self._grid.cell_area_at_node.copy()
            node_cell_area[self._grid.closed_boundary_nodes] = 0.0

        self._node_cell_area = node_cell_area

        # STEP 2:
        # identify Flow Director method, save name, import and initialize the correct
        # flow director component if necessary
        self._add_director(flow_director)
        self._add_depression_finder(depression_finder)

        # This component will track of the following variables.
        # Attempt to create each, if they already exist, assign the existing
        # version to the local copy.

        #   - drainage area at each node
        #   - receiver of each node
        #   - delta array

        self.initialize_output_fields()

        self._drainage_area = grid.at_node["drainage_area"]
        self._discharges = grid.at_node["surface_water__discharge"]

        self._upstream_ordered_nodes = grid.at_node["flow__upstream_node_order"]
        if np.all(self._upstream_ordered_nodes == 0):
            self._upstream_ordered_nodes.fill(self._grid.BAD_INDEX)

        self._delta_structure = grid.at_node["flow__data_structure_delta"]
        if np.all(self._delta_structure == 0):
            self._delta_structure[:] = self._grid.BAD_INDEX

        self._D_structure = self._grid.BAD_INDEX * grid.ones(at="link", dtype=int)
        self._nodes_not_in_stack = True

        if len(self._kwargs) > 0:
            kwdstr = " ".join(list(self._kwargs.keys()))
            raise ValueError(
                "Extra kwargs passed to FlowAccumulator:{kwds}".format(kwds=kwdstr)
            )
コード例 #23
0
    def __init__(self,
                 grid,
                 K=None,
                 phi=None,
                 v_s=None,
                 m_sp=None,
                 n_sp=None,
                 sp_crit=0.0,
                 F_f=0.0,
                 discharge_field='surface_water__discharge',
                 solver='basic',
                 dt_min=DEFAULT_MINIMUM_TIME_STEP,
                 **kwds):
        """Initialize the ErosionDeposition model.

        Parameters
        ----------
        grid : ModelGrid
            Landlab ModelGrid object
        K : float, field name, or array
            Erodibility for substrate (units vary).
        phi : float
            Sediment porosity [-].
        v_s : float
            Effective settling velocity for chosen grain size metric [L/T].
        m_sp : float
            Drainage area exponent (units vary)
        n_sp : float
            Slope exponent (units vary)
        sp_crit : float, field name, or array
            Critical stream power to erode substrate [E/(TL^2)]
        F_f : float
            Fraction of eroded material that turns into "fines" that do not
            contribute to (coarse) sediment load. Defaults to zero.
        discharge_field : float, field name, or array
            Discharge [L^2/T].
        solver : string
            Solver to use. Options at present include:
                (1) 'basic' (default): explicit forward-time extrapolation.
                    Simple but will become unstable if time step is too large.
                (2) 'adaptive': adaptive time-step solver that estimates a
                    stable step size based on the shortest time to "flattening"
                    among all upstream-downstream node pairs.

        Examples
        ---------
        >>> import numpy as np
        >>> from landlab import RasterModelGrid
        >>> from landlab.components.flow_routing import FlowRouter
        >>> from landlab.components import DepressionFinderAndRouter
        >>> from landlab.components import ErosionDeposition
        >>> from landlab.components import FastscapeEroder
        >>> np.random.seed(seed = 5000)

        Define grid and initial topography:
            -5x5 grid with baselevel in the lower left corner
            -all other boundary nodes closed
            -Initial topography is plane tilted up to the upper right + noise

        >>> nr = 5
        >>> nc = 5
        >>> dx = 10
        >>> mg = RasterModelGrid((nr, nc), 10.0)
        >>> _ = mg.add_zeros('node', 'topographic__elevation')
        >>> mg['node']['topographic__elevation'] += (mg.node_y/10 +
        ...        mg.node_x/10 + np.random.rand(len(mg.node_y)) / 10)
        >>> 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.)
        >>> fsc_dt = 100.
        >>> ed_dt = 1.

        Check initial topography

        >>> mg.at_node['topographic__elevation'] # doctest: +NORMALIZE_WHITESPACE
        array([ 0.02290479,  1.03606698,  2.0727653 ,  3.01126678,  4.06077707,
            1.08157495,  2.09812694,  3.00637448,  4.07999597,  5.00969486,
            2.04008677,  3.06621577,  4.09655859,  5.04809001,  6.02641123,
            3.05874171,  4.00585786,  5.0595697 ,  6.04425233,  7.05334077,
            4.05922478,  5.0409473 ,  6.07035008,  7.0038935 ,  8.01034357])

        Instantiate Fastscape eroder, flow router, and depression finder

        >>> fsc = FastscapeEroder(mg, K_sp=.001, m_sp=.5, n_sp=1)
        >>> fr = FlowRouter(mg) #instantiate
        >>> df = DepressionFinderAndRouter(mg)

        Burn in an initial drainage network using the Fastscape eroder:

        >>> for x in range(100):
        ...     fr.run_one_step()
        ...     df.map_depressions()
        ...     flooded = np.where(df.flood_status==3)[0]
        ...     fsc.run_one_step(dt = fsc_dt, flooded_nodes=flooded)
        ...     mg.at_node['topographic__elevation'][0] -= 0.001 #uplift

        Instantiate the E/D component:

        >>> ed = ErosionDeposition(mg, K=0.00001, phi=0.0, v_s=0.001,
        ...                        m_sp=0.5, n_sp = 1.0, sp_crit=0)

        Now run the E/D component for 2000 short timesteps:

        >>> for x in range(2000): #E/D component loop
        ...     fr.run_one_step()
        ...     df.map_depressions()
        ...     flooded = np.where(df.flood_status==3)[0]
        ...     ed.run_one_step(dt = ed_dt, flooded_nodes=flooded)
        ...     mg.at_node['topographic__elevation'][0] -= 2e-4 * ed_dt

        Now we test to see if topography is right:

        >>> np.around(mg.at_node['topographic__elevation'], decimals=3) # doctest: +NORMALIZE_WHITESPACE
        array([-0.477,  1.036,  2.073,  3.011,  4.061,  1.082, -0.08 , -0.065,
           -0.054,  5.01 ,  2.04 , -0.065, -0.065, -0.053,  6.026,  3.059,
           -0.054, -0.053, -0.035,  7.053,  4.059,  5.041,  6.07 ,  7.004,
            8.01 ])
        """
        if (grid.at_node['flow__receiver_node'].size != grid.size('node')):
            msg = ('A route-to-multiple flow director has been '
                   'run on this grid. The landlab development team has not '
                   'verified that ErosionDeposition is compatible with '
                   'route-to-multiple methods. Please open a GitHub Issue '
                   'to start this process.')
            raise NotImplementedError(msg)

        super(ErosionDeposition,
              self).__init__(grid,
                             m_sp=m_sp,
                             n_sp=n_sp,
                             phi=phi,
                             F_f=F_f,
                             v_s=v_s,
                             dt_min=dt_min,
                             discharge_field=discharge_field)

        self._grid = grid  #store grid

        # E/D specific inits.

        # K's and critical values can be floats, grid fields, or arrays
        self.K = return_array_at_node(grid, K)
        self.sp_crit = return_array_at_node(grid, sp_crit)

        # Handle option for solver
        if solver == 'basic':
            self.run_one_step = self.run_one_step_basic
        elif solver == 'adaptive':
            self.run_one_step = self.run_with_adaptive_time_step_solver
            self.time_to_flat = np.zeros(grid.number_of_nodes)
        else:
            raise ValueError("Parameter 'solver' must be one of: " +
                             "'basic', 'adaptive'")
コード例 #24
0
 def K_br(self, new_val):
     self._K_br = return_array_at_node(self._grid, new_val)
コード例 #25
0
    def __init__(
        self,
        grid,
        soil_density=1330,
        water_density=997.9,
        C=12.0,
        N=0.85,
        gravity=constants.g,
        channel_bottom_sediment_grain__d50_diameter="channel_bottom_sediment_grain__d50_diameter",
    ):
        """Initialize the DimensionlessDischarge.

        Parameters
        ----------
        soil_density : float, field name, or array, optional
            density of soil (kg/m^3) (defaults to 1330)
        water_density : float, optional
            density of water (kg/m^3) (defaults to 997.9)
        C : float, optional
            Numerator of the debris flow threshold equation; Empirically
            derived constant (See Tang et al. 2019). (defaults to 12.0)
        N : float, optional
            Exponent for slope in the denominator of the debris flow
            threshold equation; Empirically derived constant (See Tang
            et al. 2019). (defaults to 0.85)
        gravity : float, optional
            (defaults to scipy's standard acceleration of gravity)
        channel_bottom_sediment_grain__d50_diameter : float, field_name, or array
            defaults to the field name
            "channel_bottom_sediment_grain__d50_diameter"
        """

        super().__init__(grid)

        # Store parameters

        # scalar parameters.
        self._c = C
        self._n = N
        self._water_density = water_density
        self._gravity = gravity

        # get topographic__elevation values and change into slope of a
        # stream segment
        self._stream_slopes = self._elevationToSlope()

        # both soil density and d50 can be float, array, or field name.
        self._soil_density = return_array_at_node(self.grid, soil_density)
        self._channel_bottom_sediment_grain__d50_diameter = return_array_at_node(
            self.grid, channel_bottom_sediment_grain__d50_diameter)

        # create output fields
        self.grid.add_zeros("dimensionless_discharge", at="node")
        self.grid.add_full("dimensionless_discharge_above_threshold",
                           False,
                           at="node",
                           dtype=bool)
        self.grid.add_zeros("dimensionless_discharge_threshold",
                            at="node",
                            dtype=float)

        # calculate threshold values for each segment
        self._calc_threshold()
コード例 #26
0
ファイル: space.py プロジェクト: Glader011235/Landlab
    def __init__(self, grid, K_sed=None, K_br=None, F_f=None,
                 phi=None, H_star=None, v_s=None,
                 m_sp=None, n_sp=None, sp_crit_sed=None,
                 sp_crit_br=None, discharge_field='surface_water__discharge',
                 solver='basic',
                 dt_min=DEFAULT_MINIMUM_TIME_STEP,
                 **kwds):
        """Initialize the Space model.

        """
        if (grid.at_node['flow__receiver_node'].size != grid.size('node')):
            msg = ('A route-to-multiple flow director has been '
                   'run on this grid. The landlab development team has not '
                   'verified that SPACE is compatible with '
                   'route-to-multiple methods. Please open a GitHub Issue '
                   'to start this process.')
            raise NotImplementedError(msg)

        super(Space, self).__init__(grid, m_sp=m_sp, n_sp=n_sp,
                                    phi=phi, F_f=F_f, v_s=v_s,
                                    dt_min=dt_min,
                                    discharge_field=discharge_field)

        self._grid = grid #store grid

        # space specific inits
        self.H_star = H_star

        if 'soil__depth' in grid.at_node:
            self.soil__depth = grid.at_node['soil__depth']
        else:
            self.soil__depth = grid.add_zeros(
                'soil__depth', at='node', dtype=float)

        if 'bedrock__elevation' in grid.at_node:
            self.bedrock__elevation = grid.at_node['bedrock__elevation']
        else:
            self.bedrock__elevation = grid.add_zeros(
                'bedrock__elevation', at='node', dtype=float)
            self.bedrock__elevation[:] = self.topographic__elevation -\
                self.soil__depth

        self.Es = np.zeros(grid.number_of_nodes)
        self.Er = np.zeros(grid.number_of_nodes)

        #K's and critical values can be floats, grid fields, or arrays
        self.K_sed = return_array_at_node(grid, K_sed)
        self.K_br = return_array_at_node(grid, K_br)

        self.sp_crit_sed = return_array_at_node(grid, sp_crit_sed)
        self.sp_crit_br = return_array_at_node(grid, sp_crit_br)

        # Handle option for solver
        if solver == 'basic':
            self.run_one_step = self.run_one_step_basic
        elif solver == 'adaptive':
            self.run_one_step = self.run_with_adaptive_time_step_solver
            self.time_to_flat = np.zeros(grid.number_of_nodes)
            self.porosity_factor = 1.0 / (1.0 - self.phi)
        else:
            raise ValueError("Parameter 'solver' must be one of: "
                             + "'basic', 'adaptive'")
コード例 #27
0
def flow_directions_dinf(grid,
                         elevs="topographic__elevation",
                         baselevel_nodes=None):
    """
    Find Dinfinity flow directions and proportions on a raster grid.

    Finds and returns flow directions and proportions for a given elevation
    grid by the D infinity method (Tarboton, 1997). Each node is assigned two
    flow directions, toward the two neighboring nodes that are on the steepest
    subtriangle. Partitioning of flow is done based on the aspect of the
    subtriangle.

    This method does not support irregular grids.

    Parameters
    ----------
    grid : ModelGrid
        A grid of type Voroni.
    elevs : field name at node or array of length node
        The surface to direct flow across.
    baselevel_nodes : array_like, optional
        IDs of open boundary (baselevel) nodes.

    Returns
    -------
    receivers : ndarray of size (num nodes, max neighbors at node)
        For each node, the IDs of the nodes that receive its flow. For nodes
        that do not direct flow to all neighbors, BAD_INDEX_VALUE is given as
        a placeholder. The ID of the node itself is given if no other receiver
        is assigned.
    proportions : ndarray of size (num nodes, max neighbors at node)
        For each receiver, the proportion of flow (between 0 and 1) is given.
        A proportion of zero indicates that the link does not have flow along
        it.
    slopes: ndarray of size (num nodes, max neighbors at node)
        For each node in the array ``recievers``, the slope value (positive
        downhill) in the direction of flow. If no flow occurs (value of
        ``recievers`` is -1), then this array is set to 0.
    steepest_slope : ndarray
        The slope value (positive downhill) in the direction of flow.
    steepest_receiver : ndarray
        For each node, the node ID of the node connected by the steepest link.
        BAD_INDEX_VALUE is given if no flow emmanates from the node.
    sink : ndarray
        IDs of nodes that are flow sinks (they are their own receivers)
    receiver_links : ndarray of size (num nodes, max neighbors at node)
        ID of links that leads from each node to its receiver, or
        UNDEFINED_INDEX if no flow occurs on this link.
    steepest_link : ndarray
        For each node, the link ID of the steepest link.
        BAD_INDEX_VALUE is given if no flow emmanates from the node.

    Examples
    --------
    >>> from landlab import RasterModelGrid
    >>> from landlab.components.flow_director.flow_direction_dinf import(
    ...                                                  flow_directions_dinf)

    Dinfinity routes flow based on the relative proportion of flow along the
    triangular facets around a central raster node.

    >>> grid = RasterModelGrid((3,3), spacing=(1, 1))
    >>> _ = grid.add_field('topographic__elevation',
    ...                     2.*grid.node_x+grid.node_y,
    ...                     at = 'node')
    >>> (receivers, proportions, slopes,
    ... steepest_slope, steepest_receiver,
    ... sink, receiver_links, steepest_link) = flow_directions_dinf(grid)
    >>> receivers
    array([[ 0, -1],
           [ 0, -1],
           [ 1, -1],
           [ 0, -1],
           [ 3,  0],
           [ 4,  1],
           [ 3, -1],
           [ 6,  3],
           [ 7,  4]])
    >>> proportions
    array([[ 1.        ,  0.        ],
           [ 1.        , -0.        ],
           [ 1.        , -0.        ],
           [ 1.        ,  0.        ],
           [ 0.40966553,  0.59033447],
           [ 0.40966553,  0.59033447],
           [ 1.        ,  0.        ],
           [ 0.40966553,  0.59033447],
           [ 0.40966553,  0.59033447]])

    This method also works if the elevations are passed as an array instead of
    the (implied) field name 'topographic__elevation'.

    >>> z = grid['node']['topographic__elevation']
    >>> (receivers, proportions, slopes,
    ... steepest_slope, steepest_receiver,
    ... sink, receiver_links, steepest_link) = flow_directions_dinf(grid, z)
    >>> receivers
    array([[ 0, -1],
           [ 0, -1],
           [ 1, -1],
           [ 0, -1],
           [ 3,  0],
           [ 4,  1],
           [ 3, -1],
           [ 6,  3],
           [ 7,  4]])
    >>> slopes
    array([[-1.        , -2.12132034],
           [ 2.        ,  0.70710678],
           [ 2.        ,  0.70710678],
           [ 1.        , -0.70710678],
           [ 2.        ,  2.12132034],
           [ 2.        ,  2.12132034],
           [ 1.        , -0.70710678],
           [ 2.        ,  2.12132034],
           [ 2.        ,  2.12132034]])
    >>> proportions
    array([[ 1.        ,  0.        ],
           [ 1.        , -0.        ],
           [ 1.        , -0.        ],
           [ 1.        ,  0.        ],
           [ 0.40966553,  0.59033447],
           [ 0.40966553,  0.59033447],
           [ 1.        ,  0.        ],
           [ 0.40966553,  0.59033447],
           [ 0.40966553,  0.59033447]])
    """
    # grid type testing
    if isinstance(grid, VoronoiDelaunayGrid):
        raise NotImplementedError("Dinfinity is currently implemented for"
                                  " Raster grids only")
    # get elevs
    elevs = return_array_at_node(grid, elevs)

    # find where there are closed nodes.
    closed_nodes = grid.status_at_node == CLOSED_BOUNDARY

    closed_elevation = np.max(elevs[closed_nodes == False]) + 1000

    elevs[closed_nodes] = closed_elevation

    ### Step 1, some basic set-up, gathering information about the grid.

    # Calculate the number of nodes.
    num_nodes = len(elevs)

    # Set the number of receivers and facets.
    num_receivers = 2
    num_facets = 8

    # Create a node array
    node_id = np.arange(num_nodes)

    # create an array of the triangle numbers
    tri_numbers = np.arange(num_facets)

    ### Step 3, create some triangle datastructures because landlab (smartly)
    # makes it hard to deal with diagonals.

    # create list of triangle neighbors at node. Use orientation associated
    # with tarboton's 1997 algorithm, orthogonal link first, then diagonal.
    # has shape, (nnodes, 8 triangles, 2 neighbors)
    n_at_node = grid.adjacent_nodes_at_node
    dn_at_node = grid.diagonal_adjacent_nodes_at_node

    triangle_neighbors_at_node = np.stack(
        [
            np.vstack((n_at_node[:, 0], dn_at_node[:, 0])),
            np.vstack((n_at_node[:, 1], dn_at_node[:, 0])),
            np.vstack((n_at_node[:, 1], dn_at_node[:, 1])),
            np.vstack((n_at_node[:, 2], dn_at_node[:, 1])),
            np.vstack((n_at_node[:, 2], dn_at_node[:, 2])),
            np.vstack((n_at_node[:, 3], dn_at_node[:, 2])),
            np.vstack((n_at_node[:, 3], dn_at_node[:, 3])),
            np.vstack((n_at_node[:, 0], dn_at_node[:, 3])),
        ],
        axis=-1,
    )
    triangle_neighbors_at_node = triangle_neighbors_at_node.swapaxes(0, 1)

    # next create, triangle links at node
    l_at_node = grid.d8s_at_node[:, :4]
    dl_at_node = grid.d8s_at_node[:, 4:]
    triangle_links_at_node = np.stack(
        [
            np.vstack((l_at_node[:, 0], dl_at_node[:, 0])),
            np.vstack((l_at_node[:, 1], dl_at_node[:, 0])),
            np.vstack((l_at_node[:, 1], dl_at_node[:, 1])),
            np.vstack((l_at_node[:, 2], dl_at_node[:, 1])),
            np.vstack((l_at_node[:, 2], dl_at_node[:, 2])),
            np.vstack((l_at_node[:, 3], dl_at_node[:, 2])),
            np.vstack((l_at_node[:, 3], dl_at_node[:, 3])),
            np.vstack((l_at_node[:, 0], dl_at_node[:, 3])),
        ],
        axis=-1,
    )
    triangle_links_at_node = triangle_links_at_node.swapaxes(0, 1)

    # next create link directions and active link directions at node
    # link directions
    ld_at_node = grid.link_dirs_at_node
    dld_at_node = grid.diagonal_dirs_at_node
    triangle_link_dirs_at_node = np.stack(
        [
            np.vstack((ld_at_node[:, 0], dld_at_node[:, 0])),
            np.vstack((ld_at_node[:, 1], dld_at_node[:, 0])),
            np.vstack((ld_at_node[:, 1], dld_at_node[:, 1])),
            np.vstack((ld_at_node[:, 2], dld_at_node[:, 1])),
            np.vstack((ld_at_node[:, 2], dld_at_node[:, 2])),
            np.vstack((ld_at_node[:, 3], dld_at_node[:, 2])),
            np.vstack((ld_at_node[:, 3], dld_at_node[:, 3])),
            np.vstack((ld_at_node[:, 0], dld_at_node[:, 3])),
        ],
        axis=-1,
    )
    triangle_link_dirs_at_node = triangle_link_dirs_at_node.swapaxes(0, 1)

    # need to create a list of diagonal links since it doesn't exist.
    diag_links = np.sort(np.unique(grid.d8s_at_node[:, 4:]))
    diag_links = diag_links[diag_links > 0]

    # calculate graidents across diagonals and orthogonals
    diag_grads = grid._calculate_gradients_at_d8_links(elevs)
    ortho_grads = grid.calc_grad_at_link(elevs)

    # finally compile link slopes
    link_slope = np.hstack((ortho_grads, diag_grads))

    # Construct the array of slope to triangles at node. This also will adjust
    # for the slope convention based on the direction of the links.
    # this is a (nnodes, 2, 8) array
    slopes_to_triangles_at_node = (link_slope[triangle_links_at_node] *
                                   triangle_link_dirs_at_node)

    #### Step 3: make arrays necessary for the specific tarboton algorithm.
    # create a arrays
    ac = np.array([0., 1., 1., 2., 2., 3., 3., 4.])
    af = np.array([1., -1., 1., -1., 1., -1., 1., -1.])

    # construct d1 and d2, we know these because we know where the orthogonal
    # links are
    diag_length = ((grid.dx)**2 + (grid.dy)**2)**0.5

    # for irregular grids, d1 and d2 will need to be matricies
    d1 = np.array([
        grid.dx, grid.dy, grid.dy, grid.dx, grid.dx, grid.dy, grid.dy, grid.dy
    ])
    d2 = np.array([
        grid.dx, grid.dx, grid.dy, grid.dy, grid.dx, grid.dx, grid.dy, grid.dy
    ])

    thresh = np.arctan(d2 / d1)

    ##### Step 4, Initialize receiver and proportion arrays
    receivers = UNDEFINED_INDEX * np.ones(
        (num_nodes, num_receivers), dtype=int)
    receiver_closed = UNDEFINED_INDEX * np.ones(
        (num_nodes, num_receivers), dtype=int)
    proportions = np.zeros((num_nodes, num_receivers), dtype=float)
    receiver_links = UNDEFINED_INDEX * np.ones(
        (num_nodes, num_receivers), dtype=int)
    slopes_to_receivers = np.zeros((num_nodes, num_receivers), dtype=float)

    #### Step  5  begin the algorithm in earnest

    # construct e0, e1, e2 for all triangles at all nodes.
    # will be (nnodes, nfacets=8 for raster or nfacets = max number of patches
    # for irregular grids.

    # e0 is origin point of the facet
    e0 = elevs[node_id]

    # e1 is the point on the orthogoanal edges
    e1 = elevs[triangle_neighbors_at_node[:, 0, :]]
    # e2 is the point on the diagonal edges
    e2 = elevs[triangle_neighbors_at_node[:, 1, :]]

    # modification of original algorithm to address Landlab boundary conditions.
    # first,
    # for e1 and e2, mask out where nodes do not exits (e.g.
    # triangle_neighbors_at_node == -1)
    e1[triangle_neighbors_at_node[:, 0, :] == -1] = np.nan
    e2[triangle_neighbors_at_node[:, 1, :] == -1] = np.nan

    # loop through and calculate s1 and s2
    # this will only loop nfacets times.
    s1 = np.empty_like(e1)
    s2 = np.empty_like(e2)

    for i in range(num_facets):
        s1[:, i] = (e0 - e1[:, i]) / d1[i]
        s2[:, i] = (e1[:, i] - e2[:, i]) / d2[i]

    # calculate r and s, the direction and magnitude
    r = np.arctan2(s2, s1)
    s = ((s1**2) + (s2**2))**0.5

    r[np.isnan(r)] = 0
    # adjust r if it doesn't sit in the realm of (0, arctan(d2,d1))
    too_small = r < 0
    radj = r.copy()
    radj[too_small] = 0
    s[too_small] = s1[too_small]

    # to consider two big, we need to look by triangle.
    for i in range(num_facets):
        too_big = r[:, i] > thresh[i]
        radj[too_big, i] = thresh[i]
        s[too_big, i] = (e0[too_big] - e2[too_big, i]) / diag_length

    # calculate the geospatial version of r based on radj
    rg = np.empty_like(r)
    for i in range(num_facets):
        rg[:, i] = (af[i] * radj[:, i]) + (ac[i] * np.pi / 2.)

    # set slopes that are nan to below zero
    # if there is a flat slope, it should be chosen over the closed or non-existant
    # triangles that are represented by the nan values.
    s[np.isnan(s)] = -999.0

    # sort slopes
    # we've set slopes going to closed or non-existant triangles to -999.0, so
    # we shouldn't ever choose these.
    steepest_sort = np.argsort(s)

    # determine the steepest triangle
    steepest_triangle = tri_numbers[steepest_sort[:, -1]]

    # initialize arrays for the steepest rg and steepest s
    steepest_rg = np.empty_like(node_id, dtype=float)
    steepest_s = np.empty_like(node_id, dtype=float)

    closed_triangle_neighbors = closed_nodes[triangle_neighbors_at_node]
    for n in node_id:
        steepest_rg[n] = rg[n, steepest_sort[n, -1]]
        receiver_closed[n] = closed_triangle_neighbors[n, :, steepest_sort[n,
                                                                           -1]]
        steepest_s[n] = s[n, steepest_sort[n, -1]]
        receivers[n, :] = triangle_neighbors_at_node[n, :, steepest_sort[n,
                                                                         -1]]
        receiver_links[n, :] = triangle_links_at_node[n, :, steepest_sort[n,
                                                                          -1]]
        slopes_to_receivers[n, :] = slopes_to_triangles_at_node[
            n, :, steepest_sort[n, -1]]

    # construct the baseline for proportions
    rg_baseline = np.array([0., 1., 1., 2., 2., 3., 3., 4]) * np.pi / 2.

    # calculate alpha1 and alpha 2
    alpha2 = (steepest_rg -
              rg_baseline[steepest_triangle]) * af[steepest_triangle]
    alpha1 = thresh[steepest_triangle] - alpha2

    # calculate proportions from alpha
    proportions[:, 0] = (alpha1) / (alpha1 + alpha2)
    proportions[:, 1] = (alpha2) / (alpha1 + alpha2)

    # where proportions == 0, set reciever  to -1
    receivers[proportions == 0] = -1

    ### END OF THE Tarboton algorithm, start of work to make this code mesh
    # with other landlab flow directing algorithms.

    # identify what drains to itself, and set proportion and id values based on
    # that.

    # if proportions is nan, drain to self
    drains_to_self = np.isnan(proportions[:, 0])

    # if all slopes are leading out or flat, drain to self
    drains_to_self[steepest_s <= 0] = True

    # if both receiver nodes are closed, drain to self
    drains_to_two_closed = receiver_closed.sum(axis=1) == num_receivers
    drains_to_self[drains_to_two_closed] = True

    # if drains to one closed receiver, check that the open receiver actually
    # gets flow. If so, route all to the open receiver. If the receiver getting
    # all the flow is closed, then drain to self.
    all_flow_to_closed = np.sum(receiver_closed * proportions, axis=1) == 1
    drains_to_self[all_flow_to_closed] = True

    drains_to_one_closed = receiver_closed.sum(axis=1) == 1
    fix_flow = drains_to_one_closed * (all_flow_to_closed == False)
    first_column_has_closed = np.array(receiver_closed[:, 0] * fix_flow,
                                       dtype=bool)
    second_column_has_closed = np.array(receiver_closed[:, 1] * fix_flow,
                                        dtype=bool)

    # remove the link to the closed node
    receivers[first_column_has_closed, 0] = -1
    receivers[second_column_has_closed, 1] = -1

    # change the proportions
    proportions[first_column_has_closed, 0] = 0.
    proportions[first_column_has_closed, 1] = 1.

    proportions[second_column_has_closed, 0] = 1.
    proportions[second_column_has_closed, 1] = 0.

    # set properties of drains to self.
    receivers[drains_to_self, 0] = node_id[drains_to_self]
    receivers[drains_to_self, 1] = -1

    proportions[drains_to_self, 0] = 1.
    proportions[drains_to_self, 1] = 0.

    # set properties of closed
    receivers[closed_nodes, 0] = node_id[closed_nodes]
    receivers[closed_nodes, 1] = -1
    proportions[closed_nodes, 0] = 1.
    proportions[closed_nodes, 1] = 0.

    # mask the receiver_links by where flow doesn't occur to return
    receiver_links[drains_to_self, :] = UNDEFINED_INDEX

    # identify the steepest link so that the steepest receiver, link, and slope
    # can be returned.
    slope_sort = np.argsort(np.argsort(slopes_to_receivers, axis=1),
                            axis=1) == (num_receivers - 1)
    steepest_slope = slopes_to_receivers[slope_sort]
    steepest_slope[drains_to_self] = 0.

    ## identify the steepest link and steepest receiever.
    steepest_link = receiver_links[slope_sort]
    steepest_link[drains_to_self] = UNDEFINED_INDEX

    steepest_receiver = receivers[slope_sort]
    steepest_receiver[drains_to_self] = node_id[drains_to_self]

    # Optionally, handle baselevel nodes: they are their own receivers
    if baselevel_nodes is not None:
        receivers[baselevel_nodes, 0] = node_id[baselevel_nodes]
        receivers[baselevel_nodes, 1:] = -1
        proportions[baselevel_nodes, 0] = 1
        proportions[baselevel_nodes, 1:] = 0
        receiver_links[baselevel_nodes, :] = UNDEFINED_INDEX
        steepest_slope[baselevel_nodes] = 0.

    # ensure that if there is a -1, it is in the second column.
    order_reversed = receivers[:, 0] == -1

    receivers_out = receivers.copy()
    receivers_out[order_reversed, 1] = receivers[order_reversed, 0]
    receivers_out[order_reversed, 0] = receivers[order_reversed, 1]

    proportions_out = proportions.copy()
    proportions_out[order_reversed, 1] = proportions[order_reversed, 0]
    proportions_out[order_reversed, 0] = proportions[order_reversed, 1]

    slopes_to_receivers_out = slopes_to_receivers.copy()
    slopes_to_receivers_out[order_reversed,
                            1] = slopes_to_receivers[order_reversed, 0]
    slopes_to_receivers_out[order_reversed,
                            0] = slopes_to_receivers[order_reversed, 1]

    receiver_links_out = receiver_links.copy()
    receiver_links_out[order_reversed, 1] = receiver_links[order_reversed, 0]
    receiver_links_out[order_reversed, 0] = receiver_links[order_reversed, 1]

    # The sink nodes are those that are their own receivers (this will normally
    # include boundary nodes as well as interior ones; "pits" would be sink
    # nodes that are also interior nodes).
    (sink, ) = np.where(node_id == receivers[:, 0])
    sink = as_id_array(sink)

    return (
        receivers_out,
        proportions_out,
        slopes_to_receivers_out,
        steepest_slope,
        steepest_receiver,
        sink,
        receiver_links_out,
        steepest_link,
    )
コード例 #28
0
ファイル: stream_power.py プロジェクト: pfeiffea/landlab
    def __init__(
        self,
        grid,
        K_sp=0.001,
        threshold_sp=0.0,
        sp_type="set_mn",
        m_sp=0.5,
        n_sp=1.0,
        a_sp=None,
        b_sp=None,
        c_sp=None,
        channel_width_field=1.0,
        discharge_field="drainage_area",
        erode_flooded_nodes=True,
    ):
        """Initialize the StreamPowerEroder.

        Parameters
        ----------
        grid : ModelGrid
            A grid.
        K_sp : float, array, or field name
            K in the stream power equation (units vary with other parameters).
        threshold_sp : positive float, array, or field name, optional
            The threshold stream power, below which no erosion occurs. This
            threshold is assumed to be in "stream power" units, i.e., if
            sp_type is 'Shear_stress', the value should be tau**a.
        sp_type : {'set_mn', 'Total', 'Unit', 'Shear_stress'}
            Controls how the law is implemented. If 'set_mn', use the supplied
            values of m_sp and n_sp. Else, component will derive values of m and n
            from supplied values of a_sp, b_sp, and c_sp, following Whipple and
            Tucker:

            *  If ``'Total'``, ``m = a * c``, ``n = a``.
            *  If ``'Unit'``, ``m = a * c *(1 - b)``, ``n = a``.
            *  If ``'Shear_stress'``, ``m = 2 * a * c * (1 - b) / 3``,
               ``n = 2 * a / 3``.

        m_sp : float, optional
            m in the stream power equation (power on drainage area). Overridden if
            a_sp, b_sp, and c_sp are supplied.
        n_sp : float, optional, ~ 0.5<n_sp<4.
            n in the stream power equation (power on slope). Overridden if
            a_sp, b_sp, and c_sp are supplied.
        a_sp : float, optional
            The power on the SP/shear term to get the erosion rate; the "erosional
            process" term. Only used if sp_type is not 'set_mn'.
        b_sp : float, optional
            The power on discharge to get width; the "hydraulic geometry" term.
            Only used if sp_type in ('Unit', 'Shear_stress').
        c_sp : float, optional
            The power on area to get discharge; the "basin hydology" term. Only
            used if sp_type is not 'set_mn'.
        channel_width_field : None, float, array, or field name, optional
            If not None, component will look for node-centered data describing
            channel width or if an array, will take the array as the channel
            widths. It will use the widths to implement incision ~ stream power
            per unit width. If sp_type is 'set_mn', follows the equation given
            above. If sp_type in ('Unit', 'Shear_stress'), the width value will
            be implemented directly. W has no effect if sp_type is 'Total'.
        discharge_field : float, field name, or array, optional
            Discharge [L^2/T]. The default is to use the grid field
            'drainage_area'. To use custom spatially/temporally varying
            rainfall, use 'water__unit_flux_in' to specify water input to the
            FlowAccumulator and use "surface_water__discharge" for this
            keyword argument.
        erode_flooded_nodes : bool (optional)
            Whether erosion occurs in flooded nodes identified by a
            depression/lake mapper (e.g., DepressionFinderAndRouter). When set
            to false, the field *flood_status_code* must be present on the grid
            (this is created by the DepressionFinderAndRouter). Default True.
        """
        super().__init__(grid)

        if "flow__receiver_node" in grid.at_node:
            if grid.at_node["flow__receiver_node"].size != grid.size("node"):
                msg = (
                    "A route-to-multiple flow director has been "
                    "run on this grid. The landlab development team has not "
                    "verified that StreamPowerEroder is compatible with "
                    "route-to-multiple methods. Please open a GitHub Issue "
                    "to start this process.")
                raise NotImplementedError(msg)

        if not erode_flooded_nodes:
            if "flood_status_code" not in self._grid.at_node:
                msg = (
                    "In order to not erode flooded nodes another component "
                    "must create the field *flood_status_code*. You want to "
                    "run a lake mapper/depression finder.")
                raise ValueError(msg)

        self._erode_flooded_nodes = erode_flooded_nodes

        self._A = return_array_at_node(grid, discharge_field)
        self._elevs = return_array_at_node(grid, "topographic__elevation")
        self._sp_crit = return_array_at_node(grid, threshold_sp)

        # use setter for K defined below
        self.K = K_sp

        assert np.all(self._sp_crit >= 0.0)

        if discharge_field == "drainage_area":
            self._use_Q = False
        else:
            self._use_Q = True

        if channel_width_field is None:
            self._use_W = False
        else:
            self._use_W = True
            self._W = return_array_at_node(grid, channel_width_field)

        if np.any(threshold_sp != 0.0):
            self._set_threshold = True
            # ^flag for sed_flux_dep_incision to see if the threshold was
            # manually set.
        else:
            self._set_threshold = False

        self._type = sp_type
        if sp_type == "set_mn":
            assert (float(m_sp) >= 0.0) and (float(n_sp) >=
                                             0.0), "m and n must be positive"
            self._m = float(m_sp)
            self._n = float(n_sp)
            assert (
                (a_sp is None) and (b_sp is None) and (c_sp is None)
            ), "If sp_type is 'set_mn', do not pass values for a, b, or c!"
        else:
            assert sp_type in ("Total", "Unit", "Shear_stress"), (
                "sp_type not recognised. It must be 'set_mn', 'Total', " +
                "'Unit', or 'Shear_stress'.")
            assert (m_sp == 0.5 and n_sp
                    == 1.0), "Do not set m and n if sp_type is not 'set_mn'!"
            assert float(a_sp) >= 0.0, "a must be positive"
            self._a = float(a_sp)
            if b_sp is not None:
                assert float(b_sp) >= 0.0, "b must be positive"
                self._b = float(b_sp)
            else:
                assert self._use_W, "b was not set"
                self._b = 0.0
            if c_sp is not None:
                assert float(c_sp) >= 0.0, "c must be positive"
                self._c = float(c_sp)
            else:
                assert self._use_Q, "c was not set"
                self._c = 1.0
            if self._type == "Total":
                self._n = self._a
                self._m = self._a * self._c  # ==_a if use_Q
            elif self._type == "Unit":
                self._n = self._a
                self._m = self._a * self._c * (1.0 - self._b)
                # ^ ==_a iff use_Q&use_W etc
            elif self._type == "Shear_stress":
                self._m = 2.0 * self._a * self._c * (1.0 - self._b) / 3.0
                self._n = 2.0 * self._a / 3.0
            else:
                raise MissingKeyError(
                    "Not enough information was provided on the exponents to use!"
                )

        # m and n will always be set, but care needs to be taken to include Q
        # and W directly if appropriate

        self._stream_power_erosion = self._grid.zeros(centering="node")
        self._alpha = self._grid.zeros("node")
コード例 #29
0
    def __init__(self,
                 grid,
                 m_sp=None,
                 n_sp=None,
                 phi=None,
                 F_f=None,
                 v_s=None,
                 discharge_field='surface_water__discharge',
                 dt_min=DEFAULT_MINIMUM_TIME_STEP):
        """Initialize the ErosionDeposition model.

        Parameters
        ----------
        grid : ModelGrid
            Landlab ModelGrid object
        m_sp : float
            Drainage area exponent (units vary)
        n_sp : float
            Slope exponent (units vary)
        phi : float
            Sediment porosity [-].
        F_f : float
            Fraction of eroded material that turns into "fines" that do not
            contribute to (coarse) sediment load. Defaults to zero.
        v_s : float
            Effective settling velocity for chosen grain size metric [L/T].
        discharge_field : float, field name, or array
            Discharge [L^2/T].
        dt_min : float, optional
            Only applies when adaptive solver is used. Minimum timestep that
            adaptive solver will use when subdividing unstable timesteps.
            Default values is 0.001. [T].
        """
        super(_GeneralizedErosionDeposition, self).__init__(grid)

        self.flow_receivers = grid.at_node['flow__receiver_node']
        self.stack = grid.at_node['flow__upstream_node_order']
        self.topographic__elevation = grid.at_node['topographic__elevation']
        self.slope = grid.at_node['topographic__steepest_slope']
        self.link_to_reciever = grid.at_node['flow__link_to_receiver_node']
        self.cell_area_at_node = grid.cell_area_at_node

        if isinstance(grid, RasterModelGrid):
            self.link_lengths = grid.length_of_d8
        else:
            self.link_lengths = grid.length_of_link

        if 'sediment__flux' in grid.at_node:
            self.qs = grid.at_node['sediment__flux']
        else:
            self.qs = grid.add_zeros('sediment__flux', at='node', dtype=float)

        self.q = return_array_at_node(grid, discharge_field)

        # Create arrays for sediment influx at each node, discharge to the
        # power "m", and deposition rate
        self.qs_in = np.zeros(grid.number_of_nodes)
        self.Q_to_the_m = np.zeros(grid.number_of_nodes)
        self.S_to_the_n = np.zeros(grid.number_of_nodes)
        self.depo_rate = np.zeros(grid.number_of_nodes)

        # store other constants
        self.m_sp = float(m_sp)
        self.n_sp = float(n_sp)
        self.phi = float(phi)
        self.v_s = float(v_s)
        self.dt_min = dt_min
        self.F_f = float(F_f)

        if phi >= 1.0:
            raise ValueError('Porosity must be < 1.0')

        if F_f > 1.0:
            raise ValueError('Fraction of fines must be <= 1.0')

        if phi < 0.0:
            raise ValueError('Porosity must be > 0.0')

        if F_f < 0.0:
            raise ValueError('Fraction of fines must be > 0.0')
コード例 #30
0
ファイル: stream_power.py プロジェクト: pfeiffea/landlab
 def K(self, new_val):
     self._K = return_array_at_node(self._grid, new_val)
コード例 #31
0
ファイル: flow_accumulator.py プロジェクト: awickert/landlab
    def __init__(self,
                 grid,
                 surface='topographic__elevation',
                 flow_director='FlowDirectorSteepest',
                 runoff_rate=None,
                 depression_finder=None,
                 **kwargs):
        """
        Initialize the FlowAccumulator component.

        Saves the grid, tests grid type, tests imput types and compatability
        for the flow_director and depression_finder keyword arguments, tests
        the argument of runoff_rate, and initializes new fields.
        """
        super(FlowAccumulator, self).__init__(grid)
        # Keep a local reference to the grid
        self._grid = grid

        # Grid type testing
        self._is_raster = isinstance(self._grid, RasterModelGrid)
        self._is_Voroni = isinstance(self._grid, VoronoiDelaunayGrid)

        self.kwargs = kwargs
        # STEP 1: Testing of input values, supplied either in function call or
        # as part of the grid.
        self._test_water_inputs(grid, runoff_rate)

        # save elevations and node_cell_area to class properites.
        self.surface = surface
        self.surface_values = return_array_at_node(grid, surface)

        node_cell_area = self._grid.cell_area_at_node.copy()
        node_cell_area[self._grid.closed_boundary_nodes] = 0.

        self.node_cell_area = node_cell_area

        # STEP 2:
        # identify Flow Director method, save name, import and initialize the correct
        # flow director component if necessary
        self._add_director(flow_director)
        self._add_depression_finder(depression_finder)

        # This component will track of the following variables.
        # Attempt to create each, if they already exist, assign the existing
        # version to the local copy.

        #   - drainage area at each node
        #   - receiver of each node
        #   - D array
        #   - delta array
        #   - missing nodes in stack.
        try:
            self.drainage_area = grid.add_zeros('drainage_area', at='node',
                                                dtype=float)
        except FieldError:
            self.drainage_area = grid.at_node['drainage_area']

        try:
            self.discharges = grid.add_zeros('surface_water__discharge',
                                             at='node', dtype=float)
        except FieldError:
            self.discharges = grid.at_node['surface_water__discharge']

        try:
            self.upstream_ordered_nodes = grid.add_field('flow__upstream_node_order',
                                                         BAD_INDEX_VALUE*grid.ones(at='node', dtype=int),
                                                         at='node', dtype=int)

        except FieldError:
            self.upstream_ordered_nodes = grid.at_node[
                'flow__upstream_node_order']

        try:
            self.delta_structure = grid.add_field('flow__data_structure_delta',
                                                  BAD_INDEX_VALUE*grid.ones(at='node', dtype=int),
                                                  at='node', dtype=int)
        except FieldError:
            self.delta_structure = grid.at_node['flow__data_structure_delta']

        try:

            if self.flow_director.to_n_receivers == 'many' and self._is_raster:
                # needs to be BAD_INDEX_VALUE
                self.D_structure = grid.add_field('flow__data_structure_D',
                                                  BAD_INDEX_VALUE*np.ones((self._grid.number_of_links, 2),
                                                  dtype=int),
                                                  at='link',
                                                  dtype=int,
                                                  noclobber=False)
            else:

                # needs to be BAD_INDEX_VALUE
                self.D_structure = grid.add_field('flow__data_structure_D',
                                                  BAD_INDEX_VALUE*grid.ones(at='link'),
                                                  at='link', dtype=int)
        except FieldError:
            self.D_structure = grid.at_link['flow__data_structure_D']

        self.nodes_not_in_stack = True
コード例 #32
0
    def __init__(
        self,
        grid,
        m_sp,
        n_sp,
        F_f,
        v_s,
        discharge_field="surface_water__discharge",
        dt_min=DEFAULT_MINIMUM_TIME_STEP,
    ):
        """Initialize the GeneralizedErosionDeposition model.

        Parameters
        ----------
        grid : ModelGrid
            Landlab ModelGrid object
        m_sp : float
            Discharge exponent (units vary)
        n_sp : float
            Slope exponent (units vary)
        F_f : float
            Fraction of eroded material that turns into "fines" that do not
            contribute to (coarse) sediment load. Defaults to zero.
        v_s : float
            Effective settling velocity for chosen grain size metric [L/T].
        discharge_field : float, field name, or array
            Discharge [L^2/T].
        dt_min : float, optional
            Only applies when adaptive solver is used. Minimum timestep that
            adaptive solver will use when subdividing unstable timesteps.
            Default values is 0.001. [T].
        """
        super().__init__(grid)

        self._flow_receivers = grid.at_node["flow__receiver_node"]
        self._stack = grid.at_node["flow__upstream_node_order"]
        self._topographic__elevation = grid.at_node["topographic__elevation"]
        self._slope = grid.at_node["topographic__steepest_slope"]
        self._link_to_reciever = grid.at_node["flow__link_to_receiver_node"]
        self._cell_area_at_node = grid.cell_area_at_node

        if isinstance(grid, RasterModelGrid):
            self._link_lengths = grid.length_of_d8
        else:
            self._link_lengths = grid.length_of_link

        self.initialize_output_fields()

        self._qs = grid.at_node["sediment__flux"]
        self._q = return_array_at_node(grid, discharge_field)

        # Create arrays for sediment influx at each node, discharge to the
        # power "m", and deposition rate
        self._qs_in = np.zeros(grid.number_of_nodes)
        self._Q_to_the_m = np.zeros(grid.number_of_nodes)
        self._S_to_the_n = np.zeros(grid.number_of_nodes)
        self._depo_rate = np.zeros(grid.number_of_nodes)

        # store other constants
        self._m_sp = float(m_sp)
        self._n_sp = float(n_sp)
        self._v_s = float(v_s)
        self._dt_min = dt_min
        self._F_f = float(F_f)

        if F_f > 1.0:
            raise ValueError("Fraction of fines must be <= 1.0")

        if F_f < 0.0:
            raise ValueError("Fraction of fines must be > 0.0")
コード例 #33
0
ファイル: flow_accumulator.py プロジェクト: landlab/landlab
    def __init__(
        self,
        grid,
        surface="topographic__elevation",
        flow_director="FlowDirectorSteepest",
        runoff_rate=None,
        depression_finder=None,
        **kwargs
    ):
        """Initialize the FlowAccumulator component.

        Saves the grid, tests grid type, tests imput types and
        compatability for the flow_director and depression_finder
        keyword arguments, tests the argument of runoff_rate, and
        initializes new fields.
        """
        super(FlowAccumulator, self).__init__(grid)
        # Keep a local reference to the grid
        self._grid = grid

        # Grid type testing
        self._is_raster = isinstance(self._grid, RasterModelGrid)
        self._is_Voroni = isinstance(self._grid, VoronoiDelaunayGrid)
        self._is_Network = isinstance(self._grid, NetworkModelGrid)
        self.kwargs = kwargs
        # STEP 1: Testing of input values, supplied either in function call or
        # as part of the grid.
        self._test_water_inputs(grid, runoff_rate)

        # save elevations and node_cell_area to class properites.
        self.surface = surface
        self.surface_values = return_array_at_node(grid, surface)

        if self._is_Network:
            try:
                node_cell_area = self._grid.at_node["cell_area_at_node"]
            except FieldError:
                raise FieldError(
                    "In order for the FlowAccumulator to work, the "
                    "grid must have an at-node field called "
                    "cell_area_at_node."
                )
        else:
            node_cell_area = self._grid.cell_area_at_node.copy()
            node_cell_area[self._grid.closed_boundary_nodes] = 0.0

        self.node_cell_area = node_cell_area

        # STEP 2:
        # identify Flow Director method, save name, import and initialize the correct
        # flow director component if necessary
        self._add_director(flow_director)
        self._add_depression_finder(depression_finder)

        # This component will track of the following variables.
        # Attempt to create each, if they already exist, assign the existing
        # version to the local copy.

        #   - drainage area at each node
        #   - receiver of each node
        #   - D array
        #   - delta array
        #   - missing nodes in stack.
        if "drainage_area" not in grid.at_node:
            self.drainage_area = grid.add_zeros("drainage_area", at="node", dtype=float)
        else:
            self.drainage_area = grid.at_node["drainage_area"]

        if "surface_water__discharge" not in grid.at_node:
            self.discharges = grid.add_zeros(
                "surface_water__discharge", at="node", dtype=float
            )
        else:
            self.discharges = grid.at_node["surface_water__discharge"]

        if "flow__upstream_node_order" not in grid.at_node:
            self.upstream_ordered_nodes = grid.add_field(
                "flow__upstream_node_order",
                BAD_INDEX_VALUE * grid.ones(at="node", dtype=int),
                at="node",
                dtype=int,
            )
        else:
            self.upstream_ordered_nodes = grid.at_node["flow__upstream_node_order"]

        if "flow__data_structure_delta" not in grid.at_node:
            self.delta_structure = grid.add_field(
                "flow__data_structure_delta",
                BAD_INDEX_VALUE * grid.ones(at="node", dtype=int),
                at="node",
                dtype=int,
            )
        else:
            self.delta_structure = grid.at_node["flow__data_structure_delta"]

        try:
            D = BAD_INDEX_VALUE * grid.ones(at="link", dtype=int)
            D_structure = np.array([D], dtype=object)
            self.D_structure = grid.add_field(
                "flow__data_structure_D",
                D_structure,
                at="grid",
                dtype=object,
                noclobber=False,
            )

        except FieldError:
            self.D_structure = grid.at_grid["flow__data_structure_D"]

        self.nodes_not_in_stack = True
コード例 #34
0
ファイル: space.py プロジェクト: zrduan/landlab
    def __init__(self,
                 grid,
                 K_sed=None,
                 K_br=None,
                 F_f=None,
                 phi=None,
                 H_star=None,
                 v_s=None,
                 m_sp=None,
                 n_sp=None,
                 sp_crit_sed=None,
                 sp_crit_br=None,
                 discharge_field='surface_water__discharge',
                 solver='basic',
                 dt_min=DEFAULT_MINIMUM_TIME_STEP,
                 **kwds):
        """Initialize the Space model.

        """
        if (grid.at_node['flow__receiver_node'].size != grid.size('node')):
            msg = ('A route-to-multiple flow director has been '
                   'run on this grid. The landlab development team has not '
                   'verified that SPACE is compatible with '
                   'route-to-multiple methods. Please open a GitHub Issue '
                   'to start this process.')
            raise NotImplementedError(msg)

        super(Space, self).__init__(grid,
                                    m_sp=m_sp,
                                    n_sp=n_sp,
                                    phi=phi,
                                    F_f=F_f,
                                    v_s=v_s,
                                    dt_min=dt_min,
                                    discharge_field=discharge_field)

        self._grid = grid  #store grid

        # space specific inits
        self.H_star = H_star

        if 'soil__depth' in grid.at_node:
            self.soil__depth = grid.at_node['soil__depth']
        else:
            self.soil__depth = grid.add_zeros('soil__depth',
                                              at='node',
                                              dtype=float)

        if 'bedrock__elevation' in grid.at_node:
            self.bedrock__elevation = grid.at_node['bedrock__elevation']
        else:
            self.bedrock__elevation = grid.add_zeros('bedrock__elevation',
                                                     at='node',
                                                     dtype=float)
            self.bedrock__elevation[:] = self.topographic__elevation -\
                self.soil__depth

        self.Es = np.zeros(grid.number_of_nodes)
        self.Er = np.zeros(grid.number_of_nodes)

        #K's and critical values can be floats, grid fields, or arrays
        self.K_sed = return_array_at_node(grid, K_sed)
        self.K_br = return_array_at_node(grid, K_br)

        self.sp_crit_sed = return_array_at_node(grid, sp_crit_sed)
        self.sp_crit_br = return_array_at_node(grid, sp_crit_br)

        # Handle option for solver
        if solver == 'basic':
            self.run_one_step = self.run_one_step_basic
        elif solver == 'adaptive':
            self.run_one_step = self.run_with_adaptive_time_step_solver
            self.time_to_flat = np.zeros(grid.number_of_nodes)
            self.porosity_factor = 1.0 / (1.0 - self.phi)
        else:
            raise ValueError("Parameter 'solver' must be one of: " +
                             "'basic', 'adaptive'")
コード例 #35
0
 def rock_id(self, rock_id):
     return_array_at_node(self._grid, rock_id)  # verify that this will work.
     # verify that all rock types are valid
     self._rock_id = rock_id
コード例 #36
0
    def __init__(
        self,
        grid,
        surface="topographic__elevation",
        method="D8",
        fill_flat=False,
        ignore_overfill=False,
    ):
        """Initialise the component.

        Parameters
        ----------
        grid : ModelGrid
            A grid.
        surface : field name at node or array of length node
            The surface to fill.
        method : {'Steepest', 'D8'}
            Whether or not to recognise diagonals as valid flow paths, if a raster.
            Otherwise, no effect.
        fill_flat : bool
            If True, pits will be filled to perfectly horizontal. If False, the new
            surface will be slightly inclined (at machine precision) to give
            steepest descent flow paths to the outlet, once they are calculated.
        ignore_overfill : bool
            If True, suppresses the Error that would normally be raised during
            creation of a gentle incline on a fill surface (i.e., if not
            fill_flat). Typically this would happen on a synthetic DEM where more
            than one outlet is possible at the same elevation. If True, the
            was_there_overfill property can still be used to see if this has
            occurred.

        """
        if "flow__receiver_node" in grid.at_node:
            if grid.at_node["flow__receiver_node"].size != grid.size("node"):
                msg = (
                    "A route-to-multiple flow director has been "
                    "run on this grid. The landlab development team has not "
                    "verified that SinkFillerBarnes is compatible with "
                    "route-to-multiple methods. Please open a GitHub Issue "
                    "to start this process."
                )
                raise NotImplementedError(msg)

        # Most of the functionality of this component is directly inherited
        # from SinkFillerBarnes, so
        super().__init__(
            grid,
            surface=surface,
            method=method,
            fill_flat=fill_flat,
            fill_surface=surface,
            redirect_flow_steepest_descent=False,
            reaccumulate_flow=False,
            ignore_overfill=ignore_overfill,
            track_lakes=True,
        )
        # note we will always track the fills, since we're only doing this
        # once... Likewise, no need for flow routing; this is not going to
        # get used dynamically.
        self._supplied_surface = return_array_at_node(grid, surface).copy()
        # create the only new output field:
        self._sed_fill_depth = self._grid.add_zeros(
            "node", "sediment_fill__depth", clobber=True
        )
コード例 #37
0
ファイル: test_return_grid.py プロジェクト: landlab/landlab
def test_no_field():
    mg = RasterModelGrid((10, 10))
    with pytest.raises(FieldError):
        return_array_at_node(mg, "spam")
コード例 #38
0
    def __init__(
        self,
        grid,
        K_sed=0.02,
        K_br=0.02,
        F_f=0.0,
        phi=0.3,
        H_star=0.1,
        v_s=1.0,
        m_sp=0.5,
        n_sp=1.0,
        sp_crit_sed=0.0,
        sp_crit_br=0.0,
        discharge_field="surface_water__discharge",
        solver="basic",
        erode_flooded_nodes=True,
        dt_min=DEFAULT_MINIMUM_TIME_STEP,
    ):
        """Initialize the Space model.

        Parameters
        ----------
        grid : ModelGrid
            Landlab ModelGrid object
        K_sed : float, field name, or array
            Erodibility for sediment (units vary).
        K_br : float, field name, or array
            Erodibility for bedrock (units vary).
        F_f : float
            Fraction of permanently suspendable fines in bedrock [-].
        phi : float
            Sediment porosity [-].
        H_star : float
            Sediment thickness required for full entrainment [L].
        v_s : float
            Effective settling velocity for chosen grain size metric [L/T].
        m_sp : float
            Drainage area exponent (units vary)
        n_sp : float
            Slope exponent (units vary)
        sp_crit_sed : float, field name, or array
            Critical stream power to erode sediment [E/(TL^2)]
        sp_crit_br : float, field name, or array
            Critical stream power to erode rock [E/(TL^2)]
        discharge_field : float, field name, or array
            Discharge [L^2/T]. The default is to use the grid field
            'surface_water__discharge', which is simply drainage area
            multiplied by the default rainfall rate (1 m/yr). To use custom
            spatially/temporally varying rainfall, use 'water__unit_flux_in'
            to specify water input to the FlowAccumulator.
        erode_flooded_nodes : bool (optional)
            Whether erosion occurs in flooded nodes identified by a
            depression/lake mapper (e.g., DepressionFinderAndRouter). When set
            to false, the field *flood_status_code* must be present on the grid
            (this is created by the DepressionFinderAndRouter). Default True.
        solver : string
            Solver to use. Options at present include:
                (1) 'basic' (default): explicit forward-time extrapolation.
                    Simple but will become unstable if time step is too large.
                (2) 'adaptive': subdivides global time step as needed to
                    prevent slopes from reversing and alluvium from going
                    negative.

        """
        if grid.at_node["flow__receiver_node"].size != grid.size("node"):
            msg = ("A route-to-multiple flow director has been "
                   "run on this grid. The landlab development team has not "
                   "verified that SPACE is compatible with "
                   "route-to-multiple methods. Please open a GitHub Issue "
                   "to start this process.")
            raise NotImplementedError(msg)

        super(Space, self).__init__(
            grid,
            m_sp=m_sp,
            n_sp=n_sp,
            phi=phi,
            F_f=F_f,
            v_s=v_s,
            dt_min=dt_min,
            discharge_field=discharge_field,
            erode_flooded_nodes=erode_flooded_nodes,
        )

        # space specific inits
        self._H_star = H_star
        self._soil__depth = grid.at_node["soil__depth"]

        if "bedrock__elevation" in grid.at_node:
            self._bedrock__elevation = grid.at_node["bedrock__elevation"]
        else:
            self._bedrock__elevation = grid.add_zeros("bedrock__elevation",
                                                      at="node",
                                                      dtype=float)

            self._bedrock__elevation[:] = (self._topographic__elevation -
                                           self._soil__depth)

        self._Es = np.zeros(grid.number_of_nodes)
        self._Er = np.zeros(grid.number_of_nodes)

        # K's and critical values can be floats, grid fields, or arrays
        # use setters defined below
        self.K_sed = K_sed
        self.K_br = K_br

        self._sp_crit_sed = return_array_at_node(grid, sp_crit_sed)
        self._sp_crit_br = return_array_at_node(grid, sp_crit_br)

        # Handle option for solver
        if solver == "basic":
            self.run_one_step = self.run_one_step_basic
        elif solver == "adaptive":
            self.run_one_step = self.run_with_adaptive_time_step_solver
            self._time_to_flat = np.zeros(grid.number_of_nodes)
            self._porosity_factor = 1.0 / (1.0 - self._phi)
        else:
            raise ValueError("Parameter 'solver' must be one of: " +
                             "'basic', 'adaptive'")
コード例 #39
0
    def __init__(
        self,
        grid,
        K_sp=0.001,
        m_sp=0.5,
        n_sp=1.0,
        threshold_sp=0.0,
        discharge_field="drainage_area",
        erode_flooded_nodes=True,
    ):
        """Initialize the Fastscape stream power component. Note: a timestep,
        dt, can no longer be supplied to this component through the input file.
        It must instead be passed directly to the run method.

        Parameters
        ----------
        grid : ModelGrid
            A grid.
        K_sp : float, array, or field name
            K in the stream power equation (units vary with other parameters).
        m_sp : float, optional
            m in the stream power equation (power on drainage area).
        n_sp : float, optional
            n in the stream power equation (power on slope).
        threshold_sp : float, array, or field name
            Erosion threshold in the stream power equation.
        discharge_field : float, field name, or array, optional
            Discharge [L^2/T]. The default is to use the grid field
            'drainage_area'. To use custom spatially/temporally varying
            rainfall, use 'water__unit_flux_in' to specify water input to the
            FlowAccumulator and use "surface_water__discharge" for this
            keyword argument.
        erode_flooded_nodes : bool (optional)
            Whether erosion occurs in flooded nodes identified by a
            depression/lake mapper (e.g., DepressionFinderAndRouter). When set
            to false, the field *flood_status_code* must be present on the grid
            (this is created by the DepressionFinderAndRouter). Default True.
        """
        super().__init__(grid)

        if "flow__receiver_node" in grid.at_node:
            if grid.at_node["flow__receiver_node"].size != grid.size("node"):
                msg = (
                    "A route-to-multiple flow director has been "
                    "run on this grid. The landlab development team has not "
                    "verified that FastscapeEroder is compatible with "
                    "route-to-multiple methods. Please open a GitHub Issue "
                    "to start this process.")
                raise NotImplementedError(msg)

        if not erode_flooded_nodes:
            if "flood_status_code" not in self._grid.at_node:
                msg = (
                    "In order to not erode flooded nodes another component "
                    "must create the field *flood_status_code*. You want to "
                    "run a lake mapper/depression finder.")
                raise ValueError(msg)

        self._erode_flooded_nodes = erode_flooded_nodes

        # use setter for K defined below
        self.K = K_sp

        self._m = float(m_sp)
        self._n = float(n_sp)

        if isinstance(threshold_sp, (float, int)):
            self._thresholds = float(threshold_sp)
        else:
            self._thresholds = return_array_at_node(grid, threshold_sp)

        self._A = return_array_at_node(grid, discharge_field)

        # make storage variables
        self._A_to_the_m = grid.zeros(at="node")
        self._alpha = grid.empty(at="node")
コード例 #40
0
 def K_sed(self, new_val):
     self._K_sed = return_array_at_node(self._grid, new_val)
コード例 #41
0
    def __init__(
        self,
        grid,
        m_sp=None,
        n_sp=None,
        phi=None,
        F_f=None,
        v_s=None,
        discharge_field="surface_water__discharge",
        dt_min=DEFAULT_MINIMUM_TIME_STEP,
    ):
        """Initialize the ErosionDeposition model.

        Parameters
        ----------
        grid : ModelGrid
            Landlab ModelGrid object
        m_sp : float
            Discharge exponent (units vary)
        n_sp : float
            Slope exponent (units vary)
        phi : float
            Sediment porosity [-].
        F_f : float
            Fraction of eroded material that turns into "fines" that do not
            contribute to (coarse) sediment load. Defaults to zero.
        v_s : float
            Effective settling velocity for chosen grain size metric [L/T].
        discharge_field : float, field name, or array
            Discharge [L^2/T].
        dt_min : float, optional
            Only applies when adaptive solver is used. Minimum timestep that
            adaptive solver will use when subdividing unstable timesteps.
            Default values is 0.001. [T].
        """
        super(_GeneralizedErosionDeposition, self).__init__(grid)

        self.flow_receivers = grid.at_node["flow__receiver_node"]
        self.stack = grid.at_node["flow__upstream_node_order"]
        self.topographic__elevation = grid.at_node["topographic__elevation"]
        self.slope = grid.at_node["topographic__steepest_slope"]
        self.link_to_reciever = grid.at_node["flow__link_to_receiver_node"]
        self.cell_area_at_node = grid.cell_area_at_node

        if isinstance(grid, RasterModelGrid):
            self.link_lengths = grid.length_of_d8
        else:
            self.link_lengths = grid.length_of_link

        if "sediment__flux" in grid.at_node:
            self.qs = grid.at_node["sediment__flux"]
        else:
            self.qs = grid.add_zeros("sediment__flux", at="node", dtype=float)

        self.q = return_array_at_node(grid, discharge_field)

        # Create arrays for sediment influx at each node, discharge to the
        # power "m", and deposition rate
        self.qs_in = np.zeros(grid.number_of_nodes)
        self.Q_to_the_m = np.zeros(grid.number_of_nodes)
        self.S_to_the_n = np.zeros(grid.number_of_nodes)
        self.depo_rate = np.zeros(grid.number_of_nodes)

        # store other constants
        self.m_sp = float(m_sp)
        self.n_sp = float(n_sp)
        self.phi = float(phi)
        self.v_s = float(v_s)
        self.dt_min = dt_min
        self.F_f = float(F_f)

        if phi >= 1.0:
            raise ValueError("Porosity must be < 1.0")

        if F_f > 1.0:
            raise ValueError("Fraction of fines must be <= 1.0")

        if phi < 0.0:
            raise ValueError("Porosity must be > 0.0")

        if F_f < 0.0:
            raise ValueError("Fraction of fines must be > 0.0")