예제 #1
0
    def __init__(self, modern_dem_name, outlet_id, chi_mask_dem_name=None, from_file=None):
        """Initialize MetricCalculator with names of postglacial and modern
        DEMs."""


        if from_file is None:

            # Read and remember the modern DEM (whether data or model)
            (self.grid, self.z) = self.read_topography(modern_dem_name)
            #print self.grid.x_of_node
    
            self.grid.set_watershed_boundary_condition_outlet_id(outlet_id,
                                                                 self.z, nodata_value=-9999)
    
            # Instantiate and run a FlowRouter and lake filler, so we get
            # drainage area for cumulative-area statistic, and also fields for chi.
            fr = FlowRouter(self.grid)
            dfr = DepressionFinderAndRouter(self.grid)
            fr.route_flow()
            dfr.map_depressions()
    
            # Remember modern drainage area grid
            self.area = self.grid.at_node['drainage_area']
    
            # Instantiate a ChiFinder for chi-index
            self.chi_finder = ChiFinder(self.grid, min_drainage_area=10000.,
                                        reference_concavity=0.5)
            
            core_nodes = np.zeros(self.area.shape, dtype=bool)
            core_nodes[self.grid.core_nodes] = True
            # Read and remember the MASK, if provided
            if chi_mask_dem_name is None:
                 self.mask = (self.area>1e5)
                 self.till_mask = np.zeros(self.mask.shape, dtype=bool) 
                 self.till_mask[self.grid.core_nodes] = 1
            else:
                (self.mask_grid, zmask) = self.read_topography(chi_mask_dem_name)
                mask = (zmask>0)*1
                self.mask = (self.area>1e5)*(mask==1)
                
                mask_bool = (zmask>0)
                self.till_mask = np.zeros(self.mask.shape, dtype=bool) 
                self.till_mask[mask_bool*core_nodes] = 1
            # 
            
            # Create dictionary to contain metrics
            self.metric = {}
        
        else:
            with open(from_file, 'r') as f:
                metrics = load(f)
                self.modern_dem_name = metrics.pop('Topo file')

                self.metric = metrics
    
            fn_split = from_file.split('.')
            fn_split[-1] = 'chi'
            fn_split.append('txt')
            chi_filename = '.'.join(fn_split)
            self.density_chi = np.loadtxt(chi_filename)
예제 #2
0
def test_functions_with_Hex():
    mg = HexModelGrid(10, 10)
    z = mg.add_zeros("node", "topographic__elevation")
    z += mg.x_of_node + mg.y_of_node
    fa = FlowAccumulator(mg)
    fa.run_one_step()

    ch = ChiFinder(mg, min_drainage_area=1.0, reference_concavity=1.0)
    ch.calculate_chi()
예제 #3
0
def test_route_to_multiple_error_raised():
    mg = RasterModelGrid((10, 10))
    z = mg.add_zeros("node", "topographic__elevation")
    z += mg.x_of_node + mg.y_of_node
    fa = FlowAccumulator(mg, flow_director="MFD")
    fa.run_one_step()

    with pytest.raises(NotImplementedError):
        ChiFinder(mg, min_drainage_area=1., reference_concavity=1.)
예제 #4
0
def test_bad_reference_area():
    mg = RasterModelGrid((10, 10))
    z = mg.add_zeros("node", "topographic__elevation")
    z += mg.x_of_node + mg.y_of_node
    fa = FlowAccumulator(mg)
    fa.run_one_step()

    with pytest.raises(ValueError):
        ChiFinder(mg, min_drainage_area=1.0, reference_area=-1)
예제 #5
0
#%%
input_file = glob.glob(os.path.join(os.sep, *(work_dir+['model**', '**', 'inputs_template.txt'])))[0]
with open(input_file, 'r') as f:
    inputs = yaml.load(f)

outlet_id = inputs['outlet_id']
# observed_topography
observed_topo_file_name = inputs['modern_dem_name']
(grid, z) = read_esri_ascii(observed_topo_file_name, name='topographic__elevation', halo=1)
grid.set_watershed_boundary_condition_outlet_id(inputs['outlet_id'], z, nodata_value=-9999)
fa = FlowAccumulator(grid,
                     flow_director='D8',
                     depression_finder = 'DepressionFinderAndRouter')
fa.run_one_step()
#hs = grid.calc_hillshade_at_node()
ch = ChiFinder(grid, min_drainage_area=10*grid.dx**2)
ch.calculate_chi()

