예제 #1
0
def mip_cavity_fields(var_name, roms_grid, roms_file, fesom_mesh_path,
                      fesom_file):

    # Name of each ice shelf
    shelf_names = [
        'Larsen D Ice Shelf', 'Larsen C Ice Shelf',
        'Wilkins & George VI & Stange Ice Shelves', 'Ronne-Filchner Ice Shelf',
        'Abbot Ice Shelf', 'Pine Island Glacier Ice Shelf',
        'Thwaites Ice Shelf', 'Dotson Ice Shelf', 'Getz Ice Shelf',
        'Nickerson Ice Shelf', 'Sulzberger Ice Shelf', 'Mertz Ice Shelf',
        'Totten & Moscow University Ice Shelves', 'Shackleton Ice Shelf',
        'West Ice Shelf', 'Amery Ice Shelf', 'Prince Harald Ice Shelf',
        'Baudouin & Borchgrevink Ice Shelves', 'Lazarev Ice Shelf',
        'Nivl Ice Shelf', 'Fimbul & Jelbart & Ekstrom Ice Shelves',
        'Brunt & Riiser-Larsen Ice Shelves', 'Ross Ice Shelf'
    ]
    # Beginnings of filenames for figures
    fig_heads = [
        'larsen_d', 'larsen_c', 'wilkins_georgevi_stange', 'ronne_filchner',
        'abbot', 'pig', 'thwaites', 'dotson', 'getz', 'nickerson',
        'sulzberger', 'mertz', 'totten_moscowuni', 'shackleton', 'west',
        'amery', 'prince_harald', 'baudouin_borchgrevink', 'lazarev', 'nivl',
        'fimbul_jelbart_ekstrom', 'brunt_riiser_larsen', 'ross'
    ]
    # Limits on longitude and latitude for each ice shelf
    # Note Ross crosses 180W=180E
    lon_min = [
        -62.67, -65.5, -79.17, -85, -104.17, -102.5, -108.33, -114.5, -135.67,
        -149.17, -155, 144, 115, 94.17, 80.83, 65, 33.83, 19, 12.9, 9.33,
        -10.05, -28.33, 158.33
    ]
    lon_max = [
        -59.33, -60, -66.67, -28.33, -88.83, -99.17, -103.33, -111.5, -114.33,
        -140, -145, 146.62, 123.33, 102.5, 89.17, 75, 37.67, 33.33, 16.17,
        12.88, 7.6, -10.33, -146.67
    ]
    lat_min = [
        -73.03, -69.35, -74.17, -83.5, -73.28, -75.5, -75.5, -75.33, -74.9,
        -76.42, -78, -67.83, -67.17, -66.67, -67.83, -73.67, -69.83, -71.67,
        -70.5, -70.75, -71.83, -76.33, -85
    ]
    lat_max = [
        -69.37, -66.13, -69.5, -74.67, -71.67, -74.17, -74.67, -73.67, -73,
        -75.17, -76.41, -66.67, -66.5, -64.83, -66.17, -68.33, -68.67, -68.33,
        -69.33, -69.83, -69.33, -71.5, -77
    ]
    num_shelves = len(shelf_names)

    # Constants
    sec_per_year = 365 * 24 * 3600
    deg2rad = pi / 180.0
    # Parameters for missing circle in ROMS grid
    lon_c = 50
    lat_c = -83
    radius = 10.1
    nbdry = -63 + 90
    # ROMS vertical grid parameters
    theta_s = 7.0
    theta_b = 2.0
    hc = 250
    N = 31
    # Number of bins in each direction for vector overlay
    num_bins = 50

    print 'Reading ROMS fields'
    if var_name == 'draft':
        id = Dataset(roms_grid, 'r')
    else:
        id = Dataset(roms_file, 'r')
    roms_lon = id.variables['lon_rho'][:, :]
    roms_lat = id.variables['lat_rho'][:, :]
    roms_mask = id.variables['mask_rho'][:, :]
    roms_zice = id.variables['zice'][:, :]
    if var_name == 'draft':
        # Switch signs
        roms_data = -1 * id.variables['zice'][:, :]
    elif var_name == 'melt':
        # Convert from m/s to m/y
        roms_data = id.variables['m'][0, :, :] * sec_per_year
    elif var_name == 'temp':
        # Bottom layer
        roms_data = id.variables['temp'][0, 0, :, :]
    elif var_name == 'salt':
        # Bottom layer
        roms_data = id.variables['salt'][0, 0, :, :]
    elif var_name in ['vsfc', 'vavg']:
        # Get angle from the grid file
        id2 = Dataset(roms_grid, 'r')
        angle = id2.variables['angle'][:, :]
        id2.close()
        if var_name == 'vsfc':
            # Read surface u and v
            u_tmp = id.variables['u'][0, -1, :, :]
            v_tmp = id.variables['v'][0, -1, :, :]
            # Interpolate to rho grid and unrotate
            u_rho, v_rho = rotate_vector_roms(u_tmp, v_tmp, angle)
        elif var_name == 'vavg':
            # Read full 3D u and v
            u_3d_tmp = id.variables['u'][0, :, :, :]
            v_3d_tmp = id.variables['v'][0, :, :, :]
            # Read bathymetry from grid file
            id2 = Dataset(roms_grid, 'r')
            roms_h = id2.variables['h'][:, :]
            id2.close()
            # Get integrands on 3D grid; we only care about dz
            dx, dy, dz, z = cartesian_grid_3d(roms_lon, roms_lat, roms_h,
                                              roms_zice, theta_s, theta_b, hc,
                                              N)
            # Unrotate each vertical level
            u_3d = ma.empty(shape(dz))
            v_3d = ma.empty(shape(dz))
            for k in range(N):
                u_k, v_k = rotate_vector_roms(u_3d_tmp[k, :, :],
                                              v_3d_tmp[k, :, :], angle)
                u_3d[k, :, :] = u_k
                v_3d[k, :, :] = v_k
            # Vertically average u and v
            u_rho = sum(u_3d * dz, axis=0) / sum(dz, axis=0)
            v_rho = sum(v_3d * dz, axis=0) / sum(dz, axis=0)
        # Get speed
        roms_data = sqrt(u_rho**2 + v_rho**2)
    id.close()
    # Get land/zice mask
    open_ocn = copy(roms_mask)
    open_ocn[roms_zice != 0] = 0
    land_zice = ma.masked_where(open_ocn == 1, open_ocn)
    # Mask the open ocean and land out of the data field
    roms_data = ma.masked_where(roms_zice == 0, roms_data)
    # Convert grid to spherical coordinates
    roms_x = -(roms_lat + 90) * cos(roms_lon * deg2rad + pi / 2)
    roms_y = (roms_lat + 90) * sin(roms_lon * deg2rad + pi / 2)
    # Find centre in spherical coordinates
    x_c = -(lat_c + 90) * cos(lon_c * deg2rad + pi / 2)
    y_c = (lat_c + 90) * sin(lon_c * deg2rad + pi / 2)
    # Build a regular x-y grid and select the missing circle
    x_reg_roms, y_reg_roms = meshgrid(linspace(-nbdry, nbdry, num=1000),
                                      linspace(-nbdry, nbdry, num=1000))
    land_circle = zeros(shape(x_reg_roms))
    land_circle = ma.masked_where(
        sqrt((x_reg_roms - x_c)**2 + (y_reg_roms - y_c)**2) > radius,
        land_circle)

    print 'Reading FESOM fields'
    # Mask open ocean
    elements, mask_patches = make_patches(fesom_mesh_path,
                                          circumpolar=True,
                                          mask_cavities=True)
    # Unmask ice shelves
    patches = iceshelf_mask(elements)
    if var_name == 'draft':
        # Nothing more to read
        pass
    else:
        id = Dataset(fesom_file, 'r')
        if var_name == 'melt':
            # Convert from m/s to m/y
            node_data = id.variables['wnet'][0, :] * sec_per_year
        elif var_name == 'temp':
            # Read full 3D field for now
            node_data = id.variables['temp'][0, :]
        elif var_name == 'salt':
            # Read full 3D field for now
            node_data = id.variables['salt'][0, :]
        elif var_name in ['vsfc', 'vavg']:
            # The overlaid vectors are based on nodes not elements, so many
            # of the fesom_grid data structures fail to apply and we need to
            # read some of the FESOM grid files again.
            # Read the cavity flag for each 2D surface node
            fesom_cavity = []
            f = open(fesom_mesh_path + 'cavity_flag_nod2d.out', 'r')
            for line in f:
                tmp = int(line)
                if tmp == 1:
                    fesom_cavity.append(True)
                elif tmp == 0:
                    fesom_cavity.append(False)
                else:
                    print 'Problem'
                    return
            f.close()
            # Save the number of 2D nodes
            fesom_n2d = len(fesom_cavity)
            # Read rotated lat and lon for each node; also read depth which is
            # needed for vertically averaged velocity
            f = open(fesom_mesh_path + 'nod3d.out', 'r')
            f.readline()
            rlon = []
            rlat = []
            node_depth = []
            for line in f:
                tmp = line.split()
                lon_tmp = float(tmp[1])
                lat_tmp = float(tmp[2])
                node_depth_tmp = -1 * float(tmp[3])
                if lon_tmp < -180:
                    lon_tmp += 360
                elif lon_tmp > 180:
                    lon_tmp -= 360
                rlon.append(lon_tmp)
                rlat.append(lat_tmp)
                node_depth.append(node_depth_tmp)
            f.close()
            # For lat and lon, only care about the 2D nodes (the first
            # fesom_n2d indices)
            rlon = array(rlon[0:fesom_n2d])
            rlat = array(rlat[0:fesom_n2d])
            node_depth = array(node_depth)
            # Unrotate longitude
            fesom_lon, fesom_lat = unrotate_grid(rlon, rlat)
            # Calculate polar coordinates of each node
            fesom_x = -(fesom_lat + 90) * cos(fesom_lon * deg2rad + pi / 2)
            fesom_y = (fesom_lat + 90) * sin(fesom_lon * deg2rad + pi / 2)
            if var_name == 'vavg':
                # Read lists of which nodes are directly below which
                f = open(fesom_mesh_path + 'aux3d.out', 'r')
                max_num_layers = int(f.readline())
                node_columns = zeros([fesom_n2d, max_num_layers])
                for n in range(fesom_n2d):
                    for k in range(max_num_layers):
                        node_columns[n, k] = int(f.readline())
                node_columns = node_columns.astype(int)
                f.close()
            # Now we can actually read the data
            # Read full 3D field for both u and v
            node_ur_3d = id.variables['u'][0, :]
            node_vr_3d = id.variables['v'][0, :]
            if var_name == 'vsfc':
                # Only care about the first fesom_n2d nodes (surface)
                node_ur = node_ur_3d[0:fesom_n2d]
                node_vr = node_vr_3d[0:fesom_n2d]
            elif var_name == 'vavg':
                # Vertically average
                node_ur = zeros(fesom_n2d)
                node_vr = zeros(fesom_n2d)
                for n in range(fesom_n2d):
                    # Integrate udz, vdz, and dz over this water column
                    udz_col = 0
                    vdz_col = 0
                    dz_col = 0
                    for k in range(max_num_layers - 1):
                        if node_columns[n, k + 1] == -999:
                            # Reached the bottom
                            break
                        # Trapezoidal rule
                        top_id = node_columns[n, k]
                        bot_id = node_columns[n, k + 1]
                        dz_tmp = node_depth[bot_id - 1] - node_depth[top_id -
                                                                     1]
                        udz_col += 0.5 * (node_ur_3d[top_id - 1] +
                                          node_ur_3d[bot_id - 1]) * dz_tmp
                        vdz_col += 0.5 * (node_vr_3d[top_id - 1] +
                                          node_vr_3d[bot_id - 1]) * dz_tmp
                        dz_col += dz_tmp
                    # Convert from integrals to averages
                    node_ur[n] = udz_col / dz_col
                    node_vr[n] = vdz_col / dz_col
            # Unrotate
            node_u, node_v = unrotate_vector(rlon, rlat, node_ur, node_vr)
            # Calculate speed
            node_data = sqrt(node_u**2 + node_v**2)
        id.close()
    # Calculate given field at each element
    fesom_data = []
    for elm in elements:
        # For each element in an ice shelf cavity, append the mean value
        # for the 3 component Nodes
        if elm.cavity:
            if var_name == 'draft':
                # Ice shelf draft is depth of surface layer
                fesom_data.append(
                    mean([
                        elm.nodes[0].depth, elm.nodes[1].depth,
                        elm.nodes[2].depth
                    ]))
            elif var_name in ['melt', 'vsfc', 'vavg']:
                # Surface nodes (or 2D in the case of vavg)
                fesom_data.append(
                    mean([
                        node_data[elm.nodes[0].id], node_data[elm.nodes[1].id],
                        node_data[elm.nodes[2].id]
                    ]))
            elif var_name in ['temp', 'salt']:
                # Bottom nodes
                fesom_data.append(
                    mean([
                        node_data[elm.nodes[0].find_bottom().id],
                        node_data[elm.nodes[1].find_bottom().id],
                        node_data[elm.nodes[2].find_bottom().id]
                    ]))

    # Loop over ice shelves
    for index in range(num_shelves):
        print 'Processing ' + shelf_names[index]
        # Convert lat/lon bounds to polar coordinates for plotting
        x1 = -(lat_min[index] + 90) * cos(lon_min[index] * deg2rad + pi / 2)
        y1 = (lat_min[index] + 90) * sin(lon_min[index] * deg2rad + pi / 2)
        x2 = -(lat_min[index] + 90) * cos(lon_max[index] * deg2rad + pi / 2)
        y2 = (lat_min[index] + 90) * sin(lon_max[index] * deg2rad + pi / 2)
        x3 = -(lat_max[index] + 90) * cos(lon_min[index] * deg2rad + pi / 2)
        y3 = (lat_max[index] + 90) * sin(lon_min[index] * deg2rad + pi / 2)
        x4 = -(lat_max[index] + 90) * cos(lon_max[index] * deg2rad + pi / 2)
        y4 = (lat_max[index] + 90) * sin(lon_max[index] * deg2rad + pi / 2)
        # Find the new bounds on x and y
        x_min = amin(array([x1, x2, x3, x4]))
        x_max = amax(array([x1, x2, x3, x4]))
        y_min = amin(array([y1, y2, y3, y4]))
        y_max = amax(array([y1, y2, y3, y4]))
        # Now make the plot square: enlarge the smaller of delta_x and delta_y
        # so they are equal
        delta_x = x_max - x_min
        delta_y = y_max - y_min
        if delta_x > delta_y:
            diff = 0.5 * (delta_x - delta_y)
            y_min -= diff
            y_max += diff
        elif delta_y > delta_x:
            diff = 0.5 * (delta_y - delta_x)
            x_min -= diff
            x_max += diff
        # Set up a grey square for FESOM to fill the background with land
        x_reg_fesom, y_reg_fesom = meshgrid(linspace(x_min, x_max, num=100),
                                            linspace(y_min, y_max, num=100))
        land_square = zeros(shape(x_reg_fesom))
        # Find bounds on variable in this region, for both ROMS and FESOM
        # Start with ROMS
        loc = (roms_x >= x_min) * (roms_x <= x_max) * (roms_y >= y_min) * (
            roms_y <= y_max)
        var_min = amin(roms_data[loc])
        var_max = amax(roms_data[loc])
        # Modify with FESOM
        i = 0
        for elm in elements:
            if elm.cavity:
                if any(elm.x >= x_min) and any(elm.x <= x_max) and any(
                        elm.y >= y_min) and any(elm.y <= y_max):
                    if fesom_data[i] < var_min:
                        var_min = fesom_data[i]
                    if fesom_data[i] > var_max:
                        var_max = fesom_data[i]
                i += 1
        if var_name == 'melt':
            # Special colour map
            if var_min < 0:
                # There is refreezing here; include blue for elements below 0
                cmap_vals = array([
                    var_min, 0, 0.25 * var_max, 0.5 * var_max, 0.75 * var_max,
                    var_max
                ])
                cmap_colors = [(0.26, 0.45, 0.86), (1, 1, 1), (1, 0.9, 0.4),
                               (0.99, 0.59, 0.18), (0.5, 0.0, 0.08),
                               (0.96, 0.17, 0.89)]
                cmap_vals_norm = (cmap_vals - var_min) / (var_max - var_min)
                cmap_list = []
                for i in range(size(cmap_vals)):
                    cmap_list.append((cmap_vals_norm[i], cmap_colors[i]))
                mf_cmap = LinearSegmentedColormap.from_list(
                    'melt_freeze', cmap_list)
            else:
                # No refreezing
                cmap_vals = array([
                    0, 0.25 * var_max, 0.5 * var_max, 0.75 * var_max, var_max
                ])
                cmap_colors = [(1, 1, 1), (1, 0.9, 0.4), (0.99, 0.59, 0.18),
                               (0.5, 0.0, 0.08), (0.96, 0.17, 0.89)]
                cmap_vals_norm = cmap_vals / var_max
                cmap_list = []
                for i in range(size(cmap_vals)):
                    cmap_list.append((cmap_vals_norm[i], cmap_colors[i]))
                mf_cmap = LinearSegmentedColormap.from_list(
                    'melt_freeze', cmap_list)
            colour_map = mf_cmap
        else:
            colour_map = 'jet'
        if var_name in ['vsfc', 'vavg']:
            # Make vectors for overlay
            # Set up bins (edges)
            x_bins = linspace(x_min, x_max, num=num_bins + 1)
            y_bins = linspace(y_min, y_max, num=num_bins + 1)
            # Calculate centres of bins (for plotting)
            x_centres = 0.5 * (x_bins[:-1] + x_bins[1:])
            y_centres = 0.5 * (y_bins[:-1] + y_bins[1:])
            # ROMS
            # First set up arrays to integrate velocity in each bin
            # Simple averaging of all the points inside each bin
            roms_u = zeros([size(y_centres), size(x_centres)])
            roms_v = zeros([size(y_centres), size(x_centres)])
            roms_num_pts = zeros([size(y_centres), size(x_centres)])
            # First convert to polar coordinates, rotate to account for
            # longitude in circumpolar projection, and convert back to vector
            # components
            theta_roms = arctan2(v_rho, u_rho)
            theta_circ_roms = theta_roms - roms_lon * deg2rad
            u_circ_roms = roms_data * cos(
                theta_circ_roms)  # roms_data is speed
            v_circ_roms = roms_data * sin(theta_circ_roms)
            # Loop over all points (can't find a better way to do this)
            for j in range(size(roms_data, 0)):
                for i in range(size(roms_data, 1)):
                    # Make sure data isn't masked (i.e. land or open ocean)
                    if u_circ_roms[j, i] is not ma.masked:
                        # Check if we're in the region of interest
                        if roms_x[j, i] > x_min and roms_x[
                                j, i] < x_max and roms_y[
                                    j, i] > y_min and roms_y[j, i] < y_max:
                            # Figure out which bins this falls into
                            x_index = nonzero(x_bins > roms_x[j, i])[0][0] - 1
                            y_index = nonzero(y_bins > roms_y[j, i])[0][0] - 1
                            # Integrate
                            roms_u[y_index, x_index] += u_circ_roms[j, i]
                            roms_v[y_index, x_index] += v_circ_roms[j, i]
                            roms_num_pts[y_index, x_index] += 1
            # Convert from sums to averages
            # First mask out points with no data
            roms_u = ma.masked_where(roms_num_pts == 0, roms_u)
            roms_v = ma.masked_where(roms_num_pts == 0, roms_v)
            # Divide everything else by the number of points
            flag = roms_num_pts > 0
            roms_u[flag] = roms_u[flag] / roms_num_pts[flag]
            roms_v[flag] = roms_v[flag] / roms_num_pts[flag]
            # FESOM
            fesom_u = zeros([size(y_centres), size(x_centres)])
            fesom_v = zeros([size(y_centres), size(x_centres)])
            fesom_num_pts = zeros([size(y_centres), size(x_centres)])
            theta_fesom = arctan2(node_v, node_u)
            theta_circ_fesom = theta_fesom - fesom_lon * deg2rad
            u_circ_fesom = node_data * cos(
                theta_circ_fesom)  # node_data is speed
            v_circ_fesom = node_data * sin(theta_circ_fesom)
            # Loop over 2D nodes to fill in the velocity bins as before
            for n in range(fesom_n2d):
                if fesom_cavity[n]:
                    if fesom_x[n] > x_min and fesom_x[n] < x_max and fesom_y[
                            n] > y_min and fesom_y[n] < y_max:
                        x_index = nonzero(x_bins > fesom_x[n])[0][0] - 1
                        y_index = nonzero(y_bins > fesom_y[n])[0][0] - 1
                        fesom_u[y_index, x_index] += u_circ_fesom[n]
                        fesom_v[y_index, x_index] += v_circ_fesom[n]
                        fesom_num_pts[y_index, x_index] += 1
            fesom_u = ma.masked_where(fesom_num_pts == 0, fesom_u)
            fesom_v = ma.masked_where(fesom_num_pts == 0, fesom_v)
            flag = fesom_num_pts > 0
            fesom_u[flag] = fesom_u[flag] / fesom_num_pts[flag]
            fesom_v[flag] = fesom_v[flag] / fesom_num_pts[flag]
        # Plot
        fig = figure(figsize=(30, 12))
        fig.patch.set_facecolor('white')
        # ROMS
        ax1 = fig.add_subplot(1, 2, 1, aspect='equal')
        # First shade land and zice in grey
        contourf(roms_x, roms_y, land_zice, 1, colors=(('0.6', '0.6', '0.6')))
        # Fill in the missing circle
        contourf(x_reg_roms,
                 y_reg_roms,
                 land_circle,
                 1,
                 colors=(('0.6', '0.6', '0.6')))
        # Now shade the data
        pcolor(roms_x,
               roms_y,
               roms_data,
               vmin=var_min,
               vmax=var_max,
               cmap=colour_map)
        if var_name in ['vsfc', 'vavg']:
            # Overlay vectors
            quiver(x_centres,
                   y_centres,
                   roms_u,
                   roms_v,
                   scale=1.5,
                   color='black')
        xlim([x_min, x_max])
        ylim([y_min, y_max])
        axis('off')
        title('MetROMS', fontsize=24)
        # FESOM
        ax2 = fig.add_subplot(1, 2, 2, aspect='equal')
        # Start with land background
        contourf(x_reg_fesom,
                 y_reg_fesom,
                 land_square,
                 1,
                 colors=(('0.6', '0.6', '0.6')))
        # Add ice shelf elements
        img = PatchCollection(patches, cmap=colour_map)
        img.set_array(array(fesom_data))
        img.set_edgecolor('face')
        img.set_clim(vmin=var_min, vmax=var_max)
        ax2.add_collection(img)
        # Mask out the open ocean in white
        overlay = PatchCollection(mask_patches, facecolor=(1, 1, 1))
        overlay.set_edgecolor('face')
        ax2.add_collection(overlay)
        if var_name in ['vsfc', 'vavg']:
            quiver(x_centres,
                   y_centres,
                   fesom_u,
                   fesom_v,
                   scale=1.5,
                   color='black')
        xlim([x_min, x_max])
        ylim([y_min, y_max])
        axis('off')
        title('FESOM', fontsize=24)
        # Colourbar on the right
        cbaxes = fig.add_axes([0.92, 0.2, 0.01, 0.6])
        cbar = colorbar(img, cax=cbaxes)
        cbar.ax.tick_params(labelsize=20)
        # Main title
        if var_name == 'draft':
            title_string = ' draft (m)'
        elif var_name == 'melt':
            title_string = ' melt rate (m/y)'
        elif var_name == 'temp':
            title_string = r' bottom water temperature ($^{\circ}$C)'
        elif var_name == 'salt':
            title_string = ' bottom water salinity (psu)'
        elif var_name == 'vsfc':
            title_string = ' surface velocity (m/s)'
        elif var_name == 'vavg':
            title_string = ' vertically averaged velocity (m/s)'
        suptitle(shelf_names[index] + title_string, fontsize=30)
        subplots_adjust(wspace=0.05)
        #fig.show()
        fig.savefig(fig_heads[index] + '_' + var_name + '.png')
예제 #2
0
def timeseries_dpt (mesh_path, ocn_file, log_file):

    circumpolar = False  # Don't transform x and y coordinates, we need them!
    cross_180 = False    # Don't make second copies of elements that cross 180E
    days_per_output = 5  # Number of days for each output step

    # Longitude of Drake Passage zonal slice
    lon0 = -67
    # Latitude bounds on Drake Passage zonal slice
    lat_min = -68
    lat_max = -54.5

    dpt = []
    # Check if the log file exists
    if exists(log_file):
        print 'Reading previously calculated values'
        f = open(log_file, 'r')
        # Skip the first line (header)
        f.readline()
        for line in f:
            dpt.append(float(line))
        f.close()

    print 'Building grid'
    # First get regular 2D elements
    elm2D = fesom_grid(mesh_path, circumpolar, cross_180)
    # Read longitude and latitude of each node in order (needed for rotation)
    fid = open(mesh_path + 'nod3d.out', 'r')
    fid.readline()
    lon = []
    lat = []
    for line in fid:
        tmp = line.split()
        lon_tmp = float(tmp[1])
        lat_tmp = float(tmp[2])
        if lon_tmp < -180:
            lon_tmp += 360
        elif lon_tmp > 180:
            lon_tmp -= 360
        lon.append(lon_tmp)
        lat.append(lat_tmp)
    fid.close()
    lon = array(lon)
    lat = array(lat)

    print 'Reading data'
    id = Dataset(ocn_file, 'r')
    num_time = id.variables['time'].shape[0]
    # Read both u and v so we can rotate to get the real u
    u_r = id.variables['u'][:,:]
    v_r = id.variables['v'][:,:]
    id.close()

    print 'Unrotating velocity vector'
    u = zeros(shape(u_r))
    # Rotate one time index at a time
    for t in range(num_time):
        u_tmp, v_tmp = unrotate_vector(lon, lat, u_r[t,:], v_r[t,:])
        u[t,:] = u_tmp

    print 'Extracting zonal slice through Drake Passage'
    # Get quadrilateral elements in the latitude vs depth slice
    selements = fesom_sidegrid(elm2D, u, lon0, lat_max, lat_min)

    print 'Setting up arrays'
    # Eastward velocity at each element
    u_selm = zeros([num_time, len(selements)])
    # Area of each element
    area_selm = zeros(len(selements))
    # Loop over elements to fill these in
    for i in range(len(selements)):
        selm = selements[i]
        u_selm[:,i] = selm.var
        area_selm[i] = selm.area()
    # Build timeseries
    for t in range(num_time):
        # Integrate u*area and convert to Sv
        dpt.append(sum(u_selm[t,:]*area_selm)*1e-6)

    # Calculate time values
    time = arange(len(dpt))*days_per_output/365.

    print 'Plotting'
    clf()
    plot(time, dpt)
    xlabel('Years')
    ylabel('Drake Passage Transport (Sv)')
    grid(True)
    savefig('drakepsgtrans.png')

    print 'Saving results to log file'
    f = open(log_file, 'w')
    f.write('Drake Passage Transport (Sv):\n')
    for elm in dpt:
        f.write(str(elm) + '\n')
    f.close()
예제 #3
0
def fesom_intersectgrid(mesh_path, file_path, var_name, tstep, lon_min,
                        lon_max, lat_min, lat_max, depth_min, depth_max,
                        num_lat, num_depth):

    if lon_min == -180 and lon_max == 180:
        lon_bounds = False
    else:
        lon_bounds = True

    # Build the regular FESOM grid
    elements = fesom_grid(mesh_path, cross_180=False)

    # Read data
    id = Dataset(file_path, 'r')
    data = id.variables[var_name][tstep - 1, :]
    # Check for vector variables that need to be unrotated
    if var_name in ['u', 'v']:
        # Read the rotated lat and lon
        fid = open(mesh_path + 'nod3d.out', 'r')
        fid.readline()
        lon = []
        lat = []
        for line in fid:
            tmp = line.split()
            lon_tmp = float(tmp[1])
            lat_tmp = float(tmp[2])
            if lon_tmp < -180:
                lon_tmp += 360
            elif lon_tmp > 180:
                lon_tmp -= 360
            lon.append(lon_tmp)
            lat.append(lat_tmp)
        fid.close()
        lon = array(lon)
        lat = array(lat)
        if var_name == 'u':
            u_data = data[:]
            v_data = id.variables['v'][tstep - 1, :]
            u_data_lonlat, v_data_lonlat = unrotate_vector(
                lon, lat, u_data, v_data)
            data = u_data_lonlat[:]
        elif var_name == 'v':
            v_data = data[:]
            u_data = id.variables['u'][tstep - 1, :]
            u_data_lonlat, v_data_lonlat = unrotate_vector(
                lon, lat, u_data, v_data)
            data = v_data_lonlat[:]
    id.close()

    # Build the regular grid
    lat_vals = linspace(lat_min, lat_max, num_lat)
    # Make depth positive to match the "depth" attribute in grid Nodes
    depth_vals = -1 * linspace(depth_min, depth_max, num_depth)

    # Set up array of NaNs to overwrite with zonally averaged data
    data_reg = zeros((num_depth, num_lat))
    data_reg[:, :] = NaN

    # Process one latitude value at a time
    for j in range(num_lat):
        ielm_list = []
        # Loop over 2D grid Elements
        for elm in elements:
            # Select elements which intersect the current latitude, and which
            # fall entirely between the longitude bounds
            if lon_bounds:
                keep = any(elm.y <= lat_vals[j]) and any(
                    elm.y >= lat_vals[j]) and all(elm.x >= lon_min) and all(
                        elm.x <= lon_max)
            else:
                # No bounds on longitude
                keep = any(elm.y <= lat_vals[j]) and any(elm.y >= lat_vals[j])
            if keep:
                # Create an IntersectElement
                ielm = create_ielm(elm, lat_vals[j], depth_vals, data)
                # Check for cases where the Element intersected the given
                # latitude at exactly one corner; these aren't useful
                if ielm is not None:
                    ielm_list.append(ielm)
        # Zonally average at each depth
        for k in range(num_depth):
            # Set up integrals of var*dx and dx
            int_vardx = 0
            int_dx = 0
            for ielm in ielm_list:
                # Check if data exists at the current depth level
                if ielm.var[k] is not NaN:
                    int_vardx += ielm.var[k] * ielm.dx
                    int_dx += ielm.dx
            if int_dx > 0:
                data_reg[k, j] = int_vardx / int_dx

    # Convert depth back to negative for plotting
    depth_vals = -1 * depth_vals

    return lat_vals, depth_vals, data_reg
