Пример #1
0
    def route_flow(self, **kwds):
        """Route surface-water flow over a landscape.

        Routes surface-water flow by (1) assigning to each node a single
        drainage direction, and then (2) adding up the number of nodes that
        contribute flow to each node on the grid (including the node itself).

        Stores as ModelGrid fields:

        -  Node array of receivers (nodes that receive flow), or ITS OWN ID if
           there is no receiver: *'flow__receiver_node'*
        -  Node array of drainage areas: *'drainage_area'*
        -  Node array of discharges: *'surface_water__discharge'*
        -  Node array of steepest downhill slopes:
           *'topographic__steepest_slope'*
        -  Node array containing downstream-to-upstream ordered list of node
           IDs: *'flow__upstream_node_order'*
        -  Node array containing ID of link that leads from each node to its
           receiver, or BAD_INDEX_VALUE if no link:
           *'flow__link_to_receiver_node'*
        -  Boolean node array of all local lows: *'flow__sink_flag'*

        Returns
        -------
        ModelGrid
            The modified grid object

        Examples
        --------
        >>> import numpy as np
        >>> from landlab import RasterModelGrid
        >>> from landlab.components.flow_routing import FlowRouter
        >>> mg = RasterModelGrid((5, 4), spacing=(1, 1))
        >>> elev = np.array([0.,  0.,  0., 0.,
        ...                  0., 21., 10., 0.,
        ...                  0., 31., 20., 0.,
        ...                  0., 32., 30., 0.,
        ...                  0.,  0.,  0., 0.])
        >>> _ = mg.add_field('node','topographic__elevation', elev)
        >>> mg.set_closed_boundaries_at_grid_edges(True, True, True, False)
        >>> fr = FlowRouter(mg)
        >>> mg = fr.route_flow()
        >>> mg.at_node['flow__receiver_node'] # doctest: +NORMALIZE_WHITESPACE
        array([  0,  1,  2,  3,
                 4,  1,  2,  7,
                 8,  6,  6, 11,
                12, 10, 10, 15,
                16, 17, 18, 19])
        >>> mg.at_node['drainage_area'] # doctest: +NORMALIZE_WHITESPACE
        array([ 0.,  1.,  5.,  0.,
                0.,  1.,  5.,  0.,
                0.,  1.,  3.,  0.,
                0.,  1.,  1.,  0.,
                0.,  0.,  0.,  0.])

        Now let's change the cell area (100.) and the runoff rates:

        >>> mg = RasterModelGrid((5, 4), spacing=(10., 10))

        Put the data back into the new grid.

        >>> _ = mg.add_field('node','topographic__elevation', elev)
        >>> mg.set_closed_boundaries_at_grid_edges(True, True, True, False)
        >>> fr = FlowRouter(mg)
        >>> runoff_rate = np.arange(mg.number_of_nodes)
        >>> _ = mg.add_field('node', 'water__unit_flux_in', runoff_rate,
        ...                  noclobber=False)
        >>> mg = fr.route_flow()
        >>> mg.at_node['surface_water__discharge'] # doctest: +NORMALIZE_WHITESPACE
        array([    0.,   500.,  5200.,     0.,
                   0.,   500.,  5200.,     0.,
                   0.,   900.,  3700.,     0.,
                   0.,  1300.,  1400.,     0.,
                   0.,     0.,     0.,     0.])

        """
        # this retained for back compatibility - method now set in __init__.
        if 'method' in kwds:
            warnings.warn("'method' should be set at initialization now. " +
                          "Please update your code.", DeprecationWarning)
            # raise NameError
            if kwds['method'] not in ('D8', 'D4'):
                raise ValueError('method not understood ({method})'.format(
                    method=method))
            else:
                self.method = kwds['method']
            if not self._is_raster:
                self.method = None

        if self._bc_set_code != self.grid.bc_set_code:
            self.updated_boundary_conditions()
            self._bc_set_code = self.grid.bc_set_code

        # We assume that elevations are provided in a field called
        # 'topographic__elevation'
        elevs = self._grid['node']['topographic__elevation']

        node_cell_area = self._grid.cell_area_at_node.copy()
        node_cell_area[self._grid.closed_boundary_nodes] = 0.
        # closed cells can't contribute

        # Calculate the downhill-positive slopes at the d8 active links
        if self.method == 'D8':
            link_slope = - self._grid._calculate_gradients_at_d8_active_links(
                elevs)
        else:
            link_slope = - self._grid.calc_grad_of_active_link(
                elevs)

        # Find the baselevel nodes
        (baselevel_nodes, ) = numpy.where(
            numpy.logical_or(self._grid.status_at_node == FIXED_VALUE_BOUNDARY,
                             self._grid.status_at_node == FIXED_GRADIENT_BOUNDARY))

        # Calculate flow directions
        if self.method == 'D4':
            num_d4_active = self._grid.number_of_active_links  # only d4
            receiver, steepest_slope, sink, recvr_link = \
                flow_direction_DN.flow_directions(elevs, self._active_links,
                                         self._activelink_tail[:num_d4_active],
                                         self._activelink_head[:num_d4_active],
                                         link_slope,
                                         grid=self._grid,
                                         baselevel_nodes=baselevel_nodes)
        else:  # Voronoi or D8
            receiver, steepest_slope, sink, recvr_link = \
                flow_direction_DN.flow_directions(elevs, self._active_links,
                                     self._activelink_tail,
                                     self._activelink_head, link_slope,
                                     grid=self._grid,
                                     baselevel_nodes=baselevel_nodes)

        # TODO: either need a way to calculate and return the *length* of the
        # flow links, OR the caller has to handle the raster / non-raster case.

        # Calculate drainage area, discharge, and ...
        a, q, s = flow_accum_bw.flow_accumulation(
            receiver, sink, node_cell_area=node_cell_area,
            runoff_rate=self._grid.at_node['water__unit_flux_in'])

        # added DEJH March 2014:
        # store the generated data in the grid
        self._grid['node']['drainage_area'][:] = a
        self._grid['node']['flow__receiver_node'][:] = receiver
        self._grid['node']['topographic__steepest_slope'][:] = steepest_slope
        self._grid['node']['surface_water__discharge'][:] = q
        self._grid['node']['flow__upstream_node_order'][:] = s
        self._grid['node']['flow__link_to_receiver_node'][:] = recvr_link
        self._grid['node']['flow__sink_flag'][:] = numpy.zeros_like(receiver,
                                                                    dtype=bool)
        self._grid['node']['flow__sink_flag'][sink] = True

        return self._grid
