Esempio n. 1
0
def give_ibound(line, number_of_layers, nx, ny, xmin, xmax, ymin, ymax):
    """
    Function that finds if cells of a given grid are inside a given polygon.
    Returns an IBOUND - 2D array object, in which inner cells have values of '1' and outer have values of '0'
    Inner points defined using the matplotlib's mplPath function    
    """
    # Convert input to floats
    xmin = float(xmin)
    xmax = float(xmax)
    ymin = float(ymin)
    ymax = float(ymax)
    # Application of the 'line_area_intersect function to define cells intersected by the polygon
    area_line_cols, area_line_rows = intersector.line_area_intersect(line = line, xmax = xmax, xmin = xmin, ymax = ymax, ymin = ymin, nx = nx, ny = ny)
    # Domain definition
    ibound = np.zeros((int(number_of_layers), ny, nx), dtype=np.int32)
    x = np.linspace(xmin, xmax, nx)
    y = np.linspace(ymin, ymax, ny)
    
    bbPath = mplPath.Path(np.array(line[:-1]))
    for i in range(ny):
        for j in range(nx):
            cell_is_inside = bbPath.contains_point((x[j], y[i]))
            if cell_is_inside:
                ibound[:, i, j] = 1
    for i in range(len(area_line_rows)):
        ibound[:, area_line_rows[i], area_line_cols[i]] = 1
    # Rotete the array to 180 degrees to fit flopy IBOUND convention
    return np.rot90(ibound, 2)
Esempio n. 2
0
    def give_ibound(self, line, number_of_layers):
        """

        """
        self.line = line
        #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        self.line_cols, self.line_rows = intersector.line_area_intersect(line = self.line, xmax = self.xmax, xmin = self.xmin, ymax = self.ymax, ymin = self.ymin, nx = self.nx, ny = self.ny)
        #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        
        area_line_rows = np.ones(len(self.line_rows), dtype = np.int32) * (self.ny - 1) - np.array(self.line_rows)
        area_line_cols = np.array(self.line_cols)
        empty_rows = []
        empty_cols = []
        for i in range(self.ny):
            empty_cols += range(self.ny)
            empty_rows += [i] * self.nx
            #Lists of cols and rows to be removed from the entire list:
        rem_cols = []
        rem_rows = []
        for i in range(len(area_line_cols)):
            rem_cols.append(area_line_rows[i] * self.nx + area_line_cols[i])
            rem_rows.append(area_line_rows[i] * self.nx + area_line_cols[i])
        rem_rows = list(set(rem_rows))
        rem_cols = list(set(rem_cols))
        for i in sorted(rem_cols, reverse=True):
            del empty_cols[i]
        for i in sorted(rem_rows, reverse=True):
            del empty_rows[i]

        inner_cell_idx = []
        for i in range(len(empty_rows)):
            x = (self.xmin + empty_cols[i] * self.dx) + self.dx/2
            y = (self.ymax - empty_rows[i] * self.dy) - self.dy/2
            crossed = 0
            for j in range(len(self.line) - 1):
                bw1 =  y < self.line[j][1] and y > self.line[j+1][1]
                bw2 =  y > self.line[j][1] and y < self.line[j+1][1]
                if (bw1 or bw2) and x < self.line[j][0] - ((self.line[j][1] - y)/(self.line[j][1] - self.line[j+1][1]) * (self.line[j][0] - self.line[j+1][0])):
                    crossed += 1
            if crossed % 2 == 0:
                inner_cell_idx.append(0)
            else:
                inner_cell_idx.append(1)

        self.ibound = np.zeros((int(number_of_layers), self.nx, self.ny), dtype=np.int32)
        for i in range(len(empty_rows)):
            self.ibound[:, empty_rows[i], empty_cols[i]] = inner_cell_idx[i]
        for i in range(len(area_line_rows)):
            self.ibound[:, area_line_rows[i], area_line_cols[i]] = 1

        return self.ibound
