Example #1
0
    def _perform(self):
        """
        Returns an Argument() with the parameters that depends on this operation
        """
        self.logger.info("Creating master illumination correction")

        suffix = self.action.args.new_type.lower()
        insuff = self.action.args.stack_type.lower()

        stack_list = list(self.stack_list['filename'])

        if len(stack_list) <= 0:
            self.logger.warning("No flats found!")
            return self.action.args

        # get root for maps
        tab = self.context.proctab.search_proctab(
            frame=self.action.args.ccddata, target_type='ARCLAMP',
            target_group=self.action.args.groupid)
        if len(tab) <= 0:
            self.logger.error("Geometry not solved!")
            return self.action.args

        mroot = strip_fname(tab['filename'][-1])

        # Wavelength map image
        wmf = mroot + '_wavemap.fits'
        self.logger.info("Reading image: %s" % wmf)
        wavemap = kcwi_fits_reader(
            os.path.join(self.config.instrument.cwd, 'redux',
                         wmf))[0]

        # Slice map image
        slf = mroot + '_slicemap.fits'
        self.logger.info("Reading image: %s" % slf)
        slicemap = kcwi_fits_reader(os.path.join(
            self.config.instrument.cwd, 'redux',
                         slf))[0]

        # Position map image
        pof = mroot + '_posmap.fits'
        self.logger.info("Reading image: %s" % pof)
        posmap = kcwi_fits_reader(os.path.join(
            self.config.instrument.cwd, 'redux',
                         pof))[0]

        # Read in stacked flat image
        stname = strip_fname(stack_list[0]) + '_' + insuff + '.fits'

        self.logger.info("Reading image: %s" % stname)
        stacked = kcwi_fits_reader(os.path.join(
            self.config.instrument.cwd, 'redux',
                         stname))[0]

        # get type of flat
        internal = ('SFLAT' in stacked.header['IMTYPE'])
        twiflat = ('STWIF' in stacked.header['IMTYPE'])
        domeflat = ('SDOME' in stacked.header['IMTYPE'])

        if internal:
            self.logger.info("Internal Flat")
        elif twiflat:
            self.logger.info("Twilight Flat")
        elif domeflat:
            self.logger.info("Dome Flat")
        else:
            self.logger.error("Flat of Unknown Type!")
            return self.action.args

        # knots per pixel
        knotspp = self.config.instrument.KNOTSPP

        # get image size
        ny = stacked.header['NAXIS2']

        # get binning
        xbin = self.action.args.xbinsize

        # Parameters for fitting

        # vignetted slice position range
        fitl = int(4/xbin)
        fitr = int(24/xbin)

        # un-vignetted slice position range
        flatl = int(34/xbin)
        flatr = int(72/xbin)

        # flat fitting slice position range
        ffleft = int(10/xbin)
        ffright = int(70/xbin)
        nrefx = int(ffright - ffleft)

        buffer = 6.0/float(xbin)

        # reference slice
        refslice = 9
        allidx = np.arange(int(140/xbin))
        newflat = stacked.data.copy()

        # dichroic fraction
        try:
            dichroic_fraction = wavemap.header['DICHFRAC']
        except KeyError:
            dichroic_fraction = 1.

        # get reference slice data
        q = [i for i, v in enumerate(slicemap.data.flat) if v == refslice]
        # get wavelength limits
        waves = wavemap.data.compress((wavemap.data > 0.).flat)
        waves = [waves.min(), waves.max()]
        self.logger.info("Wavelength limits: %.1f - %1.f" % (waves[0],
                                                             waves[1]))

        # correct vignetting if we are using internal flats
        if internal:
            self.logger.info("Internal flats require vignetting correction")
            # get good region for fitting
            if self.action.args.camera == 0:    # Blue
                wmin = waves[0]
                wmax = min([waves[1], 5620.])
            elif self.action.args.camera == 1:  # Red
                wmin = max([waves[0], 5580.])
                wmax = waves[1]
            else:
                self.logger.warning("Camera keyword not defined")
                wmin = waves[0]
                wmax = waves[1]
            dw = (wmax - wmin) / 30.0
            wavemin = (wmin+wmax) / 2.0 - dw
            wavemax = (wmin+wmax) / 2.0 + dw
            self.logger.info("Using %.1f - %.1f A of slice %d" % (wavemin,
                                                                  wavemax,
                                                                  refslice))
            xflat = []
            yflat = []
            wflat = []
            qq = []
            for i in q:
                if wavemin < wavemap.data.flat[i] < wavemax:
                    xflat.append(posmap.data.flat[i])
                    yflat.append(stacked.data.flat[i])
                    wflat.append(wavemap.data.flat[i])
                    qq.append(i)
            # get un-vignetted portion
            qflat = [i for i, v in enumerate(xflat) if flatl <= v <= flatr]
            xflat = [xflat[i] for i in qflat]
            yflat = [yflat[i] for i in qflat]
            wflat = [wflat[i] for i in qflat]
            # sort on wavelength
            sw = np.argsort(wflat)
            ywflat = [yflat[i] for i in sw]
            wwflat = [wflat[i] for i in sw]
            ww0 = np.min(wwflat)
            # fit wavelength slope
            wavelinfit = np.polyfit(wwflat-ww0, ywflat, 2)
            wslfit = np.polyval(wavelinfit, wflat-ww0)
            # plot slope fit
            if self.config.instrument.plot_level >= 1:
                p = figure(title=self.action.args.plotlabel + ' WAVE SLOPE FIT',
                           x_axis_label='wave px',
                           y_axis_label='counts',
                           plot_width=self.config.instrument.plot_width,
                           plot_height=self.config.instrument.plot_height)
                p.circle(wwflat, ywflat, legend_label="Data")
                p.line(wflat, wslfit, line_color='red', line_width=3,
                       legend_label="Fit")
                bokeh_plot(p, self.context.bokeh_session)
                if self.config.instrument.plot_level >= 2:
                    input("Next? <cr>: ")
                else:
                    time.sleep(self.config.instrument.plot_pause)
            # take out slope
            yflat = yflat / wslfit
            # now sort on slice position
            ss = np.argsort(xflat)
            xflat = [xflat[i] for i in ss]
            yflat = [yflat[i] for i in ss]
            # fit un-vignetted slope
            resflat = np.polyfit(xflat, yflat, 1)

            # select the points we will fit for the vignetting
            # get reference region
            xfit = [posmap.data.flat[i] for i in qq]
            yfit = [stacked.data.flat[i] for i in qq]
            wflat = [wavemap.data.flat[i] for i in qq]
            # take out wavelength slope
            yfit = yfit / np.polyval(wavelinfit, wflat-ww0)

            # select the vignetted region
            qfit = [i for i, v in enumerate(xfit) if fitl <= v <= fitr]
            xfit = [xfit[i] for i in qfit]
            yfit = [yfit[i] for i in qfit]
            # sort on slice position
            s = np.argsort(xfit)
            xfit = [xfit[i] for i in s]
            yfit = [yfit[i] for i in s]
            # fit vignetted slope
            resfit = np.polyfit(xfit, yfit, 1)
            # corrected data
            ycdata = stacked.data.flat[qq] / \
                np.polyval(wavelinfit, wavemap.data.flat[qq]-ww0)
            ycmin = 0.5     # np.min(ycdata)
            ycmax = 1.25    # np.max(ycdata)
            # compute the intersection
            xinter = -(resflat[1] - resfit[1]) / (resflat[0] - resfit[0])
            # plot slice profile and fits
            if self.config.instrument.plot_level >= 1:
                p = figure(title=self.action.args.plotlabel + ' Vignetting',
                           x_axis_label='Slice Pos (px)',
                           y_axis_label='Ratio',
                           plot_width=self.config.instrument.plot_width,
                           plot_height=self.config.instrument.plot_height)
                p.circle(posmap.data.flat[qq], ycdata, legend_label='Data')
                p.line(allidx, resfit[1] + resfit[0]*allidx,
                       line_color='purple', legend_label='Vign.')
                p.line(allidx, resflat[1] + resflat[0]*allidx, line_color='red',
                       legend_label='UnVign.')
                p.line([fitl, fitl], [ycmin, ycmax], line_color='blue')
                p.line([fitr, fitr], [ycmin, ycmax], line_color='blue')
                p.line([flatl, flatl], [ycmin, ycmax], line_color='green')
                p.line([flatr, flatr], [ycmin, ycmax], line_color='green')
                p.line([xinter-buffer, xinter-buffer], [ycmin, ycmax],
                       line_color='black')
                p.line([xinter + buffer, xinter + buffer], [ycmin, ycmax],
                       line_color='black')
                p.line([xinter, xinter], [ycmin, ycmax], line_color='red')
                p.y_range = Range1d(ycmin, ycmax)
                bokeh_plot(p, self.context.bokeh_session)
                if self.config.instrument.plot_level >= 2:
                    input("Next? <cr>: ")
                else:
                    time.sleep(self.config.instrument.plot_pause)

            # figure out where the correction applies
            qcor = [i for i, v in enumerate(posmap.data.flat)
                    if 0 <= v <= (xinter-buffer)]
            # apply the correction!
            self.logger.info("Applying vignetting correction...")
            for i in qcor:
                newflat.flat[i] = (resflat[1]+resflat[0]*posmap.data.flat[i]) \
                                / (resfit[1]+resfit[0]*posmap.data.flat[i]) * \
                                stacked.data.flat[i]
            # now deal with the intermediate (buffer) region
            self.logger.info("Done, now handling buffer region")
            # get buffer points to fit in reference region
            qbff = [i for i in qq if (xinter-buffer) <=
                    posmap.data.flat[i] <= (xinter+buffer)]
            # get slice pos and data for buffer fitting
            xbuff = [posmap.data.flat[i] for i in qbff]
            ybuff = [stacked.data.flat[i] / np.polyval(wavelinfit,
                                                       wavemap.data.flat[i]-ww0)
                     for i in qbff]
            # sort on slice position
            ssp = np.argsort(xbuff)
            xbuff = [xbuff[i] for i in ssp]
            ybuff = [ybuff[i] for i in ssp]
            # fit buffer with low-order poly
            buffit = np.polyfit(xbuff, ybuff, 3)
            # plot buffer fit
            if self.config.instrument.plot_level >= 1:
                p = figure(title=self.action.args.plotlabel + ' Buffer Region',
                           x_axis_label='Slice Pos (px)',
                           y_axis_label='Ratio',
                           plot_width=self.config.instrument.plot_width,
                           plot_height=self.config.instrument.plot_height)
                p.circle(xbuff, ybuff)
                p.line(xbuff, np.polyval(buffit, xbuff), line_color='red')
                bokeh_plot(p, self.context.bokeh_session)
                if self.config.instrument.plot_level >= 2:
                    input("Next? <cr>: ")
                else:
                    time.sleep(self.config.instrument.plot_pause)
            # get all buffer points in image
            qbuf = [i for i, v in enumerate(posmap.data.flat)
                    if (xinter-buffer) <= v <= (xinter+buffer)]
            # apply buffer correction to all buffer points in newflat
            for i in qbuf:
                newflat.flat[i] = \
                    (resflat[1] + resflat[0] * posmap.data.flat[i]) / \
                    np.polyval(buffit, posmap.data.flat[i]) * newflat.flat[i]
            self.logger.info("Vignetting correction complete.")

        self.logger.info("Fitting master illumination")
        # now fit master flat
        # get reference slice points
        qref = [i for i in q if ffleft <= posmap.data.flat[i] <= ffright]
        xfr = wavemap.data.flat[qref]
        yfr = newflat.flat[qref]
        # sort on wavelength
        s = np.argsort(xfr)
        xfr = xfr[s]
        yfr = yfr[s]

        wavegood0 = wavemap.header['WAVGOOD0']
        wavegood1 = wavemap.header['WAVGOOD1']

        # correction for BM where we see a ledge
        if 'BM' in self.action.args.grating:
            ledge_wave = bm_ledge_position(self.action.args.cwave)

            self.logger.info("BM ledge calculated wavelength "
                             "for ref slice = %.2f (A)" % ledge_wave)
            if wavegood0 <= ledge_wave <= wavegood1:
                self.logger.info("BM grating requires correction")
                qledge = [i for i, v in enumerate(xfr)
                          if ledge_wave-25 <= v <= ledge_wave+25]
                xledge = [xfr[i] for i in qledge]
                yledge = [yfr[i] for i in qledge]
                s = np.argsort(xledge)
                xledge = [xledge[i] for i in s]
                yledge = [yledge[i] for i in s]
                win = boxcar(250)
                smyledge = sp.signal.convolve(yledge,
                                              win, mode='same') / sum(win)
                ylmax = np.max(yledge)
                ylmin = np.min(yledge)
                fpoints = np.arange(0, 100) / 100. * 50 + (ledge_wave-25)
                ledgefit, ledgemsk = Bspline.iterfit(np.asarray(xledge),
                                                     smyledge, fullbkpt=fpoints,
                                                     upper=1, lower=1)
                ylfit, _ = ledgefit.value(np.asarray(fpoints))
                deriv = -(np.roll(ylfit, 1) - np.roll(ylfit, -1)) / 2.0
                # trim edges
                trm = int(len(deriv)/5)
                deriv = deriv[trm:-trm]
                xvals = fpoints[trm:-trm]
                peaks, _ = find_peaks(deriv, height=100)
                if len(peaks) != 1:
                    self.logger.warning("Extra peak found!")
                    p = figure(title=self.action.args.plotlabel +
                               ' Ledge', x_axis_label='Wavelength (A)',
                               y_axis_label='Value',
                               plot_width=self.config.instrument.plot_width,
                               plot_height=self.config.instrument.plot_height)
                    p.circle(xledge, smyledge, fill_color='green')
                    p.line(fpoints, ylfit)
                    bokeh_plot(p, self.context.bokeh_session)
                    input("Next? <cr>: ")
                    p = figure(title=self.action.args.plotlabel +
                               ' Deriv', x_axis_label='px',
                               y_axis_label='Value',
                               plot_width=self.config.instrument.plot_width,
                               plot_height=self.config.instrument.plot_height)
                    xx = list(range(len(deriv)))
                    ylim = get_plot_lims(deriv)
                    p.circle(xx, deriv)
                    for pk in peaks:
                        p.line([pk, pk], ylim)
                    bokeh_plot(p, self.context.bokeh_session)
                    print("Please indicate the integer pixel value of the peak")
                    ipk = int(input("Peak? <int>: "))
                else:
                    ipk = peaks[0]
                apk = xvals[ipk]
                if self.config.instrument.plot_level >= 3:
                    p = figure(
                        title=self.action.args.plotlabel + ' Peak of ledge',
                        x_axis_label='Wave (A)',
                        y_axis_label='Value',
                        plot_width=self.config.instrument.plot_width,
                        plot_height=self.config.instrument.plot_height)
                    p.circle(xvals, deriv, legend_label='Data')
                    p.line([apk, apk], [-50, 200], line_color='red',
                           legend_label='Pk')
                    bokeh_plot(p, self.context.bokeh_session)
                    if self.config.instrument.plot_level >= 2:
                        input("Next? <cr>: ")
                    else:
                        time.sleep(self.config.instrument.plot_pause)
                xlow = apk - 3 - 5
                xhi = apk - 3
                zlow = apk + 3
                zhi = apk + 3 + 5
                qlow = [i for i, v in enumerate(fpoints) if xlow <= v <= xhi]
                xlf = np.asarray([fpoints[i] for i in qlow])
                ylf = np.asarray([ylfit[i] for i in qlow])
                lowfit = np.polyfit(xlf, ylf, 1)
                qhi = [i for i, v in enumerate(fpoints) if zlow <= v <= zhi]
                xlf = np.asarray([fpoints[i] for i in qhi])
                ylf = np.asarray([ylfit[i] for i in qhi])
                hifit = np.polyfit(xlf, ylf, 1)
                ratio = (hifit[1] + hifit[0] * apk) / \
                        (lowfit[1] + lowfit[0] * apk)
                self.logger.info("BM ledge ratio: %.3f" % ratio)
                # correct flat data
                qcorr = [i for i, v in enumerate(xfr) if v >= apk]
                for i in qcorr:
                    yfr[i] /= ratio
                # plot BM ledge
                if self.config.instrument.plot_level >= 1:
                    p = figure(
                        title=self.action.args.plotlabel + ' BM Ledge Region',
                        x_axis_label='Wave (A)',
                        y_axis_label='Value',
                        plot_width=self.config.instrument.plot_width,
                        plot_height=self.config.instrument.plot_height)
                    # Input data
                    p.circle(xledge, yledge, fill_color='blue',
                             legend_label='Data')
                    # correct input data
                    qcorrect = [i for i, v in enumerate(xledge) if v >= apk]
                    xplt = []
                    yplt = []
                    for i in qcorrect:
                        xplt.append(xledge[i])
                        yplt.append(yledge[i] / ratio)
                    p.circle(xplt, yplt, fill_color='orange',
                             legend_label='Corrected')
                    p.line(fpoints, ylfit, line_color='red', legend_label='Fit')
                    p.line([xlow, xlow], [ylmin, ylmax], line_color='blue')
                    p.line([xhi, xhi], [ylmin, ylmax], line_color='blue')
                    p.line([zlow, zlow], [ylmin, ylmax], line_color='black')
                    p.line([zhi, zhi], [ylmin, ylmax], line_color='black')
                    p.line(fpoints, lowfit[1] + lowfit[0] * fpoints,
                           line_color='purple')
                    p.line(fpoints, hifit[1] + hifit[0] * fpoints,
                           line_color='green')
                    p.line([apk, apk], [ylmin, ylmax], line_color='green',
                           legend_label='Pk')
                    p.y_range = Range1d(ylmin, ylmax)
                    p.legend.location = 'top_left'
                    bokeh_plot(p, self.context.bokeh_session)
                    if self.config.instrument.plot_level >= 2:
                        input("Next? <cr>: ")
                    else:
                        time.sleep(self.config.instrument.plot_pause)
        # END: handling BM grating ledge

        # if we are fitting a twilight flat, treat it like a sky image with a
        # larger number of knots
        if twiflat:
            knots = int(ny * knotspp)
        else:
            knots = 100
        self.logger.info("Using %d knots for bspline fit" % knots)

        # generate a fit from ref slice points
        bkpt = np.min(xfr) + np.arange(knots+1) * \
            (np.max(xfr) - np.min(xfr)) / knots
        sftr, _ = Bspline.iterfit(xfr[nrefx:-nrefx], yfr[nrefx:-nrefx],
                                  fullbkpt=bkpt)
        yfitr, _ = sftr.value(xfr)

        # generate a blue slice spectrum bspline fit
        blueslice = 12
        blueleft = 60 / xbin
        blueright = 80 / xbin
        qb = [i for i, v in enumerate(slicemap.data.flat) if v == blueslice]
        qblue = [i for i in qb if blueleft <= posmap.data.flat[i] <= blueright]
        xfb = wavemap.data.flat[qblue]
        yfb = newflat.flat[qblue]
        s = np.argsort(xfb)
        xfb = xfb[s]
        yfb = yfb[s]
        bkpt = np.min(xfb) + np.arange(knots+1) * \
            (np.max(xfb) - np.min(xfb)) / knots
        sftb, _ = Bspline.iterfit(xfb[nrefx:-nrefx], yfb[nrefx:-nrefx],
                                  fullbkpt=bkpt)
        yfitb, _ = sftb.value(xfb)

        # generate a red slice spectrum bspline fit
        redslice = 23
        redleft = 60 / xbin
        redright = 80 / xbin
        qr = [i for i, v in enumerate(slicemap.data.flat) if v == redslice]
        qred = [i for i in qr if redleft <= posmap.data.flat[i] <= redright]
        xfd = wavemap.data.flat[qred]
        yfd = newflat.flat[qred]
        s = np.argsort(xfd)
        xfd = xfd[s]
        yfd = yfd[s]
        bkpt = np.min(xfd) + np.arange(knots + 1) * \
            (np.max(xfd) - np.min(xfd)) / knots
        sftd, _ = Bspline.iterfit(xfd[nrefx:-nrefx], yfd[nrefx:-nrefx],
                                  fullbkpt=bkpt)
        yfitd, _ = sftd.value(xfd)

        # waves
        minwave = np.min(xfb)
        maxwave = np.max(xfd)
        # are we a twilight flat?
        if twiflat:
            nwaves = int(ny * knotspp)
        else:
            nwaves = 1000
        waves = minwave + (maxwave - minwave) * np.arange(nwaves+1) / nwaves
        if self.config.instrument.plot_level >= 1:
            # output filename stub
            rbfnam = "redblue_%05d_%s_%s_%s" % \
                      (self.action.args.ccddata.header['FRAMENO'],
                       self.action.args.illum, self.action.args.grating,
                       self.action.args.ifuname)
            if xbin == 1:
                stride = int(len(xfr) / 8000.)
                if stride <= 0:
                    stride = 1
            else:
                stride = 1
            xrplt = xfr[::stride]
            yrplt = yfitr[::stride]
            yrplt_d = yfr[::stride]
            xbplt = xfb[::stride]
            ybplt = yfitb[::stride]
            ybplt_d = yfb[::stride]
            xdplt = xfd[::stride]
            ydplt = yfitd[::stride]
            ydplt_d = yfd[::stride]
            p = figure(
                title=self.action.args.plotlabel + ' Blue/Red fits',
                x_axis_label='Wave (A)',
                y_axis_label='Flux (e-)',
                plot_width=self.config.instrument.plot_width,
                plot_height=self.config.instrument.plot_height)
            p.line(xrplt, yrplt, line_color='black', legend_label='Ref')
            p.circle(xrplt, yrplt_d, size=1, line_alpha=0., fill_color='black')
            p.line(xbplt, ybplt, line_color='blue', legend_label='Blue')
            p.circle(xbplt, ybplt_d, size=1, line_alpha=0., fill_color='blue')
            p.line(xdplt, ydplt, line_color='red', legend_label='Red')
            p.circle(xdplt, ydplt_d, size=1, line_alpha=0., fill_color='red')
            bokeh_plot(p, self.context.bokeh_session)
            if self.config.instrument.plot_level >= 2:
                input("Next? <cr>: ")
            else:
                time.sleep(self.config.instrument.plot_pause)
            save_plot(p, filename=rbfnam+'.png')

        wavebuffer = 0.1
        minrwave = np.min(xfr)
        maxrwave = np.max(xfr)
        wavebuffer2 = 0.05
        wlb0 = minrwave+(maxrwave-minrwave)*wavebuffer2
        wlb1 = minrwave+(maxrwave-minrwave)*wavebuffer
        wlr0 = minrwave+(maxrwave-minrwave)*(1.-wavebuffer)
        wlr1 = minrwave+(maxrwave-minrwave)*(1.-wavebuffer2)
        qbluefit = [i for i, v in enumerate(waves) if wlb0 < v < wlb1]
        qredfit = [i for i, v in enumerate(waves) if wlr0 < v < wlr1]

        nqb = len(qbluefit)
        nqr = len(qredfit)
        self.logger.info("Wavelength regions: blue = %.1f - %.1f, "
                         "red = %.1f - %.1f" % (wlb0, wlb1, wlr0, wlr1))
        self.logger.info("Fit points: blue = %d, red = %d" % (nqb, nqr))

        if nqb > 0:
            bluefit, _ = sftb.value(waves[qbluefit])
            refbluefit, _ = sftr.value(waves[qbluefit])
            blue_zero_cross = np.nanmin(bluefit) <= 0. or np.nanmin(
                refbluefit) <= 0.
            bluelinfit = np.polyfit(waves[qbluefit], refbluefit/bluefit, 1)
            bluelinfity = bluelinfit[1] + bluelinfit[0] * waves[qbluefit]
        else:
            bluefit = None
            blue_zero_cross = False
            refbluefit = None
            bluelinfit = None
            bluelinfity = None
        if nqr > 0:
            redfit, _ = sftd.value(waves[qredfit])
            refredfit, _ = sftr.value(waves[qredfit])
            red_zero_cross = np.nanmin(redfit) <= 0. or np.nanmin(
                refredfit) <= 0.
            redlinfit = np.polyfit(waves[qredfit], refredfit/redfit, 1)
            redlinfity = redlinfit[1] + redlinfit[0] * waves[qredfit]
        else:
            redfit = None
            red_zero_cross = False
            refredfit = None
            redlinfit = None
            redlinfity = None
        if blue_zero_cross:
            self.logger.info("Blue extension zero crossing detected")
        if red_zero_cross:
            self.logger.info("Red extension zero crossing detected")
        if self.config.instrument.plot_level >= 1:
            if nqb > 1:
                # plot blue fits
                p = figure(
                    title=self.action.args.plotlabel + ' Blue fits',
                    x_axis_label='Wave (A)',
                    y_axis_label='Flux (e-)',
                    plot_width=self.config.instrument.plot_width,
                    plot_height=self.config.instrument.plot_height)
                p.line(waves[qbluefit], refbluefit, line_color='black',
                       legend_label='Ref')
                p.circle(waves[qbluefit], refbluefit, fill_color='black')
                p.line(waves[qbluefit], bluefit, line_color='blue',
                       legend_label='Blue')
                p.circle(waves[qbluefit], bluefit, fill_color='blue')
                bokeh_plot(p, self.context.bokeh_session)
                if self.config.instrument.plot_level >= 2:
                    input("Next? <cr>: ")
                else:
                    time.sleep(self.config.instrument.plot_pause)
                # plot blue ratios
                p = figure(
                    title=self.action.args.plotlabel + ' Blue ratios',
                    x_axis_label='Wave (A)',
                    y_axis_label='Ratio',
                    plot_width=self.config.instrument.plot_width,
                    plot_height=self.config.instrument.plot_height)
                p.line(waves[qbluefit], refbluefit/bluefit, line_color='black',
                       legend_label='Ref')
                p.circle(waves[qbluefit], refbluefit/bluefit,
                         fill_color='black')
                p.line(waves[qbluefit], bluelinfity,
                       line_color='blue', legend_label='Blue')
                p.circle(waves[qbluefit], bluelinfity,
                         fill_color='blue')
                bokeh_plot(p, self.context.bokeh_session)
                if self.config.instrument.plot_level >= 2:
                    input("Next? <cr>: ")
                else:
                    time.sleep(self.config.instrument.plot_pause)
            if nqr > 1:
                # plot red fits
                p = figure(
                    title=self.action.args.plotlabel + ' Red fits',
                    x_axis_label='Wave (A)',
                    y_axis_label='Flux (e-)',
                    plot_width=self.config.instrument.plot_width,
                    plot_height=self.config.instrument.plot_height)
                p.line(waves[qredfit], refredfit, line_color='black',
                       legend_label='Ref')
                p.circle(waves[qredfit], refredfit, fill_color='black')
                p.line(waves[qredfit], redfit, line_color='red',
                       legend_label='Red')
                p.circle(waves[qredfit], redfit, fill_color='red')
                bokeh_plot(p, self.context.bokeh_session)
                if self.config.instrument.plot_level >= 2:
                    input("Next? <cr>: ")
                else:
                    time.sleep(self.config.instrument.plot_pause)
                # plot red ratios
                p = figure(
                    title=self.action.args.plotlabel + ' Red ratios',
                    x_axis_label='Wave (A)',
                    y_axis_label='Ratio',
                    plot_width=self.config.instrument.plot_width,
                    plot_height=self.config.instrument.plot_height)
                p.line(waves[qredfit], refredfit/redfit, line_color='black',
                       legend_label='Ref')
                p.circle(waves[qredfit], refredfit/redfit, fill_color='black')
                p.line(waves[qredfit], redlinfity,
                       line_color='red', legend_label='Red')
                p.circle(waves[qredfit], redlinfity, fill_color='red')
                bokeh_plot(p, self.context.bokeh_session)
                if self.config.instrument.plot_level >= 2:
                    input("Next? <cr>: ")
                else:
                    time.sleep(self.config.instrument.plot_pause)

        # at this point we are going to try to merge the points
        self.logger.info("Correcting points outside %.1f - %.1f A"
                         % (minrwave, maxrwave))
        qselblue = [i for i, v in enumerate(xfb) if v <= minrwave]
        qselred = [i for i, v in enumerate(xfd) if v >= maxrwave]
        nqsb = len(qselblue)
        nqsr = len(qselred)
        blue_all_tie = yfitr[0]
        red_all_tie = yfitr[-1]
        self.logger.info("Blue/Red ref tie values: %.3f, %.3f"
                         % (blue_all_tie, red_all_tie))

        if nqsb > 0:
            self.logger.info("Blue ext tie value: %.3f" % yfb[qselblue[-1]])
            if blue_zero_cross:
                blue_offset = yfb[qselblue[-1]] - blue_all_tie
                bluefluxes = [yfb[i] - blue_offset for i in qselblue]
                self.logger.info("Blue zero crossing, only applying offset")
            else:
                blue_offset = yfb[qselblue[-1]] * \
                              (bluelinfit[1]+bluelinfit[0]*xfb[qselblue[-1]]) \
                              - blue_all_tie
                bluefluxes = [yfb[i] * (bluelinfit[1]+bluelinfit[0]*xfb[i])
                              - blue_offset for i in qselblue]
                self.logger.info("Blue linear ratio fit scaling applied")
            self.logger.info("Blue offset of %.2f applied" % blue_offset)
        else:
            bluefluxes = None
        if nqsr > 0:
            self.logger.info("Red ext tie value: %.3f" % yfd[qselred[0]])
            if red_zero_cross:
                red_offset = yfd[qselred[0]] - red_all_tie
                redfluxes = [yfd[i] - red_offset for i in qselred]
                self.logger.info("Red zero crossing, only applying offset")
            else:
                red_offset = yfd[qselred[0]] * \
                             (redlinfit[1]+redlinfit[0]*xfd[qselred[0]]) \
                             - red_all_tie
                redfluxes = [yfd[i] * (redlinfit[1]+redlinfit[0]*xfd[i])
                             - red_offset for i in qselred]
                self.logger.info("Red linear ratio fit scaling applied")
            self.logger.info("Red offset of %.2f applied" % red_offset)
        else:
            redfluxes = None
        allx = xfr
        allfx = xfr[nrefx:-nrefx]
        ally = yfr[nrefx:-nrefx]
        if nqsb > 0:
            bluex = xfb[qselblue]
            allx = np.append(bluex, allx)
            allfx = np.append(bluex[nrefx:], allfx)
            ally = np.append(bluefluxes[nrefx:], ally)
        if nqsr > 0:
            redx = xfd[qselred]
            allx = np.append(allx, redx)
            allfx = np.append(allfx, redx[:-nrefx])
            ally = np.append(ally, redfluxes[:-nrefx])
        s = np.argsort(allx)
        allx = allx[s]
        s = np.argsort(allfx)
        allfx = allfx[s]
        ally = ally[s]

        bkpt = np.min(allx) + np.arange(knots+1) * \
            (np.max(allx) - np.min(allx)) / knots
        sftall, _ = Bspline.iterfit(allfx, ally, fullbkpt=bkpt)
        yfitall, _ = sftall.value(allx)

        if self.config.instrument.plot_level >= 1:
            # output filename stub
            fltfnam = "flat_%05d_%s_%s_%s" % \
                      (self.action.args.ccddata.header['FRAMENO'],
                       self.action.args.illum, self.action.args.grating,
                       self.action.args.ifuname)
            if xbin == 1:
                stride = int(len(allx) / 8000.)
            else:
                stride = 1
            xplt = allfx[::stride]
            yplt = ally[::stride]
            fxplt = allx[::stride]
            fplt = yfitall[::stride]
            yran = [np.nanmin(ally), np.nanmax(ally)]
            p = figure(
                title=self.action.args.plotlabel + ' Master Illumination',
                x_axis_label='Wave (A)',
                y_axis_label='Flux (e-)',
                plot_width=self.config.instrument.plot_width,
                plot_height=self.config.instrument.plot_height)
            p.circle(xplt, yplt, size=1, line_alpha=0., fill_color='black',
                     legend_label='Data')
            p.line(fxplt, fplt, line_color='red', legend_label='Fit',
                   line_width=2)
            p.line([minrwave, minrwave], yran, line_color='orange',
                   legend_label='Cor lim')
            p.line([maxrwave, maxrwave], yran, line_color='orange')
            p.legend.location = "top_left"
            bokeh_plot(p, self.context.bokeh_session)
            if self.config.instrument.plot_level >= 2:
                input("Next? <cr>: ")
            else:
                time.sleep(self.config.instrument.plot_pause)
            save_plot(p, filename=fltfnam+".png")

        # OK, Now we have extended to the full range... so... we are going to
        # make a ratio flat!
        comflat = np.zeros(newflat.shape, dtype=float)
        qz = [i for i, v in enumerate(wavemap.data.flat) if v >= 0]

        comvals = sftall.value(wavemap.data.flat[qz])

        comflat.flat[qz] = comvals
        ratio = np.zeros(newflat.shape, dtype=float)
        qzer = [i for i, v in enumerate(newflat.flat) if v != 0]
        ratio.flat[qzer] = comflat.flat[qzer] / newflat.flat[qzer]

        # trim negative points
        qq = [i for i, v in enumerate(ratio.flat) if v < 0]
        if len(qq) > 0:
            ratio.flat[qq] = 0.0

        # trim the high points near edges of slice
        qq = [i for i, v in enumerate(ratio.flat) if v >= 3. and
              (posmap.data.flat[i] <= 4/xbin or
               posmap.data.flat[i] >= 136/xbin)]
        if len(qq) > 0:
            ratio.flat[qq] = 0.0

        # don't correct low signal points
        qq = [i for i, v in enumerate(newflat.flat) if v < 30.]
        if len(qq) > 0:
            ratio.flat[qq] = 1.0

        # get master flat output name
        mfname = stack_list[0].split('.fits')[0] + '_' + suffix + '.fits'

        log_string = MakeMasterFlat.__module__
        stacked.header['IMTYPE'] = self.action.args.new_type
        stacked.header['HISTORY'] = log_string
        stacked.header['MASTFLAT'] = (True, 'master flat image?')
        stacked.header['WAVMAPF'] = wmf
        stacked.header['SLIMAPF'] = slf
        stacked.header['POSMAPF'] = pof

        # store flat in output frame
        stacked.data = ratio

        # output master flat
        kcwi_fits_writer(stacked, output_file=mfname,
                         output_dir=self.config.instrument.output_directory)
        self.context.proctab.update_proctab(frame=stacked, suffix=suffix,
                                            newtype=self.action.args.new_type,
                                            filename=self.action.args.name)
        self.context.proctab.write_proctab()

        self.logger.info(log_string)
        return self.action.args
