Example #1
0
def read_qframe(filename, nspec=None, skip_resolution=False):
    """Reads a frame fits file and returns its data.

    Args:
        filename: path to a file
        skip_resolution: bool, option
            Speed up read time (>5x) by avoiding the Resolution matrix

    Returns:
        desispec.Frame object with attributes wave, flux, ivar, etc.
    """
    log = get_logger()

    if not os.path.isfile(filename):
        raise IOError("cannot open" + filename)

    fx = fits.open(filename, uint=True, memmap=False)
    hdr = fx[0].header
    flux = native_endian(fx['FLUX'].data.astype('f8'))
    ivar = native_endian(fx['IVAR'].data.astype('f8'))
    wave = native_endian(fx['WAVELENGTH'].data.astype('f8'))

    if wave.shape != flux.shape:
        log.error(
            "{} is not a valid QFrame file because wave.shape != flux.shape".
            format(filename))
        return None

    if 'MASK' in fx:
        mask = native_endian(fx['MASK'].data)
    else:
        mask = None  #- let the Frame object create the default mask

    if 'SIGMA' in fx:
        sigma = native_endian(fx['SIGMA'].data.astype('f8'))
    else:
        sigma = None

    if 'FIBERMAP' in fx:
        fibermap = fx['FIBERMAP'].data
        fibers = fibermap['FIBER']
    else:
        fibermap = None
        fibers = None

    fx.close()

    if nspec is not None:
        flux = flux[0:nspec]
        ivar = ivar[0:nspec]
        if mask is not None:
            mask = mask[0:nspec]
        if fibermap is not None:
            fibermap = fibermap[:][0:nspec]
        if fibers is not None:
            fibers = fibers[:][0:nspec]

    # return flux,ivar,wave,resolution_data, hdr
    qframe = QFrame(wave,
                    flux,
                    ivar,
                    mask=mask,
                    sigma=sigma,
                    meta=hdr,
                    fibermap=fibermap,
                    fibers=fibers)

    # Return
    return qframe
