Beispiel #1
0
def test_degenerate_drainage():
    """
    This "hourglass" configuration should be one of the hardest to correctly
    re-route.
    """
    mg = RasterModelGrid(9,5)
    z_init = mg.node_x.copy()*0.0001 + 1.
    lake_pits = np.array([7, 11, 12, 13, 17, 27, 31, 32, 33, 37])
    z_init[lake_pits] = -1.
    z_init[22] = 0.  # the common spill pt for both lakes
    z_init[21] = 0.1  # an adverse bump in the spillway
    z_init[20] = -0.2  # the spillway
    z = mg.add_field('node', 'topographic__elevation', z_init)

    fr = FlowRouter(mg)
    lf = DepressionFinderAndRouter(mg)
    fr.route_flow()
    lf.map_depressions()

    correct_A = np.array([ 0.,   0.,   0.,   0.,   0.,
                           0.,   1.,   1.,   1.,   0.,
                           0.,   4.,   1.,   3.,   0.,
                           0.,   1.,  10.,   1.,   0.,
                          21.,  21.,   1.,   1.,   0.,
                           0.,   1.,   9.,   1.,   0.,
                           0.,   4.,   1.,   3.,   0.,
                           0.,   1.,   1.,   1.,   0.,
                           0.,   0.,   0.,   0.,   0.])
    
    thelake = np.concatenate((lake_pits, [22])).sort()

    assert_array_almost_equal(mg.at_node['drainage_area'], correct_A)
Beispiel #2
0
def test_three_pits():
    """
    A test to ensure the component correctly handles cases where there are
    multiple pits.
    """
    mg = RasterModelGrid(10,10,1.)
    z = mg.add_field('node', 'topographic__elevation', mg.node_x.copy())
    # a sloping plane
    #np.random.seed(seed=0)
    #z += np.random.rand(100)/10000.
    # punch some holes
    z[33] = 1.
    z[43] = 1.
    z[37] = 4.
    z[74:76] = 1.
    fr = FlowRouter(mg)
    lf = DepressionFinderAndRouter(mg)
    fr.route_flow()
    lf.map_depressions()
    
    flow_sinks_target = np.zeros(100, dtype=bool)
    flow_sinks_target[mg.boundary_nodes] = True
    # no internal sinks now:
    assert_array_equal(mg.at_node['flow_sinks'], flow_sinks_target)
    
    # test conservation of mass:
    assert_almost_equal(mg.at_node['drainage_area'
                                       ].reshape((10,10))[1:-1,1].sum(), 8.**2)
    # ^all the core nodes
    
    # test the actual flow field:
    nA = np.array([  0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,
                     8.,   8.,   7.,   6.,   5.,   4.,   3.,   2.,   1.,   0.,
                     2.,   2.,   1.,   1.,   2.,   1.,   1.,   1.,   1.,   0.,
                    26.,  26.,  25.,  15.,  11.,  10.,   9.,   8.,   1.,   0.,
                     2.,   2.,   1.,   9.,   2.,   1.,   1.,   1.,   1.,   0.,
                     2.,   2.,   1.,   1.,   5.,   4.,   3.,   2.,   1.,   0.,
                     2.,   2.,   1.,   1.,   1.,   1.,   3.,   2.,   1.,   0.,
                    20.,  20.,  19.,  18.,  17.,  12.,   3.,   2.,   1.,   0.,
                     2.,   2.,   1.,   1.,   1.,   1.,   3.,   2.,   1.,   0.,
                     0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.])
    assert_array_equal(mg.at_node['drainage_area'], nA)
    
    #test a couple more properties:
    lc = np.empty(100, dtype=int)
    lc.fill(XX)
    lc[33] = 33
    lc[43] = 33
    lc[37] = 37
    lc[74:76] = 74
    assert_array_equal(lf.lake_map, lc)
    assert_array_equal(lf.lake_codes, [33, 37, 74])
    assert_equal(lf.number_of_lakes, 3)
    assert_array_almost_equal(lf.lake_areas, [2., 1., 2.])
    assert_array_almost_equal(lf.lake_volumes, [2., 2., 4.])
Beispiel #3
0
def test_internal_closed():
    """
    Test closed nodes in the core of the grid.
    """
    fr = FlowRouter(mg)
    fr.route_flow()
    assert_array_almost_equal(A_target, mg.at_node['drainage_area'])
    assert_array_equal(frcvr_target, mg.at_node['flow_receiver'])
    assert_array_equal(links2rcvr_target, mg.at_node['links_to_flow_receiver'])
    assert_array_almost_equal(A_target, mg.at_node['water__volume_flux'])
    assert_array_almost_equal(steepest_target,
                              mg.at_node['topographic__steepest_slope'])
Beispiel #4
0
def test_accumulate_D8():
    """
    Test accumulation works for D8 in a simple scenario
    """
    fr = FlowRouter(mg)
    fr.route_flow()
    assert_array_equal(A_target, mg.at_node['drainage_area'])
    assert_array_equal(frcvr_target, mg.at_node['flow_receiver'])
    assert_array_equal(upids_target, mg.at_node['upstream_node_order'])
    assert_array_equal(links2rcvr_target, mg.at_node['links_to_flow_receiver'])
    assert_array_equal(A_target, mg.at_node['water__volume_flux'])
    assert_array_equal(steepest_target,
                       mg.at_node['topographic__steepest_slope'])
Beispiel #5
0
def test_irreg_topo():
    """
    Tests D4 routing on a toy irregular topo.
    """
    fr = FlowRouter(mg)
    fr.route_flow(method='D4')
    assert_array_equal(A_target_D4, mg.at_node['drainage_area'])
    assert_array_equal(frcvr_target_D4, mg.at_node['flow_receiver'])
    assert_array_equal(upids_target_D4, mg.at_node['upstream_node_order'])
    assert_array_equal(links2rcvr_target_D4,
                       mg.at_node['links_to_flow_receiver'])
    assert_array_almost_equal(steepest_target_D4,
                              mg.at_node['topographic__steepest_slope'])
Beispiel #6
0
def test_variable_Qin():
    """
    Tests a variable Qin field.
    """
    Qin_local = np.zeros(25, dtype=float)
    Qin_local[13] = 2.
    mg.add_field('node', 'water__volume_flux_in',
                 Qin_local, units='m**3/s')
    fr = FlowRouter(mg)
    fr.route_flow()
    Qout_local = np.zeros_like(Qin_local)
    Qout_local[10:14] = 200.
    assert_array_equal(Qout_local, mg.at_node['water__volume_flux'])
    assert_array_equal(A_target, mg.at_node['drainage_area'])
