Source code for marvin.tools.plate

#!/usr/bin/env python
# encoding: utf-8

# Created by Brian Cherinka on 2016-05-17 10:17:35
# Licensed under a 3-clause BSD license.

# Revision History:
#     Initial Version: 2016-05-17 10:17:35 by Brian Cherinka
#     Last Modified On: 2016-05-17 10:17:35 by Brian

from __future__ import division, print_function

import inspect

from astropy.io import fits

from marvin import config
from marvin.core.exceptions import MarvinError
from marvin.tools.cube import Cube
from marvin.utils.general.structs import FuzzyList

from .core import MarvinToolsClass


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


[docs]class Plate(MarvinToolsClass, FuzzyList): '''A class to interface with MaNGA Plate. This class represents a Plate, initialised either from a file, a database, or remotely via the Marvin API. The class inherits from Python's list class, and is defined as a list of Cube objects. As it inherits from list, it can do all the standard Python list operations. When instanstantiated, Marvin Plate will attempt to discover and load all the Cubes associated with this plate. Parameters: plate (str): The plate id of the Plate to load. plateifu (str): The plate-ifu of the Plate to load filename (str): The path of the file containing the data cube to load. mode ({'local', 'remote', 'auto'}): The load mode to use. See :doc:`Mode secision tree</mode_decision>`.. release (str): The MPL/DR version of the data to use. nocubes (bool): Set this to turn off the Cube loading Attributes: cubeXXXX (object): The Marvin Cube object for the given ifu, e.g. cube1901 refers to the Cube for plateifu 8485-1901 plate/plateid (int): The plate id for this plate cartid (str): The cart id for this plate designid (int): The design id for this plate ra (float): The RA of the plate center dec (float): The declination of the plate center dateobs (str): The date of observation for this plate surveymode (str): The survey mode for this plate isbright (bool): True if this is a bright time plate Return: plate: An object representing the Plate entity. The object is a list of Cube objects, one for each IFU cube in the Plate entity. Example: >>> from marvin.tools.plate import Plate >>> plate = Plate(plate=8485) >>> print(plate) >>> <Marvin Plate (plate=8485, n_cubes=17, mode='local', data_origin='db')> >>> >>> print('Cubes found in this plate: {0}'.format(len(plate))) >>> Cubes found in this plate: 4 >>> >>> # access the plate via index to access the individual cubes >>> plate[0] >>> <Marvin Cube (plateifu='8485-12701', mode='local', data_origin='db')> >>> >>> # or by name >>> plate['12702'] >>> <Marvin Cube (plateifu='8485-12702', mode='local', data_origin='db')> >>> ''' def __init__(self, input=None, filename=None, mangaid=None, plateifu=None, mode=None, data=None, release=None, plate=None, download=None, nocubes=None): self._cubes = None self._plate = None self._pdict = None self.platedir = None self.nocubes = nocubes # If plateid specified, force a temp plateifu if plate: self.plateid = plate plateifu = '{0}-XXXX'.format(self.plateid) self.plateifu = plateifu args = [plate, plateifu] assert any(args), 'Enter plate or plateifu!' MarvinToolsClass.__init__(self, input=input, filename=filename, mangaid=mangaid, plateifu=plateifu, mode=mode, data=data, release=release, download=download) # sort out any plateid, plate-ifu, mangaid name snafus self._sortOutNames() # grab the plate info if self.data_origin == 'file': self._getPlateFromFile() elif self.data_origin == 'db': self._getPlateFromDB() elif self.data_origin == 'api': self._getPlateFromAPI() # load the plate params and init the Marvin Cubes self._setParams() if not self.nocubes: self._initCubes() def __repr__(self): '''Representation for Plate.''' return ('<Marvin Plate (plate={self.plateid!r}, n_cubes={0}, mode={self.mode!r}, ' 'data_origin={self.data_origin!r})>'.format(len(self), self=self)) def __dir__(self): ''' Overriding dir for Plate ''' # get the attributes from the class itself class_members = list(list(zip(*inspect.getmembers(self.__class__)))[0]) instance_attr = list(self.__dict__.keys()) # get the dir from FuzzyList listattr = ['cube{0}'.format(i.plateifu.split('-')[1]) for i in self] listattr.sort() return listattr + sorted(class_members + instance_attr) def __getattr__(self, value): if 'cube' in value: ifu = value.split('cube')[-1] plateifu = '{0}-{1}'.format(self.plate, ifu) return self[plateifu] return super(Plate, self).__getattribute__(value) def _getFullPath(self, **kwargs): """Returns the full path of the file in the tree.""" self.filename = super(Plate, self)._getFullPath('mangaplate', drpver=self._drpver, plate=self.plateid, **kwargs) self.platedir = self.filename self._checkFilename() return self.filename def _getPlateFromFile(self): ''' Initialize a Plate from a Cube/RSS File''' # Load file try: self._hdr = fits.getheader(self.filename, 1) self.plateid = int(self._hdr['PLATEID']) except Exception as e: raise MarvinError('Could not initialize via filename: {0}' .format(e)) else: self.data_origin = 'file' self._makePdict() def _getPlateFromDB(self): ''' Initialize a Plate from the DB ''' import sqlalchemy from marvin import marvindb as mdb if not mdb.isdbconnected: raise MarvinError('No db connected') # Grab any cube for this plate cube = None try: cube = mdb.session.query(mdb.datadb.Cube).join( mdb.datadb.PipelineInfo, mdb.datadb.PipelineVersion).\ filter(mdb.datadb.Cube.plate == self.plateid, mdb.datadb.PipelineVersion.version == self._drpver).first() except sqlalchemy.orm.exc.NoResultFound as ee: raise MarvinError('Could not retrieve Cube for plate {0}: ' 'No Results Found: {1}' .format(self.plateid, ee)) except Exception as ee: raise MarvinError('Could not retrieve Cube for plate {0}: ' 'Unknown exception: {1}' .format(self.plateid, ee)) else: # no cube if not cube: raise MarvinError('No cube found in db for plate {0}, drpver {1}' .format(self.plateid, self._drpver)) # cube but no plateclass try: self._plate = cube.plateclass except AttributeError as ee: raise MarvinError('AttributeError: cube has no plateclass for plate {0}: {1}' .format(self.plateid, ee)) else: self._hdr = self._plate._hdr self._pdict = self._plate.__dict__ self.data_origin = 'db' if not self._plate: raise MarvinError('Could not retrieve Plate for id {0}'.format(self.plateid)) def _getPlateFromAPI(self): ''' Initialize a Plate using the API ''' # Checks that the Plate exists. routeparams = {'plateid': self.plateid} url = config.urlmap['api']['getPlate']['url'].format(**routeparams) # Make the API call response = self._toolInteraction(url) data = response.getData() self._hdr = data['header'] self.data_origin = 'api' self._makePdict() def _initCubes(self): ''' Initialize a list of Marvin Cube objects ''' _cubes = [None] if self.data_origin == 'file': sdss_path = Path(release=self.release) if self.dir3d == 'stack': cubes = sdss_path.expand('mangacube', drpver=self._drpver, plate=self.plateid, ifu='*', wave='LOG') else: cubes = sdss_path.expand('mangamastar', drpver=self._drpver, plate=self.plateid, ifu='*', wave='LOG') _cubes = [Cube(filename=cube, mode=self.mode, release=self.release) for cube in cubes] elif self.data_origin == 'db': _cubes = [Cube(plateifu=cube.plateifu, mode=self.mode, release=self.release) for cube in self._plate.cubes] elif self.data_origin == 'api': routeparams = {'plateid': self.plateid} url = config.urlmap['api']['getPlateCubes']['url'].format(**routeparams) # Make the API call response = self._toolInteraction(url) data = response.getData() plateifus = data['plateifus'] _cubes = [Cube(plateifu=pifu, mode=self.mode, release=self.release) for pifu in plateifus] FuzzyList.__init__(self, _cubes) self.mapper = (lambda e: e.plateifu) def _setParams(self): ''' Set the plate parameters ''' self.ra = self._pdict.get('ra', None) self.dec = self._pdict.get('dec', None) self.designid = self._pdict.get('designid', None) self.cartid = self._pdict.get('cartid', None) self.dateobs = self._pdict.get('dateobs', None) self.platetype = self._pdict.get('platetype', None) self.surveymode = self._pdict.get('surveymode', None) self.isbright = self._pdict.get('isbright', None) self.dir3d = self._pdict.get('dir3d', None) self.plateid = int(self.plateid) def _makePdict(self): ''' Make the necessary plate dictionary ''' self._pdict = {} self._pdict['ra'] = self._hdr.get('CENRA', None) self._pdict['dec'] = self._hdr.get('CENDEC', None) self._pdict['designid'] = self._hdr.get('DESIGNID', None) self._pdict['cartid'] = self._hdr.get('CARTID', None) self._pdict['dateobs'] = self._hdr.get('DATE-OBS', None) self._pdict['platetype'] = self._hdr.get('PLATETYP', None) self._pdict['surveymode'] = self._hdr.get('SRVYMODE', None) self._pdict['isbright'] = 'APOGEE' in self._pdict['surveymode'] self._pdict['dir3d'] = 'mastar' if self._pdict['isbright'] else 'stack' self._pdict['ra'] = float(self._pdict['ra']) self._pdict['dec'] = float(self._pdict['dec']) self._pdict['designid'] = float(self._pdict['designid']) def _sortOutNames(self): ''' Sort out any name issues with plateid, plateifu, mangaid inputs ''' if self.plateifu and 'XXX' not in self.plateifu: plate, ifu = self.plateifu.split('-') self.plateid = int(plate) def _checkFilename(self): ''' Checks the filename for a proper FITS file ''' # if filename is not FITS, then try to load one if 'fits' not in self.filename.lower(): if not Path: raise MarvinError('sdss_access is not installed') else: # is_public = 'DR' in self._release # path_release = self._release.lower() if is_public else None sdss_path = Path(release=self._release) # try a cube full = sdss_path.full('mangacube', drpver=self._drpver, plate=self.plateid, ifu='*', wave='LOG') cubeexists = sdss_path.any('', full=full) if cubeexists: file = sdss_path.one('', full=full) else: # try an rss full = sdss_path.full('mangarss', drpver=self._drpver, plate=self.plateid, ifu='*', wave='LOG') rssexists = sdss_path.any('', full=full) if rssexists: file = sdss_path.one('', full=full) else: file = None # load the file if file: self.filename = file else: self.filename = None