Пример #2
0
    def route_flow(self):
        """
        Routes surface-water flow by (1) assigning to each node a single 
        drainage direction, and then (2) adding up the number of nodes that
        contribute flow to each node on the grid (including the node itself).
                
        Stores as ModelGrid fields:
            - Node array of receivers (nodes that receive flow): 
              *'flow_receiver'*
            - Node array of drainage areas: *'drainage_area'*
            - Node array of discharges: *'water__volume_flux'*
            - Node array of steepest downhill slopes: *'topographic__steepest_slope'*
            - Node array containing downstream-to-upstream ordered list of node
              IDs: *'upstream_ID_order'*
            - Node array containing ID of link that leads from each node to its
              receiver (or ITS OWN ID if there is no receiver):
              *'links_to_flow_receiver'*
            - Boolean node array of all local lows: *'flow_sinks'*
        
        Returns:
            - the modified grid object
        
        Examples
        --------
        >>> import numpy as np
        >>> from landlab import RasterModelGrid
        >>> from landlab.components.flow_routing.route_flow_dn import FlowRouter
        >>> mg = RasterModelGrid(5, 4, 1.0)
        >>> elev = np.array([0.,  0.,  0., 0.,
        ...                  0., 21., 10., 0.,
        ...                  0., 31., 20., 0.,
        ...                  0., 32., 30., 0.,
        ...                  0.,  0.,  0., 0.])
        >>> _ = mg.add_field('node','topographic__elevation', elev)
        >>> mg.set_closed_boundaries_at_grid_edges(False, True, True, True)
        >>> fr = FlowRouter(mg)
        >>> mg = fr.route_flow()
        >>> mg.at_node['flow_receiver']
        array([ 0,  1,  2,  3,  4,  1,  2,  7,  8,  6,  6, 11, 12, 10, 10, 15, 16,
               17, 18, 19])
        >>> mg.at_node['drainage_area']
        array([ 1.,  2.,  6.,  1.,  1.,  1.,  5.,  1.,  1.,  1.,  3.,  1.,  1.,
                1.,  1.,  1.,  1.,  1.,  1.,  1.])

        Now let's change the cell area and the runoff rates:
        
        >>> mg = RasterModelGrid(5, 4, 10.) #so cell area==100.
        >>> _ = mg.add_field('node','topographic__elevation', elev) #put the data back into the new grid
        >>> mg.set_closed_boundaries_at_grid_edges(False, True, True, True)
        >>> fr = FlowRouter(mg)
        >>> runoff_rate = np.arange(mg.number_of_nodes)
        >>> _ = mg.add_field('node', 'water__volume_flux_in', runoff_rate)
        >>> mg = fr.route_flow()
        >>> mg.at_node['water__volume_flux']
        array([    0.,   600.,  5400.,   300.,   400.,   500.,  5200.,   700.,
                 800.,   900.,  3700.,  1100.,  1200.,  1300.,  1400.,  1500.,
                1600.,  1700.,  1800.,  1900.])
        
        """
        
        #if elevs is not provided, default to stored grid values, which must be provided as grid
        elevs = self._grid['node'][self.value_field]
        
        node_cell_area = self._grid.forced_cell_areas
            
        
        # Calculate the downhill-positive slopes at the d8 active links
        #TODO: generalize to use EITHER D8, if raster, or just active links,
        # otherwise.
        link_slope = -self._grid.calculate_gradients_at_d8_active_links(elevs)
        # Find the baselevel nodes
        (baselevel_nodes, ) = numpy.where(numpy.logical_or(self._grid.node_status==1, self._grid.node_status==2))

        # Calculate flow directions
        receiver, steepest_slope, sink, recvr_link  = \
            flow_direction_DN.flow_directions(elevs, self._active_links, 
                                         self._activelink_from,
                                         self._activelink_to, link_slope, 
                                         grid=self._grid,
                                         baselevel_nodes=baselevel_nodes)
