Source code for pypeit.spectrographs.magellan_fire

"""
Module for Magellan/FIRE specific methods.

Important Notes:

    - If you are reducing old FIRE data (before the broken happened
      in 2016), please change the ord_spat_pos array (see lines from
      ~220 to ~230)

.. include:: ../include/links.rst
"""
import numpy as np

from pypeit import msgs
from pypeit import telescopes
from pypeit.core import framematch
from pypeit.spectrographs import spectrograph
from pypeit.images import detector_container


[docs]class MagellanFIRESpectrograph(spectrograph.Spectrograph): """ Child to handle Magellan/FIRE specific code .. note:: For FIRE Echelle, we usually use high gain and SUTR read mode. The exposure time is usually around 900s. The detector parameters below are based on such mode. Standard star and calibrations are usually use Fowler 1 read mode in which case the read noise is ~20 electron. """ ndet = 1 telescope = telescopes.MagellanTelescopePar() camera = 'FIRE' url = 'http://web.mit.edu/~rsimcoe/www/FIRE/index.html' header_name = 'FIRE'
[docs] 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') self.meta['decker'] = dict(ext=0, card=None, default='default') self.meta['dichroic'] = dict(ext=0, card=None, default='default') self.meta['binning'] = dict(ext=0, card=None, default='1,1') self.meta['mjd'] = dict(ext=0, card='ACQTIME') self.meta['exptime'] = dict(ext=0, card='EXPTIME') self.meta['airmass'] = dict(ext=0, card='AIRMASS') # Extras for config and frametyping self.meta['dispname'] = dict(ext=0, card='GRISM') self.meta['idname'] = dict(ext=0, card='OBSTYPE') self.meta['instrument'] = dict(ext=0, card='INSTRUME')
[docs] 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 pypeit_keys += ['calib', 'comb_id', 'bkg_id'] return pypeit_keys
[docs]class MagellanFIREEchelleSpectrograph(MagellanFIRESpectrograph): """ Child to handle Magellan/FIRE Echelle data .. note:: For FIRE Echelle, we usually use high gain and SUTR read mode. The exposure time is usually around 900s. The detector parameters below are based on such mode. Standard star and calibrations are usually use Fowler 1 read mode in which case the read noise is ~20 electron. """ name = 'magellan_fire' pypeline = 'Echelle' ech_fixed_format = True supported = True comment = 'Magellan/FIRE in echelle mode'
[docs] def get_detector_par(self, det, hdu=None): """ Return metadata for the selected detector. Args: det (:obj:`int`): 1-indexed detector number. hdu (`astropy.io.fits.HDUList`_, optional): The open fits file with the raw image of interest. If not provided, frame-dependent parameters are set to a default. Returns: :class:`~pypeit.images.detector_container.DetectorContainer`: Object with the detector metadata. """ # Detector 1 detector_dict = dict( binning = '1,1', det = 1, dataext = 0, specaxis = 1, specflip = True, spatflip = False, platescale = 0.18, darkcurr = 3.06, # e-/pixel/hour (=0.00085 e-/pixel/s) #saturation = 20000., # high gain is 20000 ADU, low gain is 32000 ADU saturation = 100000., # This is an arbitrary value. nonlinear = 1.0, # high gain mode, low gain is 0.875 mincounts = -1e10, numamplifiers = 1, gain = np.atleast_1d(1.2), # high gain mode, low gain is 3.8 e-/DN ronoise = np.atleast_1d(5.0), # for high gain mode and SUTR read modes with exptime ~ 900s datasec = np.atleast_1d('[5:2044,5:2044]'), oscansec = np.atleast_1d('[5:2044,:5]') ) return detector_container.DetectorContainer(**detector_dict)
[docs] @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() # Wavelengths # 1D wavelength solution with OH lines par['calibrations']['wavelengths']['rms_thresh_frac_fwhm'] = 0.25 par['calibrations']['wavelengths']['sigdetect']=[5,5,10,10,10,20,30,30,30,30,30,10,30,30,60,30,30,10,20,30,10] par['calibrations']['wavelengths']['n_first']=2 par['calibrations']['wavelengths']['n_final']=[3,2,3,2,4,4,4,3,4,4,4,3,4,4,4,4,4,4,6,6,4] par['calibrations']['wavelengths']['lamps'] = ['OH_FIRE_Echelle'] #par['calibrations']['wavelengths']['nonlinear_counts'] = self.detector[0]['nonlinear'] * self.detector[0]['saturation'] par['calibrations']['wavelengths']['method'] = 'reidentify' par['calibrations']['wavelengths']['cc_thresh'] = 0.35 par['calibrations']['wavelengths']['reid_arxiv'] = 'magellan_fire_echelle.fits' # par['calibrations']['wavelengths']['match_toler']=30.0 # Echelle parameters par['calibrations']['wavelengths']['echelle'] = True # par['calibrations']['wavelengths']['ech_fix_format'] = True par['calibrations']['wavelengths']['ech_nspec_coeff'] = 4 par['calibrations']['wavelengths']['ech_norder_coeff'] = 6 par['calibrations']['wavelengths']['ech_sigrej'] = 3.0 # Always correct for flexure, starting with default parameters par['scienceframe']['process']['sigclip'] = 20.0 par['scienceframe']['process']['satpix'] ='nothing' # Set slits and tilts parameters par['calibrations']['tilts']['tracethresh'] = 5 par['calibrations']['slitedges']['edge_thresh'] = 3. par['calibrations']['slitedges']['trace_thresh'] = 10. par['calibrations']['slitedges']['fit_order'] = 5 par['calibrations']['slitedges']['max_shift_adj'] = 0.5 par['calibrations']['slitedges']['fit_min_spec_length'] = 0.5 par['calibrations']['slitedges']['left_right_pca'] = True par['calibrations']['slitedges']['pca_order'] = 3 # Model entire slit par['reduce']['extraction']['model_full_slit'] = True # local sky subtraction operates on entire slit par['reduce']['findobj']['maxnumber_sci'] = 2 # Slit is narrow so allow one object per order par['reduce']['findobj']['maxnumber_std'] = 1 # Slit is narrow so allow one object per order # Processing steps turn_off = dict(use_illumflat=False, use_biasimage=False, use_overscan=False, use_darkimage=False) par.reset_all_processimages_par(**turn_off) # Do not correct for flexure par['flexure']['spec_method'] = 'skip' # Set the default exposure time ranges for the frame typing par['calibrations']['standardframe']['exprng'] = [None, 60] par['calibrations']['arcframe']['exprng'] = [20, None] par['calibrations']['darkframe']['exprng'] = [20, None] par['scienceframe']['exprng'] = [20, None] # Sensitivity function parameters # Sensitivity function parameters par['sensfunc']['algorithm'] = 'IR' par['sensfunc']['polyorder'] = 5 par['sensfunc']['IR']['maxiter'] = 2 # place holder for telgrid file par['sensfunc']['IR']['telgridfile'] = 'TellPCA_3000_26000_R15000.fits' # Coadding. I'm not sure what this should be for PRISM mode? par['coadd1d']['wave_method'] = 'log10' return par
[docs] 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 ['pinhole', 'bias']: # No pinhole or bias frames return np.zeros(len(fitstbl), dtype=bool) if ftype in ['pixelflat', 'trace']: return good_exp & (fitstbl['idname'] == 'PixFlat') if ftype == 'standard': return good_exp & (fitstbl['idname'] == 'Telluric') if ftype == 'science': return good_exp & (fitstbl['idname'] == 'Science') if ftype in ['arc', 'tilt']: return good_exp & (fitstbl['idname'] == 'Science') msgs.warn('Cannot determine if frames are of type {0}.'.format(ftype)) return np.zeros(len(fitstbl), dtype=bool)
@property def norders(self): """ Number of orders for this spectograph. Should only defined for echelle spectrographs, and it is undefined for the base class. """ return 21 @property def order_spat_pos(self): """ Return the expected spatial position of each echelle order. """ # ToDo: We somehow need to automate this. ## For OLD data, i.e. before 2017 #ord_spat_pos = np.array([0.06054688, 0.14160156, 0.17089844, 0.22753906, 0.27539062, # 0.32128906, 0.36474609, 0.40673828, 0.45019531, 0.48974609, # 0.52978516, 0.56054688, 0.59814453, 0.63378906, 0.66503906, # 0.70019531, 0.7421875 , 0.77978516, 0.82763672, 0.87109375, # 0.9296875]) ## For NEW data ord_spat_pos = np.array([0.078125, 0.13769531, 0.19189453, 0.24414062, 0.29296875, 0.34179688, 0.38330078, 0.42724609, 0.46582031, 0.50439453, 0.54199219, 0.57763672, 0.61279297, 0.6484375 , 0.68457031, 0.71875 , 0.75439453, 0.79443359, 0.83789062, 0.88671875, 0.94091797]) return ord_spat_pos @property def orders(self): """ Return the order number for each echelle order. """ return np.arange(31, 10, -1, dtype=int) @property def spec_min_max(self): """ Return the minimum and maximum spectral pixel expected for the spectral range of each order. """ spec_max = np.asarray([2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048, 2048,2048,2048,2048,2048]) spec_min = np.asarray([ 500, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) return np.vstack((spec_min, spec_max))
[docs] def order_platescale(self, order_vec, binning=None): """ Return the platescale for each echelle order. Note that FIRE has no binning. Args: order_vec (`numpy.ndarray`_): The vector providing the order numbers. binning (:obj:`str`, optional): The string defining the spectral and spatial binning. **This is always ignored.** Returns: `numpy.ndarray`_: An array with the platescale for each order provided by ``order``. """ return np.full(order_vec.size, 0.15)
@property def dloglam(self): """ Return the logarithmic step in wavelength for output spectra. """ # This number was determined using the resolution and sampling quoted on the FIRE website R = 6000.0 * 2.7 dloglam = 1.0 / R / np.log(10.0) return dloglam @property def loglam_minmax(self): """ Return the base-10 logarithm of the first and last wavelength for ouput spectra. """ return np.log10(8000.0), np.log10(25700)
[docs]class MagellanFIRELONGSpectrograph(MagellanFIRESpectrograph): """ Child to handle Magellan/FIRE high-throughput data .. note:: For FIRE longslit, science data are usually taken with SUTR readout mode with ~600s exposure (at least for quasar hunting people) and the readout noise is ~6 e- """ name = 'magellan_fire_long' supported = True comment = 'Magellan/FIRE in long-slit/high-throughput mode'
[docs] def get_detector_par(self, det, hdu=None): """ Return metadata for the selected detector. Args: det (:obj:`int`): 1-indexed detector number. hdu (`astropy.io.fits.HDUList`_, optional): The open fits file with the raw image of interest. If not provided, frame-dependent parameters are set to a default. Returns: :class:`~pypeit.images.detector_container.DetectorContainer`: Object with the detector metadata. """ # Detector 1 detector_dict = dict( binning = '1,1', det = 1, dataext = 0, specaxis = 0, specflip = False, spatflip = False, platescale = 0.15, darkcurr = 3.06, # e-/pixel/hour (=0.00085 e-/pixel/s) saturation = 320000., #32000 for low gain, I set to a higher value to keep data in K-band nonlinear = 0.875, mincounts = -1e10, numamplifiers = 1, gain = np.atleast_1d(3.8), ronoise = np.atleast_1d(6.0), # SUTR readout mode with exposure~600s datasec = np.atleast_1d('[5:2044, 900:1250]'), oscansec = np.atleast_1d('[:5, 900:1250]') ) return detector_container.DetectorContainer(**detector_dict)
[docs] @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() # Wavelengths # 1D wavelength solution with arc lines par['calibrations']['wavelengths']['rms_thresh_frac_fwhm'] = 0.05 par['calibrations']['wavelengths']['sigdetect']=3 par['calibrations']['wavelengths']['fwhm'] = 10 par['calibrations']['wavelengths']['n_first']=2 par['calibrations']['wavelengths']['n_final']=4 par['calibrations']['wavelengths']['lamps'] = ['ArI', 'ArII', 'ThAr', 'NeI'] #par['calibrations']['wavelengths']['nonlinear_counts'] = self.detector[0]['nonlinear'] * self.detector[0]['saturation'] par['calibrations']['wavelengths']['method'] = 'full_template' par['calibrations']['wavelengths']['reid_arxiv'] = 'magellan_fire_long.fits' par['calibrations']['wavelengths']['match_toler']=5.0 # Set slits and tilts parameters par['calibrations']['tilts']['tracethresh'] = 5 par['calibrations']['slitedges']['trace_thresh'] = 10. par['calibrations']['slitedges']['sync_predict'] = 'nearest' # Processing steps turn_off = dict(use_illumflat=False, use_biasimage=False, use_overscan=False, use_darkimage=False) par.reset_all_processimages_par(**turn_off) # Scienceimage parameters par['reduce']['findobj']['snr_thresh'] = 5 #par['reduce']['maxnumber'] = 2 par['reduce']['findobj']['find_trim_edge'] = [50,50] par['flexure']['spec_method'] = 'skip' par['sensfunc']['IR']['telgridfile'] = 'TellPCA_3000_26000_R10000.fits' # Set the default exposure time ranges for the frame typing par['calibrations']['standardframe']['exprng'] = [None, 60] par['calibrations']['arcframe']['exprng'] = [1, 50] par['calibrations']['darkframe']['exprng'] = [20, None] par['scienceframe']['exprng'] = [20, None] return par
[docs] 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 ['pinhole', 'bias']: # No pinhole or bias frames return np.zeros(len(fitstbl), dtype=bool) if ftype in ['pixelflat', 'trace']: return good_exp & (fitstbl['idname'] == 'PixFlat') if ftype == 'standard': return good_exp & (fitstbl['idname'] == 'Telluric') if ftype == 'science': return good_exp & (fitstbl['idname'] == 'Science') if ftype in ['arc', 'tilt']: return good_exp & (fitstbl['idname'] == 'Arc') msgs.warn('Cannot determine if frames are of type {0}.'.format(ftype)) return np.zeros(len(fitstbl), dtype=bool)