# initial condition topography
initial_topo_file_name = inputs['DEM_filename']
(igrid, iz) = read_esri_ascii(initial_topo_file_name, name='topographic__elevation', halo=1)
igrid.set_watershed_boundary_condition_outlet_id(inputs['outlet_id'], iz, nodata_value=-9999)
ifa = FlowAccumulator(igrid,
                      flow_director='D8',
                      depression_finder = 'DepressionFinderAndRouter')
ifa.run_one_step()
#ihs = grid.calc_hillshade_at_node()
ich = ChiFinder(igrid, min_drainage_area=10*grid.dx**2)
ich.calculate_chi()

#%%
예제 #6
0
z[blockuplift_nodes] += 100.0
z[blockuplift_nodes] += np.random.rand(len(blockuplift_nodes[0])) * 10

pl.figure()
imshow_grid(mg,
            'topographic__elevation',
            plot_name='Initial Topography of Uplifted block',
            allow_colorbar=True)

ld = LinearDiffuser(mg, linear_diffusivity=0.01)
fr = FlowAccumulator(mg, flow_director='D8')
fse = FastscapeEroder(mg, K_sp=5e-5, m_sp=0.5, n_sp=1.)
sf = SinkFiller(mg, routing='D8')

## instantiate helper components
chif = ChiFinder(mg)
steepnessf = SteepnessFinder(mg, reference_concavity=0.5)

## Set some variables
rock_up_rate = 1e-3  #m/yr
dt = 1000  # yr
rock_up_len = dt * rock_up_rate  # m
nr_time_steps = 500
## Time loop where evolution happens
for i in range(nr_time_steps):
    z[mg.core_nodes] += rock_up_len  #uplift only the core nodes
    ld.run_one_step(dt)  #linear diffusion happens.
    sf.run_one_step()  #sink filling happens, time step not needed
    fr.run_one_step()  #flow routing happens, time step not needed
    fse.run_one_step(dt)  #fluvial incision happens
    ## optional print statement