예제 #4
0
def lonlat_plot(
    mesh_path,
    file_path,
    var_name,
    depth_key,
    depth,
    depth_bounds,
    tstep,
    circumpolar,
    elements,
    patches,
    mask_cavities=False,
    save=False,
    fig_name=None,
    set_limits=False,
    limits=None,
):

    # Set bounds for domain
    if circumpolar:
        # Northern boundary 30S
        lat_max = -30 + 90
    else:
        lon_min = -180
        lon_max = 180
        lat_min = -90
        lat_max = 90
        # Configure position of latitude and longitude labels
        lon_ticks = arange(-120, 120 + 1, 60)
        lat_ticks = arange(-60, 60 + 1, 30)
    # Set font sizes
    font_sizes = [30, 24, 20]
    # Seconds per year, for conversion of ice shelf melt rate
    sec_per_year = 365 * 24 * 3600

    # Read data
    file = Dataset(file_path, "r")
    varid = file.variables[var_name]
    data = varid[tstep - 1, :]
    # Check for vector variables that need to be unrotated
    if var_name in [
        "uwind",
        "vwind",
        "stress_x",
        "stress_y",
        "uhice",
        "vhice",
        "uhsnow",
        "vhsnow",
        "uice",
        "vice",
        "u",
        "v",
    ]:
        # Read the rotated lat and lon
        if var_name in ["u", "v"]:
            # 3D variable
            mesh_file = mesh_path + "nod3d.out"
        else:
            # 2D variable
            mesh_file = mesh_path + "nod2d.out"
        fid = open(mesh_file, "r")
        fid.readline()
        lon = []
        lat = []
        for line in fid:
            tmp = line.split()
            lon_tmp = float(tmp[1])
            lat_tmp = float(tmp[2])
            if lon_tmp < -180:
                lon_tmp += 360
            elif lon_tmp > 180:
                lon_tmp -= 360
            lon.append(lon_tmp)
            lat.append(lat_tmp)
        fid.close()
        lon = array(lon)
        lat = array(lat)
        if var_name in ["uwind", "stress_x", "uhice", "uhsnow", "uice", "u"]:
            # u-variable
            u_data = data[:]
            if var_name == "stress_x":
                v_data = file.variables["stress_y"][tstep - 1, :]
            else:
                v_data = file.variables[var_name.replace("u", "v")][tstep - 1, :]
            u_data_lonlat, v_data_lonlat = unrotate_vector(lon, lat, u_data, v_data)
            data = u_data_lonlat[:]
        elif var_name in ["vwind", "stress_y", "vhice", "vhsnow", "vice", "v"]:
            # v-variable
            v_data = data[:]
            if var_name == "stress_y":
                u_data = file.variables["stress_x"][tstep - 1, :]
            else:
                u_data = file.variables[var_name.replace("v", "u")][tstep - 1, :]
            u_data_lonlat, v_data_lonlat = unrotate_vector(lon, lat, u_data, v_data)
            data = v_data_lonlat[:]

    # Set descriptive variable name and units for title
    if var_name == "area":
        name = "ice concentration"
        units = "fraction"
    elif var_name == "wnet":
        # Convert from m/s to m/y
        data = data * sec_per_year
        name = "ice shelf melt rate"
        units = "m/y"
    else:
        name = varid.getncattr("description")
        units = varid.getncattr("units")
    if depth_key == 0:
        if var_name in ["temp", "salt", "u", "v"]:
            depth_string = "at surface"
        else:
            depth_string = ""
    elif depth_key == 1:
        depth_string = "at bottom"
    elif depth_key == 2:
        depth_string = "vertically averaged"
    elif depth_key == 3:
        depth_string = "at " + str(depth) + " m"
    elif depth_key == 4:
        depth_string = "vertically averaged between " + str(depth_bounds[0]) + " and " + str(depth_bounds[1]) + " m"

    # Build an array of data values corresponding to each Element
    values = []
    plot_patches = []
    for elm in elements:
        # If mask_cavities is true, only include elements which are not inside
        # an ice shelf cavity; otherwise, include all elements
        if (mask_cavities and not elm.cavity) or (not mask_cavities):

            if depth_key == 0:
                # Surface nodes; this is easy
                # Average the data value for each of the three component nodes
                values.append(mean([data[elm.nodes[0].id], data[elm.nodes[1].id], data[elm.nodes[2].id]]))

            elif depth_key == 1:
                # Bottom nodes
                values_tmp = []
                # For each of the three component nodes, find the id of the
                # bottom node beneath it
                for node in elm.nodes:
                    id = node.find_bottom().id
                    values_tmp.append(data[id])
                # Average over these three values
                values.append(mean(values_tmp))

            elif depth_key == 2:
                # Vertical average throughout entire water column
                # First calculate volume: area of triangular face * water
                # column thickness (mean of three corners)
                wct = []
                for node in elm.nodes:
                    wct.append(abs(node.find_bottom().depth - node.depth))
                area = elm.area()
                volume = area * mean(array(wct))
                # Now integrate down
                integral = 0
                nodes = [elm.nodes[0], elm.nodes[1], elm.nodes[2]]
                while True:
                    if nodes[0].below is None or nodes[1].below is None or nodes[2].below is None:
                        break
                    # Calculate mean of data at six corners of this triangular
                    # prism, and mean depths at three edges
                    values_tmp = []
                    dz_tmp = []
                    # Must loop over indices of nodes array, not entries,
                    # because we want to change the contents of the nodes array
                    for i in range(3):
                        values_tmp.append(data[nodes[i].id])
                        values_tmp.append(data[nodes[i].below.id])
                        dz_tmp.append(abs(nodes[i].depth - nodes[i].below.depth))
                        # Get ready for next iteration of the while loop
                        nodes[i] = nodes[i].below
                    # Integrand is mean of data at corners * area of upper face
                    # * mean of depths at edges
                    integral += mean(array(values_tmp)) * area * mean(array(dz_tmp))
                # All done; divide integral by volume to get the average
                values.append(integral / volume)

            elif depth_key == 3:
                # Specified depth
                values_tmp = []
                # For each of the three component nodes, linearly interpolate
                # to the correct depth
                for i in range(3):
                    # Find the ids of the nodes above and below this depth,
                    # and the coefficients for the linear interpolation
                    id1, id2, coeff1, coeff2 = elm.nodes[i].find_depth(depth)
                    if any(isnan(array([id1, id2, coeff1, coeff2]))):
                        # No such node at this depth
                        values_tmp.append(NaN)
                    else:
                        values_tmp.append(coeff1 * data[id1] + coeff2 * data[id2])
                if any(isnan(array(values_tmp))):
                    pass
                else:
                    values.append(mean(values_tmp))
                    coord = transpose(vstack((elm.x, elm.y)))
                    # Make new patches for elements which exist at this depth
                    plot_patches.append(Polygon(coord, True, linewidth=0.0))

            elif depth_key == 4:
                # Vertical average between two specified depths
                shallow_bound = depth_bounds[0]
                deep_bound = depth_bounds[1]
                area = elm.area()
                # Volume is area of triangular face * difference between
                # depth bounds
                volume = abs(deep_bound - shallow_bound) * area
                nodes = [elm.nodes[0], elm.nodes[1], elm.nodes[2]]
                # Check if we're already too deep
                if nodes[0].depth > deep_bound or nodes[1].depth > deep_bound or nodes[2].depth > deep_bound:
                    pass
                # Check if the seafloor is shallower than shallow_bound
                elif (
                    nodes[0].find_bottom().depth <= shallow_bound
                    or nodes[1].find_bottom().depth <= shallow_bound
                    or nodes[2].find_bottom().depth <= shallow_bound
                ):
                    pass

                else:
                    # Here we go

                    # Find the first 3D element which is entirely below
                    # shallow_bound
                    while True:
                        if (
                            nodes[0].depth > shallow_bound
                            and nodes[1].depth > shallow_bound
                            and nodes[2].depth > shallow_bound
                        ):
                            # Save these nodes
                            first_nodes = []
                            for i in range(3):
                                first_nodes.append(nodes[i])
                            break
                        else:
                            for i in range(3):
                                nodes[i] = nodes[i].below

                    # Integrate downward until one of the next nodes hits the
                    # seafloor, or is deeper than deep_bound
                    integral = 0
                    while True:
                        if nodes[0].below is None or nodes[1].below is None or nodes[2].below is None:
                            # We've reached the seafloor
                            last_nodes = None
                            break
                        if (
                            nodes[0].below.depth > deep_bound
                            or nodes[1].below.depth > deep_bound
                            or nodes[2].below.depth > deep_bound
                        ):
                            # At least one of the next nodes will pass
                            # deep_bound; save the current nodes
                            last_nodes = []
                            for i in range(3):
                                last_nodes.append(nodes[i])
                            break
                        else:
                            # Calculate mean of data at six corners of this
                            # triangular prism, and mean depths at three edges
                            values_tmp = []
                            dz_tmp = []
                            for i in range(3):
                                values_tmp.append(data[nodes[i].id])
                                values_tmp.append(data[nodes[i].below.id])
                                dz_tmp.append(abs(nodes[i].depth - nodes[i].below.depth))
                                # Get ready for next iteration of while loop
                                nodes[i] = nodes[i].below
                            # Integrand is mean of data at corners *
                            # area of upper face * mean of depths at edges
                            integral += mean(array(values_tmp)) * area * mean(array(dz_tmp))

                    # Now integrate from shallow_bound to first_nodes by
                    # linearly interpolating each node to shallow_bound
                    values_tmp = []
                    dz_tmp = []
                    for i in range(3):
                        values_tmp.append(data[first_nodes[i].id])
                        id1, id2, coeff1, coeff2 = elm.nodes[i].find_depth(shallow_bound)
                        if any(isnan(array([id1, id2, coeff1, coeff2]))):
                            # first_nodes[i] was the shallowest node, we can't
                            # interpolate above it
                            values_tmp.append(NaN)
                        else:
                            values_tmp.append(coeff1 * data[id1] + coeff2 * data[id2])
                        dz_tmp.append(abs(first_nodes[i].depth - shallow_bound))
                    if any(isnan(array(values_tmp))):
                        pass
                    else:
                        integral += mean(array(values_tmp)) * area * mean(array(dz_tmp))

                    # Now integrate from last_nodes to deep_bound by linearly
                    # interpolating each node to shallow_bound, unless we hit
                    # the seafloor earlier
                    if last_nodes is not None:
                        values_tmp = []
                        dz_tmp = []
                        for i in range(3):
                            values_tmp.append(data[last_nodes[i].id])
                            id1, id2, coeff1, coeff2 = elm.nodes[i].find_depth(deep_bound)
                            if any(isnan(array([id1, id2, coeff1, coeff2]))):
                                # last_nodes[i] was the deepest node, we can't
                                # interpolate below it
                                values_tmp.append(NaN)
                            else:
                                values_tmp.append(coeff1 * data[id1] + coeff2 * data[id2])
                            dz_tmp.append(abs(deep_bound - last_nodes[i].depth))
                        if any(isnan(array(values_tmp))):
                            pass
                        else:
                            integral += mean(array(values_tmp)) * area * mean(array(dz_tmp))

                    # All done; divide integral by volume to get the average
                    values.append(integral / volume)
                    # Make new patches for elements which exist at this depth
                    coord = transpose(vstack((elm.x, elm.y)))
                    plot_patches.append(Polygon(coord, True, linewidth=0.0))

    if depth_key < 3:
        # Use all patches
        plot_patches = patches[:]

    if mask_cavities:
        # Get mask array of patches for ice shelf cavity elements
        mask_patches = iceshelf_mask(elements)
        if var_name == "wnet":
            # Swap with regular patches so that open ocean elements are masked,
            # ice shelf cavity nodes are not
            tmp = plot_patches
            plot_patches = mask_patches
            mask_patches = tmp

    # Choose colour bounds
    if set_limits:
        # User-specified bounds
        var_min = limits[0]
        var_max = limits[1]
        if var_min == -var_max:
            # Bounds are centered on zero, so choose a blue-to-red colourmap
            # centered on yellow
            colour_map = "RdYlBu_r"
        else:
            colour_map = "jet"
    else:
        # Determine bounds automatically
        if var_name in [
            "uwind",
            "vwind",
            "qnet",
            "olat",
            "osen",
            "wnet",
            "virtual_salt",
            "relax_salt",
            "stress_x",
            "stress_y",
            "uice",
            "vice",
            "u",
            "v",
            "w",
        ]:
            # Center levels on 0 for certain variables, with a blue-to-red
            # colourmap
            max_val = amax(abs(array(values)))
            var_min = -max_val
            var_max = max_val
            colour_map = "RdYlBu_r"
        else:
            var_min = amin(array(values))
            var_max = amax(array(values))
            colour_map = "jet"

    # Set up plot
    if circumpolar:
        fig = figure(figsize=(16, 12))
        ax = fig.add_subplot(1, 1, 1, aspect="equal")
    else:
        fig = figure(figsize=(16, 8))
        ax = fig.add_subplot(1, 1, 1)
    # Set colourmap for patches, and refer it to the values array
    img = PatchCollection(plot_patches, cmap=colour_map)
    img.set_array(array(values))
    img.set_edgecolor("face")
    # Add patches to plot
    ax.add_collection(img)
    if mask_cavities:
        # Set colour to light grey for patches in mask
        overlay = PatchCollection(mask_patches, facecolor=(0.6, 0.6, 0.6))
        overlay.set_edgecolor("face")
        # Add mask to plot
        ax.add_collection(overlay)

    # Configure plot
    if circumpolar:
        xlim([-lat_max, lat_max])
        ylim([-lat_max, lat_max])
        ax.get_xaxis().set_ticks([])
        ax.get_yaxis().set_ticks([])
        axis("off")
    else:
        xlim([lon_min, lon_max])
        ylim([lat_min, lat_max])
        xticks(lon_ticks)
        yticks(lat_ticks)
        xlabel("Longitude", fontsize=font_sizes[1])
        ylabel("Latitude", fontsize=font_sizes[1])
        setp(ax.get_xticklabels(), fontsize=font_sizes[2])
        setp(ax.get_yticklabels(), fontsize=font_sizes[2])
    title(name + " (" + units + ") " + depth_string, fontsize=font_sizes[0])
    cbar = colorbar(img)
    cbar.ax.tick_params(labelsize=font_sizes[2])
    img.set_clim(vmin=var_min, vmax=var_max)

    # Plot specified points
    # problem_ids = [16, 17, 36, 64, 67, 70, 160, 262, 266, 267, 268, 273, 274, 280, 283, 287, 288, 289, 290, 291, 292, 293, 294, 296, 297, 350, 351, 352, 353, 360, 479, 480, 493, 507, 508, 509, 521, 539, 547, 548, 549, 550, 554, 555]
    # problem_x = []
    # problem_y = []
    # for elm in elements:
    # for i in range(3):
    # if elm.nodes[i].id in problem_ids:
    # problem_x.append(elm.x[i])
    # problem_y.append(elm.y[i])
    # problem_ids.remove(elm.nodes[i].id)
    # ax.plot(problem_x, problem_y, 'or')

    if save:
        fig.savefig(fig_name)
    else:
        fig.show()
예제 #5
0
def peninsula_res(var_name):

    # File paths
    mesh_path_lr = '/short/y99/kaa561/FESOM/mesh/meshA/'
    mesh_path_hr = '/short/y99/kaa561/FESOM/mesh/meshB/'
    directory_lr = '/short/y99/kaa561/FESOM/intercomparison_lowres/output/'
    directory_hr = '/short/y99/kaa561/FESOM/intercomparison_highres/output/'
    if var_name == 'hi':
        seasonal_file = 'seasonal_climatology_ice.nc'
    elif var_name in ['thdgr', 'div']:
        seasonal_file = 'seasonal_climatology_ice_diag.nc'
    elif var_name == 'sst':
        seasonal_file = 'seasonal_climatology_oce.nc'
    elif var_name == 'vel':
        seasonal_file = 'seasonal_climatology_uv.nc'
    # Degrees to radians conversion factor
    deg2rad = pi / 180.0
    # Bounds on plot (for polar coordinate transformation)
    x_min = -22.5
    x_max = -12
    y_min = 6
    y_max = 15
    if var_name == 'div':
        # Bounds on regular grid
        lon_min = -80
        lon_max = -30
        lat_min = -75
        lat_max = -55
        # Size of regular grid
        num_lon = 500
        num_lat = 500
        # Radius of the Earth in metres
        r = 6.371e6
    if var_name == 'vel':
        num_bins_x = 20
        num_bins_y = 20
    if var_name == 'f/h':
        omega = 7.2921
    # Plotting parameters
    circumpolar = True
    mask_cavities = True
    # Season names for plot titles
    season_names = ['DJF', 'MAM', 'JJA', 'SON']
    # Colour bounds and colour map
    if var_name == 'hi':
        bounds = [0, 1.5]
        cbar_ticks = arange(0, 1.5 + 0.5, 0.5)
        colour_map = 'jet'
    elif var_name == 'thdgr':
        bounds = [-4, 4]
        cbar_ticks = arange(-4, 4 + 2, 2)
        colour_map = 'RdBu_r'
    elif var_name == 'div':
        bounds = [-2, 2]
        cbar_ticks = arange(-2, 2 + 1, 1)
        colour_map = 'RdBu_r'
    elif var_name == 'sst':
        bounds = [-2, 0]
        cbar_ticks = arange(-2, 0 + 1, 1)
        colour_map = 'jet'
    elif var_name == 'vel':
        bounds = [0, 0.2]
        cbar_ticks = arange(0, 0.2 + 0.05, 0.05)
        colour_map = 'cool'
    elif var_name == 'f/h':
        bounds = [0.02, 0.06]
        cbar_ticks = arange(0.02, 0.06 + 0.01, 0.01)
        colour_map = 'jet'
    if var_name == 'div':
        # Set up regular grid
        # Start with boundaries
        lon_reg_edges = linspace(lon_min, lon_max, num_lon + 1)
        lat_reg_edges = linspace(lat_min, lat_max, num_lat + 1)
        # Now get centres
        lon_reg = 0.5 * (lon_reg_edges[:-1] + lon_reg_edges[1:])
        lat_reg = 0.5 * (lat_reg_edges[:-1] + lat_reg_edges[1:])
        # Also get differentials in lon-lat space
        dlon = lon_reg_edges[1:] - lon_reg_edges[:-1]
        dlat = lat_reg_edges[1:] - lat_reg_edges[:-1]
        # Make 2D versions
        lon_reg_2d, lat_reg_2d = meshgrid(lon_reg, lat_reg)
        dlon_2d, dlat_2d = meshgrid(dlon, dlat)
        # Calculate polar coordinates transformation for plotting
        x_reg = -(lat_reg_2d + 90) * cos(lon_reg_2d * deg2rad + pi / 2)
        y_reg = (lat_reg_2d + 90) * sin(lon_reg_2d * deg2rad + pi / 2)
        # Calculate differentials in Cartesian space
        dx = r * cos(lat_reg_2d * deg2rad) * dlon_2d * deg2rad
        dy = r * dlat_2d * deg2rad
        # Set up arrays for uhice and vhice at each resolution
        uhice_reg_lr = zeros([4, num_lat, num_lon])
        vhice_reg_lr = zeros([4, num_lat, num_lon])
        uhice_reg_hr = zeros([4, num_lat, num_lon])
        vhice_reg_hr = zeros([4, num_lat, num_lon])
        # Set up colour levels
        lev = linspace(bounds[0], bounds[1], 50)
    if var_name == 'vel':
        # Set up bins for vectors
        x_bins = linspace(x_min, x_max, num=num_bins_x + 1)
        y_bins = linspace(y_min, y_max, num=num_bins_y + 1)
        # Calculate centres of bins (for plotting)
        x_centres = 0.5 * (x_bins[:-1] + x_bins[1:])
        y_centres = 0.5 * (y_bins[:-1] + y_bins[1:])

    print 'Processing low-res FESOM'
    # Build mesh
    elements_lr, patches_lr = make_patches(mesh_path_lr, circumpolar,
                                           mask_cavities)
    if var_name in ['div', 'vel']:
        # Read rotated latitude and longitude at each node
        file = open(mesh_path_lr + 'nod2d.out', 'r')
        file.readline()
        rlon_lr = []
        rlat_lr = []
        for line in file:
            tmp = line.split()
            lon_tmp = float(tmp[1])
            lat_tmp = float(tmp[2])
            if lon_tmp < -180:
                lon_tmp += 360
            elif lon_tmp > 180:
                lon_tmp -= 360
            rlon_lr.append(lon_tmp)
            rlat_lr.append(lat_tmp)
        file.close()
        rlon_lr = array(rlon_lr)
        rlat_lr = array(rlat_lr)
        if var_name == 'vel':
            # Unrotate
            lon_lr, lat_lr = unrotate_grid(rlon_lr, rlat_lr)
            # Calculate polar coordinates for vector plotting
            x_lr = -(lat_lr + 90) * cos(lon_lr * deg2rad + pi / 2)
            y_lr = (lat_lr + 90) * sin(lon_lr * deg2rad + pi / 2)
            # Read cavity flag for each 2D node
            cavity_lr = []
            f = open(mesh_path_lr + 'cavity_flag_nod2d.out', 'r')
            for line in f:
                tmp = int(line)
                if tmp == 1:
                    cavity_lr.append(True)
                elif tmp == 0:
                    cavity_lr.append(False)
                else:
                    print 'Problem'
            f.close()
    # Read data
    if var_name != 'f/h':
        id = Dataset(directory_lr + seasonal_file, 'r')
        if var_name == 'hi':
            data_nodes_lr = id.variables['hice'][:, :]
        elif var_name == 'thdgr':
            data_nodes_lr = id.variables['thdgr'][:, :] * 1e7
        elif var_name == 'sst':
            # Fine to read all 3D nodes
            data_nodes_lr = id.variables['temp'][:, :]
        elif var_name == 'vel':
            # Only read the surface nodes
            n2d_lr = size(rlon_lr)
            ur_nodes_lr = id.variables['u'][:, :n2d_lr]
            vr_nodes_lr = id.variables['v'][:, :n2d_lr]
            # Unrotate vectors
            u_nodes_lr, v_nodes_lr = unrotate_vector(rlon_lr, rlat_lr,
                                                     ur_nodes_lr, vr_nodes_lr)
            # Save speed
            data_nodes_lr = sqrt(u_nodes_lr**2 + v_nodes_lr**2)
        elif var_name == 'div':
            urhice_nodes_lr = id.variables['uhice'][:, :]
            vrhice_nodes_lr = id.variables['vhice'][:, :]
            # Unrotate vectors
            uhice_nodes_lr, vhice_nodes_lr = unrotate_vector(
                rlon_lr, rlat_lr, urhice_nodes_lr, vrhice_nodes_lr)
        id.close()
    if var_name != 'div':
        # Count the number of elements not in ice shelf cavities
        num_elm_lr = 0
        for elm in elements_lr:
            if not elm.cavity:
                num_elm_lr += 1
        # Set up array for element-averages for each season
        if var_name == 'f/h':
            # No seasonal variation
            data_lr = zeros(num_elm_lr)
        else:
            data_lr = zeros([4, num_elm_lr])
        # Loop over elements to fill this in
        i = 0
        for elm in elements_lr:
            if not elm.cavity:
                # Average over 3 component nodes
                if var_name == 'f/h':
                    data_lr[i] = (
                        abs(2 * omega * sin(elm.nodes[0].lat * deg2rad) /
                            elm.nodes[0].find_bottom().depth) +
                        abs(2 * omega * sin(elm.nodes[1].lat * deg2rad) /
                            elm.nodes[1].find_bottom().depth) +
                        abs(2 * omega * sin(elm.nodes[2].lat * deg2rad) /
                            elm.nodes[2].find_bottom().depth)) / 3
                else:
                    data_lr[:, i] = (data_nodes_lr[:, elm.nodes[0].id] +
                                     data_nodes_lr[:, elm.nodes[1].id] +
                                     data_nodes_lr[:, elm.nodes[2].id]) / 3
                i += 1
    if var_name == 'vel':
        # Make vectors for overlay
        # First set up arrays to integrate velocity in each bin
        # Simple averaging of all the points inside each bin
        ubin_lr = zeros([4, size(y_centres), size(x_centres)])
        vbin_lr = zeros([4, size(y_centres), size(x_centres)])
        num_pts_lr = zeros([4, size(y_centres), size(x_centres)])
        # First convert to polar coordinates, rotate to account for
        # longitude in circumpolar projection, and convert back to vector
        # components
        theta_lr = arctan2(v_nodes_lr, u_nodes_lr)
        theta_circ_lr = theta_lr - lon_lr * deg2rad
        u_circ_lr = data_nodes_lr * cos(theta_circ_lr)
        v_circ_lr = data_nodes_lr * sin(theta_circ_lr)
        # Loop over 2D nodes
        for n in range(n2d_lr):
            # Ignore cavities
            if not cavity_lr[n]:
                if x_lr[n] > x_min and x_lr[n] < x_max and y_lr[
                        n] > y_min and y_lr[n] < y_max:
                    # Figure out which bins this falls into
                    x_index = nonzero(x_bins > x_lr[n])[0][0] - 1
                    y_index = nonzero(y_bins > y_lr[n])[0][0] - 1
                    # Integrate
                    ubin_lr[:, y_index, x_index] += u_circ_lr[:, n]
                    vbin_lr[:, y_index, x_index] += v_circ_lr[:, n]
                    num_pts_lr[:, y_index, x_index] += 1
        # Convert from sums to averages
        # First mask out points with no data
        ubin_lr = ma.masked_where(num_pts_lr == 0, ubin_lr)
        vbin_lr = ma.masked_where(num_pts_lr == 0, vbin_lr)
        # Divide everything else by number of points
        flag = num_pts_lr > 0
        ubin_lr[flag] = ubin_lr[flag] / num_pts_lr[flag]
        vbin_lr[flag] = vbin_lr[flag] / num_pts_lr[flag]
    if var_name == 'div':
        # Interpolate to regular grid
        # For each element, check if a point on the regular lat-lon grid lies
        # within. If so, do barycentric interpolation to that point.
        for elm in elements_lr:
            # Don't care about ice shelf cavities
            if not elm.cavity:
                # Check if we are within domain of regular grid
                if amax(elm.lon) > lon_min and amin(
                        elm.lon) < lon_max and amax(
                            elm.lat) > lat_min and amin(elm.lat) < lat_max:
                    # Find largest regular longitude value west of Element
                    tmp = nonzero(lon_reg > amin(elm.lon))[0]
                    if len(tmp) == 0:
                        # Element crosses the western boundary
                        iW = 0
                    else:
                        iW = tmp[0] - 1
                    # Find smallest regular longitude value east of Element
                    tmp = nonzero(lon_reg > amax(elm.lon))[0]
                    if len(tmp) == 0:
                        # Element crosses the eastern boundary
                        iE = num_lon
                    else:
                        iE = tmp[0]
                    # Find largest regular latitude value south of Element
                    tmp = nonzero(lat_reg > amin(elm.lat))[0]
                    if len(tmp) == 0:
                        # Element crosses the southern boundary
                        jS = 0
                    else:
                        jS = tmp[0] - 1
                    # Find smallest regular latitude value north of Element
                    tmp = nonzero(lat_reg > amax(elm.lat))[0]
                    if len(tmp) == 0:
                        # Element crosses the northern boundary
                        jN = num_lat
                    else:
                        jN = tmp[0]
                    for i in range(iW + 1, iE):
                        for j in range(jS + 1, jN):
                            # There is a chance that the regular gridpoint at (i,j)
                            # lies within this element
                            lon0 = lon_reg[i]
                            lat0 = lat_reg[j]
                            if in_triangle(elm, lon0, lat0):
                                # Yes it does
                                # Get area of entire triangle
                                area = triangle_area(elm.lon, elm.lat)
                                # Get area of each sub-triangle formed by
                                # (lon0, lat0)
                                area0 = triangle_area(
                                    [lon0, elm.lon[1], elm.lon[2]],
                                    [lat0, elm.lat[1], elm.lat[2]])
                                area1 = triangle_area(
                                    [lon0, elm.lon[0], elm.lon[2]],
                                    [lat0, elm.lat[0], elm.lat[2]])
                                area2 = triangle_area(
                                    [lon0, elm.lon[0], elm.lon[1]],
                                    [lat0, elm.lat[0], elm.lat[1]])
                                # Find fractional area of each
                                cff = [
                                    area0 / area, area1 / area, area2 / area
                                ]
                                # Loop over seasons
                                for season in range(4):
                                    # Find value of uhice and vhice at each Node
                                    uhice_vals = []
                                    vhice_vals = []
                                    for n in range(3):
                                        uhice_vals.append(
                                            uhice_nodes_lr[season,
                                                           elm.nodes[n].id])
                                        vhice_vals.append(
                                            vhice_nodes_lr[season,
                                                           elm.nodes[n].id])
                                    # Barycentric interpolation to lon0, lat0
                                    uhice_reg_lr[season, j, i] = sum(
                                        array(cff) * array(uhice_vals))
                                    vhice_reg_lr[season, j, i] = sum(
                                        array(cff) * array(vhice_vals))
        # Save land mask: wherever identically zero
        mask_lr = ones([num_lat, num_lon])
        index = uhice_reg_lr[0, :, :] == 0.0
        mask_lr[index] = 0.0
        # Calculate divergence
        div_lr = ma.empty(shape(uhice_reg_lr))
        # Loop over seasons
        for season in range(4):
            # First calculate the two derivatives
            duhice_dx_lr = zeros([num_lat, num_lon])
            # Forward difference approximation
            duhice_dx_lr[:, :-1] = (uhice_reg_lr[season, :, 1:] -
                                    uhice_reg_lr[season, :, :-1]) / dx[:, :-1]
            # Backward difference for the last row
            duhice_dx_lr[:, -1] = (uhice_reg_lr[season, :, -1] -
                                   uhice_reg_lr[season, :, -2]) / dx[:, -1]
            dvhice_dy_lr = zeros([num_lat, num_lon])
            dvhice_dy_lr[:-1, :] = (vhice_reg_lr[season, 1:, :] -
                                    vhice_reg_lr[season, :-1, :]) / dy[:-1, :]
            dvhice_dy_lr[-1, :] = (vhice_reg_lr[season, -1, :] -
                                   vhice_reg_lr[season, -2, :]) / dy[-1, :]
            # Sum for the divergence
            div_lr_tmp = duhice_dx_lr + dvhice_dy_lr
            # Apply land mask
            div_lr[season, :, :] = ma.masked_where(mask_lr == 0, div_lr_tmp)
        # Multiply by 10^7 so colourbar is more readable
        div_lr *= 1e7

    print 'Processing high-res FESOM'
    elements_hr, patches_hr = make_patches(mesh_path_hr, circumpolar,
                                           mask_cavities)
    if var_name in ['div', 'vel']:
        file = open(mesh_path_hr + 'nod2d.out', 'r')
        file.readline()
        rlon_hr = []
        rlat_hr = []
        for line in file:
            tmp = line.split()
            lon_tmp = float(tmp[1])
            lat_tmp = float(tmp[2])
            if lon_tmp < -180:
                lon_tmp += 360
            elif lon_tmp > 180:
                lon_tmp -= 360
            rlon_hr.append(lon_tmp)
            rlat_hr.append(lat_tmp)
        file.close()
        rlon_hr = array(rlon_hr)
        rlat_hr = array(rlat_hr)
        if var_name == 'vel':
            lon_hr, lat_hr = unrotate_grid(rlon_hr, rlat_hr)
            x_hr = -(lat_hr + 90) * cos(lon_hr * deg2rad + pi / 2)
            y_hr = (lat_hr + 90) * sin(lon_hr * deg2rad + pi / 2)
            cavity_hr = []
            f = open(mesh_path_hr + 'cavity_flag_nod2d.out', 'r')
            for line in f:
                tmp = int(line)
                if tmp == 1:
                    cavity_hr.append(True)
                elif tmp == 0:
                    cavity_hr.append(False)
                else:
                    print 'Problem'
            f.close()
    if var_name != 'f/h':
        id = Dataset(directory_hr + seasonal_file, 'r')
        if var_name == 'hi':
            data_nodes_hr = id.variables['hice'][:, :]
        elif var_name == 'thdgr':
            data_nodes_hr = id.variables['thdgr'][:, :] * 1e7
        elif var_name == 'sst':
            data_nodes_hr = id.variables['temp'][:, :]
        elif var_name == 'div':
            urhice_nodes_hr = id.variables['uhice'][:, :]
            vrhice_nodes_hr = id.variables['vhice'][:, :]
            uhice_nodes_hr, vhice_nodes_hr = unrotate_vector(
                rlon_hr, rlat_hr, urhice_nodes_hr, vrhice_nodes_hr)
        elif var_name == 'vel':
            # Only read the surface nodes
            n2d_hr = size(rlon_hr)
            ur_nodes_hr = id.variables['u'][:, :n2d_hr]
            vr_nodes_hr = id.variables['v'][:, :n2d_hr]
            # Unrotate vectors
            u_nodes_hr, v_nodes_hr = unrotate_vector(rlon_hr, rlat_hr,
                                                     ur_nodes_hr, vr_nodes_hr)
            data_nodes_hr = sqrt(u_nodes_hr**2 + v_nodes_hr**2)
        id.close()
    if var_name != 'div':
        num_elm_hr = 0
        for elm in elements_hr:
            if not elm.cavity:
                num_elm_hr += 1
        if var_name == 'f/h':
            data_hr = zeros(num_elm_hr)
        else:
            data_hr = zeros([4, num_elm_hr])
        i = 0
        for elm in elements_hr:
            if not elm.cavity:
                if var_name == 'f/h':
                    data_hr[i] = (
                        abs(2 * omega * sin(elm.nodes[0].lat * deg2rad) /
                            elm.nodes[0].find_bottom().depth) +
                        abs(2 * omega * sin(elm.nodes[1].lat * deg2rad) /
                            elm.nodes[1].find_bottom().depth) +
                        abs(2 * omega * sin(elm.nodes[2].lat * deg2rad) /
                            elm.nodes[2].find_bottom().depth)) / 3
                else:
                    data_hr[:, i] = (data_nodes_hr[:, elm.nodes[0].id] +
                                     data_nodes_hr[:, elm.nodes[1].id] +
                                     data_nodes_hr[:, elm.nodes[2].id]) / 3
                i += 1
    if var_name == 'vel':
        ubin_hr = zeros([4, size(y_centres), size(x_centres)])
        vbin_hr = zeros([4, size(y_centres), size(x_centres)])
        num_pts_hr = zeros([4, size(y_centres), size(x_centres)])
        theta_hr = arctan2(v_nodes_hr, u_nodes_hr)
        theta_circ_hr = theta_hr - lon_hr * deg2rad
        u_circ_hr = data_nodes_hr * cos(theta_circ_hr)
        v_circ_hr = data_nodes_hr * sin(theta_circ_hr)
        for n in range(n2d_hr):
            if not cavity_hr[n]:
                if x_hr[n] > x_min and x_hr[n] < x_max and y_hr[
                        n] > y_min and y_hr[n] < y_max:
                    x_index = nonzero(x_bins > x_hr[n])[0][0] - 1
                    y_index = nonzero(y_bins > y_hr[n])[0][0] - 1
                    ubin_hr[:, y_index, x_index] += u_circ_hr[:, n]
                    vbin_hr[:, y_index, x_index] += v_circ_hr[:, n]
                    num_pts_hr[:, y_index, x_index] += 1
        ubin_hr = ma.masked_where(num_pts_hr == 0, ubin_hr)
        vbin_hr = ma.masked_where(num_pts_hr == 0, vbin_hr)
        flag = num_pts_hr > 0
        ubin_hr[flag] = ubin_hr[flag] / num_pts_hr[flag]
        vbin_hr[flag] = vbin_hr[flag] / num_pts_hr[flag]
    if var_name == 'div':
        for elm in elements_hr:
            if not elm.cavity:
                if amax(elm.lon) > lon_min and amin(
                        elm.lon) < lon_max and amax(
                            elm.lat) > lat_min and amin(elm.lat) < lat_max:
                    tmp = nonzero(lon_reg > amin(elm.lon))[0]
                    if len(tmp) == 0:
                        iW = 0
                    else:
                        iW = tmp[0] - 1
                    tmp = nonzero(lon_reg > amax(elm.lon))[0]
                    if len(tmp) == 0:
                        iE = num_lon
                    else:
                        iE = tmp[0]
                    tmp = nonzero(lat_reg > amin(elm.lat))[0]
                    if len(tmp) == 0:
                        jS = 0
                    else:
                        jS = tmp[0] - 1
                    tmp = nonzero(lat_reg > amax(elm.lat))[0]
                    if len(tmp) == 0:
                        jN = num_lat
                    else:
                        jN = tmp[0]
                    for i in range(iW + 1, iE):
                        for j in range(jS + 1, jN):
                            lon0 = lon_reg[i]
                            lat0 = lat_reg[j]
                            if in_triangle(elm, lon0, lat0):
                                area = triangle_area(elm.lon, elm.lat)
                                area0 = triangle_area(
                                    [lon0, elm.lon[1], elm.lon[2]],
                                    [lat0, elm.lat[1], elm.lat[2]])
                                area1 = triangle_area(
                                    [lon0, elm.lon[0], elm.lon[2]],
                                    [lat0, elm.lat[0], elm.lat[2]])
                                area2 = triangle_area(
                                    [lon0, elm.lon[0], elm.lon[1]],
                                    [lat0, elm.lat[0], elm.lat[1]])
                                cff = [
                                    area0 / area, area1 / area, area2 / area
                                ]
                                for season in range(4):
                                    uhice_vals = []
                                    vhice_vals = []
                                    for n in range(3):
                                        uhice_vals.append(
                                            uhice_nodes_hr[season,
                                                           elm.nodes[n].id])
                                        vhice_vals.append(
                                            vhice_nodes_hr[season,
                                                           elm.nodes[n].id])
                                    uhice_reg_hr[season, j, i] = sum(
                                        array(cff) * array(uhice_vals))
                                    vhice_reg_hr[season, j, i] = sum(
                                        array(cff) * array(vhice_vals))
        mask_hr = ones([num_lat, num_lon])
        index = uhice_reg_hr[0, :, :] == 0.0
        mask_hr[index] = 0.0
        div_hr = ma.empty(shape(uhice_reg_hr))
        for season in range(4):
            duhice_dx_hr = zeros([num_lat, num_lon])
            duhice_dx_hr[:, :-1] = (uhice_reg_hr[season, :, 1:] -
                                    uhice_reg_hr[season, :, :-1]) / dx[:, :-1]
            duhice_dx_hr[:, -1] = (uhice_reg_hr[season, :, -1] -
                                   uhice_reg_hr[season, :, -2]) / dx[:, -1]
            dvhice_dy_hr = zeros([num_lat, num_lon])
            dvhice_dy_hr[:-1, :] = (vhice_reg_hr[season, 1:, :] -
                                    vhice_reg_hr[season, :-1, :]) / dy[:-1, :]
            dvhice_dy_hr[-1, :] = (vhice_reg_hr[season, -1, :] -
                                   vhice_reg_hr[season, -2, :]) / dy[-1, :]
            div_hr_tmp = duhice_dx_hr + dvhice_dy_hr
            div_hr[season, :, :] = ma.masked_where(mask_hr == 0, div_hr_tmp)
        div_hr *= 1e7

    print 'Plotting'
    if var_name == 'f/h':
        fig = figure(figsize=(6, 9))
        num_t = 1
    else:
        fig = figure(figsize=(19, 9))
        num_t = 4
    for season in range(num_t):
        # Low-res
        ax = fig.add_subplot(2, num_t, season + 1, aspect='equal')
        if var_name == 'div':
            img = contourf(x_reg,
                           y_reg,
                           div_lr[season, :, :],
                           lev,
                           cmap=colour_map,
                           extend='both')
        else:
            img = PatchCollection(patches_lr, cmap=colour_map)
            if var_name == 'f/h':
                img.set_array(data_lr)
            else:
                img.set_array(data_lr[season, :])
            img.set_clim(vmin=bounds[0], vmax=bounds[1])
            img.set_edgecolor('face')
            ax.add_collection(img)
            if var_name == 'vel':
                # Overlay vectors
                quiver(x_centres,
                       y_centres,
                       ubin_lr[season, :, :],
                       vbin_lr[season, :, :],
                       scale=0.9,
                       headwidth=8,
                       headlength=9,
                       color='black')
        if var_name != 'f/h':
            title(season_names[season], fontsize=24)
        xlim([x_min, x_max])
        ylim([y_min, y_max])
        axis('off')
        if season == 0:
            if var_name == 'f/h':
                text(-24, 14, 'low-res', fontsize=24, ha='right', rotation=90)
            else:
                text(-24, 14, 'low-res', fontsize=24, ha='right')
        # High-res
        ax = fig.add_subplot(2, num_t, season + num_t + 1, aspect='equal')
        if var_name == 'div':
            img = contourf(x_reg,
                           y_reg,
                           div_hr[season, :, :],
                           lev,
                           cmap=colour_map,
                           extend='both')
        else:
            img = PatchCollection(patches_hr, cmap=colour_map)
            if var_name == 'f/h':
                img.set_array(data_hr)
            else:
                img.set_array(data_hr[season, :])
            img.set_clim(vmin=bounds[0], vmax=bounds[1])
            img.set_edgecolor('face')
            ax.add_collection(img)
            if var_name == 'vel':
                quiver(x_centres,
                       y_centres,
                       ubin_hr[season, :, :],
                       vbin_hr[season, :, :],
                       scale=0.9,
                       headwidth=8,
                       headlength=9,
                       color='black')
        xlim([x_min, x_max])
        ylim([y_min, y_max])
        axis('off')
        if season == 0:
            if var_name == 'f/h':
                text(-24, 14, 'high-res', fontsize=24, ha='right', rotation=90)
            else:
                text(-24, 14, 'high-res', fontsize=24, ha='right')
    cbaxes = fig.add_axes([0.35, 0.04, 0.3, 0.02])
    cbar = colorbar(img,
                    orientation='horizontal',
                    cax=cbaxes,
                    ticks=cbar_ticks,
                    extend='both')
    cbar.ax.tick_params(labelsize=20)
    if var_name == 'hi':
        suptitle('FESOM sea ice effective thickness (m), 1992-2016 average',
                 fontsize=30)
    elif var_name == 'thdgr':
        suptitle(
            r'FESOM sea ice thermodynamic growth rate (10$^{-7}$ m/s), 1992-2016 average',
            fontsize=30)
    elif var_name == 'sst':
        suptitle(
            r'FESOM sea surface temperature ($^{\circ}$C), 1992-2016 average',
            fontsize=30)
    elif var_name == 'div':
        suptitle(
            r'FESOM sea ice flux divergence (10$^{-7}$ m/s), 1992-2016 average',
            fontsize=30)
    elif var_name == 'vel':
        suptitle('FESOM surface ocean velocity (m/s), 1992-2016 average',
                 fontsize=30)
    elif var_name == 'f/h':
        suptitle('abs(f/h)', fontsize=30)
    subplots_adjust(wspace=0.025, hspace=0.025)
    fig.show()
    if var_name == 'f/h':
        fig.savefig('f_h_peninsula_res.png')
    else:
        fig.savefig(var_name + '_peninsula_res.png')
