def get_sed_flux_function(self, rel_sed_flux):
     if self.type == "generalized_humped":
         "Returns K*f(qs,qc)"
         sed_flux_fn = (self.kappa * (rel_sed_flux**self.nu + self.c) *
                        np.exp(-self.phi * rel_sed_flux))
     elif self.type == "linear_decline":
         sed_flux_fn = 1.0 - rel_sed_flux
     elif self.type == "parabolic":
         raise MissingKeyError(
             "Pure parabolic (where intersect at zero flux is exactly " +
             "zero) is currently not supported, sorry. Try " +
             "almost_parabolic instead?")
         sed_flux_fn = 1.0 - 4.0 * (rel_sed_flux - 0.5)**2.0
     elif self.type == "almost_parabolic":
         sed_flux_fn = np.where(
             rel_sed_flux > 0.1,
             1.0 - 4.0 * (rel_sed_flux - 0.5)**2.0,
             2.6 * rel_sed_flux + 0.1,
         )
     elif self.type == "None":
         sed_flux_fn = 1.0
     else:
         raise MissingKeyError(
             "Provided sed flux sensitivity type in input file was not " +
             "recognised!")
     return sed_flux_fn
    def get_sed_flux_function_pseudoimplicit(self, sed_in, trans_cap_vol_out,
                                             prefactor_for_volume,
                                             prefactor_for_dz):
        rel_sed_flux_in = sed_in / trans_cap_vol_out
        rel_sed_flux = rel_sed_flux_in
        if self.type == 'generalized_humped':
            "Returns K*f(qs,qc)"

            def sed_flux_fn_gen(rel_sed_flux_in):
                return self.kappa * (rel_sed_flux_in**self.nu + self.c
                                     ) * np.exp(-self.phi * rel_sed_flux_in)
        elif self.type == 'linear_decline':

            def sed_flux_fn_gen(rel_sed_flux_in):
                return 1. - rel_sed_flux_in
        elif self.type == 'parabolic':
            raise MissingKeyError(
                'Pure parabolic (where intersect at zero flux is exactly zero) is currently not supported, sorry. Try almost_parabolic instead?'
            )

            def sed_flux_fn_gen(rel_sed_flux_in):
                return 1. - 4. * (rel_sed_flux_in - 0.5)**2.
        elif self.type == 'almost_parabolic':

            def sed_flux_fn_gen(rel_sed_flux_in):
                return np.where(rel_sed_flux_in > 0.1,
                                1. - 4. * (rel_sed_flux_in - 0.5)**2.,
                                2.6 * rel_sed_flux_in + 0.1)
        elif self.type == 'None':

            def sed_flux_fn_gen(rel_sed_flux_in):
                return 1.
        else:
            raise MissingKeyError(
                'Provided sed flux sensitivity type in input file was not recognised!'
            )

        for i in xrange(self.pseudoimplicit_repeats):
            sed_flux_fn = sed_flux_fn_gen(rel_sed_flux)
            sed_vol_added = prefactor_for_volume * sed_flux_fn
            rel_sed_flux = rel_sed_flux_in + sed_vol_added / trans_cap_vol_out
            #print rel_sed_flux
            if rel_sed_flux >= 1.:
                rel_sed_flux = 1.
                break
            if rel_sed_flux < 0.:
                rel_sed_flux = 0.
                break
        last_sed_flux_fn = sed_flux_fn
        sed_flux_fn = sed_flux_fn_gen(rel_sed_flux)
        #this error could alternatively be used to break the loop
        error_in_sed_flux_fn = sed_flux_fn - last_sed_flux_fn
        dz = prefactor_for_dz * sed_flux_fn
        sed_flux_out = rel_sed_flux * trans_cap_vol_out
        return dz, sed_flux_out, rel_sed_flux, error_in_sed_flux_fn