Esempio n. 3
0
def give_ibound(line,
                number_of_layers,
                nx,
                ny,
                xmin,
                xmax,
                ymin,
                ymax,
                boundary_value=1):
    """
    Function that finds if cells of a given grid are inside a given polygon.
    Returns an IBOUND - 2D array object, in which inner cells have values of '1' and outer have values of '0'
    Inner points defined using the matplotlib's mplPath function
    """
    # Convert input to floats
    #    print line
    xmin = float(xmin)
    xmax = float(xmax)
    ymin = float(ymin)
    ymax = float(ymax)
    # Application of the 'line_area_intersect function to define cells intersected by the polygon
    area_line_cols, area_line_rows = intersector.line_area_intersect(line=line,
                                                                     xmax=xmax,
                                                                     xmin=xmin,
                                                                     ymax=ymax,
                                                                     ymin=ymin,
                                                                     nx=nx,
                                                                     ny=ny)
    # Domain definition
    ibound = np.zeros((int(number_of_layers), ny, nx), dtype=np.int32)
    x = np.linspace(xmin, xmax, nx)
    y = np.linspace(ymin, ymax, ny)

    bbPath = mplPath.Path(np.array(line[:-1]))

    for i in range(ny):
        for j in range(nx):
            cell_is_inside = bbPath.contains_point((x[j], y[i]))
            if cell_is_inside:
                ibound[:, i, j] = 1
#    print len(area_line_rows)
    for i in range(len(area_line_rows)):
        ibound[:, area_line_rows[i], area_line_cols[i]] = boundary_value
    # Rotete the array to 180 degrees to fit flopy IBOUND convention
    return np.rot90(ibound, 2)
Esempio n. 4
0
 def __init__(self, bo_ids, op, area):
     self.area = area        
     self.bo = bo_ids
     self.op = op
     self.values_list = op.values_list
     self.line = []
     for i in self.bo:
         cur.execute("""with dump as (select (st_dumppoints(geometry)).* from boundaries where id = %s) select st_x(geom) from dump;""", [i])
         xlist = cur.fetchall()
         cur.execute("""with dump as (select (st_dumppoints(geometry)).* from boundaries where id = %s) select st_y(geom) from dump;""", [i])
         ylist = cur.fetchall()
         for i in xlist:
             self.line.append(list(i))
         for i in range(len(ylist)):
             self.line[i] += ylist[i]
     
     self.SPD_multi = {}
     
     #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     self.line_cols, self.line_rows = intersector.line_area_intersect(line = self.line, xmax = self.area.xmax, xmin = self.area.xmin, ymax = self.area.ymax, ymin = self.area.ymin, nx = self.area.nx, ny = self.area.ny)
     #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     self.vals = []
Esempio n. 5
0
def give_SPD(points, point_vals, line, stress_period_list, interract_layers, xmax, xmin, ymax, ymin, nx, ny, layers_botm = None):
    """
    Function interpolating given point values along on a grid along given line 
    and returning Stress Period Data dictionary object
    """
    # Definition of the cells intersected by a line boundary and by observation points
    line_cols, line_rows = intersector.line_area_intersect(line, xmax, xmin, ymax, ymin, nx, ny)
    point_cols, point_rows = [],[]

    # Columns and rows of the observation points
    for point in points:
        point_cols.append(int((point[0] - xmin)/(xmax - xmin) * nx)  if point[0] < xmax else nx - 1)
        point_rows.append(int((point[1] - ymin)/(ymax - ymin) * ny)  if point[1] < ymax else ny - 1)

    # Create a list of line cell values in which cells under points inherit their values and others beckome None
    list_of_values = []
    for period in stress_period_list:
        list_of_values_single_timestep = []
        for line_idx in range(len(line_cols)):
            for point_idx in range(len(point_cols)):
                if line_cols[line_idx] == point_cols[point_idx] and line_rows[line_idx] == point_rows[point_idx]:
                    list_of_values_single_timestep.append(point_vals[period][point_idx])
                else:
                    list_of_values_single_timestep.append(None)
        
        # Fill the None values with distance - weighted average of closest not-None cells
        for i in range(len(list_of_values_single_timestep)):
            if list_of_values_single_timestep[i] is None:
                j = 1
                l = 1
                k = list_of_values_single_timestep[i] # Backward value
                m = list_of_values_single_timestep[i] # Forward value
                while m is None:
                    m = list_of_values_single_timestep[i - l] 
                    l += 1
                while k is None:
                    k = list_of_values_single_timestep[i + j] if (i + j) < len(list_of_values_single_timestep) else list_of_values_single_timestep[(i + j - len(list_of_values_single_timestep))]
                    j += 1
                # Interpolating values using IDW method
                list_of_values_single_timestep[i] = (m * 1./(l-1) + k * 1./(j-1))/(1./(j-1) + 1./(l-1))
        # Write resulting values lists for every time step
        list_of_values.append(list_of_values_single_timestep)
    
    # Reversing rows upside down due to flopy error
    line_rows_reversed = [(ny-1) - i for i in line_rows]

    # Checking if the boundary cells will become dry due to low specified head. If so, layer removed from the interracted layers list
    for idx, layer in enumerate(layers_botm):
        for period in stress_period_list:
            for i in range(len(line_cols)):
                cell_botm_elevation = layer[line_rows[i], line_cols[i]] if type(layer) == list else layer
                if list_of_values[period][i] <= cell_botm_elevation:
                    del interract_layers[idx]
                    break
            break

    
    # Writing CHD Stress Period Data dictionary    
    CHD_stress_period_data = {}
    for period in stress_period_list:
        SPD_single = []
        for lay in interract_layers:
            for i in range(len(line_cols)):
                # For periods except the last one head at begining and end vary
                if period != stress_period_list[-1]:
                    SPD_single.append([lay, line_rows_reversed[i], line_cols[i], list_of_values[period][i], list_of_values[period + 1][i]])
                else:
                    SPD_single.append([lay, line_rows_reversed[i], line_cols[i], list_of_values[period][i], list_of_values[period][i]])
                CHD_stress_period_data[period] = SPD_single
    
    return CHD_stress_period_data