예제 #6
0
def mip_sfc_stress():

    # File paths
    roms_grid = '/short/m68/kaa561/metroms_iceshelf/apps/common/grid/circ30S_quarterdegree.nc'
    roms_file = '/short/m68/kaa561/metroms_iceshelf/tmproms/run/intercomparison/stress_firstyear.nc'  # Already averaged over first year
    fesom_mesh_path_lr = '/short/y99/kaa561/FESOM/mesh/meshA/'
    fesom_mesh_path_hr = '/short/y99/kaa561/FESOM/mesh/meshB/'
    fesom_file_lr = '/short/y99/kaa561/FESOM/intercomparison_lowres/output/MK44005.1992.forcing.diag.nc'
    fesom_file_hr = '/short/y99/kaa561/FESOM/intercomparison_highres/output/MK44005.1992.forcing.diag.nc'
    # Degrees to radians conversion factor
    deg2rad = pi / 180.0
    # Northern boundaries for plots
    nbdry_acc = -30 + 90
    nbdry_shelf = -64 + 90
    # Bounds for colour scale
    colour_bound_acc = 0.25
    colour_bound_shelf = 0.25

    print 'Processing ROMS'
    # Read grid
    id = Dataset(roms_grid, 'r')
    roms_lat = id.variables['lat_rho'][:, :]
    roms_lon = id.variables['lon_rho'][:, :]
    angle = id.variables['angle'][:, :]
    zice = id.variables['zice'][:, :]
    id.close()
    # Read surface stress
    id = Dataset(roms_file, 'r')
    sustr_tmp = id.variables['sustr'][0, :, :]
    svstr_tmp = id.variables['svstr'][0, :, :]
    id.close()
    # Unrotate
    sustr, svstr = rotate_vector_roms(sustr_tmp, svstr_tmp, angle)
    # Get magnitude
    roms_stress = sqrt(sustr**2 + svstr**2)
    # Mask cavities
    roms_stress = ma.masked_where(zice < 0, roms_stress)
    # Calculate polar projection
    roms_x = -(roms_lat + 90) * cos(roms_lon * deg2rad + pi / 2)
    roms_y = (roms_lat + 90) * sin(roms_lon * deg2rad + pi / 2)

    print 'Processing low-res FESOM'
    # Build mesh and patches
    elements_lr, patches_lr = make_patches(fesom_mesh_path_lr,
                                           circumpolar=True,
                                           mask_cavities=True)
    # Read rotated and and lon
    f = open(fesom_mesh_path_lr + 'nod2d.out', 'r')
    f.readline()
    rlon_lr = []
    rlat_lr = []
    for line in f:
        tmp = line.split()
        lon_tmp = float(tmp[1])
        if lon_tmp < -180:
            lon_tmp += 360
        elif lon_tmp > 180:
            lon_tmp -= 360
        rlon_lr.append(lon_tmp)
        rlat_lr.append(float(tmp[2]))
    f.close()
    rlon_lr = array(rlon_lr)
    rlat_lr = array(rlat_lr)
    # Read surface stress
    id = Dataset(fesom_file_lr, 'r')
    stress_x_tmp = mean(id.variables['stress_x'][:, :], axis=0)
    stress_y_tmp = mean(id.variables['stress_y'][:, :], axis=0)
    id.close()
    # Unrotate
    stress_x_lr, stress_y_lr = unrotate_vector(rlon_lr, rlat_lr, stress_x_tmp,
                                               stress_y_tmp)
    # Get magnitude
    fesom_stress_lr_nodes = sqrt(stress_x_lr**2 + stress_y_lr**2)
    # Average over elements
    fesom_stress_lr = []
    for elm in elements_lr:
        if not elm.cavity:
            fesom_stress_lr.append(
                mean([
                    fesom_stress_lr_nodes[elm.nodes[0].id],
                    fesom_stress_lr_nodes[elm.nodes[1].id],
                    fesom_stress_lr_nodes[elm.nodes[2].id]
                ]))

    print 'Processing high-res FESOM'
    elements_hr, patches_hr = make_patches(fesom_mesh_path_hr,
                                           circumpolar=True,
                                           mask_cavities=True)
    f = open(fesom_mesh_path_hr + 'nod2d.out', 'r')
    f.readline()
    rlon_hr = []
    rlat_hr = []
    for line in f:
        tmp = line.split()
        lon_tmp = float(tmp[1])
        if lon_tmp < -180:
            lon_tmp += 360
        elif lon_tmp > 180:
            lon_tmp -= 360
        rlon_hr.append(lon_tmp)
        rlat_hr.append(float(tmp[2]))
    f.close()
    rlon_hr = array(rlon_hr)
    rlat_hr = array(rlat_hr)
    id = Dataset(fesom_file_hr, 'r')
    stress_x_tmp = mean(id.variables['stress_x'][:, :], axis=0)
    stress_y_tmp = mean(id.variables['stress_y'][:, :], axis=0)
    id.close()
    stress_x_hr, stress_y_hr = unrotate_vector(rlon_hr, rlat_hr, stress_x_tmp,
                                               stress_y_tmp)
    fesom_stress_hr_nodes = sqrt(stress_x_hr**2 + stress_y_hr**2)
    fesom_stress_hr = []
    for elm in elements_hr:
        if not elm.cavity:
            fesom_stress_hr.append(
                mean([
                    fesom_stress_hr_nodes[elm.nodes[0].id],
                    fesom_stress_hr_nodes[elm.nodes[1].id],
                    fesom_stress_hr_nodes[elm.nodes[2].id]
                ]))

    print 'Plotting'

    # ACC
    fig = figure(figsize=(19, 8))
    fig.patch.set_facecolor('white')
    gs = GridSpec(1, 3)
    gs.update(left=0.05, right=0.95, bottom=0.1, top=0.85, wspace=0.05)
    # ROMS
    ax = subplot(gs[0, 0], aspect='equal')
    ax.pcolor(roms_x,
              roms_y,
              roms_stress,
              vmin=0,
              vmax=colour_bound_acc,
              cmap='jet')
    xlim([-nbdry_acc, nbdry_acc])
    ylim([-nbdry_acc, nbdry_acc])
    title('a) MetROMS', fontsize=28)
    ax.set_xticks([])
    ax.set_yticks([])
    # FESOM (low-res)
    ax = subplot(gs[0, 1], aspect='equal')
    img = PatchCollection(patches_lr, cmap='jet')
    img.set_array(array(fesom_stress_lr))
    img.set_clim(vmin=0, vmax=colour_bound_acc)
    img.set_edgecolor('face')
    ax.add_collection(img)
    xlim([-nbdry_acc, nbdry_acc])
    ylim([-nbdry_acc, nbdry_acc])
    title('b) FESOM (low-res)', fontsize=28)
    ax.set_xticks([])
    ax.set_yticks([])
    # FESOM (high-res)
    ax = subplot(gs[0, 2], aspect='equal')
    img = PatchCollection(patches_hr, cmap='jet')
    img.set_array(array(fesom_stress_hr))
    img.set_clim(vmin=0, vmax=colour_bound_acc)
    img.set_edgecolor('face')
    ax.add_collection(img)
    xlim([-nbdry_acc, nbdry_acc])
    ylim([-nbdry_acc, nbdry_acc])
    title('c) FESOM (high-res)', fontsize=28)
    ax.set_xticks([])
    ax.set_yticks([])
    # Add a horizontal colourbar on the bottom
    cbaxes = fig.add_axes([0.3, 0.05, 0.4, 0.04])
    cbar = colorbar(img,
                    orientation='horizontal',
                    cax=cbaxes,
                    extend='max',
                    ticks=arange(0, colour_bound_acc + 0.05, 0.05))
    cbar.ax.tick_params(labelsize=20)
    # Main title
    suptitle(r'Ocean surface stress (N/m$^2$), 1992 mean', fontsize=34)
    fig.show()
    fig.savefig('sfc_stress_acc.png')

    # Continental shelf
    fig = figure(figsize=(19, 8))
    fig.patch.set_facecolor('white')
    gs = GridSpec(1, 3)
    gs.update(left=0.05, right=0.95, bottom=0.1, top=0.85, wspace=0.05)
    # ROMS
    ax = subplot(gs[0, 0], aspect='equal')
    ax.pcolor(roms_x,
              roms_y,
              roms_stress,
              vmin=0,
              vmax=colour_bound_shelf,
              cmap='jet')
    xlim([-nbdry_shelf, nbdry_shelf])
    ylim([-nbdry_shelf, nbdry_shelf])
    title('a) MetROMS', fontsize=28)
    ax.set_xticks([])
    ax.set_yticks([])
    # FESOM (low-res)
    ax = subplot(gs[0, 1], aspect='equal')
    img = PatchCollection(patches_lr, cmap='jet')
    img.set_array(array(fesom_stress_lr))
    img.set_clim(vmin=0, vmax=colour_bound_shelf)
    img.set_edgecolor('face')
    ax.add_collection(img)
    xlim([-nbdry_shelf, nbdry_shelf])
    ylim([-nbdry_shelf, nbdry_shelf])
    title('b) FESOM (low-res)', fontsize=28)
    ax.set_xticks([])
    ax.set_yticks([])
    # FESOM (high-res)
    ax = subplot(gs[0, 2], aspect='equal')
    img = PatchCollection(patches_hr, cmap='jet')
    img.set_array(array(fesom_stress_hr))
    img.set_clim(vmin=0, vmax=colour_bound_shelf)
    img.set_edgecolor('face')
    ax.add_collection(img)
    xlim([-nbdry_shelf, nbdry_shelf])
    ylim([-nbdry_shelf, nbdry_shelf])
    title('c) FESOM (high-res)', fontsize=28)
    ax.set_xticks([])
    ax.set_yticks([])
    # Add a horizontal colourbar on the bottom
    cbaxes = fig.add_axes([0.3, 0.05, 0.4, 0.04])
    cbar = colorbar(img,
                    orientation='horizontal',
                    cax=cbaxes,
                    extend='max',
                    ticks=arange(0, colour_bound_shelf + 0.05, 0.05))
    cbar.ax.tick_params(labelsize=20)
    # Main title
    suptitle(r'Ocean surface stress (N/m$^2$), 1992 mean', fontsize=34)
    fig.show()
    fig.savefig('sfc_stress_shelf.png')
예제 #7
0
def mip_regions_1var ():

    # Path to ROMS grid file
    roms_grid = '/short/m68/kaa561/metroms_iceshelf/apps/common/grid/circ30S_quarterdegree.nc'
    # Path to ROMS time-averaged file
    roms_file = '/short/m68/kaa561/metroms_iceshelf/tmproms/run/intercomparison/2002_2016_avg.nc'
    # Path to FESOM mesh directories
    fesom_mesh_path_lr = '/short/y99/kaa561/FESOM/mesh/meshA/'
    fesom_mesh_path_hr = '/short/y99/kaa561/FESOM/mesh/meshB/'
    # Path to FESOM time-averaged ocean files (temp, salt, u, v)
    fesom_file_lr_o = '/short/y99/kaa561/FESOM/intercomparison_lowres/output/oce_2002_2016_avg.nc'
    fesom_file_hr_o = '/short/y99/kaa561/FESOM/intercomparison_highres/output/oce_2002_2016_avg.nc'
    # Path to FESOM time-averaged ice shelf files (wnet)
    fesom_file_lr_i = '/short/y99/kaa561/FESOM/intercomparison_lowres/output/wnet_2002_2016_avg.nc'
    fesom_file_hr_i = '/short/y99/kaa561/FESOM/intercomparison_highres/output/wnet_2002_2016_avg.nc'

    # Name of each region
    region_names = ['Filchner-Ronne Ice Shelf', 'Eastern Weddell Region', 'Amery Ice Shelf', 'Australian Sector', 'Ross Sea', 'Amundsen Sea', 'Bellingshausen Sea', 'Larsen Ice Shelves']
    num_regions = len(region_names)
    # Beginning of filenames for figures
    fig_heads = ['filchner_ronne', 'eweddell', 'amery', 'australian', 'ross', 'amundsen', 'bellingshausen', 'larsen']
    # Bounds for each region (using polar coordinate transformation as below)
    x_min = [-14, -8, 15.25, 12, -9.5, -15.5, -20.25, -22.5]
    x_max = [-4.5, 13, 20.5, 25.5, 4, -10.5, -15.5, -14.5]
    y_min = [1, 12, 4.75, -20, -13, -11.25, -4.5, 8.3]
    y_max = [10, 21, 8, 4, -4.75, -2.25, 7.6, 13]
    # Size of each plot in the y direction
    ysize = [8, 6, 7, 9, 7, 9, 10, 7]
    # Variables to process
    var_names = ['vel'] #['bathy', 'draft', 'wct', 'melt', 'temp', 'salt', 'vel']
    # Constants
    sec_per_year = 365*24*3600
    deg2rad = pi/180.0
    # Parameters for missing circle in ROMS grid
    lon_c = 50
    lat_c = -83
    radius = 10.1
    nbdry = -63+90
    # ROMS vertical grid parameters
    theta_s = 7.0
    theta_b = 2.0
    hc = 250
    N = 31
    # Number of bins in each direction for vector overlay
    num_bins = 30

    print 'Reading ROMS grid'
    # Read the fields we need
    id = Dataset(roms_grid, 'r')
    roms_lon = id.variables['lon_rho'][:,:]
    roms_lat = id.variables['lat_rho'][:,:]
    roms_h = id.variables['h'][:,:]
    roms_mask = id.variables['mask_rho'][:,:]
    roms_zice = id.variables['zice'][:,:]
    roms_angle = id.variables['angle'][:,:]
    id.close()
    # Get land/zice mask
    open_ocn = copy(roms_mask)
    open_ocn[roms_zice!=0] = 0
    land_zice = ma.masked_where(open_ocn==1, open_ocn)
    # Convert grid to spherical coordinates
    roms_x = -(roms_lat+90)*cos(roms_lon*deg2rad+pi/2)
    roms_y = (roms_lat+90)*sin(roms_lon*deg2rad+pi/2)
    # Find centre in spherical coordinates
    x_c = -(lat_c+90)*cos(lon_c*deg2rad+pi/2)
    y_c = (lat_c+90)*sin(lon_c*deg2rad+pi/2)
    # Build a regular x-y grid and select the missing circle
    x_reg_roms, y_reg_roms = meshgrid(linspace(-nbdry, nbdry, num=1000), linspace(-nbdry, nbdry, num=1000))
    land_circle = zeros(shape(x_reg_roms))
    land_circle = ma.masked_where(sqrt((x_reg_roms-x_c)**2 + (y_reg_roms-y_c)**2) > radius, land_circle)

    print 'Building FESOM low-res mesh'
    elements_lr = fesom_grid(fesom_mesh_path_lr, circumpolar=True)
    # Make patches for all elements, ice shelf elements, and open ocean elements
    patches_lr = []
    patches_shelf_lr = []
    patches_ocn_lr = []
    for elm in elements_lr:
        coord = transpose(vstack((elm.x, elm.y)))
        patches_lr.append(Polygon(coord, True, linewidth=0.))
        if elm.cavity:
            patches_shelf_lr.append(Polygon(coord, True, linewidth=0.))
        else:
            patches_ocn_lr.append(Polygon(coord, True, linewidth=0.))

    print 'Building FESOM high-res mesh'
    elements_hr = fesom_grid(fesom_mesh_path_hr, circumpolar=True)
    patches_hr = []
    patches_shelf_hr = []
    patches_ocn_hr = []
    for elm in elements_hr:
        coord = transpose(vstack((elm.x, elm.y)))
        patches_hr.append(Polygon(coord, True, linewidth=0.))
        if elm.cavity:
            patches_shelf_hr.append(Polygon(coord, True, linewidth=0.))
        else:
            patches_ocn_hr.append(Polygon(coord, True, linewidth=0.))

    for var in var_names:
        print 'Processing variable ' + var

        print 'Reading ROMS fields'
        if var == 'draft':
            # Swap sign on existing zice field; nothing more to read
            roms_data = -1*roms_zice
        elif var == 'bathy':
            # Point to h field and mask out land mask; nothing more to read
            roms_data = ma.masked_where(roms_mask==0, roms_h)
        elif var == 'wct':
            # Add h (positive) and zice (negative); nothing more to read
            roms_data = roms_h + roms_zice
        else:
            id = Dataset(roms_file, 'r')
            if var == 'melt':
                # Convert from m/s to m/y
                roms_data = id.variables['m'][0,:,:]*sec_per_year
            elif var in ['temp', 'salt']:
                # Bottom layer
                roms_data = id.variables[var][0,0,:,:]
            elif var == 'vel':
                # Read full 3D u and v
                u_3d_tmp = id.variables['u'][0,:,:,:]
                v_3d_tmp = id.variables['v'][0,:,:,:]
                # Get integrands on 3D grid; we only care about dz
                dx, dy, dz, z = cartesian_grid_3d(roms_lon, roms_lat, roms_h, roms_zice, theta_s, theta_b, hc, N)
                # Unrotate each vertical level
                u_3d = ma.empty(shape(dz))
                v_3d = ma.empty(shape(dz))
                num_lat_u = size(u_3d_tmp,1)
                num_lon_u = size(u_3d_tmp,2)
                num_lat_v = size(v_3d_tmp,1)
                num_lon_v = size(v_3d_tmp,2)
                for k in range(N):
                    # Extend into land mask before interpolation to rho-grid so
                    # the land mask doesn't change in the final plot
                    for j in range(1,num_lat_u-1):
                        for i in range(1,num_lon_u-1):
                            # Check for masked points
                            if u_3d_tmp[k,j,i] is ma.masked:
                                # Look at 4 neighbours
                                neighbours = ma.array([u_3d_tmp[k,j-1,i], u_3d_tmp[k,j,i-1], u_3d_tmp[k,j+1,i], u_3d_tmp[k,j,i+1]])
                                # Find how many of them are unmasked
                                num_unmasked = MaskedArray.count(neighbours)
                                if num_unmasked > 0:
                                    # There is at least one unmasked neighbour;
                                    # set u_3d_tmp to their average
                                    u_3d_tmp[k,j,i] = sum(neighbours)/num_unmasked
                    # Repeat for v
                    for j in range(1,num_lat_v-1):
                        for i in range(1,num_lon_v-1):
                            if v_3d_tmp[k,j,i] is ma.masked:
                                neighbours = ma.array([v_3d_tmp[k,j-1,i], v_3d_tmp[k,j,i-1], v_3d_tmp[k,j+1,i], v_3d_tmp[k,j,i+1]])
                                num_unmasked = MaskedArray.count(neighbours)
                                if num_unmasked > 0:
                                    v_3d_tmp[k,j,i] = sum(neighbours)/num_unmasked
                    # Interpolate to rho grid and rotate
                    u_k, v_k = rotate_vector_roms(u_3d_tmp[k,:,:], v_3d_tmp[k,:,:], roms_angle)
                    u_3d[k,:,:] = u_k
                    v_3d[k,:,:] = v_k
                # Vertically average u and v
                u_rho = sum(u_3d*dz, axis=0)/sum(dz, axis=0)
                v_rho = sum(v_3d*dz, axis=0)/sum(dz, axis=0)    
                # Get speed
                roms_data = sqrt(u_rho**2 + v_rho**2)
                # Mask out land
                u_rho = ma.masked_where(roms_mask==0, u_rho)
                v_rho = ma.masked_where(roms_mask==0, v_rho)
                roms_data = ma.masked_where(roms_mask==0, roms_data)
            id.close()
        if var in ['draft', 'melt', 'wct']:
            # Mask out open ocean
            roms_data = ma.masked_where(roms_zice==0, roms_data)

        print 'Reading FESOM low-res fields'
        if var not in ['draft', 'bathy', 'wct']:
            if var == 'melt':
                id = Dataset(fesom_file_lr_i, 'r')
                # Convert from m/s to m/y
                node_data_lr = id.variables['wnet'][0,:]*sec_per_year
            elif var in ['temp', 'salt']:
                id = Dataset(fesom_file_lr_o, 'r')
                # Read full 3D field for now
                node_data_lr = id.variables[var][0,:]
            elif var == 'vel':
                id = Dataset(fesom_file_lr_o, 'r')
                # The overlaid vectors are based on nodes not elements, so many
                # of the fesom_grid data structures fail to apply and we need to
                # read some of the FESOM grid files again.
                # Read the cavity flag for each 2D surface node
                fesom_cavity_lr = []
                f = open(fesom_mesh_path_lr + 'cavity_flag_nod2d.out', 'r')
                for line in f:
                    tmp = int(line)
                    if tmp == 1:
                        fesom_cavity_lr.append(True)
                    elif tmp == 0:
                        fesom_cavity_lr.append(False)
                    else:
                        print 'Problem'
                        #return
                f.close()
                # Save the number of 2D nodes
                fesom_n2d_lr = len(fesom_cavity_lr)
                # Read rotated lat and lon for each node, also depth
                f = open(fesom_mesh_path_lr + 'nod3d.out', 'r')
                f.readline()
                rlon_lr = []
                rlat_lr = []
                node_depth_lr = []
                for line in f:
                    tmp = line.split()
                    lon_tmp = float(tmp[1])
                    lat_tmp = float(tmp[2])
                    node_depth_tmp = -1*float(tmp[3])
                    if lon_tmp < -180:
                        lon_tmp += 360
                    elif lon_tmp > 180:
                        lon_tmp -= 360
                    rlon_lr.append(lon_tmp)
                    rlat_lr.append(lat_tmp)
                    node_depth_lr.append(node_depth_tmp)
                f.close()
                # For lat and lon, only care about the 2D nodes (the first
                # fesom_n2d indices)
                rlon_lr = array(rlon_lr[0:fesom_n2d_lr])
                rlat_lr = array(rlat_lr[0:fesom_n2d_lr])
                node_depth_lr = array(node_depth_lr)
                # Unrotate longitude
                fesom_lon_lr, fesom_lat_lr = unrotate_grid(rlon_lr, rlat_lr)
                # Calculate polar coordinates of each node
                fesom_x_lr = -(fesom_lat_lr+90)*cos(fesom_lon_lr*deg2rad+pi/2)
                fesom_y_lr = (fesom_lat_lr+90)*sin(fesom_lon_lr*deg2rad+pi/2)
                # Read lists of which nodes are directly below which
                f = open(fesom_mesh_path_lr + 'aux3d.out', 'r')
                max_num_layers_lr = int(f.readline())
                node_columns_lr = zeros([fesom_n2d_lr, max_num_layers_lr])
                for n in range(fesom_n2d_lr):
                    for k in range(max_num_layers_lr):
                        node_columns_lr[n,k] = int(f.readline())
                node_columns_lr = node_columns_lr.astype(int)
                f.close()
                # Now we can actually read the data
                # Read full 3D field for both u and v
                node_ur_3d_lr = id.variables['u'][0,:]
                node_vr_3d_lr = id.variables['v'][0,:]
                # Vertically average
                node_ur_lr = zeros(fesom_n2d_lr)
                node_vr_lr = zeros(fesom_n2d_lr)
                for n in range(fesom_n2d_lr):
                    # Integrate udz, vdz, and dz over this water column
                    udz_col = 0
                    vdz_col = 0
                    dz_col = 0
                    for k in range(max_num_layers_lr-1):
                        if node_columns_lr[n,k+1] == -999:
                            # Reached the bottom
                            break
                        # Trapezoidal rule
                        top_id = node_columns_lr[n,k]
                        bot_id = node_columns_lr[n,k+1]
                        dz_tmp = node_depth_lr[bot_id-1] - node_depth_lr[top_id-1]
                        udz_col += 0.5*(node_ur_3d_lr[top_id-1]+node_ur_3d_lr[bot_id-1])*dz_tmp
                        vdz_col += 0.5*(node_vr_3d_lr[top_id-1]+node_vr_3d_lr[bot_id-1])*dz_tmp
                        dz_col += dz_tmp
                    # Convert from integrals to averages
                    node_ur_lr[n] = udz_col/dz_col
                    node_vr_lr[n] = vdz_col/dz_col
                # Unrotate
                node_u_lr, node_v_lr = unrotate_vector(rlon_lr, rlat_lr, node_ur_lr, node_vr_lr)
                # Calculate speed
                node_data_lr = sqrt(node_u_lr**2 + node_v_lr**2)
            id.close()
        # Calculate given field at each element
        fesom_data_lr = []
        for elm in elements_lr:
            # For each element, append the mean value for the 3 component Nodes
            # Restrict to ice shelf cavities for draft, melt, wct
            if elm.cavity or var not in ['draft', 'melt', 'wct']:
                if var == 'draft':
                    # Ice shelf draft is depth of surface layer
                    fesom_data_lr.append(mean([elm.nodes[0].depth, elm.nodes[1].depth, elm.nodes[2].depth]))
                elif var == 'bathy':
                    # Bathymetry is depth of bottom layer
                    fesom_data_lr.append(mean([elm.nodes[0].find_bottom().depth, elm.nodes[1].find_bottom().depth, elm.nodes[2].find_bottom().depth]))
                elif var == 'wct':
                    # Water column thickness is depth of bottom layer minus
                    # depth of surface layer
                    fesom_data_lr.append(mean([elm.nodes[0].find_bottom().depth - elm.nodes[0].depth, elm.nodes[1].find_bottom().depth - elm.nodes[1].depth, elm.nodes[2].find_bottom().depth - elm.nodes[2].depth]))
                elif var in ['melt', 'vel']:
                    # Surface nodes
                    fesom_data_lr.append(mean([node_data_lr[elm.nodes[0].id], node_data_lr[elm.nodes[1].id], node_data_lr[elm.nodes[2].id]]))
                elif var in ['temp', 'salt']:
                    # Bottom nodes
                    fesom_data_lr.append(mean([node_data_lr[elm.nodes[0].find_bottom().id], node_data_lr[elm.nodes[1].find_bottom().id], node_data_lr[elm.nodes[2].find_bottom().id]]))

        print 'Reading FESOM high-res fields'
        # As before
        if var not in ['draft', 'bathy', 'wct']:
            if var == 'melt':
                id = Dataset(fesom_file_hr_i, 'r')
                node_data_hr = id.variables['wnet'][0,:]*sec_per_year
            elif var in ['temp', 'salt']:
                id = Dataset(fesom_file_hr_o, 'r')
                node_data_hr = id.variables[var][0,:]
            elif var == 'vel':
                id = Dataset(fesom_file_hr_o, 'r')
                fesom_cavity_hr = []
                f = open(fesom_mesh_path_hr + 'cavity_flag_nod2d.out', 'r')
                for line in f:
                    tmp = int(line)
                    if tmp == 1:
                        fesom_cavity_hr.append(True)
                    elif tmp == 0:
                        fesom_cavity_hr.append(False)
                    else:
                        print 'Problem'
                        #return
                f.close()
                fesom_n2d_hr = len(fesom_cavity_hr)
                f = open(fesom_mesh_path_hr + 'nod3d.out', 'r')
                f.readline()
                rlon_hr = []
                rlat_hr = []
                node_depth_hr = []
                for line in f:
                    tmp = line.split()
                    lon_tmp = float(tmp[1])
                    lat_tmp = float(tmp[2])
                    node_depth_tmp = -1*float(tmp[3])
                    if lon_tmp < -180:
                        lon_tmp += 360
                    elif lon_tmp > 180:
                        lon_tmp -= 360
                    rlon_hr.append(lon_tmp)
                    rlat_hr.append(lat_tmp)
                    node_depth_hr.append(node_depth_tmp)
                f.close()
                rlon_hr = array(rlon_hr[0:fesom_n2d_hr])
                rlat_hr = array(rlat_hr[0:fesom_n2d_hr])
                node_depth_hr = array(node_depth_hr)
                fesom_lon_hr, fesom_lat_hr = unrotate_grid(rlon_hr, rlat_hr)
                fesom_x_hr = -(fesom_lat_hr+90)*cos(fesom_lon_hr*deg2rad+pi/2)
                fesom_y_hr = (fesom_lat_hr+90)*sin(fesom_lon_hr*deg2rad+pi/2)
                f = open(fesom_mesh_path_hr + 'aux3d.out', 'r')
                max_num_layers_hr = int(f.readline())
                node_columns_hr = zeros([fesom_n2d_hr, max_num_layers_hr])
                for n in range(fesom_n2d_hr):
                    for k in range(max_num_layers_hr):
                        node_columns_hr[n,k] = int(f.readline())
                node_columns_hr = node_columns_hr.astype(int)
                f.close()
                node_ur_3d_hr = id.variables['u'][0,:]
                node_vr_3d_hr = id.variables['v'][0,:]
                node_ur_hr = zeros(fesom_n2d_hr)
                node_vr_hr = zeros(fesom_n2d_hr)
                for n in range(fesom_n2d_hr):
                    udz_col = 0
                    vdz_col = 0
                    dz_col = 0
                    for k in range(max_num_layers_hr-1):
                        if node_columns_hr[n,k+1] == -999:
                            break
                        top_id = node_columns_hr[n,k]
                        bot_id = node_columns_hr[n,k+1]
                        dz_tmp = node_depth_hr[bot_id-1] - node_depth_hr[top_id-1]
                        udz_col += 0.5*(node_ur_3d_hr[top_id-1]+node_ur_3d_hr[bot_id-1])*dz_tmp
                        vdz_col += 0.5*(node_vr_3d_hr[top_id-1]+node_vr_3d_hr[bot_id-1])*dz_tmp
                        dz_col += dz_tmp
                    node_ur_hr[n] = udz_col/dz_col
                    node_vr_hr[n] = vdz_col/dz_col
                node_u_hr, node_v_hr = unrotate_vector(rlon_hr, rlat_hr, node_ur_hr, node_vr_hr)
                node_data_hr = sqrt(node_u_hr**2 + node_v_hr**2)
            id.close()
        fesom_data_hr = []
        for elm in elements_hr:
            if elm.cavity or var not in ['draft', 'melt', 'wct']:
                if var == 'draft':
                    fesom_data_hr.append(mean([elm.nodes[0].depth, elm.nodes[1].depth, elm.nodes[2].depth]))
                elif var == 'bathy':
                    fesom_data_hr.append(mean([elm.nodes[0].find_bottom().depth, elm.nodes[1].find_bottom().depth, elm.nodes[2].find_bottom().depth]))
                elif var == 'wct':
                    fesom_data_hr.append(mean([elm.nodes[0].find_bottom().depth - elm.nodes[0].depth, elm.nodes[1].find_bottom().depth - elm.nodes[1].depth, elm.nodes[2].find_bottom().depth - elm.nodes[2].depth]))
                elif var in ['melt', 'vel']:
                    fesom_data_hr.append(mean([node_data_hr[elm.nodes[0].id], node_data_hr[elm.nodes[1].id], node_data_hr[elm.nodes[2].id]]))
                elif var in ['temp', 'salt']:
                    fesom_data_hr.append(mean([node_data_hr[elm.nodes[0].find_bottom().id], node_data_hr[elm.nodes[1].find_bottom().id], node_data_hr[elm.nodes[2].find_bottom().id]]))

        # Loop over regions
        for index in range(num_regions):
            print 'Processing ' + region_names[index]
            # Set up a grey square for FESOM to fill the background with land
            x_reg_fesom, y_reg_fesom = meshgrid(linspace(x_min[index], x_max[index], num=100), linspace(y_min[index], y_max[index], num=100))
            land_square = zeros(shape(x_reg_fesom))
            # Find bounds on variable in this region, for both ROMS and FESOM
            # Start with ROMS
            loc = (roms_x >= x_min[index])*(roms_x <= x_max[index])*(roms_y >= y_min[index])*(roms_y <= y_max[index])
            var_min = amin(roms_data[loc])
            var_max = amax(roms_data[loc])
            # Modify with FESOM
            # Low-res
            i = 0
            for elm in elements_lr:
                if elm.cavity or var not in ['draft', 'melt', 'wct']:
                    if any(elm.x >= x_min[index]) and any(elm.x <= x_max[index]) and any(elm.y >= y_min[index]) and any(elm.y <= y_max[index]):
                        if fesom_data_lr[i] < var_min:
                            var_min = fesom_data_lr[i]
                        if fesom_data_lr[i] > var_max:
                            var_max = fesom_data_lr[i]
                    i += 1
            # High-res
            i = 0
            for elm in elements_hr:
                if elm.cavity or var not in ['draft', 'melt', 'wct']:
                    if any(elm.x >= x_min[index]) and any(elm.x <= x_max[index]) and any(elm.y >= y_min[index]) and any(elm.y <= y_max[index]):
                        if fesom_data_hr[i] < var_min:
                            var_min = fesom_data_hr[i]
                        if fesom_data_hr[i] > var_max:
                            var_max = fesom_data_hr[i]
                    i += 1
            if var == 'melt':
                # Special colour map
                if var_min < 0:
                    # There is refreezing here; include blue for elements < 0
                    cmap_vals = array([var_min, 0, 0.25*var_max, 0.5*var_max, 0.75*var_max, var_max])
                    cmap_colors = [(0.26, 0.45, 0.86), (1, 1, 1), (1, 0.9, 0.4), (0.99, 0.59, 0.18), (0.5, 0.0, 0.08), (0.96, 0.17, 0.89)]
                    cmap_vals_norm = (cmap_vals - var_min)/(var_max - var_min)
                    cmap_list = []
                    for i in range(size(cmap_vals)):
                        cmap_list.append((cmap_vals_norm[i], cmap_colors[i]))
                    mf_cmap = LinearSegmentedColormap.from_list('melt_freeze', cmap_list)
                else:
                    # No refreezing
                    cmap_vals = array([0, 0.25*var_max, 0.5*var_max, 0.75*var_max, var_max])
                    cmap_colors = [(1, 1, 1), (1, 0.9, 0.4), (0.99, 0.59, 0.18), (0.5, 0.0, 0.08), (0.96, 0.17, 0.89)]
                    cmap_vals_norm = cmap_vals/var_max
                    cmap_list = []
                    for i in range(size(cmap_vals)):
                        cmap_list.append((cmap_vals_norm[i], cmap_colors[i]))
                    mf_cmap = LinearSegmentedColormap.from_list('melt_freeze', cmap_list)
                colour_map = mf_cmap            
            elif var == 'vel':
                colour_map = 'cool'
            else:
                colour_map = 'jet'
            if var == 'vel':
                # Make vectors for overlay
                # Set up bins (edges)
                x_bins = linspace(x_min[index], x_max[index], num=num_bins+1)
                y_bins = linspace(y_min[index], y_max[index], num=num_bins+1)
                # Calculate centres of bins (for plotting)
                x_centres = 0.5*(x_bins[:-1] + x_bins[1:])
                y_centres = 0.5*(y_bins[:-1] + y_bins[1:])
                # ROMS
                # First set up arrays to integrate velocity in each bin
                # Simple averaging of all the points inside each bin
                roms_u = zeros([size(y_centres), size(x_centres)])
                roms_v = zeros([size(y_centres), size(x_centres)])
                roms_num_pts = zeros([size(y_centres), size(x_centres)])
                # First convert to polar coordinates, rotate to account for
                # longitude in circumpolar projection, and convert back to vector
                # components
                theta_roms = arctan2(v_rho, u_rho)
                theta_circ_roms = theta_roms - roms_lon*deg2rad
                u_circ_roms = roms_data*cos(theta_circ_roms) # roms_data is speed
                v_circ_roms = roms_data*sin(theta_circ_roms)
                # Loop over all points (can't find a better way to do this)
                for j in range(size(roms_data,0)):
                    for i in range(size(roms_data,1)):
                        # Make sure data isn't masked (i.e. land)
                        if u_circ_roms[j,i] is not ma.masked:
                            # Check if we're in the region of interest
                            if roms_x[j,i] > x_min[index] and roms_x[j,i] < x_max[index] and roms_y[j,i] > y_min[index] and roms_y[j,i] < y_max[index]:
                                # Figure out which bins this falls into
                                x_index = nonzero(x_bins > roms_x[j,i])[0][0]-1
                                y_index = nonzero(y_bins > roms_y[j,i])[0][0]-1
                                # Integrate
                                roms_u[y_index, x_index] += u_circ_roms[j,i]
                                roms_v[y_index, x_index] += v_circ_roms[j,i]
                                roms_num_pts[y_index, x_index] += 1
                # Convert from sums to averages
                # First mask out points with no data
                roms_u = ma.masked_where(roms_num_pts==0, roms_u)
                roms_v = ma.masked_where(roms_num_pts==0, roms_v)
                # Divide everything else by the number of points
                flag = roms_num_pts > 0
                roms_u[flag] = roms_u[flag]/roms_num_pts[flag]
                roms_v[flag] = roms_v[flag]/roms_num_pts[flag]
                # FESOM low-res
                fesom_u_lr = zeros([size(y_centres), size(x_centres)])
                fesom_v_lr = zeros([size(y_centres), size(x_centres)])
                fesom_num_pts_lr = zeros([size(y_centres), size(x_centres)])
                theta_fesom_lr = arctan2(node_v_lr, node_u_lr)
                theta_circ_fesom_lr = theta_fesom_lr - fesom_lon_lr*deg2rad
                u_circ_fesom_lr = node_data_lr*cos(theta_circ_fesom_lr) # node_data is speed
                v_circ_fesom_lr = node_data_lr*sin(theta_circ_fesom_lr)
                # Loop over 2D nodes to fill in the velocity bins as before
                for n in range(fesom_n2d_lr):
                    if fesom_x_lr[n] > x_min[index] and fesom_x_lr[n] < x_max[index] and fesom_y_lr[n] > y_min[index] and fesom_y_lr[n] < y_max[index]:
                        x_index = nonzero(x_bins > fesom_x_lr[n])[0][0]-1
                        y_index = nonzero(y_bins > fesom_y_lr[n])[0][0]-1
                        fesom_u_lr[y_index, x_index] += u_circ_fesom_lr[n]
                        fesom_v_lr[y_index, x_index] += v_circ_fesom_lr[n]
                        fesom_num_pts_lr[y_index, x_index] += 1
                fesom_u_lr = ma.masked_where(fesom_num_pts_lr==0, fesom_u_lr)
                fesom_v_lr = ma.masked_where(fesom_num_pts_lr==0, fesom_v_lr)
                flag = fesom_num_pts_lr > 0
                fesom_u_lr[flag] = fesom_u_lr[flag]/fesom_num_pts_lr[flag]
                fesom_v_lr[flag] = fesom_v_lr[flag]/fesom_num_pts_lr[flag]
                # FESOM high-res
                fesom_u_hr = zeros([size(y_centres), size(x_centres)])
                fesom_v_hr = zeros([size(y_centres), size(x_centres)])
                fesom_num_pts_hr = zeros([size(y_centres), size(x_centres)])
                theta_fesom_hr = arctan2(node_v_hr, node_u_hr)
                theta_circ_fesom_hr = theta_fesom_hr - fesom_lon_hr*deg2rad
                u_circ_fesom_hr = node_data_hr*cos(theta_circ_fesom_hr) # node_data is speed
                v_circ_fesom_hr = node_data_hr*sin(theta_circ_fesom_hr)
                # Loop over 2D nodes to fill in the velocity bins as before
                for n in range(fesom_n2d_hr):
                    if fesom_x_hr[n] > x_min[index] and fesom_x_hr[n] < x_max[index] and fesom_y_hr[n] > y_min[index] and fesom_y_hr[n] < y_max[index]:
                        x_index = nonzero(x_bins > fesom_x_hr[n])[0][0]-1
                        y_index = nonzero(y_bins > fesom_y_hr[n])[0][0]-1
                        fesom_u_hr[y_index, x_index] += u_circ_fesom_hr[n]
                        fesom_v_hr[y_index, x_index] += v_circ_fesom_hr[n]
                        fesom_num_pts_hr[y_index, x_index] += 1
                fesom_u_hr = ma.masked_where(fesom_num_pts_hr==0, fesom_u_hr)
                fesom_v_hr = ma.masked_where(fesom_num_pts_hr==0, fesom_v_hr)
                flag = fesom_num_pts_hr > 0
                fesom_u_hr[flag] = fesom_u_hr[flag]/fesom_num_pts_hr[flag]
                fesom_v_hr[flag] = fesom_v_hr[flag]/fesom_num_pts_hr[flag]
            # Plot
            fig = figure(figsize=(20, ysize[index]))
            fig.patch.set_facecolor('white')
            # MetROMS
            ax = fig.add_subplot(1,3,1, aspect='equal')
            # First shade land and zice in grey
            contourf(roms_x, roms_y, land_zice, 1, colors=(('0.6', '0.6', '0.6')))
            # Fill in the missing circle
            contourf(x_reg_roms, y_reg_roms, land_circle, 1, colors=(('0.6', '0.6', '0.6')))
            # Now shade the data
            pcolor(roms_x, roms_y, roms_data, vmin=var_min, vmax=var_max, cmap=colour_map)
            if var == 'vel':
                # Overlay vectors
                quiver(x_centres, y_centres, roms_u, roms_v, scale=1.5, headwidth=6, headlength=7, color='black')
            xlim([x_min[index], x_max[index]])
            ylim([y_min[index], y_max[index]])
            axis('off')
            title('MetROMS', fontsize=24)
            # FESOM low-res
            ax = fig.add_subplot(1,3,2, aspect='equal')
            # Start with land background
            contourf(x_reg_fesom, y_reg_fesom, land_square, 1, colors=(('0.6', '0.6', '0.6')))
            # Add elements
            if var in ['draft', 'melt', 'wct']:
                # Ice shelf elements only
                img = PatchCollection(patches_shelf_lr, cmap=colour_map)
            else:
                img = PatchCollection(patches_lr, cmap=colour_map)
            img.set_array(array(fesom_data_lr))
            img.set_edgecolor('face')
            img.set_clim(vmin=var_min, vmax=var_max)
            ax.add_collection(img)
            if var in ['draft', 'melt', 'wct']:
                # Mask out the open ocean in white
                overlay = PatchCollection(patches_ocn_lr, facecolor=(1,1,1))
                overlay.set_edgecolor('face')
                ax.add_collection(overlay)
            if var == 'vel':
                # Overlay vectors
                quiver(x_centres, y_centres, fesom_u_lr, fesom_v_lr, scale=1.5, headwidth=6, headlength=7, color='black')
            xlim([x_min[index], x_max[index]])
            ylim([y_min[index], y_max[index]])
            axis('off')
            title('FESOM (low-res)', fontsize=24)
            # FESOM high-res
            ax = fig.add_subplot(1,3,3, aspect='equal')
            contourf(x_reg_fesom, y_reg_fesom, land_square, 1, colors=(('0.6', '0.6', '0.6')))
            if var in ['draft', 'melt', 'wct']:
                # Ice shelf elements only
                img = PatchCollection(patches_shelf_hr, cmap=colour_map)
            else:
                img = PatchCollection(patches_hr, cmap=colour_map)
            img.set_array(array(fesom_data_hr))
            img.set_edgecolor('face')
            img.set_clim(vmin=var_min, vmax=var_max)
            ax.add_collection(img)
            if var in ['draft', 'melt', 'wct']:
                overlay = PatchCollection(patches_ocn_hr, facecolor=(1,1,1))
                overlay.set_edgecolor('face')
                ax.add_collection(overlay)
            if var == 'vel':
                # Overlay vectors
                quiver(x_centres, y_centres, fesom_u_hr, fesom_v_hr, scale=1.5, headwidth=6, headlength=7, color='black')
            xlim([x_min[index], x_max[index]])
            ylim([y_min[index], y_max[index]])
            axis('off')
            title('FESOM (high-res)', fontsize=24)
            # Colourbar on the right
            cbaxes = fig.add_axes([0.92, 0.2, 0.01, 0.6])
            cbar = colorbar(img, cax=cbaxes)
            cbar.ax.tick_params(labelsize=20)
            # Main title
            if var == 'draft':
                title_string = ' draft (m)'
            elif var == 'bathy':
                title_string = ' bathymetry (m)'
            elif var == 'wct':
                title_string = ' water column thickness (m)'
            elif var == 'melt':
                title_string = ' melt rate (m/y)'
            elif var == 'temp':
                title_string = r' bottom water temperature ($^{\circ}$C)'
            elif var == 'salt':
                title_string = ' bottom water salinity (psu)'
            elif var == 'vel':
                title_string = ' vertically averaged ocean velocity (m/s)'
            suptitle(region_names[index] + title_string, fontsize=30)
            subplots_adjust(wspace=0.05)
            #fig.show()
            fig.savefig(fig_heads[index] + '_' + var + '.png')
