Source code for marvin.tools.core

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# @Author: Brian Cherinka, José Sánchez-Gallego, and Brett Andrews
# @Filename: core.py
# @License: BSD 3-clause (http://www.opensource.org/licenses/BSD-3-Clause)
#
# @Last modified by: José Sánchez-Gallego (gallegoj@uw.edu)
# @Last modified time: 2018-11-09 09:55:04

from __future__ import absolute_import, division, print_function

import abc
import warnings

import astropy.io.fits

import marvin
import marvin.api.api
from marvin.core import marvin_pickle
from marvin.core.exceptions import MarvinBreadCrumb, MarvinError, MarvinUserWarning
from marvin.tools.mixins import MMAMixIn, CacheMixIn
from marvin.utils.general.maskbit import get_manga_target


try:
    from sdss_access.path import Path
except ImportError:
    Path = None

try:
    from sdss_access import Access
except ImportError:
    Access = None


__ALL__ = ['MarvinToolsClass']


def kwargsGet(kwargs, key, replacement):
    """As kwargs.get but uses replacement if the value is None."""

    if key not in kwargs:
        return replacement
    elif key in kwargs and kwargs[key] is None:
        return replacement
    else:
        return kwargs[key]


breadcrumb = MarvinBreadCrumb()


[docs]class MarvinToolsClass(MMAMixIn, CacheMixIn): """Marvin tools main base class. This super class implements the :ref:`decision tree <marvin-dma>` for using local files, database, or remote connection when initialising a Marvin tools object. Parameters: input (str): A string that can be a filename, plate-ifu, or mangaid. It will be automatically identified based on its unique format. This argument is always the first one, so it can be defined without the keyword for convenience. filename (str): The path of the file containing the file to load. If set, ``input`` is ignored. mangaid (str): The mangaid of the file to load. If set, ``input`` is ignored. plateifu (str): The plate-ifu of the data cube to load. If set, ``input`` is ignored. mode ({'local', 'remote', 'auto'}): The load mode to use. See :ref:`mode-decision-tree`. data (:class:`~astropy.io.fits.HDUList`, SQLAlchemy object, or None): An astropy ``HDUList`` or a SQLAlchemy object, to be used for initialisation. If ``None``, the :ref:`normal <marvin-dma>`` mode will be used. release (str): The MPL/DR version of the data to use. drpall (str): The path to the `drpall <https://trac.sdss.org/wiki/MANGA/TRM/TRM_MPL-5/metadata#DRP:DRPall>`_ file to use. If not set it will use the default path for the file based on the ``release``. download (bool): If ``True``, the data will be downloaded on instantiation. See :ref:`marvin-download-objects`. Attributes: data (:class:`~astropy.io.fits.HDUList`, SQLAlchemy object, or dict): Depending on the access mode, ``data`` is populated with the |HDUList| from the FITS file, a `SQLAlchemy <http://www.sqlalchemy.org>`_ object, or a dictionary of values returned by an API call. datamodel: A datamodel object, whose type depends on the subclass that initialises the datamodel. data_origin ({'file', 'db', 'api'}): Indicates the origin of the data, either from a file, the DB, or an API call. filename (str): The path of the file used, if any. mangaid (str): The mangaid of the target. plateifu: The plateifu of the target """ def __init__(self, input=None, filename=None, mangaid=None, plateifu=None, mode=None, data=None, release=None, drpall=None, download=None): MMAMixIn.__init__(self, input=input, filename=filename, mangaid=mangaid, plateifu=plateifu, mode=mode, data=data, release=release, download=download) CacheMixIn.__init__(self) # drop breadcrumb breadcrumb.drop(message='Initializing MarvinTool {0}'.format(self.__class__), category=self.__class__) # Load VACs from marvin.contrib.vacs.base import VACMixIn self.vacs = VACMixIn.get_vacs(self) def _toolInteraction(self, url, params=None): """Runs an Interaction and passes self._release.""" params = params or {'release': self._release} return marvin.api.api.Interaction(url, params=params) @staticmethod def _check_file(header, data, objtype): ''' Check the file input to ensure correct tool ''' # get/check various header keywords bininhdr = ('binkey' in header) or ('bintype' in header) dapinhdr = 'dapfrmt' in header dapfrmt = header['DAPFRMT'] if dapinhdr else None # check the file if objtype == 'Maps' or objtype == 'ModelCube': # get indices in daptype daptype = ['MAPS', 'LOGCUBE'] dapindex = daptype.index("MAPS") if objtype == 'Maps' else daptype.index("LOGCUBE") altdap = 1 - dapindex # check for emline_gflux extension gfluxindata = 'EMLINE_GFLUX' in data wronggflux = (gfluxindata and objtype == 'ModelCube') or \ (not gfluxindata and objtype == 'Maps') if not bininhdr: raise MarvinError('Trying to open a non DAP file with Marvin {0}'.format(objtype)) else: if (dapfrmt and dapfrmt != daptype[dapindex]) or (wronggflux): raise MarvinError('Trying to open a DAP {0} with Marvin {1}' .format(daptype[altdap], objtype)) elif objtype == 'Cube': if bininhdr or dapinhdr: raise MarvinError('Trying to open a DAP file with Marvin Cube') def __getstate__(self): if self.data_origin == 'db': raise MarvinError('objects with data_origin=\'db\' cannot be saved.') odict = self.__dict__.copy() del odict['data'] return odict def __setstate__(self, idict): data = None if idict['data_origin'] == 'file': try: data = astropy.io.fits.open(idict['filename']) except Exception as ee: warnings.warn('there was a problem reloading the FITS object: {0}. ' 'The object has been unpickled but not all the functionality ' 'will be available.'.format(str(ee)), MarvinUserWarning) self.__dict__.update(idict) self.data = data
[docs] def save(self, path=None, overwrite=False): """Pickles the object. If ``path=None``, uses the default location of the file in the tree but changes the extension of the file to ``.mpf``. Returns the path of the saved pickle file. Parameters: obj: Marvin object to pickle. path (str): Path of saved file. Default is ``None``. overwrite (bool): If ``True``, overwrite existing file. Default is ``False``. Returns: str: Path of saved file. """ return marvin_pickle.save(self, path=path, overwrite=overwrite)
[docs] @classmethod def restore(cls, path, delete=False): """Restores a MarvinToolsClass object from a pickled file. If ``delete=True``, the pickled file will be removed after it has been unplickled. Note that, for objects with ``data_origin='file'``, the original file must exists and be in the same path as when the object was first created. """ from marvin.contrib.vacs.base import VACMixIn res = marvin_pickle.restore(path, delete=delete) res.vacs = VACMixIn.get_vacs(res) return res
def __del__(self): """Destructor for closing FITS files.""" if self.data_origin == 'file' and isinstance(self.data, astropy.io.fits.HDUList): try: self.data.close() except Exception as ee: warnings.warn('failed to close FITS instance: {0}'.format(ee), MarvinUserWarning) @property def quality_flag(self): """Return quality flag.""" if self.datamodel.qual_flag is None: return None try: dapqual = self._bitmasks['MANGA_' + self.datamodel.qual_flag] except KeyError: warnings.warn('cannot find bitmask MANGA_{!r}'.format(self.datamodel.qual_flag)) dapqual = None else: dapqual.mask = int(self.header[self.datamodel.qual_flag]) return dapqual @property def manga_target1(self): """Return MANGA_TARGET1 flag.""" return get_manga_target('1', self._bitmasks, self.header) @property def manga_target2(self): """Return MANGA_TARGET2 flag.""" return get_manga_target('2', self._bitmasks, self.header) @property def manga_target3(self): """Return MANGA_TARGET3 flag.""" return get_manga_target('3', self._bitmasks, self.header) @property def target_flags(self): """Bundle MaNGA targeting flags.""" return [self.manga_target1, self.manga_target2, self.manga_target3]
[docs] def getImage(self): ''' Retrieves the Image :class:`~marvin.tools.image.Image` for this object ''' image = marvin.tools.image.Image(plateifu=self.plateifu, release=self.release) return image