def give_SPD(points,
             point_vals,
             line,
             stress_period_list,
             interract_layers,
             xmax,
             xmin,
             ymax,
             ymin,
             nx,
             ny,
             boundary_type,
             layers_botm=None,
             strt_head_mode='simple'):
    """
    Function interpolating given point values along on a grid along given line
    and returning Stress Period Data dictionary object
    """
    strt_head_mode_options = ['warmed_up', 'simple']
    if strt_head_mode not in strt_head_mode_options:
        print 'given stress period data write mode option is not supported, should be either "warmed_up" or "simple"'
        return

    xmin = float(xmin)
    xmax = float(xmax)
    ymin = float(ymin)
    ymax = float(ymax)

    # Definition of the cells intersected by a line boundary and by observation points
    line_cols, line_rows = intersector.line_area_intersect(
        line, xmax, xmin, ymax, ymin, nx, ny)
    point_cols, point_rows = [], []
    # Reversing rows upside down due to flopy error
    line_rows_reversed = [(ny - 1) - i for i in line_rows]

    # Columns and rows of the observation points
    for point in points:
        point_cols.append(
            int((point[0] - xmin) / (xmax - xmin) *
                nx) if point[0] < xmax else nx - 1)
        point_rows.append(
            int((point[1] - ymin) / (ymax - ymin) *
                ny) if point[1] < ymax else ny - 1)

    # Create a list of line cell values in which cells under points inherit their values and others beckome None
    def interpolate_property(stress_period_list, line_cols, line_rows,
                             point_cols, point_rows, point_vals):
        list_of_values = []
        for period in stress_period_list:
            list_of_values_single_timestep = []
            for line_idx in range(len(line_cols)):
                for point_idx in range(len(point_cols)):
                    if line_cols[line_idx] == point_cols[
                            point_idx] and line_rows[line_idx] == point_rows[
                                point_idx]:
                        list_of_values_single_timestep.append(
                            point_vals[point_idx][period])
                    else:
                        list_of_values_single_timestep.append(None)

            # Fill the None values with distance - weighted average of closest not-None cells
            for i in range(len(list_of_values_single_timestep)):
                if list_of_values_single_timestep[i] is None:
                    j = 1
                    l = 1
                    k = list_of_values_single_timestep[i]  # Backward value
                    m = list_of_values_single_timestep[i]  # Forward value
                    while m is None:
                        m = list_of_values_single_timestep[i - l]
                        l += 1
                    while k is None:
                        k = list_of_values_single_timestep[i + j] if (
                            i +
                            j) < len(list_of_values_single_timestep
                                     ) else list_of_values_single_timestep[(
                                         i + j -
                                         len(list_of_values_single_timestep))]
                        j += 1
                    # Interpolating values using IDW method
                    list_of_values_single_timestep[i] = (
                        m * 1. / (l - 1) + k * 1. /
                        (j - 1)) / (1. / (j - 1) + 1. / (l - 1))
            # Write resulting values lists for every time step
            list_of_values.append(list_of_values_single_timestep)
        return list_of_values

    if 'hh' in point_vals:
        point_head_vals = point_vals['hh']
        list_of_head_values = interpolate_property(stress_period_list,
                                                   line_cols, line_rows,
                                                   point_cols, point_rows,
                                                   point_head_vals)
        # Checking if the boundary head cells will become dry due to low specified head.
        # If so, layer removed from the interracted layers list
        n_removed_layers = 0
        for idx, layer in enumerate(layers_botm):
            for period in stress_period_list:
                for i in range(len(line_cols)):
                    cell_botm_elevation = layer[line_rows_reversed[i]][
                        line_cols[i]] if type(layer) == list else layer
                    if list_of_head_values[period][i] <= cell_botm_elevation:
                        del interract_layers[idx - n_removed_layers]
                        n_removed_layers += 1
                        break
                break

    if 'rs' in point_vals:
        point_stage_vals = point_vals['rs']
        list_of_stage_values = interpolate_property(stress_period_list,
                                                    line_cols, line_rows,
                                                    point_cols, point_rows,
                                                    point_stage_vals)
    if 'rbc' in point_vals:
        point_conductance_vals = point_vals['rbc']
        list_of_conductance_values = interpolate_property(
            stress_period_list, line_cols, line_rows, point_cols, point_rows,
            point_conductance_vals)
    if 'eb' in point_vals:
        point_elevation_vals = point_vals['eb']
        list_of_elevation_values = interpolate_property(
            stress_period_list, line_cols, line_rows, point_cols, point_rows,
            point_elevation_vals)

    if boundary_type == 'CHB':
        # Writing CHD Stress Period Data dictionary
        if strt_head_mode == 'simple':
            CHD_stress_period_data = {}
            for period in stress_period_list:
                SPD_single = []
                for lay in interract_layers:
                    for i in range(len(line_cols)):
                        # For periods except the last one head at begining and end vary
                        if period != stress_period_list[-1]:
                            SPD_single.append([
                                lay, line_rows_reversed[i], line_cols[i],
                                list_of_head_values[period][i],
                                list_of_head_values[period + 1][i]
                            ])
                        else:
                            SPD_single.append([
                                lay, line_rows_reversed[i], line_cols[i],
                                list_of_head_values[period][i],
                                list_of_head_values[period][i]
                            ])
                        CHD_stress_period_data[period] = SPD_single

        elif strt_head_mode == 'warmed_up':
            CHD_stress_period_data = {}
            for period in stress_period_list:
                SPD_single = []
                for lay in interract_layers:
                    for i in range(len(line_cols)):
                        # For periods except the last one head at begining and end vary
                        if period != stress_period_list[-1]:
                            SPD_single.append([
                                lay, line_rows_reversed[i], line_cols[i],
                                list_of_head_values[period][i],
                                list_of_head_values[period + 1][i]
                            ])
                        else:
                            SPD_single.append([
                                lay, line_rows_reversed[i], line_cols[i],
                                list_of_head_values[period][i],
                                list_of_head_values[period][i]
                            ])
                        if len(CHD_stress_period_data) == 0:
                            CHD_stress_period_data[period] = SPD_single
                        CHD_stress_period_data[period + 1] = SPD_single
        else:
            print 'given strt_mode is not supported'
            return

    elif boundary_type == 'RIV':
        if strt_head_mode == 'simple':
            CHD_stress_period_data = {}
            for period in stress_period_list:
                SPD_single = []
                for i in range(len(line_cols)):
                    # For periods except the last one head at begining and end vary
                    SPD_single.append([
                        0, line_rows_reversed[i], line_cols[i],
                        list_of_stage_values[period][i],
                        list_of_conductance_values[period][i],
                        list_of_elevation_values[period][i]
                    ])
                    CHD_stress_period_data[period] = SPD_single

        elif strt_head_mode == 'warmed_up':
            CHD_stress_period_data = {}
            for period in stress_period_list:
                SPD_single = []
                for i in range(len(line_cols)):
                    # For periods except the last one head at begining and end vary
                    SPD_single.append([
                        0, line_rows_reversed[i], line_cols[i],
                        list_of_stage_values[period][i],
                        list_of_conductance_values[period][i],
                        list_of_elevation_values[period][i]
                    ])
                    if len(CHD_stress_period_data) == 0:
                        CHD_stress_period_data[period] = SPD_single
                    CHD_stress_period_data[period + 1] = SPD_single

        else:
            print 'given strt_mode is not supported'
            return