예제 #7
0
class MetricCalculator(object):
    """Calculator for topographic metrics used in sensitivity analysis and
    model evaluation."""
    
    def __init__(self, modern_dem_name, outlet_id, chi_mask_dem_name=None, from_file=None):
        """Initialize MetricCalculator with names of postglacial and modern
        DEMs."""


        if from_file is None:

            # Read and remember the modern DEM (whether data or model)
            (self.grid, self.z) = self.read_topography(modern_dem_name)
            #print self.grid.x_of_node
    
            self.grid.set_watershed_boundary_condition_outlet_id(outlet_id,
                                                                 self.z, nodata_value=-9999)
    
            # Instantiate and run a FlowRouter and lake filler, so we get
            # drainage area for cumulative-area statistic, and also fields for chi.
            fr = FlowRouter(self.grid)
            dfr = DepressionFinderAndRouter(self.grid)
            fr.route_flow()
            dfr.map_depressions()
    
            # Remember modern drainage area grid
            self.area = self.grid.at_node['drainage_area']
    
            # Instantiate a ChiFinder for chi-index
            self.chi_finder = ChiFinder(self.grid, min_drainage_area=10000.,
                                        reference_concavity=0.5)
            
            core_nodes = np.zeros(self.area.shape, dtype=bool)
            core_nodes[self.grid.core_nodes] = True
            # Read and remember the MASK, if provided
            if chi_mask_dem_name is None:
                 self.mask = (self.area>1e5)
                 self.till_mask = np.zeros(self.mask.shape, dtype=bool) 
                 self.till_mask[self.grid.core_nodes] = 1
            else:
                (self.mask_grid, zmask) = self.read_topography(chi_mask_dem_name)
                mask = (zmask>0)*1
                self.mask = (self.area>1e5)*(mask==1)
                
                mask_bool = (zmask>0)
                self.till_mask = np.zeros(self.mask.shape, dtype=bool) 
                self.till_mask[mask_bool*core_nodes] = 1
            # 
            
            # Create dictionary to contain metrics
            self.metric = {}
        
        else:
            with open(from_file, 'r') as f:
                metrics = load(f)
                self.modern_dem_name = metrics.pop('Topo file')

                self.metric = metrics
    
            fn_split = from_file.split('.')
            fn_split[-1] = 'chi'
            fn_split.append('txt')
            chi_filename = '.'.join(fn_split)
            self.density_chi = np.loadtxt(chi_filename)

    def read_topography(self, topo_file_name):
        """Read and return topography from file, as a Landlab grid and field.
        
        Along the way, process the topography to identify the watershed."""
        try:
            (grid, z) = read_esri_ascii(topo_file_name,
                                        name='topographic__elevation',
                                        halo=1)
        except:
            grid = read_netcdf(topo_file_name)
            z = grid.at_node['topographic__elevation']
        
        return (grid, z)

    def calc_hyps_integral(self):
        """Calculate and return hypsometric integral of modern topo."""

        # Get just those elevation values that are within the watershed
        wshed_elevs = self.z[self.grid.core_nodes]

        # Get min and max
        min_elev = np.amin(wshed_elevs)
        max_elev = np.amax(wshed_elevs)

        # Calc and return hyps int
        return np.mean(wshed_elevs - min_elev) / (max_elev - min_elev)

    def calc_mean_and_var_gradient(self):
        """Calculate and return the mean and variance of gradients.
        """
        # Calculate the gradient at links
        grad = self.grid.calc_grad_at_link(self.z)
        
        # Find IDs of nodes that have four active links
        active_links_at_node = (self.grid.links_at_node * 
                                np.abs(self.grid.active_link_dirs_at_node))
        nodes = np.where(np.amin(active_links_at_node, axis=1) > 0)
        
        # Get the x and y components of slope at each of these.
        # We're assuming we're dealing with raster grids, in which columns
        # 0 and 2 of the links_at_node array contain east-west links, and
        # columns 1 and 3 contain north-south links. We take the average in
        # each direction.
        grad_x = 0.5 * (grad[self.grid.links_at_node[nodes, 0]] +
                        grad[self.grid.links_at_node[nodes, 2]])
        grad_y = 0.5 * (grad[self.grid.links_at_node[nodes, 1]] +
                        grad[self.grid.links_at_node[nodes, 3]])
        
        # Combine them to get the slope magnitude
        slope = np.sqrt(grad_x**2 + grad_y**2)
        
        # Return the mean and variance
        return np.mean(slope), np.var(slope)
        
    def calc_mean_and_var_gradient_chi_area(self):
        """Calculate and return the mean and variance of gradients in chi area.
        """
        # Calculate the gradient at links
        grad = self.grid.calc_grad_at_link(self.z)
        
        # Find IDs of nodes that have four active links
        active_links_at_node = (self.grid.links_at_node * 
                                np.abs(self.grid.active_link_dirs_at_node))
        
        nodes = np.where((np.amin(active_links_at_node, axis=1) > 0) & self.till_mask)
        
        # Get the x and y components of slope at each of these.
        # We're assuming we're dealing with raster grids, in which columns
        # 0 and 2 of the links_at_node array contain east-west links, and
        # columns 1 and 3 contain north-south links. We take the average in
        # each direction.
        grad_x = 0.5 * (grad[self.grid.links_at_node[nodes, 0]] +
                        grad[self.grid.links_at_node[nodes, 2]])
        grad_y = 0.5 * (grad[self.grid.links_at_node[nodes, 1]] +
                        grad[self.grid.links_at_node[nodes, 3]])
        
        # Combine them to get the slope magnitude
        slope = np.sqrt(grad_x**2 + grad_y**2)
        
        # Return the mean and variance
        return np.mean(slope), np.var(slope)
        
    def calc_chi_index(self):
        """Calculate and return coefficients of chi plot."""
        self.chi_finder.calculate_chi()
        return self.chi_finder.best_fit_chi_elevation_gradient_and_intercept()

    def calculate_channel_chi_distribution(self):
        """Calculate and return Chi distribution."""
        # set the x and y bin edges for consistency. 
        xedges = np.arange(1160, 2000, 5)
        yedges = np.arange(0, 6.2, 0.2)
        
        #calculate on DEM
        self.density_chi, xedges, yedges = np.histogram2d(self.z[self.mask], 
                                                     self.grid.at_node['channel__chi_index'][self.mask], 
                                                     bins=(xedges,yedges), normed=True)
        return self.density_chi
    
    def calc_number_source_nodes(self, factor=1.0):
        """Calculate and return number of nodes below a given threshold.
        
        Parameters
            factor, float, default
            
        Threshold given by cell area x factor
        """
        single_cell_area = factor*(self.grid.dx**2.0)
        
        source_nodes = np.sum(self.area[self.grid.core_nodes] <= single_cell_area)
        
        return source_nodes                                                
    
    def calculate_metrics(self):
        """Calculate and store each metric."""
        
        # characterize the lowest portions of the area distribution
        self.metric['one_cell_nodes'] = self.calc_number_source_nodes(factor=1.0)
        self.metric['two_cell_nodes'] = self.calc_number_source_nodes(factor=2.0)
        self.metric['three_cell_nodes'] = self.calc_number_source_nodes(factor=3.0)
        self.metric['four_cell_nodes'] = self.calc_number_source_nodes(factor=4.0)
    
        # uppermost portion of the distribution
        self.metric['cumarea95'] = np.percentile(self.area[self.grid.core_nodes], 95)
        self.metric['cumarea96'] = np.percentile(self.area[self.grid.core_nodes], 96)
        self.metric['cumarea97'] = np.percentile(self.area[self.grid.core_nodes], 97)
        self.metric['cumarea98'] = np.percentile(self.area[self.grid.core_nodes], 98)
        self.metric['cumarea99'] = np.percentile(self.area[self.grid.core_nodes], 99)
        
        # hypsometric integral
        self.metric['hypsometric_integral'] = self.calc_hyps_integral()
        
        # mean and variance of elevation 
        self.metric['mean_elevation'] = np.mean(self.z[self.grid.core_nodes])
        self.metric['var_elevation'] = np.var(self.z[self.grid.core_nodes])
        
        self.metric['mean_elevation_chi_area'] = np.mean(self.z[self.till_mask])
        self.metric['var_elevation_chi_area'] = np.var(self.z[self.till_mask])
        
        # mean and variance of slope
        (mean_slope, var_slope) = self.calc_mean_and_var_gradient()
        self.metric['mean_gradient'] = mean_slope
        self.metric['var_gradient'] = var_slope
        
        (mean_slope_chi, var_slope_chi) = self.calc_mean_and_var_gradient_chi_area()
        self.metric['mean_gradient_chi_area'] = mean_slope_chi
        self.metric['var_gradient_chi_area'] = var_slope_chi
        
        # distribution of elevations,
        # these percentiles chosen to characterize the shape of the modern distribution
        self.metric['elev02'] = np.percentile(self.z[self.grid.core_nodes], 2)
        self.metric['elev08'] = np.percentile(self.z[self.grid.core_nodes], 8)
        self.metric['elev23'] = np.percentile(self.z[self.grid.core_nodes], 23)
        self.metric['elev30'] = np.percentile(self.z[self.grid.core_nodes], 30)
        self.metric['elev36'] = np.percentile(self.z[self.grid.core_nodes], 36)
        self.metric['elev50'] = np.percentile(self.z[self.grid.core_nodes], 50)
        self.metric['elev75'] = np.percentile(self.z[self.grid.core_nodes], 75)
        self.metric['elev85'] = np.percentile(self.z[self.grid.core_nodes], 85)
        self.metric['elev90'] = np.percentile(self.z[self.grid.core_nodes], 90)
        self.metric['elev96'] = np.percentile(self.z[self.grid.core_nodes], 96)
        self.metric['elev100'] = np.percentile(self.z[self.grid.core_nodes], 100)
        
        # Chi-related metrics
        (chi_grad, chi_intercept) = self.calc_chi_index()
        self.metric['chi_gradient'] = chi_grad
        self.metric['chi_intercept'] = chi_intercept
        self.density_chi = self.calculate_channel_chi_distribution()

        return self.metric


    def save_metrics(self, topofile, filename='metrics.txt'):
        """Write metrics to file."""
        outfile = open(filename, 'w')
        outfile.write('Topo file : ' + topofile + '\n')
        
        sort_keys = sorted(list(self.metric.keys()))
        
        for m in sort_keys:
            outfile.write(m + ': ' + str(self.metric[m])  + '\n')
        outfile.close()
        
        if (filename=='metrics.txt'):
            chi_filename = 'metrics.chi.txt'
        else:
            fn_split = filename.split('.')
            fn_split[-1] = 'chi'
            fn_split.append('txt')
            chi_filename = '.'.join(fn_split)
    
        np.savetxt(chi_filename, self.density_chi,fmt='%f')
