Source code for skysim.airglow

"""Compute optical airglow emission and transmission.

Refer to Section 4 of Nolll 2012 for details.
"""
import numpy as np

import skysim.utils.data
import skysim.utils.resample
import skysim.transmission


[docs]def airmass_ag(z): """Calculate the effective airmass of airglow emission from ~90km. Use equation (23) of Noll 2012. Parameters ---------- z : float or array Zenith angle(s) in degrees. Returns ------- float or array Airmass(es) corresponding to each input zenith angle. """ z = np.asarray(z) return (1 - 0.972 * np.sin(np.deg2rad(z)) ** 2) ** -0.5
[docs]def airglow_scattering(z): """Calculate net Rayleigh and Mie scattering of airglow emission. Use equations (24) and (25) of Noll 2012 to approximate the net effect of scattering as an optical depth multiplier. Negative values are possible and indicate that scattering of indirect airglow into the line of sight exceeds scattering of direct airglow out of the line of sight. Parameters ---------- z : float or array Zenith angle(s) in degrees. Returns ------- tuple Tuple (fR, fM) giving the net optical depth multipliers for Rayleigh and Mie scattering, respectively. The components fR, fM will be floats or arrays matching the input z shape. """ x = np.log10(airmass_ag(z)) # Calculate the Rayleigh scattering fraction. fR = 1.669 * x - 0.146 fM = 1.732 * x - 0.318 return fR, fM
[docs]def get_airglow(lam, z, p=744., H=2.64, Rayleigh=True, Mie=True, absorption=True): """Calculate airglow flux. Automatically broadcasts over input arrays, but the wavelength input ``lam`` must be 1D and appear in the last axis. The CPU time scales with the number of unique values in ``z`` so calculating on a grid with many duplicates of the same value is relatively efficient (e.g., :class:`skysim.utils.AltAzGrid`). Parameters ---------- lam : float or 1D array Wavelength in nanometers. z : float or array Zenith angle(s) in degrees. p : float or array Pressure at the observation elevation in hPa, used for Rayleigh scattering. H : float or array Elevation of the observation in km, used for Rayleigh scattering. Rayleigh : bool Apply Rayleigh scattering effects. Mie : bool Apply aerosol Mie scattering effects. absorption : bool Apply molecular (but not ozone) absorption effects. Returns ------- tuple Tuple (cont, line) of arrays of airglow continuum and line fluxes in ph / (s cm2 nm). """ lam = np.atleast_1d(lam) z = np.asarray(z) z_unique = np.unique(z) if 4 * z_unique.size < 3 * z.size: # Only do the calculation for unique values of z, remembering how # to assemble the output array. z_compressed = True assert z.shape[-1] == 1 idx_out = np.searchsorted(z_unique, np.squeeze(z, -1)) z = z_unique.reshape(-1, 1) else: z_compressed = False # Lookup the unextincted airglow fluxes. atm = skysim.utils.data.get('atmosphere') cont = atm['airglow_cont'].data.copy() line = atm['airglow_line'].data.copy() # Apply absorption on the high-resolution atmosphere table grid. Xag = airmass_ag(z) if absorption: transmission = atm['trans_ma'].data ** Xag cont = cont * transmission line = line * transmission # Resample to output wavelength grid for scattering calculations. ag_lam = atm['wavelength'].data.copy() cont = skysim.utils.resample.resample_density(lam, ag_lam, cont) line = skysim.utils.resample.resample_density(lam, ag_lam, line) # Apply scattering effects. if Rayleigh or Mie: fR, fM = airglow_scattering(z) tau0 = np.zeros_like(cont) if Rayleigh: tau0 += fR * skysim.transmission.tau0R(lam, p, H) if Mie: tau0 += fM * skysim.transmission.tau0M(lam) scattering = np.exp(-tau0 * Xag) cont = cont * scattering line = line * scattering if z_compressed: # Assemble the ouput, repeating values with the same z as necessary. cont = cont[idx_out] line = line[idx_out] return cont, line