From 4356b2437eedad4d2341d02e80f52ff81ad56ee6 Mon Sep 17 00:00:00 2001 From: rcooke Date: Fri, 28 Apr 2023 18:02:43 +0100 Subject: [PATCH 01/27] getting started --- pypeit/spectrographs/gemini_gnirs.py | 452 +++++++++++++++++++++++++-- 1 file changed, 430 insertions(+), 22 deletions(-) diff --git a/pypeit/spectrographs/gemini_gnirs.py b/pypeit/spectrographs/gemini_gnirs.py index 556da625d7..1e7743dbbb 100644 --- a/pypeit/spectrographs/gemini_gnirs.py +++ b/pypeit/spectrographs/gemini_gnirs.py @@ -87,7 +87,6 @@ def default_pypeit_par(cls): par['reduce']['findobj']['find_trim_edge'] = [2,2] # Slit is too short to trim 5,5 especially par['reduce']['skysub']['bspline_spacing'] = 0.8 par['reduce']['skysub']['global_sky_std'] = False # Do not perform global sky subtraction for standard stars - # TODO: JFH: Is this the correct behavior? (Is why we have sky-subtraction problems for GNIRS?) par['reduce']['skysub']['no_poly'] = True # Do not use polynomial degree of freedom for global skysub 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 @@ -128,7 +127,7 @@ def config_specific_par(self, scifile, inp_par=None): adjusted for configuration specific parameter values. """ par = super().config_specific_par(scifile, inp_par=inp_par) - + par['calibrations']['wavelengths']['echelle'] = True # TODO This is a hack for now until we figure out how to set dispname # and other meta information in the spectrograph class itself self.dispname = self.get_meta_value(scifile, 'dispname') @@ -158,6 +157,8 @@ def config_specific_par(self, scifile, inp_par=None): # par['calibrations']['wavelengths']['ech_fix_format'] = True # Echelle parameters # JFH This is provisional these IDs should be checked. + # TODO :: Should probably make a primary GNIRS spectrograph class + # together with separate Echelle and IFU spectrographs children. par['calibrations']['wavelengths']['echelle'] = True par['calibrations']['wavelengths']['ech_nspec_coeff'] = 3 par['calibrations']['wavelengths']['ech_norder_coeff'] = 5 @@ -255,6 +256,30 @@ def compound_meta(self, headarr, meta_key): return headarr[0].get('QOFFSET') else: return 0.0 + elif meta_key == 'obstime': + try: + return headarr[0]['TIME-OBS'] + except KeyError: + msgs.warn("Time of observation is not in header") + return 0.0 + elif meta_key == 'pressure': + try: + return headarr[0]['PRESSURE'] * 0.001 # Must be in astropy.units.bar + except KeyError: + msgs.warn("Pressure is not in header") + return 0.0 + elif meta_key == 'temperature': + try: + return headarr[0]['TAMBIENT'] # Must be in astropy.units.deg_C + except KeyError: + msgs.warn("Temperature is not in header") + return 0.0 + elif meta_key == 'humidity': + try: + return headarr[0]['HUMIDITY'] + except KeyError: + msgs.warn("Humidity is not in header") + return 0.0 else: msgs.error("Not ready for this compound meta") @@ -274,26 +299,6 @@ def configuration_keys(self): """ return ['decker', 'dispname', 'dispangle'] - def raw_header_cards(self): - """ - Return additional raw header cards to be propagated in - downstream output files for configuration identification. - - The list of raw data FITS keywords should be those used to populate - the :meth:`~pypeit.spectrograph.Spectrograph.configuration_keys` - or are used in :meth:`~pypeit.spectrograph.Spectrograph.config_specific_par` - for a particular spectrograph, if different from the name of the - PypeIt metadata keyword. - - This list is used by :meth:`~pypeit.spectrograph.Spectrograph.subheader_for_spec` - to include additional FITS keywords in downstream output files. - - Returns: - :obj:`list`: List of keywords from the raw data files that should - be propagated in output files. - """ - return ['SLIT', 'GRATING', 'GRATTILT'] - def pypeit_file_keys(self): """ Define the list of keys to be output into a standard ``PypeIt`` file. @@ -470,5 +475,408 @@ def bpm(self, filename, det, shape=None, msbias=None): return bpm_img +class GNIRSHRIFUSpectrograph(GeminiGNIRSSpectrograph): + pypeline = 'IFU' + name = 'gemini_gnirs_hrifu' + ech_fixed_format = False + + def init_meta(self): + super().init_meta() + self.meta['obstime'] = dict(card=None, compound=True, required=False) + self.meta['pressure'] = dict(card=None, compound=True, required=False) + self.meta['temperature'] = dict(card=None, compound=True, required=False) + self.meta['humidity'] = dict(card=None, compound=True, required=False) + + @classmethod + def default_pypeit_par(cls): + par = super().default_pypeit_par() + + # LACosmics parameters + par['scienceframe']['process']['sigclip'] = 4.0 + par['scienceframe']['process']['objlim'] = 1.5 + par['scienceframe']['process']['use_illumflat'] = False # illumflat is applied when building the relative scale image in reduce.py, so should be applied to scienceframe too. + par['scienceframe']['process']['use_specillum'] = False # apply relative spectral illumination + par['scienceframe']['process']['spat_flexure_correct'] = False # don't correct for spatial flexure - varying spatial illumination profile could throw this correction off. Also, there's no way to do astrometric correction if we can't correct for spatial flexure of the contbars frames + par['scienceframe']['process']['use_biasimage'] = False + par['scienceframe']['process']['use_darkimage'] = False + par['calibrations']['flatfield']['slit_illum_finecorr'] = False + # Don't do 1D extraction for 3D data - it's meaningless because the DAR correction must be performed on the 3D data. + par['reduce']['extraction']['skip_extraction'] = True # Because extraction occurs before the DAR correction, don't extract + + # Decrease the wave tilts order, given the shorter slits of the IFU + par['calibrations']['tilts']['spat_order'] = 1 + par['calibrations']['tilts']['spec_order'] = 1 + + # Make sure that this is reduced as a slit (as opposed to fiber) spectrograph + par['reduce']['cube']['slit_spec'] = True + par['reduce']['cube']['combine'] = False # Make separate spec3d files from the input spec2d files + + # Sky subtraction parameters + par['reduce']['skysub']['no_poly'] = True + par['reduce']['skysub']['bspline_spacing'] = 0.6 + par['reduce']['skysub']['joint_fit'] = False + par['reduce']['findobj']['skip_skysub'] = True + par['reduce']['findobj']['skip_final_global'] = True + + # Don't correct flexure by default, but you should use slitcen, + # because this is a slit-based IFU where no objects are extracted. + par['flexure']['spec_method'] = 'skip' + par['flexure']['spec_maxshift'] = 2.5 # Just in case someone switches on spectral flexure, this needs to be minimal + + # Flux calibration parameters + par['sensfunc']['UVIS']['extinct_correct'] = False # This must be False - the extinction correction is performed when making the datacube + + return par + + def get_wcs(self, hdr, slits, platescale, wave0, dwv, spatial_scale=None): + """ + Construct/Read a World-Coordinate System for a frame. + + Args: + hdr (`astropy.io.fits.Header`_): + The header of the raw frame. The information in this + header will be extracted and returned as a WCS. + slits (:class:`~pypeit.slittrace.SlitTraceSet`): + Slit traces. + platescale (:obj:`float`): + The platescale of an unbinned pixel in arcsec/pixel (e.g. + detector.platescale). + wave0 (:obj:`float`): + The wavelength zeropoint. + dwv (:obj:`float`): + Change in wavelength per spectral pixel. + + Returns: + `astropy.wcs.wcs.WCS`_: The world-coordinate system. + """ + msgs.info("Calculating the WCS") + # Get the x and y binning factors, and the typical slit length + binspec, binspat = parse.parse_binning(self.get_meta_value([hdr], 'binning')) + + # Get the pixel and slice scales + pxscl = platescale * binspat / 3600.0 # Need to convert arcsec to degrees + slscl = self.get_meta_value([hdr], 'slitwid') + if spatial_scale is not None: + if pxscl > spatial_scale / 3600.0: + msgs.warn("Spatial scale requested ({0:f}'') is less than the pixel scale ({1:f}'')".format(spatial_scale, pxscl*3600.0)) + # Update the pixel scale + pxscl = spatial_scale / 3600.0 # 3600 is to convert arcsec to degrees + + # Get the typical slit length (this changes by ~0.3% over all slits, so a constant is fine for now) + slitlength = int(np.round(np.median(slits.get_slitlengths(initial=True, median=True)))) + + # Get RA/DEC + raval = self.get_meta_value([hdr], 'ra') + decval = self.get_meta_value([hdr], 'dec') + + # Create a coordinate + coord = SkyCoord(raval, decval, unit=(units.deg, units.deg)) + + # Get rotator position + msgs.warn("HACK FOR HRIFU SIMS --- NEED TO FIGURE OUT RPOS and RREF FOR HRIFU FROM HEADER INFO") + if 'ROTPOSN' in hdr: + rpos = hdr['ROTPOSN'] + else: + rpos = 0. + if 'ROTREFAN' in hdr: + rref = hdr['ROTREFAN'] + else: + rref = 0. + # Get the offset and PA + rotoff = 0.0 # IFU-SKYPA offset (degrees) + skypa = rpos + rref # IFU position angle (degrees) + crota = np.radians(-(skypa + rotoff)) + + # Calculate the fits coordinates + cdelt1 = -slscl + cdelt2 = pxscl + if coord is None: + ra = 0. + dec = 0. + crota = 1 + else: + ra = coord.ra.degree + dec = coord.dec.degree + # Calculate the CD Matrix + cd11 = cdelt1 * np.cos(crota) # RA degrees per column + cd12 = abs(cdelt2) * np.sign(cdelt1) * np.sin(crota) # RA degrees per row + cd21 = -abs(cdelt1) * np.sign(cdelt2) * np.sin(crota) # DEC degress per column + cd22 = cdelt2 * np.cos(crota) # DEC degrees per row + # Get reference pixels (set these to the middle of the FOV) + crpix1 = 13 # i.e. see get_datacube_bins (13 is used as the reference point - somewhere in the middle of the FOV) + crpix2 = slitlength / 2. + crpix3 = 1. + # Get the offset + msgs.warn("HACK FOR HRIFU SIMS --- Need to obtain offset from header?") + off1 = 0. + off2 = 0. + off1 /= binspec + off2 /= binspat + crpix1 += off1 + crpix2 += off2 + + # Create a new WCS object. + msgs.info("Generating HRIFU WCS") + w = wcs.WCS(naxis=3) + w.wcs.equinox = hdr['EQUINOX'] + w.wcs.name = 'HRIFU' + w.wcs.radesys = 'FK5' + # Insert the coordinate frame + w.wcs.cname = ['HRIFU RA', 'HRIFU DEC', 'HRIFU Wavelength'] + w.wcs.cunit = [units.degree, units.degree, units.Angstrom] + w.wcs.ctype = ["RA---TAN", "DEC--TAN", "WAVE"] + w.wcs.crval = [ra, dec, wave0] # RA, DEC, and wavelength zeropoints + w.wcs.crpix = [crpix1, crpix2, crpix3] # RA, DEC, and wavelength reference pixels + w.wcs.cd = np.array([[cd11, cd12, 0.0], [cd21, cd22, 0.0], [0.0, 0.0, dwv]]) + w.wcs.lonpole = 180.0 # Native longitude of the Celestial pole + w.wcs.latpole = 0.0 # Native latitude of the Celestial pole + + return w + + def get_datacube_bins(self, slitlength, minmax, num_wave): + r""" + Calculate the bin edges to be used when making a datacube. + + Args: + slitlength (:obj:`int`): + Length of the slit in pixels + minmax (`numpy.ndarray`_): + An array with the minimum and maximum pixel locations on each + slit relative to the reference location (usually the centre + of the slit). Shape must be :math:`(N_{\rm slits},2)`, and is + typically the array returned by + :func:`~pypeit.slittrace.SlitTraceSet.get_radec_image`. + num_wave (:obj:`int`): + Number of wavelength steps. Given by:: + int(round((wavemax-wavemin)/delta_wave)) + + Args: + :obj:`tuple`: Three 1D `numpy.ndarray`_ providing the bins to use + when constructing a histogram of the spec2d files. The elements + are :math:`(x,y,\lambda)`. + """ + xbins = np.arange(1 + 25) - 13.0 - 0.5 # 25 is for 25 slices, and 13 is the reference slit + ybins = np.linspace(np.min(minmax[:, 0]), np.max(minmax[:, 1]), 1+slitlength) - 0.5 + spec_bins = np.arange(1+num_wave) - 0.5 + return xbins, ybins, spec_bins + + +class GNIRSLRIFUSpectrograph(GeminiGNIRSSpectrograph): + pypeline = 'IFU' + name = 'gemini_gnirs_lrifu' + ech_fixed_format = False + + def init_meta(self): + super().init_meta() + self.meta['obstime'] = dict(card=None, compound=True, required=False) + self.meta['pressure'] = dict(card=None, compound=True, required=False) + self.meta['temperature'] = dict(card=None, compound=True, required=False) + self.meta['humidity'] = dict(card=None, compound=True, required=False) + + @classmethod + def default_pypeit_par(cls): + par = super().default_pypeit_par() + + # LACosmics parameters + par['scienceframe']['process']['sigclip'] = 4.0 + par['scienceframe']['process']['objlim'] = 1.5 + par['scienceframe']['process']['use_illumflat'] = False # illumflat is applied when building the relative scale image in reduce.py, so should be applied to scienceframe too. + par['scienceframe']['process']['use_specillum'] = False # apply relative spectral illumination + par['scienceframe']['process']['spat_flexure_correct'] = False # don't correct for spatial flexure - varying spatial illumination profile could throw this correction off. Also, there's no way to do astrometric correction if we can't correct for spatial flexure of the contbars frames + par['scienceframe']['process']['use_biasimage'] = False + par['scienceframe']['process']['use_darkimage'] = False + par['calibrations']['flatfield']['slit_illum_finecorr'] = False + # Don't do 1D extraction for 3D data - it's meaningless because the DAR correction must be performed on the 3D data. + par['reduce']['extraction']['skip_extraction'] = True # Because extraction occurs before the DAR correction, don't extract + + # Decrease the wave tilts order, given the shorter slits of the IFU + par['calibrations']['tilts']['spat_order'] = 1 + par['calibrations']['tilts']['spec_order'] = 1 + + # Make sure that this is reduced as a slit (as opposed to fiber) spectrograph + par['reduce']['cube']['slit_spec'] = True + par['reduce']['cube']['combine'] = False # Make separate spec3d files from the input spec2d files + + # Sky subtraction parameters + par['reduce']['skysub']['no_poly'] = True + par['reduce']['skysub']['bspline_spacing'] = 0.6 + par['reduce']['skysub']['joint_fit'] = False + par['reduce']['findobj']['skip_skysub'] = True + par['reduce']['findobj']['skip_final_global'] = True + + # Don't correct flexure by default, but you should use slitcen, + # because this is a slit-based IFU where no objects are extracted. + par['flexure']['spec_method'] = 'skip' + par['flexure']['spec_maxshift'] = 2.5 # Just in case someone switches on spectral flexure, this needs to be minimal + + # Flux calibration parameters + par['sensfunc']['UVIS']['extinct_correct'] = False # This must be False - the extinction correction is performed when making the datacube + + return par + + 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) + par['calibrations']['wavelengths']['echelle'] = False + return par + 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 :: Think about this... do we even need these extra file keys if skysub is done during the reduction + #pypeit_keys += ['calib', 'comb_id', 'bkg_id'] + return pypeit_keys + + def get_wcs(self, hdr, slits, platescale, wave0, dwv, spatial_scale=None): + """ + Construct/Read a World-Coordinate System for a frame. + + Args: + hdr (`astropy.io.fits.Header`_): + The header of the raw frame. The information in this + header will be extracted and returned as a WCS. + slits (:class:`~pypeit.slittrace.SlitTraceSet`): + Slit traces. + platescale (:obj:`float`): + The platescale of an unbinned pixel in arcsec/pixel (e.g. + detector.platescale). + wave0 (:obj:`float`): + The wavelength zeropoint. + dwv (:obj:`float`): + Change in wavelength per spectral pixel. + + Returns: + `astropy.wcs.wcs.WCS`_: The world-coordinate system. + """ + msgs.info("Calculating the WCS") + # Get the x and y binning factors, and the typical slit length + binspec, binspat = parse.parse_binning(self.get_meta_value([hdr], 'binning')) + + # Get the pixel and slice scales + pxscl = platescale * binspat / 3600.0 # Need to convert arcsec to degrees + slscl = self.get_meta_value([hdr], 'slitwid') + if spatial_scale is not None: + if pxscl > spatial_scale / 3600.0: + msgs.warn("Spatial scale requested ({0:f}'') is less than the pixel scale ({1:f}'')".format(spatial_scale, pxscl*3600.0)) + # Update the pixel scale + pxscl = spatial_scale / 3600.0 # 3600 is to convert arcsec to degrees + + # Get the typical slit length (this changes by ~0.3% over all slits, so a constant is fine for now) + slitlength = int(np.round(np.median(slits.get_slitlengths(initial=True, median=True)))) + + # Get RA/DEC + raval = self.get_meta_value([hdr], 'ra') + decval = self.get_meta_value([hdr], 'dec') + + # Create a coordinate + coord = SkyCoord(raval, decval, unit=(units.deg, units.deg)) + + # Get rotator position + msgs.warn("HACK FOR HRIFU SIMS --- NEED TO FIGURE OUT RPOS and RREF FOR HRIFU FROM HEADER INFO") + if 'ROTPOSN' in hdr: + rpos = hdr['ROTPOSN'] + else: + rpos = 0. + if 'ROTREFAN' in hdr: + rref = hdr['ROTREFAN'] + else: + rref = 0. + # Get the offset and PA + rotoff = 0.0 # IFU-SKYPA offset (degrees) + skypa = rpos + rref # IFU position angle (degrees) + crota = np.radians(-(skypa + rotoff)) + + # Calculate the fits coordinates + cdelt1 = -slscl + cdelt2 = pxscl + if coord is None: + ra = 0. + dec = 0. + crota = 1 + else: + ra = coord.ra.degree + dec = coord.dec.degree + # Calculate the CD Matrix + cd11 = cdelt1 * np.cos(crota) # RA degrees per column + cd12 = abs(cdelt2) * np.sign(cdelt1) * np.sin(crota) # RA degrees per row + cd21 = -abs(cdelt1) * np.sign(cdelt2) * np.sin(crota) # DEC degress per column + cd22 = cdelt2 * np.cos(crota) # DEC degrees per row + # Get reference pixels (set these to the middle of the FOV) + crpix1 = 11 # i.e. see get_datacube_bins (11 is used as the reference point - somewhere in the middle of the FOV) + crpix2 = slitlength / 2. + crpix3 = 1. + # Get the offset + msgs.warn("HACK FOR HRIFU SIMS --- Need to obtain offset from header?") + off1 = 0. + off2 = 0. + off1 /= binspec + off2 /= binspat + crpix1 += off1 + crpix2 += off2 + + # Create a new WCS object. + msgs.info("Generating HRIFU WCS") + w = wcs.WCS(naxis=3) + w.wcs.equinox = hdr['EQUINOX'] + w.wcs.name = 'HRIFU' + w.wcs.radesys = 'FK5' + # Insert the coordinate frame + w.wcs.cname = ['HRIFU RA', 'HRIFU DEC', 'HRIFU Wavelength'] + w.wcs.cunit = [units.degree, units.degree, units.Angstrom] + w.wcs.ctype = ["RA---TAN", "DEC--TAN", "WAVE"] + w.wcs.crval = [ra, dec, wave0] # RA, DEC, and wavelength zeropoints + w.wcs.crpix = [crpix1, crpix2, crpix3] # RA, DEC, and wavelength reference pixels + w.wcs.cd = np.array([[cd11, cd12, 0.0], [cd21, cd22, 0.0], [0.0, 0.0, dwv]]) + w.wcs.lonpole = 180.0 # Native longitude of the Celestial pole + w.wcs.latpole = 0.0 # Native latitude of the Celestial pole + + return w + + def get_datacube_bins(self, slitlength, minmax, num_wave): + r""" + Calculate the bin edges to be used when making a datacube. + + Args: + slitlength (:obj:`int`): + Length of the slit in pixels + minmax (`numpy.ndarray`_): + An array with the minimum and maximum pixel locations on each + slit relative to the reference location (usually the centre + of the slit). Shape must be :math:`(N_{\rm slits},2)`, and is + typically the array returned by + :func:`~pypeit.slittrace.SlitTraceSet.get_radec_image`. + num_wave (:obj:`int`): + Number of wavelength steps. Given by:: + int(round((wavemax-wavemin)/delta_wave)) + + Args: + :obj:`tuple`: Three 1D `numpy.ndarray`_ providing the bins to use + when constructing a histogram of the spec2d files. The elements + are :math:`(x,y,\lambda)`. + """ + xbins = np.arange(1 + 21) - 11.0 - 0.5 # 21 is for 21 slices, and 11 is the reference slit + ybins = np.linspace(np.min(minmax[:, 0]), np.max(minmax[:, 1]), 1+slitlength) - 0.5 + spec_bins = np.arange(1+num_wave) - 0.5 + return xbins, ybins, spec_bins From 0687e533694ae435b7375e589c10b7a5dfee8a2b Mon Sep 17 00:00:00 2001 From: rcooke Date: Sat, 29 Apr 2023 14:01:27 +0100 Subject: [PATCH 02/27] split echelle & ifu --- pypeit/spectrographs/gemini_gnirs.py | 658 ++++++++++----------------- 1 file changed, 251 insertions(+), 407 deletions(-) diff --git a/pypeit/spectrographs/gemini_gnirs.py b/pypeit/spectrographs/gemini_gnirs.py index 1e7743dbbb..be13c6e239 100644 --- a/pypeit/spectrographs/gemini_gnirs.py +++ b/pypeit/spectrographs/gemini_gnirs.py @@ -17,14 +17,10 @@ class GeminiGNIRSSpectrograph(spectrograph.Spectrograph): Child to handle Gemini/GNIRS specific code """ ndet = 1 - name = 'gemini_gnirs' camera = 'GNIRS' url = 'https://www.gemini.edu/instrumentation/gnirs' header_name = 'GNIRS' telescope = telescopes.GeminiNTelescopePar() - pypeline = 'Echelle' - ech_fixed_format = True - supported = True def get_detector_par(self, det, hdu=None): """ @@ -62,151 +58,6 @@ def get_detector_par(self, det, hdu=None): ) return detector_container.DetectorContainer(**detector_dict) - @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() - - # Image processing steps - turn_off = dict(use_illumflat=False, use_biasimage=False, use_overscan=False, - use_darkimage=False) - par.reset_all_processimages_par(**turn_off) - - # Flats - par['calibrations']['flatfield']['tweak_slits_thresh'] = 0.90 - par['calibrations']['flatfield']['tweak_slits_maxfrac'] = 0.10 - - # Reduce parameters - #par['reduce']['findobj']['snr_thresh'] = 5.0 # Object finding threshold - par['reduce']['findobj']['find_trim_edge'] = [2,2] # Slit is too short to trim 5,5 especially - par['reduce']['skysub']['bspline_spacing'] = 0.8 - par['reduce']['skysub']['global_sky_std'] = False # Do not perform global sky subtraction for standard stars - par['reduce']['skysub']['no_poly'] = True # Do not use polynomial degree of freedom for global skysub - 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 - # Standards - par['calibrations']['standardframe']['process']['mask_cr'] = False # Do not mask_cr standards - - # Do not correct for flexure - par['flexure']['spec_method'] = 'skip' - - # Set the default exposure time ranges for the frame typing - par['calibrations']['pixelflatframe']['exprng'] = [None, 30] - par['calibrations']['traceframe']['exprng'] = [None, 30] - par['calibrations']['standardframe']['exprng'] = [None, 30] - par['scienceframe']['exprng'] = [30, None] - - # Sensitivity function parameters - par['sensfunc']['algorithm'] = 'IR' - par['sensfunc']['polyorder'] = 6 - par['sensfunc']['IR']['telgridfile'] = 'TelFit_MaunaKea_3100_26100_R20000.fits' - return par - - 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) - par['calibrations']['wavelengths']['echelle'] = True - # TODO This is a hack for now until we figure out how to set dispname - # and other meta information in the spectrograph class itself - self.dispname = self.get_meta_value(scifile, 'dispname') - # 32/mmSB_G5533 setup, covering XYJHK with short blue camera - if '32/mm' in self.dispname: - # Edges - par['calibrations']['slitedges']['edge_thresh'] = 20. - 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 - - # Wavelengths - par['calibrations']['wavelengths']['rms_threshold'] = 1.0 # Might be grating dependent.. - par['calibrations']['wavelengths']['sigdetect'] = [4.0, 5.0, 5.0, 5.0, 5.0, 5.0] #5.0 - par['calibrations']['wavelengths']['lamps'] = ['OH_GNIRS'] - #par['calibrations']['wavelengths']['nonlinear_counts'] = self.detector[0]['nonlinear'] * self.detector[0]['saturation'] - par['calibrations']['wavelengths']['n_first'] = 2 - par['calibrations']['wavelengths']['n_final'] = [1, 3, 3, 3, 3, 3] - - # Reidentification parameters - par['calibrations']['wavelengths']['method'] = 'reidentify' - par['calibrations']['wavelengths']['cc_thresh'] = 0.6 - par['calibrations']['wavelengths']['reid_arxiv'] = 'gemini_gnirs.fits' -# par['calibrations']['wavelengths']['ech_fix_format'] = True - # Echelle parameters - # JFH This is provisional these IDs should be checked. - # TODO :: Should probably make a primary GNIRS spectrograph class - # together with separate Echelle and IFU spectrographs children. - par['calibrations']['wavelengths']['echelle'] = True - par['calibrations']['wavelengths']['ech_nspec_coeff'] = 3 - par['calibrations']['wavelengths']['ech_norder_coeff'] = 5 - par['calibrations']['wavelengths']['ech_sigrej'] = 3.0 - - # Tilts - par['calibrations']['tilts']['tracethresh'] = [5.0, 10, 10, 10, 10, 10] - par['calibrations']['tilts']['sig_neigh'] = 5.0 - par['calibrations']['tilts']['nfwhm_neigh'] = 2.0 - # 10/mmLBSX_G5532 setup, covering YJHK with the long blue camera and SXD prism - elif '10/mmLBSX' in self.dispname: - # Edges - par['calibrations']['slitedges']['edge_thresh'] = 20. - par['calibrations']['slitedges']['trace_thresh'] = 10. - par['calibrations']['slitedges']['fit_order'] = 2 - par['calibrations']['slitedges']['max_shift_adj'] = 0.5 - par['calibrations']['slitedges']['det_min_spec_length'] = 0.20 - par['calibrations']['slitedges']['fit_min_spec_length'] = 0.20 - par['calibrations']['slitedges']['left_right_pca'] = True # Actually we need a parameter to disable PCA entirely - par['calibrations']['slitedges']['sync_predict'] = 'nearest' - - # Wavelengths - par['calibrations']['wavelengths']['rms_threshold'] = 1.0 # Might be grating dependent.. - par['calibrations']['wavelengths']['sigdetect'] = 5.0 - par['calibrations']['wavelengths']['lamps'] = ['Ar_IR_GNIRS'] - #par['calibrations']['wavelengths']['nonlinear_counts'] = self.detector[0]['nonlinear'] * self.detector[0]['saturation'] - par['calibrations']['wavelengths']['n_first'] = 2 - par['calibrations']['wavelengths']['n_final'] = [3, 3, 3, 3] - # Reidentification parameters - par['calibrations']['wavelengths']['method'] = 'reidentify' - par['calibrations']['wavelengths']['cc_thresh'] = 0.6 - par['calibrations']['wavelengths']['reid_arxiv'] = 'gemini_gnirs_10mm_LBSX.fits' -# par['calibrations']['wavelengths']['ech_fix_format'] = True - # Echelle parameters - par['calibrations']['wavelengths']['echelle'] = True - par['calibrations']['wavelengths']['ech_nspec_coeff'] = 3 - par['calibrations']['wavelengths']['ech_norder_coeff'] = 3 - par['calibrations']['wavelengths']['ech_sigrej'] = 3.0 - - # Tilts - par['calibrations']['tilts']['tracethresh'] = [10, 10, 10, 10] - par['calibrations']['tilts']['sig_neigh'] = 5.0 - par['calibrations']['tilts']['nfwhm_neigh'] = 2.0 - else: - msgs.error('Unrecognized GNIRS dispname') - - return par - def init_meta(self): """ Define how metadata are derived from the spectrograph files. @@ -347,8 +198,243 @@ def check_frame_type(self, ftype, fitstbl, exprng=None): elif '10/mmLBSX' in fitstbl['dispname'][0]: 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) + msgs.warn('Cannot determine if frames are of type {0}.'.format(ftype)) + return np.zeros(len(fitstbl), dtype=bool) + + @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() + + # Image processing steps + turn_off = dict(use_illumflat=False, use_biasimage=False, use_overscan=False, + use_darkimage=False) + par.reset_all_processimages_par(**turn_off) + + # Flats + par['calibrations']['flatfield']['tweak_slits_thresh'] = 0.90 + par['calibrations']['flatfield']['tweak_slits_maxfrac'] = 0.10 + + # Reduce parameters + # par['reduce']['findobj']['snr_thresh'] = 5.0 # Object finding threshold + par['reduce']['findobj']['find_trim_edge'] = [2, 2] # Slit is too short to trim 5,5 especially + par['reduce']['skysub']['bspline_spacing'] = 0.8 + par['reduce']['skysub']['global_sky_std'] = False # Do not perform global sky subtraction for standard stars + par['reduce']['skysub']['no_poly'] = True # Do not use polynomial degree of freedom for global skysub + 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 two objects per order + par['reduce']['findobj']['maxnumber_std'] = 1 # Slit is narrow so allow one object per order + # Standards + par['calibrations']['standardframe']['process']['mask_cr'] = False # Do not mask_cr standards + + # Do not correct for flexure + par['flexure']['spec_method'] = 'skip' + + # Set the default exposure time ranges for the frame typing + par['calibrations']['pixelflatframe']['exprng'] = [None, 30] + par['calibrations']['traceframe']['exprng'] = [None, 30] + par['calibrations']['standardframe']['exprng'] = [None, 30] + par['scienceframe']['exprng'] = [30, None] + + # Sensitivity function parameters + par['sensfunc']['algorithm'] = 'IR' + par['sensfunc']['polyorder'] = 6 + par['sensfunc']['IR']['telgridfile'] = 'TelFit_MaunaKea_3100_26100_R20000.fits' + return par + + 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) + # TODO This is a hack for now until we figure out how to set dispname + # and other meta information in the spectrograph class itself + self.dispname = self.get_meta_value(scifile, 'dispname') + # 32/mmSB_G5533 setup, covering XYJHK with short blue camera + if '32/mm' in self.dispname: + # Edges + par['calibrations']['slitedges']['edge_thresh'] = 20. + 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 + + # Wavelengths + par['calibrations']['wavelengths']['rms_threshold'] = 1.0 # Might be grating dependent.. + par['calibrations']['wavelengths']['sigdetect'] = 5.0 + par['calibrations']['wavelengths']['lamps'] = ['OH_GNIRS'] + # par['calibrations']['wavelengths']['nonlinear_counts'] = self.detector[0]['nonlinear'] * self.detector[0]['saturation'] + par['calibrations']['wavelengths']['n_first'] = 2 + par['calibrations']['wavelengths']['n_final'] = 3 + + # Reidentification parameters + par['calibrations']['wavelengths']['method'] = 'reidentify' + par['calibrations']['wavelengths']['cc_thresh'] = 0.6 + par['calibrations']['wavelengths']['reid_arxiv'] = 'gemini_gnirs.fits' + + # Tilts + par['calibrations']['tilts']['tracethresh'] = 10 + par['calibrations']['tilts']['sig_neigh'] = 5.0 + par['calibrations']['tilts']['nfwhm_neigh'] = 2.0 + # 10/mmLBSX_G5532 setup, covering YJHK with the long blue camera and SXD prism + elif '10/mmLBSX' in self.dispname: + # Edges + par['calibrations']['slitedges']['edge_thresh'] = 20. + par['calibrations']['slitedges']['trace_thresh'] = 10. + par['calibrations']['slitedges']['fit_order'] = 2 + par['calibrations']['slitedges']['max_shift_adj'] = 0.5 + par['calibrations']['slitedges']['det_min_spec_length'] = 0.20 + par['calibrations']['slitedges']['fit_min_spec_length'] = 0.20 + par['calibrations']['slitedges']['sync_predict'] = 'nearest' + + # Wavelengths + par['calibrations']['wavelengths']['rms_threshold'] = 1.0 # Might be grating dependent.. + par['calibrations']['wavelengths']['sigdetect'] = 5.0 + par['calibrations']['wavelengths']['lamps'] = ['Ar_IR_GNIRS'] + # par['calibrations']['wavelengths']['nonlinear_counts'] = self.detector[0]['nonlinear'] * self.detector[0]['saturation'] + par['calibrations']['wavelengths']['n_first'] = 2 + par['calibrations']['wavelengths']['n_final'] = 3 + # Reidentification parameters + par['calibrations']['wavelengths']['method'] = 'reidentify' + par['calibrations']['wavelengths']['cc_thresh'] = 0.6 + par['calibrations']['wavelengths']['reid_arxiv'] = 'gemini_gnirs_10mm_LBSX.fits' + + # Tilts + par['calibrations']['tilts']['tracethresh'] = 10 + par['calibrations']['tilts']['sig_neigh'] = 5.0 + par['calibrations']['tilts']['nfwhm_neigh'] = 2.0 + else: + msgs.error('Unrecognized GNIRS dispname') + + return par + + 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. + """ + msgs.info("Custom bad pixel mask for GNIRS") + # Call the base-class method to generate the empty bpm + bpm_img = super().bpm(filename, det, shape=shape, msbias=msbias) + + # JFH Changed. Dealing with detector scratch + if det == 1: + bpm_img[687:765,12:16] = 1. + bpm_img[671:687,8:13] = 1. + # bpm_img[:, 1000:] = 1. + + return bpm_img + + +class GeminiGNIRSEchelleSpectrograph(GeminiGNIRSSpectrograph): + """ + Child to handle Gemini/GNIRS echelle specific code + """ + name = 'gemini_gnirs_echelle' + header_name = 'GNIRS Echelle' + pypeline = 'Echelle' + ech_fixed_format = True + + 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) + # TODO This is a hack for now until we figure out how to set dispname + # and other meta information in the spectrograph class itself + self.dispname = self.get_meta_value(scifile, 'dispname') + # 32/mmSB_G5533 setup, covering XYJHK with short blue camera + if '32/mm' in self.dispname: + # Edges + par['calibrations']['slitedges']['left_right_pca'] = True + par['calibrations']['slitedges']['pca_order'] = 3 + + # Wavelengths + par['calibrations']['wavelengths']['sigdetect'] = [4.0, 5.0, 5.0, 5.0, 5.0, 5.0] #5.0 + par['calibrations']['wavelengths']['n_final'] = [1, 3, 3, 3, 3, 3] + + # Echelle parameters + # JFH This is provisional these IDs should be checked. + par['calibrations']['wavelengths']['echelle'] = True + par['calibrations']['wavelengths']['ech_nspec_coeff'] = 3 + par['calibrations']['wavelengths']['ech_norder_coeff'] = 5 + par['calibrations']['wavelengths']['ech_sigrej'] = 3.0 + + # Tilts + par['calibrations']['tilts']['tracethresh'] = [5.0, 10, 10, 10, 10, 10] + # 10/mmLBSX_G5532 setup, covering YJHK with the long blue camera and SXD prism + elif '10/mmLBSX' in self.dispname: + # Edges + par['calibrations']['slitedges']['left_right_pca'] = True # Actually we need a parameter to disable PCA entirely + + # Wavelengths + par['calibrations']['wavelengths']['n_final'] = [3, 3, 3, 3] + # Echelle parameters + par['calibrations']['wavelengths']['echelle'] = True + par['calibrations']['wavelengths']['ech_nspec_coeff'] = 3 + par['calibrations']['wavelengths']['ech_norder_coeff'] = 3 + par['calibrations']['wavelengths']['ech_sigrej'] = 3.0 + + # Tilts + par['calibrations']['tilts']['tracethresh'] = [10, 10, 10, 10] + else: + msgs.error('Unrecognized GNIRS dispname') + + return par def order_platescale(self, order_vec, binning=None): """ @@ -436,49 +522,11 @@ def spec_min_max(self): else: msgs.error('Unrecognized disperser') - 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. - """ - msgs.info("Custom bad pixel mask for GNIRS") - # Call the base-class method to generate the empty bpm - bpm_img = super().bpm(filename, det, shape=shape, msbias=msbias) - - # JFH Changed. Dealing with detector scratch - if det == 1: - bpm_img[687:765,12:16] = 1. - bpm_img[671:687,8:13] = 1. - # bpm_img[:, 1000:] = 1. - - return bpm_img -class GNIRSHRIFUSpectrograph(GeminiGNIRSSpectrograph): +class GNIRSIFUSpectrograph(GeminiGNIRSSpectrograph): + name = 'gemini_gnirs_ifu' + header_name = 'GNIRS IFU' pypeline = 'IFU' - name = 'gemini_gnirs_hrifu' - ech_fixed_format = False def init_meta(self): super().init_meta() @@ -491,6 +539,8 @@ def init_meta(self): def default_pypeit_par(cls): par = super().default_pypeit_par() + par['calibrations']['wavelengths']['method'] = 'holy-grail' + # LACosmics parameters par['scienceframe']['process']['sigclip'] = 4.0 par['scienceframe']['process']['objlim'] = 1.5 @@ -573,7 +623,7 @@ def get_wcs(self, hdr, slits, platescale, wave0, dwv, spatial_scale=None): coord = SkyCoord(raval, decval, unit=(units.deg, units.deg)) # Get rotator position - msgs.warn("HACK FOR HRIFU SIMS --- NEED TO FIGURE OUT RPOS and RREF FOR HRIFU FROM HEADER INFO") + msgs.warn("CURRENTLY A HACK --- NEED TO FIGURE OUT RPOS and RREF FOR HRIFU FROM HEADER INFO") if 'ROTPOSN' in hdr: rpos = hdr['ROTPOSN'] else: @@ -603,11 +653,11 @@ def get_wcs(self, hdr, slits, platescale, wave0, dwv, spatial_scale=None): cd21 = -abs(cdelt1) * np.sign(cdelt2) * np.sin(crota) # DEC degress per column cd22 = cdelt2 * np.cos(crota) # DEC degrees per row # Get reference pixels (set these to the middle of the FOV) - crpix1 = 13 # i.e. see get_datacube_bins (13 is used as the reference point - somewhere in the middle of the FOV) + crpix1 = 11 # i.e. see get_datacube_bins (11 is used as the reference point - somewhere in the middle of the FOV) crpix2 = slitlength / 2. crpix3 = 1. # Get the offset - msgs.warn("HACK FOR HRIFU SIMS --- Need to obtain offset from header?") + msgs.warn("HACK FOR HRIFU --- Need to obtain offset from header?") off1 = 0. off2 = 0. off1 /= binspec @@ -616,15 +666,15 @@ def get_wcs(self, hdr, slits, platescale, wave0, dwv, spatial_scale=None): crpix2 += off2 # Create a new WCS object. - msgs.info("Generating HRIFU WCS") + msgs.info("Generating GNIRS IFU WCS") w = wcs.WCS(naxis=3) w.wcs.equinox = hdr['EQUINOX'] - w.wcs.name = 'HRIFU' + w.wcs.name = 'GNIRS IFU' w.wcs.radesys = 'FK5' # Insert the coordinate frame - w.wcs.cname = ['HRIFU RA', 'HRIFU DEC', 'HRIFU Wavelength'] + w.wcs.cname = ['RA', 'DEC', 'Wavelength'] w.wcs.cunit = [units.degree, units.degree, units.Angstrom] - w.wcs.ctype = ["RA---TAN", "DEC--TAN", "WAVE"] + w.wcs.ctype = ["RA---TAN", "DEC--TAN", "WAVE"] # Note, WAVE is in vacuum w.wcs.crval = [ra, dec, wave0] # RA, DEC, and wavelength zeropoints w.wcs.crpix = [crpix1, crpix2, crpix3] # RA, DEC, and wavelength reference pixels w.wcs.cd = np.array([[cd11, cd12, 0.0], [cd21, cd22, 0.0], [0.0, 0.0, dwv]]) @@ -655,86 +705,12 @@ def get_datacube_bins(self, slitlength, minmax, num_wave): when constructing a histogram of the spec2d files. The elements are :math:`(x,y,\lambda)`. """ - xbins = np.arange(1 + 25) - 13.0 - 0.5 # 25 is for 25 slices, and 13 is the reference slit + # TODO :: The HRIFU might have 25 slits with 13 being the reference + xbins = np.arange(1 + 21) - 11.0 - 0.5 # 21 is for 21 slices, and 11 is the reference slit ybins = np.linspace(np.min(minmax[:, 0]), np.max(minmax[:, 1]), 1+slitlength) - 0.5 spec_bins = np.arange(1+num_wave) - 0.5 return xbins, ybins, spec_bins - -class GNIRSLRIFUSpectrograph(GeminiGNIRSSpectrograph): - pypeline = 'IFU' - name = 'gemini_gnirs_lrifu' - ech_fixed_format = False - - def init_meta(self): - super().init_meta() - self.meta['obstime'] = dict(card=None, compound=True, required=False) - self.meta['pressure'] = dict(card=None, compound=True, required=False) - self.meta['temperature'] = dict(card=None, compound=True, required=False) - self.meta['humidity'] = dict(card=None, compound=True, required=False) - - @classmethod - def default_pypeit_par(cls): - par = super().default_pypeit_par() - - # LACosmics parameters - par['scienceframe']['process']['sigclip'] = 4.0 - par['scienceframe']['process']['objlim'] = 1.5 - par['scienceframe']['process']['use_illumflat'] = False # illumflat is applied when building the relative scale image in reduce.py, so should be applied to scienceframe too. - par['scienceframe']['process']['use_specillum'] = False # apply relative spectral illumination - par['scienceframe']['process']['spat_flexure_correct'] = False # don't correct for spatial flexure - varying spatial illumination profile could throw this correction off. Also, there's no way to do astrometric correction if we can't correct for spatial flexure of the contbars frames - par['scienceframe']['process']['use_biasimage'] = False - par['scienceframe']['process']['use_darkimage'] = False - par['calibrations']['flatfield']['slit_illum_finecorr'] = False - # Don't do 1D extraction for 3D data - it's meaningless because the DAR correction must be performed on the 3D data. - par['reduce']['extraction']['skip_extraction'] = True # Because extraction occurs before the DAR correction, don't extract - - # Decrease the wave tilts order, given the shorter slits of the IFU - par['calibrations']['tilts']['spat_order'] = 1 - par['calibrations']['tilts']['spec_order'] = 1 - - # Make sure that this is reduced as a slit (as opposed to fiber) spectrograph - par['reduce']['cube']['slit_spec'] = True - par['reduce']['cube']['combine'] = False # Make separate spec3d files from the input spec2d files - - # Sky subtraction parameters - par['reduce']['skysub']['no_poly'] = True - par['reduce']['skysub']['bspline_spacing'] = 0.6 - par['reduce']['skysub']['joint_fit'] = False - par['reduce']['findobj']['skip_skysub'] = True - par['reduce']['findobj']['skip_final_global'] = True - - # Don't correct flexure by default, but you should use slitcen, - # because this is a slit-based IFU where no objects are extracted. - par['flexure']['spec_method'] = 'skip' - par['flexure']['spec_maxshift'] = 2.5 # Just in case someone switches on spectral flexure, this needs to be minimal - - # Flux calibration parameters - par['sensfunc']['UVIS']['extinct_correct'] = False # This must be False - the extinction correction is performed when making the datacube - - return par - - 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) - par['calibrations']['wavelengths']['echelle'] = False - return par - def pypeit_file_keys(self): """ Define the list of keys to be output into a standard ``PypeIt`` file. @@ -748,135 +724,3 @@ def pypeit_file_keys(self): # TODO :: Think about this... do we even need these extra file keys if skysub is done during the reduction #pypeit_keys += ['calib', 'comb_id', 'bkg_id'] return pypeit_keys - - def get_wcs(self, hdr, slits, platescale, wave0, dwv, spatial_scale=None): - """ - Construct/Read a World-Coordinate System for a frame. - - Args: - hdr (`astropy.io.fits.Header`_): - The header of the raw frame. The information in this - header will be extracted and returned as a WCS. - slits (:class:`~pypeit.slittrace.SlitTraceSet`): - Slit traces. - platescale (:obj:`float`): - The platescale of an unbinned pixel in arcsec/pixel (e.g. - detector.platescale). - wave0 (:obj:`float`): - The wavelength zeropoint. - dwv (:obj:`float`): - Change in wavelength per spectral pixel. - - Returns: - `astropy.wcs.wcs.WCS`_: The world-coordinate system. - """ - msgs.info("Calculating the WCS") - # Get the x and y binning factors, and the typical slit length - binspec, binspat = parse.parse_binning(self.get_meta_value([hdr], 'binning')) - - # Get the pixel and slice scales - pxscl = platescale * binspat / 3600.0 # Need to convert arcsec to degrees - slscl = self.get_meta_value([hdr], 'slitwid') - if spatial_scale is not None: - if pxscl > spatial_scale / 3600.0: - msgs.warn("Spatial scale requested ({0:f}'') is less than the pixel scale ({1:f}'')".format(spatial_scale, pxscl*3600.0)) - # Update the pixel scale - pxscl = spatial_scale / 3600.0 # 3600 is to convert arcsec to degrees - - # Get the typical slit length (this changes by ~0.3% over all slits, so a constant is fine for now) - slitlength = int(np.round(np.median(slits.get_slitlengths(initial=True, median=True)))) - - # Get RA/DEC - raval = self.get_meta_value([hdr], 'ra') - decval = self.get_meta_value([hdr], 'dec') - - # Create a coordinate - coord = SkyCoord(raval, decval, unit=(units.deg, units.deg)) - - # Get rotator position - msgs.warn("HACK FOR HRIFU SIMS --- NEED TO FIGURE OUT RPOS and RREF FOR HRIFU FROM HEADER INFO") - if 'ROTPOSN' in hdr: - rpos = hdr['ROTPOSN'] - else: - rpos = 0. - if 'ROTREFAN' in hdr: - rref = hdr['ROTREFAN'] - else: - rref = 0. - # Get the offset and PA - rotoff = 0.0 # IFU-SKYPA offset (degrees) - skypa = rpos + rref # IFU position angle (degrees) - crota = np.radians(-(skypa + rotoff)) - - # Calculate the fits coordinates - cdelt1 = -slscl - cdelt2 = pxscl - if coord is None: - ra = 0. - dec = 0. - crota = 1 - else: - ra = coord.ra.degree - dec = coord.dec.degree - # Calculate the CD Matrix - cd11 = cdelt1 * np.cos(crota) # RA degrees per column - cd12 = abs(cdelt2) * np.sign(cdelt1) * np.sin(crota) # RA degrees per row - cd21 = -abs(cdelt1) * np.sign(cdelt2) * np.sin(crota) # DEC degress per column - cd22 = cdelt2 * np.cos(crota) # DEC degrees per row - # Get reference pixels (set these to the middle of the FOV) - crpix1 = 11 # i.e. see get_datacube_bins (11 is used as the reference point - somewhere in the middle of the FOV) - crpix2 = slitlength / 2. - crpix3 = 1. - # Get the offset - msgs.warn("HACK FOR HRIFU SIMS --- Need to obtain offset from header?") - off1 = 0. - off2 = 0. - off1 /= binspec - off2 /= binspat - crpix1 += off1 - crpix2 += off2 - - # Create a new WCS object. - msgs.info("Generating HRIFU WCS") - w = wcs.WCS(naxis=3) - w.wcs.equinox = hdr['EQUINOX'] - w.wcs.name = 'HRIFU' - w.wcs.radesys = 'FK5' - # Insert the coordinate frame - w.wcs.cname = ['HRIFU RA', 'HRIFU DEC', 'HRIFU Wavelength'] - w.wcs.cunit = [units.degree, units.degree, units.Angstrom] - w.wcs.ctype = ["RA---TAN", "DEC--TAN", "WAVE"] - w.wcs.crval = [ra, dec, wave0] # RA, DEC, and wavelength zeropoints - w.wcs.crpix = [crpix1, crpix2, crpix3] # RA, DEC, and wavelength reference pixels - w.wcs.cd = np.array([[cd11, cd12, 0.0], [cd21, cd22, 0.0], [0.0, 0.0, dwv]]) - w.wcs.lonpole = 180.0 # Native longitude of the Celestial pole - w.wcs.latpole = 0.0 # Native latitude of the Celestial pole - - return w - - def get_datacube_bins(self, slitlength, minmax, num_wave): - r""" - Calculate the bin edges to be used when making a datacube. - - Args: - slitlength (:obj:`int`): - Length of the slit in pixels - minmax (`numpy.ndarray`_): - An array with the minimum and maximum pixel locations on each - slit relative to the reference location (usually the centre - of the slit). Shape must be :math:`(N_{\rm slits},2)`, and is - typically the array returned by - :func:`~pypeit.slittrace.SlitTraceSet.get_radec_image`. - num_wave (:obj:`int`): - Number of wavelength steps. Given by:: - int(round((wavemax-wavemin)/delta_wave)) - - Args: - :obj:`tuple`: Three 1D `numpy.ndarray`_ providing the bins to use - when constructing a histogram of the spec2d files. The elements - are :math:`(x,y,\lambda)`. - """ - xbins = np.arange(1 + 21) - 11.0 - 0.5 # 21 is for 21 slices, and 11 is the reference slit - ybins = np.linspace(np.min(minmax[:, 0]), np.max(minmax[:, 1]), 1+slitlength) - 0.5 - spec_bins = np.arange(1+num_wave) - 0.5 - return xbins, ybins, spec_bins From e45a83d24b1a7c42e4cfaafe0698c5dc8f3ce69a Mon Sep 17 00:00:00 2001 From: rcooke Date: Sat, 29 Apr 2023 14:08:27 +0100 Subject: [PATCH 03/27] better wave cal --- pypeit/spectrographs/gemini_gnirs.py | 32 ++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/pypeit/spectrographs/gemini_gnirs.py b/pypeit/spectrographs/gemini_gnirs.py index be13c6e239..7f6d5326f3 100644 --- a/pypeit/spectrographs/gemini_gnirs.py +++ b/pypeit/spectrographs/gemini_gnirs.py @@ -539,8 +539,6 @@ def init_meta(self): def default_pypeit_par(cls): par = super().default_pypeit_par() - par['calibrations']['wavelengths']['method'] = 'holy-grail' - # LACosmics parameters par['scienceframe']['process']['sigclip'] = 4.0 par['scienceframe']['process']['objlim'] = 1.5 @@ -578,6 +576,36 @@ def default_pypeit_par(cls): return par + 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) + # TODO This is a hack for now until we figure out how to set dispname + # and other meta information in the spectrograph class itself + self.dispname = self.get_meta_value(scifile, 'dispname') + # 32/mmSB_G5533 setup, covering XYJHK with short blue camera + par['calibrations']['wavelengths']['method'] = 'holy-grail' + if '32/mm' in self.dispname: + pass + else: + msgs.error('Unrecognized GNIRS dispname') + + return par + def get_wcs(self, hdr, slits, platescale, wave0, dwv, spatial_scale=None): """ Construct/Read a World-Coordinate System for a frame. From 80765251ed262a901bdea8d235899702020b5c56 Mon Sep 17 00:00:00 2001 From: rcooke Date: Sat, 29 Apr 2023 14:30:28 +0100 Subject: [PATCH 04/27] better slit edges --- pypeit/spectrographs/gemini_gnirs.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pypeit/spectrographs/gemini_gnirs.py b/pypeit/spectrographs/gemini_gnirs.py index 7f6d5326f3..31b34d1a9d 100644 --- a/pypeit/spectrographs/gemini_gnirs.py +++ b/pypeit/spectrographs/gemini_gnirs.py @@ -524,6 +524,9 @@ def spec_min_max(self): class GNIRSIFUSpectrograph(GeminiGNIRSSpectrograph): + # TODO :: A list of steps that could improve the reduction + # * Have a high threshold for detecting slit edges (par['calibrations']['slitedges']['edge_thresh'] = 100.), and have an option when inserting new traces to be the median of all other slit lengths (or a fit to the slit lengths). + # * Need to store a wavelength solution for different grating options (Note, the Holy Grail algorithm works pretty well, most of the time) name = 'gemini_gnirs_ifu' header_name = 'GNIRS IFU' pypeline = 'IFU' @@ -599,6 +602,7 @@ def config_specific_par(self, scifile, inp_par=None): self.dispname = self.get_meta_value(scifile, 'dispname') # 32/mmSB_G5533 setup, covering XYJHK with short blue camera par['calibrations']['wavelengths']['method'] = 'holy-grail' + par['calibrations']['slitedges']['edge_thresh'] = 30. if '32/mm' in self.dispname: pass else: From f37fac2f534ed15fe352a4553a2e0a82f929786a Mon Sep 17 00:00:00 2001 From: rcooke Date: Sat, 29 Apr 2023 14:35:46 +0100 Subject: [PATCH 05/27] print out typo --- pypeit/core/wavecal/autoid.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pypeit/core/wavecal/autoid.py b/pypeit/core/wavecal/autoid.py index 8ccf6517a9..b163bb3eba 100644 --- a/pypeit/core/wavecal/autoid.py +++ b/pypeit/core/wavecal/autoid.py @@ -2604,14 +2604,14 @@ def report_prelim(self, slit, best_patt_dict, best_final_fit): # Report on the best preliminary result if best_final_fit is None: msgs.warn('---------------------------------------------------' + msgs.newline() + - 'Preliminary report for slit {0:d}/{1:d}:'.format(slit, self._nslit-1) + msgs.newline() + + 'Preliminary report for slit {0:d}/{1:d}:'.format(slit+1, self._nslit) + msgs.newline() + ' No matches! Attempting to cross match.' + msgs.newline() + '---------------------------------------------------') self._all_patt_dict[str(slit)] = None self._all_final_fit[str(slit)] = None elif best_final_fit['rms'] > wvutils.parse_param(self._par, 'rms_threshold', slit): msgs.warn('---------------------------------------------------' + msgs.newline() + - 'Preliminary report for slit {0:d}/{1:d}:'.format(slit, self._nslit-1) + msgs.newline() + + 'Preliminary report for slit {0:d}/{1:d}:'.format(slit+1, self._nslit) + msgs.newline() + ' Poor RMS ({0:.3f})! Attempting to cross match.'.format(best_final_fit['rms']) + msgs.newline() + '---------------------------------------------------') self._all_patt_dict[str(slit)] = None @@ -2624,7 +2624,7 @@ def report_prelim(self, slit, best_patt_dict, best_final_fit): signtxt = 'anitcorrelate' # Report msgs.info('---------------------------------------------------' + msgs.newline() + - 'Preliminary report for slit {0:d}/{1:d}:'.format(slit, self._nslit-1) + msgs.newline() + + 'Preliminary report for slit {0:d}/{1:d}:'.format(slit+1, self._nslit) + msgs.newline() + ' Pixels {:s} with wavelength'.format(signtxt) + msgs.newline() + ' Number of weak lines = {:d}'.format(self._det_weak[str(slit)][0].size) + msgs.newline() + ' Number of strong lines = {:d}'.format(self._det_stro[str(slit)][0].size) + msgs.newline() + @@ -2644,7 +2644,7 @@ def report_final(self): for slit in range(self._nslit): # Prepare a message for bad wavelength solutions badmsg = '---------------------------------------------------' + msgs.newline() +\ - 'Final report for slit {0:d}/{1:d}:'.format(slit, self._nslit-1) + msgs.newline() +\ + 'Final report for slit {0:d}/{1:d}:'.format(slit+1, self._nslit) + msgs.newline() +\ ' Wavelength calibration not performed!' if slit not in self._ok_mask: msgs.warn(badmsg) @@ -2663,7 +2663,7 @@ def report_final(self): centdisp = abs(centwave-tempwave) msgs.info(msgs.newline() + '---------------------------------------------------' + msgs.newline() + - 'Final report for slit {0:d}/{1:d}:'.format(slit, self._nslit-1) + msgs.newline() + + 'Final report for slit {0:d}/{1:d}:'.format(slit+1, self._nslit) + msgs.newline() + ' Pixels {:s} with wavelength'.format(signtxt) + msgs.newline() + ' Number of weak lines = {:d}'.format(self._det_weak[str(slit)][0].size) + msgs.newline() + ' Number of strong lines = {:d}'.format(self._det_stro[str(slit)][0].size) + msgs.newline() + From 59762c61ee52616efa06e251d90c76aeffec89ec Mon Sep 17 00:00:00 2001 From: rcooke Date: Sat, 29 Apr 2023 14:54:42 +0100 Subject: [PATCH 06/27] fix alignments calibrations --- pypeit/calibrations.py | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/pypeit/calibrations.py b/pypeit/calibrations.py index fbe6395b76..78f9f44444 100644 --- a/pypeit/calibrations.py +++ b/pypeit/calibrations.py @@ -356,24 +356,26 @@ def get_align(self): self.alignments = alignframe.Alignments.from_file(masterframe_filename) self.alignments.is_synced(self.slits) return self.alignments + elif len(dark_files) == 0: + self.alignments = None + else: + msalign = buildimage.buildimage_fromlist(self.spectrograph, self.det, + self.par['alignframe'], align_files, + bias=self.msbias, bpm=self.msbpm, + dark=self.msdark) + + # Extract some header info needed by the algorithm + binning = self.spectrograph.get_meta_value(align_files[0], 'binning') - msalign = buildimage.buildimage_fromlist(self.spectrograph, self.det, - self.par['alignframe'], align_files, - bias=self.msbias, bpm=self.msbpm, - dark=self.msdark) - - # Extract some header info needed by the algorithm - binning = self.spectrograph.get_meta_value(align_files[0], 'binning') - - # Instantiate - # TODO: From JFH: Do we need the bpm here? Check that this was in the previous code. - alignment = alignframe.TraceAlignment(msalign, self.slits, self.spectrograph, - self.par['alignment'], det=self.det, binning=binning, - qa_path=self.qa_path, msbpm=self.msbpm) - # Run - self.alignments = alignment.run(show=self.show) - # Save to Masters - self.alignments.to_master_file(masterframe_filename) + # Instantiate + # TODO: From JFH: Do we need the bpm here? Check that this was in the previous code. + alignment = alignframe.TraceAlignment(msalign, self.slits, self.spectrograph, + self.par['alignment'], det=self.det, binning=binning, + qa_path=self.qa_path, msbpm=self.msbpm) + # Run + self.alignments = alignment.run(show=self.show) + # Save to Masters + self.alignments.to_master_file(masterframe_filename) return self.alignments From 30b72da1982ab80d91fcd3e0247fe9631871364b Mon Sep 17 00:00:00 2001 From: rcooke Date: Sat, 29 Apr 2023 14:56:18 +0100 Subject: [PATCH 07/27] fix alignment cal --- pypeit/calibrations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypeit/calibrations.py b/pypeit/calibrations.py index 78f9f44444..8c0d893a55 100644 --- a/pypeit/calibrations.py +++ b/pypeit/calibrations.py @@ -356,7 +356,7 @@ def get_align(self): self.alignments = alignframe.Alignments.from_file(masterframe_filename) self.alignments.is_synced(self.slits) return self.alignments - elif len(dark_files) == 0: + elif len(align_files) == 0: self.alignments = None else: msalign = buildimage.buildimage_fromlist(self.spectrograph, self.det, From cb848ec51ad98954e06d5edc9be4a3e67c82df8a Mon Sep 17 00:00:00 2001 From: rcooke Date: Sun, 30 Apr 2023 13:18:41 +0100 Subject: [PATCH 08/27] pad --- pypeit/core/datacube.py | 6 +++--- pypeit/spectrographs/gemini_gnirs.py | 31 ++++++++++++++++++++++++++-- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/pypeit/core/datacube.py b/pypeit/core/datacube.py index 299e951475..093c355a03 100644 --- a/pypeit/core/datacube.py +++ b/pypeit/core/datacube.py @@ -677,7 +677,7 @@ def make_good_skymask(slitimg, tilts): unq = np.unique(slitimg[slitimg>0]) for uu in range(unq.size): # Find the x,y pixels in this slit - ww = np.where(slitimg==unq[uu]) + ww = np.where((slitimg==unq[uu]) & (tilts != 0.0)) # Mask the bottom pixels first wb = np.where(ww[0] == 0)[0] wt = np.where(ww[0] == np.max(ww[0]))[0] @@ -685,7 +685,7 @@ def make_good_skymask(slitimg, tilts): maxtlt = np.max(tilts[0, ww[1][wb]]) mintlt = np.min(tilts[-1, ww[1][wt]]) # Mask all values below this maximum - gpm[ww] = (tilts[ww]>=maxtlt) & (tilts[ww]<=mintlt) # The signs are correct here. + gpm[ww] = (tilts[ww] >= maxtlt) & (tilts[ww] <= mintlt) # The signs are correct here. return gpm @@ -1820,7 +1820,7 @@ def coadd_cube(files, opts, spectrograph=None, parset=None, overwrite=False): alignments = alignframe.Alignments.from_file(alignfile) else: msgs.warn("Could not find Master Alignment frame:"+msgs.newline()+alignfile) - msgs.warn("Astrometric correction will not be performed") + msgs.info("Using slit edges for astrometric transform") else: msgs.info("Astrometric correction will not be performed") # If nothing better was provided, use the slit edges diff --git a/pypeit/spectrographs/gemini_gnirs.py b/pypeit/spectrographs/gemini_gnirs.py index 31b34d1a9d..f428fcd920 100644 --- a/pypeit/spectrographs/gemini_gnirs.py +++ b/pypeit/spectrographs/gemini_gnirs.py @@ -4,10 +4,13 @@ .. include:: ../include/links.rst """ import numpy as np +from astropy import wcs, units +from astropy.coordinates import SkyCoord, EarthLocation +from astropy.time import Time from pypeit import msgs from pypeit import telescopes -from pypeit.core import framematch +from pypeit.core import framematch, parse from pypeit.images import detector_container from pypeit.spectrographs import spectrograph @@ -22,6 +25,17 @@ class GeminiGNIRSSpectrograph(spectrograph.Spectrograph): header_name = 'GNIRS' telescope = telescopes.GeminiNTelescopePar() + def __init__(self): + super().__init__() + + # TODO :: Might need to change the tolerance of disperser angle in + # pypeit setup (two BH2 nights where sufficiently different that this + # was important). + + # TODO :: Might consider changing TelescopePar to use the astropy + # EarthLocation. KBW: Fine with me! + self.location = EarthLocation.of_site('Gemini North') + def get_detector_par(self, det, hdu=None): """ Return metadata for the selected detector. @@ -82,6 +96,7 @@ def init_meta(self): self.meta['dithoff'] = dict(card=None, compound=True) # Extras for config and frametyping + self.meta['slitwid'] = dict(ext=0, compound=True, card=None) self.meta['dispname'] = dict(ext=0, card='GRATING') self.meta['hatch'] = dict(ext=0, card='COVER') self.meta['dispangle'] = dict(ext=0, card='GRATTILT', rtol=1e-4) @@ -107,9 +122,18 @@ def compound_meta(self, headarr, meta_key): return headarr[0].get('QOFFSET') else: return 0.0 + elif meta_key == 'slitwid': + deckname = headarr[0].get('DECKER') + if 'LR-IFU' in deckname: + return 0.15 + elif 'HR-IFU' in deckname: + return 0.05 + else: + # TODO :: Need to provide a more complete set of options here + return None elif meta_key == 'obstime': try: - return headarr[0]['TIME-OBS'] + return Time(headarr[0]['DATE-OBS'] + "T" + headarr[0]['TIME-OBS']) except KeyError: msgs.warn("Time of observation is not in header") return 0.0 @@ -555,11 +579,13 @@ def default_pypeit_par(cls): par['reduce']['extraction']['skip_extraction'] = True # Because extraction occurs before the DAR correction, don't extract # Decrease the wave tilts order, given the shorter slits of the IFU + par['calibrations']['slitedges']['pad'] = 2 # Need to pad out the tilts for the astrometric transform when creating a datacube. par['calibrations']['tilts']['spat_order'] = 1 par['calibrations']['tilts']['spec_order'] = 1 # Make sure that this is reduced as a slit (as opposed to fiber) spectrograph par['reduce']['cube']['slit_spec'] = True + par['reduce']['cube']['grating_corr'] = False par['reduce']['cube']['combine'] = False # Make separate spec3d files from the input spec2d files # Sky subtraction parameters @@ -637,6 +663,7 @@ def get_wcs(self, hdr, slits, platescale, wave0, dwv, spatial_scale=None): # Get the pixel and slice scales pxscl = platescale * binspat / 3600.0 # Need to convert arcsec to degrees + msgs.work("NEED TO WORK OUT SLICER SCALE AND PIXEL SCALE") slscl = self.get_meta_value([hdr], 'slitwid') if spatial_scale is not None: if pxscl > spatial_scale / 3600.0: From 9cb73b2199e9288b3bce143cc9c470f707794efd Mon Sep 17 00:00:00 2001 From: rcooke Date: Sun, 30 Apr 2023 14:19:24 +0100 Subject: [PATCH 09/27] fixes --- pypeit/core/datacube.py | 2 +- pypeit/spectrographs/gemini_gnirs.py | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/pypeit/core/datacube.py b/pypeit/core/datacube.py index 093c355a03..c80bb3983d 100644 --- a/pypeit/core/datacube.py +++ b/pypeit/core/datacube.py @@ -1836,7 +1836,7 @@ def coadd_cube(files, opts, spectrograph=None, parset=None, overwrite=False): alignSplines = alignframe.AlignmentSplines(traces, locations, spec2DObj.tilts) raimg, decimg, minmax = slits.get_radec_image(frame_wcs, alignSplines, spec2DObj.tilts, initial=True, flexure=spat_flexure) - + embed() # Perform the DAR correction if wave_ref is None: wave_ref = 0.5*(np.min(waveimg[onslit_gpm]) + np.max(waveimg[onslit_gpm])) diff --git a/pypeit/spectrographs/gemini_gnirs.py b/pypeit/spectrographs/gemini_gnirs.py index f428fcd920..5c2589135e 100644 --- a/pypeit/spectrographs/gemini_gnirs.py +++ b/pypeit/spectrographs/gemini_gnirs.py @@ -578,8 +578,13 @@ def default_pypeit_par(cls): # Don't do 1D extraction for 3D data - it's meaningless because the DAR correction must be performed on the 3D data. par['reduce']['extraction']['skip_extraction'] = True # Because extraction occurs before the DAR correction, don't extract - # Decrease the wave tilts order, given the shorter slits of the IFU + #par['calibrations']['flatfield']['tweak_slits'] = False # Do not tweak the slit edges (we want to use the full slit) + par['calibrations']['flatfield']['tweak_slits_thresh'] = 0.0 # Make sure the full slit is used (i.e. when the illumination fraction is > 0.5) + par['calibrations']['flatfield']['tweak_slits_maxfrac'] = 0.0 # Make sure the full slit is used (i.e. no padding) + par['calibrations']['flatfield']['slit_trim'] = 2 # Trim the slit edges par['calibrations']['slitedges']['pad'] = 2 # Need to pad out the tilts for the astrometric transform when creating a datacube. + + # Decrease the wave tilts order, given the shorter slits of the IFU par['calibrations']['tilts']['spat_order'] = 1 par['calibrations']['tilts']['spec_order'] = 1 @@ -595,10 +600,10 @@ def default_pypeit_par(cls): par['reduce']['findobj']['skip_skysub'] = True par['reduce']['findobj']['skip_final_global'] = True - # Don't correct flexure by default, but you should use slitcen, + # Don't correct flexure by default since the OH lines are used for wavelength calibration + # If someone does want to do a spectral flexure correction, you should use slitcen, # because this is a slit-based IFU where no objects are extracted. par['flexure']['spec_method'] = 'skip' - par['flexure']['spec_maxshift'] = 2.5 # Just in case someone switches on spectral flexure, this needs to be minimal # Flux calibration parameters par['sensfunc']['UVIS']['extinct_correct'] = False # This must be False - the extinction correction is performed when making the datacube From 66b474aefc05a73483f32de2efb0571ce2a1e495 Mon Sep 17 00:00:00 2001 From: rcooke Date: Sun, 30 Apr 2023 21:39:02 +0100 Subject: [PATCH 10/27] wavecal --- pypeit/core/datacube.py | 1 - .../reid_arxiv/gemini_gnirs_lrifu_H.fits | Bin 0 -> 23040 bytes pypeit/spectrographs/gemini_gnirs.py | 3 +++ 3 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 pypeit/data/arc_lines/reid_arxiv/gemini_gnirs_lrifu_H.fits diff --git a/pypeit/core/datacube.py b/pypeit/core/datacube.py index c80bb3983d..6034799082 100644 --- a/pypeit/core/datacube.py +++ b/pypeit/core/datacube.py @@ -1836,7 +1836,6 @@ def coadd_cube(files, opts, spectrograph=None, parset=None, overwrite=False): alignSplines = alignframe.AlignmentSplines(traces, locations, spec2DObj.tilts) raimg, decimg, minmax = slits.get_radec_image(frame_wcs, alignSplines, spec2DObj.tilts, initial=True, flexure=spat_flexure) - embed() # Perform the DAR correction if wave_ref is None: wave_ref = 0.5*(np.min(waveimg[onslit_gpm]) + np.max(waveimg[onslit_gpm])) diff --git a/pypeit/data/arc_lines/reid_arxiv/gemini_gnirs_lrifu_H.fits b/pypeit/data/arc_lines/reid_arxiv/gemini_gnirs_lrifu_H.fits new file mode 100644 index 0000000000000000000000000000000000000000..c347b9ca14db8b8cf3a6571c3ff4032fb64af9ba GIT binary patch literal 23040 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 Date: Mon, 1 May 2023 20:22:23 +0100 Subject: [PATCH 11/27] rel only --- pypeit/find_objects.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pypeit/find_objects.py b/pypeit/find_objects.py index 40ef6c2872..12732bc809 100644 --- a/pypeit/find_objects.py +++ b/pypeit/find_objects.py @@ -1382,7 +1382,10 @@ def calculate_flexure(self, global_sky): mxshft=self.par['flexure']['spec_maxshift'], excess_shft=self.par['flexure']['excessive_shift'], method="slitcen") - this_slitshift = np.ones(self.slits.nslits) * flex_dict['shift'] + this_slitshift = np.zeros(self.slits.nslits) + if flex_dict is not None: + msgs.warn("Only a relative spectral flexure correction will be performed") + this_slitshift = np.ones(self.slits.nslits) * flex_dict['shift'] # Now loop through all slits to calculate the additional shift relative to the reference slit for slit_idx, slit_spat in enumerate(self.slits.spat_id): thismask = (self.slitmask == slit_spat) From a209827f918465a1549e7e568c58f91bb3a936bc Mon Sep 17 00:00:00 2001 From: rcooke Date: Mon, 1 May 2023 20:22:35 +0100 Subject: [PATCH 12/27] updates --- pypeit/spectrographs/gemini_gnirs.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/pypeit/spectrographs/gemini_gnirs.py b/pypeit/spectrographs/gemini_gnirs.py index ff31e20511..a2a83cb382 100644 --- a/pypeit/spectrographs/gemini_gnirs.py +++ b/pypeit/spectrographs/gemini_gnirs.py @@ -96,6 +96,7 @@ def init_meta(self): self.meta['dithoff'] = dict(card=None, compound=True) # Extras for config and frametyping + self.meta['calpos'] = dict(ext=0, card='GRATORD') self.meta['slitwid'] = dict(ext=0, compound=True, card=None) self.meta['dispname'] = dict(ext=0, card='GRATING') self.meta['hatch'] = dict(ext=0, card='COVER') @@ -125,9 +126,9 @@ def compound_meta(self, headarr, meta_key): elif meta_key == 'slitwid': deckname = headarr[0].get('DECKER') if 'LR-IFU' in deckname: - return 0.15 + return 0.15/3600.0 # divide by 3600 for degrees elif 'HR-IFU' in deckname: - return 0.05 + return 0.05/3600.0 # divide by 3600 for degrees else: # TODO :: Need to provide a more complete set of options here return None @@ -396,7 +397,6 @@ class GeminiGNIRSEchelleSpectrograph(GeminiGNIRSSpectrograph): Child to handle Gemini/GNIRS echelle specific code """ name = 'gemini_gnirs_echelle' - header_name = 'GNIRS Echelle' pypeline = 'Echelle' ech_fixed_format = True @@ -552,7 +552,6 @@ class GNIRSIFUSpectrograph(GeminiGNIRSSpectrograph): # * Have a high threshold for detecting slit edges (par['calibrations']['slitedges']['edge_thresh'] = 100.), and have an option when inserting new traces to be the median of all other slit lengths (or a fit to the slit lengths). # * Need to store a wavelength solution for different grating options (Note, the Holy Grail algorithm works pretty well, most of the time) name = 'gemini_gnirs_ifu' - header_name = 'GNIRS IFU' pypeline = 'IFU' def init_meta(self): @@ -597,13 +596,12 @@ def default_pypeit_par(cls): par['reduce']['skysub']['no_poly'] = True par['reduce']['skysub']['bspline_spacing'] = 0.6 par['reduce']['skysub']['joint_fit'] = False - par['reduce']['findobj']['skip_skysub'] = True - par['reduce']['findobj']['skip_final_global'] = True # Don't correct flexure by default since the OH lines are used for wavelength calibration # If someone does want to do a spectral flexure correction, you should use slitcen, # because this is a slit-based IFU where no objects are extracted. par['flexure']['spec_method'] = 'skip' + par['flexure']['spec_maxshift'] = 0 # The sky lines are used for calibration - don't allow flexure # Flux calibration parameters par['sensfunc']['UVIS']['extinct_correct'] = False # This must be False - the extinction correction is performed when making the datacube @@ -628,19 +626,20 @@ def config_specific_par(self, scifile, inp_par=None): adjusted for configuration specific parameter values. """ par = super().config_specific_par(scifile, inp_par=inp_par) - # TODO This is a hack for now until we figure out how to set dispname - # and other meta information in the spectrograph class itself - self.dispname = self.get_meta_value(scifile, 'dispname') - # 32/mmSB_G5533 setup, covering XYJHK with short blue camera - par['calibrations']['wavelengths']['method'] = 'holy-grail' + # Obtain a header keyword to determine which range is being used + gratord = self.get_meta_value(scifile, 'calpos') par['calibrations']['slitedges']['edge_thresh'] = 30. # TODO :: The following wavelength solutions are not general enough - need to implement a solution for each setup - if '32/mm' in self.dispname: + if gratord == 4: # H band par['calibrations']['wavelengths']['method'] = 'full_template' par['calibrations']['wavelengths']['reid_arxiv'] = 'gemini_gnirs_lrifu_H.fits' pass + elif gratord == 3: # K band + par['calibrations']['wavelengths']['method'] = 'full_template' + par['calibrations']['wavelengths']['reid_arxiv'] = 'gemini_gnirs_lrifu_K.fits' + pass else: - msgs.error('Unrecognized GNIRS dispname') + par['calibrations']['wavelengths']['method'] = 'holy-grail' return par From 3823b26066dbe38843d8536bc3f7a2644ef2556f Mon Sep 17 00:00:00 2001 From: rcooke Date: Mon, 1 May 2023 21:49:06 +0100 Subject: [PATCH 13/27] arxiv solution --- pypeit/scripts/arxiv_solution.py | 60 ++++++++++++++++++++++++++++++++ setup.cfg | 1 + 2 files changed, 61 insertions(+) create mode 100644 pypeit/scripts/arxiv_solution.py diff --git a/pypeit/scripts/arxiv_solution.py b/pypeit/scripts/arxiv_solution.py new file mode 100644 index 0000000000..b2f592fb1e --- /dev/null +++ b/pypeit/scripts/arxiv_solution.py @@ -0,0 +1,60 @@ +""" +This script enables the user to convert a MasterWaveCalib wavelength solution fits file +into a PypeIt arxiv solution that can be used with the full_template method. + +.. include common links, assuming primary doc root is up one directory +.. include:: ../include/links.rst +""" +import time +from pypeit import msgs +from pypeit import par +from pypeit import inputfiles +from pypeit import utils +from pypeit.scripts import scriptbase + + +class ArxivSolution(scriptbase.ScriptBase): + + @classmethod + def get_parser(cls, width=None): + parser = super().get_parser(description='Read in a MasterWaveCalib solution and convert it into the ' + 'format required for the PypeIt full template archive', width=width) + parser.add_argument('file', type = str, default=None, help='MasterWaveCalib file') + parser.add_argument('binning', type=int, help="Spectral binning") + parser.add_argument('-s', '--slit', default=0, type=int, help='Slit number to use') + parser.add_argument('-v', '--verbosity', type=int, default=1, + help='Verbosity level between 0 [none] and 2 [all]. Default: 1. ' + 'Level 2 writes a log with filename make_arxiv_solution_YYYYMMDD-HHMM.log') + return parser + + @staticmethod + def main(args): + import os + from pypeit.wavecalib import WaveCalib + from pypeit.core.wavecal import wvutils + + # Set the verbosity, and create a logfile if verbosity == 2 + msgs.set_logfile_and_verbosity('arxiv_solution', args.verbosity) + + # Check that a file has been provided + if args.file is None: + msgs.error('You must input a MasterWaveCalib file') + elif not os.path.exists(args.file): + msgs.error("The following MasterWaveCalib file does not exist:" + msgs.newline() + args.file) + + # Load the wavelength calibration file + wv_calib = WaveCalib.from_file(args.file) + from IPython import embed + embed() + wave = wv_calib['wv_fits'][args.slit]['wave_soln'].flatten() + spec = wv_calib['wv_fits'][args.slit]['spec'].flatten() + outname = args.file.replace(".fits", "_arXiv.fits") + wvutils.write_template(wave, spec, args.binning, './', outname) + print("") # Empty line for clarity + msgs.info("To include the newly generated solution in the PypeIt archive," + msgs.newline() + + "move (and appropriately rename) the following file: " + msgs.newline() + + outname + msgs.newline() + + "to the following directory:" + msgs.newline() + + "pypeit/data/arc_lines/reid_arxiv/") + print("") # Empty line for clarity + msgs.info("Please also consider sharing your solution with the PypeIt Developers.") diff --git a/setup.cfg b/setup.cfg index 670cc997c6..ac00b5b3fd 100644 --- a/setup.cfg +++ b/setup.cfg @@ -92,6 +92,7 @@ dev = console_scripts = # non-GUI scripts + pypeit_arxiv_solution = pypeit.scripts.arxiv_solution:ArxivSolution.entry_point pypeit_cache_github_data = pypeit.scripts.cache_github_data:CacheGithubData.entry_point pypeit_chk_for_calibs = pypeit.scripts.chk_for_calibs:ChkForCalibs.entry_point pypeit_chk_noise_1dspec = pypeit.scripts.chk_noise_1dspec:ChkNoise1D.entry_point From 1651de1ceedb6386d378797867d9f69aac381559 Mon Sep 17 00:00:00 2001 From: rcooke Date: Tue, 2 May 2023 09:00:17 +0100 Subject: [PATCH 14/27] arxiv solutions --- .../reid_arxiv/gemini_gnirs_lrifu_K.fits | Bin 0 -> 23040 bytes pypeit/spectrographs/gemini_gnirs.py | 17 ++++++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) create mode 100644 pypeit/data/arc_lines/reid_arxiv/gemini_gnirs_lrifu_K.fits diff --git a/pypeit/data/arc_lines/reid_arxiv/gemini_gnirs_lrifu_K.fits b/pypeit/data/arc_lines/reid_arxiv/gemini_gnirs_lrifu_K.fits new file mode 100644 index 0000000000000000000000000000000000000000..e2ed76cf88cf84f12451adfcc8ca7f4d7dd8005f GIT binary patch literal 23040 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 Date: Tue, 2 May 2023 13:40:28 +0100 Subject: [PATCH 15/27] fix dc --- pypeit/core/datacube.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pypeit/core/datacube.py b/pypeit/core/datacube.py index 6034799082..b7f9a2014d 100644 --- a/pypeit/core/datacube.py +++ b/pypeit/core/datacube.py @@ -1998,8 +1998,7 @@ def coadd_cube(files, opts, spectrograph=None, parset=None, overwrite=False): # Register spatial offsets between all frames if translate: # Find the wavelength range where all frames overlap - min_wl, max_wl = np.max(mnmx_wv[:, :, 0]), np.min( - mnmx_wv[:, :, 1]) # This is the max blue wavelength and the min red wavelength + min_wl, max_wl = np.max(mnmx_wv[:, :, 0]), np.min(mnmx_wv[:, :, 1]) # This is the max blue wavelength and the min red wavelength wavediff = np.max(all_wave) - np.min(all_wave) if min_wl < max_wl: ww = np.where((all_wave > min_wl) & (all_wave < max_wl)) From 43c2c75b1d3a4447d4322d407088b64f4a7b8560 Mon Sep 17 00:00:00 2001 From: rcooke Date: Sat, 13 May 2023 12:53:51 +0100 Subject: [PATCH 16/27] align bug --- pypeit/core/datacube.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pypeit/core/datacube.py b/pypeit/core/datacube.py index c143c1762c..e5dc4dff26 100644 --- a/pypeit/core/datacube.py +++ b/pypeit/core/datacube.py @@ -1890,10 +1890,10 @@ def coadd_cube(files, opts, spectrograph=None, parset=None, overwrite=False): msgs.info("Loading alignments") alignments = alignframe.Alignments.from_file(alignfile) else: - msgs.warn(f'Processed Alignment frame not recorded or not found: {alignfile}') + msgs.warn(f'Processed alignment frame not recorded or not found!') msgs.info("Using slit edges for astrometric transform") else: - msgs.info("Astrometric correction will not be performed") + msgs.info("Using slit edges for astrometric transform") # If nothing better was provided, use the slit edges if alignments is None: left, right, _ = slits.select_edges(initial=True, flexure=spat_flexure) From 5a952ef79b11340bc9d390f8983073cd23a64b31 Mon Sep 17 00:00:00 2001 From: rcooke Date: Sat, 13 May 2023 13:09:56 +0100 Subject: [PATCH 17/27] minor todo fixes --- pypeit/core/datacube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypeit/core/datacube.py b/pypeit/core/datacube.py index e5dc4dff26..ee56b8b66b 100644 --- a/pypeit/core/datacube.py +++ b/pypeit/core/datacube.py @@ -2160,7 +2160,7 @@ def coadd_cube(files, opts, spectrograph=None, parset=None, overwrite=False): cubepar['whitelight_range']) # TODO :: THIS IS JUST TEMPORARY (probably)... the first bit outputs separate files, the second bit combines all frames. # Might want to have an option where if reference_image is provided, but not combine, the first option is done. - if True: + if False: # embed() # assert(False) for ff in range(numfiles): From 9204ad2ac440070e826cd3fd0a788141ea4915b6 Mon Sep 17 00:00:00 2001 From: rcooke Date: Wed, 17 May 2023 08:14:55 +0100 Subject: [PATCH 18/27] add grat --- pypeit/spectrographs/gemini_gnirs.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pypeit/spectrographs/gemini_gnirs.py b/pypeit/spectrographs/gemini_gnirs.py index 75973c96a8..0ccb9b3e42 100644 --- a/pypeit/spectrographs/gemini_gnirs.py +++ b/pypeit/spectrographs/gemini_gnirs.py @@ -347,8 +347,11 @@ def config_specific_par(self, scifile, inp_par=None): par['calibrations']['tilts']['tracethresh'] = 10 par['calibrations']['tilts']['sig_neigh'] = 5.0 par['calibrations']['tilts']['nfwhm_neigh'] = 2.0 + elif '10/mmLBHR_G5532' in self.dispname: + # TODO :: Need to fill this in + pass else: - msgs.error('Unrecognized GNIRS dispname') + msgs.error(f'Unrecognized GNIRS dispname: {self.dispname}') return par From 2ee2f6426fcf9f1418dea8ab4def75f9fd7acaee Mon Sep 17 00:00:00 2001 From: rcooke Date: Thu, 22 Jun 2023 14:19:10 +0100 Subject: [PATCH 19/27] cleanup --- pypeit/scripts/arxiv_solution.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pypeit/scripts/arxiv_solution.py b/pypeit/scripts/arxiv_solution.py index b2f592fb1e..d0e47f31a2 100644 --- a/pypeit/scripts/arxiv_solution.py +++ b/pypeit/scripts/arxiv_solution.py @@ -44,8 +44,6 @@ def main(args): # Load the wavelength calibration file wv_calib = WaveCalib.from_file(args.file) - from IPython import embed - embed() wave = wv_calib['wv_fits'][args.slit]['wave_soln'].flatten() spec = wv_calib['wv_fits'][args.slit]['spec'].flatten() outname = args.file.replace(".fits", "_arXiv.fits") From faf70a9320a580f7223215194e027c9a57ecfac8 Mon Sep 17 00:00:00 2001 From: rcooke Date: Thu, 22 Jun 2023 14:20:12 +0100 Subject: [PATCH 20/27] changes --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 107bc70199..ff794e2758 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,8 @@ 1.13.1dev (6 June 2023) ------------------------ +- Add support for Gemini/GNIRS (IFU) +- Added a script to convert a wavelength solution into something that can be placed in the reid archive. - Hotfix for rebin (speed-up and conserves flux) - Hotfix for skysub regions GUI that used np.bool - Hotfix to stop pypeit_setup from crashing on data from lbt_luci1, lbt_luci2, magellan_fire, From 49e1897015794d9d741df435c88cd6069a11bd75 Mon Sep 17 00:00:00 2001 From: rcooke Date: Thu, 22 Jun 2023 20:21:24 +0100 Subject: [PATCH 21/27] fix tests --- pypeit/spectrographs/gemini_gnirs.py | 7 +------ pypeit/tests/test_pypeitpar.py | 4 ++-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/pypeit/spectrographs/gemini_gnirs.py b/pypeit/spectrographs/gemini_gnirs.py index 0bb64e2130..6587cb548d 100644 --- a/pypeit/spectrographs/gemini_gnirs.py +++ b/pypeit/spectrographs/gemini_gnirs.py @@ -28,12 +28,7 @@ class GeminiGNIRSSpectrograph(spectrograph.Spectrograph): def __init__(self): super().__init__() - # TODO :: Might need to change the tolerance of disperser angle in - # pypeit setup (two BH2 nights where sufficiently different that this - # was important). - - # TODO :: Might consider changing TelescopePar to use the astropy - # EarthLocation. KBW: Fine with me! + # TODO :: Might consider changing TelescopePar to use the astropy EarthLocation. self.location = EarthLocation.of_site('Gemini North') def get_detector_par(self, det, hdu=None): diff --git a/pypeit/tests/test_pypeitpar.py b/pypeit/tests/test_pypeitpar.py index 5115faf81d..dfb71b59d2 100644 --- a/pypeit/tests/test_pypeitpar.py +++ b/pypeit/tests/test_pypeitpar.py @@ -155,7 +155,7 @@ def test_telescope(): pypeitpar.TelescopePar() def test_fail_badpar(): - p = load_spectrograph('gemini_gnirs').default_pypeit_par() + p = load_spectrograph('gemini_gnirs_echelle').default_pypeit_par() # Faults because there's no junk parameter cfg_lines = ['[calibrations]', '[[biasframe]]', '[[[process]]]', 'junk = True'] @@ -164,7 +164,7 @@ def test_fail_badpar(): merge_with=cfg_lines) # Once as list def test_fail_badlevel(): - p = load_spectrograph('gemini_gnirs').default_pypeit_par() + p = load_spectrograph('gemini_gnirs_echelle').default_pypeit_par() # Faults because process isn't at the right level (i.e., there's no # process parameter for CalibrationsPar) From c930f56b0a97c8d324671c212175a32bdada7305 Mon Sep 17 00:00:00 2001 From: rcooke Date: Tue, 27 Jun 2023 13:30:52 +0100 Subject: [PATCH 22/27] update file keys --- pypeit/spectrographs/gemini_gnirs.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pypeit/spectrographs/gemini_gnirs.py b/pypeit/spectrographs/gemini_gnirs.py index 6587cb548d..8125f2ab1a 100644 --- a/pypeit/spectrographs/gemini_gnirs.py +++ b/pypeit/spectrographs/gemini_gnirs.py @@ -810,7 +810,4 @@ def pypeit_file_keys(self): :class:`~pypeit.metadata.PypeItMetaData` instance to print to the :ref:`pypeit_file`. """ - pypeit_keys = super().pypeit_file_keys() - # TODO :: Think about this... do we even need these extra file keys if skysub is done during the reduction - #pypeit_keys += ['calib', 'comb_id', 'bkg_id'] - return pypeit_keys + return super().pypeit_file_keys() + ['filter'] From e9e7b74149864526a872e29e0f64347ea7a6b8af Mon Sep 17 00:00:00 2001 From: rcooke Date: Mon, 10 Jul 2023 10:58:09 +0100 Subject: [PATCH 23/27] fix GTC OSIRIS --- pypeit/spectrographs/gtc_osiris.py | 44 +++++++++++++++--------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/pypeit/spectrographs/gtc_osiris.py b/pypeit/spectrographs/gtc_osiris.py index cead6e8dbb..4c007cfd0f 100644 --- a/pypeit/spectrographs/gtc_osiris.py +++ b/pypeit/spectrographs/gtc_osiris.py @@ -100,7 +100,7 @@ def default_pypeit_par(cls): par['calibrations']['pixelflatframe']['process']['combine'] = 'median' # Wavelength calibration methods par['calibrations']['wavelengths']['method'] = 'full_template' - par['calibrations']['wavelengths']['lamps'] = ['XeI,HgI,NeI,ArI'] + par['calibrations']['wavelengths']['lamps'] = ['XeI','HgI','NeI','ArI'] # Set the default exposure time ranges for the frame typing par['scienceframe']['exprng'] = [90, None] @@ -320,7 +320,7 @@ def config_specific_par(self, scifile, inp_par=None): # Wavelength calibration and setup-dependent parameters if self.get_meta_value(scifile, 'dispname') == 'R300B': - par['calibrations']['wavelengths']['lamps'] = ['XeI,HgI,NeI'] + par['calibrations']['wavelengths']['lamps'] = ['XeI','HgI','NeI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R300B.fits' par['reduce']['findobj']['find_min_max'] = [750, 2051] par['calibrations']['slitedges']['det_min_spec_length'] = 0.25 @@ -330,7 +330,7 @@ def config_specific_par(self, scifile, inp_par=None): par['reduce']['cube']['wave_min'] = 3600.0 par['reduce']['cube']['wave_max'] = 7200.0 elif self.get_meta_value(scifile, 'dispname') == 'R300R': - par['calibrations']['wavelengths']['lamps'] = ['XeI,HgI,NeI'] + par['calibrations']['wavelengths']['lamps'] = ['XeI','HgI','NeI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R300R.fits' par['reduce']['findobj']['find_min_max'] = [750, 2051] par['calibrations']['slitedges']['det_min_spec_length'] = 0.25 @@ -340,38 +340,38 @@ def config_specific_par(self, scifile, inp_par=None): par['reduce']['cube']['wave_min'] = 4800.0 par['reduce']['cube']['wave_max'] = 10000.0 elif self.get_meta_value(scifile, 'dispname') == 'R500B': - par['calibrations']['wavelengths']['lamps'] = ['HgI,NeI'] + par['calibrations']['wavelengths']['lamps'] = ['HgI','NeI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R500B.fits' par['reduce']['findobj']['find_min_max'] = [500, 2051] par['reduce']['cube']['wave_min'] = 3600.0 par['reduce']['cube']['wave_max'] = 7200.0 elif self.get_meta_value(scifile, 'dispname') == 'R500R': - par['calibrations']['wavelengths']['lamps'] = ['XeI,HgI,NeI'] + par['calibrations']['wavelengths']['lamps'] = ['XeI','HgI','NeI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R500R.fits' par['reduce']['findobj']['find_min_max'] = [450, 2051] par['reduce']['cube']['wave_min'] = 4800.0 par['reduce']['cube']['wave_max'] = 10000.0 elif self.get_meta_value(scifile, 'dispname') == 'R1000B': - par['calibrations']['wavelengths']['lamps'] = ['ArI,HgI,NeI'] + par['calibrations']['wavelengths']['lamps'] = ['ArI','HgI','NeI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R1000B.fits' elif self.get_meta_value(scifile, 'dispname') == 'R1000R': - par['calibrations']['wavelengths']['lamps'] = ['XeI,HgI,NeI'] + par['calibrations']['wavelengths']['lamps'] = ['XeI','HgI','NeI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R1000R.fits' elif self.get_meta_value(scifile, 'dispname') == 'R2000B': par['calibrations']['wavelengths']['fwhm'] = 15.0 - par['calibrations']['wavelengths']['lamps'] = ['XeI,HgI,NeI'] + par['calibrations']['wavelengths']['lamps'] = ['XeI','HgI','NeI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R2000B.fits' elif self.get_meta_value(scifile, 'dispname') == 'R2500U': - par['calibrations']['wavelengths']['lamps'] = ['XeI,HgI'] + par['calibrations']['wavelengths']['lamps'] = ['XeI','HgI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R2500U.fits' elif self.get_meta_value(scifile, 'dispname') == 'R2500V': par['calibrations']['wavelengths']['lamps'] = ['HgI','NeI','XeI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R2500V.fits' elif self.get_meta_value(scifile, 'dispname') == 'R2500R': - par['calibrations']['wavelengths']['lamps'] = ['ArI,HgI,NeI'] + par['calibrations']['wavelengths']['lamps'] = ['ArI','HgI','NeI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R2500R.fits' elif self.get_meta_value(scifile, 'dispname') == 'R2500I': - par['calibrations']['wavelengths']['lamps'] = ['ArI,XeI,NeI'] + par['calibrations']['wavelengths']['lamps'] = ['ArI','XeI','NeI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R2500I.fits' par['sensfunc']['algorithm'] = 'IR' par['sensfunc']['IR']['telgridfile'] = "TelFit_MaunaKea_3100_26100_R20000.fits" @@ -711,7 +711,7 @@ def default_pypeit_par(cls): par['calibrations']['pixelflatframe']['process']['combine'] = 'median' # Wavelength calibration methods par['calibrations']['wavelengths']['method'] = 'full_template' - par['calibrations']['wavelengths']['lamps'] = ['XeI,HgI,NeI,ArI'] + par['calibrations']['wavelengths']['lamps'] = ['XeI','HgI','NeI','ArI'] # Set the default exposure time ranges for the frame typing par['scienceframe']['exprng'] = [90, None] @@ -906,41 +906,41 @@ def config_specific_par(self, scifile, inp_par=None): # Wavelength calibrations if self.get_meta_value(scifile, 'dispname') == 'R300B': - par['calibrations']['wavelengths']['lamps'] = ['XeI,HgI,NeI'] + par['calibrations']['wavelengths']['lamps'] = ['XeI','HgI','NeI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R300B.fits' par['reduce']['findobj']['find_min_max']=[750,2051] elif self.get_meta_value(scifile, 'dispname') == 'R300R': - par['calibrations']['wavelengths']['lamps'] = ['XeI,HgI,NeI'] + par['calibrations']['wavelengths']['lamps'] = ['XeI','HgI','NeI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R300R.fits' par['reduce']['findobj']['find_min_max']=[750,2051] elif self.get_meta_value(scifile, 'dispname') == 'R500B': - par['calibrations']['wavelengths']['lamps'] = ['HgI,NeI'] + par['calibrations']['wavelengths']['lamps'] = ['HgI','NeI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R500B.fits' par['reduce']['findobj']['find_min_max']=[500,2051] elif self.get_meta_value(scifile, 'dispname') == 'R500R': - par['calibrations']['wavelengths']['lamps'] = ['XeI,HgI,NeI'] + par['calibrations']['wavelengths']['lamps'] = ['XeI','HgI','NeI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R500R.fits' par['reduce']['findobj']['find_min_max']=[450,2051] elif self.get_meta_value(scifile, 'dispname') == 'R1000B': - par['calibrations']['wavelengths']['lamps'] = ['ArI,HgI,NeI'] + par['calibrations']['wavelengths']['lamps'] = ['ArI','HgI','NeI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R1000B.fits' elif self.get_meta_value(scifile, 'dispname') == 'R1000R': - par['calibrations']['wavelengths']['lamps'] = ['XeI,HgI,NeI'] + par['calibrations']['wavelengths']['lamps'] = ['XeI','HgI','NeI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R1000R.fits' elif self.get_meta_value(scifile, 'dispname') == 'R2000B': - par['calibrations']['wavelengths']['lamps'] = ['XeI,HgI,NeI'] + par['calibrations']['wavelengths']['lamps'] = ['XeI','HgI','NeI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R2000B.fits' elif self.get_meta_value(scifile, 'dispname') == 'R2500U': - par['calibrations']['wavelengths']['lamps'] = ['XeI,HgI'] + par['calibrations']['wavelengths']['lamps'] = ['XeI','HgI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R2500U.fits' elif self.get_meta_value(scifile, 'dispname') == 'R2500V': par['calibrations']['wavelengths']['lamps'] = ['HgI','NeI','XeI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R2500V.fits' elif self.get_meta_value(scifile, 'dispname') == 'R2500R': - par['calibrations']['wavelengths']['lamps'] = ['ArI,HgI,NeI'] + par['calibrations']['wavelengths']['lamps'] = ['ArI','HgI','NeI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R2500R.fits' elif self.get_meta_value(scifile, 'dispname') == 'R2500I': - par['calibrations']['wavelengths']['lamps'] = ['ArI,XeI,NeI'] + par['calibrations']['wavelengths']['lamps'] = ['ArI','XeI','NeI'] par['calibrations']['wavelengths']['reid_arxiv'] = 'gtc_osiris_R2500I.fits' par['sensfunc']['algorithm'] = 'IR' par['sensfunc']['IR']['telgridfile'] = "TelFit_MaunaKea_3100_26100_R20000.fits" From a0368719c49b819d8319afa75f05b3e6149b5b04 Mon Sep 17 00:00:00 2001 From: rcooke Date: Mon, 10 Jul 2023 10:58:15 +0100 Subject: [PATCH 24/27] fix plotting --- pypeit/core/wavecal/autoid.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pypeit/core/wavecal/autoid.py b/pypeit/core/wavecal/autoid.py index 5dce82c7b9..7cdc7415ef 100644 --- a/pypeit/core/wavecal/autoid.py +++ b/pypeit/core/wavecal/autoid.py @@ -176,8 +176,8 @@ def arc_fit_qa(waveFit, outfile=None, ids_only=False, title=None, # Stats wave_soln_fit = waveFit.pypeitfit.eval(waveFit.pixel_fit/waveFit.xnorm)#, 'legendre',minx=fit['fmin'], maxx=fit['fmax']) - ax_fit.text(0.1*len(arc_spec), 0.90*ymin+(ymax-ymin),r'$\Delta\lambda$={:.3f}$\AA$ (per pix)'.format(waveFit.cen_disp), size='small') - ax_fit.text(0.1*len(arc_spec), 0.80*ymin+(ymax-ymin),'RMS={:.3f} (pixels)'.format(waveFit.rms), size='small') + ax_fit.text(0.1, 0.9, r'$\Delta\lambda$={:.3f}$\AA$ (per pix)'.format(waveFit.cen_disp), size='small', transform=ax_fit.transAxes) + ax_fit.text(0.1, 0.8, 'RMS={:.3f} (pixels)'.format(waveFit.rms), size='small', transform=ax_fit.transAxes) # Arc Residuals ax_res = plt.subplot(gs[1,1]) res = waveFit.wave_fit-wave_soln_fit From e779a2ecc890aa930e22f8462b28895ffc894814 Mon Sep 17 00:00:00 2001 From: rcooke Date: Mon, 10 Jul 2023 10:59:15 +0100 Subject: [PATCH 25/27] fix plotting --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index e3e9aa7f19..57ce3e4cb3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,8 @@ 1.13.1dev (6 June 2023) ------------------------ +- Hotfix for GTC/OSIRIS lamp list +- Hotfix for Arc1D stats annotations on the QA - Hotfix for metadata (correctly set config_independent frames when multiple configurations are being setup) - Hotfix for rebin (speed-up and conserves flux) - Hotfix for skysub regions GUI that used np.bool From d8f6d050fb22cfc66ca41e002dc6c0da3be29c98 Mon Sep 17 00:00:00 2001 From: rcooke-ast Date: Wed, 12 Jul 2023 12:28:46 +0100 Subject: [PATCH 26/27] fix mask check --- pypeit/find_objects.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pypeit/find_objects.py b/pypeit/find_objects.py index 53422c1b66..3bb531a651 100644 --- a/pypeit/find_objects.py +++ b/pypeit/find_objects.py @@ -1184,10 +1184,12 @@ def joint_skysub(self, skymask=None, update_crmask=True, trim_edg=(0,0), counts = global_sky _scale = None if self.sciImg.img_scale is None else self.sciImg.img_scale[thismask] # NOTE: darkcurr must be a float for the call below to work. - var = procimg.variance_model(self.sciImg.base_var[thismask], counts=counts[thismask], - count_scale=_scale, noise_floor=adderr) - model_ivar[thismask] = utils.inverse(var) - + if not self.bkg_redux: + var = procimg.variance_model(self.sciImg.base_var[thismask], counts=counts[thismask], + count_scale=_scale, noise_floor=adderr) + model_ivar[thismask] = utils.inverse(var) + else: + model_ivar[thismask] = self.sciImg.ivar[thismask] # RJC :: Recalculating the global sky and flexure is probably overkill... but please keep this code in for now # Recalculate the sky on each individual slit and redetermine the spectral flexure # global_sky_sep = super().global_skysub(skymask=skymask, update_crmask=update_crmask, @@ -1227,7 +1229,8 @@ def global_skysub(self, skymask=None, update_crmask=True, trim_edg=(0,0), global_sky_sep = super().global_skysub(skymask=skymask, update_crmask=update_crmask, trim_edg=trim_edg, show_fit=show_fit, show=show, show_objs=show_objs) - if np.any(global_sky_sep[skymask] == 0): + # Check if any slits failed + if np.any(global_sky_sep[self.slitmask>=0] == 0) and not self.bkg_redux: # Cannot continue without a sky model for all slits msgs.error("Global sky subtraction has failed for at least one slit.") From 9fe78e5b3becf2a6277d5eea73f40cb5803e11e0 Mon Sep 17 00:00:00 2001 From: rcooke Date: Wed, 12 Jul 2023 21:17:00 +0100 Subject: [PATCH 27/27] gnirs docs --- doc/include/spectrographs_table.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/include/spectrographs_table.rst b/doc/include/spectrographs_table.rst index abb5d645f0..82de9081ed 100644 --- a/doc/include/spectrographs_table.rst +++ b/doc/include/spectrographs_table.rst @@ -8,8 +8,9 @@ gemini_gmos_north_e2v :class:`~pypeit.spectrographs.gemini_gmos.GeminiGMOSNE gemini_gmos_north_ham :class:`~pypeit.spectrographs.gemini_gmos.GeminiGMOSNHamSpectrograph` GEMINI-N GMOS-N `Link `__ MultiSlit True False Hamamatsu detector (R400, B600, R831); Used since Feb 2017; see :doc:`gemini_gmos` gemini_gmos_north_ham_ns :class:`~pypeit.spectrographs.gemini_gmos.GeminiGMOSNHamNSSpectrograph` GEMINI-N GMOS-N `Link `__ MultiSlit True False Same as gemini_gmos_north_ham when used in nod-and-shuffle mode; see :doc:`gemini_gmos` gemini_gmos_south_ham :class:`~pypeit.spectrographs.gemini_gmos.GeminiGMOSSHamSpectrograph` GEMINI-S GMOS-S `Link `__ MultiSlit True False Hamamatsu detector (R400, B600, R831); see :doc:`gemini_gmos` -gemini_gnirs :class:`~pypeit.spectrographs.gemini_gnirs.GeminiGNIRSSpectrograph` GEMINI-N GNIRS `Link `__ Echelle True False -gtc_maat :class:`~pypeit.spectrographs.gtc_osiris.GTCMAATSpectrograph` GTC OSIRIS `Link `__ IFU True False See :doc:`gtc_osiris` +gemini_gnirs_echelle :class:`~pypeit.spectrographs.gemini_gnirs.GeminiGNIRSEchelleSpectrograph` GEMINI-N GNIRS `Link `__ Echelle True False +gemini_gnirs_ifu :class:`~pypeit.spectrographs.gemini_gnirs.GNIRSIFUSpectrograph` GEMINI-N GNIRS `Link `__ IFU True False Support for LR-IFU and HR-IFU mode. Further testing still required... +gtc_maat :class:`~pypeit.spectrographs.gtc_osiris.GTCMAATSpectrograph` GTC OSIRIS `Link `__ IFU True False See :doc:`gtc_osiris` gtc_osiris :class:`~pypeit.spectrographs.gtc_osiris.GTCOSIRISSpectrograph` GTC OSIRIS `Link `__ MultiSlit True False See :doc:`gtc_osiris` gtc_osiris_plus :class:`~pypeit.spectrographs.gtc_osiris.GTCOSIRISPlusSpectrograph` GTC OSIRIS `Link `__ MultiSlit True False See :doc:`gtc_osiris` jwst_nircam :class:`~pypeit.spectrographs.jwst_nircam.JWSTNIRCamSpectrograph` JWST NIRCAM `Link `__ MultiSlit False False