## Make a grid that is 100 by 100 with dx=dy=100. m
rmg1 = RasterModelGrid((100, 100), 100.)
## Add elevation field to the grid.
z1 = rmg1.add_ones('node', 'topographic__elevation')
## Add noise to initial landscape
z1[rmg1.core_nodes] += np.random.rand(len(rmg1.core_nodes))

## Instantiate process components
ld1 = LinearDiffuser(rmg1, linear_diffusivity=0.1)
fr1 = FlowRouter(rmg1, method='D8')
fse1 = FastscapeEroder(rmg1, K_sp=1e-5, m_sp=0.5, n_sp=1.)
sf1 = SinkFiller(rmg1, routing='D8')

## instantiate helper components
chif = ChiFinder(rmg1)
steepf = SteepnessFinder(rmg1)

## Set some variables
rock_up_rate = 1e-3  #m/yr
dt = 1000  # yr
rock_up_len = dt * rock_up_rate  # m

## Time loop where evolution happens
for i in range(500):
    z1[rmg1.core_nodes] += rock_up_len  #uplift only the core nodes
    ld1.run_one_step(dt)  #linear diffusion happens.
    sf1.run_one_step()  #sink filling happens, time step not needed
    fr1.run_one_step()  #flow routing happens, time step not needed
    fse1.run_one_step(dt)  #fluvial incision happens
    ## optional print statement