Beispiel #7
0
    def initialize(self, input_stream=None):
        """
        The BMI-style initialize method takes an optional input_stream
        parameter, which may be either a ModelParameterDictionary object or
        an input stream from which a ModelParameterDictionary can read values.
        """
        # Create a ModelParameterDictionary for the inputs
        if input_stream is None:
            inputs = None
        elif type(input_stream) == ModelParameterDictionary:
            inputs = input_stream
        else:
            inputs = ModelParameterDictionary(input_stream)

        # Make sure the grid includes elevation data. This means either:
        #  1. The grid has a node field called 'topographic__elevation', or
        #  2. The input file has an item called 'ELEVATION_FIELD_NAME' *and*
        #     a field by this name exists in the grid.
        try:
            self._elev = self._grid.at_node['topographic__elevation']
        except FieldError:
            try:
                self.topo_field_name = inputs.read_string('ELEVATION_' +
                                                          'FIELD_NAME')
            except AttributeError:
                print('Error: Because your grid does not have a node field')
                print('called "topographic__elevation", you need to pass the')
                print('name of a text input file or ModelParameterDictionary,')
                print('and this file or dictionary needs to include the name')
                print('of another field in your grid that contains your')
                print('elevation data.')
                raise AttributeError
            except MissingKeyError:
                print('Error: Because your grid does not have a node field')
                print('called "topographic__elevation", your input file (or')
                print('ModelParameterDictionary) must include an entry with')
                print('the key "ELEVATION_FIELD_NAME", which gives the name')
                print('of a field in your grid that contains your elevation')
                print('data.')
                raise MissingKeyError('ELEVATION_FIELD_NAME')
            try:
                self._elev = self._grid.at_node[self.topo_field_name]
            except AttributeError:
                print('Your grid does not seem to have a node field called',
                      self.topo_field_name)
        else:
            self.topo_field_name = 'topographic__elevation'
        # create the only new output field:
        self.sed_fill_depth = self._grid.add_zeros('node',
                                                   'sediment_fill__depth')

        self._lf = DepressionFinderAndRouter(self._grid, routing=self._routing)
        self._fr = FlowRouter(self._grid)