Beispiel #3
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",
                                                   noclobber=False)

        self._lf = DepressionFinderAndRouter(self._grid, routing=self._routing)
        self._fr = FlowAccumulator(self._grid, flow_director=self._routing)
 def get_sed_flux_function(self, rel_sed_flux):
     if self.type == 'generalized_humped':
         "Returns K*f(qs,qc)"
         sed_flux_fn = self.kappa * (rel_sed_flux**self.nu + self.c
                                     ) * np.exp(-self.phi * rel_sed_flux)
     elif self.type == 'linear_decline':
         sed_flux_fn = (1. - rel_sed_flux)
     elif self.type == 'parabolic':
         raise MissingKeyError(
             'Pure parabolic (where intersect at zero flux is exactly zero) is currently not supported, sorry. Try almost_parabolic instead?'
         )
         sed_flux_fn = 1. - 4. * (rel_sed_flux - 0.5)**2.
     elif self.type == 'almost_parabolic':
         sed_flux_fn = np.where(rel_sed_flux > 0.1,
                                1. - 4. * (rel_sed_flux - 0.5)**2.,
                                2.6 * rel_sed_flux + 0.1)
     elif self.type == 'None':
         sed_flux_fn = 1.
     else:
         raise MissingKeyError(
             'Provided sed flux sensitivity type in input file was not recognised!'
         )
     return sed_flux_fn
    def initialize(self, grid, params_file):
        '''
        params_file is the name of the text file containing the parameters 
        needed for this stream power component.
        
        ***Parameters for input file***
        OBLIGATORY:
            * Qc -> String. Controls how to set the carrying capacity.
                Either 'MPM', or a string giving the name of the model field
                where capacity values are stored on nodes.
                At the moment, only 'MPM' is permitted as a way to set the 
                capacity automatically, but expansion would be trivial.
                If 'from_array', the module will attempt to set the capacity
                Note capacities must be specified as volume flux.
            * 
            
            ...Then, assuming you set Qc=='MPM':
            * b_sp, c_sp -> Floats. These are the powers on discharge and 
                drainage area in the equations used to control channel width and 
                basin hydrology, respectively:
                        W = k_w * Q**b_sp
                        Q = k_Q * A**c_sp
                These parameters are used to constrain flow depth, and may be
                omitted if use_W or use_Q are set.
            *k_Q, k_w, mannings_n -> floats. These are the prefactors on the 
                basin hydrology and channel width-discharge relations, and n
                from the Manning's equation, respectively. These are 
                needed to allow calculation of shear stresses and hence carrying
                capacities from the local slope and drainage area alone.
                The equation for depth used to derive shear stress and hence
                carrying capacity contains a prefactor:
                    mannings_n*(k_Q**(1-b)/K_w)**0.6 
                (so shear = fluid_density*g*depth_equation_prefactor*A**(0.6*c*(1-b)*S**0.7 !)
                Don't know what to set these values to? k_w=0.002, k_Q=1.e-9,
                mannings_n=0.03 give vaguely plausible numbers (e.g., for a
                drainage area ~400km2, like Boulder Creek at Boulder,
                => depth~2.5m, width~35m, shear stress ~O(1000Pa)).
            *Dchar -> float.  The characteristic grain diameter in meters
                (==D50 in most cases) used to calculate Shields numbers
                in the channel. If you want to define Dchar values at each node,
                don't set, and use the Dchar_if_used argument in erode() 
                instead.
            
        OPTIONS:
            *rock_density -> in kg/m3 (defaults to 2700)
            *sediment_density -> in kg/m3 (defaults to 2700)
            *fluid_density -> in most cases water density, in kg/m3 (defaults to 1000)
            *g -> acceleration due to gravity, in m/s**2 (defaults to 9.81)
            
            *threshold_shields -> +ve float; the threshold taustar_crit. 
                Defaults to 0.047, or if 'slope_sensitive_threshold' is set True,
                becomes a weak function of local slope following Lamb et al 
                (2008):
                    threshold_shields=0.15*S**0.25
            *slope_sensitive_threshold -> bool, defaults to 'False'.
                If true, threshold_shields is set according to the Lamb
                equation. An exception will be raised if threshold_shields is 
                also set.
            *Parker_epsilon -> float, defaults to 0.4. This is Parker's (1978)
                epsilon, which is used in the relation
                    tau - tauc = tau * (epsilon/(epsilon+1))
                The 0.4 default is appropriate for coarse (gravelly) channels.
                The value approaches infinity as the river banks become more
                cohesive.
            *dt -> +ve float. If set, this is the fixed timestep for this
                component. Can be overridden easily as a parameter in erode(). 
                If not set (default), this parameter MUST be set in erode().
            *use_W -> Bool; if True, component will look for node-centered data
                describing channel width in grid.at_node['channel_width'], and 
                use it to implement incision ~ stream power per unit width. 
                Defaults to False.
            *use_Q -> Bool. Overrides the basin hydrology relation, using an
                local water discharge value assumed already calculated and
                stored in grid.at_node['discharge'].
            *C_MPM -> float. Defaults to 1. Allows tuning of the MPM prefactor,
                which is calculated as
                    Qc = 8.*C_MPM*(taustar - taustarcrit)**1.5
                In almost all cases, tuning depth_equation_prefactor' is 
                preferred to tuning this parameter.
            *return_capacity -> bool (default False). NOT YET IMPLEMENTED.
                If True, this component
                will save the calculated capacity in the field 
                'fluvial_sediment_transport_capacity'. (Requires some additional
                math, so is suppressed for speed by default).
            
        '''
        self.grid = grid
        self.link_S_with_trailing_blank = np.zeros(grid.number_of_links+1) #needs to be filled with values in execution
        self.count_active_links = np.zeros_like(self.link_S_with_trailing_blank, dtype=int)
        self.count_active_links[:-1] = 1
        inputs = ModelParameterDictionary(params_file)
        try:
            self.g = inputs.read_float('g')
        except MissingKeyError:
            self.g = 9.81
        try:
            self.rock_density = inputs.read_float('rock_density')
        except MissingKeyError:
            self.rock_density = 2700.
        try:
            self.sed_density = inputs.read_float('sediment_density')
        except MissingKeyError:
            self.sed_density = 2700.
        try:
            self.fluid_density = inputs.read_float('fluid_density')
        except MissingKeyError:
            self.fluid_density = 1000.
        self.rho_g = self.fluid_density * self.g
        
        try:
            self.Qc = inputs.read_string('Qc')
        except MissingKeyError:
            raise MissingKeyError("Qc must be 'MPM' or a grid field name!")
        else:
            if self.Qc=='MPM':
                self.calc_cap_flag = True
            else:
                self.calc_cap_flag = False
                
        try:
            self.lamb_flag = inputs.read_bool('slope_sensitive_threshold')
        except:
            self.lamb_flag = False
        try:
            self.shields_crit = inputs.read_float('threshold_shields')
            self.set_threshold = True #flag for sed_flux_dep_incision to see if the threshold was manually set.
            print "Found a threshold to use: ", self.shields_crit
            assert self.lamb_flag == False
        except MissingKeyError:
            if not self.lamb_flag:
                self.shields_crit = 0.047
            self.set_threshold = False
        try:
            self.tstep = inputs.read_float('dt')
        except MissingKeyError:
            pass
        try:
            self.use_W = inputs.read_bool('use_W')
        except MissingKeyError:
            self.use_W = False
        try:
            self.use_Q = inputs.read_bool('use_Q')
        except MissingKeyError:
            self.use_Q = False
        try:
            self.return_capacity = inputs.read_bool('return_capacity')
        except MissingKeyError:
            self.return_capacity = False
            
        try:
            self._b = inputs.read_float('b_sp')
        except MissingKeyError:
            if self.use_W:
                self._b = 0.
            else:
                if self.calc_cap_flag:
                    raise NameError('b was not set')
        try:
            self._c = inputs.read_float('c_sp')
        except MissingKeyError:
            if self.use_Q:
                self._c = 1.
            else:
                if self.calc_cap_flag:
                    raise NameError('c was not set')
        try:
            self.Dchar_in = inputs.read_float('Dchar')
        except MissingKeyError:
            pass
            
        #assume Manning's equation to set the power on A for shear stress:
        self.shear_area_power = 0.6*self._c*(1.-self._b)
        
        self.k_Q = inputs.read_float('k_Q')
        self.k_w = inputs.read_float('k_w')
        mannings_n = inputs.read_float('mannings_n')
        if mannings_n<0. or mannings_n>0.2:
            print "***STOP. LOOK. THINK. You appear to have set Manning's n outside it's typical range. Did you mean it? Proceeding...***"
            sleep(2)
        self.depth_prefactor = self.rho_g*mannings_n*(self.k_Q**(1.-self._b)/self.k_w)**0.6
        ##Note the depth_prefactor we store already holds rho*g   
        try:
            epsilon = inputs.read_float('Parker_epsilon')
        except MissingKeyError:
            epsilon = 0.4

        try:
            self.C_MPM = inputs.read_float('C_MPM')
        except MissingKeyError:
            self.C_MPM = 1.
        try:
            self.shields_prefactor = 1./((self.sed_density-self.fluid_density)*self.g*self.Dchar_in)
            self.MPM_prefactor = 8.*self.C_MPM*np.sqrt(self.relative_weight*self.Dchar_in*self.Dchar_in*self.Dchar_in)
            self.MPM_prefactor_alt = 4.*self.g**(-2./3.)/self.excess_SG/self.fluid_density/self.sed_density
        except AttributeError:
            #have to set these manually as needed
            self.shields_prefactor_noD = 1./((self.sed_density-self.fluid_density)*self.g)
        self.diffusivity_prefactor = 8.*np.sqrt(8.*self.g)/(self.sed_density/self.fluid_density-1.)*(epsilon/(epsilon+1.))**1.5*mannings_n**(5./6.)*self.k_w**-0.9*self.k_Q**(0.9*(1.-self._b)) #...this is multiplied by A**c(1-0.1*(1-b))
        #we consciously skip out a factor of S**0.05-->1. in the diffusion prefactor, to avoid delinearizing the diffusion. Only a possible problem at tiny S (20% error @S==0.01; 37% error @S==10**-4)
        #we could include this as a static adjustment in the actual looping code (i.e., just multiply by S**0.05, and don't work with it as part of the problem)
        #in reality, Manning's n changes downstream too, so... whatever
        self.diffusivity_power_on_A = 0.9*self._c*(1.-self._b) #i.e., q/D**(1/6)

        self.cell_areas = np.empty(grid.number_of_nodes)
        self.cell_areas.fill(np.mean(grid.cell_areas))
        self.cell_areas[grid.cell_node] = grid.cell_areas
        self.dx2 = grid.node_spacing_horizontal**2
        self.dy2 = grid.node_spacing_vertical**2
        self.bad_neighbor_mask = np.equal(grid.get_neighbor_list(bad_index=-1),-1)
