Ejemplo n.º 1
0
    def initialize(self, grid, params_file):
        '''
        params_file is the name of the text file containing the parameters
        needed for this stream power component.

        Module erodes where channels are, implemented as

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

        and if E<0, E=0.

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

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

        ***Parameters for input file***
        OBLIGATORY:
            K_sp -> positive float, the prefactor. This is defined per unit
                time, not per tstep. 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')
Ejemplo n.º 2
0
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')
    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):
        '''
        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)
Ejemplo n.º 5
0
    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
Ejemplo n.º 6
0
    def initialize(self, grid, params_file):
        r"""
        NOW DEPRECATED, USE __INIT__ DIRECTLY.
        params_file is the name of the text file containing the parameters
        needed for this stream power component.

        Module erodes where channels are, implemented as

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

        and if E<0, E=0.

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

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

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

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

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

        ***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()
Ejemplo n.º 8
0
def run_model(input_file=None,
              savepath=None,
              initial_topo_file=None,
              initial_seed_file=None):
    from landlab.components.flow_routing.route_flow_dn_JL import FlowRouter
    from landlab.components.stream_power.fastscape_stream_power_JL import FastscapeEroder
    #from landlab.components.stream_power.stream_power import StreamPowerEroder
    from landlab.components.sink_fill.pit_fill_pf import PitFiller
    from landlab.components.diffusion.diffusion import LinearDiffuser
    from landlab import ModelParameterDictionary
    #from landlab.plot import channel_profile as prf
    from landlab.plot.imshow import imshow_node_grid
    from landlab.io.esri_ascii import write_esri_ascii
    from landlab.io.esri_ascii import read_esri_ascii
    from landlab import RasterModelGrid

    #from analysis_method import analyze_drainage_percentage
    #from analysis_method import analyze_drainage_percentage_each_grid
    #from analysis_method import analyze_mean_erosion
    #from analysis_method import elev_diff_btwn_moraine_upland
    #from analysis_method import label_catchment
    #from analysis_method import cross_section
    #from analysis_method import analyze_node_below_threshold
    #from analysis_method import identify_drained_area
    #from analysis_method import save_result
    from analysis_method import shiftColorMap

    import copy
    import numpy as np
    import matplotlib
    import matplotlib.pyplot as plt
    import os
    import time
    import sys
    import shutil

    sys.setrecursionlimit(5000)

    #===============================================================================
    #get the needed properties to build the grid:
    if input_file is None:
        input_file = './coupled_params_sp.txt'
    inputs = ModelParameterDictionary(input_file)
    nrows = inputs.read_int('nrows')
    ncols = inputs.read_int('ncols')
    dx = inputs.read_float('dx')
    initial_slope = inputs.read_float('initial_slope')
    rightmost_elevation = initial_slope * ncols * dx
    #rightmost_elevation = inputs.read_float('rightmost_elevation')
    uplift_rate = inputs.read_float('uplift_rate')
    incision_rate = inputs.read_float('incision_rate')
    runtime = inputs.read_float('total_time')
    dt = inputs.read_float('dt')
    nt = int(runtime // dt)
    k_sp = inputs.read_float('K_sp')
    m_sp = inputs.read_float('m_sp')
    uplift_per_step = uplift_rate * dt
    incision_per_step = incision_rate * dt
    moraine_height = inputs.read_float('moraine_height')
    moraine_width = inputs.read_float('moraine_width')
    #valley_width = inputs.read_float('valley_width')
    valley_depth = inputs.read_float('valley_depth')
    num_outs = inputs.read_int('number_of_outputs')
    output_interval = int(nt // num_outs)
    diff = inputs.read_float('linear_diffusivity')
    #threshold_stream_power = inputs.read_float('threshold_stream_power')
    threshold_AS = inputs.read_float('threshold_AS')
    #threshold_erosion = dt*threshold_stream_power
    gw_coeff = inputs.read_float('gw_coeff')
    all_dry = inputs.read_bool('all_dry')
    fill_sink_with_water = inputs.read_bool('fill_sink_with_water')
    #===============================================================================

    #===============================================================================
    if initial_topo_file is None:
        #instantiate the grid object
        mg = RasterModelGrid(nrows, ncols, dx)

        ##create the elevation field in the grid:
        #create the field
        #specifically, this field has a triangular ramp
        #moraine at the north edge of the domain.
        mg.add_zeros('node', 'topographic__elevation', units='m')
        z = mg.at_node['topographic__elevation']
        moraine_start_y = np.max(mg.node_y) - moraine_width
        moraine_ys = np.where(mg.node_y > moraine_start_y)
        z[moraine_ys] += (mg.node_y[moraine_ys] - np.min(
            mg.node_y[moraine_ys])) * (moraine_height / moraine_width)

        #set valley
        #valley_start_x = np.min(mg.node_x)+valley_width
        #valley_ys = np.where((mg.node_x<valley_start_x)&(mg.node_y<moraine_start_y-valley_width))
        #z[valley_ys] -= (np.max(mg.node_x[valley_ys])-mg.node_x[valley_ys])*(valley_depth/valley_width)

        #set ramp (towards valley)
        upland = np.where(mg.node_y < moraine_start_y)
        z[upland] -= (np.max(mg.node_x[upland]) -
                      mg.node_x[upland]) * (rightmost_elevation / (ncols * dx))
        z += rightmost_elevation

        #set ramp (away from moraine)
        #upland = np.where(mg.node_y<moraine_start_y)
        #z[upland] -= (moraine_start_y-mg.node_y[upland])*initial_slope

        #put these values plus roughness into that field
        if initial_seed_file is None:
            z += np.random.rand(len(z)) / 1
        else:
            (seedgrid,
             seed) = read_esri_ascii(initial_seed_file,
                                     name='topographic__elevation_seed')
            z += seed
        mg.at_node['topographic__elevation'] = z

        #set river valley
        river_valley, = np.where(
            np.logical_and(
                mg.node_x == 0,
                np.logical_or(mg.status_at_node == 1, mg.status_at_node == 2)))
        mg.at_node['topographic__elevation'][river_valley] = -valley_depth
    else:
        (mg, z) = read_esri_ascii(initial_topo_file,
                                  name='topographic__elevation')

    #set river valley
    river_valley, = np.where(
        np.logical_and(
            mg.node_x == 0,
            np.logical_or(mg.status_at_node == 1, mg.status_at_node == 2)))
    mg.at_node['topographic__elevation'][river_valley] = -valley_depth

    #set up grid's boundary conditions (right, top, left, bottom) is inactive
    mg.set_closed_boundaries_at_grid_edges(True, True, False, True)

    #set up boundary along moraine
    #moraine_start_y = np.max(mg.node_y)-moraine_width
    #bdy_moraine_ids = np.where((mg.node_y > moraine_start_y) & (mg.node_x == 0))
    #mg.status_at_node[bdy_moraine_ids]=4
    #mg._update_links_nodes_cells_to_new_BCs()
    #===============================================================================

    #===============================================================================
    #instantiate the components:
    fr = FlowRouter(mg)
    pf = PitFiller(mg)
    sp = FastscapeEroder(mg, input_file, threshold_sp=threshold_AS * k_sp)
    #sp = StreamPowerEroder(mg, input_file, threshold_sp=threshold_erosion, use_Q=True)
    #diffuse = PerronNLDiffuse(mg, input_file)
    #lin_diffuse = LinearDiffuser(mg, input_file, method='on_diagonals')
    lin_diffuse = LinearDiffuser(mg, input_file)
    #===============================================================================

    #===============================================================================
    #instantiate plot setting
    plt.close('all')
    output_time = output_interval
    plot_num = 0
    mycmap = shiftColorMap(matplotlib.cm.gist_earth, 'mycmap')

    #folder name
    if savepath is None:
        if all_dry:
            name_tag = 'All_dry'
        elif fill_sink_with_water:
            name_tag = 'Not_all_dry_no_sink'
        else:
            name_tag = 'Not_all_dry_sink'
        savepath = 'results/sensitivity_test_threshold_same_seed/'+name_tag+'_dt=' + str(dt) + '_total_time=' + str(runtime) + '_k_sp=' + str(k_sp) + \
                '_uplift_rate=' + str(uplift_rate) + '_incision_rate=' + str(incision_rate) + '_initial_slope=' + str(initial_slope) + \
                '_threshold=' + str(threshold_stream_power)
    if not os.path.isdir(savepath):
        os.makedirs(savepath)

    #copy params_file
    if not os.path.isfile(savepath + '/coupled_params_sp.txt'):
        shutil.copy(input_file, savepath + '/coupled_params_sp.txt')

    # Display a message
    print 'Running ...'
    print savepath

    #save initial topography
    write_esri_ascii(savepath + '/Topography_t=0.0.txt', mg,
                     'topographic__elevation')

    #time
    start_time = time.time()
    #===============================================================================

    #===============================================================================
    #perform the loops:
    for i in xrange(nt):
        #note the input arguments here are not totally standardized between modules
        ''' 
        # simulate changing climate
        if (((i+1)*dt // 5000.) % 2) == 0.:
            all_dry = False
        else:
            all_dry = True
        '''

        #sp = FastscapeEroder(mg, input_file, threshold_sp = threshold_stream_power)

        #update elevation
        mg.at_node['topographic__elevation'][mg.core_nodes] += uplift_per_step
        mg.at_node['topographic__elevation'][river_valley] -= incision_per_step

        #mg = lin_diffuse.diffuse(dt)

        #route and erode
        original_topo = mg.at_node['topographic__elevation'].copy()
        slope = mg.at_node['topographic__steepest_slope'].copy()
        filled_nodes = None
        if all_dry or fill_sink_with_water:
            mg = pf.pit_fill()
            filled_nodes, = np.where(
                (mg.at_node['topographic__elevation'] - original_topo) > 0)
        mg = fr.route_flow(routing_flat=all_dry)
        old_topo = mg.at_node['topographic__elevation'].copy()
        #mg, temp_z, temp_sp = sp.erode(mg, dt, Q_if_used='water__volume_flux')
        mg = sp.erode(mg, dt, flooded_nodes=filled_nodes)

        new_topo = mg.at_node['topographic__elevation'].copy()
        mg.at_node[
            'topographic__elevation'] = original_topo + new_topo - old_topo

        #diffuse
        #for j in range(10):
        #    mg = lin_diffuse.diffuse(dt/10)
        mg = lin_diffuse.diffuse(dt)

        if i + 1 == output_time:
            print 'Saving data...'

            plot_num += 1
            plt.figure(plot_num)
            im = imshow_node_grid(mg, 'topographic__elevation', plot_name='t = '+str(int((i+1)*dt))+' years', \
                grid_units = ['m','m'], cmap=mycmap, allow_colorbar=True, \
                vmin=0-valley_depth-incision_rate*runtime, vmax=5.+moraine_height+uplift_rate*runtime)
            plt.savefig(savepath + '/Topography_t=' + str(
                (i + 1) * dt) + '.jpg',
                        dpi=300)

            write_esri_ascii(
                savepath + '/Topography_t=' + str((i + 1) * dt) + '.txt', mg,
                'topographic__elevation')

            output_time += output_interval

        plt.close('all')

        print("--- %.2f minutes ---" %
              ((time.time() - start_time) / 60)), 'Completed loop', i + 1

    plt.close('all')

    print 'Finished simulating.'
    #===============================================================================

    #===============================================================================
    #analyze_drainage_percentage(savepath, True)
    #analyze_mean_erosion(savepath, True)
    #elev_diff_btwn_moraine_upland(savepath, True)
    #label_catchment(savepath)
    #cross_section(savepath)
    #===============================================================================

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