From 7755dee0f047661b3b02da3ee51a110b0cc9ff04 Mon Sep 17 00:00:00 2001 From: rcooke Date: Wed, 2 Oct 2024 13:05:30 +0100 Subject: [PATCH] review comments addressed --- deprecated/flat.py | 2 +- pypeit/calibrations.py | 2 +- pypeit/core/tracewave.py | 18 ++++++++++++++---- pypeit/flatfield.py | 7 ++++--- pypeit/images/pypeitimage.py | 12 +++++++++--- pypeit/images/rawimage.py | 10 +++++----- pypeit/par/pypeitpar.py | 18 +++++++++--------- pypeit/pypeit.py | 6 +++--- pypeit/spectrographs/gemini_gnirs.py | 2 +- pypeit/spectrographs/gtc_osiris.py | 2 +- pypeit/spectrographs/keck_kcwi.py | 2 +- pypeit/spectrographs/keck_lris.py | 4 ++-- pypeit/wavetilts.py | 6 +++--- 13 files changed, 54 insertions(+), 37 deletions(-) diff --git a/deprecated/flat.py b/deprecated/flat.py index f59334af70..65e737494a 100644 --- a/deprecated/flat.py +++ b/deprecated/flat.py @@ -226,7 +226,7 @@ def fit_flat(flat, tilts_dict, tslits_dict_in, slit, inmask = None, slitmask_pad = pixels.tslits2mask(tslits_dict_in, pad = pad) thismask = (slitmask_pad == slit) # mask enclosing the wider slit bounadries # Create a tilts image using this padded thismask, rather than using the original thismask_in slit pixels - tilts = tracewave.fit2tilts(shape, tilts_dict['coeffs'], tilts_dict['func2d']) + tilts = tracewave.fit2tilts(tilts_dict['coeffs'], tilts_dict['func2d'], shape=shape) piximg = tilts * (nspec-1) pixvec = np.arange(nspec) diff --git a/pypeit/calibrations.py b/pypeit/calibrations.py index c89b2ce7d8..167cefddca 100644 --- a/pypeit/calibrations.py +++ b/pypeit/calibrations.py @@ -1173,7 +1173,7 @@ def get_tilts(self): return self.wavetilts # Get flexure - _spat_flexure = self.mstilt.spat_flexure if self.par['tiltframe']['process']['spat_flexure_correct'] != "none" \ + _spat_flexure = self.mstilt.spat_flexure if self.par['tiltframe']['process']['spat_flexure_method'] != "skip" \ else None # Build diff --git a/pypeit/core/tracewave.py b/pypeit/core/tracewave.py index e6e0ad325c..9f68ac2419 100644 --- a/pypeit/core/tracewave.py +++ b/pypeit/core/tracewave.py @@ -900,18 +900,20 @@ def fit2tilts_prepareSlit(slit_left, slit_right, thismask_science, spat_flexure= return _spec_eval, _spat_eval -def fit2tilts(shape, coeff2, func2d, spec_eval=None, spat_eval=None): +def fit2tilts(coeff2, func2d, shape=None, spec_eval=None, spat_eval=None): """ - Evaluate the wavelength tilt model over the full image. + Evaluate the wavelength tilt model over the full image. Note that this function + requires either shape or both spec_eval and spat_eval to be provided. If all three + are provided, spec_eval and spat_eval will be used. Parameters ---------- - shape : tuple of ints, - shape of image coeff2 : `numpy.ndarray`_, float result of griddata tilt fit func2d : str the 2d function used to fit the tilts + shape : tuple of ints, optional + Shape of image. Only used if spat_eval and spec_eval are not provided. spat_eval : `numpy.ndarray`_, optional 1D array indicating how spatial pixel locations move across the image. If spat_eval is provided, spec_eval must also be provided. @@ -937,6 +939,14 @@ def fit2tilts(shape, coeff2, func2d, spec_eval=None, spat_eval=None): if (spec_eval is None and spat_eval is not None) or (spec_eval is not None and spat_eval is None): msgs.warn('Both spec_eval and spat_eval must be provided.' + msgs.newline() + 'Only one variable provided, so a new (full) grid will be generated.') + # Print a warning if neither are provided + if spec_eval is None and spat_eval is None: + msgs.warn('No spatial and spectral coordinates provided.' + msgs.newline() + + 'A new (full) grid will be generated.') + # Print a warning is shape is not provided + if shape is None: + msgs.error('No shape provided for the image.' + msgs.newline() + + 'You must provide either `shape` or both `spat_eval` and `spec_eval`.') msgs.warn("Assuming no spatial flexure.") _spat_shift = 0.0 # Setup the evaluation grid diff --git a/pypeit/flatfield.py b/pypeit/flatfield.py index ae94ef053b..03de5e41b9 100644 --- a/pypeit/flatfield.py +++ b/pypeit/flatfield.py @@ -711,7 +711,7 @@ def build_waveimg(self): msgs.error("Wavelength calib or tilts are not available. Cannot generate wavelength image.") else: spat_flexure = self.wavetilts.spat_flexure - left, right, msk = self.slits.select_edges(spat_flexure=spat_flexure) + left, right, msk = self.slits.select_edges(initial=True, spat_flexure=spat_flexure) slitmask = self.slits.slit_img(initial=True, spat_flexure=spat_flexure) tilts = self.wavetilts.fit2tiltimg(slitmask, left, right, spat_flexure=spat_flexure) # Save to class attribute for inclusion in the Flat calibration frame @@ -1004,8 +1004,9 @@ def fit(self, spat_illum_only=False, doqa=True, debug=False): self.slits.right_init[:, slit_idx], onslit_init, _flexure) tilts = np.zeros(rawflat.shape, dtype=float) - tilts[onslit_init] = tracewave.fit2tilts(rawflat.shape, self.wavetilts['coeffs'][:,:,slit_idx], - self.wavetilts['func2d'], spec_eval=_spec_eval, spat_eval=_spat_eval) + tilts[onslit_init] = tracewave.fit2tilts(self.wavetilts['coeffs'][:,:,slit_idx], + self.wavetilts['func2d'], + spec_eval=_spec_eval, spat_eval=_spat_eval) # Convert the tilt image to an image with the spectral pixel index spec_coo = tilts * (nspec-1) diff --git a/pypeit/images/pypeitimage.py b/pypeit/images/pypeitimage.py index a39e951552..d69f174b22 100644 --- a/pypeit/images/pypeitimage.py +++ b/pypeit/images/pypeitimage.py @@ -785,10 +785,16 @@ def sub(self, other): # Spatial flexure spat_flexure = self.spat_flexure if other.spat_flexure is not None and spat_flexure is not None \ - and np.array_equal(other.spat_flexure, spat_flexure): - msgs.warn(f'Spatial flexure different for images being subtracted. Adopting ' \ + and not np.array_equal(other.spat_flexure, spat_flexure): + msgs.warn(f'Spatial flexure different for images being subtracted. Adopting ' f'the maximum spatial flexure of each individual edge.') - spat_flexure = np.maximum(spat_flexure, other.spat_flexure) + # Loop through all slit edges and find the largest flexure + for ii in range(spat_flexure.shape[0]): + # Assign the largest flexure (irrespective of sign) for each edge + if np.abs(other.spat_flexure[ii,0]) > np.abs(spat_flexure[ii,0]): + spat_flexure[ii,0] = other.spat_flexure[ii,0] + if np.abs(other.spat_flexure[ii,1]) > np.abs(spat_flexure[ii,1]): + spat_flexure[ii,1] = other.spat_flexure[ii,1] # Create a copy of the detector, if it is defined, to be used when # creating the new pypeit image below diff --git a/pypeit/images/rawimage.py b/pypeit/images/rawimage.py index 37c1a4ab25..41907a8fca 100644 --- a/pypeit/images/rawimage.py +++ b/pypeit/images/rawimage.py @@ -241,7 +241,7 @@ def use_slits(self): """ if self.par is None: return False - return (self.par['spat_flexure_correct'] != "none") or (self.use_flat and self.par['use_illumflat']) + return (self.par['spat_flexure_method'] != "skip") or (self.use_flat and self.par['use_illumflat']) def apply_gain(self, force=False): """ @@ -506,7 +506,7 @@ def process(self, par, bpm=None, scattlight=None, flatimages=None, bias=None, sl Bias image for bias subtraction. slits (:class:`~pypeit.slittrace.SlitTraceSet`, optional): Used to calculate spatial flexure between the image and the - slits, if requested via the ``spat_flexure_correct`` parameter + slits, if requested via the ``spat_flexure_method`` parameter in :attr:`par`; see :func:`~pypeit.core.flexure.spat_flexure_shift`. Also used to construct the slit illumination profile, if requested via the @@ -546,7 +546,7 @@ def process(self, par, bpm=None, scattlight=None, flatimages=None, bias=None, sl msgs.error('No dark available for dark subtraction!') if self.par['subtract_scattlight'] and scattlight is None: msgs.error('Scattered light subtraction requested, but scattered light model not provided.') - if (self.par['spat_flexure_correct'] != "none") and slits is None: + if (self.par['spat_flexure_method'] != "skip") and slits is None: msgs.error('Spatial flexure correction requested but no slits provided.') if self.use_flat and flatimages is None: msgs.error('Flat-field corrections requested but no flat-field images generated ' @@ -671,9 +671,9 @@ def process(self, par, bpm=None, scattlight=None, flatimages=None, bias=None, sl # bias and dark subtraction) and before field flattening. Also the # function checks that the slits exist if running the spatial flexure # correction, so no need to do it again here. - self.spat_flexure_shift = self.spatial_flexure_shift(slits, method=self.par['spat_flexure_correct'], + self.spat_flexure_shift = self.spatial_flexure_shift(slits, method=self.par['spat_flexure_method'], maxlag=self.par['spat_flexure_maxlag']) \ - if self.par['spat_flexure_correct'] != "none" else None + if self.par['spat_flexure_method'] != "skip" else None # - Subtract scattered light... this needs to be done before flatfielding. if self.par['subtract_scattlight']: diff --git a/pypeit/par/pypeitpar.py b/pypeit/par/pypeitpar.py index a7028441b9..1cef1022d3 100644 --- a/pypeit/par/pypeitpar.py +++ b/pypeit/par/pypeitpar.py @@ -222,7 +222,7 @@ def __init__(self, trim=None, apply_gain=None, orient=None, empirical_rn=None, shot_noise=None, noise_floor=None, use_pixelflat=None, use_illumflat=None, use_specillum=None, use_pattern=None, subtract_scattlight=None, scattlight=None, subtract_continuum=None, - spat_flexure_correct=None, spat_flexure_maxlag=None): + spat_flexure_method=None, spat_flexure_maxlag=None): # Grab the parameter names and values from the function # arguments @@ -356,12 +356,12 @@ def __init__(self, trim=None, apply_gain=None, orient=None, '``slit_illum_relative=True`` in the ``flatfield`` parameter set!' # Flexure - defaults['spat_flexure_correct'] = 'none' - options['spat_flexure_correct'] = ProcessImagesPar.valid_spatial_flexure() - dtypes['spat_flexure_correct'] = str - descr['spat_flexure_correct'] = 'Correct slits, illumination flat, etc. for spatial flexure. ' \ - 'Options are: {0}'.format(', '.join(options['spat_flexure_correct'])) + \ - '"none" means no correction is performed. ' \ + defaults['spat_flexure_method'] = 'skip' + options['spat_flexure_method'] = ProcessImagesPar.valid_spatial_flexure() + dtypes['spat_flexure_method'] = str + descr['spat_flexure_method'] = 'Correct slits, illumination flat, etc. for spatial flexure. ' \ + 'Options are: {0}'.format(', '.join(options['spat_flexure_method'])) + \ + '"skip" means no correction is performed. ' \ '"detector" means that a single shift is applied to all slits. ' \ '"slit" means that each slit is shifted independently.' \ '"edge" means that each slit edge is shifted independently.' @@ -467,7 +467,7 @@ def from_dict(cls, cfg): parkeys = ['trim', 'apply_gain', 'orient', 'use_biasimage', 'subtract_continuum', 'subtract_scattlight', 'scattlight', 'use_pattern', 'use_overscan', 'overscan_method', 'overscan_par', 'use_darkimage', 'dark_expscale', - 'spat_flexure_correct', 'spat_flexure_maxlag', 'use_illumflat', 'use_specillum', + 'spat_flexure_method', 'spat_flexure_maxlag', 'use_illumflat', 'use_specillum', 'empirical_rn', 'shot_noise', 'noise_floor', 'use_pixelflat', 'combine', 'scale_to_mean', 'correct_nonlinear', 'satpix', #'calib_setup_and_bit', 'n_lohi', 'mask_cr', 'lamaxiter', 'grow', 'clip', 'comb_sigrej', 'rmcompact', @@ -504,7 +504,7 @@ def valid_spatial_flexure(): """ Return the valid methods for combining frames. """ - return ['none', 'detector', 'slit', 'edge'] + return ['skip', 'detector', 'slit', 'edge'] @staticmethod def valid_saturation_handling(): diff --git a/pypeit/pypeit.py b/pypeit/pypeit.py index b8edbe7a17..824924dbd2 100644 --- a/pypeit/pypeit.py +++ b/pypeit/pypeit.py @@ -814,11 +814,11 @@ def objfind_one(self, frames, det, bg_frames=None, std_outfile=None): spat_flexure = np.zeros((self.caliBrate.slits.nslits, 2)) # No spatial flexure, unless we find it below # use the flexure correction in the "shift" column manual_flexure = self.fitstbl[frames[0]]['shift'] - if (self.objtype == 'science' and self.par['scienceframe']['process']['spat_flexure_correct'] != "none") or \ - (self.objtype == 'standard' and self.par['calibrations']['standardframe']['process']['spat_flexure_correct'] != "none") or \ + if (self.objtype == 'science' and self.par['scienceframe']['process']['spat_flexure_method'] != "skip") or \ + (self.objtype == 'standard' and self.par['calibrations']['standardframe']['process']['spat_flexure_method'] != "skip") or \ manual_flexure: if (manual_flexure != self.fitstbl.MASKED_VALUE) and np.issubdtype(self.fitstbl[frames[0]]["shift"], np.integer): - msgs.info(f'Implementing manual spatial flexure of {manual_flexure}') + msgs.info(f'Implementing manual spatial flexure of {manual_flexure} pixels') spat_flexure = np.full((self.caliBrate.slits.nslits, 2), np.float64(manual_flexure)) sciImg.spat_flexure = spat_flexure else: diff --git a/pypeit/spectrographs/gemini_gnirs.py b/pypeit/spectrographs/gemini_gnirs.py index 5871bbd8a5..60a7bf8577 100644 --- a/pypeit/spectrographs/gemini_gnirs.py +++ b/pypeit/spectrographs/gemini_gnirs.py @@ -606,7 +606,7 @@ def default_pypeit_par(cls): 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'] = "none" # 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']['spat_flexure_method'] = "skip" # 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 diff --git a/pypeit/spectrographs/gtc_osiris.py b/pypeit/spectrographs/gtc_osiris.py index 80eb64a5d4..922fa72546 100644 --- a/pypeit/spectrographs/gtc_osiris.py +++ b/pypeit/spectrographs/gtc_osiris.py @@ -460,7 +460,7 @@ def default_pypeit_par(cls): 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'] = "none" # 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']['spat_flexure_method'] = "skip" # 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 diff --git a/pypeit/spectrographs/keck_kcwi.py b/pypeit/spectrographs/keck_kcwi.py index 218fb99a69..1ead2754b6 100644 --- a/pypeit/spectrographs/keck_kcwi.py +++ b/pypeit/spectrographs/keck_kcwi.py @@ -319,7 +319,7 @@ def default_pypeit_par(cls): # Illumination corrections par['scienceframe']['process']['use_illumflat'] = True # illumflat is applied when building the relative scale image in reduce.py, so should be applied to scienceframe too. par['scienceframe']['process']['use_specillum'] = True # apply relative spectral illumination - par['scienceframe']['process']['spat_flexure_correct'] = "none" # 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']['spat_flexure_method'] = "skip" # 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'] = True # Need to use bias frames for KCWI, because the bias level varies monotonically with spatial and spectral direction par['scienceframe']['process']['use_darkimage'] = False diff --git a/pypeit/spectrographs/keck_lris.py b/pypeit/spectrographs/keck_lris.py index 07848a8294..2b63aa1c7e 100644 --- a/pypeit/spectrographs/keck_lris.py +++ b/pypeit/spectrographs/keck_lris.py @@ -102,8 +102,8 @@ def default_pypeit_par(cls): # Always correct for spatial flexure on science images # TODO -- Decide whether to make the following defaults # May not want to do them for LongSlit - par['scienceframe']['process']['spat_flexure_correct'] = "detector" - par['calibrations']['standardframe']['process']['spat_flexure_correct'] = "detector" + par['scienceframe']['process']['spat_flexure_method'] = "detector" + par['calibrations']['standardframe']['process']['spat_flexure_method'] = "detector" par['scienceframe']['exprng'] = [61, None] diff --git a/pypeit/wavetilts.py b/pypeit/wavetilts.py index 818f5cef72..cd89b9d589 100644 --- a/pypeit/wavetilts.py +++ b/pypeit/wavetilts.py @@ -172,7 +172,7 @@ def fit2tiltimg(self, slitmask, slits_left, slits_right, spat_flexure=None): _spec_eval, _spat_eval = tracewave.fit2tilts_prepareSlit(slits_left[:, slit_idx], slits_right[:, slit_idx], thismask_science, _spat_flexure[slit_idx, :]) # Calculate the tilts - final_tilts[thismask_science] = tracewave.fit2tilts(final_tilts.shape, coeff_out, self.func2d, + final_tilts[thismask_science] = tracewave.fit2tilts(coeff_out, self.func2d, spec_eval=_spec_eval, spat_eval=_spat_eval) # Return return final_tilts @@ -797,8 +797,8 @@ def run(self, doqa=True, debug=False, show=False): thismask_science = self.slitmask_science == slit_spat _spec_eval, _spat_eval = tracewave.fit2tilts_prepareSlit(slits_left[:, slit_idx], slits_right[:, slit_idx], thismask_science, self.spat_flexure[slit_idx, :]) - self.final_tilts[thismask_science] = tracewave.fit2tilts(self.slitmask_science.shape, coeff_out, self.par['func2d'], - spec_eval=_spec_eval, spat_eval=_spat_eval) + self.final_tilts[thismask_science] = tracewave.fit2tilts(coeff_out, self.par['func2d'], + spec_eval=_spec_eval, spat_eval=_spat_eval) if show: viewer, ch = display.show_image(self.mstilt.image * (self.slitmask > -1), chname='tilts')