"""
Provides a simple datamodel for a single spectrum.
.. include common links, assuming primary doc root is up one directory
.. include:: ../include/links.rst
"""
import inspect
from IPython import embed
import numpy as np
from pypeit import utils
from pypeit import datamodel
from pypeit import io
from pypeit.spectrographs.util import load_spectrograph
[docs]
class OneSpec(datamodel.DataContainer):
"""
DataContainer to hold single spectra, e.g., from
:class:`~pypeit.coadd1d.CoAdd1D`.
The datamodel attributes are:
.. include:: ../include/class_datamodel_onespec.rst
Args:
wave
wave_grid_mid
flux
PYP_SPEC
ivar
mask
telluric
obj_model
ext_mode
fluxed
Attributes:
head0 (`astropy.io.fits.Header`):
Primary header
spect_meta (:obj:`dict`):
Parsed meta from the header
spectrograph (:class:`pypeit.spectrographs.spectrograph.Spectrograph`):
Build from PYP_SPEC
"""
version = '1.0.2'
datamodel = {'wave': dict(otype=np.ndarray, atype=np.floating,
# TODO: The "weighted by pixel contributions" part
# should be better explained.
descr='Wavelength array (angstroms in vacuum), weighted by pixel '
'contributions'),
'wave_grid_mid': dict(otype=np.ndarray, atype=np.floating,
descr='Wavelength (angstroms in vacuum) evaluated at the '
'bin centers of a grid that is uniformly spaced '
'in either lambda or log10-lambda/velocity'),
'flux': dict(otype=np.ndarray, atype=np.floating,
descr='Flux array in units of counts/s or 10^-17 erg/s/cm^2/Ang; '
'see ``fluxed``'),
'ivar': dict(otype=np.ndarray, atype=np.floating,
descr='Inverse variance array (matches units of flux)'),
'sigma': dict(otype=np.ndarray, atype=np.floating,
descr='One sigma noise array, equivalent to 1/sqrt(ivar) (matches units of flux)'),
'mask': dict(otype=np.ndarray, atype=np.integer,
descr='Mask array (1=Good,0=Bad)'),
'telluric': dict(otype=np.ndarray, atype=np.floating, descr='Telluric model'),
'PYP_SPEC': dict(otype=str, descr='``PypeIt`` spectrograph designation'),
'obj_model': dict(otype=np.ndarray, atype=np.floating,
descr='Object model for tellurics'),
'ext_mode': dict(otype=str, descr='Extraction mode (options: BOX, OPT)'),
'fluxed': dict(otype=bool, descr='Boolean indicating if the spectrum is fluxed.'),
# TODO: Needs a better description. What's in the dictionary?
# Why isn't this dictionary expanded into its elements? I.e.,
# shouldn't each element of this dictionary be a component of
# the datamodel?
'spect_meta': dict(otype=dict, descr='header dict')}
internals = ['head0',
'filename',
'spectrograph',
'spect_meta',
'history']
[docs]
@classmethod
def from_file(cls, ifile, verbose=True, chk_version=True, **kwargs):
"""
Instantiate the object from an extension in the specified fits file.
Over-load :func:`~pypeit.datamodel.DataContainer.from_file`
to deal with the header
Args:
ifile (:obj:`str`, `Path`_):
Fits file with the data to read
verbose (:obj:`bool`, optional):
Print informational messages (not currently used)
chk_version (:obj:`bool`, optional):
Passed to :func:`from_hdu`.
kwargs (:obj:`dict`, optional):
Arguments passed directly to :func:`from_hdu`.
"""
with io.fits_open(ifile) as hdu:
self = cls.from_hdu(hdu, chk_version=chk_version, **kwargs)
self.filename = ifile
self.head0 = hdu[0].header
self.spectrograph = load_spectrograph(self.PYP_SPEC)
self.spect_meta = self.spectrograph.parse_spec_header(self.head0)
return self
def __init__(self, wave, wave_grid_mid, flux, PYP_SPEC=None, ivar=None, sigma=None, mask=None, telluric=None,
obj_model=None, ext_mode=None, fluxed=None):
args, _, _, values = inspect.getargvalues(inspect.currentframe())
_d = dict([(k,values[k]) for k in args[1:]])
# Setup the DataContainer
datamodel.DataContainer.__init__(self, d=_d)
[docs]
def _bundle(self):
"""
Override the base class method simply to set the HDU extension name.
"""
return super()._bundle(ext='SPECTRUM')
[docs]
def to_file(self, ofile, primary_hdr=None, history=None, **kwargs):
"""
Over-load :func:`pypeit.datamodel.DataContainer.to_file`
to deal with the header
Args:
ofile (:obj:`str`): Filename
primary_hdr (`astropy.io.fits.Header`_, optional):
**kwargs: Passed to super.to_file()
"""
if primary_hdr is None:
primary_hdr = io.initialize_header()
# Build the header
if self.head0 is not None and self.PYP_SPEC is not None:
spectrograph = load_spectrograph(self.PYP_SPEC)
subheader = spectrograph.subheader_for_spec(self.head0, self.head0,
extra_header_cards = ['RA_OBJ', 'DEC_OBJ'])
else:
subheader = {}
# Add em in
for key in subheader:
primary_hdr[key] = subheader[key]
# Add history
if history is not None:
history.write_to_header(primary_hdr)
# Do it
super().to_file(ofile, primary_hdr=primary_hdr, **kwargs)