def read_process_data (file_path, var_name, grid, mask_option='3d', gtype='t', lev_option=None, ismr=False, psi=False): data = read_netcdf(file_path, var_name) if mask_option == '3d': data = mask_3d(data, grid, gtype=gtype, time_dependent=True) elif mask_option == 'except_ice': data = mask_except_ice(data, grid, gtype=gtype, time_dependent=True) elif mask_option == 'land': data = mask_land(data, grid, gtype=gtype, time_dependent=True) elif mask_option == 'land_ice': data = mask_land_ice(data, grid, gtype=gtype, time_dependent=True) else: print 'Error (read_process_data): invalid mask_option ' + mask_option sys.exit() if lev_option is not None: if lev_option == 'top': data = select_top(data) elif lev_option == 'bottom': data = select_bottom(data) else: print 'Error (read_process_data): invalid lev_option ' + lev_option sys.exit() if ismr: data = convert_ismr(data) if psi: data = np.sum(data, axis=-3)*1e-6 return data
def plot_topo(var, grid, vmin=None, vmax=None, zoom_fris=False, xmin=None, xmax=None, ymin=None, ymax=None, fig_name=None): if not isinstance(grid, Grid): # This is the path to the NetCDF grid file, not a Grid object # Make a grid object from it grid = Grid(grid) if var == 'bathy': data = abs(mask_land(grid.bathy, grid)) title = 'Bathymetry (m)' elif var == 'zice': data = abs(mask_except_zice(grid.zice, grid)) title = 'Ice shelf draft (m)' elif var == 'wct': data = abs(mask_land(grid.wct, grid)) title = 'Water column thickness (m)' latlon_plot(data, grid, vmin=vmin, vmax=vmax, zoom_fris=zoom_fris, xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax, title=title, fig_name=fig_name)
def interp_grid(data, grid, gtype_in, gtype_out, time_dependent=False, mask=True, mask_shelf=False, mask_with_zeros=False, periodic=False): depth_dependent = is_depth_dependent(data, time_dependent=time_dependent) # Make sure we're not trying to mask the ice shelf from a depth-dependent field if mask_shelf and depth_dependent: print "Error (interp_grid): can't set mask_shelf=True for a depth-dependent field." sys.exit() if mask and gtype_in in ['u', 'v', 'psi', 'w']: # Fill the mask with zeros (okay because no-slip boundary condition) data_tmp = np.copy(data) data_tmp[data.mask] = 0.0 else: # Tracer land mask is the least restrictive, so it doesn't matter what the masked values are - they will definitely get re-masked at the end. data_tmp = data # Interpolate data_interp = np.empty(data_tmp.shape) if gtype_in == 'u' and gtype_out == 't': # Midpoints in the x direction data_interp[..., :-1] = 0.5 * (data_tmp[..., :-1] + data_tmp[..., 1:]) # Extend/wrap the easternmost column if periodic: data_interp[..., -1] = data_interp[..., 0] else: data_interp[..., -1] = data_tmp[..., -1] elif gtype_in == 'v' and gtype_out == 't': # Midpoints in the y direction data_interp[..., :-1, :] = 0.5 * (data_tmp[..., :-1, :] + data_tmp[..., 1:, :]) # Extend the northernmost row data_interp[..., -1, :] = data_tmp[..., -1, :] elif gtype_in == 't' and gtype_out == 'u': # Midpoints in the x direction data_interp[..., 1:] = 0.5 * (data_tmp[..., :-1] + data_tmp[..., 1:]) # Extend/wrap the westernmost column if periodic: data_interp[..., 0] = data_interp[..., -1] else: data_interp[..., 0] = data_tmp[..., 0] elif gtype_in == 't' and gtype_out == 'v': # Midpoints in the y direction data_interp[..., 1:, :] = 0.5 * (data_tmp[..., :-1, :] + data_tmp[..., :-1, :]) # Extend the southernmost row data_interp[..., 0, :] = data_tmp[..., 0, :] else: print 'Error (interp_grid): interpolation from the ' + gtype_in + '-grid to the ' + gtype_out + '-grid is not yet supported' sys.exit() if mask: # Now apply the mask if depth_dependent: data_interp = mask_3d(data_interp, grid, gtype=gtype_out, time_dependent=time_dependent) else: if mask_shelf: data_interp = mask_land_ice(data_interp, grid, gtype=gtype_out, time_dependent=time_dependent) else: data_interp = mask_land(data_interp, grid, gtype=gtype_out, time_dependent=time_dependent) if mask_with_zeros: # Remove mask and fill with zeros data_interp[data_interp.mask] = 0 data_interp = data_interp.data return data_interp
def balance_obcs (grid_path, option='balance', obcs_file_w_u=None, obcs_file_e_u=None, obcs_file_s_v=None, obcs_file_n_v=None, d_eta=None, d_t=None, max_deta_dt=0.5, prec=32): if option == 'correct' and (d_eta is None or d_t is None): print 'Error (balance_obcs): must set d_eta and d_t for option="correct"' sys.exit() print 'Building grid' grid = Grid(grid_path) # Calculate integrands of area, scaled by hFacC # Note that dx and dy are only available on western and southern edges of cells respectively; for the eastern and northern boundary, will just have to use 1 cell in. Not perfect, but this correction wouldn't perfectly conserve anyway. # Area of western face = dy*dz*hfac dA_w = xy_to_xyz(grid.dy_w, grid)*z_to_xyz(grid.dz, grid)*grid.hfac # Area of southern face = dx*dz*hfac dA_s = xy_to_xyz(grid.dx_s, grid)*z_to_xyz(grid.dz, grid)*grid.hfac # Now extract the area array at each boundary, and wrap up into a list for easy iteration later dA_bdry = [dA_w[:,:,0], dA_w[:,:,-1], dA_s[:,0,:], dA_s[:,-1,:]] # Some more lists: bdry_key = ['W', 'E', 'S', 'N'] files = [obcs_file_w_u, obcs_file_e_u, obcs_file_s_v, obcs_file_n_v] dimensions = ['yzt', 'yzt', 'xzt', 'xzt'] sign = [1, -1, 1, -1] # Multiply velocity variable by this to get incoming transport # Initialise number of timesteps num_time = None # Integrate the total area of ocean cells on boundaries total_area = 0 for i in range(len(files)): if files[i] is not None: print 'Calculating area of ' + bdry_key[i] + ' boundary' total_area += np.sum(dA_bdry[i]) # Calculate the net transport into the domain if option in ['balance', 'dampen']: # Transport based on OBCS normal velocities if option == 'balance': net_transport = 0 elif option == 'dampen': net_transport = None for i in range(len(files)): if files[i] is not None: print 'Processing ' + bdry_key[i] + ' boundary from ' + files[i] # Read data vel = read_binary(files[i], [grid.nx, grid.ny, grid.nz], dimensions[i], prec=prec) if num_time is None: # Find number of time indices num_time = vel.shape[0] elif num_time != vel.shape[0]: print 'Error (balance_obcs): inconsistent number of time indices between OBCS files' sys.exit() if option == 'dampen' and net_transport is None: # Initialise transport per month net_transport = np.zeros(num_time) if option == 'balance': # Time-average velocity (this is equivalent to calculating the transport at each month and then time-averaging at the end - it's all just sums) vel = np.mean(vel, axis=0) # Integrate net transport through this boundary into the domain, and add to global sum net_transport += np.sum(sign[i]*vel*dA_bdry[i]) elif option == 'dampen': # Integrate net transport at each month for t in range(num_time): net_transport[t] += np.sum(sign[i]*vel[t,:]*dA_bdry[i]) elif option == 'correct': # Transport based on simulated changes in sea surface height # Need area of sea surface dA_sfc = np.sum(grid.dA*np.invert(grid.land_mask).astype(float)) # Calculate transport in m^3/s net_transport = d_eta*dA_sfc/(d_t*sec_per_year) # Inner function to nicely print the net transport to the user def print_net_transport (transport): if transport < 0: direction = 'out of the domain' else: direction = 'into the domain' print 'Net transport is ' + str(abs(transport*1e-6)) + ' Sv ' + direction if option == 'dampen': for t in range(num_time): print 'Month ' + str(t+1) print_net_transport(net_transport[t]) else: print_net_transport(net_transport) if option == 'dampen': # Calculate the acceptable maximum absolute transport # First need total area of sea surface (including cavities) in domain surface_area = np.sum(mask_land(grid.dA, grid)) max_transport = max_deta_dt*surface_area/(sec_per_day*30) print 'Maximum allowable transport is ' + str(max_transport*1e-6) + ' Sv' if np.max(np.abs(net_transport)) <= max_transport: print 'OBCS satisfy this; nothing to do' return # Work out by what factor to dampen the transports scale_factor = max_transport/np.max(np.abs(net_transport)) print 'Will scale transports by ' + str(scale_factor) # Calculate corresponding velocity correction at each month correction = np.zeros(num_time) for t in range(num_time): correction[t] = (scale_factor-1)*net_transport[t]/total_area print 'Month ' + str(t+1) + ': will apply correction of ' + str(correction[t]) + ' m/s to normal velocity at each boundary' else: # Calculate single correction in m/s correction = -1*net_transport/total_area print 'Will apply correction of ' + str(correction) + ' m/s to normal velocity at each boundary' # Now apply the correction for i in range(len(files)): if files[i] is not None: print 'Correcting ' + files[i] # Read all the data again vel = read_binary(files[i], [grid.nx, grid.ny, grid.nz], dimensions[i], prec=prec) # Apply the correction if option == 'dampen': for t in range(num_time): vel[t,:] += sign[i]*correction[t] else: vel += sign[i]*correction # Overwrite the file write_binary(vel, files[i], prec=prec) if option in ['balance', 'dampen']: # Recalculate the transport to make sure it worked if option == 'balance': net_transport_new = 0 elif option == 'dampen': net_transport_new = np.zeros(num_time) for i in range(len(files)): if files[i] is not None: vel = read_binary(files[i], [grid.nx, grid.ny, grid.nz], dimensions[i], prec=prec) if option == 'balance': vel = np.mean(vel, axis=0) net_transport_new += np.sum(sign[i]*vel*dA_bdry[i]) elif option == 'dampen': for t in range(num_time): net_transport_new[t] += np.sum(sign[i]*vel[t,:]*dA_bdry[i]) if option == 'balance': print_net_transport(net_transport_new) elif option == 'dampen': for t in range(num_time): print 'Month ' + str(t+1) print_net_transport(net_transport_new[t])