예제 #9
0
    observed_topo_file_name = glob.glob(path + os.sep + 'modern' + os.sep +
                                        '*.txt')[0]
    outlet_id = df[site]['outlet_id']
    (grid, z) = read_esri_ascii(observed_topo_file_name,
                                name='topographic__elevation',
                                halo=1)
    grid.set_watershed_boundary_condition_outlet_id(outlet_id,
                                                    z,
                                                    nodata_value=-9999)
    fa = FlowAccumulator(grid,
                         flow_director='D8',
                         depression_finder='DepressionFinderAndRouter')
    fa.run_one_step()
    area = grid.dx**2

    ch = ChiFinder(grid, min_drainage_area=0.0)
    ch.calculate_chi()

    # initial condition topography
    initial_topo_file_name = np.sort(
        glob.glob(path + os.sep + 'initial_conditions' + os.sep + '*.txt'))[0]

    (igrid, iz) = read_esri_ascii(initial_topo_file_name,
                                  name='topographic__elevation',
                                  halo=1)
    igrid.set_watershed_boundary_condition_outlet_id(outlet_id,
                                                     iz,
                                                     nodata_value=-9999)
    ifa = FlowAccumulator(igrid,
                          flow_director='D8',
                          depression_finder='DepressionFinderAndRouter')
#figure the rest out yourself!
#use log binning or the following approach: https://github.com/keflavich/plfit
figure('final slope-area plot')
loglog(a,g, '.')
xlabel('Drainage area (k**2)')
ylabel('Local slope (m/m)')

#%% Steepness finder and Chi plots
fd = FlowDirectorSteepest(mg, 'topographic__elevation')
fd.run_one_step()
fa = FlowAccumulator(mg, 'topographic__elevation', flow_director='FlowDirectorSteepest')
fa.run_one_step()
sf = SteepnessFinder(mg, reference_concavity=0.45, 
                     min_drainage_area=1e3)
sf.calculate_steepnesses()
cf = ChiFinder(mg, reference_concavity=0.45, min_drainage_area=1.e3, reference_area=1., use_true_dx=False)
cf.calculate_chi()

imshow_grid(mg, 'channel__chi_index', plot_name='Channel steepness (theta = 0.45)', var_units='norm. steepness (m^0.9)', cmap='hot', limits=(0,300))

imshow_grid(mg, 'channel__chi_index', 
            plot_name='Chi finder', var_units='norm. steepness (m^0.9)', cmap='hot', limits=(0,300))

scatter(mg.x_of_node[da_outlet_idx], mg.y_of_node[da_outlet_idx], c='red', marker='x', label='outlet')
#plt.scatter(mg.x_of_node[endpoints_idx], mg.y_of_node[endpoints_idx], c='white', marker='.', label='drainage head')

ax = fig1.add_subplot(2,3,4)
imshow_grid(mg, 'channel__chi_index', plot_name='Channel Chi index (theta = 0.45)', var_units='chi ()', cmap='hot', limits=(np.floor(np.min(mg.at_node['channel__chi_index'][mg.at_node['channel__chi_index'] >0])), np.ceil(np.max(mg.at_node['channel__chi_index'][mg.at_node['channel__chi_index'] >0]))))
plt.scatter(mg.x_of_node[da_outlet_idx], mg.y_of_node[da_outlet_idx], c='red', marker='x', label='outlet')