예제 #8
0
def slope_current ():

# File paths
roms_grid = '/short/m68/kaa561/metroms_iceshelf/apps/common/grid/circ30S_quarterdegree.nc'
roms_file = '/short/m68/kaa561/metroms_iceshelf/tmproms/run/intercomparison/2002_2016_avg.nc'
fesom_mesh_path_lr = '/short/y99/kaa561/FESOM/mesh/meshA/'
fesom_mesh_path_hr = '/short/y99/kaa561/FESOM/mesh/meshB/'
fesom_file_lr = '/short/y99/kaa561/FESOM/intercomparison_lowres/output/oce_2002_2016_avg.nc'
fesom_file_hr = '/short/y99/kaa561/FESOM/intercomparison_highres/output/oce_2002_2016_avg.nc'
# ROMS vertical grid parameters
theta_s = 7.0
theta_b = 2.0
hc = 250
N = 31
# FESOM mesh parameters
circumpolar = False
cross_180 = False
# Spacing of longitude bins
dlon = 1
# Parameters for continental shelf selection
lat0 = -64  # Maximum latitude to consider
h0 = 2500  # Deepest depth to consider

# Set up longitude bins
# Start with edges
lon_bins = arange(-180, 180+dlon, dlon)
# Centres for plotting
lon_centres = 0.5*(lon_bins[:-1] + lon_bins[1:])
num_bins = size(lon_centres)
# Set up arrays to store maximum barotropic speed in each bin
current_roms = zeros(num_bins)
current_fesom_lr = zeros(num_bins)
current_fesom_hr = zeros(num_bins)

print 'Processing MetROMS'

print 'Reading grid'
id = Dataset(roms_grid, 'r')
roms_lon = id.variables['lon_rho'][:,:]
roms_lat = id.variables['lat_rho'][:,:]
roms_h = id.variables['h'][:,:]
roms_zice = id.variables['zice'][:,:]
roms_angle = id.variables['angle'][:,:]
id.close()
print 'Reading data'
# Read full 3D u and v
id = Dataset(roms_file, 'r')
u_3d_tmp = id.variables['u'][0,:,:,:]
v_3d_tmp = id.variables['v'][0,:,:,:]
id.close()
print 'Vertically averaging velocity'
# Get integrands on 3D grid; we only care about dz
dx, dy, dz, z = cartesian_grid_3d(roms_lon, roms_lat, roms_h, roms_zice, theta_s, theta_b, hc, N)
# Unrotate each vertical level
u_3d = ma.empty(shape(dz))
v_3d = ma.empty(shape(dz))
num_lat_u = size(u_3d_tmp,1)
num_lon_u = size(u_3d_tmp,2)
num_lat_v = size(v_3d_tmp,1)
num_lon_v = size(v_3d_tmp,2)
for k in range(N):
    u_k, v_k = rotate_vector_roms(u_3d_tmp[k,:,:], v_3d_tmp[k,:,:], roms_angle)
    u_3d[k,:,:] = u_k
    v_3d[k,:,:] = v_k
# Vertically average u and v
roms_u = sum(u_3d*dz, axis=0)/sum(dz, axis=0)
roms_v = sum(v_3d*dz, axis=0)/sum(dz, axis=0)
# Calculate speed
roms_speed = sqrt(roms_u**2 + roms_v**2)
print 'Selecting slope current'
# First make sure longitude is between -180 and 180
index = roms_lon > 180
roms_lon[index] = roms_lon[index] - 360
for j in range(size(roms_speed,0)):
    for i in range(size(roms_speed,1)):
        # Check if we care about this point
        if roms_lat[j,i] <= lat0 and roms_h[j,i] <= h0 and roms_zice[j,i] == 0:
            # Find longitude bin
            lon_index = nonzero(lon_bins > roms_lon[j,i])[0][0] - 1
            # Update slope current speed in this bin if needed
            if roms_speed[j,i] > current_roms[lon_index]:
                current_roms[lon_index] = roms_speed[j,i]

print 'Processing low-res FESOM'

print 'Building mesh'
# We only care about nodes, not elements, so don't need to use the
# fesom_grid function.
# Read cavity flag for each 2D surface node
fesom_cavity_lr = []
f = open(fesom_mesh_path_lr + 'cavity_flag_nod2d.out', 'r')
for line in f:
    tmp = int(line)
    if tmp == 1:
        fesom_cavity_lr.append(True)
    elif tmp == 0:
        fesom_cavity_lr.append(False)
    else:
        print 'Problem'
f.close()
# Save the number of 2D nodes
fesom_n2d_lr = len(fesom_cavity_lr)
# Read rotated lat and lon for each node, also depth
f = open(fesom_mesh_path_lr + 'nod3d.out', 'r')
f.readline()
rlon_lr = []
rlat_lr = []
node_depth_lr = []
for line in f:
    tmp = line.split()
    lon_tmp = float(tmp[1])
    lat_tmp = float(tmp[2])
    node_depth_tmp = -1*float(tmp[3])
    if lon_tmp < -180:
        lon_tmp += 360
    elif lon_tmp > 180:
        lon_tmp -= 360
    rlon_lr.append(lon_tmp)
    rlat_lr.append(lat_tmp)
    node_depth_lr.append(node_depth_tmp)
f.close()
# For lat and lon, only care about the 2D nodes (the first
# fesom_n2d indices)
rlon_lr = array(rlon_lr[0:fesom_n2d_lr])
rlat_lr = array(rlat_lr[0:fesom_n2d_lr])
node_depth_lr = array(node_depth_lr)
# Unrotate longitude
fesom_lon_lr, fesom_lat_lr = unrotate_grid(rlon_lr, rlat_lr)
# Read lists of which nodes are directly below which
f = open(fesom_mesh_path_lr + 'aux3d.out', 'r')
max_num_layers_lr = int(f.readline())
node_columns_lr = zeros([fesom_n2d_lr, max_num_layers_lr])
for n in range(fesom_n2d_lr):
    for k in range(max_num_layers_lr):
        node_columns_lr[n,k] = int(f.readline())
node_columns_lr = node_columns_lr.astype(int)
f.close()
# Now figure out the bottom depth of each 2D node
bottom_depth_lr = zeros(fesom_n2d_lr)
for n in range(fesom_n2d_lr):
    node_id = node_columns_lr[n,0] - 1
    for k in range(1, max_num_layers_lr):
        if node_columns_lr[n,k] == -999:
            # Reached the bottom
            break
        node_id = node_columns_lr[n,k] - 1
    # Save the last valid depth
    bottom_depth_lr[n] = node_depth_lr[n]
print 'Reading data'
# Read full 3D field for both u and v
id = Dataset(fesom_file_lr, 'r')
node_ur_3d_lr = id.variables['u'][0,:]
node_vr_3d_lr = id.variables['v'][0,:]
id.close()
print 'Vertically averaging velocity'
# Vertically average
node_ur_lr = zeros(fesom_n2d_lr)
node_vr_lr = zeros(fesom_n2d_lr)
for n in range(fesom_n2d_lr):
    # Integrate udz, vdz, and dz over this water column
    udz_col = 0
    vdz_col = 0
    dz_col = 0
    for k in range(max_num_layers_lr-1):
        if node_columns_lr[n,k+1] == -999:
            # Reached the bottom
            break
        # Trapezoidal rule
        top_id = node_columns_lr[n,k]
        bot_id = node_columns_lr[n,k+1]
        dz_tmp = node_depth_lr[bot_id-1] - node_depth_lr[top_id-1]
        udz_col += 0.5*(node_ur_3d_lr[top_id-1]+node_ur_3d_lr[bot_id-1])*dz_tmp
        vdz_col += 0.5*(node_vr_3d_lr[top_id-1]+node_vr_3d_lr[bot_id-1])*dz_tmp
        dz_col += dz_tmp
    # Convert from integrals to averages
    node_ur_lr[n] = udz_col/dz_col
    node_vr_lr[n] = vdz_col/dz_col
# Unrotate
node_u_lr, node_v_lr = unrotate_vector(rlon_lr, rlat_lr, node_ur_lr, node_vr_lr)
# Calculate speed
node_speed_lr = sqrt(node_u_lr**2 + node_v_lr**2)
print 'Selecting slope current'
for n in range(fesom_n2d_lr):
    # Check if we care about this node
    if fesom_lat_lr[n] <= lat0 and bottom_depth_lr[n] <= h0 and not fesom_cavity_lr[n]:
        # Find longitude bin
        lon_index = nonzero(lon_bins > fesom_lon_lr[n])[0][0] - 1
        # Update slope current speed in this bin if needed
        if node_speed_lr[n] > current_fesom_lr[lon_index]:
            current_fesom_lr[lon_index] = node_speed_lr[n]

print 'Processing high-res FESOM'

print 'Building mesh'
fesom_cavity_hr = []
f = open(fesom_mesh_path_hr + 'cavity_flag_nod2d.out', 'r')
for line in f:
    tmp = int(line)
    if tmp == 1:
        fesom_cavity_hr.append(True)
    elif tmp == 0:
        fesom_cavity_hr.append(False)
    else:
        print 'Problem'
f.close()
fesom_n2d_hr = len(fesom_cavity_hr)
f = open(fesom_mesh_path_hr + 'nod3d.out', 'r')
f.readline()
rlon_hr = []
rlat_hr = []
node_depth_hr = []
for line in f:
    tmp = line.split()
    lon_tmp = float(tmp[1])
    lat_tmp = float(tmp[2])
    node_depth_tmp = -1*float(tmp[3])
    if lon_tmp < -180:
        lon_tmp += 360
    elif lon_tmp > 180:
        lon_tmp -= 360
    rlon_hr.append(lon_tmp)
    rlat_hr.append(lat_tmp)
    node_depth_hr.append(node_depth_tmp)
f.close()
rlon_hr = array(rlon_hr[0:fesom_n2d_hr])
rlat_hr = array(rlat_hr[0:fesom_n2d_hr])
node_depth_hr = array(node_depth_hr)
fesom_lon_hr, fesom_lat_hr = unrotate_grid(rlon_hr, rlat_hr)
f = open(fesom_mesh_path_hr + 'aux3d.out', 'r')
max_num_layers_hr = int(f.readline())
node_columns_hr = zeros([fesom_n2d_hr, max_num_layers_hr])
for n in range(fesom_n2d_hr):
    for k in range(max_num_layers_hr):
        node_columns_hr[n,k] = int(f.readline())
node_columns_hr = node_columns_hr.astype(int)
f.close()
bottom_depth_hr = zeros(fesom_n2d_hr)
for n in range(fesom_n2d_hr):
    node_id = node_columns_hr[n,0] - 1
    for k in range(1, max_num_layers_hr):
        if node_columns_hr[n,k] == -999:
            break
        node_id = node_columns_hr[n,k] - 1
    bottom_depth_hr[n] = node_depth_hr[n]
print 'Reading data'
id = Dataset(fesom_file_hr, 'r')
node_ur_3d_hr = id.variables['u'][0,:]
node_vr_3d_hr = id.variables['v'][0,:]
id.close()
print 'Vertically averaging velocity'
node_ur_hr = zeros(fesom_n2d_hr)
node_vr_hr = zeros(fesom_n2d_hr)
for n in range(fesom_n2d_hr):
    udz_col = 0
    vdz_col = 0
    dz_col = 0
    for k in range(max_num_layers_hr-1):
        if node_columns_hr[n,k+1] == -999:
            break
        top_id = node_columns_hr[n,k]
        bot_id = node_columns_hr[n,k+1]
        dz_tmp = node_depth_hr[bot_id-1] - node_depth_hr[top_id-1]
        udz_col += 0.5*(node_ur_3d_hr[top_id-1]+node_ur_3d_hr[bot_id-1])*dz_tmp
        vdz_col += 0.5*(node_vr_3d_hr[top_id-1]+node_vr_3d_hr[bot_id-1])*dz_tmp
        dz_col += dz_tmp
    node_ur_hr[n] = udz_col/dz_col
    node_vr_hr[n] = vdz_col/dz_col
node_u_hr, node_v_hr = unrotate_vector(rlon_hr, rlat_hr, node_ur_hr, node_vr_hr)
node_speed_hr = sqrt(node_u_hr**2 + node_v_hr**2)
print 'Selecting slope current'
for n in range(fesom_n2d_hr):
    if fesom_lat_hr[n] <= lat0 and bottom_depth_hr[n] <= h0 and not fesom_cavity_hr[n]:
        lon_index = nonzero(lon_bins > fesom_lon_hr[n])[0][0] - 1
        if node_speed_hr[n] > current_fesom_hr[lon_index]:
            current_fesom_hr[lon_index] = node_speed_hr[n]

print 'Plotting'
fig = figure(figsize=(12,8))
plot(lon_centres, current_roms, color='blue', label='MetROMS')
plot(lon_centres, current_fesom_lr, color='green', label='FESOM low-res')
plot(lon_centres, current_fesom_hr, color='magenta', label='FESOM high-res')
grid(True)
title('Slope current speed', fontsize=20)
xlabel('Longitude', fontsize=14)
ylabel('m/s', fontsize=14)
xlim([-180, 180])
legend()
fig.savefig('slope_current.png')

print 'Mean slope current in MetROMS: ' + str(mean(current_roms)) + ' m/s'
print 'Mean slope current in low-res FESOM: ' + str(mean(current_fesom_lr)) + ' m/s'
print 'Mean slope current in high-res FESOM: ' + str(mean(current_fesom_hr)) + ' m/s'


# Command-line interface
if __name__ == "__main__":

    coastal_current()
예제 #9
0
def timeseries_dpt (mesh_path, ocn_file, log_file, fig_dir=''):

    circumpolar = False  # Needs to be global for SideElements
    cross_180 = False    # Don't make second copies of elements that cross 180E
    days_per_output = 5  # Number of days for each output step

    # Longitude of Drake Passage zonal slice
    lon0 = -67
    # Latitude bounds on Drake Passage zonal slice
    lat_min = -68
    lat_max = -54.5

    dpt = []
    # Check if the log file exists
    if exists(log_file):
        print 'Reading previously calculated values'
        f = open(log_file, 'r')
        # Skip the first line (header)
        f.readline()
        for line in f:
            dpt.append(float(line))
        f.close()

    print 'Building grid'
    # First get regular 2D elements
    elm2D = fesom_grid(mesh_path, circumpolar, cross_180)
    # Read longitude and latitude of each node in order (needed for rotation)
    fid = open(mesh_path + 'nod3d.out', 'r')
    fid.readline()
    lon = []
    lat = []
    for line in fid:
        tmp = line.split()
        lon_tmp = float(tmp[1])
        lat_tmp = float(tmp[2])
        if lon_tmp < -180:
            lon_tmp += 360
        elif lon_tmp > 180:
            lon_tmp -= 360
        lon.append(lon_tmp)
        lat.append(lat_tmp)
    fid.close()
    lon = array(lon)
    lat = array(lat)

    print 'Reading data'
    id = Dataset(ocn_file, 'r')
    num_time = id.variables['time'].shape[0]
    # Read both u and v so we can rotate to get the real u
    u_r = id.variables['u'][:,:]
    v_r = id.variables['v'][:,:]
    id.close()

    print 'Unrotating velocity vector'
    u = zeros(shape(u_r))
    # Rotate one time index at a time
    for t in range(num_time):
        u_tmp, v_tmp = unrotate_vector(lon, lat, u_r[t,:], v_r[t,:])
        u[t,:] = u_tmp

    print 'Extracting zonal slice through Drake Passage'
    # Get quadrilateral elements in the latitude vs depth slice
    selements = fesom_sidegrid(elm2D, u, lon0, lat_max, lat_min)

    print 'Setting up arrays'
    # Eastward velocity at each element
    u_selm = zeros([num_time, len(selements)])
    # Area of each element
    area_selm = zeros(len(selements))
    # Loop over elements to fill these in
    for i in range(len(selements)):
        selm = selements[i]
        u_selm[:,i] = selm.var
        area_selm[i] = selm.area()
    # Build timeseries
    for t in range(num_time):
        # Integrate u*area and convert to Sv
        dpt.append(sum(u_selm[t,:]*area_selm)*1e-6)

    # Calculate time values
    time = arange(len(dpt))*days_per_output/365.

    print 'Plotting'
    clf()
    plot(time, dpt)
    xlabel('Years')
    ylabel('Drake Passage Transport (Sv)')
    grid(True)
    savefig(fig_dir + 'drakepsgtrans.png')

    print 'Saving results to log file'
    f = open(log_file, 'w')
    f.write('Drake Passage Transport (Sv):\n')
    for elm in dpt:
        f.write(str(elm) + '\n')
    f.close()