Beispiel #8
0
class SinkFiller(Component):
    """
    This component identifies depressions in a topographic surface, then fills
    them in in the topography.  No attempt is made to conserve sediment mass.
    User may specify whether the holes should be filled to flat, or with a
    gradient downwards towards the depression outlet. The gradient can be
    spatially variable, and is chosen to not reverse any drainage directions
    at the perimeter of each lake.
    """
    _name = 'HoleFiller'

    _input_var_names = set(['topographic__elevation',
                            ])

    _output_var_names = set(['topographic__elevation',
                             'sediment_fill__depth',
                             ])

    _var_units = {'topographic__elevation': 'm',
                  'sediment_fill__depth': 'm',
                  }

    _var_mapping = {'topographic__elevation': 'node',
                    'sediment_fill__depth': 'node',
                    }

    _var_doc = {'topographic__elevation': 'Surface topographic elevation',
                 'sediment_fill__depth': 'Depth of sediment added at each' +
                                         'node',
                 }

    def __init__(self, grid, input_stream=None, current_time=0.,
                 routing='D8'):
        """
        Constructor assigns a copy of the grid, and calls the initialize
        method.

        Parameters
        ----------
        grid : RasterModelGrid
            A landlab RasterModelGrid.
        input_stream : str, file_like, or ModelParameterDictionary, optional
            ModelParameterDictionary that holds the input parameters.
        current_time : float, optional
            The current time for the mapper.
        routing : 'D8' or 'D4' (optional)
            If grid is a raster type, controls whether fill connectivity can
            occur on diagonals ('D8', default), or only orthogonally ('D4').
            Has no effect if grid is not a raster.
        """
        self._grid = grid
        if routing is not 'D8':
            assert routing is 'D4'
        self._routing = routing
        if ((type(self._grid) is landlab.grid.raster.RasterModelGrid) and
                (routing is 'D8')):
            self._D8 = True
            self.num_nbrs = 8
        else:
            self._D8 = False  # useful shorthand for thia test we do a lot
            if type(self._grid) is landlab.grid.raster.RasterModelGrid:
                self.num_nbrs = 4
        self.initialize(input_stream)

    def initialize(self, input_stream=None):
        """
        The BMI-style initialize method takes an optional input_stream
        parameter, which may be either a ModelParameterDictionary object or
        an input stream from which a ModelParameterDictionary can read values.
        """
        # Create a ModelParameterDictionary for the inputs
        if input_stream is None:
            inputs = None
        elif type(input_stream) == ModelParameterDictionary:
            inputs = input_stream
        else:
            inputs = ModelParameterDictionary(input_stream)

        # Make sure the grid includes elevation data. This means either:
        #  1. The grid has a node field called 'topographic__elevation', or
        #  2. The input file has an item called 'ELEVATION_FIELD_NAME' *and*
        #     a field by this name exists in the grid.
        try:
            self._elev = self._grid.at_node['topographic__elevation']
        except FieldError:
            try:
                self.topo_field_name = inputs.read_string('ELEVATION_' +
                                                          'FIELD_NAME')
            except AttributeError:
                print('Error: Because your grid does not have a node field')
                print('called "topographic__elevation", you need to pass the')
                print('name of a text input file or ModelParameterDictionary,')
                print('and this file or dictionary needs to include the name')
                print('of another field in your grid that contains your')
                print('elevation data.')
                raise AttributeError
            except MissingKeyError:
                print('Error: Because your grid does not have a node field')
                print('called "topographic__elevation", your input file (or')
                print('ModelParameterDictionary) must include an entry with')
                print('the key "ELEVATION_FIELD_NAME", which gives the name')
                print('of a field in your grid that contains your elevation')
                print('data.')
                raise MissingKeyError('ELEVATION_FIELD_NAME')
            try:
                self._elev = self._grid.at_node[self.topo_field_name]
            except AttributeError:
                print('Your grid does not seem to have a node field called',
                      self.topo_field_name)
        else:
            self.topo_field_name = 'topographic__elevation'
        # create the only new output field:
        self.sed_fill_depth = self._grid.add_zeros('node',
                                                   'sediment_fill__depth')

        self._lf = DepressionFinderAndRouter(self._grid, routing=self._routing)
        self._fr = FlowRouter(self._grid)

    def fill_pits(self, apply_slope=None):
        """
        This is the main method. Call it to fill depressions in a starting
        topography.

        Parameters
        ----------
        apply_slope : bool
            Whether to leave the filled surface flat (default), or apply a
            gradient downwards through all lake nodes towards the outlet.
            A test is performed to ensure applying this slope will not alter
            the drainage structure at the edge of the filled region (i.e.,
            that we are not accidentally reversing the flow direction far
            from the outlet.)

        Return fields
        -------------
        'topographic__elevation' : the updated elevations
        'sediment_fill__depth' : the depth of sediment added at each node
        """
        self.original_elev = self._elev.copy()
        # We need this, as we'll have to do ALL this again if we manage
        # to jack the elevs too high in one of the "subsidiary" lakes.
        # We're going to implement the lake_mapper component to do the heavy
        # lifting here, then delete its fields. This means we first need to
        # test if these fields already exist, in which case, we should *not*
        # delete them!
        existing_fields = {}
        spurious_fields = set()
        set_of_outputs = self._lf.output_var_names | self._fr.output_var_names
        try:
            set_of_outputs.remove(self.topo_field_name)
        except KeyError:
            pass
        for field in set_of_outputs:
            try:
                existing_fields[field] = self._grid.at_node[field].copy()
            except FieldError:  # not there; good!
                spurious_fields.add(field)

        self._fr.route_flow(method=self._routing)
        self._lf.map_depressions(pits=self._grid.at_node['flow_sinks'],
                                 reroute_flow=True)
        # add the depression depths to get up to flat:
        self._elev += self._grid.at_node['depression__depth']
        # if apply_slope is none, we're now done! But if not...
        if apply_slope:
            # new way of doing this - use the upstream structure! Should be
            # both more general and more efficient
            for (outlet_node, lake_code) in zip(self._lf.lake_outlets,
                                                self._lf.lake_codes):
                lake_nodes = np.where(self._lf.lake_map == lake_code)[0]
                lake_perim = self.get_lake_ext_margin(lake_nodes)
                perim_elevs = self._elev[lake_perim]
                out_elev = self._elev[outlet_node]
                lowest_elev_perim = perim_elevs[perim_elevs != out_elev].min()
                # note we exclude the outlet node
                elev_increment = ((lowest_elev_perim-self._elev[outlet_node]) /
                                  (lake_nodes.size + 2.))
                assert elev_increment > 0.
                all_ordering = self._grid.at_node['upstream_node_order']
                upstream_order_bool = np.in1d(all_ordering, lake_nodes,
                                              assume_unique=True)
                lake_upstream_order = all_ordering[upstream_order_bool]
                argsort_lake = np.argsort(lake_upstream_order)
                elevs_to_add = (np.arange(lake_nodes.size, dtype=float) +
                                1.) * elev_increment
                sorted_elevs_to_add = elevs_to_add[argsort_lake]
                self._elev[lake_nodes] += sorted_elevs_to_add
        # now put back any fields that were present initially, and wipe the
        # rest:
        for delete_me in spurious_fields:
            self._grid.delete_field('node', delete_me)
        for update_me in existing_fields.keys():
            self.grid.at_node[update_me] = existing_fields[update_me]
        # fill the output field
        self.sed_fill_depth[:] = self._elev - self.original_elev

    def fill_pits_old(self, apply_slope=None):
        """

        .. deprecated:: 0.1.38
            Use :func:`fill_pits` instead.

        This is the main method. Call it to fill depressions in a starting
        topography.

        Parameters
        ----------
        apply_slope : None, bool, or float
            If a float is provided this is the slope of the surface down
            towards the lake outlet. Supply a small positive number, e.g.,
            1.e-5 (or True, to use this default value).
            A test is performed to ensure applying this slope will not alter
            the drainage structure at the edge of the filled region (i.e.,
            that we are not accidentally reversing the flow direction far
            from the outlet.) The component will automatically decrease the
            (supplied or default) gradient a number of times to try to
            accommodate this, but will eventually raise an OverflowError
            if it can't deal with it. If you pass True, the method will use
            the default value of 1.e-5.

        Return fields
        -------------
        'topographic__elevation' : the updated elevations
        'sediment_fill__depth' : the depth of sediment added at each node
        """
        self.original_elev = self._elev.copy()
        # We need this, as we'll have to do ALL this again if we manage
        # to jack the elevs too high in one of the "subsidiary" lakes.
        # We're going to implement the lake_mapper component to do the heavy
        # lifting here, then delete its fields. This means we first need to
        # test if these fields already exist, in which case, we should *not*
        # delete them!
        existing_fields = {}
        spurious_fields = set()
        set_of_outputs = self._lf.output_var_names | self._fr.output_var_names
        try:
            set_of_outputs.remove(self.topo_field_name)
        except KeyError:
            pass
        for field in set_of_outputs:
            try:
                existing_fields[field] = self._grid.at_node[field].copy()
            except FieldError:  # not there; good!
                spurious_fields.add(field)

        self._fr.route_flow(method=self._routing)
        self._lf.map_depressions(pits=self._grid.at_node['flow_sinks'],
                                 reroute_flow=False)
        # add the depression depths to get up to flat:
        self._elev += self._grid.at_node['depression__depth']
        # if apply_slope is none, we're now done! But if not...
        if apply_slope is True:
            apply_slope = DEFAULT_SLOPE
        elif type(apply_slope) in (float, int):
            assert apply_slope >= 0.
        if apply_slope:
            # this isn't very efficient, but OK as we're only running this
            # code ONCE in almost all use cases
            sublake = False
            unstable = True
            stability_increment = 0
            self.lake_nodes_treated = np.array([], dtype=int)
            while unstable:
                while 1:
                    for (outlet_node, lake_code) in zip(self._lf.lake_outlets,
                                                        self._lf.lake_codes):
                        self.apply_slope_current_lake(apply_slope, outlet_node,
                                                      lake_code, sublake)
                    # Call the mapper again here. Bail out if no core pits are
                    # found.
                    # This is necessary as there are some configs where adding
                    # the slope could create subsidiary pits in the topo
                    self._lf.map_depressions(pits=None, reroute_flow=False)
                    if len(self._lf.lake_outlets) == 0.:
                        break
                    self._elev += self._grid.at_node['depression__depth']
                    sublake = True
                    self.lake_nodes_treated = np.array([], dtype=int)
                # final test that all lakes are not reversing flow dirs
                all_lakes = np.where(self._lf.flood_status <
                                     BAD_INDEX_VALUE)[0]
                unstable = self.drainage_directions_change(all_lakes,
                                                           self.original_elev,
                                                           self._elev)
                if unstable:
                    apply_slope *= 0.1
                    sublake = False
                    self.lake_nodes_treated = np.array([], dtype=int)
                    self._elev[:] = original_elev  # put back init conds
                    stability_increment += 1
                    if stability_increment == 10:
                        raise OverflowError('Filler could not find a stable ' +
                                            'condition with a sloping ' +
                                            'surface!')
        # now put back any fields that were present initially, and wipe the
        # rest:
        for delete_me in spurious_fields:
            self._grid.delete_field('node', delete_me)
        for update_me in existing_fields.keys():
            self.grid.at_node[update_me] = existing_fields[update_me]
        # fill the output field
        self.sed_fill_depth[:] = self._elev - self.original_elev

    def add_slopes(self, slope, outlet_node, lake_code):
        """
        Assuming you have already run the lake_mapper, adds an incline towards
        the outlet to the nodes in the lake.
        """
        new_elevs = self._elev.copy()
        outlet_coord = (self._grid.node_x[outlet_node],
                        self._grid.node_y[outlet_node])
        lake_nodes = np.where(self._lf.lake_map == lake_code)[0]
        lake_nodes = np.setdiff1d(lake_nodes, self.lake_nodes_treated)
        lake_ext_margin = self.get_lake_ext_margin(lake_nodes)
        d = self._grid.get_distances_of_nodes_to_point(outlet_coord,
                                                       node_subset=lake_nodes)
        add_vals = slope*d
        new_elevs[lake_nodes] += add_vals
        self.lake_nodes_treated = np.union1d(self.lake_nodes_treated,
                                             lake_nodes)
        return new_elevs, lake_nodes

    def get_lake_ext_margin(self, lake_nodes):
        """
        Returns the nodes forming the external margin of the lake, honoring
        the *routing* method (D4/D8) if applicable.
        """
        if self._D8 is True:
            all_poss = np.union1d(self._grid.get_active_neighbors_at_node(lake_nodes),
                                  self._grid.get_diagonal_list(lake_nodes))
        else:
            all_poss = np.unique(self._grid.get_active_neighbors_at_node(lake_nodes))
        lake_ext_edge = np.setdiff1d(all_poss, lake_nodes)
        return lake_ext_edge[lake_ext_edge != BAD_INDEX_VALUE]

    def get_lake_int_margin(self, lake_nodes, lake_ext_edge):
        """
        Returns the nodes forming the internal margin of the lake, honoring
        the *routing* method (D4/D8) if applicable.
        """
        lee = lake_ext_edge
        if self._D8 is True:
            all_poss_int = np.union1d(self._grid.get_active_neighbors_at_node(lee),
                                      self._grid.get_diagonal_list(lee))
        else:
            all_poss_int = np.unique(self._grid.get_active_neighbors_at_node(lee))
        lake_int_edge = np.intersect1d(all_poss_int, lake_nodes)
        return lake_int_edge[lake_int_edge != BAD_INDEX_VALUE]

    def apply_slope_current_lake(self, apply_slope, outlet_node, lake_code,
                                 sublake):
        """
        Wraps the add_slopes method to allow handling of conditions where the
        drainage structure would be changed or we're dealing with a sublake.
        """
        while 1:
            starting_elevs = self._elev.copy()
            self._elev[:], lake_nodes = self.add_slopes(apply_slope,
                                                        outlet_node, lake_code)
            ext_edge = self.get_lake_ext_margin(lake_nodes)
            if sublake:
                break
            else:
                if not self.drainage_directions_change(lake_nodes,
                                                       starting_elevs,
                                                       self._elev):
                    break
                else:
                    # put the elevs back...
                    self._elev[lake_nodes] = starting_elevs[lake_nodes]
                    # the slope was too big. Reduce it.
                    apply_slope *= 0.1
        # if we get here, either sublake, or drainage dirs are stable

    def drainage_directions_change(self, lake_nodes, old_elevs, new_elevs):
        """
        True if the drainage structure at lake margin changes, False otherwise.
        """
        ext_edge = self.get_lake_ext_margin(lake_nodes)
        if self._D8:
            edge_neighbors = np.hstack((self._grid.get_active_neighbors_at_node(ext_edge),
                                        self._grid.get_diagonal_list(
                                            ext_edge)))
        else:
            edge_neighbors = self._grid.get_active_neighbors_at_node(ext_edge).copy()
        edge_neighbors[edge_neighbors == BAD_INDEX_VALUE] = -1
        # ^value irrelevant
        old_neighbor_elevs = old_elevs[edge_neighbors]
        new_neighbor_elevs = new_elevs[edge_neighbors]
        # enforce the "don't change drainage direction" condition:
        edge_elevs = old_elevs[ext_edge].reshape((ext_edge.size, 1))
        cond = np.allclose((edge_elevs >= old_neighbor_elevs),
                           (edge_elevs >= new_neighbor_elevs))
        # if True, we're good, the tilting didn't mess with the fr
        return not cond
