diff --git a/doc/releases/1.17.1dev.rst b/doc/releases/1.17.1dev.rst index 341be5e76..194bea27a 100644 --- a/doc/releases/1.17.1dev.rst +++ b/doc/releases/1.17.1dev.rst @@ -41,4 +41,6 @@ Bug Fixes - Fix the WCS for the datacube generation. There was an offset in both spatial dimensions equal to half the field of view. +- Fix the code for the extraction of the 1D flat spectrum, so that + the spectrum is extracted even when `pixelflat_model` does not exist. diff --git a/pypeit/extraction.py b/pypeit/extraction.py index 0a818b1c2..012d241dc 100644 --- a/pypeit/extraction.py +++ b/pypeit/extraction.py @@ -15,8 +15,7 @@ from pypeit import msgs, utils from pypeit.display import display -from pypeit.core import skysub, extract, flexure - +from pypeit.core import skysub, extract, flexure, flat from IPython import embed @@ -64,7 +63,7 @@ class Extract: @classmethod def get_instance(cls, sciImg, slits, sobjs_obj, spectrograph, par, objtype, global_sky=None, bkg_redux_global_sky=None, waveTilts=None, tilts=None, wv_calib=None, waveimg=None, - flatimg=None, bkg_redux=False, return_negative=False, std_redux=False, show=False, basename=None): + flatimages=None, bkg_redux=False, return_negative=False, std_redux=False, show=False, basename=None): """ Instantiate the Extract subclass appropriate for the provided spectrograph. @@ -101,9 +100,9 @@ def get_instance(cls, sciImg, slits, sobjs_obj, spectrograph, par, objtype, glob This is the waveCalib object which is optional, but either wv_calib or waveimg must be provided. waveimg (`numpy.ndarray`_, optional): Wave image. Either a wave image or wv_calib object (above) must be provided - flatimg (`numpy.ndarray`_, optional): - Flat image. This is optional, but if provided, it is used to extract the - normalized blaze profile. Same shape as ``sciImg``. + flatimages (:class:`~pypeit.flatfield.FlatImages`, optional): + FlatImages class. This is optional, but if provided, it is used to extract the + normalized blaze profile. bkg_redux (:obj:`bool`, optional): If True, the sciImg has been subtracted by a background image (e.g. standard treatment in the IR) @@ -131,12 +130,12 @@ def get_instance(cls, sciImg, slits, sobjs_obj, spectrograph, par, objtype, glob if c.__name__ == (spectrograph.pypeline + 'Extract'))( sciImg, slits, sobjs_obj, spectrograph, par, objtype, global_sky=global_sky, bkg_redux_global_sky=bkg_redux_global_sky, waveTilts=waveTilts, tilts=tilts, - wv_calib=wv_calib, waveimg=waveimg, flatimg=flatimg, bkg_redux=bkg_redux, return_negative=return_negative, - std_redux=std_redux, show=show, basename=basename) + wv_calib=wv_calib, waveimg=waveimg, flatimages=flatimages, bkg_redux=bkg_redux, + return_negative=return_negative, std_redux=std_redux, show=show, basename=basename) def __init__(self, sciImg, slits, sobjs_obj, spectrograph, par, objtype, global_sky=None, bkg_redux_global_sky=None, waveTilts=None, tilts=None, wv_calib=None, waveimg=None, - flatimg=None, bkg_redux=False, return_negative=False, std_redux=False, show=False, + flatimages=None, bkg_redux=False, return_negative=False, std_redux=False, show=False, basename=None): # Setup the parameters sets for this object. NOTE: This uses objtype, not frametype! @@ -149,7 +148,6 @@ def __init__(self, sciImg, slits, sobjs_obj, spectrograph, par, objtype, global_ self.par = par self.global_sky = global_sky if global_sky is not None else np.zeros_like(sciImg.image) self.bkg_redux_global_sky = bkg_redux_global_sky - self.flatimg = flatimg self.basename = basename # Parse @@ -247,6 +245,18 @@ def __init__(self, sciImg, slits, sobjs_obj, spectrograph, par, objtype, global_ else: msgs.warn("Spectral FWHM image could not be generated") + # get flatfield image for blaze function + self.flatimg = None + if flatimages is not None: + # This way of getting the flat image (instead of just reading flatimages.pixelflat_model) ensures that the + # flat image is available also when an archival pixel flat is used. + flat_raw = flatimages.pixelflat_raw if flatimages.pixelflat_raw is not None else flatimages.illumflat_raw + if flat_raw is not None and flatimages.pixelflat_norm is not None: + # TODO: Can we just use flat_raw if flatimages.pixelflat_norm is None? + self.flatimg, _ = flat.flatfield(flat_raw, flatimages.pixelflat_norm) + if self.flatimg is None: + msgs.warn("No flat image was found. A spectrum of the flatfield will not be extracted!") + # Now apply a global flexure correction to each slit provided it's not a standard star if self.par['flexure']['spec_method'] != 'skip' and not self.std_redux: # Update slitshift values diff --git a/pypeit/pypeit.py b/pypeit/pypeit.py index 432bfe76f..4c1e520f2 100644 --- a/pypeit/pypeit.py +++ b/pypeit/pypeit.py @@ -1029,10 +1029,6 @@ def extract_one(self, frames, det, sciImg, bkg_redux_sciimg, objFind, initial_sk if not self.par['reduce']['extraction']['skip_extraction']: msgs.info(f"Extraction begins for {self.basename} on det={det}") - # set the flatimg, if it exists - flatimg = None if self.caliBrate.flatimages is None else self.caliBrate.flatimages.pixelflat_model - if flatimg is None: - msgs.warn("No flat image was found. A spectrum of the flatfield will not be extracted!") # Instantiate Reduce object # Required for pipeline specific object # At instantiation, the fullmask in self.sciImg is modified @@ -1040,7 +1036,7 @@ def extract_one(self, frames, det, sciImg, bkg_redux_sciimg, objFind, initial_sk self.exTract = extraction.Extract.get_instance( sciImg, slits, sobjs_obj, self.spectrograph, self.par, self.objtype, global_sky=final_global_sky, bkg_redux_global_sky=bkg_redux_global_sky, - waveTilts=self.caliBrate.wavetilts, wv_calib=self.caliBrate.wv_calib, flatimg=flatimg, + waveTilts=self.caliBrate.wavetilts, wv_calib=self.caliBrate.wv_calib, flatimages=self.caliBrate.flatimages, bkg_redux=self.bkg_redux, return_negative=self.par['reduce']['extraction']['return_negative'], std_redux=self.std_redux, basename=self.basename, show=self.show) # Perform the extraction diff --git a/pypeit/specobjs.py b/pypeit/specobjs.py index f992cf7cd..dac41406d 100644 --- a/pypeit/specobjs.py +++ b/pypeit/specobjs.py @@ -237,8 +237,8 @@ def unpack_object(self, ret_flam=False, log10blaze=False, min_blaze_value=1e-3, # Check for missing data none_flux = [f is None for f in getattr(self, flux_key)] + other = 'OPT' if extract_type == 'BOX' else 'BOX' if np.any(none_flux): - other = 'OPT' if extract_type == 'BOX' else 'BOX' msg = f"{extract_type} extracted flux is not available for all slits/orders. " \ f"Consider trying the {other} extraction." if not remove_missing: @@ -249,6 +249,12 @@ def unpack_object(self, ret_flam=False, log10blaze=False, min_blaze_value=1e-3, # Remove missing data r_indx = np.where(none_flux)[0] self.remove_sobj(r_indx) + # check for missing blaze + if extract_blaze: + none_blaze = [f is None for f in getattr(self, blaze_key)] + if np.any(none_blaze): + msgs.error(f"{extract_type} extracted blaze is not available for all slits/orders. " + f"Consider trying the {other} extraction, or NOT using the flat.") # norddet = self.nobj @@ -299,7 +305,7 @@ def unpack_object(self, ret_flam=False, log10blaze=False, min_blaze_value=1e-3, # Return if self[0].PYPELINE in ['MultiSlit', 'SlicerIFU'] and self.nobj == 1: meta_spec['ECH_ORDERS'] = None - blaze_ret = blaze_function.reshape(nspec) if extract_blaze else None + blaze_ret = blaze_function.reshape(nspec) if blaze_function is not None else None return wave.reshape(nspec), flux.reshape(nspec), flux_ivar.reshape(nspec), \ flux_gpm.reshape(nspec), blaze_ret, meta_spec, self.header else: