コード例 #1
0
def test_select_detectors():
    # Generate a PYPIT file
    pypit_file = data_path('test.pypeit')
    make_pypeit_file(pypit_file,
                     'shane_kast_blue', [data_path('b*fits.gz')],
                     setup_mode=True)

    # Perform the setup
    setup = pypeitsetup.PypeItSetup.from_pypeit_file(pypit_file)
    par, spectrograph, fitstbl = setup.run(sort_dir=data_path(''))

    assert PypeIt.select_detectors(detnum=par['rdx']['detnum'], ndet=spectrograph.ndet) == [1], \
            'Incorrect detectors selected.'

    # Clean-up
    os.remove(data_path('test.calib'))
    os.remove(data_path('test.pypeit'))

    assert np.array_equal(PypeIt.select_detectors(),
                          [1]), 'Incorrect detectors selected.'
    assert np.array_equal(PypeIt.select_detectors(detnum=3, ndet=5), [3]), \
            'Incorrect detectors selected.'
    assert np.array_equal(PypeIt.select_detectors(ndet=5), [1,2,3,4,5]), \
            'Incorrect detectors selected.'
    assert np.array_equal(PypeIt.select_detectors(detnum=[1,3]), [1,3]), \
            'Incorrect detectors selected.'
コード例 #2
0
def main(args):
    """ Executes 2d coadding
    """

    # Load the file
    if args.file is not None:
        spectrograph, config_lines, spec2d_files = read_coadd2d_file(args.file)
        # Parameters
        # TODO: Shouldn't this reinstantiate the same parameters used in
        # the PypeIt run that extracted the objects?  Why are we not
        # just passing the pypeit file?
        spectrograph_def_par = spectrograph.default_pypeit_par()
        par = par.PypeItPar.from_cfg_lines(
            cfg_lines=spectrograph_def_par.to_config(),
            merge_with=config_lines)
    elif args.obj is not None:
        # TODO: This needs to define the science path
        spec2d_files = glob.glob('./Science/spec2d_*' + args.obj + '*')
        head0 = fits.getheader(spec2d_files[0])
        spectrograph_name = head0['SPECTROG']
        spectrograph = load_spectrograph(spectrograph_name)
        par = spectrograph.default_pypeit_par()
    else:
        msgs.error(
            'You must either input a coadd2d file with --file or an object name with --obj'
        )

    # If detector was passed as an argument override whatever was in the coadd2d_file
    if args.det is not None:
        msgs.info("Restricting reductions to detector={}".format(args.det))
        par['rdx']['detnum'] = int(args.det)

    # Get headers and base names
    spec1d_files = [
        files.replace('spec2d', 'spec1d') for files in spec2d_files
    ]
    head1d = fits.getheader(spec1d_files[0])
    head2d = fits.getheader(spec2d_files[0])
    if args.basename is None:
        filename = os.path.basename(spec2d_files[0])
        basename = filename.split('_')[1]
    else:
        basename = args.basename

    # Write the par to disk
    par_outfile = basename + '_coadd2d.par'
    print("Writing the parameters to {}".format(par_outfile))
    par.to_config(par_outfile)

    # Now run the coadds

    skysub_mode = head2d['SKYSUB']
    ir_redux = True if 'DIFF' in skysub_mode else False

    # Print status message
    msgs_string = 'Reducing target {:s}'.format(basename) + msgs.newline()
    msgs_string += 'Performing coadd of frames reduce with {:s} imaging'.format(
        skysub_mode)
    msgs_string += msgs.newline(
    ) + 'Combining frames in 2d coadd:' + msgs.newline()
    for file in spec2d_files:
        msgs_string += '{0:s}'.format(os.path.basename(file)) + msgs.newline()
    msgs.info(msgs_string)

    # TODO: This needs to be added to the parameter list for rdx
    redux_path = os.getcwd()
    master_dirname = os.path.basename(head2d['PYPMFDIR']) + '_coadd'
    master_dir = os.path.join(redux_path, master_dirname)

    # Make the new Master dir
    if not os.path.isdir(master_dir):
        msgs.info(
            'Creating directory for Master output: {0}'.format(master_dir))
        os.makedirs(master_dir)

    # Instantiate the sci_dict
    sci_dict = OrderedDict()  # This needs to be ordered
    sci_dict['meta'] = {}
    sci_dict['meta']['vel_corr'] = 0.
    sci_dict['meta']['ir_redux'] = ir_redux

    # Find the detectors to reduce
    detectors = PypeIt.select_detectors(detnum=par['rdx']['detnum'],
                                        ndet=spectrograph.ndet)
    if len(detectors) != spectrograph.ndet:
        msgs.warn('Not reducing detectors: {0}'.format(' '.join([
            str(d) for d in set(np.arange(spectrograph.ndet)) - set(detectors)
        ])))

    # Loop on detectors
    for det in detectors:
        msgs.info("Working on detector {0}".format(det))
        sci_dict[det] = {}

        # Read in the images stacks and other clibration/meta data for this detector
        stack_dict = coadd2d.load_coadd2d_stacks(spec2d_files, det)

        sci_dict[det]['sciimg'], sci_dict[det]['sciivar'], sci_dict[det]['skymodel'], \
                sci_dict[det]['objmodel'], sci_dict[det]['ivarmodel'], sci_dict[det]['outmask'], \
                sci_dict[det]['specobjs'] \
                        = coadd2d.extract_coadd2d(stack_dict, master_dir, det, ir_redux=ir_redux,
                                                  par=par, show=args.show, show_peaks=args.peaks,
                                                  std=args.std, samp_fact=args.samp_fact)

    # Make the new Science dir
    # TODO: This needs to be defined by the user
    scipath = os.path.join(redux_path, 'Science_coadd')
    if not os.path.isdir(scipath):
        msgs.info('Creating directory for Science output: {0}'.format(scipath))
        os.makedirs(scipath)

    # Save the results
    save.save_all(sci_dict, stack_dict['master_key_dict'], master_dir,
                  spectrograph, head1d, head2d, scipath, basename)
