def initialize(self, input_stream=None): # 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 called') print('"topographic__elevation", you need to pass the name of') print('a text input file or ModelParameterDictionary, and this') print('file or dictionary needs to include the name of another') print('field in your grid that contains your elevation data.') raise AttributeError except MissingKeyError: print('Error: Because your grid does not have a node field called') print('"topographic__elevation", your input file (or') print('ModelParameterDictionary) must include an entry with the') print('key "ELEVATION_FIELD_NAME", which gives the name of a') print('field in your grid that contains your elevation 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 include 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)
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 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 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 BAD_INDEX_VALUE (which is a major clue!) self.depression_depth = self._grid.add_zeros("node", "depression__depth") self.depression_outlet_map = self._grid.add_zeros("node", "depression__outlet_node", dtype=int) self.depression_outlet_map += 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.get_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.link_length 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) self.flood_status = self._grid.add_zeros("node", "flood_status_code", dtype=int) self._lake_map = np.empty(self._grid.number_of_nodes, dtype=int) self._lake_map.fill(BAD_INDEX_VALUE)
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. 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. 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.fraction_gradient_change = 0.25 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.0 try: self.sed_density = inputs.read_float("sediment_density") except MissingKeyError: self.sed_density = 2700.0 try: self.fluid_density = inputs.read_float("fluid_density") except MissingKeyError: self.fluid_density = 1000.0 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.return_ch_props = inputs.read_bool("return_stream_properties") except MissingKeyError: self.return_ch_props = 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.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.0 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.0 - 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.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.0 self.diffusivity_power_on_A = 0.9 * self._c * (1.0 - 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.0 / 3.0 self.Qs_prefactor = ( 4.0 * self.C_MPM ** twothirds * self.fluid_density ** twothirds / (self.sed_density - self.fluid_density) ** twothirds * self.g ** (twothirds / 2.0) * mannings_n ** 0.6 * self.k_w ** (1.0 / 15.0) * self.k_Q ** (0.6 + self._b / 15.0) / self.sed_density ** twothirds ) self.Qs_thresh_prefactor = ( 4.0 * ( 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.0) self.Qs_power_onAthresh = twothirds * self._b * self._c 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.cell_areas)) self.cell_areas[grid.node_at_cell] = grid.cell_areas self.bad_neighbor_mask = np.equal(grid.get_neighbor_list(bad_index=-1), -1) self.routing_code = """
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')
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)
def initialize(self, input_stream): # Create a ModelParameterDictionary for the inputs if type(input_stream)==ModelParameterDictionary: inputs = input_stream else: inputs = ModelParameterDictionary(input_stream) # Read input/configuration parameters self.kd = inputs.read_float('linear_diffusivity') try: self.uplift_rate = inputs.read_float('uplift_rate') except MissingKeyError: self.uplift_rate = 0. try: self.values_to_diffuse = inputs.read_string('values_to_diffuse') except MissingKeyError: self.values_to_diffuse = 'topographic__elevation' else: #take switch in the new field name in the class properties for mysets in (self._input_var_names, self._output_var_names): mysets.remove('topographic__elevation') mysets.add(self.values_to_diffuse) for mydicts in (self._var_units, self._var_mapping, self._var_doc): mydicts[self.values_to_diffuse] = mydicts.pop('topographic__elevation') try: self.timestep_in = inputs.read_float('dt') except MissingKeyError: pass # Create grid if one doesn't already exist if self._grid is None: self._grid = create_and_initialize_grid(input_stream) # Set internal time step # ..todo: # implement mechanism to compute time-steps dynamically if grid is # adaptive/changing dx = self._grid.min_active_link_length() # smallest active link length self.dt = _ALPHA*dx*dx/self.kd # CFL condition try: self.tstep_ratio = self.timestep_in/self.dt except AttributeError: pass # Get a list of interior cells self.interior_cells = self._grid.node_at_core_cell ##DEJH bites the bullet and forces the 2015 style with fields # # Here we're experimenting with different approaches: with # # 'make_all_data', we create and manage all the data we need and embed # # it all in the grid. With 'explicit', we require the caller/user to # # provide data. # if _VERSION=='make_all_data': # #print('creating internal data') # self.z = self._grid.add_zeros('node', 'landscape_surface__elevation') # self.g = self._grid.add_zeros('active_link', 'landscape_surface__gradient') # surface gradients # self.qs = self._grid.add_zeros('active_link','unit_sediment_flux') # unit sediment flux # self.dqds = self._grid.add_zeros('node', 'sediment_flux_divergence') # sed flux derivative # elif _VERSION=='explicit': # pass # else: # # Create data arrays for variables that won't (?) be shared with other # # components # self.g = self._grid.create_active_link_array_zeros() # surface gradients # self.qs = self._grid.create_active_link_array_zeros() # unit sediment flux # self.dqds = self._grid.create_node_array_zeros() # sed flux derivative self.z = self._grid.at_node[self.values_to_diffuse] g = self._grid.zeros(centering='link') qs = self._grid.zeros(centering='link') try: self.g = self._grid.add_field('link', 'surface__gradient', g, noclobber=True) #note this will object if this exists already except FieldError: pass #field exists, so no problem try: self.qs = self._grid.add_field('link', 'unit_flux', qs, noclobber=True) except FieldError: pass
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')
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)
def initialize(self, grid, params_file): """ This module implements sediment flux dependent channel incision following: E = f(Qs, Qc) * stream_power - sp_crit, where stream_power is the stream power (often ==K*A**m*S**n) provided by the stream_power.py component. Note that under this incision paradigm, sp_crit is assumed to be controlled exclusively by sediment mobility, i.e., it is not a function of bedrock resistance. If you want it to represent a bedrock resistance term, be sure to set Dchar if you use the MPM transport capacity relation, and do not use the flag 'slope_sensitive_threshold'. The component currently assumes that the threshold on bed incision is controlled by the threshold of motion of its sediment cover. This means there is assumed interplay between the supplied Shields number, characteristic grain size, and shear stress threshold. This calculation has a tendency to be slow, and can easily result in numerical instabilities. These instabilities are suppressed by retaining a memory of what the sediment flux was in the last time step, and weighting the next timestep by that value. XXXmore detail needed. Possibilities: 1. weight by last timestep/2timesteps (what about early ones?) 2. do it iteratively; only do incision one the sed flux you are using stabilises (so the previous iter "seed" becomes less important) Parameters needed in the initialization file follow those for stream_power.py. However, we now require additional input terms for the f(Qs,Qc) term: REQUIRED: *Set the stream power terms using a, b, and c NOT m, n. *...remember, any threshold set is set as tau**a, not just tau. *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] *sed_dependency_type -> 'None', 'linear_decline', 'parabolic', 'almost_parabolic', 'generalized_humped'. For definitions, see Gasparini et al., 2006; Hobley et al., 2011. *Qc -> This input controls the sediment capacity used by the component. It can calculate sediment carrying capacity for itself if this parameter is a string 'MPM', which will cause the component to use a slightly modified version of the Meyer-Peter Muller equation (again, see Hobley et al., 2011). Alternatively, it can be another string denoting the grid field name in which a precalculated capacity is stored. Depending on which options are specified above, further parameters may be required: *If sed_dependency_type=='generalized_humped', need the shape parameters used by Hobley et al: kappa_hump nu_hump phi_hump c_hump Note the onus is on the user to ensure that these parameters result in a viable shape, i.e., one where the maximum is 1 and there is indeed a hump in the profile. If these parameters are NOT specified, they will default to the form of the curve for Leh valley as found in Hobley et al 2011: nu=1.13; phi=4.24; c=0.00181; kappa=13.683. *If Qc=='MPM', these parameters may optionally be provided: Dchar -> characteristic grain size (i.e., D50) on the bed, in m. C_MPM -> the prefactor in the MPM relation. Defaults to 1, as in the relation sensu stricto, but can be modified to "tune" the equations to a known point where sediment deposition begins. In cases where k_Q and k_w are not known from real data, it is recommended these parameters be tuned in preference to C. *...if Dchar is NOT provided, the component will attempt to set (and will report) an appropriate characteristic grain size, such that it is consistent both with the threshold provided *and* a critical Shields number of 0.05. (If you really, really want to, you can override this critical Shields number too; use parameter *threshold_Shields*). OPTIONAL: *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_shear_stress -> a float. If not provided, may be overridden by the following parameters. If it is not, defaults to 0. *slope_sensitive_threshold -> a boolean, defaults to FALSE. In steep mountain environments, the critical Shields number for particle motion appears to be weakly sensitive to the local slope, as taustar_c=0.15*S**0.25 (Lamb et al, 2008). If this flag is set to TRUE, the critical threshold in the landscape is allowed to become slope sensitive as well, in order to be consistent with this equation. This modification was used by Hobley et al., 2011. *set_threshold_from_Dchar -> a boolean, defaults to FALSE. Use this flag to force an appropriate threshold value from a provided Dchar. i.e., this is the inverse of the procedure that is used to find Dchar if it isn't provided. No threshold can be specified in the parameter file, and Dchar must be specified. *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.fraction_gradient_change = 0.25 self.pseudoimplicit_repeats = 5 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.thresh = inputs.read_float('threshold_shear_stress') self.set_threshold = True #flag for sed_flux_dep_incision to see if the threshold was manually set. print "Found a shear stress threshold to use: ", self.thresh except MissingKeyError: print "Found no incision threshold to use." self.thresh = 0. self.set_threshold = False try: self._a = inputs.read_float('a_sp') except: print "a not supplied. Setting power on shear stress to 1..." self._a = 1. 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') #we need to restore this functionality later #'To use the sed flux dependent model, you must set a,b,c not m,n. Try a=1,b=0.5,c=1...?' self._K_unit_time = inputs.read_float( 'K_sp') / 31557600. #...because we work with dt in seconds #set gravity 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.relative_weight = ( self.sed_density - self.fluid_density ) / self.fluid_density * self.g #to accelerate MPM calcs self.rho_g = self.fluid_density * self.g 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) self.diffusivity_power_on_A = 0.9 * self._c * (1. - self._b ) #i.e., q/D**(1/6) self.type = inputs.read_string('sed_dependency_type') try: self.Qc = inputs.read_string('Qc') except MissingKeyError: self.Qc = None else: if self.Qc == 'MPM': self.calc_cap_flag = True else: self.calc_cap_flag = False try: self.override_threshold = inputs.read_bool( 'set_threshold_from_Dchar') except MissingKeyError: self.override_threshold = False try: self.shields_crit = inputs.read_float('threshold_Shields') except MissingKeyError: self.shields_crit = 0.05 try: self.return_ch_props = inputs.read_bool('return_stream_properties') except MissingKeyError: self.return_ch_props = False #now conditional inputs if self.type == 'generalized_humped': try: self.kappa = inputs.read_float('kappa_hump') self.nu = inputs.read_float('nu_hump') self.phi = inputs.read_float('phi_hump') self.c = inputs.read_float('c_hump') except MissingKeyError: self.kappa = 13.683 self.nu = 1.13 self.phi = 4.24 self.c = 0.00181 print 'Adopting inbuilt parameters for the humped function form...' try: self.lamb_flag = inputs.read_bool('slope_sensitive_threshold') #this is going to be a nightmare to implement... except: self.lamb_flag = False if self.Qc == 'MPM': try: self.Dchar_in = inputs.read_float('Dchar') except MissingKeyError: assert self.thresh > 0., "Can't automatically set characteristic grain size if threshold is 0 or unset!" #remember the threshold getting set is already tau**a self.Dchar_in = self.thresh / self.g / ( self.sed_density - self.fluid_density) / self.shields_crit print 'Setting characteristic grain size from the Shields criterion...' print 'Characteristic grain size is: ', self.Dchar_in try: self.C_MPM = inputs.read_float('C_MPM') except MissingKeyError: self.C_MPM = 1. if self.override_threshold: print "Overriding any supplied threshold..." try: self.thresh = self.shields_crit * self.g * ( self.sed_density - self.fluid_density) * self.Dchar_in except AttributeError: self.thresh = self.shields_crit * self.g * ( self.sed_density - self.fluid_density) * inputs.read_float('Dchar') print "Threshold derived from grain size and Shields number is: ", self.thresh 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 #new 11/12/14 self.point6onelessb = 0.6 * (1. - self._b) self.shear_stress_prefactor = self.fluid_density * self.g * ( self.mannings_n / self.k_w)**0.6 if self.set_threshold is False or self.override_threshold: 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
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)
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)
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)
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()
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. 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 = 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')
def initialize(self, grid, params_file): """ This module implements sediment flux dependent channel incision following: E = f(Qs, Qc) * stream_power - sp_crit, where stream_power is the stream power (often ==K*A**m*S**n) provided by the stream_power.py component. Note that under this incision paradigm, sp_crit is assumed to be controlled exclusively by sediment mobility, i.e., it is not a function of bedrock resistance. If you want it to represent a bedrock resistance term, be sure to set Dchar if you use the MPM transport capacity relation, and do not use the flag 'slope_sensitive_threshold'. The component currently assumes that the threshold on bed incision is controlled by the threshold of motion of its sediment cover. This means there is assumed interplay between the supplied Shields number, characteristic grain size, and shear stress threshold. This calculation has a tendency to be slow, and can easily result in numerical instabilities. These instabilities are suppressed by retaining a memory of what the sediment flux was in the last time step, and weighting the next timestep by that value. XXXmore detail needed. Possibilities: 1. weight by last timestep/2timesteps (what about early ones?) 2. do it iteratively; only do incision one the sed flux you are using stabilises (so the previous iter "seed" becomes less important) Parameters needed in the initialization file follow those for stream_power.py. However, we now require additional input terms for the f(Qs,Qc) term: REQUIRED: *Set the stream power terms using a, b, and c NOT m, n. *...remember, any threshold set is set as tau**a, not just tau. *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] *sed_dependency_type -> 'None', 'linear_decline', 'parabolic', 'almost_parabolic', 'generalized_humped'. For definitions, see Gasparini et al., 2006; Hobley et al., 2011. *Qc -> This input controls the sediment capacity used by the component. It can calculate sediment carrying capacity for itself if this parameter is a string 'MPM', which will cause the component to use a slightly modified version of the Meyer-Peter Muller equation (again, see Hobley et al., 2011). Alternatively, it can be another string denoting the grid field name in which a precalculated capacity is stored. Depending on which options are specified above, further parameters may be required: *If sed_dependency_type=='generalized_humped', need the shape parameters used by Hobley et al: kappa_hump nu_hump phi_hump c_hump Note the onus is on the user to ensure that these parameters result in a viable shape, i.e., one where the maximum is 1 and there is indeed a hump in the profile. If these parameters are NOT specified, they will default to the form of the curve for Leh valley as found in Hobley et al 2011: nu=1.13; phi=4.24; c=0.00181; kappa=13.683. *If Qc=='MPM', these parameters may optionally be provided: Dchar -> characteristic grain size (i.e., D50) on the bed, in m. C_MPM -> the prefactor in the MPM relation. Defaults to 1, as in the relation sensu stricto, but can be modified to "tune" the equations to a known point where sediment deposition begins. In cases where k_Q and k_w are not known from real data, it is recommended these parameters be tuned in preference to C. *...if Dchar is NOT provided, the component will attempt to set (and will report) an appropriate characteristic grain size, such that it is consistent both with the threshold provided *and* a critical Shields number of 0.05. (If you really, really want to, you can override this critical Shields number too; use parameter *threshold_Shields*). OPTIONAL: *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_shear_stress -> a float. If not provided, may be overridden by the following parameters. If it is not, defaults to 0. *slope_sensitive_threshold -> a boolean, defaults to FALSE. In steep mountain environments, the critical Shields number for particle motion appears to be weakly sensitive to the local slope, as taustar_c=0.15*S**0.25 (Lamb et al, 2008). If this flag is set to TRUE, the critical threshold in the landscape is allowed to become slope sensitive as well, in order to be consistent with this equation. This modification was used by Hobley et al., 2011. *set_threshold_from_Dchar -> a boolean, defaults to FALSE. Use this flag to force an appropriate threshold value from a provided Dchar. i.e., this is the inverse of the procedure that is used to find Dchar if it isn't provided. No threshold can be specified in the parameter file, and Dchar must be specified. *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.fraction_gradient_change = 0.25 self.pseudoimplicit_repeats = 5 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.thresh = inputs.read_float('threshold_shear_stress') self.set_threshold = True #flag for sed_flux_dep_incision to see if the threshold was manually set. print "Found a shear stress threshold to use: ", self.thresh except MissingKeyError: print "Found no incision threshold to use." self.thresh = 0. self.set_threshold = False try: self._a = inputs.read_float('a_sp') except: print "a not supplied. Setting power on shear stress to 1..." self._a = 1. 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') #we need to restore this functionality later #'To use the sed flux dependent model, you must set a,b,c not m,n. Try a=1,b=0.5,c=1...?' self._K_unit_time = inputs.read_float('K_sp')/31557600. #...because we work with dt in seconds #set gravity 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.relative_weight = (self.sed_density-self.fluid_density)/self.fluid_density*self.g #to accelerate MPM calcs self.rho_g = self.fluid_density*self.g 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) self.diffusivity_power_on_A = 0.9*self._c*(1.-self._b) #i.e., q/D**(1/6) self.type = inputs.read_string('sed_dependency_type') try: self.Qc = inputs.read_string('Qc') except MissingKeyError: self.Qc = None else: if self.Qc=='MPM': self.calc_cap_flag = True else: self.calc_cap_flag = False try: self.override_threshold = inputs.read_bool('set_threshold_from_Dchar') except MissingKeyError: self.override_threshold = False try: self.shields_crit = inputs.read_float('threshold_Shields') except MissingKeyError: self.shields_crit = 0.05 try: self.return_ch_props = inputs.read_bool('return_stream_properties') except MissingKeyError: self.return_ch_props = False #now conditional inputs if self.type == 'generalized_humped': try: self.kappa = inputs.read_float('kappa_hump') self.nu = inputs.read_float('nu_hump') self.phi = inputs.read_float('phi_hump') self.c = inputs.read_float('c_hump') except MissingKeyError: self.kappa = 13.683 self.nu = 1.13 self.phi = 4.24 self.c = 0.00181 print 'Adopting inbuilt parameters for the humped function form...' try: self.lamb_flag = inputs.read_bool('slope_sensitive_threshold') #this is going to be a nightmare to implement... except: self.lamb_flag = False if self.Qc == 'MPM': try: self.Dchar_in = inputs.read_float('Dchar') except MissingKeyError: assert self.thresh > 0., "Can't automatically set characteristic grain size if threshold is 0 or unset!" #remember the threshold getting set is already tau**a self.Dchar_in = self.thresh/self.g/(self.sed_density-self.fluid_density)/self.shields_crit print 'Setting characteristic grain size from the Shields criterion...' print 'Characteristic grain size is: ', self.Dchar_in try: self.C_MPM = inputs.read_float('C_MPM') except MissingKeyError: self.C_MPM = 1. if self.override_threshold: print "Overriding any supplied threshold..." try: self.thresh = self.shields_crit*self.g*(self.sed_density-self.fluid_density)*self.Dchar_in except AttributeError: self.thresh = self.shields_crit*self.g*(self.sed_density-self.fluid_density)*inputs.read_float('Dchar') print "Threshold derived from grain size and Shields number is: ", self.thresh 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 #new 11/12/14 self.point6onelessb = 0.6*(1.-self._b) self.shear_stress_prefactor = self.fluid_density*self.g*(self.mannings_n/self.k_w)**0.6 if self.set_threshold is False or self.override_threshold: 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
class TestModelParameterDictionary(unittest.TestCase): def setUp(self): from StringIO import StringIO self.param_file = StringIO(_TEST_PARAM_DICT_FILE) self.param_dict = ModelParameterDictionary() self.param_dict.read_from_file(self.param_file) def test_read_file(self): all_keys = set([ 'FLOAT_VAL', 'INT_VAL', 'STRING_VAL', 'TRUE_BOOL_VAL', 'FALSE_BOOL_VAL', ]) param_list = set(self.param_dict.params()) self.assertEqual(param_list, all_keys) def test_read_file_name(self): (prm_fd, prm_file_name) = tempfile.mkstemp() prm_file = os.fdopen(prm_fd, 'w') prm_file.write(_TEST_PARAM_DICT_FILE) prm_file.close() param_dict = ModelParameterDictionary() param_dict.read_from_file(prm_file_name) os.remove(prm_file_name) all_keys = set([ 'FLOAT_VAL', 'INT_VAL', 'STRING_VAL', 'TRUE_BOOL_VAL', 'FALSE_BOOL_VAL', ]) param_list = set(param_dict.params()) self.assertEqual(param_list, all_keys) def test_read_file_like_twice(self): from StringIO import StringIO param_file = StringIO(_TEST_PARAM_DICT_FILE) param_dict_1 = ModelParameterDictionary() param_dict_2 = ModelParameterDictionary() param_dict_1.read_from_file(param_file) param_dict_2.read_from_file(param_file) def test_read_int(self): self.assertEqual(self.param_dict.read_int('INT_VAL'), 1) with self.assertRaises(ParameterValueError): self.param_dict.read_int('FLOAT_VAL') with self.assertRaises(MissingKeyError): self.param_dict.read_int('MISSING_INT') self.assertEqual(self.param_dict.read_int('MISSING_INT', 2), 2) def test_get_int(self): self.assertEqual(self.param_dict.get('INT_VAL', ptype=int), 1) with self.assertRaises(ParameterValueError): self.param_dict.get('FLOAT_VAL', ptype=int) with self.assertRaises(MissingKeyError): self.param_dict.get('MISSING_INT', ptype=int) def test_set_default(self): self.param_dict.setdefault('MISSING_INT', 2) self.assertEqual(self.param_dict.read_int('MISSING_INT'), 2) def test_read_float(self): self.assertEqual(self.param_dict.read_float('FLOAT_VAL'), 2.2) self.assertEqual(self.param_dict.read_float('INT_VAL'), 1) with self.assertRaises(ParameterValueError): self.param_dict.read_float('STRING_VAL') with self.assertRaises(MissingKeyError): self.param_dict.read_float('MISSING_FLOAT') def test_read_string(self): self.assertEqual(self.param_dict.read_string('STRING_VAL'), 'The Landlab') self.assertEqual(self.param_dict.read_string('INT_VAL'), '1') self.assertEqual(self.param_dict.read_string('FLOAT_VAL'), '2.2') with self.assertRaises(MissingKeyError): self.param_dict.read_string('MISSING_STRING') def test_read_bool(self): self.assertEqual(self.param_dict.read_bool('TRUE_BOOL_VAL'), True) self.assertEqual(self.param_dict.read_bool('FALSE_BOOL_VAL'), False) with self.assertRaises(MissingKeyError): self.param_dict.read_bool('MISSING_BOOLEAN') with self.assertRaises(ParameterValueError): self.param_dict.read_bool('STRING_VAL') def test_dict_keys(self): all_keys = set([ 'FLOAT_VAL', 'INT_VAL', 'STRING_VAL', 'TRUE_BOOL_VAL', 'FALSE_BOOL_VAL', ]) self.assertEqual(set(self.param_dict), all_keys) for key in all_keys: self.assertTrue(key in self.param_dict) def test_dict_index(self): self.assertEqual(self.param_dict['INT_VAL'], '1')
class TestModelParameterDictionary(unittest.TestCase): def setUp(self): from StringIO import StringIO self.param_file = StringIO(_TEST_PARAM_DICT_FILE) self.param_dict = ModelParameterDictionary() self.param_dict.read_from_file(self.param_file) def test_read_file(self): all_keys = set([ 'FLOAT_VAL', 'INT_VAL', 'STRING_VAL', 'TRUE_BOOL_VAL', 'FALSE_BOOL_VAL', ]) param_list = set(self.param_dict.params()) self.assertEqual(param_list, all_keys) def test_read_file_name(self): (prm_fd, prm_file_name) = tempfile.mkstemp() prm_file = os.fdopen(prm_fd, 'w') prm_file.write(_TEST_PARAM_DICT_FILE) prm_file.close() param_dict = ModelParameterDictionary() param_dict.read_from_file(prm_file_name) os.remove(prm_file_name) all_keys = set([ 'FLOAT_VAL', 'INT_VAL', 'STRING_VAL', 'TRUE_BOOL_VAL', 'FALSE_BOOL_VAL', ]) param_list = set(param_dict.params()) self.assertEqual(param_list, all_keys) def test_read_file_like_twice(self): from StringIO import StringIO param_file = StringIO(_TEST_PARAM_DICT_FILE) param_dict_1 = ModelParameterDictionary() param_dict_2 = ModelParameterDictionary() param_dict_1.read_from_file(param_file) param_dict_2.read_from_file(param_file) def test_read_int(self): self.assertEqual(self.param_dict.read_int('INT_VAL'), 1) with self.assertRaises(ParameterValueError): self.param_dict.read_int('FLOAT_VAL') with self.assertRaises(MissingKeyError): self.param_dict.read_int('MISSING_INT') self.assertEqual(self.param_dict.read_int('MISSING_INT', 2), 2) def test_get_int(self): self.assertEqual(self.param_dict.get('INT_VAL', ptype=int), 1) with self.assertRaises(ParameterValueError): self.param_dict.get('FLOAT_VAL', ptype=int) with self.assertRaises(MissingKeyError): self.param_dict.get('MISSING_INT', ptype=int) def test_set_default(self): self.param_dict.setdefault('MISSING_INT', 2) self.assertEqual(self.param_dict.read_int('MISSING_INT'), 2) def test_read_float(self): self.assertEqual(self.param_dict.read_float('FLOAT_VAL'), 2.2) self.assertEqual(self.param_dict.read_float('INT_VAL'), 1) with self.assertRaises(ParameterValueError): self.param_dict.read_float('STRING_VAL') with self.assertRaises(MissingKeyError): self.param_dict.read_float('MISSING_FLOAT') def test_read_string(self): self.assertEqual(self.param_dict.read_string('STRING_VAL'), 'The Landlab') self.assertEqual(self.param_dict.read_string('INT_VAL'), '1') self.assertEqual(self.param_dict.read_string('FLOAT_VAL'), '2.2') with self.assertRaises(MissingKeyError): self.param_dict.read_string('MISSING_STRING') def test_read_bool(self): self.assertEqual(self.param_dict.read_bool('TRUE_BOOL_VAL'), True) self.assertEqual(self.param_dict.read_bool('FALSE_BOOL_VAL'), False) with self.assertRaises(MissingKeyError): self.param_dict.read_bool('MISSING_BOOLEAN') with self.assertRaises(ParameterValueError): self.param_dict.read_bool('STRING_VAL') def test_dict_keys(self): all_keys = set([ 'FLOAT_VAL', 'INT_VAL', 'STRING_VAL', 'TRUE_BOOL_VAL', 'FALSE_BOOL_VAL', ]) self.assertEqual(set(self.param_dict), all_keys) for key in all_keys: self.assertTrue(key in self.param_dict) def test_dict_index(self): self.assertEqual(self.param_dict['INT_VAL'], '1')