def calculate_bottle_oxygen(ssscc_list, ssscc_col, titr_vol, titr_temp, flask_nums): """ Wrapper function for collecting parameters and calculating oxygen values from Winkler titrations. Parameters ---------- ssscc_list : list of str List of stations to process ssscc_col : array-like Station/cast for each sample taken titr_vol : array-like Titration volume [mL] titr_temp : array-like Temperature of titration [degC] flask_nums : array-like Oxygen flask used for each sample Returns ------- oxy_mL_L : array-like Oxygen concentration [mL/L] Notes ----- Titration equation comes from WHP Operations and Methods, Culberson (1991): https://cchdo.github.io/hdo-assets/documentation/manuals/pdf/91_1/culber2.pdf """ params = pd.DataFrame() for ssscc in ssscc_list: df = gather_oxy_params(cfg.directory["oxy"] + ssscc) df["SSSCC"] = ssscc params = pd.concat([params, df]) # get flask volumes and merge with titration parameters flask_df = load_flasks() # TODO: volume correction from thermal expansion? volumes = pd.merge(flask_nums, flask_df, how="left")["FLASK_VOL"].values params = pd.merge(ssscc_col, params, how="left") # find 20degC equivalents rho_20C = gsw.rho_t_exact(0, 20, 0) rho_T_KIO3 = gsw.rho_t_exact(0, params["T_KIO3"], 0) N_KIO3_20C = params["N_KIO3"] * (rho_T_KIO3 / rho_20C) # TODO: does KIO3 volume get corrected? what is the recorded value? # V_KIO3_20C = correct_flask_vol(params["V_KIO3"], t=params["T_KIO3"]) # calculate O2 concentration (in mL/L) E = 5598 # stoichiometric relationship between thio_n and DO DO_reg = 0.0017 # correction for oxygen added by reagents V_reg = 2.0 # volume of reagents (mL) oxy_mL_L = ((((titr_vol.values - params["V_blank"]) * params["V_KIO3"] * N_KIO3_20C * E) / (params["V_std"] - params["V_blank"]) - 1000 * DO_reg)) / (volumes - V_reg) return oxy_mL_L.values
def bfrq(T, S, z, lat, lon): """ Calculate Bouyancy frequency from practical salinity and temperature. Converts to potential temperature and absolute salinity to calculate density """ SA = gsw.conversions.SA_from_SP(S, z, lon, lat) g = gsw.grav(lat, z) pdens = gsw.rho_t_exact(SA, T, z) dpdens = np.gradient(gsw.rho_t_exact(SA, T, z), axis=0) dz = np.gradient(z, axis=0) N2 = (g / pdens) * (dpdens / dz) return N2
def read_dbds(self, fns, settings): if settings['buoyancy_engine']=='shallow': buoyancy_variable='m_ballast_pumped' elif settings['buoyancy_engine']=='deep': buoyancy_variable='m_de_oil_vol' else: raise ValueError('Unknown buoyancy engine. Accepted values: (shallow|deep)') dbd = dbdreader.MultiDBD(filenames=fns, include_paired=True) tmp = dbd.get_sync("sci_ctd41cp_timestamp",["sci_water_pressure", "sci_water_cond", "sci_water_temp", "m_pitch", "m_battpos", buoyancy_variable]) _, tctd, P, C, T, pitch, battpos, buoyancy_change = tmp.compress(tmp[3]>0.01, axis=1) SP = gsw.SP_from_C(C*10, T, P*10) SA = gsw.SA_from_SP(SP, P*10, settings['longitude'], settings['latitude']) density = gsw.rho_t_exact(SA, T, P*10) #density = fast_gsw.rho(C*10, T, P*10, settings['longitude'], settings['latitude']) data = dict(time=tctd, pressure=P, pitch=pitch, buoyancy_change=buoyancy_change, battpos=battpos, density=density) self.model.input_data = data mask=np.zeros(tctd.shape,'int').astype(bool) # use all data by default self.model.set_mask(mask) self.model.OR(P*10<settings['minlimitdepth']) self.model.OR(P*10>settings['maxlimitdepth'])
def compute_buoyancy(data, temperature_var='SST', salinity_var='SSS'): """ Compute buoyancy from a dataset containing salinity and temperature variables Parameters ---------- data : xarray.Dataset A Dataset containing salinity and temperature variables temperature_var : str, optional The name of the temperature variable. Default is "SST". salinity_var : str, optional The name of the salinity variable Default is "SSS" Returns ------- res : xarray.Dataset A Dataset similar to the input but with a new variable "buoyancy" """ p = 0 * data['lon'] SA = gsw.SA_from_SP(data[salinity_var], p, data['lon'], data['lat']) rho = gsw.rho_t_exact(SA, data[temperature_var], p) b = 9.81 * (1 - rho / 1025.) return data.assign(buoyancy=xr.DataArray(b, dims='time'))
def read_dbds(self, fns, settings): if settings['buoyancy_engine'] == 'shallow': buoyancy_variable = 'm_ballast_pumped' elif settings['buoyancy_engine'] == 'deep': buoyancy_variable = 'm_de_oil_vol' else: raise ValueError( 'Unknown buoyancy engine. Accepted values: (shallow|deep)') dbd = dbdreader.MultiDBD(filenames=fns, complement_files=True) try: tmp = dbd.get_CTD_sync("m_pitch", "m_battpos", buoyancy_variable) except dbdreader.DbdError: tmp = dbd.get_sync("sci_water_cond", "sci_water_temp", "sci_water_pressure", "m_pitch", "m_battpos", buoyancy_variable) try: (_, lat), (_, lon) = dbd.get("m_gps_lat", "m_gps_lon") except dbdreader.DbdError: latitude = float(input("Provide decimal latitude:")) longitude = float(input("Provide decimal longitude:")) else: latitude = np.median(lat) longitude = np.median(lon) condition = np.logical_and(tmp[2] > 0.01, np.isfinite(np.sum(tmp, axis=0))) tctd, C, T, P, pitch, battpos, buoyancy_change = np.compress(condition, tmp, axis=1) SP = gsw.SP_from_C(C * 10, T, P * 10) SA = gsw.SA_from_SP(SP, P * 10, longitude, latitude) density = gsw.rho_t_exact(SA, T, P * 10) #density = fast_gsw.rho(C*10, T, P*10, longitude, latitude) data = dict(time=tctd, pressure=P, pitch=pitch, buoyancy_change=buoyancy_change, battpos=battpos, density=density) self.model.input_data = data mask = np.zeros(tctd.shape, 'int').astype(bool) # use all data by default self.model.set_mask(mask) self.model.OR(P * 10 < settings['minlimitdepth']) self.model.OR(P * 10 > settings['maxlimitdepth'])