Source code for mangadap.spectra.manga
"""
Implement the derived class for MaNGA row-stacked spectra.
----
.. 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
from IPython import embed
import numpy
from astropy.io import fits
from ..config.manga import MaNGAConfig
from ..util.drpbitmask import DRPFitsBitMask
from ..util.constants import DAPConstants
from ..util.filter import interpolate_masked_vector
from .rowstackedspectra import RowStackedSpectra
[docs]
class MaNGARSS(MaNGAConfig, RowStackedSpectra):
r"""
Container class for MaNGA row-stacked spectra.
For additional description and attributes, see the two base
classes.
See :func:`from_plateifu` to instantiate using the plate and ifu
numbers. See :func:`from_config` to instantiate from a
configuration file.
Args:
ifile (:obj:`str`):
The file with the MaNGA row-stacked spectra. The name is
expected to follow the nominal naming convention, which
is used to parse the plate, ifudesign, and whether or not
the spectra are binned logarithmically in wavelength.
sres_ext (:obj:`str`, optional):
The extension to use when constructing the spectral
resolution vectors. See :func:`spectral_resolution`.
sres_fill (:obj:`bool`, optional):
Fill masked values by interpolation. Default is to leave
masked pixels in returned array.
"""
def __init__(self, ifile, sres_ext=None, sres_fill=True):
# Use the configuration class method to construct the configuration
# based on the input data file
cfg = MaNGAConfig.from_file(ifile)
# and then use the parent class init to set it to self. NOTE: This is
# round about, but the way I got it to work. There must be a better way
# to do this...
MaNGAConfig.__init__(self, cfg.plate, cfg.ifudesign, mode=cfg.mode, log=cfg.log,
drpver=cfg.drpver, redux_path=cfg.redux_path,
chart_path=cfg.chart_path, directory_path=cfg.directory_path)
# Define the relevant BitMask object
bitmask = DRPFitsBitMask(mode='RSS')
# Open the file and initialize the base class
with fits.open(ifile) as hdu:
print('Reading MaNGA row-stacked spectra data ...', end='\r')
prihdr = hdu[0].header
fluxhdr = hdu['FLUX'].header
self.sres_ext, sres = MaNGAConfig.spectral_resolution(hdu, ext=sres_ext, fill=sres_fill)
self.sres_fill = sres_fill
sres = sres.filled(0.0)
# The negative for XPOS is because the data in that
# extension has negative offsets going toward increasing
# RA. Opposite of the RowStackedSpectra convention. The
# area of the fiber aperture is normalized to pi arcsec^2
# by the DRP.
RowStackedSpectra.__init__(self, hdu['WAVE'].data, hdu['FLUX'].data,
ivar=hdu['IVAR'].data, mask=hdu['MASK'].data,
bitmask=bitmask, sres=sres, xpos=-hdu['XPOS'].data,
ypos=hdu['YPOS'].data, area=numpy.pi,
log=self.log, prihdr=prihdr, fluxhdr=fluxhdr)
print('Reading MaNGA row-stacked spectra data ... DONE')
[docs]
@classmethod
def from_plateifu(cls, plate, ifudesign, log=True, drpver=None, redux_path=None,
chart_path=None, directory_path=None, **kwargs):
"""
Construct a :class:`mangadap.spectra.manga.MaNGARSS`
object based on its plate-ifu designation.
This is a simple wrapper function that calls
:func:`default_paths` to construct the file names, and then
calls the class instantiation method.
Args:
plate (:obj:`int`):
Plate number
ifudesign (:obj:`int`):
IFU design
log (:obj:`bool`, optional):
Use the spectra that are logarithmically binned in
wavelength.
drpver (:obj:`str`, optional):
DRP version, which is used to define the default DRP
redux path. Default is defined by
:func:`mangadap.config.manga.drp_version`
redux_path (:obj:`str`, optional):
The path to the top level directory containing the
DRP output files for a given DRP version. Default is
defined by
:func:`mangadap.config.manga.drp_redux_path`.
chart_path (:obj:`str`, optional):
The path to the directory containing the finding chart images
for this plate. Default is defined by
:func:`mangadap.config.manga.drp_finding_chart_path`.
directory_path (:obj:`str`, optional):
The exact path to the DRP file. Default is defined by
:func:`mangadap.config.manga.drp_directory_path`.
Providing this ignores anything provided for
``drpver`` or ``redux_path``.
**kwargs:
Keyword arguments passed directly to the primary
instantiation method; see
:class:`mangadap.spectra.manga.MaNGARSS`.
"""
# Use the input to instantiate the configuration
cfg = MaNGAConfig(plate, ifudesign, mode='RSS', log=log, drpver=drpver,
redux_path=redux_path, chart_path=chart_path,
directory_path=directory_path)
# Then use the configuration to instantiate the object
return cls(str(cfg.file_path), **kwargs)
[docs]
@classmethod
def from_config(cls, cfgfile):
"""
Construct a :class:`mangadap.datacube.manga.MaNGADataCube` or
:class:`mangadap.spectra.manga.MaNGARSS` object from a
configuration file.
Using the data read from the configuration file, the method
instantiates the class using
:func:`mangadap.spectra.manga.MaNGARSS.from_plateifu` or
:func:`mangadap.datacube.manga.MaNGADataCube.from_plateifu`.
This method will therefore fault for this base class!
The format of the configuration file is:
.. todo::
Fill this in.
Args:
cfgfile (:obj:`str`):
Configuration file
drpver (:obj:`str`, optional):
DRP version, which is used to define the default DRP
redux path. Overrides any value in the configuration
file.
redux_path (:obj:`str`, optional):
The path to the top level directory containing the
DRP output files for a given DRP version. Overrides
any value in the configuration file.
directory_path (:obj:`str`, optional):
The exact path to the DRP file. Providing this
ignores anything provided for ``drpver`` or
``redux_path``. Overrides any value in the
configuration file.
"""
# First use the base class method to instantiate the configuration
cfg = MaNGAConfig.from_config(cfgfile, mode='RSS')
# Then read the file (again...) to get the relevant keyword arguments
full_cfg = DefaultConfig(cfgfile, interpolate=True)
kwargs = {}
kwargs['z'] = full_cfg.getfloat('z')
kwargs['vdisp'] = full_cfg.getfloat('vdisp')
kwargs['ell'] = full_cfg.getfloat('ell')
kwargs['pa'] = full_cfg.getfloat('pa')
kwargs['reff'] = full_cfg.getfloat('reff')
kwargs['sres_ext'] = full_cfg.get('sres_ext')
kwargs['sres_fill'] = full_cfg.getbool('sres_fill', default=True)
kwargs['covar_ext'] = full_cfg.get('covar_ext')
return cls(str(cfg.file_path), **kwargs)
[docs]
def pointing_offset(self):
"""
Return the offsets in RA and DEC between the pointing
coordinates (``IFURA``, ``IFUDEC``) and the designated object
center coordinates (``OBJRA``, ``OBJDEC``), drawn from the
primary header of the DRP fits file.
Returns:
:obj:`float`: Sky-right arcsecond offsets in RA and DEC.
"""
return ((self.prihdr['IFURA'] - self.prihdr['OBJRA']) \
* numpy.cos(numpy.radians(self.prihdr['OBJDEC'])) * 3600.), \
((self.prihdr['IFUDEC'] - self.prihdr['OBJDEC']) * 3600.)
[docs]
def mean_sky_coordinates(self, offset=True, **kwargs):
r"""
Compute the weighted or unweighted mean sky coordinates for
each spectrum.
This is a simple wrapper for
:func:`mangadap.spectra.rowstackedspectra.RowStackedSpectra.mean_sky_coordinates`;
see that method for additional keyword arguments.
.. warning::
Flux-weighting the coordinates can produce spurious
results in low-flux regimes.
Args:
offset (:obj:`bool`, optional):
Offset the coordinates in :attr:`xpos` and
:attr:`ypos` by the difference between the IFU and
OBJ coordinates in the header (see
:func:`pointing_offset`). The coordinates are
nominally defined with respect to the IFU pointing
center, such that this offsets the coordinates to be
centered on the galaxy.
**kwargs:
Passed directly to
:func:`mangadap.spectra.rowstackedspectra.RowStackedSpectra.mean_sky_coordinates`;
Returns:
:obj:`tuple`: Two `numpy.ndarray`_ objects with the
fiducial x and y on-sky positions of each spectrum in
arcseconds relative to a given center. The shape of the
two arrays is :math:`(N_{\rm spec},)`.
"""
# TODO: offset cannot be a keyword in kwargs.
_offset = self.pointing_offset() if offset else None
return super(MaNGARSS, self).mean_sky_coordinates(offset=_offset, **kwargs)
[docs]
def binned_on_sky_area(self, bin_indx, offset=True, **kwargs):
r"""
Compute the on-sky area of a set of binned spectra.
This is a simple wrapper for
:func:`mangadap.spectra.rowstackedspectra.RowStackedSpectra.binned_on_sky_area`
and :func:`mean_sky_coordinates`.
The arguments are passed directly to
:func:`mean_sky_coordinates`, and the results are then passed
to the base class function. The area of the fibers
"beams" are all renormalized to :math:`\pi {\rm arcsec}^2` by
the DRP.
Args:
bin_indx (array-like):
An array with size :math:`N_{\rm spec}` that gives
which spaxels were included in each bin. Valid bins
have indices of :math:`\geq 0`.
offset (:obj:`bool`, optional):
Offset the coordinates in :attr:`xpos` and
:attr:`ypos` by the difference between the IFU and
OBJ coordinates in the header (see
:func:`pointing_offset`). The coordinates are
nominally defined with respect to the IFU pointing
center, such that this offsets the coordinates to be
centered on the galaxy.
**kwargs:
Passed directly to :func:`mean_sky_coordinates`.
Returns:
:obj:`tuple`: Two `numpy.ndarray`_ objects are returned.
The first has the unique (non-negative) bin indices, and
the second provides the on-sky area of that bin.
"""
# TODO: May need a better way to do this. I.e., coordinates
# will be need to figure out which spectra to bin, and this
# recalculation of the fiducial coordinates runs the risk that
# the binned area is not calculated using the same coordinates
# used to determine which spectra went in each bin.
x, y = self.mean_sky_coordinates(offset=offset, **kwargs)
return super(MaNGARSS, self).binned_on_sky_area(bin_indx, x=x, y=y)
[docs]
@staticmethod
def _parse_rectification_parameters(pixelscale, rlim, sigma, recenter, width_buffer):
"""
Parse the datacube rectification parameters.
Values that are None are set to their defaults as set by
:class:`~mangadap.config.manga.MaNGAConfig`.
Returns:
:obj:`tuple`: The default or input values of arguments,
in the same order.
"""
pixelscale = MaNGAConfig.cube_pixelscale() if pixelscale is None else pixelscale
rlim = MaNGAConfig.regrid_rlim() if rlim is None else rlim
sigma = MaNGAConfig.regrid_sigma() if sigma is None else sigma
recenter = MaNGAConfig.cube_recenter() if recenter is None else recenter
width_buffer = MaNGAConfig.cube_width_buffer() if width_buffer is None else width_buffer
return pixelscale, rlim, sigma, recenter, width_buffer
[docs]
def rectification_transfer_matrix(self, channel, pixelscale=None, rlim=None, sigma=None,
recenter=None, width_buffer=None, quiet=False,
rej_flag='3DREJECT'):
"""
Construct the rectification transfer matrix.
A simple wrapper for
:func:`mangadap.spectra.rowstackedspectra.RowStackedSpectra.rectification_transfer_matrix`
that defaults to the nominal rectification parameters for
MaNGA datacubes. See this base class function for the
argument descriptions.
"""
_pixelscale, _rlim, _sigma, _recenter, _width_buffer \
= MaNGARSS._parse_rectification_parameters(pixelscale, rlim, sigma, recenter,
width_buffer)
return super(MaNGARSS,
self).rectification_transfer_matrix(channel, _pixelscale, _rlim, _sigma,
recenter=_recenter, width_buffer=_width_buffer,
quiet=quiet, rej_flag='3DREJECT')
[docs]
def rectify_wavelength_plane(self, channel, pixelscale=None, rlim=None, sigma=None,
recenter=None, width_buffer=None, quiet=False,
rej_flag='3DREJECT', return_ivar=False, return_covar=False):
"""
Rectify a wavelength channel into a monochromatic flux map.
A simple wrapper for
:func:`mangadap.spectra.rowstackedspectra.RowStackedSpectra.rectify_wavelength_plane`
that defaults to the nominal rectification parameters for
MaNGA datacubes. See this base class function for the
argument descriptions.
"""
_pixelscale, _rlim, _sigma, _recenter, _width_buffer \
= MaNGARSS._parse_rectification_parameters(pixelscale, rlim, sigma, recenter,
width_buffer)
return super(MaNGARSS, self).rectify_wavelength_plane(channel, pixelscale=_pixelscale,
rlim=_rlim, sigma=_sigma,
recenter=_recenter,
width_buffer=_width_buffer,
quiet=quiet, rej_flag='3DREJECT',
return_ivar=return_ivar,
return_covar=return_covar)
[docs]
def covariance_matrix(self, channel, pixelscale=None, rlim=None, sigma=None, recenter=None,
width_buffer=None, csr=False, quiet=False, rej_flag='3DREJECT'):
"""
Construct the covariance matrix for a given wavelength channel.
A simple wrapper for
:func:`mangadap.spectra.rowstackedspectra.RowStackedSpectra.covariance_matrix`
that defaults to the nominal rectification parameters for
MaNGA datacubes. See this base class function for the
argument descriptions.
"""
_pixelscale, _rlim, _sigma, _recenter, _width_buffer \
= MaNGARSS._parse_rectification_parameters(pixelscale, rlim, sigma, recenter,
width_buffer)
return super(MaNGARSS, self).covariance_matrix(channel, pixelscale=_pixelscale, rlim=_rlim,
sigma=_sigma, recenter=_recenter,
width_buffer=_width_buffer, csr=csr,
quiet=quiet, rej_flag=rej_flag)
[docs]
def covariance_cube(self, channels=None, pixelscale=None, rlim=None, sigma=None, recenter=None,
width_buffer=None, csr=False, quiet=False, rej_flag='3DREJECT'):
"""
Construct the covariance matrices for many wavelength channels.
A simple wrapper for
:func:`mangadap.spectra.rowstackedspectra.RowStackedSpectra.covariance_cube`
that defaults to the nominal rectification parameters for
MaNGA datacubes. See this base class function for the
argument descriptions.
"""
_pixelscale, _rlim, _sigma, _recenter, _width_buffer \
= MaNGARSS._parse_rectification_parameters(pixelscale, rlim, sigma, recenter,
width_buffer)
return super(MaNGARSS, self).covariance_cube(channels=channels, pixelscale=_pixelscale,
rlim=_rlim, sigma=_sigma, recenter=_recenter,
width_buffer=_width_buffer, csr=csr,
quiet=quiet, rej_flag=rej_flag)
[docs]
def instrumental_dispersion_plane(self, channel, dispersion_factor=None, pixelscale=None,
rlim=None, sigma=None, recenter=None, width_buffer=None,
quiet=False, rej_flag='3DREJECT'):
"""
Construct the instrumental dispersion for a given wavelength channel.
A simple wrapper for
:func:`mangadap.spectra.rowstackedspectra.RowStackedSpectra.instrumental_dispersion_plane`
that defaults to the nominal rectification parameters for
MaNGA datacubes. See this base class function for the
argument descriptions.
"""
_pixelscale, _rlim, _sigma, _recenter, _width_buffer \
= MaNGARSS._parse_rectification_parameters(pixelscale, rlim, sigma, recenter,
width_buffer)
return super(MaNGARSS,
self).instrumental_dispersion_plane(channel,
dispersion_factor=dispersion_factor,
pixelscale=_pixelscale, rlim=_rlim,
sigma=_sigma, recenter=_recenter,
width_buffer=_width_buffer, quiet=quiet,
rej_flag=rej_flag)