Source code for pyLBL.mt_ckd.utils
from os.path import dirname, join, realpath
from netCDF4 import Dataset
from numpy import asarray, copy, exp, interp, where, zeros
LOSCHMIDT = 2.6867775e19 # Loschmidt constant [cm-3]
P0 = 1013.25 # Reference pressure (1 atmosphere) [mb].
SECOND_RADIATION_CONSTANT = 1.4387752 # Second radiation constant [cm K].
T0 = 296. # Reference temperature [K].
T273 = 273.15 # Reference temperature (0 celcius) [K].
m_to_cm = 100. # [cm m-1].
Pa_to_mb = 0.01 # [mb Pa-1].
[docs]def air_number_density(pressure, temperature, volume_mixing_ratio):
"""Calculates the air number density.
Args:
pressure: Pressure [mb].
temperature: Temperature [K].
volume_mixing_ratio: Dictionary of volume mixing ratios [mol mol-1].
Returns:
Number density of air [cm-3].
"""
return sum([dry_air_number_density(pressure, temperature, volume_mixing_ratio)*x
for x in volume_mixing_ratio.values()])
[docs]def dry_air_number_density(pressure, temperature, volume_mixing_ratio):
"""Calculates the dry air number density.
Args:
pressure: Pressure [mb].
temperature: Temperature [K].
volume_mixing_ratio: Dictionary of volume mixing ratios [mol mol-1].
Returns:
Number density of dry air [cm-3].
"""
return LOSCHMIDT*(pressure/P0)*(T273/temperature)*(1. - volume_mixing_ratio["H2O"])
[docs]def radiation_term(wavenumber, temperature):
"""Calculates the radiation term.
Args:
wavenumber: Array of wavenumber [cm-1].
temperature: Temperature [K].
Returns:
The radiation term [cm-1].
"""
t = temperature/SECOND_RADIATION_CONSTANT
x = wavenumber[:]/t
r = wavenumber[:]
r = where(x <= 0.01, 0.5*x*wavenumber, r)
return where(x <= 10., wavenumber*(1. - exp(-x))/(1. + exp(-x)), r)
[docs]def subgrid_bounds(grid, subgrid):
"""Calculates the starting and ending grid indices of a subgrid.
Args:
grid: A dictionary describing the main grid.
subgrid: A dictionary describing the subgrid.
Returns:
The starting and ending grid indices of the subgrid.
"""
if grid["resolution"] != subgrid["resolution"]:
raise ValueError("grid and subgrid have different resolutions.")
if grid["lower_bound"] > subgrid["lower_bound"] or \
grid["upper_bound"] < subgrid["upper_bound"]:
raise ValueError("subgrid not contained in grid.")
lower = int((subgrid["lower_bound"] - grid["lower_bound"])/grid["resolution"])
upper = int((subgrid["upper_bound"] - grid["lower_bound"])/grid["resolution"])
return lower, upper
[docs]class Continuum(object):
"""Abstract class for gridded continuum coefficients."""
def __init__(self, path):
"""Reads in the necessary data from an input dataset.
Args:
path: Path to the netcdf dataset.
"""
raise NotImplementedError("You must override this class.")
[docs] def spectra(self, temperature, pressure, vmr):
"""Calculates the spectral feature.
Args:
temperature: Temperature [K].
pressure: Pressure [mb].
vmr: Dictionary of volume mixing ratios [mol mol-1].
Return:
An array of continuum extinction [cm-1].
"""
raise NotImplementedError("You must override this method.")
[docs] def grid(self):
"""Calculates the wavenumber grid [cm-1].
Returns:
A 1d numpy array containing the wavenumber grid [cm-1].
"""
raise NotImplementedError("You must override this method.")
[docs]class Spectrum(object):
"""Helper class that reads data from a variable in the input dataset.
Attributes:
path: Path to the netcdf dataset.
grid: Dictionary describing the wavenumber grid.
"""
def __init__(self, path, name):
"""Reads the data from a variable in the input dataset.
Args:
path: Path to the netcdf dataset.
name: Name of the variable in the dataset.
"""
with Dataset(path, "r") as dataset:
v = dataset.variables[name]
self.data = copy(v[:])
self.grid = {x: v.getncattr("wavenumber_{}".format(x)) for x in
["lower_bound", "upper_bound", "resolution"]}
# self.units = v.getncattr("units")
[docs] def wavenumbers(self):
"""Calculates the wavenumber grid [cm-1] for the variable.
Returns:
A 1d numpy array containing the wavenumber grid [cm-1].
"""
return asarray([self.grid["lower_bound"] + i*self.grid["resolution"]
for i in range(self.data.size)])
[docs]class BandedContinuum(object):
"""Contains all bands for a specific molecule's continuum.
Attributes:
bands: List of Continuum objects.
"""
path = join(dirname(realpath(__file__)), "mt-ckd.nc")
def __init__(self):
"""Reads in the necessary data from an input dataset."""
raise NotImplementedError("You must override this method.")
[docs] def spectra(self, temperature, pressure, vmr, grid):
"""Calculates the continum spectrum and interpolates to the input grid.
Args:
temperature: Temperature [K].
pressure: Pressure [Pa].
vmr: Dictionary of volume mixing ratios [mol mol-1].
grid: Array containing the spectral grid [cm-1].
Return:
An array of continuum extinction [m-1].
"""
s = zeros(grid.size)
for band in self.bands:
s[:] += interp(grid, band.grid(),
band.spectra(temperature, pressure*Pa_to_mb, vmr),
left=0., right=0.)[:]*m_to_cm
return s