#############grid=None???
        
        # TODO: either need a way to calculate and return the *length* of the
        # flow links, OR the caller has to handle the raster / non-raster case.
        
        #print 'sinks:', sink

        # Calculate drainage area, discharge, and ...
        a, q, s = flow_accum_bw.flow_accumulation(receiver, sink,
                                                  node_cell_area=node_cell_area, 
                                                  runoff_rate=self._grid.at_node['water__volume_flux_in'])
                                                  
        #added DEJH March 2014:
        #store the generated data in the grid
        self._grid['node']['drainage_area'] = a
        self._grid['node']['flow_receiver'] = receiver
        self._grid['node']['topographic__steepest_slope'] = steepest_slope
        self._grid['node']['water__volume_flux'] = q
        self._grid['node']['upstream_ID_order'] = s
        self._grid['node']['links_to_flow_receiver'] = recvr_link
        self._grid['node']['flow_sinks'] = numpy.zeros_like(receiver, dtype=bool)
        self._grid['node']['flow_sinks'][sink] = True
        
        return self._grid
Пример #3
0
    def route_flow(self, **kwds):
        """Route surface-water flow over a landscape.

        Routes surface-water flow by (1) assigning to each node a single
        drainage direction, and then (2) adding up the number of nodes that
        contribute flow to each node on the grid (including the node itself).

        Stores as ModelGrid fields:
        - Node array of receivers (nodes that receive flow), or ITS OWN ID if
          there is no receiver: *'flow_receiver'*
        - Node array of drainage areas: *'drainage_area'*
        - Node array of discharges: *'water__volume_flux'*
        - Node array of steepest downhill slopes:
          *'topographic__steepest_slope'*
        - Node array containing downstream-to-upstream ordered list of node
          IDs: *'upstream_node_order'*
        - Node array containing ID of link that leads from each node to its
          receiver, or BAD_INDEX_VALUE if no link:
          *'links_to_flow_receiver'*
        - Boolean node array of all local lows: *'flow_sinks'*

        Returns
        -------
        ModelGrid
            The modified grid object

        Examples
        --------
        >>> import numpy as np
        >>> from landlab import RasterModelGrid
        >>> from landlab.components.flow_routing import FlowRouter
        >>> mg = RasterModelGrid((5, 4), spacing=(1, 1))
        >>> elev = np.array([0.,  0.,  0., 0.,
        ...                  0., 21., 10., 0.,
        ...                  0., 31., 20., 0.,
        ...                  0., 32., 30., 0.,
        ...                  0.,  0.,  0., 0.])
        >>> _ = mg.add_field('node','topographic__elevation', elev)
        >>> mg.set_closed_boundaries_at_grid_edges(True, True, True, False)
        >>> fr = FlowRouter(mg)
        >>> mg = fr.route_flow()
        >>> mg.at_node['flow_receiver'] # doctest: +NORMALIZE_WHITESPACE
        array([  0,  1,  2,  3,
                 4,  1,  2,  7,
                 8,  6,  6, 11,
                12, 10, 10, 15,
                16, 17, 18, 19])
        >>> mg.at_node['drainage_area'] # doctest: +NORMALIZE_WHITESPACE
        array([ 0.,  1.,  5.,  0.,
                0.,  1.,  5.,  0.,
                0.,  1.,  3.,  0.,
                0.,  1.,  1.,  0.,
                0.,  0.,  0.,  0.])

        Now let's change the cell area (100.) and the runoff rates:

        >>> mg = RasterModelGrid((5, 4), spacing=(10., 10))

        Put the data back into the new grid.

        >>> _ = mg.add_field('node','topographic__elevation', elev)
        >>> mg.set_closed_boundaries_at_grid_edges(True, True, True, False)
        >>> fr = FlowRouter(mg)
        >>> runoff_rate = np.arange(mg.number_of_nodes)
        >>> _ = mg.add_field('node', 'water__unit_flux_in', runoff_rate,
        ...                  noclobber=False)
        >>> mg = fr.route_flow()
        >>> mg.at_node['water__volume_flux'] # doctest: +NORMALIZE_WHITESPACE
        array([    0.,   500.,  5200.,     0.,
                   0.,   500.,  5200.,     0.,
                   0.,   900.,  3700.,     0.,
                   0.,  1300.,  1400.,     0.,
                   0.,     0.,     0.,     0.])

        """
        # this retained for back compatibility - method now set in __init__.
        if 'method' in kwds:
            warnings.warn(
                "'method' should be set at initialization now. " +
                "Please update your code.", DeprecationWarning)
            # raise NameError
            if kwds['method'] not in ('D8', 'D4'):
                raise ValueError(
                    'method not understood ({method})'.format(method=method))
            else:
                self.method = kwds['method']
            if not self._is_raster:
                self.method = None

        # if elevs is not provided, default to stored grid values, which must
        # be provided as grid
        elevs = self._grid['node']['topographic__elevation']

        node_cell_area = self._grid.cell_area_at_node.copy()
        node_cell_area[self._grid.closed_boundary_nodes] = 0.
        # closed cells can't contribute

        # Calculate the downhill-positive slopes at the d8 active links
        if self.method == 'D8':
            link_slope = -self._grid.calculate_gradients_at_d8_active_links(
                elevs)
        else:
            link_slope = -self._grid.calculate_gradients_at_active_links(elevs)

        # Find the baselevel nodes
        (baselevel_nodes, ) = numpy.where(
            numpy.logical_or(self._grid.status_at_node == 1,
                             self._grid.status_at_node == 2))

        # Calculate flow directions
        if self.method == 'D4':
            num_d4_active = self._grid.number_of_active_links  # only d4
            receiver, steepest_slope, sink, recvr_link = \
                flow_direction_DN.flow_directions(elevs, self._active_links,
                                         self._activelink_from[:num_d4_active],
                                         self._activelink_to[:num_d4_active],
                                         link_slope,
                                         grid=self._grid,
                                         baselevel_nodes=baselevel_nodes)
        else:  # Voronoi or D8
            receiver, steepest_slope, sink, recvr_link = \
                flow_direction_DN.flow_directions(elevs, self._active_links,
                                     self._activelink_from,
                                     self._activelink_to, link_slope,
                                     grid=self._grid,
                                     baselevel_nodes=baselevel_nodes)

        # TODO: either need a way to calculate and return the *length* of the
        # flow links, OR the caller has to handle the raster / non-raster case.

        # Calculate drainage area, discharge, and ...
        a, q, s = flow_accum_bw.flow_accumulation(
            receiver,
            sink,
            node_cell_area=node_cell_area,
            runoff_rate=self._grid.at_node['water__unit_flux_in'])

        # added DEJH March 2014:
        # store the generated data in the grid
        self._grid['node']['drainage_area'][:] = a
        self._grid['node']['flow_receiver'][:] = receiver
        self._grid['node']['topographic__steepest_slope'][:] = steepest_slope
        self._grid['node']['water__volume_flux'][:] = q
        self._grid['node']['upstream_node_order'][:] = s
        self._grid['node']['links_to_flow_receiver'][:] = recvr_link
        self._grid['node']['flow_sinks'][:] = numpy.zeros_like(receiver,
                                                               dtype=bool)
        self._grid['node']['flow_sinks'][sink] = True

        return self._grid
