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')
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()
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
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()
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')
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')
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')
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()
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()
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()
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')
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()
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')
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
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')
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()
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')
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')
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()
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()