Example #2
0
    def _perform(self):
        self.logger.info("Finding inter-bar offsets")
        arcs = self.context.arcs
        if arcs is not None:
            # Do we plot?
            do_plot = (self.config.instrument.plot_level >= 2)
            # Compare with reference arc
            reference_arc = arcs[self.config.instrument.REFBAR][:]
            tkwgt = signal.windows.tukey(len(reference_arc), alpha=0.2)
            reference_arc *= tkwgt
            # number of cross-correlation samples (avoiding ends)
            number_of_samples = len(reference_arc[10:-10])
            # possible offsets
            offsets_array = np.arange(1-number_of_samples, number_of_samples)
            # Collect offsets
            offsets = []
            next_bar_to_plot = 0
            for arc_number, arc in enumerate(arcs):
                arc *= tkwgt
                # Cross-correlate, avoiding junk on the ends
                cross_correlation = np.correlate(reference_arc[10:-10],
                                                 arc[10:-10], mode='full')
                # Calculate offset
                ncross = len(cross_correlation)
                cr0 = int(ncross * 0.25)
                cr1 = int(ncross * 0.75)
                central_cross = cross_correlation[cr0:cr1]
                central_offs = offsets_array[cr0:cr1]
                offset = central_offs[central_cross.argmax()]
                offsets.append(offset)
                self.logger.info("Arc %d Slice %d XCorr shift = %d" %
                                 (arc_number, int(arc_number/5), offset))
                # display if requested
                if do_plot and arc_number == next_bar_to_plot:
                    p = figure(title=self.action.args.plotlabel +
                               "BAR OFFSET for Arc: %d Slice: %d = %d" %
                               (arc_number, int(arc_number/5), offset),
                               x_axis_label="CCD y (px)", y_axis_label="e-",
                               plot_width=self.config.instrument.plot_width,
                               plot_height=self.config.instrument.plot_height)
                    x = range(len(reference_arc))
                    p.line(x, reference_arc, color='green',
                           legend_label='ref bar (%d)' %
                           self.config.instrument.REFBAR)
                    p.line(x, np.roll(arc, offset), color='red',
                           legend_label='bar %d' % arc_number)
                    bokeh_plot(p, self.context.bokeh_session)
                    q = input("Next? <int> or <cr>, q to quit: ")
                    if 'Q' in q.upper():
                        do_plot = False
                    else:
                        try:
                            next_bar_to_plot = int(q)
                        except ValueError:
                            next_bar_to_plot = arc_number + 1
            # plot offsets
            if self.config.instrument.plot_level >= 1:
                p = figure(title=self.action.args.plotlabel + "BAR OFFSETS ",
                           x_axis_label="Bar #",
                           y_axis_label="Wave offset (px)",
                           plot_width=self.config.instrument.plot_width,
                           plot_height=self.config.instrument.plot_height)
                p.diamond(list(range(120)), offsets, size=8)
                xlim = [-1, 120]
                ylim = get_plot_lims(offsets)
                p.xgrid.grid_line_color = None
                oplot_slices(p, ylim)
                set_plot_lims(p, xlim=xlim, ylim=ylim)
                bokeh_plot(p, self.context.bokeh_session)
                if self.config.instrument.plot_level >= 2:
                    input("Next? <cr>: ")
                else:
                    time.sleep(self.config.instrument.plot_pause)
            self.context.bar_offsets = offsets
        else:
            self.logger.error("No extracted arcs found")

        log_string = ArcOffsets.__module__
        self.action.args.ccddata.header['HISTORY'] = log_string
        self.logger.info(log_string)

        return self.action.args