Beispiel #6
0
    def __init__(self,
                 grid,
                 K_sp=None,
                 threshold_sp=0.,
                 sp_type='set_mn',
                 m_sp=0.5,
                 n_sp=1.,
                 a_sp=None,
                 b_sp=None,
                 c_sp=None,
                 use_W=None,
                 use_Q=None,
                 **kwds):
        """Initialize the StreamPowerEroder"""
        if type(use_Q) is str and use_Q == 'water__discharge':
            use_Q = 'surface_water__discharge'
        self._grid = grid

        self.use_K = False  # grandfathered in; only if K_sp == 'array'
        if type(K_sp) is np.ndarray:
            self._K_unit_time = K_sp
        else:
            try:
                self._K_unit_time = self.grid.zeros('node', dtype=float)
                self._K_unit_time.fill(K_sp)
            except ValueError:  # could not cast => was a str
                if K_sp == 'array':
                    self.use_K = True
                else:
                    self._K_unit_time = grid.at_node[K_sp]

        assert np.all(threshold_sp >= 0.)
        # for now, enforce threshold as a float
        assert type(threshold_sp) in (float, int)
        try:
            self.sp_crit = float(threshold_sp)
        except TypeError:
            try:
                self.sp_crit = self.grid.at_node[threshold_sp]
            except TypeError:  # was an array
                self.sp_crit = threshold_sp
                assert self.sp_crit.size == self.grid.number_of_nodes
        if np.any(threshold_sp != 0.):
            self.set_threshold = True
            # ^flag for sed_flux_dep_incision to see if the threshold was
            # manually set.
        else:
            self.set_threshold = False
        try:
            self.tstep = kwds['dt']
        except KeyError:
            self.tstep = None
            # retained for back compatibility; undocumented functionality
        if type(use_W) is bool:  # again for back-compatibility
            self.use_W = use_W
            self._W = None
        elif use_W is None:
            self.use_W = False
            self._W = None
        else:
            self.use_W = True
            try:
                self._W = self.grid.at_node[use_W]
            except (FieldError, TypeError):
                assert use_W.size == self._grid.number_of_nodes
                self._W = use_W
        if type(use_Q) is bool:
            self.use_Q = use_Q
            self._Q = None
        elif use_Q is None:
            self.use_Q = False
            self._Q = None
        else:
            self.use_Q = True
            try:
                self._Q = self.grid.at_node[use_Q]
            except (FieldError, TypeError):
                assert use_Q.size == self._grid.number_of_nodes
                self._Q = use_Q
        self._type = sp_type
        if sp_type is 'set_mn':
            assert (float(m_sp) >= 0.) and (float(n_sp) >= 0.), \
                "m and n must be positive"
            self._m = float(m_sp)
            self._n = float(n_sp)
            assert ((a_sp is None) and (b_sp is None) and (c_sp is None)), (
                "If sp_type is 'set_mn', do not pass values for a, b, or c!")
        else:
            assert sp_type in ('Total', 'Unit', 'Shear_stress'), (
                "sp_type not recognised. It must be 'set_mn', 'Total', " +
                "'Unit', or 'Shear_stress'.")
            assert (m_sp == 0.5 and n_sp == 1.), \
                "Do not set m and n if sp_type is not 'set_mn'!"
            assert float(a_sp) >= 0., "a must be positive"
            self._a = float(a_sp)
            if b_sp is not None:
                assert float(b_sp) >= 0., "b must be positive"
                self._b = float(b_sp)
            else:
                assert self.use_W, "b was not set"
                self._b = 0.
            if c_sp is not None:
                assert float(c_sp) >= 0., "c must be positive"
                self._c = float(c_sp)
            else:
                assert self.use_Q, "c was not set"
                self._c = 1.
            if self._type == 'Total':
                self._n = self._a
                self._m = self._a * self._c  # ==_a if use_Q
            elif self._type == 'Unit':
                self._n = self._a
                self._m = self._a * self._c * (1. - self._b)
                # ^ ==_a iff use_Q&use_W etc
            elif self._type == 'Shear_stress':
                self._m = 2. * self._a * self._c * (1. - self._b) / 3.
                self._n = 2. * self._a / 3.
            else:
                raise MissingKeyError('Not enough information was provided ' +
                                      'on the exponents to use!')
        # m and n will always be set, but care needs to be taken to include Q
        # and W directly if appropriate

        self.stream_power_erosion = grid.zeros(centering='node')
        self.alpha = self.grid.zeros('node')
