def correct_ts(datadir, filename, corr_coeff_filename): """ Corrects TB Temp and salinity according to correlation coefficients and adds corrected var to ncfile Also converts practical salinity to absolute salinity and conservative temperature to potential temperature """ coeffs = pd.read_csv(corr_coeff_filename, delim_whitespace=True) ls = os.listdir(datadir) ls.sort() for filename in ls: if filename[:11] == 'TB_20181211': # print(filename) data = xr.open_dataset(os.path.join(datadir, filename)) varkey = [i for i in data.data_vars.keys()] if 'lon' in varkey: print(filename) data['t_corrected']=('DEPTH', \ (coeffs.a_temp.values*data.TEMP+(coeffs.b_temp.values))) data['s_corrected']=('DEPTH', \ (coeffs.a_sal.values*data.PSAL+(coeffs.b_sal.values))) data['ab_sal']=('DEPTH', \ gsw.SA_from_SP(data.s_corrected,data.DEPTH,data.lon.values,data.lat.values)) data['ptemp']=('DEPTH', \ gsw.pt_from_CT(data.ab_sal,data.TEMP)) data['ab_sal_bal']=('DEPTH', \ gsw.SA_from_SP_Baltic(data.s_corrected,data.lon.values,data.lat.values)) data['ptemp_bal']=('DEPTH', \ gsw.pt_from_CT(data.ab_sal_bal,data.TEMP)) data.to_netcdf( os.path.join('Data/ctd_files/gridded_calibrated_updated', filename))
def oned_moxy(tsal, ttemp, tdic, tta, pres_atm, depth_this): ### GET pCO2 (and Omega, etc) GIVEN DIC, TA import sys sys.path.append('/data/tjarniko/mocsy') import mocsy import numpy as np import gsw size_box = np.shape(tdic) size_0 = size_box[0] size_1 = size_box[1] tsra = np.ravel(tsal) ttera = np.ravel(ttemp) #convert cons. temperature to potential temperature ttera_pot = gsw.pt_from_CT(tsra, ttera) ttara = np.ravel(tta) * 1e-3 tdra = np.ravel(tdic) * 1e-3 tzero = np.zeros_like(tsra) tpressure = np.zeros_like(tsra) #tdepth = np.zeros_like(tsra) tpressure[:] = pres_atm tdepth = np.ravel(depth_this) tzero = tpressure * 0 tsra_psu = tsra * 35 / 35.16504 #ttera_is = gsw.t_from_CT(tsra,ttera,tzero) response_tup = mocsy.mvars(temp=ttera_pot, sal=tsra_psu, alk=ttara, dic=tdra, sil=tzero, phos=tzero, patm=tpressure, depth=tdepth, lat=tzero, optcon='mol/m3', optt='Tpot', optp='m', optb='l10', optk1k2='m10', optkf='dg', optgas='Pinsitu') pH, pco2, fco2, co2, hco3, co3, OmegaA, OmegaC, BetaD, DENis, p, Tis = response_tup pHr = pH.reshape(size_0, size_1) OmAr = OmegaA.reshape(size_0, size_1) pco2r = pco2.reshape(size_0, size_1) return pHr, OmAr, pco2r
def convert_ts(datadir, filename): """ Function to convert Practical salinity to Absolute salinity and Conservative Temperature to potential temperature """ ls = os.listdir(datadir) ls.sort() for filename in ls: if filename[:11] == 'SK_20181210': # print(filename) data = xr.open_dataset(os.path.join(datadir, filename)) print(filename) data['ab_sal']=('DEPTH', \ gsw.SA_from_SP(data.PSAL,data.DEPTH,data.lon.values,data.lat.values)) data['ptemp']=('DEPTH', \ gsw.pt_from_CT(data.ab_sal,data.TEMP)) data['ab_sal_bal']=('DEPTH', \ gsw.SA_from_SP_Baltic(data.PSAL,data.lon.values,data.lat.values)) data['ptemp_bal']=('DEPTH', \ gsw.pt_from_CT(data.ab_sal_bal,data.TEMP)) data.to_netcdf( os.path.join('Data/ctd_files/gridded_calibrated_updated', filename))
def point_moxy(tsal, ttemp, tdic, tta, pres_atm, depth_this): ### GET pCO2 (and Omega, etc) GIVEN DIC, TA import sys sys.path.append('/data/tjarniko/mocsy') import mocsy import numpy as np import gsw tsra = (tsal) ttera = (ttemp) #convert cons. temperature to potential temperature ttera_pot = gsw.pt_from_CT(tsra, ttera) ttara = (tta) * 1e-3 tdra = (tdic) * 1e-3 tzero = 0 tpressure = pres_atm tdepth = (depth_this) tsra_psu = tsra * 35 / 35.16504 #ttera_is = gsw.t_from_CT(tsra,ttera,tzero) response_tup = mocsy.mvars(temp=ttera_pot, sal=tsra_psu, alk=ttara, dic=tdra, sil=tzero, phos=tzero, patm=tpressure, depth=tdepth, lat=tzero, optcon='mol/m3', optt='Tpot', optp='m', optb='l10', optk1k2='m10', optkf='dg', optgas='Pinsitu') pH, pco2, fco2, co2, hco3, co3, OmegaA, OmegaC, BetaD, DENis, p, Tis = response_tup pHr = pH OmAr = OmegaA pco2r = pco2 return pHr, OmAr, pco2r
def text_to_netcdf(inDir, outDir): inFileName = '{}/Antarctic_shelf_data.txt'.format(inDir) outFileName = '{}/Schmidtko_et_al_2014_bottom_PT_S_PD_' \ 'SouthernOcean_0.25x0.125degree.nc'.format(outDir) if os.path.exists(outFileName): return # 1/4 x 1/8 degree grid cells cellsPerLon = 4 cellsPerLat = 8 obsFile = pandas.read_csv(inFileName, delim_whitespace=True) inLon = numpy.array(obsFile.iloc[:, 0]) inLat = numpy.array(obsFile.iloc[:, 1]) inZ = numpy.array(obsFile.iloc[:, 2]) inCT = numpy.array(obsFile.iloc[:, 3]) inCT_std = numpy.array(obsFile.iloc[:, 4]) inSA = numpy.array(obsFile.iloc[:, 5]) inSA_std = numpy.array(obsFile.iloc[:, 6]) pressure = gsw.p_from_z(inZ, inLat) inS = gsw.SP_from_SA(inSA, pressure, inLon, inLat) inPT = gsw.pt_from_CT(inSA, inCT) inPD = gsw.rho(inSA, inCT, 0.) minLat = int(numpy.amin(inLat) * cellsPerLat) / cellsPerLat maxLat = int(numpy.amax(inLat) * cellsPerLat) / cellsPerLat deltaLat = 1. / cellsPerLat outLat = numpy.arange(minLat - deltaLat, maxLat + 2 * deltaLat, deltaLat) deltaLon = 1. / cellsPerLon outLon = numpy.arange(0., 360., deltaLon) xIndices = numpy.array(cellsPerLon * inLon + 0.5, int) yIndices = numpy.array(cellsPerLat * (inLat - outLat[0]) + 0.5, int) Lon, Lat = numpy.meshgrid(outLon, outLat) ds = xarray.Dataset() ds['lon'] = (('lon', ), outLon) ds.lon.attrs['units'] = 'degrees' ds.lon.attrs['description'] = 'longitutude' ds['lat'] = (('lat', ), outLat) ds.lat.attrs['units'] = 'degrees' ds.lat.attrs['description'] = 'latitutude' z = numpy.ma.masked_all(Lon.shape) z[yIndices, xIndices] = inZ ds['z'] = (('lat', 'lon'), z) ds.z.attrs['units'] = 'meters' ds.z.attrs['description'] = 'depth of the seafloor (positive up)' PT = numpy.ma.masked_all(Lon.shape) PT[yIndices, xIndices] = inPT ds['botTheta'] = (('lat', 'lon'), PT) ds.botTheta.attrs['units'] = '$\degree$C' ds.botTheta.attrs['description'] = \ 'potential temperature at sea floor' PT_std = numpy.ma.masked_all(Lon.shape) # neglect difference between std of PT and CT PT_std[yIndices, xIndices] = inCT_std ds['botThetaStd'] = (('lat', 'lon'), PT_std) ds.botThetaStd.attrs['units'] = '$\degree$C' ds.botThetaStd.attrs['description'] = \ 'standard deviation in potential temperature at sea floor' S = numpy.ma.masked_all(Lon.shape) S[yIndices, xIndices] = inS ds['botSalinity'] = (('lat', 'lon'), S) ds.botSalinity.attrs['units'] = 'PSU' ds.botSalinity.attrs['description'] = \ 'salinity at sea floor' S_std = numpy.ma.masked_all(Lon.shape) # neglect difference between std of S and SA S_std[yIndices, xIndices] = inSA_std ds['botSalinityStd'] = (('lat', 'lon'), S_std) ds.botSalinityStd.attrs['units'] = 'PSU' ds.botSalinityStd.attrs['description'] = \ 'standard deviation in salinity at sea floor' PD = numpy.ma.masked_all(Lon.shape) PD[yIndices, xIndices] = inPD ds['botPotentialDensity'] = (('lat', 'lon'), PD) ds.botPotentialDensity.attrs['units'] = 'kg m$^{-3}$' ds.botPotentialDensity.attrs['description'] = \ 'potential desnity at sea floor' write_netcdf(ds, outFileName)
def find_DIC_corresp_to_pco2(tsal, ttemp, tpco2, tta, pres_atm, depth_this): import numpy as np import mocsy import gsw steps = 10000 tsal_r = np.zeros([steps]) tsal_r[:] = tsal #convert to psu tsal_r_psu = tsal_r * 35 / 35.16504 ttemp_r = np.zeros([steps]) ttemp_r[:] = ttemp #convert temperature to potential temperature ttemp_r_pot = gsw.pt_from_CT(tsal_r, ttemp_r) tta_r = np.zeros([steps]) tta_r[:] = tta * 1e-3 tpres_r = np.zeros([steps]) tpres_r[:] = pres_atm depth_r = np.zeros([steps]) depth_r[:] = depth_this tzero = np.zeros([steps]) tlat = np.zeros([steps]) tlat[:] = 50 end_d = 2400 start_d = 600 intvl = (end_d - start_d) / steps tdic_r = np.arange(start_d, end_d - 0.1, intvl) * 1e-3 #change to take potential temperature response_tup = mocsy.mvars(temp=ttemp_r_pot, sal=tsal_r_psu, alk=tta_r, dic=tdic_r, sil=tzero, phos=tzero, patm=tpres_r, depth=depth_r, lat=tzero, optcon='mol/m3', optt='Tpot', optp='m', optb='l10', optk1k2='m10', optkf='dg', optgas='Pinsitu') pH, pco2, fco2, co2, hco3, co3, OmegaA, OmegaC, BetaD, DENis, p, Tis = response_tup diffmat = pco2 - tpco2 idx, ans = find_nearest(diffmat, 0) if ans > 2: print('Danger, pco2 found >2 uatm from pco2 given') # print(idx) # print('difference between real pco2 and pco2 from calc. dic: ',ans) # print('DIC found this way:', tdic_r[idx]*1e3) fin_dic = tdic_r[idx] * 1e3 return fin_dic
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()
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()
def teos10(self, vlist: list = ['SA', 'CT', 'SIG0', 'N2', 'PV', 'PTEMP'], inplace: bool = True): """ Add TEOS10 variables to the dataset By default, adds: 'SA', 'CT', 'SIG0', 'N2', 'PV', 'PTEMP' Relies on the gsw library. If one exists, the correct CF standard name will be added to the attrs. Parameters ---------- vlist: list(str) List with the name of variables to add. Must be a list containing one or more of the following string values: * `"SA"` Adds an absolute salinity variable * `"CT"` Adds a conservative temperature variable * `"SIG0"` Adds a potential density anomaly variable referenced to 0 dbar * `"N2"` Adds a buoyancy (Brunt-Vaisala) frequency squared variable. This variable has been regridded to the original pressure levels in the Dataset using a linear interpolation. * `"PV"` Adds a planetary vorticity variable calculated from :math:`\\frac{f N^2}{\\text{gravity}}`. This is not a TEOS-10 variable from the gsw toolbox, but is provided for convenience. This variable has been regridded to the original pressure levels in the Dataset using a linear interpolation. * `"PTEMP"` Adds a potential temperature variable inplace: boolean, True by default If True, return the input :class:`xarray.Dataset` with new TEOS10 variables added as a new :class:`xarray.DataArray` If False, return a :class:`xarray.Dataset` with new TEOS10 variables Returns ------- :class:`xarray.Dataset` """ if not with_gsw: raise ModuleNotFoundError( "This functionality requires the gsw library") allowed = ['SA', 'CT', 'SIG0', 'N2', 'PV', 'PTEMP'] if any(var not in allowed for var in vlist): raise ValueError(f"vlist must be a subset of {allowed}, instead found {vlist}") this = self._obj to_profile = False if self._type == 'profile': to_profile = True this = this.argo.profile2point() # Get base variables as numpy arrays: psal = this['PSAL'].values temp = this['TEMP'].values pres = this['PRES'].values lon = this['LONGITUDE'].values lat = this['LATITUDE'].values f = lat # Coriolis f = gsw.f(lat) # Absolute salinity sa = gsw.SA_from_SP(psal, pres, lon, lat) # Conservative temperature ct = gsw.CT_from_t(sa, temp, pres) # Potential Temperature if 'PTEMP' in vlist: pt = gsw.pt_from_CT(sa, ct) # Potential density referenced to surface if 'SIG0' in vlist: sig0 = gsw.sigma0(sa, ct) # N2 if 'N2' in vlist or 'PV' in vlist: n2_mid, p_mid = gsw.Nsquared(sa, ct, pres, lat) # N2 on the CT grid: ishallow = (slice(0, -1), Ellipsis) ideep = (slice(1, None), Ellipsis) def mid(x): return 0.5 * (x[ideep] + x[ishallow]) n2 = np.zeros(ct.shape) * np.nan n2[1:-1] = mid(n2_mid) # PV: if 'PV' in vlist: pv = f * n2 / gsw.grav(lat, pres) # Back to the dataset: that = [] if 'SA' in vlist: SA = xr.DataArray(sa, coords=this['PSAL'].coords, name='SA') SA.attrs['long_name'] = 'Absolute Salinity' SA.attrs['standard_name'] = 'sea_water_absolute_salinity' SA.attrs['unit'] = 'g/kg' that.append(SA) if 'CT' in vlist: CT = xr.DataArray(ct, coords=this['TEMP'].coords, name='CT') CT.attrs['long_name'] = 'Conservative Temperature' CT.attrs['standard_name'] = 'sea_water_conservative_temperature' CT.attrs['unit'] = 'degC' that.append(CT) if 'SIG0' in vlist: SIG0 = xr.DataArray(sig0, coords=this['TEMP'].coords, name='SIG0') SIG0.attrs['long_name'] = 'Potential density anomaly with reference pressure of 0 dbar' SIG0.attrs['standard_name'] = 'sea_water_sigma_theta' SIG0.attrs['unit'] = 'kg/m^3' that.append(SIG0) if 'N2' in vlist: N2 = xr.DataArray(n2, coords=this['TEMP'].coords, name='N2') N2.attrs['long_name'] = 'Squared buoyancy frequency' N2.attrs['unit'] = '1/s^2' that.append(N2) if 'PV' in vlist: PV = xr.DataArray(pv, coords=this['TEMP'].coords, name='PV') PV.attrs['long_name'] = 'Planetary Potential Vorticity' PV.attrs['unit'] = '1/m/s' that.append(PV) if 'PTEMP' in vlist: PTEMP = xr.DataArray(pt, coords=this['TEMP'].coords, name='PTEMP') PTEMP.attrs['long_name'] = 'Potential Temperature' PTEMP.attrs['standard_name'] = 'sea_water_potential_temperature' PTEMP.attrs['unit'] = 'degC' that.append(PTEMP) # Create a dataset with all new variables: that = xr.merge(that) # Add to the dataset essential Argo variables (allows to keep using the argo accessor): that = that.assign({k: this[k] for k in ['TIME', ' LATITUDE', 'LONGITUDE', 'PRES', 'PRES_ADJUSTED', 'PLATFORM_NUMBER', 'CYCLE_NUMBER', 'DIRECTION'] if k in this}) # Manage output: if inplace: # Merge previous with new variables for v in that.variables: this[v] = that[v] if to_profile: this = this.argo.point2profile() for k in this: if k not in self._obj: self._obj[k] = this[k] return self._obj else: if to_profile: return that.argo.point2profile() else: return that
def load_seals(sealdir, bathy_data): from datetime import datetime, date, timedelta # initialize maxNprof = 0 ll = 0 ll1 = 0 l0 = 0 dateFlNum = [] XC = bathy_data[0] YC = bathy_data[1] x1 = bathy_data[3] x2 = bathy_data[4] y1 = bathy_data[5] y2 = bathy_data[6] # initialize big arrays CT_dataSO = np.nan * np.ones((100 * 310, 1000), '>f4') SA_dataSO = np.nan * np.ones((100 * 310, 1000), '>f4') SP_dataSO = np.nan * np.ones((100 * 310, 1000), '>f4') lon_dataSO = np.nan * np.ones((100 * 310), '>f4') lat_dataSO = np.nan * np.ones((100 * 310), '>f4') pr_dataSO = np.nan * np.ones((100 * 1000 * 310), '>f4') yySO = np.zeros((100 * 310), 'int32') mmSO = np.zeros((100 * 310), 'int32') IDSO = np.zeros((100 * 310), 'int32') data_list = [ g for g in os.listdir(seadir) if g.endswith('nc') and not g.startswith('WOD') ] for ff_SO in data_list: #printff_SO tot_fl = 0 iniDate = datetime(1950, 1, 1) minDate = datetime(2019, 1, 1).toordinal() #printff_SO file = os.path.join(sealdir, '%s' % ff_SO) # load data data = nc.Dataset(file) cruise = data.variables['PLATFORM_NUMBER'][:] if "CTD2020" in ff_SO: cruise = np.array(['202020%0.3i' % (int(f)) for f in cruise]) elif "CTD2019" in ff_SO: cruise = np.array(['191919%0.3i' % (int(f)) for f in cruise]) else: cruise = np.array(['%0.9i' % f for f in cruise]) time = data.variables['JULD'][:] lon = data.variables['LONGITUDE'][:] lat = data.variables['LATITUDE'][:] pressPre = data.variables['PRES_ADJUSTED'][:].transpose() tempPre = data.variables['TEMP_ADJUSTED'][:].transpose() tempQC = data.variables['TEMP_ADJUSTED_QC'][:].transpose() psaPre = data.variables['PSAL_ADJUSTED'][:].transpose() psaQC = data.variables['PSAL_ADJUSTED_QC'][:].transpose() psaPre[np.where(psaQC == '4')] = np.nan tempPre[np.where(tempQC == '4')] = np.nan # let's get rid of some profiles that are out of the Amudsen Sea tot_fl += 1 msk1 = np.where(np.logical_and(lon >= XC[x1], lon < XC[x2]))[0][:] msk2 = np.where(np.logical_and(lat[msk1] > YC[y1], lat[msk1] < YC[y2]))[0][:] lon = lon[msk1][msk2] lat = lat[msk1][msk2] time = time[msk1][msk2] pressPre = pressPre[:, msk1[msk2]] tempPre = tempPre[:, msk1[msk2]] psaPre = psaPre[:, msk1[msk2]] cruise = cruise[msk1][msk2] if time[0] + iniDate.toordinal() < minDate: minDate = time[0] if len(lon) > maxNprof: maxNprof = len(lon[~np.isnan(lon)]) flN = ff_SO # remove data below 1000m (there are just so few seal profiles with data below 1000m anyway) msk = np.where(pressPre > 1000) pressPre[msk] = np.nan tempPre[msk] = np.nan psaPre[msk] = np.nan # interpolate and prepare the data lat = np.ma.masked_less(lat, -90) lon = np.ma.masked_less(lon, -500) lon[lon > 360.] = lon[lon > 360.] - 360. Nprof = np.linspace(1, len(lat), len(lat)) # turn the variables upside down, to have from the surface to depth and not viceversa if any(pressPre[:10, 0] > 500.): pressPre = pressPre[::-1, :] psaPre = psaPre[::-1, :] tempPre = tempPre[::-1, :] # interpolate data on vertical grid with 1db of resolution (this is fundamental to then create profile means) fields = [psaPre, tempPre] press = np.nan * np.ones((1000, pressPre.shape[1])) for kk in range(press.shape[1]): press[:, kk] = np.arange(2, 1002, 1) psa = np.nan * np.ones((press.shape), '>f4') inst_temp = np.nan * np.ones((press.shape), '>f4') for ii, ff in enumerate(fields): for nn in range(pressPre.shape[1]): # only use non-nan values, otherwise it doesn't interpolate well try: f1 = ff[:, nn][ff[:, nn].mask == False] #ff[:,nn][~np.isnan(ff[:,nn])] f2 = pressPre[:, nn][ff[:, nn].mask == False] except: f1 = ff[:, nn] f2 = pressPre[:, nn] if len(f1) == 0: f1 = ff[:, nn] f2 = pressPre[:, nn] try: sp = interpolate.interp1d(f2[~np.isnan(f1)], f1[~np.isnan(f1)], kind='linear', bounds_error=False, fill_value=np.nan) ff_int = sp(press[:, nn]) if ii == 0: psa[:, nn] = ff_int elif ii == 1: inst_temp[:, nn] = ff_int except: print( 'At profile number %i, the float %s has only 1 record valid: len(f2)=%i' % (nn, ff_SO, len(f2))) # To compute theta, I need absolute salinity [g/kg] from practical salinity (PSS-78) [unitless] and conservative temperature. sa = np.nan * np.ones((press.shape), '>f4') for kk in range(press.shape[1]): sa[:, kk] = gsw.SA_from_SP(psa[:, kk], press[:, 0], lon[kk], lat[kk]) temp = gsw.CT_from_t(sa, inst_temp, press) ptemp = gsw.pt_from_CT(sa, temp) # mask out the profiles with : msk = np.where(lat < -1000) lat[msk] = np.nan lon[msk] = np.nan cruise[msk] = np.nan psa[msk] = np.nan sa[msk] = np.nan psa[sa == 0.] = np.nan sa[sa == 0.] = np.nan # use CT instead of PT temp[msk] = np.nan temp[temp == 0.] = np.nan # save the nprofiles NN = np.ones((temp.shape), 'int32') for ii in range(len(Nprof)): NN[:, ii] = Nprof[ii] lon_dataSO[ll1:ll1 + len(lon)] = lon lat_dataSO[ll1:ll1 + len(lon)] = lat CT_dataSO[ll:ll + len(sa[0, :]), :] = temp.T SA_dataSO[ll:ll + len(sa[0, :]), :] = sa.T SP_dataSO[ll:ll + len(sa[0, :]), :] = psa.T IDSO[ll1:ll1 + len(lon)] = [int(f) for f in cruise] # separate seasons dateFl = [] for dd in time: floatDate = iniDate + timedelta(float(dd)) dateFl.append(floatDate) dateFlNum = np.append(dateFlNum, floatDate.toordinal()) yearsSO = np.array([int(dd.year) for dd in dateFl]) monthsSO = np.array([int(dd.month) for dd in dateFl]) SPR = np.where(np.logical_and(monthsSO >= 9, monthsSO <= 11)) SUM = np.where(np.logical_or(monthsSO == 12, monthsSO <= 2)) AUT = np.where(np.logical_and(monthsSO >= 3, monthsSO <= 5)) WIN = np.where(np.logical_and(monthsSO >= 6, monthsSO <= 8)) mmFlSO = monthsSO.copy() mmFlSO[SPR] = 1 mmFlSO[SUM] = 2 mmFlSO[AUT] = 3 mmFlSO[WIN] = 4 mmSO[ll:ll + len(sa[0, :])] = mmFlSO yySO[ll:ll + len(sa[0, :])] = yearsSO ll = ll + len(sa[0, :]) ll1 = ll1 + len(lon) minArgoDate = timedelta(float(minDate)) + iniDate minArgoDate.strftime('%Y/%m/%d %H:%M:%S%z') # chop away the part of the array with no data CT_dataSO = CT_dataSO[:ll, :] SA_dataSO = SA_dataSO[:ll, :] SP_dataSO = SP_dataSO[:ll, :] IDSO = IDSO[:ll] lat_dataSO = lat_dataSO[:ll] lon_dataSO = lon_dataSO[:ll] mmSO = mmSO[:ll] yySO = yySO[:ll] # remove entire columns of NaNs idBad = [] for ii in range(CT_dataSO.shape[1]): f0 = CT_dataSO[:, ii] f1 = f0[~np.isnan(f0)] if len(f1) == 0: idBad.append(ii) CT_dataSO = np.delete(CT_dataSO, idBad, 0) SA_dataSO = np.delete(SA_dataSO, idBad, 0) SP_dataSO = np.delete(SP_dataSO, idBad, 0) IDSO = np.delete(IDSO, idBad, 0) lon_dataSO = np.delete(lon_dataSO, idBad, 0) lat_dataSO = np.delete(lat_dataSO, idBad, 0) mmSO = np.delete(mmSO, idBad, 0) yySO = np.delete(yySO, idBad, 0) load_seals.profSO = [ CT_dataSO, SA_dataSO, lon_dataSO, lat_dataSO, IDSO, SP_dataSO ] load_seals.temporalSO = [yySO, mmSO] return [load_seals.profSO, load_seals.temporalSO]
def IO_argo(floatdir): from datetime import datetime, date, timedelta programs = os.listdir(floatdir) programs = programs[1:] # initialize tot_fl = 0 maxNprof = 0 ll = 0 ll1 = 0 l0 = 0 dateFlNum = [] iniDate = datetime(1950, 1, 1) minDate = datetime(2019, 1, 1).toordinal() # initialize big arrays PT_dataSO = np.nan * np.ones((822 * 310, 2000), '>f4') SA_dataSO = np.nan * np.ones((822 * 310, 2000), '>f4') lon_dataSO = np.nan * np.ones((822 * 310), '>f4') lat_dataSO = np.nan * np.ones((822 * 310), '>f4') pr_dataSO = np.nan * np.ones((822 * 2000 * 310), '>f4') yySO = np.zeros((822 * 310), 'int32') IDSO = np.zeros((822 * 2000 * 310), 'int32') for pp in programs: SO_floats = os.listdir(os.path.join(floatdir, '%s' % pp)) for ff_SO in SO_floats: print ff_SO # @hidden_cell SO_prof = [ f for f in os.listdir( os.path.join(floatdir, '%s/%s' % (pp, ff_SO))) if 'prof.nc' in f ] tot_fl += 1 file = os.path.join(floatdir, '%s/%s/%s' % (pp, ff_SO, SO_prof[0])) data = nc.Dataset(file) time = data.variables['JULD'][:] lon = data.variables['LONGITUDE'][:] lat = data.variables['LATITUDE'][:] pressPre = data.variables['PRES_ADJUSTED'][:].transpose() tempPre = data.variables['TEMP_ADJUSTED'][:].transpose() tempQC = data.variables['TEMP_ADJUSTED_QC'][:].transpose() psaPre = data.variables['PSAL_ADJUSTED'][:].transpose() psaQC = data.variables['PSAL_ADJUSTED_QC'][:].transpose() # have to add the good QC: [1,2,5,8] (not in ['1','2','5','8']) psaPre[np.where(psaQC != '1')] = np.nan tempPre[np.where(tempQC != '1')] = np.nan # let's get rid of some profiles that are out of the indian sector msk1 = np.where(np.logical_and(lon >= 0, lon < 180))[0][:] msk2 = np.where(np.logical_and(lat[msk1] > -70, lat[msk1] < -30))[0][:] lon = lon[msk1][msk2] lat = lat[msk1][msk2] time = time[msk1][msk2] pressPre = pressPre[:, msk1[msk2]] tempPre = tempPre[:, msk1[msk2]] psaPre = psaPre[:, msk1[msk2]] if time[0] + iniDate.toordinal() < minDate: minDate = time[0] if len(lon) > maxNprof: maxNprof = len(lon[~np.isnan(lon)]) flN = ff_SO # interpolate lat = np.ma.masked_less(lat, -90) lon = np.ma.masked_less(lon, -500) lon[lon > 360.] = lon[lon > 360.] - 360. Nprof = np.linspace(1, len(lat), len(lat)) # turn the variables upside down, to have from the surface to depth and not viceversa if any(pressPre[:10, 0] > 500.): pressPre = pressPre[::-1, :] psaPre = psaPre[::-1, :] tempPre = tempPre[::-1, :] # interpolate data on vertical grid with 1db of resolution (this is fundamental to then create profile means) fields = [psaPre, tempPre] press = np.nan * np.ones((2000, pressPre.shape[1])) for kk in range(press.shape[1]): press[:, kk] = np.arange(2, 2002, 1) psa = np.nan * np.ones((press.shape), '>f4') temp = np.nan * np.ones((press.shape), '>f4') for ii, ff in enumerate(fields): for nn in range(pressPre.shape[1]): # only use non-nan values, otherwise it doesn't interpolate well try: f1 = ff[:, nn][ff[:, nn].mask == False] #ff[:,nn][~np.isnan(ff[:,nn])] f2 = pressPre[:, nn][ff[:, nn].mask == False] except: f1 = ff[:, nn] f2 = pressPre[:, nn] if len(f1) == 0: f1 = ff[:, nn] f2 = pressPre[:, nn] try: sp = interpolate.interp1d(f2[~np.isnan(f1)], f1[~np.isnan(f1)], kind='linear', bounds_error=False, fill_value=np.nan) ff_int = sp(press[:, nn]) if ii == 0: psa[:, nn] = ff_int elif ii == 1: temp[:, nn] = ff_int except: continue #print 'At profile number %i, the float %s has only 1 record valid' # To compute theta, I need absolute salinity [g/kg] from practical salinity (PSS-78) [unitless] and conservative temperature. sa = np.nan * np.ones((press.shape), '>f4') for kk in range(press.shape[1]): sa[:, kk] = gsw.SA_from_SP(psa[:, kk], press[:, 0], lon[kk], lat[kk]) ptemp = gsw.pt_from_CT(sa, temp) # mask out the profiles with : msk = np.where(lat < -1000) lat[msk] = np.nan lon[msk] = np.nan sa[msk] = np.nan sa[sa == 0.] = np.nan ptemp[msk] = np.nan ptemp[temp == 0.] = np.nan # save the nprofiles NN = np.ones((temp.shape), 'int32') for ii in range(len(Nprof)): NN[:, ii] = Nprof[ii] lon_dataSO[ll1:ll1 + len(lon)] = lon lat_dataSO[ll1:ll1 + len(lon)] = lat PT_dataSO[ll:ll + len(sa[0, :]), :] = ptemp.T SA_dataSO[ll:ll + len(sa[0, :]), :] = sa.T floatID = int(ff_SO) * np.ones((sa.shape[1]), 'int32') IDSO[ll:ll + len(sa[0, :])] = floatID # separate seasons dateFl = [] for dd in time: floatDate = iniDate + timedelta(float(dd)) dateFl.append(floatDate) dateFlNum = np.append(dateFlNum, floatDate.toordinal()) yearsSO = np.array([int(dd.year) for dd in dateFl]) yySO[ll:ll + len(sa[0, :])] = yearsSO ll = ll + len(sa[0, :]) ll1 = ll1 + len(lon) # chop away the part of the array with no data PT_dataSO = PT_dataSO[:ll, :] SA_dataSO = SA_dataSO[:ll, :] IDSO = IDSO[:ll] lat_dataSO = lat_dataSO[:ll] lon_dataSO = lon_dataSO[:ll] mmSO = mmSO[:ll] yySO = yySO[:ll] # remove entire columns of NaNs idBad = [] for ii in range(PT_dataSO.shape[0]): f0 = PT_dataSO[ii, :] f1 = f0[~np.isnan(f0)] if len(f1) == 0: idBad.append(ii) PT_dataSO = np.delete(PT_dataSO, idBad, 0) SA_dataSO = np.delete(SA_dataSO, idBad, 0) IDSO = np.delete(IDSO, idBad, 0) lon_dataSO = np.delete(lon_dataSO, idBad, 0) lat_dataSO = np.delete(lat_dataSO, idBad, 0) mmSO = np.delete(mmSO, idBad, 0) yySO = np.delete(yySO, idBad, 0) idBad = [] # Interpolate again in depth.. for some reason, some profiles have still wholes in the middle: for ii in range(PT_dataSO.shape[0]): f0 = PT_dataSO[ii, :] try: sp = interpolate.interp1d(press[~np.isnan(f0), 0], f0[~np.isnan(f0)], kind='linear', bounds_error=False, fill_value=np.nan) PT_dataSO[ii, :] = sp(press[:, 0]) f0 = SA_dataSO[ii, :] sp = interpolate.interp1d(press[~np.isnan(f0), 0], f0[~np.isnan(f0)], kind='linear', bounds_error=False, fill_value=np.nan) SA_dataSO[ii, :] = sp(press[:, 0]) except: idBad.append(ii) #print ii, ' has only 1 number' PT_dataSO = np.delete(PT_dataSO, idBad, 0) SA_dataSO = np.delete(SA_dataSO, idBad, 0) IDSO = np.delete(IDSO, idBad, 0) lon_dataSO = np.delete(lon_dataSO, idBad, 0) lat_dataSO = np.delete(lat_dataSO, idBad, 0) mmSO = np.delete(mmSO, idBad, 0) yySO = np.delete(yySO, idBad, 0) IO_argo.profSO = [PT_dataSO, SA_dataSO, lon_dataSO, lat_dataSO, IDSO] IO_argo.temporalSO = [yySO, mmSO] return [IO_argo.profSO, IO_argo.temporalSO]
def teos10(self, vlist: list = ['SA', 'CT', 'SIG0', 'N2', 'PV', 'PTEMP'], inplace: bool = True): """ Add TEOS10 variables to the dataset By default, add: 'SA', 'CT', 'SIG0', 'N2', 'PV', 'PTEMP' Rely on the gsw library. Parameters ---------- vlist: list(str) List with the name of variables to add. inplace: boolean, True by default If True, return the input :class:`xarray.Dataset` with new TEOS10 variables added as a new :class:`xarray.DataArray` If False, return a :class:`xarray.Dataset` with new TEOS10 variables Returns ------- :class:`xarray.Dataset` """ if not with_gsw: raise ModuleNotFoundError( "This functionality requires the gsw library") this = self._obj to_profile = False if self._type == 'profile': to_profile = True this = this.argo.profile2point() # Get base variables as numpy arrays: psal = this['PSAL'].values temp = this['TEMP'].values pres = this['PRES'].values lon = this['LONGITUDE'].values lat = this['LATITUDE'].values f = lat # Coriolis f = gsw.f(lat) # Depth: depth = gsw.z_from_p(pres, lat) # Absolute salinity sa = gsw.SA_from_SP(psal, pres, lon, lat) # Conservative temperature ct = gsw.CT_from_t(sa, temp, depth) # Potential Temperature if 'PTEMP' in vlist: pt = gsw.pt_from_CT(sa, ct) # Potential density referenced to surface if 'SIG0' in vlist: sig0 = gsw.sigma0(sa, ct) # N2 if 'N2' in vlist or 'PV' in vlist: n2_mid, p_mid = gsw.Nsquared(sa, ct, pres, lat) # N2 on the CT grid: ishallow = (slice(0, -1), Ellipsis) ideep = (slice(1, None), Ellipsis) def mid(x): return 0.5 * (x[ideep] + x[ishallow]) n2 = np.zeros(ct.shape) * np.nan n2[1:-1] = mid(n2_mid) # PV: if 'PV' in vlist: pv = f * n2 / gsw.grav(lat, pres) # Back to the dataset: that = [] if 'SA' in vlist: SA = xr.DataArray(sa, coords=this['PSAL'].coords, name='SA') SA.attrs['standard_name'] = 'Absolute Salinity' SA.attrs['unit'] = 'g/kg' that.append(SA) if 'CT' in vlist: CT = xr.DataArray(ct, coords=this['TEMP'].coords, name='CT') CT.attrs['standard_name'] = 'Conservative Temperature' CT.attrs['unit'] = 'degC' that.append(CT) if 'SIG0' in vlist: SIG0 = xr.DataArray(sig0, coords=this['TEMP'].coords, name='SIG0') SIG0.attrs[ 'long_name'] = 'Potential density anomaly with reference pressure of 0 dbar' SIG0.attrs['standard_name'] = 'Potential Density' SIG0.attrs['unit'] = 'kg/m^3' that.append(SIG0) if 'N2' in vlist: N2 = xr.DataArray(n2, coords=this['TEMP'].coords, name='N2') N2.attrs['standard_name'] = 'Squared buoyancy frequency' N2.attrs['unit'] = '1/s^2' that.append(N2) if 'PV' in vlist: PV = xr.DataArray(pv, coords=this['TEMP'].coords, name='PV') PV.attrs['standard_name'] = 'Planetary Potential Vorticity' PV.attrs['unit'] = '1/m/s' that.append(PV) if 'PTEMP' in vlist: PTEMP = xr.DataArray(pt, coords=this['TEMP'].coords, name='PTEMP') PTEMP.attrs['standard_name'] = 'Potential Temperature' PTEMP.attrs['unit'] = 'degC' that.append(PTEMP) # Create a dataset with all new variables: that = xr.merge(that) # Add to the dataset essential Argo variables (allows to keep using the argo accessor): that = that.assign({ k: this[k] for k in [ 'TIME', ' LATITUDE', 'LONGITUDE', 'PRES', 'PRES_ADJUSTED', 'PLATFORM_NUMBER', 'CYCLE_NUMBER', 'DIRECTION' ] if k in this }) # Manage output: if inplace: # Merge previous with new variables for v in that.variables: this[v] = that[v] if to_profile: this = this.argo.point2profile() for k in this: if k not in self._obj: self._obj[k] = this[k] return self._obj else: if to_profile: return that.argo.point2profile() else: return that
def compute_transport(historyFileList, casename, meshfile, maskfile, figdir,\ transectName='Drake Passage', outfile='transport.nc'): mesh = xr.open_dataset(meshfile) mask = get_mask_short_names(xr.open_dataset(maskfile)) if transectName == 'all' or transectName == 'StandardTransportSectionsRegionsGroup': transectList = mask.shortNames[:].values condition = transectList != "Atlantic Transec" transectList = np.extract(condition, transectList) else: transectList = transectName.split(',') if platform.python_version()[0] == '3': for i in range(len(transectList)): transectList[i] = "b'" + transectList[i] print('Computing Transport for the following transects ', transectList) nTransects = len(transectList) maxEdges = mask.dims['maxEdgesInTransect'] # Compute refLayerThickness to avoid need for hist file refBottom = mesh.refBottomDepth.values nz = mesh.dims['nVertLevels'] h = np.zeros(nz) h[0] = refBottom[0] for i in range(1, nz): h[i] = refBottom[i] - refBottom[i - 1] # Get a list of edges and total edges in each transect nEdgesInTransect = np.zeros(nTransects) edgeVals = np.zeros((nTransects, maxEdges)) for i in range(nTransects): amask = mask.sel(shortNames=transectList[i]).squeeze() transectEdges = amask.transectEdgeGlobalIDs.values inds = np.where(transectEdges > 0)[0] nEdgesInTransect[i] = len(inds) transectEdges = transectEdges[inds] edgeVals[i, :len(inds)] = np.asarray(transectEdges - 1, dtype='i') nEdgesInTransect = np.asarray(nEdgesInTransect, dtype='i') # Create a list with the start and stop for transect bounds nTransectStartStop = np.zeros(nTransects + 1) for j in range(1, nTransects + 1): nTransectStartStop[j] = nTransectStartStop[j - 1] + nEdgesInTransect[j - 1] edgesToRead = edgeVals[0, :nEdgesInTransect[0]] for i in range(1, nTransects): edgesToRead = np.hstack( [edgesToRead, edgeVals[i, :nEdgesInTransect[i]]]) edgesToRead = np.asarray(edgesToRead, dtype='i') dvEdge = mesh.dvEdge.sel(nEdges=edgesToRead).values cellsOnEdge = mesh.cellsOnEdge.sel(nEdges=edgesToRead).values edgeSigns = np.zeros((nTransects, len(edgesToRead))) for i in range(nTransects): edgeSigns[i, :] = mask.sel( nEdges=edgesToRead, shortNames=transectList[i]).squeeze().transectEdgeMaskSigns.values latmean = 180.0 / np.pi * np.nanmean( mesh.latEdge.sel(nEdges=edgesToRead).values) lonmean = 180.0 / np.pi * np.nanmean( mesh.lonEdge.sel(nEdges=edgesToRead).values) pressure = gsw.p_from_z(-refBottom, latmean) # Read history files one at a time and slice fileList = sorted(glob.glob(historyFileList)) vol_transport = np.zeros((len(fileList), nTransects)) vol_transportIn = np.zeros((len(fileList), nTransects)) vol_transportOut = np.zeros((len(fileList), nTransects)) heat_transport = np.zeros((len(fileList), nTransects)) # Tref = 0degC heat_transportIn = np.zeros((len(fileList), nTransects)) heat_transportOut = np.zeros((len(fileList), nTransects)) heat_transportTfp = np.zeros( (len(fileList), nTransects)) # Tref = T freezing point computed below (Tfp) heat_transportTfpIn = np.zeros((len(fileList), nTransects)) heat_transportTfpOut = np.zeros((len(fileList), nTransects)) salt_transport = np.zeros((len(fileList), nTransects)) # Sref = saltRef salt_transportIn = np.zeros((len(fileList), nTransects)) salt_transportOut = np.zeros((len(fileList), nTransects)) t = np.zeros(len(fileList)) for i, fname in enumerate(fileList): ncid = Dataset(fname, 'r') if 'timeMonthly_avg_normalTransportVelocity' in ncid.variables.keys(): vel = ncid.variables['timeMonthly_avg_normalTransportVelocity'][ 0, edgesToRead, :] elif 'timeMonthly_avg_normalVelocity' in ncid.variables.keys(): vel = ncid.variables['timeMonthly_avg_normalVelocity'][ 0, edgesToRead, :] if 'timeMonthly_avg_normalGMBolusVelocity' in ncid.variables.keys( ): vel += ncid.variables['timeMonthly_avg_normalGMBolusVelocity'][ 0, edgesToRead, :] else: raise KeyError('no appropriate normalVelocity variable found') tempOnCell1 = ncid.variables[ 'timeMonthly_avg_activeTracers_temperature'][0, cellsOnEdge[:, 0] - 1, :] tempOnCell2 = ncid.variables[ 'timeMonthly_avg_activeTracers_temperature'][0, cellsOnEdge[:, 1] - 1, :] saltOnCell1 = ncid.variables['timeMonthly_avg_activeTracers_salinity'][ 0, cellsOnEdge[:, 0] - 1, :] saltOnCell2 = ncid.variables['timeMonthly_avg_activeTracers_salinity'][ 0, cellsOnEdge[:, 1] - 1, :] t[i] = ncid.variables['timeMonthly_avg_daysSinceStartOfSim'][:] / 365. ncid.close() # Mask T,S values that fall on land tempOnCell1[cellsOnEdge[:, 0] == 0, :] = np.nan tempOnCell2[cellsOnEdge[:, 1] == 0, :] = np.nan saltOnCell1[cellsOnEdge[:, 0] == 0, :] = np.nan saltOnCell2[cellsOnEdge[:, 1] == 0, :] = np.nan # Interpolate T,S values onto edges tempOnEdge = np.nanmean(np.array([tempOnCell1, tempOnCell2]), axis=0) saltOnEdge = np.nanmean(np.array([saltOnCell1, saltOnCell2]), axis=0) # Mask values that fall onto topography tempOnEdge[np.logical_or(tempOnEdge > 1e15, tempOnEdge < -1e15)] = np.nan saltOnEdge[np.logical_or(saltOnEdge > 1e15, saltOnEdge < -1e15)] = np.nan # Compute freezing temperature SA = gsw.SA_from_SP(saltOnEdge, pressure, lonmean, latmean) CTfp = gsw.CT_freezing(SA, pressure, 0.) Tfp = gsw.pt_from_CT(SA, CTfp) # Compute transports for each transect for j in range(nTransects): start = int(nTransectStartStop[j]) stop = int(nTransectStartStop[j + 1]) dArea = dvEdge[start:stop, np.newaxis] * h[np.newaxis, :] normalVel = vel[start:stop, :] * edgeSigns[j, start:stop, np.newaxis] temp = tempOnEdge[start:stop, :] salt = saltOnEdge[start:stop, :] tfreezing = Tfp[start:stop, :] indVelP = np.where(normalVel > 0) indVelM = np.where(normalVel < 0) vol_transport[i, j] = np.nansum(np.nansum(normalVel * dArea)) vol_transportIn[i, j] = np.nansum( np.nansum(normalVel[indVelP] * dArea[indVelP])) vol_transportOut[i, j] = np.nansum( np.nansum(normalVel[indVelM] * dArea[indVelM])) heat_transport[i, j] = np.nansum(np.nansum(temp * normalVel * dArea)) heat_transportIn[i, j] = np.nansum( np.nansum(temp[indVelP] * normalVel[indVelP] * dArea[indVelP])) heat_transportOut[i, j] = np.nansum( np.nansum(temp[indVelM] * normalVel[indVelM] * dArea[indVelM])) heat_transportTfp[i, j] = np.nansum( np.nansum((temp - tfreezing) * normalVel * dArea)) heat_transportTfpIn[i, j] = np.nansum( np.nansum((temp[indVelP] - tfreezing[indVelP]) * normalVel[indVelP] * dArea[indVelP])) heat_transportTfpOut[i, j] = np.nansum( np.nansum((temp[indVelM] - tfreezing[indVelM]) * normalVel[indVelM] * dArea[indVelM])) salt_transport[i, j] = np.nansum(np.nansum(salt * normalVel * dArea)) salt_transport[ i, j] = vol_transport[i, j] - salt_transport[i, j] / saltRef salt_transportIn[i, j] = np.nansum( np.nansum(salt[indVelP] * normalVel[indVelP] * dArea[indVelP])) salt_transportIn[ i, j] = vol_transportIn[i, j] - salt_transportIn[i, j] / saltRef salt_transportOut[i, j] = np.nansum( np.nansum(salt[indVelM] * normalVel[indVelM] * dArea[indVelM])) salt_transportOut[ i, j] = vol_transportOut[i, j] - salt_transportOut[i, j] / saltRef vol_transport = m3ps_to_Sv * vol_transport vol_transportIn = m3ps_to_Sv * vol_transportIn vol_transportOut = m3ps_to_Sv * vol_transportOut heat_transport = W_to_TW * rhoRef * cp * heat_transport heat_transportIn = W_to_TW * rhoRef * cp * heat_transportIn heat_transportOut = W_to_TW * rhoRef * cp * heat_transportOut heat_transportTfp = W_to_TW * rhoRef * cp * heat_transportTfp heat_transportTfpIn = W_to_TW * rhoRef * cp * heat_transportTfpIn heat_transportTfpOut = W_to_TW * rhoRef * cp * heat_transportTfpOut salt_transport = m3ps_to_km3py * salt_transport salt_transportIn = m3ps_to_km3py * salt_transportIn salt_transportOut = m3ps_to_km3py * salt_transportOut # Define some dictionaries for transect plotting obsDict = {'Drake Passage':[120, 175], 'Tasmania-Ant':[147, 167], 'Africa-Ant':None, 'Antilles Inflow':[-23.1, -13.7], \ 'Mona Passage':[-3.8, -1.4],'Windward Passage':[-7.2, -6.8], 'Florida-Cuba':[30, 33], 'Florida-Bahamas':[30, 33], \ 'Indonesian Throughflow':[-21, -11], 'Agulhas':[-90, -50], 'Mozambique Channel':[-20, -8], \ 'Bering Strait':[0.6, 1.0], 'Lancaster Sound':[-1.0, -0.5], 'Fram Strait':[-4.7, 0.7], \ 'Robeson Channel':None, 'Davis Strait':[-1.6, -3.6], 'Barents Sea Opening':[1.4, 2.6], \ 'Nares Strait':[-1.8, 0.2], 'Denmark Strait':None, 'Iceland-Faroe-Scotland':None} labelDict = {'Drake Passage':'drake', 'Tasmania-Ant':'tasmania', 'Africa-Ant':'africaAnt', 'Antilles Inflow':'antilles', \ 'Mona Passage':'monaPassage', 'Windward Passage':'windwardPassage', 'Florida-Cuba':'floridaCuba', \ 'Florida-Bahamas':'floridaBahamas', 'Indonesian Throughflow':'indonesia', 'Agulhas':'agulhas', \ 'Mozambique Channel':'mozambique', 'Bering Strait':'beringStrait', 'Lancaster Sound':'lancasterSound', \ 'Fram Strait':'framStrait', 'Robeson Channel':'robeson', 'Davis Strait':'davisStrait', 'Barents Sea Opening':'barentsSea', \ 'Nares Strait':'naresStrait', 'Denmark Strait':'denmarkStrait', 'Iceland-Faroe-Scotland':'icelandFaroeScotland'} figsize = (20, 10) figdpi = 80 for i in range(nTransects): if platform.python_version()[0] == '3': searchString = transectList[i][2:] else: searchString = transectList[i] # Plot Volume Transport figfile = '{}/volTransport_{}_{}.png'.format(figdir, labelDict[searchString], casename) plt.figure(figsize=figsize, dpi=figdpi) bounds = obsDict[searchString] plt.plot(t, vol_transport[:, i], 'k', linewidth=2, label='model (net)') plt.plot(t, vol_transportIn[:, i], 'r', linewidth=2, label='model (inflow)') plt.plot(t, vol_transportOut[:, i], 'b', linewidth=2, label='model (outflow)') if bounds is not None: plt.gca().fill_between(t, bounds[0] * np.ones_like(t), bounds[1] * np.ones_like(t), alpha=0.3, label='observations (net)') plt.autoscale(enable=True, axis='x', tight=True) plt.ylabel('Volume transport (Sv)', fontsize=12, fontweight='bold') plt.xlabel('Time (Years)', fontsize=12, fontweight='bold') plt.title('Volume transport for {} ({}, mean (net)={:5.2f} $\pm$ {:5.2f})'.format(searchString, casename, \ np.nanmean(vol_transport[:,i]), np.nanstd(vol_transport[:,i]), fontsize=16, fontweight='bold')) plt.legend() plt.savefig(figfile, bbox_inches='tight') # Plot Heat Transport wrt Tref=0 figfile = '{}/heatTransport_{}_{}.png'.format(figdir, labelDict[searchString], casename) plt.figure(figsize=figsize, dpi=figdpi) plt.plot(t, heat_transport[:, i], 'k', linewidth=2, label='model (net)') plt.plot(t, heat_transportIn[:, i], 'r', linewidth=2, label='model (inflow)') plt.plot(t, heat_transportOut[:, i], 'b', linewidth=2, label='model (outflow)') plt.autoscale(enable=True, axis='x', tight=True) plt.ylabel('Heat transport wrt 0$^\circ$C (TW)', fontsize=12, fontweight='bold') plt.xlabel('Time (Years)', fontsize=12, fontweight='bold') plt.title('Heat transport for {} ({}, mean (net)={:5.2f} $\pm$ {:5.2f})'.format(searchString, casename, \ np.nanmean(heat_transport[:,i]), np.nanstd(heat_transport[:,i]), fontsize=16, fontweight='bold')) plt.legend() plt.savefig(figfile, bbox_inches='tight') # Plot Heat Transport wrt Tref=tempRef figfile = '{}/heatTransportTfp_{}_{}.png'.format( figdir, labelDict[searchString], casename) plt.figure(figsize=figsize, dpi=figdpi) plt.plot(t, heat_transportTfp[:, i], 'k', linewidth=2, label='model (net)') plt.plot(t, heat_transportTfpIn[:, i], 'r', linewidth=2, label='model (inflow)') plt.plot(t, heat_transportTfpOut[:, i], 'b', linewidth=2, label='model (outflow)') plt.autoscale(enable=True, axis='x', tight=True) plt.ylabel('Heat transport wrt freezing point (TW)', fontsize=12, fontweight='bold') plt.xlabel('Time (Years)', fontsize=12, fontweight='bold') plt.title('Heat transport for {} ({}, mean (net)={:5.2f} $\pm$ {:5.2f})'.format(searchString, casename, \ np.nanmean(heat_transportTfp[:,i]), np.nanstd(heat_transportTfp[:,i]), fontsize=16, fontweight='bold')) plt.legend() plt.savefig(figfile, bbox_inches='tight') # Plot FW Transport figfile = '{}/fwTransport_{}_{}.png'.format(figdir, labelDict[searchString], casename) plt.figure(figsize=figsize, dpi=figdpi) plt.plot(t, salt_transport[:, i], 'k', linewidth=2, label='model (net)') plt.plot(t, salt_transportIn[:, i], 'r', linewidth=2, label='model (inflow)') plt.plot(t, salt_transportOut[:, i], 'b', linewidth=2, label='model (outflow)') plt.autoscale(enable=True, axis='x', tight=True) plt.ylabel('FW transport (km$^3$/year)', fontsize=12, fontweight='bold') plt.xlabel('Time (Years)', fontsize=12, fontweight='bold') plt.title('FW transport for {} ({}, mean (net)={:5.2f} $\pm$ {:5.2f})'.format(searchString, casename, \ np.nanmean(salt_transport[:,i]), np.nanstd(salt_transport[:,i]), fontsize=16, fontweight='bold')) plt.legend() plt.savefig(figfile, bbox_inches='tight') # Add calls to save transport and then can build up ncid = Dataset(outfile, mode='w', clobber=True, format='NETCDF3_CLASSIC') # Add calls to save transport and then can build up ncid = Dataset(outfile, mode='w', clobber=True, format='NETCDF3_CLASSIC') ncid.createDimension('Time', None) ncid.createDimension('nTransects', nTransects) ncid.createDimension('StrLen', 64) transectNames = ncid.createVariable('TransectNames', 'c', ('nTransects', 'StrLen')) times = ncid.createVariable('Time', 'f8', 'Time') vol_transportVar = ncid.createVariable('volTransport', 'f8', ('Time', 'nTransects')) vol_transportInVar = ncid.createVariable('volTransportIn', 'f8', ('Time', 'nTransects')) vol_transportOutVar = ncid.createVariable('volTransportOut', 'f8', ('Time', 'nTransects')) heat_transportVar = ncid.createVariable('heatTransport', 'f8', ('Time', 'nTransects')) heat_transportInVar = ncid.createVariable('heatTransportIn', 'f8', ('Time', 'nTransects')) heat_transportOutVar = ncid.createVariable('heatTransportOut', 'f8', ('Time', 'nTransects')) heat_transportTfpVar = ncid.createVariable('heatTransportTfp', 'f8', ('Time', 'nTransects')) heat_transportTfpInVar = ncid.createVariable('heatTransportTfpIn', 'f8', ('Time', 'nTransects')) heat_transportTfpOutVar = ncid.createVariable('heatTransportTfpOut', 'f8', ('Time', 'nTransects')) salt_transportVar = ncid.createVariable('FWTransport', 'f8', ('Time', 'nTransects')) salt_transportInVar = ncid.createVariable('FWTransportIn', 'f8', ('Time', 'nTransects')) salt_transportOutVar = ncid.createVariable('FWTransportOut', 'f8', ('Time', 'nTransects')) vol_transportVar.units = 'Sv' vol_transportInVar.units = 'Sv' vol_transportOutVar.units = 'Sv' heat_transportVar.units = 'TW' heat_transportInVar.units = 'TW' heat_transportOutVar.units = 'TW' heat_transportTfpVar.units = 'TW' heat_transportTfpInVar.units = 'TW' heat_transportTfpOutVar.units = 'TW' salt_transportVar.units = 'km^3/year' salt_transportInVar.units = 'km^3/year' salt_transportOutVar.units = 'km^3/year' vol_transportVar.description = 'Net volume transport across transect' vol_transportInVar.description = 'Inflow volume transport across transect (in/out determined by edgeSign)' vol_transportOutVar.description = 'Outflow volume transport across transect (in/out determined by edgeSign)' heat_transportVar.description = 'Net heat transport (wrt 0degC) across transect' heat_transportInVar.description = 'Inflow heat transport (wrt 0degC) across transect (in/out determined by edgeSign)' heat_transportOutVar.description = 'Outflow heat transport (wrt 0degC) across transect (in/out determined by edgeSign)' heat_transportTfpVar.description = 'Net heat transport (wrt freezing point) across transect' heat_transportTfpInVar.description = 'Inflow heat transport (wrt freezing point) across transect (in/out determined by edgeSign)' heat_transportTfpOutVar.description = 'Outflow heat transport (wrt freezing point) across transect (in/out determined by edgeSign)' salt_transportVar.description = 'Net FW transport (wrt {:4.1f} psu) across transect'.format( saltRef) salt_transportInVar.description = 'Inflow FW transport (wrt {:4.1f} psu) across transect (in/out determined by edgeSign)'.format( saltRef) salt_transportOutVar.description = 'Outflow FW transport (wrt {:4.1f} psu) across transect (in/out determined by edgeSign)'.format( saltRef) times[:] = t vol_transportVar[:, :] = vol_transport vol_transportInVar[:, :] = vol_transportIn vol_transportOutVar[:, :] = vol_transportOut heat_transportVar[:, :] = heat_transport heat_transportInVar[:, :] = heat_transportIn heat_transportOutVar[:, :] = heat_transportOut heat_transportTfpVar[:, :] = heat_transportTfp heat_transportTfpInVar[:, :] = heat_transportTfpIn heat_transportTfpOutVar[:, :] = heat_transportTfpOut salt_transportVar[:, :] = salt_transport salt_transportInVar[:, :] = salt_transportIn salt_transportOutVar[:, :] = salt_transportOut for i in range(nTransects): nLetters = len(transectList[i]) transectNames[i, :nLetters] = transectList[i] ncid.close()