From 13daa62b0ae81fbbd283dc20d9f88b183ab90b5a Mon Sep 17 00:00:00 2001 From: rcooke Date: Wed, 11 Sep 2024 21:41:29 +0100 Subject: [PATCH] corrected spatial flexure in flatfield --- deprecated/find_objects.py | 2 +- deprecated/reduce.py | 2 +- pypeit/coadd2d.py | 2 +- pypeit/coadd3d.py | 2 +- pypeit/flatfield.py | 46 ++++++++++++++++++-------------------- pypeit/slittrace.py | 8 +++---- 6 files changed, 30 insertions(+), 32 deletions(-) diff --git a/deprecated/find_objects.py b/deprecated/find_objects.py index 819ccac820..23b93749ac 100644 --- a/deprecated/find_objects.py +++ b/deprecated/find_objects.py @@ -290,7 +290,7 @@ def illum_profile_spectral(self, global_sky, skymask=None): # relative spectral sensitivity is calculated at a given wavelength for all slits simultaneously. scaleImg = flatfield.illum_profile_spectral(self.sciImg.image.copy(), self.waveimg, self.slits, slit_illum_ref_idx=sl_ref, model=global_sky, gpmask=gpm, - skymask=skymask, trim=trim, flexure=self.spat_flexure_shift, + skymask=skymask, trim=trim, spat_flexure=self.spat_flexure_shift, smooth_npix=smooth_npix) # Now apply the correction to the science frame self.apply_relative_scale(scaleImg) diff --git a/deprecated/reduce.py b/deprecated/reduce.py index fa5a803ec2..610e281575 100644 --- a/deprecated/reduce.py +++ b/deprecated/reduce.py @@ -1701,7 +1701,7 @@ def illum_profile_spectral(self, global_sky, skymask=None): gpm = self.sciImg.select_flag(invert=True) scaleImg = flatfield.illum_profile_spectral(self.sciImg.image.copy(), self.waveimg, self.slits, slit_illum_ref_idx=ref_idx, model=global_sky, gpmask=gpm, - skymask=skymask, trim=trim, flexure=self.spat_flexure_shift) + skymask=skymask, trim=trim, spat_flexure=self.spat_flexure_shift) # Now apply the correction to the science frame self.apply_relative_scale(scaleImg) diff --git a/pypeit/coadd2d.py b/pypeit/coadd2d.py index 9f7ceaf5b6..42941f22f5 100644 --- a/pypeit/coadd2d.py +++ b/pypeit/coadd2d.py @@ -838,7 +838,7 @@ def reduce(self, pseudo_dict, show=False, clear_ginga=True, show_peaks=False, sh platescale = sciImage.detector.platescale * self.spat_samp_fact # Assign slitmask design information to detected objects - slits.assign_maskinfo(sobjs_obj, platescale, None, TOLER=parcopy['reduce']['slitmask']['obj_toler']) + slits.assign_maskinfo(sobjs_obj, platescale, None, tolerance=parcopy['reduce']['slitmask']['obj_toler']) if parcopy['reduce']['slitmask']['extract_missing_objs'] is True: # Set the FWHM for the extraction of missing objects diff --git a/pypeit/coadd3d.py b/pypeit/coadd3d.py index 6473aa6aa6..3a7aefe88b 100644 --- a/pypeit/coadd3d.py +++ b/pypeit/coadd3d.py @@ -846,7 +846,7 @@ def add_grating_corr(self, flatfile, waveimg, slits, spat_flexure=None): scale_model = flatfield.illum_profile_spectral(flatframe, waveimg, slits, slit_illum_ref_idx=self.flatpar['slit_illum_ref_idx'], model=None, trim=self.flatpar['slit_trim'], - flexure=spat_flexure, + spat_flexure=spat_flexure, smooth_npix=self.flatpar['slit_illum_smooth_npix']) else: msgs.info("Using relative spectral illumination from FlatImages") diff --git a/pypeit/flatfield.py b/pypeit/flatfield.py index 709f1a1cb1..dc0edfb4e1 100644 --- a/pypeit/flatfield.py +++ b/pypeit/flatfield.py @@ -856,10 +856,6 @@ def fit(self, spat_illum_only=False, doqa=True, debug=False): npoly = self.flatpar['twod_fit_npoly'] saturated_slits = self.flatpar['saturated_slits'] - # Build wavelength image -- not always used, but for convenience done here - # TODO :: This was deleted in a recent PR -- figure out why? I think it was moved to the init method - if self.waveimg is None: self.build_waveimg() - # Setup images nspec, nspat = self.rawflatimg.image.shape rawflat = self.rawflatimg.image @@ -1487,8 +1483,8 @@ def spatial_fit_finecorr(self, normed, onslit_tweak, slit_idx, slit_spat, gpm, onslit_tweak_trim = self.slits.slit_img(pad=-slit_trim, slitidx=slit_idx, initial=False) == slit_spat # Setup slitimg = (slit_spat + 1) * onslit_tweak.astype(int) - 1 # Need to +1 and -1 so that slitimg=-1 when off the slit - # TODO :: need to fix the 0.0 value on the next line, and the same one a few lines below. - left, right, msk = self.slits.select_edges(spat_flexure=self.wavetilts.spat_flexure if self.wavetilts is not None else 0.0) + spat_flexure = self.wavetilts.spat_flexure if self.wavetilts is not None else np.zeros((self.slits.nslits, 2)) + left, right, msk = self.slits.select_edges(spat_flexure=spat_flexure) this_left = left[:, slit_idx] this_right = right[:, slit_idx] slitlen = int(np.median(this_right - this_left)) @@ -1498,7 +1494,7 @@ def spatial_fit_finecorr(self, normed, onslit_tweak, slit_idx, slit_spat, gpm, this_wave = self.waveimg[this_slit] xpos_img = self.slits.spatial_coordinate_image(slitidx=slit_idx, slitid_img=slitimg, - spat_flexure=self.wavetilts.spat_flexure if self.wavetilts is not None else 0.0) + spat_flexure=spat_flexure) # Generate the trimmed versions for fitting this_slit_trim = np.where(onslit_tweak_trim & self.rawflatimg.select_flag(invert=True)) this_wave_trim = self.waveimg[this_slit_trim] @@ -1595,11 +1591,11 @@ def extract_structure(self, rawflat_orig, slit_trim=3): # Now fit the spectral profile # TODO: Should this be *any* flag, or just BPM? gpm = self.rawflatimg.select_flag(flag='BPM', invert=True) - # TODO :: Need to fix the flexure 0.0 value below, and also rename this argument to spat_flexure for consistency + spat_flexure = self.wavetilts.spat_flexure if self.wavetilts is not None else np.zeros((self.slits.nslits, 2)) scale_model = illum_profile_spectral(rawflat, self.waveimg, self.slits, slit_illum_ref_idx=self.flatpar['slit_illum_ref_idx'], model=None, gpmask=gpm, skymask=None, trim=self.flatpar['slit_trim'], - flexure=self.wavetilts.spat_flexure if self.wavetilts is not None else 0.0, + spat_flexure=spat_flexure, smooth_npix=self.flatpar['slit_illum_smooth_npix']) # Trim the edges by a few pixels to avoid edge effects onslits_trim = gpm & (self.slits.slit_img(pad=-slit_trim, initial=False) != -1) @@ -1648,8 +1644,7 @@ def spectral_illumination(self, gpm=None, debug=False): # check if the waveimg is available if self.waveimg is None: msgs.warn("Cannot perform the spectral illumination without the wavelength image.") - # TODO :: Should this really be None? Probably it should be ones. - return None + return np.ones_like(self.rawflatimg.image) msgs.info('Performing a joint fit to the flat-field response') # Grab some parameters trim = self.flatpar['slit_trim'] @@ -1660,13 +1655,11 @@ def spectral_illumination(self, gpm=None, debug=False): gpm = self.rawflatimg.select_flag(flag='BPM', invert=True) # Obtain relative spectral illumination - # TODO :: fix the 0.0 value for the flexure argument. Also, rename flexure to spat_flexure for consistency + spat_flexure = self.wavetilts.spat_flexure if self.wavetilts is not None else np.zeros((self.slits.nslits, 2)) return illum_profile_spectral(rawflat, self.waveimg, self.slits, slit_illum_ref_idx=self.flatpar['slit_illum_ref_idx'], - model=None, gpmask=gpm, skymask=None, trim=trim, - flexure=self.wavetilts.spat_flexure if self.wavetilts is not None else 0.0, - smooth_npix=self.flatpar['slit_illum_smooth_npix'], - debug=debug) + model=None, gpmask=gpm, skymask=None, trim=trim, spat_flexure=spat_flexure, + smooth_npix=self.flatpar['slit_illum_smooth_npix'], debug=debug) def tweak_slit_edges(self, left, right, spat_coo, norm_flat, method='threshold', thresh=0.93, maxfrac=0.1, debug=False): @@ -2164,7 +2157,7 @@ def show_flats(image_list, wcs_match=True, slits=None, waveimg=None): # TODO :: This could possibly be moved to core.flat def illum_profile_spectral(rawimg, waveimg, slits, slit_illum_ref_idx=0, smooth_npix=None, polydeg=None, - model=None, gpmask=None, skymask=None, trim=3, flexure=None, maxiter=5, debug=False): + model=None, gpmask=None, skymask=None, trim=3, spat_flexure=None, maxiter=5, debug=False): """ Determine the relative spectral illumination of all slits. Currently only used for image slicer IFUs. @@ -2184,17 +2177,22 @@ def illum_profile_spectral(rawimg, waveimg, slits, slit_illum_ref_idx=0, smooth_ polydeg : int, optional Degree of polynomial to be used for determining relative spectral sensitivity. If None, coadd.smooth_weights will be used, with the smoothing length set to smooth_npix. - model : `numpy.ndarray`_, None + model : `numpy.ndarray`_, optional A model of the rawimg data. If None, rawimg will be used. - gpmask : `numpy.ndarray`_, None + gpmask : `numpy.ndarray`_, optional Good pixel mask - skymask : `numpy.ndarray`_, None + skymask : `numpy.ndarray`_, optional Sky mask trim : int Number of pixels to trim from the edges of the slit when deriving the spectral illumination - flexure : float, None - Spatial flexure + spat_flexure : `numpy.ndarray`_, optional + If provided, this is the shift, in spatial pixels, to + apply to each slit. This is used to correct for spatial + flexure. The shape of the array should be (nslits, 2), + where the first column is the shift to apply to the + left edge of each slit and the second column is the + shift to apply to the right edge of each slit. maxiter : :obj:`int` Maximum number of iterations to perform debug : :obj:`bool` @@ -2215,8 +2213,8 @@ def illum_profile_spectral(rawimg, waveimg, slits, slit_illum_ref_idx=0, smooth_ gpm = gpmask if (gpmask is not None) else np.ones_like(rawimg, dtype=bool) modelimg = model if (model is not None) else rawimg.copy() # Setup the slits - slitid_img = slits.slit_img(pad=0, spat_flexure=flexure) - slitid_img_trim = slits.slit_img(pad=-trim, spat_flexure=flexure) + slitid_img = slits.slit_img(pad=0, spat_flexure=spat_flexure) + slitid_img_trim = slits.slit_img(pad=-trim, spat_flexure=spat_flexure) scaleImg = np.ones_like(rawimg) modelimg_copy = modelimg.copy() # Obtain the minimum and maximum wavelength of all slits diff --git a/pypeit/slittrace.py b/pypeit/slittrace.py index d36d38444e..d01821c0d9 100644 --- a/pypeit/slittrace.py +++ b/pypeit/slittrace.py @@ -1011,7 +1011,7 @@ def mask_add_missing_obj(self, sobjs, spat_flexure, fwhm, boxcar_rad): # Return return sobjs - def assign_maskinfo(self, sobjs, plate_scale, spat_flexure, TOLER=1.): + def assign_maskinfo(self, sobjs, plate_scale, spat_flexure, tolerance=1.): """ Assign RA, DEC, Name to objects. Modified in place. @@ -1030,7 +1030,7 @@ def assign_maskinfo(self, sobjs, plate_scale, spat_flexure, TOLER=1.): shift to apply to the right edge of each slit. det_buffer (:obj:`int`): Minimum separation between detector edges and a slit edge. - TOLER (:obj:`float`, optional): + tolerance (:obj:`float`, optional): Matching tolerance in arcsec. Returns: @@ -1124,7 +1124,7 @@ def assign_maskinfo(self, sobjs, plate_scale, spat_flexure, TOLER=1.): obj_fwhm = cut_sobjs[ipeak].FWHM*plate_scale else: obj_fwhm = 0. - in_toler = np.abs(separ*plate_scale) < (TOLER + cc_rms + obj_fwhm/2) + in_toler = np.abs(separ*plate_scale) < (tolerance + cc_rms + obj_fwhm / 2) if np.any(in_toler): # Find positive peakflux peak_flux = cut_sobjs[idx].smash_peakflux[in_toler] @@ -1780,7 +1780,7 @@ def assign_addobjs_alldets(sobjs, calib_slits, spat_flexure, platescale, slitmas if calib_slits[i].maskdef_designtab is not None: # Assign slitmask design information to detected objects sobjs = calib_slits[i].assign_maskinfo(sobjs, platescale[i], spat_flexure[i], - TOLER=slitmask_par['obj_toler']) + tolerance=slitmask_par['obj_toler']) if slitmask_par['extract_missing_objs']: # Set the FWHM for the extraction of missing objects