예제 #10
0
def common_grid(mesh_path, output_dir, start_year, end_year, common_file,
                out_file):

    # Resolution of common grid (degrees, same for lat and lon)
    res = 0.25
    # Northern boundary to interpolate to
    nbdry = -50
    # Radius of the Earth in metres
    r = 6.371e6
    # Degrees to radians conversion factor
    deg2rad = pi / 180.0
    # Name stamped on FESOM output files
    expt_name = 'MK44005'

    print 'Calculating grids'
    # Make the latitude and longitude arrays for the common grid
    lon_common = arange(-180, 180 + res, res)
    lat_common = arange(-90, nbdry + res, res)
    # Get a 2D version of each to calculate dx and dy in metres
    lon_2d, lat_2d = meshgrid(lon_common, lat_common)
    # dx = r*cos(lat)*dlon where lat and dlon (i.e. res) are in radians
    dx = r * cos(lat_2d * deg2rad) * res * deg2rad
    # dy = r*dlat where dlat (i.e. res) is in radians
    # This is constant so reshape to an array of the right dimensions
    dy = zeros(shape(dx)) + r * res * deg2rad

    # Read the land mask from the existing ROMS common grid file
    id = Dataset(common_file, 'r')
    mask_common = id.variables['mask'][:, :]
    id.close()

    # Read FESOM 2D grid
    file = open(mesh_path + 'nod2d.out', 'r')
    file.readline()
    rlon = []
    rlat = []
    for line in file:
        tmp = line.split()
        lon_tmp = float(tmp[1])
        lat_tmp = float(tmp[2])
        if lon_tmp < -180:
            lon_tmp += 360
        elif lon_tmp > 180:
            lon_tmp -= 360
        rlon.append(lon_tmp)
        rlat.append(lat_tmp)
    file.close()
    rlon = array(rlon)
    rlat = array(rlat)
    n2d = size(rlon)
    # Unrotate grid
    lon_fesom, lat_fesom = unrotate_grid(rlon, rlat)

    print 'Setting up ' + out_file
    id = Dataset(out_file, 'w')
    id.createDimension('longitude', size(lon_common))
    id.createDimension('latitude', size(lat_common))
    id.createDimension('time', None)
    id.createVariable('longitude', 'f8', ('longitude'))
    id.variables['longitude'].units = 'degrees'
    id.variables['longitude'][:] = lon_common
    id.createVariable('latitude', 'f8', ('latitude'))
    id.variables['latitude'].units = 'degrees'
    id.variables['latitude'][:] = lat_common
    id.createVariable('time', 'f8', ('time'))
    id.variables['time'].units = 'months'
    id.createVariable('mask', 'f8', ('latitude', 'longitude'))
    id.variables['mask'].units = '1'
    id.variables['mask'][:, :] = mask_common
    id.createVariable('sst', 'f8', ('time', 'latitude', 'longitude'))
    id.variables['sst'].long_name = 'sea surface temperature'
    id.variables['sst'].units = 'C'
    id.createVariable('sss', 'f8', ('time', 'latitude', 'longitude'))
    id.variables['sss'].long_name = 'sea surface salinity'
    id.variables['sss'].units = 'psu'
    id.createVariable('shflux', 'f8', ('time', 'latitude', 'longitude'))
    id.variables['shflux'].long_name = 'surface heat flux into ocean'
    id.variables['shflux'].units = 'W/m^2'
    id.createVariable('ssflux', 'f8', ('time', 'latitude', 'longitude'))
    id.variables[
        'ssflux'].long_name = 'surface virtual salinity flux into ocean'
    id.variables['ssflux'].units = 'psu m/s'
    id.createVariable('aice', 'f8', ('time', 'latitude', 'longitude'))
    id.variables['aice'].long_name = 'sea ice concentration'
    id.variables['aice'].units = '1'
    id.createVariable('hice', 'f8', ('time', 'latitude', 'longitude'))
    id.variables['hice'].long_name = 'sea ice thickness'
    id.variables['hice'].units = 'm'
    id.createVariable('uocn', 'f8', ('time', 'latitude', 'longitude'))
    id.variables['uocn'].long_name = 'ocean surface velocity eastward'
    id.variables['uocn'].units = 'm/s'
    id.createVariable('vocn', 'f8', ('time', 'latitude', 'longitude'))
    id.variables['vocn'].long_name = 'ocean surface velocity northward'
    id.variables['vocn'].units = 'm/s'
    id.createVariable('uice', 'f8', ('time', 'latitude', 'longitude'))
    id.variables['uice'].long_name = 'sea ice velocity eastward'
    id.variables['uice'].units = 'm/s'
    id.createVariable('vice', 'f8', ('time', 'latitude', 'longitude'))
    id.variables['vice'].long_name = 'sea ice velocity northward'
    id.variables['vice'].units = 'm/s'
    id.createVariable('sustr', 'f8', ('time', 'latitude', 'longitude'))
    id.variables['sustr'].long_name = 'zonal surface stress'
    id.variables['sustr'].units = 'N/m^2'
    id.createVariable('svstr', 'f8', ('time', 'latitude', 'longitude'))
    id.variables['svstr'].long_name = 'meridional surface stress'
    id.variables['svstr'].units = 'N/m^2'
    id.createVariable('curl_str', 'f8', ('time', 'latitude', 'longitude'))
    id.variables['curl_str'].long_name = 'curl of surface stress'
    id.variables['curl_str'].units = 'N/m^3'

    for year in range(start_year, end_year + 1):
        print 'Processing year ' + str(year)
        for month in range(12):
            print 'Processing month ' + str(month + 1)
            curr_month = (year - start_year) * 12 + month
            # Write time value for this month
            id.variables['time'][curr_month] = curr_month + 1

            # Construct file names
            oce_mean_file = output_dir + expt_name + '.' + str(
                year) + '.oce.mean.nc'
            forcing_diag_file = output_dir + expt_name + '.' + str(
                year) + '.forcing.diag.nc'
            ice_mean_file = output_dir + expt_name + '.' + str(
                year) + '.ice.mean.nc'

            print '...sea surface temperature'
            # Get monthly average of 3D variable
            temp_fesom = monthly_avg(oce_mean_file, 'temp', month)
            # Select surface nodes
            sst_fesom = temp_fesom[:n2d]
            # Interpolate to common grid
            sst_common = interp_fesom2common(lon_common, lat_common, lon_fesom,
                                             lat_fesom, sst_fesom)
            # Apply land mask
            sst = ma.masked_where(mask_common == 0, sst_common)
            # Write to file
            id.variables['sst'][curr_month, :, :] = sst

            print '...sea surface salinity'
            # Get monthly average of 3D variable
            salt_fesom = monthly_avg(oce_mean_file, 'salt', month)
            # Select surface nodes
            sss_fesom = salt_fesom[:n2d]
            # Interpolate to common grid
            sss_common = interp_fesom2common(lon_common, lat_common, lon_fesom,
                                             lat_fesom, sss_fesom)
            # Apply land mask
            sss = ma.masked_where(mask_common == 0, sss_common)
            # Write to file
            id.variables['sss'][curr_month, :, :] = sss

            print '...surface heat flux'
            # Get monthly average
            shflux_fesom = monthly_avg(forcing_diag_file, 'qnet', month)
            # Interpolate to common grid
            shflux_common = interp_fesom2common(lon_common, lat_common,
                                                lon_fesom, lat_fesom,
                                                shflux_fesom)
            # Apply land mask
            shflux = ma.masked_where(mask_common == 0, shflux_common)
            # Write to file
            id.variables['shflux'][curr_month, :, :] = shflux

            print '...surface salt flux'
            # Get monthly average
            ssflux_fesom = monthly_avg(forcing_diag_file, 'virtual_salt',
                                       month)
            # Interpolate to common grid
            ssflux_common = interp_fesom2common(lon_common, lat_common,
                                                lon_fesom, lat_fesom,
                                                ssflux_fesom)
            # Apply land mask
            ssflux = ma.masked_where(mask_common == 0, ssflux_common)
            # Write to file
            id.variables['ssflux'][curr_month, :, :] = ssflux

            print '...sea ice concentration'
            # Get monthly average
            aice_fesom = monthly_avg(ice_mean_file, 'area', month)
            # Interpolate to common grid
            aice_common = interp_fesom2common(lon_common, lat_common,
                                              lon_fesom, lat_fesom, aice_fesom)
            # Apply land mask
            aice = ma.masked_where(mask_common == 0, aice_common)
            # Write to file
            id.variables['aice'][curr_month, :, :] = aice

            print '...sea ice thickness'
            # Get monthly average
            hice_fesom = monthly_avg(ice_mean_file, 'hice', month)
            # Interpolate to common grid
            hice_common = interp_fesom2common(lon_common, lat_common,
                                              lon_fesom, lat_fesom, hice_fesom)
            # Apply land mask
            hice = ma.masked_where(mask_common == 0, hice_common)
            # Write to file
            id.variables['hice'][curr_month, :, :] = hice

            print '...surface ocean velocity vector'
            # Get monthly averages of both vector components in 3D
            uocn_3d_tmp = monthly_avg(oce_mean_file, 'u', month)
            vocn_3d_tmp = monthly_avg(oce_mean_file, 'v', month)
            # Select surface nodes
            uocn_tmp = uocn_3d_tmp[:n2d]
            vocn_tmp = vocn_3d_tmp[:n2d]
            # Unrotate
            uocn_fesom, vocn_fesom = unrotate_vector(rlon, rlat, uocn_tmp,
                                                     vocn_tmp)
            # Interpolate to common grid
            uocn_common = interp_fesom2common(lon_common, lat_common,
                                              lon_fesom, lat_fesom, uocn_fesom)
            vocn_common = interp_fesom2common(lon_common, lat_common,
                                              lon_fesom, lat_fesom, vocn_fesom)
            # Apply land mask
            uocn = ma.masked_where(mask_common == 0, uocn_common)
            vocn = ma.masked_where(mask_common == 0, vocn_common)
            # Write to file
            id.variables['uocn'][curr_month, :, :] = uocn
            id.variables['vocn'][curr_month, :, :] = vocn

            print '...sea ice velocity vector'
            # Get monthly averages of both vector components
            uice_tmp = monthly_avg(ice_mean_file, 'uice', month)
            vice_tmp = monthly_avg(ice_mean_file, 'vice', month)
            # Unrotate
            uice_fesom, vice_fesom = unrotate_vector(rlon, rlat, uice_tmp,
                                                     vice_tmp)
            # Interpolate to common grid
            uice_common = interp_fesom2common(lon_common, lat_common,
                                              lon_fesom, lat_fesom, uice_fesom)
            vice_common = interp_fesom2common(lon_common, lat_common,
                                              lon_fesom, lat_fesom, vice_fesom)
            # Apply land mask
            uice = ma.masked_where(mask_common == 0, uice_common)
            vice = ma.masked_where(mask_common == 0, vice_common)
            # Write to file
            id.variables['uice'][curr_month, :, :] = uice
            id.variables['vice'][curr_month, :, :] = vice

            print '...surface stress vector'
            # Surface stresses
            # Get monthly averages of both vector components
            sustr_tmp = monthly_avg(forcing_diag_file, 'stress_x', month)
            svstr_tmp = monthly_avg(forcing_diag_file, 'stress_y', month)
            # Unrotate
            sustr_fesom, svstr_fesom = unrotate_vector(rlon, rlat, sustr_tmp,
                                                       svstr_tmp)
            # Interpolate to common grid
            sustr_common = interp_fesom2common(lon_common, lat_common,
                                               lon_fesom, lat_fesom,
                                               sustr_fesom)
            svstr_common = interp_fesom2common(lon_common, lat_common,
                                               lon_fesom, lat_fesom,
                                               svstr_fesom)
            # Apply land mask
            sustr = ma.masked_where(mask_common == 0, sustr_common)
            svstr = ma.masked_where(mask_common == 0, svstr_common)
            # Write to file
            id.variables['sustr'][curr_month, :, :] = sustr
            id.variables['svstr'][curr_month, :, :] = svstr

            print '...curl of surface stress vector'
            # Curl of surface stress = d/dx (svstr) - d/dy (sustr)
            # First calculate the two derivatives
            dsvstr_dx = ma.empty(shape(svstr_common))
            # Forward difference approximation
            dsvstr_dx[:, :-1] = (svstr_common[:, 1:] -
                                 svstr_common[:, :-1]) / dx[:, :-1]
            # Backward difference for the last row
            dsvstr_dx[:, -1] = (svstr_common[:, -1] -
                                svstr_common[:, -2]) / dx[:, -1]
            dsustr_dy = ma.empty(shape(sustr_common))
            dsustr_dy[:-1, :] = (sustr_common[1:, :] -
                                 sustr_common[:-1, :]) / dy[:-1, :]
            dsustr_dy[-1, :] = (sustr_common[-1, :] -
                                sustr_common[-2, :]) / dy[-1, :]
            curl_str = dsvstr_dx - dsustr_dy
            curl_str = ma.masked_where(mask_common == 0, curl_str)
            # Write to file
            id.variables['curl_str'][curr_month, :, :] = curl_str

    print 'Finished'
    id.close()
예제 #11
0
def ross_circulation():

    # File paths
    mesh_path = '/short/y99/kaa561/FESOM/mesh/high_res/'
    file_beg = '/short/y99/kaa561/FESOM/highres_spinup/annual_avg.oce.mean.1996.2005.nc'
    file_end = '/short/y99/kaa561/FESOM/rcp85_M_highres/output/annual_avg.oce.mean.2091.2100.nc'
    deg2rad = pi / 180.0
    # Limits on x and y (polar coordinate transformation)
    x_min = -6
    x_max = 4
    y_min = -13
    y_max = -4.75
    num_bins_x = 30
    num_bins_y = 30

    print 'Building mesh'
    # Mask open ocean
    elements, mask_patches = make_patches(mesh_path,
                                          circumpolar=True,
                                          mask_cavities=True)
    # Unmask ice shelves
    patches = iceshelf_mask(elements)
    # The overlaid vectors are based on nodes not elements, so many of the
    # fesom_grid data structures fail to apply and we need to read some of the
    # mesh files again.
    # Read the cavity flag for each 2D surface node
    node_cavity = []
    f = open(mesh_path + 'cavity_flag_nod2d.out', 'r')
    for line in f:
        tmp = int(line)
        if tmp == 1:
            node_cavity.append(True)
        elif tmp == 0:
            node_cavity.append(False)
        else:
            print 'Problem'
    f.close()
    # Save the number of 2D nodes
    n2d = len(node_cavity)
    # Read rotated lat and lon for each node, also depth
    f = open(mesh_path + 'nod3d.out', 'r')
    f.readline()
    rlon = []
    rlat = []
    node_depth = []
    for line in f:
        tmp = line.split()
        lon_tmp = float(tmp[1])
        lat_tmp = float(tmp[2])
        node_depth_tmp = -1 * float(tmp[3])
        if lon_tmp < -180:
            lon_tmp += 360
        elif lon_tmp > 180:
            lon_tmp -= 360
        rlon.append(lon_tmp)
        rlat.append(lat_tmp)
        node_depth.append(node_depth_tmp)
    f.close()
    # For lat and lon, only care about the 2D nodes (the first n2d indices)
    rlon = array(rlon[0:n2d])
    rlat = array(rlat[0:n2d])
    node_depth = array(node_depth)
    # Unrotate longitude
    lon, lat = unrotate_grid(rlon, rlat)
    # Calculate polar coordinates of each node
    x = -(lat + 90) * cos(lon * deg2rad + pi / 2)
    y = (lat + 90) * sin(lon * deg2rad + pi / 2)
    # Read lists of which nodes are directly below which
    f = open(mesh_path + 'aux3d.out', 'r')
    max_num_layers = int(f.readline())
    node_columns = zeros([n2d, max_num_layers])
    for n in range(n2d):
        for k in range(max_num_layers):
            node_columns[n, k] = int(f.readline())
    node_columns = node_columns.astype(int)
    f.close()
    # Count the number of elements in ice shelf cavities
    num_cavity_elm = 0
    for elm in elements:
        if elm.cavity:
            num_cavity_elm += 1
    num_elm = len(elements)
    num_ice_elm = num_elm - num_cavity_elm

    print 'Calculating vertically averaged velocity'
    velavg_beg = zeros([num_cavity_elm])
    velavg_end = zeros([num_cavity_elm])
    # Read full 3D fields for both u and v
    id = Dataset(file_beg, 'r')
    node_ur_3d_beg = id.variables['u'][0, :]
    node_vr_3d_beg = id.variables['v'][0, :]
    id.close()
    id = Dataset(file_end, 'r')
    node_ur_3d_end = id.variables['u'][0, :]
    node_vr_3d_end = id.variables['v'][0, :]
    id.close()
    # Vertically average
    node_ur_beg = zeros(n2d)
    node_vr_beg = zeros(n2d)
    node_ur_end = zeros(n2d)
    node_vr_end = zeros(n2d)
    for n in range(n2d):
        # Integrate udz, vdz, and dz over this water column
        udz_col_beg = 0
        vdz_col_beg = 0
        udz_col_end = 0
        vdz_col_end = 0
        dz_col = 0
        for k in range(max_num_layers - 1):
            if node_columns[n, k + 1] == -999:
                # Reached the bottom
                break
            # Trapezoidal rule
            top_id = node_columns[n, k]
            bot_id = node_columns[n, k + 1]
            dz_tmp = node_depth[bot_id - 1] - node_depth[top_id - 1]
            udz_col_beg += 0.5 * (node_ur_3d_beg[top_id - 1] +
                                  node_ur_3d_beg[bot_id - 1]) * dz_tmp
            vdz_col_beg += 0.5 * (node_vr_3d_beg[top_id - 1] +
                                  node_vr_3d_beg[bot_id - 1]) * dz_tmp
            udz_col_end += 0.5 * (node_ur_3d_end[top_id - 1] +
                                  node_ur_3d_end[bot_id - 1]) * dz_tmp
            vdz_col_end += 0.5 * (node_vr_3d_end[top_id - 1] +
                                  node_vr_3d_end[bot_id - 1]) * dz_tmp
            dz_col += dz_tmp
        # Convert from integrals to averages
        node_ur_beg[n] = udz_col_beg / dz_col
        node_vr_beg[n] = vdz_col_beg / dz_col
        node_ur_end[n] = udz_col_end / dz_col
        node_vr_end[n] = vdz_col_end / dz_col
    # Unrotate
    node_u_beg, node_v_beg = unrotate_vector(rlon, rlat, node_ur_beg,
                                             node_vr_beg)
    node_u_end, node_v_end = unrotate_vector(rlon, rlat, node_ur_end,
                                             node_vr_end)
    # Calculate speed
    node_speed_beg = sqrt(node_u_beg**2 + node_v_beg**2)
    node_speed_end = sqrt(node_u_end**2 + node_v_end**2)
    # Calculate speed at each element, averaged over 3 corners
    i = 0
    for elm in elements:
        if elm.cavity:
            velavg_beg[i] = mean([
                node_speed_beg[elm.nodes[0].id],
                node_speed_beg[elm.nodes[1].id],
                node_speed_beg[elm.nodes[2].id]
            ])
            velavg_end[i] = mean([
                node_speed_end[elm.nodes[0].id],
                node_speed_end[elm.nodes[1].id],
                node_speed_end[elm.nodes[2].id]
            ])
            i += 1
    # Find bounds on speed in this region
    # Initialise with something impossible
    var_min = amax(array(velavg_beg))
    var_max = amin(array(velavg_beg))
    # Modify as needed
    i = 0
    for elm in elements:
        if elm.cavity:
            if any(elm.x >= x_min) and any(elm.x <= x_max) and any(
                    elm.y >= y_min) and any(elm.y <= y_max):
                if velavg_beg[i] < var_min:
                    var_min = velavg_beg[i]
                if velavg_beg[i] > var_max:
                    var_max = velavg_beg[i]
                if velavg_end[i] < var_min:
                    var_min = velavg_end[i]
                if velavg_end[i] > var_max:
                    var_max = velavg_end[i]
            i += 1

    print 'Making vectors for overlay'
    # Set up bins (edges)
    x_bins = linspace(x_min, x_max, num=num_bins_x + 1)
    y_bins = linspace(y_min, y_max, num=num_bins_y + 1)
    # Calculate centres of bins (for plotting)
    x_centres = 0.5 * (x_bins[:-1] + x_bins[1:])
    y_centres = 0.5 * (y_bins[:-1] + y_bins[1:])
    # First set up arrays to integrate velocity in each bin
    # Simple averaging of all the points inside each bin
    ubin_beg = zeros([size(y_centres), size(x_centres)])
    vbin_beg = zeros([size(y_centres), size(x_centres)])
    ubin_end = zeros([size(y_centres), size(x_centres)])
    vbin_end = zeros([size(y_centres), size(x_centres)])
    num_pts = zeros([size(y_centres), size(x_centres)])
    # Convert to polar coordinates, rotate to account for longitude in
    # circumpolar projection, and convert back to vector components
    theta_beg = arctan2(node_v_beg, node_u_beg)
    theta_circ_beg = theta_beg - lon * deg2rad
    u_circ_beg = node_speed_beg * cos(theta_circ_beg)
    v_circ_beg = node_speed_beg * sin(theta_circ_beg)
    theta_end = arctan2(node_v_end, node_u_end)
    theta_circ_end = theta_end - lon * deg2rad
    u_circ_end = node_speed_end * cos(theta_circ_end)
    v_circ_end = node_speed_end * sin(theta_circ_end)
    # Loop over 2D nodes to fill in the velocity bins
    for n in range(n2d):
        if node_cavity[n]:
            if x[n] > x_min and x[n] < x_max and y[n] > y_min and y[n] < y_max:
                x_index = nonzero(x_bins > x[n])[0][0] - 1
                y_index = nonzero(y_bins > y[n])[0][0] - 1
                ubin_beg[y_index, x_index] += u_circ_beg[n]
                vbin_beg[y_index, x_index] += v_circ_beg[n]
                ubin_end[y_index, x_index] += u_circ_end[n]
                vbin_end[y_index, x_index] += v_circ_end[n]
                num_pts[y_index, x_index] += 1
    # Convert from sums to averages
    # First mask out points with no data
    ubin_beg = ma.masked_where(num_pts == 0, ubin_beg)
    vbin_beg = ma.masked_where(num_pts == 0, vbin_beg)
    ubin_end = ma.masked_where(num_pts == 0, ubin_end)
    vbin_end = ma.masked_where(num_pts == 0, vbin_end)
    # Divide everything else by the number of points
    flag = num_pts > 0
    ubin_beg[flag] = ubin_beg[flag] / num_pts[flag]
    vbin_beg[flag] = vbin_beg[flag] / num_pts[flag]
    ubin_end[flag] = ubin_end[flag] / num_pts[flag]
    vbin_end[flag] = vbin_end[flag] / num_pts[flag]

    print 'Plotting'
    fig = figure(figsize=(18, 9))
    fig.patch.set_facecolor('white')
    # Set up a grey square to fill the background with land
    x_reg, y_reg = meshgrid(linspace(x_min, x_max, num=100),
                            linspace(y_min, y_max, num=100))
    land_square = zeros(shape(x_reg))
    # 1996-2005
    ax = fig.add_subplot(1, 2, 1, aspect='equal')
    # Start with land background
    contourf(x_reg, y_reg, land_square, 1, colors=(('0.6', '0.6', '0.6')))
    # Add ice shelf elements
    img = PatchCollection(patches, cmap='cool')
    img.set_array(array(velavg_beg))
    img.set_edgecolor('face')
    img.set_clim(vmin=var_min, vmax=var_max)
    ax.add_collection(img)
    # Mask out the open ocean in white
    overlay = PatchCollection(mask_patches, facecolor=(1, 1, 1))
    overlay.set_edgecolor('face')
    ax.add_collection(overlay)
    # Overlay vectors
    quiver(x_centres,
           y_centres,
           ubin_beg,
           vbin_beg,
           scale=0.9,
           headwidth=8,
           headlength=9,
           color='black')
    xlim([x_min, x_max])
    ylim([y_min, y_max])
    ax.set_xticks([])
    ax.set_yticks([])
    title('1996-2005', fontsize=20)
    # RCP
    ax = fig.add_subplot(1, 2, 2, aspect='equal')
    contourf(x_reg, y_reg, land_square, 1, colors=(('0.6', '0.6', '0.6')))
    img = PatchCollection(patches, cmap='cool')
    img.set_array(array(velavg_end))
    img.set_edgecolor('face')
    img.set_clim(vmin=var_min, vmax=var_max)
    ax.add_collection(img)
    overlay = PatchCollection(mask_patches, facecolor=(1, 1, 1))
    overlay.set_edgecolor('face')
    ax.add_collection(overlay)
    quiver(x_centres,
           y_centres,
           ubin_end,
           vbin_end,
           scale=0.9,
           headwidth=8,
           headlength=9,
           color='black')
    xlim([x_min, x_max])
    ylim([y_min, y_max])
    ax.set_xticks([])
    ax.set_yticks([])
    title('RCP 8.5 (2091-2100)', fontsize=20)
    # Colourbar on the right
    cbaxes = fig.add_axes([0.93, 0.3, 0.02, 0.4])
    cbar = colorbar(img, cax=cbaxes)
    # Main title
    suptitle('Ross Sea vertically averaged velocity (m/s)', fontsize=30)
    subplots_adjust(wspace=0.01)
    fig.show()
    fig.savefig('ross_circulation_change.png')
예제 #12
0
def timeseries_subpolar_gyres (mesh_path, output_path, start_year, end_year, log_file, fig_dir=''):

    # Lat-lon bounds on regions to search for each gyre
    # Weddell Sea gyre
    ws_wbdry = -60
    ws_ebdry = 30
    ws_sbdry = -90
    ws_nbdry = -50
    # Ross Sea gyre (crosses 180E)
    rs_wbdry = 150
    rs_ebdry = -140
    rs_sbdry = -90
    rs_nbdry = -60
    # Resolution of regular grid (degrees)
    res = 0.1
    # Radius of the Earth in metres
    r = 6.371e6
    # Degrees to radians coversion factor
    deg2rad = pi/180.0
    # Naming conventions for FESOM output files
    file_head = output_path + 'MK44005.'
    file_tail = '.oce.mean.nc'
    num_years = end_year - start_year + 1

    # Check if the log file exists
    if exists(log_file):
        print 'Reading previously calculated values'
        f = open(log_file, 'r')
        f.readline()
        ws_trans_tmp = []
        for line in f:
            try:
                ws_trans_tmp.append(float(line))
            except(ValueError):
                break
        rs_trans_tmp = []
        for line in f:
            try:
                rs_trans_tmp.append(float(line))
            except(ValueError):
                break
        f.close()
        prev_years = len(ws_trans_tmp)
        # Set up proper timeseries now
        ws_trans = empty(prev_years+num_years)
        ws_trans[:prev_years] = ws_trans_tmp[:]
        rs_trans = empty(prev_years+num_years)
        rs_trans[:prev_years] = rs_trans_tmp[:]
    else:
        prev_years = 0
        ws_trans = empty(num_years)
        rs_trans = empty(num_years)

    print 'Building mesh'
    elements = fesom_grid(mesh_path, circumpolar=True, cross_180=True)
    # Read number of nodes, 2D and 3D
    f = open(mesh_path + 'nod2d.out', 'r')
    n2d = int(f.readline())
    f.close()
    f = open(mesh_path + 'nod3d.out', 'r')
    n3d = int(f.readline())
    f.close()
    # Read (rotated) lon, lat, and depth, at each 3D node
    f = open(mesh_path + 'nod3d.out', 'r')
    f.readline()
    rlon = []
    rlat = []
    node_depth = []
    for line in f:
        tmp = line.split()
        lon_tmp = float(tmp[1])
        lat_tmp = float(tmp[2])
        node_depth_tmp = -1*float(tmp[3])
        if lon_tmp < -180:
            lon_tmp += 360
        elif lon_tmp > 180:
            lon_tmp -= 360
        rlon.append(lon_tmp)
        rlat.append(lat_tmp)
        node_depth.append(node_depth_tmp)
    f.close()
    rlon = array(rlon)
    rlat = array(rlat)
    node_depth = array(node_depth)
    # Read lists of which nodes are directly below which
    f = open(mesh_path + 'aux3d.out', 'r')
    max_num_layers = int(f.readline())
    node_columns = zeros([n2d, max_num_layers])
    for n in range(n2d):
        for k in range(max_num_layers):
            node_columns[n,k] = int(f.readline())
    node_columns = node_columns.astype(int)
    f.close()
    # Set up regular grids
    # Weddell Sea gyre
    # Start with boundaries
    ws_lon_reg_edges = arange(ws_wbdry, ws_ebdry+res, res)
    ws_lat_reg_edges = arange(ws_sbdry, ws_nbdry+res, res)
    # Now get centres
    ws_lon_reg = 0.5*(ws_lon_reg_edges[:-1] + ws_lon_reg_edges[1:])
    ws_lat_reg = 0.5*(ws_lat_reg_edges[:-1] + ws_lat_reg_edges[1:])
    # Also get differentials in lon-lat space
    ws_dlon = ws_lon_reg_edges[1:] - ws_lon_reg_edges[:-1]
    ws_dlat = ws_lat_reg_edges[1:] - ws_lat_reg_edges[:-1]
    # Make 2D versions
    ws_lon_reg_2d, ws_lat_reg_2d = meshgrid(ws_lon_reg, ws_lat_reg)
    ws_dlon_2d, ws_dlat_2d = meshgrid(ws_dlon, ws_dlat)
    # Calculate differentials in Cartesian space
    ws_dx = r*cos(ws_lat_reg_2d*deg2rad)*ws_dlon_2d*deg2rad
    ws_dy = r*ws_dlat_2d*deg2rad
    # Ross Sea gyre (split into 2 across 180E)
    rs_lon1_reg_edges = arange(rs_wbdry, 180, res)
    rs_lon2_reg_edges = arange(-180, rs_ebdry+res, res)
    rs_lat_reg_edges = arange(rs_sbdry, rs_nbdry+res, res)
    # Now get centres
    rs_lon1_reg = 0.5*(rs_lon1_reg_edges[:-1] + rs_lon1_reg_edges[1:])
    rs_lon2_reg = 0.5*(rs_lon2_reg_edges[:-1] + rs_lon2_reg_edges[1:])
    rs_lat_reg = 0.5*(rs_lat_reg_edges[:-1] + rs_lat_reg_edges[1:])
    # Also get differentials in lon-lat space
    rs_dlon1 = rs_lon1_reg_edges[1:] - rs_lon1_reg_edges[:-1]
    rs_dlon2 = rs_lon2_reg_edges[1:] - rs_lon2_reg_edges[:-1]
    rs_dlat = rs_lat_reg_edges[1:] - rs_lat_reg_edges[:-1]
    # Make 2D versions
    rs_lon1_reg_2d, rs_lat1_reg_2d = meshgrid(rs_lon1_reg, rs_lat_reg)
    rs_lon2_reg_2d, rs_lat2_reg_2d = meshgrid(rs_lon2_reg, rs_lat_reg)
    rs_dlon1_2d, rs_dlat1_2d = meshgrid(rs_dlon1, rs_dlat)
    rs_dlon2_2d, rs_dlat2_2d = meshgrid(rs_dlon2, rs_dlat)
    # Calculate differentials in Cartesian space
    rs_dx1 = r*cos(rs_lat1_reg_2d*deg2rad)*rs_dlon1_2d*deg2rad
    rs_dx2 = r*cos(rs_lat2_reg_2d*deg2rad)*rs_dlon2_2d*deg2rad
    rs_dy1 = r*rs_dlat1_2d*deg2rad
    rs_dy2 = r*rs_dlat2_2d*deg2rad

    print 'Reading data'
    u = empty([num_years, n3d])
    for year in range(start_year, end_year+1):
        print '...' + str(year)
        # Read horizontal velocity components for this year, annually average
        id = Dataset(file_head + str(year) + file_tail, 'r')
        ur = mean(id.variables['u'][:,:], axis=0)
        vr = mean(id.variables['v'][:,:], axis=0)
        id.close()
        # Unrotate
        u_tmp, v_tmp = unrotate_vector(rlon, rlat, ur, vr)
        # Save in array
        u[year-start_year,:] = u_tmp

    print 'Vertically integrating u*dz'
    int_udz = zeros([num_years, n2d])
    # Loop over nodes
    for n in range(n2d):
        # Loop over depth
        for k in range(max_num_layers-1):
            if node_columns[n,k+1] == -999:
                # Reached the bottom
                break
            # Trapezoidal rule
            top_id = node_columns[n,k]
            bot_id = node_columns[n,k+1]
            dz = node_depth[bot_id-1] - node_depth[top_id-1]
            # Now loop over years
            for year in range(num_years):
                int_udz[year,n] += 0.5*(u[year,top_id-1] + u[year,bot_id-1])*dz

    print 'Interpolating to regular grid'
    int_udz_reg_ws = zeros([num_years, size(ws_dy,0), size(ws_dy,1)])
    int_udz_reg_rs1 = zeros([num_years, size(rs_dy1,0), size(rs_dy1,1)])
    int_udz_reg_rs2 = zeros([num_years, size(rs_dy2,0), size(rs_dy2,1)])
    # For each element, check if a point on the regular lat-lon grid lies
    # within. If so, do barycentric interpolation to that point.
    for elm in elements:
        # Weddell Sea
        if amax(elm.lon) > ws_wbdry and amin(elm.lon) < ws_ebdry and amin(elm.lat) < ws_nbdry:
            # Find largest regular longitude value west of Element
            tmp = nonzero(ws_lon_reg > amin(elm.lon))[0]
            if len(tmp) == 0:
                # Element crosses the western boundary
                iW = 0
            else:
                iW = tmp[0] - 1
            # Find smallest regular longitude value east of Element
            tmp = nonzero(ws_lon_reg > amax(elm.lon))[0]
            if len(tmp) == 0:
                # Element crosses the eastern boundary
                iE = size(ws_lon_reg)
            else:
                iE = tmp[0]
            # Find largest regular latitude value south of Element
            tmp = nonzero(ws_lat_reg > amin(elm.lat))[0]
            if len(tmp) == 0:
                # Element crosses the southern boundary
                jS = 0
            else:
                jS = tmp[0] - 1
            # Find smallest regular latitude value north of Element
            tmp = nonzero(ws_lat_reg > amax(elm.lat))[0]
            if len(tmp) == 0:
                # Element crosses the northern boundary
                jN = size(ws_lat_reg)
            else:
                jN = tmp[0]
            for i in range(iW+1,iE):
                for j in range(jS+1,jN):
                    # There is a chance that the regular gridpoint at (i,j)
                    # lies within this element
                    lon0 = ws_lon_reg[i]
                    lat0 = ws_lat_reg[j]
                    if in_triangle(elm, lon0, lat0):
                        # Get area of entire triangle
                        area = triangle_area(elm.lon, elm.lat)
                        # Get area of each sub-triangle formed by
                        # (lon0, lat0)
                        area0 = triangle_area([lon0, elm.lon[1], elm.lon[2]], [lat0, elm.lat[1], elm.lat[2]])
                        area1 = triangle_area([lon0, elm.lon[0], elm.lon[2]], [lat0, elm.lat[0], elm.lat[2]])
                        area2 = triangle_area([lon0, elm.lon[0], elm.lon[1]], [lat0, elm.lat[0], elm.lat[1]])
                        # Find fractional area of each
                        cff = [area0/area, area1/area, area2/area]
                        # Now loop over years
                        for year in range(num_years):
                            # Find value of int_udz at each Node
                            vals = []
                            for n in range(3):
                                vals.append(int_udz[year,elm.nodes[n].id])
                            # Barycentric interpolation to lon0, lat0
                            int_udz_reg_ws[year,j,i] = sum(array(cff)*array(vals))
        # Ross Sea, part 1
        if amax(elm.lon) > rs_wbdry and amin(elm.lat) < rs_nbdry:
            tmp = nonzero(rs_lon1_reg > amin(elm.lon))[0]
            if len(tmp) == 0:
                iW = 0
            else:
                iW = tmp[0] - 1
            tmp = nonzero(rs_lon1_reg > amax(elm.lon))[0]
            if len(tmp) == 0:
                iE = size(rs_lon1_reg)
            else:
                iE = tmp[0]
            tmp = nonzero(rs_lat_reg > amin(elm.lat))[0]
            if len(tmp) == 0:
                jS = 0
            else:
                jS = tmp[0] - 1
            tmp = nonzero(rs_lat_reg > amax(elm.lat))[0]
            if len(tmp) == 0:
                jN = size(rs_lat_reg)
            else:
                jN = tmp[0]
            for i in range(iW+1,iE):
                for j in range(jS+1,jN):
                    lon0 = rs_lon1_reg[i]
                    lat0 = rs_lat_reg[j]
                    if in_triangle(elm, lon0, lat0):
                        area = triangle_area(elm.lon, elm.lat)
                        area0 = triangle_area([lon0, elm.lon[1], elm.lon[2]], [lat0, elm.lat[1], elm.lat[2]])
                        area1 = triangle_area([lon0, elm.lon[0], elm.lon[2]], [lat0, elm.lat[0], elm.lat[2]])
                        area2 = triangle_area([lon0, elm.lon[0], elm.lon[1]], [lat0, elm.lat[0], elm.lat[1]])
                        cff = [area0/area, area1/area, area2/area]
                        for year in range(num_years):
                            vals = []
                            for n in range(3):
                                vals.append(int_udz[year,elm.nodes[n].id])
                            int_udz_reg_rs1[year,j,i] = sum(array(cff)*array(vals))
        # Ross Sea, part 2
        if amin(elm.lon) < rs_ebdry and amin(elm.lat) < rs_nbdry:
            tmp = nonzero(rs_lon2_reg > amin(elm.lon))[0]
            if len(tmp) == 0:
                iW = 0
            else:
                iW = tmp[0] - 1
            tmp = nonzero(rs_lon2_reg > amax(elm.lon))[0]
            if len(tmp) == 0:
                iE = size(rs_lon2_reg)
            else:
                iE = tmp[0]
            tmp = nonzero(rs_lat_reg > amin(elm.lat))[0]
            if len(tmp) == 0:
                jS = 0
            else:
                jS = tmp[0] - 1
            tmp = nonzero(rs_lat_reg > amax(elm.lat))[0]
            if len(tmp) == 0:
                jN = size(rs_lat_reg)
            else:
                jN = tmp[0]
            for i in range(iW+1,iE):
                for j in range(jS+1,jN):
                    lon0 = rs_lon2_reg[i]
                    lat0 = rs_lat_reg[j]
                    if in_triangle(elm, lon0, lat0):
                        area = triangle_area(elm.lon, elm.lat)
                        area0 = triangle_area([lon0, elm.lon[1], elm.lon[2]], [lat0, elm.lat[1], elm.lat[2]])
                        area1 = triangle_area([lon0, elm.lon[0], elm.lon[2]], [lat0, elm.lat[0], elm.lat[2]])
                        area2 = triangle_area([lon0, elm.lon[0], elm.lon[1]], [lat0, elm.lat[0], elm.lat[1]])
                        cff = [area0/area, area1/area, area2/area]
                        for year in range(num_years):
                            vals = []
                            for n in range(3):
                                vals.append(int_udz[year,elm.nodes[n].id])
                            int_udz_reg_rs2[year,j,i] = sum(array(cff)*array(vals))

    # Indefinite integral from south to north of udz*dy, convert to Sv
    strf_ws = cumsum(int_udz_reg_ws*ws_dy, axis=1)*1e-6
    strf_rs1 = cumsum(int_udz_reg_rs1*rs_dy1, axis=1)*1e-6
    strf_rs2 = cumsum(int_udz_reg_rs2*rs_dy2, axis=1)*1e-6
    # Build timeseries
    for year in range(num_years):
        # Find most negative value    
        ws_trans[prev_years+year] = -1*amin(strf_ws[year,:])
        rs_trans[prev_years+year] = -1*min(amin(strf_rs1[year,:]), amin(strf_rs2[year,:]))

    # Make time axis
    time = range(start_year-prev_years, end_year+1)

    print 'Plotting'
    # Weddell Sea
    fig = figure()
    plot(time, ws_trans)
    xlabel('year')
    ylabel('Sv')
    xlim([start_year-prev_years, end_year])
    title('Weddell Sea Gyre transport')
    grid(True)
    fig.savefig(fig_dir + 'weddell_gyre.png')
    # Ross Sea
    fig = figure()
    plot(time, rs_trans)
    xlabel('year')
    ylabel('Sv')
    xlim([start_year-prev_years, end_year])
    title('Ross Sea Gyre transport')
    grid(True)
    fig.savefig(fig_dir + 'ross_gyre.png')

    print 'Saving results to log file'
    f = open(log_file, 'w')
    f.write('Weddell Sea Gyre transport (Sv)\n')
    for t in range(prev_years+num_years):
        f.write(str(ws_trans[t]) + '\n')
    f.write('Ross Sea Gyre transport (Sv)\n')
    for t in range(prev_years+num_years):
        f.write(str(rs_trans[t]) + '\n')
    f.close()