Пример #4
0
    def route_flow(self, elevs=None, grid=None, node_cell_area=1.0, runoff_rate=1.0,
                   boundary_nodes=None):
        """
        Routes surface-water flow by (1) assigning to each node a single 
        drainage direction, and then (2) adding up the number of nodes that
        contribute flow to each node on the grid (including the node itself).
        If a scalar is specified for cell_area, computes the total surface 
        contributing area by assuming that each cell has the same surface area.
        If an array is given (with length equal to the number of nodes), these
        areas are used for each cell. Likewise, runoff_rate, in length per
        time (volume per area per time) may be given either as a scalar
        (identical for each cell) or as an array whose length is the number of
        nodes in the grid.
        
        Takes:
            - Either *elevs*, an array of node elevations, or *grid*, a 
              reference to a ModelGrid.
              
        Takes as optional inputs:
            - *node_cell_area*, a float of the cell areas, if a raster, or
              an array of the cell areas.
            - *runoff_rate*, a float (for constant rainfall) or array (for
              spatially variable rainfall) of runoff rates, such that drainage
              area is in volume, rather than number of upstream cells.
        
        Stores as ModelGrid fields, or returns, if *elevs* was provided rather
        than *grid*:
            - Node array of receivers (nodes that receive flow): 
              *'flow_receiver'*
            - Node array of drainage areas: *'drainage_area'*
            - Node array of discharges: *'water_discharges'*
            - Node array of steepest downhill slopes: *'steepest_slope'*
            - Node array containing downstream-to-upstream ordered list of node
              IDs: *'upstream_ID_order'*
            - Node array containing ID of link that leads from each node to its
              receiver (or UNDEFINED_INDEX if there is no receiver):
              *'links_to_flow_receiver'*
        
        Returns, if *grid* was provided:
            - the modified grid object
        
        Example:
            >>> from landlab import RasterModelGrid
            >>> mg = RasterModelGrid(5, 4, 1.0)
            >>> elev = numpy.array([0.,  0.,  0., 0.,
            ...                     0., 21., 10., 0.,
            ...                     0., 31., 20., 0.,
            ...                     0., 32., 30., 0.,
            ...                     0.,  0.,  0., 0.])
            >>> mg.set_inactive_boundaries(False, True, True, True)
            >>> fr = FlowRouter(mg)
            >>> r, a, q, ss, s, rl = fr.route_flow(elevs=elev)
            >>> r
            array([ 0,  1,  2,  3,  4,  1,  2,  7,  8,  6,  6, 11, 12, 10, 10, 15, 16,
                   17, 18, 19])
            >>> a
            array([ 1.,  2.,  6.,  1.,  1.,  1.,  5.,  1.,  1.,  1.,  3.,  1.,  1.,
                    1.,  1.,  1.,  1.,  1.,  1.,  1.])
            >>> r, a, q, ss, s, rl = fr.route_flow(elevs=elev, node_cell_area=10.0)
            >>> a
            array([ 10.,  20.,  60.,  10.,  10.,  10.,  50.,  10.,  10.,  10.,  30.,
                    10.,  10.,  10.,  10.,  10.,  10.,  10.,  10.,  10.])
            >>> cell_areas = 10.0 + numpy.arange(mg.number_of_nodes)
            >>> r, a, q, ss, s, rl = fr.route_flow(elevs=elev, node_cell_area=cell_areas)
            >>> a
            array([  10.,   26.,  114.,   13.,   14.,   15.,  102.,   17.,   18.,
                     19.,   67.,   21.,   22.,   23.,   24.,   25.,   26.,   27.,
                     28.,   29.])
            >>> runoff_rate = numpy.arange(mg.number_of_nodes)
            >>> r, a, q, ss, s, rl = fr.route_flow(elevs=elev, node_cell_area=100.0, runoff_rate=runoff_rate)
            >>> q
            array([    0.,   600.,  5400.,   300.,   400.,   500.,  5200.,   700.,
                     800.,   900.,  3700.,  1100.,  1200.,  1300.,  1400.,  1500.,
                    1600.,  1700.,  1800.,  1900.])
            >>> r, a, q, ss, s, rl = fr.route_flow(elevs=elev, node_cell_area=cell_areas, runoff_rate=runoff_rate)
            >>> q
            array([    0.,    86.,  1126.,    39.,    56.,    75.,  1102.,   119.,
                     144.,   171.,   835.,   231.,   264.,   299.,   336.,   375.,
                     416.,   459.,   504.,   551.])
        """
        
        #if elevs is not provided, default to stored grid values, which must be provided as grid
        if elevs is None:
            if grid is not None:
                self._grid = grid
                elevs = grid['node'][self.value_field]
            else:
                raise ValueError('Either an elevation array or a copy of the grid must be provided!')
        
        try:
            node_cell_area = self._grid.forced_cell_areas
        except:
            pass
        
        # Calculate the downhill-positive slopes at the d8 active links
        #TODO: generalize to use EITHER D8, if raster, or just active links,
        # otherwise.
        link_slope = -self._grid.calculate_gradients_at_d8_active_links(elevs)
        
        # Find the baselevel nodes
        (baselevel_nodes, ) = numpy.where( self._grid.node_status==1 )

        # Calculate flow directions
        receiver, steepest_slope, sink, recvr_link  = \
            flow_direction_DN.flow_directions(elevs, self._active_links, self._activelink_from,
                                         self._activelink_to, link_slope, 
                                         grid=grid,
                                         baselevel_nodes=baselevel_nodes)
