From da470158b794b64eb36f45e69d22ab0ffc43d720 Mon Sep 17 00:00:00 2001 From: jhennawi Date: Fri, 28 Jun 2024 18:43:54 -1000 Subject: [PATCH 01/13] Fixed a bug in coadd2d where the platescale was not using the binning --- pypeit/coadd2d.py | 39 ++++++++++++++++++------------- pypeit/par/pypeitpar.py | 21 +++++++++++++++-- pypeit/scripts/coadd_2dspec.py | 16 ++----------- pypeit/scripts/ql.py | 41 +++++++++++++++++++-------------- pypeit/scripts/setup_coadd2d.py | 15 +++++++++++- 5 files changed, 82 insertions(+), 50 deletions(-) diff --git a/pypeit/coadd2d.py b/pypeit/coadd2d.py index 65d92b3542..598757e7df 100644 --- a/pypeit/coadd2d.py +++ b/pypeit/coadd2d.py @@ -27,6 +27,7 @@ from pypeit.core import findobj_skymask from pypeit.core.wavecal import wvutils from pypeit.core import coadd +from pypeit.core import parse #from pypeit.core import parse from pypeit import calibrations from pypeit import spec2dobj @@ -835,7 +836,8 @@ def reduce(self, pseudo_dict, show=False, clear_ginga=True, show_peaks=False, sh # maskdef stuff if parcopy['reduce']['slitmask']['assign_obj'] and slits.maskdef_designtab is not None: # Get plate scale - platescale = sciImage.detector.platescale * self.spat_samp_fact + platescale = parse.parse_binning(sciImage.detector.binning)[1]*sciImage.detector.platescale*self.spat_samp_fact + #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']) @@ -895,26 +897,29 @@ def snr_report(self, snr_bar, slitid=None): msg_string += msgs.newline() + '-------------------------------------' msgs.info(msg_string) - def offsets_report(self, offsets, offsets_method): + def offsets_report(self, offsets, platescale, offsets_method): """ Print out a report on the offsets Args: - offsets: - offsets_method: - + offsets (`numpy.ndarray`_) + Array of offsets + platescale (float): + the plate scale for this detector + offsets_method (str): + A string describing the method used to determine the offsets Returns: """ if offsets_method is not None and offsets is not None: - msg_string = msgs.newline() + '---------------------------------------------' + msg_string = msgs.newline() + '---------------------------------------------------------------------------------' msg_string += msgs.newline() + ' Summary of offsets from {} '.format(offsets_method) - msg_string += msgs.newline() + '---------------------------------------------' - msg_string += msgs.newline() + ' exp# offset ' + msg_string += msgs.newline() + '---------------------------------------------------------------------------------' + msg_string += msgs.newline() + ' exp# offset (pixels) offset (arcsec)' for iexp, off in enumerate(offsets): - msg_string += msgs.newline() + ' {:d} {:5.2f}'.format(iexp, off) - msg_string += msgs.newline() + '-----------------------------------------------' + msg_string += msgs.newline() + ' {:d} {:5.2f} {:6.3f}'.format(iexp, off, off*platescale) + msg_string += msgs.newline() + '---------------------------------------------------------------------------------' msgs.info(msg_string) def offset_slit_cen(self, slitid, offsets): @@ -1126,17 +1131,18 @@ def compute_offsets(self, offsets): """ msgs.info('Get Offsets') + platescale = parse.parse_binning(self.stack_dict['detectors'][0].binning)[1]*self.stack_dict['detectors'][0].platescale + #platescale = self.stack_dict['detectors'][0].platescale # 1) offsets are provided in the header of the spec2d files if offsets == 'header': msgs.info('Using offsets from header') - pscale = self.stack_dict['detectors'][0].platescale dithoffs = [self.spectrograph.get_meta_value(f, 'dithoff') for f in self.spec2d] if None in dithoffs: msgs.error('Dither offsets keyword not found for one or more spec2d files. ' 'Choose another option for `offsets`') - dithoffs_pix = - np.array(dithoffs) / pscale + dithoffs_pix = - np.array(dithoffs) / platescale self.offsets = dithoffs_pix[0] - dithoffs_pix - self.offsets_report(self.offsets, 'header keyword') + self.offsets_report(self.offsets, platescale, 'header keyword') elif self.objid_bri is None and offsets == 'auto': msgs.error('Offsets cannot be computed because no unique reference object ' @@ -1147,7 +1153,7 @@ def compute_offsets(self, offsets): msgs.info('Using user input offsets') # use them self.offsets = self.check_input(offsets, 'offsets') - self.offsets_report(self.offsets, 'user input') + self.offsets_report(self.offsets, platescale, 'user input') # 3) parset `offsets` is = 'maskdef_offsets' (no matter if we have a bright object or not) elif offsets == 'maskdef_offsets': @@ -1155,7 +1161,7 @@ def compute_offsets(self, offsets): # the offsets computed during the main reduction (`run_pypeit`) are used msgs.info('Determining offsets using maskdef_offset recoded in SlitTraceSet') self.offsets = self.maskdef_offset[0] - self.maskdef_offset - self.offsets_report(self.offsets, 'maskdef_offset') + self.offsets_report(self.offsets, platescale, 'maskdef_offset') else: # if maskdef_offsets were not computed during the main reduction, we cannot continue msgs.error('No maskdef_offset recoded in SlitTraceSet') @@ -1402,7 +1408,8 @@ def compute_offsets(self, offsets): plt.show() self.offsets = offsets - self.offsets_report(self.offsets, offsets_method) + platescale = parse.parse_binning(self.stack_dict['detectors'][0].binning)[1]*self.stack_dict['detectors'][0].platescale + self.offsets_report(self.offsets, platescale, offsets_method) def compute_weights(self, weights): """ diff --git a/pypeit/par/pypeitpar.py b/pypeit/par/pypeitpar.py index d9f42ec6dd..128b1029c9 100644 --- a/pypeit/par/pypeitpar.py +++ b/pypeit/par/pypeitpar.py @@ -1428,7 +1428,7 @@ class Coadd2DPar(ParSet): see :ref:`parameters`. """ def __init__(self, only_slits=None, exclude_slits=None, offsets=None, spat_toler=None, weights=None, user_obj=None, - use_slits4wvgrid=None, manual=None, wave_method=None): + use_slits4wvgrid=None, manual=None, wave_method=None, spec_samp_fact=None, spat_samp_fact=None): # Grab the parameter names and values from the function # arguments @@ -1517,6 +1517,23 @@ def __init__(self, only_slits=None, exclude_slits=None, offsets=None, spat_toler "* 'log10' -- Grid is uniform in log10(wave). This is the same as velocity." \ "* 'linear' -- Grid is uniform in wavelength" \ + + defaults['spec_samp_fact'] = 1.0 + dtypes['spec_samp_fact'] = float + descr['spec_samp_fact'] = "Make the wavelength grid sampling finer (``spec_samp_fact`` less than 1.0)" \ + "or coarser (``spec_samp_fact`` greater than 1.0) by this sampling factor." \ + "This multiples the 'native' spectral pixel size by ``spec_samp_fact``," \ + "i.e. the units of ``spec_samp_fact`` are pixels." + + + defaults['spat_samp_fact'] = 1.0 + dtypes['spat_samp_fact'] = float + descr['spat_samp_fact'] = "Make the spatial sampling finer (``spat_samp_fact`` less" \ + "than 1.0) or coarser (``spat_samp_fact`` greather than 1.0) by" \ + "this sampling factor. This basically multiples the 'native'" \ + "spatial pixel size by ``spat_samp_fact``, i.e. the units of" \ + "``spat_samp_fact`` are pixels." + # Instantiate the parameter set super(Coadd2DPar, self).__init__(list(pars.keys()), @@ -1532,7 +1549,7 @@ def __init__(self, only_slits=None, exclude_slits=None, offsets=None, spat_toler def from_dict(cls, cfg): k = np.array([*cfg.keys()]) parkeys = ['only_slits', 'exclude_slits', 'offsets', 'spat_toler', 'weights', 'user_obj', 'use_slits4wvgrid', - 'manual', 'wave_method'] + 'manual', 'wave_method', 'spec_samp_fact', 'spat_samp_fact'] badkeys = np.array([pk not in parkeys for pk in k]) if np.any(badkeys): diff --git a/pypeit/scripts/coadd_2dspec.py b/pypeit/scripts/coadd_2dspec.py index 355c6dd72b..2fb0c1038c 100644 --- a/pypeit/scripts/coadd_2dspec.py +++ b/pypeit/scripts/coadd_2dspec.py @@ -32,18 +32,6 @@ def get_parser(cls, width=None): 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 coadd_2dspec_YYYYMMDD-HHMM.log') - - # TODO: Make spec_samp_fact and spat_samp_fact parameters in CoAdd2DPar, - # and then move these to setup_coadd2d.py - parser.add_argument('--spec_samp_fact', default=1.0, type=float, - help="Make the wavelength grid finer (spec_samp_fact < 1.0) or " - "coarser (spec_samp_fact > 1.0) by this sampling factor, i.e. " - "units of spec_samp_fact are pixels.") - parser.add_argument('--spat_samp_fact', default=1.0, type=float, - help="Make the spatial grid finer (spat_samp_fact < 1.0) or coarser " - "(spat_samp_fact > 1.0) by this sampling factor, i.e. units of " - "spat_samp_fact are pixels.") - #parser.add_argument("--wave_method", type=str, default=None, # help="Wavelength method for wavelength grid. If not set, code will " # "use linear for Multislit and log10 for Echelle") @@ -177,8 +165,8 @@ def main(args): weights=par['coadd2d']['weights'], only_slits=this_only_slits, exclude_slits=this_exclude_slits, - spec_samp_fact=args.spec_samp_fact, - spat_samp_fact=args.spat_samp_fact, + spec_samp_fact=par['coadd2d']['spec_samp_fact'], + spat_samp_fact=par['coadd2d']['spat_samp_fact'], bkg_redux=bkg_redux, find_negative=find_negative, debug_offsets=args.debug_offsets, debug=args.debug) diff --git a/pypeit/scripts/ql.py b/pypeit/scripts/ql.py index fbfcebcff0..53566e8d2d 100644 --- a/pypeit/scripts/ql.py +++ b/pypeit/scripts/ql.py @@ -714,36 +714,40 @@ def get_parser(cls, width=None): help='If standard star observations are automatically detected, ' 'ignore those frames. Otherwise, they are included with the ' 'reduction of the science frames.') - parser.add_argument('--skip_display', dest='show', default=True, action='store_false', - help='Run the quicklook without displaying any results.') + parser.add_argument('--skip_display', dest='skip_display', default=False, action='store_true', + help='Run the quicklook without displaying any results. The default skip_display=False will show the results.') + parser.add_argument('--removetrace', default=False, action='store_true', + help='When the image is shown, do not overplot traces in the skysub, sky_resid, and resid ' + 'channels') # TODO: Add fluxing option? # Coadding options parser.add_argument('--coadd2d', default=False, action='store_true', help='Perform default 2D coadding.') - # TODO: Consolidate slitspatnum and only_slits! - parser.add_argument('--only_slits', type=str, nargs='+', - help='If coadding, only coadd this space-separated set of slits. If ' - 'not provided, all slits are coadded.') + parser.add_argument('--spec_samp_fact', default=1.0, type=float, + help='If coadding, adjust the wavelength grid sampling by this ' + 'factor. For a finer grid, set value to <1.0; for coarser ' + 'sampling, set value to >1.0).') + parser.add_argument('--spat_samp_fact', default=1.0, type=float, + help='If coadding, adjust the spatial grid sampling by this ' + 'factor. For a finer grid, set value to <1.0; for coarser ' + 'sampling, set value to >1.0).') parser.add_argument('--offsets', type=str, default=None, help='If coadding, spatial offsets to apply to each image; see the ' '[coadd2d][offsets] parameter. Options are restricted here to ' 'either maskdef_offsets or auto. If not specified, the ' - '(spectrograph-specific) default is used.') + '(spectrograph-specific) default is used.') parser.add_argument('--weights', type=str, default=None, help='If coadding, weights used to coadd images; see the ' '[coadd2d][weights] parameter. Options are restricted here to ' 'either uniform or auto. If not specified, the ' '(spectrograph-specific) default is used.') - parser.add_argument('--spec_samp_fact', default=1.0, type=float, - help='If coadding, adjust the wavelength grid sampling by this ' - 'factor. For a finer grid, set value to <1.0; for coarser ' - 'sampling, set value to >1.0).') - parser.add_argument('--spat_samp_fact', default=1.0, type=float, - help='If coadding, adjust the spatial grid sampling by this ' - 'factor. For a finer grid, set value to <1.0; for coarser ' - 'sampling, set value to >1.0).') + # TODO: Consolidate slitspatnum and only_slits! + parser.add_argument('--only_slits', type=str, nargs='+', + help='If coadding, only coadd this space-separated set of slits. If ' + 'not provided, all slits are coadded.') + parser.add_argument('--try_old', default=False, action='store_true', help='Attempt to load old datamodel versions. A crash may ensue..') @@ -1017,9 +1021,12 @@ def main(args): frame = pypeIt.fitstbl.find_frames('science', index=True)[0] spec2d_file = pypeIt.spec_output_file(frame, twod=True) - if args.show: + if not args.skip_display: # TODO: Need to parse detector here? - Show2DSpec.main(Show2DSpec.parse_args([spec2d_file])) + show2d_spec_args = [spec2d_file] + if args.removetrace: + show2d_spec_args += ['--removetrace'] + Show2DSpec.main(Show2DSpec.parse_args(show2d_spec_args)) # TODO: # - Print a statement that allows users to copy-paste the correct diff --git a/pypeit/scripts/setup_coadd2d.py b/pypeit/scripts/setup_coadd2d.py index 428c347da0..b19214c06d 100644 --- a/pypeit/scripts/setup_coadd2d.py +++ b/pypeit/scripts/setup_coadd2d.py @@ -70,6 +70,14 @@ def get_parser(cls, width=None): 'either uniform or auto. If not specified, the ' '(spectrograph-specific) default is used. Other options exist ' 'but must be entered by directly editing the coadd2d file.') + parser.add_argument('--spec_samp_fact', default=1.0, type=float, + help="Make the wavelength grid finer (spec_samp_fact < 1.0) or " + "coarser (spec_samp_fact > 1.0) by this sampling factor, i.e. " + "units of spec_samp_fact are pixels.") + parser.add_argument('--spat_samp_fact', default=1.0, type=float, + help="Make the spatial grid finer (spat_samp_fact < 1.0) or coarser " + "(spat_samp_fact > 1.0) by this sampling factor, i.e. units of " + "spat_samp_fact are pixels.") return parser @@ -178,7 +186,12 @@ def main(args): if args.weights is None else args.weights cfg['coadd2d']['spat_toler'] = par['coadd2d']['spat_toler'] \ if args.spat_toler is None else args.spat_toler - + cfg['coadd2d']['spec_samp_fact'] = par['coadd2d']['spec_samp_fact'] \ + if args.spec_samp_fact is None else args.spec_samp_fact + cfg['coadd2d']['spat_samp_fact'] = par['coadd2d']['spat_samp_fact'] \ + if args.spat_samp_fact is None else args.spat_samp_fact + + # TODO JFH Why are exclude_slits and only_slits set here when they are parameters in the parset? # Build the default parameters cfg = CoAdd2D.default_par(spec_name, inp_cfg=cfg, det=args.det, only_slits=args.only_slits, exclude_slits=args.exclude_slits) From 2fc470899ff238d2e5a1a358e882e26ddb8f0e77 Mon Sep 17 00:00:00 2001 From: jhennawi Date: Thu, 4 Jul 2024 18:55:50 -1000 Subject: [PATCH 02/13] trying to improve show_2dspec detector handling --- pypeit/coadd2d.py | 3 ++- pypeit/core/coadd.py | 2 +- pypeit/scripts/ql.py | 10 +++++++--- pypeit/scripts/show_2dspec.py | 22 ++++++++++++++++++---- 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/pypeit/coadd2d.py b/pypeit/coadd2d.py index 598757e7df..cf0ea17b86 100644 --- a/pypeit/coadd2d.py +++ b/pypeit/coadd2d.py @@ -34,7 +34,8 @@ from pypeit.core.moment import moment1d from pypeit.manual_extract import ManualExtractionObj - +#TODO We should decide which parameters go in through the parset +# and which parameters are passed in to the method as arguments class CoAdd2D: """ diff --git a/pypeit/core/coadd.py b/pypeit/core/coadd.py index 364198cd67..6a14c2a090 100644 --- a/pypeit/core/coadd.py +++ b/pypeit/core/coadd.py @@ -882,7 +882,7 @@ def sn_weights(fluxes, ivars, gpms, sn_smooth_npix=None, weight_method='auto', v # Check if relative weights input if verbose: - msgs.info('Computing weights with weight_method=%s'.format(weight_method)) + msgs.info('Computing weights with weight_method={:s}'.format(weight_method)) weights = [] diff --git a/pypeit/scripts/ql.py b/pypeit/scripts/ql.py index 53566e8d2d..db8d59fd14 100644 --- a/pypeit/scripts/ql.py +++ b/pypeit/scripts/ql.py @@ -993,6 +993,10 @@ def main(args): command_line_args += ['--offsets', args.offsets] if args.weights is not None: command_line_args += ['--weights', args.weights] + if args.spec_samp_fact != 1.0: + command_line_args += ['--spec_samp_fact', str(args.spec_samp_fact)] + if args.spat_samp_fact != 1.0: + command_line_args += ['--spat_samp_fact', str(args.spat_samp_fact)] SetupCoAdd2D.main(SetupCoAdd2D.parse_args(command_line_args)) # Find all the coadd2d scripts @@ -1006,9 +1010,9 @@ def main(args): # Run the coadding coadd2dFile = inputfiles.Coadd2DFile.from_file(coadd_file) - CoAdd2DSpec.main(CoAdd2DSpec.parse_args([str(coadd_file), - '--spec_samp_fact', str(args.spec_samp_fact), - '--spat_samp_fact', str(args.spat_samp_fact)])) + CoAdd2DSpec.main(CoAdd2DSpec.parse_args([str(coadd_file)])) + # '--spec_samp_fact', str(args.spec_samp_fact), + # '--spat_samp_fact', str(args.spat_samp_fact)])) # Get the output file name spectrograph, par, _ = coadd2dFile.get_pypeitpar() diff --git a/pypeit/scripts/show_2dspec.py b/pypeit/scripts/show_2dspec.py index 6c54be71f0..4bb48bdaab 100644 --- a/pypeit/scripts/show_2dspec.py +++ b/pypeit/scripts/show_2dspec.py @@ -41,8 +41,10 @@ def show_trace(sobjs, det, viewer, ch): det (:obj:`str`): The string identifier for the detector or mosaic used to select the extractions to show. - viewer (?): - ch (?): + viewer (:class:`~ginga.misc.Bunch.Bunch`): + Ginga viewer object. + ch (:obj:`str`): + The name of the channel in the Ginga viewer to plot the traces. """ if sobjs is None: return @@ -79,11 +81,12 @@ def get_parser(cls, width=None): parser.add_argument('file', type=str, default=None, help='Path to a PypeIt spec2d file') parser.add_argument('--list', default=False, action='store_true', help='List the extensions only?') - parser.add_argument('--det', default='1', type=str, + parser.add_argument('--det', default=None, type=str, help='Detector name or number. If a number, the name is constructed ' 'assuming the reduction is for a single detector. If a string, ' 'it must match the name of the detector object (e.g., DET01 for ' - 'a detector, MSC01 for a mosaic).') + 'a detector, MSC01 for a mosaic). If not set, the first available detector' + 'in the spec2d file will be shown') parser.add_argument('--spat_id', type=int, default=None, help='Restrict plotting to this slit (PypeIt ID notation)') parser.add_argument('--maskID', type=int, default=None, @@ -134,6 +137,17 @@ def main(args): msgs.set_logfile_and_verbosity('show_2dspec', args.verbosity) # Parse the detector name + if args.det is None: + allspec2d_det = fits.getval(args.file,'HIERARCH ALLSPEC2D_DETS') + detname = allspec2d_det.split(',')[0] + else: + try: + det = int(args.det) + except: + detname = args.det + else: + detname = DetectorContainer.get_name(det) + try: det = int(args.det) except: From d4b51a33aefa1e9c9e5d366a28866fca757f9428 Mon Sep 17 00:00:00 2001 From: jhennawi Date: Sat, 6 Jul 2024 21:07:00 +0900 Subject: [PATCH 03/13] minor changes to coadd2d --- pypeit/coadd2d.py | 6 +++--- pypeit/scripts/show_2dspec.py | 15 +++++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/pypeit/coadd2d.py b/pypeit/coadd2d.py index cf0ea17b86..05b3fe0b4b 100644 --- a/pypeit/coadd2d.py +++ b/pypeit/coadd2d.py @@ -1218,7 +1218,7 @@ def compute_weights(self, weights): msgs.error('Invalid value for `weights`') - def get_brightest_object(self, specobjs_list, spat_ids): + def get_brightest_obj(self, specobjs_list, spat_ids): """ Dummy method to identify the brightest object. Overloaded by child methods. @@ -1437,7 +1437,7 @@ def compute_weights(self, weights): msgs.info(f'Weights computed using a unique reference object in slit={self.spatid_bri} provided by the user') else: msgs.info(f'Weights computed using a unique reference object in slit={self.spatid_bri} with the highest S/N') - self.snr_report(self.snr_bar_bri, slitid=self.spatid_bri) + self.snr_report(self.snr_bar_bri, slitid=self.spatid_bri, objid=self.objid_bri) def get_brightest_obj(self, specobjs_list, spat_ids): @@ -1451,7 +1451,7 @@ def get_brightest_obj(self, specobjs_list, spat_ids): Returns: tuple: Returns the following: - - objid: ndarray, int, shape (len(specobjs_list),): + - objid: ndarray, int, shape=(len(specobjs_list),): Array of object ids representing the brightest object in each exposure - slit_idx (int): 0-based index diff --git a/pypeit/scripts/show_2dspec.py b/pypeit/scripts/show_2dspec.py index 4bb48bdaab..ff1679e5d4 100644 --- a/pypeit/scripts/show_2dspec.py +++ b/pypeit/scripts/show_2dspec.py @@ -46,9 +46,12 @@ def show_trace(sobjs, det, viewer, ch): ch (:obj:`str`): The name of the channel in the Ginga viewer to plot the traces. """ + if sobjs is None: return in_det = np.where(sobjs.DET == det)[0] + if len(in_det) == 0: + return trace_list = [] trc_name_list = [] maskdef_extr_list = [] @@ -148,12 +151,12 @@ def main(args): else: detname = DetectorContainer.get_name(det) - try: - det = int(args.det) - except: - detname = args.det - else: - detname = DetectorContainer.get_name(det) + #try: + # det = int(args.det) + #except: + # detname = args.det + #else: + # detname = DetectorContainer.get_name(det) # Find the set of channels to show show_channels = [0,1,2,3] if args.channels is None \ From f3d7a2db31cde5e9d95086fcda455a558a63e309 Mon Sep 17 00:00:00 2001 From: jhennawi Date: Sat, 6 Jul 2024 21:45:41 +0900 Subject: [PATCH 04/13] The SNR report in multislit coadding now reports the spat_pixpos of the object used --- pypeit/coadd2d.py | 116 +++++++++++++++++++++++++++++++--------------- 1 file changed, 79 insertions(+), 37 deletions(-) diff --git a/pypeit/coadd2d.py b/pypeit/coadd2d.py index 05b3fe0b4b..bd5f59e4ab 100644 --- a/pypeit/coadd2d.py +++ b/pypeit/coadd2d.py @@ -873,30 +873,6 @@ def reduce(self, pseudo_dict, show=False, clear_ginga=True, show_peaks=False, sh objmodel_pseudo, ivarmodel_pseudo, outmask_pseudo, sobjs, sciImage.detector, slits, \ pseudo_dict['tilts'], pseudo_dict['waveimg'] - def snr_report(self, snr_bar, slitid=None): - """ - ..todo.. I need a doc string - - Args: - snr_bar: - slitid: - - Returns: - - """ - - # Print out a report on the SNR - msg_string = msgs.newline() + '-------------------------------------' - msg_string += msgs.newline() + ' Summary for highest S/N object' - if slitid is not None: - msg_string += msgs.newline() + ' found on slitid = {:d} '.format(slitid) - msg_string += msgs.newline() + '-------------------------------------' - msg_string += msgs.newline() + ' exp# S/N' - for iexp, snr in enumerate(snr_bar): - msg_string += msgs.newline() + ' {:d} {:5.2f}'.format(iexp, snr) - - msg_string += msgs.newline() + '-------------------------------------' - msgs.info(msg_string) def offsets_report(self, offsets, platescale, offsets_method): """ @@ -1319,7 +1295,7 @@ def __init__(self, spec2d_files, spectrograph, par, det=1, offsets=None, weights self.objid_bri, self.slitidx_bri, self.spatid_bri, self.snr_bar_bri = \ np.repeat(user_objid, self.nexp), _slitidx_bri, user_slit, None elif len(self.stack_dict['specobjs_list']) > 0 and (offsets == 'auto' or weights == 'auto'): - self.objid_bri, self.slitidx_bri, self.spatid_bri, self.snr_bar_bri = \ + self.objid_bri, self.spat_pixpos_bri, self.slitidx_bri, self.spatid_bri, self.snr_bar_bri = \ self.get_brightest_obj(self.stack_dict['specobjs_list'], self.spat_ids) else: self.objid_bri, self.slitidx_bri, self.spatid_bri, self.snr_bar_bri = (None,)*4 @@ -1435,47 +1411,55 @@ def compute_weights(self, weights): _, self.use_weights = self.optimal_weights(self.spatid_bri, self.objid_bri, weight_method='constant') if self.par['coadd2d']['user_obj'] is not None: msgs.info(f'Weights computed using a unique reference object in slit={self.spatid_bri} provided by the user') + # TODO Should we not still printo out a report for this usage case? else: msgs.info(f'Weights computed using a unique reference object in slit={self.spatid_bri} with the highest S/N') - self.snr_report(self.snr_bar_bri, slitid=self.spatid_bri, objid=self.objid_bri) + self.snr_report(self.spatid_bri, self.spat_pixpos_bri, self.snr_bar_bri) - def get_brightest_obj(self, specobjs_list, spat_ids): + def get_brightest_obj(self, specobjs_list, slit_spat_ids): """ - Utility routine to find the brightest object in each exposure given a specobjs_list for MultiSlit reductions. + Utility routine to find the brightest reference object in each exposure given a specobjs_list + for MultiSlit reductions. Args: specobjs_list: list List of SpecObjs objects. - spat_ids (`numpy.ndarray`_): + slit_spat_ids (`numpy.ndarray`_): Returns: tuple: Returns the following: - objid: ndarray, int, shape=(len(specobjs_list),): - Array of object ids representing the brightest object + Array of object ids representing the brightest reference object in each exposure - - slit_idx (int): 0-based index - - spat_id (int): SPAT_ID for slit that highest S/N ratio object is on - (only for pypeline=MultiSlit) + - spatid_pixpos: ndarray, float, shape=(len(specobjs_list),): + Array of spatial pixel positions of the brightest reference object + in each exposure + - slit_idx (int): + A zero-based index for the slit that the brightest object is on + - spat_id (int): + The SPAT_ID for the slit that the highest S/N ratio object is on - snr_bar: ndarray, float, shape (len(list),): Average - S/N over all the orders for this object + S/N computed over all the exposures for this brightest reference object """ msgs.info('Finding brightest object') nexp = len(specobjs_list) - nslits = spat_ids.size + nslits = slit_spat_ids.size slit_snr_max = np.zeros((nslits, nexp), dtype=float) bpm = np.ones(slit_snr_max.shape, dtype=bool) objid_max = np.zeros((nslits, nexp), dtype=int) + spat_pixpos_max = np.zeros((nslits, nexp), dtype=float) # Loop over each exposure, slit, find the brightest object on that slit for every exposure for iexp, sobjs in enumerate(specobjs_list): msgs.info("Working on exposure {}".format(iexp)) - for islit, spat_id in enumerate(spat_ids): + for islit, spat_id in enumerate(slit_spat_ids): if len(sobjs) == 0: continue ithis = np.abs(sobjs.SLITID - spat_id) <= self.par['coadd2d']['spat_toler'] if np.any(ithis): objid_this = sobjs[ithis].OBJID + spat_pixpos_this = sobjs[ithis].SPAT_PIXPOS fluxes, ivars, gpms = [], [], [] for iobj, spec in enumerate(sobjs[ithis]): # check if OPT_COUNTS is available @@ -1503,6 +1487,7 @@ def get_brightest_obj(self, specobjs_list, spat_ids): imax = np.argmax(rms_sn) slit_snr_max[islit, iexp] = rms_sn[imax] objid_max[islit, iexp] = objid_this[imax] + spat_pixpos_max[islit, iexp] = spat_pixpos_this[imax] bpm[islit, iexp] = False # If a slit has bpm = True for some exposures and not for others, set bpm = True for all exposures @@ -1525,8 +1510,41 @@ def get_brightest_obj(self, specobjs_list, spat_ids): snr_bar_mean = slit_snr[slitid] snr_bar = slit_snr_max[slitid, :] objid = objid_max[slitid, :] + spat_pixpos = spat_pixpos_max[slitid, :] + + return objid, spat_pixpos, slitid, slit_spat_ids[slitid], snr_bar + + + def snr_report(self, slitid, spat_pixpos, snr_bar): + """ + + Print out a SNR report for the reference object used to compute the weights for multislit 2D coadds. + + Args: + slitid (:obj:`int`): + The SPAT_ID of the slit that the reference object is on + spat_pixpos (:obj:`numpy.ndarray`): + Array of spatial pixel position of the reference object in the slit for each exposure shape = (nexp,) + snr_bar (:obj:`numpy.ndarray`): + Array of average S/N ratios for the reference object in each exposure, shape = (nexp,) + + + Returns: + + """ + + # Print out a report on the SNR + msg_string = msgs.newline() + '-------------------------------------' + msg_string += msgs.newline() + ' Summary for highest S/N object' + msg_string += msgs.newline() + ' found on slitid = {:d} '.format(slitid) + msg_string += msgs.newline() + '-------------------------------------' + msg_string += msgs.newline() + ' exp# spat_pixpos S/N' + msg_string += msgs.newline() + '-------------------------------------' + for iexp, (spat,snr) in enumerate(zip(spat_pixpos, snr_bar)): + msg_string += msgs.newline() + ' {:2d} {:7.1f} {:5.2f}'.format(iexp, spat, snr) + msg_string += msgs.newline() + '-------------------------------------' + msgs.info(msg_string) - return objid, slitid, spat_ids[slitid], snr_bar # TODO add an option here to actually use the reference trace for cases where they are on the same slit and it is # single slit??? @@ -1807,6 +1825,30 @@ def get_brightest_obj(self, specobjs_list, nslits): return None, None, None return objid, None, snr_bar + def snr_report(self, snr_bar): + """ + Printo out a SNR report for echelle 2D coadds. + + Args: + snr_bar (:obj:`numpy.ndarray`): + Array of average S/N ratios for the brightest object in each exposure. Shape = (nexp,) + + Returns: + + """ + + # Print out a report on the SNR + msg_string = msgs.newline() + '-------------------------------------' + msg_string += msgs.newline() + ' Summary for highest S/N object' + msg_string += msgs.newline() + '-------------------------------------' + msg_string += msgs.newline() + ' exp# S/N' + for iexp, snr in enumerate(snr_bar): + msg_string += msgs.newline() + ' {:d} {:5.2f}'.format(iexp, snr) + + msg_string += msgs.newline() + '-------------------------------------' + msgs.info(msg_string) + + def reference_trace_stack(self, slitid, offsets=None, objid=None): """ Utility function for determining the reference trace about From 7541c1f4acf8240541f9cac02d7fb402081df357 Mon Sep 17 00:00:00 2001 From: jhennawi Date: Sat, 6 Jul 2024 21:52:43 +0900 Subject: [PATCH 05/13] The SNR report in multislit coadding now reports the spat_pixpos of the object used --- pypeit/coadd2d.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypeit/coadd2d.py b/pypeit/coadd2d.py index bd5f59e4ab..b586e6a04a 100644 --- a/pypeit/coadd2d.py +++ b/pypeit/coadd2d.py @@ -895,7 +895,7 @@ def offsets_report(self, offsets, platescale, offsets_method): msg_string += msgs.newline() + '---------------------------------------------------------------------------------' msg_string += msgs.newline() + ' exp# offset (pixels) offset (arcsec)' for iexp, off in enumerate(offsets): - msg_string += msgs.newline() + ' {:d} {:5.2f} {:6.3f}'.format(iexp, off, off*platescale) + msg_string += msgs.newline() + ' {:2d} {:5.2f} {:6.3f}'.format(iexp, off, off*platescale) msg_string += msgs.newline() + '---------------------------------------------------------------------------------' msgs.info(msg_string) From 27e33b570a199a1e1adc917639064639be587a41 Mon Sep 17 00:00:00 2001 From: jhennawi Date: Sat, 6 Jul 2024 22:04:55 +0900 Subject: [PATCH 06/13] The SNR report in multislit coadding now reports the spat_pixpos of the object used --- pypeit/coadd2d.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pypeit/coadd2d.py b/pypeit/coadd2d.py index b586e6a04a..1f8887bc37 100644 --- a/pypeit/coadd2d.py +++ b/pypeit/coadd2d.py @@ -895,7 +895,7 @@ def offsets_report(self, offsets, platescale, offsets_method): msg_string += msgs.newline() + '---------------------------------------------------------------------------------' msg_string += msgs.newline() + ' exp# offset (pixels) offset (arcsec)' for iexp, off in enumerate(offsets): - msg_string += msgs.newline() + ' {:2d} {:5.2f} {:6.3f}'.format(iexp, off, off*platescale) + msg_string += msgs.newline() + ' {:2d} {:6.2f} {:6.3f}'.format(iexp, off, off*platescale) msg_string += msgs.newline() + '---------------------------------------------------------------------------------' msgs.info(msg_string) From 13402bc5a85e8c2c3c01fc6a43cf83787086a043 Mon Sep 17 00:00:00 2001 From: jhennawi Date: Fri, 19 Jul 2024 21:18:24 +0900 Subject: [PATCH 07/13] Minor modifications. --- pypeit/core/coadd.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pypeit/core/coadd.py b/pypeit/core/coadd.py index 6a14c2a090..a70d2c8102 100644 --- a/pypeit/core/coadd.py +++ b/pypeit/core/coadd.py @@ -1859,18 +1859,18 @@ def spec_reject_comb(wave_grid, wave_grid_mid, waves_list, fluxes_list, ivars_li # Compute the stack wave_stack, flux_stack, ivar_stack, gpm_stack, nused = compute_stack( wave_grid, waves_list, fluxes_list, ivars_list, utils.array_to_explist(this_gpms, nspec_list=nspec_list), weights_list) - # Interpolate the individual spectra onto the wavelength grid of the stack. Use wave_grid_mid for this - # since it has no masked values + # Interpolate the stack onto the wavelength grids of the individual spectra. This will be used to perform + # the rejection of pixels in the individual spectra. flux_stack_nat, ivar_stack_nat, gpm_stack_nat, _ = interp_spec( waves, wave_grid_mid, flux_stack, ivar_stack, gpm_stack) - ## TESTING - #nused_stack_nat, _, _ = interp_spec( - # waves, wave_grid_mid, nused, ivar_stack, mask_stack) - #embed() - rejivars, sigma_corrs, outchi, chigpm = update_errors(fluxes, ivars, this_gpms, - flux_stack_nat, ivar_stack_nat, gpm_stack_nat, sn_clip=sn_clip) - this_gpms, qdone = pydl.djs_reject(fluxes, flux_stack_nat, outmask=this_gpms,inmask=gpms, invvar=rejivars, + rejivars, sigma_corrs, outchi, chigpm = update_errors( + fluxes, ivars, this_gpms, flux_stack_nat, ivar_stack_nat, gpm_stack_nat, sn_clip=sn_clip) + this_gpms, qdone = pydl.djs_reject(fluxes, flux_stack_nat, outmask=this_gpms, inmask=gpms, invvar=rejivars, lower=lower,upper=upper, maxrej=maxrej, sticky=False) + #rejivars, sigma_corrs, outchi, chigpm = update_errors( + # fluxes, ivars, this_gpms, flux_stack_nat, ivar_stack_nat, gpm_stack_nat, sn_clip=sn_clip) + #this_gpms, qdone = pydl.djs_reject(fluxes, flux_stack_nat, outmask=this_gpms, inmask=gpms, invvar=rejivars, + # lower=lower,upper=upper, maxrej=maxrej, sticky=False) iter += 1 From 6825f1b203436e1dd9a12bdf4b9c17b66ad13a2a Mon Sep 17 00:00:00 2001 From: Kyle Westfall Date: Fri, 6 Sep 2024 16:23:56 -0700 Subject: [PATCH 08/13] doc update --- doc/help/pypeit_coadd_2dspec.rst | 10 -------- doc/help/pypeit_ql.rst | 40 +++++++++++++++++-------------- doc/help/pypeit_setup_coadd2d.rst | 10 ++++++++ doc/help/pypeit_show_2dspec.rst | 3 ++- doc/help/run_pypeit.rst | 2 +- doc/pypeit_par.rst | 2 ++ 6 files changed, 37 insertions(+), 30 deletions(-) diff --git a/doc/help/pypeit_coadd_2dspec.rst b/doc/help/pypeit_coadd_2dspec.rst index 53cb0d2e57..d5d0aae36c 100644 --- a/doc/help/pypeit_coadd_2dspec.rst +++ b/doc/help/pypeit_coadd_2dspec.rst @@ -3,8 +3,6 @@ $ pypeit_coadd_2dspec -h usage: pypeit_coadd_2dspec [-h] [--show] [--debug_offsets] [--peaks] [--basename BASENAME] [--debug] [-v VERBOSITY] - [--spec_samp_fact SPEC_SAMP_FACT] - [--spat_samp_fact SPAT_SAMP_FACT] coadd2d_file Coadd 2D spectra produced by PypeIt @@ -27,12 +25,4 @@ Verbosity level between 0 [none] and 2 [all]. Default: 1. Level 2 writes a log with filename coadd_2dspec_YYYYMMDD-HHMM.log (default: 1) - --spec_samp_fact SPEC_SAMP_FACT - Make the wavelength grid finer (spec_samp_fact < 1.0) or - coarser (spec_samp_fact > 1.0) by this sampling factor, - i.e. units of spec_samp_fact are pixels. (default: 1.0) - --spat_samp_fact SPAT_SAMP_FACT - Make the spatial grid finer (spat_samp_fact < 1.0) or - coarser (spat_samp_fact > 1.0) by this sampling factor, - i.e. units of spat_samp_fact are pixels. (default: 1.0) \ No newline at end of file diff --git a/doc/help/pypeit_ql.rst b/doc/help/pypeit_ql.rst index 9302bc39c0..1653437b5d 100644 --- a/doc/help/pypeit_ql.rst +++ b/doc/help/pypeit_ql.rst @@ -8,10 +8,11 @@ [--calibs_only] [--overwrite_calibs] [--det DET [DET ...]] [--slitspatnum SLITSPATNUM] [--maskID MASKID] [--boxcar_radius BOXCAR_RADIUS] [--snr_thresh SNR_THRESH] - [--ignore_std] [--skip_display] [--coadd2d] - [--only_slits ONLY_SLITS [ONLY_SLITS ...]] [--offsets OFFSETS] - [--weights WEIGHTS] [--spec_samp_fact SPEC_SAMP_FACT] - [--spat_samp_fact SPAT_SAMP_FACT] [--try_old] + [--ignore_std] [--skip_display] [--removetrace] [--coadd2d] + [--spec_samp_fact SPEC_SAMP_FACT] + [--spat_samp_fact SPAT_SAMP_FACT] [--offsets OFFSETS] + [--weights WEIGHTS] [--only_slits ONLY_SLITS [ONLY_SLITS ...]] + [--try_old] spectrograph Script to produce quick-look PypeIt reductions @@ -110,13 +111,20 @@ detected, ignore those frames. Otherwise, they are included with the reduction of the science frames. (default: False) - --skip_display Run the quicklook without displaying any results. - (default: True) + --skip_display Run the quicklook without displaying any results. The + default skip_display=False will show the results. + (default: False) + --removetrace When the image is shown, do not overplot traces in the + skysub, sky_resid, and resid channels (default: False) --coadd2d Perform default 2D coadding. (default: False) - --only_slits ONLY_SLITS [ONLY_SLITS ...] - If coadding, only coadd this space-separated set of - slits. If not provided, all slits are coadded. (default: - None) + --spec_samp_fact SPEC_SAMP_FACT + If coadding, adjust the wavelength grid sampling by this + factor. For a finer grid, set value to <1.0; for coarser + sampling, set value to >1.0). (default: 1.0) + --spat_samp_fact SPAT_SAMP_FACT + If coadding, adjust the spatial grid sampling by this + factor. For a finer grid, set value to <1.0; for coarser + sampling, set value to >1.0). (default: 1.0) --offsets OFFSETS If coadding, spatial offsets to apply to each image; see the [coadd2d][offsets] parameter. Options are restricted here to either maskdef_offsets or auto. If not @@ -126,14 +134,10 @@ [coadd2d][weights] parameter. Options are restricted here to either uniform or auto. If not specified, the (spectrograph-specific) default is used. (default: None) - --spec_samp_fact SPEC_SAMP_FACT - If coadding, adjust the wavelength grid sampling by this - factor. For a finer grid, set value to <1.0; for coarser - sampling, set value to >1.0). (default: 1.0) - --spat_samp_fact SPAT_SAMP_FACT - If coadding, adjust the spatial grid sampling by this - factor. For a finer grid, set value to <1.0; for coarser - sampling, set value to >1.0). (default: 1.0) + --only_slits ONLY_SLITS [ONLY_SLITS ...] + If coadding, only coadd this space-separated set of + slits. If not provided, all slits are coadded. (default: + None) --try_old Attempt to load old datamodel versions. A crash may ensue.. (default: False) \ No newline at end of file diff --git a/doc/help/pypeit_setup_coadd2d.rst b/doc/help/pypeit_setup_coadd2d.rst index b9d76bd679..54d9f23f6a 100644 --- a/doc/help/pypeit_setup_coadd2d.rst +++ b/doc/help/pypeit_setup_coadd2d.rst @@ -9,6 +9,8 @@ [--exclude_slits EXCLUDE_SLITS [EXCLUDE_SLITS ...]] [--spat_toler SPAT_TOLER] [--offsets OFFSETS] [--weights WEIGHTS] + [--spec_samp_fact SPEC_SAMP_FACT] + [--spat_samp_fact SPAT_SAMP_FACT] Prepare a configuration file for performing 2D coadds @@ -68,4 +70,12 @@ or auto. If not specified, the (spectrograph-specific) default is used. Other options exist but must be entered by directly editing the coadd2d file. (default: None) + --spec_samp_fact SPEC_SAMP_FACT + Make the wavelength grid finer (spec_samp_fact < 1.0) or + coarser (spec_samp_fact > 1.0) by this sampling factor, + i.e. units of spec_samp_fact are pixels. (default: 1.0) + --spat_samp_fact SPAT_SAMP_FACT + Make the spatial grid finer (spat_samp_fact < 1.0) or + coarser (spat_samp_fact > 1.0) by this sampling factor, + i.e. units of spat_samp_fact are pixels. (default: 1.0) \ No newline at end of file diff --git a/doc/help/pypeit_show_2dspec.rst b/doc/help/pypeit_show_2dspec.rst index 1ab710aebf..9b46fbc745 100644 --- a/doc/help/pypeit_show_2dspec.rst +++ b/doc/help/pypeit_show_2dspec.rst @@ -20,7 +20,8 @@ constructed assuming the reduction is for a single detector. If a string, it must match the name of the detector object (e.g., DET01 for a detector, MSC01 for a - mosaic). (default: 1) + mosaic). If not set, the first available detectorin the + spec2d file will be shown (default: None) --spat_id SPAT_ID Restrict plotting to this slit (PypeIt ID notation) (default: None) --maskID MASKID Restrict plotting to this maskID (default: None) diff --git a/doc/help/run_pypeit.rst b/doc/help/run_pypeit.rst index 4cb290e4fa..77d8e84bbc 100644 --- a/doc/help/run_pypeit.rst +++ b/doc/help/run_pypeit.rst @@ -4,7 +4,7 @@ usage: run_pypeit [-h] [-v VERBOSITY] [-r REDUX_PATH] [-m] [-s] [-o] [-c] pypeit_file - ## PypeIt : The Python Spectroscopic Data Reduction Pipeline v1.16.1.dev336+gdf3013372.d20240827 + ## PypeIt : The Python Spectroscopic Data Reduction Pipeline v1.16.1.dev468+g832ee84e4 ## ## Available spectrographs include: ## aat_uhrf, bok_bc, gemini_flamingos1, gemini_flamingos2, diff --git a/doc/pypeit_par.rst b/doc/pypeit_par.rst index ee49c46478..7bbb47919a 100644 --- a/doc/pypeit_par.rst +++ b/doc/pypeit_par.rst @@ -570,7 +570,9 @@ Key Type Options Default Description ``manual`` str .. .. Manual extraction parameters. det:spat:spec:fwhm:boxcar_radius. Multiple manual extractions are semi-colon separated, and spat,spec are in the pseudo-image generated by COADD2D.boxcar_radius is optional and in pixels (not arcsec!). ``offsets`` str, list .. ``auto`` Offsets for the images being combined (spat pixels). Options are: ``maskdef_offsets``, ``header``, ``auto``, and a list of offsets. Use ``maskdef_offsets`` to use the offsets computed during the slitmask design matching (currently available for these :ref:`slitmask_info_instruments` only). If equal to ``header``, the dither offsets recorded in the header, when available, will be used. If ``auto`` is chosen, PypeIt will try to compute the offsets using a reference object with the highest S/N, or an object selected by the user (see ``user_obj``). If a list of offsets is provided, PypeIt will use it. ``only_slits`` str, list .. .. Restrict coaddition to one or more of slits. Example syntax -- DET01:175,DET02:205 or MSC02:2234. This and ``exclude_slits`` are mutually exclusive. If both are provided, ``only_slits`` takes precedence. +``spat_samp_fact`` float .. 1.0 Make the spatial sampling finer (``spat_samp_fact`` lessthan 1.0) or coarser (``spat_samp_fact`` greather than 1.0) bythis sampling factor. This basically multiples the 'native'spatial pixel size by ``spat_samp_fact``, i.e. the units of``spat_samp_fact`` are pixels. ``spat_toler`` int .. 5 This parameter provides the desired tolerance in spatial pixel used to identify slits in different exposures +``spec_samp_fact`` float .. 1.0 Make the wavelength grid sampling finer (``spec_samp_fact`` less than 1.0)or coarser (``spec_samp_fact`` greater than 1.0) by this sampling factor.This multiples the 'native' spectral pixel size by ``spec_samp_fact``,i.e. the units of ``spec_samp_fact`` are pixels. ``use_slits4wvgrid`` bool .. False If True, use the slits to set the trace down the center ``user_obj`` int, list .. .. Object that the user wants to use to compute the weights and/or the offsets for coadding images. For longslit/multislit spectroscopy, provide the ``SLITID`` and the ``OBJID``, separated by comma, of the selected object. For echelle spectroscopy, provide the ``ECH_OBJID`` of the selected object. See :doc:`out_spec1D` for more info about ``SLITID``, ``OBJID`` and ``ECH_OBJID``. If this parameter is not ``None``, it will be used to compute the offsets only if ``offsets = auto``, and it will used to compute the weights only if ``weights = auto``. ``wave_method`` str .. .. Argument to :func:`~pypeit.core.wavecal.wvutils.get_wave_grid` method, which determines how the 2d coadd wavelength grid is constructed. The default is None, which will use a linear gridfor longslit/multislit coadds and a log10 grid for echelle coadds. Currently supported options with 2d coadding are:* 'iref' -- Use one of the exposures (the first) as the reference for the wavelength grid * 'velocity' -- Grid is uniform in velocity* 'log10' -- Grid is uniform in log10(wave). This is the same as velocity.* 'linear' -- Grid is uniform in wavelength From 2e1ae708b554dae8dcd6bf2cb17fb306ffe1d25d Mon Sep 17 00:00:00 2001 From: Debora Pelliccia Date: Mon, 16 Sep 2024 17:58:05 -1000 Subject: [PATCH 09/13] comments --- pypeit/coadd2d.py | 51 ++++++++++++++++------------------- pypeit/scripts/ql.py | 2 +- pypeit/scripts/show_2dspec.py | 7 ----- 3 files changed, 24 insertions(+), 36 deletions(-) diff --git a/pypeit/coadd2d.py b/pypeit/coadd2d.py index 1f8887bc37..91e66e133d 100644 --- a/pypeit/coadd2d.py +++ b/pypeit/coadd2d.py @@ -28,8 +28,6 @@ from pypeit.core.wavecal import wvutils from pypeit.core import coadd from pypeit.core import parse -#from pypeit.core import parse -from pypeit import calibrations from pypeit import spec2dobj from pypeit.core.moment import moment1d from pypeit.manual_extract import ManualExtractionObj @@ -210,6 +208,7 @@ def __init__(self, spec2d, spectrograph, par, det=1, offsets=None, weights='auto self.pseudo_dict = None self.objid_bri = None + self.spat_pixpos_bri = None self.slitidx_bri = None self.snr_bar_bri = None self.use_weights = None # This is a list of length self.nexp that is assigned by the compute_weights method @@ -836,21 +835,20 @@ def reduce(self, pseudo_dict, show=False, clear_ginga=True, show_peaks=False, sh # maskdef stuff if parcopy['reduce']['slitmask']['assign_obj'] and slits.maskdef_designtab is not None: - # Get plate scale - platescale = parse.parse_binning(sciImage.detector.binning)[1]*sciImage.detector.platescale*self.spat_samp_fact - #platescale = sciImage.detector.platescale * self.spat_samp_fact + # Get pixel scale, binned and resampled (if requested), i.e., pixel scale of the pseudo image + resampled_pixscale = parse.parse_binning(sciImage.detector.binning)[1]*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, resampled_pixscale, None, TOLER=parcopy['reduce']['slitmask']['obj_toler']) if parcopy['reduce']['slitmask']['extract_missing_objs'] is True: # Set the FWHM for the extraction of missing objects - fwhm = slits.get_maskdef_extract_fwhm(sobjs_obj, platescale, + fwhm = slits.get_maskdef_extract_fwhm(sobjs_obj, resampled_pixscale, parcopy['reduce']['slitmask']['missing_objs_fwhm'], parcopy['reduce']['findobj']['find_fwhm']) # Assign undetected objects sobjs_obj = slits.mask_add_missing_obj(sobjs_obj, None, fwhm, - parcopy['reduce']['slitmask']['missing_objs_boxcar_rad']/platescale) + parcopy['reduce']['slitmask']['missing_objs_boxcar_rad']/resampled_pixscale) # Initiate Extract object exTract = extraction.Extract.get_instance(sciImage, pseudo_dict['slits'], sobjs_obj, self.spectrograph, parcopy, @@ -873,19 +871,18 @@ def reduce(self, pseudo_dict, show=False, clear_ginga=True, show_peaks=False, sh objmodel_pseudo, ivarmodel_pseudo, outmask_pseudo, sobjs, sciImage.detector, slits, \ pseudo_dict['tilts'], pseudo_dict['waveimg'] - - def offsets_report(self, offsets, platescale, offsets_method): + @staticmethod + def offsets_report(offsets, pixscale, offsets_method): """ - Print out a report on the offsets + Print out a report on the offsets of the frames to be coadded Args: offsets (`numpy.ndarray`_) Array of offsets - platescale (float): - the plate scale for this detector + pixscale (float): + The (binned) pixelscale in arcsec/pixel. offsets_method (str): A string describing the method used to determine the offsets - Returns: """ @@ -895,7 +892,7 @@ def offsets_report(self, offsets, platescale, offsets_method): msg_string += msgs.newline() + '---------------------------------------------------------------------------------' msg_string += msgs.newline() + ' exp# offset (pixels) offset (arcsec)' for iexp, off in enumerate(offsets): - msg_string += msgs.newline() + ' {:2d} {:6.2f} {:6.3f}'.format(iexp, off, off*platescale) + msg_string += msgs.newline() + ' {:2d} {:6.2f} {:6.3f}'.format(iexp, off, off*pixscale) msg_string += msgs.newline() + '---------------------------------------------------------------------------------' msgs.info(msg_string) @@ -1108,8 +1105,8 @@ def compute_offsets(self, offsets): """ msgs.info('Get Offsets') - platescale = parse.parse_binning(self.stack_dict['detectors'][0].binning)[1]*self.stack_dict['detectors'][0].platescale - #platescale = self.stack_dict['detectors'][0].platescale + # binned pixel scale of the frames to be coadded + pixscale = parse.parse_binning(self.stack_dict['detectors'][0].binning)[1]*self.stack_dict['detectors'][0].platescale # 1) offsets are provided in the header of the spec2d files if offsets == 'header': msgs.info('Using offsets from header') @@ -1117,9 +1114,9 @@ def compute_offsets(self, offsets): if None in dithoffs: msgs.error('Dither offsets keyword not found for one or more spec2d files. ' 'Choose another option for `offsets`') - dithoffs_pix = - np.array(dithoffs) / platescale + dithoffs_pix = - np.array(dithoffs) / pixscale self.offsets = dithoffs_pix[0] - dithoffs_pix - self.offsets_report(self.offsets, platescale, 'header keyword') + self.offsets_report(self.offsets, pixscale, 'header keyword') elif self.objid_bri is None and offsets == 'auto': msgs.error('Offsets cannot be computed because no unique reference object ' @@ -1130,7 +1127,7 @@ def compute_offsets(self, offsets): msgs.info('Using user input offsets') # use them self.offsets = self.check_input(offsets, 'offsets') - self.offsets_report(self.offsets, platescale, 'user input') + self.offsets_report(self.offsets, pixscale, 'user input') # 3) parset `offsets` is = 'maskdef_offsets' (no matter if we have a bright object or not) elif offsets == 'maskdef_offsets': @@ -1138,7 +1135,7 @@ def compute_offsets(self, offsets): # the offsets computed during the main reduction (`run_pypeit`) are used msgs.info('Determining offsets using maskdef_offset recoded in SlitTraceSet') self.offsets = self.maskdef_offset[0] - self.maskdef_offset - self.offsets_report(self.offsets, platescale, 'maskdef_offset') + self.offsets_report(self.offsets, pixscale, 'maskdef_offset') else: # if maskdef_offsets were not computed during the main reduction, we cannot continue msgs.error('No maskdef_offset recoded in SlitTraceSet') @@ -1193,7 +1190,6 @@ def compute_weights(self, weights): else: msgs.error('Invalid value for `weights`') - def get_brightest_obj(self, specobjs_list, spat_ids): """ Dummy method to identify the brightest object. Overloaded by child methods. @@ -1292,13 +1288,11 @@ def __init__(self, spec2d_files, spectrograph, par, det=1, offsets=None, weights # find if there is a bright object we could use if len(self.stack_dict['specobjs_list']) > 0 and self.par['coadd2d']['user_obj'] is not None: _slitidx_bri = np.where(np.abs(self.spat_ids - user_slit) <= self.par['coadd2d']['spat_toler'])[0][0] - self.objid_bri, self.slitidx_bri, self.spatid_bri, self.snr_bar_bri = \ - np.repeat(user_objid, self.nexp), _slitidx_bri, user_slit, None + self.objid_bri, self.spat_pixpos_bri, self.slitidx_bri, self.spatid_bri, self.snr_bar_bri = \ + np.repeat(user_objid, self.nexp), None, _slitidx_bri, user_slit, None elif len(self.stack_dict['specobjs_list']) > 0 and (offsets == 'auto' or weights == 'auto'): self.objid_bri, self.spat_pixpos_bri, self.slitidx_bri, self.spatid_bri, self.snr_bar_bri = \ self.get_brightest_obj(self.stack_dict['specobjs_list'], self.spat_ids) - else: - self.objid_bri, self.slitidx_bri, self.spatid_bri, self.snr_bar_bri = (None,)*4 # get self.use_weights self.compute_weights(weights) @@ -1385,8 +1379,9 @@ def compute_offsets(self, offsets): plt.show() self.offsets = offsets - platescale = parse.parse_binning(self.stack_dict['detectors'][0].binning)[1]*self.stack_dict['detectors'][0].platescale - self.offsets_report(self.offsets, platescale, offsets_method) + # binned pixel scale of the frames to be coadded + pixscale = parse.parse_binning(self.stack_dict['detectors'][0].binning)[1]*self.stack_dict['detectors'][0].platescale + self.offsets_report(self.offsets, pixscale, offsets_method) def compute_weights(self, weights): """ diff --git a/pypeit/scripts/ql.py b/pypeit/scripts/ql.py index db8d59fd14..5403685788 100644 --- a/pypeit/scripts/ql.py +++ b/pypeit/scripts/ql.py @@ -714,7 +714,7 @@ def get_parser(cls, width=None): help='If standard star observations are automatically detected, ' 'ignore those frames. Otherwise, they are included with the ' 'reduction of the science frames.') - parser.add_argument('--skip_display', dest='skip_display', default=False, action='store_true', + parser.add_argument('--skip_display', default=False, action='store_true', help='Run the quicklook without displaying any results. The default skip_display=False will show the results.') parser.add_argument('--removetrace', default=False, action='store_true', help='When the image is shown, do not overplot traces in the skysub, sky_resid, and resid ' diff --git a/pypeit/scripts/show_2dspec.py b/pypeit/scripts/show_2dspec.py index 3cba819571..fd16e874fd 100644 --- a/pypeit/scripts/show_2dspec.py +++ b/pypeit/scripts/show_2dspec.py @@ -154,13 +154,6 @@ def main(args): else: detname = DetectorContainer.get_name(det) - #try: - # det = int(args.det) - #except: - # detname = args.det - #else: - # detname = DetectorContainer.get_name(det) - # Find the set of channels to show show_channels = [0,1,2,3] if args.channels is None \ else [int(item) for item in args.channels.split(',')] From 2a074b289a833a278ee5e64ef392bd04ff36f213 Mon Sep 17 00:00:00 2001 From: Debora Pelliccia Date: Tue, 17 Sep 2024 17:10:08 -1000 Subject: [PATCH 10/13] user_objid --- pypeit/coadd2d.py | 141 ++++++++++++++++++++++++++++------------------ 1 file changed, 85 insertions(+), 56 deletions(-) diff --git a/pypeit/coadd2d.py b/pypeit/coadd2d.py index 91e66e133d..02b4697eaf 100644 --- a/pypeit/coadd2d.py +++ b/pypeit/coadd2d.py @@ -1190,6 +1190,43 @@ def compute_weights(self, weights): else: msgs.error('Invalid value for `weights`') + @staticmethod + def unpack_specobj(spec, spatord_id=None): + """ + Utility routine to unpack flux, ivar, and gpm from a single SpecObj object. + Args: + spec (:class:`~pypeit.specobj.SpecObj`): + SpecObj object to unpack. + spatord_id (:obj:`int`, optional): + Slit/order ID to unpack. If None, the Slit/order ID + of the SpecObj object is used. + + Returns: + :obj:`tuple`: Returns the following: flux (`numpy.ndarray`_), ivar + (`numpy.ndarray`_), gpm (`numpy.ndarray`_). + + """ + # Get the slit/order ID if not provided + if spatord_id is None: + spatord_id = spec.ECH_ORDER if spec.ECH_ORDER is not None else spec.SLITID + + # get OBJID, which is different for Echelle and MultiSlit + objid = spec.ECH_OBJID if spec.ECH_OBJID is not None else spec.OBJID + + # check if OPT_COUNTS is available + if spec.has_opt_ext() and np.any(spec.OPT_MASK): + _, flux, ivar, gpm = spec.get_opt_ext() + # check if BOX_COUNTS is available + elif spec.has_box_ext() and np.any(spec.BOX_MASK): + _, flux, ivar, gpm = spec.get_box_ext() + msgs.warn(f'Optimal extraction not available for obj {objid} ' + f'in slit/order {spatord_id}. Using box extraction.') + else: + msgs.warn(f'Optimal and Boxcar extraction not available for obj {objid} in slit/order {spatord_id}.') + _, flux, ivar, gpm = None, None, None, None + + return flux, ivar, gpm + def get_brightest_obj(self, specobjs_list, spat_ids): """ Dummy method to identify the brightest object. Overloaded by child methods. @@ -1271,25 +1308,40 @@ def __init__(self, spec2d_files, spectrograph, par, det=1, offsets=None, weights wave_method = 'linear' if self.par['coadd2d']['wave_method'] is None else self.par['coadd2d']['wave_method'] self.wave_grid, self.wave_grid_mid, self.dsamp = self.get_wave_grid(wave_method) - # Check if the user-input object to compute offsets and weights exists - if self.par['coadd2d']['user_obj'] is not None: + # If a user-input object to compute offsets and weights is provided, check if it exists and get the needed info + if len(self.stack_dict['specobjs_list']) > 0 and self.par['coadd2d']['user_obj'] is not None: if len(self.par['coadd2d']['user_obj']) != 2: msgs.error('Parameter `user_obj` must include both SLITID and OBJID.') else: user_slit, user_objid = self.par['coadd2d']['user_obj'] # does it exists? - user_obj_exist = [] - for sobjs in self.stack_dict['specobjs_list']: - user_obj_exist.append(np.any(sobjs.slitorder_objid_indices(user_slit, user_objid, - toler=self.par['coadd2d']['spat_toler']))) + user_obj_exist = np.zeros(self.nexp, dtype=bool) + # get the flux, ivar, gpm, and spatial pixel position of the user object + fluxes, ivars, gpms, spat_pixpos = [], [], [], [] + for i, sobjs in enumerate(self.stack_dict['specobjs_list']): + user_idx = sobjs.slitorder_objid_indices(user_slit, user_objid, + toler=self.par['coadd2d']['spat_toler']) + if np.any(user_idx): + this_sobj = sobjs[user_idx][0] + flux_iobj, ivar_iobj, gpm_iobj = self.unpack_specobj(this_sobj) + spat_pixpos_iobj = this_sobj.SPAT_PIXPOS + if flux_iobj is not None and ivar_iobj is not None and gpm_iobj is not None: + fluxes.append(flux_iobj) + ivars.append(ivar_iobj) + gpms.append(gpm_iobj) + spat_pixpos.append(spat_pixpos_iobj) + user_obj_exist[i] = True + # check if the user object exists in all the exposures if not np.all(user_obj_exist): msgs.error('Object provided through `user_obj` does not exist in all the exposures.') - - # find if there is a bright object we could use - if len(self.stack_dict['specobjs_list']) > 0 and self.par['coadd2d']['user_obj'] is not None: - _slitidx_bri = np.where(np.abs(self.spat_ids - user_slit) <= self.par['coadd2d']['spat_toler'])[0][0] - self.objid_bri, self.spat_pixpos_bri, self.slitidx_bri, self.spatid_bri, self.snr_bar_bri = \ - np.repeat(user_objid, self.nexp), None, _slitidx_bri, user_slit, None + # get the needed info about the user object + self.objid_bri = np.repeat(user_objid, self.nexp) + self.spat_pixpos_bri = spat_pixpos + self.slitidx_bri = np.where(np.abs(self.spat_ids - user_slit) <= self.par['coadd2d']['spat_toler'])[0][0] + self.spatid_bri = user_slit + self.snr_bar_bri, _ = coadd.calc_snr(fluxes, ivars, gpms) + + # otherwise, find if there is a bright object we could use elif len(self.stack_dict['specobjs_list']) > 0 and (offsets == 'auto' or weights == 'auto'): self.objid_bri, self.spat_pixpos_bri, self.slitidx_bri, self.spatid_bri, self.snr_bar_bri = \ self.get_brightest_obj(self.stack_dict['specobjs_list'], self.spat_ids) @@ -1406,10 +1458,9 @@ def compute_weights(self, weights): _, self.use_weights = self.optimal_weights(self.spatid_bri, self.objid_bri, weight_method='constant') if self.par['coadd2d']['user_obj'] is not None: msgs.info(f'Weights computed using a unique reference object in slit={self.spatid_bri} provided by the user') - # TODO Should we not still printo out a report for this usage case? else: msgs.info(f'Weights computed using a unique reference object in slit={self.spatid_bri} with the highest S/N') - self.snr_report(self.spatid_bri, self.spat_pixpos_bri, self.snr_bar_bri) + self.snr_report(self.spatid_bri, self.spat_pixpos_bri, self.snr_bar_bri) def get_brightest_obj(self, specobjs_list, slit_spat_ids): @@ -1456,26 +1507,13 @@ def get_brightest_obj(self, specobjs_list, slit_spat_ids): objid_this = sobjs[ithis].OBJID spat_pixpos_this = sobjs[ithis].SPAT_PIXPOS fluxes, ivars, gpms = [], [], [] - for iobj, spec in enumerate(sobjs[ithis]): - # check if OPT_COUNTS is available - if spec.has_opt_ext() and np.any(spec.OPT_MASK): - _, flux_iobj, ivar_iobj, gpm_iobj = spec.get_opt_ext() + for spec in sobjs[ithis]: + flux_iobj, ivar_iobj, gpm_iobj = self.unpack_specobj(spec, spatord_id=spat_id) + if flux_iobj is not None and ivar_iobj is not None and gpm_iobj is not None: fluxes.append(flux_iobj) ivars.append(ivar_iobj) gpms.append(gpm_iobj) - # check if BOX_COUNTS is available - elif spec.has_box_ext() and np.any(spec.BOX_MASK): - _, flux_iobj, ivar_iobj, gpm_iobj = spec.get_box_ext() - fluxes.append(flux_iobj) - ivars.append(ivar_iobj) - gpms.append(gpm_iobj) - msgs.warn(f'Optimal extraction not available for obj {spec.OBJID} ' - f'in slit {spat_id}. Using box extraction.') - # if both are not available, we remove the object in this slit, - # because otherwise coadd.sn_weights will crash - else: - msgs.warn(f'Optimal and Boxcar extraction not available for obj {spec.OBJID} in slit {spat_id}.') - #remove_indx.append(iobj) + # if there are objects on this slit left, we can proceed with computing rms_sn if len(fluxes) > 0: rms_sn, _ = coadd.calc_snr(fluxes, ivars, gpms) @@ -1509,7 +1547,6 @@ def get_brightest_obj(self, specobjs_list, slit_spat_ids): return objid, spat_pixpos, slitid, slit_spat_ids[slitid], snr_bar - def snr_report(self, slitid, spat_pixpos, snr_bar): """ @@ -1669,28 +1706,30 @@ def __init__(self, spec2d_files, spectrograph, par, det=1, offsets=None, weights wave_method = 'log10' if self.par['coadd2d']['wave_method'] is None else self.par['coadd2d']['wave_method'] self.wave_grid, self.wave_grid_mid, self.dsamp = self.get_wave_grid(wave_method) - # Check if the user-input object to compute offsets and weights exists - if self.par['coadd2d']['user_obj'] is not None: + # If a user-input object to compute offsets and weights is provided, check if it exists and get the needed info + if len(self.stack_dict['specobjs_list']) > 0 and self.par['coadd2d']['user_obj'] is not None: if not isinstance(self.par['coadd2d']['user_obj'], int): msgs.error('Parameter `user_obj` must include only the object OBJID.') else: user_objid = self.par['coadd2d']['user_obj'] # does it exists? - user_obj_exist = [] - for sobjs in self.stack_dict['specobjs_list']: + user_obj_exist = np.zeros((self.nexp,self.nslits_single), dtype=bool) + for i, sobjs in enumerate(self.stack_dict['specobjs_list']): for iord in range(self.nslits_single): - user_obj_exist.append(np.any(sobjs.slitorder_objid_indices(sobjs.ECH_ORDER[iord], user_objid))) + # check if the object exists in this exposure + ind = (sobjs.ECH_ORDERINDX == iord) & (sobjs.ECH_OBJID == user_objid) + flux, ivar, mask = self.unpack_specobj(sobjs[ind][0]) + if flux is not None and ivar is not None and mask is not None: + user_obj_exist[i, iord] = True if not np.all(user_obj_exist): msgs.error('Object provided through `user_obj` does not exist in all the exposures.') - # find if there is a bright object we could use - if len(self.stack_dict['specobjs_list']) > 0 and self.par['coadd2d']['user_obj'] is not None: - self.objid_bri, self.slitidx_bri, self.snr_bar_bri = np.repeat(user_objid, self.nexp), None, None + # get the needed info about the user object + self.objid_bri, self.slitidx_bri, self.snr_bar_bri = np.repeat(user_objid, self.nexp), None, None + elif len(self.stack_dict['specobjs_list']) > 0 and (offsets == 'auto' or weights == 'auto'): self.objid_bri, self.slitidx_bri, self.snr_bar_bri = \ self.get_brightest_obj(self.stack_dict['specobjs_list'], self.nslits_single) - else: - self.objid_bri, self.slitidx_bri, self.snr_bar_bri = (None,)*3 # get self.use_weights self.compute_weights(weights) @@ -1747,6 +1786,7 @@ def compute_weights(self, weights): self.use_weights.append(iweights) if self.par['coadd2d']['user_obj'] is not None: msgs.info('Weights computed using a unique reference object provided by the user') + # TODO: implement something here to print out the snr_report else: msgs.info('Weights computed using a unique reference object with the highest S/N') self.snr_report(self.snr_bar_bri) @@ -1781,21 +1821,10 @@ def get_brightest_obj(self, specobjs_list, nslits): bpm = np.ones((nslits, nobjs), dtype=bool) for iord in range(nslits): for iobj in range(nobjs): - flux = None ind = (sobjs.ECH_ORDERINDX == iord) & (sobjs.ECH_OBJID == uni_objid[iobj]) - # check if OPT_COUNTS is available - if sobjs[ind][0].has_opt_ext() and np.any(sobjs[ind][0].OPT_MASK): - _, flux, ivar, mask = sobjs[ind][0].get_opt_ext() - # check if BOX_COUNTS is available - elif sobjs[ind][0].has_box_ext() and np.any(sobjs[ind][0].BOX_MASK): - _, flux, ivar, mask = sobjs[ind][0].get_box_ext() - msgs.warn(f'Optimal extraction not available for object {sobjs[ind][0].ECH_OBJID} ' - f'in order {sobjs[ind][0].ECH_ORDER}. Using box extraction.') - else: - msgs.warn(f'Optimal and Boxcar extraction not available for ' - f'object {sobjs[ind][0].ECH_OBJID} in order {sobjs[ind][0].ECH_ORDER}.') - continue - if flux is not None: + flux, ivar, mask = self.unpack_specobj(sobjs[ind][0], spatord_id=sobjs[ind][0].ECH_ORDER) + + if flux is not None and ivar is not None and mask is not None: rms_sn, _ = coadd.calc_snr([flux], [ivar], [mask]) order_snr[iord, iobj] = rms_sn[0] bpm[iord, iobj] = False From 7a7bb85df60e5bac565cf06f05e68a7550ba8285 Mon Sep 17 00:00:00 2001 From: Kyle Westfall Date: Thu, 19 Sep 2024 16:22:51 -0700 Subject: [PATCH 11/13] doc update --- doc/coadd1d.rst | 2 +- doc/help/pypeit_sensfunc.rst | 26 ++++++++++++------------- doc/include/class_datamodel_specobj.rst | 4 +++- doc/include/datamodel_specobj.rst | 4 +++- doc/include/spectrographs_table.rst | 2 +- doc/pypeit_par.rst | 2 +- pypeit/coadd2d.py | 4 +++- pypeit/core/skysub.py | 2 +- 8 files changed, 26 insertions(+), 20 deletions(-) diff --git a/doc/coadd1d.rst b/doc/coadd1d.rst index 0c4f2b8da4..6520efe6aa 100644 --- a/doc/coadd1d.rst +++ b/doc/coadd1d.rst @@ -312,7 +312,7 @@ This tool is developed by Michael Murphy and is available at `this link `__. Here is an example of a coadded spectrum using UVES_popler: -.. image:: ../figures/uves_popler.png +.. image:: figures/uves_popler.png :scale: 60% UVES_popler was originally written to coadd ESO/UVES echelle spectra diff --git a/doc/help/pypeit_sensfunc.rst b/doc/help/pypeit_sensfunc.rst index 701944f327..26ac6639bb 100644 --- a/doc/help/pypeit_sensfunc.rst +++ b/doc/help/pypeit_sensfunc.rst @@ -2,7 +2,7 @@ $ pypeit_sensfunc -h usage: pypeit_sensfunc [-h] [--extr {OPT,BOX}] [--algorithm {UVIS,IR}] - [--multi MULTI] [-o OUTFILE] [-s SENS_FILE] [-f FLATFILE] + [--multi MULTI] [-o OUTFILE] [-s SENS_FILE] [-f] [--debug] [--par_outfile PAR_OUTFILE] [-v VERBOSITY] spec1dfile @@ -71,20 +71,20 @@ in the filename. -s SENS_FILE, --sens_file SENS_FILE Configuration file with sensitivity function parameters - -f FLATFILE, --flatfile FLATFILE - Use a flat calibration file to compute the blaze - function when generating the sensitivity function. This - is helpful to account for small scale undulations in the - sensitivity function. Note that it is not possible to - set --flatfile and simultaneously use a .sens file with - the --sens_file option. If you are using a .sens file, - set the flatfile there via e.g.: + -f, --use_flat Use the extracted spectrum of the flatfield calibration + to estimate the blaze function when generating the + sensitivity function. This is helpful to account for + small scale undulations in the sensitivity function. The + spec1dfile must contain the extracted flatfield response + in order to use this option. This spectrum is extracted + by default, unless you did not compute a pixelflat + frame. Note that it is not possible to set --use_flat + and simultaneously use a .sens file with the --sens_file + option. If you are using a .sens file, set the use_flat + flag with the argument: [sensfunc] - flatfile = Calibrations/Flat_A_0_DET01.fits - - Where Flat_A_0_DET01.fits is the flat file in your - Calibrations directory + use_flat = True --debug show debug plots? --par_outfile PAR_OUTFILE Name of output file to save the parameters used by the diff --git a/doc/include/class_datamodel_specobj.rst b/doc/include/class_datamodel_specobj.rst index 11492ddf32..9c6afca3d2 100644 --- a/doc/include/class_datamodel_specobj.rst +++ b/doc/include/class_datamodel_specobj.rst @@ -1,5 +1,5 @@ -**Version**: 1.1.10 +**Version**: 1.1.11 ======================= =================================================================================================== ===================== ==================================================================================================================================================================================== Attribute Type Array Type Description @@ -14,6 +14,7 @@ Attribute Type ``BOX_FLAM`` `numpy.ndarray`_ float Boxcar flux (erg/s/cm^2/Ang) ``BOX_FLAM_IVAR`` `numpy.ndarray`_ float Boxcar flux inverse variance (1e-17 erg/s/cm^2/Ang)^-2 ``BOX_FLAM_SIG`` `numpy.ndarray`_ float Boxcar flux uncertainty (1e-17 erg/s/cm^2/Ang) +``BOX_FLAT`` `numpy.ndarray`_ float Boxcar extracted flatfield spectrum, normalized to the peak value. ``BOX_FRAC_USE`` `numpy.ndarray`_ float Fraction of pixels in the object profile subimage used for this extraction ``BOX_FWHM`` `numpy.ndarray`_ float Spectral FWHM (in Angstroms) at every pixel of the boxcar extracted flux. ``BOX_MASK`` `numpy.ndarray`_ `numpy.bool`_ Mask for boxcar extracted flux. True=good @@ -51,6 +52,7 @@ Attribute Type ``OPT_FLAM`` `numpy.ndarray`_ float Optimal flux (1e-17 erg/s/cm^2/Ang) ``OPT_FLAM_IVAR`` `numpy.ndarray`_ float Optimal flux inverse variance (1e-17 erg/s/cm^2/Ang)^-2 ``OPT_FLAM_SIG`` `numpy.ndarray`_ float Optimal flux uncertainty (1e-17 erg/s/cm^2/Ang) +``OPT_FLAT`` `numpy.ndarray`_ float Optimally extracted flatfield spectrum, normalised to the peak value. ``OPT_FRAC_USE`` `numpy.ndarray`_ float Fraction of pixels in the object profile subimage used for this extraction ``OPT_FWHM`` `numpy.ndarray`_ float Spectral FWHM (in Angstroms) at every pixel of the optimally extracted flux. ``OPT_MASK`` `numpy.ndarray`_ `numpy.bool`_ Mask for optimally extracted flux. True=good diff --git a/doc/include/datamodel_specobj.rst b/doc/include/datamodel_specobj.rst index deb7cb2c19..7de4f07a1a 100644 --- a/doc/include/datamodel_specobj.rst +++ b/doc/include/datamodel_specobj.rst @@ -1,6 +1,6 @@ -Version: 1.1.10 +Version: 1.1.11 ======================= ========================= ================= ==================================================================================================================================================================================== Obj Key Obj Type Array Type Description @@ -15,6 +15,7 @@ Obj Key Obj Type Array Type Descripti ``BOX_FLAM`` ndarray float Boxcar flux (erg/s/cm^2/Ang) ``BOX_FLAM_IVAR`` ndarray float Boxcar flux inverse variance (1e-17 erg/s/cm^2/Ang)^-2 ``BOX_FLAM_SIG`` ndarray float Boxcar flux uncertainty (1e-17 erg/s/cm^2/Ang) +``BOX_FLAT`` ndarray float Boxcar extracted flatfield spectrum, normalized to the peak value. ``BOX_FRAC_USE`` ndarray float Fraction of pixels in the object profile subimage used for this extraction ``BOX_FWHM`` ndarray float Spectral FWHM (in Angstroms) at every pixel of the boxcar extracted flux. ``BOX_MASK`` ndarray bool Mask for boxcar extracted flux. True=good @@ -52,6 +53,7 @@ Obj Key Obj Type Array Type Descripti ``OPT_FLAM`` ndarray float Optimal flux (1e-17 erg/s/cm^2/Ang) ``OPT_FLAM_IVAR`` ndarray float Optimal flux inverse variance (1e-17 erg/s/cm^2/Ang)^-2 ``OPT_FLAM_SIG`` ndarray float Optimal flux uncertainty (1e-17 erg/s/cm^2/Ang) +``OPT_FLAT`` ndarray float Optimally extracted flatfield spectrum, normalised to the peak value. ``OPT_FRAC_USE`` ndarray float Fraction of pixels in the object profile subimage used for this extraction ``OPT_FWHM`` ndarray float Spectral FWHM (in Angstroms) at every pixel of the optimally extracted flux. ``OPT_MASK`` ndarray bool Mask for optimally extracted flux. True=good diff --git a/doc/include/spectrographs_table.rst b/doc/include/spectrographs_table.rst index 99bef11a21..2b877fac53 100644 --- a/doc/include/spectrographs_table.rst +++ b/doc/include/spectrographs_table.rst @@ -20,7 +20,7 @@ keck_deimos :class:`~pypeit.spectrographs.keck_deimos.KeckDEIMOSSp keck_esi :class:`~pypeit.spectrographs.keck_esi.KeckESISpectrograph` KECK ESI Echelle True False keck_hires :class:`~pypeit.spectrographs.keck_hires.KECKHIRESSpectrograph` KECK HIRES `Link `__ Echelle False False Post detector upgrade (~ August 2004). See :doc:`keck_hires` keck_kcrm :class:`~pypeit.spectrographs.keck_kcwi.KeckKCRMSpectrograph` KECK KCRM `Link `__ SlicerIFU True False Supported setups: RL, RM1, RM2, RH3; see :doc:`keck_kcwi` -keck_kcwi :class:`~pypeit.spectrographs.keck_kcwi.KeckKCWISpectrograph` KECK KCWI `Link `__ SlicerIFU True False Supported setups: BL, BM, BH2, BH3; see :doc:`keck_kcwi` +keck_kcwi :class:`~pypeit.spectrographs.keck_kcwi.KeckKCWISpectrograph` KECK KCWI `Link `__ SlicerIFU True False Supported setups: BL, BM, BH2, BH3; see :doc:`keck_kcwi` keck_lris_blue :class:`~pypeit.spectrographs.keck_lris.KeckLRISBSpectrograph` KECK LRISb `Link `__ MultiSlit True False Blue camera; Current FITS file format; used from May 2009, see :doc:`lris` keck_lris_blue_orig :class:`~pypeit.spectrographs.keck_lris.KeckLRISBOrigSpectrograph` KECK LRISb `Link `__ MultiSlit True False Blue camera; Original FITS file format; used until April 2009; see :doc:`lris` keck_lris_red :class:`~pypeit.spectrographs.keck_lris.KeckLRISRSpectrograph` KECK LRISr `Link `__ MultiSlit True True Red camera; Current FITS file format; LBNL detector, 2kx4k; used from May 2009, see :doc:`lris` diff --git a/doc/pypeit_par.rst b/doc/pypeit_par.rst index 7bbb47919a..4ba6149515 100644 --- a/doc/pypeit_par.rst +++ b/doc/pypeit_par.rst @@ -950,7 +950,6 @@ Key Type Options ``extr`` str .. ``OPT`` Extraction method to use for the sensitivity function. Options are: 'OPT' (optimal extraction), 'BOX' (boxcar extraction). Default is 'OPT'. ``extrap_blu`` float .. 0.1 Fraction of minimum wavelength coverage to grow the wavelength coverage of the sensitivitity function in the blue direction (`i.e.`, if the standard star spectrum cuts off at ``wave_min``) the sensfunc will be extrapolated to cover down to (1.0 - ``extrap_blu``) * ``wave_min`` ``extrap_red`` float .. 0.1 Fraction of maximum wavelength coverage to grow the wavelength coverage of the sensitivitity function in the red direction (`i.e.`, if the standard star spectrumcuts off at ``wave_max``) the sensfunc will be extrapolated to cover up to (1.0 + ``extrap_red``) * ``wave_max`` -``flatfile`` str .. .. Flat field file to be used if the sensitivity function model will utilize the blaze function computed from a flat field file in the Calibrations directory, e.g.Calibrations/Flat_A_0_DET01.fits ``hydrogen_mask_wid`` float .. 10.0 Mask width from line center for hydrogen recombination lines in Angstroms (total mask width is 2x this value). ``mask_helium_lines`` bool .. False Mask certain ``HeII`` recombination lines prominent in O-type stars in the sensitivity function fit A region equal to 0.5 * ``hydrogen_mask_wid`` on either side of the line center is masked. ``mask_hydrogen_lines`` bool .. True Mask hydrogen Balmer, Paschen, Brackett, and Pfund recombination lines in the sensitivity function fit. A region equal to ``hydrogen_mask_wid`` on either side of the line center is masked. @@ -961,6 +960,7 @@ Key Type Options ``star_mag`` float .. .. Magnitude of the standard star (for near-IR mainly) ``star_ra`` float .. .. RA of the standard star. This will override values in the header (`i.e.`, if they are wrong or absent) ``star_type`` str .. .. Spectral type of the standard star (for near-IR mainly) +``use_flat`` bool .. False If True, the flatfield spectrum will be used when computing the sensitivity function. ======================= ============================================== ================ =========================== ============================================================================================================================================================================================================================================================================================================================================================================================ diff --git a/pypeit/coadd2d.py b/pypeit/coadd2d.py index 02b4697eaf..7864a2e63f 100644 --- a/pypeit/coadd2d.py +++ b/pypeit/coadd2d.py @@ -1193,7 +1193,9 @@ def compute_weights(self, weights): @staticmethod def unpack_specobj(spec, spatord_id=None): """ - Utility routine to unpack flux, ivar, and gpm from a single SpecObj object. + Utility routine to unpack flux, ivar, and gpm from a single SpecObj + object. + Args: spec (:class:`~pypeit.specobj.SpecObj`): SpecObj object to unpack. diff --git a/pypeit/core/skysub.py b/pypeit/core/skysub.py index e1a44e53ca..7957785ef0 100644 --- a/pypeit/core/skysub.py +++ b/pypeit/core/skysub.py @@ -1067,7 +1067,7 @@ def ech_local_skysub_extract(sciimg, sciivar, fullmask, tilts, waveimg, model_noise=True, debug_bkpts=False, show_profile=False, show_resids=False, show_fwhm=False, adderr=0.01, base_var=None, count_scale=None, no_local_sky:bool=False): - """ + r""" Perform local sky subtraction, profile fitting, and optimal extraction slit by slit. Objects are sky/subtracted extracted in order of the highest average (across all orders) S/N ratio object first, and then for a given From 05065cab1268072b98956340d193c3819ef140db Mon Sep 17 00:00:00 2001 From: Debora Pelliccia Date: Fri, 20 Sep 2024 09:31:16 -1000 Subject: [PATCH 12/13] small cleanup --- pypeit/core/coadd.py | 5 ----- pypeit/scripts/ql.py | 2 -- 2 files changed, 7 deletions(-) diff --git a/pypeit/core/coadd.py b/pypeit/core/coadd.py index a608b16069..ed65f12bcf 100644 --- a/pypeit/core/coadd.py +++ b/pypeit/core/coadd.py @@ -1867,13 +1867,8 @@ def spec_reject_comb(wave_grid, wave_grid_mid, waves_list, fluxes_list, ivars_li fluxes, ivars, this_gpms, flux_stack_nat, ivar_stack_nat, gpm_stack_nat, sn_clip=sn_clip) this_gpms, qdone = pydl.djs_reject(fluxes, flux_stack_nat, outmask=this_gpms, inmask=gpms, invvar=rejivars, lower=lower,upper=upper, maxrej=maxrej, sticky=False) - #rejivars, sigma_corrs, outchi, chigpm = update_errors( - # fluxes, ivars, this_gpms, flux_stack_nat, ivar_stack_nat, gpm_stack_nat, sn_clip=sn_clip) - #this_gpms, qdone = pydl.djs_reject(fluxes, flux_stack_nat, outmask=this_gpms, inmask=gpms, invvar=rejivars, - # lower=lower,upper=upper, maxrej=maxrej, sticky=False) iter += 1 - if (iter == maxiter_reject) & (maxiter_reject != 0): msgs.warn('Maximum number of iterations maxiter={:}'.format(maxiter_reject) + ' reached in spec_reject_comb') out_gpms = np.copy(this_gpms) diff --git a/pypeit/scripts/ql.py b/pypeit/scripts/ql.py index 5403685788..f3c27e95f5 100644 --- a/pypeit/scripts/ql.py +++ b/pypeit/scripts/ql.py @@ -1011,8 +1011,6 @@ def main(args): # Run the coadding coadd2dFile = inputfiles.Coadd2DFile.from_file(coadd_file) CoAdd2DSpec.main(CoAdd2DSpec.parse_args([str(coadd_file)])) - # '--spec_samp_fact', str(args.spec_samp_fact), - # '--spat_samp_fact', str(args.spat_samp_fact)])) # Get the output file name spectrograph, par, _ = coadd2dFile.get_pypeitpar() From 0c2795044b612c0f7dde52671fba6e68f4b6bd13 Mon Sep 17 00:00:00 2001 From: Debora Pelliccia Date: Fri, 20 Sep 2024 09:47:34 -1000 Subject: [PATCH 13/13] added to realease doc --- doc/releases/1.16.1dev.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/releases/1.16.1dev.rst b/doc/releases/1.16.1dev.rst index dfda8303cb..9efeb54374 100644 --- a/doc/releases/1.16.1dev.rst +++ b/doc/releases/1.16.1dev.rst @@ -89,6 +89,14 @@ Script Changes the reduced data. - Treatment of file names is now more formal. Compression signatures are now considered, and filename matching is now more strict. +- Removed `--spec_samp_fact` and `--spat_samp_fact` command line options from + `pypeit_coadd_2d`. These options are now parameters in `Coadd2dPar`. +- `pypeit_show_2dspec` now shows the first available detector in the 2D spectrum + by default. The user can specify the detector to show with the `--det` option. +- Added `--removetrace` command line option to `pypeit_ql` to not show the object + trace when displaying the 2D spectrum. +- Change the default value for `--skip_display` in `pypeit_ql` to `True`. + Datamodel Changes ----------------- @@ -122,6 +130,8 @@ Bug Fixes - Fixed an hidden bug that was causing the spatial flexure to fail. The bug was in the `SlitTraceBitMask` class, where the function `exclude_for_flexure()` was not returning the 'BOXSLIT' flag. +- Fix a bug in `pypeit_coadd_2d` related to how the binning was taken into account + in the mask definition, and in the calculation of the offset between frames.