Ejemplo n.º 1
0
    def run_one_step_adaptive(self, grid, dt=None, Klr=None,
                              inlet_area_ts=None, qsinlet_ts=None, **kwds):

        if Klr is None:  # Added10/9 to allow changing rainrate (indirectly this way.)
            Klr = self.Klr
        UC = self._UC
        TB = self._TB
        inlet_on = self.inlet_on  # this is a true/false flag
        Kv = self.Kv
        frac = self.frac
        qs_in = self.qs_in
        dzdt = self.dzdt
        alph = self.alph
        self.dt = dt
        vol_lat = self.grid.at_node['volume__lateral_erosion']
        kw = 10.
        F = 0.02
        runoffms = (Klr * F / kw)**2
        Kl = Kv * Klr
        z = grid.at_node['topographic__elevation']
        # clear qsin for next loop
        qs_in = grid.add_zeros('node', 'qs_in', noclobber=False)
        lat_nodes = np.zeros(grid.number_of_nodes, dtype=int)
        dzlat = np.zeros(grid.number_of_nodes)
        dzver = np.zeros(grid.number_of_nodes)
        vol_lat_dt = np.zeros(grid.number_of_nodes)

        if inlet_on is True:
            # define inlet_node
            inlet_node = self.inlet_node
            # if a value is passed with qsinlet_ts, qsinlet has changed with this timestep,
            # so reset qsinlet to qsinlet_ts
            if qsinlet_ts is not None:
                qsinlet = qsinlet_ts
                qs_in[inlet_node] = qsinlet
            # if nothing is passed with qsinlet_ts, qsinlet remains the same from
            # initialized parameters
            else:  # qsinlet_ts==None:
                qsinlet = self.qsinlet
                qs_in[inlet_node] = qsinlet

            if inlet_area_ts is not None:
                inlet_area = inlet_area_ts
                runoffinlet = np.ones(grid.number_of_nodes) * grid.dx**2
                # Change the runoff at the inlet node to node area + inlet node
                runoffinlet[inlet_node] += inlet_area
                _ = grid.add_field('node', 'water__unit_flux_in', runoffinlet,
                                   noclobber=False)
                # if inlet area has changed with time (so we have a new inlet area here)
                fa = FlowAccumulator(grid,
                                     surface='topographic__elevation',
                                     flow_director='FlowDirectorD8',
                                     runoff_rate=None,
                                     depression_finder=None)  # "DepressionFinderAndRouter", router="D8")
                (da, q) = fa.accumulate_flow()
                q = grid.at_node['surface_water__discharge']
                # this is the drainage area that I need for code below with an inlet set
                # by spatially varible runoff.
                da = q / grid.dx**2
            else:
                q = grid.at_node['surface_water__discharge']
                # this is the drainage area that I need for code below with an inlet set
                # by spatially varible runoff.
                da = q / grid.dx**2
        # if inlet flag is not on, proceed as normal.
        else:
            # renamed this drainage area set by flow router
            da = grid.at_node['drainage_area']
        s = grid.at_node['flow__upstream_node_order']
        max_slopes = grid.at_node['topographic__steepest_slope']
        flowdirs = grid.at_node['flow__receiver_node']
        l = s[np.where((grid.status_at_node[s] == 0))[0]]
        dwnst_nodes = l
        # reverse list so we go from upstream to down stream
        dwnst_nodes = dwnst_nodes[::-1]
        # local time
        time = 0
        globdt = dt

        while time < globdt:
            max_slopes[:] = max_slopes.clip(0)
            # here calculate dzdt for each node, with initial time step
            for i in dwnst_nodes:
                dep = alph * qs_in[i] / da[i]
                ero = -Kv[i] * da[i]**(0.5) * max_slopes[i]
                dzver[i] = dep + ero
                petlat = 0.
                # water depth in meters, needed for lateral erosion calc
                wd = 0.4 * (da[i] * runoffms)**0.35

                if i in flowdirs:
                    # Node_finder picks the lateral node to erode based on angle
                    # between segments between three nodes
                    [lat_node, inv_rad_curv] = Node_Finder2(grid, i, flowdirs, da)
                    # node_finder returns the lateral node ID and the radius of
                    # curvature
                    lat_nodes[i] = lat_node

                    # if the lateral node is not 0 or -1 continue.
                    if lat_node > 0:
                        # if the elevation of the lateral node is higher than primary node,
                        # calculate a new potential lateral erosion (L/T), which is
                        # negative
                        if z[lat_node] > z[i]:
                            petlat = -Kl[i] * da[i] * max_slopes[i] * inv_rad_curv
                            # the calculated potential lateral erosion is mutiplied by the length of the node
                            # and the bank height, then added to an array, vol_lat_dt, for volume eroded
                            # laterally  *per timestep* at each node. This vol_lat_dt is reset to zero for
                            # each timestep loop. vol_lat_dt is added to itself more than one primary nodes are
                            # laterally eroding this lat_node
                            # volume of lateral erosion per timestep
                            vol_lat_dt[lat_node] += abs(petlat) * grid.dx * wd
                # send sediment downstream. sediment eroded from vertical incision
                # and lateral erosion is sent downstream
                qs_in[flowdirs[i]] += qs_in[i] - \
                    (dzver[i] * grid.dx**2) - \
                    (petlat * grid.dx * wd)  # qsin to next node
            dzdt[:] = dzver
            # Do a time-step check
            # If the downstream node is eroding at a slower rate than the
            # upstream node, there is a possibility of flow direction reversal,
            # or at least a flattening of the landscape.
            # Limit dt so that this flattening or reversal doesn't happen.
            # How close you allow these two points to get to eachother is
            # determined by the variable frac.
            # dtn is an arbitrarily large number to begin with, but will be adapted as we step through
            # the nodes
            dtn = dt * 50  # starting minimum timestep for this round
            for i in dwnst_nodes:
                # are points converging? ie, downstream eroding slower than upstream
                dzdtdif = dzdt[flowdirs[i]] - dzdt[i]
                # if points converging, find time to zero slope
                if dzdtdif > 1.e-5 and max_slopes[i] > 1e-5:
                    # time to flat between points
                    dtflat = (z[i] - z[flowdirs[i]]) / dzdtdif
                    # if time to flat is smaller than dt, take the lower value
                    if dtflat < dtn:
                        dtn = dtflat