Beispiel #7
0
    def __init__(self,
                 grid,
                 K_sp=None,
                 threshold_sp=0.,
                 sp_type="set_mn",
                 m_sp=0.5,
                 n_sp=1.,
                 a_sp=None,
                 b_sp=None,
                 c_sp=None,
                 use_W=None,
                 use_Q=None,
                 **kwds):
        """Initialize the StreamPowerEroder

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

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

        m_sp : float, optional
            m in the stream power equation (power on drainage area). Overridden if
            a_sp, b_sp, and c_sp are supplied.
        n_sp : float, optional, ~ 0.5<n_sp<4.
            n in the stream power equation (power on slope). Overridden if
            a_sp, b_sp, and c_sp are supplied.
        a_sp : float, optional
            The power on the SP/shear term to get the erosion rate; the "erosional
            process" term. Only used if sp_type is not 'set_mn'.
        b_sp : float, optional
            The power on discharge to get width; the "hydraulic geometry" term.
            Only used if sp_type in ('Unit', 'Shear_stress').
        c_sp : float, optional
            The power on area to get discharge; the "basin hydology" term. Only
            used if sp_type is not 'set_mn'.
        use_W : None, array, or field name, optional
            If not None, component will look for node-centered data describing
            channel width in grid.at_node[use_W] or if an array, will take the
            array as the channel widths. It will use the widths to implement
            incision ~ stream power per unit width. If sp_type is 'set_mn',
            follows the equation given above. If sp_type in ('Unit',
            'Shear_stress'), the width value will be implemented directly. W has no
            effect if sp_type is 'Total'.
        use_Q : None, array, or field name, optional
            If not None, the equation becomes E=K*Q**m*S**n. Effectively sets c=1
            in Wh&T's 1999 derivation, if you are setting m and n through a, b,
            and c.
        """
        if "flow__receiver_node" in grid.at_node:
            if grid.at_node["flow__receiver_node"].size != grid.size("node"):
                msg = (
                    "A route-to-multiple flow director has been "
                    "run on this grid. The landlab development team has not "
                    "verified that StreamPowerEroder is compatible with "
                    "route-to-multiple methods. Please open a GitHub Issue "
                    "to start this process.")
                raise NotImplementedError(msg)

        if type(use_Q) is str and use_Q == "water__discharge":
            use_Q = "surface_water__discharge"
        self._grid = grid

        self.use_K = False  # grandfathered in; only if K_sp == 'array'
        if type(K_sp) is np.ndarray:
            self._K_unit_time = K_sp
        else:
            try:
                self._K_unit_time = self.grid.zeros("node", dtype=float)
                self._K_unit_time.fill(K_sp)
            except ValueError:  # could not cast => was a str
                if K_sp == "array":
                    self.use_K = True
                else:
                    self._K_unit_time = grid.at_node[K_sp]

        assert np.all(threshold_sp >= 0.)
        # for now, enforce threshold as a float
        assert type(threshold_sp) in (float, int)
        try:
            self.sp_crit = float(threshold_sp)
        except TypeError:
            try:
                self.sp_crit = self.grid.at_node[threshold_sp]
            except TypeError:  # was an array
                self.sp_crit = threshold_sp
                assert self.sp_crit.size == self.grid.number_of_nodes
        if np.any(threshold_sp != 0.):
            self.set_threshold = True
            # ^flag for sed_flux_dep_incision to see if the threshold was
            # manually set.
        else:
            self.set_threshold = False
        try:
            self.tstep = kwds["dt"]
        except KeyError:
            self.tstep = None
            # retained for back compatibility; undocumented functionality
        if type(use_W) is bool:  # again for back-compatibility
            self.use_W = use_W
            self._W = None
        elif use_W is None:
            self.use_W = False
            self._W = None
        else:
            self.use_W = True
            try:
                self._W = self.grid.at_node[use_W]
            except (FieldError, TypeError):
                assert use_W.size == self._grid.number_of_nodes
                self._W = use_W
        if type(use_Q) is bool:
            self.use_Q = use_Q
            self._Q = None
        elif use_Q is None:
            self.use_Q = False
            self._Q = None
        else:
            self.use_Q = True
            try:
                self._Q = self.grid.at_node[use_Q]
            except (FieldError, TypeError):
                assert use_Q.size == self._grid.number_of_nodes
                self._Q = use_Q
        self._type = sp_type
        if sp_type is "set_mn":
            assert (float(m_sp) >= 0.) and (float(n_sp) >=
                                            0.), "m and n must be positive"
            self._m = float(m_sp)
            self._n = float(n_sp)
            assert (
                (a_sp is None) and (b_sp is None) and (c_sp is None)
            ), "If sp_type is 'set_mn', do not pass values for a, b, or c!"
        else:
            assert sp_type in ("Total", "Unit", "Shear_stress"), (
                "sp_type not recognised. It must be 'set_mn', 'Total', " +
                "'Unit', or 'Shear_stress'.")
            assert (m_sp == 0.5 and n_sp
                    == 1.), "Do not set m and n if sp_type is not 'set_mn'!"
            assert float(a_sp) >= 0., "a must be positive"
            self._a = float(a_sp)
            if b_sp is not None:
                assert float(b_sp) >= 0., "b must be positive"
                self._b = float(b_sp)
            else:
                assert self.use_W, "b was not set"
                self._b = 0.
            if c_sp is not None:
                assert float(c_sp) >= 0., "c must be positive"
                self._c = float(c_sp)
            else:
                assert self.use_Q, "c was not set"
                self._c = 1.
            if self._type == "Total":
                self._n = self._a
                self._m = self._a * self._c  # ==_a if use_Q
            elif self._type == "Unit":
                self._n = self._a
                self._m = self._a * self._c * (1. - self._b)
                # ^ ==_a iff use_Q&use_W etc
            elif self._type == "Shear_stress":
                self._m = 2. * self._a * self._c * (1. - self._b) / 3.
                self._n = 2. * self._a / 3.
            else:
                raise MissingKeyError("Not enough information was provided " +
                                      "on the exponents to use!")
        # m and n will always be set, but care needs to be taken to include Q
        # and W directly if appropriate

        self.stream_power_erosion = grid.zeros(centering="node")
        self.alpha = self.grid.zeros("node")
Beispiel #8
0
    def initialize(self, grid, params_file):
        r"""
        NOW DEPRECATED, USE __INIT__ DIRECTLY.
        params_file is the name of the text file containing the parameters
        needed for this stream power component.

        Module erodes where channels are, implemented as

        E = K * A**m * S**n - sp_crit,

        and if E<0, E=0.

        If 'use_W' is declared and True, the module instead implements:

        E = K * A**m * S**n / W - sp_crit

        ***Parameters for input file***
        OBLIGATORY:
            K_sp -> positive float, the prefactor. This is defined per unit
                time, not per tstep. Type the string 'array' to cause the
                component's erode method to look for an array of values of K
                (see documentation for 'erode').
        ALTERNATIVES:
        *either*
            m_sp -> positive float, the power on A
        and
            n_sp -> positive float, the power on S
        *or*
            sp_type -> String. Must be one of 'Total', 'Unit', or
                'Shear_stress'.
        and (following Whipple & Tucker 1999)
            a_sp -> +ve float. The power on the SP/shear term to get the
                erosion rate.
            b_sp -> +ve float. The power on discharge to get width, "hydraulic
                geometry". Unnecessary if sp_type='Total'.
            c_sp -> +ve float. The power on area to get discharge, "basin
                hydology".
            ... If 'Total', m=a*c, n=a.
            ... If 'Unit', m=a*c*(1-b), n=a.
            ... If 'Shear_stress', m=2*a*c*(1-b)/3, n = 2*a/3.
        OPTIONS:
            threshold_sp -> +ve float; the threshold sp_crit. Defaults to 0.
                This threshold is assumed to be in "stream power" units, i.e.,
                if 'Shear_stress', the value should be tau**a.
            dt -> +ve float. If set, this is the fixed timestep for this
                component. Can be overridden easily as a parameter in erode().
                If not set (default), this parameter MUST be set in erode().
            use_W -> Bool; if True, component will look for node-centered data
                describing channel width in grid.at_node['channel_width'], and
                use it to implement incision ~ stream power per unit width.
                Defaults to False. If you set sp_m and sp_n, follows the
                equation given above. If you set sp_type, it will be ignored if
                'Total', but used directly if you want 'Unit' or
                'Shear_stress'.
            use_Q -> Bool. If true, the equation becomes E=K*Q**m*S**n.
                Effectively sets c=1 in Wh&T's 1999 derivation, if you are
                setting m and n through a, b, and c.
        """

        self._grid = grid
        self.fraction_gradient_change = 1.
        self.link_S_with_trailing_blank = np.zeros(grid.number_of_links + 1)
        # ^needs to be filled with values in execution
        self.count_active_links = np.zeros_like(
            self.link_S_with_trailing_blank, dtype=int)
        self.count_active_links[:-1] = 1
        inputs = ModelParameterDictionary(params_file)
        try:
            self._K_unit_time = np.full((grid.status_at_node != 4).sum(),
                                        inputs.read_float('K_sp'))
        except ParameterValueError:  # it was a string
            self.use_K = True
        else:
            self.use_K = False
        try:
            self.sp_crit = inputs.read_float('threshold_sp')
            self.set_threshold = True
            # ^flag for sed_flux_dep_incision to see if the threshold was
            # manually set.
            # print("Found a threshold to use: ", self.sp_crit)
        except MissingKeyError:
            self.sp_crit = 0.
            self.set_threshold = False
        try:
            self.tstep = inputs.read_float('dt')
        except MissingKeyError:
            pass
        try:
            self.use_W = inputs.read_bool('use_W')
        except MissingKeyError:
            self.use_W = False
        try:
            self.use_Q = inputs.read_bool('use_Q')
        except MissingKeyError:
            self.use_Q = False
        try:
            self._m = inputs.read_float('m_sp')
        except MissingKeyError:
            self._type = inputs.read_string('sp_type')
            self._a = inputs.read_float('a_sp')
            try:
                self._b = inputs.read_float('b_sp')
            except MissingKeyError:
                if self.use_W:
                    self._b = 0.
                else:
                    raise NameError('b was not set')
            try:
                self._c = inputs.read_float('c_sp')
            except MissingKeyError:
                if self.use_Q:
                    self._c = 1.
                else:
                    raise NameError('c was not set')
            if self._type == 'Total':
                self._n = self._a
                self._m = self._a * self._c  # ==_a if use_Q
            elif self._type == 'Unit':
                self._n = self._a
                self._m = self._a * self._c * (1. - self._b)
                # ^ ==_a iff use_Q&use_W etc
            elif self._type == 'Shear_stress':
                self._m = 2. * self._a * self._c * (1. - self._b) / 3.
                self._n = 2. * self._a / 3.
            else:
                raise MissingKeyError('Not enough information was provided ' +
                                      'on the exponents to use!')
        else:
            self._n = inputs.read_float('n_sp')
        # m and n will always be set, but care needs to be taken to include Q
        # and W directly if appropriate

        self.stream_power_erosion = grid.zeros(centering='node')
