Source code for pypeit.scripts.trace_edges

"""
Trace slit edges for a set of images.

.. include common links, assuming primary doc root is up one directory
.. include:: ../include/links.rst
"""

from pypeit.scripts import scriptbase


[docs] class TraceEdges(scriptbase.ScriptBase):
[docs] @classmethod def get_parser(cls, width=None): from pypeit.spectrographs.util import available_spectrographs parser = super().get_parser(description='Trace slit edges', width=width, default_log_file=True) # Require either a pypeit file or a fits file inp = parser.add_mutually_exclusive_group(required=True) inp.add_argument('-f', '--pypeit_file', default=None, type=str, help='PypeIt reduction file') inp.add_argument('-t', '--trace_file', default=None, type=str, help='Image to trace') parser.add_argument('-g', '--group', default=None, help='If providing a pypeit file, use the trace images for this ' 'calibration group. If None, use the first calibration group.') parser.add_argument('-d', '--detector', type=str, default=None, nargs='*', help='Detector(s) to process. If more than one, the list of detectors ' 'must be one of the allowed mosaics hard-coded for the selected ' 'spectrograph. Using "mosaic" for gemini_gmos, keck_deimos, or ' 'keck_lris will use the default mosaic.') parser.add_argument('-s', '--spectrograph', default=None, type=str, help='A valid spectrograph identifier, which is only used if providing' ' files directly: {0}'.format(', '.join(available_spectrographs))) parser.add_argument('-b', '--binning', default=None, type=str, help='Image binning in spectral and spatial directions. Only used if ' 'providing files directly; default is 1,1.') parser.add_argument('-p', '--redux_path', type=str, default='current working directory', help='Path to top-level output directory.') parser.add_argument('-c', '--calib_dir', default='Calibrations', help='Name for directory in output path for calibration file(s) ' 'relative to the top-level directory.') parser.add_argument('-o', '--overwrite', default=False, action='store_true', help='Overwrite any existing files/directories') parser.add_argument('--debug', type=int, default=0, help='Debug level. (1) Show the result of each stage of the tracing ' 'algorithm (previously the --show option). (2) Also show summary ' 'plots related to the PCA decomposition and the slit and order ' 'matching. (3) Also show the individual polynomial fits to the ' 'detected edges.') return parser
[docs] @classmethod def main(cls, args): import time from pathlib import Path from IPython import embed import numpy as np from pypeit import edgetrace from pypeit import log from pypeit import PypeItError from pypeit.core import parse from pypeit.images import buildimage from pypeit.pypeit import PypeIt from pypeit.spectrographs.util import load_spectrograph # Initialize the log cls.init_log(args) if args.pypeit_file is not None: pypeit_file = Path(args.pypeit_file).absolute() if not pypeit_file.exists(): raise PypeItError(f'File does not exist: {pypeit_file}') redux_path = pypeit_file.parent if args.redux_path is None \ else Path(args.redux_path).absolute() rdx = PypeIt(str(pypeit_file), redux_path=str(redux_path)) detectors = rdx.par['rdx']['detnum'] if args.detector is None else args.detector # Save the spectrograph spec = rdx.spectrograph # Get the calibration group to use group = np.unique(rdx.fitstbl['calib'])[0] if args.group is None else args.group if group not in np.unique(rdx.fitstbl['calib']): raise PypeItError(f'Invalid calibration group: {group}') # Find the rows in the metadata table with trace frames in the # specified calibration group tbl_rows = rdx.fitstbl.find_frames('trace', calib_ID=int(group), index=True) setup = rdx.fitstbl['setup'][tbl_rows[0]] calib_id = rdx.fitstbl['calib'][tbl_rows[0]] # Save the binning binning = rdx.fitstbl['binning'][tbl_rows[0]] # Save the full file paths files = rdx.fitstbl.frame_paths(tbl_rows) # Trace image processing parameters proc_par = rdx.par['calibrations']['traceframe'] # Slit tracing parameters trace_par = rdx.par['calibrations']['slitedges'] # Use the bias frames to set the BPM bpm_usebias = rdx.par['calibrations']['bpm_usebias'] # Get the bias files, if requested bias_files = rdx.fitstbl.find_frame_files('bias', calib_ID=int(group)) bias_par = rdx.par['calibrations']['biasframe'] if len(bias_files) == 0: bias_files = None # Get the dark files, if requested dark_files = rdx.fitstbl.find_frame_files('dark', calib_ID=int(group)) dark_par = rdx.par['calibrations']['darkframe'] if len(dark_files) == 0: dark_files = None # Lamp-off files lampoff_files = rdx.fitstbl.find_frame_files('lampoffflats', calib_ID=int(group)) lampoff_par = rdx.par['calibrations']['lampoffflatsframe'] if len(lampoff_files) == 0: lampoff_files = None # Set the QA path qa_path = rdx.qa_path else: detectors = args.detector spec = load_spectrograph(args.spectrograph) setup = 'A' # Dummy value calib_id = '1' # Dummy value binning = '1,1' if args.binning is None else args.binning trace_file = Path(args.trace_file).absolute() if not trace_file.exists(): raise PypeItError(f'File does not exist: {trace_file}') files = [str(trace_file)] redux_path = trace_file.parent if args.redux_path is None \ else Path(args.redux_path).absolute() par = spec.default_pypeit_par() proc_par = par['calibrations']['traceframe'] trace_par = par['calibrations']['slitedges'] bpm_usebias = par['calibrations']['bpm_usebias'] bias_files = None bias_par = None dark_files = None dark_par = None lampoff_files = None lampoff_par = None # Set the QA path qa_path = redux_path / 'QA' if detectors is None: detectors = np.arange(spec.ndet)+1 elif isinstance(detectors, (int, tuple)): detectors = [detectors] elif any([isinstance(d,str) for d in detectors]): detectors = [parse.eval_detectors(d) for d in detectors] calib_dir = redux_path / args.calib_dir for det in detectors: # Get the bias frame if bias_files is None: proc_par['process']['use_biasimage'] = False msbias = None else: msbias = buildimage.buildimage_fromlist(spec, det, bias_par, bias_files) # Get the bad-pixel mask msbpm = spec.bpm(files[0], det, msbias=msbias if bpm_usebias else None) # Save a copy original_bpm = msbpm.copy() # Get the dark frame if dark_files is None: proc_par['process']['use_darkimage'] = False msdark = None else: msdark = buildimage.buildimage_fromlist(spec, det, dark_par, dark_files, bias=msbias) # Build the trace image msbpm = original_bpm.copy() # Reset bpm traceImage = buildimage.buildimage_fromlist(spec, det, proc_par, files, bias=msbias, bpm=msbpm, dark=msdark, setup=setup, calib_id=calib_id, calib_dir=calib_dir) # Subtract the lamp-off flats, if they exist if lampoff_files is not None: msbpm = original_bpm.copy() # Reset bpm lampoff_flat = buildimage.buildimage_fromlist(spec, det, lampoff_par, lampoff_files, dark=msdark, bias=msbias, bpm=msbpm) traceImage = traceImage.sub(lampoff_flat) # Trace the slit edges t = time.perf_counter() edges = edgetrace.EdgeTraceSet(traceImage, spec, trace_par, auto=True, debug=args.debug, qa_path=qa_path) if not edges.success: log.warning(f'Edge tracing for detector {det} failed. Continuing...') continue log.info(f'Tracing for detector {det} finished in { time.perf_counter()-t:.1f} s.') # Write the two calibration frames edges.to_file() edges.get_slits().to_file() return 0