Source code for scintillometry.backend.constants

"""Copyright 2019-2023 Scintillometry Contributors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

=====

Defines classes and functions for determining constants.
"""


[docs]class AtmosConstants(object): """Atmospheric constants. There seems to be a contradiction between the values for |a_11| in the Scintillometer Theory Manual 1.05 (Chapter 1.1.5) [#scintec2022]_, and the BLS Manual 1.49 (A.2 Eq.6) [#scintec2008]_. The Scintillometer Theory Manual is more recent. Attributes: alpha11 (dict): |Cn2| measurement coefficients |a_11| for each BLS type, [|m^(7/3)|]. alpha12 (dict): |Cn2| measurement coefficients |a_12| for each BLS type. lamda (float): BLS wavelength, |lamda| [nm]. lamda_error (float): BLS wavelength error, [nm]. at_opt (float): |A_T| coefficient for 880 nm & typical atmospheric conditions, from Ward et al. (2013). [#ward2013]_ aq_opt (float): |A_q| coefficient for 880 nm & typical atmospheric conditions, Ward et al. (2013). [#ward2013]_ most_coeffs_ft (dict[list[tuple, tuple]]): Coefficients for MOST functions |f_CT2|, in format: [(unstable 01, unstable 02), (stable 01, stable 02)] cp (float): Specific heat capacity of air at constant pressure, |c_p| [|JK^-1| |kg^-1|]. dalr (float): Dry adiabatic lapse rate |Gamma_d| [|Km^-1|]. The lapse rate is positive. g (float): Gravitational acceleration [|ms^-2|]. k (float): von Kármán's constant. kelvin (float): 0°C in kelvins. latent_vapour (float): Latent heat of vapourisation at 20°C [|Jkg^-1|]. r_dry (float): Specific gas constant for dry air, |R_dry| [|JK^-1| |kg^-1|]. r_vapour (float): Specific gas constant for water vapour, |R_v| [|JK^-1| |kg^-1|]. ratio_rmm (float): Ratio of molecular masses of water vapour and dry air i.e. ratio of gas constants |epsilon|. ref_pressure (int): Reference pressure (not SLP), |P_b| [Pa]. rho (float): Density of air at STP, |rho| [|kgm^-3|]. """ def __init__(self): """Initialise hardcoded constants.""" super().__init__() self.alpha11 = { "450": 4.9491e-2, # BLS450, 4.48 * D^(7/3), Theory manual 1.1.5 "900": 4.9491e-2, # BLS900, 4.48 * D^(7/3), Theory manual 1.1.5 "2000": 1.9775e-1, # BLS2000 4.48 * D^(7/3), Theory manual 1.1.5 "450_pra": 1.0964e-2, # with path reduction aperture "900_pra": 1.0964e-2, "2000_pra": 4.5618e-2, "450_alt": 5.5339e-2, # BLS Manual A.2 Eq.6 "900_alt": 5.5339e-2, "2000_alt": 1.9372e-1, } self.alpha12 = { # N/A for BLS 450 "900": 0.50175, "2000": 1.4768, "900_pra": 0.86308, # with path reduction aperture "2000_pra": 2.2726, } self.lamda = 880e-9 # Scintillometer Theory Manual recommends 880 nm self.lamda_error = 20e-9 # From Ward et al., (2012) self.at_opt = -270e-6 # A_T coefficient for 880 nm & typical atmos conditions self.aq_opt = -0.685e-6 # A_q coefficient for 880 nm & typical atmos conditions # MOST coefficients for similarity function self.most_coeffs_ft = { # [(unstable, unstable), (stable, stable)] "an1988": [(4.9, 6.1), (4.9, 2.2)], # Andreas (1998) "li2012": [(6.7, 14.9), (4.5, 1.3)], # Li et al. (2012) "wy1971": [(4.9, 7), (4.9, 2.75)], # Wyngaard et al. (1971) "wy1973": [(4.9, 7), (4.9, 2.4)], # Wyngaard et al. (1973) "ma2014": [(6.1, 7.6), (0, 0)], # Maronga et al. (2014) "br2014": [(4.4, 10.2), (0, 0)], # Braam et al. (2014) } # Physical constants self.cp = 1004.67 self.cp_dry = 1003.5 self.g = 9.81 self.dalr = self.g / self.cp self.kelvin = 273.15 self.k = 0.4 self.latent_vapour = 2.45e6 self.r_dry = 287.04 self.r_vapour = 461.5 self.ratio_rmm = self.r_dry / self.r_vapour self.ref_pressure = 100000 # Pa self.rho = 1.225
[docs] def get(self, constant_name: str): return getattr(self, constant_name)
[docs] def overwrite(self, constant_name: str, value: float): return setattr(self, constant_name, value)
[docs] def convert_pressure(self, pressure, base=True): """Converts pressure data to pascals [Pa] or hectopascals [hPa]. The input dataframe or series is copied, as this function may be called before the processing of the original data has finished. This method handles 0 and NaN values, but only converts correctly for pressure values in these intervals: - |P| [bar] < 2 bar - 2 hPa < |P| [hPa] < 2000 hPa - |P| [Pa] > 2000 Pa This method should therefore only be used on pre-processed data as a *convenience*. By default, converts to pascals. Args: pressure (Union[pd.DataFrame, pd.Series]): Pressure measurements |P| in pascals [Pa], hectopascals [hPa], or bars [bar]. base (bool): If True, converts to pascals [Pa]. Otherwise, converts to hectopascals [hPa]. Default True. Returns: Union[pd.DataFrame, pd.Series]: Pressure measurements |P| in either pascals [Pa] or hectopascals [hPa]. """ convert = pressure.copy(deep=True) check = convert[convert.gt(0)].dropna() # NaN and 0 fail check if not base: if (check.lt(2)).any().any(): # P in bar convert = convert.multiply(1000) elif not (check.lt(2000)).any().any(): # P in Pa convert = convert.divide(100) else: if (check.lt(2)).any().any(): # P in bar convert = convert.multiply(100000) elif (check.lt(2000)).any().any(): # P in hPa/mbar convert = convert.multiply(100) return convert
[docs] def convert_temperature(self, temperature, base=True): """Converts temperature to Celsius [°C] or kelvins [K]. The input dataframe or series is copied, as this function may be called before the processing of the original data has finished. This method handles 0 and NaN values, but only converts correctly for temperature values in these intervals: - T [K] > 130 K - T [°C] < 130 °C This method should therefore only be used on pre-processed data as a *convenience*. By default, converts to kelvins. Args: temperature (Union[pd.DataFrame, pd.Series]): Temperature measurements |T| in kelvins [K] or Celsius [°C]. base (bool): If True, converts to kelvins [K]. Otherwise, converts to Celsius [°C]. Default True. Returns: Union[pd.DataFrame, pd.Series]: Temperature measurements |T| in either kelvins [K] or Celsius [°C]. """ convert = temperature.copy(deep=True) check = convert[convert.gt(0)].dropna() # NaN and 0 fail check if (not base) and ((check.gt(130)).any().any()): # convert to Celsius convert = convert - AtmosConstants().kelvin elif base and (check.lt(130)).any().any(): # convert to Kelvins convert = convert + AtmosConstants().kelvin else: pass return convert