Beispiel #9
0
    def __init__(self,
                 grid,
                 K_sp=None,
                 threshold_sp=0.,
                 sp_type='set_mn',
                 m_sp=0.5,
                 n_sp=1.,
                 a_sp=None,
                 b_sp=None,
                 c_sp=None,
                 use_W=None,
                 use_Q=None,
                 **kwds):
        self._grid = grid
        self.fraction_gradient_change = 1.
        self.link_S_with_trailing_blank = np.zeros(grid.number_of_links + 1)
        # ^needs to be filled with values in execution
        self.count_active_links = np.zeros_like(
            self.link_S_with_trailing_blank, dtype=int)
        self.count_active_links[:-1] = 1

        active_nodes = grid.status_at_node != 4
        self._K_unit_time = np.empty(active_nodes.sum(), dtype=float)
        self.use_K = False  # grandfathered in; only if K_sp == 'array'
        if type(K_sp) is np.ndarray:
            self._K_unit_time[:] = K_sp[active_nodes]
        else:
            try:
                self._K_unit_time.fill(K_sp)
            except ValueError:  # could not cast => was a str
                if K_sp == 'array':
                    self.use_K = True
                else:
                    self._K_unit_time = grid.at_node[K_sp]

        assert float(threshold_sp) >= 0.
        self.sp_crit = float(threshold_sp)
        if threshold_sp != 0.:
            self.set_threshold = True
            # ^flag for sed_flux_dep_incision to see if the threshold was
            # manually set.
        else:
            self.set_threshold = False
        try:
            self.tstep = kwds['dt']
        except KeyError:
            self.tstep = None
            # retained for back compatibility; undocumented functionality
        if type(use_W) is bool:  # again for back-compatibility
            self.use_W = use_W
            self._W = None
        elif use_W is None:
            self.use_W = False
            self._W = None
        else:
            self.use_W = True
            try:
                self._W = self.grid.at_node[use_W]
            except (FieldError, TypeError):
                assert use_W.size == self._grid.number_of_nodes
                self._W = use_W
        if type(use_Q) is bool:
            self.use_Q = use_Q
            self._Q = None
        elif use_Q is None:
            self.use_Q = False
            self._Q = None
        else:
            self.use_Q = True
            try:
                self._Q = self.grid.at_node[use_Q]
            except (FieldError, TypeError):
                assert use_Q.size == self._grid.number_of_nodes
                self._Q = use_Q
        self._type = sp_type
        if sp_type is 'set_mn':
            assert (float(m_sp) >= 0.) and (float(n_sp) >= 0.), \
                "m and n must be positive"
            self._m = float(m_sp)
            self._n = float(n_sp)
            assert ((a_sp is None) and (b_sp is None) and (c_sp is None)), (
                "If sp_type is 'set_mn', do not pass values for a, b, or c!")
        else:
            assert sp_type in ('Total', 'Unit', 'Shear_stress'), (
                "sp_type not recognised. It must be 'set_mn', 'Total', " +
                "'Unit', or 'Shear_stress'.")
            assert (m_sp == 0.5 and n_sp == 1.), \
                "Do not set m and n if sp_type is not 'set_mn'!"
            assert float(a_sp) >= 0., "a must be positive"
            self._a = float(a_sp)
            if b_sp is not None:
                assert float(b_sp) >= 0., "b must be positive"
                self._b = float(b_sp)
            else:
                assert self.use_W, "b was not set"
                self._b = 0.
            if c_sp is not None:
                assert float(c_sp) >= 0., "c must be positive"
                self._c = float(c_sp)
            else:
                assert self.use_Q, "c was not set"
                self._c = 1.
            if self._type == 'Total':
                self._n = self._a
                self._m = self._a * self._c  # ==_a if use_Q
            elif self._type == 'Unit':
                self._n = self._a
                self._m = self._a * self._c * (1. - self._b)
                # ^ ==_a iff use_Q&use_W etc
            elif self._type == 'Shear_stress':
                self._m = 2. * self._a * self._c * (1. - self._b) / 3.
                self._n = 2. * self._a / 3.
            else:
                raise MissingKeyError('Not enough information was provided ' +
                                      'on the exponents to use!')
        # m and n will always be set, but care needs to be taken to include Q
        # and W directly if appropriate

        self.stream_power_erosion = grid.zeros(centering='node')
        grid.add_zeros('stream_power_erosion', at='node')
    def initialize(self, grid, params_file):
        '''
        params_file is the name of the text file containing the parameters
        needed for this stream power component.

        ***Parameters for input file***
        OBLIGATORY:
            * Qc -> String. Controls how to set the carrying capacity.
                Either 'MPM', 'power_law', or a string giving the name of the
                model field where capacity values are stored on nodes.
                At the moment, only 'MPM' and power_law' are permitted as a way
                to set the capacity automatically, but expansion would be
                trivial.
                If 'from_array', the module will attempt to set the capacity
                Note capacities must be specified as volume flux.
            *

            ...Then, assuming you set Qc=='MPM':
            * b_sp, c_sp -> Floats. These are the powers on discharge and
                drainage area in the equations used to control channel width
                and basin hydrology, respectively:
                        W = k_w * Q**b_sp
                        Q = k_Q * A**c_sp
                These parameters are used to constrain flow depth, and may be
                omitted if use_W or use_Q are set.
            *k_Q, k_w, mannings_n -> floats. These are the prefactors on the
                basin hydrology and channel width-discharge relations, and n
                from the Manning's equation, respectively. These are
                needed to allow calculation of shear stresses and hence
                carrying capacities from the local slope and drainage area
                alone.
                Don't know what to set these values to? k_w=2.5, k_Q=2.5e-7,
                mannings_n=0.05 give vaguely plausible numbers with b=0.5,
                c = 1.(e.g., for a drainage area ~350km2, like Boulder Creek
                at Boulder, => depth~1.3m, width~23m, shear stress ~O(200Pa)
                for an "annual-ish" flood). [If you want to continue playing
                with calibration, the ?50yr return time 2013 floods produced
                depths ~2.3m with Q~200m3/s]
            *Dchar -> float.  The characteristic grain diameter in meters
                (==D50 in most cases) used to calculate Shields numbers
                in the channel. If you want to define Dchar values at each
                node, don't set, and use the Dchar_if_used argument in erode()
                instead.

            ...or if you set power_law, Qc = K_t*A**m_t*S**n_t,
            * m_t, n_t -> Floats. The powers on A and S repectively in this
                equation.
            * K_t -> float. The prefactor (note time units are years).
            Note that Qc is total capacity, not per unit width.

        OPTIONS:
            *rock_density -> in kg/m3 (defaults to 2700)
            *sediment_density -> in kg/m3 (defaults to 2700)
            *fluid_density -> in most cases water density, in kg/m3 (defaults
                to 1000)
            *g -> acceleration due to gravity, in m/s**2 (defaults to 9.81)

            *threshold_shields -> +ve float; the threshold taustar_crit.
                Defaults to 0.047, or if 'slope_sensitive_threshold' is set
                True, becomes a weak function of local slope following Lamb et
                al (2008):
                    threshold_shields=0.15*S**0.25
            *slope_sensitive_threshold -> bool, defaults to 'False'.
                If true, threshold_shields is set according to the Lamb
                equation,
                An exception will be raised if threshold_shields is
                also set.
            *dt -> +ve float. If set, this is the fixed timestep for this
                component. Can be overridden easily as a parameter in erode().
                If not set (default), this parameter MUST be set in erode().
            *use_W -> Bool; if True, component will look for node-centered data
                describing channel width in grid.at_node['channel_width'], and
                use it to implement incision ~ stream power per unit width.
                Defaults to False. NOT YET IMPLEMENTED
            *use_Q -> Bool. Overrides the basin hydrology relation, using an
                local water discharge value assumed already calculated and
                stored in grid.at_node['discharge']. NOT YET IMPLEMENTED
            *C_MPM -> float. Defaults to 1. Allows tuning of the MPM prefactor,
                which is calculated as
                    Qc = 8.*C_MPM*(taustar - taustarcrit)**1.5
                In almost all cases, tuning depth_equation_prefactor' is
                preferred to tuning this parameter.
            *return_stream_properties -> bool (default False).
                If True, this component will save the calculations for
                'channel_width', 'channel_depth', and 'channel_discharge' in
                those grid fields. (Requires some
                additional math, so is suppressed for speed by default).

        '''
        # this is the fraction we allow any given slope in the grid to evolve
        # by in one go (suppresses numerical instabilities)
        self.capacity_options = ['MPM', 'power_law']
        self.fraction_gradient_change = 0.25
        self._grid = grid
        # needs to be filled with values in execution
        self.link_S_with_trailing_blank = np.zeros(grid.number_of_links + 1)
        self.count_active_links = np.zeros_like(
            self.link_S_with_trailing_blank, dtype=int)
        self.count_active_links[:-1] = 1
        inputs = ModelParameterDictionary(params_file)
        try:
            self.g = inputs.read_float('g')
        except MissingKeyError:
            self.g = 9.81
        try:
            self.rock_density = inputs.read_float('rock_density')
        except MissingKeyError:
            self.rock_density = 2700.
        try:
            self.sed_density = inputs.read_float('sediment_density')
        except MissingKeyError:
            self.sed_density = 2700.
        try:
            self.fluid_density = inputs.read_float('fluid_density')
        except MissingKeyError:
            self.fluid_density = 1000.
        self.rho_g = self.fluid_density * self.g

        try:
            self.Qc = inputs.read_string('Qc')
        except MissingKeyError:
            raise MissingKeyError("Qc must be 'MPM' or a grid field name!")
        else:
            if self.Qc in self.capacity_options:
                self.calc_cap_flag = True
            else:
                self.calc_cap_flag = False
        try:
            self.return_ch_props = inputs.read_bool('return_stream_properties')
        except MissingKeyError:
            self.return_ch_props = False
        try:
            self.tstep = inputs.read_float('dt')
        except MissingKeyError:
            pass
        try:
            self.return_capacity = inputs.read_bool('return_capacity')
        except MissingKeyError:
            self.return_capacity = False

        if self.Qc == 'MPM':
            try:
                self.lamb_flag = inputs.read_bool('slope_sensitive_threshold')
            except:
                self.lamb_flag = False
            try:
                self.shields_crit = inputs.read_float('threshold_shields')
                # flag for sed_flux_dep_incision to see if the threshold was
                # manually set.
                self.set_threshold = True
                # print("Found a threshold to use: ", self.shields_crit)
                assert self.lamb_flag is False
            except MissingKeyError:
                if not self.lamb_flag:
                    self.shields_crit = 0.047
                self.set_threshold = False
            try:
                self.use_W = inputs.read_bool('use_W')
            except MissingKeyError:
                self.use_W = False
            try:
                self.use_Q = inputs.read_bool('use_Q')
            except MissingKeyError:
                self.use_Q = False

            try:
                self._b = inputs.read_float('b_sp')
            except MissingKeyError:
                if self.use_W:
                    self._b = 0.
                else:
                    if self.calc_cap_flag:
                        raise NameError('b was not set')
            try:
                self._c = inputs.read_float('c_sp')
            except MissingKeyError:
                if self.use_Q:
                    self._c = 1.
                else:
                    if self.calc_cap_flag:
                        raise NameError('c was not set')
            try:
                self.Dchar_in = inputs.read_float('Dchar')
            except MissingKeyError:
                pass

            # assume Manning's equation to set the power on A for shear stress:
            self.shear_area_power = 0.6 * self._c * (1. - self._b)

            self.k_Q = inputs.read_float('k_Q')
            self.k_w = inputs.read_float('k_w')
            mannings_n = inputs.read_float('mannings_n')
            self.mannings_n = mannings_n
            if mannings_n < 0. or mannings_n > 0.2:
                print(
                    "***STOP. LOOK. THINK. You appear to have set Manning's n "
                    +
                    "outside its typical range. Did you mean it? Proceeding..."
                    + "***")
                sleep(2)

            try:
                self.C_MPM = inputs.read_float('C_MPM')
            except MissingKeyError:
                self.C_MPM = 1.
            self.diffusivity_power_on_A = 0.9 * self._c * \
                (1. - self._b)  # i.e., q/D**(1/6)

            # new for v3:
            # set thresh in shear stress if poss at this stage:
            try:  # fails if no Dchar provided, or shields crit is being set
                # dynamically from slope
                self.thresh = self.shields_crit * (
                    self.sed_density -
                    self.fluid_density) * self.g * self.Dchar_in
            except AttributeError:
                try:
                    self.shields_prefactor_to_shear = (
                        (self.sed_density - self.fluid_density) * self.g *
                        self.Dchar_in)
                except AttributeError:  # no Dchar
                    self.shields_prefactor_to_shear_noDchar = (
                        self.sed_density - self.fluid_density) * self.g
            twothirds = 2. / 3.
            self.Qs_prefactor = (
                4. * self.C_MPM**twothirds * self.fluid_density**twothirds /
                (self.sed_density - self.fluid_density)**twothirds *
                self.g**(twothirds / 2.) * mannings_n**0.6 *
                self.k_w**(1. / 15.) * self.k_Q**(0.6 + self._b / 15.) /
                self.sed_density**twothirds)
            self.Qs_thresh_prefactor = 4. * (
                self.C_MPM * self.k_w * self.k_Q**self._b /
                self.fluid_density**0.5 /
                (self.sed_density - self.fluid_density) / self.g /
                self.sed_density)**twothirds
            # both these are divided by sed density to give a vol flux
            self.Qs_power_onA = self._c * (0.6 + self._b / 15.)
            self.Qs_power_onAthresh = twothirds * self._b * self._c

        elif self.Qc == 'power_law':
            self._Kt = inputs.read_float('K_t') / 31557600.  # in sec
            self._mt = inputs.read_float('m_t')
            self._nt = inputs.read_float('n_t')
            self.return_ch_props = False

        if RasterModelGrid in inspect.getmro(grid.__class__):
            self.cell_areas = grid.dx * grid.dy
        else:
            self.cell_areas = np.empty(grid.number_of_nodes)
            self.cell_areas.fill(np.mean(grid.area_of_cell))
            self.cell_areas[grid.node_at_cell] = grid.area_of_cell
        self.bad_neighbor_mask = np.equal(
            grid.get_active_neighbors_at_node(bad_index=-1), -1)

        self.routing_code = """
            double sed_flux_into_this_node;
            double sed_flux_out_of_this_node;
            double flux_excess;
            for (int i=len_s_in; i>0; i--) {
                sed_flux_into_this_node = sed_into_node[i];
                sed_flux_out_of_this_node = transport_capacities[i];
                flux_excess = sed_flux_into_this_node - sed_flux_out_of_this_node;
                dz[i] = flux_excess/cell_areas*dt_this_step;
                sed_into_node[flow_receiver[i]] += sed_flux_out_of_this_node;
            }
            """

        # set up the necessary fields:
        self.initialize_output_fields()
        if self.return_ch_props:
            self.initialize_optional_output_fields()
