Source code for mangadap.par.emissionmomentsdb

# Licensed under a 3-clause BSD style license - see LICENSE.rst
# -*- coding: utf-8 -*-
"""
Container class for the database of emission-line bandpass filters used
for non-parameteric measurements of emission-line flux and velocity
moments.

.. todo::
    Combine this with the main emissionlinemoments.py file.

Class usage examples
--------------------

Emission-line moment databases are defined using SDSS parameter
files. To define a database, you can use one of the default set of
available emission-line moment databases::

    from mangadap.par.emissionmomentsdb import EmissionMomentsDB
    print(EmissionMomentsDB.available_databases())
    elmom = EmissionMomentsDB.from_key('ELBMPL9')

The above call uses the
:func:`~mangadap.par.spectralfeaturedb.SpectralFeatureDB.from_key`
method to define the database using its keyword and the database
provided with the MaNGA DAP source distribution. You can also define
the database directly for an SDSS-style parameter file::

    from mangadap.par.emissionmomentsdb import EmissionMomentsDB
    elmom = EmissionMomentsDB('/path/to/emission/moments/database/myelm.par')

The above will read the file and set the database keyword to 'MYELM'
(i.e., the capitalized root name of the ``*.par`` file). See
:ref:`emissionlines` for the format of the parameter file.

----

.. include license and copyright
.. include:: ../include/copy.rst

----

.. include common links, assuming primary doc root is up one directory
.. include:: ../include/links.rst
"""
import os
import warnings

import numpy

from pydl.goddard.astro import airtovac
from pydl.pydlutils.yanny import yanny

from .spectralfeaturedb import SpectralFeatureDB
from ..proc.bandpassfilter import BandPassFilterPar
from ..util.datatable import DataTable

#    Table includes:
#
#    .. include:: ../tables/emissionlinemomentsdatatable.rst


[docs] class EmissionMomentsDefinitionTable(DataTable): """ Wrapper for an :class:`EmissionMomentsDB`, primarily for output to an `astropy.io.fits.BinTableHDU`_. .. include:: ../tables/emissionmomentsdefinitiontable.rst Args: name_len (:obj:`int`): The maximum length of any of the emission feature names. shape (:obj:`int`, :obj:`tuple`, optional): The shape of the initial array. If None, the data array will not be instantiated; use :func:`~mangadap.util.datatable.DataTable.init` to initialize the data array after instantiation. """ def __init__(self, name_len=1, shape=None): # NOTE: This should require python 3.7 to make sure that this # is an "ordered" dictionary. datamodel = dict(ID=dict(typ=int, shape=None, descr='Emission feature ID number'), NAME=dict(typ='<U{0:d}'.format(name_len), shape=None, descr='Name of the emission feature'), RESTWAVE=dict(typ=float, shape=None, descr='Rest wavelength of the feature'), PASSBAND=dict(typ=float, shape=(2,), descr='The lower and upper wavelength that bracket the ' 'emission feature used for the moment calculations'), BLUEBAND=dict(typ=float, shape=(2,), descr='The lower and upper wavelength of a passband toward ' 'the blue of the emission feature, used to define a ' 'linear continuum below the emission feature.'), REDBAND=dict(typ=float, shape=(2,), descr='The lower and upper wavelength of a passband toward ' 'the red of the emission feature, used to define a ' 'linear continuum below the emission feature.')) keys = list(datamodel.keys()) super(EmissionMomentsDefinitionTable, self).__init__(keys, [datamodel[k]['typ'] for k in keys], element_shapes=[datamodel[k]['shape'] for k in keys], descr=[datamodel[k]['descr'] for k in keys], shape=shape)
[docs] class EmissionMomentsDB(SpectralFeatureDB): r""" Basic container class for the database of emission-line moments to calculate. Each row of the database is parsed using :class:`~mangadap.proc.bandpassfilter.BandPassFilterPar`. For the format of the input file, see :ref:`emissionlines-moments`. The primary instantiation requires the SDSS parameter file with the bandpass data. To instantiate using a keyword (and optionally a directory that holds the parameter files), use the :func:`~mangadap.par.spectralfeaturedb.SpectralFeatureDB.from_key` class method. See the base class for additional attributes. Args: parfile (:obj:`str`): The SDSS parameter file with the database. Attributes: key (:obj:`str`): Database signifying keyword file (:obj:`str`): File with the data size (:obj:`int`): Number of features in the database. dummy (`numpy.ndarray`_): Boolean array flagging bandpasses as dummy placeholders. """ default_data_dir = 'emission_bandpass_filters'
[docs] def _parse_yanny(self): """ Parse the yanny file (provided by :attr:`file`) for the emission-line moment database. Returns: :obj:`list`: The list of :class:`mangadap.par.parset.ParSet` instances for each line of the database. """ # Read the yanny file par = yanny(filename=self.file, raw=True) if len(par['DAPELB']['index']) == 0: raise ValueError('Could not find DAPELB entries in {0}!'.format(self.file)) # Check if any of the bands are dummy bands and warn the user self.dummy = numpy.any(numpy.array(par['DAPELB']['blueside']) < 0, axis=1) self.dummy |= numpy.any(numpy.array(par['DAPELB']['redside']) < 0, axis=1) self.dummy |= numpy.any(numpy.array(par['DAPELB']['primary']) < 0, axis=1) if numpy.sum(self.dummy) > 0: warnings.warn('Bands with negative wavelengths are used to insert dummy values.' ' Ignoring input bands with indices: {0}'.format( numpy.array(par['DAPELB']['index'])[self.dummy])) # Setup the array of absorption-line index database parameters self.size = len(par['DAPELB']['index']) parlist = [] for i in range(self.size): invac = par['DAPELB']['waveref'][i] == 'vac' parlist += [ BandPassFilterPar(index=par['DAPELB']['index'][i], name=par['DAPELB']['name'][i], blueside=par['DAPELB']['blueside'][i] if invac \ else airtovac(numpy.array(par['DAPELB']['blueside'][i])), redside=par['DAPELB']['redside'][i] if invac \ else airtovac(numpy.array(par['DAPELB']['redside'][i])), restwave=par['DAPELB']['lambda'][i] if invac \ else airtovac(numpy.array(par['DAPELB']['lambda'][i])), primary=par['DAPELB']['primary'][i] if invac \ else airtovac(numpy.array(par['DAPELB']['primary'][i]))) ] return parlist
[docs] def channel_names(self, dicttype=True): """ Return a dictionary with the channel names as the dictionary key and the channel number as the dictionary value. If ``dicttype`` is False, a list is returned with just the string keys. """ channels = [ '{0}-{1}'.format(self.data['name'][i], int(self.data['restwave'][i])) for i in range(self.size) ] return { n:i for i,n in enumerate(channels) } if dicttype else channels
[docs] def to_datatable(self): """ Compile the database with the specifications of each index. """ name_len = 0 for n in self.data['name']: if name_len < len(n): name_len = len(n) # Instatiate the table data that will be saved defining the set # of emission-line moments measured db = EmissionMomentsDefinitionTable(name_len=name_len, shape=self.size) hk = [ 'ID', 'NAME', 'RESTWAVE', 'PASSBAND', 'BLUEBAND', 'REDBAND' ] mk = [ 'index', 'name', 'restwave', 'primary', 'blueside', 'redside' ] for _hk, _mk in zip(hk,mk): db[_hk] = self.data[_mk] return db