grid = RasterModelGrid(4, 5, 1.0)
grid.set_inactive_boundaries(True, False, True, True)
z = grid.add_zeros('node', 'Land_Surface__Elevation')
z[6] = 4.5
z[7] = 3.
z[8] = 1.
z[11] = 4.
z[12] = 2.8
z[13] = 2.


# Get array of interior (active) node IDs
interior_nodes = grid.get_active_cell_node_ids()

# Route flow
flow_router = FlowRouter(grid)
grid = flow_router.route_flow()

for i in range(grid.number_of_nodes):
    print(i, grid.node_x[i], grid.node_y[i], z[i], grid.node_status[i], \
          r[i], a[i], q[i], ss[i], rl[i])

# Let's take a look for debugging
#print 'node  receiver  flow_link'
#for i in interior_nodes:
#    print i, r[i], rl[i]

# Calculate lengths of flow links
flow_link_length = ones(size(z))
flow_link_length[interior_nodes] = grid.link_length[rl[interior_nodes]] #DEJH suspects a node ordering bug here - rl is not in ID order, but interior_nodes is
print('fll:', flow_link_length)
Beispiel #10
0
mg = RasterModelGrid(nrows, ncols, dx)

#create the fields in the grid
mg.create_node_array_zeros('topographic__elevation')
z = mg.create_node_array_zeros() + init_elev
mg['node'][ 'topographic__elevation'] = z + numpy.random.rand(len(z))/1000.

#make some K values in a field to test
mg.at_node['K_values'] = 0.1+numpy.random.rand(nrows*ncols)/10.

print( 'Running ...' )
time_on = time.time()

#instantiate the components:
fr = FlowRouter(mg)
sp = StreamPowerEroder(mg, './drive_sp_params.txt')
#load the Fastscape module too, to allow direct comparison
fsp = Fsc(mg, './drive_sp_params.txt')

#perform the loop:
elapsed_time = 0. #total time in simulation
while elapsed_time < time_to_run:
    print(elapsed_time)
    if elapsed_time+dt>time_to_run:
        print("Short step!")
        dt = time_to_run - elapsed_time
    mg = fr.route_flow()
    #print 'Area: ', numpy.max(mg.at_node['drainage_area'])
    #mg = fsp.erode(mg)
    mg,_,_ = sp.erode(mg, dt, node_drainage_areas='drainage_area', slopes_at_nodes='topographic__steepest_slope', K_if_used='K_values')
mg = RasterModelGrid(nrows, ncols, dx)

# create the fields in the grid
mg.create_node_array_zeros('topographic__elevation')
z = np.array([5., 5., 0., 5., 5.,
              5., 2., 1., 2., 5.,
              5., 3., 2., 3., 5.,
              5., 4., 4., 4., 5.,
              5., 5., 5., 5., 5.])
mg['node']['topographic__elevation'] = z

print('Running ...')

# instantiate the components:
fr = FlowRouter(mg)
sp = StreamPowerEroder(mg, './drive_sp_params_discharge.txt')
# load the Fastscape module too, to allow direct comparison
fsp = Fsc(mg, './drive_sp_params_discharge.txt')

# perform the loop (once!)
for i in xrange(1):
    fr.route_flow(method='D8')
    my_Q = mg.at_node['water__volume_flux']*1.
    sp.erode(mg, dt, node_drainage_areas='drainage_area',
             slopes_at_nodes='topographic__steepest_slope',
             Q_if_used=my_Q)
    # no uplift

# print the stream power that was calculated:
print('stream power values:')
from landlab.plot.imshow import imshow_node_grid
import os
import pylab

dem_name = './west_bijou_gully.asc'
outlet_row = 82
outlet_column = 38

# Read in a DEM and set its boundaries
DATA_FILE = os.path.join(os.path.dirname(__file__), dem_name)
(grid, z) = read_esri_ascii(DATA_FILE, name='topographic__elevation')
grid.set_nodata_nodes_to_closed(z, 0.)  # set nodata nodes to inactive bounds
outlet_node = grid.grid_coords_to_node_id(outlet_row, outlet_column)

# Route flow
flow_router = FlowRouter(grid)
flow_router.route_flow()