예제 #13
0
def cavity_fields_res(var_name):

    # Paths to mesh directories
    mesh_path_low = '../FESOM/mesh/low_res/'
    mesh_path_high = '../FESOM/mesh/high_res/'
    # Paths to output files
    output_path_low = '../FESOM/lowres_spinup/rep3/'
    output_path_high = '../FESOM/highres_spinup/rep3/'
    if var_name == 'melt':
        file_name = 'annual_avg.forcing.diag.nc'
    elif var_name in ['temp', 'salt', 'vsfc', 'vavg']:
        file_name = 'annual_avg.oce.mean.nc'

    # Name of each ice shelf
    shelf_names = [
        'Larsen D Ice Shelf', 'Larsen C Ice Shelf',
        'Wilkins & George VI & Stange Ice Shelves', 'Ronne-Filchner Ice Shelf',
        'Abbot Ice Shelf', 'Pine Island Glacier Ice Shelf',
        'Thwaites Ice Shelf', 'Dotson Ice Shelf', 'Getz Ice Shelf',
        'Nickerson Ice Shelf', 'Sulzberger Ice Shelf', 'Mertz Ice Shelf',
        'Totten & Moscow University Ice Shelves', 'Shackleton Ice Shelf',
        'West Ice Shelf', 'Amery Ice Shelf', 'Prince Harald Ice Shelf',
        'Baudouin & Borchgrevink Ice Shelves', 'Lazarev Ice Shelf',
        'Nivl Ice Shelf', 'Fimbul & Jelbart & Ekstrom Ice Shelves',
        'Brunt & Riiser-Larsen Ice Shelves', 'Ross Ice Shelf'
    ]
    # Beginnings of filenames for figures
    fig_heads = [
        'larsen_d', 'larsen_c', 'wilkins_georgevi_stange', 'ronne_filchner',
        'abbot', 'pig', 'thwaites', 'dotson', 'getz', 'nickerson',
        'sulzberger', 'mertz', 'totten_moscowuni', 'shackleton', 'west',
        'amery', 'prince_harald', 'baudouin_borchgrevink', 'lazarev', 'nivl',
        'fimbul_jelbart_ekstrom', 'brunt_riiser_larsen', 'ross'
    ]
    # Limits on longitude and latitude for each ice shelf
    # Note Ross crosses 180W=180E
    lon_min = [
        -62.67, -65.5, -79.17, -85, -104.17, -102.5, -108.33, -114.5, -135.67,
        -149.17, -155, 144, 115, 94.17, 80.83, 65, 33.83, 19, 12.9, 9.33,
        -10.05, -28.33, 158.33
    ]
    lon_max = [
        -59.33, -60, -66.67, -28.33, -88.83, -99.17, -103.33, -111.5, -114.33,
        -140, -145, 146.62, 123.33, 102.5, 89.17, 75, 37.67, 33.33, 16.17,
        12.88, 7.6, -10.33, -146.67
    ]
    lat_min = [
        -73.03, -69.35, -74.17, -83.5, -73.28, -75.5, -75.5, -75.33, -74.9,
        -76.42, -78, -67.83, -67.17, -66.67, -67.83, -73.67, -69.83, -71.67,
        -70.5, -70.75, -71.83, -76.33, -85
    ]
    lat_max = [
        -69.37, -66.13, -69.5, -74.67, -71.67, -74.17, -74.67, -73.67, -73,
        -75.17, -76.41, -66.67, -66.5, -64.83, -66.17, -68.33, -68.67, -68.33,
        -69.33, -69.83, -69.33, -71.5, -77
    ]
    num_shelves = len(shelf_names)

    # Constants
    sec_per_year = 365.25 * 24 * 3600
    deg2rad = pi / 180.0
    # Number of bins in each direction for vector overlay
    num_bins = 50

    print 'Building FESOM mesh'
    # Mask open ocean
    elements_low, mask_patches_low = make_patches(mesh_path_low,
                                                  circumpolar=True,
                                                  mask_cavities=True)
    elements_high, mask_patches_high = make_patches(mesh_path_high,
                                                    circumpolar=True,
                                                    mask_cavities=True)
    # Unmask ice shelves
    patches_low = iceshelf_mask(elements_low)
    patches_high = iceshelf_mask(elements_high)
    if var_name == 'draft':
        # Nothing more to read
        pass
    else:
        print 'Reading data'
        id_low = Dataset(output_path_low + file_name, 'r')
        id_high = Dataset(output_path_high + file_name, 'r')
        if var_name == 'melt':
            # Convert from m/s to m/y
            node_data_low = id_low.variables['wnet'][0, :] * sec_per_year
            node_data_high = id_high.variables['wnet'][0, :] * sec_per_year
        elif var_name == 'temp':
            # Read full 3D field for now
            node_data_low = id_low.variables['temp'][0, :]
            node_data_high = id_high.variables['temp'][0, :]
        elif var_name == 'salt':
            # Read full 3D field for now
            node_data_low = id_low.variables['salt'][0, :]
            node_data_high = id_high.variables['salt'][0, :]
        elif var_name in ['vsfc', 'vavg']:
            # The overlaid vectors are based on nodes not elements, so many
            # of the fesom_grid data structures fail to apply and we need to
            # read some of the FESOM grid files again.
            # Read the cavity flag for each 2D surface node
            cavity_low = []
            f = open(mesh_path_low + 'cavity_flag_nod2d.out', 'r')
            for line in f:
                tmp = int(line)
                if tmp == 1:
                    cavity_low.append(True)
                elif tmp == 0:
                    cavity_low.append(False)
                else:
                    print 'Problem'
                    #return
            f.close()
            cavity_high = []
            f = open(mesh_path_high + 'cavity_flag_nod2d.out', 'r')
            for line in f:
                tmp = int(line)
                if tmp == 1:
                    cavity_high.append(True)
                elif tmp == 0:
                    cavity_high.append(False)
                else:
                    print 'Problem'
                    #return
            f.close()
            # Save the number of 2D nodes
            n2d_low = len(cavity_low)
            n2d_high = len(cavity_high)
            # Read rotated lat and lon for each node; also read depth which is
            # needed for vertically averaged velocity
            f = open(mesh_path_low + 'nod3d.out', 'r')
            f.readline()
            rlon_low = []
            rlat_low = []
            node_depth_low = []
            for line in f:
                tmp = line.split()
                lon_tmp = float(tmp[1])
                lat_tmp = float(tmp[2])
                node_depth_tmp = -1 * float(tmp[3])
                if lon_tmp < -180:
                    lon_tmp += 360
                elif lon_tmp > 180:
                    lon_tmp -= 360
                rlon_low.append(lon_tmp)
                rlat_low.append(lat_tmp)
                node_depth_low.append(node_depth_tmp)
            f.close()
            # For lat and lon, only care about the 2D nodes (the first n2d
            # indices)
            rlon_low = array(rlon_low[0:n2d_low])
            rlat_low = array(rlat_low[0:n2d_low])
            node_depth_low = array(node_depth_low)
            # Repeat for high resolution
            f = open(mesh_path_high + 'nod3d.out', 'r')
            f.readline()
            rlon_high = []
            rlat_high = []
            node_depth_high = []
            for line in f:
                tmp = line.split()
                lon_tmp = float(tmp[1])
                lat_tmp = float(tmp[2])
                node_depth_tmp = -1 * float(tmp[3])
                if lon_tmp < -180:
                    lon_tmp += 360
                elif lon_tmp > 180:
                    lon_tmp -= 360
                rlon_high.append(lon_tmp)
                rlat_high.append(lat_tmp)
                node_depth_high.append(node_depth_tmp)
            f.close()
            rlon_high = array(rlon_high[0:n2d_high])
            rlat_high = array(rlat_high[0:n2d_high])
            node_depth_high = array(node_depth_high)
            # Unrotate longitude
            lon_low, lat_low = unrotate_grid(rlon_low, rlat_low)
            lon_high, lat_high = unrotate_grid(rlon_high, rlat_high)
            # Calculate polar coordinates of each node
            x_low = -(lat_low + 90) * cos(lon_low * deg2rad + pi / 2)
            y_low = (lat_low + 90) * sin(lon_low * deg2rad + pi / 2)
            x_high = -(lat_high + 90) * cos(lon_high * deg2rad + pi / 2)
            y_high = (lat_high + 90) * sin(lon_high * deg2rad + pi / 2)
            if var_name == 'vavg':
                # Read lists of which nodes are directly below which
                f = open(mesh_path_low + 'aux3d.out', 'r')
                max_num_layers_low = int(f.readline())
                node_columns_low = zeros([n2d_low, max_num_layers_low])
                for n in range(n2d_low):
                    for k in range(max_num_layers_low):
                        node_columns_low[n, k] = int(f.readline())
                node_columns_low = node_columns_low.astype(int)
                f.close()
                # Repeat for high resolution
                f = open(mesh_path_high + 'aux3d.out', 'r')
                max_num_layers_high = int(f.readline())
                node_columns_high = zeros([n2d_high, max_num_layers_high])
                for n in range(n2d_high):
                    for k in range(max_num_layers_high):
                        node_columns_high[n, k] = int(f.readline())
                node_columns_high = node_columns_high.astype(int)
                f.close()
            # Now we can actually read the data
            # Read full 3D field for both u and v
            node_ur_3d_low = id_low.variables['u'][0, :]
            node_vr_3d_low = id_low.variables['v'][0, :]
            node_ur_3d_high = id_high.variables['u'][0, :]
            node_vr_3d_high = id_high.variables['v'][0, :]
            if var_name == 'vsfc':
                # Only care about the first n2d nodes
                node_ur_low = node_ur_3d_low[0:n2d_low]
                node_vr_low = node_vr_3d_low[0:n2d_low]
                node_ur_high = node_ur_3d_high[0:n2d_high]
                node_vr_high = node_vr_3d_high[0:n2d_high]
            elif var_name == 'vavg':
                # Vertically average
                node_ur_low = zeros(n2d_low)
                node_vr_low = zeros(n2d_low)
                for n in range(n2d_low):
                    # Integrate udz, vdz, and dz over this water column
                    udz_col = 0
                    vdz_col = 0
                    dz_col = 0
                    for k in range(max_num_layers_low - 1):
                        if node_columns_low[n, k + 1] == -999:
                            # Reached the bottom
                            break
                        # Trapezoidal rule
                        top_id = node_columns_low[n, k]
                        bot_id = node_columns_low[n, k + 1]
                        dz_tmp = node_depth_low[bot_id -
                                                1] - node_depth_low[top_id - 1]
                        udz_col += 0.5 * (node_ur_3d_low[top_id - 1] +
                                          node_ur_3d_low[bot_id - 1]) * dz_tmp
                        vdz_col += 0.5 * (node_vr_3d_low[top_id - 1] +
                                          node_vr_3d_low[bot_id - 1]) * dz_tmp
                        dz_col += dz_tmp
                    # Convert from integrals to averages
                    node_ur_low[n] = udz_col / dz_col
                    node_vr_low[n] = vdz_col / dz_col
                # Repeat for high resolution
                node_ur_high = zeros(n2d_high)
                node_vr_high = zeros(n2d_high)
                for n in range(n2d_high):
                    udz_col = 0
                    vdz_col = 0
                    dz_col = 0
                    for k in range(max_num_layers_high - 1):
                        if node_columns_high[n, k + 1] == -999:
                            break
                        top_id = node_columns_high[n, k]
                        bot_id = node_columns_high[n, k + 1]
                        dz_tmp = node_depth_high[bot_id -
                                                 1] - node_depth_high[top_id -
                                                                      1]
                        udz_col += 0.5 * (node_ur_3d_high[top_id - 1] +
                                          node_ur_3d_high[bot_id - 1]) * dz_tmp
                        vdz_col += 0.5 * (node_vr_3d_high[top_id - 1] +
                                          node_vr_3d_high[bot_id - 1]) * dz_tmp
                        dz_col += dz_tmp
                    node_ur_high[n] = udz_col / dz_col
                    node_vr_high[n] = vdz_col / dz_col
            # Unrotate
            node_u_low, node_v_low = unrotate_vector(rlon_low, rlat_low,
                                                     node_ur_low, node_vr_low)
            node_u_high, node_v_high = unrotate_vector(rlon_high, rlat_high,
                                                       node_ur_high,
                                                       node_vr_high)
            # Calculate speed
            node_data_low = sqrt(node_u_low**2 + node_v_low**2)
            node_data_high = sqrt(node_u_high**2 + node_v_high**2)
        id_low.close()
        id_high.close()
    # Calculate given field at each element
    data_low = []
    for elm in elements_low:
        # For each element in an ice shelf cavity, append the mean value
        # for the 3 component Nodes
        if elm.cavity:
            if var_name == 'draft':
                # Ice shelf draft is depth of surface layer
                data_low.append(
                    mean([
                        elm.nodes[0].depth, elm.nodes[1].depth,
                        elm.nodes[2].depth
                    ]))
            elif var_name in ['melt', 'vsfc', 'vavg']:
                # Surface nodes (or 2D in the case of vavg)
                data_low.append(
                    mean([
                        node_data_low[elm.nodes[0].id],
                        node_data_low[elm.nodes[1].id],
                        node_data_low[elm.nodes[2].id]
                    ]))
            elif var_name in ['temp', 'salt']:
                # Bottom nodes
                data_low.append(
                    mean([
                        node_data_low[elm.nodes[0].find_bottom().id],
                        node_data_low[elm.nodes[1].find_bottom().id],
                        node_data_low[elm.nodes[2].find_bottom().id]
                    ]))
    # Repeat for high resolution
    data_high = []
    for elm in elements_high:
        if elm.cavity:
            if var_name == 'draft':
                data_high.append(
                    mean([
                        elm.nodes[0].depth, elm.nodes[1].depth,
                        elm.nodes[2].depth
                    ]))
            elif var_name in ['melt', 'vsfc', 'vavg']:
                data_high.append(
                    mean([
                        node_data_high[elm.nodes[0].id],
                        node_data_high[elm.nodes[1].id],
                        node_data_high[elm.nodes[2].id]
                    ]))
            elif var_name in ['temp', 'salt']:
                data_high.append(
                    mean([
                        node_data_high[elm.nodes[0].find_bottom().id],
                        node_data_high[elm.nodes[1].find_bottom().id],
                        node_data_high[elm.nodes[2].find_bottom().id]
                    ]))

    # Loop over ice shelves
    for index in range(num_shelves):
        print 'Processing ' + shelf_names[index]
        # Convert lat/lon bounds to polar coordinates for plotting
        x1 = -(lat_min[index] + 90) * cos(lon_min[index] * deg2rad + pi / 2)
        y1 = (lat_min[index] + 90) * sin(lon_min[index] * deg2rad + pi / 2)
        x2 = -(lat_min[index] + 90) * cos(lon_max[index] * deg2rad + pi / 2)
        y2 = (lat_min[index] + 90) * sin(lon_max[index] * deg2rad + pi / 2)
        x3 = -(lat_max[index] + 90) * cos(lon_min[index] * deg2rad + pi / 2)
        y3 = (lat_max[index] + 90) * sin(lon_min[index] * deg2rad + pi / 2)
        x4 = -(lat_max[index] + 90) * cos(lon_max[index] * deg2rad + pi / 2)
        y4 = (lat_max[index] + 90) * sin(lon_max[index] * deg2rad + pi / 2)
        # Find the new bounds on x and y
        x_min = amin(array([x1, x2, x3, x4]))
        x_max = amax(array([x1, x2, x3, x4]))
        y_min = amin(array([y1, y2, y3, y4]))
        y_max = amax(array([y1, y2, y3, y4]))
        # Now make the plot square: enlarge the smaller of delta_x and delta_y
        # so they are equal
        delta_x = x_max - x_min
        delta_y = y_max - y_min
        if delta_x > delta_y:
            diff = 0.5 * (delta_x - delta_y)
            y_min -= diff
            y_max += diff
        elif delta_y > delta_x:
            diff = 0.5 * (delta_y - delta_x)
            x_min -= diff
            x_max += diff
        # Set up a grey square to fill the background with land
        x_reg, y_reg = meshgrid(linspace(x_min, x_max, num=100),
                                linspace(y_min, y_max, num=100))
        land_square = zeros(shape(x_reg))
        # Find bounds on variables in this region
        # Start with something impossible
        var_min = amax(data_low)
        var_max = amin(data_low)
        # Modify with low-res
        i = 0
        for elm in elements_low:
            if elm.cavity:
                if any(elm.x >= x_min) and any(elm.x <= x_max) and any(
                        elm.y >= y_min) and any(elm.y <= y_max):
                    if data_low[i] < var_min:
                        var_min = data_low[i]
                    if data_low[i] > var_max:
                        var_max = data_low[i]
                i += 1
        # Modify with high-res
        i = 0
        for elm in elements_high:
            if elm.cavity:
                if any(elm.x >= x_min) and any(elm.x <= x_max) and any(
                        elm.y >= y_min) and any(elm.y <= y_max):
                    if data_high[i] < var_min:
                        var_min = data_high[i]
                    if data_high[i] > var_max:
                        var_max = data_high[i]
                i += 1
        if var_name == 'melt':
            # Special colour map
            if var_min < 0:
                # There is refreezing here; include blue for elements below 0
                cmap_vals = array([
                    var_min, 0, 0.25 * var_max, 0.5 * var_max, 0.75 * var_max,
                    var_max
                ])
                cmap_colors = [(0.26, 0.45, 0.86), (1, 1, 1), (1, 0.9, 0.4),
                               (0.99, 0.59, 0.18), (0.5, 0.0, 0.08),
                               (0.96, 0.17, 0.89)]
                cmap_vals_norm = (cmap_vals - var_min) / (var_max - var_min)
                cmap_list = []
                for i in range(size(cmap_vals)):
                    cmap_list.append((cmap_vals_norm[i], cmap_colors[i]))
                mf_cmap = LinearSegmentedColormap.from_list(
                    'melt_freeze', cmap_list)
            else:
                # No refreezing
                cmap_vals = array([
                    0, 0.25 * var_max, 0.5 * var_max, 0.75 * var_max, var_max
                ])
                cmap_colors = [(1, 1, 1), (1, 0.9, 0.4), (0.99, 0.59, 0.18),
                               (0.5, 0.0, 0.08), (0.96, 0.17, 0.89)]
                cmap_vals_norm = cmap_vals / var_max
                cmap_list = []
                for i in range(size(cmap_vals)):
                    cmap_list.append((cmap_vals_norm[i], cmap_colors[i]))
                mf_cmap = LinearSegmentedColormap.from_list(
                    'melt_freeze', cmap_list)
            colour_map = mf_cmap
        else:
            colour_map = 'jet'
        if var_name in ['vsfc', 'vavg']:
            # Make vectors for overlay
            # Set up bins (edges)
            x_bins = linspace(x_min, x_max, num=num_bins + 1)
            y_bins = linspace(y_min, y_max, num=num_bins + 1)
            # Calculate centres of bins (for plotting)
            x_centres = 0.5 * (x_bins[:-1] + x_bins[1:])
            y_centres = 0.5 * (y_bins[:-1] + y_bins[1:])
            # Low resolution
            # First set up arrays to integrate velocity in each bin
            # Simple averaging of all the points inside each bin
            u_low = zeros([size(y_centres), size(x_centres)])
            v_low = zeros([size(y_centres), size(x_centres)])
            num_pts_low = zeros([size(y_centres), size(x_centres)])
            # Convert to polar coordinates, rotate to account for longitude in
            # circumpolar projection, and convert back to vector components
            theta_low = arctan2(node_v_low, node_u_low)
            theta_circ_low = theta_low - lon_low * deg2rad
            u_circ_low = node_data_low * cos(
                theta_circ_low)  # node_data_low is speed
            v_circ_low = node_data_low * sin(theta_circ_low)
            # Loop over 2D nodes to fill in the velocity bins
            for n in range(n2d_low):
                # Make sure we're in an ice shelf cavity
                if cavity_low[n]:
                    # Check if we're in the region of interest
                    if x_low[n] > x_min and x_low[n] < x_max and y_low[
                            n] > y_min and y_low[n] < y_max:
                        # Figure out which bins this falls into
                        x_index = nonzero(x_bins > x_low[n])[0][0] - 1
                        y_index = nonzero(y_bins > y_low[n])[0][0] - 1
                        # Integrate
                        u_low[y_index, x_index] += u_circ_low[n]
                        v_low[y_index, x_index] += v_circ_low[n]
                        num_pts_low[y_index, x_index] += 1
            # Convert from sums to averages
            # First mask out points with no data
            u_low = ma.masked_where(num_pts_low == 0, u_low)
            v_low = ma.masked_where(num_pts_low == 0, v_low)
            # Divide everything else by the number of points
            flag = num_pts_low > 0
            u_low[flag] = u_low[flag] / num_pts_low[flag]
            v_low[flag] = v_low[flag] / num_pts_low[flag]
            # Repeat for high resolution
            u_high = zeros([size(y_centres), size(x_centres)])
            v_high = zeros([size(y_centres), size(x_centres)])
            num_pts_high = zeros([size(y_centres), size(x_centres)])
            theta_high = arctan2(node_v_high, node_u_high)
            theta_circ_high = theta_high - lon_high * deg2rad
            u_circ_high = node_data_high * cos(theta_circ_high)
            v_circ_high = node_data_high * sin(theta_circ_high)
            for n in range(n2d_high):
                if cavity_high[n]:
                    if x_high[n] > x_min and x_high[n] < x_max and y_high[
                            n] > y_min and y_high[n] < y_max:
                        x_index = nonzero(x_bins > x_high[n])[0][0] - 1
                        y_index = nonzero(y_bins > y_high[n])[0][0] - 1
                        u_high[y_index, x_index] += u_circ_high[n]
                        v_high[y_index, x_index] += v_circ_high[n]
                        num_pts_high[y_index, x_index] += 1
            u_high = ma.masked_where(num_pts_high == 0, u_high)
            v_high = ma.masked_where(num_pts_high == 0, v_high)
            flag = num_pts_high > 0
            u_high[flag] = u_high[flag] / num_pts_high[flag]
            v_high[flag] = v_high[flag] / num_pts_high[flag]
        # Plot
        fig = figure(figsize=(30, 12))
        # Low resolution
        ax1 = fig.add_subplot(1, 2, 1, aspect='equal')
        # Start with land background
        contourf(x_reg, y_reg, land_square, 1, colors=(('0.6', '0.6', '0.6')))
        # Add ice shelf elements
        img1 = PatchCollection(patches_low, cmap=colour_map)
        img1.set_array(array(data_low))
        img1.set_edgecolor('face')
        img1.set_clim(vmin=var_min, vmax=var_max)
        ax1.add_collection(img1)
        # Mask out the open ocean in white
        overlay1 = PatchCollection(mask_patches_low, facecolor=(1, 1, 1))
        overlay1.set_edgecolor('face')
        ax1.add_collection(overlay1)
        if var_name in ['vsfc', 'vavg']:
            # Overlay vectors
            quiver(x_centres,
                   y_centres,
                   u_low,
                   v_low,
                   scale=1.5,
                   color='black')
        xlim([x_min, x_max])
        ylim([y_min, y_max])
        axis('off')
        title('Low-res', fontsize=24)
        # Repeat for high resolution
        ax2 = fig.add_subplot(1, 2, 2, aspect='equal')
        contourf(x_reg, y_reg, land_square, 1, colors=(('0.6', '0.6', '0.6')))
        img2 = PatchCollection(patches_high, cmap=colour_map)
        img2.set_array(array(data_high))
        img2.set_edgecolor('face')
        img2.set_clim(vmin=var_min, vmax=var_max)
        ax2.add_collection(img2)
        overlay2 = PatchCollection(mask_patches_high, facecolor=(1, 1, 1))
        overlay2.set_edgecolor('face')
        ax2.add_collection(overlay2)
        if var_name in ['vsfc', 'vavg']:
            quiver(x_centres,
                   y_centres,
                   u_high,
                   v_high,
                   scale=1.5,
                   color='black')
        xlim([x_min, x_max])
        ylim([y_min, y_max])
        axis('off')
        title('High-res', fontsize=24)
        # Colourbar on the right
        cbaxes = fig.add_axes([0.92, 0.2, 0.01, 0.6])
        cbar = colorbar(img2, cax=cbaxes)
        cbar.ax.tick_params(labelsize=20)
        # Main title
        if var_name == 'draft':
            title_string = ' draft (m)'
        elif var_name == 'melt':
            title_string = ' melt rate (m/y)'
        elif var_name == 'temp':
            title_string = r' bottom water temperature ($^{\circ}$C)'
        elif var_name == 'salt':
            title_string = ' bottom water salinity (psu)'
        elif var_name == 'vsfc':
            title_string = ' surface velocity (m/s)'
        elif var_name == 'vavg':
            title_string = ' vertically averaged velocity (m/s)'
        suptitle(shelf_names[index] + title_string, fontsize=30)
        subplots_adjust(wspace=0.05)
        #fig.show()
        fig.savefig(fig_heads[index] + '_' + var_name + '.png')
예제 #14
0
def fesom_intersectgrid (mesh_path, file_path, var_name, tstep, lon_min, lon_max, lat_min, lat_max, depth_min, depth_max, num_lat, num_depth):

    # Build the regular FESOM grid
    elements = fesom_grid(mesh_path)

    # Read data
    id = Dataset(file_path, 'r')
    data = id.variables[var_name][tstep-1,:]
    # Check for vector variables that need to be unrotated
    if var_name in ['u', 'v']:
        # Read the rotated lat and lon
        fid = open(mesh_path + 'nod3d.out', 'r')
        fid.readline()
        lon = []
        lat = []
        for line in fid:
            tmp = line.split()
            lon_tmp = float(tmp[1])
            lat_tmp = float(tmp[2])
            if lon_tmp < -180:
                lon_tmp += 360
            elif lon_tmp > 180:
                lon_tmp -= 360
            lon.append(lon_tmp)
            lat.append(lat_tmp)
        fid.close()
        lon = array(lon)
        lat = array(lat)
        if var_name == 'u':
            u_data = data[:]
            v_data = id.variables['v'][tstep-1,:]
            u_data_lonlat, v_data_lonlat = unrotate_vector(lon, lat, u_data, v_data)
            data = u_data_lonlat[:]
        elif var_name == 'v':
            v_data = data[:]
            u_data = id.variables['u'][tstep-1,:]
            u_data_lonlat, v_data_lonlat = unrotate_vector(lon, lat, u_data, v_data)
            data = v_data_lonlat[:]
    id.close()

    # Build the regular grid
    lat_vals = linspace(lat_min, lat_max, num_lat)
    # Make depth positive to match the "depth" attribute in grid Nodes
    depth_vals = -1*linspace(depth_min, depth_max, num_depth)

    # Set up array of NaNs to overwrite with zonally averaged data
    data_reg = zeros((num_depth, num_lat))
    data_reg[:,:] = NaN

    # Process one latitude value at a time
    for j in range(num_lat):
        inodes_lat = []
        # Loop over 2D grid Elements
        for elm in elements:
            # Select elements which intersect the current latitude, and which
            # fall entirely between the longitude bounds
            if any(elm.y <= lat_vals[j]) and any(elm.y >= lat_vals[j]) and all(elm.x >= lon_min) and all(elm.x <= lon_max):
                # Special case where nodes (corners) of the element are exactly
                # at lat_vals[j]
                if any(elm.y == lat_vals[j]):
                    # If exactly one of the corners is at lat_vals[j], ignore
                    # it; this element only touches lat_vals[j] at one point
                    # If two of the corners are at lat_vals[j], an entire side
                    # of the element lies along the line lat_vals[j]
                    if count_nonzero(elm.y == lat_vals[j]) == 2:
                        # Select these two Nodes
                        index = nonzero(elm.y == lat_vals[j])
                        nodes = elm.nodes[index]
                        node1 = nodes[0]
                        node2 = nodes[1]
                        # Convert to IntersectNodes and add them to inodes_lat
                        inodes_lat.append(coincide_inode(node1, depth_vals, data))
                        inodes_lat.append(coincide_inode(node2, depth_vals, data))
                    # Impossible for all three corners to be at lat_vals[j]
                else:
                    # Regular case
                    # Find the two sides of the triangular element which
                    # intersect lat_vals[j]
                    # For each such side, interpolate an IntersectNode between
                    # the two endpoint nodes, and add them to inodes_lat
                    if any(array([elm.y[0], elm.y[1]]) < lat_vals[j]) and any(array([elm.y[0], elm.y[1]]) > lat_vals[j]):
                        inodes_lat.append(interp_inode(elm.nodes[0], elm.nodes[1], lat_vals[j], depth_vals, data))
                    if any(array([elm.y[1], elm.y[2]]) < lat_vals[j]) and any(array([elm.y[1], elm.y[2]]) > lat_vals[j]):
                        inodes_lat.append(interp_inode(elm.nodes[1], elm.nodes[2], lat_vals[j], depth_vals, data))
                    if any(array([elm.y[0], elm.y[2]]) < lat_vals[j]) and any(array([elm.y[0], elm.y[2]]) > lat_vals[j]):
                        inodes_lat.append(interp_inode(elm.nodes[0], elm.nodes[2], lat_vals[j], depth_vals, data))

        # Sort inodes_lat by longitude (ascending)
        inodes_lat.sort(key=lambda inode: inode.lon)

        # Interpolate the variable values at each depth
        for k in range(num_depth):
            valid_lon = []
            valid_var = []
            for inode in inodes_lat:
                # Select all IntersectNodes where data exists at the current
                # depth level
                if inode.var[k] is not nan:
                    # Only continue if an identical inode (same longitude)
                    # hasn't already been added to valid_lon and valid_var
                    # (this will happen on adjacent elements which share a side)
                    if inode.lon not in valid_lon:
                        # Save longitude and variable values
                        valid_lon.append(inode.lon)
                        valid_var.append(inode.var[k])
            # Convert to numpy arrays so we can do math with them
            valid_lon = array(valid_lon)
            valid_var = array(valid_var)
            if len(valid_lon) == 0:
                # No valid data; leave data_reg[k,j] as NaN
                pass
            elif len(valid_lon) == 1:
                # Only one valid data point; save to data_reg
                data_reg[k,j] = valid_var[0]
            else:
                # Average over longitude
                # Trapezoidal rule for integration
                dlon = valid_lon[1:] - valid_lon[0:-1]
                var_centres = 0.5*(valid_var[0:-1] + valid_var[1:])
                # Divide integral of var_centres*dlon by integral of dlon
                # to get average; save to data_reg
                var_avg = sum(var_centres*dlon)/sum(dlon)
                data_reg[k,j] = var_avg

    # Convert depth back to negative for plotting
    depth_vals = -1*depth_vals

    return lat_vals, depth_vals, data_reg