#                        assert dtn>0, "dtn <0 at dtflat"
                    # if dzdtdif*dtflat will make upstream lower than downstream, find
                    # time to flat
                    if dzdtdif * dtflat > (z[i] - z[flowdirs[i]]):
                        dtn = (z[i] - z[flowdirs[i]]) / dzdtdif
            dtn *= frac
            # new minimum timestep for this round of nodes
            dt = min(abs(dtn), dt)
            assert dt > 0., "timesteps less than 0."

            # vol_lat is the total volume eroded from the lateral nodes through
            # the entire model run. So vol_lat is itself plus vol_lat_dt (for current loop)
            # times stable timestep size
            vol_lat[:] += vol_lat_dt * dt
            # this loop determines if enough lateral erosion has happened to change
            # the height of the neighbor node.
            for i in dwnst_nodes:
                lat_node = lat_nodes[i]
                wd = 0.4 * (da[i] * runoffms)**0.35
                if lat_node > 0:  # greater than zero now bc inactive neighbors are value -1
                    if z[lat_node] > z[i]:
                        # vol_diff is the volume that must be eroded from lat_node so that its
                        # elevation is the same as node downstream of primary node
                        # UC model: this would represent undercutting (the water height
                        # at node i), slumping, and instant removal.
                        if UC == 1:
                            voldiff = (z[i] + wd - z[flowdirs[i]]) * grid.dx**2
                        # TB model: entire lat node must be eroded before lateral
                        # erosion occurs
                        if TB == 1:
                            voldiff = (z[lat_node] - z[flowdirs[i]]) * grid.dx**2
                        # if the total volume eroded from lat_node is greater than the volume
                        # needed to be removed to make node equal elevation,
                        # then instantaneously remove this height from lat node. already
                        # has timestep in it
                        if vol_lat[lat_node] >= voldiff:
                            dzlat[lat_node] = z[flowdirs[i]] - z[lat_node]  # -0.001
                            # after the lateral node is eroded, reset its volume eroded
                            # to zero
                            vol_lat[lat_node] = 0.0

            # multiply dzver(changed to dzdt above) by timestep size and combine with lateral erosion
            # dzlat, which is already a length for the chosen time step
            dz = dzdt * dt + dzlat
            # change height of landscape
            z[:] = dz + z
            grid['node']['topographic__elevation'][grid.core_nodes] = z[grid.core_nodes]
            # update elapsed time
            time = dt + time
            # check to see that you are within 0.01% of the storm duration, if so
            # done, if not continue

            if time > 0.9999 * globdt:
                time = globdt

            else:
                dt = globdt - time
                qs_in = grid.zeros(centering='node')
                # recalculate flow directions
                fa = FlowAccumulator(grid,
                                     surface='topographic__elevation',
                                     flow_director='FlowDirectorD8',
                                     runoff_rate=None,
                                     depression_finder=None)  # "DepressionFinderAndRouter", router="D8")
                (da, q) = fa.accumulate_flow()
                if inlet_on:
                    #                   #if inlet on, reset drainage area and qsin to reflect inlet conditions
                    # this is the drainage area that I need for code below with an inlet
                    # set by spatially varible runoff.
                    da = q / grid.dx**2
                    qs_in[inlet_node] = qsinlet
                else:
                    # otherwise, drainage area is just drainage area. *** could remove the
                    # below line to speed up model. It's not really necessary.
                    # renamed this drainage area set by flow router
                    da = grid.at_node['drainage_area']
                s = grid.at_node['flow__upstream_node_order']
                max_slopes = grid.at_node['topographic__steepest_slope']
                q = grid.at_node['surface_water__discharge']
                flowdirs = grid.at_node['flow__receiver_node']
                l = s[np.where((grid.status_at_node[s] == 0))[0]]
                dwnst_nodes = l
                dwnst_nodes = dwnst_nodes[::-1]

                lat_nodes = np.zeros(grid.number_of_nodes, dtype=int)
                dzlat = np.zeros(grid.number_of_nodes)
                vol_lat_dt = np.zeros(grid.number_of_nodes)
                dzver = np.zeros(grid.number_of_nodes)

        return grid, dzlat