Beispiel #11
0
    def _initialize(self, input_stream=None):
        """Initialize the component from an input file.

        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.

        Parameters
        ----------
        input_stream : str, file_like, or ModelParameterDictionary, optional
            ModelParameterDictionary that holds the input parameters.
        """
        # 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:
                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[topo_field_name]
            except AttributeError:
                print('Your grid does not seem to have a node field called',
                      topo_field_name)

        # Create output variables.
        #
        # Note that we initialize depression
        # outlet ID to LOCAL_BAD_INDEX_VALUE (which is a major clue!)
        self.depression_depth = self._grid.add_zeros('node',
                                                     'depression__depth',
                                                     noclobber=False)
        self.depression_outlet_map = self._grid.add_zeros(
            'node', 'depression__outlet_node', dtype=int, noclobber=False)
        self.depression_outlet_map += LOCAL_BAD_INDEX_VALUE

        # Later on, we'll need a number that's guaranteed to be larger than the
        # highest elevation in the grid.
        self._BIG_ELEV = np.amax(self._elev) + 1

        # We'll also need a handy copy of the node neighbor lists
        # TODO: presently, this grid method seems to only exist for Raster
        # grids. We need it for *all* grids!
        self._node_nbrs = self._grid.active_neighbors_at_node()
        dx = self._grid.dx
        dy = self._grid.dy
        if self._D8:
            diag_nbrs = self._grid._get_diagonal_list()
            self._node_nbrs = np.concatenate((self._node_nbrs, diag_nbrs), 1)
            self._link_lengths = np.empty(8, dtype=float)
            self._link_lengths[0] = dx
            self._link_lengths[2] = dx
            self._link_lengths[1] = dy
            self._link_lengths[3] = dy
            self._link_lengths[4:].fill(np.sqrt(dx * dx + dy * dy))
        elif ((type(self._grid) is landlab.grid.raster.RasterModelGrid)
              and (self._routing is 'D4')):
            self._link_lengths = np.empty(4, dtype=float)
            self._link_lengths[0] = dx
            self._link_lengths[2] = dx
            self._link_lengths[1] = dy
            self._link_lengths[3] = dy
        else:
            self._link_lengths = self._grid._length_of_link_with_diagonals
        self._lake_outlets = []  # a list of each unique lake outlet
        # ^note this is nlakes-long

        self.is_pit = self._grid.add_ones('node',
                                          'is_pit',
                                          dtype=bool,
                                          noclobber=False)
        self.flood_status = self._grid.add_zeros('node',
                                                 'flood_status_code',
                                                 dtype=int,
                                                 noclobber=False)
        self._lake_map = np.empty(self._grid.number_of_nodes, dtype=int)
        self._lake_map.fill(LOCAL_BAD_INDEX_VALUE)
