def test_parse_binning(): """ Test parse binning algorithm """ bin1, bin2 = parse.parse_binning('2,2') assert bin1 == 2 assert bin2 == 2 # Other input bin1, bin2 = parse.parse_binning((2,2)) assert bin1 == 2 assert bin2 == 2
def config_specific_par(self, par, scifile): # Templates if self.get_meta_value(scifile, 'dispname') == '600ZD': par['calibrations']['wavelengths']['method'] = 'full_template' par['calibrations']['wavelengths'][ 'reid_arxiv'] = 'keck_deimos_600.fits' par['calibrations']['wavelengths']['lamps'] += [ 'CdI', 'ZnI', 'HgI' ] elif self.get_meta_value(scifile, 'dispname') == '830G': par['calibrations']['wavelengths']['method'] = 'full_template' par['calibrations']['wavelengths'][ 'reid_arxiv'] = 'keck_deimos_830G.fits' elif self.get_meta_value(scifile, 'dispname') == '1200G': par['calibrations']['wavelengths']['method'] = 'full_template' par['calibrations']['wavelengths'][ 'reid_arxiv'] = 'keck_deimos_1200G.fits' # FWHM binning = parse.parse_binning(self.get_meta_value(scifile, 'binning')) par['calibrations']['wavelengths']['fwhm'] = 6.0 / binning[1] # Return return par
def order_platescale(self, binning=None): """ Returns the plate scale in arcseconds for each order Parameters ---------- None Optional Parameters -------------------- binning: str Returns ------- order_platescale: ndarray, float """ # VIS has no binning, but for an instrument with binning we would do this binspectral, binspatial = parse.parse_binning(binning) # ToDO Either assume a linear trend or measure this # X-shooter manual says, but gives no exact numbers per order. # VIS: 65.9 pixels (0.167"/pix) at order 17 to 72.0 pixels (0.153"/pix) at order 30. # Right now I just assume a simple linear trend slit_vec = np.arange(self.norders) order_vec = self.slit2order(slit_vec) plate_scale = 0.153 + (order_vec - 30)*(0.153-0.167)/(30 - 17) return plate_scale*binspatial
def compound_meta(self, headarr, meta_key): if meta_key == 'binning': binspatial, binspec = parse.parse_binning(headarr[0]['BINNING']) binning = parse.binning2string(binspec, binspatial) return binning else: msgs.error("Not ready for this compound meta")
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': # binning in the raw frames ccdsum = headarr[1].get('CCDSUM') if ccdsum is not None: binspatial, binspec = parse.parse_binning(ccdsum) binning = parse.binning2string(binspec, binspatial) else: # binning in the spec2d file binning = headarr[0].get('BINNING') if binning is None: msgs.error('Binning not found') return binning
def config_specific_par(self, scifile, inp_par=None): """ Modify the ``PypeIt`` parameters to hard-wired values used for specific instrument configurations. Args: scifile (:obj:`str`): File to use when determining the configuration and how to adjust the input parameters. inp_par (:class:`~pypeit.par.parset.ParSet`, optional): Parameter set used for the full run of PypeIt. If None, use :func:`default_pypeit_par`. Returns: :class:`~pypeit.par.parset.ParSet`: The PypeIt parameter set adjusted for configuration specific parameter values. """ par = super().config_specific_par(scifile, inp_par=inp_par) headarr = self.get_headarr(scifile) # Turn PCA off for long slits if 'arcsec' in self.get_meta_value(headarr, 'decker'): par['calibrations']['slitedges']['sync_predict'] = 'nearest' # Allow for various binning binning = parse.parse_binning(self.get_meta_value(headarr, 'binning')) par['calibrations']['wavelengths']['fwhm_fromlines'] = True return par
def order_platescale(self, order_vec, binning=None): """ Return the platescale for each echelle order. This routine is only defined for echelle spectrographs, and it is undefined in the base class. Args: order_vec (`numpy.ndarray`_): The vector providing the order numbers. binning (:obj:`str`, optional): The string defining the spectral and spatial binning. Returns: `numpy.ndarray`_: An array with the platescale for each order provided by ``order``. """ binspectral, binspatial = parse.parse_binning(binning) # ToDO Either assume a linear trend or measure this # X-shooter manual says, but gives no exact numbers per order. # UVB: 65.9 pixels (0.167“/pix) at order 14 to 70.8 pixels (0.155”/pix) at order 24 # Assume a simple linear trend plate_scale = 0.155 + (order_vec - 24) * (0.155 - 0.167) / (24 - 14) # Right now I just took the average return np.full(self.norders, 0.161) * binspatial
def order_platescale(self, order_vec, binning=None): """ Return the platescale for each echelle order. This routine is only defined for echelle spectrographs, and it is undefined in the base class. Args: order_vec (`numpy.ndarray`_): The vector providing the order numbers. binning (:obj:`str`, optional): The string defining the spectral and spatial binning. Returns: `numpy.ndarray`_: An array with the platescale for each order provided by ``order``. """ # VIS has no binning, but for an instrument with binning we would do this binspectral, binspatial = parse.parse_binning(binning) # ToDO Either assume a linear trend or measure this # X-shooter manual says, but gives no exact numbers per order. # VIS: 65.9 pixels (0.167"/pix) at order 17 to 72.0 pixels (0.153"/pix) at order 30. # Right now I just assume a simple linear trend plate_scale = 0.153 + (order_vec - 30) * (0.153 - 0.167) / (30 - 17) return plate_scale * binspatial
def order_platescale(self, binning=None): """ Returns the plate scale in arcseconds for each order Parameters ---------- None Optional Parameters -------------------- binning: str Returns ------- order_platescale: ndarray, float """ msgs.error("REFACTOR") binspectral, binspatial = parse.parse_binning(binning) # ToDO Either assume a linear trend or measure this # X-shooter manual says, but gives no exact numbers per order. # UVB: 65.9 pixels (0.167“/pix) at order 14 to 70.8 pixels (0.155”/pix) at order 24 # Right now I just took the average return np.full(self.norders, 0.161) * binspatial
def bpm(self, shape=None, filename=None, det=None, **null_kwargs): """ Override parent bpm function with BPM specific to X-Shooter VIS. .. todo:: Allow for binning changes. Parameters ---------- det : int, REQUIRED **null_kwargs: Captured and never used Returns ------- bpix : ndarray 0 = ok; 1 = Mask """ msgs.info("Custom bad pixel mask for MAGE") self.empty_bpm(shape=shape, filename=filename, det=det) # Get the binning hdu = fits.open(filename) binspatial, binspec = parse.parse_binning(hdu[0].header['BINNING']) hdu.close() # Do it self.bpm_img[:, :10 // binspatial] = 1. self.bpm_img[:, 1020 // binspatial:] = 1. # Return return self.bpm_img
def compound_meta(self, headarr, meta_key): """ Args: headarr: list meta_key: str Returns: value """ if meta_key == 'binning': binspatial, binspec = parse.parse_binning(headarr[0]['BINNING']) binning = parse.binning2string(binspec, binspatial) return binning elif meta_key == 'dispangle': if headarr[0]['GRATEPOS'] == 3: return headarr[0]['G3TLTWAV'] elif headarr[0]['GRATEPOS'] == 4: return headarr[0]['G4TLTWAV'] else: msgs.warn( 'This is probably a problem. Non-standard DEIMOS GRATEPOS={0}.' .format(headarr[0]['GRATEPOS'])) else: msgs.error("Not ready for this compound meta")
def wavegrid(self, binning=None, midpoint=False, samp_fact=1.0): r""" Construct a fixed wavelength grid in :math:`\log_{10} \lambda`. This method is mostly used for echelle spectrographs. This is primarily a wrapper for :func:`pypeit.core.wavecal.wvutils.wavegrid`. Args: binning (:obj:`str`, optional): String representation of the spectral and spatial binning. If None, assumed to be ``'1,1'``. midpoint (:obj:`bool`, optional): Increment the grid to the spectral mid-point. samp_fact (:obj:`float`, optional): The pixel-sampling factor. If 1., the spectra are sampled by the logarithmic step associated with a single binned pixel (:attr:`dloglam`). Returns: `numpy.ndarray`_: The logarithmically sampled wavelength grid; grid points are in wavelength, *not* ``log10(wavelength)``. """ binspectral, binspatial = parse.parse_binning(binning) logmin, logmax = self.loglam_minmax loglam_grid = wvutils.wavegrid(logmin, logmax, self.dloglam * binspectral, samp_fact=samp_fact) if midpoint: loglam_grid = loglam_grid + self.dloglam * binspectral / samp_fact / 2.0 return np.power(10.0, loglam_grid)
def get_wv_calib(self): """ Load or generate the 1D wavelength calibrations Requirements: msarc, msbpm, slits, det, par Returns: dict: :attr:`wv_calib` calibration dict and the updated slit mask array """ # Check for existing data if not self._chk_objs(['msarc', 'msbpm', 'slits']): msgs.warn( 'Not enough information to load/generate the wavelength calibration. Skipping and may crash down the line' ) return None # Check internals self._chk_set(['det', 'calib_ID', 'par']) if 'arc' not in self.master_key_dict.keys(): msgs.error('Arc master key not set. First run get_arc.') # No wavelength calibration requested if self.par['wavelengths']['reference'] == 'pixel': msgs.info("A wavelength calibration will not be performed") self.wv_calib = None return self.wv_calib # Grab arc binning (may be different from science!) # TODO : Do this internally when we have a wv_calib DataContainer binspec, binspat = parse.parse_binning(self.msarc.detector.binning) # Instantiate self.waveCalib = wavecalib.WaveCalib( self.msarc, self.slits, self.spectrograph, self.par['wavelengths'], binspectral=binspec, det=self.det, master_key=self.master_key_dict['arc'], # For QA naming qa_path=self.qa_path, msbpm=self.msbpm) # Load from disk (MasterFrame)? masterframe_name = masterframe.construct_file_name( wavecalib.WaveCalib, self.master_key_dict['arc'], master_dir=self.master_dir) if os.path.isfile(masterframe_name) and self.reuse_masters: # Load from disk self.wv_calib = self.waveCalib.load(masterframe_name) self.slits.mask_wvcalib(self.wv_calib) else: self.wv_calib = self.waveCalib.run(skip_QA=(not self.write_qa)) # Save to Masters self.waveCalib.save(outfile=masterframe_name) # Return return self.wv_calib
def config_specific_par(self, scifile, inp_par=None): """ Modify the PypeIt parameters to hard-wired values used for specific instrument configurations. .. todo:: Document the changes made! Args: scifile (str): File to use when determining the configuration and how to adjust the input parameters. inp_par (:class:`pypeit.par.parset.ParSet`, optional): Parameter set used for the full run of PypeIt. If None, use :func:`default_pypeit_par`. Returns: :class:`pypeit.par.parset.ParSet`: The PypeIt paramter set adjusted for configuration specific parameter values. """ # Start with instrument wide par = super(KeckLRISRSpectrograph, self).config_specific_par(scifile, inp_par=inp_par) # Lacosmic CR settings # Grab the defaults for LRISr binning = self.get_meta_value(scifile, 'binning') # Unbinned LRISr needs very aggressive LACosmics parameters for 1x1 binning if binning == '1,1': sigclip = 3.0 objlim = 0.5 par['scienceframe']['process']['sigclip'] = sigclip par['scienceframe']['process']['objlim'] = objlim # Wavelength calibrations if self.get_meta_value( scifile, 'dispname') == '400/8500': # This is basically a reidentify par['calibrations']['wavelengths'][ 'reid_arxiv'] = 'keck_lris_red_400.fits' par['calibrations']['wavelengths']['method'] = 'full_template' par['calibrations']['wavelengths']['sigdetect'] = 20.0 par['calibrations']['wavelengths']['nsnippet'] = 1 elif self.get_meta_value(scifile, 'dispname') == '600/5000': par['calibrations']['wavelengths'][ 'reid_arxiv'] = 'keck_lris_red_600_5000.fits' par['calibrations']['wavelengths']['method'] = 'full_template' elif self.get_meta_value(scifile, 'dispname') == '1200/9000': par['calibrations']['wavelengths'][ 'reid_arxiv'] = 'keck_lris_red_1200_9000.fits' par['calibrations']['wavelengths']['method'] = 'full_template' # FWHM binning = parse.parse_binning(self.get_meta_value(scifile, 'binning')) par['calibrations']['wavelengths']['fwhm'] = 8.0 / binning[0] # Return return par
def config_specific_par(self, scifile, inp_par=None): """ Modify the PypeIt parameters to hard-wired values used for specific instrument configurations. .. todo:: Document the changes made! Args: scifile (str): File to use when determining the configuration and how to adjust the input parameters. inp_par (:class:`pypeit.par.parset.ParSet`, optional): Parameter set used for the full run of PypeIt. If None, use :func:`default_pypeit_par`. Returns: :class:`pypeit.par.parset.ParSet`: The PypeIt paramter set adjusted for configuration specific parameter values. """ par = self.default_pypeit_par() if inp_par is None else inp_par headarr = self.get_headarr(scifile) # Turn PCA off for long slits # TODO: I'm a bit worried that this won't catch all # long-slits... if ('Long' in self.get_meta_value( headarr, 'decker')) or ('LVMslit' in self.get_meta_value( headarr, 'decker')): par['calibrations']['slitedges']['sync_predict'] = 'nearest' # Templates if self.get_meta_value(headarr, 'dispname') == '600ZD': par['calibrations']['wavelengths']['method'] = 'full_template' par['calibrations']['wavelengths'][ 'reid_arxiv'] = 'keck_deimos_600.fits' par['calibrations']['wavelengths']['lamps'] += [ 'CdI', 'ZnI', 'HgI' ] elif self.get_meta_value(headarr, 'dispname') == '830G': par['calibrations']['wavelengths']['method'] = 'full_template' par['calibrations']['wavelengths'][ 'reid_arxiv'] = 'keck_deimos_830G.fits' elif self.get_meta_value(headarr, 'dispname') == '1200G': par['calibrations']['wavelengths']['method'] = 'full_template' par['calibrations']['wavelengths'][ 'reid_arxiv'] = 'keck_deimos_1200G.fits' # FWHM binning = parse.parse_binning(self.get_meta_value(headarr, 'binning')) par['calibrations']['wavelengths']['fwhm'] = 6.0 / binning[1] # Return return par
def config_specific_par(self, scifile, inp_par=None): """ Modify the PypeIt parameters to hard-wired values used for specific instrument configurations. .. todo:: Document the changes made! Args: scifile (str): File to use when determining the configuration and how to adjust the input parameters. inp_par (:class:`pypeit.par.parset.ParSet`, optional): Parameter set used for the full run of PypeIt. If None, use :func:`default_pypeit_par`. Returns: :class:`pypeit.par.parset.ParSet`: The PypeIt paramter set adjusted for configuration specific parameter values. """ par = self.default_pypeit_par() if inp_par is None else inp_par # TODO: Should we allow the user to override these? # Wavelength calibrations if self.get_meta_value(scifile, 'dispname') == '300/5000': par['calibrations']['wavelengths']['reid_arxiv'] = 'keck_lris_blue_300_d680.fits' par['flexure']['spectrum'] = os.path.join(resource_filename('pypeit', 'data/sky_spec/'), 'sky_LRISb_400.fits') elif self.get_meta_value(scifile, 'dispname') == '400/3400': par['calibrations']['wavelengths']['reid_arxiv'] = 'keck_lris_blue_400_d560.fits' par['flexure']['spectrum'] = os.path.join(resource_filename('pypeit', 'data/sky_spec/'), 'sky_LRISb_400.fits') elif self.get_meta_value(scifile, 'dispname') == '600/4000': par['calibrations']['wavelengths']['reid_arxiv'] = 'keck_lris_blue_600_d560.fits' par['flexure']['spectrum'] = os.path.join(resource_filename('pypeit', 'data/sky_spec/'), 'sky_LRISb_600.fits') elif self.get_meta_value(scifile, 'dispname') == '1200/3400': par['calibrations']['wavelengths']['reid_arxiv'] = 'keck_lris_blue_1200_d460.fits' par['flexure']['spectrum'] = os.path.join(resource_filename('pypeit', 'data/sky_spec/'), 'sky_LRISb_600.fits') # FWHM binning = parse.parse_binning(self.get_meta_value(scifile, 'binning')) par['calibrations']['wavelengths']['fwhm'] = 8.0 / binning[0] # Slit tracing # Reduce the slit parameters because the flux does not span the full detector # It is primarily on the upper half of the detector (usually) if self.get_meta_value(scifile, 'dispname') == '300/5000': par['calibrations']['slits']['mask_frac_thresh'] = 0.45 par['calibrations']['slits']['smash_range'] = [0.5, 1.] # Return return par
def _fill_tslits_dict(self): """ Build a simple dictionary holding the key trace bits and pieces that PypeIt wants Returns: dict: Trace slits dict """ self.tslits_dict = {} # Have the slit boundaries been tweaked? If so use the tweaked # boundaries. TODO: Have the dict keys have the same name as # the attribute self.tslits_dict['slit_left_orig'] = self.slit_left self.tslits_dict['slit_righ_orig'] = self.slit_righ if self.slit_left_tweak is not None: self.tslits_dict['slit_left_tweak'] = self.slit_left_tweak self.tslits_dict['slit_righ_tweak'] = self.slit_righ_tweak self.tslits_dict['slit_left'] = self.slit_left_tweak self.tslits_dict['slit_righ'] = self.slit_righ_tweak else: self.tslits_dict['slit_left'] = self.slit_left self.tslits_dict['slit_righ'] = self.slit_righ # Fill in the rest of the keys that were generated by # make_pixel_arrays from the slit boundaries. This was done with # tweaked boundaries if they exist. TODO: Some of these # quantities may be deprecated. #for key in ['slitcen', 'pixwid', 'lordpix','rordpix', 'extrapord']: # self.tslits_dict[key] = getattr(self, key) # add in the image size and some stuff to create the slitmask self.tslits_dict['maskslits'] = self.maskslits self.tslits_dict['slitcen'] = self.slitcen self.tslits_dict['nspec'] = self.mstrace.shape[0] self.tslits_dict['nspat'] = self.mstrace.shape[1] self.tslits_dict['nslits'] = self.slit_left.shape[1] self.tslits_dict['pad'] = self.par['pad'] binspectral, binspatial = parse.parse_binning(self.binning) self.tslits_dict['binspectral'] = binspectral self.tslits_dict['binspatial'] = binspatial self.tslits_dict['spectrograph'] = self.spectrograph.spectrograph slit_spat_pos = trace_slits.slit_spat_pos(self.tslits_dict) spec_min_max = self.spectrograph.slit_minmax(slit_spat_pos, binspectral = binspectral) self.tslits_dict['spec_min'], self.tslits_dict['spec_max'] = spec_min_max[0, :], spec_min_max[1, :] # Now extrapolate the traces JFH turning this off for now. #self.tslits_dict['slit_left'] = trace_slits.extrapolate_trace(self.tslits_dict['slit_left'], spec_min_max) #self.tslits_dict['slit_righ'] = trace_slits.extrapolate_trace(self.tslits_dict['slit_righ'], spec_min_max) return self.tslits_dict
def config_specific_par(self, par, scifile): """ Set par values according to the specific frame Args: par: ParSet scifile: str Name of the science file to use Returns: par """ # Wavelength calibrations if self.get_meta_value(scifile, 'dispname') == '300/5000': par['calibrations']['wavelengths'][ 'reid_arxiv'] = 'keck_lris_blue_300_d680.fits' par['flexure']['spectrum'] = os.path.join( resource_filename('pypeit', 'data/sky_spec/'), 'sky_LRISb_400.fits') elif self.get_meta_value(scifile, 'dispname') == '400/3400': par['calibrations']['wavelengths'][ 'reid_arxiv'] = 'keck_lris_blue_400_d560.fits' par['flexure']['spectrum'] = os.path.join( resource_filename('pypeit', 'data/sky_spec/'), 'sky_LRISb_400.fits') elif self.get_meta_value(scifile, 'dispname') == '600/4000': par['calibrations']['wavelengths'][ 'reid_arxiv'] = 'keck_lris_blue_600_d560.fits' par['flexure']['spectrum'] = os.path.join( resource_filename('pypeit', 'data/sky_spec/'), 'sky_LRISb_600.fits') elif self.get_meta_value(scifile, 'dispname') == '1200/3400': par['calibrations']['wavelengths'][ 'reid_arxiv'] = 'keck_lris_blue_1200_d460.fits' par['flexure']['spectrum'] = os.path.join( resource_filename('pypeit', 'data/sky_spec/'), 'sky_LRISb_600.fits') # FWHM binning = parse.parse_binning(self.get_meta_value(scifile, 'binning')) par['calibrations']['wavelengths']['fwhm'] = 8.0 / binning[0] # Slit tracing # Reduce the slit parameters because the flux does not span the full detector # It is primarily on the upper half of the detector (usually) if self.get_meta_value(scifile, 'dispname') == '300/5000': par['calibrations']['slits']['mask_frac_thresh'] = 0.45 par['calibrations']['slits']['smash_range'] = [0.5, 1.] # Return return par
def slitmask(self, tslits_dict, pad=None): """ Generic routine ton construct a slitmask image from a tslits_dict. Children of this class can overload this function to implement instrument specific slitmask behavior, for example setting where the orders on an echelle spectrograph end Parameters ----------- tslits_dict: dict Trace slits dictionary with slit boundary information Optional Parameters pad: int or float Padding of the slit boundaries binning: tuple Spectrograph binning in spectral and spatial directions Returns ------- slitmask: ndarray int Image with -1 where there are no slits/orders, and an integer where there are slits/order with the integer indicating the slit number going from 0 to nslit-1 from left to right. """ # These lines are always the same pad = tslits_dict['pad'] if pad is None else pad nslits = tslits_dict['lcen'].shape[1] if nslits != self.norders: msgs.error('Not all the orders were identified!') slitmask = pixels.slit_pixels(tslits_dict['lcen'], tslits_dict['rcen'], tslits_dict['nspat'], pad=pad) spec_img = np.outer( np.arange(tslits_dict['nspec'], dtype=int), np.ones(tslits_dict['nspat'], dtype=int)) # spectral position everywhere along image binning = tslits_dict['binning'] binspectral, binspatial = parse.parse_binning(binning) # These are the order boundaries determined by eye by JFH. order_max = np.asarray([4000] * 14 + [3000]) // binspectral order_min = np.asarray([2000, 1000] + [0] * 13) // binspectral # TODO add binning adjustments to these for iorder in range(self.norders): orderbad = (slitmask == iorder) & ((spec_img < order_min[iorder]) | (spec_img > order_max[iorder])) slitmask[orderbad] = -1 return slitmask
def compound_meta(self, headarr, meta_key): """ Args: headarr: list meta_key: str Returns: value """ if meta_key == 'binning': binspatial, binspec = parse.parse_binning(headarr[0]['BINNING']) return parse.binning2string(binspec, binspatial)
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(headarr[0]['BINNING']) binning = parse.binning2string(binspec, binspatial) return binning elif meta_key == 'slitwid': # Get the slice scale slicescale = 0.00037718 # Degrees per 'large slicer' slice ifunum = headarr[0]['IFUNUM'] if ifunum == 2: slicescale /= 2.0 elif ifunum == 3: slicescale /= 4.0 return slicescale elif meta_key == 'ra' or meta_key == 'dec': try: if self.is_nasmask(headarr[0]): hdrstr = 'RABASE' if meta_key == 'ra' else 'DECBASE' else: hdrstr = 'RA' if meta_key == 'ra' else 'DEC' except KeyError: try: hdrstr = 'TARGRA' if meta_key == 'ra' else 'TARGDEC' except KeyError: hdrstr = '' return headarr[0][hdrstr] elif meta_key == 'pressure': return headarr[0]['WXPRESS'] * 0.001 * units.bar elif meta_key == 'temperature': return headarr[0]['WXOUTTMP'] * units.deg_C elif meta_key == 'humidity': return headarr[0]['WXOUTHUM'] / 100.0 elif meta_key == 'obstime': return Time(headarr[0]['DATE-END']) else: msgs.error("Not ready for this compound meta")
def order_platescale(self, order_vec, binning=None): """ Returns the plate scale in arcseconds for each order Args: order_vec (np.ndarray): Order numbers binning (optional): Returns: np.ndarray: Platescale """ norders = len(order_vec) binspatial, binspec = parse.parse_binning(binning) return np.full(norders, 0.30 * binspatial)
def compound_meta(self, headarr, meta_key): """ Args: headarr: list meta_key: str Returns: value """ 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 config_specific_par(self, par, scifile): """ Set par values according to the specific frame Args: par: ParSet scifile: str Name of the science file to use Returns: par """ # Lacosmic CR settings # Grab the defaults for LRISr binning = self.get_meta_value(scifile, 'binning') # Unbinned LRISr needs very aggressive LACosmics parameters for 1x1 binning if binning == '1,1': sigclip = 3.0 objlim = 0.5 par['scienceframe']['process']['sigclip'] = sigclip par['scienceframe']['process']['objlim'] = objlim # Wavelength calibrations if self.get_meta_value( scifile, 'dispname') == '400/8500': # This is basically a reidentify par['calibrations']['wavelengths'][ 'reid_arxiv'] = 'keck_lris_red_400.fits' par['calibrations']['wavelengths']['method'] = 'full_template' par['calibrations']['wavelengths']['sigdetect'] = 20.0 par['calibrations']['wavelengths']['nsnippet'] = 1 elif self.get_meta_value(scifile, 'dispname') == '600/5000': par['calibrations']['wavelengths'][ 'reid_arxiv'] = 'keck_lris_red_600_5000.fits' par['calibrations']['wavelengths']['method'] = 'full_template' elif self.get_meta_value(scifile, 'dispname') == '1200/9000': par['calibrations']['wavelengths'][ 'reid_arxiv'] = 'keck_lris_red_1200_9000.fits' par['calibrations']['wavelengths']['method'] = 'full_template' # FWHM binning = parse.parse_binning(self.get_meta_value(scifile, 'binning')) par['calibrations']['wavelengths']['fwhm'] = 8.0 / binning[0] # Return return par
def config_specific_par(self, scifile, inp_par=None): """ Modify the PypeIt parameters to hard-wired values used for specific instrument configurations. .. todo:: Document the changes made! Args: scifile (str): File to use when determining the configuration and how to adjust the input parameters. inp_par (:class:`pypeit.par.parset.ParSet`, optional): Parameter set used for the full run of PypeIt. If None, use :func:`default_pypeit_par`. Returns: :class:`pypeit.par.parset.ParSet`: The PypeIt paramter set adjusted for configuration specific parameter values. """ par = self.default_pypeit_par() if inp_par is None else inp_par # TODO: Should we allow the user to override these? # Templates if self.get_meta_value(scifile, 'dispname') == '600ZD': par['calibrations']['wavelengths']['method'] = 'full_template' par['calibrations']['wavelengths'][ 'reid_arxiv'] = 'keck_deimos_600.fits' par['calibrations']['wavelengths']['lamps'] += [ 'CdI', 'ZnI', 'HgI' ] elif self.get_meta_value(scifile, 'dispname') == '830G': par['calibrations']['wavelengths']['method'] = 'full_template' par['calibrations']['wavelengths'][ 'reid_arxiv'] = 'keck_deimos_830G.fits' elif self.get_meta_value(scifile, 'dispname') == '1200G': par['calibrations']['wavelengths']['method'] = 'full_template' par['calibrations']['wavelengths'][ 'reid_arxiv'] = 'keck_deimos_1200G.fits' # FWHM binning = parse.parse_binning(self.get_meta_value(scifile, 'binning')) par['calibrations']['wavelengths']['fwhm'] = 6.0 / binning[1] # Return 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 == 'binning': binspatial, binspec = parse.parse_binning(headarr[0]['CCDSUM']) binning = parse.binning2string(binspec, binspatial) return binning
def compound_meta(self, headarr, meta_key): """ Args: headarr: list meta_key: str Returns: value """ if meta_key == 'binning': binspatial, binspec = parse.parse_binning(headarr[0]['BINNING']) return parse.binning2string(binspec, binspatial) elif meta_key == 'mjd': time = '{:s}T{:s}'.format(headarr[0]['UT-DATE'], headarr[0]['UT-TIME']) ttime = Time(time, format='isot') return ttime.mjd else: msgs.error("Not ready for this compound meta")
def order_platescale(self, order_vec, binning=None): """ Return the platescale for each echelle order. This routine is only defined for echelle spectrographs, and it is undefined in the base class. Args: order_vec (`numpy.ndarray`_): The vector providing the order numbers. binning (:obj:`str`, optional): The string defining the spectral and spatial binning. Returns: `numpy.ndarray`_: An array with the platescale for each order provided by ``order``. """ norders = len(order_vec) binspatial, binspec = parse.parse_binning(binning) return np.full(norders, 0.30 * binspatial)
def wavegrid(self, binning=None, midpoint=False,samp_fact=1.0): """ Routine to generate a fixed wavelength grid in log_10 lambda. Mostly used by echelle spectrographs Args: binning: midpoint: samp_fact: Returns: """ binspectral, binspatial = parse.parse_binning(binning) logmin, logmax = self.loglam_minmax loglam_grid = wvutils.wavegrid(logmin, logmax, self.dloglam*binspectral, samp_fact=samp_fact) if midpoint: loglam_grid = loglam_grid + self.dloglam*binspectral/samp_fact/2.0 return np.power(10.0,loglam_grid)
def bpm(self, filename, det, shape=None, msbias=None): """ Generate a default bad-pixel mask. Even though they are both optional, either the precise shape for the image (``shape``) or an example file that can be read to get the shape (``filename`` using :func:`get_image_shape`) *must* be provided. Args: filename (:obj:`str` or None): An example file to use to get the image shape. det (:obj:`int`): 1-indexed detector number to use when getting the image shape from the example file. shape (tuple, optional): Processed image shape Required if filename is None Ignored if filename is not None msbias (`numpy.ndarray`_, optional): Master bias frame used to identify bad pixels Returns: `numpy.ndarray`_: An integer array with a masked value set to 1 and an unmasked value set to 0. All values are set to 0. """ # Call the base-class method to generate the empty bpm bpm_img = super().bpm(filename, det, shape=shape, msbias=msbias) # Get the binning msgs.info("Custom bad pixel mask for MAGE") hdu = io.fits_open(filename) binspatial, binspec = parse.parse_binning(hdu[0].header['BINNING']) hdu.close() # Do it bpm_img[:, :10 // binspatial] = 1. # Setting BPM on the edge of the detector often leads to false edges bpm_img[:, 1020 // binspatial:] = 1. # Return return bpm_img