Example #3
0
    def _perform(self):
        """Solve individual arc bar spectra for wavelength"""
        self.logger.info("Solving individual arc spectra")
        # plot control booleans
        master_inter = (self.config.instrument.plot_level >= 2)
        do_inter = (self.config.instrument.plot_level >= 3)
        # output control
        verbose = (self.config.instrument.verbose > 1)

        # Bar statistics
        bar_sig = []
        bar_nls = []
        # set thresh for finding lines
        hgt = 50.
        self.logger.info("line thresh = %.2f" % hgt)
        # get relevant part of atlas spectrum
        atwave = self.action.args.refwave[self.action.args.atminrow:self.
                                          action.args.atmaxrow]
        atspec = self.action.args.reflux[self.action.args.atminrow:self.action.
                                         args.atmaxrow]
        # convert list into ndarray
        at_wave = np.asarray(self.action.args.at_wave)
        at_flux = np.asarray(self.action.args.at_flux)
        # get x values starting at zero pixels
        self.action.args.xsvals = np.arange(
            0, len(self.context.arcs[self.config.instrument.REFBAR]))
        # loop over arcs and generate a wavelength solution for each
        next_bar_to_plot = 0
        poly_order = 4
        for ib, b in enumerate(self.context.arcs):
            # Starting with pascal shifted coeffs from fit_center()
            coeff = self.action.args.twkcoeff[ib]
            # get bar wavelengths
            bw = np.polyval(coeff, self.action.args.xsvals)
            # smooth spectrum according to slicer
            if 'Small' in self.action.args.ifuname:
                # no smoothing for Small slicer
                bspec = b
            else:
                if 'Large' in self.action.args.ifuname:
                    # max smoothing for Large slicer
                    win = boxcar(5)
                else:
                    # intermediate smoothing for Medium slicer
                    win = boxcar(3)
                # do the smoothing
                bspec = sp.signal.convolve(b, win, mode='same') / sum(win)
            # store values to fit
            at_wave_dat = []  # atlas line wavelengths
            at_flux_dat = []  # atlas line peak fluxes
            arc_pix_dat = []  # arc line pixel positions
            arc_int_dat = []  # arc line pixel intensities
            rej_wave = []  # rejected line wavelengths
            rej_flux = []  # rejected line fluxes
            gaus_sig = []
            nrej = 0
            # loop over lines
            for iw, aw in enumerate(self.action.args.at_wave):
                # get window for this line
                try:
                    # get arc line initial pixel position
                    line_x = [i for i, v in enumerate(bw) if v >= aw][0]
                    # get window for arc line
                    minow, maxow, count = get_line_window(
                        bspec,
                        line_x,
                        thresh=hgt,
                        logger=(self.logger if verbose else None))
                    # do we have enough points to fit?
                    if count < 5 or not minow or not maxow:
                        rej_wave.append(aw)
                        rej_flux.append(self.action.args.at_flux[iw])
                        nrej += 1
                        if verbose:
                            self.logger.info(
                                "Arc window rejected for line %.3f" % aw)
                        continue
                    # check if window no longer contains initial value
                    if minow > line_x > maxow:
                        rej_wave.append(aw)
                        rej_flux.append(self.action.args.at_flux[iw])
                        nrej += 1
                        if verbose:
                            self.logger.info(
                                "Arc window wandered off for line %.3f" % aw)
                        continue
                    # get data to fit
                    yvec = bspec[minow:maxow + 1]
                    xvec = self.action.args.xsvals[minow:maxow + 1]
                    wvec = bw[minow:maxow + 1]
                    f0 = max(yvec)
                    par_start = [f0, np.nanmean(xvec), 1.0]
                    par_bounds = ([f0 * 0.9, np.min(xvec),
                                   0.5], [f0 * 1.1,
                                          np.max(xvec), 2.5])
                    # Gaussian fit
                    try:
                        fit, _ = curve_fit(gaus, xvec, yvec, p0=par_start)
                        #  bounds=par_bounds, method='trf')
                        sp_pk_x = fit[1]
                        gaus_sig.append(fit[2])
                    except (RuntimeError, ValueError):
                        rej_wave.append(aw)
                        rej_flux.append(self.action.args.at_flux[iw])
                        nrej += 1
                        if verbose:
                            self.logger.info(
                                "Arc Gaussian fit rejected for line %.3f" % aw)
                        # sp_pk_x = line_x
                        continue

                    # get interpolation of arc line
                    int_line = interpolate.interp1d(xvec,
                                                    yvec,
                                                    kind='cubic',
                                                    bounds_error=False,
                                                    fill_value='extrapolate')
                    # use very dense sampling
                    xplot = np.linspace(min(xvec), max(xvec), num=1000)
                    # re-sample line with dense sampling
                    plt_line = int_line(xplot)
                    # get peak position
                    max_index = plt_line.argmax()
                    peak = xplot[max_index]
                    # calculate centroid
                    cent = np.sum(xvec * yvec) / np.sum(yvec)
                    # how different is the centroid from the peak?
                    if abs(cent - peak) > 0.7:
                        # keep track of rejected line
                        rej_wave.append(aw)
                        rej_flux.append(self.action.args.at_flux[iw])
                        nrej += 1
                        if verbose:
                            self.logger.info("Arc peak - cent offset = %.2f "
                                             "rejected for line %.3f" %
                                             (abs(cent - peak), aw))
                        continue
                    if plt_line[max_index] < 100:
                        # keep track of rejected line
                        rej_wave.append(aw)
                        rej_flux.append(self.action.args.at_flux[iw])
                        nrej += 1
                        if verbose:
                            self.logger.info("Arc peak too low = %.2f "
                                             "rejected for line %.3f" %
                                             (plt_line[max_index], aw))
                        continue
                    # store surviving line data
                    arc_pix_dat.append(peak)
                    arc_int_dat.append(plt_line[max_index])
                    at_wave_dat.append(aw)
                    at_flux_dat.append(self.action.args.at_flux[iw])
                    # plot, if requested
                    if do_inter and ib == next_bar_to_plot:
                        ptitle = " Bar# %d - line %3d/%3d: xc = %.1f, " \
                                 "Wave = %9.2f" % \
                                 (ib, (iw + 1), len(self.action.args.at_wave),
                                  peak, aw)
                        atx0 = [
                            i for i, v in enumerate(atwave) if v >= min(wvec)
                        ][0]
                        atx1 = [
                            i for i, v in enumerate(atwave) if v >= max(wvec)
                        ][0]
                        atnorm = np.nanmax(yvec) / np.nanmax(atspec[atx0:atx1])
                        p = figure(
                            title=self.action.args.plotlabel +
                            "ATLAS/ARC LINE FITS" + ptitle,
                            x_axis_label="Wavelength (A)",
                            y_axis_label="Relative Flux",
                            plot_width=self.config.instrument.plot_width,
                            plot_height=self.config.instrument.plot_height)
                        ylim = [0, np.nanmax(yvec)]
                        p.line(atwave[atx0:atx1],
                               atspec[atx0:atx1] * atnorm,
                               color='blue',
                               legend_label='Atlas')
                        p.circle(atwave[atx0:atx1],
                                 atspec[atx0:atx1] * atnorm,
                                 color='green',
                                 legend_label='Atlas')
                        p.line([aw, aw],
                               ylim,
                               color='red',
                               legend_label='AtCntr')
                        p.x_range = Range1d(start=min(wvec), end=max(wvec))
                        p.extra_x_ranges = {
                            "pix": Range1d(start=min(xvec), end=max(xvec))
                        }
                        p.add_layout(
                            LinearAxis(x_range_name="pix",
                                       axis_label="CCD Y pix"), 'above')
                        p.line(xplot,
                               plt_line,
                               color='black',
                               legend_label='Arc',
                               x_range_name="pix")
                        p.circle(xvec,
                                 yvec,
                                 legend_label='Arc',
                                 color='red',
                                 x_range_name="pix")
                        ylim = [0, np.nanmax(plt_line)]
                        p.line([cent, cent],
                               ylim,
                               color='green',
                               legend_label='Cntr',
                               line_dash='dashed',
                               x_range_name="pix")
                        p.line([sp_pk_x, sp_pk_x],
                               ylim,
                               color='magenta',
                               legend_label='Gpeak',
                               line_dash='dashdot',
                               x_range_name="pix")
                        p.line([peak, peak],
                               ylim,
                               color='black',
                               legend_label='Peak',
                               line_dash='dashdot',
                               x_range_name="pix")
                        p.y_range.start = 0
                        bokeh_plot(p, self.context.bokeh_session)

                        q = input(ptitle + " - Next? <cr>, q to quit: ")
                        if 'Q' in q.upper():
                            do_inter = False
                except IndexError:
                    if verbose:
                        self.logger.info(
                            "Atlas line not in observation: %.2f" % aw)
                    rej_wave.append(aw)
                    rej_flux.append(self.action.args.at_flux[iw])
                    nrej += 1
                    continue
                except ValueError:
                    if verbose:
                        self.logger.info(
                            "Interpolation error for line at %.2f" % aw)
                    rej_wave.append(aw)
                    rej_flux.append(self.action.args.at_flux[iw])
                    nrej += 1
            self.logger.info("")
            self.logger.info("Fitting wavelength solution starting with %d "
                             "lines after rejecting %d lines" %
                             (len(arc_pix_dat), nrej))
            # Fit wavelengths
            # Get poly order
            if self.action.args.dichroic_fraction <= 0.6:
                poly_order = 2
            elif 0.6 < self.action.args.dichroic_fraction < 0.75:
                poly_order = 3
            else:
                poly_order = 4
            self.logger.info("Fitting with polynomial order %d" % poly_order)
            # Initial fit
            wfit = np.polyfit(arc_pix_dat, at_wave_dat, poly_order)
            pwfit = np.poly1d(wfit)
            arc_wave_fit = pwfit(arc_pix_dat)
            # fit residuals
            resid = arc_wave_fit - at_wave_dat
            resid_c, low, upp = sigmaclip(resid, low=3., high=3.)
            wsig = resid_c.std()
            # maximum outlier
            max_resid = np.max(abs(resid))
            self.logger.info("wsig: %.3f, max_resid: %.3f" % (wsig, max_resid))
            # keep track of rejected lines
            rej_rsd = []  # rejected line residuals
            rej_rsd_wave = []  # rejected line wavelengths
            rej_rsd_flux = []  # rejected line fluxes
            # iteratively remove outliers
            it = 0
            while max_resid > 2.5 * wsig and it < 25:
                arc_dat = []  # arc line pixel values
                arc_fdat = []  # arc line flux data
                at_dat = []  # atlas line wavelength values
                at_fdat = []  # atlas line flux data
                # trim largest outlier
                for il, rsd in enumerate(resid):
                    if abs(rsd) < max_resid:
                        # append data for line that passed cut
                        arc_dat.append(arc_pix_dat[il])
                        arc_fdat.append(arc_int_dat[il])
                        at_dat.append(at_wave_dat[il])
                        at_fdat.append(at_flux_dat[il])
                    else:
                        if verbose:
                            self.logger.info("It%d REJ: %d, %.2f, %.3f, %.3f" %
                                             (it, il, arc_pix_dat[il],
                                              at_wave_dat[il], rsd))
                        # keep track of rejected lines
                        rej_rsd_wave.append(at_wave_dat[il])
                        rej_rsd_flux.append(at_flux_dat[il])
                        rej_rsd.append(rsd)
                # copy cleaned data back into input arrays
                arc_pix_dat = arc_dat.copy()
                arc_int_dat = arc_fdat.copy()
                at_wave_dat = at_dat.copy()
                at_flux_dat = at_fdat.copy()
                # refit cleaned data
                wfit = np.polyfit(arc_pix_dat, at_wave_dat, poly_order)
                # new wavelength function
                pwfit = np.poly1d(wfit)
                # new wavelengths for arc lines
                arc_wave_fit = pwfit(arc_pix_dat)
                # calculate residuals of arc lines
                resid = arc_wave_fit - at_wave_dat
                # get statistics
                resid_c, low, upp = sigmaclip(resid, low=3., high=3.)
                wsig = resid_c.std()
                # maximum outlier
                max_resid = np.max(abs(resid))
                # wsig = np.nanstd(resid)
                it += 1
            # END while max_resid > 3.5 * wsig and it < 5:
            # log arc bar results
            self.logger.info("")
            self.logger.info("BAR %03d, Slice = %02d, RMS = %.3f, N = %d" %
                             (ib, int(ib / 5), wsig, len(arc_pix_dat)))
            self.logger.info("Nits: %d, wsig: %.3f, max_resid: %.3f" %
                             (it, wsig, max_resid))
            self.logger.info("NRejRsd: %d, NRejFit: %d" %
                             (len(rej_rsd_wave), len(rej_wave)))
            self.logger.info("Line width median sigma: %.2f px" %
                             np.nanmedian(gaus_sig))
            self.logger.info("Coefs: " +
                             ' '.join(['%.6g' % (c, )
                                       for c in reversed(wfit)]))
            # store final fit coefficients
            self.action.args.fincoeff.append(wfit)
            # store statistics
            bar_sig.append(wsig)
            bar_nls.append(len(arc_pix_dat))
            # do plotting?
            if master_inter and ib == next_bar_to_plot:
                # plot bar fit residuals
                ptitle = " for Bar %03d, Slice %02d, RMS = %.3f, N = %d" % \
                         (ib, int(ib / 5), wsig, len(arc_pix_dat))
                p = figure(title=self.action.args.plotlabel + "RESIDUALS" +
                           ptitle,
                           x_axis_label="Wavelength (A)",
                           y_axis_label="Fit - Inp (A)",
                           plot_width=self.config.instrument.plot_width,
                           plot_height=self.config.instrument.plot_height)
                p.diamond(at_wave_dat, resid, legend_label='Rsd', size=8)
                if rej_rsd_wave:
                    p.diamond(rej_rsd_wave,
                              rej_rsd,
                              color='orange',
                              legend_label='Rej',
                              size=8)
                xlim = [self.action.args.atminwave, self.action.args.atmaxwave]
                ylim = [
                    np.nanmin(list(resid) + list(rej_rsd)),
                    np.nanmax(list(resid) + list(rej_rsd))
                ]
                p.line(xlim, [0., 0.], color='black', line_dash='dotted')
                p.line(xlim, [wsig, wsig], color='gray', line_dash='dashdot')
                p.line(xlim, [-wsig, -wsig], color='gray', line_dash='dashdot')
                p.line([self.action.args.cwave, self.action.args.cwave],
                       ylim,
                       legend_label='CWAV',
                       color='magenta',
                       line_dash='dashdot')
                bokeh_plot(p, self.context.bokeh_session)
                input("Next? <cr>: ")

                # overplot atlas and bar using fit wavelengths
                p = figure(title=self.action.args.plotlabel + "ATLAS/ARC FIT" +
                           ptitle,
                           x_axis_label="Wavelength (A)",
                           y_axis_label="Flux",
                           plot_width=self.config.instrument.plot_width,
                           plot_height=self.config.instrument.plot_height)
                bwav = pwfit(self.action.args.xsvals)
                p.line(bwav, b, color='darkgrey', legend_label='Arc')
                p.diamond(arc_wave_fit, arc_int_dat, color='darkgrey', size=8)
                ylim = [np.nanmin(b), np.nanmax(b)]
                atnorm = np.nanmax(b) / np.nanmax(atspec)
                p.line(atwave,
                       atspec * atnorm,
                       color='blue',
                       legend_label='Atlas')
                p.line([self.action.args.cwave, self.action.args.cwave],
                       ylim,
                       color='magenta',
                       line_dash='dashdot',
                       legend_label='CWAV')
                p.diamond(at_wave,
                          at_flux * atnorm,
                          legend_label='Kept',
                          color='green',
                          size=8)
                if rej_rsd_wave:
                    p.diamond(rej_rsd_wave,
                              [rj * atnorm for rj in rej_rsd_flux],
                              color='orange',
                              legend_label='RejRsd',
                              size=6)
                p.diamond(rej_wave, [rj * atnorm for rj in rej_flux],
                          color='red',
                          legend_label='RejFit',
                          size=6)
                bokeh_plot(p, self.context.bokeh_session)
                q = input("Next? <int> or <cr>, q - quit: ")
                if 'Q' in q.upper():
                    master_inter = False
                else:
                    try:
                        next_bar_to_plot = int(q)
                    except ValueError:
                        next_bar_to_plot = ib + 1

        # Plot final results

        # plot output name stub
        pfname = "arc_%05d_%s_%s_%s_tf%02d" % (
            self.action.args.ccddata.header['FRAMENO'], self.action.args.illum,
            self.action.args.grating, self.action.args.ifuname,
            int(100 * self.config.instrument.TAPERFRAC))

        # Plot coefs
        if self.config.instrument.plot_level >= 1:
            ylabs = ['Ang/px^4', 'Ang/px^3', 'Ang/px^2', 'Ang/px', 'Ang']
            ylabs = ylabs[-(poly_order + 1):]
            for ic in reversed(range(len(self.action.args.fincoeff[0]))):
                cn = poly_order - ic
                ptitle = self.action.args.plotlabel + "COEF %d VALUES" % cn
                p = figure(title=ptitle,
                           x_axis_label="Bar #",
                           y_axis_label="Coef %d (%s)" % (cn, ylabs[ic]),
                           plot_width=self.config.instrument.plot_width,
                           plot_height=self.config.instrument.plot_height)
                coef = []
                for c in self.action.args.fincoeff:
                    coef.append(c[ic])
                p.diamond(list(range(120)), coef, size=8)
                xlim = [-1, 120]
                ylim = get_plot_lims(coef)
                p.xgrid.grid_line_color = None
                oplot_slices(p, ylim)
                set_plot_lims(p, xlim=xlim, ylim=ylim)
                bokeh_plot(p, self.context.bokeh_session)
                if self.config.instrument.plot_level >= 2:
                    input("Next? <cr>: ")
                else:
                    time.sleep(self.config.instrument.plot_pause)
                # save coefficients plot
                save_plot(p, filename=pfname + '_coef%d.png' % cn)

        # Plot number of lines fit
        self.action.args.av_bar_nls = float(np.nanmean(bar_nls))
        self.action.args.st_bar_nls = float(np.nanstd(bar_nls))
        ptitle = self.action.args.plotlabel + \
            "FIT STATS <Nlns> = %.1f +- %.1f" % (self.action.args.av_bar_nls,
                                                 self.action.args.st_bar_nls)
        p = figure(title=ptitle,
                   x_axis_label="Bar #",
                   y_axis_label="N Lines",
                   plot_width=self.config.instrument.plot_width,
                   plot_height=self.config.instrument.plot_height)
        p.diamond(list(range(120)), bar_nls, size=8)
        xlim = [-1, 120]
        ylim = get_plot_lims(bar_nls)
        self.logger.info(
            "<N Lines> = %.1f +- %.1f" %
            (self.action.args.av_bar_nls, self.action.args.st_bar_nls))
        p.line(xlim,
               [self.action.args.av_bar_nls, self.action.args.av_bar_nls],
               color='red')
        p.line(xlim,
               [(self.action.args.av_bar_nls - self.action.args.st_bar_nls),
                (self.action.args.av_bar_nls - self.action.args.st_bar_nls)],
               color='green',
               line_dash='dashed')
        p.line(xlim,
               [(self.action.args.av_bar_nls + self.action.args.st_bar_nls),
                (self.action.args.av_bar_nls + self.action.args.st_bar_nls)],
               color='green',
               line_dash='dashed')
        p.xgrid.grid_line_color = None
        oplot_slices(p, ylim)
        set_plot_lims(p, xlim=xlim, ylim=ylim)
        if self.config.instrument.plot_level >= 1:
            bokeh_plot(p, self.context.bokeh_session)
            if self.config.instrument.plot_level >= 2:
                input("Next? <cr>: ")
            else:
                time.sleep(self.config.instrument.plot_pause)
        # save N lines plot
        save_plot(p, filename=pfname + '_nlines.png')

        # Plot fit sigmas
        self.action.args.av_bar_sig = float(np.nanmean(bar_sig))
        self.action.args.st_bar_sig = float(np.nanstd(bar_sig))
        self.logger.info(
            "<STD>     = %.3f +- %.3f (A)" %
            (self.action.args.av_bar_sig, self.action.args.st_bar_sig))

        ptitle = self.action.args.plotlabel + \
            "FIT STATS <RMS> = %.3f +- %.3f" % (self.action.args.av_bar_sig,
                                                self.action.args.st_bar_sig)
        p = figure(title=ptitle,
                   x_axis_label="Bar #",
                   y_axis_label="RMS (A)",
                   plot_width=self.config.instrument.plot_width,
                   plot_height=self.config.instrument.plot_height)
        p.diamond(list(range(120)), bar_sig, size=8)
        xlim = [-1, 120]
        ylim = get_plot_lims(bar_sig)
        p.line(xlim,
               [self.action.args.av_bar_sig, self.action.args.av_bar_sig],
               color='red')
        p.line(xlim,
               [(self.action.args.av_bar_sig - self.action.args.st_bar_sig),
                (self.action.args.av_bar_sig - self.action.args.st_bar_sig)],
               color='green',
               line_dash='dashed')
        p.line(xlim,
               [(self.action.args.av_bar_sig + self.action.args.st_bar_sig),
                (self.action.args.av_bar_sig + self.action.args.st_bar_sig)],
               color='green',
               line_dash='dashed')
        p.xgrid.grid_line_color = None
        oplot_slices(p, ylim)
        set_plot_lims(p, xlim=xlim, ylim=ylim)
        if self.config.instrument.plot_level >= 1:
            bokeh_plot(p, self.context.bokeh_session)
            if self.config.instrument.plot_level >= 2:
                input("Next? <cr>: ")
            else:
                time.sleep(self.config.instrument.plot_pause)

        # save residual plot
        save_plot(p, filename=pfname + '_resid.png')

        log_string = SolveArcs.__module__
        self.action.args.ccddata.header['HISTORY'] = log_string
        self.logger.info(log_string)

        return self.action.args
