def _calibrate_ir(radiance, coefs): """Convert IR radiance to brightness temperature Reference: [IR] Args: radiance: Radiance [mW m-2 cm-1 sr-1] coefs: Dictionary of calibration coefficients. Keys: n: The channel's central wavenumber [cm-1] a: Offset [K] b: Slope [1] btmin: Minimum brightness temperature threshold [K] btmax: Maximum brightness temperature threshold [K] Returns: Brightness temperature [K] """ logger.debug('Calibrating to brightness temperature') # Compute brightness temperature using inverse Planck formula n = coefs['n'] bteff = C2 * n / xu.log(1 + C1 * n**3 / radiance.where(radiance > 0)) bt = xr.DataArray(bteff * coefs['b'] + coefs['a']) # Apply BT threshold return bt.where( xu.logical_and(bt >= coefs['btmin'], bt <= coefs['btmax']))
def get_atm_variables(mus, muv, phi, height, coeffs): (ah2o, bh2o, ao3, tau) = coeffs # From GetAtmVariables tau_step = np.linspace(TAUSTEP4SPHALB, MAXNUMSPHALBVALUES * TAUSTEP4SPHALB, MAXNUMSPHALBVALUES) sphalb0 = csalbr(tau_step) air_mass = 1.0 / mus + 1 / muv air_mass = air_mass.where(air_mass <= MAXAIRMASS, -1.0) taur = tau * xu.exp(-height / SCALEHEIGHT) rhoray, trdown, trup = chand(phi, muv, mus, taur) sphalb = sphalb0[np.int32(taur / TAUSTEP4SPHALB + 0.5)] Ttotrayu = ((2 / 3. + muv) + (2 / 3. - muv) * trup) / (4 / 3. + taur) Ttotrayd = ((2 / 3. + mus) + (2 / 3. - mus) * trdown) / (4 / 3. + taur) tO3 = 1.0 tO2 = 1.0 tH2O = 1.0 if ao3 != 0: tO3 = xu.exp(-air_mass * UO3 * ao3) if bh2o != 0: if bUseV171: tH2O = xu.exp(-xu.exp(ah2o + bh2o * xu.log(air_mass * UH2O))) else: tH2O = xu.exp(-(ah2o * ((air_mass * UH2O)**bh2o))) #t02 = exp(-m * aO2) TtotraytH2O = Ttotrayu * Ttotrayd * tH2O tOG = tO3 * tO2 return sphalb, rhoray, TtotraytH2O, tOG
def calibrate_tb(array, attributes, index, band_name): """Calibration for the emissive channels.""" offset = np.float32(attributes["radiance_offsets"][index]) scale = np.float32(attributes["radiance_scales"][index]) array = (array - offset) * scale # Planck constant (Joule second) h__ = np.float32(6.6260755e-34) # Speed of light in vacuum (meters per second) c__ = np.float32(2.9979246e+8) # Boltzmann constant (Joules per Kelvin) k__ = np.float32(1.380658e-23) # Derived constants c_1 = 2 * h__ * c__ * c__ c_2 = (h__ * c__) / k__ # Effective central wavenumber (inverse centimeters) cwn = np.array([ 2.641775E+3, 2.505277E+3, 2.518028E+3, 2.465428E+3, 2.235815E+3, 2.200346E+3, 1.477967E+3, 1.362737E+3, 1.173190E+3, 1.027715E+3, 9.080884E+2, 8.315399E+2, 7.483394E+2, 7.308963E+2, 7.188681E+2, 7.045367E+2], dtype=np.float32) # Temperature correction slope (no units) tcs = np.array([ 9.993411E-1, 9.998646E-1, 9.998584E-1, 9.998682E-1, 9.998819E-1, 9.998845E-1, 9.994877E-1, 9.994918E-1, 9.995495E-1, 9.997398E-1, 9.995608E-1, 9.997256E-1, 9.999160E-1, 9.999167E-1, 9.999191E-1, 9.999281E-1], dtype=np.float32) # Temperature correction intercept (Kelvin) tci = np.array([ 4.770532E-1, 9.262664E-2, 9.757996E-2, 8.929242E-2, 7.310901E-2, 7.060415E-2, 2.204921E-1, 2.046087E-1, 1.599191E-1, 8.253401E-2, 1.302699E-1, 7.181833E-2, 1.972608E-2, 1.913568E-2, 1.817817E-2, 1.583042E-2], dtype=np.float32) # Transfer wavenumber [cm^(-1)] to wavelength [m] cwn = 1. / (cwn * 100) # Some versions of the modis files do not contain all the bands. emmissive_channels = ["20", "21", "22", "23", "24", "25", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36"] global_index = emmissive_channels.index(band_name) cwn = cwn[global_index] tcs = tcs[global_index] tci = tci[global_index] array = c_2 / (cwn * xu.log(c_1 / (1000000 * array * cwn ** 5) + 1)) array = (array - tci) / tcs return array
def calibrate_bt(array, attributes, index, band_name): """Calibration for the emissive channels.""" offset = np.float32(attributes["radiance_offsets"][index]) scale = np.float32(attributes["radiance_scales"][index]) array = (array - offset) * scale # Planck constant (Joule second) h__ = np.float32(6.6260755e-34) # Speed of light in vacuum (meters per second) c__ = np.float32(2.9979246e+8) # Boltzmann constant (Joules per Kelvin) k__ = np.float32(1.380658e-23) # Derived constants c_1 = 2 * h__ * c__ * c__ c_2 = (h__ * c__) / k__ # Effective central wavenumber (inverse centimeters) cwn = np.array([ 2.641775E+3, 2.505277E+3, 2.518028E+3, 2.465428E+3, 2.235815E+3, 2.200346E+3, 1.477967E+3, 1.362737E+3, 1.173190E+3, 1.027715E+3, 9.080884E+2, 8.315399E+2, 7.483394E+2, 7.308963E+2, 7.188681E+2, 7.045367E+2], dtype=np.float32) # Temperature correction slope (no units) tcs = np.array([ 9.993411E-1, 9.998646E-1, 9.998584E-1, 9.998682E-1, 9.998819E-1, 9.998845E-1, 9.994877E-1, 9.994918E-1, 9.995495E-1, 9.997398E-1, 9.995608E-1, 9.997256E-1, 9.999160E-1, 9.999167E-1, 9.999191E-1, 9.999281E-1], dtype=np.float32) # Temperature correction intercept (Kelvin) tci = np.array([ 4.770532E-1, 9.262664E-2, 9.757996E-2, 8.929242E-2, 7.310901E-2, 7.060415E-2, 2.204921E-1, 2.046087E-1, 1.599191E-1, 8.253401E-2, 1.302699E-1, 7.181833E-2, 1.972608E-2, 1.913568E-2, 1.817817E-2, 1.583042E-2], dtype=np.float32) # Transfer wavenumber [cm^(-1)] to wavelength [m] cwn = 1. / (cwn * 100) # Some versions of the modis files do not contain all the bands. emmissive_channels = ["20", "21", "22", "23", "24", "25", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36"] global_index = emmissive_channels.index(band_name) cwn = cwn[global_index] tcs = tcs[global_index] tci = tci[global_index] array = c_2 / (cwn * xu.log(c_1 / (1000000 * array * cwn ** 5) + 1)) array = (array - tci) / tcs return array
def ds_arithmetics(ds: DatasetLike.TYPE, op: str, monitor: Monitor = Monitor.NONE) -> xr.Dataset: """ Do arithmetic operations on the given dataset by providing a list of arithmetic operations and the corresponding constant. The operations will be applied to the dataset in the order in which they appear in the list. For example: 'log,+5,-2,/3,*2' Currently supported arithmetic operations: log,log10,log2,log1p,exp,+,-,/,* where: log - natural logarithm log10 - base 10 logarithm log2 - base 2 logarithm log1p - log(1+x) exp - the exponential The operations will be applied element-wise to all arrays of the dataset. :param ds: The dataset to which to apply arithmetic operations :param op: A comma separated list of arithmetic operations to apply :param monitor: a progress monitor. :return: The dataset with given arithmetic operations applied """ ds = DatasetLike.convert(ds) retset = ds with monitor.starting('Calculate result', total_work=len(op.split(','))): for item in op.split(','): with monitor.child(1).observing("Calculate"): item = item.strip() if item[0] == '+': retset = retset + float(item[1:]) elif item[0] == '-': retset = retset - float(item[1:]) elif item[0] == '*': retset = retset * float(item[1:]) elif item[0] == '/': retset = retset / float(item[1:]) elif item[:] == 'log': retset = xu.log(retset) elif item[:] == 'log10': retset = xu.log10(retset) elif item[:] == 'log2': retset = xu.log2(retset) elif item[:] == 'log1p': retset = xu.log1p(retset) elif item[:] == 'exp': retset = xu.exp(retset) else: raise ValueError('Arithmetic operation {} not' ' implemented.'.format(item[0])) return retset
def stretch_weber_fechner(self, k, s0): """Stretch according to the Weber-Fechner law. p = k.ln(S/S0) p is perception, S is the stimulus, S0 is the stimulus threshold (the highest unpercieved stimulus), and k is the factor. """ attrs = self.data.attrs self.data = k * xu.log(self.data / s0) self.data.attrs = attrs
def stretch_weber_fechner(self, k, s0): """Stretch according to the Weber-Fechner law. p = k.ln(S/S0) p is perception, S is the stimulus, S0 is the stimulus threshold (the highest unpercieved stimulus), and k is the factor. """ attrs = self.data.attrs self.data = k * xu.log(self.data / s0) self.data.attrs = attrs
def calculate_dz(domain): """Use the hydrostatic relation to get dz from dp.""" # hydrostatic: dp = -rho g dz # ideal gas: rho = p / RT # => dz = -RT/g d[lnp] T = pfull_to_phalf(domain.temp, domain) pfull = (domain.pfull / domain.phalf.max()) * domain.ps dlnp = diff_pfull(xruf.log(pfull), domain) dz = -R_dry * T / grav * dlnp return dz
def _ir_calibrate(self, data): """Calibrate IR channels to BT.""" fk1 = float(self["planck_fk1"]) fk2 = float(self["planck_fk2"]) bc1 = float(self["planck_bc1"]) bc2 = float(self["planck_bc2"]) res = (fk2 / xu.log(fk1 / data + 1) - bc1) / bc2 res.attrs = data.attrs res.attrs['units'] = 'K' return res
def _ir_calibrate(self, data): """Calibrate IR channels to BT.""" fk1 = float(self["planck_fk1"]) fk2 = float(self["planck_fk2"]) bc1 = float(self["planck_bc1"]) bc2 = float(self["planck_bc2"]) res = (fk2 / xu.log(fk1 / data + 1) - bc1) / bc2 res.attrs = data.attrs res.attrs['units'] = 'K' res.attrs['standard_name'] = 'toa_brightness_temperature' return res
def ds_arithmetics(ds: xr.Dataset, op: str) -> xr.Dataset: """ Do arithmetic operations on the given dataset by providing a list of arithmetic operations and the corresponding constant. The operations will be applied to the dataset in the order in which they appear in the list. For example: 'log,+5,-2,/3,*2' Currently supported arithmetic operations: log,log10,log2,log1p,exp,+,-,/,* where: log - natural logarithm log10 - base 10 logarithm log2 - base 2 logarithm log1p - log(1+x) exp - the exponential The operations will be applied element-wise to all arrays of the dataset. :param ds: The dataset to which to apply arithmetic operations :param op: A comma separated list of arithmetic operations to apply :return: The dataset with given arithmetic operations applied """ retset = ds for item in op.split(','): item = item.strip() if item[0] == '+': retset = retset + float(item[1:]) elif item[0] == '-': retset = retset - float(item[1:]) elif item[0] == '*': retset = retset * float(item[1:]) elif item[0] == '/': retset = retset / float(item[1:]) elif item[:] == 'log': retset = xu.log(retset) elif item[:] == 'log10': retset = xu.log10(retset) elif item[:] == 'log2': retset = xu.log2(retset) elif item[:] == 'log1p': retset = xu.log1p(retset) elif item[:] == 'exp': retset = xu.exp(retset) else: raise ValueError('Arithmetic operation {} not' ' implemented.'.format(item[0])) return retset
def logscale_precip(data, varname='tp'): """ log-scale variable "varname" in data Parameters ---------- data: xarray dataarray, where varname is the variable that needs to be log-scaled Returns ---------- data: data with variable log-scaled """ data.loc[varname] = data.loc[varname].where(data.loc[varname] > 0.000001, -1) # define lower threshold for "no rain" data.loc[varname] = xu.log(data.loc[varname]) data.loc[varname] = data.loc[varname].where(~np.isnan(data.loc[varname]), -20) # all zero precip got -1, all -1 got nan, all nan get -20 return data
def read_dataset(self, dataset_key, info): h5f = self.h5f channel = chans_dict[dataset_key.name] chan_dict = dict([(key.split("-")[1], key) for key in h5f["All_Data"].keys() if key.startswith("VIIRS")]) h5rads = h5f["All_Data"][chan_dict[channel]]["Radiance"] chunks = h5rads.chunks or CHUNK_SIZE rads = xr.DataArray(da.from_array(h5rads, chunks=chunks), name=dataset_key.name, dims=['y', 'x']).astype(np.float32) h5attrs = h5rads.attrs scans = h5f["All_Data"]["NumberOfScans"][0] rads = rads[:scans * 16, :] # if channel in ("M9", ): # arr = rads[:scans * 16, :].astype(np.float32) # arr[arr > 65526] = np.nan # arr = np.ma.masked_array(arr, mask=arr_mask) # else: # arr = np.ma.masked_greater(rads[:scans * 16, :].astype(np.float32), # 65526) rads = rads.where(rads <= 65526) try: rads = xr.where( rads <= h5attrs['Threshold'], rads * h5attrs['RadianceScaleLow'] + h5attrs['RadianceOffsetLow'], rads * h5attrs['RadianceScaleHigh'] + h5attrs['RadianceOffsetHigh']) except (KeyError, AttributeError): logger.info("Missing attribute for scaling of %s.", channel) pass unit = "W m-2 sr-1 μm-1" if dataset_key.calibration == 'counts': raise NotImplementedError("Can't get counts from this data") if dataset_key.calibration in [ 'reflectance', 'brightness_temperature' ]: # do calibrate try: # First guess: VIS or NIR data a_vis = h5attrs['EquivalentWidth'] b_vis = h5attrs['IntegratedSolarIrradiance'] dse = h5attrs['EarthSunDistanceNormalised'] rads *= 100 * np.pi * a_vis / b_vis * (dse**2) unit = "%" except KeyError: # Maybe it's IR data? try: a_ir = h5attrs['BandCorrectionCoefficientA'] b_ir = h5attrs['BandCorrectionCoefficientB'] lambda_c = h5attrs['CentralWaveLength'] rads *= 1e6 rads = (h * c) / (k * lambda_c * xu.log(1 + (2 * h * c**2) / ((lambda_c**5) * rads))) rads *= a_ir rads += b_ir unit = "K" except KeyError: logger.warning("Calibration failed.") elif dataset_key.calibration != 'radiance': raise ValueError("Calibration parameter should be radiance, " "reflectance or brightness_temperature") rads = rads.clip(min=0) rads.attrs = self.mda rads.attrs['units'] = unit return rads
def read_dataset(self, dataset_key, info): h5f = self.h5f channel = chans_dict[dataset_key.name] chan_dict = dict([(key.split("-")[1], key) for key in h5f["All_Data"].keys() if key.startswith("VIIRS")]) h5rads = h5f["All_Data"][chan_dict[channel]]["Radiance"] chunks = h5rads.chunks or CHUNK_SIZE rads = xr.DataArray(da.from_array(h5rads, chunks=chunks), name=dataset_key.name, dims=['y', 'x']).astype(np.float32) h5attrs = h5rads.attrs # scans = h5f["All_Data"]["NumberOfScans"][0] # if channel in ("M9", ): # arr = rads[:scans * 16, :].astype(np.float32) # arr[arr > 65526] = np.nan # arr = np.ma.masked_array(arr, mask=arr_mask) # else: # arr = np.ma.masked_greater(rads[:scans * 16, :].astype(np.float32), # 65526) rads = rads.where(rads <= 65526) try: rads = xr.where(rads <= h5attrs['Threshold'], rads * h5attrs['RadianceScaleLow'] + h5attrs['RadianceOffsetLow'], rads * h5attrs['RadianceScaleHigh'] + h5attrs['RadianceOffsetHigh']) except (KeyError, AttributeError): logger.info("Missing attribute for scaling of %s.", channel) pass unit = "W m-2 sr-1 μm-1" if dataset_key.calibration == 'counts': raise NotImplementedError("Can't get counts from this data") if dataset_key.calibration in ['reflectance', 'brightness_temperature']: # do calibrate try: # First guess: VIS or NIR data a_vis = h5attrs['EquivalentWidth'] b_vis = h5attrs['IntegratedSolarIrradiance'] dse = h5attrs['EarthSunDistanceNormalised'] rads *= 100 * np.pi * a_vis / b_vis * (dse**2) unit = "%" except KeyError: # Maybe it's IR data? try: a_ir = h5attrs['BandCorrectionCoefficientA'] b_ir = h5attrs['BandCorrectionCoefficientB'] lambda_c = h5attrs['CentralWaveLength'] rads *= 1e6 rads = (h * c) / (k * lambda_c * xu.log(1 + (2 * h * c ** 2) / ((lambda_c ** 5) * rads))) rads *= a_ir rads += b_ir unit = "K" except KeyError: logger.warning("Calibration failed.") elif dataset_key.calibration != 'radiance': raise ValueError("Calibration parameter should be radiance, " "reflectance or brightness_temperature") rads = rads.clip(min=0) rads.attrs = self.mda rads.attrs['units'] = unit return rads
def global_XCO2(datetime): execfile('../../rpnenv/bin/activate_this.py', dict(__file__='../../rpnenv/bin/activate_this.py')) import fstd2nc # get the ACOS pressure coefficients and acceleration due to gravity at set pressure levels acos_plevs = np.load('../earth_data/ACOS_plevs.npy') grav_plevs, grav = np.load('../earth_data/ACOS_grav.npy') # get the model files that will be read from model_fname = datetime.strftime(_rpn_format) model_fname_prev = (datetime - dt.timedelta(days=1)).strftime(_rpn_format) model = fstd2nc.Buffer(model_fname, vars='CO2,HU,P0', diag_as_model_level=True).to_xarray() try: # open files for the day of the observations and the final time step of the previous day mprev = fstd2nc.Buffer(model_fname_prev, vars='CO2,HU,P0', diag_as_model_level=True).to_xarray() model = xarray.concat([ mprev.sel(time=datetime.strftime('%Y-%m-%dT00:00:00.000000000')), model ], 'time').transpose('time', 'level', 'lat', 'lon') except: # First day - no previous day to get data from, so the first 2 hours will have to be extrapolated print("Error due to first day of run ignored") pass model.load() model_interp = model.interp(time=datetime, kwargs={'fill_value': None}) print(model_interp) logp = model_interp.a + model_interp.b * log(model_interp.P0 / 1000.) p = exp(logp) / 100. p = p.transpose('level', 'lat', 'lon').values psurf = model_interp.P0.values acos_p = psurf[np.newaxis, ...] * acos_plevs[:, np.newaxis, np.newaxis] CO2 = model_interp.CO2.values HU = model_interp.CO2.values print("interpolating to ACOS algorithm pressure levels") u = np.zeros(acos_p.shape) q = np.zeros(acos_p.shape) g = np.zeros(acos_p.shape) for i in range(p.shape[1]): for j in range(p.shape[2]): u[:, i, j] = np.interp(acos_p[:, i, j], p[:, i, j], CO2[:, i, j]) q[:, i, j] = np.interp(acos_p[:, i, j], p[:, i, j], HU[:, i, j]) g[:, i, j] = np.interp(acos_p[:, i, j], grav_plevs, grav) c = (1. - q) / mdry / g cavg = 0.5 * (c[:-1, ...] + c[1:, ...]) delp = acos_p[:-1, ...] - acos_p[1:, ...] hprime = cavg * delp / np.sum(cavg * delp, axis=0)[np.newaxis, ...] f = 0.5 * np.ones(acos_p.shape) f[-1, ...] = (psurf - acos_p[-2, ...]) / (acos_p[-1, ...] - acos_p[-2, ...]) h = np.zeros(acos_p.shape) h[0, ...] = (1. - f[0, ...]) * hprime[0, ...] h[1:-2, ...] = f[:-3, ...] * hprime[:-2, ...] + ( 1. - f[1:-2, ...]) * hprime[1:-1, ...] h[-2, ...] = f[-3, ...] * hprime[-2, ...] + ( 1 - f[-2, ...] * f[-1, ...]) * hprime[-1, ...] h[-1, ...] = f[-1, ...] * f[-2, ...] * hprime[-1, ...] profile = 0.0024141666666666665 * u xco2 = np.sum(h * profile, axis=0) lats = model_interp.lat.values lons = model_interp.lon.values lons[lons > 180.] -= 360. reorder = np.argsort(lons) xco2 = xco2[:, reorder] lons = lons[reorder] return xco2, lats, lons
def wanninkhof92(T=None, S=None, P=None, u_mean=None, u_var=None, xCO2=None, pCO2_sw=None, iceFrac=None, scale_factor=0.27): ''' Calculate air-sea CO2 flux following Wanninkhof 1992 Inputs ============ T = Temperature [degC] S = Salinity [parts per thousand] P = Atmospheric pressure at 10m [atm] u_mean = mean wind speed [m/s] u_var = variance of wind speed m/s averaged monthly [m/s] xcO2 = atmoapheric mixing ratio of CO2 [ppm] pCO2_sw = CO2 partial pressure of seawter [uatm] scale_factor = gas transfer scale factor (default=0.27) Output ============ F_co2 = air-sea co2 flux [molC/m2/yr] References ============ Weiss (1974) [for solubility of CO2] Carbon dioxide in water and seawater: the solubility of a non-ideal gas Weiss and Price (1980) [for saturation vapor pressure, not used here] Nitrous oxide solubility in water and seawater Dickson et al (2007) [for saturation vapor pressure] Guide to best practices for Ocean CO2 measurements, Chapter 5 section 3.2 Wanninkhof (1992) [for gas transfer and Schmidt number] Relationship between wind speed and gas exchange over the ocean Sweeney et al. (2007) [for gas transfer scale factor] Constraining global air‐sea gas exchange for CO2 with recent bomb 14C measurements Sarmiento and Gruber (2007) [for overview and discussion of CO2 flux] Ocean biogeochmiecal dynsmics, Ch3 see panel 3.2.1 Notes ============ this code is optimized to work with Xarray ## Notes on partial pressure moist air P_h20/P = saturation vapor pressure (Weiss and Price 1980) Water vapor is generally assumed to be at saturation in the vicinity of the air-sea interfae. p_moist = X_dry (P - P_h20) = x_dry * P (1-P_h20/P) = P_dry (1-P_h20/P) ## Notes on U2 Need to add wind speed variance if using Wanninkhof (1992) If we don't, then we are underestimating the gas-transfer velocity U2 = (U_mean)^2 + (U_prime)^2, (U_prime)^2 is the standard deviation See Sarmiento and Gruber (2007) for a discussion of this ## Test values # T = 298.15 # S = 35 ''' # ================================================================== # 0. Conversions # ================================================================== # Convert from Celsius to kelvin T_kelvin = T + 273.15 # ================================================================== # 1. partial pressure of CO2 in moist air # Units : uatm # ================================================================== # Weiss and Price (1980) formula ## P_sat_vapor = xu.exp(24.4543 - 67.4509*(100/T_kelvin) - 4.8489*xu.log(T_kelvin/100) - 0.000544*S) # Following Dickson et al (2007) Chapter 5 section 3.2 a1 = -7.85951783 a2 = 1.84408259 a3 = -11.7866497 a4 = 22.6807411 a5 = -15.9618719 a6 = 1.80122502 Tc = 647.096 pc = (22.064 * 10**6) / 101325 g = (1 - (T_kelvin / Tc)) # pure water saturation vapor pressure, Wagner and Pruß, 2002 p_h20 = pc * xu.exp( (Tc / T_kelvin) * (a1 * g + a2 * g**(1.5) + a3 * g**(3) + a4 * g** (3.5) + a5 * g**(4) + a6 * g**(7.5))) # total molality MT = (31.998 * S) / (1000 - 1.005 * S) # osmotic coefficient at 25C by Millero (1974) b1 = 0.90799 b2 = -0.08992 b3 = 0.18458 b4 = -0.07395 b5 = -0.00221 phi = b1 + b2 * (0.5 * MT) + b3 * (0.5 * MT)**2 + b4 * ( 0.5 * MT)**3 + b5 * (0.5 * MT)**4 # vapor pressure above sea-water, Dickson et al. (2007), Chapter 5, section 3.2 P_sat_vapor = p_h20 * xu.exp(-0.018 * phi * MT) # Partial pressure of CO2 in moist air, Dickson et al. (2007), SOP 4 section 8.3 P_moist = (P * xCO2) * (1 - P_sat_vapor) # ================================================================== # 2. Solubility of CO2 # Notes : T in degC, S in PSU, # 1.0E-6 converts to correct to muatm # Units : to mol.kg^{-1}uatm^{-1} # Reference : Weiss (1974) # ================================================================== S_co2 = xu.exp( 9345.17 / T_kelvin - 60.2409 + \ 23.3585 * xu.log( T_kelvin / 100 ) + \ S * ( 0.023517 - 0.00023656 * T_kelvin + \ 0.0047036 * ( T_kelvin / 100 )**2 )) *1.0E-6 # ================================================================== # 3. Gas transfer velocity # Units : cm/hr # Reference : Wanninkhof (1992) # Sweeney et al. (2007) # per.comm with P. Landschutzer Feb. 19 2019 # ================================================================== # Dimensionless Schmidt number (Sc) # References Wanninkhof 1992 A = 2073.1 B = 125.62 C = 3.6276 D = 0.043219 # Schmidt number # units : dimensionless Sc = A - B * T + C * T**2 - D * T**3 # Gas transfer velocity # References : Wanninkhof 1992, # Sweeney et al. 2007 scale factor k_w = scale_factor * (Sc / 660)**(-0.5) * (u_mean**2 + u_var) # ================================================ # 4. air-sea CO2 exchange # Units : mol/m2/yr # Reference : Wanninkhof (1992) # ================================================ # Convert from cm*mol/hr/kg to mol/m2/yr conversion = (1000 / 100) * 365 * 24 # Air-sea CO2 flux F_co2 = k_w * S_co2 * (1 - iceFrac) * (P_moist - pCO2_sw) * conversion return F_co2
def _tl15(self, data, wavenumber): """Compute the L15 temperature.""" return ((C2 * wavenumber) / xu.log((1.0 / data) * C1 * wavenumber ** 3 + 1.0))
def _tl15(self, data, wavenumber): """Compute the L15 temperature.""" return ((C2 * wavenumber) / xu.log((1.0 / data) * C1 * wavenumber**3 + 1.0))