예제 #15
0
def barotropic_streamfunction_diff():

    mesh_path = '/short/y99/kaa561/FESOM/mesh/meshB/'
    directory_beg = '/short/y99/kaa561/FESOM/highres_spinup/'
    directories = [
        '/short/y99/kaa561/FESOM/rcp45_M/', '/short/y99/kaa561/FESOM/rcp45_A/',
        '/short/y99/kaa561/FESOM/rcp85_M/', '/short/y99/kaa561/FESOM/rcp85_A/'
    ]
    file_beg = 'annual_avg.oce.mean.1996.2005.nc'
    file_end = 'annual_avg.oce.mean.2091.2100.nc'
    num_expts = len(directories)
    expt_names = ['RCP 4.5 M', 'RCP 4.5 A', 'RCP 8.5 M', 'RCP 8.5 A']
    expt_filetails = ['rcp45_M', 'rcp45_A', 'rcp85_M', 'rcp85_A']
    # Bounds on regular grid
    lon_min = -180
    lon_max = 180
    lat_min = -85
    lat_max = -60
    # Number of points on regular grid
    num_lon = 1000
    num_lat = 250
    # Radius of the Earth in metres
    r = 6.371e6
    # Degrees to radians coversion factor
    deg2rad = pi / 180.0

    print 'Building mesh'
    elements = fesom_grid(mesh_path, circumpolar=False, cross_180=True)
    # Read number of 2D nodes
    f = open(mesh_path + 'nod2d.out', 'r')
    n2d = int(f.readline())
    f.close()
    # Read (rotated) lon, lat, and depth, at each 3D node
    f = open(mesh_path + 'nod3d.out', 'r')
    f.readline()
    rlon = []
    rlat = []
    node_depth = []
    for line in f:
        tmp = line.split()
        lon_tmp = float(tmp[1])
        lat_tmp = float(tmp[2])
        node_depth_tmp = -1 * float(tmp[3])
        if lon_tmp < -180:
            lon_tmp += 360
        elif lon_tmp > 180:
            lon_tmp -= 360
        rlon.append(lon_tmp)
        rlat.append(lat_tmp)
        node_depth.append(node_depth_tmp)
    f.close()
    rlon = array(rlon)
    rlat = array(rlat)
    node_depth = array(node_depth)
    # Read lists of which nodes are directly below which
    f = open(mesh_path + 'aux3d.out', 'r')
    max_num_layers = int(f.readline())
    node_columns = zeros([n2d, max_num_layers])
    for n in range(n2d):
        for k in range(max_num_layers):
            node_columns[n, k] = int(f.readline())
    node_columns = node_columns.astype(int)
    f.close()
    # Set up regular grid
    # Start with boundaries
    lon_reg_edges = linspace(lon_min, lon_max, num_lon + 1)
    lat_reg_edges = linspace(lat_min, lat_max, num_lat + 1)
    # Now get centres
    lon_reg = 0.5 * (lon_reg_edges[:-1] + lon_reg_edges[1:])
    lat_reg = 0.5 * (lat_reg_edges[:-1] + lat_reg_edges[1:])
    # Also get differentials in lon-lat space
    dlon = lon_reg_edges[1:] - lon_reg_edges[:-1]
    dlat = lat_reg_edges[1:] - lat_reg_edges[:-1]
    # Make 2D versions
    lon_reg_2d, lat_reg_2d = meshgrid(lon_reg, lat_reg)
    dlon_2d, dlat_2d = meshgrid(dlon, dlat)
    # Calculate differentials in Cartesian space
    dx = r * cos(lat_reg_2d * deg2rad) * dlon_2d * deg2rad
    dy = r * dlat_2d * deg2rad

    print 'Reading data'
    print '...1996-2005'
    # Read 3D rotated u and v
    id = Dataset(directory_beg + file_beg, 'r')
    ur = id.variables['u'][0, :]
    vr = id.variables['v'][0, :]
    id.close()
    # Unrotate
    u, v = unrotate_vector(rlon, rlat, ur, vr)
    # Vertically integrate u*dz
    int_udz_beg = zeros(n2d)
    # Loop over nodes
    for n in range(n2d):
        # Loop over depth
        for k in range(max_num_layers - 1):
            if node_columns[n, k + 1] == -999:
                # Reached the bottom
                break
            # Trapezoidal rule
            top_id = node_columns[n, k]
            bot_id = node_columns[n, k + 1]
            dz = node_depth[bot_id - 1] - node_depth[top_id - 1]
            int_udz_beg[n] += 0.5 * (u[top_id - 1] + u[bot_id - 1]) * dz
    int_udz_end = zeros([num_expts, n2d])
    for expt in range(num_expts):
        print '...' + expt_names[expt]
        id = Dataset(directories[expt] + file_end, 'r')
        ur = id.variables['u'][0, :]
        vr = id.variables['v'][0, :]
        id.close()
        u, v = unrotate_vector(rlon, rlat, ur, vr)
        for n in range(n2d):
            for k in range(max_num_layers - 1):
                if node_columns[n, k + 1] == -999:
                    break
                top_id = node_columns[n, k]
                bot_id = node_columns[n, k + 1]
                dz = node_depth[bot_id - 1] - node_depth[top_id - 1]
                int_udz_end[expt,
                            n] += 0.5 * (u[top_id - 1] + u[bot_id - 1]) * dz

    print 'Interpolating to regular grid'
    int_udz_reg_beg = zeros([num_lat, num_lon])
    int_udz_reg_end = zeros([num_expts, num_lat, num_lon])
    # For each element, check if a point on the regular lat-lon grid lies
    # within. If so, do barycentric interpolation to that point.
    for elm in elements:
        # Check if we are within domain of regular grid
        if amin(elm.lat) > lat_max:
            continue
        # Find largest regular longitude value west of Element
        tmp = nonzero(lon_reg > amin(elm.lon))[0]
        if len(tmp) == 0:
            # Element crosses the western boundary
            iW = 0
        else:
            iW = tmp[0] - 1
        # Find smallest regular longitude value east of Element
        tmp = nonzero(lon_reg > amax(elm.lon))[0]
        if len(tmp) == 0:
            # Element crosses the eastern boundary
            iE = num_lon
        else:
            iE = tmp[0]
        # Find largest regular latitude value south of Element
        tmp = nonzero(lat_reg > amin(elm.lat))[0]
        if len(tmp) == 0:
            # Element crosses the southern boundary
            jS = 0
        else:
            jS = tmp[0] - 1
        # Find smallest regular latitude value north of Element
        tmp = nonzero(lat_reg > amax(elm.lat))[0]
        if len(tmp) == 0:
            # Element crosses the northern boundary
            jN = num_lat
        else:
            jN = tmp[0]
        for i in range(iW + 1, iE):
            for j in range(jS + 1, jN):
                # There is a chance that the regular gridpoint at (i,j)
                # lies within this element
                lon0 = lon_reg[i]
                lat0 = lat_reg[j]
                if in_triangle(elm, lon0, lat0):
                    # Get area of entire triangle
                    area = triangle_area(elm.lon, elm.lat)
                    # Get area of each sub-triangle formed by
                    # (lon0, lat0)
                    area0 = triangle_area([lon0, elm.lon[1], elm.lon[2]],
                                          [lat0, elm.lat[1], elm.lat[2]])
                    area1 = triangle_area([lon0, elm.lon[0], elm.lon[2]],
                                          [lat0, elm.lat[0], elm.lat[2]])
                    area2 = triangle_area([lon0, elm.lon[0], elm.lon[1]],
                                          [lat0, elm.lat[0], elm.lat[1]])
                    # Find fractional area of each
                    cff = [area0 / area, area1 / area, area2 / area]
                    # Find value of int_udz at each Node
                    # 1996-2005
                    vals = []
                    for n in range(3):
                        vals.append(int_udz_beg[elm.nodes[n].id])
                    # Barycentric interpolation to lon0, lat0
                    int_udz_reg_beg[j, i] = sum(array(cff) * array(vals))
                    # Loop over other experiments
                    for expt in range(num_expts):
                        vals = []
                        for n in range(3):
                            vals.append(int_udz_end[expt, elm.nodes[n].id])
                        int_udz_reg_end[expt, j,
                                        i] = sum(array(cff) * array(vals))

    # Indefinite integral from south to north of udz*dy, convert to Sv
    strf_beg = cumsum(int_udz_reg_beg * dy, axis=0) * 1e-6
    # Apply land mask: wherever interpolated field was identically zero
    strf_beg = ma.masked_where(int_udz_reg_beg == 0, strf_beg)
    # Calculate difference for each RCP experiment
    strf_diff = ma.empty(shape(int_udz_reg_end))
    for expt in range(num_expts):
        strf_end = cumsum(int_udz_reg_end[expt, :, :] * dy, axis=0) * 1e-6
        strf_end = ma.masked_where(int_udz_reg_beg == 0, strf_end)
        strf_diff[expt, :, :] = strf_end - strf_beg

    print 'Plotting'
    print '...1996-2005'
    bound = amax(abs(strf_beg))
    fig = figure(figsize=(10, 6))
    ax = fig.add_subplot(1, 1, 1)
    pcolor(lon_reg, lat_reg, strf_beg, vmin=-bound, vmax=bound, cmap='RdBu_r')
    xlabel('Longitude')
    ylabel('Latitude')
    xlim([lon_min, lon_max])
    ylim([lat_min, lat_max])
    colorbar()
    title('Barotropic streamfunction (Sv), 1996-2005', fontsize=20)
    fig.savefig('strf_beg.png')
    for expt in range(num_expts):
        print '...' + expt_names[expt]
        bound = amax(abs(strf_diff[expt, :, :]))
        fig = figure(figsize=(10, 6))
        ax = fig.add_subplot(1, 1, 1)
        pcolor(lon_reg,
               lat_reg,
               strf_diff[expt, :, :],
               vmin=-bound,
               vmax=bound,
               cmap='RdBu_r')
        xlabel('Longitude')
        ylabel('Latitude')
        xlim([lon_min, lon_max])
        ylim([lat_min, lat_max])
        colorbar()
        title('Barotropic streamfunction (Sv), 2091-2100 minus 1996-2005 (' +
              expt_names[expt] + ')',
              fontsize=20)
        fig.savefig('strf_diff_' + expt_filetails[expt] + '.png')
예제 #16
0
def zonal_slice_plot (mesh_path, file_path, var_name, tstep, lon0, depth_min, save=False, fig_name=None, set_limits=False, limits=None):

    # Set northern boundary and upper (surface) boundary
    lat_max = -50
    depth_max = 0
    # Font sizes for figure
    font_sizes = [30, 24, 20]

    # Read variable name and units for title
    id = Dataset(file_path, 'r')
    varid = id.variables[var_name]
    name = varid.getncattr('description')
    units = varid.getncattr('units')
    if lon0 < 0:
        lon_string = 'at ' + str(-lon0) + 'W'
    else:
        lon_string = 'at ' + str(lon0) + 'E'

    # Read data
    data = id.variables[var_name][tstep-1,:]
    # Check for vector variables that need to be unrotated
    if var_name in ['u', 'v']:
        # Read the rotated lat and lon
        fid = open(mesh_path + 'nod3d.out', 'r')
        fid.readline()
        lon = []
        lat = []
        for line in fid:
            tmp = line.split()
            lon_tmp = float(tmp[1])
            lat_tmp = float(tmp[2])
            if lon_tmp < -180:
                lon_tmp += 360
            elif lon_tmp > 180:
                lon_tmp -= 360
            lon.append(lon_tmp)
            lat.append(lat_tmp)
        fid.close()
        lon = array(lon)
        lat = array(lat)
        if var_name == 'u':
            u_data = data[:]
            v_data = id.variables['v'][tstep-1,:]
            u_data_lonlat, v_data_lonlat = unrotate_vector(lon, lat, u_data, v_data)
            data = u_data_lonlat[:]
        elif var_name == 'v':
            v_data = data[:]
            u_data = id.variables['u'][tstep-1,:]
            u_data_lonlat, v_data_lonlat = unrotate_vector(lon, lat, u_data, v_data)
            data = v_data_lonlat[:]
    id.close()    

    # Build the regular FESOM grid
    elm2D = fesom_grid(mesh_path)

    # Build the array of SideElements making up the zonal slice
    selements = fesom_sidegrid(elm2D, data, lon0, lat_max)

    # Build an array of quadrilateral patches for the plot, and of data values
    # corresponding to each SideElement
    # Also find the minimum latitude of any SideElement
    patches = []
    values = []
    lat_min = lat_max
    for selm in selements:
        # Make patch
        coord = transpose(vstack((selm.y,selm.z)))
        patches.append(Polygon(coord, True, linewidth=0.))
        # Save data value
        values.append(selm.var)
        # Update minimum latitude if needed
        lat_min = min(lat_min, amin(selm.y))
    # Set southern boundary to be just south of the minimum latitude
    lat_min = lat_min-1

    # Choose colour bounds
    if set_limits:
        # User-specified bounds
        var_min = limits[0]
        var_max = limits[1]
        if var_min == -var_max:
            # Bounds are centered on zero, so choose a blue-to-red colourmap
            # centered on yellow
            colour_map = 'RdYlBu_r'
        else:
            colour_map = 'jet'
    else:
        # Determine bounds automatically
        if var_name in ['u', 'v', 'w']:
            # Center levels on 0 for certain variables, with a blue-to-red
            # colourmap
            max_val = amax(abs(array(values)))
            var_min = -max_val
            var_max = max_val
            colour_map = 'RdYlBu_r'
        else:
            var_min = amin(array(values))
            var_max = amax(array(values))
            colour_map = 'jet'

    # Set up plot
    fig = figure(figsize=(16,8))
    ax = fig.add_subplot(1,1,1)
    # Set colourmap for patches, and refer it to the values array
    img = PatchCollection(patches, cmap=colour_map)
    img.set_array(array(values))
    img.set_edgecolor('face')
    # Add patches to plot
    ax.add_collection(img)

    # Configure plot
    xlim(lat_min, lat_max)
    ylim(depth_min, depth_max)
    title(name + ' (' + units + ') ' + lon_string, fontsize=font_sizes[0])
    xlabel('Latitude', fontsize=font_sizes[1])
    ylabel('Depth (m)', fontsize=font_sizes[1])
    setp(ax.get_xticklabels(), fontsize=font_sizes[2])
    setp(ax.get_yticklabels(), fontsize=font_sizes[2])
    cbar = colorbar(img)
    cbar.ax.tick_params(labelsize=font_sizes[2])
    img.set_clim(vmin=var_min, vmax=var_max)

    if save:
        fig.savefig(fig_name)
    else:
        fig.show()
예제 #17
0
def wind_stress_curl():

    # File paths
    mesh_path = '/short/y99/kaa561/FESOM/mesh/meshB/'
    directory_beg = '/short/y99/kaa561/FESOM/highres_spinup/'
    directories = [
        '/short/y99/kaa561/FESOM/rcp45_M/', '/short/y99/kaa561/FESOM/rcp45_A/',
        '/short/y99/kaa561/FESOM/rcp85_M/', '/short/y99/kaa561/FESOM/rcp85_A/'
    ]
    file_beg = 'annual_avg.forcing.diag.1996.2005.nc'
    file_end = 'annual_avg.forcing.diag.2091.2100.nc'
    # Titles for plotting
    expt_names = [
        'RCP 4.5 MMM', 'RCP 4.5 ACCESS', 'RCP 8.5 MMM', 'RCP 8.5 ACCESS'
    ]
    expt_filenames = ['rcp45_m', 'rcp45_a', 'rcp85_m', 'rcp85_a']
    num_expts = len(directories)
    colours = ['blue', 'cyan', 'green', 'magenta']
    # Bounds on regular grid
    lon_min = -180
    lon_max = 180
    lat_min = -75
    lat_max = -50
    # Number of points on regular grid
    num_lon = 1000
    num_lat = 200
    # Radius of the Earth in metres
    r = 6.371e6
    # Degrees to radians coversion factor
    deg2rad = pi / 180.0
    # Don't consider values above this threshold (small, negative)
    threshold = -5e-8

    print 'Building mesh'
    elements = fesom_grid(mesh_path, circumpolar=True, cross_180=True)
    # Read (rotated) lon and lat at each 2D node
    f = open(mesh_path + 'nod2d.out', 'r')
    n2d = int(f.readline())
    rlon = []
    rlat = []
    for line in f:
        tmp = line.split()
        lon_tmp = float(tmp[1])
        lat_tmp = float(tmp[2])
        if lon_tmp < -180:
            lon_tmp += 360
        elif lon_tmp > 180:
            lon_tmp -= 360
        rlon.append(lon_tmp)
        rlat.append(lat_tmp)
    f.close()
    rlon = array(rlon)
    rlat = array(rlat)

    print 'Reading data'
    print '...1996-2005'
    # Read rotated wind stress components
    id = Dataset(directory_beg + file_beg, 'r')
    stress_xr = id.variables['stress_x'][0, :]
    stress_yr = id.variables['stress_y'][0, :]
    id.close()
    # Unrotate
    stress_x_beg, stress_y_beg = unrotate_vector(rlon, rlat, stress_xr,
                                                 stress_yr)
    # Set up array for wind stress in each RCP experiment
    stress_x_end = zeros([num_expts, n2d])
    stress_y_end = zeros([num_expts, n2d])
    for expt in range(num_expts):
        print '...' + expt_names[expt]
        id = Dataset(directories[expt] + file_end, 'r')
        stress_xr = id.variables['stress_x'][0, :]
        stress_yr = id.variables['stress_y'][0, :]
        id.close()
        stress_x_tmp, stress_y_tmp = unrotate_vector(rlon, rlat, stress_xr,
                                                     stress_yr)
        stress_x_end[expt, :] = stress_x_tmp
        stress_y_end[expt, :] = stress_y_tmp

    print 'Interpolating to regular grid'
    # Set up regular grid
    # Start with boundaries
    lon_reg_edges = linspace(lon_min, lon_max, num_lon + 1)
    lat_reg_edges = linspace(lat_min, lat_max, num_lat + 1)
    # Now get centres
    lon_reg = 0.5 * (lon_reg_edges[:-1] + lon_reg_edges[1:])
    lat_reg = 0.5 * (lat_reg_edges[:-1] + lat_reg_edges[1:])
    # Also get differentials in lon-lat space
    dlon = lon_reg_edges[1:] - lon_reg_edges[:-1]
    dlat = lat_reg_edges[1:] - lat_reg_edges[:-1]
    # Make 2D versions
    lon_reg_2d, lat_reg_2d = meshgrid(lon_reg, lat_reg)
    dlon_2d, dlat_2d = meshgrid(dlon, dlat)
    # Calculate differentials in Cartesian space
    dx = r * cos(lat_reg_2d * deg2rad) * dlon_2d * deg2rad
    dy = r * dlat_2d * deg2rad
    # Set up arrays for result
    stress_x_reg_beg = zeros([num_lat, num_lon])
    stress_y_reg_beg = zeros([num_lat, num_lon])
    stress_x_reg_end = zeros([num_expts, num_lat, num_lon])
    stress_y_reg_end = zeros([num_expts, num_lat, num_lon])
    # For each element, check if a point on the regular lat-lon grid lies
    # within. If so, do barycentric interpolation to that point.
    for elm in elements:
        # Check if we are within domain of regular grid
        if amin(elm.lat) > lat_max:
            continue
        # Find largest regular longitude value west of Element
        tmp = nonzero(lon_reg > amin(elm.lon))[0]
        if len(tmp) == 0:
            # Element crosses the western boundary
            iW = 0
        else:
            iW = tmp[0] - 1
        # Find smallest regular longitude value east of Element
        tmp = nonzero(lon_reg > amax(elm.lon))[0]
        if len(tmp) == 0:
            # Element crosses the eastern boundary
            iE = num_lon
        else:
            iE = tmp[0]
        # Find largest regular latitude value south of Element
        tmp = nonzero(lat_reg > amin(elm.lat))[0]
        if len(tmp) == 0:
            # Element crosses the southern boundary
            jS = 0
        else:
            jS = tmp[0] - 1
        # Find smallest regular latitude value north of Element
        tmp = nonzero(lat_reg > amax(elm.lat))[0]
        if len(tmp) == 0:
            # Element crosses the northern boundary
            jN = num_lat
        else:
            jN = tmp[0]
        for i in range(iW + 1, iE):
            for j in range(jS + 1, jN):
                # There is a chance that the regular gridpoint at (i,j)
                # lies within this element
                lon0 = lon_reg[i]
                lat0 = lat_reg[j]
                if in_triangle(elm, lon0, lat0):
                    # Get area of entire triangle
                    area = triangle_area(elm.lon, elm.lat)
                    # Get area of each sub-triangle formed by
                    # (lon0, lat0)
                    area0 = triangle_area([lon0, elm.lon[1], elm.lon[2]],
                                          [lat0, elm.lat[1], elm.lat[2]])
                    area1 = triangle_area([lon0, elm.lon[0], elm.lon[2]],
                                          [lat0, elm.lat[0], elm.lat[2]])
                    area2 = triangle_area([lon0, elm.lon[0], elm.lon[1]],
                                          [lat0, elm.lat[0], elm.lat[1]])
                    # Find fractional area of each
                    cff = [area0 / area, area1 / area, area2 / area]
                    # 1996-2005
                    # Find value of stress_x and stress_y at each Node
                    vals_x = []
                    vals_y = []
                    for n in range(3):
                        vals_x.append(stress_x_beg[elm.nodes[n].id])
                        vals_y.append(stress_y_beg[elm.nodes[n].id])
                    # Barycentric interpolation to lon0, lat0
                    stress_x_reg_beg[j, i] = sum(array(cff) * array(vals_x))
                    stress_y_reg_beg[j, i] = sum(array(cff) * array(vals_y))
                    # RCPs
                    for expt in range(num_expts):
                        vals_x = []
                        vals_y = []
                        for n in range(3):
                            vals_x.append(stress_x_end[expt, elm.nodes[n].id])
                            vals_y.append(stress_y_end[expt, elm.nodes[n].id])
                        stress_x_reg_end[expt, j,
                                         i] = sum(array(cff) * array(vals_x))
                        stress_y_reg_end[expt, j,
                                         i] = sum(array(cff) * array(vals_y))

    print 'Calculating curl'
    # 1996-2005
    # First calculate the two derivatives
    dv_dx = zeros(shape(stress_x_reg_beg))
    du_dy = zeros(shape(stress_x_reg_beg))
    # Forward difference approximation
    dv_dx[:, :-1] = (stress_y_reg_beg[:, 1:] -
                     stress_y_reg_beg[:, :-1]) / dx[:, :-1]
    du_dy[:-1, :] = (stress_x_reg_beg[1:, :] -
                     stress_x_reg_beg[:-1, :]) / dy[:-1, :]
    # Backward difference for the last row
    dv_dx[:,
          -1] = (stress_y_reg_beg[:, -1] - stress_y_reg_beg[:, -2]) / dx[:, -1]
    du_dy[-1, :] = (stress_x_reg_beg[-1, :] -
                    stress_x_reg_beg[-2, :]) / dy[-1, :]
    curl_beg = dv_dx - du_dy
    # RCPs
    curl_end_tmp = zeros(shape(stress_x_reg_end))
    for expt in range(num_expts):
        dv_dx = zeros(shape(stress_x_reg_beg))
        du_dy = zeros(shape(stress_x_reg_beg))
        dv_dx[:, :-1] = (stress_y_reg_end[expt, :, 1:] -
                         stress_y_reg_end[expt, :, :-1]) / dx[:, :-1]
        du_dy[:-1, :] = (stress_x_reg_end[expt, 1:, :] -
                         stress_x_reg_end[expt, :-1, :]) / dy[:-1, :]
        dv_dx[:, -1] = (stress_y_reg_end[expt, :, -1] -
                        stress_y_reg_end[expt, :, -2]) / dx[:, -1]
        du_dy[-1, :] = (stress_x_reg_end[expt, -1, :] -
                        stress_x_reg_end[expt, -2, :]) / dy[-1, :]
        curl_end_tmp[expt, :, :] = dv_dx - du_dy

    print 'Plotting zonal averages'
    # Calculate zonal averages
    curl_beg_avg = mean(curl_beg, axis=1)
    curl_end_avg = mean(curl_end_tmp, axis=2)
    # Plot zonal averages
    fig, ax = subplots(figsize=(10, 6))
    ax.plot(curl_beg_avg,
            lat_reg,
            label='1996-2005',
            color='black',
            linewidth=2)
    for expt in range(num_expts):
        ax.plot(curl_end_avg[expt, :],
                lat_reg,
                label=expt_names[expt],
                color=colours[expt],
                linewidth=2)
    title('Curl of wind stress (2091-2100)', fontsize=18)
    xlabel(r'N/m$^3$', fontsize=14)
    ylabel('latitude', fontsize=14)
    ylim([lat_min, lat_max])
    grid(True)
    # Move plot over to make room for legend
    box = ax.get_position()
    ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
    # Make legend
    ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
    fig.show()
    fig.savefig('windstress_curl_rcp.png')
    # Plot anomalies in zonal averages
    fig, ax = subplots(figsize=(10, 6))
    for expt in range(num_expts):
        ax.plot(curl_end_avg[expt, :] - curl_beg_avg,
                lat_reg,
                label=expt_names[expt],
                color=colours[expt],
                linewidth=2)
    title('Anomalies in curl of wind stress (2091-2100 minus 1996-2005)',
          fontsize=18)
    xlabel(r'N/m$^3$', fontsize=14)
    ylabel('latitude', fontsize=14)
    ylim([lat_min, lat_max])
    grid(True)
    # Move plot over to make room for legend
    box = ax.get_position()
    ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
    # Make legend
    ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
    fig.show()
    fig.savefig('windstress_curl_diff_rcp.png')
    # Plot percent change in zonal averages
    fig, ax = subplots(figsize=(10, 6))
    for expt in range(num_expts):
        ax.plot((curl_end_avg[expt, :] - curl_beg_avg) / curl_beg_avg * 100,
                lat_reg,
                label=expt_names[expt],
                color=colours[expt],
                linewidth=2)
    title('Percent change in curl of wind stress (2091-2100 minus 1996-2005)',
          fontsize=18)
    xlabel('%', fontsize=14)
    ylabel('latitude', fontsize=14)
    xlim([-20, 20])
    ylim([-65.5, -58])
    grid(True)
    # Move plot over to make room for legend
    box = ax.get_position()
    ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
    # Make legend
    ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
    fig.show()
    fig.savefig('windstress_curl_percent_rcp.png')

    print 'Plotting 2D fields'
    # First mask out regions above threshold at beginning
    curl_beg = ma.masked_where(curl_beg > threshold, curl_beg)
    curl_end = ma.empty(shape(curl_end_tmp))
    for expt in range(num_expts):
        curl_end[expt, :, :] = ma.masked_where(curl_beg > threshold,
                                               curl_end_tmp[expt, :, :])
    # Calculate percent change for each RCP
    percent_change = ma.empty(shape(curl_end))
    for expt in range(num_expts):
        percent_change[expt, :, :] = (curl_end[expt, :, :] -
                                      curl_beg) / curl_beg * 100
    # 1996-2005
    fig, ax = subplots(figsize=(10, 6))
    bound = 1e-6
    lev = linspace(-bound, bound, num=50)
    img = ax.contourf(lon_reg, lat_reg, curl_beg, lev, cmap='RdBu_r')
    xlabel('Longitude')
    ylabel('Latitude')
    xlim([lon_min, lon_max])
    ylim([lat_min, lat_max])
    title('Wind stress curl, 1996-2005 (N/m^3)', fontsize=18)
    colorbar(img)
    fig.show()
    fig.savefig('windstress_curl_2D_beg.png')
    for expt in range(num_expts):
        bound = 50
        lev = linspace(-bound, bound, num=50)
        fig, ax = subplots(figsize=(10, 6))
        img = ax.contourf(lon_reg,
                          lat_reg,
                          percent_change[expt, :, :],
                          lev,
                          cmap='RdBu_r')
        xlabel('Longitude')
        ylabel('Latitude')
        xlim([lon_min, lon_max])
        ylim([lat_min, lat_max])
        title(
            'Percent change in wind stress curl, 2091-2100 versus 1996-2005 ('
            + expt_names[expt] + ')',
            fontsize=18)
        colorbar(img)
        fig.show()
        fig.savefig('windstress_curl_2D_percent_' + expt_filenames[expt] +
                    '.png')