Ejemplo n.º 2
0
    def run_one_step_basic(self, grid, dt=None, Klr=None,
                           inlet_area_ts=None, qsinlet_ts=None, **kwds):
        if Klr is None:  # Added10/9 to allow changing rainrate (indirectly this way.)
            Klr = self.Klr

        UC = self._UC
        TB = self._TB
        inlet_on = self.inlet_on  # this is a true/false flag
        Kv = self.Kv
        qs_in = self.qs_in
        dzdt = self.dzdt
        alph = self.alph
        self.dt = dt
        vol_lat = self.grid.at_node['volume__lateral_erosion']
        kw = 10.
        F = 0.02
        # May 2, runoff calculated below (in m/s) is important for calculating
        # discharge and water depth correctly. renamed runoffms to prevent
        # confusion with other uses of runoff
        runoffms = (Klr * F / kw)**2
        # Kl is calculated from ratio of lateral to vertical K parameters
        Kl = Kv * Klr
        z = grid.at_node['topographic__elevation']
        # clear qsin for next loop
        qs_in = grid.add_zeros('node', 'qs_in', noclobber=False)
        qs = grid.add_zeros('node', 'qs', noclobber=False)
        lat_nodes = np.zeros(grid.number_of_nodes, dtype=int)
        dzlat = np.zeros(grid.number_of_nodes)
        dzver = np.zeros(grid.number_of_nodes)
        vol_lat_dt = np.zeros(grid.number_of_nodes)
        if inlet_on is True:
            inlet_node = self.inlet_node
            # if a value is passed with qsinlet_ts, qsinlet has changed with this timestep,
            # so reset qsinlet to qsinlet_ts
            if qsinlet_ts is not None:
                qsinlet = qsinlet_ts
                qs_in[inlet_node] = qsinlet
            # if nothing is passed with qsinlet_ts, qsinlet remains the same from
            # initialized parameters
            else:  # qsinlet_ts==None:
                qsinlet = self.qsinlet
                qs_in[inlet_node] = qsinlet
            if inlet_area_ts is not None:
                inlet_area = inlet_area_ts
                runoffinlet = np.ones(grid.number_of_nodes) * grid.dx**2
                # Change the runoff at the inlet node to node area + inlet node
                runoffinlet[inlet_node] += inlet_area
                _ = grid.add_field('node', 'water__unit_flux_in', runoffinlet,
                                   noclobber=False)
                # if inlet area has changed with time (so we have a new inlet area here)
                fa = FlowAccumulator(grid,
                                     surface='topographic__elevation',
                                     flow_director='FlowDirectorD8',
                                     runoff_rate=None,
                                     depression_finder=None)  # "DepressionFinderAndRouter", router="D8")
                (da, q) = fa.accumulate_flow()
                q = grid.at_node['surface_water__discharge']
                # this is the drainage area that I need for code below with an inlet set
                # by spatially varible runoff.
                da = q / grid.dx**2
            else:  # inletarea not changing with time
                q = grid.at_node['surface_water__discharge']
                # this is the drainage area that I need for code below with an inlet set
                # by spatially varible runoff.
                da = q / grid.dx**2
        # if inlet flag is not on, proceed as normal.
        else:
            # renamed this drainage area set by flow router
            da = grid.at_node['drainage_area']
        # flow__upstream_node_order is node array contianing downstream to
        # upstream order list of node ids
        s = grid.at_node['flow__upstream_node_order']
        max_slopes = grid.at_node['topographic__steepest_slope']
        flowdirs = grid.at_node['flow__receiver_node']

        # make a list l, where node status is interior (signified by label 0) in s
        l = s[np.where((grid.status_at_node[s] == 0))[0]]
        dwnst_nodes = l.copy()
        # reverse list so we go from upstream to down stream
        dwnst_nodes = dwnst_nodes[::-1]
        max_slopes[:] = max_slopes.clip(0)
        for i in dwnst_nodes:
            # calc deposition and erosion
            dep = alph * qs_in[i] / da[i]
            ero = -Kv[i] * da[i]**(0.5) * max_slopes[i]
            dzver[i] = dep + ero
            # potential lateral erosion initially set to 0
            petlat = 0.
            # water depth in meters, needed for lateral erosion calc
            wd = 0.4 * (da[i] * runoffms)**0.35

            # Choose lateral node for node i. If node i flows downstream, continue.
            # if node i is the first cell at the top of the drainage network, don't go
            # into this loop because in this case, node i won't have a "donor" node
            if i in flowdirs:
                # Node_finder picks the lateral node to erode based on angle
                # between segments between three nodes
                [lat_node, inv_rad_curv] = Node_Finder2(grid, i, flowdirs, da)
                # node_finder returns the lateral node ID and the radius of curvature
                lat_nodes[i] = lat_node
                # if the lateral node is not 0 or -1 continue. lateral node may be
                # 0 or -1 if a boundary node was chosen as a lateral node. then
                # radius of curavature is also 0 so there is no lateral erosion
                if lat_node > 0:
                    # if the elevation of the lateral node is higher than primary node,
                    # calculate a new potential lateral erosion (L/T), which is negative
                    if z[lat_node] > z[i]:
                        petlat = -Kl[i] * da[i] * max_slopes[i] * inv_rad_curv
                        # the calculated potential lateral erosion is mutiplied by the length of the node
                        # and the bank height, then added to an array, vol_lat_dt, for volume eroded
                        # laterally  *per timestep* at each node. This vol_lat_dt is reset to zero for
                        # each timestep loop. vol_lat_dt is added to itself in case more than one primary
                        # nodes are laterally eroding this lat_node
                        # volume of lateral erosion per timestep
                        vol_lat_dt[lat_node] += abs(petlat) * grid.dx * wd

            # send sediment downstream. sediment eroded from vertical incision
            # and lateral erosion is sent downstream
            qs_in[flowdirs[i]] += qs_in[i] - \
                (dzver[i] * grid.dx**2) - (petlat * grid.dx * wd)  # qsin to next node
        qs[:] = qs_in - (dzver * grid.dx**2)
        dzdt[:] = dzver * dt
        vol_lat[:] += vol_lat_dt * dt
        # this loop determines if enough lateral erosion has happened to change
        # the height of the neighbor node.
        for i in dwnst_nodes:
            lat_node = lat_nodes[i]
            wd = 0.4 * (da[i] * runoffms)**0.35
            if lat_node > 0:  # greater than zero now bc inactive neighbors are value -1
                if z[lat_node] > z[i]:
                    # vol_diff is the volume that must be eroded from lat_node so that its
                    # elevation is the same as node downstream of primary node
                    # UC model: this would represent undercutting (the water height at
                    # node i), slumping, and instant removal.
                    if UC == 1:
                        voldiff = (z[i] + wd - z[flowdirs[i]]) * grid.dx**2
                    # TB model: entire lat node must be eroded before lateral erosion
                    # occurs
                    if TB == 1:
                        voldiff = (z[lat_node] - z[flowdirs[i]]) * grid.dx**2
                    # if the total volume eroded from lat_node is greater than the volume
                    # needed to be removed to make node equal elevation,
                    # then instantaneously remove this height from lat node. already has
                    # timestep in it
                    if vol_lat[lat_node] >= voldiff:
                        dzlat[lat_node] = z[flowdirs[i]] - z[lat_node]  # -0.001
                        # after the lateral node is eroded, reset its volume eroded to
                        # zero
                        vol_lat[lat_node] = 0.0
        # combine vertical and lateral erosion
        dz = dzdt + dzlat
        # change height of landscape
        z[:] = dz + z
        grid['node']['topographic__elevation'][grid.core_nodes] = z[grid.core_nodes]
        return grid, dzlat