コード例 #3
0
ファイル: trace_edges.py プロジェクト: joshwalawender/PypeIt
def main(args):

    import time
    import os
    import numpy as np
    from pypeit.spectrographs.util import load_spectrograph
    from pypeit import traceimage, edgetrace, biasframe
    from pypeit.pypeit import PypeIt
    from pypeit.core import parse

    from IPython import embed

    if args.pypeit_file is not None:
        pypeit_file = args.pypeit_file
        if not os.path.isfile(pypeit_file):
            raise FileNotFoundError(
                'File does not exist: {0}'.format(pypeit_file))
        pypeit_file = os.path.abspath(pypeit_file)
        redux_path = os.path.abspath(
            os.path.split(pypeit_file)[0] if args.redux_path is None else args.
            redux_path)

        rdx = PypeIt(pypeit_file, redux_path=redux_path)
        # 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 ValueError(
                'Not a valid calibration group: {0}'.format(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)
        # Master keyword
        master_key_base = '_'.join(
            rdx.fitstbl.master_key(tbl_rows[0]).split('_')[:2])
        # 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.caliBrate.par['traceframe']
        # Slit tracing parameters
        trace_par = rdx.caliBrate.par['slitedges']

        # Get the bias files, if requested
        bias_rows = rdx.fitstbl.find_frames('bias',
                                            calib_ID=int(group),
                                            index=True)
        bias_files = rdx.fitstbl.frame_paths(bias_rows)
        bias_par = rdx.caliBrate.par['biasframe']

        # Set the QA path
        qa_path = rdx.qa_path
    else:
        spec = load_spectrograph(args.spectrograph)
        master_key_base = 'A_1'
        binning = '1,1' if args.binning is None else args.binning
        if not os.path.isfile(args.trace_file):
            raise FileNotFoundError('File does not exist: {0}'.format(
                args.trace_file))
        files = [os.path.abspath(args.trace_file)]
        redux_path = os.path.abspath(
            os.path.split(files[0])[0] if args.redux_path is None else args.
            redux_path)
        par = spec.default_pypeit_par()
        proc_par = par['calibrations']['traceframe']
        trace_par = par['calibrations']['slitedges']
        bias_files = None
        bias_par = None

        # Set the QA path
        qa_path = os.path.join(os.path.abspath(os.path.split(files[0])[0]),
                               'QA')

    detectors = np.arange(spec.ndet) + 1 if args.detector is None else [
        args.detector
    ]
    master_dir = os.path.join(redux_path, args.master_dir)
    for det in detectors:
        # Master keyword for output file name
        master_key = '{0}_{1}'.format(master_key_base, str(det).zfill(2))

        # Get the bias frame if requested
        if bias_files is None:
            proc_par['process']['bias'] = 'skip'
            msbias = None
        else:
            biasFrame = biasframe.BiasFrame(spec,
                                            files=bias_files,
                                            det=det,
                                            par=bias_par,
                                            master_key=master_key,
                                            master_dir=master_dir)
            msbias = biasFrame.build_image()

        msbpm = spec.bpm(files[0], det)

        # Build the trace image
        traceImage = traceimage.TraceImage(spec,
                                           files=files,
                                           det=det,
                                           par=proc_par,
                                           bias=msbias)
        traceImage.build_image(bias=msbias, bpm=msbpm)

        # Trace the slit edges
        t = time.perf_counter()
        edges = edgetrace.EdgeTraceSet(spec,
                                       trace_par,
                                       master_key=master_key,
                                       master_dir=master_dir,
                                       img=traceImage,
                                       det=det,
                                       bpm=msbpm,
                                       auto=True,
                                       debug=args.debug,
                                       show_stages=args.show,
                                       qa_path=qa_path)
        print('Tracing for detector {0} finished in {1} s.'.format(
            det,
            time.perf_counter() - t))
        edges.save()

    return 0
