def section_integral(v, xmin, xmax): """ Section integral between x values """ minind, maxind = utils.get_indrange(v.x, xmin, xmax) if v.surface_field: dx = v.cell_widths_as_data[:, minind:maxind] return np.sum(v.data[:, minind:maxind] * dx, axis=1) else: da = v.cell_widths_as_data[:, :, minind:maxind] * v.dz_as_data[:, :, minind:maxind] return np.sum(v.data[:, :, minind:maxind] * da, axis=(1, 2))
def calc_ek(v, tau, minlon, maxlon, ek_level, profile='uniform'): """ Return ZonalSections containing Ekman velocities """ # Copy velocity data structure ek = copy.deepcopy(v) ek.data.data[:] = 0.0 # Get indices for gyre interior intmin, intmax = utils.get_indrange(tau.x, minlon, maxlon) # Calculate depth-integrated Ekman transports dx = tau.cell_widths_as_data[:, intmin:intmax] lats = tau.y[intmin:intmax] taux = tau.data[:, intmin:intmax] corf = 2 * ROT * np.sin(np.pi * (lats / 180.)) ek_trans = ((-1. * taux / (corf * RHO_REF)) * dx).sum(axis=1) # Calculate velocities over ekman layer ek_minind, ek_maxind = utils.get_indrange(v.z, 0, ek_level) dz = v.dz_as_data[0, ek_minind:ek_maxind, intmin:intmax] dx = v.cell_widths_as_data[0, ek_minind:ek_maxind, intmin:intmax] if profile == 'uniform': # Use uniform Ekman velocities ek_area = (dx * dz).sum() ek.data[:, ek_minind:ek_maxind, intmin:intmax] = ek_trans[:, np.newaxis, np.newaxis] / ek_area ek.data = np.ma.MaskedArray(ek.data, mask=v.mask) elif profile == 'linear': # Use Ekman transport profile that linearly reduces to zero at z=zek zprof = ek.z[ek_minind:ek_maxind] dzprof = ek.dz[ek_minind:ek_maxind] zmax = dzprof.sum() vek = get_linear_profiles(ek_trans, zprof, dzprof, zmax) / dx[np.newaxis].sum(axis=2) ek.data[:, ek_minind:ek_maxind, intmin:intmax] = vek[:, :, np.newaxis] ek.data = np.ma.MaskedArray(ek.data, mask=v.mask) else: raise ValueError('Unrecognized ekman profile type') return ek
def rapid_mass_balance(vgeo, ek, minlon, midlon, maxlon): """ Return vgeo after applying RAPID-style mass-balance constraint as a barotropic velocity over geostrophic interior """ # Calculate net transports fcwbw_tot = section_integral(vgeo, minlon, midlon) ek_tot = section_integral(ek, midlon, maxlon) int_tot = section_integral(vgeo, midlon, maxlon) net = int_tot + ek_tot + fcwbw_tot # Get cell dimensions in gyre interior minind, maxind = utils.get_indrange(vgeo.x, midlon, maxlon) dz = vgeo.dz_as_data[0] dx = vgeo.cell_widths_as_data[0] da = (dx[:, minind:maxind] * dz[:, minind:maxind]) # Correct geostrophic transports in gyre interior corr = net / da.sum() vgeo.data[:, :, minind:maxind] = (vgeo.data[:, :, minind:maxind] - corr[:, np.newaxis, np.newaxis]) return vgeo
def merge_vgeo_and_v(vgeo, v, minlon, maxlon): """ Return vgeo with velocities from v west of lonbnd """ minind, maxind = utils.get_indrange(vgeo.x, minlon, maxlon) vgeo.data[:, :, minind:maxind] = v.data[:, :, minind:maxind] return vgeo
def calc_transports_from_sections(config, v, tau, t_on_v, s_on_v): """ High-level routine to call transport calculations and return integrated transports on RAPID section as netcdf object """ # Extract sub-section boundaries fc_minlon = config.getfloat( 'options', 'fc_minlon') # Minimum longitude for Florida current fc_maxlon = config.getfloat( 'options', 'fc_maxlon') # Longitude of Florida current/WBW boundary wbw_maxlon = config.getfloat( 'options', 'wbw_maxlon') # Longitude of WBW/gyre boundary int_maxlon = config.getfloat( 'options', 'int_maxlon') # Maximum longitude of gyre interior # Get indices for sub-sections fcmin, fcmax = utils.get_indrange(v.x, fc_minlon, fc_maxlon) # Florida current wbwmin, wbwmax = utils.get_indrange(v.x, fc_maxlon, wbw_maxlon) # WBW intmin, intmax = utils.get_indrange(v.x, wbw_maxlon, int_maxlon) # Gyre interior # Calculate dynamic heights dh = calc_dh(t_on_v, s_on_v) # Calculate geostrophic transports georef = config.getfloat('options', 'georef_level') vgeo = calc_vgeo(v, dh, georef=georef) # Optionally reference geostrophic transports to model velocities if config.has_option('options', 'vref_level'): vref_level = config.getfloat('options', 'vref_level') vgeo = update_georef(vgeo, v, vref_level) # Calculate Ekman velocities ek_level = config.getfloat('options', 'ekman_depth') if config.has_option('options', 'ek_profile_type'): ek_profile = config.get('options', 'ek_profile_type') else: ek_profile = 'uniform' ek = calc_ek(v, tau, wbw_maxlon, int_maxlon, ek_level, profile=ek_profile) # Use model velocities in fc and WBW regions vgeo = merge_vgeo_and_v(vgeo, v, fc_minlon, wbw_maxlon) # Apply mass-balance constraints to section vgeo = rapid_mass_balance(vgeo, ek, fc_minlon, wbw_maxlon, int_maxlon) # Add ekman to geostrophic transports for combined rapid velocities vrapid = copy.deepcopy(vgeo) vrapid.data = vgeo.data + ek.data # Get volume and heat transports on each (sub-)section fc_trans = Transports(vgeo, t_on_v, fcmin, fcmax) # Florida current transports wbw_trans = Transports(vgeo, t_on_v, wbwmin, wbwmax) # Western-boundary wedge transports int_trans = Transports(vgeo, t_on_v, intmin, intmax) # Gyre interior transports ek_trans = Transports(ek, t_on_v, intmin, intmax) # Ekman transports model_trans = Transports( v, t_on_v, fcmin, intmax) # Total section transports using model velocities rapid_trans = Transports( vrapid, t_on_v, fcmin, intmax) # Total section transports using RAPID approximation # Create netcdf object for output/plotting trans = output.create_netcdf(config, rapid_trans, model_trans, fc_trans, wbw_trans, int_trans, ek_trans) return trans