def get_sed_flux_function(self, rel_sed_flux): """Get the sediment flux function. Parameters ---------- 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 ): """Get the pseudoimplicit sediment flux function. Parameters ---------- 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.0 - 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.0 - 4.0 * (rel_sed_flux_in - 0.5) ** 2.0 elif self._type == "almost_parabolic": def sed_flux_fn_gen(rel_sed_flux_in): return np.where( rel_sed_flux_in > 0.1, 1.0 - 4.0 * (rel_sed_flux_in - 0.5) ** 2.0, 2.6 * rel_sed_flux_in + 0.1, ) elif self._type == "None": def sed_flux_fn_gen(rel_sed_flux_in): return 1.0 else: raise MissingKeyError( "Provided sed flux sensitivity type in input file was not " + "recognised!" ) for i in range(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.0: rel_sed_flux = 1.0 break if rel_sed_flux < 0.0: rel_sed_flux = 0.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
def __init__( self, grid, K_sp=0.001, threshold_sp=0.0, sp_type="set_mn", m_sp=0.5, n_sp=1.0, a_sp=None, b_sp=None, c_sp=None, channel_width_field=1.0, discharge_field="drainage_area", erode_flooded_nodes=True, ): """Initialize the StreamPowerEroder. Parameters ---------- grid : ModelGrid A grid. K_sp : float, array, or field name K in the stream power equation (units vary with other parameters). threshold_sp : positive float, array, or field name, optional The threshold stream power, below which no erosion occurs. This threshold is assumed to be in "stream power" units, i.e., if sp_type is 'Shear_stress', the value should be tau**a. sp_type : {'set_mn', 'Total', 'Unit', 'Shear_stress'} Controls how the law is implemented. If 'set_mn', use the supplied values of m_sp and n_sp. Else, component will derive values of m and n from supplied values of a_sp, b_sp, and c_sp, following Whipple and Tucker: * If ``'Total'``, ``m = a * c``, ``n = a``. * If ``'Unit'``, ``m = a * c *(1 - b)``, ``n = a``. * If ``'Shear_stress'``, ``m = 2 * a * c * (1 - b) / 3``, ``n = 2 * a / 3``. m_sp : float, optional m in the stream power equation (power on drainage area). Overridden if a_sp, b_sp, and c_sp are supplied. n_sp : float, optional, ~ 0.5<n_sp<4. n in the stream power equation (power on slope). Overridden if a_sp, b_sp, and c_sp are supplied. a_sp : float, optional The power on the SP/shear term to get the erosion rate; the "erosional process" term. Only used if sp_type is not 'set_mn'. b_sp : float, optional The power on discharge to get width; the "hydraulic geometry" term. Only used if sp_type in ('Unit', 'Shear_stress'). c_sp : float, optional The power on area to get discharge; the "basin hydology" term. Only used if sp_type is not 'set_mn'. channel_width_field : None, float, array, or field name, optional If not None, component will look for node-centered data describing channel width or if an array, will take the array as the channel widths. It will use the widths to implement incision ~ stream power per unit width. If sp_type is 'set_mn', follows the equation given above. If sp_type in ('Unit', 'Shear_stress'), the width value will be implemented directly. W has no effect if sp_type is 'Total'. discharge_field : float, field name, or array, optional Discharge [L^2/T]. The default is to use the grid field 'drainage_area'. To use custom spatially/temporally varying rainfall, use 'water__unit_flux_in' to specify water input to the FlowAccumulator and use "surface_water__discharge" for this keyword argument. erode_flooded_nodes : bool (optional) Whether erosion occurs in flooded nodes identified by a depression/lake mapper (e.g., DepressionFinderAndRouter). When set to false, the field *flood_status_code* must be present on the grid (this is created by the DepressionFinderAndRouter). Default True. """ super().__init__(grid) if "flow__receiver_node" in grid.at_node: if grid.at_node["flow__receiver_node"].size != grid.size("node"): msg = ( "A route-to-multiple flow director has been " "run on this grid. The landlab development team has not " "verified that StreamPowerEroder is compatible with " "route-to-multiple methods. Please open a GitHub Issue " "to start this process.") raise NotImplementedError(msg) if not erode_flooded_nodes: if "flood_status_code" not in self._grid.at_node: msg = ( "In order to not erode flooded nodes another component " "must create the field *flood_status_code*. You want to " "run a lake mapper/depression finder.") raise ValueError(msg) self._erode_flooded_nodes = erode_flooded_nodes self._A = return_array_at_node(grid, discharge_field) self._elevs = return_array_at_node(grid, "topographic__elevation") self._sp_crit = return_array_at_node(grid, threshold_sp) # use setter for K defined below self.K = K_sp assert np.all(self._sp_crit >= 0.0) if discharge_field == "drainage_area": self._use_Q = False else: self._use_Q = True if channel_width_field is None: self._use_W = False else: self._use_W = True self._W = return_array_at_node(grid, channel_width_field) if np.any(threshold_sp != 0.0): self._set_threshold = True # ^flag for sed_flux_dep_incision to see if the threshold was # manually set. else: self._set_threshold = False self._type = sp_type if sp_type == "set_mn": assert (float(m_sp) >= 0.0) and (float(n_sp) >= 0.0), "m and n must be positive" self._m = float(m_sp) self._n = float(n_sp) assert ( (a_sp is None) and (b_sp is None) and (c_sp is None) ), "If sp_type is 'set_mn', do not pass values for a, b, or c!" else: assert sp_type in ("Total", "Unit", "Shear_stress"), ( "sp_type not recognised. It must be 'set_mn', 'Total', " + "'Unit', or 'Shear_stress'.") assert (m_sp == 0.5 and n_sp == 1.0), "Do not set m and n if sp_type is not 'set_mn'!" assert float(a_sp) >= 0.0, "a must be positive" self._a = float(a_sp) if b_sp is not None: assert float(b_sp) >= 0.0, "b must be positive" self._b = float(b_sp) else: assert self._use_W, "b was not set" self._b = 0.0 if c_sp is not None: assert float(c_sp) >= 0.0, "c must be positive" self._c = float(c_sp) else: assert self._use_Q, "c was not set" self._c = 1.0 if self._type == "Total": self._n = self._a self._m = self._a * self._c # ==_a if use_Q elif self._type == "Unit": self._n = self._a self._m = self._a * self._c * (1.0 - self._b) # ^ ==_a iff use_Q&use_W etc elif self._type == "Shear_stress": self._m = 2.0 * self._a * self._c * (1.0 - self._b) / 3.0 self._n = 2.0 * self._a / 3.0 else: raise MissingKeyError( "Not enough information was provided on the exponents to use!" ) # m and n will always be set, but care needs to be taken to include Q # and W directly if appropriate self._stream_power_erosion = self._grid.zeros(centering="node") self._alpha = self._grid.zeros("node")