Example #2
0
def qproc_boxcar_extraction(xytraceset, image, fibers=None, width=7, fibermap=None, save_sigma=True) :    
    """
    Fast boxcar extraction of spectra from a preprocessed image and a trace set
    
    Args:
        xytraceset : DESI XYTraceSet object
        image : DESI preprocessed Image object

    Optional:   
        fibers : 1D np.array of int (default is all fibers, the first fiber is always = 0)
        width  : extraction boxcar width, default is 7
        fibermap : table

    Returns:
        QFrame object
    """
    log=get_logger()
    log.info("Starting...")
    
    t0=time.time()
    
    wavemin = xytraceset.wavemin
    wavemax = xytraceset.wavemax
    xcoef   = xytraceset.x_vs_wave_traceset._coeff 
    ycoef   = xytraceset.y_vs_wave_traceset._coeff 
    
    spectrograph = 0
    if "CAMERA" in image.meta :
        camera=image.meta["CAMERA"].strip()
        spectrograph = int(camera[-1])
        log.info("camera='{}' -> spectrograph={}. I AM USING THIS TO DEFINE THE FIBER NUMBER (ASSUMING 500 FIBERS PER SPECTRO).".format(camera,spectrograph))
    
    allfibers = np.arange(xcoef.shape[0])+500*spectrograph
    
    if fibers is None :
        fibers = allfibers

    

    #log.info("wavelength range : [%f,%f]"%(wavemin,wavemax))
    
    if image.mask is not None :
        image.ivar *= (image.mask==0)
    
    #  Applying a mask that keeps positive value to get the Variance by inversing the inverse variance.
    var=np.zeros(image.ivar.size)
    ok=image.ivar.ravel()>0
    var[ok] = 1./image.ivar.ravel()[ok]
    var=var.reshape(image.ivar.shape)

    badimage=(image.ivar==0)
    
    n0 = image.pix.shape[0]
    n1 = image.pix.shape[1]
    
    frame_flux = np.zeros((fibers.size,n0))
    frame_ivar = np.zeros((fibers.size,n0))
    frame_wave = np.zeros((fibers.size,n0))

    frame_sigma = None
    ysigcoef = None
    if save_sigma :
        if  xytraceset.ysig_vs_wave_traceset is None :
            log.warning("will not save sigma in qframe because missing in traceset")
        else :
            frame_sigma = np.zeros((fibers.size,n0))
            ysigcoef    = xytraceset.ysig_vs_wave_traceset._coeff

    xx         = np.tile(np.arange(n1),(n0,1))
    hw = width//2
    
    
    twave=np.linspace(wavemin, wavemax, n0//4) # this number of bins n0//p is calibrated to give a negligible difference of wavelength precision
    rwave=(twave-wavemin)/(wavemax-wavemin)*2-1.
    y=np.arange(n0).astype(float)
    
    
    dwave = np.zeros(n0)
    for f,fiber in enumerate(fibers) :
        log.debug("extracting fiber #%03d"%fiber)
        ty = legval(rwave, ycoef[f])
        tx = legval(rwave, xcoef[f])
        frame_wave[f] = np.interp(y,ty,twave)
        x_of_y        = np.interp(y,ty,tx)        

        i=np.where(y<ty[0])[0]
        if i.size>0 : # need extrapolation
            frame_wave[f,i] = twave[0]+(twave[1]-twave[0])/(ty[1]-ty[0])*(y[i]-ty[0])
        i=np.where(y>ty[-1])[0]
        if i.size>0 : # need extrapolation
            frame_wave[f,i] = twave[-1]+(twave[-2]-twave[-1])/(ty[-2]-ty[-1])*(y[i]-ty[-1])

        dwave[1:]     = frame_wave[f,1:]-frame_wave[f,:-1]
        dwave[0]      = 2*dwave[1]-dwave[2]
        if np.any(dwave<=0) :
            log.error("neg. or null dwave")
            raise ValueError("neg. or null dwave")

        frame_flux[f],frame_ivar[f] = numba_extract(image.pix,var,x_of_y,hw)
        # flux density
        frame_flux[f] /= dwave
        frame_ivar[f] *= dwave**2

        if frame_sigma is not None :
            ts = legval(rwave, ysigcoef[f])
            frame_sigma[f] = np.interp(y,ty,ts)

    t1=time.time()
    log.info(" done {} fibers in {:3.1f} sec".format(len(fibers),t1-t0))
    
    if fibermap is None: 
        log.warning("setting up a fibermap to save the FIBER identifiers")
        fibermap = empty_fibermap(fibers.size)
        fibermap["FIBER"] = fibers
    else :        
        indices = np.arange(fibermap["FIBER"].size)[np.in1d(fibermap["FIBER"],fibers)]
        fibermap = fibermap[:][indices]
            
    return QFrame(frame_wave, frame_flux, frame_ivar, mask=None, sigma=frame_sigma , fibers=fibers, meta=image.meta, fibermap=fibermap)
Example #3
0
def main(args=None):

    if args is None:
        args = parse()
    elif isinstance(args, (list, tuple)):
        args = parse(args)

    t0 = time.time()
    log = get_logger()

    # guess if it is a preprocessed or a raw image
    hdulist = fits.open(args.image)
    is_input_preprocessed = ("IMAGE" in hdulist) & ("IVAR" in hdulist)
    primary_header = hdulist[0].header
    hdulist.close()

    if is_input_preprocessed:
        image = read_image(args.image)
    else:
        if args.camera is None:
            print(
                "ERROR: Need to specify camera to open a raw fits image (with all cameras in different fits HDUs)"
            )
            print(
                "Try adding the option '--camera xx', with xx in {brz}{0-9}, like r7,  or type 'desi_qproc --help' for more options"
            )
            sys.exit(12)
        image = read_raw(args.image, args.camera, fill_header=[
            1,
        ])

    if args.auto:
        log.debug("AUTOMATIC MODE")
        try:
            night = image.meta['NIGHT']
            if not 'EXPID' in image.meta:
                if 'EXPNUM' in image.meta:
                    log.warning('using EXPNUM {} for EXPID'.format(
                        image.meta['EXPNUM']))
                    image.meta['EXPID'] = image.meta['EXPNUM']
            expid = image.meta['EXPID']
        except KeyError as e:
            log.error(
                "Need at least NIGHT and EXPID (or EXPNUM) to run in auto mode. Retry without the --auto option."
            )
            log.error(str(e))
            sys.exit(12)

        indir = os.path.dirname(args.image)
        if args.fibermap is None:
            filename = '{}/fibermap-{:08d}.fits'.format(indir, expid)
            if os.path.isfile(filename):
                log.debug("auto-mode: found a fibermap, {}, using it!".format(
                    filename))
                args.fibermap = filename
        if args.output_preproc is None:
            if not is_input_preprocessed:
                args.output_preproc = '{}/preproc-{}-{:08d}.fits'.format(
                    args.auto_output_dir, args.camera.lower(), expid)
                log.debug("auto-mode: will write preproc in " +
                          args.output_preproc)
            else:
                log.debug(
                    "auto-mode: will not write preproc because input is a preprocessed image"
                )

        if args.auto_output_dir != '.':
            if not os.path.isdir(args.auto_output_dir):
                log.debug("auto-mode: creating directory " +
                          args.auto_output_dir)
                os.makedirs(args.auto_output_dir)

    if args.output_preproc is not None:
        write_image(args.output_preproc, image)

    cfinder = None

    if args.psf is None:
        if cfinder is None:
            cfinder = CalibFinder([image.meta, primary_header])
        args.psf = cfinder.findfile("PSF")
        log.info(" Using PSF {}".format(args.psf))

    tset = read_xytraceset(args.psf)

    # add fibermap
    if args.fibermap:
        if os.path.isfile(args.fibermap):
            fibermap = read_fibermap(args.fibermap)
        else:
            log.error("no fibermap file {}".format(args.fibermap))
            fibermap = None
    else:
        fibermap = None

    if "OBSTYPE" in image.meta:
        obstype = image.meta["OBSTYPE"].upper()
        image.meta["OBSTYPE"] = obstype  # make sure it's upper case
        qframe = None
    else:
        log.warning("No OBSTYPE keyword, trying to guess ...")
        qframe = qproc_boxcar_extraction(tset,
                                         image,
                                         width=args.width,
                                         fibermap=fibermap)
        obstype = check_qframe_flavor(
            qframe, input_flavor=image.meta["FLAVOR"]).upper()
        image.meta["OBSTYPE"] = obstype

    log.info("OBSTYPE = '{}'".format(obstype))

    if args.auto:

        # now set the things to do
        if obstype == "SKY" or obstype == "TWILIGHT" or obstype == "SCIENCE":

            args.shift_psf = True
            args.output_psf = '{}/psf-{}-{:08d}.fits'.format(
                args.auto_output_dir, args.camera, expid)
            args.output_rawframe = '{}/qframe-{}-{:08d}.fits'.format(
                args.auto_output_dir, args.camera, expid)
            args.apply_fiberflat = True
            args.skysub = True
            args.output_skyframe = '{}/qsky-{}-{:08d}.fits'.format(
                args.auto_output_dir, args.camera, expid)
            args.fluxcalib = True
            args.outframe = '{}/qcframe-{}-{:08d}.fits'.format(
                args.auto_output_dir, args.camera, expid)

        elif obstype == "ARC" or obstype == "TESTARC":

            args.shift_psf = True
            args.output_psf = '{}/psf-{}-{:08d}.fits'.format(
                args.auto_output_dir, args.camera, expid)
            args.output_rawframe = '{}/qframe-{}-{:08d}.fits'.format(
                args.auto_output_dir, args.camera, expid)
            args.compute_lsf_sigma = True

        elif obstype == "FLAT" or obstype == "TESTFLAT":
            args.shift_psf = True
            args.output_psf = '{}/psf-{}-{:08d}.fits'.format(
                args.auto_output_dir, args.camera, expid)
            args.output_rawframe = '{}/qframe-{}-{:08d}.fits'.format(
                args.auto_output_dir, args.camera, expid)
            args.compute_fiberflat = '{}/qfiberflat-{}-{:08d}.fits'.format(
                args.auto_output_dir, args.camera, expid)

    if args.shift_psf:

        # using the trace shift script
        if args.auto:
            options = option_list({
                "psf":
                args.psf,
                "image":
                "dummy",
                "outpsf":
                "dummy",
                "continuum": ((obstype == "FLAT") | (obstype == "TESTFLAT")),
                "sky": ((obstype == "SCIENCE") | (obstype == "SKY"))
            })
        else:
            options = option_list({
                "psf": args.psf,
                "image": "dummy",
                "outpsf": "dummy"
            })
        tmp_args = trace_shifts_script.parse(options=options)
        tset = trace_shifts_script.fit_trace_shifts(image=image, args=tmp_args)

    qframe = qproc_boxcar_extraction(tset,
                                     image,
                                     width=args.width,
                                     fibermap=fibermap)

    if tset.meta is not None:
        # add traceshift info in the qframe, this will be saved in the qframe header
        if qframe.meta is None:
            qframe.meta = dict()
        for k in tset.meta.keys():
            qframe.meta[k] = tset.meta[k]

    if args.output_rawframe is not None:
        write_qframe(args.output_rawframe, qframe)
        log.info("wrote raw extracted frame in {}".format(
            args.output_rawframe))

    if args.compute_lsf_sigma:
        tset = process_arc(qframe, tset, linelist=None, npoly=2, nbins=2)

    if args.output_psf is not None:
        for k in qframe.meta:
            if k not in tset.meta:
                tset.meta[k] = qframe.meta[k]
        write_xytraceset(args.output_psf, tset)

    if args.compute_fiberflat is not None:
        fiberflat = qproc_compute_fiberflat(qframe)
        #write_qframe(args.compute_fiberflat,qflat)
        write_fiberflat(args.compute_fiberflat, fiberflat, header=qframe.meta)
        log.info("wrote fiberflat in {}".format(args.compute_fiberflat))

    if args.apply_fiberflat or args.input_fiberflat:

        if args.input_fiberflat is None:
            if cfinder is None:
                cfinder = CalibFinder([image.meta, primary_header])
            try:
                args.input_fiberflat = cfinder.findfile("FIBERFLAT")
            except KeyError as e:
                log.error("no FIBERFLAT for this spectro config")
                sys.exit(12)
        log.info("applying fiber flat {}".format(args.input_fiberflat))
        flat = read_fiberflat(args.input_fiberflat)
        qproc_apply_fiberflat(qframe, flat)

    if args.skysub:
        log.info("sky subtraction")
        if args.output_skyframe is not None:
            skyflux = qproc_sky_subtraction(qframe, return_skymodel=True)
            sqframe = QFrame(qframe.wave, skyflux, np.ones(skyflux.shape))
            write_qframe(args.output_skyframe, sqframe)
            log.info("wrote sky model in {}".format(args.output_skyframe))
        else:
            qproc_sky_subtraction(qframe)

    if args.fluxcalib:
        if cfinder is None:
            cfinder = CalibFinder([image.meta, primary_header])
        # check for flux calib
        if cfinder.haskey("FLUXCALIB"):
            fluxcalib_filename = cfinder.findfile("FLUXCALIB")
            fluxcalib = read_average_flux_calibration(fluxcalib_filename)
            log.info("read average calib in {}".format(fluxcalib_filename))
            seeing = qframe.meta["SEEING"]
            airmass = qframe.meta["AIRMASS"]
            exptime = qframe.meta["EXPTIME"]
            exposure_calib = fluxcalib.value(seeing=seeing, airmass=airmass)
            for q in range(qframe.nspec):
                fiber_calib = np.interp(qframe.wave[q], fluxcalib.wave,
                                        exposure_calib) * exptime
                inv_calib = (fiber_calib > 0) / (fiber_calib +
                                                 (fiber_calib == 0))
                qframe.flux[q] *= inv_calib
                qframe.ivar[q] *= fiber_calib**2 * (fiber_calib > 0)

            # add keyword in header giving the calibration factor applied at a reference wavelength
            band = qframe.meta["CAMERA"].upper()[0]
            if band == "B":
                refwave = 4500
            elif band == "R":
                refwave = 6500
            else:
                refwave = 8500
            calvalue = np.interp(refwave, fluxcalib.wave,
                                 exposure_calib) * exptime
            qframe.meta["CALWAVE"] = refwave
            qframe.meta["CALVALUE"] = calvalue
        else:
            log.error(
                "Cannot calibrate fluxes because no FLUXCALIB keywork in calibration files"
            )

    fibers = parse_fibers(args.fibers)
    if fibers is None:
        fibers = qframe.flux.shape[0]
    else:
        ii = np.arange(qframe.fibers.size)[np.in1d(qframe.fibers, fibers)]
        if ii.size == 0:
            log.error("no such fibers in frame,")
            log.error("fibers are in range [{}:{}]".format(
                qframe.fibers[0], qframe.fibers[-1] + 1))
            sys.exit(12)
        qframe = qframe[ii]

    if args.outframe is not None:
        write_qframe(args.outframe, qframe)
        log.info("wrote {}".format(args.outframe))

    t1 = time.time()
    log.info("all done in {:3.1f} sec".format(t1 - t0))

    if args.plot:
        log.info("plotting {} spectra".format(qframe.wave.shape[0]))

        import matplotlib.pyplot as plt
        fig = plt.figure()
        for i in range(qframe.wave.shape[0]):
            j = (qframe.ivar[i] > 0)
            plt.plot(qframe.wave[i, j], qframe.flux[i, j])
        plt.grid()
        plt.xlabel("wavelength")
        plt.ylabel("flux")
        plt.show()