import fnmatch
import re
from pyaerocom.exceptions import VariableDefinitionError
[docs]
class VarNameInfo:
"""This class can be used to retrieve information from variable names"""
#: valid number range for retrieval of wavelengths from variable name
_VALID_WVL_RANGE = [0.1, 10000] # nm
#: valid variable families for wavelength retrievals
_VALID_WVL_IDS = ["od", "abs", "ec", "sc", "ac", "bsc", "ssa"]
PATTERNS = {"od": r"od\d+aer"}
DEFAULT_VERT_CODE_PATTERNS = {
"abs*": "Column",
"od*": "Column",
"ang*": "Column",
"load*": "Column",
"wet*": "Surface",
"dry*": "Surface",
"emi*": "Surface",
}
def __init__(self, var_name):
self.var_name = var_name
self._nums = []
try:
self._nums = self._numbers_in_string(var_name)
except Exception:
pass
[docs]
def get_default_vert_code(self):
"""Get default vertical code for variable name"""
for pattern, code in self.DEFAULT_VERT_CODE_PATTERNS.items():
if fnmatch.fnmatch(self.var_name, pattern):
return code
raise ValueError(f"No default vertical code could be found for {self.var_name}")
@staticmethod
def _numbers_in_string(input_str):
"""Get list of all numbers in input str
Parameters
----------
input_str : str
string to be checked
Returns
-------
list
list of numbers that were found in input string
"""
return [int(x) for x in re.findall(r"\d+", input_str)]
@property
def contains_numbers(self):
"""Boolean specifying whether this variable name contains numbers"""
if len(self._nums) > 0:
return True
return False
@property
def is_wavelength_dependent(self):
"""Boolean specifying whether this variable name is wavelength dependent"""
for item in self._VALID_WVL_IDS:
if self.var_name.startswith(item):
return True
return False
@property
def contains_wavelength_nm(self):
"""Boolean specifying whether this variable contains a certain wavelength"""
if not self.contains_numbers:
return False
low, high = self._VALID_WVL_RANGE
if self._nums and low <= self._nums[0] <= high:
return True
return False
@property
def wavelength_nm(self):
"""Wavelength in nm (if appliable)"""
if not self.is_wavelength_dependent:
raise VariableDefinitionError(
f"Variable {self.var_name} is not wavelength "
f"dependent (does not start with either of {self._VALID_WVL_IDS})"
)
elif not self.contains_wavelength_nm:
raise VariableDefinitionError("Wavelength could not be extracted from variable name")
return self._nums[0]
[docs]
def in_wavelength_range(self, low, high):
"""Boolean specifying whether variable is within wavelength range
Parameters
----------
low : float
lower end of wavelength range to be tested
high : float
upper end of wavelength range to be tested
Returns
-------
bool
True, if this variable is wavelength dependent and if the
wavelength that is inferred from the filename is within the
specified input range
"""
return low <= self.wavelength <= high
[docs]
def translate_to_wavelength(self, to_wavelength):
"""Create new variable name at a different wavelength
Parameters
----------
to_wavelength : float
new wavelength in nm
Returns
-------
VarNameInfo
new variable name
"""
if not self.contains_wavelength_nm:
raise ValueError(f"Variable {self.var_name} is not wavelength dependent")
name = self.var_name.replace(str(self.wavelength_nm), str(to_wavelength))
return VarNameInfo(name)
def __str__(self):
s = (
f"\nVariable {self.var_name}\n"
f"is_wavelength_dependent: {self.is_wavelength_dependent}\n"
)
if hasattr(self, "is_optical_density"): # pragma: no cover
s += f"is_optical_density: {self.is_optical_density}\n" # can't find situation where this happens however not sure if depricated
if self.is_wavelength_dependent:
s += f"\nwavelength_nm: {self.wavelength_nm}"
return s