Beispiel #12
0
    def _initialize(self, input_stream=None):
        """Initialize the component from an input file.

        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.

        Parameters
        ----------
        input_stream : str, file_like, or ModelParameterDictionary, optional
            ModelParameterDictionary that holds the input parameters.
        """
        # 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:
                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[topo_field_name]
            except AttributeError:
                print('Your grid does not seem to have a node field called',
                      topo_field_name)

        # Create output variables.
        #
        # Note that we initialize depression
        # outlet ID to LOCAL_BAD_INDEX_VALUE (which is a major clue!)
        self.depression_depth = self._grid.add_zeros('node',
                                                     'depression__depth',
                                                     noclobber=False)
        self.depression_outlet_map = self._grid.add_zeros(
            'node', 'depression__outlet_node', dtype=int, noclobber=False)
        self.depression_outlet_map += LOCAL_BAD_INDEX_VALUE

        # Later on, we'll need a number that's guaranteed to be larger than the
        # highest elevation in the grid.
        self._BIG_ELEV = 1.0e99

        self.updated_boundary_conditions()

        self._lake_outlets = []  # a list of each unique lake outlet
        # ^note this is nlakes-long

        self.is_pit = self._grid.add_ones('node',
                                          'is_pit',
                                          dtype=bool,
                                          noclobber=False)
        self.flood_status = self._grid.add_zeros('node',
                                                 'flood_status_code',
                                                 dtype=int,
                                                 noclobber=False)
        self._lake_map = np.empty(self._grid.number_of_nodes, dtype=int)
        self._lake_map.fill(LOCAL_BAD_INDEX_VALUE)
