def backrotate_phase(data): import gsw data['ang'] = np.arctan2(data.v_resid, data.u_resid) ref_time = pd.to_datetime('1/1/2016') timestamp = pd.to_datetime(data.time.values) Tf = 2 * np.pi / gsw.f(40) dt = (timestamp - ref_time) / pd.to_timedelta(1, unit='s') % Tf phase_add = (dt.values * gsw.f(40)).astype('float') phase_add[phase_add > np.pi] = phase_add[phase_add > np.pi] - 2 * np.pi data['ang_br'] = data['ang'] + phase_add data['ang_br'] = xr.where( data.ang_br > np.pi, (data['ang_br'].where(data.ang_br > np.pi) - np.pi), data['ang_br']) data['ang_br'] = xr.where( data.ang_br < -np.pi, 2 * np.pi + (data['ang_br'].where(data.ang_br > np.pi)), data['ang_br']) data['ang'] = np.degrees(data['ang']) data['ang_br'] = np.degrees(data['ang_br']) return data
def backrotate_phase(ang): ref_time = pd.to_datetime('28/12/1988') timestamp = pd.to_datetime(ang.time.values) Tf =2 * np.pi / gsw.f(40.7) dt = (timestamp - ref_time) / pd.to_timedelta(1, unit='s') % Tf phase_add = (dt.values * gsw.f(40.7)).astype('float') ang_br = ang + phase_add*180/np.pi return wrap(ang_br)
def bandpass_filter_variables(data, resample_period, order): ''' apply bandpass filter to multiple variables ''' sampling_period = np.int(resample_period.split('h')[0]) fs = 1 / (3600 * sampling_period) # sample rate, Hz f = gsw.f(40) # inertial frequency highcut = 1.2 * f lowcut = 0.8 * f # loop over depths bucket = [] for z in range(len(data.z)): dat = data.isel(z=z).dropna(dim='time') # temp = dat[var].fillna(0) if dat.count() > 27: filtered = butter_bandpass_filter(dat, lowcut, highcut, fs, order) bucket.append( xr.DataArray(filtered, coords=[dat.time], dims=['time'])) else: bucket.append( xr.DataArray(np.ones(dat.time.size) * np.nan, coords=[dat.time], dims=['time'])) ds = xr.concat(bucket, data.z) return ds
def latitude_correction(f, N): r""" Latitudinal correction term Parameters ---------- f : float Coriolis parameter N : float Buoyancy frequency Returns ------- L : float Latitudinal correction Notes ----- Calculates the latitudinal dependence term as described in Gregg et al. (2003) :cite:`Gregg2003`: .. math:: L(\theta, N) = \frac{f \cosh^{-1}(N/f)}{f_{30^{\circ}} \cosh^{-1}(N_0/f_{30^\circ})} with Coriolis parameter at 30° latitude :math:`f_{30^\circ}` and reference GM buoyancy frequency :math:`N_0=5.24\times10^{-3}\,\mathrm{s}^{-1}`. """ # Coriolis parameter at 30 degrees latitude f30 = gsw.f(30) # rad s-1 # GM model reference stratification: N0 = 5.24e-3 # rad s-1 f = np.abs(f) return f * np.arccosh(N / f) / (f30 * np.arccosh(N0 / f30))
def compute_iso_disp(raw): f = gsw.f(40.7)/(2*np.pi) raw = lowpass_variable(raw,'rho0', 0.6*f, 1.25*f) raw['rho_ref'] = raw.rho0LOW raw['sigma'] = raw.rho0 -1000 raw['sigma_ref'] = raw.rho_ref -1000 raw['sigma'] = xr.where(raw['sigma']<20, np.nan, raw['sigma']) raw['sigma_ref'] = xr.where(raw['sigma_ref']<20, np.nan, raw['sigma_ref']) new_min = raw.sigma.min() new_max = raw.sigma.max() #return raw sigma_coords = get_new_coodinates(raw,'sigma', new_min, new_max) sigma_ref_coords = get_new_coodinates(raw,'sigma_ref', new_min, new_max) ds_z = remap_variable(raw,'sigma',sigma_coords).rename({'remapped':'sigma'}) ds_z_ref = remap_variable(raw,'sigma_ref',sigma_ref_coords).rename({'remapped':'sigma'}) eta_sigma = (ds_z_ref-ds_z).transpose() # this is in sigma space z_values = xr.DataArray(raw.z.values, coords=[('z', raw.z.values)]) # define the new z grid z_coord = linear_interpolation_regrid(ds_z.sigma, ds_z.transpose(), z_values, target_value_dim='z') eta = linear_interpolation_remap(eta_sigma.sigma, eta_sigma, z_coord).transpose() raw['eta'] = eta.rename({'remapped':'z'}) #.transpose() if raw.z.max()>0: raw.coords['z'] = -raw.z return raw
def lowpass_filter_variables(data, resample_period, filter_period, order): ''' apply lowpass filter to multiple variables ''' sampling_period = np.int(resample_period.split('h')[0]) fs = 1 / (3600 * sampling_period) # sample rate, Hz f = gsw.f(40) # inertial frequency Tf = 2 * np.pi / f # inertial period # desired cutoff frequency of the filter, Hz cutoff = 1 / (Tf * filter_period) # loop over depths bucket = [] for z in range(len(data.z)): dat = data.isel(z=z).dropna(dim='time') # temp = dat[var].fillna(0) if dat.count() > 21: filtered = butter_lowpass_filter(dat, cutoff, fs, order) bucket.append( xr.DataArray(filtered, coords=[dat.time], dims=['time'])) else: bucket.append( xr.DataArray(np.ones(dat.time.size) * np.nan, coords=[dat.time], dims=['time'])) ds = xr.concat(bucket, data.z) return ds
def least_square_method(x0, y0, u0, v0, method): ''' Least square method to estimate velocity gradients ''' import numpy as np import scipy.linalg as la import gsw ncc = x0.size dlon = (x0 - np.nanmean(x0)) * 1000 dlat = (y0 - np.nanmean(y0)) * 1000 f = gsw.f(17) R = np.mat(np.vstack((np.ones((ncc, )), dlon, dlat)).T) u0 = np.mat(u0).T - np.nanmean(u0) v0 = np.mat(v0).T - np.nanmean(v0) if method is 'lstsq': A, _, _, _ = la.lstsq(R, u0) B, _, _, _ = la.lstsq(R, v0) elif method is 'inv': A = np.linalg.inv(R.T * R) * R.T * u0 B = np.linalg.inv(R.T * R) * R.T * v0 elif method is 'solve': A = np.linalg.solve(R, u0) B = np.linalg.solve(R, v0) vort = (B[1] - A[2]) / f strain = np.sqrt((A[1] - B[2])**2 + (B[1] + A[2])**2) / f div = (A[1] + B[2]) / f return vort, strain, div
def backrotate_rc(phase0): f= gsw.f(40.7) ref_time = pd.to_datetime('28/12/1988') timestamp = pd.to_datetime(phase0.time.values) dt = (timestamp - ref_time) / pd.to_timedelta(1, unit='s') backrotate = np.arctan2(np.sin(f*dt.values),np.cos(f*dt.values))*180/np.pi return wrap(phase0 + backrotate)
def backrotate(data, var): newvar = var + '_br' ref_time = pd.to_datetime('1/1/2016') timestamp = pd.to_datetime(data.time.values) Tf = 2 * np.pi / gsw.f(40) dt = (timestamp - ref_time) / pd.to_timedelta(1, unit='s') % Tf phase_add = (dt.values * gsw.f(40)).astype('float') phase_add[phase_add > np.pi] = phase_add[phase_add > np.pi] - 2 * np.pi data[newvar] = data[var] + phase_add data[newvar] = xr.where( data[newvar] > np.pi, (data[newvar].where(data[newvar] > np.pi) - np.pi), data[newvar]) data[newvar] = xr.where( data[newvar] < -np.pi, 2 * np.pi + (data[newvar].where(data[newvar] > np.pi)), data[newvar]) return data
def jAndDerivatives(N, f, Q, dQdz): g = 9.81 f = f Q = Q f0 = gsw.f(30) N0 = 5.2 * (10**-3) C0 = np.arccosh(N0 / f0) j = (f / (f0 * C0)) * np.arccosh(N / f) djdz = (f / (f0 * C0)) * ((((g * Q) / (f**3)) - 1)**(-1 / 2)) d2jdz2 = -(g / (2 * f0 * C0 * (f**2))) * dQdz * ((((g * Q) / (f**3)) - 1)**(-3 / 2)) return j, djdz, d2jdz2
def Burger(self, data, H, L): ''' Bu = NH/fL in which N is the buoyancy frequency, f is the Coriolis parameter H characteristic vertical length scale L characteristic horizontal length scale ''' sal, CT, pres, lat = data N2 = gsw.Nsquared(sal, CT, pres, lat=lat) f = gsw.f(lat) Bu = (np.sqrt(N2) * H) / (f * L) return Bu
def u_model(params, pfl, zlim, deg): phi_0, X, Z, phase_0 = params zmin, zmax = zlim nope = np.isnan(pfl.zef) | (pfl.zef < zmin) | (pfl.zef > zmax) t = 60*60*24*(pfl.UTCef - np.nanmin(pfl.UTCef)) x = 1000.*(pfl.dist_ef - np.nanmin(pfl.dist_ef)) k = 2*np.pi/X l = 0. m = 2*np.pi/Z f = gsw.f(pfl.lat_start) N = np.mean(np.sqrt(pfl.N2_ref[~nope])) om = gw.omega(N, k, m, l, f) u = gw.u(x, 0., pfl.zef, t, phi_0, k, l, m, om, phase_0=phase_0) return u[~nope] - utils.nan_detrend(pfl.zef[~nope], pfl.U[~nope], deg)
def w_model(params, pfl, zlim, deg): phi_0, X, Z, phase_0 = params zmin, zmax = zlim nope = np.isnan(pfl.z) | (pfl.z < zmin) | (pfl.z > zmax) t = 60*60*24*(pfl.UTC - np.nanmin(pfl.UTC)) x = 1000.*(pfl.dist_ctd - np.nanmin(pfl.dist_ctd)) k = 2*np.pi/X l = 0. m = 2*np.pi/Z f = gsw.f(pfl.lat_start) N = np.mean(np.sqrt(pfl.N2_ref[~nope])) om = gw.omega(N, k, m, l, f) w = gw.w(x, 0., pfl.z, t, phi_0, k, l, m, om, N, phase_0=phase_0) return w[~nope] - pfl.Ww[~nope]
def momentumFluxes(kh, m, N2,ladcp, z_ctd, bin_size=512, h0=1000): """ Calculating vertical and horizontal momentum fluxes of internal waves within safe range """ dshift = doppler_shifts(kh, ladcp) # Test whether K*U is between N and f f = (np.nanmean(gsw.f(lat))) dshiftTest = np.full_like(kh, np.nan) for i, dump in enumerate(kh.T): N2mean = np.nanmean(N2[:,i]) dshiftTest[:,i] = np.logical_and(dshift[:,i]**2 >= f**2, dshift[:,i]**2<= N2mean) dshiftTest2 = dshiftTest == 0 kh[dshiftTest2] = np.nan maxDepth = 4000 idx_ladcp = z[:,-1] <= maxDepth idx_ctd = z_ctd[:,-1] <= maxDepth z = z[idx_ladcp,:] U = U[idx_ladcp,:] V = V[idx_ladcp,:] rho = rho_neutral[idx_ctd,:] bins = oc.binData(U, z[:,0], bin_size) Umean = np.vstack([np.nanmean(U[binIn,:], axis=0) for binIn in bins]) Vmean = np.vstack([np.nanmean(V[binIn,:], axis=0) for binIn in bins]) Umag = np.sqrt(Umean**2 + Vmean**2) bins = oc.binData(rho, z_ctd[:,0], bin_size) rho0 = np.vstack([np.nanmean(rho[binIn,:], axis=0) for binIn in bins]) tau = .5*kh*rho0*Umag*np.sqrt((N2/Umag**2)-kh**2) np.savetxt('wave_momentum.csv', tau)
def calcFQ(surfaces, k, found, scales, kpvs, distances, debug=False): dqdx = fetchWithFallback(surfaces, k, "dqdx", found) dqdy = fetchWithFallback(surfaces, k, "dqdy", found) dqdz = fetchWithFallback(surfaces, k, "dqdz", found) d2qdz2 = fetchWithFallback(surfaces, k, "d2qdz2", found) d2qdx2 = fetchWithFallback(surfaces, k, "d2qdx2", found) d2qdy2 = fetchWithFallback(surfaces, k, "d2qdy2", found) dqnotdx = fetchWithFallback(surfaces, k, "dqnotdx", found) dqnotdy = fetchWithFallback(surfaces, k, "dqnotdy", found) ddiffkrdz = fetchWithFallback(surfaces, k, "ddiffkrdz", found) d2diffkrdz2 = fetchWithFallback(surfaces, k, "d2diffkrdz2", found) khpdz = fetchWithFallback(surfaces, k, "khpdz", found) f = gsw.f(surfaces[k]["lats"][found]) pv = fetchWithFallback(surfaces, k, "pv", found) missingpiece = -(2 * (dqnotdx * dqdx + dqnotdy * dqdy) / pv) * scales["kh"] if debug: print("#" * 5) print("diffkr: ", surfaces[k]["data"]["diffkr"][found]) print("q: ", pv) print("Qkvterm part 1: ", surfaces[k]["data"]["diffkr"][found] * d2qdz2) print("Qkvterm part 2: ", 2 * ddiffkrdz * dqdz) print("Qkvterm part 3: ", d2diffkrdz2 * pv) print("Qkhterm part 1 : ", f * khpdz) print("Qkhterm part 2 : ", surfaces[k]["data"]["kapgm"][found] * (missingpiece)) print("kapgm: ", surfaces[k]["data"]["kapgm"][found]) print("kapredi: ", surfaces[k]["data"]["kapredi"][found]) FQ = surfaces[k]["data"]["diffkr"][found]*d2qdz2 +\ 2*ddiffkrdz*dqdz+ d2diffkrdz2*pv + \ f*khpdz+\ surfaces[k]["data"]["kapgm"][found]*(missingpiece) +\ surfaces[k]["data"]["kapredi"][found]*(d2qdx2+d2qdy2) if np.isnan(FQ): FQ = 0 surfaces[k]["data"]["FQ"][found] = FQ return FQ
# interpolate flow velocities from u,v measurements ui = griddata((x[mask], y[mask]), dict['u'][plev_adcp, 120:-120][mask], (xxi, yyi), method='linear') vi = griddata((x[mask], y[mask]), dict['v'][plev_adcp, 120:-120][mask], (xxi, yyi), method='linear') # relative vorticity dvdx = np.gradient(vi)[1] / np.gradient(xxi)[1] dudy = np.gradient(ui)[0] / np.gradient(yyi)[0] zeta = dvdx - dudy # planetary/ absolute vorticity fcor = f(lti) eta = fcor + zeta # continuity/ horizontally divergence free dudx = np.gradient(ui)[0] / np.gradient(xxi)[1] dvdy = np.gradient(vi)[1] / np.gradient(yyi)[0] divH = dudx + dvdy ### PLOT def subp_figure(x, y, zz, x2=None, y2=None,
def nepbCTDExtractInterpSurfaces(fname,calcDeriv = False): ctddata = sio.loadmat(fname) ##note: any x,y gradients wont be equivalent because of grid, ## advisable to look at total gradient probably quantmap = {"CT_s":"t","S_s":"s","dQdz_s":"dqdz","dSdz_s":"dsdz",\ "d2CTdS2_s":"d2thetads2","alpha_s":"alpha","beta_s":"beta",\ "P_s":"pres","Q_s":"pv","d2Qdx2_s":"d2qdx2","d2Qdy2_s":"d2qdy2",\ "d2Qdz2_s":"d2qdz2","d2Sdx2_s":"d2sdx2","d2Sdy2_s":"d2sdy2",\ "aT":"dalphadtheta","aP":"dalphadp","A_s":"psi",\ "lat_field":"khp","dCTdS_s":"dthetads","Sx":"dsdx",\ "Sy":"dsdy","CTx":"dtdx","CTy":"dtdy","dCTdz_s":"dtdz",\ "Qx":"dqdx","Qy":"dqdy","Hvar":"bathvar",\ "Q0x":"dqnotdx","Q0y":"dqnotdy"} latlist = range(20,60,2) lonlist = list(range(170,180,2)) lonlist = lonlist+list(range(-180,-120,2)) ns = np.asarray(ctddata["P_gref"]) surfaces = {} for field in ctddata.keys(): if field in list(quantmap.keys())+["F_Ssmooth",\ "F_CTsmooth","F_Qsmooth","F_N2smooth"]: if ctddata[field].shape == (20, 35, 30): ctddata[field] = np.transpose(ctddata[field],(2,0,1)) if ctddata[field].shape == (41, 71, 30): ctddata[field] = np.transpose(ctddata[field],(2,0,1)) for k in Bar("surface").iter(range(len(ns))): tempSurf = nstools.emptySurface() for j in quantmap.values(): tempSurf["data"][j]=[] tempSurf["data"]["dsdx"]=[] tempSurf["data"]["dsdy"]=[] tempSurf["data"]["dtdx"]=[] tempSurf["data"]["dtdy"]=[] tempSurf["data"]["dqdx"]=[] tempSurf["data"]["dqdy"]=[] tempSurf["data"]["dqnotdx"]=[] tempSurf["data"]["dqnotdy"]=[] tempSurf["data"]["bathvar"]=[] for j in range(len(latlist)): for l in range(len(lonlist)): tempSurf["lats"].append(latlist[j]) tempSurf["lons"].append(lonlist[l]) tempSurf["ids"].append(j*30+l) for field in ctddata.keys(): if field in quantmap.keys(): if field == "Hvar": tempSurf["data"]["bathvar"].append(ctddata[field][j*2][l*2]) else: if calcDeriv and (field not in ["CTx","CTy",\ "Sx","Sy","Qx","Qy","Q0x","Q0y"]): tempSurf["data"][quantmap[field]].append(ctddata[field][k][j][l]) elif not calcDeriv: tempSurf["data"][quantmap[field]].append(ctddata[field][k][j][l]) if calcDeriv and field == "F_Ssmooth": if j == len(latlist)-1 or l == len(lonlist)-1: ydist = ((latlist[j] - latlist[j-1])/2.0)*111.0*1000.0 xdist = ydist *np.cos(np.deg2rad(latlist[j]+1)) else: ydist = ((latlist[j+1] - latlist[j])/2.0)*111.0*1000.0 xdist =ydist * np.cos(np.deg2rad(latlist[j]+1)) dx = ctddata[field][k][j*2][l*2+1] - ctddata[field][k][j*2][l*2] dx += ctddata[field][k][j*2+1][l*2+1] - ctddata[field][k][j*2+1][l*2] dy = ctddata[field][k][j*2+1][l*2] - ctddata[field][k][j*2][l*2] dy += ctddata[field][k][j*2+1][l*2+1] - ctddata[field][k][j*2][l*2+1] tempSurf["data"]["dsdx"].append(dx/(2*xdist)) tempSurf["data"]["dsdy"].append(dy/(2*ydist)) if calcDeriv and field == "F_CTsmooth": if j == len(latlist)-1 or l == len(lonlist)-1: ydist = ((latlist[j] - latlist[j-1])/2.0)*111.0*1000.0 xdist = ydist *np.cos(np.deg2rad(latlist[j]+1)) else: ydist = ((latlist[j+1] - latlist[j])/2.0)*111.0*1000.0 xdist =ydist * np.cos(np.deg2rad(latlist[j]+1)) dx = ctddata[field][k][j*2][l*2+1] - ctddata[field][k][j*2][l*2] dx += ctddata[field][k][j*2+1][l*2+1] - ctddata[field][k][j*2+1][l*2] dy = ctddata[field][k][j*2+1][l*2] - ctddata[field][k][j*2][l*2] dy += ctddata[field][k][j*2+1][l*2+1] - ctddata[field][k][j*2][l*2+1] tempSurf["data"]["dtdx"].append(dx/(2*xdist)) tempSurf["data"]["dtdy"].append(dy/(2*ydist)) if calcDeriv and field == "F_Qsmooth": if j == len(latlist)-1 or l == len(lonlist)-1: ydist = ((latlist[j] - latlist[j-1])/2.0)*111.0*1000.0 xdist = ydist *np.cos(np.deg2rad(latlist[j]+1)) else: ydist = ((latlist[j+1] - latlist[j])/2.0)*111.0*1000.0 xdist =ydist * np.cos(np.deg2rad(latlist[j]+1)) dx = ctddata[field][k][j*2][l*2+1] - ctddata[field][k][j*2][l*2] dx += ctddata[field][k][j*2+1][l*2+1] - ctddata[field][k][j*2+1][l*2] dy = ctddata[field][k][j*2+1][l*2] - ctddata[field][k][j*2][l*2] dy += ctddata[field][k][j*2+1][l*2+1] - ctddata[field][k][j*2][l*2+1] tempSurf["data"]["dqdx"].append(dx/(2*xdist)) tempSurf["data"]["dqdy"].append(dy/(2*ydist)) if calcDeriv and field == "F_N2smooth": if j == len(latlist)-1 or l == len(lonlist)-1: ydist = ((latlist[j] - latlist[j-1])/2.0)*111.0*1000.0 xdist = ydist *np.cos(np.deg2rad(latlist[j]+1)) else: ydist = ((latlist[j+1] - latlist[j])/2.0)*111.0*1000.0 xdist =ydist * np.cos(np.deg2rad(latlist[j]+1)) dx = ctddata[field][k][j*2][l*2+1] - ctddata[field][k][j*2][l*2] dx += ctddata[field][k][j*2+1][l*2+1] - ctddata[field][k][j*2+1][l*2] dy = ctddata[field][k][j*2+1][l*2] - ctddata[field][k][j*2][l*2] dy += ctddata[field][k][j*2+1][l*2+1] - ctddata[field][k][j*2][l*2+1] tempSurf["data"]["dqnotdx"].append(gsw.f(latlist[j])*dx/(9.81*2*xdist)) tempSurf["data"]["dqnotdy"].append(gsw.f(latlist[j])*dy/(9.81*2*ydist)) surfaces[ns[k][0]] = tempSurf return surfaces
def run(self, tstep=30, duration=5, lonpad=1.5, latpad=1.5, tpad=7, direction='forward', bottom=3000, rho0=1030, clearance=.5, shear=-.001, fname='ray_trace.csv', strain=True, stops=True, vertspeed=True, time_constant=False, save_data=False, progress_bar=False): """ INSTRUCTIONS: TSTEP: TIMESTEP IN SECONDS (DEFAULT 30 SECONDS) DURATION: DURATION (IN DAYS) - DEFAULT 5 IGNORE LONPAD AND LATPAD (DIDNT CHANGE FROM OLDER VERSION) DIRECTION: "forward" and "reverse" (SETS INTEGRATION TIME DIRECTION) BOTTOM: can set default bottom instead of using bathymetry file Setup for midpoint method integration: 1. Get field values 2. Get Cg @ t_n, X_n 3. Get field values at t_n+dt/2, X_n + (dt/2)(Cg_n) 4. Calculate Cg @(t_n+dt/2, X_n + (dt/2)(Cg_n)) 5. X_(n+1) = X_n + [dt * Cg @(t_n+dt/2, X_n + (dt/2)(Cg_n))] """ if direction == 'forward': # convert duration in hours to seconds T = np.arange(0, duration * 60 * 60, tstep) else: T = np.arange(0, -duration * 60 * 60, -tstep) tstep = -tstep Xall = [] Kall = [] amplitudes = [] energy = [] # names of all the columns in results frame names = ('Lon', 'Lat', 'depth', 'distance', 'bottom_depth', 'k', 'l', 'm', 'omega', 'N2', 'U', 'V', 'dudx', 'dvdx', 'dndx', 'dudy', 'dvdy', 'dndy', 'dudz', 'dvdz', 'dndz', 'cgx', 'cgy', 'cgz', 'x', 'y', 'z', 'u0', 'v0', 'w0', 'u', 'v', 'w', 'b', 'energy', 'u_momentum', 'v_momentum', 'horiz_momentum', 'time') cg = [] steps = [] localfield = [] X = self.X0[:] lon0 = X[0] lat0 = X[1] K = self.K0[:] t0 = self.t0 allbottom = [] if progress_bar: pbar = FloatProgress(min=0, max=T.shape[0]) pbar.value display(pbar) if not hasattr(self.F, 'dudx'): lonlim, latlim, tlim = self.F.createFuncs(X, lonpad, latpad, tpad) for ii, t1 in enumerate(T): # Get field values if progress_bar: pbar.value = float(ii) t = t0 + t1 / (24 * 60 * 60) if X[2] > 6000: zi1 = 2499 else: zi1 = X[2] f = gsw.f(X[1]) if time_constant: t = np.copy(t0) xi = (X[0], X[1], zi1, t) field = self.F.getfield(xi) f = gsw.f(X[1]) # Step 1 dy1 = self._cgy(field[0], K[3], K, field[2], f) * tstep / 2 dz1 = self._cgz(field[0], K[3], K, f) * tstep / 2 dx1 = self._cgx(field[0], K[3], K, field[1], f) * tstep / 2 # midpoint position lon2, lat2 = inverse_hav(dx1, dy1, X[0], X[1]) if X[2] + dz1 > 6000: zi = 2499 else: zi = X[2] + dz1 xi2 = (lon2, lat2, zi, t + tstep / (24 * 60 * 60 * 2)) if time_constant: xi2 = (lon2, lat2, zi, t) field1 = self.F.getfield(xi2) f2 = gsw.f(lat2) # Update Wave properties at midpoint (midpoint refraction) dK = self._dKdt(field1, K, xi, xi2, tstep / 2) if not np.all(np.isfinite(dK)): K1 = K[:] else: if strain: K1 = [ K[0] + dK[0], K[1] + dK[1], K[2] + dK[2], K[3] + dK[3] ] else: K1 = [ K[0], K[1], K[2] + (tstep / 2) * (-(shear) * (K[0] + K[1])), K[3] + dK[3] ] # Step2 dx2 = self._cgx(field1[0], K1[3], K1, field1[1], f2) * tstep dy2 = self._cgy(field1[0], K1[3], K1, field1[2], f2) * tstep dz2 = self._cgz(field1[0], K1[3], K1, f2) * tstep lon3, lat3 = inverse_hav(dx2, dy2, X[0], X[1]) lonr = np.expand_dims(np.array([lon0, lon3]), axis=1) latr = np.expand_dims(np.array([lat0, lat3]), axis=1) distance = gsw.distance(lonr, latr, axis=0) if X[2] + dz2 > 6000: zi = 2499 bathypad = np.linspace(-.01, .01, num=5) loncheck = bathypad + X[0] latcheck = bathypad + X[1] loncheck, latcheck = np.meshgrid(loncheck, latcheck) tester = np.array([loncheck.flatten(), latcheck.flatten()]) bottom = np.nanmax([-self.F.bathy((p1[0], p1[1])) \ for p1 in tester.T]) # bottom = -self.F.bathy((X[0], X[1])) X1 = [lon3, lat3, X[2] + dz2, distance, bottom] steps.append([dx2, dy2, -dz2]) cg.append([dx2 / tstep, dy2 / tstep, -dz2 / tstep]) localfield.append(field) Kall.append(K1) K = K1 Xall.append(X1) X = X1 dist_so_far = np.cumsum(steps, axis=0) # print(dK[3]) # print(K[3]**2) k = np.copy(K1[0]) l = np.copy(K1[1]) m = np.copy(K1[2]) omega = np.copy(K1[3]) f = gsw.f(lat3) w0 = (self.p0 * (-m * omega) / (field[0] - omega**2)) # Perturbation amplitudes u0 = (self.p0 * (k * omega + l * f * 1j) / (omega**2 - f**2)) v0 = (self.p0 * (l * omega - k * f * 1j) / (omega**2 - f**2)) b0 = (self.p0 * (-1j * m * field[0]) / (field[0] - omega**2)) # total distance so far xx = np.copy(dist_so_far[ii, 0]) yy = np.copy(dist_so_far[ii, 1]) zz = np.copy(dist_so_far[ii, 2]) phase = k * xx + l * yy \ + m * zz - omega * t1 # INtegration Limits period = np.abs(2 * np.pi / omega) t11 = t1 - period / 2 t22 = t1 + period / 2 # mean value theorem to get average over one wave period u2 = .5 * np.real(w0)**2 v2 = .5 * np.real(v0)**2 w2 = .5 * np.real(w0)**2 b2 = .5 * np.real(b0)**2 u = (quad(self._planewave, t11, t22, args=(u0, xx, yy, zz, k, l, m, omega))[0]) v = (quad(self._planewave, t11, t22, args=(v0, xx, yy, zz, k, l, m, omega))[0]) w = (quad(self._planewave, t11, t22, args=(w0, xx, yy, zz, k, l, m, omega))[0]) b = (quad(self._planewave, t11, t22, args=(b0, xx, yy, zz, k, l, m, omega))[0]) amplitudes.append([u0, v0, w0, u, v, w, b]) # Calculate U and V momentum Umom = rho0 * (u * w) / period Vmom = rho0 * (v * w) / period # Calculate momentum flux mFlux = np.sqrt(((u * w) / period)**2 + ((v * w) / period)**2) # b = -(field[0] /omega / 9.8) * rho0 * w0 * np.sin(phase) # Internal wave energy E = .5 * rho0 * (u2 + v2 + w2) \ + .5 *rho0* b2 * np.sqrt(field[0])**-2 # E =E/rho0 energy.append([E, Umom, Vmom, mFlux]) if stops: # check if vertical speed goes to zero if vertspeed: if np.abs(dz2 / tstep) < 1e-4: print( 'Vertical Group speed = zero {} meters from bottom' .format(bottom - X[2])) break if np.abs(E) > 1000: # this checks if energy has gone to some unrealistic asymptote like behavior print('ENERGY ERROR') break if ii > 3: if np.abs(E - energy[ii - 2][0]) > .8 * E: print('Non Linear') break # data Boundary checks if not self.lonlim[0] <= X[0] <= self.lonlim[1]: print('lon out of bounds') break if not self.latlim[0] <= X[1] <= self.latlim[1]: print('lat out of bounds') break if not self.tlim[0] <= t <= self.tlim[1]: print('time out of bounds') print(t) print(self.tlim) break # Check if near the bottom or surface if X[2] + clearance * np.abs((2 * np.pi) / K1[2]) >= bottom: print('Hit Bottom - {} meters from bottom'.format(bottom - X[2])) break if X[2] <= 0: print('Hit Surface') break # Check if frequency gets too high if K1[3]**2 >= self.F.N2(xi2): print('frequency above Bouyancy frequency') # print(K[3]**2) # print(self.F.N2(xi2)) break if not np.isfinite(X1[0]): print('X Update Error') break if np.abs(u0) < 0.0001: print('U amplitude zero') break if np.abs(v0) < 0.0001: print('v amplitude zero') break if np.abs(w0) < 0.0001: print('w amplitude zero') break if not np.isfinite(dx1): print('Field Error') break # Save data in pandas data data = pd.DataFrame(np.concatenate( (np.real(np.stack(Xall)), np.real(np.stack(Kall)), np.real(np.stack(localfield)), np.real( np.stack(cg)), np.real(np.stack(np.cumsum(steps, axis=0))), np.real(np.stack(amplitudes)), np.real(np.stack(energy)), np.real(np.expand_dims(T[:ii + 1], axis=1))), axis=1), columns=names) if save_data: data.to_csv(fname) return data
fig = plt.figure(figsize=(6.5, 3)) gs = gridspec.GridSpec(1, 5, width_ratios=[3, 1, 1, 1, 1]) gs.update(wspace=0.9) axs = [plt.subplot(gs[0]), plt.subplot(gs[1]), plt.subplot(gs[2]), plt.subplot(gs[3]), plt.subplot(gs[4])] #for ax in axs[1:]: # ax.yaxis.tick_right() # ax.yaxis.set_ticks_position('both') # ax.yaxis.set_label_position('right') colors = ['blue', 'green', 'grey'] N = 2.2e-3 f = gsw.f(-57.5) for i, M in enumerate(Ms): phi_0 = M.trace('phi_0')[:] k = np.pi*2./M.trace('X')[:] l = np.pi*2./M.trace('Y')[:] m = np.pi*2./M.trace('Z')[:] print("complete wavelength = {}".format(np.mean(np.pi*2/np.sqrt(k**2 + l**2 + m**2)))) om = gw.omega(N, k, m, l, f) print("om/N = {}".format(np.mean(om/N))) w_0 = gw.W_0(phi_0, m, om, N) Edens = gw.Edens(w_0, k, m, l) Efluxz = gw.Efluxz(w_0, k, m, N, l, f) Mfluxz = gw.Mfluxz(phi_0, k, l, m, om, N)
ds # %% IDs = np.unique(ds.ID.values[np.isfinite(ds.ID.values)]) fig, axs = plt.subplots(1, 2) axs[0].plot(IDs, '.') axs[1].hist(IDs[IDs > 5e7]) print(IDs[IDs > 5e7][::20]) # %% ds_ = ds.isel(TIME=ds.BID == 64734970.) ds_ # %% fcor = gsw.f(ds_.LAT.min()) # minimum coriolis Tcor = 2 * np.pi / np.abs(fcor) Ts = 60. * 60. # Sampling period Tlpf = 1.1 * Tcor # Low pass period ul = utils.butter_filter(ds_.U, 1 / Tlpf, 1 / Ts) vl = utils.butter_filter(ds_.V, 1 / Tlpf, 1 / Ts) fig, ax = plt.subplots(1, 1) ax.plot(ds_.LON, ds_.LAT) ax.plot(ds_.LON[0], ds_.LAT[0], "go") ax.plot(ds_.LON[-1], ds_.LAT[-1], "ro") fig, axs = plt.subplots(3, 1, sharex=True, figsize=(6, 9)) axs[0].plot(ds_.TIME, ds_.U) axs[0].plot(ds_.TIME, ul)
def wave_components_with_strain(ctd, ladcp, strain, rho0=default_params['rho0'], ctd_bin_size=1024, ladcp_bin_size=1024, wl_min=300, wl_max=1000, nfft=default_params['nfft'], plots=default_params['plots'], save_data=False): """ Calculating Internal Wave Energy Internal wave energy calcuations following methods in waterman et al 2012. """ # Load Hydrographic Data g = 9.8 U, V, p_ladcp = oc.loadLADCP(ladcp) S, T, p_ctd, lat, lon = oc.loadCTD(ctd) SA = gsw.SA_from_SP(S, p_ctd, lon, lat) CT = gsw.CT_from_t(SA, T, p_ctd) N2, dump = gsw.stability.Nsquared(SA, CT, p_ctd, lat) maxDepth = 4000 idx_ladcp = p_ladcp[:, -1] <= maxDepth idx_ctd = p_ctd[:, -1] <= maxDepth strain = strain[idx_ctd, :] S = S[idx_ctd,:] T = T[idx_ctd,:] p_ctd = p_ctd[idx_ctd, :] U = U[idx_ladcp, :] V = V[idx_ladcp, :] p_ladcp = p_ladcp[idx_ladcp, :] rho = oc.rhoFromCTD(S, T, p_ctd, lon, lat) # Bin CTD data ctd_bins = oc.binData(S, p_ctd[:, 0], ctd_bin_size) # Bin Ladcp Data ladcp_bins = oc.binData(U, p_ladcp[:, 0], ladcp_bin_size) # Depth and lat/long grids depths = np.vstack([np.nanmean(p_ctd[binIn]) for binIn in ctd_bins]) dist = gsw.distance(lon, lat) dist = np.cumsum(dist)/1000 dist = np.append(0,dist) # Calculate Potential Energy z = -1*gsw.z_from_p(p_ctd, lat) PE, PE_grid, eta_psd, N2mean, pe_peaks = PE_strain(N2, z, strain, wl_min, wl_max, ctd_bins, nfft=2048) # Calculate Kinetic Energy z = -1*gsw.z_from_p(p_ladcp, lat) KE, KE_grid, KE_psd, Uprime, Vprime, ke_peaks = KE_UV(U, V, z, ladcp_bins, wl_min, wl_max, lc=wl_min-50, nfft=2048, detrend='constant') # Total Kinetic Energy Etotal = 1027*(KE + PE) # Multiply by density to get Joules # wave components f = np.nanmean(gsw.f(lat)) # version 2 omega calculation omega = f*np.sqrt((KE+PE)/(KE-PE)) # version 2 omega calculation omega2 = np.abs((f**2)*((KE+PE)/(KE-PE))) rw = KE/PE w0 = ((f**2)*((rw+1)/(rw-1))) # m = (2*np.pi)/np.mean((wl_min, wl_max)) m = np.nanmean(ke_peaks, axis=1) m = ke_peaks[:,0] m = m.reshape(omega.shape) m = (2*np.pi)*m # version 1 kh calculation khi = m*np.sqrt(((f**2 - omega**2)/(omega**2 - N2mean))) # version 2 kh calculation kh = (m/np.sqrt(N2mean))*(np.sqrt(omega2 - f**2)) mask = khi == 0 khi[mask]= np.nan lambdaH = 1e-3*(2*np.pi)/khi # Get coherence of u'b' and v'b' and use to estimate horizontal wavenumber # components. This uses the previously binned data but now regrids velocity # onto the density grid so there are the same number of grid points b = (-g*rho)/rho0 b_poly = [] z = -1*gsw.z_from_p(p_ctd, lat) fs = 1/np.nanmean(np.diff(z, axis=0)) for cast in b.T: fitrev = oc.vert_polyFit(cast, z[:, 0], 100, deg=1) b_poly.append(fitrev) b_poly = np.vstack(b_poly).T b_prime = b - b_poly dz = 1/fs # This is the vertical spacing between measurements in metres. lc = wl_min-50 # This is the cut off vertical scale in metres, the filter will remove variability smaller than this. mc = 1./lc # Cut off wavenumber. normal_cutoff = mc*dz*2. # Nyquist frequency is half 1/dz. a1, a2 = sig.butter(4, normal_cutoff, btype='lowpass') # This specifies you use a lowpass butterworth filter of order 4, you can use something else if you want for i in range(b_prime.shape[1]): mask = ~np.isnan(b_prime[:,i]) b_prime[mask,i] = sig.filtfilt(a1, a2, b_prime[mask,i]) ub = [] vb = [] for i in range(ctd_bins.shape[0]): Uf = interpolate.interp1d(p_ladcp[ladcp_bins[i,:]].squeeze(), Uprime[ladcp_bins[i, :], :], axis=0, fill_value='extrapolate') Vf = interpolate.interp1d(p_ladcp[ladcp_bins[i,:]].squeeze(), Vprime[ladcp_bins[i, :], :], axis=0, fill_value='extrapolate') new_z = p_ctd[ctd_bins[i,:],0] u_f, ub_i = sig.coherence(b_prime[ctd_bins[i,:],:], Uf(new_z), nfft=nfft, fs=fs, axis=0) v_f, vb_i = sig.coherence(b_prime[ctd_bins[i,:],:], Vf(new_z), nfft=nfft, fs=fs, axis=0) ub.append(ub_i) vb.append(vb_i) ub = np.hstack(ub).T vb = np.hstack(vb).T # Random plots (only run if youre feeling brave) m_plot = np.array([(2*np.pi)/wl_max, (2*np.pi)/wl_max, (2*np.pi)/wl_min, (2*np.pi)/wl_min]) if plots: plt.figure(figsize=[12,6]) plt.subplot(121) plt.loglog(KE_grid, KE_psd.T, linewidth=.6, c='b', alpha=.1) plt.loglog(KE_grid, np.nanmean(KE_psd, axis=0).T, lw=1.5, c='k') ylims = plt.gca().get_ylim() ylim1 = np.array([ylims[0], ylims[1]]) plt.plot(m_plot[2:], ylim1, lw=1, c='k', alpha=.5, linestyle='dotted') plt.plot(m_plot[:2], ylim1, lw=1, c='k', alpha=.5, linestyle='dotted') plt.ylim(ylims) plt.ylabel('Kinetic Energy Density') plt.xlabel('Vertical Wavenumber') plt.gca().grid(True, which="both", color='k', linestyle='dotted', linewidth=.2) plt.subplot(122) plt.loglog(PE_grid, .5*np.nanmean(N2)*eta_psd.T, lw=.6, c='b', alpha=.1) plt.loglog(KE_grid, .5*np.nanmean(N2)*np.nanmean(eta_psd, axis=0).T, lw=1.5, c='k') plt.plot(m_plot[2:], ylim1, lw=1, c='k', alpha=.5, linestyle='dotted') plt.plot(m_plot[:2], ylim1, lw=1, c='k', alpha=.5, linestyle='dotted') plt.ylim(ylims) plt.gca().grid(True, which="both", color='k', linestyle='dotted', linewidth=.2) plt.ylabel('Potential Energy Density') plt.xlabel('Vertical Wavenumber') plt.figure() Kemax = np.nanmax(KE_psd, axis=1) kespots = np.nanargmax(KE_psd, axis=1) ax = plt.gca() ax.scatter(KE_grid[kespots],Kemax , c='blue', alpha=0.3, edgecolors='none') ax.set_yscale('log') ax.set_xscale('log') plt.figure(figsize=[12,6]) plt.subplot(121) plt.semilogx(u_f, ub.T, linewidth=.5, alpha=.5) plt.gca().grid(True, which="both", color='k', linestyle='dotted', linewidth=.2) plt.subplot(122) plt.semilogx(v_f, vb.T, linewidth=.5) plt.gca().grid(True, which="both", color='k', linestyle='dotted', linewidth=.2) # plt.xlim([10**(-2.5), 10**(-2)]) plt.figure() ub_max = np.nanmax(ub, axis=1) kespots = np.argmax(ub, axis=1) ax = plt.gca() ax.scatter(u_f[kespots],ub_max , c='blue', alpha=0.3, edgecolors='none') ax.set_xscale('log') ax.set_xlim([1e-3, 1e-5]) Kemax = np.nanmax(.5*np.nanmean(N2)*eta_psd.T, axis=1) kespots = np.nanargmax(.5*np.nanmean(N2)*eta_psd.T, axis=1) ax = plt.gca() ax.scatter(PE_grid[kespots],Kemax , c='red', alpha=0.3, edgecolors='none') ax.set_yscale('log') ax.set_xscale('log') # Peaks lots plt.figure() mask = np.isfinite(Etotal) Etotal[~mask]= 0 distrev = np.tile(dist, [kh.shape[0],1]) depthrev = np.tile(depths, [1, kh.shape[1]]) plt.pcolormesh(distrev, depthrev, Etotal, shading='gouraud') plt.gca().invert_yaxis() plt.figure() plt.pcolormesh(dist, p_ladcp.squeeze(), Uprime, cmap=cmocean.cm.balance, shading='flat') levels = np.arange(np.nanmin(Etotal), np.nanmax(Etotal)+.5,.05) plt.contour(distrev, depthrev, Etotal) plt.gca().invert_yaxis() if save_data: file2save = pd.DataFrame(lambdaH) file2save.index = np.squeeze(depths) file2save.to_excel('lambdaH_dec24.xlsx') file2save = pd.DataFrame(Etotal) file2save.index = np.squeeze(depths) file2save.to_excel('E_total.xlsx') return PE, KE, omega, m, kh, lambdaH,\ Etotal, khi, Uprime, Vprime, b_prime,\ ctd_bins, ladcp_bins, KE_grid, PE_grid,\ ke_peaks, pe_peaks, dist, depths, KE_psd,\ eta_psd, N2, N2mean
def shearstrain( depth, t, SP, lon, lat, ladcp_u=None, ladcp_v=None, ladcp_depth=None, m=None, depth_bin=None, window_size=None, m_include_sh=np.arange(4), m_include_st=np.arange(4), ladcp_is_shear=False, smooth="AL", sh_integration_limit=0.66, st_integration_limit=0.22, window="hamming", return_diagnostics=False, ): """ Compute krho and epsilon from CTD/LADCP data via the shear/strain parameterization. Parameters ---------- depth : array-like CTD depth [m] t : array-like CTD in-situ temperature [ITS-90, degrees C] SP : array-like CTD practical salinity [psu] lon : array-like or float Longitude lat : array-like or float Latitude ladcp_u : array-like, optional LADCP velocity east-west component [m/s]. If not provided, only the strain solution with a fixed shear/strain ratio of 3 will be computed. ladcp_v : array-like, optional LADCP velocity north-south component [m/s] ladcp_depth : array-like, optional LADCP depth vector [m] m : array-like, optional Wavenumber vector to interpolate spectra onto depth_bin : array-like, optional Centers of windows over which spectra are computed. Defaults to np.arange(75, max(depth), 150). Note that windows are half-overlapping so the 150 spacing above means each window is 300 m tall. window_size : float Size of depth window [m]. m_include_sh : array-like, optional Wavenumber integration range for shear spectra. Array must consist of indices or boolans to index m. Defaults to first 4 wavenumbers. m_include_st : array-like, optional Wavenumber integration range for strain spectra. Array must consist of indices or boolans to index m. Defaults to first 4 wavenumbers. ladcp_is_shear : bool, optional Indicate whether LADCP data is velocity or shear. Defaults to False (velocity). smooth : {'AL', 'PF'}, optional Select type of N^2 smoothing and subsequent strain calculation. 'AL' selects the adiabatic leveling method as applied in `strain_adiabatic_leveling`. 'PF' selects second order polynomial fits to the buoyancy frequency in each window as applied in `strain_polynomial_fits`. Defaults to the adiabatic leveling method. sh_integration_limit : float, optional Shear variance level for determining integration cutoff wavenumber. Defaults to 0.66, compare Gargett (1990) :cite:`Gargett1990` and Gregg et al. (2003) :cite:`Gregg2003`. st_integration_limit : float, optional Strain variance level for determining integration cutoff wavenumber. Defaults to 0.22, compare Gargett (1990) :cite:`Gargett1990` and Gregg et al. (2003) :cite:`Gregg2003`. window : str or tuple, optional Window type. Defaults to 'hamming' (which corresponds to a sin square taper as used in various studies. See `scipy.signal.get_window` for details. return_diagnostics : bool, optional Default is False. If True, this function will return a dictionary containing variables such as shear spectra, shear/strain ratios, Returns ------- eps_shst : array-like Epsilon calculated from both shear and strain spectra krho_shst : array-like krho calculated from both shear and strain spectra diag : dict, optional Dictionary of diagnostic variables, set return with the `return_diagnostics' argument. `diag` holds the following variables: ``"P_shear"`` Matrix of shear spectra for each depth window (`array-like`). ``"P_strain"`` Matrix of strain spectra for each depth window ``"Mmax_sh"`` Cutoff wavenumber kc (`array-like`). ``"Mmax_st"`` Cutoff wavenubmer used for strain only calculation (`array-like`). ``"Rwtot"`` Shear/strain ratio used, computed from spectra unless specificed in input (`array-like`). ``"krho_st"`` krho calculated from strain only (`array-like`). ``"eps_st"`` Epsilon calculated from strain only (`array-like`). ``"m"`` Wavenumber vector (`array-like`). ``"depth_bin"`` Center points of depth windows (`array-like`). ``"strain"`` Results from strain calculation (`dict`). ``"Int_sh"`` Results from shear variance integration (`array-like`). ``"Int_st"`` Results from strain variance integration (`array-like`). Notes ----- Adapted from Jen MacKinnon and Amy Waterhouse. """ # Average lon, lat into one value if they are vectors. lon = np.nanmean(lon) lat = np.nanmean(lat) depth = np.asarray(depth) t = np.asarray(t) SP = np.asarray(SP) # Make sure there are no NaNs in the input data notnan = np.isfinite(SP) & np.isfinite(t) & np.isfinite(depth) isnan = ~notnan if not isnan.sum() == 0: raise ValueError( "No NaNs allowed in CTD data. Consider using `nan_shearstrain` instead." ) # Can we work with velocity data? calcsh = False if ladcp_u is None else True # No NaNs in velocity/shear data if calcsh: ladcp_u = np.asarray(ladcp_u) ladcp_v = np.asarray(ladcp_v) ladcp_depth = np.asarray(ladcp_depth) notnansh = (np.isfinite(ladcp_u) & np.isfinite(ladcp_v) & np.isfinite(ladcp_depth)) isnansh = ~notnansh if not isnansh.sum() == 0: raise ValueError( "No NaNs allowed in LADCP data. Consider using `nan_shearstrain` instead." ) # Coriolis parameter for this latitude f = np.absolute(gsw.f(lat)) if calcsh: depth_sh = ladcp_depth # Calculate shear if necessary if ladcp_is_shear is False: uz = helpers.calc_shear(ladcp_u, depth_sh) vz = helpers.calc_shear(ladcp_v, depth_sh) else: uz = ladcp_u vz = ladcp_v # Create an evenly spaced wavenumber vector if none was provided if m is None: m = wavenumber_vector(w=300) # Generate depth bin vector if none provided if depth_bin is None: depth_bin = np.arange(75, np.max(depth), 150) else: # cut out any bins that won't hold any data depth_bin = depth_bin[depth_bin < np.max(depth)] nz = np.squeeze(depth_bin.shape) # delz = np.mean(np.diff(depth_bin)) # Calculate a smoothed N^2 profile and strain, either using 2nd order # polynomial fits to N^2 for each window (PF) or the adiabatic leveling # method (AL). Returns a list with data dict for each depth window. if smooth == "PF": straincalc = strain_polynomial_fits(depth, t, SP, lon, lat, depth_bin, window_size) elif smooth == "AL": straincalc = strain_adiabatic_leveling( depth, t, SP, lon, lat, bin_width=300, depth_bin=depth_bin, window_size=window_size, ) # Convert wavenumber integration range to np.array in case it is something # else: m_include_sh = np.asarray(m_include_sh) m_include_st = np.asarray(m_include_st) N0 = 5.24e-3 # (3 cph) K0 = 0.05 * 1e-4 eps0 = 7.8e-10 # Waterman et al. 2014 # eps0 = 7.9e-10 # Polzin et al. 1995 # eps0 = 6.73e-10 # Gregg et al. 2003 P_shear = np.full((nz, m.size), np.nan) P_strain = P_shear.copy() Mmax_sh = np.full(nz, np.nan) Mmax_st = Mmax_sh.copy() Rwtot = np.full(nz, np.nan) krho_shst = np.full(nz, np.nan) krho_st = np.full(nz, np.nan) eps_shst = np.full(nz, np.nan) eps_st = np.full(nz, np.nan) Int_st = np.full(nz, np.nan) Int_sh = np.full(nz, np.nan) for iwin, (zi, sti) in enumerate(zip(depth_bin, straincalc)): assert zi == sti["depth_bin"] zw = depth_bin[iwin] # Shear spectra if calcsh: iz = (depth_sh >= (zw - window_size / 2)) & (depth_sh <= (zw + window_size / 2)) # Buoyancy-normalize shear shear_un = uz[iz] / np.real(np.sqrt(sti["N2mean"])) shear_vn = vz[iz] / np.real(np.sqrt(sti["N2mean"])) shearn = shear_un + 1j * shear_vn ig = ~np.isnan(shearn) if np.flatnonzero(ig).size > 10: dz = np.mean(np.diff(depth_sh[iz])) _, _, Ptot, m0 = helpers.psd(shearn[ig], dz, ffttype="t", detrend=True, window=window) # Compensation for first differencing H = np.sinc(m0 * dz / 2 / np.pi)**2 Ptot = Ptot / H Ptot_sh = interp1d(m0, Ptot, bounds_error=False)(m) P_shear[iwin, :] = Ptot_sh else: P_shear[iwin, :] = np.nan Ptot_sh = np.zeros_like(m) * np.nan # Strain spectra # iz = (depth_st >= (zw - delz)) & (depth_st <= (zw + delz)) segstrain = sti["strain"] segstraindep = sti["segz"] ig = ~np.isnan(segstrain) if np.flatnonzero(ig).size > 10: dz = np.mean(np.diff(segstraindep)) _, _, Ptot, m0 = helpers.psd(segstrain[ig], dz, ffttype="t", detrend=True, window=window) # Compensation for first differencing H = np.sinc(m0 * dz / 2 / np.pi)**2 Ptot = Ptot / H Ptot_st = interp1d(m0, Ptot, bounds_error=False)(m) P_strain[iwin, :] = Ptot_st else: P_strain[iwin, :] = np.nan Ptot_st = np.zeros_like(m) * np.nan # Mean stratification per segment Nm = np.sqrt(sti["N2mean"]) # Shear cutoff wavenumber if calcsh: iimsh, Mmax_sh[iwin] = find_cutoff_wavenumber( Ptot_sh[m_include_sh], m[m_include_sh], sh_integration_limit) # Strain cutoff wavenumber iimst, Mmax_st[iwin] = find_cutoff_wavenumber(Ptot_st[m_include_st], m[m_include_st], st_integration_limit) if calcsh: # Integrate shear spectrum to obtain shear variance Ssh = np.trapz(Ptot_sh[iimsh], m[iimsh]) Int_sh[iwin] = Ssh # GM shear variance Sshgm, Pshgm = gm_shear_variance(m, iimsh, Nm) # Integrate strain spectrum to obtain strain variance Sst = np.trapz(Ptot_st[iimst], m[iimst]) Int_st[iwin] = Sst # GM strain variance Sstgm, Pstgm = gm_strain_variance(m, iimst, Nm) if calcsh: # Shear/strain ratio normalized by GM. Factor 3 corrects for the ratio # of GM shear to strain = 3 N^2. Rw = 3 * (Ssh / Sshgm) / (Sst / Sstgm) Rwtot[iwin] = Rw # Avoid negative square roots in hRw below Rw = 1.01 if Rw < 1.01 else Rw # Shear/strain parameterization hRw = 3 * (Rw + 1) / (2 * np.sqrt(2) * Rw * np.sqrt(Rw - 1)) krho_shst[iwin] = (K0 * (Ssh**2 / Sshgm**2) * (hRw * latitude_correction(f, Nm))) eps_shst[iwin] = (eps0 * (Nm**2 / N0**2) * (Ssh**2 / Sshgm**2) * (hRw * latitude_correction(f, Nm))) # Strain only parameterization # Use assumed shear/strain ratio of 3 Rw = 3 h2Rw = 1 / 6 / np.sqrt(2) * Rw * (Rw + 1) / np.sqrt(Rw - 1) krho_st[iwin] = (K0 * (Sst**2 / Sstgm**2) * (h2Rw * latitude_correction(f, Nm))) eps_st[iwin] = (eps0 * (Nm**2 / N0**2) * (Sst**2 / Sstgm**2) * (h2Rw * latitude_correction(f, Nm))) if return_diagnostics: diag = dict( eps_st=eps_st, krho_st=krho_st, P_shear=P_shear, P_strain=P_strain, Mmax_sh=Mmax_sh, Mmax_st=Mmax_st, Rwtot=Rwtot, m=m, depth_bin=depth_bin, strain=straincalc, Int_sh=Int_sh, Int_st=Int_st, ) return eps_shst, krho_shst, diag else: return eps_shst, krho_shst
def analyse(z, U, V, dUdz, dVdz, strain, N2_ref, lat, params=default_params): """ """ X = [U, V, dUdz, dVdz, strain, N2_ref] if params['plot_profiles']: fig, axs = plt.subplots(1, 4, sharey=True) axs[0].set_ylabel('$z$ (m)') axs[0].plot(np.sqrt(N2_ref), z, 'k-', label='$N_{ref}$') axs[0].plot(np.sqrt(strain*N2_ref + N2_ref), z, 'k--', label='$N$') axs[0].set_xlabel('$N$ (rad s$^{-1}$)') axs[0].legend(loc=0) axs[0].set_xticklabels(axs[0].get_xticks(), rotation='vertical') axs[1].plot(U, z, 'k-', label='$U$') axs[1].plot(V, z, 'r-', label='$V$') axs[1].set_xlabel('$U$, $V$ (m s$^{-1}$)') axs[1].legend(loc=0) axs[1].set_xticklabels(axs[1].get_xticks(), rotation='vertical') axs[2].plot(dUdz, z, 'k-', label=r'$\frac{dU}{dz}$') axs[2].plot(dVdz, z, 'r-', label=r'$\frac{dV}{dz}$') axs[2].set_xlabel(r'$\frac{dU}{dz}$, $\frac{dV}{dz}$ (s$^{-1}$)') axs[2].legend(loc=0) axs[2].set_xticklabels(axs[2].get_xticks(), rotation='vertical') axs[3].plot(strain, z, 'k-') axs[3].set_xlabel(r'$\xi_z$ (-)') # Split varables into overlapping window segments, bare in mind the last # window may not be full. width = params['bin_width'] overlap = params['bin_overlap'] wdws = [wdw.window(z, x, width=width, overlap=overlap) for x in X] n = wdws[0].shape[0] z_mean = np.empty(n) EK = np.empty(n) R_pol = np.empty(n) R_om = np.empty(n) epsilon = np.empty(n) kappa = np.empty(n) for i, w in enumerate(zip(*wdws)): # This takes the z values from the horizontal velocity. wz = w[0][0] z_mean[i] = np.mean(wz) # This (poor code) removes the z values from windowed variables. w = [var[1] for var in w] N2_mean = np.mean(w[-1]) N_mean = np.sqrt(N2_mean) # Get the useful power spectra. m, PCW, PCCW, Pshear, Pstrain, PEK = \ window_ps(params['dz'], *w, params=params) # Integrate the spectra. I = [integrated_ps(m, P, params['m_c'], params['m_0']) for P in [Pshear, Pstrain, PCW, PCCW, PEK]] Ishear, Istrain, ICW, ICCW, IEK = I # Garrett-Munk shear power spectral density normalised. # The factor of 2 pi is there to convert to cyclical units. GMshear = 2.*np.pi*GM.E_she_z(2*np.pi*m, N_mean)/N_mean IGMshear = integrated_ps(m, GMshear, params['m_c'], params['m_0']) EK[i] = IEK R_pol[i] = ICCW/ICW R_om[i] = Ishear/Istrain epsilon[i] = GM.epsilon_0*N2_mean/GM.N0**2*Ishear**2/IGMshear**2 # Apply correcting factors epsilon[i] *= L(gsw.f(lat), N_mean)*h_gregg(R_om[i]) kappa[i] = params['mixing_efficiency']*epsilon[i]/N2_mean if params['print_diagnostics']: print("Ishear = {}".format(Ishear)) print("IGMshear = {}".format(IGMshear)) print("lat = {}. f = {}.".format(lat, gsw.f(lat))) print("N_mean = {}".format(N_mean)) print("R_om = {}".format(R_om[i])) print("L = {}".format(L(gsw.f(lat), N_mean))) print("h = {}".format(h_gregg(R_om[i]))) # Plotting here generates a crazy number of plots. if params['plot_spectra']: # The factor of 2 pi is there to convert to cyclical units. GMstrain = 2.*np.pi*GM.E_str_z(2*np.pi*m, N_mean) GMvel = 2.*np.pi*GM.E_vel_z(2*np.pi*m, N_mean) fig, axs = plt.subplots(4, 1, sharex=True) axs[0].loglog(m, PEK, 'k-', label="$E_{KE}$") axs[0].loglog(m, GMvel, 'k--', label="GM $E_{KE}$") axs[0].set_title("height {:1.0f} m".format(z_mean[i])) axs[1].loglog(m, Pshear, 'k-', label="$V_z$") axs[1].loglog(m, GMshear, 'k--', label="GM $V_z$") axs[2].loglog(m, Pstrain, 'k', label=r"$\xi_z$") axs[2].loglog(m, GMstrain, 'k--', label=r"GM $\xi_z$") axs[3].loglog(m, PCW, 'r-', label="CW") axs[3].loglog(m, PCCW, 'k-', label="CCW") axs[-1].set_xlabel('$k_z$ (m$^{-1}$)') for ax in axs: ax.vlines(params['m_c'], *ax.get_ylim()) ax.vlines(params['m_0'], *ax.get_ylim()) ax.grid() ax.legend() if params['plot_results']: fig, axs = plt.subplots(1, 5, sharey=True) axs[0].plot(np.log10(EK), z_mean, 'k-o') axs[0].set_xlabel('$\log_{10}E_{KE}$ (m$^{2}$ s$^{-2}$)') axs[0].set_ylabel('$z$ (m)') axs[1].plot(np.log10(R_pol), z_mean, 'k-o') axs[1].set_xlabel('$\log_{10}R_{pol}$ (-)') axs[1].set_xlim(-1, 1) axs[2].plot(np.log10(R_om), z_mean, 'k-o') axs[2].set_xlabel('$\log_{10}R_{\omega}$ (-)') axs[2].set_xlim(-1, 1) axs[3].plot(np.log10(epsilon), z_mean, 'k-o') axs[3].set_xlabel('$\log_{10}\epsilon$ (W kg$^{-1}$)') axs[4].plot(np.log10(kappa), z_mean, 'k-o') axs[4].set_xlabel('$\log_{10}\kappa$ (m$^{2}$ s$^{-1}$)') for ax in axs: ax.grid() ax.set_xticklabels(ax.get_xticks(), rotation='vertical') return z_mean, EK, R_pol, R_om, epsilon, kappa
def lee_wave_tests(kh, omega, N2, ctd, ladcp, dist, depths, error_factor=2, plots=False): """ Testing whether or not the observations can be attributed to lee waves """ S, T, p_ctd, lat, lon = oc.loadCTD(ctd) # k, l = horizontal_wave_vector_decomposition(Uspec, Vspec) dshift = doppler_shifts(kh, ladcp) # Test whether K*U is between N and f f = (np.nanmean(gsw.f(lat))) dshiftTest = np.full_like(kh, np.nan) test_final = np.full_like(kh, np.nan) for i, dump in enumerate(kh.T): N2mean = np.nanmean(N2[:,i]) testA = np.abs(dshift[:,i]**2-f**2) <= f**2 testB = np.abs(dshift[:,i]**2-f**2) <= N2mean test_final[:,i] = np.logical_and(testA, testB) dshiftTest[:,i] = np.logical_and(dshift[:,i]**2 >= f**2, dshift[:,i]**2<= N2mean) dshiftTest2 = dshiftTest == 0 mask = np.logical_not(test_final) kh[mask] = np.nan omega[mask] = np.nan lambdaH[mask] = np.nan k[mask] = np.nan l[mask] = np.nan # kh[dshiftTest2] = np.nan # omega[dshiftTest2] = np.nan # lambdaH[dshiftTest2] = np.nan # k[dshiftTest2] = np.nan # l[dshiftTest2] = np.nan file2save = pd.DataFrame(kh) file2save.index = np.squeeze(depths) file2save.to_excel('Kh_masked.xlsx') file2save = pd.DataFrame(omega) file2save.index = np.squeeze(depths) file2save.to_excel('omega_masked.xlsx') file2save = pd.DataFrame(lambdaH) file2save.index = np.squeeze(depths) file2save.to_excel('lambda_masked.xlsx') np.savetxt('kh_masked2.csv', kh) np.savetxt('k_masked2.csv', k) np.savetxt('l_masked2.csv', l) np.savetxt('omega_masked.csv', omega) # Test phase of velocity and isopycnal perturbations stns = np.arange(1, 1+S.shape[1]) stns = np.expand_dims(stns, 1) stns = np.repeat(stns.T, kh.shape[0], axis=0) np.savetxt('dshift_mask.csv',dshiftTest) if plots: fig = plt.figure() plt.contourf(dist, np.squeeze(p_ladcp), U, cmap='seismic') plt.colorbar() plt.pcolormesh(dist, np.squeeze(depths), dshiftTest, cmap='binary', alpha=.2) plt.fill_between(dist, bathy, 4000, color = '#B4B4B4') plt.ylim(0, 4000) plt.gca().invert_yaxis() plt.title("u' with bins with f < Kh*U < N") plt.xlabel('Distance Along Transect (km)') plt.ylabel('Pressure (dB)') for i in range(U.shape[1]): plt.annotate(str(i+1), (dist[i], 3600))
import utils def U_const(z): """Constant velocity of 50 cm s-1.""" return 0.5 def U_shear(z): """Default shear of 20 cm s-1 over 1500 m.""" return 1.3333e-4*z + 5e-1 default_params = { 'Ufunc': U_const, 'f': gsw.f(-57.5), 'N': 1.8e-3, 'w_0': 0.17, 'Wf_pvals': np.polyfit([0., 0.06], [0.14, 0.12], 1), 'dt': 10., 't_1': 15000., 'x_0': 0., 'y_0': 0., 'z_0': -1500, 'print': False } def drdt(r, t, phi_0, Ufunc, Wf_pvals, k, l, m, om, N, f=0., phase_0=0.): x = r[0] y = r[1]
# author: Tiago Bilo import numpy as np import matplotlib.pyplot as plt from oceans_old.plotting import rstyle import gsw plt.rcParams['text.usetex'] = True plt.rcParams['text.latex.unicode'] = True ## Parameters f = gsw.f(45) # Coriolis parameter (s^-1) H = 10.*1000 # Height of the domain (m) u0 = 10. # Maximum velocity (m s^-1) N = 0.01 # Brunt-Vaisala frequency (s^-1) Rd = N*H/f # Rossby deformation radius (m) ## Domain axes (m) z = np.linspace(0,H,100) x = np.linspace(-10/1.0e-6,10/1.0e-6,100) ## Zonal wave numbers (m^-1) k = np.linspace(0,4.0e-6,100) ## Admensional wavenumber mu mu = N*H*k/f
from scipy.interpolate import griddata, interp1d from netCDF4 import Dataset import gsw import glob import datetime as dt import xarray as xr import seawater as sw import palettable as pal import matplotlib.gridspec as gridspec from scipy import signal import csv from seabird import cnv fcor = gsw.f(60) matplotlib.rcParams['pdf.fonttype'] = 42 matplotlib.rcParams['ps.fonttype'] = 42 matplotlib.rcParams['contour.negative_linestyle'] = 'solid' rc('xtick', labelsize='Large') rc('ytick', labelsize='Large') rc('axes', labelsize='Large') datadir = '/home/isabela/Documents/projects/OSNAP/data/' figdir = '/home/isabela/Documents/projects/OSNAP/figures_1418_merged/' # theta=arctan(vmean[2]/umean[2]) This is how new theta was determined -- just estimated from depth and time averaged flow at this point. # Copied value here for now so I don't have to load vel necessarily. theta = 1.164248016423335
lond, latd = (lon[1:] + lon[:-1]) / 2, (lat[1:] + lat[:-1]) / 2, # Plotting snap = 240 figsize = (12, 6) fig = plt.figure(figsize=figsize) ax1 = fig.add_subplot(131, aspect=1) speed = np.sqrt(surface['u'][snap, 0]**2 + surface['v'][snap, 0]**2) imspeed = plt.pcolor(lon, lat, speed, vmin=0., vmax=1.5, cmap=cmocean.cm.ice_r) ax2 = fig.add_subplot(132, aspect=1) imvort = plt.pcolor(lon, lat, vort['rvort'][snap] / gsw.f(lat), vmin=-5, vmax=5, cmap=cmocean.cm.curl) ax3 = fig.add_subplot(133, aspect=1) imdiv = plt.pcolor(lond, latd, div['surface_div'][240] / gsw.f(latd), vmin=-5, vmax=5, cmap=cmocean.cm.balance) ax1.set_xlabel("Longitude") ax1.set_ylabel("Latitude") ax2.set_xlabel("Longitude")
def analyse(z, U, V, dUdz, dVdz, strain, N2_ref, lat, params=default_params): """ """ X = [U, V, dUdz, dVdz, strain, N2_ref] if params['plot_profiles']: fig, axs = plt.subplots(1, 4, sharey=True) axs[0].set_ylabel('$z$ (m)') axs[0].plot(np.sqrt(N2_ref), z, 'k-', label='$N_{ref}$') axs[0].plot(np.sqrt(strain * N2_ref + N2_ref), z, 'k--', label='$N$') axs[0].set_xlabel('$N$ (rad s$^{-1}$)') axs[0].legend(loc=0) axs[0].set_xticklabels(axs[0].get_xticks(), rotation='vertical') axs[1].plot(U, z, 'k-', label='$U$') axs[1].plot(V, z, 'r-', label='$V$') axs[1].set_xlabel('$U$, $V$ (m s$^{-1}$)') axs[1].legend(loc=0) axs[1].set_xticklabels(axs[1].get_xticks(), rotation='vertical') axs[2].plot(dUdz, z, 'k-', label=r'$\frac{dU}{dz}$') axs[2].plot(dVdz, z, 'r-', label=r'$\frac{dV}{dz}$') axs[2].set_xlabel(r'$\frac{dU}{dz}$, $\frac{dV}{dz}$ (s$^{-1}$)') axs[2].legend(loc=0) axs[2].set_xticklabels(axs[2].get_xticks(), rotation='vertical') axs[3].plot(strain, z, 'k-') axs[3].set_xlabel(r'$\xi_z$ (-)') # Split varables into overlapping window segments, bare in mind the last # window may not be full. width = params['bin_width'] overlap = params['bin_overlap'] wdws = [wdw.window(z, x, width=width, overlap=overlap) for x in X] n = wdws[0].shape[0] z_mean = np.empty(n) EK = np.empty(n) R_pol = np.empty(n) R_om = np.empty(n) epsilon = np.empty(n) kappa = np.empty(n) for i, w in enumerate(zip(*wdws)): # This takes the z values from the horizontal velocity. wz = w[0][0] z_mean[i] = np.mean(wz) # This (poor code) removes the z values from windowed variables. w = [var[1] for var in w] N2_mean = np.mean(w[-1]) N_mean = np.sqrt(N2_mean) # Get the useful power spectra. m, PCW, PCCW, Pshear, Pstrain, PEK = \ window_ps(params['dz'], *w, params=params) # Integrate the spectra. I = [ integrated_ps(m, P, params['m_c'], params['m_0']) for P in [Pshear, Pstrain, PCW, PCCW, PEK] ] Ishear, Istrain, ICW, ICCW, IEK = I # Garrett-Munk shear power spectral density normalised. # The factor of 2 pi is there to convert to cyclical units. GMshear = 2. * np.pi * GM.E_she_z(2 * np.pi * m, N_mean) / N_mean IGMshear = integrated_ps(m, GMshear, params['m_c'], params['m_0']) EK[i] = IEK R_pol[i] = ICCW / ICW R_om[i] = Ishear / Istrain epsilon[ i] = GM.epsilon_0 * N2_mean / GM.N0**2 * Ishear**2 / IGMshear**2 # Apply correcting factors epsilon[i] *= L(gsw.f(lat), N_mean) * h_gregg(R_om[i]) kappa[i] = params['mixing_efficiency'] * epsilon[i] / N2_mean if params['print_diagnostics']: print("Ishear = {}".format(Ishear)) print("IGMshear = {}".format(IGMshear)) print("lat = {}. f = {}.".format(lat, gsw.f(lat))) print("N_mean = {}".format(N_mean)) print("R_om = {}".format(R_om[i])) print("L = {}".format(L(gsw.f(lat), N_mean))) print("h = {}".format(h_gregg(R_om[i]))) # Plotting here generates a crazy number of plots. if params['plot_spectra']: # The factor of 2 pi is there to convert to cyclical units. GMstrain = 2. * np.pi * GM.E_str_z(2 * np.pi * m, N_mean) GMvel = 2. * np.pi * GM.E_vel_z(2 * np.pi * m, N_mean) fig, axs = plt.subplots(4, 1, sharex=True) axs[0].loglog(m, PEK, 'k-', label="$E_{KE}$") axs[0].loglog(m, GMvel, 'k--', label="GM $E_{KE}$") axs[0].set_title("height {:1.0f} m".format(z_mean[i])) axs[1].loglog(m, Pshear, 'k-', label="$V_z$") axs[1].loglog(m, GMshear, 'k--', label="GM $V_z$") axs[2].loglog(m, Pstrain, 'k', label=r"$\xi_z$") axs[2].loglog(m, GMstrain, 'k--', label=r"GM $\xi_z$") axs[3].loglog(m, PCW, 'r-', label="CW") axs[3].loglog(m, PCCW, 'k-', label="CCW") axs[-1].set_xlabel('$k_z$ (m$^{-1}$)') for ax in axs: ax.vlines(params['m_c'], *ax.get_ylim()) ax.vlines(params['m_0'], *ax.get_ylim()) ax.grid() ax.legend() if params['plot_results']: fig, axs = plt.subplots(1, 5, sharey=True) axs[0].plot(np.log10(EK), z_mean, 'k-o') axs[0].set_xlabel('$\log_{10}E_{KE}$ (m$^{2}$ s$^{-2}$)') axs[0].set_ylabel('$z$ (m)') axs[1].plot(np.log10(R_pol), z_mean, 'k-o') axs[1].set_xlabel('$\log_{10}R_{pol}$ (-)') axs[1].set_xlim(-1, 1) axs[2].plot(np.log10(R_om), z_mean, 'k-o') axs[2].set_xlabel('$\log_{10}R_{\omega}$ (-)') axs[2].set_xlim(-1, 1) axs[3].plot(np.log10(epsilon), z_mean, 'k-o') axs[3].set_xlabel('$\log_{10}\epsilon$ (W kg$^{-1}$)') axs[4].plot(np.log10(kappa), z_mean, 'k-o') axs[4].set_xlabel('$\log_{10}\kappa$ (m$^{2}$ s$^{-1}$)') for ax in axs: ax.grid() ax.set_xticklabels(ax.get_xticks(), rotation='vertical') return z_mean, EK, R_pol, R_om, epsilon, kappa
z = np.linspace(*plt.ylim(), num=10) xg, zg = np.meshgrid(dist, z) tg = zg - np.max(topo) levels = np.arange(-1000, 1, 100) CS = plt.contour(xg, zg, tg, colors='k', levels=levels) plt.clabel(CS, inline=1, fontsize=8, fmt='%1.f') #pf.my_savefig(fig, 'topo', 'section', sdir, fsize='double_col') # %% Characteristics of the flow # --------------------------- # The inertial frequency... lat_mean = -57.5 # np.mean(lats) f = np.abs(gsw.f(lat_mean)) print("Mean inertial frequency at {:1.1f} degrees is {:.2E} rad s-1. " "The period is {:1.1f} hours.".format(lat_mean, f, 2 * np.pi / (60 * 60 * f))) # - $N \approx 2.3 \times 10^{-3}$ rad s$^{-1}$. (Near bottom of profiles # upstream of the ridge.) # - $U \approx 0.3$ m s$^{-1}$. (Near bottom of profiles upstream of the # ridge.) # - $f \approx 1.2 \times 10^{-4}$ rad s$^{-1}$. (57 degrees south.) # - $h_0 \approx 2 \times 10^3$ m (Ridge height.) # - $L \approx 2 \times 10^4$ m (Ridge width.) # # Periods of motion. # # - Buoyancy period, $T_N \approx 50$ mins.
import utils def U_const(z): """Constant velocity of 50 cm s-1.""" return 0.5 def U_shear(z): """Default shear of 20 cm s-1 over 1500 m.""" return 1.3333e-4 * z + 5e-1 default_params = { 'Ufunc': U_const, 'f': gsw.f(-57.5), 'N': 1.8e-3, 'w_0': 0.17, 'Wf_pvals': np.polyfit([0., 0.06], [0.14, 0.12], 1), 'dt': 10., 't_1': 15000., 'x_0': 0., 'y_0': 0., 'z_0': -1500, 'print': False } def drdt(r, t, phi_0, Ufunc, Wf_pvals, k, l, m, om, N, f=0., phase_0=0.): x = r[0] y = r[1]
nans = np.isnan(z) | np.isnan(t) | np.isnan(Ww) | np.isnan(N2_ref) | (z > -50) z, t, Ww, N2_ref = z[~nans], t[~nans], Ww[~nans], N2_ref[~nans] # Setting up the spectral analysis. ts = 60.*t # Convert to seconds. dt = np.round(np.mean(np.diff(ts))) fs = 1./dt it = np.arange(np.min(ts), np.max(ts), dt) iWw = np.interp(it, ts, Ww) diWw = sig.detrend(iWw) # Get an idea of the buoyancy frequency. N_mean = np.mean(np.sqrt(N2_ref[z < -200]))/(2.*np.pi) # The inertial frequency. fcor = np.abs(gsw.f(-57.5))/(2.*np.pi) # Perform the spectral analysis. freqs, Pw = sig.welch(iWw, fs=fs, detrend='linear') # Fit a curve. popt, __ = op.curve_fit(plane_wave, it, diWw, p0=[0.1, 0.002, 0.]) omfit = popt[1]/(2.*np.pi) period = 1/omfit/60. plt.figure(figsize=(4,6)) # plt.subplot(1, 2, 1) plt.plot(diWw, it, plane_wave(it, *popt), it) plt.xticks(rotation=45)
# hmw4_q2.py # # purpose: Plot the Eady Model solutions from question 2 # # author: Tiago Bilo import numpy as np import matplotlib.pyplot as plt from oceans_old.plotting import rstyle import gsw plt.rcParams['text.usetex'] = True plt.rcParams['text.latex.unicode'] = True ## Parameters f = gsw.f(45) # Coriolis parameter (s^-1) H = 10. * 1000 # Height of the domain (m) u0 = 10. # Maximum velocity (m s^-1) N = 0.01 # Brunt-Vaisala frequency (s^-1) Rd = N * H / f # Rossby deformation radius (m) ## Domain axes (m) z = np.linspace(0, H, 100) x = np.linspace(-10 / 1.0e-6, 10 / 1.0e-6, 100) ## Zonal wave numbers (m^-1) k = np.linspace(0, 4.0e-6, 100) ## Admensional wavenumber mu mu = N * H * k / f