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