class LBTLUCISpectrograph(spectrograph.Spectrograph): """ Class to handle LBT/LUCI specific code """ ndet = 1 telescope = telescopes.LBTTelescopePar() # def __init__(self): # super().__init__() # self.timeunit = 'isot' # @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() # # # Processing steps # turn_off = dict(use_illumflat=False, use_biasimage=False, use_overscan=False, # use_darkimage=False) # par.reset_all_processimages_par(**turn_off) # # 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, 60] # par['calibrations']['standardframe']['exprng'] = [1, 200] # par['scienceframe']['exprng'] = [200, None] # return par 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='OBJRA') self.meta['dec'] = dict(ext=0, card='OBJDEC') self.meta['target'] = dict(ext=0, card='OBJECT') self.meta['decker'] = dict(ext=0, card='MASKID') self.meta['binning'] = dict(ext=0, card=None, default='1,1') self.meta['filter1'] = dict(ext=0, card='FILTERS') self.meta['idname'] = dict(card=None, compound=True) self.meta['mjd'] = dict(ext=0, card='MJD-OBS') self.meta['exptime'] = dict(ext=0, card='EXPTIME') self.meta['airmass'] = dict(ext=0, card='AIRMASS') self.meta['dispname'] = dict(ext=0, card='GRATNAME') # TODO: Deal with isot time here. 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). """ # Populate the idname based on the header information of LUCI # This is an implicit way of pre-typing without adding too many # variables to the self.meta. if meta_key == 'idname': targetname = (headarr[0].get('OBJECT')) dispname = (headarr[0].get('GRATNAME')) calib_unit = (headarr[0].get('CALIB')) filter1 = (headarr[0].get('FILTER1')) filter2 = (headarr[0].get('FILTER2')) lamp1 = (headarr[0].get('LAMP1')) lamp2 = (headarr[0].get('LAMP2')) lamp3 = (headarr[0].get('LAMP3')) lamp4 = (headarr[0].get('LAMP4')) lamp5 = (headarr[0].get('LAMP5')) lamp6 = (headarr[0].get('LAMP6')) # object frame -> will be typed as science # This currently includes sky flats, science and standard images # We will guess standards using the beginning of their names. if ((dispname != 'Mirror') and (calib_unit == False) and (lamp1 == False) and (lamp2 == False) and (lamp3 == False) and (lamp4 == False) and (lamp5 == False) and (lamp6 == False)): if (targetname[:3] == 'HIP' or targetname[:2] == 'HD' or targetname[:5] == 'Feige'): return 'standard' else: return 'object' # flat frames -> will be typed as pixelflat, trace elif ((calib_unit == True) and ((lamp4 == True) or (lamp5 == True) or (lamp6 == True))): return 'flat' # arcs -> will be typed as arc, tilt elif ((dispname != 'Mirror') and (calib_unit == True) and ((lamp1 == True) or (lamp2 == True) or (lamp3 == True))): return 'arc' # pixelflat off -> will be typed as bias elif ((dispname != 'Mirror') and (calib_unit == True) and (lamp1 == False) and (lamp2 == False) and (lamp3 == False) and (lamp4 == False) and (lamp5 == False) and (lamp6 == False) and (filter1 != 'blind') and (filter2 != 'blind')): return 'flat_off' # darks -> will not be typed currently elif ((filter1 == 'blind') or (filter2 == 'blind')): return 'dark' msgs.error("Not ready for this compound meta") 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. """ return ['decker', 'dispname'] def pypeit_file_keys(self): """ Define the list of keys to be output into a standard ``PypeIt`` file. Returns: :obj:`list`: The list of keywords in the relevant :class:`~pypeit.metadata.PypeItMetaData` instance to print to the :ref:`pypeit_file`. """ pypeit_keys = super().pypeit_file_keys() # TODO: Why are these added here? See # pypeit.metadata.PypeItMetaData.set_pypeit_cols # TODO: This should only add idname pypeit_keys += ['calib', 'comb_id', 'bkg_id', 'idname'] return pypeit_keys 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) # ATTENTION: Standards have to be added manually for LUCI because # there is not unique flag that allows to distinguish between targets # and standards if ftype in ['science']: return good_exp & (fitstbl['idname'] == 'object') if ftype in ['standard']: return good_exp & (fitstbl['idname'] == 'standard') if ftype == 'bias': # for NIR data we type off lamp flats as biases return good_exp & (fitstbl['idname'] == 'flat_off') if ftype in ['pixelflat', 'trace']: # Flats and trace frames are typed together return good_exp & (fitstbl['idname'] == 'flat') if ftype in ['dark']: # NOT Typing dark frames # return np.zeros(len(fitstbl), dtype=bool) # for testing dark typing uncommen the following line and comment # out the previous line return good_exp & (fitstbl['idname'] == 'dark') if ftype in ['arc', 'tilt']: return (good_exp & ((fitstbl['idname'] == 'object') | (fitstbl['idname'] == 'arc'))) msgs.warn('Cannot determine if frames are of type {0}.'.format(ftype)) return np.zeros(len(fitstbl), dtype=bool)
def __init__(self): # Get it started super(LBTMODSSpectrograph, self).__init__() self.spectrograph = 'lbt_mods' self.telescope = telescopes.LBTTelescopePar() self.timeunit = 'isot'
class LBTMODSSpectrograph(spectrograph.Spectrograph): """ Child to handle Shane/Kast specific code """ ndet = 1 telescope = telescopes.LBTTelescopePar() # def __init__(self): # super().__init__() # self.timeunit = 'isot' @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() # Scienceimage default parameters # 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, None] par['calibrations']['standardframe']['exprng'] = [1, 200] par['scienceframe']['exprng'] = [200, None] return par 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='OBJRA') self.meta['dec'] = dict(ext=0, card='OBJDEC') self.meta['target'] = dict(ext=0, card='OBJECT') self.meta['decker'] = dict(ext=0, card='MASKNAME') self.meta['binning'] = dict(card=None, compound=True) self.meta['mjd'] = dict(ext=0, card='MJD-OBS') self.meta['exptime'] = dict(ext=0, card='EXPTIME') self.meta['airmass'] = dict(ext=0, card='AIRMASS') self.meta['dispname'] = dict(ext=0, card='GRATNAME') self.meta['dichroic'] = dict(ext=0, card='FILTNAME') self.meta['idname'] = dict(ext=0, card='IMAGETYP') 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 == 'binning': binspatial, binspec = parse.parse_binning( np.array([headarr[0]['CCDXBIN'], headarr[0]['CCDYBIN']])) binning = parse.binning2string(binspatial, binspec) return binning msgs.error("Not ready for this compound meta") 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 standards are usually taken with a 5" slit and arc using 0.8" slit return ['dispname', 'binning'] 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 & (fitstbl['idname'] == 'OBJECT') & (fitstbl['ra'] != 'none') \ & (fitstbl['dispname'] != 'Flat') if ftype == 'bias': return good_exp & (fitstbl['idname'] == 'BIAS') if ftype in ['pixelflat', 'trace', 'illumflat']: # Flats and trace frames are typed together return good_exp & (fitstbl['idname'] == 'FLAT') & (fitstbl['decker'] != 'Imaging') 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 & (fitstbl['idname'] == 'COMP') & (fitstbl['dispname'] != 'Flat') msgs.warn('Cannot determine if frames are of type {0}.'.format(ftype)) return np.zeros(len(fitstbl), dtype=bool) def get_rawimage(self, raw_file, det): """ Read raw images and generate a few other bits and pieces that are key for image processing. Parameters ---------- raw_file : :obj:`str` File to read det : :obj:`int` 1-indexed detector to read Returns ------- detector_par : :class:`pypeit.images.detector_container.DetectorContainer` Detector metadata parameters. raw_img : `numpy.ndarray`_ Raw image for this detector. hdu : `astropy.io.fits.HDUList`_ Opened fits file exptime : :obj:`float` Exposure time read from the file header rawdatasec_img : `numpy.ndarray`_ Data (Science) section of the detector as provided by setting the (1-indexed) number of the amplifier used to read each detector pixel. Pixels unassociated with any amplifier are set to 0. oscansec_img : `numpy.ndarray`_ Overscan section of the detector as provided by setting the (1-indexed) number of the amplifier used to read each detector pixel. Pixels unassociated with any amplifier are set to 0. """ # Check for file; allow for extra .gz, etc. suffix fil = glob.glob(raw_file + '*') if len(fil) != 1: msgs.error("Found {:d} files matching {:s}".format(len(fil))) # Read msgs.info("Reading LBT/MODS file: {:s}".format(fil[0])) hdu = io.fits_open(fil[0]) head = hdu[0].header # TODO These parameters should probably be stored in the detector par # Number of amplifiers (could pull from DetectorPar but this avoids needing the spectrograph, e.g. view_fits) detector_par = self.get_detector_par(hdu, det if det is None else 1) numamp = detector_par['numamplifiers'] # get the x and y binning factors... xbin, ybin = head['CCDXBIN'], head['CCDYBIN'] datasize = head['DETSIZE'] # Unbinned size of detector full array _, nx_full, _, ny_full = np.array( parse.load_sections(datasize, fmt_iraf=False)).flatten() # Determine the size of the output array... nx, ny = int(nx_full / xbin), int(ny_full / ybin) nbias1 = 48 nbias2 = 8240 # allocate output array... array = hdu[ 0].data.T * 1.0 ## Convert to float in order to get it processed with procimg.py rawdatasec_img = np.zeros_like(array, dtype=int) oscansec_img = np.zeros_like(array, dtype=int) ## allocate datasec and oscansec to the image # apm 1 rawdatasec_img[int(nbias1 / xbin):int(nx / 2), :int(ny / 2)] = 1 oscansec_img[1:int(nbias1 / xbin), :int( ny / 2)] = 1 # exclude the first pixel since it always has problem # apm 2 rawdatasec_img[int(nx / 2):int(nbias2 / xbin), :int(ny / 2)] = 2 oscansec_img[int(nbias2 / xbin):nx - 1, :int( ny / 2)] = 2 # exclude the last pixel since it always has problem # apm 3 rawdatasec_img[int(nbias1 / xbin):int(nx / 2), int(ny / 2):] = 3 oscansec_img[ 1:int(nbias1 / xbin), int(ny / 2):] = 3 # exclude the first pixel since it always has problem # apm 4 rawdatasec_img[int(nx / 2):int(nbias2 / xbin), int(ny / 2):] = 4 oscansec_img[ int(nbias2 / xbin):nx - 1, int(ny / 2):] = 4 # exclude the last pixel since it always has problem # Need the exposure time exptime = hdu[self.meta['exptime']['ext']].header[self.meta['exptime'] ['card']] # Return, transposing array back to orient the overscan properly return detector_par, np.flipud(array), hdu, exptime, np.flipud( rawdatasec_img), np.flipud(oscansec_img)