Пример #1
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
Пример #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
Пример #3
0
    def _perform(self):
        self.logger.info("Making inverse sensitivity curve")

        suffix = 'invsens'
        stdname = self.action.args.stdname

        # get size
        sz = self.action.args.ccddata.data.shape
        # default pixel ranges
        z = np.arange(sz[0])
        z0 = 175
        z1 = sz[0] - 175
        # get exposure time
        expt = self.action.args.ccddata.header['XPOSURE']
        if expt == 0.:
            self.logger.warning("No exposure time found, setting to 1s")
            expt = 1.
        else:
            self.logger.info("Using exposure time of %.1f" % expt)
        # get wavelength scale
        w0 = self.action.args.ccddata.header['CRVAL3']
        dw = self.action.args.ccddata.header['CD3_3']
        crpixw = self.action.args.ccddata.header['CRPIX3']
        # get all good wavelength range
        wgoo0 = self.action.args.ccddata.header['WAVGOOD0']
        if wgoo0 < 3650:
            wgoo0 = 3650.
        wgoo1 = self.action.args.ccddata.header['WAVGOOD1']
        # get all inclusive wavelength range
        wall0 = self.action.args.ccddata.header['WAVALL0']
        wall1 = self.action.args.ccddata.header['WAVALL1']
        # get DAR padding in y
        pad_y = self.action.args.ccddata.header['DARPADY']
        # get sky subtraction status
        skycor = self.action.args.ccddata.header['SKYCOR']
        # get telescope and atm. correction
        if 'TELESCOP' in self.action.args.ccddata.header:
            tel = self.action.args.ccddata.header['TELESCOP']
        else:
            tel = 'KeckI'
        if 'Keck' in tel:
            area = 760000.0
        else:
            area = -1.0
        # compute good y pixel ranges
        if w0 > 0. and dw > 0. and wgoo0 > 0. and wgoo1 > 0.:
            z0 = int((wgoo0 - w0) / dw) + 10
            z1 = int((wgoo1 - w0) / dw) - 10
        # wavelength scale
        w = w0 + z * dw
        # good spatial range
        gy0 = pad_y if pad_y > 1 else 1
        gy1 = sz[1] - (pad_y if pad_y > 2 else 2)
        # log results
        self.logger.info("Invsen. Pars: Y0, Y1, Z0, Z1, Wav0, Wav1: "
                         "%d, %d, %d, %d, %.3f, %.3f" %
                         (gy0, gy1, z0, z1, w[z0], w[z1]))
        # central wavelength
        cwv = self.action.args.cwave
        # find standard in slices
        # sum over wavelength
        tot = np.sum(self.action.args.ccddata.data[z0:z1, gy0:gy1, :], 0)
        # yy = np.arange(gy1-gy0) + gy0
        mxsl = -1.
        mxsg = 0.
        # for each slice
        for i in range(sz[2]):
            tstd = float(np.nanstd(tot[:, i]))
            if tstd > mxsg:
                mxsg = tstd
                mxsl = i

        # relevant slices
        sl0 = (mxsl - 3) if mxsl >= 3 else 0
        sl1 = (mxsl + 3) if (mxsl + 3) <= sz[2] - 1 else sz[2] - 1
        # get y position of std
        cy, _ = find_peaks(tot[:, mxsl], height=np.nanmean(tot[:, mxsl]))
        cy = int(cy[0]) + gy0
        # log results
        self.logger.info("Std lices: max, sl0, sl1, spatial cntrd: "
                         "%d, %d, %d, %.2f" % (mxsl, sl0, sl1, cy))
        # get dwave spectrum
        ofn = self.action.args.ccddata.header['OFNAME']
        delfn = ofn.split('.')[0] + '_dcubed.fits'
        full_path = os.path.join(os.path.dirname(self.action.args.name),
                                 self.config.instrument.output_directory,
                                 delfn)
        if os.path.exists(full_path):
            dew = kcwi_fits_reader(full_path)[0]
            dwspec = dew.data[:, cy, mxsl]
            zeros = np.where(dwspec == 0)
            if len(zeros) > 0:
                dwspec[zeros] = dw
        else:
            dwspec = np.zeros(sz[0]) + dw
        # copy of input cube
        scub = self.action.args.ccddata.data.copy()
        # sky window width in pixels
        skywin = int(self.config.instrument.psfwid / self.action.args.xbinsize)
        # do sky subtraction, if needed
        if not skycor:
            self.logger.warning("Sky should have been subtraced already")
        # apply extinction correction
        ucub = scub.copy()  # uncorrected cube
        kcwi_correct_extin(scub,
                           self.action.args.ccddata.header,
                           logger=self.logger)

        # get slice spectra y limits
        sy0 = (cy - skywin) if (cy - skywin) > 0 else 0
        sy1 = (cy + skywin) if (cy + skywin) < (sz[1] - 1) else (sz[1] - 1)
        # sum over y range
        slspec = np.sum(scub[:, sy0:sy1, :], 1)
        ulspec = np.sum(ucub[:, sy0:sy1, :], 1)
        # sum over slices
        obsspec = np.sum(slspec[:, sl0:sl1], 1)
        ubsspec = np.sum(ulspec[:, sl0:sl1], 1)
        # convert to e-/second
        obsspec /= expt
        ubsspec /= expt
        # check for zeros
        zeros = np.where(obsspec == 0.)
        if len(zeros) > 0:
            obsmean = np.nanmean(obsspec)
            obsspec[zeros] = obsmean
        # read in standard star spectrum
        hdul = pf.open(self.action.args.stdfile)
        swl = hdul[1].data['WAVELENGTH']
        sflx = hdul[1].data['FLUX']
        sfw = hdul[1].data['FWHM']
        hdul.close()
        # get region of interest
        sroi = [i for i, v in enumerate(swl) if w[0] <= v <= w[-1]]
        nsroi = len(sroi)
        if nsroi <= 0:
            self.logger.error("No standard wavelengths in common")
            return self.action.args
        # expand range after checking for edges
        if sroi[0] > 0:
            sroi.insert(0, sroi[0] - 1)
            nsroi += 1
        if sroi[-1] < (len(swl) - 1):
            sroi.append(sroi[-1] + 1)
            nsroi += 1
        # very sparsely sampled w.r.t. object
        if nsroi <= 1:
            self.logger.error("Not enough standard points")
            return self.action.args
        self.logger.info("Number of standard point = %d" % nsroi)
        # how many points?
        # do_line = nsroi > 20

        swl = swl[sroi]
        sflx = sflx[sroi]
        sfw = sfw[sroi]
        fwhm = np.max(sfw)
        self.logger.info("Reference spectrum FWHM used = %.1f (A)" % fwhm)
        # resample standard onto our wavelength grid
        rsint = interp1d(swl, sflx, kind='cubic', fill_value='extrapolate')
        rsflx = rsint(w)
        # get effective inverse sensitivity
        invsen = rsflx / obsspec
        # convert to photons/s/cm^2/(wl bin = dw)
        rspho = 5.03411250e7 * rsflx * w * dw
        # get effective area
        earea = ubsspec / rspho
        # correct to native bins
        earea *= dw / dwspec
        # Balmer lines
        blines = [6563., 4861., 4341., 4102., 3970., 3889., 3835.]
        # default values (for BM)
        bwid = 0.008  # fractional width to mask
        ford = 9  # fit order
        if 'BL' in self.action.args.grating:
            bwid = 0.004
            ford = 7
        elif 'BH' in self.action.args.grating:
            bwid = 0.012
            ford = 9
        # Adjust for dichroic fraction
        try:
            dichroic_fraction = self.action.args.ccddata.header['DICHFRAC']
        except KeyError:
            dichroic_fraction = 1.
        ford = int(ford * dichroic_fraction)
        if ford < 3:
            ford = 3
        self.logger.info("Fitting Invsens with polynomial order %d" % ford)
        bwids = [bl * bwid for bl in blines]
        # fit inverse sensitivity and effective area
        # get initial region of interest
        wl_good = [i for i, v in enumerate(w) if wgoo0 <= v <= wgoo1]
        nwl_good = len(wl_good)
        if nwl_good <= 0:
            self.logger.error("No good wavelengths to fit")
            return self.action.args
        wlm0 = wgoo0
        wlm1 = wgoo1
        # interactively set wavelength limits
        if self.config.instrument.plot_level >= 1:
            yran = [np.min(obsspec), np.max(obsspec)]
            source = ColumnDataSource(data=dict(x=w, y=obsspec))
            done = False
            while not done:
                p = figure(tooltips=[("x", "@x{0,0.0}"), ("y", "@y{0,0.0}")],
                           title=self.action.args.plotlabel + ' Obs Spec',
                           x_axis_label='Wave (A)',
                           y_axis_label='Intensity (e-)',
                           plot_width=self.config.instrument.plot_width,
                           plot_height=self.config.instrument.plot_height)
                p.line('x', 'y', line_color='black', source=source)
                p.line([wgoo0, wgoo0],
                       yran,
                       line_color='green',
                       legend_label='WAVGOOD')
                p.line([wgoo1, wgoo1], yran, line_color='green')
                p.line([wlm0, wlm0],
                       yran,
                       line_color='blue',
                       legend_label='LIMITS')
                p.line([wlm1, wlm1], yran, line_color='blue')
                p.line([cwv, cwv], yran, line_color='red', legend_label='CWAV')
                set_plot_lims(p, xlim=[wall0, wall1], ylim=yran)
                bokeh_plot(p, self.context.bokeh_session)
                print("WL limits: %.1f - %.1f" % (wlm0, wlm1))
                qstr = input("New? <float> <float>, <cr> - done: ")
                if len(qstr) <= 0:
                    done = True
                else:
                    try:
                        wlm0 = float(qstr.split()[0])
                        wlm1 = float(qstr.split()[1])
                        if wlm1 < wlm0 or wlm0 < wall0 or wlm1 > wall1:
                            wlm0 = wgoo0
                            wlm1 = wgoo1
                            print("range/order error, try again")
                    except (IndexError, ValueError):
                        wlm0 = wgoo0
                        wlm1 = wgoo1
                        print("format error, try again")
            # update region of interest
            wl_good = [i for i, v in enumerate(w) if wlm0 <= v <= wlm1]
            nwl_good = len(wl_good)
        # END: interactively set wavelength limits
        # Now interactively identify lines
        if self.config.instrument.plot_level >= 1:
            yran = [np.min(obsspec), np.max(obsspec)]
            # source = ColumnDataSource(data=dict(x=w, y=obsspec))
            done = False
            while not done:
                p = figure(tooltips=[("x", "@x{0.0}"), ("y", "@y{0.0}")],
                           title=self.action.args.plotlabel + ' Obs Spec',
                           x_axis_label='Wave (A)',
                           y_axis_label='Intensity (e-)',
                           plot_width=self.config.instrument.plot_width,
                           plot_height=self.config.instrument.plot_height)
                p.line(w, obsspec, line_color='black')
                p.line([wgoo0, wgoo0],
                       yran,
                       line_color='green',
                       legend_label='WAVGOOD')
                p.line([wgoo1, wgoo1], yran, line_color='green')
                p.line([wlm0, wlm0],
                       yran,
                       line_color='blue',
                       legend_label='LIMITS')
                p.line([wlm1, wlm1], yran, line_color='blue')
                p.line([cwv, cwv], yran, line_color='red', legend_label='CWAV')
                for il, bl in enumerate(blines):
                    if wall0 < bl < wall1:
                        p.line([bl, bl], yran, line_color='orange')
                        p.line([bl - bwids[il], bl - bwids[il]],
                               yran,
                               line_color='orange',
                               line_dash='dashed')
                        p.line([bl + bwids[il], bl + bwids[il]],
                               yran,
                               line_color='orange',
                               line_dash='dashed')
                set_plot_lims(p, xlim=[wall0, wall1], ylim=yran)
                bokeh_plot(p, self.context.bokeh_session)
                qstr = input("New lines? <float> [<float>] ... (A), "
                             "<cr> - done: ")
                if len(qstr) <= 0:
                    done = True
                else:
                    for lstr in qstr.split():
                        try:
                            new_line = float(lstr)
                        except ValueError:
                            print("bad line: %s" % lstr)
                            continue
                        if wlm0 < new_line < wlm1:
                            blines.append(new_line)
                            bwids.append(bwid * new_line)
                        else:
                            print("line outside range: %s" % lstr)
        # END: interactively identify lines
        # set up fitting vectors, flux, waves, measure errors
        sf = invsen[wl_good]  # dependent variable
        af = earea[wl_good]  # effective area
        wf = w[wl_good]  # independent variable
        bf = obsspec[wl_good]  # input electrons
        rf = rsflx[wl_good]  # reference flux
        mw = np.ones(nwl_good, dtype=float)  # weights
        use = np.ones(nwl_good, dtype=int)  # toggles for usage
        # loop over Balmer lines
        for il, bl in enumerate(blines):
            roi = [
                i for i, v in enumerate(wf)
                if (bl - bwids[il]) <= v <= (bl + bwids[il])
            ]
            nroi = len(roi)
            if nroi > 0:
                use[roi] = 0
                self.logger.info("Masking line at %.1f +- %.4f (A)" %
                                 (bl, bwids[il]))
        # ignore bad points by setting large errors
        mf = []
        ef = []
        ww = []
        used = []
        not_used = []
        for i in range(len(use)):
            if use[i] == 1:
                used.append(i)
            else:
                mw[i] = 1.e-9
                mf.append(sf[i])
                ef.append(100. * af[i] / area)
                ww.append(wf[i])
                not_used.append(i)

        # initial polynomial fit of inverse sensitivity
        wf0 = np.min(wf)
        res = np.polyfit(wf - wf0, sf, deg=ford, w=mw)
        finvsen = np.polyval(res, w - wf0)
        sinvsen = np.polyval(res, wf - wf0)
        calspec = obsspec * finvsen
        scalspec = bf * sinvsen
        # initial polynomial fit of effective area
        res = np.polyfit(wf - wf0, af, ford, w=mw)
        fearea = np.polyval(res, w - wf0)
        # calculate residuals
        resid = 100.0 * (scalspec - rf) / rf
        if len(not_used) > 0:
            rbad = resid[not_used]
        else:
            rbad = None
        rsd_mean = float(np.nanmean(resid[used]))
        rsd_stdv = float(np.nanstd(resid[used]))
        self.logger.info("Calibration residuals = %f +- %f %%" %
                         (rsd_mean, rsd_stdv))
        # plots
        peff = None
        pivs = None
        pcal = None
        prsd = None
        # interactively adjust fit
        if self.config.instrument.plot_level >= 1:
            done = False
            while not done:
                yran = [np.min(100. * af / area), np.max(100. * af / area)]
                effmax = np.nanmax(100. * fearea / area)
                effmean = np.nanmean(100. * fearea / area)
                peff = figure(title=self.action.args.plotlabel + ' Efficiency',
                              x_axis_label='Wave (A)',
                              y_axis_label='Effective Efficiency (%)',
                              plot_width=self.config.instrument.plot_width,
                              plot_height=self.config.instrument.plot_height)
                peff.line(wf,
                          100. * af / area,
                          line_color='black',
                          legend_label='Data')
                peff.line(w,
                          100. * fearea / area,
                          line_color='red',
                          legend_label='Fit')
                peff.scatter(ww, ef, marker='x', legend_label='Rej')
                peff.line([wlm0, wlm0],
                          yran,
                          line_color='green',
                          legend_label='WL lims')
                peff.line([wlm1, wlm1], yran, line_color='green')
                peff.line([wall0, wall1], [effmax, effmax],
                          line_color='black',
                          line_dash='dashed')
                peff.line([wall0, wall1], [effmean, effmean],
                          line_color='black',
                          line_dash='dashdot')
                set_plot_lims(peff, xlim=[wall0, wall1], ylim=yran)
                bokeh_plot(peff, self.context.bokeh_session)
                if self.config.instrument.plot_level >= 1:
                    input("Next? <cr>: ")
                else:
                    time.sleep(2. * self.config.instrument.plot_pause)

                yran = [np.min(sf), np.max(sf)]
                pivs = figure(title=self.action.args.plotlabel +
                              ' Inverse sensitivity',
                              x_axis_label='Wave (A)',
                              y_axis_label='Invserse Sensitivity (Flux/e-/s)',
                              y_axis_type='log',
                              plot_width=self.config.instrument.plot_width,
                              plot_height=self.config.instrument.plot_height)
                pivs.line(wf, sf, line_color='black', legend_label='Data')
                pivs.line(w, finvsen, line_color='red', legend_label='Fit')
                pivs.scatter(ww, mf, marker='x', legend_label='Rej')
                pivs.line([wlm0, wlm0],
                          yran,
                          line_color='green',
                          legend_label='WL lims')
                pivs.line([wlm1, wlm1], yran, line_color='green')
                set_plot_lims(pivs, xlim=[wall0, wall1], ylim=yran)
                bokeh_plot(pivs, self.context.bokeh_session)
                if self.config.instrument.plot_level >= 1:
                    input("Next? <cr>: ")
                else:
                    time.sleep(2. * self.config.instrument.plot_pause)

                yran = [np.min(calspec[wl_good]), np.max(calspec[wl_good])]
                pcal = figure(title=self.action.args.plotlabel + ' Calibrated',
                              x_axis_label='Wave (A)',
                              y_axis_label='Flux (ergs/s/cm^2/A)',
                              plot_width=self.config.instrument.plot_width,
                              plot_height=self.config.instrument.plot_height)
                pcal.line(w, calspec, line_color='black', legend_label='Obs')
                pcal.line(w, rsflx, line_color='red', legend_label='Ref')
                pcal.line([wlm0, wlm0],
                          yran,
                          line_color='green',
                          legend_label='WL lims')
                pcal.line([wlm1, wlm1], yran, line_color='green')
                set_plot_lims(pcal, xlim=[wall0, wall1], ylim=yran)
                bokeh_plot(pcal, self.context.bokeh_session)
                if self.config.instrument.plot_level >= 1:
                    input("Next? <cr>: ")
                else:
                    time.sleep(2. * self.config.instrument.plot_pause)

                yran = [np.min(resid), np.max(resid)]
                prsd = figure(title=self.action.args.plotlabel +
                              ' Residuals = %.1f +- %.1f (%%)' %
                              (rsd_mean, rsd_stdv),
                              x_axis_label='Wave (A)',
                              y_axis_label='Obs - Ref / Ref (%)',
                              plot_width=self.config.instrument.plot_width,
                              plot_height=self.config.instrument.plot_height)
                prsd.line(wf,
                          resid,
                          line_color='black',
                          legend_label='Obs - Ref (%)')
                if len(not_used) > 0:
                    prsd.scatter(ww, rbad, marker='x', legend_label='Rej')
                prsd.line([wlm0, wlm0],
                          yran,
                          line_color='green',
                          legend_label='WL lims')
                prsd.line([wlm1, wlm1], yran, line_color='green')
                prsd.line([wall0, wall1], [rsd_mean, rsd_mean],
                          line_color='red')
                prsd.line([wall0, wall1],
                          [rsd_mean + rsd_stdv, rsd_mean + rsd_stdv],
                          line_color='black',
                          line_dash='dashed')
                prsd.line([wall0, wall1],
                          [rsd_mean - rsd_stdv, rsd_mean - rsd_stdv],
                          line_color='black',
                          line_dash='dashed')
                set_plot_lims(prsd, xlim=[wall0, wall1], ylim=yran)
                bokeh_plot(prsd, self.context.bokeh_session)
                if self.config.instrument.plot_level >= 1:
                    qstr = input("Current fit order = %d, "
                                 "New fit order? <int>, <cr> - done: " % ford)
                    if len(qstr) <= 0:
                        done = True
                    else:
                        try:
                            ford = int(qstr)
                            # update fit of inverse sensitivity
                            res = np.polyfit(wf - wf0, sf, deg=ford, w=mw)
                            finvsen = np.polyval(res, w - wf0)
                            sinvsen = np.polyval(res, wf - wf0)
                            calspec = obsspec * finvsen
                            scalspec = bf * sinvsen
                            # update polynomial fit of effective area
                            res = np.polyfit(wf - wf0, af, ford, w=mw)
                            fearea = np.polyval(res, w - wf0)
                            # re-calculate residuals
                            resid = 100.0 * (scalspec - rf) / rf
                            if len(not_used) > 0:
                                rbad = resid[not_used]
                            else:
                                rbad = None
                            rsd_mean = float(np.nanmean(resid[used]))
                            rsd_stdv = float(np.nanstd(resid[used]))
                            self.logger.info(
                                "Calibration residuals = %f +- %f %%" %
                                (rsd_mean, rsd_stdv))
                        except ValueError:
                            print("Bad fit order, try again")
                else:
                    done = True
                    time.sleep(2. * self.config.instrument.plot_pause)
            # log results
            effmax = float(np.nanmax(100. * fearea / area))
            effmean = float(np.nanmean(100. * fearea / area))
            self.logger.info("Peak, mean efficiency (%%): %.1f, %.1f" %
                             (effmax, effmean))
            self.logger.info("Fit order = %d" % ford)
            # output plots
            pfname = "std_%05d_%s_%s_%s_%d" % (
                self.action.args.ccddata.header['FRAMENO'], stdname,
                self.action.args.grating.strip(),
                self.action.args.ifuname.strip(), int(self.action.args.cwave))
            # Save plots
            save_plot(peff, filename=pfname + '_eff.png')  # Efficiency
            save_plot(pivs, filename=pfname + '_invsens.png')  # Inv. Sens.
            save_plot(prsd, filename=pfname + '_resid.png')  # Residuals
            save_plot(pcal, filename=pfname + '_cal.png')  # Calibrated

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

        # write out effective inverse sensitivity

        # update inverse sensitivity header
        hdr = self.action.args.ccddata.header.copy()
        hdr['HISTORY'] = log_string
        hdr['INVSENS'] = (True, 'effective inv. sens. spectrum?')
        hdr['INVSW0'] = (w[z0], 'low wavelength for eff. inv. sens.')
        hdr['INVSW1'] = (w[z1], 'high wavelength for eff. inv. sens.')
        hdr['INVSZ0'] = (z0, 'low wave pixel for eff. inv. sens.')
        hdr['INVSZ1'] = (z1, 'high wave pixel for eff. inv. sens.')
        hdr['INVSY0'] = (gy0, 'low spatial pixel for eff. inv. sens.')
        hdr['INVSY1'] = (gy1, 'high spatial pixel for eff. inv. sens.')
        hdr['INVSLMX'] = (mxsl, 'brightest std star slice')
        hdr['INVSL0'] = (sl0, 'lowest std star slice summed')
        hdr['INVSL1'] = (sl1, 'highest std star slice summed')
        hdr['INVSLY'] = (cy, 'spatial pixel position of std within slice')
        hdr['INVFW0'] = (wlm0, 'low wavelength for fits')
        hdr['INVFW1'] = (wlm1, 'high wavelength for fits')
        hdr['INVFORD'] = (ford, 'fit order')
        hdr['EXPTIME'] = (1., 'effective exposure time (seconds)')
        hdr['XPOSURE'] = (1., 'effective exposure time (seconds)')

        # remove old WCS
        del hdr['RADESYS']
        del hdr['EQUINOX']
        del hdr['LONPOLE']
        del hdr['LATPOLE']
        del hdr['NAXIS2']
        if 'NAXIS3' in hdr:
            del hdr['NAXIS3']
        del hdr['CTYPE1']
        del hdr['CTYPE2']
        del hdr['CTYPE3']
        del hdr['CUNIT1']
        del hdr['CUNIT2']
        del hdr['CUNIT3']
        del hdr['CNAME1']
        del hdr['CNAME2']
        del hdr['CNAME3']
        del hdr['CRVAL1']
        del hdr['CRVAL2']
        del hdr['CRVAL3']
        del hdr['CRPIX1']
        del hdr['CRPIX2']
        del hdr['CRPIX3']
        del hdr['CD1_1']
        del hdr['CD1_2']
        del hdr['CD2_1']
        del hdr['CD2_2']
        del hdr['CD3_3']

        # set wavelength axis WCS values
        hdr['WCSDIM'] = 1
        hdr['CTYPE1'] = ('AWAV', 'Air Wavelengths')
        hdr['CUNIT1'] = ('Angstrom', 'Wavelength units')
        hdr['CNAME1'] = ('KCWI INVSENS Wavelength', 'Wavelength name')
        hdr['CRVAL1'] = (w0, 'Wavelength zeropoint')
        hdr['CRPIX1'] = (crpixw, 'Wavelength reference pixel')
        hdr['CDELT1'] = (dw, 'Wavelength Angstroms per pixel')

        ofn = self.action.args.ccddata.header['OFNAME']
        invsname = ofn.split('.fits')[0] + '_' + suffix + '.fits'
        eaname = ofn.split('.fits')[0] + '_ea.fits'

        # set units
        invsens_u = u.erg / (u.angstrom * u.cm**2 * u.s * u.electron)
        # output inverse sensitivity
        out_invsens = CCDData(np.asarray([invsen, finvsen, obsspec]),
                              meta=hdr,
                              unit=invsens_u)
        kcwi_fits_writer(out_invsens,
                         output_file=invsname,
                         output_dir=self.config.instrument.output_directory)
        self.context.proctab.update_proctab(frame=out_invsens,
                                            suffix=suffix,
                                            newtype='INVSENS')
        # output effective area
        ea_u = u.cm**2 / u.angstrom
        out_ea = CCDData(np.asarray([earea, fearea]), meta=hdr, unit=ea_u)
        kcwi_fits_writer(out_ea,
                         output_file=eaname,
                         output_dir=self.config.instrument.output_directory)
        self.context.proctab.update_proctab(frame=out_ea,
                                            suffix='ea',
                                            newtype='EAREA')

        return self.action.args
Пример #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