# Create a shaded image
pylab.close()  # clear any pre-existing plot
pylab.figure(1)
im = imshow_node_grid(grid, 'water__volume_flux', cmap=pylab.cm.RdBu)

# add a title and axis labels
pylab.title('Discharge')
pylab.xlabel('Distance (m)')
pylab.ylabel('Distance (m)')

pylab.figure(2)
im = imshow_node_grid(grid, 'topographic__elevation')
pylab.title('DEM')
    def flow_across_grid(self,
                         grid,
                         z,
                         total_t=None,
                         rainrate=None,
                         rainduration=None):
        '''
        This function calculates depth, discharge and shear stress
        at each active node in the grid. 
    
        As of right now, this is going to be incredibly time consuming.
        WORK IN PROGRESS!!!!!
        '''

        self.h = self.hstart
        h = self.h
        q = self.q
        dhdt = self.dhdt

        #self.tau
        #        dtdt = self.dtaudt

        g = self.g
        alpha = self.alpha
        m_n_sq = self.m_n_sq
        ten_thirds = self.ten_thirds
        rho = self.rho

        #interior_nodes are the nodes on which you will be calculating flow
        self.interior_nodes = grid.get_active_cell_node_ids()

        if total_t == None:
            total_t = self.rain_duration
        if rainrate == None:
            rainrate = self.rainfall_rate
        if rainduration == None:
            rainduration = total_t

        elapsed_time = 0

        #below is for calculating water surface slope at interior nodes
        w_slope = np.zeros(self.interior_nodes.size)

        t = []  #time array for plotting
        t.append(0.)  #initialize array
        self.dqds = grid.zeros(centering='node')

        while elapsed_time < total_t:

            # Calculate time-step size for this iteration (Bates et al., eq 14)
            dtmax = self.alpha * grid.dx / np.sqrt(self.g * np.amax(self.h))

            # Take the smaller of delt or calculated time-step
            dt = min(dtmax, total_t)

            # Calculate the effective flow depth at active links. Bates et al. 2010
            # recommend using the difference between the highest water-surface
            # and the highest bed elevation between each pair of cells.
            zmax = grid.max_of_link_end_node_values(
                z)  #array of length of active links
            w = self.h + z  # water-surface height, array of length num nodes
            wmax = grid.max_of_link_end_node_values(
                w)  #array of length of active links
            hflow = wmax - zmax  #array of length of active links

            # Calculate water-surface slopes: across links, but water heights are
            #defined at nodes.
            water_surface_slope = grid.calculate_gradients_at_active_links(w)

            # Calculate the unit discharges (Bates et al., eq 11)
            self.q = (self.q-g*hflow*dtmax*water_surface_slope)/ \
                (1.+g*hflow*dtmax*0.06*0.06*abs(self.q)/(hflow**ten_thirds))
            #NOTES:
            # q is calculated at links
            # water_surface_slope is at links
            # hflow is at links, but w is at nodes

            # Calculate water-flux divergence at nodes
            self.dqds = grid.calculate_flux_divergence_at_nodes(
                self.q, self.dqds)

            # Calculate rate of change of water depth
            self.dhdt = rainrate - self.dqds

            # Second time-step limiter (experimental): make sure you don't allow
            # water-depth to go negative
            if np.amin(self.dhdt) < 0.:
                shallowing_locations = np.where(self.dhdt < 0.)
                time_to_drain = -self.h[shallowing_locations] / self.dhdt[
                    shallowing_locations]
                dtmax2 = self.alpha * np.amin(time_to_drain)
                dt = np.min([dtmax, dtmax2, total_t])

            # Update the water-depth field
            self.h[self.interior_nodes] = self.h[
                self.interior_nodes] + self.dhdt[self.interior_nodes] * dt

            # Let's calculate shear stress at the nodes.
            # First get water height at the nodes.
            # Then calculate the maximum gradient in water surface elevations (S).
            # Then you can calculate shear stress! (rho g h S)
            # h (water depth) is at nodes

            w = self.h + z  # water-surface height, array of length num nodes

            #Below if is for limiting shear stress calculations to only times when
            #q surpasses a threshold (in this case q should be in m^3/sec)
            #Note that this threshold is HARDWIRED below (on right of >)

            #self.slopes_at_node, garbage = grid.calculate_steepest_descent_on_nodes(w, water_surface_slope)
            fr = FlowRouter(grid)
            r, a, q, ss, s, d = fr.route_flow(w)
            self.slopes_at_node = ss
            if self.q.any() * grid.dx > 0.2:
                self.tau = self.rho * self.g * self.slopes_at_node * self.h

                #for i in range(24899):
                #    #w_slope[i],garbage=grid.calculate_gradient_across_cell_faces(grid, w, self.interior_nodes)    #grid.calculate_max_gradient_across_node_d4(w,self.interior_nodes[i])
                #    self.tau[self.interior_nodes[i]]=self.rho*self.g*self.slopes_at_node[self.interior_nodes[i]]*self.h[self.interior_nodes[i]]
                #    #self.tau[i] = tau[i]

            self.tau[np.where(self.tau < 0)] = 0

            #self.tau[interior_nodes] = self.tau[interior_nodes] + self.dtds[interior_nodes]*dt
            #tau[np.where(tau<0)] = 0
            # Update model run time
            elapsed_time += dt
            print "elapsed time", elapsed_time
##create the elevation field in the grid:
#create the field
mg.create_node_array_zeros('topographic__elevation')
z = mg.create_node_array_zeros() + leftmost_elev
z += initial_slope * np.amax(mg.node_y) - initial_slope * mg.node_y
#put these values plus roughness into that field
mg.at_node['topographic__elevation'] = z + np.random.rand(len(z)) / 100000.

#set up grid's boundary conditions (bottom, right, top, left is inactive)
mg.set_closed_boundaries_at_grid_edges(False, True, False, True)

# Display a message
print 'Running ...'

#instantiate the components:
fr = FlowRouter(mg)
sp = FastscapeEroder(mg, input_file)
diffuse = PerronNLDiffuse(mg, input_file)
lin_diffuse = LinearDiffuser(grid=mg, input_stream=input_file)

#perform the loops:
for i in xrange(nt):
    #note the input arguments here are not totally standardized between modules
    #mg = diffuse.diffuse(mg, i*dt)
    mg = lin_diffuse.diffuse(dt)
    mg = fr.route_flow()
    mg = sp.erode(mg, dt)
    mg.at_node['topographic__elevation'][mg.core_nodes] += uplift_per_step

    ##plot long profiles along channels
    pylab.figure(6)
#create the fields in the grid
mg.create_node_array_zeros('topographic_elevation')
z = mg.create_node_array_zeros() + init_elev
mg['node'][ 'topographic_elevation'] = z + numpy.random.rand(len(z))/1000.

#make some K values in a field to test 
#mg.at_node['K_values'] = 0.1+numpy.random.rand(nrows*ncols)/10.
mg.at_node['K_values'] = numpy.empty(nrows*ncols, dtype=float)
#mg.at_node['K_values'].fill(0.1+numpy.random.rand()/10.)
mg.at_node['K_values'].fill(0.001)

print( 'Running ...' )

