Source code for pyrt.radiation

"""The :code:`radiation` module contains data structures for holding the
radiation variables used in DISORT.
"""
import numpy as np


[docs]class IncidentFlux: """A data structure for holding the incident fluxes. IncidentFlux creates scalars for the incident beam and isotropic fluxes and performs checks that the input fluxes are valid inputs to DISORT. """ def __init__(self, beam_flux: float = np.pi, isotropic_flux: float = 0.0) -> None: r""" Parameters ---------- beam_flux The intensity of the incident beam at the top boundary. Ensure this variable and :code:`isotropic_flux` have the same units. Note that this is an infinitely wide beam. isotropic_flux The intensity of the incident beam at the top boundary. Ensure this variable and :code:`beam_flux` have the same units. Raises ------ TypeError Raised if either input flux cannot be cast into a float. Notes ----- If :py:attr:`~ThermalEmission.thermal_emission` is set to :code:`True`, this is assumed to have the same units as :code:`PLKAVG` (which defaults to [:math:`\frac{\text{W}}{\text{m}^2}`]) and the corresponding incident flux is umu0 * beam_flux and pi * isotropic_flux. If :py:attr:`~ThermalEmission.thermal_emission` is set to :code:`False`, :code:`beam_flux` and :code:`isotropic_flux` have arbitrary units, and the output fluxes and intensities are assumed to have the same units as these variables. """ self.__beam_flux = self.__cast_to_float(beam_flux, 'beam_flux') self.__isotropic_flux = \ self.__cast_to_float(isotropic_flux, 'isotropic_flux') @staticmethod def __cast_to_float(flux: float, name: str) -> float: try: return float(flux) except ValueError as ve: raise TypeError(f'{name} could not be cast to a float.') from ve except TypeError as te: raise TypeError(f'{name} could not be cast to a float.') from te @property def beam_flux(self) -> float: """Get the input flux of the incident beam at the top boundary. Notes ----- In DISORT, this variable is named :code:`FBEAM`. """ return self.__beam_flux @property def isotropic_flux(self) -> float: """Get the input flux of isotropic sources at the top boundary. Notes ----- In DISORT, this variable is named :code:`FISOT`. """ return self.__isotropic_flux
[docs]class ThermalEmission: """A data structure for holding thermal emission variables. ThermalEmission creates variables needs to include thermal emission in DISORT. """ def __init__(self, thermal_emission: bool = False, bottom_temperature: float = 0.0, top_temperature: float = 0.0, top_emissivity: float = 1.0) -> None: """ Parameters ---------- thermal_emission Denote whether to use thermal emission. If :code:`True`, DISORT will include thermal emission and will need the following variables: - :code:`bottom_temperature` - :code:`top_temperature` - :code:`top_emissivity` - :code:`low_wavenumber` (aka :code:`WVNMLO`) - :code:`high_wavenumber` (aka :code:`WVNMHI`) - :code:`temperature` (aka :code:`TEMPER`) If :code:`False`, DISORT will save computation time by ignoring all thermal emission and all of the aforementioned variables. bottom_temperature The temperature of the bottom boundary [K]. top_temperature The temperature of the top boundary [K]. top_emissivity The emissivity of the top boundary. Raises ------ TypeError Raised if bottom_temperature, top_temperature, or top_emissivity cannot be cast to a float. ValueError Raised if bottom_temperature or top_temperature is negative, or if top_emissivity is not between 0 and 1. See Also -------- :class:`~observation.Spectral`, :class:`~eos.Hydrostatic` """ self.__thermal_emission = self.__make_thermal_emission(thermal_emission) self.__bottom_temperature = \ self.__make_temperature(bottom_temperature, 'bottom_temperature') self.__top_temperature = \ self.__make_temperature(top_temperature, 'top_temperature') self.__top_emissivity = self.__make_emissivity(top_emissivity) def __make_thermal_emission(self, thermal_emission: bool) -> bool: return self.__cast_variable_to_bool( thermal_emission, 'thermal_emission') def __make_temperature(self, temperature: float, name: str) -> float: temperature = self.__cast_variable_to_float(temperature, name) self.__raise_value_error_if_temperature_is_unphysical(temperature, name) return temperature def __make_emissivity(self, top_emissivity: float) -> float: top_emissivity = self.__cast_variable_to_float( top_emissivity, 'top_emissivity') self.__raise_value_error_if_emissivity_not_in_range(top_emissivity) return top_emissivity @staticmethod def __cast_variable_to_bool(variable: bool, name: str) -> bool: try: return bool(variable) except TypeError as te: raise TypeError(f'{name} cannot be cast into a boolean.') from te @staticmethod def __cast_variable_to_float(variable: float, name: str) -> float: try: return float(variable) except TypeError as te: raise TypeError(f'{name} cannot be cast into a float.') from te except ValueError as ve: raise ValueError(f'{name} cannot be cast into a float.') from ve @staticmethod def __raise_value_error_if_temperature_is_unphysical( temperature: float, name: str) -> None: if temperature < 0 or np.isinf(temperature) or np.isnan(temperature): raise ValueError(f'{name} must be non-negative and finite.') @staticmethod def __raise_value_error_if_emissivity_not_in_range( top_emissivity: float) -> None: if not 0 <= top_emissivity <= 1: raise ValueError('top_emissivity must be between 0 and 1.') @property def thermal_emission(self) -> bool: """Get whether thermal emission will be used in the model. Notes ----- In DISORT, this variable is named :code:`PLANK`. """ return self.__thermal_emission @property def bottom_temperature(self) -> float: """Get the input temperature at the bottom boundary [K]. Notes ----- In DISORT, this variable is named :code:`BTEMP`. It is only used by DISORT if :code:`thermal_emission` is set to :code:`True`. """ return self.__bottom_temperature @property def top_temperature(self) -> float: """Get the input temperature at the top boundary [K]. Notes ----- In DISORT, this variable is named :code:`TTEMP`. It is only used by DISORT if :code:`thermal_emission` is set to :code:`True`. """ return self.__top_temperature @property def top_emissivity(self) -> float: """Get the input emissivity at the top boundary. Notes ----- In DISORT, this variable is named :code:`TEMIS`. It is only used by DISORT if :code:`thermal_emission` is set to :code:`True`. """ return self.__top_emissivity