Beispiel #13
0
 def initialize(self, grid, params_file):
     '''
     params_file is the name of the text file containing the parameters 
     needed for this stream power component.
     
     Module erodes where channels are, implemented as
     
     E = K * A**m * S**n - sp_crit,
     
     and if E<0, E=0.
     
     If 'use_W' is declared and True, the module instead implements:
         
     E = K * A**m * S**n / W - sp_crit
     
     ***Parameters for input file***
     OBLIGATORY:
         K_sp -> positive float, the prefactor. This is defined per unit 
             time, not per tstep.
     ALTERNATIVES:
     *either*
         m_sp -> positive float, the power on A
     and
         n_sp -> positive float, the power on S
     *or*
         sp_type -> String. Must be one of 'Total', 'Unit', or 'Shear_stress'.
     and (following Whipple & Tucker 1999)
         a_sp -> +ve float. The power on the SP/shear term to get the erosion
             rate.
         b_sp -> +ve float. The power on discharge to get width, "hydraulic
             geometry". Unnecessary if sp_type='Total'.
         c_sp -> +ve float. The power on area to get discharge, "basin
             hydology".
         ... If 'Total', m=a*c, n=a.
         ... If 'Unit', m=a*c*(1-b), n=a.
         ... If 'Shear_stress', m=2*a*c*(1-b)/3, n = 2*a/3.
     OPTIONS:
         threshold_sp -> +ve float; the threshold sp_crit. Defaults to 0.
         dt -> +ve float. If set, this is the fixed timestep for this
             component. Can be overridden easily as a parameter in erode(). 
             If not set (default), this parameter MUST be set in erode().
         use_W -> Bool; if True, component will look for node-centered data
             describing channel width in grid.at_node['channel_width'], and 
             use it to implement incision ~ stream power per unit width. 
             Defaults to False. If you set sp_m and sp_n, follows the
             equation given above. If you set sp_type, it will be ignored if
             'Total', but used directly if you want 'Unit' or 'Shear_stress'.
         use_Q -> Bool. If true, the equation becomes E=K*Q**m*S**n. 
             Effectively sets c=1 in Wh&T's 1999 derivation, if you are 
             setting m and n through a, b, and c.
         prevent_erosion -> Bool. If True, stream powers are calculated and
             stored in the grid, but incision is NOT IMPLEMENTED. i.e., values of
             elevation are NOT updated. Use if you wish to derive stream power
             values for some other purpose, but do not wish to actually model
             stream power dependent incision. Defaults to False.
         
     '''
     self.grid = grid
     self.link_S_with_trailing_blank = np.zeros(grid.number_of_links+1) #needs to be filled with values in execution
     self.count_active_links = np.zeros_like(self.link_S_with_trailing_blank, dtype=int)
     self.count_active_links[:-1] = 1
     inputs = ModelParameterDictionary(params_file)
     self._K_unit_time = inputs.read_float('K_sp')
     try:
         self.sp_crit = inputs.read_float('threshold_sp')
         print "Found a threshold to use: ", self.sp_crit
     except MissingKeyError:
         self.sp_crit = 0.
     try:
         self.tstep = inputs.read_float('dt')
     except MissingKeyError:
         pass
     try:
         self.use_W = inputs.read_bool('use_W')
     except MissingKeyError:
         self.use_W = False
     try:
         self.use_Q = inputs.read_bool('use_Q')
     except MissingKeyError:
         self.use_Q = False
     try:
         self.no_erode = inputs.read_bool('prevent_erosion')
     except MissingKeyError:
         self.no_erode = False
     try:
         self._m = inputs.read_float('m_sp')
     except MissingKeyError:
         self._type = inputs.read_string('sp_type')
         self._a = inputs.read_float('a_sp')
         try:
             self._b = inputs.read_float('b_sp')
         except MissingKeyError:
             if self.use_W:
                 self._b = 0.
             else:
                 raise NameError('b was not set')
         try:
             self._c = inputs.read_float('c_sp')
         except MissingKeyError:
             if self.use_Q:
                 self._c = 1.
             else:
                 raise NameError('c was not set')
         if self._type == 'Total':
             self._n = self._a
             self._m = self._a*self._c #==_a if use_Q
         elif self._type == 'Unit':
             self._n = self._a
             self._m = self._a*self._c*(1.-self._b) #==_a iff use_Q&use_W etc
         elif self._type == 'Shear_stress':
             self._m = 2.*self._a*self._c*(1.-self._b)/3. 
             self._n = 2.*self._a/3.
         else:
             raise MissingKeyError('Not enough information was provided on the exponents to use!')
     else:
         self._n = inputs.read_float('n_sp')
     #m and n will always be set, but care needs to be taken to include Q and W directly if appropriate
             
     self.stream_power_erosion = grid.zeros(centering='node')
Beispiel #14
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:
                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[topo_field_name]
            except AttributeError:
                print('Your grid does not seem to have a node field called', \
                      topo_field_name)

        # Create output variables.
        #
        # Note that we initialize depression depth to -1 (negative values make
        # no sense, so this is a clue to non-flooded nodes), and depression
        # outlet ID to BAD_INDEX_VALUE (which is a major clue!)
        self.depression_depth = self._grid.add_zeros('node', \
                                                     'depression__depth') - 1.0
        self.depression_outlet = self._grid.add_zeros('node', \
                                                'depression__outlet_node_id', \
                                                dtype=int) + BAD_INDEX_VALUE

        # Later on, we'll need a number that's guaranteed to be larger than the
        # highest elevation in the grid.
        self._BIG_ELEV = numpy.amax(self._elev) + 1

        # We'll also need a handy copy of the node neighbor lists
        # TODO: presently, this grid method seems to only exist for Raster grids.
        # We need it for *all* grids!
        self._node_nbrs = self._grid.get_neighbor_list()
        if type(self._grid) is landlab.grid.raster.RasterModelGrid:
            diag_nbrs = self._grid.get_diagonal_list()
            self._node_nbrs = numpy.concatenate((self._node_nbrs, diag_nbrs),
                                                1)