#instantiate the components:
fr = FlowRouter(mg)
sp = StreamPowerEroder(mg, input_file_string)
#fsp = Fsc(mg, input_file_string)
precip = PrecipitationDistribution(input_file=input_file_string)

#load the Fastscape module too, to allow direct comparison
fsp = Fsc(mg, input_file_string)

try:
    #raise NameError
    mg = copy.deepcopy(mg_mature)
except NameError:
    print 'building a new grid...'
    out_interval = 50000.
    last_trunc = time_to_run #we use this to trigger taking an output plot
    #run to a steady state:
from landlab.plot.imshow import imshow_node_grid
import os
import pylab

dem_name = '../../../io/tests/data/west_bijou_gully.asc'
outlet_row = 82
outlet_column = 38

# Read in a DEM and set its boundaries
DATA_FILE = os.path.join(os.path.dirname(__file__), dem_name)
(grid, z) = read_esri_ascii(DATA_FILE)
grid.set_nodata_nodes_to_inactive(z, 0) # set nodata nodes to inactive bounds
outlet_node = grid.grid_coords_to_node_id(outlet_row, outlet_column)

# Route flow
flow_router = FlowRouter(grid)
flow_router.route_flow(z)

# Get a 2D array version of the elevations
ar = grid.node_vector_to_raster(grid['node']['drainage_area'])

numcols = grid.number_of_node_columns
numrows = grid.number_of_node_rows
dx = grid.dx

# Create a shaded image
pylab.close()  # clear any pre-existing plot
im = pylab.imshow(ar, cmap=pylab.cm.RdBu, extent=[0,numcols*dx,0,numrows*dx])
# add contour lines with labels
cset = pylab.contour(ar, extent=[0,numcols*dx,numrows*dx,0])
pylab.clabel(cset, inline=True, fmt='%1.1f', fontsize=10)
grid = RasterModelGrid(4, 5, 1.0)
grid.set_inactive_boundaries(True, False, True, True)
z = grid.add_zeros('node', 'Land_Surface__Elevation')
z[6] = 4.5
z[7] = 3.
z[8] = 1.
z[11] = 4.
z[12] = 2.8
z[13] = 2.

# Get array of interior (active) node IDs
interior_nodes = grid.get_active_cell_node_ids()

# Route flow
flow_router = FlowRouter(grid)
r, a, q, ss, s, rl = flow_router.route_flow(z)

for i in range(grid.number_of_nodes):
    print i, grid.node_x[i], grid.node_y[i], z[i], grid.node_status[i], \
          r[i], a[i], q[i], ss[i], rl[i]

# Let's take a look for debugging
#print 'node  receiver  flow_link'
#for i in interior_nodes:
#    print i, r[i], rl[i]

# Calculate lengths of flow links
flow_link_length = ones(size(z))
flow_link_length[interior_nodes] = grid.link_length[rl[
    interior_nodes]]  #DEJH suspects a node ordering bug here - rl is not in ID order, but interior_nodes is
grid = RasterModelGrid(4, 5, 1.0)
grid.set_inactive_boundaries(True, False, True, True)
z = grid.add_zeros('node', 'Land_Surface__Elevation')
z[6] = 4.5
z[7] = 3.
z[8] = 1.
z[11] = 4.
z[12] = 2.8
z[13] = 2.


# Get array of interior (active) node IDs
interior_nodes = grid.get_active_cell_node_ids()

# Route flow
flow_router = FlowRouter(grid)
r, a, q, ss, s, rl = flow_router.route_flow(z)

for i in range(grid.number_of_nodes):
    print i, grid.node_x[i], grid.node_y[i], z[i], grid.node_status[i], \
          r[i], a[i], q[i], ss[i], rl[i]

# Let's take a look for debugging
#print 'node  receiver  flow_link'
#for i in interior_nodes:
#    print i, r[i], rl[i]

# Calculate lengths of flow links
flow_link_length = ones(size(z))
flow_link_length[interior_nodes] = grid.link_length[rl[interior_nodes]] #DEJH suspects a node ordering bug here - rl is not in ID order, but interior_nodes is
print 'fll:', flow_link_length
from landlab.plot.imshow import imshow_node_grid
import os
import pylab

dem_name = './west_bijou_gully.asc'
outlet_row = 82
outlet_column = 38

# Read in a DEM and set its boundaries
DATA_FILE = os.path.join(os.path.dirname(__file__), dem_name)
(grid, z) = read_esri_ascii(DATA_FILE, name='topographic__elevation')
grid.set_nodata_nodes_to_closed(z, 0.) # set nodata nodes to inactive bounds
outlet_node = grid.grid_coords_to_node_id(outlet_row, outlet_column)

# Route flow
flow_router = FlowRouter(grid)
flow_router.route_flow()

# Create a shaded image
pylab.close()  # clear any pre-existing plot
pylab.figure(1)
im = imshow_node_grid(grid, 'water__volume_flux', cmap = pylab.cm.RdBu)

# add a title and axis labels
pylab.title('Discharge')
pylab.xlabel('Distance (m)')
pylab.ylabel('Distance (m)')

pylab.figure(2)
im = imshow_node_grid(grid, 'topographic__elevation')
pylab.title('DEM')
Beispiel #20
0
def test_composite_pits():
    """
    A test to ensure the component correctly handles cases where there are
    multiple pits, inset into each other.
    """
    mg = RasterModelGrid(10, 10, 1.)
    z = mg.add_field('node', 'topographic__elevation', mg.node_x.copy())
    # a sloping plane
    #np.random.seed(seed=0)
    #z += np.random.rand(100)/10000.
    # punch one big hole
    z.reshape((10,10))[3:8,3:8] = 0.
    # dig a couple of inset holes
    z[57] = -1.
    z[44] = -2.
    z[54] = -10.
    fr = FlowRouter(mg)
    lf = DepressionFinderAndRouter(mg)
    fr.route_flow()
    lf.map_depressions()
    
    flow_sinks_target = np.zeros(100, dtype=bool)
    flow_sinks_target[mg.boundary_nodes] = True
    # no internal sinks now:
    assert_array_equal(mg.at_node['flow_sinks'], flow_sinks_target)
    
    # test conservation of mass:
    assert_almost_equal(mg.at_node['drainage_area'
                                       ].reshape((10,10))[1:-1,1].sum(), 8.**2)
    # ^all the core nodes
    
    # test the actual flow field:
    nA = np.array([  0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,
                     8.,   8.,   7.,   6.,   5.,   4.,   3.,   2.,   1.,   0.,
                     1.,   1.,   1.,   1.,   1.,   1.,   1.,   1.,   1.,   0.,
                     1.,   1.,   1.,   4.,   2.,   2.,   8.,   4.,   1.,   0.,
                     1.,   1.,   1.,   8.,   3.,  15.,   3.,   2.,   1.,   0.,
                     1.,   1.,   1.,  13.,  25.,   6.,   3.,   2.,   1.,   0.,
                     1.,   1.,   1.,  45.,   3.,   3.,   5.,   2.,   1.,   0.,
                    50.,  50.,  49.,   3.,   2.,   2.,   2.,   4.,   1.,   0.,
                     1.,   1.,   1.,   1.,   1.,   1.,   1.,   1.,   1.,   0.,
                     0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.,   0.])
    assert_array_equal(mg.at_node['drainage_area'], nA)
    
    # the lake code map:
    lc = np.array([XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
                   XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
                   XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
                   XX, XX, XX, 57, 57, 57, 57, 57, XX, XX,
                   XX, XX, XX, 57, 57, 57, 57, 57, XX, XX,
                   XX, XX, XX, 57, 57, 57, 57, 57, XX, XX,
                   XX, XX, XX, 57, 57, 57, 57, 57, XX, XX,
                   XX, XX, XX, 57, 57, 57, 57, 57, XX, XX,
                   XX, XX, XX, XX, XX, XX, XX, XX, XX, XX,
                   XX, XX, XX, XX, XX, XX, XX, XX, XX, XX])
    
    #test the remaining properties:
    assert_equal(lf.lake_outlets.size, 1)
    assert_equal(lf.lake_outlets[0], 72)
    outlets_in_map = np.unique(lf.depression_outlet_map)
    assert_equal(outlets_in_map.size, 2)
    assert_equal(outlets_in_map[0], 72)
    assert_equal(lf.number_of_lakes, 1)
    assert_equal(lf.lake_codes[0], 57)
    assert_array_equal(lf.lake_map, lc)
    assert_almost_equal(lf.lake_areas[0], 25.)
    assert_almost_equal(lf.lake_volumes[0], 63.)