Example #4
0
    def _perform(self):
        """At this point we have the offsets between bars and the approximate
        offset from the reference bar to the atlas spectrum and the approximate
        dispersion.
        """
        self.logger.info("Finding wavelength solution for central region")
        # Are we interactive?
        do_inter = (self.config.instrument.plot_level >= 2)

        # y binning
        y_binning = self.action.args.ybinsize
        # let's populate the 0 points vector
        p0 = self.action.args.cwave + np.array(self.context.bar_offsets) * \
            self.context.prelim_disp - self.action.args.offset_wave
        # next we are going to brute-force scan around the preliminary
        # dispersion for a better solution. We will wander 5% away from it.
        maximum_dispersion_deviation = 0.05  # fraction
        # we will try nn values
        self.logger.info("prelim disp = %.3f, refdisp = %.3f,"
                         " min,max rows = %d, %d" %
                         (self.context.prelim_disp, self.action.args.refdisp,
                          self.action.args.minrow, self.action.args.maxrow))
        number_of_values_to_try = (int(
            maximum_dispersion_deviation * abs(self.context.prelim_disp) /
            self.action.args.refdisp *
            (self.action.args.maxrow - self.action.args.minrow) / 2.0))
        if number_of_values_to_try < 10:
            number_of_values_to_try = 10
        if number_of_values_to_try > 50:
            number_of_values_to_try = 50
        self.logger.info("N disp. samples: %d" % number_of_values_to_try)
        # dispersions to try
        disps = self.context.prelim_disp * (
            1.0 + maximum_dispersion_deviation *
            (np.arange(0, number_of_values_to_try + 1) -
             number_of_values_to_try / 2.) * 2.0 / number_of_values_to_try)
        # values for central fit
        subxvals = self.action.args.xvals[self.action.args.minrow:self.action.
                                          args.maxrow]

        # log taperfrac: important!
        self.logger.info("Using TAPERFRAC = %.3f" %
                         self.config.instrument.TAPERFRAC)
        self.action.args.ccddata.header['TAPFRAC'] = (
            self.config.instrument.TAPERFRAC, "taper fraction for central fit")
        # loop over bars and assemble input arguments
        my_arguments = []
        for b, bs in enumerate(self.context.arcs):
            arguments = {
                'b': b,
                'bs': bs,
                'minrow': self.action.args.minrow,
                'maxrow': self.action.args.maxrow,
                'disps': disps,
                'p0': p0,
                'PIX': self.config.instrument.PIX,
                'ybin': y_binning,
                'rho': self.action.args.rho,
                'FCAM': self.config.instrument.FCAM,
                'xvals': self.action.args.xvals,
                'refwave': self.action.args.refwave,
                'reflux': self.action.args.reflux,
                'taperfrac': self.config.instrument.TAPERFRAC,
                'refdisp': self.action.args.refdisp,
                'subxvals': subxvals,
                'nn': number_of_values_to_try,
                'x0': self.action.args.x0
            }
            my_arguments.append(arguments)

        twkcoeff = {}
        centwave = []
        centdisp = []

        p = get_context("spawn").Pool()
        results = p.map(bar_fit_helper, list(my_arguments))
        p.close()

        next_bar_to_plot = 0
        for ir, result in enumerate(results):
            b = result[0]
            shifted_coefficients = result[1]
            _centwave = result[2]
            _centdisp = result[3]
            twkcoeff[b] = shifted_coefficients
            centwave.append(_centwave)
            centdisp.append(_centdisp)
            maxima = result[4]
            bardisp = result[5]
            self.logger.info(
                "Central Fit: Bar# %3d, Cdisp %.4f, "
                "Coefs: %.2f %.4f %13.5e %13.5e" %
                (b, bardisp, shifted_coefficients[4], shifted_coefficients[3],
                 shifted_coefficients[2], shifted_coefficients[1]))
            if do_inter and ir == next_bar_to_plot:
                # plot maxima
                p = figure(title=self.action.args.plotlabel +
                           "CENTRAL DISPERSION FIT for Bar: %d Slice: %d" %
                           (b, int(b / 5)),
                           plot_width=self.config.instrument.plot_width,
                           plot_height=self.config.instrument.plot_height,
                           x_axis_label="Central dispersion (Ang/px)",
                           y_axis_label="X-Corr Peak Value")
                p.scatter(disps, maxima, color='red', legend_label="Data")
                p.line(disps, maxima, color='blue', legend_label="Data")
                ylim = [min(maxima), max(maxima)]
                p.line([_centdisp, _centdisp],
                       ylim,
                       color='green',
                       legend_label="Fit Disp")
                p.line([self.context.prelim_disp, self.context.prelim_disp],
                       ylim,
                       color='red',
                       legend_label="Calc Disp")
                bokeh_plot(p, self.context.bokeh_session)
                q = input("Next? <int> or <cr>, q to quit: ")
                if 'Q' in q.upper():
                    do_inter = False
                else:
                    try:
                        next_bar_to_plot = int(q)
                    except ValueError:
                        next_bar_to_plot = ir + 1

        self.action.args.twkcoeff = twkcoeff
        # Plot results
        if self.config.instrument.plot_level >= 1:
            # Plot central wavelength
            p = figure(title=self.action.args.plotlabel + "CENTRAL VALUES",
                       x_axis_label="Bar #",
                       y_axis_label="Central Wavelength (A)",
                       plot_width=self.config.instrument.plot_width,
                       plot_height=self.config.instrument.plot_height)
            x = range(len(centwave))
            p.scatter(x, centwave, marker='x', legend_label='bar wave')
            p.line([0, 120], [self.action.args.cwave, self.action.args.cwave],
                   color='red',
                   legend_label='CWAVE')
            xlim = [-1, 120]
            ylim = get_plot_lims(centwave)
            p.xgrid.grid_line_color = None
            oplot_slices(p, ylim)
            p.legend.location = "top_center"
            set_plot_lims(p, xlim=xlim, ylim=ylim)
            bokeh_plot(p, self.context.bokeh_session)
            if self.config.instrument.plot_level >= 2:
                input("Next? <cr>: ")
            else:
                time.sleep(self.config.instrument.plot_pause)
            # Plot central dispersion
            p = figure(title=self.action.args.plotlabel + "CENTRAL VALUES",
                       x_axis_label="Bar #",
                       y_axis_label="Central Dispersion (A)",
                       plot_width=self.config.instrument.plot_width,
                       plot_height=self.config.instrument.plot_height)
            x = range(len(centdisp))
            p.scatter(x, centdisp, marker='x', legend_label='bar disp')
            p.line([0, 120],
                   [self.context.prelim_disp, self.context.prelim_disp],
                   color='red',
                   legend_label='Calc Disp')
            xlim = [-2, 121]
            ylim = get_plot_lims(centdisp)
            p.xgrid.grid_line_color = None
            oplot_slices(p, ylim)
            p.legend.location = "bottom_center"
            set_plot_lims(p, xlim=xlim, ylim=ylim)
            bokeh_plot(p, self.context.bokeh_session)
            if self.config.instrument.plot_level >= 2:
                input("Next? <cr>: ")
            else:
                time.sleep(self.config.instrument.plot_pause)

        log_string = FitCenter.__module__
        self.action.args.ccddata.header['HISTORY'] = log_string
        self.logger.info(log_string)

        return self.action.args