def circumpolar_cice_plot(file_path, var_name, tstep, colour_bounds=None, save=False, fig_name=None): deg2rad = pi / 180 month_names = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ] # Read the variable id = Dataset(file_path, 'r') data_tmp = id.variables[var_name][tstep - 1, :-15, :] if var_name == 'aice': units = 'fraction' else: units = id.variables[var_name].units # Check for vector variables that need to be rotated if var_name in [ 'uvel', 'vvel', 'uatm', 'vatm', 'uocn', 'vocn', 'strairx', 'strairy', 'strtltx', 'strtlty', 'strcorx', 'strcory', 'strocnx', 'strocny', 'strintx', 'strinty' ]: angle = id.variables['ANGLE'][:-15, :] if var_name in [ 'uvel', 'uatm', 'uocn', 'strairx', 'strtltx', 'strcorx', 'strocnx', 'strintx' ]: # u-variable u_data = data_tmp[:, :] if var_name[0] == 'u': v_data = id.variables[var_name.replace('u', 'v')][tstep - 1, :-15, :] else: v_data = id.variables[var_name.replace('x', 'y')][tstep - 1, :-15, :] u_data_lonlat, v_data_lonlat = rotate_vector_cice( u_data, v_data, angle) data_tmp = u_data_lonlat elif var_name in [ 'vvel', 'vatm', 'vocn', 'strairy', 'strtlty', 'strcory', 'strocny', 'strinty' ]: # v-variable v_data = data_tmp[:, :] if var_name[0] == 'v': u_data = id.variables[var_name.replace('v', 'u', 1)][tstep - 1, :-15, :] else: u_data = id.variables[var_name.replace('y', 'x')][tstep - 1, :-15, :] u_data_lonlat, v_data_lonlat = rotate_vector_cice( u_data, v_data, angle) data_tmp = v_data_lonlat # Figure out which grid we're on grid_string = id.variables[var_name].coordinates if grid_string.startswith('ULON'): grid_name = 'u' lon_name = 'ULON' lat_name = 'ULAT' elif grid_string.startswith('TLON'): grid_name = 't' lon_name = 'TLON' lat_name = 'TLAT' else: print 'Grid type ' + grid_string + ' not supported' id.close() return # Read the correct lat and lon for this grid lon_tmp = id.variables[lon_name][:-15, :] lat_tmp = id.variables[lat_name][:-15, :] time_id = id.variables['time'] time = num2date(time_id[tstep - 1], units=time_id.units, calendar=time_id.calendar.lower()) id.close() # Wrap the periodic boundary by 1 cell lon = ma.empty([size(lon_tmp, 0), size(lon_tmp, 1) + 1]) lat = ma.empty([size(lat_tmp, 0), size(lat_tmp, 1) + 1]) data = ma.empty([size(data_tmp, 0), size(data_tmp, 1) + 1]) lon[:, :-1] = lon_tmp lon[:, -1] = lon_tmp[:, 0] lat[:, :-1] = lat_tmp lat[:, -1] = lat_tmp[:, 0] data[:, :-1] = data_tmp data[:, -1] = data_tmp[:, 0] # Convert to spherical coordinates x = -(lat + 90) * cos(lon * deg2rad + pi / 2) y = (lat + 90) * sin(lon * deg2rad + pi / 2) if colour_bounds is not None: # User has set bounds on colour scale lev = linspace(colour_bounds[0], colour_bounds[1], num=40) if colour_bounds[0] == -colour_bounds[1]: # 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 [ 'uvel', 'vvel', 'uatm', 'vatm', 'uocn', 'vocn', 'fresh_ai', 'fsalt_ai', 'fhocn_ai', 'strairx', 'strairy', 'strtltx', 'strtlty', 'strcorx', 'strcory', 'strocnx', 'strocny', 'strintx', 'strinty' ]: # Center levels on 0 for certain variables, with a blue-to-red # colourmap max_val = amax(abs(data)) lev = linspace(-max_val, max_val, num=40) colour_map = 'RdYlBu_r' else: lev = linspace(amin(data), amax(data), num=40) colour_map = 'jet' # Plot fig = figure(figsize=(16, 12)) fig.add_subplot(1, 1, 1, aspect='equal') contourf(x, y, data, lev, cmap=colour_map, extend='both') cbar = colorbar() cbar.ax.tick_params(labelsize=20) title(var_name + ' (' + units + ')\n' + str(time.day) + ' ' + month_names[time.month - 1] + ' ' + str(time.year), fontsize=24) #title(var_name+' ('+units+')', fontsize=30) axis('off') if save: fig.savefig(fig_name) else: fig.show()
def common_grid(roms_file, cice_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 N = 31 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 ROMS grid id = Dataset(roms_file, 'r') # We only need lat and lon on the rho grid lon_rho = id.variables['lon_rho'][:, :] lat_rho = id.variables['lat_rho'][:, :] # Get shape of u and v grids u_shape = id.variables['lon_u'].shape v_shape = id.variables['lon_v'].shape # Read land mask mask_roms = id.variables['mask_rho'][:, :] # Mask out ice shelves too zice = id.variables['zice'][:, :] mask_roms[zice != 0] = 0.0 # Read angle (for rotation of vector components) angle_roms = id.variables['angle'][:, :] # Get time as an array of Date objects time_id = id.variables['ocean_time'] time = num2date(time_id[:], units=time_id.units, calendar=time_id.calendar.lower()) id.close() # Read the CICE grid id = Dataset(cice_file, 'r') # We only need lat and lon on the velocity grid lon_cice = id.variables['ULON'][:, :] lat_cice = id.variables['ULAT'][:, :] # Read angle (for rotation of vector components) angle_cice = id.variables['ANGLE'][:, :] id.close() # Make sure longitude is between -180 and 180 index = lon_rho > 180 lon_rho[index] = lon_rho[index] - 360 index = lon_cice > 180 lon_cice[index] = lon_cice[index] - 360 print 'Counting months' # Assume we start at the beginning of a year # Figure out how many complete years have happened since then num_full_years = time[-1].year - time[0].year if time[-1].month == 12 and time[-1].day in range(29, 31 + 1): # We happen to end at the very end of a year num_full_years += 1 else: # Count the complete months that have happened this year num_extra_months = time[-1].month - 1 # Don't bother with the hassle of considering cases where we end at # the very end of a month. Just ignore the month. num_months = 12 * num_full_years + num_extra_months print 'Interpolating land mask to new grid' mask_common = interp_roms2common(lon_common, lat_common, lon_rho, lat_rho, mask_roms) mask_common[isnan(mask_common)] = 0 # Cut it off at 1 mask_common[mask_common < 0.5] = 0 mask_common[mask_common >= 0.5] = 1 # 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' # id.close() # Loop over months for month in range(18, num_months): print 'Processing month ' + str(month + 1) + ' of ' + str(num_months) id = Dataset(out_file, 'a') # Write time value for this month id.variables['time'][month] = month + 1 print '...sea surface temperature' # Get monthly average of 3D variable temp_roms = monthly_avg_roms( roms_file, 'temp', [N, size(lon_rho, 0), size(lon_rho, 1)], month % 12, instance=month / 12 + 1) # Select surface layer sst_roms = temp_roms[-1, :, :] # Interpolate to common grid sst_common = interp_roms2common(lon_common, lat_common, lon_rho, lat_rho, sst_roms) # Apply land mask sst = ma.masked_where(mask_common == 0, sst_common) # Write to file id.variables['sst'][month, :, :] = sst print '...sea surface salinity' # Get monthly average of 3D variable salt_roms = monthly_avg_roms( roms_file, 'salt', [N, size(lon_rho, 0), size(lon_rho, 1)], month % 12, instance=month / 12 + 1) # Select surface layer sss_roms = salt_roms[-1, :, :] # Interpolate to common grid sss_common = interp_roms2common(lon_common, lat_common, lon_rho, lat_rho, sss_roms) # Apply land mask sss = ma.masked_where(mask_common == 0, sss_common) # Write to file id.variables['sss'][month, :, :] = sss print '...surface heat flux' # Get monthly average shflux_roms = monthly_avg_roms(roms_file, 'shflux', shape(lon_rho), month % 12, instance=month / 12 + 1) # Interpolate to common grid shflux_common = interp_roms2common(lon_common, lat_common, lon_rho, lat_rho, shflux_roms) # Apply land mask shflux = ma.masked_where(mask_common == 0, shflux_common) # Write to file id.variables['shflux'][month, :, :] = shflux print '...surface salt flux' # Get monthly average ssflux_roms = monthly_avg_roms(roms_file, 'ssflux', shape(lon_rho), month % 12, instance=month / 12 + 1) # Interpolate to common grid ssflux_common = interp_roms2common(lon_common, lat_common, lon_rho, lat_rho, ssflux_roms) # Apply land mask ssflux = ma.masked_where(mask_common == 0, ssflux_common) # Write to file id.variables['ssflux'][month, :, :] = ssflux print '...sea ice concentration' # Get monthly average (use CICE file) aice_cice = monthly_avg_cice(cice_file, 'aice', shape(lon_cice), month % 12, instance=month / 12 + 1) # Interpolate to common grid (note CICE grid not ROMS) aice_common = interp_roms2common(lon_common, lat_common, lon_cice, lat_cice, aice_cice) # Apply land mask aice = ma.masked_where(mask_common == 0, aice_common) # Write to file id.variables['aice'][month, :, :] = aice print '...sea ice thickness' # Get monthly average (use CICE file) hice_cice = monthly_avg_cice(cice_file, 'hi', shape(lon_cice), month % 12, instance=month / 12 + 1) # Interpolate to common grid (note CICE grid not ROMS) hice_common = interp_roms2common(lon_common, lat_common, lon_cice, lat_cice, hice_cice) # Apply land mask hice = ma.masked_where(mask_common == 0, hice_common) # Write to file id.variables['hice'][month, :, :] = hice print '...surface ocean velocity vector' # Surface ocean velocity # Get monthly averages of both 3D vector components uocn_3d_tmp = monthly_avg_roms(roms_file, 'u', [N, u_shape[0], u_shape[1]], month % 12, instance=month / 12 + 1) vocn_3d_tmp = monthly_avg_roms(roms_file, 'v', [N, v_shape[0], v_shape[1]], month % 12, instance=month / 12 + 1) # Select surface layer uocn_tmp = uocn_3d_tmp[-1, :, :] vocn_tmp = vocn_3d_tmp[-1, :, :] # Rotate to lon-lat space (note they are on the rho grid now) uocn_roms, vocn_roms = rotate_vector_roms(uocn_tmp, vocn_tmp, angle_roms) # Interpolate to common grid uocn_common = interp_roms2common(lon_common, lat_common, lon_rho, lat_rho, uocn_roms) vocn_common = interp_roms2common(lon_common, lat_common, lon_rho, lat_rho, vocn_roms) # 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'][month, :, :] = uocn id.variables['vocn'][month, :, :] = vocn print '...sea ice velocity vector' # Sea ice velocity (CICE variable not ROMS) # Get monthly averages of both vector components uice_tmp = monthly_avg_cice(cice_file, 'uvel', shape(lon_cice), month % 12, instance=month / 12 + 1) vice_tmp = monthly_avg_cice(cice_file, 'vvel', shape(lon_cice), month % 12, instance=month / 12 + 1) # Rotate to lon-lat space uice_cice, vice_cice = rotate_vector_cice(uice_tmp, vice_tmp, angle_cice) # Interpolate to common grid (note CICE grid not ROMS) uice_common = interp_roms2common(lon_common, lat_common, lon_cice, lat_cice, uice_cice) vice_common = interp_roms2common(lon_common, lat_common, lon_cice, lat_cice, vice_cice) # 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'][month, :, :] = uice id.variables['vice'][month, :, :] = vice print '...surface stress vector' # Surface stresses # Get monthly averages of both vector components sustr_tmp = monthly_avg_roms(roms_file, 'sustr', u_shape, month % 12, instance=month / 12 + 1) svstr_tmp = monthly_avg_roms(roms_file, 'svstr', v_shape, month % 12, instance=month / 12 + 1) # Rotate to lon-lat space (note they are on the rho grid now) sustr_roms, svstr_roms = rotate_vector_roms(sustr_tmp, svstr_tmp, angle_roms) # Interpolate to common grid sustr_common = interp_roms2common(lon_common, lat_common, lon_rho, lat_rho, sustr_roms) svstr_common = interp_roms2common(lon_common, lat_common, lon_rho, lat_rho, svstr_roms) # 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'][month, :, :] = sustr id.variables['svstr'][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'][month, :, :] = curl_str id.close() print 'Finished'
def compare_nic_seasonal(cice_file, var_name, colour_bounds=None, save=False, fig_name=None): # Piece together the paths to Nic's monthly averaged output on raijin nic_dir_head = '/g/data/gh5/access_om_025-CORE_NYF/output' output_number = 137 nic_dir_tail = '/ice/HISTORY/' nic_file_head = 'iceh.0' nic_year_number = 133 # Maximum j-index to read in Nic's output max_j = 300 # Look at the variable in my output id = Dataset(cice_file, 'r') # Save units units = id.variables[var_name].units # Check if this is a vector we need to rotate to lon-lat space if var_name in [ 'uvel', 'vvel', 'strairx', 'strairy', 'strocnx', 'strocny' ]: rotate = True # Read the angle of grid rotation angle = id.variables['ANGLE'][:, :] # Figure out whether this is an x or y component, and what the name of # the other component is if var_name == 'uvel': cmp_flag = 'x' other_name = 'vvel' elif var_name == 'vvel': cmp_flag = 'y' other_name = 'uvel' elif var_name in ['strairx', 'strocnx']: cmp_flag = 'x' other_name = var_name.replace('x', 'y') elif var_name in ['strairy', 'strocny']: cmp_flag = 'y' other_name = var_name.replace('x', 'y') else: rotate = False # Read the correct grid (tracer or velocity) grid_string = id.variables[var_name].coordinates if grid_string.startswith('ULON'): lon_name = 'ULON' lat_name = 'ULAT' else: lon_name = 'TLON' lat_name = 'TLAT' id.close() # Number of days in each month (this is just for Nic's output) # Note Nic doesn't run with leap years ndays_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] # Season names for titles season_names = ['DJF', 'MAM', 'JJA', 'SON'] # Degrees to radians conversion deg2rad = pi / 180.0 # Read my CICE grid id = Dataset(cice_file, 'r') cice_lon_tmp = id.variables[lon_name][:-15, :] cice_lat_tmp = id.variables[lat_name][:-15, :] num_lon = id.variables['TLON'].shape[1] num_lat = id.variables['TLAT'].shape[0] id.close() # Wrap the periodic boundary by 1 cell cice_lon = ma.empty([size(cice_lon_tmp, 0), size(cice_lon_tmp, 1) + 1]) cice_lat = ma.empty([size(cice_lat_tmp, 0), size(cice_lat_tmp, 1) + 1]) cice_lon[:, :-1] = cice_lon_tmp cice_lon[:, -1] = cice_lon_tmp[:, 0] cice_lat[:, :-1] = cice_lat_tmp cice_lat[:, -1] = cice_lat_tmp[:, 0] # Get seasonal averages of CICE data if rotate: # Average both components of the vector this_cmp = seasonal_avg_cice(cice_file, var_name, [num_lat, num_lon]) other_cmp = seasonal_avg_cice(cice_file, other_name, [num_lat, num_lon]) # Rotate to lon-lat space if cmp_flag == 'x': cice_data_tmp = ma.empty(shape(this_cmp)) for season in range(4): tmp1, tmp2 = rotate_vector_cice(this_cmp[season, :, :], other_cmp[season, :, :], angle) cice_data_tmp[season, :, :] = tmp1 elif cmp_flag == 'y': cice_data_tmp = ma.empty(shape(this_cmp)) for season in range(4): tmp1, tmp2 = rotate_vector_cice(other_cmp[season, :, :], this_cmp[season, :, :], angle) cice_data_tmp[season, :, :] = tmp2 else: cice_data_tmp = seasonal_avg_cice(cice_file, var_name, [num_lat, num_lon]) # Chop off northern boundary cice_data_tmp = cice_data_tmp[:, :-15, :] # Wrap the periodic boundary cice_data = ma.empty([ size(cice_data_tmp, 0), size(cice_data_tmp, 1), size(cice_data_tmp, 2) + 1 ]) cice_data[:, :, :-1] = cice_data_tmp cice_data[:, :, -1] = cice_data_tmp[:, :, 0] # Conversions to account for different thermodynamics schemes if var_name in ['frazil', 'snoice']: cice_data /= 3.6 # Read Nic's grid from the January file id = Dataset( nic_dir_head + str(output_number) + nic_dir_tail + nic_file_head + str(nic_year_number) + '-01.nc', 'r') nic_lon = id.variables[lon_name][:max_j, :] nic_lat = id.variables[lat_name][:max_j, :] id.close() # Get seasonal averages of Nic's output nic_data = ma.empty([4, size(nic_lon, 0), size(nic_lon, 1)]) nic_data[:, :, :] = 0.0 # Loop over seasons for season in range(4): # Figure out what months we care about for this season if season == 0: months = [12, 1, 2] elif season == 1: months = [3, 4, 5] elif season == 2: months = [6, 7, 8] elif season == 3: months = [9, 10, 11] # Days in season so far season_days = 0 # Loop over months for month in months: if month == 12: # Read December from the previous year filename = nic_dir_head + str( output_number - 1) + nic_dir_tail + nic_file_head + str( nic_year_number - 1) + '-' + str(month) + '.nc' else: if month < 10: filename = nic_dir_head + str( output_number) + nic_dir_tail + nic_file_head + str( nic_year_number) + '-0' + str(month) + '.nc' else: filename = nic_dir_head + str( output_number) + nic_dir_tail + nic_file_head + str( nic_year_number) + '-' + str(month) + '.nc' id = Dataset(filename, 'r') # Integrate over time nic_data[season, :, :] += id.variables[var_name][ 0, :max_j, :] * ndays_month[month - 1] season_days += ndays_month[month - 1] # Convert from integral to average nic_data[season, :, :] /= season_days # Convert both grids to spherical coordinates cice_x = -(cice_lat + 90) * cos(cice_lon * deg2rad + pi / 2) cice_y = (cice_lat + 90) * sin(cice_lon * deg2rad + pi / 2) nic_x = -(nic_lat + 90) * cos(nic_lon * deg2rad + pi / 2) nic_y = (nic_lat + 90) * sin(nic_lon * deg2rad + pi / 2) # Boundaries on plots bdry1 = -35 bdry2 = 39 bdry3 = -35 bdry4 = 39 if colour_bounds is not None: # User-defined colour bounds lev = linspace(colour_bounds[0], colour_bounds[1], num=50) if colour_bounds[0] == -colour_bounds[1]: # Centered on zero; use red-yellow-blue colourmap colour_map = 'RdYlBu_r' else: # Not centered on zero; go for a rainbow colour_map = 'jet' else: # Automatic colour bounds based on min/max of the data if var_name in [ 'uvel', 'vvel', 'strairx', 'strairy', 'strocnx', 'strocny' ]: # Center on zero and use red-yellow-blue colourmap max_val = max(amax(abs(nic_data)), amax(abs(cice_data))) lev = linspace(-max_val, max_val, num=50) colour_map = 'RdYlBu_r' else: # Not centered on zero; go for a rainbow min_val = min(amin(nic_data), amin(cice_data)) max_val = max(amax(nic_data), amax(cice_data)) lev = linspace(min_val, max_val, num=50) colour_map = 'jet' # Make the figure fig = figure(figsize=(20, 9)) # Loop over seasons for season in range(4): # Nic's output ax = fig.add_subplot(2, 4, season + 1, aspect='equal') contourf(nic_x, nic_y, nic_data[season, :, :], lev, cmap=colour_map, extend='both') if season == 0: text(-39, 0, 'Nic', fontsize=24, ha='right') title(season_names[season], fontsize=24) xlim([bdry1, bdry2]) ylim([bdry3, bdry4]) axis('off') # My output ax = fig.add_subplot(2, 4, season + 5, aspect='equal') img = contourf(cice_x, cice_y, cice_data[season, :, :], lev, cmap=colour_map, extend='both') if season == 0: text(-39, 0, 'Me', fontsize=24, ha='right') xlim([bdry1, bdry2]) ylim([bdry3, bdry4]) axis('off') # Add colourbar on the bottom cbaxes = fig.add_axes([0.25, 0.04, 0.5, 0.02]) cbar = colorbar(img, orientation='horizontal', cax=cbaxes) cbar.ax.tick_params(labelsize=16) # Main title with variable name and units suptitle(var_name + ' (' + units + ')', fontsize=30) subplots_adjust(wspace=0.025, hspace=0.025) # Finished if save: fig.savefig(fig_name) else: fig.show()
def circumpolar_cice_plot (file_path, var_name, tstep, colour_bounds=None, save=False, fig_name=None): deg2rad = pi/180 # Read the variable id = Dataset(file_path, 'r') data_tmp = id.variables[var_name][tstep-1,:-15,:] if var_name == 'aice': units = 'fraction' else: units = id.variables[var_name].units # Check for vector variables that need to be rotated if var_name in ['uvel', 'vvel', 'uatm', 'vatm', 'uocn', 'vocn', 'strairx', 'strairy', 'strtltx', 'strtlty', 'strcorx', 'strcory', 'strocnx', 'strocny', 'strintx', 'strinty']: angle = id.variables['ANGLET'][:-15,:] if var_name in ['uvel', 'uatm', 'uocn', 'strairx', 'strtltx', 'strcorx', 'strocnx', 'strintx']: # u-variable u_data = data_tmp[:,:] if var_name[0] == 'u': v_data = id.variables[var_name.replace('u','v')][tstep-1,:-15,:] else: v_data = id.variables[var_name.replace('x','y')][tstep-1,:-15,:] u_data_lonlat, v_data_lonlat = rotate_vector_cice(u_data, v_data, angle) data_tmp = u_data_lonlat elif var_name in ['vvel', 'vatm', 'vocn', 'strairy', 'strtlty', 'strcory', 'strocny', 'strinty']: # v-variable v_data = data_tmp[:,:] if var_name[0] == 'v': u_data = id.variables[var_name.replace('v','u',1)][tstep-1,:-15,:] else: u_data = id.variables[var_name.replace('y','x')][tstep-1,:-15,:] u_data_lonlat, v_data_lonlat = rotate_vector_cice(u_data, v_data, angle) data_tmp = v_data_lonlat # Figure out which grid we're on grid_string = id.variables[var_name].coordinates if grid_string.startswith('ULON'): grid_name = 'u' lon_name = 'ULON' lat_name = 'ULAT' elif grid_string.startswith('TLON'): grid_name = 't' lon_name = 'TLON' lat_name = 'TLAT' else: print 'Grid type ' + grid_string + ' not supported' id.close() return # Read the correct lat and lon for this grid lon_tmp = id.variables[lon_name][:-15,:] lat_tmp = id.variables[lat_name][:-15,:] id.close() # Wrap the periodic boundary by 1 cell lon = ma.empty([size(lon_tmp,0), size(lon_tmp,1)+1]) lat = ma.empty([size(lat_tmp,0), size(lat_tmp,1)+1]) data = ma.empty([size(data_tmp,0), size(data_tmp,1)+1]) lon[:,:-1] = lon_tmp lon[:,-1] = lon_tmp[:,0] lat[:,:-1] = lat_tmp lat[:,-1] = lat_tmp[:,0] data[:,:-1] = data_tmp data[:,-1] = data_tmp[:,0] # Convert to spherical coordinates x = -(lat+90)*cos(lon*deg2rad+pi/2) y = (lat+90)*sin(lon*deg2rad+pi/2) if colour_bounds is not None: # User has set bounds on colour scale lev = linspace(colour_bounds[0], colour_bounds[1], num=40) if colour_bounds[0] == -colour_bounds[1]: # 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 ['uvel', 'vvel', 'uatm', 'vatm', 'uocn', 'vocn', 'fresh_ai', 'fsalt_ai', 'fhocn_ai', 'strairx', 'strairy', 'strtltx', 'strtlty', 'strcorx', 'strcory', 'strocnx', 'strocny', 'strintx', 'strinty']: # Center levels on 0 for certain variables, with a blue-to-red # colourmap max_val = amax(abs(data)) lev = linspace(-max_val, max_val, num=40) colour_map = 'RdYlBu_r' else: lev = linspace(amin(data), amax(data), num=40) colour_map = 'jet' # Plot fig = figure(figsize=(16,12)) fig.add_subplot(1,1,1, aspect='equal') contourf(x, y, data, lev, cmap=colour_map, extend='both') cbar = colorbar() cbar.ax.tick_params(labelsize=20) title(var_name+' ('+units+')', fontsize=30) axis('off') if save: fig.savefig(fig_name) else: fig.show()
def cice_vectorplot (file_path, tstep, xname, yname, cmax=None, save=False, fig_name=None): # Radius of the Earth in metres r = 6.371e6 # Degrees to radians conversion factor deg2rad = pi/180 # Side length of blocks to average vectors over (can't plot vector at # every single point or the plot will be way too crowded) block = 15 # Read grid (including rotation angle) and vector components id = Dataset(file_path, 'r') lon_tmp = id.variables['ULON'][:-15,:] lat_tmp = id.variables['ULAT'][:-15,:] angle_tmp = id.variables['ANGLET'][:-15,:] u_xy_tmp = id.variables[xname][tstep-1,:-15,:] v_xy_tmp = id.variables[yname][tstep-1,:-15,:] id.close() # Wrap periodic boundary by 1 cell lon = ma.empty([size(lon_tmp,0), size(lon_tmp,1)+1]) lat = ma.empty([size(lat_tmp,0), size(lat_tmp,1)+1]) angle = ma.empty([size(angle_tmp,0), size(angle_tmp,1)+1]) u_xy = ma.empty([size(u_xy_tmp,0), size(u_xy_tmp,1)+1]) v_xy = ma.empty([size(v_xy_tmp,0), size(v_xy_tmp,1)+1]) lon[:,:-1] = lon_tmp lon[:,-1] = lon_tmp[:,0] lat[:,:-1] = lat_tmp lat[:,-1] = lat_tmp[:,0] angle[:,:-1] = angle_tmp angle[:,-1] = angle_tmp[:,0] u_xy[:,:-1] = u_xy_tmp u_xy[:,-1] = u_xy_tmp[:,0] v_xy[:,:-1] = v_xy_tmp v_xy[:,-1] = v_xy_tmp[:,0] # Rotate from local x-y space to lon-lat space u, v = rotate_vector_cice(u_xy, v_xy, angle) # Calculate magnitude for the background filled contour plot speed = sqrt(u**2 + v**2) # Calculate X and Y coordinates for plotting circumpolar projection X = -(lat+90)*cos(lon*deg2rad+pi/2) Y = (lat+90)*sin(lon*deg2rad+pi/2) # Calculate vector components in spherical coordinate space # (just differentiate and rearrange spherical coordinate transformation) dlon_dt = u/(r*cos(lat*deg2rad)*deg2rad) dlat_dt = v/(r*deg2rad) dX_dt = -dlat_dt*cos(lon*deg2rad+pi/2) + (lat+90)*sin(lon*deg2rad+pi/2)*dlon_dt*deg2rad dY_dt = dlat_dt*sin(lon*deg2rad+pi/2) + (lat+90)*cos(lon*deg2rad+pi/2)*dlon_dt*deg2rad # Average X, Y, dX_dt, and dY_dt over block x block intervals # Calculate number of blocks size0 = int(ceil(size(X,0)/float(block))) size1 = int(ceil((size(X,1)-1)/float(block))) # Set up arrays for averaged fields X_block = ma.empty([size0, size1]) Y_block = ma.empty([size0, size1]) dX_dt_block = ma.empty([size0, size1]) dY_dt_block = ma.empty([size0, size1]) # Set up arrays containing boundary indices posn0 = range(0, size(X,0), block) posn0.append(size(X,0)) posn1 = range(0, size(X,1), block) posn1.append(size(X,1)) # Double loop to average each block (can't find a more efficient way to do # this) for j in range(size0): for i in range(size1): start0 = posn0[j] end0 = posn0[j+1] start1 = posn1[i] end1 = posn1[i+1] X_block[j,i] = mean(X[start0:end0, start1:end1]) Y_block[j,i] = mean(Y[start0:end0, start1:end1]) dX_dt_block[j,i] = mean(dX_dt[start0:end0, start1:end1]) dY_dt_block[j,i] = mean(dY_dt[start0:end0, start1:end1]) # Set up colour scale levels if cmax is None: lev = linspace(0, amax(speed), num=50) else: lev = linspace(0, cmax, num=50) # Make the plot fig = figure(figsize=(16,12)) fig.add_subplot(1,1,1, aspect='equal') # Contour speed values at every point # Use pastel colour map so overlaid vectors will show up contourf(X, Y, speed, lev, cmap='Paired', extend='both') cbar = colorbar() cbar.ax.tick_params(labelsize=20) # Add vectors for each block quiver(X_block, Y_block, dX_dt_block, dY_dt_block, color='black') title(xname + ', ' + yname, fontsize=30) axis('off') if save: fig.savefig(fig_name) else: fig.show()
def ice_drift_seasonal(cice_file, save=False, fig_name=None): # Starting and ending months (1-based) for each season start_month = [2, 5, 8, 11] end_month = [4, 7, 10, 1] # Starting and ending days of the month (1-based) for each season start_day = [1, 1, 1, 1] end_day = [30, 31, 31, 31] # Number of days in each season # Assume no leap years, we'll fix this later if needed ndays_season = [89, 92, 92, 92] # Season names for titles season_names = ['FMA', 'MMJ', 'ASO', 'NDJ'] # Order of figures (clockwise) figure_order = [1, 2, 4, 3] # Degrees to radians conversion deg2rad = pi / 180.0 # Side length of blocks to average vectors over (can't plot vector at # every single point or the plot will be way too crowded) block = 15 # Read CICE grid (including angle) and time values id = Dataset(cice_file, 'r') lon_tmp = id.variables['TLON'][:-15, :] lat_tmp = id.variables['TLAT'][:-15, :] angle_tmp = id.variables['ANGLE'][:-15, :] # Wrap the periodic boundary by 1 cell lon = ma.empty([size(lon_tmp, 0), size(lon_tmp, 1) + 1]) lat = ma.empty([size(lat_tmp, 0), size(lat_tmp, 1) + 1]) angle = ma.empty([size(angle_tmp, 0), size(angle_tmp, 1) + 1]) lon[:, :-1] = lon_tmp lon[:, -1] = lon_tmp[:, 0] lat[:, :-1] = lat_tmp lat[:, -1] = lat_tmp[:, 0] angle[:, :-1] = angle_tmp angle[:, -1] = angle_tmp[:, 0] time_id = id.variables['time'] # Get the year, month, and day (all 1-based) for each output step # These are 5-day averages marked with the next day's date. time = num2date(time_id[:], units=time_id.units, calendar=time_id.calendar.lower()) # Loop backwards through time indices to find the last one we care about # (which contains 31 Jan in its averaging period) end_t = -1 # Missing value flag for t in range(size(time) - 1, -1, -1): if time[t].month == start_month[0] and time[t].day in range( start_day[0], start_day[0] + 5): end_t = t break # Make sure we actually found it if end_t == -1: print 'Error: ' + cice_file + ' does not contain a complete Feb-Jan period' return # Continue looping backwards to find the first time index we care about # (which contains 1 Feb the previous year in its averaging period) start_t = -1 # Missing value flag for t in range(end_t - 60, -1, -1): if time[t].month == start_month[0] and time[t].day in range( start_day[0] + 1, start_day[0] + 6): start_t = t break # Make sure we actually found it if start_t == -1: print 'Error: ' + cice_file + ' does not contain a complete Feb-Jan period' return # Check for leap years leap_year = False if mod(time[start_t].year, 4) == 0: # Years divisible by 4 are leap years leap_year = True if mod(time[start_t].year, 100) == 0: # Unless they're also divisible by 100, in which case they aren't # leap years leap_year = False if mod(time[start_t].year, 400) == 0: # Unless they're also divisible by 400, in which case they are # leap years after all leap_year = True if leap_year: # Update last day in February ndays_season[0] += 1 # Initialise seasonal averages of CICE output aice_tmp = ma.empty([4, size(lon_tmp, 0), size(lon_tmp, 1)]) aice_tmp[:, :, :] = 0.0 uxy_tmp = ma.empty([4, size(lon_tmp, 0), size(lon_tmp, 1)]) uxy_tmp[:, :, :] = 0.0 vxy_tmp = ma.empty([4, size(lon_tmp, 0), size(lon_tmp, 1)]) vxy_tmp[:, :, :] = 0.0 # Process one season at a time for season in range(4): season_days = 0 # Number of days in season; this will be incremented next_season = mod(season + 1, 4) # Find starting timestep start_t_season = -1 for t in range(start_t, end_t + 1): if time[t].month == start_month[season] and time[t].day in range( start_day[season] + 1, start_day[season] + 6): start_t_season = t break # Make sure we actually found it if start_t_season == -1: print 'Error: could not find starting timestep for season ' + season_names[ season] return # Find ending timestep end_t_season = -1 for t in range(start_t_season + 1, end_t + 1): if time[t].month == start_month[ next_season] and time[t].day in range( start_day[next_season], start_day[next_season] + 5): end_t_season = t break # Make sure we actually found it if end_t_season == -1: print 'Error: could not find ending timestep for season ' + season_names[ season] return # Figure out how many of the 5 days averaged in start_t_season are # actually within this season if time[start_t_season].month == start_month[season] and time[ start_t_season].day == start_day[season] + 5: # Starting day is in position 1 of 5; we care about all of them start_days = 5 elif time[start_t_season].month == start_month[season] and time[ start_t_season].day == start_day[season] + 4: # Starting day is in position 2 of 5; we care about the last 4 start_days = 4 elif time[start_t_season].month == start_month[season] and time[ start_t_season].day == start_day[season] + 3: # Starting day is in position 3 of 5; we care about the last 3 start_days = 3 elif time[start_t_season].month == start_month[season] and time[ start_t_season].day == start_day[season] + 2: # Starting day is in position 4 of 5; we care about the last 2 start_days = 2 elif time[start_t_season].month == start_month[season] and time[ start_t_season].day == start_day[season] + 1: # Starting day is in position 5 of 5; we care about the last 1 start_days = 1 else: print 'Error for season ' + season_names[ season] + ': starting index is month ' + str( time[start_t_season].month) + ', day ' + str( time[start_t_season].day) return # Start accumulating data weighted by days aice_tmp[season, :, :] += id.variables['aice'][ start_t_season, :-15, :] * start_days uxy_tmp[season, :, :] += id.variables['uvel'][ start_t_season, :-15, :] * start_days vxy_tmp[season, :, :] += id.variables['vvel'][ start_t_season, :-15, :] * start_days season_days += start_days # Between start_t_season and end_t_season, we want all the days for t in range(start_t_season + 1, end_t_season): aice_tmp[season, :, :] += id.variables['aice'][t, :-15, :] * 5 uxy_tmp[season, :, :] += id.variables['uvel'][t, :-15, :] * 5 vxy_tmp[season, :, :] += id.variables['vvel'][t, :-15, :] * 5 season_days += 5 # Figure out how many of the 5 days averaged in end_t_season are # actually within this season if time[end_t_season].month == start_month[next_season] and time[ end_t_season].day == start_day[next_season] + 4: # Ending day is in position 1 of 5; we care about the first 1 end_days = 1 elif time[end_t_season].month == start_month[next_season] and time[ end_t_season].day == start_day[next_season] + 3: # Ending day is in position 2 of 5; we care about the first 2 end_days = 2 elif time[end_t_season].month == start_month[next_season] and time[ end_t_season].day == start_day[next_season] + 2: # Ending day is in position 3 of 5; we care about the first 3 end_days = 3 elif time[end_t_season].month == start_month[next_season] and time[ end_t_season].day == start_day[next_season] + 1: # Ending day is in position 4 of 5; we care about the first 4 end_days = 4 elif time[end_t_season].month == start_month[next_season] and time[ end_t_season].day == start_day[next_season]: # Ending day is in position 5 of 5; we care about all 5 end_days = 5 else: print 'Error for season ' + season_names[ season] + ': ending index is month ' + str( time[end_t_season].month) + ', day ' + str( time[end_t_season].day) return aice_tmp[season, :, :] += id.variables['aice'][ end_t_season, :-15, :] * end_days uxy_tmp[season, :, :] += id.variables['uvel'][ end_t_season, :-15, :] * end_days vxy_tmp[season, :, :] += id.variables['vvel'][ end_t_season, :-15, :] * end_days season_days += end_days # Check that we got the correct number of days if season_days != ndays_season[season]: print 'Error: found ' + str( season_days) + ' days instead of ' + str(ndays_season[season]) return # Finished accumulating data, now convert from sum to average aice_tmp[season, :, :] /= season_days uxy_tmp[season, :, :] /= season_days vxy_tmp[season, :, :] /= season_days # Finished reading all CICE data id.close() # Wrap the periodic boundary aice = ma.empty( [size(aice_tmp, 0), size(aice_tmp, 1), size(aice_tmp, 2) + 1]) aice[:, :, :-1] = aice_tmp aice[:, :, -1] = aice_tmp[:, :, 0] u_xy = ma.empty([size(uxy_tmp, 0), size(uxy_tmp, 1), size(uxy_tmp, 2) + 1]) u_xy[:, :, :-1] = uxy_tmp u_xy[:, :, -1] = uxy_tmp[:, :, 0] v_xy = ma.empty([size(vxy_tmp, 0), size(vxy_tmp, 1), size(vxy_tmp, 2) + 1]) v_xy[:, :, :-1] = vxy_tmp v_xy[:, :, -1] = vxy_tmp[:, :, 0] # Rotate from local x-y space to lon-lat space u, v = rotate_vector_cice(u_xy, v_xy, angle) # Calculate speed speed = sqrt(u**2 + v**2) # Convert velocity to polar coordinates, rotate to account for longitude in # circumpolar projection, and convert back to vector components theta = arctan2(v, u) theta_circ = theta - lon * deg2rad u_circ = speed * cos(theta_circ) v_circ = speed * sin(theta_circ) # Calculate x and y coordinates for plotting circumpolar projection x = -(lat + 90) * cos(lon * deg2rad + pi / 2) y = (lat + 90) * sin(lon * deg2rad + pi / 2) # Average x, y, u_circ, and v_circ over block x block intervals # Calculate number of blocks size0 = int(ceil(size(x, 0) / float(block))) size1 = int(ceil((size(x, 1) - 1) / float(block))) # Set up arrays for averaged fields x_block = ma.empty([size0, size1]) y_block = ma.empty([size0, size1]) u_circ_block = ma.empty([4, size0, size1]) v_circ_block = ma.empty([4, size0, size1]) # Loop over seasons for season in range(4): # Set up arrays containing boundary indices posn0 = range(0, size(x, 0), block) posn0.append(size(x, 0)) posn1 = range(0, size(x, 1), block) posn1.append(size(x, 1)) # Double loop to average each block (can't find a more efficient way to # do this) for j in range(size0): for i in range(size1): start0 = posn0[j] end0 = posn0[j + 1] start1 = posn1[i] end1 = posn1[i + 1] if season == 0: # x_block and y_block are season-independent so just do them # for the first season x_block[j, i] = mean(x[start0:end0, start1:end1]) y_block[j, i] = mean(y[start0:end0, start1:end1]) u_circ_block[season, j, i] = mean(u_circ[season, start0:end0, start1:end1]) v_circ_block[season, j, i] = mean(v_circ[season, start0:end0, start1:end1]) # Set up colour levels for aice lev = linspace(0, 1, num=50) # Set boundaries for each side of plot bdry1 = -35 bdry2 = 35 bdry3 = -33 bdry4 = 37 # Make the plot fig = figure(figsize=(16, 12)) # Loop over seasons for season in range(4): ax = fig.add_subplot(2, 2, figure_order[season], aspect='equal') # Contour concentration img = contourf(x, y, aice[season, :, :], lev, cmap='jet') # Add velocity vectors q = quiver(x_block, y_block, u_circ_block[season, :, :], v_circ_block[season, :, :], color='black') # Configure plot xlim([bdry1, bdry2]) ylim([bdry3, bdry4]) axis('off') title(season_names[season], fontsize=24) if season == 0: # Add colourbar cbaxes = fig.add_axes([0.07, 0.6, 0.02, 0.3]) cbar = colorbar(img, ticks=arange(0, 1 + 0.25, 0.25), cax=cbaxes) cbar.ax.tick_params(labelsize=16) if season == 3: # Add 20 cm/s reference vector quiverkey(q, 0.07, 0.3, 0.2, '20 cm/s', coordinates='figure', fontproperties={'size': 16}) # Add main title suptitle('Sea ice concentration (1) and velocity (m/s)', fontsize=30) # Make plots closer together subplots_adjust(wspace=0.025, hspace=0.1) # Finished if save: fig.savefig(fig_name) else: fig.show()
def cice_vectorplot(file_path, tstep, xname, yname, cmax=None, save=False, fig_name=None): # Radius of the Earth in metres r = 6.371e6 # Degrees to radians conversion factor deg2rad = pi / 180 # Side length of blocks to average vectors over (can't plot vector at # every single point or the plot will be way too crowded) block = 15 # Read grid (including rotation angle) and vector components id = Dataset(file_path, 'r') lon_tmp = id.variables['ULON'][:-15, :] lat_tmp = id.variables['ULAT'][:-15, :] angle_tmp = id.variables['ANGLE'][:-15, :] u_xy_tmp = id.variables[xname][tstep - 1, :-15, :] v_xy_tmp = id.variables[yname][tstep - 1, :-15, :] id.close() # Wrap periodic boundary by 1 cell lon = ma.empty([size(lon_tmp, 0), size(lon_tmp, 1) + 1]) lat = ma.empty([size(lat_tmp, 0), size(lat_tmp, 1) + 1]) angle = ma.empty([size(angle_tmp, 0), size(angle_tmp, 1) + 1]) u_xy = ma.empty([size(u_xy_tmp, 0), size(u_xy_tmp, 1) + 1]) v_xy = ma.empty([size(v_xy_tmp, 0), size(v_xy_tmp, 1) + 1]) lon[:, :-1] = lon_tmp lon[:, -1] = lon_tmp[:, 0] lat[:, :-1] = lat_tmp lat[:, -1] = lat_tmp[:, 0] angle[:, :-1] = angle_tmp angle[:, -1] = angle_tmp[:, 0] u_xy[:, :-1] = u_xy_tmp u_xy[:, -1] = u_xy_tmp[:, 0] v_xy[:, :-1] = v_xy_tmp v_xy[:, -1] = v_xy_tmp[:, 0] # Rotate from local x-y space to lon-lat space u, v = rotate_vector_cice(u_xy, v_xy, angle) # Calculate magnitude of vector speed = sqrt(u**2 + v**2) # Convert vector to polar coordinates, rotate to account for longitude in # circumpolar projection, and convert back to vector components theta = arctan2(v, u) theta_circ = theta - lon * deg2rad u_circ = speed * cos(theta_circ) v_circ = speed * sin(theta_circ) # Calculate x and y coordinates for plotting circumpolar projection x = -(lat + 90) * cos(lon * deg2rad + pi / 2) y = (lat + 90) * sin(lon * deg2rad + pi / 2) # Average x, y, u_circ, and v_circ over block x block intervals # Calculate number of blocks size0 = int(ceil(size(x, 0) / float(block))) size1 = int(ceil((size(x, 1) - 1) / float(block))) # Set up arrays for averaged fields x_block = ma.empty([size0, size1]) y_block = ma.empty([size0, size1]) u_circ_block = ma.empty([size0, size1]) v_circ_block = ma.empty([size0, size1]) # Set up arrays containing boundary indices posn0 = range(0, size(x, 0), block) posn0.append(size(x, 0)) posn1 = range(0, size(x, 1), block) posn1.append(size(x, 1)) # Double loop to average each block (can't find a more efficient way to do # this) for j in range(size0): for i in range(size1): start0 = posn0[j] end0 = posn0[j + 1] start1 = posn1[i] end1 = posn1[i + 1] x_block[j, i] = mean(x[start0:end0, start1:end1]) y_block[j, i] = mean(y[start0:end0, start1:end1]) u_circ_block[j, i] = mean(u_circ[start0:end0, start1:end1]) v_circ_block[j, i] = mean(v_circ[start0:end0, start1:end1]) # Set up colour scale levels if cmax is None: lev = linspace(0, amax(speed), num=50) else: lev = linspace(0, cmax, num=50) # Make the plot fig = figure(figsize=(16, 12)) fig.add_subplot(1, 1, 1, aspect='equal') # Contour speed values at every point # Use pastel colour map so overlaid vectors will show up contourf(x, y, speed, lev, cmap='Paired', extend='both') cbar = colorbar() cbar.ax.tick_params(labelsize=20) # Add vectors for each block quiver(x_block, y_block, u_circ_block, v_circ_block, color='black') title(xname + ', ' + yname, fontsize=30) axis('off') if save: fig.savefig(fig_name) else: fig.show()