#    for i in CHD_stress_period_data:
#        print len(CHD_stress_period_data[i])

    return CHD_stress_period_data
def give_SPD(points, point_vals, line, stress_period_list, interract_layers, xmax, xmin, ymax, ymin, nx, ny, boundary_type, layers_botm = None, strt_head_mode = 'simple'):
    """
    Function interpolating given point values along on a grid along given line
    and returning Stress Period Data dictionary object
    """
    strt_head_mode_options = ['warmed_up', 'simple']
    if strt_head_mode not in strt_head_mode_options:
        print 'given stress period data write mode option is not supported, should be either "warmed_up" or "simple"'
        return
        
    xmin = float(xmin)
    xmax = float(xmax)
    ymin = float(ymin)
    ymax = float(ymax)
    
    # Definition of the cells intersected by a line boundary and by observation points
    line_cols, line_rows = intersector.line_area_intersect(line, xmax, xmin, ymax, ymin, nx, ny)
    point_cols, point_rows = [],[]
    # Reversing rows upside down due to flopy error
    line_rows_reversed = [(ny-1) - i for i in line_rows]

    # Columns and rows of the observation points
    for point in points:
        point_cols.append(int((point[0] - xmin)/(xmax - xmin) * nx)  if point[0] < xmax else nx - 1)
        point_rows.append(int((point[1] - ymin)/(ymax - ymin) * ny)  if point[1] < ymax else ny - 1)

    # Create a list of line cell values in which cells under points inherit their values and others beckome None
    def interpolate_property(stress_period_list,line_cols,line_rows,point_cols,point_rows,point_vals):        
        list_of_values = []
        for period in stress_period_list:
            list_of_values_single_timestep = []
            for line_idx in range(len(line_cols)):
                for point_idx in range(len(point_cols)):
                    if line_cols[line_idx] == point_cols[point_idx] and line_rows[line_idx] == point_rows[point_idx]:
                        list_of_values_single_timestep.append(point_vals[point_idx][period])
                    else:
                        list_of_values_single_timestep.append(None)
    
            # Fill the None values with distance - weighted average of closest not-None cells
            for i in range(len(list_of_values_single_timestep)):
                if list_of_values_single_timestep[i] is None:
                    j = 1
                    l = 1
                    k = list_of_values_single_timestep[i] # Backward value
                    m = list_of_values_single_timestep[i] # Forward value
                    while m is None:
                        m = list_of_values_single_timestep[i - l]
                        l += 1
                    while k is None:
                        k = list_of_values_single_timestep[i + j] if (i + j) < len(list_of_values_single_timestep) else list_of_values_single_timestep[(i + j - len(list_of_values_single_timestep))]
                        j += 1
                    # Interpolating values using IDW method
                    list_of_values_single_timestep[i] = (m * 1./(l-1) + k * 1./(j-1))/(1./(j-1) + 1./(l-1))
            # Write resulting values lists for every time step
            list_of_values.append(list_of_values_single_timestep)
        return list_of_values
    
    if 'hh' in point_vals:
        point_head_vals = point_vals['hh']
        list_of_head_values = interpolate_property(stress_period_list,
                                                   line_cols,line_rows,
                                                   point_cols,point_rows,
                                                   point_head_vals)
        # Checking if the boundary head cells will become dry due to low specified head.
        # If so, layer removed from the interracted layers list
        n_removed_layers = 0                                      
        for idx, layer in enumerate(layers_botm):
            for period in stress_period_list:
                for i in range(len(line_cols)):
                    cell_botm_elevation = layer[line_rows_reversed[i]][line_cols[i]] if type(layer) == list else layer
                    if list_of_head_values[period][i] <= cell_botm_elevation:
                        del interract_layers[idx - n_removed_layers]
                        n_removed_layers += 1
                        break
                break

    if 'rs' in point_vals:
        point_stage_vals = point_vals['rs']
        list_of_stage_values = interpolate_property(stress_period_list,
                                                    line_cols,line_rows,
                                                    point_cols,point_rows,
                                                    point_stage_vals)
    if 'rbc' in point_vals:
        point_conductance_vals = point_vals['rbc']
        list_of_conductance_values = interpolate_property(stress_period_list,
                                                          line_cols,line_rows,
                                                          point_cols,point_rows,
                                                          point_conductance_vals)
    if 'eb' in point_vals:
        point_elevation_vals = point_vals['eb']
        list_of_elevation_values = interpolate_property(stress_period_list,
                                                        line_cols,line_rows,
                                                        point_cols,point_rows,
                                                        point_elevation_vals)


    if boundary_type == 'CHB':
        # Writing CHD Stress Period Data dictionary
        if strt_head_mode == 'simple':
            CHD_stress_period_data = {}
            for period in stress_period_list:
                SPD_single = []
                for lay in interract_layers:
                    for i in range(len(line_cols)):
                        # For periods except the last one head at begining and end vary
                        if period != stress_period_list[-1]:
                            SPD_single.append([lay, line_rows_reversed[i], line_cols[i],
                                               list_of_head_values[period][i],
                                               list_of_head_values[period + 1][i]])
                        else:
                            SPD_single.append([lay, line_rows_reversed[i],
                                               line_cols[i], list_of_head_values[period][i],
                                               list_of_head_values[period][i]])
                        CHD_stress_period_data[period] = SPD_single

        elif strt_head_mode == 'warmed_up':
            CHD_stress_period_data = {}
            for period in stress_period_list:
                SPD_single = []
                for lay in interract_layers:
                    for i in range(len(line_cols)):
                        # For periods except the last one head at begining and end vary
                        if period != stress_period_list[-1]:
                            SPD_single.append([lay, line_rows_reversed[i], line_cols[i],
                                               list_of_head_values[period][i],
                                               list_of_head_values[period + 1][i]])
                        else:
                            SPD_single.append([lay, line_rows_reversed[i], line_cols[i],
                                               list_of_head_values[period][i],
                                               list_of_head_values[period][i]])
                        if len(CHD_stress_period_data) == 0:
                            CHD_stress_period_data[period] = SPD_single
                        CHD_stress_period_data[period + 1] = SPD_single
        else:
            print 'given strt_mode is not supported'
            return
            
    elif boundary_type == 'RIV':
        if strt_head_mode == 'simple':
            CHD_stress_period_data = {}
            for period in stress_period_list:
                SPD_single = []
                for i in range(len(line_cols)):
                        # For periods except the last one head at begining and end vary
                        SPD_single.append([0, line_rows_reversed[i], line_cols[i],
                                           list_of_stage_values[period][i],
                                           list_of_conductance_values[period][i],
                                           list_of_elevation_values[period][i]])
                        CHD_stress_period_data[period] = SPD_single

        elif strt_head_mode == 'warmed_up':
            CHD_stress_period_data = {}
            for period in stress_period_list:
                SPD_single = []
                for i in range(len(line_cols)):
                    # For periods except the last one head at begining and end vary
                    SPD_single.append([0, line_rows_reversed[i], line_cols[i],
                                       list_of_stage_values[period][i],
                                       list_of_conductance_values[period][i],
                                       list_of_elevation_values[period][i]])
                    if len(CHD_stress_period_data) == 0:
                        CHD_stress_period_data[period] = SPD_single
                    CHD_stress_period_data[period + 1] = SPD_single
       
        else:
            print 'given strt_mode is not supported'
            return
#    for i in CHD_stress_period_data:
#        print len(CHD_stress_period_data[i])

    return CHD_stress_period_data