예제 #18
0
def sws_plots ():

    # File paths
    mesh_path = '/short/y99/kaa561/FESOM/mesh/meshB/'
    oce_file_beg = '/short/y99/kaa561/FESOM/highres_spinup/annual_avg.oce.mean.1996.2005.nc'
    oce_file_end = '/short/y99/kaa561/FESOM/rcp85_A/annual_avg.oce.mean.2091.2100.nc'
    ice_file_beg = '/short/y99/kaa561/FESOM/highres_spinup/seasonal_climatology_ice_diag_1996_2005.nc'
    ice_file_end = '/short/y99/kaa561/FESOM/rcp85_A/seasonal_climatology_ice_diag_2091_2100.nc'
    # Bounds on plot (in polar coordinate transformation)
    x_min = -22
    x_max = -4
    y_min = 1
    y_max = 15
    # Number of bins for velocity vectors
    num_bins_x = 20
    num_bins_y = 20
    # Plotting parameters
    circumpolar = True
    # Season names for plot titles
    season_names = ['DJF', 'MAM', 'JJA', 'SON']
    # Degrees to radians conversion factor
    deg2rad = pi/180.0

    print 'Building mesh'
    elements = fesom_grid(mesh_path, circumpolar)
    # Build one set of plotting patches with all elements, and one with
    # ice shelf cavities masked
    patches_all = []
    patches_ocn = []
    for elm in elements:
        coord = transpose(vstack((elm.x, elm.y)))
        patches_all.append(Polygon(coord, True, linewidth=0.))
        if not elm.cavity:
            patches_ocn.append(Polygon(coord, True, linewidth=0.))
    num_elm = len(patches_all)
    num_elm_ocn = len(patches_ocn)
    # Build ice shelf front contours
    contour_lines = []
    for elm in elements:
        # Select elements where exactly 2 of the 3 nodes are in a cavity
        if count_nonzero(elm.cavity_nodes) == 2:
            # Save the coastal flags and x- and y- coordinates of these 2
            coast_tmp = []
            x_tmp = []
            y_tmp = []
            for i in range(3):
                if elm.cavity_nodes[i]:
                    coast_tmp.append(elm.coast_nodes[i])
                    x_tmp.append(elm.x[i])
                    y_tmp.append(elm.y[i])
            # Select elements where at most 1 of these 2 nodes are coastal
            if count_nonzero(coast_tmp) < 2:
                # Draw a line between the 2 nodes
                contour_lines.append([(x_tmp[0], y_tmp[0]), (x_tmp[1], y_tmp[1])])

    print 'Processing bathymetry'
    # Calculate bathymetry (depth of bottom node) averaged over 3 nodes making
    # up each element
    bathy = []
    for elm in elements:
        bathy.append(mean([elm.nodes[0].find_bottom().depth, elm.nodes[1].find_bottom().depth, elm.nodes[2].find_bottom().depth]))
    # Plot
    fig = figure(figsize=(12,8))
    ax = fig.add_subplot(1,1,1, aspect='equal')
    img = PatchCollection(patches_all, cmap='jet')
    img.set_array(array(bathy))
    img.set_edgecolor('face')
    img.set_clim(vmin=0, vmax=1500)
    ax.add_collection(img)
    contours = LineCollection(contour_lines, edgecolor='black', linewidth=1)
    ax.add_collection(contours)
    xlim([x_min, x_max])
    ylim([y_min, y_max])
    ax.set_xticks([])
    ax.set_yticks([])
    title('Bathymetry (m)', fontsize=24)
    cbar = colorbar(img, extend='max')
    fig.show()
    fig.savefig('sws_bathy.png')

    print 'Processing bottom water temperature'
    # Read annually averaged data
    id = Dataset(oce_file_beg, 'r')
    temp_nodes_beg = id.variables['temp'][0,:]
    id.close()
    id = Dataset(oce_file_end, 'r')
    temp_nodes_end = id.variables['temp'][0,:]
    id.close()
    # Now average bottom node temperatures over each element
    bwtemp_beg = []
    bwtemp_end = []
    for elm in elements:
        bwtemp_beg.append(mean([temp_nodes_beg[elm.nodes[0].find_bottom().id], temp_nodes_beg[elm.nodes[1].find_bottom().id], temp_nodes_beg[elm.nodes[2].find_bottom().id]]))
        bwtemp_end.append(mean([temp_nodes_end[elm.nodes[0].find_bottom().id], temp_nodes_end[elm.nodes[1].find_bottom().id], temp_nodes_end[elm.nodes[2].find_bottom().id]]))
    # Plot
    fig = figure(figsize=(16,7))
    # 1996-2005
    ax = fig.add_subplot(1, 2, 1, aspect='equal')
    img = PatchCollection(patches_all, cmap='jet')
    img.set_array(array(bwtemp_beg))
    img.set_edgecolor('face')
    img.set_clim(vmin=-2, vmax=-0.5)
    ax.add_collection(img)
    contours = LineCollection(contour_lines, edgecolor='black', linewidth=1)
    ax.add_collection(contours)
    xlim([x_min, x_max])
    ylim([y_min, y_max])
    ax.set_xticks([])
    ax.set_yticks([])
    title('1996-2005', fontsize=20)
    # 2091-2100
    ax = fig.add_subplot(1, 2, 2, aspect='equal')
    img = PatchCollection(patches_all, cmap='jet')
    img.set_array(array(bwtemp_end))
    img.set_edgecolor('face')
    img.set_clim(vmin=-2, vmax=-0.5)
    ax.add_collection(img)
    contours = LineCollection(contour_lines, edgecolor='black', linewidth=1)
    ax.add_collection(contours)
    xlim([x_min, x_max])
    ylim([y_min, y_max])
    ax.set_xticks([])
    ax.set_yticks([])
    title('2091-2100', fontsize=20)
    cbaxes = fig.add_axes([0.35, 0.04, 0.3, 0.02])
    cbar = colorbar(img, orientation='horizontal', cax=cbaxes, extend='both')
    suptitle(r'Bottom water temperature ($^{\circ}$C)', fontsize=24)
    subplots_adjust(wspace=0.025, hspace=0.025)
    fig.show()
    fig.savefig('sws_bwtemp.png')

    print 'Processing bottom water salinity'
    # Read annually averaged data
    id = Dataset(oce_file_beg, 'r')
    salt_nodes_beg = id.variables['salt'][0,:]
    id.close()
    id = Dataset(oce_file_end, 'r')
    salt_nodes_end = id.variables['salt'][0,:]
    id.close()
    # Now average bottom node salinities over each element
    bwsalt_beg = []
    bwsalt_end = []
    for elm in elements:
        bwsalt_beg.append(mean([salt_nodes_beg[elm.nodes[0].find_bottom().id], salt_nodes_beg[elm.nodes[1].find_bottom().id], salt_nodes_beg[elm.nodes[2].find_bottom().id]]))
        bwsalt_end.append(mean([salt_nodes_end[elm.nodes[0].find_bottom().id], salt_nodes_end[elm.nodes[1].find_bottom().id], salt_nodes_end[elm.nodes[2].find_bottom().id]]))
    # Plot beginning 
    fig = figure(figsize=(16,7))
    # 1996-2005
    ax = fig.add_subplot(1, 2, 1, aspect='equal')
    img = PatchCollection(patches_all, cmap='jet')
    img.set_array(array(bwsalt_beg))
    img.set_edgecolor('face')
    img.set_clim(vmin=34.2, vmax=34.7)
    ax.add_collection(img)
    contours = LineCollection(contour_lines, edgecolor='black', linewidth=1)
    ax.add_collection(contours)
    xlim([x_min, x_max])
    ylim([y_min, y_max])
    ax.set_xticks([])
    ax.set_yticks([])
    title('1996-2005', fontsize=20)
    # 2091-2100
    ax = fig.add_subplot(1, 2, 2, aspect='equal')
    img = PatchCollection(patches_all, cmap='jet')
    img.set_array(array(bwsalt_end))
    img.set_edgecolor('face')
    img.set_clim(vmin=34.2, vmax=34.7)
    ax.add_collection(img)
    contours = LineCollection(contour_lines, edgecolor='black', linewidth=1)
    ax.add_collection(contours)
    xlim([x_min, x_max])
    ylim([y_min, y_max])
    ax.set_xticks([])
    ax.set_yticks([])
    title('2091-2100', fontsize=20)
    cbaxes = fig.add_axes([0.35, 0.04, 0.3, 0.02])
    cbar = colorbar(img, orientation='horizontal', cax=cbaxes, extend='both')
    suptitle('Bottom water salinity (psu)', fontsize=24)
    subplots_adjust(wspace=0.025, hspace=0.025)
    fig.show()
    fig.savefig('sws_bwsalt.png')

    print 'Processing sea ice formation'
    # Read seasonally averaged data
    id = Dataset(ice_file_beg, 'r')
    thdgr_nodes_beg = id.variables['thdgr'][:,:]*1e7
    id.close()
    id = Dataset(ice_file_end, 'r')
    thdgr_nodes_end = id.variables['thdgr'][:,:]*1e7
    id.close()
    thdgr_nodes_diff = thdgr_nodes_end - thdgr_nodes_beg
    # Now average over each element
    thdgr_beg = empty([4, num_elm_ocn])
    thdgr_end = empty([4, num_elm_ocn])
    thdgr_diff = empty([4, num_elm_ocn])
    i = 0
    for elm in elements:
        if not elm.cavity:
            thdgr_beg[:,i] = (thdgr_nodes_beg[:,elm.nodes[0].id] + thdgr_nodes_beg[:,elm.nodes[1].id] + thdgr_nodes_beg[:,elm.nodes[2].id])/3.0
            thdgr_end[:,i] = (thdgr_nodes_end[:,elm.nodes[0].id] + thdgr_nodes_end[:,elm.nodes[1].id] + thdgr_nodes_end[:,elm.nodes[2].id])/3.0
            thdgr_diff[:,i] = (thdgr_nodes_diff[:,elm.nodes[0].id] + thdgr_nodes_diff[:,elm.nodes[1].id] + thdgr_nodes_diff[:,elm.nodes[2].id])/3.0
            i += 1
    # Plot beginning and end
    fig = figure(figsize=(19,9))
    for season in range(4):
        # 1996-2005
        ax = fig.add_subplot(2, 4, season+1, aspect='equal')
        img = PatchCollection(patches_ocn, cmap='RdBu_r')
        img.set_array(thdgr_beg[season,:])
        img.set_edgecolor('face')
        img.set_clim(vmin=-2, vmax=2)
        ax.add_collection(img)
        xlim([x_min, x_max])
        ylim([y_min, y_max])
        ax.set_xticks([])
        ax.set_yticks([])
        title(season_names[season], fontsize=24)
        if season == 0:
            text(-22, 10, '1996-2005', fontsize=20, ha='right', rotation=90)
        # 2091-2100
        ax = fig.add_subplot(2, 4, season+5, aspect='equal')
        img = PatchCollection(patches_ocn, cmap='RdBu_r')
        img.set_array(thdgr_end[season,:])
        img.set_edgecolor('face')
        img.set_clim(vmin=-2, vmax=2)
        ax.add_collection(img)
        xlim([x_min, x_max])
        ylim([y_min, y_max])
        ax.set_xticks([])
        ax.set_yticks([])
        if season == 0:
            text(-22, 10, '2091-2100', fontsize=20, ha='right', rotation=90)
        if season == 3:
            cbaxes = fig.add_axes([0.35, 0.04, 0.3, 0.02])
            cbar = colorbar(img, orientation='horizontal', cax=cbaxes, extend='both')
    suptitle(r'Sea ice thermodynamic growth rate (10$^{-7}$ m/s)', fontsize=24)
    subplots_adjust(wspace=0.025, hspace=0.025)
    fig.show()
    fig.savefig('sws_thdgr.png')
    # Plot difference
    fig = figure(figsize=(19,6))
    for season in range(4):
        ax = fig.add_subplot(1, 4, season+1, aspect='equal')
        img = PatchCollection(patches_ocn, cmap='RdBu_r')
        img.set_array(thdgr_diff[season,:])
        img.set_edgecolor('face')
        img.set_clim(vmin=-1, vmax=1)
        ax.add_collection(img)
        xlim([x_min, x_max])
        ylim([y_min, y_max])
        ax.set_xticks([])
        ax.set_yticks([])
        title(season_names[season], fontsize=24)
        if season == 3:
            cbaxes = fig.add_axes([0.35, 0.04, 0.3, 0.02])
            cbar = colorbar(img, orientation='horizontal', cax=cbaxes, extend='both')
    suptitle(r'Sea ice thermodynamic growth rate (10$^{-7}$ m/s), 2091-2100 minus 1996-2005', fontsize=24)
    subplots_adjust(wspace=0.025)
    fig.show()
    fig.savefig('sws_thdgr_diff.png')       

    print 'Processing vertically averaged velocity'
    # Need to read some more of the grid    
    # Read number of 2D nodes
    f  = open(mesh_path + 'nod2d.out', 'r')
    n2d = int(f.readline())
    f.close()
    # Read rotated lat and lon for each 3D node, also depth
    f = open(mesh_path + 'nod3d.out', 'r')
    f.readline()
    rlon = []
    rlat = []
    node_depth = []
    for line in f:
        tmp = line.split()
        lon_tmp = float(tmp[1])
        lat_tmp = float(tmp[2])
        node_depth_tmp = -1*float(tmp[3])
        if lon_tmp < -180:
            lon_tmp += 360
        elif lon_tmp > 180:
            lon_tmp -= 360
        rlon.append(lon_tmp)
        rlat.append(lat_tmp)
        node_depth.append(node_depth_tmp)
    f.close()
    # For lat and lon, only care about the 2D nodes (the first n2d indices)
    rlon = array(rlon[0:n2d])
    rlat = array(rlat[0:n2d])
    node_depth = array(node_depth)
    # Unrotate lat and lon
    lon, lat = unrotate_grid(rlon, rlat)
    # Calculate polar coordinates of each node
    x = -(lat+90)*cos(lon*deg2rad+pi/2)
    y = (lat+90)*sin(lon*deg2rad+pi/2)
    # Read lists of which nodes are directly below which
    f = open(mesh_path + 'aux3d.out', 'r')
    max_num_layers = int(f.readline())
    node_columns = zeros([n2d, max_num_layers])
    for n in range(n2d):
        for k in range(max_num_layers):
            node_columns[n,k] = int(f.readline())
    node_columns = node_columns.astype(int)
    f.close()
    # Now read the data (full 3D field for both u and v)
    id = Dataset(oce_file_beg, 'r')
    node_ur_3d_beg = id.variables['u'][0,:]
    node_vr_3d_beg = id.variables['v'][0,:]
    id.close()
    id = Dataset(oce_file_end, 'r')
    node_ur_3d_end = id.variables['u'][0,:]
    node_vr_3d_end = id.variables['v'][0,:]
    id.close()
    # Vertically average
    node_ur_beg = zeros(n2d)
    node_vr_beg = zeros(n2d)
    node_ur_end = zeros(n2d)
    node_vr_end = zeros(n2d)
    for n in range(n2d):
        # Integrate udz, vdz, and dz over this water column
        udz_col_beg = 0
        vdz_col_beg = 0
        udz_col_end = 0
        vdz_col_end = 0
        dz_col = 0
        for k in range(max_num_layers-1):
            if node_columns[n,k+1] == -999:
                # Reached the bottom
                break
            # Trapezoidal rule
            top_id = node_columns[n,k]
            bot_id = node_columns[n,k+1]
            dz_tmp = node_depth[bot_id-1] - node_depth[top_id-1]
            udz_col_beg += 0.5*(node_ur_3d_beg[top_id-1]+node_ur_3d_beg[bot_id-1])*dz_tmp
            vdz_col_beg += 0.5*(node_vr_3d_beg[top_id-1]+node_vr_3d_beg[bot_id-1])*dz_tmp
            udz_col_end += 0.5*(node_ur_3d_end[top_id-1]+node_ur_3d_end[bot_id-1])*dz_tmp
            vdz_col_end += 0.5*(node_vr_3d_end[top_id-1]+node_vr_3d_end[bot_id-1])*dz_tmp
            dz_col += dz_tmp
        # Convert from integrals to averages
        node_ur_beg[n] = udz_col_beg/dz_col
        node_vr_beg[n] = vdz_col_beg/dz_col
        node_ur_end[n] = udz_col_end/dz_col
        node_vr_end[n] = vdz_col_end/dz_col
    # Unrotate
    node_u_beg, node_v_beg = unrotate_vector(rlon, rlat, node_ur_beg, node_vr_beg)
    node_u_end, node_v_end = unrotate_vector(rlon, rlat, node_ur_end, node_vr_end)
    # Calculate speed
    node_speed_beg = sqrt(node_u_beg**2 + node_v_beg**2)
    node_speed_end = sqrt(node_u_end**2 + node_v_end**2)
    # Now calculate speed at each element, averaged over 3 corners
    speed_beg = []
    speed_end = []
    for elm in elements:
        speed_beg.append(mean([node_speed_beg[elm.nodes[0].id], node_speed_beg[elm.nodes[1].id], node_speed_beg[elm.nodes[2].id]]))
        speed_end.append(mean([node_speed_end[elm.nodes[0].id], node_speed_end[elm.nodes[1].id], node_speed_end[elm.nodes[2].id]]))
    # Set up bins for vectors
    x_bins = linspace(x_min, x_max, num=num_bins_x+1)
    y_bins = linspace(y_min, y_max, num=num_bins_y+1)
    # Calculate centres of bins (for plotting)
    x_centres = 0.5*(x_bins[:-1] + x_bins[1:])
    y_centres = 0.5*(y_bins[:-1] + y_bins[1:])
    # Set up arrays to integrate velocity in each bin
    # Simple averaging of all the points inside each bin
    ubin_beg = zeros([size(y_centres), size(x_centres)])
    vbin_beg = zeros([size(y_centres), size(x_centres)])
    ubin_end = zeros([size(y_centres), size(x_centres)])
    vbin_end = zeros([size(y_centres), size(x_centres)])
    num_pts = zeros([size(y_centres), size(x_centres)])
    # First convert to polar coordinates, rotate to account for
    # longitude in circumpolar projection, and convert back to vector
    # components
    theta_beg = arctan2(node_v_beg, node_u_beg)
    theta_circ_beg = theta_beg - lon*deg2rad
    u_circ_beg = node_speed_beg*cos(theta_circ_beg)
    v_circ_beg = node_speed_beg*sin(theta_circ_beg)
    theta_end = arctan2(node_v_end, node_u_end)
    theta_circ_end = theta_end - lon*deg2rad
    u_circ_end = node_speed_end*cos(theta_circ_end)
    v_circ_end = node_speed_end*sin(theta_circ_end)
    # Loop over 2D nodes
    for n in range(n2d):
        if x[n] > x_min and x[n] < x_max and y[n] > y_min and y[n] < y_max:
            # Figure out which bins this falls into
            x_index = nonzero(x_bins > x[n])[0][0]-1
            y_index = nonzero(y_bins > y[n])[0][0]-1
            # Integrate
            ubin_beg[y_index, x_index] += u_circ_beg[n]
            vbin_beg[y_index, x_index] += v_circ_beg[n]
            ubin_end[y_index, x_index] += u_circ_end[n]
            vbin_end[y_index, x_index] += v_circ_end[n]
            num_pts[y_index, x_index] += 1
    # Convert from sums to averages
    # First mask out points with no data
    ubin_beg = ma.masked_where(num_pts==0, ubin_beg)
    vbin_beg = ma.masked_where(num_pts==0, vbin_beg)
    ubin_end = ma.masked_where(num_pts==0, ubin_end)
    vbin_end = ma.masked_where(num_pts==0, vbin_end)
    # Divide everything else by number of points
    flag = num_pts > 0
    ubin_beg[flag] = ubin_beg[flag]/num_pts[flag]
    vbin_beg[flag] = vbin_beg[flag]/num_pts[flag]
    ubin_end[flag] = ubin_end[flag]/num_pts[flag]
    vbin_end[flag] = vbin_end[flag]/num_pts[flag]
    # Plot
    fig = figure(figsize=(16,7))
    # 1996-2005
    ax = fig.add_subplot(1, 2, 1, aspect='equal')
    img = PatchCollection(patches_all, cmap='cool')
    img.set_array(array(speed_beg))
    img.set_edgecolor('face')
    img.set_clim(vmin=0, vmax=0.25)
    ax.add_collection(img)
    contours = LineCollection(contour_lines, edgecolor='black', linewidth=1)
    ax.add_collection(contours)
    quiver(x_centres, y_centres, ubin_beg, vbin_beg, scale=0.9, headwidth=8, headlength=9, color='black')
    xlim([x_min, x_max])
    ylim([y_min, y_max])
    ax.set_xticks([])
    ax.set_yticks([])
    title('1996-2005', fontsize=20)
    # 2091-2100
    ax = fig.add_subplot(1, 2, 2, aspect='equal')
    img = PatchCollection(patches_all, cmap='cool')
    img.set_array(array(speed_end))
    img.set_edgecolor('face')
    img.set_clim(vmin=0, vmax=0.25)
    ax.add_collection(img)
    contours = LineCollection(contour_lines, edgecolor='black', linewidth=1)
    ax.add_collection(contours)
    quiver(x_centres, y_centres, ubin_end, vbin_end, scale=0.9, headwidth=8, headlength=9, color='black')
    xlim([x_min, x_max])
    ylim([y_min, y_max])
    ax.set_xticks([])
    ax.set_yticks([])
    title('2091-2100', fontsize=20)
    cbaxes = fig.add_axes([0.35, 0.04, 0.3, 0.02])
    cbar = colorbar(img, orientation='horizontal', cax=cbaxes, extend='max')
    suptitle('Vertically averaged velocity (m/s)', fontsize=24)
    subplots_adjust(wspace=0.025, hspace=0.025)
    fig.show()
    fig.savefig('sws_velavg.png')
예제 #19
0
def lonlat_plot(mesh_path,
                file_path,
                var_name,
                depth_key,
                depth,
                depth_bounds,
                tstep,
                circumpolar,
                elements,
                patches,
                mask_cavities=False,
                save=False,
                fig_name=None,
                set_limits=False,
                limits=None):

    # Set bounds for domain
    if circumpolar:
        # Northern boundary 30S
        lat_max = -30 + 90
    else:
        lon_min = -180
        lon_max = 180
        lat_min = -90
        lat_max = 90
        # Configure position of latitude and longitude labels
        lon_ticks = arange(-120, 120 + 1, 60)
        lat_ticks = arange(-60, 60 + 1, 30)
    # Set font sizes
    font_sizes = [30, 24, 20]
    # Seconds per year, for conversion of ice shelf melt rate
    sec_per_year = 365.25 * 24 * 3600

    # Read data
    file = Dataset(file_path, 'r')
    varid = file.variables[var_name]
    data = varid[tstep - 1, :]
    # Check for vector variables that need to be unrotated
    if var_name in [
            'uwind', 'vwind', 'stress_x', 'stress_y', 'uhice', 'vhice',
            'uhsnow', 'vhsnow', 'uice', 'vice', 'u', 'v'
    ]:
        # Read the rotated lat and lon
        if var_name in ['u', 'v']:
            # 3D variable
            mesh_file = mesh_path + 'nod3d.out'
        else:
            # 2D variable
            mesh_file = mesh_path + 'nod2d.out'
        fid = open(mesh_file, 'r')
        fid.readline()
        lon = []
        lat = []
        for line in fid:
            tmp = line.split()
            lon_tmp = float(tmp[1])
            lat_tmp = float(tmp[2])
            if lon_tmp < -180:
                lon_tmp += 360
            elif lon_tmp > 180:
                lon_tmp -= 360
            lon.append(lon_tmp)
            lat.append(lat_tmp)
        fid.close()
        lon = array(lon)
        lat = array(lat)
        if var_name in ['uwind', 'stress_x', 'uhice', 'uhsnow', 'uice', 'u']:
            # u-variable
            u_data = data[:]
            if var_name == 'stress_x':
                v_data = file.variables['stress_y'][tstep - 1, :]
            else:
                v_data = file.variables[var_name.replace('u',
                                                         'v')][tstep - 1, :]
            u_data_lonlat, v_data_lonlat = unrotate_vector(
                lon, lat, u_data, v_data)
            data = u_data_lonlat[:]
        elif var_name in ['vwind', 'stress_y', 'vhice', 'vhsnow', 'vice', 'v']:
            # v-variable
            v_data = data[:]
            if var_name == 'stress_y':
                u_data = file.variables['stress_x'][tstep - 1, :]
            else:
                u_data = file.variables[var_name.replace('v',
                                                         'u')][tstep - 1, :]
            u_data_lonlat, v_data_lonlat = unrotate_vector(
                lon, lat, u_data, v_data)
            data = v_data_lonlat[:]

    # Set descriptive variable name and units for title
    if var_name == 'area':
        name = 'ice concentration'
        units = 'fraction'
    elif var_name == 'wnet':
        # Convert from m/s to m/y
        data = data * sec_per_year
        name = 'ice shelf melt rate'
        units = 'm/y'
    else:
        name = varid.getncattr('description')
        units = varid.getncattr('units')
    if depth_key == 0:
        if var_name in ['temp', 'salt', 'u', 'v']:
            depth_string = 'at surface'
        else:
            depth_string = ''
    elif depth_key == 1:
        depth_string = 'at bottom'
    elif depth_key == 2:
        depth_string = 'vertically averaged'
    elif depth_key == 3:
        depth_string = 'at ' + str(depth) + ' m'
    elif depth_key == 4:
        depth_string = 'vertically averaged between ' + str(
            depth_bounds[0]) + ' and ' + str(depth_bounds[1]) + ' m'

    # Build an array of data values corresponding to each Element
    values = []
    plot_patches = []
    for elm in elements:
        # If mask_cavities is true, only include elements which are not inside
        # an ice shelf cavity; otherwise, include all elements
        if (mask_cavities and not elm.cavity) or (not mask_cavities):

            if depth_key == 0:
                # Surface nodes; this is easy
                # Average the data value for each of the three component nodes
                values.append(
                    mean([
                        data[elm.nodes[0].id], data[elm.nodes[1].id],
                        data[elm.nodes[2].id]
                    ]))

            elif depth_key == 1:
                # Bottom nodes
                values_tmp = []
                # For each of the three component nodes, find the id of the
                # bottom node beneath it
                for node in elm.nodes:
                    id = node.find_bottom().id
                    values_tmp.append(data[id])
                # Average over these three values
                values.append(mean(values_tmp))

            elif depth_key == 2:
                # Vertical average throughout entire water column
                # First calculate volume: area of triangular face * water
                # column thickness (mean of three corners)
                wct = []
                for node in elm.nodes:
                    wct.append(abs(node.find_bottom().depth - node.depth))
                area = elm.area()
                volume = area * mean(array(wct))
                # Now integrate down
                integral = 0
                nodes = [elm.nodes[0], elm.nodes[1], elm.nodes[2]]
                while True:
                    if nodes[0].below is None or nodes[
                            1].below is None or nodes[2].below is None:
                        break
                    # Calculate mean of data at six corners of this triangular
                    # prism, and mean depths at three edges
                    values_tmp = []
                    dz_tmp = []
                    # Must loop over indices of nodes array, not entries,
                    # because we want to change the contents of the nodes array
                    for i in range(3):
                        values_tmp.append(data[nodes[i].id])
                        values_tmp.append(data[nodes[i].below.id])
                        dz_tmp.append(
                            abs(nodes[i].depth - nodes[i].below.depth))
                        # Get ready for next iteration of the while loop
                        nodes[i] = nodes[i].below
                    # Integrand is mean of data at corners * area of upper face
                    # * mean of depths at edges
                    integral += mean(array(values_tmp)) * area * mean(
                        array(dz_tmp))
                # All done; divide integral by volume to get the average
                values.append(integral / volume)

            elif depth_key == 3:
                # Specified depth
                values_tmp = []
                # For each of the three component nodes, linearly interpolate
                # to the correct depth
                for i in range(3):
                    # Find the ids of the nodes above and below this depth,
                    # and the coefficients for the linear interpolation
                    id1, id2, coeff1, coeff2 = elm.nodes[i].find_depth(depth)
                    if any(isnan(array([id1, id2, coeff1, coeff2]))):
                        # No such node at this depth
                        values_tmp.append(NaN)
                    else:
                        values_tmp.append(coeff1 * data[id1] +
                                          coeff2 * data[id2])
                if any(isnan(array(values_tmp))):
                    pass
                else:
                    values.append(mean(values_tmp))
                    coord = transpose(vstack((elm.x, elm.y)))
                    # Make new patches for elements which exist at this depth
                    plot_patches.append(Polygon(coord, True, linewidth=0.))

            elif depth_key == 4:
                # Vertical average between two specified depths
                shallow_bound = depth_bounds[0]
                deep_bound = depth_bounds[1]
                area = elm.area()
                # Volume is area of triangular face * difference between
                # depth bounds
                volume = abs(deep_bound - shallow_bound) * area
                nodes = [elm.nodes[0], elm.nodes[1], elm.nodes[2]]
                # Check if we're already too deep
                if nodes[0].depth > deep_bound or nodes[
                        1].depth > deep_bound or nodes[2].depth > deep_bound:
                    pass
                # Check if the seafloor is shallower than shallow_bound
                elif nodes[0].find_bottom().depth <= shallow_bound or nodes[
                        1].find_bottom().depth <= shallow_bound or nodes[
                            2].find_bottom().depth <= shallow_bound:
                    pass

                else:
                    # Here we go

                    # Find the first 3D element which is entirely below
                    # shallow_bound
                    while True:
                        if nodes[0].depth > shallow_bound and nodes[
                                1].depth > shallow_bound and nodes[
                                    2].depth > shallow_bound:
                            # Save these nodes
                            first_nodes = []
                            for i in range(3):
                                first_nodes.append(nodes[i])
                            break
                        else:
                            for i in range(3):
                                nodes[i] = nodes[i].below

                    # Integrate downward until one of the next nodes hits the
                    # seafloor, or is deeper than deep_bound
                    integral = 0
                    while True:
                        if nodes[0].below is None or nodes[
                                1].below is None or nodes[2].below is None:
                            # We've reached the seafloor
                            last_nodes = None
                            break
                        if nodes[0].below.depth > deep_bound or nodes[
                                1].below.depth > deep_bound or nodes[
                                    2].below.depth > deep_bound:
                            # At least one of the next nodes will pass
                            # deep_bound; save the current nodes
                            last_nodes = []
                            for i in range(3):
                                last_nodes.append(nodes[i])
                            break
                        else:
                            # Calculate mean of data at six corners of this
                            # triangular prism, and mean depths at three edges
                            values_tmp = []
                            dz_tmp = []
                            for i in range(3):
                                values_tmp.append(data[nodes[i].id])
                                values_tmp.append(data[nodes[i].below.id])
                                dz_tmp.append(
                                    abs(nodes[i].depth - nodes[i].below.depth))
                                # Get ready for next iteration of while loop
                                nodes[i] = nodes[i].below
                            # Integrand is mean of data at corners *
                            # area of upper face * mean of depths at edges
                            integral += mean(array(values_tmp)) * area * mean(
                                array(dz_tmp))

                    # Now integrate from shallow_bound to first_nodes by
                    # linearly interpolating each node to shallow_bound
                    values_tmp = []
                    dz_tmp = []
                    for i in range(3):
                        values_tmp.append(data[first_nodes[i].id])
                        id1, id2, coeff1, coeff2 = elm.nodes[i].find_depth(
                            shallow_bound)
                        if any(isnan(array([id1, id2, coeff1, coeff2]))):
                            # first_nodes[i] was the shallowest node, we can't
                            # interpolate above it
                            values_tmp.append(NaN)
                        else:
                            values_tmp.append(coeff1 * data[id1] +
                                              coeff2 * data[id2])
                        dz_tmp.append(abs(first_nodes[i].depth -
                                          shallow_bound))
                    if any(isnan(array(values_tmp))):
                        pass
                    else:
                        integral += mean(array(values_tmp)) * area * mean(
                            array(dz_tmp))

                    # Now integrate from last_nodes to deep_bound by linearly
                    # interpolating each node to deep_bound, unless we hit
                    # the seafloor earlier
                    if last_nodes is not None:
                        values_tmp = []
                        dz_tmp = []
                        for i in range(3):
                            values_tmp.append(data[last_nodes[i].id])
                            id1, id2, coeff1, coeff2 = elm.nodes[i].find_depth(
                                deep_bound)
                            if any(isnan(array([id1, id2, coeff1, coeff2]))):
                                # last_nodes[i] was the deepest node, we can't
                                # interpolate below it
                                values_tmp.append(NaN)
                            else:
                                values_tmp.append(coeff1 * data[id1] +
                                                  coeff2 * data[id2])
                            dz_tmp.append(abs(deep_bound -
                                              last_nodes[i].depth))
                        if any(isnan(array(values_tmp))):
                            pass
                        else:
                            integral += mean(array(values_tmp)) * area * mean(
                                array(dz_tmp))

                    # All done; divide integral by volume to get the average
                    values.append(integral / volume)
                    # Make new patches for elements which exist at this depth
                    coord = transpose(vstack((elm.x, elm.y)))
                    plot_patches.append(Polygon(coord, True, linewidth=0.))

    if depth_key < 3:
        # Use all patches
        plot_patches = patches[:]

    if mask_cavities:
        # Get mask array of patches for ice shelf cavity elements
        mask_patches = iceshelf_mask(elements)
        if var_name == 'wnet':
            # Swap with regular patches so that open ocean elements are masked,
            # ice shelf cavity nodes are not
            tmp = plot_patches
            plot_patches = mask_patches
            mask_patches = tmp

    # Choose colour bounds
    if set_limits:
        # User-specified bounds
        var_min = limits[0]
        var_max = limits[1]
        if var_min == -var_max:
            # Bounds are centered on zero, so choose a blue-to-red colourmap
            # centered on yellow
            colour_map = 'RdYlBu_r'
        else:
            colour_map = 'jet'
    else:
        # Determine bounds automatically
        if var_name in [
                'uwind', 'vwind', 'qnet', 'olat', 'osen', 'wnet',
                'virtual_salt', 'relax_salt', 'stress_x', 'stress_y', 'uice',
                'vice', 'u', 'v', 'w', 'thdr', 'uhice', 'vhice'
        ]:
            # Center levels on 0 for certain variables, with a blue-to-red
            # colourmap
            max_val = amax(abs(array(values)))
            var_min = -max_val
            var_max = max_val
            colour_map = 'RdYlBu_r'
        else:
            var_min = amin(array(values))
            var_max = amax(array(values))
            colour_map = 'jet'

    # Set up plot
    if circumpolar:
        fig = figure(figsize=(16, 12))
        ax = fig.add_subplot(1, 1, 1, aspect='equal')
    else:
        fig = figure(figsize=(16, 8))
        ax = fig.add_subplot(1, 1, 1)
    # Set colourmap for patches, and refer it to the values array
    img = PatchCollection(plot_patches, cmap=colour_map)
    img.set_array(array(values))
    img.set_edgecolor('face')
    # Add patches to plot
    ax.add_collection(img)
    if mask_cavities:
        # Set colour to light grey for patches in mask
        overlay = PatchCollection(mask_patches, facecolor=(0.6, 0.6, 0.6))
        overlay.set_edgecolor('face')
        # Add mask to plot
        ax.add_collection(overlay)

    # Configure plot
    if circumpolar:
        xlim([-lat_max, lat_max])
        ylim([-lat_max, lat_max])
        ax.get_xaxis().set_ticks([])
        ax.get_yaxis().set_ticks([])
        axis('off')
    else:
        xlim([lon_min, lon_max])
        ylim([lat_min, lat_max])
        xticks(lon_ticks)
        yticks(lat_ticks)
        xlabel('Longitude', fontsize=font_sizes[1])
        ylabel('Latitude', fontsize=font_sizes[1])
        setp(ax.get_xticklabels(), fontsize=font_sizes[2])
        setp(ax.get_yticklabels(), fontsize=font_sizes[2])
    title(name + ' (' + units + ') ' + depth_string, fontsize=font_sizes[0])
    cbar = colorbar(img)
    cbar.ax.tick_params(labelsize=font_sizes[2])
    img.set_clim(vmin=var_min, vmax=var_max)

    # Plot specified points
    #problem_ids = [16, 17, 36, 64, 67, 70, 160, 262, 266, 267, 268, 273, 274, 280, 283, 287, 288, 289, 290, 291, 292, 293, 294, 296, 297, 350, 351, 352, 353, 360, 479, 480, 493, 507, 508, 509, 521, 539, 547, 548, 549, 550, 554, 555]
    #problem_x = []
    #problem_y = []
    #for elm in elements:
    #for i in range(3):
    #if elm.nodes[i].id in problem_ids:
    #problem_x.append(elm.x[i])
    #problem_y.append(elm.y[i])
    #problem_ids.remove(elm.nodes[i].id)
    #ax.plot(problem_x, problem_y, 'or')

    if save:
        fig.savefig(fig_name)
    else:
        fig.show()
예제 #20
0
def barotropic_streamfunction(mesh_path,
                              file_path,
                              tstep,
                              save=False,
                              fig_name=None):

    # Bounds on regular grid
    lon_min = -180
    lon_max = 180
    lat_min = -90
    lat_max = -30
    # Number of points on regular grid
    num_lon = 1000
    num_lat = 1000
    # Radius of the Earth in metres
    r = 6.371e6
    # Degrees to radians coversion factor
    deg2rad = pi / 180.0

    print 'Building mesh'
    elements = fesom_grid(mesh_path, circumpolar=False, cross_180=True)
    # Read number of 2D nodes
    f = open(mesh_path + 'nod2d.out', 'r')
    n2d = int(f.readline())
    f.close()
    # Read (rotated) lon, lat, and depth, at each 3D node
    f = open(mesh_path + 'nod3d.out', 'r')
    f.readline()
    rlon = []
    rlat = []
    node_depth = []
    for line in f:
        tmp = line.split()
        lon_tmp = float(tmp[1])
        lat_tmp = float(tmp[2])
        node_depth_tmp = -1 * float(tmp[3])
        if lon_tmp < -180:
            lon_tmp += 360
        elif lon_tmp > 180:
            lon_tmp -= 360
        rlon.append(lon_tmp)
        rlat.append(lat_tmp)
        node_depth.append(node_depth_tmp)
    f.close()
    rlon = array(rlon)
    rlat = array(rlat)
    node_depth = array(node_depth)
    # Read lists of which nodes are directly below which
    f = open(mesh_path + 'aux3d.out', 'r')
    max_num_layers = int(f.readline())
    node_columns = zeros([n2d, max_num_layers])
    for n in range(n2d):
        for k in range(max_num_layers):
            node_columns[n, k] = int(f.readline())
    node_columns = node_columns.astype(int)
    f.close()
    # Set up regular grid
    # Start with boundaries
    lon_reg_edges = linspace(lon_min, lon_max, num_lon + 1)
    lat_reg_edges = linspace(lat_min, lat_max, num_lat + 1)
    # Now get centres
    lon_reg = 0.5 * (lon_reg_edges[:-1] + lon_reg_edges[1:])
    lat_reg = 0.5 * (lat_reg_edges[:-1] + lat_reg_edges[1:])
    # Also get differentials in lon-lat space
    dlon = lon_reg_edges[1:] - lon_reg_edges[:-1]
    dlat = lat_reg_edges[1:] - lat_reg_edges[:-1]
    # Make 2D versions
    lon_reg_2d, lat_reg_2d = meshgrid(lon_reg, lat_reg)
    dlon_2d, dlat_2d = meshgrid(dlon, dlat)
    # Calculate differentials in Cartesian space
    dx = r * cos(lat_reg_2d * deg2rad) * dlon_2d * deg2rad
    dy = r * dlat_2d * deg2rad

    print 'Reading data'
    # Read 3D rotated u and v
    id = Dataset(file_path, 'r')
    ur = id.variables['u'][tstep - 1, :]
    vr = id.variables['v'][tstep - 1, :]
    id.close()
    # Unrotate
    u, v = unrotate_vector(rlon, rlat, ur, vr)
    # Vertically integrate u*dz
    int_udz = zeros(n2d)
    # Loop over nodes
    for n in range(n2d):
        # Loop over depth
        for k in range(max_num_layers - 1):
            if node_columns[n, k + 1] == -999:
                # Reached the bottom
                break
            # Trapezoidal rule
            top_id = node_columns[n, k]
            bot_id = node_columns[n, k + 1]
            dz = node_depth[bot_id - 1] - node_depth[top_id - 1]
            int_udz[n] += 0.5 * (u[top_id - 1] + u[bot_id - 1]) * dz

    print 'Interpolating to regular grid'
    int_udz_reg = zeros([num_lat, num_lon])
    # For each element, check if a point on the regular lat-lon grid lies
    # within. If so, do barycentric interpolation to that point.
    for elm in elements:
        # Check if we are within domain of regular grid
        if amin(elm.lat) > lat_max:
            continue
        # Find largest regular longitude value west of Element
        tmp = nonzero(lon_reg > amin(elm.lon))[0]
        if len(tmp) == 0:
            # Element crosses the western boundary
            iW = 0
        else:
            iW = tmp[0] - 1
        # Find smallest regular longitude value east of Element
        tmp = nonzero(lon_reg > amax(elm.lon))[0]
        if len(tmp) == 0:
            # Element crosses the eastern boundary
            iE = num_lon
        else:
            iE = tmp[0]
        # Find largest regular latitude value south of Element
        tmp = nonzero(lat_reg > amin(elm.lat))[0]
        if len(tmp) == 0:
            # Element crosses the southern boundary
            jS = 0
        else:
            jS = tmp[0] - 1
        # Find smallest regular latitude value north of Element
        tmp = nonzero(lat_reg > amax(elm.lat))[0]
        if len(tmp) == 0:
            # Element crosses the northern boundary
            jN = num_lat
        else:
            jN = tmp[0]
        for i in range(iW + 1, iE):
            for j in range(jS + 1, jN):
                # There is a chance that the regular gridpoint at (i,j)
                # lies within this element
                lon0 = lon_reg[i]
                lat0 = lat_reg[j]
                if in_triangle(elm, lon0, lat0):
                    # Get area of entire triangle
                    area = triangle_area(elm.lon, elm.lat)
                    # Get area of each sub-triangle formed by
                    # (lon0, lat0)
                    area0 = triangle_area([lon0, elm.lon[1], elm.lon[2]],
                                          [lat0, elm.lat[1], elm.lat[2]])
                    area1 = triangle_area([lon0, elm.lon[0], elm.lon[2]],
                                          [lat0, elm.lat[0], elm.lat[2]])
                    area2 = triangle_area([lon0, elm.lon[0], elm.lon[1]],
                                          [lat0, elm.lat[0], elm.lat[1]])
                    # Find fractional area of each
                    cff = [area0 / area, area1 / area, area2 / area]
                    # Find value of int_udz at each Node
                    vals = []
                    for n in range(3):
                        vals.append(int_udz[elm.nodes[n].id])
                    # Barycentric interpolation to lon0, lat0
                    int_udz_reg[j, i] = sum(array(cff) * array(vals))

    # Indefinite integral from south to north of udz*dy, convert to Sv
    strf = cumsum(int_udz_reg * dy, axis=0) * 1e-6
    # Apply land mask: wherever interpolated field was identically zero
    strf = ma.masked_where(int_udz_reg == 0, strf)

    print 'Plotting'
    fig = figure(figsize=(10, 6))
    ax = fig.add_subplot(1, 1, 1)
    pcolor(lon_reg, lat_reg, strf, cmap='jet')
    xlabel('Longitude')
    ylabel('Latitude')
    xlim([-180, 180])
    ylim([-90, -30])
    colorbar()
    title('Barotropic streamfunction (Sv)', fontsize=20)

    if save:
        fig.savefig(fig_name)
    else:
        fig.show()