#create the fields in the grid
mg.create_node_array_zeros('topographic__elevation')
z = mg.create_node_array_zeros() + init_elev
mg['node'][ 'topographic__elevation'] = z + numpy.random.rand(len(z))/1000.
mg.add_zeros('node', 'water__volume_flux_in')

#make some K values in a field to test 
#mg.at_node['K_values'] = 0.1+numpy.random.rand(nrows*ncols)/10.
mg.at_node['K_values'] = numpy.empty(nrows*ncols, dtype=float)
#mg.at_node['K_values'].fill(0.1+numpy.random.rand()/10.)
mg.at_node['K_values'].fill(0.001)

print( 'Running ...' )

#instantiate the components:
fr = FlowRouter(mg)
sp = StreamPowerEroder(mg, input_file_string)
#fsp = Fsc(mg, input_file_string)
precip = PrecipitationDistribution(input_file=input_file_string)

#load the Fastscape module too, to allow direct comparison
fsp = Fsc(mg, input_file_string)

try:
    #raise NameError
    mg = copy.deepcopy(mg_mature)
except NameError:
    print('building a new grid...')
    out_interval = 50000.
    last_trunc = time_to_run #we use this to trigger taking an output plot
    #run to a steady state:
mg.create_node_array_zeros('topographic_elevation')
z = mg.create_node_array_zeros() + leftmost_elev
z += initial_slope*np.amax(mg.node_y) - initial_slope*mg.node_y
#put these values plus roughness into that field
mg['node'][ 'topographic_elevation'] = z + np.random.rand(len(z))/100000.

#set up grid's boundary conditions (bottom, left, top, right is inactive)
mg.set_inactive_boundaries(False, True, False, True)
mg.set_fixed_value_boundaries_at_grid_edges(True, False, True, False, value_of='topographic_elevation')
print 'fixed vals in grid: ', mg.fixed_value_node_properties['values']

# Display a message
print 'Running ...' 

#instantiate the components:
fr = FlowRouter(mg)
sde = SedDepEroder(mg, input_file)
vid = VideoPlotter(mg, data_centering='node')

time_on = time()
#perform the loops:
for i in xrange(nt):
    #print 'loop ', i
    mg.at_node['topographic_elevation'][mg.core_nodes] += uplift_per_step
    mg = fr.route_flow(grid=mg)
    #mg.calculate_gradient_across_cell_faces(mg.at_node['topographic_elevation'])
    #neighbor_slopes = mg.calculate_gradient_along_node_links(mg.at_node['topographic_elevation'])
    #mean_slope = np.mean(np.fabs(neighbor_slopes),axis=1)
    #max_slope = np.max(np.fabs(neighbor_slopes),axis=1)
    #mg,_,capacity_out = tl.erode(mg,dt,slopes_at_nodes='steepest_slope')
    #mg,_,capacity_out = tl.erode(mg,dt,slopes_at_nodes=max_slope)
Beispiel #23
0
# Load topography output rotated from WRF-Hydro and add to topography field
# Since the discharge files were created on the WRF-Hydro topography, we use that topography as our input. The original Landlab topography and the WRF-Hydro topography are almost identical, with only slight differences created by the rotation.
topo=Dataset('topography_wrf_hydro.nc')
topography=topo.variables['TOPOGRAPHY'][:]
topographic__elevation=np.asarray(topography, dtype=float).ravel()
mg.add_field('node', 'topographic__elevation', topographic__elevation) #create the field


#set boundary conditions -- these should match those used in the topography creation driver
for edge in (mg.nodes_at_left_edge, mg.nodes_at_right_edge):
    mg.status_at_node[edge] = CLOSED_BOUNDARY
for edge in (mg.nodes_at_top_edge, mg.nodes_at_bottom_edge):
    mg.status_at_node[edge] = FIXED_VALUE_BOUNDARY

#instantiate the components
fr = FlowRouter(mg)
sp = StreamPowerEroder(mg, input_file)
lin_diffuse = LinearDiffuser(mg, input_file)

print( 'Running ...' )
time_on = time.time()

#text for discharge file names
streamflow = "streamflow"
nc = ".nc"

elapsed_time = 0. #total time in simulation

#create list of all discharge files to be used, repeat as many times as necessary for run time.
#Example: here we have a total of 20 discharge files -- we need one file for every time step and we have 100 timesteps. 
# We will repeat the discharge file namelist 5 times Run Time = 100 yrs dt = 1yr 
Beispiel #24
0
from landlab import VoronoiDelaunayGrid  # , RasterModelGrid
from landlab.components.flow_routing.route_flow_dn import FlowRouter
from landlab.components.stream_power.stream_power import StreamPowerEroder
from landlab.plot.imshow import imshow_node_grid
import numpy as np
from matplotlib.pyplot import figure, show

nnodes = 10000

x, y = np.random.rand(nnodes), np.random.rand(nnodes)
mg = VoronoiDelaunayGrid(x,y)
#mg = RasterModelGrid(4,5)

z = mg.add_field('node', 'topographic__elevation', np.random.rand(nnodes)/10000., copy=False)

fr = FlowRouter(mg)
spe = StreamPowerEroder(mg, 'drive_sp_params_voronoi.txt')

for i in xrange(100):
    z[mg.core_nodes] += 0.01
    fr.route_flow()
    spe.erode(mg, 1.)

imshow_node_grid(mg, 'topographic__elevation')

show()
    def flow_across_grid(self, grid, z, total_t=None, rainrate=None, rainduration=None):  
        '''
        This function calculates depth, discharge and shear stress
        at each active node in the grid. 
    
        As of right now, this is going to be incredibly time consuming.
        WORK IN PROGRESS!!!!!
        '''
        
        self.h = self.hstart
        h = self.h
        q = self.q
        dhdt = self.dhdt
        
        #self.tau
