def __init__(self): # Get it started super(ShaneKastSpectrograph, self).__init__() self.spectrograph = 'shane_kast' self.telescope = telescopes.ShaneTelescopePar()
class ShaneKastSpectrograph(spectrograph.Spectrograph): """ Child to handle Shane/Kast specific code """ ndet = 1 telescope = telescopes.ShaneTelescopePar() @classmethod def default_pypeit_par(cls): """ Return the default parameters to use for this instrument. Returns: :class:`~pypeit.par.pypeitpar.PypeItPar`: Parameters required by all of ``PypeIt`` methods. """ par = super().default_pypeit_par() # Ignore PCA par['calibrations']['slitedges']['sync_predict'] = 'nearest' # Always correct for flexure, starting with default parameters par['flexure']['spec_method'] = 'boxcar' # Set the default exposure time ranges for the frame typing par['calibrations']['biasframe']['exprng'] = [None, 1] par['calibrations']['darkframe']['exprng'] = [999999, None] # No dark frames par['calibrations']['pinholeframe']['exprng'] = [999999, None ] # No pinhole frames par['calibrations']['pixelflatframe']['exprng'] = [0, None] par['calibrations']['traceframe']['exprng'] = [0, None] par['calibrations']['arcframe']['exprng'] = [None, 61] par['calibrations']['standardframe']['exprng'] = [1, 61] # par['scienceframe']['exprng'] = [61, None] return par def compound_meta(self, headarr, meta_key): """ Methods to generate metadata requiring interpretation of the header data, instead of simply reading the value of a header card. Args: headarr (:obj:`list`): List of `astropy.io.fits.Header`_ objects. meta_key (:obj:`str`): Metadata keyword to construct. Returns: object: Metadata value read from the header(s). """ if meta_key == 'mjd': time = headarr[0]['DATE'] ttime = Time(time, format='isot') return ttime.mjd msgs.error("Not ready for this compound meta") def init_meta(self): """ Define how metadata are derived from the spectrograph files. That is, this associates the ``PypeIt``-specific metadata keywords with the instrument-specific header cards using :attr:`meta`. """ self.meta = {} # Required (core) self.meta['ra'] = dict(ext=0, card='RA') self.meta['dec'] = dict(ext=0, card='DEC') self.meta['target'] = dict(ext=0, card='OBJECT') # dispname is arm specific (blue/red) self.meta['decker'] = dict(ext=0, card='SLIT_N') self.meta['binning'] = dict(ext=0, card=None, default='1,1') self.meta['mjd'] = dict(ext=0, card=None, compound=True) self.meta['exptime'] = dict(ext=0, card='EXPTIME') self.meta['airmass'] = dict(ext=0, card='AIRMASS') # Additional ones, generally for configuration determination or time self.meta['dichroic'] = dict(ext=0, card='BSPLIT_N') lamp_names = [ '1', '2', '3', '4', '5', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K' ] for kk, lamp_name in enumerate(lamp_names): self.meta['lampstat{:02d}'.format(kk + 1)] = dict( ext=0, card='LAMPSTA{0}'.format(lamp_name)) def configuration_keys(self): """ Return the metadata keys that define a unique instrument configuration. This list is used by :class:`~pypeit.metadata.PypeItMetaData` to identify the unique configurations among the list of frames read for a given reduction. Returns: :obj:`list`: List of keywords of data pulled from file headers and used to constuct the :class:`~pypeit.metadata.PypeItMetaData` object. """ # decker is not included because arcs are often taken with a 0.5" slit return ['dispname', 'dichroic'] def check_frame_type(self, ftype, fitstbl, exprng=None): """ Check for frames of the provided type. Args: ftype (:obj:`str`): Type of frame to check. Must be a valid frame type; see frame-type :ref:`frame_type_defs`. fitstbl (`astropy.table.Table`_): The table with the metadata for one or more frames to check. exprng (:obj:`list`, optional): Range in the allowed exposure time for a frame of type ``ftype``. See :func:`pypeit.core.framematch.check_frame_exptime`. Returns: `numpy.ndarray`_: Boolean array with the flags selecting the exposures in ``fitstbl`` that are ``ftype`` type frames. """ good_exp = framematch.check_frame_exptime(fitstbl['exptime'], exprng) if ftype in ['science', 'standard']: return good_exp & self.lamps(fitstbl, 'off') if ftype == 'bias': return good_exp # & (fitstbl['target'] == 'Bias') if ftype in ['pixelflat', 'trace', 'illumflat']: # Flats and trace frames are typed together return good_exp & self.lamps( fitstbl, 'dome') # & (fitstbl['target'] == 'Dome Flat') if ftype in ['pinhole', 'dark']: # Don't type pinhole or dark frames return np.zeros(len(fitstbl), dtype=bool) if ftype in ['arc', 'tilt']: return good_exp & self.lamps( fitstbl, 'arcs') # & (fitstbl['target'] == 'Arcs') msgs.warn('Cannot determine if frames are of type {0}.'.format(ftype)) return np.zeros(len(fitstbl), dtype=bool) def lamps(self, fitstbl, status): """ Check the lamp status. Args: fitstbl (`astropy.table.Table`_): The table with the fits header meta data. status (:obj:`str`): The status to check. Can be ``'off'``, ``'arcs'``, or ``'dome'``. Returns: `numpy.ndarray`_: A boolean array selecting fits files that meet the selected lamp status. Raises: ValueError: Raised if the status is not one of the valid options. """ if status == 'off': # Check if all are off return np.all(np.array([ (fitstbl[k] == 'off') | (fitstbl[k] == 'None') for k in fitstbl.keys() if 'lampstat' in k ]), axis=0) if status == 'arcs': # Check if any arc lamps are on arc_lamp_stat = ['lampstat{0:02d}'.format(i) for i in range(6, 17)] return np.any(np.array([ fitstbl[k] == 'on' for k in fitstbl.keys() if k in arc_lamp_stat ]), axis=0) if status == 'dome': # Check if any dome lamps are on dome_lamp_stat = ['lampstat{0:02d}'.format(i) for i in range(1, 6)] return np.any(np.array([ fitstbl[k] == 'on' for k in fitstbl.keys() if k in dome_lamp_stat ]), axis=0) raise ValueError('No implementation for status = {0}'.format(status))