Source code for taurex.data.profiles.chemistry.chemistry

from taurex.log import Logger
from taurex.util import get_molecular_weight
from taurex.data.fittable import Fittable, derivedparam
import numpy as np
from taurex.output.writeable import Writeable
from taurex.cache import OpacityCache, GlobalCache
from taurex.cache.ktablecache import KTableCache
from taurex.data.citation import Citable



[docs]class Chemistry(Fittable, Logger, Writeable, Citable): """ *Abstract Class* Skeleton for defining chemistry. Must implement methods: - :func:`activeGases` - :func:`inactiveGases` - :func:`activeGasMixProfile` - :func:`inactiveGasMixProfile` *Active* are those that are actively absorbing in the atmosphere. In technical terms they are molecules that have absorption cross-sections. You can see which molecules are able to actively absorb by doing: You can find out what molecules can actively absorb by doing: >>> avail_active_mols = OpacityCache().find_list_of_molecules() Parameters ---------- name : str Name used in logging """ def __init__(self, name): Logger.__init__(self, name) Fittable.__init__(self) self.mu_profile = None if GlobalCache()['opacity_method'] == 'ktables': self._avail_active = KTableCache().find_list_of_molecules() else: self._avail_active = OpacityCache().find_list_of_molecules() #self._avail_active = OpacityCache().find_list_of_molecules() deactive_list = GlobalCache()['deactive_molecules'] if deactive_list is not None: self._avail_active = [k for k in self._avail_active if k not in deactive_list]
[docs] def set_star_planet(self, star, planet): """ Supplies the star and planet to chemistry for photochemistry reasons. Does nothing by default Parameters ---------- star: :class:`~taurex.data.stellar.star.Star` A star object planet: :class:`~taurex.data.planet.Planet` A planet object """ pass
@property def availableActive(self): """ Returns a list of available actively absorbing molecules Returns ------- molecules: :obj:`list` Actively absorbing molecules """ return self._avail_active @property def activeGases(self): """ **Requires implementation** Should return a list of molecule names Returns ------- active : :obj:`list` List of active gases """ raise NotImplementedError @property def inactiveGases(self): """ **Requires implementation** Should return a list of molecule names Returns ------- inactive : :obj:`list` List of inactive gases """ raise NotImplementedError
[docs] def initialize_chemistry(self, nlayers=100, temperature_profile=None, pressure_profile=None, altitude_profile=None): """ **Requires implementation** Derived classes should implement this to compute the active and inactive gas profiles Parameters ---------- nlayers: int Number of layers in atmosphere temperature_profile: :obj:`array` Temperature profile in K, must have length ``nlayers`` pressure_profile: :obj:`array` Pressure profile in Pa, must have length ``nlayers`` altitude_profile: :obj:`array` Altitude profile in m, must have length ``nlayers`` """ self.compute_mu_profile(nlayers)
@property def activeGasMixProfile(self): """ **Requires implementation** Should return profiles of shape ``(nactivegases,nlayers)``. Active refers to gases that are actively absorbing in the atmosphere. Another way to put it these are gases where molecular cross-sections are used. """ raise NotImplementedError @property def inactiveGasMixProfile(self): """ **Requires implementation** Should return profiles of shape ``(ninactivegases,nlayers)``. """ raise NotImplementedError @property def muProfile(self): """ Molecular weight for each layer of atmosphere Returns ------- mix_profile : :obj:`array` """ return self.mu_profile
[docs] def get_gas_mix_profile(self, gas_name): """ Returns the mix profile of a particular gas Parameters ---------- gas_name : str Name of gas Returns ------- mixprofile : :obj:`array` Mix profile of gas with shape ``(nlayer)`` """ if gas_name in self.activeGases: idx = self.activeGases.index(gas_name) return self.activeGasMixProfile[idx] elif gas_name in self.inactiveGases: idx = self.inactiveGases.index(gas_name) return self.inactiveGasMixProfile[idx] else: raise KeyError
[docs] def compute_mu_profile(self, nlayers): """ Computes molecular weight of atmosphere for each layer Parameters ---------- nlayers: int Number of layers """ self.mu_profile = np.zeros(shape=(nlayers,)) if self.activeGasMixProfile is not None: for idx, gasname in enumerate(self.activeGases): self.mu_profile += self.activeGasMixProfile[idx] * \ self.get_molecular_mass(gasname) if self.inactiveGasMixProfile is not None: for idx, gasname in enumerate(self.inactiveGases): self.mu_profile += self.inactiveGasMixProfile[idx] * \ self.get_molecular_mass(gasname)
@property def gases(self): return self.activeGases + self.inactiveGases @property def mixProfile(self): return np.concatenate((self.activeGasMixProfile, self.inactiveGasMixProfile)) @derivedparam(param_name='mu', param_latex='$\mu$', compute=True) def mu(self): """ Mean molecular weight at surface (amu) """ from taurex.constants import AMU return self.muProfile[0]/AMU
[docs] def write(self, output): """ Writes chemistry class and arguments to file Parameters ---------- output: :class:`~taurex.output.output.Output` """ gas_entry = output.create_group('Chemistry') gas_entry.write_string('chemistry_type', self.__class__.__name__) gas_entry.write_string_array('active_gases', self.activeGases) gas_entry.write_string_array('inactive_gases', self.inactiveGases) if self.hasCondensates: gas_entry.write_string_array('condensates', self.condensates) return gas_entry
@property def condensates(self): """ Returns a list of condensates in the atmosphere. Returns ------- active : :obj:`list` List of condensates """ return [] @property def hasCondensates(self): return len(self.condensates) > 0 @property def condensateMixProfile(self): """ **Requires implementation** Should return profiles of shape ``(ncondensates,nlayers)``. """ if len(self.condensates) == 0: return None else: raise NotImplementedError
[docs] def get_condensate_mix_profile(self, condensate_name): """ Returns the mix profile of a particular condensate Parameters ---------- condensate_name : str Name of condensate Returns ------- mixprofile : :obj:`array` Mix profile of condensate with shape ``(nlayer)`` """ if condensate_name in self.condensates: index = self.condensates.index(condensate_name) return self.condensateMixProfile[index] else: raise KeyError(f'Condensate {condensate_name} not found in chemistry')
[docs] def get_molecular_mass(self, molecule): from taurex.util import get_molecular_weight return get_molecular_weight(molecule)