コード例 #4
0
ファイル: coadd_2dspec.py プロジェクト: YoemanLeung/PypeIt
def main(args):
    """ Executes 2d coadding
    """
    msgs.warn('PATH =' + os.getcwd())
    # Load the file
    if args.file is not None:
        spectrograph, config_lines, spec2d_files = read_coadd2d_file(args.file)
        # Parameters
        # TODO: Shouldn't this reinstantiate the same parameters used in
        # the PypeIt run that extracted the objects?  Why are we not
        # just passing the pypeit file?
        # JFH: The reason is that the coadd2dfile may want different reduction parameters
        spectrograph_def_par = spectrograph.default_pypeit_par()
        parset = par.PypeItPar.from_cfg_lines(cfg_lines=spectrograph_def_par.to_config(),
                                                 merge_with=config_lines)
    elif args.obj is not None:
        # TODO: We should probably be reading the pypeit file and using those parameters here rather than using the
        # default parset.
        # TODO: This needs to define the science path
        spec2d_files = glob.glob('./Science/spec2d_*' + args.obj + '*')
        head0 = fits.getheader(spec2d_files[0])
        spectrograph_name = head0['SPECTROG']
        spectrograph = load_spectrograph(spectrograph_name)
        parset = spectrograph.default_pypeit_par()
    else:
        msgs.error('You must either input a coadd2d file with --file or an object name with --obj')

    # Update with configuration specific parameters (which requires science file) and initialize spectrograph
    spectrograph_cfg_lines = spectrograph.config_specific_par(spec2d_files[0]).to_config()
    parset = par.PypeItPar.from_cfg_lines(cfg_lines=spectrograph_cfg_lines, merge_with=parset.to_config())

    # If detector was passed as an argument override whatever was in the coadd2d_file
    if args.det is not None:
        msgs.info("Restricting reductions to detector={}".format(args.det))
        parset['rdx']['detnum'] = int(args.det)

    # Get headers (if possible) and base names
    spec1d_files = [files.replace('spec2d', 'spec1d') for files in spec2d_files]
    head1d = None
    for spec1d_file in spec1d_files:
        if os.path.isfile(spec1d_file):
            head1d = fits.getheader(spec1d_file)
            break
    if head1d is None:
        msgs.warn("No 1D spectra so am generating a dummy header for output")
        head1d = io.initialize_header()

    head2d = fits.getheader(spec2d_files[0])
    if args.basename is None:
        filename = os.path.basename(spec2d_files[0])
        basename = filename.split('_')[2]
    else:
        basename = args.basename

    # Write the par to disk
    par_outfile = basename+'_coadd2d.par'
    print("Writing the parameters to {}".format(par_outfile))
    parset.to_config(par_outfile)

    # Now run the coadds

    skysub_mode = head2d['SKYSUB']
    ir_redux = True if 'DIFF' in skysub_mode else False

    # Print status message
    msgs_string = 'Reducing target {:s}'.format(basename) + msgs.newline()
    msgs_string += 'Performing coadd of frames reduce with {:s} imaging'.format(skysub_mode)
    msgs_string += msgs.newline() + 'Combining frames in 2d coadd:' + msgs.newline()
    for file in spec2d_files:
        msgs_string += '{0:s}'.format(os.path.basename(file)) + msgs.newline()
    msgs.info(msgs_string)

    # TODO: This needs to be added to the parameter list for rdx
    redux_path = os.getcwd()
    master_dirname = os.path.basename(head2d['PYPMFDIR']) + '_coadd'
    master_dir = os.path.join(redux_path, master_dirname)

    # Make the new Master dir
    if not os.path.isdir(master_dir):
        msgs.info('Creating directory for Master output: {0}'.format(master_dir))
        os.makedirs(master_dir)

    # Instantiate the sci_dict
    sci_dict = OrderedDict()  # This needs to be ordered
    sci_dict['meta'] = {}
    sci_dict['meta']['vel_corr'] = 0.
    sci_dict['meta']['ir_redux'] = ir_redux

    # Find the detectors to reduce
    detectors = PypeIt.select_detectors(detnum=parset['rdx']['detnum'], ndet=spectrograph.ndet)
    if len(detectors) != spectrograph.ndet:
        msgs.warn('Not reducing detectors: {0}'.format(' '.join([str(d) for d in
        set(np.arange(spectrograph.ndet) + 1) - set(detectors)])))

    # Loop on detectors
    for det in detectors:
        msgs.info("Working on detector {0}".format(det))
        sci_dict[det] = {}

        # Instantiate Coadd2d
        coadd = coadd2d.CoAdd2D.get_instance(spec2d_files, spectrograph, parset, det=det,
                                             offsets=parset['coadd2d']['offsets'],
                                             weights=parset['coadd2d']['weights'],
                                             ir_redux=ir_redux,
                                             debug_offsets=args.debug_offsets, debug=args.debug,
                                             samp_fact=args.samp_fact)

        # Coadd the slits
        coadd_dict_list = coadd.coadd(only_slits=None) # TODO implement only_slits later
        # Create the psuedo images
        psuedo_dict = coadd.create_psuedo_image(coadd_dict_list)
        # Reduce
        msgs.info('Running the extraction')
        sci_dict[det]['sciimg'], sci_dict[det]['sciivar'], sci_dict[det]['skymodel'], sci_dict[det]['objmodel'], \
        sci_dict[det]['ivarmodel'], sci_dict[det]['outmask'], sci_dict[det]['specobjs'] = coadd.reduce(
            psuedo_dict, show = args.show, show_peaks = args.peaks)
        # Save psuedo image master files
        coadd.save_masters(master_dir)

    # Make the new Science dir
    # TODO: This needs to be defined by the user
    scipath = os.path.join(redux_path, 'Science_coadd')
    if not os.path.isdir(scipath):
        msgs.info('Creating directory for Science output: {0}'.format(scipath))
        os.makedirs(scipath)

    # Save the results
    save.save_all(sci_dict, coadd.stack_dict['master_key_dict'], master_dir, spectrograph, head1d,
                  head2d, scipath, basename)#, binning=coadd.binning)