#############grid=None???
        
        # TODO: either need a way to calculate and return the *length* of the
        # flow links, OR the caller has to handle the raster / non-raster case.
        
        #print 'sinks:', sink

        # Calculate drainage area, discharge, and ...
        a, q, s = flow_accum_bw.flow_accumulation(receiver, sink,
                                                  node_cell_area, runoff_rate,
                                                  boundary_nodes)
                                                  
        #added DEJH March 2014:
        #store the generated data in the grid
        self._grid['node']['drainage_area'] = a
        self._grid['node']['flow_receiver'] = receiver
        self._grid['node']['steepest_slope'] = steepest_slope
        self._grid['node']['water_discharges'] = q
        self._grid['node']['upstream_ID_order'] = s
        self._grid['node']['links_to_flow_receiver'] = recvr_link

        if grid:
            return self._grid
        else:
            return receiver, a, q, steepest_slope, s, recvr_link
Пример #5
0
    def route_flow(self, method='D8'):
        """Route surface-water flow over a landscape.

        Routes surface-water flow by (1) assigning to each node a single
        drainage direction, and then (2) adding up the number of nodes that
        contribute flow to each node on the grid (including the node itself).

        Stores as ModelGrid fields:
        - Node array of receivers (nodes that receive flow):
              *'flow_receiver'*
        - Node array of drainage areas: *'drainage_area'*
        - Node array of discharges: *'water__volume_flux'*
        - Node array of steepest downhill slopes:
          *'topographic__steepest_slope'*
        - Node array containing downstream-to-upstream ordered list of node
          IDs: *'upstream_node_order'*
        - Node array containing ID of link that leads from each node to its
          receiver (or ITS OWN ID if there is no receiver):
          *'links_to_flow_receiver'*
        - Boolean node array of all local lows: *'flow_sinks'*

        Parameters
        ----------
        method : {'D8', 'D4'}, optional
            Routing method ('D8' is the default). This keyword has no effect
            for a Voronoi-based grid.

        Returns
        -------
        ModelGrid
            The modified grid object

        Examples
        --------
        >>> import numpy as np
        >>> from landlab import RasterModelGrid
        >>> from landlab.components.flow_routing.route_flow_dn import FlowRouter
        >>> mg = RasterModelGrid((5, 4), spacing=(1, 1))
        >>> elev = np.array([0.,  0.,  0., 0.,
        ...                  0., 21., 10., 0.,
        ...                  0., 31., 20., 0.,
        ...                  0., 32., 30., 0.,
        ...                  0.,  0.,  0., 0.])
        >>> _ = mg.add_field('node','topographic__elevation', elev)
        >>> mg.set_closed_boundaries_at_grid_edges(False, True, True, True)
        >>> fr = FlowRouter(mg)
        >>> mg = fr.route_flow()
        >>> mg.at_node['flow_receiver'] # doctest: +NORMALIZE_WHITESPACE
        array([  0,  1,  2,  3,
                 4,  1,  2,  7,
                 8,  6,  6, 11,
                12, 10, 10, 15,
                16, 17, 18, 19])
        >>> mg.at_node['drainage_area'] # doctest: +NORMALIZE_WHITESPACE
        array([ 0.,  1.,  5.,  0.,
                0.,  1.,  5.,  0.,
                0.,  1.,  3.,  0.,
                0.,  1.,  1.,  0.,
                0.,  0.,  0.,  0.])

        Now let's change the cell area (100.) and the runoff rates:

        >>> mg = RasterModelGrid((5, 4), spacing=(10., 10))

        Put the data back into the new grid.

        >>> _ = mg.add_field('node','topographic__elevation', elev)
        >>> mg.set_closed_boundaries_at_grid_edges(False, True, True, True)
        >>> fr = FlowRouter(mg)
        >>> runoff_rate = np.arange(mg.number_of_nodes)
        >>> _ = mg.add_field('node', 'water__volume_flux_in', runoff_rate)
        >>> mg = fr.route_flow()
        >>> mg.at_node['water__volume_flux'] # doctest: +NORMALIZE_WHITESPACE
        array([    0.,   500.,  5200.,     0.,
                   0.,   500.,  5200.,     0.,
                   0.,   900.,  3700.,     0.,
                   0.,  1300.,  1400.,     0.,
                   0.,     0.,     0.,     0.])

        """
        if method not in ('D8', 'D4'):
            raise ValueError('method not understood ({method})'.format(
                method=method))
        if not self._is_raster:
            method = None

        # if elevs is not provided, default to stored grid values, which must
        # be provided as grid
        elevs = self._grid['node'][self.value_field]

        node_cell_area = self._grid.cell_area_at_node.copy()
        node_cell_area[self._grid.closed_boundary_nodes] = 0.
        # closed cells can't contribute


        # Calculate the downhill-positive slopes at the d8 active links
        if method == 'D8':
            link_slope = - self._grid.calculate_gradients_at_d8_active_links(
                elevs)
        else:
            link_slope = - self._grid.calculate_gradients_at_active_links(
                elevs)

        # Find the baselevel nodes
        (baselevel_nodes, ) = numpy.where(
            numpy.logical_or(self._grid.status_at_node == 1,
                             self._grid.status_at_node == 2))

        # Calculate flow directions
        if method == 'D4':
            num_d4_active = self._grid.number_of_active_links  # only d4
            receiver, steepest_slope, sink, recvr_link  = \
                flow_direction_DN.flow_directions(elevs, self._active_links,
                                         self._activelink_from[:num_d4_active],
                                         self._activelink_to[:num_d4_active],
                                         link_slope,
                                         grid=self._grid,
                                         baselevel_nodes=baselevel_nodes)
        else:  # Voronoi or D8
            receiver, steepest_slope, sink, recvr_link  = \
                flow_direction_DN.flow_directions(elevs, self._active_links,
                                     self._activelink_from,
                                     self._activelink_to, link_slope,
                                     grid=self._grid,
                                     baselevel_nodes=baselevel_nodes)
        #############grid=None???

        # TODO: either need a way to calculate and return the *length* of the
        # flow links, OR the caller has to handle the raster / non-raster case.

        #print 'sinks:', sink

        # Calculate drainage area, discharge, and ...
        a, q, s = flow_accum_bw.flow_accumulation(
            receiver, sink, node_cell_area=node_cell_area,
            runoff_rate=self._grid.at_node['water__volume_flux_in'])

        #added DEJH March 2014:
        #store the generated data in the grid
        self._grid['node']['drainage_area'] = a
        self._grid['node']['flow_receiver'] = receiver
        self._grid['node']['topographic__steepest_slope'] = steepest_slope
        self._grid['node']['water__volume_flux'] = q
        self._grid['node']['upstream_node_order'] = s
        self._grid['node']['links_to_flow_receiver'] = recvr_link
        self._grid['node']['flow_sinks'] = numpy.zeros_like(receiver,
                                                            dtype=bool)
        self._grid['node']['flow_sinks'][sink] = True

        return self._grid