"""
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 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