コード例 #5
0
ファイル: coadd_2dspec.py プロジェクト: ninoc/PypeIt
def main(args):
    """ Executes 2d coadding
    """
    msgs.warn('PATH =' + os.getcwd())
    # Load the file
    if args.file is not None:
        spectrograph_name, config_lines, spec2d_files = io.read_spec2d_file(
            args.file, filetype="coadd2d")
        spectrograph = load_spectrograph(spectrograph_name)

        # Parameters
        # TODO: Shouldn't this reinstantiate the same parameters used in
        # the PypeIt run that extracted the objects?  Why are we not
        # just passing the pypeit file?
        # JFH: The reason is that the coadd2dfile may want different reduction parameters
        spectrograph_def_par = spectrograph.default_pypeit_par()
        parset = par.PypeItPar.from_cfg_lines(
            cfg_lines=spectrograph_def_par.to_config(),
            merge_with=config_lines)
    elif args.obj is not None:
        # TODO: We should probably be reading the pypeit file and using those parameters here rather than using the
        # default parset.
        # TODO: This needs to define the science path
        spec2d_files = glob.glob('./Science/spec2d_*' + args.obj + '*')
        head0 = fits.getheader(spec2d_files[0])
        spectrograph_name = head0['PYP_SPEC']
        spectrograph = load_spectrograph(spectrograph_name)
        parset = spectrograph.default_pypeit_par()
    else:
        msgs.error(
            'You must either input a coadd2d file with --file or an object name with --obj'
        )

    # Update with configuration specific parameters (which requires science file) and initialize spectrograph
    spectrograph_cfg_lines = spectrograph.config_specific_par(
        spec2d_files[0]).to_config()
    parset = par.PypeItPar.from_cfg_lines(cfg_lines=spectrograph_cfg_lines,
                                          merge_with=parset.to_config())

    # If detector was passed as an argument override whatever was in the coadd2d_file
    if args.det is not None:
        msgs.info("Restricting reductions to detector={}".format(args.det))
        parset['rdx']['detnum'] = int(args.det)

    # Get headers (if possible) and base names
    spec1d_files = [
        files.replace('spec2d', 'spec1d') for files in spec2d_files
    ]
    head1d = None
    for spec1d_file in spec1d_files:
        if os.path.isfile(spec1d_file):
            head1d = fits.getheader(spec1d_file)
            break
    if head1d is None:
        msgs.warn("No 1D spectra so am generating a dummy header for output")
        head1d = io.initialize_header()

    head2d = fits.getheader(spec2d_files[0])
    if args.basename is None:
        filename = os.path.basename(spec2d_files[0])
        basename = filename.split('_')[2]
    else:
        basename = args.basename

    # Write the par to disk
    par_outfile = basename + '_coadd2d.par'
    print("Writing the parameters to {}".format(par_outfile))
    parset.to_config(par_outfile)

    # Now run the coadds

    skysub_mode = head2d['SKYSUB']
    ir_redux = True if 'DIFF' in skysub_mode else False

    # Print status message
    msgs_string = 'Reducing target {:s}'.format(basename) + msgs.newline()
    msgs_string += 'Performing coadd of frames reduce with {:s} imaging'.format(
        skysub_mode)
    msgs_string += msgs.newline(
    ) + 'Combining frames in 2d coadd:' + msgs.newline()
    for file in spec2d_files:
        msgs_string += '{0:s}'.format(os.path.basename(file)) + msgs.newline()
    msgs.info(msgs_string)

    # TODO: This needs to be added to the parameter list for rdx
    redux_path = os.getcwd()
    master_dirname = os.path.basename(head2d['PYPMFDIR']) + '_coadd'
    master_dir = os.path.join(redux_path, master_dirname)

    # Make the new Master dir
    if not os.path.isdir(master_dir):
        msgs.info(
            'Creating directory for Master output: {0}'.format(master_dir))
        os.makedirs(master_dir)

    # Instantiate the sci_dict
    sci_dict = OrderedDict()  # This needs to be ordered
    sci_dict['meta'] = {}
    sci_dict['meta']['vel_corr'] = 0.
    sci_dict['meta']['ir_redux'] = ir_redux

    # Find the detectors to reduce
    detectors = PypeIt.select_detectors(detnum=parset['rdx']['detnum'],
                                        ndet=spectrograph.ndet)
    if len(detectors) != spectrograph.ndet:
        msgs.warn('Not reducing detectors: {0}'.format(' '.join([
            str(d)
            for d in set(np.arange(spectrograph.ndet) + 1) - set(detectors)
        ])))

    # Loop on detectors
    for det in detectors:
        msgs.info("Working on detector {0}".format(det))
        sci_dict[det] = {}

        # Instantiate Coadd2d
        coadd = coadd2d.CoAdd2D.get_instance(
            spec2d_files,
            spectrograph,
            parset,
            det=det,
            offsets=parset['coadd2d']['offsets'],
            weights=parset['coadd2d']['weights'],
            ir_redux=ir_redux,
            debug_offsets=args.debug_offsets,
            debug=args.debug,
            samp_fact=args.samp_fact)

        # Coadd the slits
        coadd_dict_list = coadd.coadd(
            only_slits=None)  # TODO implement only_slits later
        # Create the pseudo images
        pseudo_dict = coadd.create_pseudo_image(coadd_dict_list)
        # Reduce
        msgs.info('Running the extraction')
        # TODO -- This should mirror what is in pypeit.extract_one
        # TODO -- JFH :: This ought to return a Spec2DObj and SpecObjs which would be slurped into
        #  AllSpec2DObj and all_specobsj, as below.
        # TODO -- JFH -- Check that the slits we are using are correct
        sci_dict[det]['sciimg'], sci_dict[det]['sciivar'], sci_dict[det]['skymodel'], sci_dict[det]['objmodel'], \
        sci_dict[det]['ivarmodel'], sci_dict[det]['outmask'], sci_dict[det]['specobjs'], sci_dict[det]['detector'], \
            sci_dict[det]['slits'], sci_dict[det]['tilts'], sci_dict[det]['waveimg'] = coadd.reduce(
            pseudo_dict, show = args.show, show_peaks = args.peaks)

        # Save pseudo image master files
        #coadd.save_masters()

    # Make the new Science dir
    # TODO: This needs to be defined by the user
    scipath = os.path.join(redux_path, 'Science_coadd')
    if not os.path.isdir(scipath):
        msgs.info('Creating directory for Science output: {0}'.format(scipath))
        os.makedirs(scipath)

    # THE FOLLOWING MIMICS THE CODE IN pypeit.save_exposure()

    # TODO -- These lines should be above once reduce() passes back something sensible
    all_specobjs = specobjs.SpecObjs()
    for det in detectors:
        all_specobjs.add_sobj(sci_dict[det]['specobjs'])

    # Write
    outfile1d = os.path.join(scipath, 'spec1d_{:s}.fits'.format(basename))
    subheader = spectrograph.subheader_for_spec(head2d, head2d)
    all_specobjs.write_to_fits(subheader, outfile1d)

    # 2D spectra
    # TODO -- These lines should be above once reduce() passes back something sensible
    all_spec2d = spec2dobj.AllSpec2DObj()
    all_spec2d['meta']['ir_redux'] = ir_redux
    for det in detectors:
        all_spec2d[det] = spec2dobj.Spec2DObj(
            det=det,
            sciimg=sci_dict[det]['sciimg'],
            ivarraw=sci_dict[det]['sciivar'],
            skymodel=sci_dict[det]['skymodel'],
            objmodel=sci_dict[det]['objmodel'],
            ivarmodel=sci_dict[det]['ivarmodel'],
            scaleimg=np.array([1.0], dtype=np.float),
            bpmmask=sci_dict[det]['outmask'],
            detector=sci_dict[det]['detector'],
            slits=sci_dict[det]['slits'],
            waveimg=sci_dict[det]['waveimg'],
            tilts=sci_dict[det]['tilts'],
            sci_spat_flexure=None,
            sci_spec_flexure=None,
            vel_corr=None,
            vel_type=None)
    # Build header
    outfile2d = os.path.join(scipath, 'spec2d_{:s}.fits'.format(basename))
    pri_hdr = all_spec2d.build_primary_hdr(
        head2d,
        spectrograph,
        subheader=subheader,
        # TODO -- JFH :: Decide if we need any of these
        redux_path=None,
        master_key_dict=None,
        master_dir=None)
    # Write
    all_spec2d.write_to_fits(outfile2d, pri_hdr=pri_hdr)