#        dtdt = self.dtaudt
        
        g=self.g
        alpha = self.alpha
        m_n_sq = self.m_n_sq
        ten_thirds = self.ten_thirds
        rho = self.rho
    
        #interior_nodes are the nodes on which you will be calculating flow 
        self.interior_nodes = grid.get_active_cell_node_ids()

        if total_t==None:
            total_t = self.rain_duration
        if rainrate==None:
            rainrate = self.rainfall_rate
        if rainduration==None:
            rainduration = total_t
            
        elapsed_time = 0
        
        #below is for calculating water surface slope at interior nodes
        w_slope=np.zeros(self.interior_nodes.size)

        t = [] #time array for plotting
        t.append(0.) #initialize array
        self.dqds= grid.zeros(centering='node')
                
        while elapsed_time < total_t:
            
            # Calculate time-step size for this iteration (Bates et al., eq 14)
            dtmax = self.alpha*grid.dx/np.sqrt(self.g*np.amax(self.h))
        
            # Take the smaller of delt or calculated time-step
            dt = min(dtmax, total_t)
        
            # Calculate the effective flow depth at active links. Bates et al. 2010
            # recommend using the difference between the highest water-surface
            # and the highest bed elevation between each pair of cells.
            zmax = grid.max_of_link_end_node_values(z) #array of length of active links
            w = self.h+z   # water-surface height, array of length num nodes
            wmax = grid.max_of_link_end_node_values(w) #array of length of active links
            hflow = wmax - zmax #array of length of active links
        
            # Calculate water-surface slopes: across links, but water heights are 
            #defined at nodes.
            water_surface_slope = grid.calculate_gradients_at_active_links(w)
       
      
            # Calculate the unit discharges (Bates et al., eq 11)
            self.q = (self.q-g*hflow*dtmax*water_surface_slope)/ \
                (1.+g*hflow*dtmax*0.06*0.06*abs(self.q)/(hflow**ten_thirds))                
            #NOTES:    
            # q is calculated at links
            # water_surface_slope is at links
            # hflow is at links, but w is at nodes

            # Calculate water-flux divergence at nodes
            self.dqds = grid.calculate_flux_divergence_at_nodes(self.q,self.dqds)
            
            # Calculate rate of change of water depth
            self.dhdt = rainrate-self.dqds

            
            # Second time-step limiter (experimental): make sure you don't allow
            # water-depth to go negative
            if np.amin(self.dhdt) < 0.:
                shallowing_locations = np.where(self.dhdt<0.)
                time_to_drain = -self.h[shallowing_locations]/self.dhdt[shallowing_locations]
                dtmax2 = self.alpha*np.amin(time_to_drain)
                dt = np.min([dtmax, dtmax2, total_t])
        
            # Update the water-depth field
            self.h[self.interior_nodes] = self.h[self.interior_nodes] + self.dhdt[self.interior_nodes]*dt


            # Let's calculate shear stress at the nodes.  
            # First get water height at the nodes.
            # Then calculate the maximum gradient in water surface elevations (S).
            # Then you can calculate shear stress! (rho g h S)       
            # h (water depth) is at nodes
            
            w = self.h+z   # water-surface height, array of length num nodes
            

            #Below if is for limiting shear stress calculations to only times when
            #q surpasses a threshold (in this case q should be in m^3/sec)
            #Note that this threshold is HARDWIRED below (on right of >)

            #self.slopes_at_node, garbage = grid.calculate_steepest_descent_on_nodes(w, water_surface_slope)
            fr=FlowRouter(grid)
            r, a, q, ss, s, d = fr.route_flow(w)
            self.slopes_at_node = ss
            if self.q.any()*grid.dx> 0.2:
                self.tau = self.rho*self.g*self.slopes_at_node*self.h

                #for i in range(24899): 
                #    #w_slope[i],garbage=grid.calculate_gradient_across_cell_faces(grid, w, self.interior_nodes)    #grid.calculate_max_gradient_across_node_d4(w,self.interior_nodes[i])
                #    self.tau[self.interior_nodes[i]]=self.rho*self.g*self.slopes_at_node[self.interior_nodes[i]]*self.h[self.interior_nodes[i]]
                #    #self.tau[i] = tau[i]

            self.tau[np.where(self.tau<0)] = 0       

            #self.tau[interior_nodes] = self.tau[interior_nodes] + self.dtds[interior_nodes]*dt
            #tau[np.where(tau<0)] = 0
            # Update model run time
            elapsed_time += dt
            print "elapsed time", elapsed_time
##create the elevation field in the grid:
#create the field
mg.create_node_array_zeros('topographic_elevation')
z = mg.create_node_array_zeros() + leftmost_elev
z += initial_slope * np.amax(mg.node_y) - initial_slope * mg.node_y
#put these values plus roughness into that field
mg.at_node['topographic_elevation'] = z + np.random.rand(len(z)) / 100000.

#set up grid's boundary conditions (bottom, right, top, left is inactive)
mg.set_closed_boundaries_at_grid_edges(False, True, False, True)

# Display a message
print 'Running ...'

#instantiate the components:
fr = FlowRouter(mg)
sp = SPEroder(mg, input_file)
diffuse = PerronNLDiffuse(mg, input_file)
lin_diffuse = DiffusionComponent(grid=mg, input_stream=input_file)

#perform the loops:
for i in xrange(nt):
    #note the input arguments here are not totally standardized between modules
    #mg = diffuse.diffuse(mg, i*dt)
    mg = lin_diffuse.diffuse(mg, dt)
    mg = fr.route_flow(grid=mg)
    mg = sp.erode(mg)

    ##plot long profiles along channels
    pylab.figure(6)
    profile_IDs = prf.channel_nodes(mg, mg.at_node['steepest_slope'],
#build the grid
mg = RasterModelGrid((nrows, ncols), dx)
z = mg.add_zeros('node', 'topographic__elevation')
# add some roughness as the initial topography, this lets "natural" channel planforms arise
initial_roughness = np.random.rand(z.size) / 100000.
z += initial_roughness

#set the boundary conditions - here we have closed boundaries at the top/bottom and fixed boundaries at the left/right
for edge in (mg.nodes_at_left_edge, mg.nodes_at_right_edge):
    mg.status_at_node[edge] = FIXED_VALUE_BOUNDARY
for edge in (mg.nodes_at_top_edge, mg.nodes_at_bottom_edge):
    mg.status_at_node[edge] = CLOSED_BOUNDARY

#instantiate the components
fr = FlowRouter(mg, input_file)
sp = StreamPowerEroder(mg, input_file)
lin_diffuse = LinearDiffuser(mg, input_file)

#run the model
elapsed_time = 0.  #total time in simulation
for i in range(nt):
    lin_diffuse.run_one_step(dt)
    fr.run_one_step(
    )  # route_flow isn't time sensitive, so it doesn't take dt as input
    sp.run_one_step(dt)
    mg.at_node['topographic__elevation'][
        mg.core_nodes] += uplift_per_step  # add the uplift

    # if you want to see the evolution of the topogrpahy through time (say to check for steady state) you can output a topography at each time step
    #write_netcdf(('topography_output'+ str(elapsed_time) +'.nc'), mg, names='topographic__elevation')