def calculate_n2(T, p, C, lat, long): """Calculates the buoyancy frequency N^2 All parameters and output are float or array-like. :param T: temperature (deg C) :param p: pressure (bar) :param C: conductivity (S/m) :param lat: latitude (decimal deg) :param long: longitude (decimal deg) :return: buoyancy (i.e. brunt-vaisala) frequency N^2 (s^-2) """ # pressure in dbars = pressure * 10 p_dbar = p * 10 # conductivity in mS/cm = conductivity * 10 C_mScm = C * 10 print(T.count()) print(p.count()) print(C.count()) print(lat.count()) print(long.count()) # calculate SP from conductivity (mS/cm), in-situ temperature (deg C), and gauge pressure (dbar) SP = gsw.SP_from_C(C_mScm, T, p_dbar) # calculate SA from SP (unitless), gauge pressure (dbar), longitude and latitude (decimal degrees) SA = gsw.SA_from_SP(SP, p_dbar, long, lat) # calculate CT from SA (g/kg), in-situ temperature (deg C), and gauge pressure (dbar) CT = gsw.CT_from_t(SA, T, p_dbar) # calculate N^2 nsquared, p_mid = gsw.Nsquared(SA, CT, p_dbar, lat=lat) return nsquared
def get_hydro(my_file): f = Dataset(my_file, mode='r') eps = f.variables['EPSILON'][:] eps = remove_bad_eps(eps) lat = f.variables['LATITUDE'][:] lon = f.variables['LONGITUDE'][:] p = f.variables['PRESSURE'][:] SP = f.variables['PSAL'][:] T = f.variables['TEMPERATURE'][:] z = gsw.z_from_p(p, lat) # m SA = gsw.SA_from_SP(SP, p, lon, lat) # g/kg, absolute salinity CT = gsw.CT_from_t(SA, T, p) # C, conservative temperature SA = remove_bad_SA(SA) CT = remove_bad_CT(CT) [N2_mid, p_mid] = gsw.Nsquared(SA, CT, p, lat) z_mid = gsw.z_from_p(p_mid, lat) N2 = interp_to_edges(N2_mid, z, z_mid, 4) N2 = np.append(np.append(N2, [np.nan]), [np.nan]) N2 = remove_bad_N2(N2) return N2, SA, CT, eps, z
def interpolate(self): self.ipres = range(int(min(self.pres)),int(max(self.pres))) if len(self.pres)>4: self.isals = np.interp(self.ipres,self.pres,self.sals) self.itemps = np.interp(self.ipres,self.pres,self.temps) if hasattr(self,"knownu"): self.iknownu = np.interp(self.ipres,self.pres,self.knownu) self.iknownv = np.interp(self.ipres,self.pres,self.knownv) if hasattr(self,"knownpsi"): self.iknownpsi = np.interp(self.ipres,self.pres,self.knownpsi) if hasattr(self,"kapredi"): self.ikapredi = np.interp(self.ipres,self.pres,self.kapredi) self.idiffkr = np.interp(self.ipres,self.pres,self.diffkr) self.ikapgm = np.interp(self.ipres,self.pres,self.kapgm) #plt.plot(self.temps,self.pres) #plt.plot(self.itemps,self.ipres) #plt.show() self.ialpha = gsw.alpha(self.isals,self.itemps,self.ipres) self.ibeta = gsw.beta(self.isals,self.itemps,self.ipres) self.idalphadtheta = gsw.cabbeling(self.isals,self.itemps,self.ipres) self.idalphadp = gsw.thermobaric(self.isals,self.itemps,self.ipres) ###using gsw self.n2 = gsw.Nsquared(self.isals,self.itemps,self.ipres,self.maplat)[0]
def common_thermodynamics(depth, lon, lat, SP, t): """Wrapper for various thermodynamic calculations. Assumes input data is 1D with size N or M, or 2D with size N*M, where M denotes profiles and N depths. Parameters ---------- depth : numpy array Depth (m), size N. lon : numpy array Longitude, size M. lat : numpy array Latitude, size M. SP : numpy array Practical salinity, size N*M. t : numpy array Temperature, size N*M. """ p = gsw.p_from_z(-depth, np.mean(lat)) SA = gsw.SA_from_SP(SP, p[:, np.newaxis], lon[np.newaxis, :], lat[np.newaxis, :]) CT = gsw.CT_from_t(SA, t, p[:, np.newaxis]) sig0 = gsw.pot_rho_t_exact(SA, t, p[:, np.newaxis], 0) N2, p_mid = gsw.Nsquared(SA, CT, p[:, np.newaxis], lat[np.newaxis, :]) return p, SA, CT, sig0, p_mid, N2
def derive_variables(dbase, which_ones='all'): """ Also include density, potential temp and density; and uncertainties!! """ SA_dat = [ gsw.SA_from_SP(dbase['sal'][n], dbase['pres'][n], dbase['lon'][n], dbase['lat'][n]) for n in range(0, len(dbase)) ] CT_dat = [ gsw.CT_from_t(SA_dat[n], dbase['temp'][n], dbase['pres'][n]) for n in range(0, len(dbase)) ] N2_dat = [ gsw.Nsquared( SA_dat[n], CT_dat[n], dbase['pres'][n], dbase['lat'][n], alphabeta=True, ) for n in range(0, len(dbase)) ] if which_ones == 'all': return SA_dat, CT_dat, N2_dat elif which_ones == 'N2': return N2_dat
def brunt_vaisala(SP, t, p, lon=-45.401666, lat=-23.817233, interpolate=True, result='frequency'): """ Returns Brunt-Väisäla (or buoyancy) frequency/period from practical salinity, in situ temperature and pressure (depth). Parameters ---------- SP : array like Salinity (PSS-78) [1e-3] t : array like Temperature (ITS-90) [degC] p : array like Pressure [dbar] lon : array like, float, optional Longitude, decimal degrees east lat : array like, float, optional Latitude, decimal degrees north interpolate : boolean, optional If True, interpolates calculated frequency to original pressure (depth) levels. If False, returns results at mid pressure between pressugre grid. result : {'freuency', 'period'}, optional Sets whether to return `frequency` [in Returns ------- N : array like Brunt-Väisälä frequency [s-1] or period [s]. p : array like Pressure levels [dbar]. """ # Calculates Absolute Salinity from Practical Salinity SA = gsw.SA_from_SP(SP, p, lon, lat) # Calcualtes Conservative Temperature from in situ temperature CT = gsw.CT_from_t(SA, t, p) # N2, p_mid = gsw.Nsquared(SA, CT, p, lat=lat) N2[N2 < 0] = 0 # if interpolate: N2 = interp(p, p_mid, N2) # if result == 'frequency': return N2**0.5, p_mid elif result == 'period': return 1. / N2**0.5, p_mid else: raise ValueError('Invalid result type {}.'.format(result))
def _update_eos(self): # derive absolute salinity and conservative temperature self.SA = gsw.SA_from_SP(self.s, self.p, self.lon, self.lat) self.CT = gsw.CT_from_t(self.SA, self.temp, self.p) # derive N2 self.N2, self.p_mid = gsw.Nsquared(self.SA, self.CT, self.p, lat=self.lat) self.z_mid = gsw.z_from_p(self.p_mid, self.lat)
def Nsquared(S, T, p, lat=None, lon=None, dim='p'): ones = xr.ones_like(S) p = ones * S[dim] if lat is None: lat = ones * S['lat'] if lon is None: lon = ones * S['lon'] SA = gsw.SA_from_SP(S, p, lon, lat) CT = gsw.CT_from_t(SA, T, p) N2, pmid = gsw.Nsquared(SA, CT, p, axis=S.get_axis_num(dim)) return N2, pmid
def test(self): sal = np.array([0.1, 0.1]) # temp = np.array([4., 21.]) # Celsius pres = np.array([10., 20.]) rho = gsw.rho(sal, temp, pres) print("density", rho) lat = [43.2, 43.2] CT = gsw.CT_from_t(sal, temp, pres) N2, p_mid = gsw.Nsquared(sal, CT, pres, lat=lat) print("N2", N2) print("p_mid", p_mid)
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 add_nsquared(mp): if "SA" not in mp: mp = add_gsw_variables(mp) N2, pmid = gsw.Nsquared(mp.SA, mp.CT, mp.P) fint = sp.interpolate.interp1d(x=pmid[:, 0], y=N2, axis=0, bounds_error=False) N2i = fint(mp.p) mp["N2"] = (("z", "time"), N2i) mp.N2.attrs["long_name"] = r"N$^2$" mp.N2.attrs["units"] = r"s$^{-2}$" return mp
def interpolate(self): self.ipres = range(int(min(self.pres)), int(max(self.pres))) if len(self.pres) > 4: self.isals = np.interp(self.ipres, self.pres, self.sals) self.itemps = np.interp(self.ipres, self.pres, self.temps) self.igamma = np.interp(self.ipres, self.pres, self.gamma) self.ialpha = gsw.alpha(self.isals, self.itemps, self.ipres) self.ibeta = gsw.beta(self.isals, self.itemps, self.ipres) self.idalphadtheta = gsw.cabbeling(self.isals, self.itemps, self.ipres) self.idalphadp = gsw.thermobaric(self.isals, self.itemps, self.ipres) ###using gsw self.n2 = gsw.Nsquared(self.isals, self.itemps, self.ipres, self.lat)[0]
def calc_n2(SA, CT, rho, depth, alpha, g=9.8, po=1027, gsw=True): """ gsw implementation of n2, decomposed into temp and salt components """ if gsw == True: # From gsw package import gsw n2 = gsw.Nsquared(SA, CT, depth, axis=0) n2_t = (-g * alpha * np.diff(CT, axis=0)) # Temperature component n2_s = n2 - n2_t # Salt component else: # From buoyancy by = g * (1 - rho / po) # Buoyancy n2 = np.diff(by * -1, axis=0) n2_t = (-g * alpha[:-1, :] * np.diff(CT, axis=0) ) # Temperature component n2_s = n2 - n2_t # Salt component return n2, n2_t, n2_s
def BV2(S,T,depth,lon,lat): p1=gsw.p_from_z(depth,np.array(lat)) sa=gsw.SA_from_SP(S,p1,lon,lat) ct=gsw.CT_from_pt(sa,T) N2,pOut=gsw.Nsquared(sa,ct,p1,lat) return N2,pOut
for var in list(dsSX.var()): if var != 'profile_index': exec('sx_' + var + '[ppitch<0] = sx_' + var + '[ppitch<0].fillna(method="ffill", limit=5, axis=1)') exec('sx_' + var + '[ppitch>0] = sx_' + var + '[ppitch>0].fillna(method="bfill", limit=5, axis=1)') print(' Done!') # TEOS-10 conversion SeaExplorer P = sx_pressure.values lon0, lat0 = dsSX['longitude'].values.mean(), dsSX['latitude'].values.mean() SA = gsw.SA_from_SP(sx_salinity.values, P, lon0, lat0) CT = gsw.CT_from_t(SA, sx_temperature.values, P) sigma0 = gsw.sigma0(SA, CT) SA_sort = np.array(list(map(lambda x, y: y[x], np.argsort(sigma0, axis=1), SA))) CT_sort = np.array(list(map(lambda x, y: y[x], np.argsort(sigma0, axis=1), CT))) P_sort = np.array(list(map(lambda x, y: y[x], np.argsort(sigma0, axis=1), P))) N2, p_mid = gsw.Nsquared(SA_sort, CT_sort, P_sort, lat=lat0, axis=1) # Oxygen conversion Ofreq = sx_oxygen_frequency.values Soc = float(dpl['calibration.SBE43F.soc']) Foffset = float(dpl['calibration.SBE43F.foffset']) A = float(dpl['calibration.SBE43F.a']) B = float(dpl['calibration.SBE43F.b']) C = float(dpl['calibration.SBE43F.c']) E = float(dpl['calibration.SBE43F.e']) K = CT + 273.15 # Temp in Kelvin O2sol = gsw.O2sol(SA, CT, P, lon0, lat0); #umol/Kg O2 = Soc*(Ofreq+Foffset)*(1.0+A*CT + B*CT**2 + C*CT**3)*O2sol*np.exp(E*P/K); # MiniFluo calib (# Assume new version)
Np = len(MP['id']) if np.ndim(MP['lon']) != 0: MP['lon'] = MP.pop('lon')[0] MP['lat'] = MP.pop('lat')[0] MP['P'] = np.tile(MP['p'][:, np.newaxis].astype(float), (1, Np)) MP['T'] = MP.pop('t') MP['S'] = MP.pop('s') MP['z'] = gsw.z_from_p(MP['P'], MP['lat']) MP['HAB'] = MP['z'] - np.nanmin(MP['z']) + 20. MP['SA'] = gsw.SA_from_SP(MP['S'], MP['P'], MP['lon'], MP['lat']) MP['CT'] = gsw.CT_from_t(MP['SA'], MP['T'], MP['P']) MP['sig4'] = gsw.pot_rho_t_exact(MP['SA'], MP['T'], MP['P'], 4000) MP['g'] = gsw.grav(MP['lat'], MP['P']) MP['sig4m'] = np.nanmean(MP['sig4']) MP['sig4_sorted'] = utils.nansort(MP['sig4'], axis=0) MP['N2'], MP['P_mid'] = gsw.Nsquared(MP['SA'], MP['CT'], MP['P'], MP['lat']) MP['z_mid'] = gsw.z_from_p(MP['P_mid'], MP['lat']) MP['ual'], MP['uac'] = rotate_overflow(MP['u'], MP['v'], MP['sig4'], sig4max) print('- Calculating the overflow integrated velocity') ual_ = MP['ual'].copy() uac_ = MP['uac'].copy() mask = MP['sig4'] < sig4max ual_[mask] = np.nan uac_[mask] = np.nan MP['uoal'] = utils.nantrapz(ual_, x=MP['z'], axis=0, xave=True) MP['uoac'] = utils.nantrapz(uac_, x=MP['z'], axis=0, xave=True) print('- Calculating neutral density') MP['PT0'] = seawater.ptmp(MP['S'], MP['T'], MP['P'])
# N^2 = -\frac{g}{\rho} \frac{d \rho}{dz} # $$ # $$ # \rho = \alpha_T \theta # $$ # $$ # T_{z0} = \frac{N^2}{g \alpha_T} # $$ # %% zshift = 50 # shift peak by how many metres tmp = sec9.where(sec9.station == 12, drop=True) SA = gsw.SA_from_SP(tmp.s.squeeze(), tmp.p.squeeze(), tmp.lon, tmp.lat) CT = gsw.CT_from_t(SA, tmp.t.squeeze(), tmp.p.squeeze()) N2, pmid = gsw.Nsquared(SA, CT, tmp.p.squeeze(), tmp.lat) zmid = utils.mid(tmp.z.data) good = np.isfinite(N2) N2smooth = utils.convolve_smooth(N2[good], 100) zs = zmid[good] imax, _ = sp.signal.find_peaks(N2smooth, height=(3e-6, 5e-6), prominence=2e-6) hw = 220 ispeak = (zs > zs[imax - hw]) & (zs < zs[imax + hw]) N2filled = np.interp(zs, zs[~ispeak], N2smooth[~ispeak]) N2peak = (N2smooth - N2filled) fpeak = sp.interpolate.interp1d(zs, N2peak, bounds_error=False, fill_value=(0, 0)) N2shifted = N2filled + fpeak(zs + zshift)
def derive(self,property): """ Derives seawater properties as salinity, buoyancy frequency squared etc. Args: ST: N2: """ try: gsw except: logger.warning('GSW toolbox missing, derive will not work, doing nothing') return if(property == 'ST'): # Poor mans check if the variables exist sensor_pair0 = False sensor_pair1 = False try: tmp1 = self.data['cond0'] tmp1 = self.data['temp0'] tmp1 = self.data['p'] sensor_pair0 = True except: sensor_pair0 = False try: tmp1 = self.data['cond1'] tmp1 = self.data['temp1'] tmp1 = self.data['p'] sensor_pair1 = True except: sensor_pair1 = False if(sensor_pair0): logger.debug('Calculating PSU0/SA11/CT11/rho11') SP = gsw.SP_from_C(self.data['cond0'],self.data['temp0'],self.data['p']) self.derived['SP00'] = SP SA = gsw.SA_from_SP(SP,self.data['p'],self.header['lon'],self.header['lat']) self.derived['SA00'] = SA CT = gsw.CT_from_t(SA,self.data['temp0'],self.data['p']) self.derived['CT00'] = CT rho = gsw.rho_CT_exact(SA,CT,self.data['p']) self.derived['rho00'] = rho if(sensor_pair1): logger.debug('Calculating PSU1/SA11/CT11/rho11') SP = gsw.SP_from_C(self.data['cond1'],self.data['temp1'],self.data['p']) self.derived['SP11'] = SP SA = gsw.SA_from_SP(SP,self.data['p'],self.header['lon'],self.header['lat']) self.derived['SA11'] = SA CT = gsw.CT_from_t(SA,self.data['temp1'],self.data['p']) self.derived['CT11'] = CT rho = gsw.rho_CT_exact(SA,CT,self.data['p']) self.derived['rho11'] = rho if(property == 'N2'): # Poor mans check if the variables exist sensor_pair0 = False sensor_pair1 = False try: tmp1 = self.derived['SA00'] tmp1 = self.derived['CT00'] tmp1 = self.data['p'] sensor_pair0 = True except: logger.info('Did not find absolute salinities and temperature, do first a .derive("ST")') sensor_pair0 = False try: tmp1 = self.derived['SA11'] tmp1 = self.derived['CT11'] tmp1 = self.data['p'] sensor_pair1 = True except: logger.info('Did not find absolute salinities and temperature, do first a .derive("ST")') sensor_pair1 = False if(sensor_pair0): logger.debug('Calculating Nsquared00') [N2,p_mid] = gsw.Nsquared(self.derived['SA00'],self.derived['CT00'],self.data['p']) self.derived['Nsquared00'] = interp(self.data['p'],p_mid,N2) if(sensor_pair1): logger.debug('Calculating Nsquared11') [N2,p_mid] = gsw.Nsquared(self.derived['SA11'],self.derived['CT11'],self.data['p']) self.derived['Nsquared11'] = interp(self.data['p'],p_mid,N2) if(property == 'alphabeta'): # Poor mans check if the variables exist sensor_pair0 = False sensor_pair1 = False try: tmp1 = self.derived['SA00'] tmp1 = self.derived['CT00'] tmp1 = self.data['p'] sensor_pair0 = True except: logger.info('Did not find absolute salinities and temperature, do first a .derive("ST")') sensor_pair0 = False try: tmp1 = self.derived['SA11'] tmp1 = self.derived['CT11'] tmp1 = self.data['p'] sensor_pair1 = True except: logger.info('Did not find absolute salinities and temperature, do first a .derive("ST")') sensor_pair1 = False if(sensor_pair0): logger.debug('Calculating Nsquared00') alpha = gsw.alpha(self.derived['SA00'],self.derived['CT00'],self.data['p']) beta = gsw.beta(self.derived['SA00'],self.derived['CT00'],self.data['p']) self.derived['alpha00'] = alpha self.derived['beta00'] = beta if(sensor_pair1): logger.debug('Calculating Nsquared11') alpha = gsw.alpha(self.derived['SA11'],self.derived['CT11'],self.data['p']) beta = gsw.beta(self.derived['SA11'],self.derived['CT11'],self.data['p']) self.derived['alpha11'] = alpha self.derived['beta11'] = beta
print('Fill NaNs in matrices - SeaExplorer') for var in list(dsSX.var()): if var != 'profile_index': exec('sx_' + var + '[ppitch<0] = sx_' + var + '[ppitch<0].fillna(method="ffill", limit=5, axis=1)') exec('sx_' + var + '[ppitch>0] = sx_' + var + '[ppitch>0].fillna(method="bfill", limit=5, axis=1)') print(' Done!') # TEOS-10 conversion SeaExplorer P = sx_pressure.values lon0, lat0 = dsSX['longitude'].values.mean(), dsSX['latitude'].values.mean() SA = gsw.SA_from_SP(sx_salinity.values, P, lon0, lat0) CT = gsw.CT_from_t(SA, sx_temperature.values, P) sigma0 = gsw.sigma0(SA, CT) N2, p_mid = gsw.Nsquared(SA, CT, P, lat=lat0) # Oxygen conversion Ofreq = sx_oxygen_frequency.values Soc = float(dpl['calibration.SBE43F.soc']) Foffset = float(dpl['calibration.SBE43F.foffset']) A = float(dpl['calibration.SBE43F.a']) B = float(dpl['calibration.SBE43F.b']) C = float(dpl['calibration.SBE43F.c']) E = float(dpl['calibration.SBE43F.e']) K = CT + 273.15 # Temp in Kelvin O2sol = gsw.O2sol(SA, CT, P, lon0, lat0) #umol/Kg O2 = Soc * (Ofreq + Foffset) * (1.0 + A * CT + B * CT**2 + C * CT**3) * O2sol * np.exp(E * P / K)
right=np.nan) test_eps_grid[i] = eps[i].flatten() eps_grid[i] = np.interp(interp_coarse_pressure, eps_pressure[i].flatten(), eps[i].flatten(), left=np.nan, right=np.nan) density_grid = gsw.rho(salinity_grid, consv_temperature_grid, pressure_grid) #density_grid_check = (1 - alpha_grid * (consv_temperature_grid) + beta_grid * (salinity_grid))*rho_0 #difference = density_grid-density_grid_check #calculate N^2 with the gsw toolbox BV_freq_squared_grid_gsw, midpoint_pressure_grid = gsw.Nsquared( salinity_grid, consv_temperature_grid, pressure_grid, lat=np.mean(lat), axis=1) test_N_gsw, test_eps_pressure_grid = gsw.Nsquared(test_salinity_grid, test_consv_temperature_grid, test_pressure_grid, lat=np.mean(lat), axis=1) eps_mid_point_pressure = np.mean(test_eps_pressure_grid, axis=0) print("\n", eps_pressure[0].flatten(), np.shape(eps_pressure[0].flatten())) print("\n", test_pressure, np.shape(test_pressure)) print("\n", eps_mid_point_pressure, np.shape(eps_mid_point_pressure)) assert (np.all(eps_mid_point_pressure == eps_pressure[0].flatten()))
lat = 55 lon = 16 Pbin = np.arange(0.5, 85, 1) MSS_S1_1_dict = loadmat('./data/MSS_DATA/S1_1.mat', squeeze_me=True, struct_as_record=False) Zmss = MSS_S1_1_dict['CTD'][2].P Tmss = MSS_S1_1_dict['CTD'][2].T Smss = MSS_S1_1_dict['CTD'][2].S digitized = np.digitize(Zmss, Pbin) #<- this is awesome! TTbin = np.array([Tmss[digitized == i].mean() for i in range(0, len(Pbin))]) SSbin = np.array([Smss[digitized == i].mean() for i in range(0, len(Pbin))]) SA_MSS_01 = gsw.SA_from_SP_Baltic(SSbin, lon, lat) CT_MSS_01 = gsw.CT_from_t(SA_MSS_01, TTbin, Pbin) SIG0_MSS_01 = gsw.sigma0(SA_MSS_01, CT_MSS_01) N2_MSS_01 = gsw.Nsquared(SA_MSS_01, CT_MSS_01, Pbin, lat) import SW_extras as swe N2_01 = N2_MSS_01[0] zN2_01 = N2_MSS_01[1] N2_period_01 = 60.0 / swe.cph(N2_01) MSS_S1_2_dict = loadmat('./data/MSS_DATA/S1_2.mat', squeeze_me=True, struct_as_record=False) Zmss = MSS_S1_2_dict['CTD'][2].P Tmss = MSS_S1_2_dict['CTD'][2].T Smss = MSS_S1_2_dict['CTD'][2].S digitized = np.digitize(Zmss, Pbin) #<- this is awesome! TTbin = np.array([Tmss[digitized == i].mean() for i in range(0, len(Pbin))]) SSbin = np.array([Smss[digitized == i].mean() for i in range(0, len(Pbin))]) SA_MSS_02 = gsw.SA_from_SP_Baltic(SSbin, lon, lat)
def compute_thermodynamics(prof): """ Perform a pre-processing on the glider data """ # 1) Make a nice time record for the profile max_depth = prof['P'].max(dim='NT') bottom = prof['P'].argmax(dim='NT') record = prof['time_since_start_of_dive'] deltat_bottom = record.data[bottom].astype('f8') deltat_total = record.data[-1].astype('f8') alpha = deltat_bottom / deltat_total t_start = prof.GPS_time[0].data t_stop = prof.GPS_time[1].data t_bottom = t_start + pd.to_timedelta( alpha * (t_stop - t_start).astype('f8')) time_profile = t_bottom + pd.to_timedelta( prof['time_since_start_of_dive'] - deltat_bottom, unit='s') prof = prof.rename({'NT': 'time'}).assign_coords( time=('time', time_profile)) # 2) Get the coordinates of the profile lat_start = prof.GPS_latitude[0].data lat_stop = prof.GPS_latitude[1].data lat_bottom = lat_start + alpha * (lat_stop - lat_start) lon_start = prof.GPS_longitude[0].data lon_stop = prof.GPS_longitude[1].data lon_bottom = lon_start + alpha * (lon_stop - lon_start) distance_start_to_bottom = 1e-3 * gsw.distance([lon_start, lon_bottom], [lat_start, lat_bottom], p=[0, max_depth]).squeeze() distance_bottom_to_stop = 1e-3 * gsw.distance([lon_bottom, lon_stop], [lat_bottom, lat_stop], p=[max_depth, 0]).squeeze() # 3) Clean up unvalid data niceT = prof['T'] niceS = prof['S'] # Do not forget to correct the offset due to surface pressure niceP = (prof['P'] - prof['Psurf']) niceDive = prof['dive'] # 4) Compute thermodynamic quantities from GSW toolbox # - Absolute Salinity SA = gsw.SA_from_SP(niceS, niceP, lat_start, lon_start) # - Conservative Temperature CT = gsw.CT_from_t(SA, niceT, niceP) # - In situ density rho = gsw.rho(SA, CT, niceP) # - Potential density referenced to surface pressure sigma0 = gsw.sigma0(SA, CT) # - Buoyancy b = 9.81 * (1 - rho / 1025) N2 = xr.DataArray(gsw.Nsquared(SA, CT, niceP)[0], name='Buoyancy frequency', dims='time', coords = {'time': ('time', prof['time'].isel(time=slice(1, None))) } ) # - Depth depth = - gsw.z_from_p(niceP, lat_start) # 5) Split the dive into one descending and one ascending path bottom = niceP.argmax(dim='time') ones = xr.ones_like(niceP) newdive = xr.concat([2 * niceDive[:bottom] - 1, 2 * niceDive[bottom:]], dim='time') lat = xr.concat([0.5 * (lat_start + lat_bottom) * ones[:bottom], 0.5 * (lat_stop + lat_bottom) * ones[bottom:]], dim='time') lon = xr.concat([0.5 * (lon_start + lon_bottom) * ones[:bottom], 0.5 * (lon_stop + lon_bottom) * ones[bottom:]], dim='time') Pmax = xr.concat([max_depth * ones[:bottom], max_depth * ones[bottom:]], dim='time') distance = xr.concat([distance_start_to_bottom * ones[:bottom], distance_bottom_to_stop * ones[bottom:]], dim='time') distance.name = 'distance between profiles in km' return xr.Dataset({'Temperature': niceT, 'Salinity': niceS, 'Pressure': niceP, 'Rho': ('time', rho), 'CT': ('time', CT), 'SA': ('time', SA), 'Sigma0': ('time', sigma0), 'b': ('time', b), 'Pmax': ('time', Pmax), 'N2': N2}, coords={'profile': ('time', newdive.data), 'depth': ('time', depth), 'lat': ('time', lat), 'lon': ('time', lon), 'distance': distance})
for t, year in enumerate(index_unique): #time loop T = df_Ttmp[df_Ttmp.index.year == year].values.squeeze() S = df_Stmp[df_Stmp.index.year == year].values.squeeze() if T.size < 1: # no data for this year pass else: Z = df_Ttmp.columns.values T[(~np.isnan(T)) & (~np.isnan(S))] TT = T[(~np.isnan(T)) & (~np.isnan(S))] SS = S[(~np.isnan(T)) & (~np.isnan(S))] ZZ = Z[(~np.isnan(T)) & (~np.isnan(S))] SA = gsw.SA_from_SP(SS, ZZ, xx, yy) CT = gsw.CT_from_t(SA, TT, ZZ) N2, pmid = gsw.Nsquared(SA, CT, ZZ, yy) yearVec.append(year) TVec.append(np.nanmean(TT)) SVec.append(np.nanmean(SS)) NVec.append(np.sqrt(np.nanmean(N2))) # compute trends if serie long enough x = np.array(yearVec) y = np.array(TVec) idx = np.where(~np.isnan(y)) if np.size(idx) > 5: m, b, r_value, p_value, std_err = stats.linregress( x[idx], y[idx]) Tmap[j, i] = m y = np.array(SVec)
def post_process(self, verbose=True): print("\nPost processing") print("---------------\n") # Very basic self.ascent = self.hpid % 2 == 0 self.ascent_ctd = self.ascent * np.ones_like(self.UTC, dtype=int) self.ascent_ef = self.ascent * np.ones_like(self.UTCef, dtype=int) # Estimate number of observations. self.nobs_ctd = np.sum(~np.isnan(self.UTC), axis=0) self.nobs_ef = np.sum(~np.isnan(self.UTCef), axis=0) # Figure out some useful times. self.UTC_start = self.UTC[0, :] self.UTC_end = np.nanmax(self.UTC, axis=0) if verbose: print("Creating time variable dUTC with units of seconds.") self.dUTC = (self.UTC - self.UTC_start) * 86400 self.dUTCef = (self.UTCef - self.UTC_start) * 86400 if verbose: print("Interpolated GPS positions to starts and ends of profiles.") # GPS interpolation to the start and end time of each half profile. idxs = ~np.isnan(self.lon_gps) & ~np.isnan(self.lat_gps) self.lon_start = np.interp(self.UTC_start, self.utc_gps[idxs], self.lon_gps[idxs]) self.lat_start = np.interp(self.UTC_start, self.utc_gps[idxs], self.lat_gps[idxs]) self.lon_end = np.interp(self.UTC_end, self.utc_gps[idxs], self.lon_gps[idxs]) self.lat_end = np.interp(self.UTC_end, self.utc_gps[idxs], self.lat_gps[idxs]) if verbose: print("Calculating heights.") # Depth. self.z = gsw.z_from_p(self.P, self.lat_start) # self.z_ca = gsw.z_from_p(self.P_ca, self.lat_start) self.zef = gsw.z_from_p(self.Pef, self.lat_start) if verbose: print("Calculating distance along trajectory.") # Distance along track from first half profile. self.__ddist = utils.lldist(self.lon_start, self.lat_start) self.dist = np.hstack((0., np.cumsum(self.__ddist))) if verbose: print("Interpolating distance to measurements.") # Distances, velocities and speeds of each half profile. self.profile_ddist = np.zeros_like(self.lon_start) self.profile_dt = np.zeros_like(self.lon_start) self.profile_bearing = np.zeros_like(self.lon_start) lons = np.zeros((len(self.lon_start), 2)) lats = lons.copy() times = lons.copy() lons[:, 0], lons[:, 1] = self.lon_start, self.lon_end lats[:, 0], lats[:, 1] = self.lat_start, self.lat_end times[:, 0], times[:, 1] = self.UTC_start, self.UTC_end self.dist_ctd = self.UTC.copy() nans = np.isnan(self.dist_ctd) for i, (lon, lat, time) in enumerate(zip(lons, lats, times)): self.profile_ddist[i] = utils.lldist(lon, lat) # Convert time from days to seconds. self.profile_dt[i] = np.diff(time) * 86400. d = np.array([self.dist[i], self.dist[i] + self.profile_ddist[i]]) idxs = ~nans[:, i] self.dist_ctd[idxs, i] = np.interp(self.UTC[idxs, i], time, d) self.dist_ef = self.__regrid('ctd', 'ef', self.dist_ctd) if verbose: print("Estimating bearings.") # Pythagorian approximation (?) of bearing. self.profile_bearing = np.arctan2(self.lon_end - self.lon_start, self.lat_end - self.lat_start) if verbose: print("Calculating sub-surface velocity.") # Convert to m s-1 calculate meridional and zonal velocities. self.sub_surf_speed = self.profile_ddist * 1000. / self.profile_dt self.sub_surf_u = self.sub_surf_speed * np.sin(self.profile_bearing) self.sub_surf_v = self.sub_surf_speed * np.cos(self.profile_bearing) if verbose: print("Interpolating missing velocity values.") # Fill missing U, V values using linear interpolation otherwise we # run into difficulties using cumtrapz next. self.U = self.__fill_missing(self.U) self.V = self.__fill_missing(self.V) # Absolute velocity self.calculate_absolute_velocity(verbose=verbose) if verbose: print("Calculating thermodynamic variables.") # Derive some important thermodynamics variables. # Absolute salinity. self.SA = gsw.SA_from_SP(self.S, self.P, self.lon_start, self.lat_start) # Conservative temperature. self.CT = gsw.CT_from_t(self.SA, self.T, self.P) # Potential temperature with respect to 0 dbar. self.PT = gsw.pt_from_CT(self.SA, self.CT) # In-situ density. self.rho = gsw.rho(self.SA, self.CT, self.P) # Potential density with respect to 1000 dbar. self.rho_1 = gsw.pot_rho_t_exact(self.SA, self.T, self.P, p_ref=1000.) # Buoyancy frequency regridded onto ctd grid. N2_ca, __ = gsw.Nsquared(self.SA, self.CT, self.P, self.lat_start) self.N2 = self.__regrid('ctd_ca', 'ctd', N2_ca) if verbose: print("Calculating float vertical velocity.") # Vertical velocity regridded onto ctd grid. dt = 86400. * np.diff(self.UTC, axis=0) # [s] Wz_ca = np.diff(self.z, axis=0) / dt self.Wz = self.__regrid('ctd_ca', 'ctd', Wz_ca) if verbose: print("Renaming Wp to Wpef.") # Vertical water velocity. self.Wpef = self.Wp.copy() del self.Wp if verbose: print("Calculating shear.") # Shear calculations. dUdz_ca = np.diff(self.U, axis=0) / np.diff(self.zef, axis=0) dVdz_ca = np.diff(self.V, axis=0) / np.diff(self.zef, axis=0) self.dUdz = self.__regrid('ef_ca', 'ef', dUdz_ca) self.dVdz = self.__regrid('ef_ca', 'ef', dVdz_ca) if verbose: print("Calculating Richardson number.") N2ef = self.__regrid('ctd', 'ef', self.N2) self.Ri = N2ef / (self.dUdz**2 + self.dVdz**2) if verbose: print("Regridding piston position to ctd.\n") # Regrid piston position. self.ppos = self.__regrid('ctd_ca', 'ctd', self.ppos_ca) self.update_profiles()
print("- Renaming variables and calculating new ones") TY["T"] = TY.pop("t1") TY["P"] = TY.pop("p") TY["S"] = TY.pop("s1") TY["spd"] = np.sqrt(TY["u"]**2 + TY["v"]**2) TY["z"] = -np.tile(TY["z"][:, np.newaxis].astype(float), (1, Np)) TY["depth"] = -TY["z"] TY["SA"] = gsw.SA_from_SP(TY["S"], TY["P"], TY["lon"], TY["lat"]) TY["CT"] = gsw.CT_from_t(TY["SA"], TY["T"], TY["P"]) TY["sig4"] = gsw.pot_rho_t_exact(TY["SA"], TY["T"], TY["P"], 4000) TY["g"] = gsw.grav(TY["lat"], TY["P"]) TY["b"] = -TY["g"] * (TY["sig4"] - sig4b) / sig4b TY["sig4m"] = np.nanmean(TY["sig4"]) TY["sig4_sorted"] = utils.nansort(TY["sig4"], axis=0) TY["b_sorted"] = -TY["g"] * (TY["sig4_sorted"] - sig4b) / sig4b TY["N2"], TY["P_mid"] = gsw.Nsquared(TY["SA"], TY["CT"], TY["P"], np.nanmean(TY["lat"], axis=0)) TY["z_mid"] = gsw.z_from_p(TY["P_mid"], TY["mlat"]) ddist = utils.lldist(TY["mlon"], TY["mlat"]) TY["dist"] = np.hstack((0, np.cumsum(ddist))) TY["dist_r"] = -(TY["dist"] - TY["dist"].max()) print("- Estimating distances") lon = TY["lon"].flatten() lat = TY["lat"].flatten() time = TY["datenum"].flatten() use = np.isfinite(lon) & np.isfinite(lat) & np.isfinite(time) idxs = np.argsort(time[use]) time_ = time[use][idxs] lon_ = lon[use][idxs] lat_ = lat[use][idxs] d = utils.lldist(lon_[::dc], lat_[::dc])
def load_clean_interpolate_and_shift_data(datafile_path, shift_value): """ loads a a .mat file of mss measurements a few files are cleaned through a hardcoded routine (emb217/TR1-8.mat, emb169/TS118, emb169/TRR109) datafile_path: absolute path of the mss measurements saved as a .mat file return values: number_of_profiles number of profiles/casts in the transect lat latitude in degrees (as a float) of the casts lon longitude in degrees (as a float) of the casts distance distance in km from the starting point of the transect interp_pressure equidistant pressure array between the highest and the lowest measured pressure value pressure_grid oxygen_grid oxygen_sat_grid oxygen saturation in percent as a grid (number_of_profiles x len(interp_pressure)) salinity_grid salinity in g/kg as a grid (number_of_profiles x len(interp_pressure)) consv_temperature_grid conservative temperature in degrees Celsius as a grid (number_of_profiles x len(interp_pressure)) density_grid density in kg/m^3 as a grid (number_of_profiles x len(interp_pressure)) eps_pressure 1D array of pressure values to the dissipation rate values (the pressure distance between points is bigger than in interp_pressure) eps_pressure_grid eps_grid measured dissipation rate values (number_of_profiles x len(eps_pressure)) eps_salinity_grid salinity in g/kg as a grid (number_of_profiles x len(eps_pressure)) eps_consv_temperature_grid conservative temperature as a grid (number_of_profiles x len(eps_pressure)) eps_oxygen_grid eps_oxygen_sat_grid oxygen saturation as a grid (number_of_profiles x len(eps_pressure)) eps_N_squared_grid N^2, the Brunt-Vaisala frequency in 1/s^2 as a grid (number_of_profiles x len(eps_pressure)) eps_density_grid density in kg/m^3 as a grid (number_of_profiles x len(eps_pressure)) 0 error code, returned if distance is not monotonically increasing (ergo a loop in the data) """ import scipy.io as sio import geopy.distance as geo import numpy as np import gsw splitted_path = datafile_path.split("/") cruisename = splitted_path[4][0:6] DATAFILENAME = splitted_path[-1] transect_name = DATAFILENAME[:-4] print(cruisename + "_" + transect_name) #print("Filename:",sio.whosmat(FILENAME)) data = sio.loadmat(datafile_path) STA_substructure = data["STA"] DATA_substructure = data["DATA"] MIX_substructure = data["MIX"] CTD_substructure = data["CTD"] #print(STA_substructure.dtype) #print(DATA_substructure.dtype) #print(CTD_substructure.dtype) #print(MIX_substructure.dtype) lat = STA_substructure["LAT"][0] lon = STA_substructure["LON"][0] pressure = CTD_substructure["P"][0] absolute_salinity = CTD_substructure["SA"][0] #is this unit sufficient consv_temperature = CTD_substructure["CT"][ 0] #TODO better use conservative temperature? alpha = CTD_substructure["ALPHA"][0] beta = CTD_substructure["BETA"][0] if cruisename == "emb217": oxygen_sat = CTD_substructure["O2"][0] elif cruisename == "emb177": try: oxygen_data = sio.loadmat( "/home/ole/windows/all_data/emb177/deployments/ship/mss/mss38/TODL/TODL_merged_oxy/" + transect_name + "_TODL_merged_oxy.mat") except OSError: print("##########################################") print(cruisename, transect_name, "is skipped!") print("No oxygen data for this file") print("##########################################") return 0 #the following code block is written by Peter Holtermann and Copy pasted here emb177_oxygen = [] emb177_oxygen_pressure = [] for icast in range(len(lon)): oxy_p_temp = oxygen_data['TODL_MSS']['P'][0, :][icast][0, :] #mT = oxygen_data['TODL_MSS']['mT'][0,:][icast][0,:] #C = oxygen_data['TODL_MSS']['C'][0,:][icast][0,:] oxy = oxygen_data['TODL_MSS']['oxy'][0, :][icast][:, 4] emb177_oxygen.append(oxy) emb177_oxygen_pressure.append(oxy_p_temp) elif cruisename == "emb169": try: oxygen_data = sio.loadmat( "/home/ole/windows/all_data/emb169/deployments/ship/mss/TODL/merged/" + transect_name + "_TODL_merged_oxy.mat") except OSError: print("##########################################") print(cruisename, transect_name, "is skipped!") print("No oxygen data for this file") print("##########################################") return 0 #the following code block is written by Peter Holtermann and Copy pasted here emb169_oxygen = [] emb169_oxygen_pressure = [] for icast in range(len(lon)): oxy_p_temp = oxygen_data['TODL_MSS']['P'][0, :][icast][0, :] #mT = oxygen_data['TODL_MSS']['mT'][0,:][icast][0,:] #C = oxygen_data['TODL_MSS']['C'][0,:][icast][0,:] oxy = oxygen_data['TODL_MSS']['oxy'][0, :][icast][:, 4] emb169_oxygen.append(oxy) emb169_oxygen_pressure.append(oxy_p_temp) else: raise AssertionError eps = MIX_substructure["eps"][0] eps_pressure = MIX_substructure["P"][0] number_of_profiles = np.shape(pressure)[-1] latitude = [] longitude = [] #calculates the distance in km of the position from every profile to the position of the starting profile. distance = np.zeros(number_of_profiles) origin = (float(lat[0][0][0]), float(lon[0][0][0]) ) #lots of brackets to get a number, not an array (workaround) for i in range(number_of_profiles): current_point = (float(lat[i][0][0]), float(lon[i][0][0])) latitude.append(float(lat[i][0][0])) longitude.append(float(lon[i][0][0])) distance[i] = geo.geodesic( origin, current_point).km #Distance in km, change to nautical miles? lat = np.asarray(latitude) lon = np.asarray(longitude) #Data Cleaning #----------------------------------------------------------------------------------------------------------------- #remove data from a file, with overlapping positional points if cruisename == "emb217" and transect_name == "TR1-8": lat = np.delete(lat, np.s_[33:47]) lon = np.delete(lon, np.s_[33:47]) distance = np.delete(distance, np.s_[33:47]) pressure = np.delete(pressure, np.s_[33:47], axis=0) oxygen_sat = np.delete(oxygen_sat, np.s_[33:47], axis=0) absolute_salinity = np.delete(absolute_salinity, np.s_[33:47], axis=0) consv_temperature = np.delete(consv_temperature, np.s_[33:47], axis=0) alpha = np.delete(alpha, np.s_[33:47], axis=0) beta = np.delete(beta, np.s_[33:47], axis=0) eps = np.delete(eps, np.s_[33:47], axis=0) eps_pressure = np.delete(eps_pressure, np.s_[33:47], axis=0) number_of_profiles = np.shape(pressure)[-1] if cruisename == "emb169" and DATAFILENAME[:-4] == "TS118": lat = lat[:21] lon = lon[:21] distance = distance[:21] pressure = pressure[:21] oxygen_sat = oxygen_sat[:21] absolute_salinity = absolute_salinity[:21] consv_temperature = consv_temperature[:21] alpha = alpha[:21] beta = beta[:21] eps = eps[:21] eps_pressure = eps_pressure[:21] number_of_profiles = np.shape(pressure)[-1] #removes the last data point, that dont seem to belong to the transect if cruisename == "emb169" and DATAFILENAME[:-4] == "TRR109": lat = lat[:-1] lon = lon[:-1] distance = distance[:-1] pressure = pressure[:-1] oxygen_sat = oxygen_sat[:-1] absolute_salinity = absolute_salinity[:-1] consv_temperature = consv_temperature[:-1] alpha = alpha[:-1] beta = beta[:-1] eps = eps[:-1] eps_pressure = eps_pressure[:-1] number_of_profiles = np.shape(pressure)[-1] #----------------------------------------------------------------------------------------------------------------- #test if distance is monotonically increasing try: assert (np.all(np.diff(distance) > 0)) except AssertionError: print("##########################################") print(cruisename, DATAFILENAME[:-4], "is skipped!") print( "Distance is not monotonically increasing. Mabye due to a loop in the transect?" ) print("##########################################") return 0 #continue #jump to the next datafile #TODO point density? #initial values min_pressure = 10 max_pressure = 60 max_size = 1000 min_size = 3000 #select the min and max values for the equidistant pressure array (later used for the grid) for i in range(number_of_profiles): if np.nanmin(pressure[i]) < min_pressure: min_pressure = np.nanmin(pressure[i]) if np.nanmax(pressure[i]) > max_pressure: max_pressure = np.nanmax(pressure[i]) if pressure[i].size > max_size: max_size = pressure[i].size if pressure[i].size < min_size: min_size = pressure[i].size #print("High resolution",min_pressure,max_pressure,min_size,max_size) #check if that worked correctly assert (max_size >= min_size) #creates a pressure axis between the maximum and minimum pressure interp_pressure = np.linspace(min_pressure, max_pressure, min_size) #creates pressure grid, where every column is equal to interp_pressure pressure_grid = np.reshape(interp_pressure, (1, -1)) * np.ones( (np.shape(pressure)[-1], min_size)) #create grids that changes in distance on x and in depth on y-axis salinity_grid = np.zeros((np.shape(pressure)[-1], min_size)) consv_temperature_grid = np.copy(salinity_grid) alpha_grid = np.copy(salinity_grid) beta_grid = np.copy(salinity_grid) if cruisename == "emb217": oxygen_sat_grid = np.copy(salinity_grid) elif cruisename == "emb177" or cruisename == "emb169": oxygen_grid = np.copy(salinity_grid) #check if the pressure points for every eps profile are the same for i in range(number_of_profiles): assert np.all(eps_pressure[i].flatten() == eps_pressure[0].flatten()) #if yes the pressure can be coverted to a 1D array instead of a 2D array eps_pressure = eps_pressure[0].flatten() #print(eps[i].flatten().size,eps_pressure.size, np.arange(1,160.5,0.5).size, np.arange(1,160.5,0.5).size - eps_pressure.size) #print(np.shape(eps),np.shape(eps[0])) desired_pressure_array = np.arange( 1, 160.5, 0.5 ) #stems from the orginal matlab script that process the raw MSS data last_element = eps_pressure[-1] old_size = eps_pressure.size if last_element < 160: for index, element in enumerate(eps): eps[index] = np.pad( eps[index], ((0, desired_pressure_array.size - eps_pressure.size), (0, 0)), 'constant', constant_values=np.nan) eps_pressure = np.append(eps_pressure, np.arange(last_element + 0.5, 160.5, 0.5)) assert (eps[0].flatten().size == eps_pressure.size) #averaged of approx 5 depth bins (???) eps_grid = np.zeros((number_of_profiles, eps_pressure.size)) #needed for the interpolation of S and T to the same grid as the eps eps_salinity_grid = np.ones((np.shape(eps_grid))) eps_consv_temperature_grid = np.ones(np.shape(eps_grid)) if cruisename == "emb217": eps_oxygen_sat_grid = np.copy(eps_salinity_grid) elif cruisename == "emb177" or cruisename == "emb169": eps_oxygen_grid = np.copy(eps_salinity_grid) #vector times matrix multiplication to get a 2D array, where every column is equal to eps_pressure eps_pressure_grid = np.reshape(eps_pressure, (1, -1)) * np.ones(np.shape(eps_grid)) #create a pressure axis where every point is shifted by half the distance to the next one shifted_pressure = eps_pressure + np.mean(np.diff(eps_pressure)) / 2 #prepend a point at the beginning to be a point longer than the pressure axis we want from the Nsquared function shifted_pressure = np.append( eps_pressure[0] - np.mean(np.diff(eps_pressure)) / 2, shifted_pressure) #from that create a grid, where we have just n-times the pressure axis with n the number of profiles shifted_pressure_grid = np.reshape(shifted_pressure, (1, -1)) * np.ones( (number_of_profiles, shifted_pressure.size)) shifted_salinity_grid = np.ones((np.shape(shifted_pressure_grid))) shifted_consv_temperature_grid = np.ones((np.shape(shifted_pressure_grid))) #Grid interpolation (loops over all profiles) for i in range(number_of_profiles): #interpolation to a common fine grid salinity_grid[i] = np.interp(interp_pressure, pressure[i].flatten(), absolute_salinity[i].flatten(), left=np.nan, right=np.nan) consv_temperature_grid[i] = np.interp(interp_pressure, pressure[i].flatten(), consv_temperature[i].flatten(), left=np.nan, right=np.nan) alpha_grid[i] = np.interp(interp_pressure, pressure[i].flatten(), alpha[i].flatten(), left=np.nan, right=np.nan) beta_grid[i] = np.interp(interp_pressure, pressure[i].flatten(), beta[i].flatten(), left=np.nan, right=np.nan) #interpolation to a shifted grid for the Nsquared function shifted_salinity_grid[i] = np.interp(shifted_pressure, pressure[i].flatten(), absolute_salinity[i].flatten(), left=np.nan, right=np.nan) shifted_consv_temperature_grid[i] = np.interp( shifted_pressure, pressure[i].flatten(), consv_temperature[i].flatten(), left=np.nan, right=np.nan) #just changes the format of eps slightly assert (eps[i].flatten().size == eps_pressure.size) eps_grid[i] = eps[i].flatten() #interpolate S and T to the same grid as eps eps_salinity_grid[i] = np.interp(eps_pressure, pressure[i].flatten(), absolute_salinity[i].flatten(), left=np.nan, right=np.nan) eps_consv_temperature_grid[i] = np.interp( eps_pressure, pressure[i].flatten(), consv_temperature[i].flatten(), left=np.nan, right=np.nan) #interpolation of the oxygen depends on which source it is if cruisename == "emb217": oxygen_sat_grid[i] = np.interp(interp_pressure, pressure[i].flatten() + shift_value, oxygen_sat[i].flatten(), left=np.nan, right=np.nan) eps_oxygen_sat_grid[i] = np.interp(eps_pressure, pressure[i].flatten() + shift_value, oxygen_sat[i].flatten(), left=np.nan, right=np.nan) elif cruisename == "emb177": oxygen_grid[i] = np.interp(interp_pressure, emb177_oxygen_pressure[i] + shift_value, emb177_oxygen[i], left=np.nan, right=np.nan) eps_oxygen_grid[i] = np.interp(eps_pressure, emb177_oxygen_pressure[i] + shift_value, emb177_oxygen[i].flatten(), left=np.nan, right=np.nan) elif cruisename == "emb169": oxygen_grid[i] = np.interp(interp_pressure, emb169_oxygen_pressure[i] + shift_value, emb169_oxygen[i], left=np.nan, right=np.nan) eps_oxygen_grid[i] = np.interp(eps_pressure, emb169_oxygen_pressure[i] + shift_value, emb169_oxygen[i].flatten(), left=np.nan, right=np.nan) if cruisename == "emb217": assert (np.shape(oxygen_sat_grid) == np.shape(salinity_grid)) assert (np.shape(eps_oxygen_sat_grid) == np.shape(eps_salinity_grid)) if cruisename == "emb177" or cruisename == "emb169": assert (np.shape(oxygen_grid) == np.shape(salinity_grid)) assert (np.shape(eps_oxygen_grid) == np.shape(eps_salinity_grid)) #fine density grid density_grid = gsw.rho(salinity_grid, consv_temperature_grid, pressure_grid) pot_density_grid = 1000 + gsw.density.sigma0(salinity_grid, consv_temperature_grid) #density grid on the same points as the eps grid eps_density_grid = gsw.rho(eps_salinity_grid, eps_consv_temperature_grid, eps_pressure_grid) eps_pot_density_grid = 1000 + gsw.density.sigma0( eps_salinity_grid, eps_consv_temperature_grid) #TODO compare with density_grid? #density_grid_check = (1 - alpha_grid * (consv_temperature_grid) + beta_grid * (salinity_grid))*rho_0 #difference = density_grid-density_grid_check #calculate N^2 with the gsw toolbox, by using the shifted grid we should get a N^2 grid that is defined at the same points as eps_grid eps_N_squared_grid, crosscheck_pressure_grid = gsw.Nsquared( shifted_salinity_grid, shifted_consv_temperature_grid, shifted_pressure_grid, lat=np.mean(lat), axis=1) crosscheck_pressure = np.mean(crosscheck_pressure_grid, axis=0) #test if we really calculated N^2 for the same points as pressure points from the dissipation measurement assert (np.all(crosscheck_pressure == eps_pressure)) #create grids that have the latitude/longitude values for every depth (size: number_of_profiles x len(interp_pressure)) lat_grid = np.reshape(lat, (-1, 1)) * np.ones( (number_of_profiles, max_size)) lon_grid = np.reshape(lon, (-1, 1)) * np.ones( (number_of_profiles, max_size)) #check if lon grid was correctly created assert (np.all(lon_grid[:, 0] == lon)) #create grids that have the latitude/longitude values for every depth (size: number_of_profiles x len(eps_pressure)) eps_lat_grid = np.reshape(lat, (-1, 1)) * np.ones( (number_of_profiles, eps_pressure.size)) eps_lon_grid = np.reshape(lat, (-1, 1)) * np.ones( (number_of_profiles, eps_pressure.size)) if cruisename == "emb217": #convert oxygen saturation to oxygen concentration (functions are selfwritten but use the gsw toolbox, for more informations see the function (also in this file)) oxygen_grid = thesis.oxygen_saturation_to_concentration( oxygen_sat_grid, salinity_grid, consv_temperature_grid, pressure_grid, lat_grid, lon_grid) eps_oxygen_grid = thesis.oxygen_saturation_to_concentration( eps_oxygen_sat_grid, eps_salinity_grid, eps_consv_temperature_grid, eps_pressure_grid, eps_lat_grid, eps_lon_grid) elif cruisename == "emb177": #scale the oxygen to be at 100% at the surface maximum_concentration_grid = gsw.O2sol(salinity_grid, consv_temperature_grid, pressure_grid, lat_grid, lon_grid) correction_factor = np.ones((number_of_profiles, 1)) for profile in range(number_of_profiles): for i in range(100): if np.isnan(oxygen_grid[profile, i]) or np.isnan( maximum_concentration_grid[profile, i]): continue else: correction_factor[profile] = maximum_concentration_grid[ profile, i] / oxygen_grid[profile, i] break oxygen_grid = oxygen_grid * correction_factor eps_oxygen_grid = eps_oxygen_grid * correction_factor #convert oxygen concentration to oxygen saturation (functions are selfwritten but use the gsw toolbox, for more informations see the function (also in this file)) oxygen_sat_grid = thesis.oxygen_concentration_to_saturation( oxygen_grid, salinity_grid, consv_temperature_grid, pressure_grid, lat_grid, lon_grid) eps_oxygen_sat_grid = thesis.oxygen_concentration_to_saturation( eps_oxygen_grid, eps_salinity_grid, eps_consv_temperature_grid, eps_pressure_grid, eps_lat_grid, eps_lon_grid) elif cruisename == "emb169": #convert oxygen concentration to oxygen saturation (functions are selfwritten but use the gsw toolbox, for more informations see the function (also in this file)) oxygen_sat_grid = thesis.oxygen_concentration_to_saturation( oxygen_grid, salinity_grid, consv_temperature_grid, pressure_grid, lat_grid, lon_grid) eps_oxygen_sat_grid = thesis.oxygen_concentration_to_saturation( eps_oxygen_grid, eps_salinity_grid, eps_consv_temperature_grid, eps_pressure_grid, eps_lat_grid, eps_lon_grid) else: raise AssertionError return [[number_of_profiles, lat, lon, distance], [ interp_pressure, oxygen_sat_grid, oxygen_grid, salinity_grid, consv_temperature_grid, density_grid, pot_density_grid ], [ eps_pressure, eps_oxygen_sat_grid, eps_oxygen_grid, eps_grid, eps_salinity_grid, eps_consv_temperature_grid, eps_N_squared_grid, eps_density_grid, eps_pot_density_grid ]]
exec('sx_' + var + '[ppitch<0] = sx_' + var + '[ppitch<0].fillna(method="ffill", limit=10, axis=1)') exec('sx_' + var + '[ppitch>0] = sx_' + var + '[ppitch>0].fillna(method="bfill", limit=10, axis=1)') exec('sx_' + var + ' = sx_' + var + '[~pd.isna(sx_' + var + '.iloc[:,0:50].mean(axis=1))]') # correction specific for this deployment print(' Done!') # TEOS-10 conversion SeaExplorer P = sx_pressure.values lon0, lat0 = ds['longitude'].values.mean(), ds['latitude'].values.mean() SA = gsw.SA_from_SP(sx_salinity.values, P, lon0, lat0) CT = gsw.CT_from_t(SA, sx_temperature.values, P) sigma0 = gsw.sigma0(SA, CT) SA_sort = np.array(list(map(lambda x, y: y[x], np.argsort(sigma0, axis=1), SA))) CT_sort = np.array(list(map(lambda x, y: y[x], np.argsort(sigma0, axis=1), CT))) P_sort = np.array(list(map(lambda x, y: y[x], np.argsort(sigma0, axis=1), P))) N2, p_mid = gsw.Nsquared(SA_sort, CT_sort, P_sort, lat=lat0, axis=1) # Oxygen conversion Ofreq = sx_oxygen_frequency.values Soc = float(dpl['calibration.SBE43F.soc']) Foffset = float(dpl['calibration.SBE43F.foffset']) A = float(dpl['calibration.SBE43F.a']) B = float(dpl['calibration.SBE43F.b']) C = float(dpl['calibration.SBE43F.c']) E = float(dpl['calibration.SBE43F.e']) K = CT + 273.15 # Temp in Kelvin O2sol = gsw.O2sol(SA, CT, P, lon0, lat0); #umol/Kg O2 = Soc*(Ofreq+Foffset)*(1.0+A*CT + B*CT**2 + C*CT**3)*O2sol*np.exp(E*P/K); # MiniFluo calib (# Assume new version)
end_index = c_time.index(a_time[-1]) data = pd.DataFrame({'time': c_time[start_index - 1:end_index + 1]}) data['c_pres'] = pd.Series(c_data["c_pres"][start_index - 1:end_index + 1]) data['c_temp'] = pd.Series(c_data["c_temp"][start_index - 1:end_index + 1]) data['c_sal'] = pd.Series(c_data["c_sal"][start_index - 1:end_index + 1]) data_start = data.index[data["c_pres"] > 10][0] data_end = data.index[data["c_pres"] > 100][0] data = data[data_start:data_end] #a_data = a_data.iloc[data_start:data_end].reset_index() #print(a_data) #Generate the bouyancy frequency plot CT = gsw.CT_from_t(data['c_sal'], data['c_temp'], data['c_pres']) SA = gsw.SA_from_SP(data['c_sal'], data['c_pres'], 174, -43) [N2, p_mid, dp] = gsw.Nsquared(SA, CT, data['c_pres']) Ri_calcs = pd.DataFrame({'pres': p_mid}) Ri_calcs['dp'] = pd.Series(dp, index=Ri_calcs.index) Ri_calcs['N2'] = pd.Series(N2, index=Ri_calcs.index) Ri_calcs['N2'] = Ri_calcs['N2'].mask( ((Ri_calcs['N2'] - Ri_calcs['N2'].mean()).abs() > 2 * Ri_calcs['N2'].std())) Ri_calcs['N2'] = Ri_calcs['N2'].interpolate() #Generate the east, north and up [east, north, up] = beam2ENU([ a_data["a_beam1"][0], a_data["a_beam2"][0], a_data["a_beam3"][0], a_data["a_beam4"][0] ], a_data['a_heading'].values, a_data['a_pitch'].values, a_data['a_roll'].values, a_data['a_vel1'].values,
def strain_adiabatic_leveling(depth, t, SP, lon, lat, bin_width, depth_bin, window_size): """ Calculate strain with a smooth :math:`N^2` profile based on the adiabatic leveling method. 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 depth_bin : array-like Window centers window_size : float Window size Returns ------- list List with results in a dict for each depth segment. The dict has the following entries: ``"segz"`` Segment depth vector (`array-like`). ``"depth_bin"`` Segment center depth (`float`). ``"N2"`` Segment buoyancy frequency squared (`array-like`). ``"N2smooth"`` Segment polynomial fit to buoyancy frequency squared (`array-like`). ``"N2mean"`` Segment mean buoyancy frequency squared (`float`). ``"strain"`` Segment strain (`array-like`). """ dz = np.absolute(np.median(np.diff(depth))) # Prepare output list. It will hold a dictionary with data for each window. out = [] # Calculate pressure pr = gsw.p_from_z(-1 * depth, lat) # Calculate smooth background N^2 N2ref = nsq.adiabatic_leveling( pr, SP, t, lon, lat, bin_width=bin_width, order=2, return_diagnostics=False, cap="both", ) # Calculate in-situ N^2 SA = gsw.SA_from_SP(SP, pr, lon, lat) CT = gsw.CT_from_t(SA, t, pr) N2, Pbar = gsw.Nsquared(SA, CT, pr, lat=lat) depth_mid = -1 * gsw.z_from_p(Pbar, lat) # Interpolate smooth N^2 to in-situ N^2 N2ref = interp1d(pr, N2ref)(Pbar) # Calculate strain strain = (N2 - N2ref) / N2ref isN = np.isfinite(N2) & np.isfinite(N2ref) # Sort into depth windows for iwin, zw in enumerate(depth_bin): # Find data points within the depth window. As we are operating on the # mid-points here, we extend the search for datapoints by delta z on # each side of the window. ij = (depth_mid >= (zw - window_size / 2 - dz)) & (depth_mid < (zw + window_size / 2 + dz)) ij = ij * isN seg = {"depth_bin": zw, "segz": depth_mid[ij], "N2": N2[ij]} seg["N2smooth"] = N2ref[ij] seg["N2mean"] = np.mean(seg["N2smooth"]) seg["strain"] = strain[ij] out.append(seg) return out
def strain_polynomial_fits(depth, t, SP, lon, lat, depth_bin, window_size): """ Calculate strain with a smooth :math:`N^2` profile from polynomial fits to windowed data. This version outputs polyfit and strain for the whole window as opposed to just half the window centered. 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 depth_bin : float Window centers window_size : float Window size Returns ------- list List with results in a dict for each depth segment. The dict has the following entries: ``"segz"`` Segment depth vector (`array-like`). ``"depth_bin"`` Segment center depth (`float`). ``"N2"`` Segment buoyancy frequency squared (`array-like`). ``"N2smooth"`` Segment polynomial fit to buoyancy frequency squared (`array-like`). ``"N2mean"`` Segment mean buoyancy frequency squared (`float`). ``"strain"`` Segment strain (`array-like`). """ # Prepare output list. It will hold a dictionary with data for each window. out = [] dz = np.absolute(np.median(np.diff(depth))) # Calculate buoyancy frequency. pr = gsw.p_from_z(-1 * depth, lat) SA = gsw.SA_from_SP(SP, pr, lon, lat) CT = gsw.CT_from_t(SA, t, pr) N2, pr_mid = gsw.Nsquared(SA, CT, pr, lat=lat) depth_mid = -1 * gsw.z_from_p(pr_mid, lat) isN = np.isfinite(N2) # Second order polynomial fit for each depth window. for iwin, zw in enumerate(depth_bin): # Find data points within the depth window. As we are operating on the # mid-points here, we extend the search for datapoints by delta z on # each side of the window. ij = (depth_mid >= (zw - window_size / 2 - dz)) & (depth_mid < (zw + window_size / 2 + dz)) ij = ij * isN seg = {"depth_bin": zw, "segz": depth_mid[ij], "N2": N2[ij]} p = np.polyfit(depth_mid[ij], N2[ij], deg=2) seg["N2smooth"] = np.polyval(p, depth_mid[ij]) seg["N2mean"] = np.mean(seg["N2smooth"]) # Calculate strain seg["strain"] = (seg["N2"] - seg["N2smooth"]) / seg["N2mean"] out.append(seg) return out