コード例 #6
0
    def main(args):

        import time
        import os
        import numpy as np
        from pypeit.spectrographs.util import load_spectrograph
        from pypeit import edgetrace
        from pypeit import slittrace
        from pypeit.pypeit import PypeIt
        from pypeit.images import buildimage
        from pypeit import masterframe

        from IPython import embed

        if args.pypeit_file is not None:
            pypeit_file = args.pypeit_file
            if not os.path.isfile(pypeit_file):
                raise FileNotFoundError('File does not exist: {0}'.format(pypeit_file))
            pypeit_file = os.path.abspath(pypeit_file)
            redux_path = os.path.abspath(os.path.split(pypeit_file)[0]
                                         if args.redux_path is None else args.redux_path)

            rdx = PypeIt(pypeit_file, redux_path=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 ValueError('Not a valid calibration group: {0}'.format(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)
            # Master keyword
            master_key_base = '_'.join(rdx.fitstbl.master_key(tbl_rows[0]).split('_')[:2])
            # 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']

            # Get the bias files, if requested
            bias_rows = rdx.fitstbl.find_frames('bias', calib_ID=int(group), index=True)
            bias_files = rdx.fitstbl.frame_paths(bias_rows)
            bias_par = rdx.par['calibrations']['biasframe']
            if len(bias_files) == 0:
                bias_files = None

            # Get the dark files, if requested
            dark_rows = rdx.fitstbl.find_frames('dark', calib_ID=int(group), index=True)
            dark_files = rdx.fitstbl.frame_paths(dark_rows)
            dark_par = rdx.par['calibrations']['darkframe']
            if len(dark_files) == 0:
                dark_files = None

            # Set the QA path
            qa_path = rdx.qa_path
        else:
            detectors = args.detector
            spec = load_spectrograph(args.spectrograph)
            master_key_base = 'A_1'
            binning = '1,1' if args.binning is None else args.binning
            if not os.path.isfile(args.trace_file):
                raise FileNotFoundError('File does not exist: {0}'.format(args.trace_file))
            files = [os.path.abspath(args.trace_file)]
            redux_path = os.path.abspath(os.path.split(files[0])[0]
                                         if args.redux_path is None else args.redux_path)
            par = spec.default_pypeit_par()
            proc_par = par['calibrations']['traceframe']
            trace_par = par['calibrations']['slitedges']
            bias_files = None
            bias_par = None

            dark_files = None
            dark_par = None

            # Set the QA path
            qa_path = os.path.join(os.path.abspath(os.path.split(files[0])[0]), '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 = [eval(d) for d in detectors]

        master_dir = os.path.join(redux_path, args.master_dir)
        for det in detectors:
            # Master keyword for output file name
            master_key = f'{master_key_base}_{spec.get_det_name(det)}'

            # Get the bias frame if requested
            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 dark frame if requested
            if dark_files is None:
                proc_par['process']['use_darkimage'] = False
                msdark = None
            else:
                msdark = buildimage.buildimage_fromlist(spec, det, dark_par, dark_files)

            msbpm = spec.bpm(files[0], det)

            # Build the trace image
            traceImage = buildimage.buildimage_fromlist(spec, det, proc_par, files, bias=msbias,
                                                        bpm=msbpm, dark=msdark)
            # Trace the slit edges
            t = time.perf_counter()
            edges = edgetrace.EdgeTraceSet(traceImage, spec, trace_par, auto=True,
                                           debug=args.debug, show_stages=args.show,
                                           qa_path=qa_path)

            print('Tracing for detector {0} finished in {1} s.'.format(det, time.perf_counter()-t))
            # Write the MasterEdges file
            edge_masterframe_name = masterframe.construct_file_name(edgetrace.EdgeTraceSet,
                                                                    master_key,
                                                                    master_dir=master_dir)
            edges.to_master_file(edge_masterframe_name)

            # Write the MasterSlits file
            slit_masterframe_name = masterframe.construct_file_name(slittrace.SlitTraceSet,
                                                                    master_key,
                                                                    master_dir=master_dir)
            edges.get_slits().to_master_file(slit_masterframe_name)

        return 0