def _perform(self): """ Returns an Argument() with the parameters that depends on this operation """ self.logger.info("Creating master illumination correction") suffix = self.action.args.new_type.lower() insuff = self.action.args.stack_type.lower() stack_list = list(self.stack_list['filename']) if len(stack_list) <= 0: self.logger.warning("No flats found!") return self.action.args # get root for maps tab = self.context.proctab.search_proctab( frame=self.action.args.ccddata, target_type='ARCLAMP', target_group=self.action.args.groupid) if len(tab) <= 0: self.logger.error("Geometry not solved!") return self.action.args mroot = strip_fname(tab['filename'][-1]) # Wavelength map image wmf = mroot + '_wavemap.fits' self.logger.info("Reading image: %s" % wmf) wavemap = kcwi_fits_reader( os.path.join(self.config.instrument.cwd, 'redux', wmf))[0] # Slice map image slf = mroot + '_slicemap.fits' self.logger.info("Reading image: %s" % slf) slicemap = kcwi_fits_reader(os.path.join( self.config.instrument.cwd, 'redux', slf))[0] # Position map image pof = mroot + '_posmap.fits' self.logger.info("Reading image: %s" % pof) posmap = kcwi_fits_reader(os.path.join( self.config.instrument.cwd, 'redux', pof))[0] # Read in stacked flat image stname = strip_fname(stack_list[0]) + '_' + insuff + '.fits' self.logger.info("Reading image: %s" % stname) stacked = kcwi_fits_reader(os.path.join( self.config.instrument.cwd, 'redux', stname))[0] # get type of flat internal = ('SFLAT' in stacked.header['IMTYPE']) twiflat = ('STWIF' in stacked.header['IMTYPE']) domeflat = ('SDOME' in stacked.header['IMTYPE']) if internal: self.logger.info("Internal Flat") elif twiflat: self.logger.info("Twilight Flat") elif domeflat: self.logger.info("Dome Flat") else: self.logger.error("Flat of Unknown Type!") return self.action.args # knots per pixel knotspp = self.config.instrument.KNOTSPP # get image size ny = stacked.header['NAXIS2'] # get binning xbin = self.action.args.xbinsize # Parameters for fitting # vignetted slice position range fitl = int(4/xbin) fitr = int(24/xbin) # un-vignetted slice position range flatl = int(34/xbin) flatr = int(72/xbin) # flat fitting slice position range ffleft = int(10/xbin) ffright = int(70/xbin) nrefx = int(ffright - ffleft) buffer = 6.0/float(xbin) # reference slice refslice = 9 allidx = np.arange(int(140/xbin)) newflat = stacked.data.copy() # dichroic fraction try: dichroic_fraction = wavemap.header['DICHFRAC'] except KeyError: dichroic_fraction = 1. # get reference slice data q = [i for i, v in enumerate(slicemap.data.flat) if v == refslice] # get wavelength limits waves = wavemap.data.compress((wavemap.data > 0.).flat) waves = [waves.min(), waves.max()] self.logger.info("Wavelength limits: %.1f - %1.f" % (waves[0], waves[1])) # correct vignetting if we are using internal flats if internal: self.logger.info("Internal flats require vignetting correction") # get good region for fitting if self.action.args.camera == 0: # Blue wmin = waves[0] wmax = min([waves[1], 5620.]) elif self.action.args.camera == 1: # Red wmin = max([waves[0], 5580.]) wmax = waves[1] else: self.logger.warning("Camera keyword not defined") wmin = waves[0] wmax = waves[1] dw = (wmax - wmin) / 30.0 wavemin = (wmin+wmax) / 2.0 - dw wavemax = (wmin+wmax) / 2.0 + dw self.logger.info("Using %.1f - %.1f A of slice %d" % (wavemin, wavemax, refslice)) xflat = [] yflat = [] wflat = [] qq = [] for i in q: if wavemin < wavemap.data.flat[i] < wavemax: xflat.append(posmap.data.flat[i]) yflat.append(stacked.data.flat[i]) wflat.append(wavemap.data.flat[i]) qq.append(i) # get un-vignetted portion qflat = [i for i, v in enumerate(xflat) if flatl <= v <= flatr] xflat = [xflat[i] for i in qflat] yflat = [yflat[i] for i in qflat] wflat = [wflat[i] for i in qflat] # sort on wavelength sw = np.argsort(wflat) ywflat = [yflat[i] for i in sw] wwflat = [wflat[i] for i in sw] ww0 = np.min(wwflat) # fit wavelength slope wavelinfit = np.polyfit(wwflat-ww0, ywflat, 2) wslfit = np.polyval(wavelinfit, wflat-ww0) # plot slope fit if self.config.instrument.plot_level >= 1: p = figure(title=self.action.args.plotlabel + ' WAVE SLOPE FIT', x_axis_label='wave px', y_axis_label='counts', plot_width=self.config.instrument.plot_width, plot_height=self.config.instrument.plot_height) p.circle(wwflat, ywflat, legend_label="Data") p.line(wflat, wslfit, line_color='red', line_width=3, legend_label="Fit") bokeh_plot(p, self.context.bokeh_session) if self.config.instrument.plot_level >= 2: input("Next? <cr>: ") else: time.sleep(self.config.instrument.plot_pause) # take out slope yflat = yflat / wslfit # now sort on slice position ss = np.argsort(xflat) xflat = [xflat[i] for i in ss] yflat = [yflat[i] for i in ss] # fit un-vignetted slope resflat = np.polyfit(xflat, yflat, 1) # select the points we will fit for the vignetting # get reference region xfit = [posmap.data.flat[i] for i in qq] yfit = [stacked.data.flat[i] for i in qq] wflat = [wavemap.data.flat[i] for i in qq] # take out wavelength slope yfit = yfit / np.polyval(wavelinfit, wflat-ww0) # select the vignetted region qfit = [i for i, v in enumerate(xfit) if fitl <= v <= fitr] xfit = [xfit[i] for i in qfit] yfit = [yfit[i] for i in qfit] # sort on slice position s = np.argsort(xfit) xfit = [xfit[i] for i in s] yfit = [yfit[i] for i in s] # fit vignetted slope resfit = np.polyfit(xfit, yfit, 1) # corrected data ycdata = stacked.data.flat[qq] / \ np.polyval(wavelinfit, wavemap.data.flat[qq]-ww0) ycmin = 0.5 # np.min(ycdata) ycmax = 1.25 # np.max(ycdata) # compute the intersection xinter = -(resflat[1] - resfit[1]) / (resflat[0] - resfit[0]) # plot slice profile and fits if self.config.instrument.plot_level >= 1: p = figure(title=self.action.args.plotlabel + ' Vignetting', x_axis_label='Slice Pos (px)', y_axis_label='Ratio', plot_width=self.config.instrument.plot_width, plot_height=self.config.instrument.plot_height) p.circle(posmap.data.flat[qq], ycdata, legend_label='Data') p.line(allidx, resfit[1] + resfit[0]*allidx, line_color='purple', legend_label='Vign.') p.line(allidx, resflat[1] + resflat[0]*allidx, line_color='red', legend_label='UnVign.') p.line([fitl, fitl], [ycmin, ycmax], line_color='blue') p.line([fitr, fitr], [ycmin, ycmax], line_color='blue') p.line([flatl, flatl], [ycmin, ycmax], line_color='green') p.line([flatr, flatr], [ycmin, ycmax], line_color='green') p.line([xinter-buffer, xinter-buffer], [ycmin, ycmax], line_color='black') p.line([xinter + buffer, xinter + buffer], [ycmin, ycmax], line_color='black') p.line([xinter, xinter], [ycmin, ycmax], line_color='red') p.y_range = Range1d(ycmin, ycmax) bokeh_plot(p, self.context.bokeh_session) if self.config.instrument.plot_level >= 2: input("Next? <cr>: ") else: time.sleep(self.config.instrument.plot_pause) # figure out where the correction applies qcor = [i for i, v in enumerate(posmap.data.flat) if 0 <= v <= (xinter-buffer)] # apply the correction! self.logger.info("Applying vignetting correction...") for i in qcor: newflat.flat[i] = (resflat[1]+resflat[0]*posmap.data.flat[i]) \ / (resfit[1]+resfit[0]*posmap.data.flat[i]) * \ stacked.data.flat[i] # now deal with the intermediate (buffer) region self.logger.info("Done, now handling buffer region") # get buffer points to fit in reference region qbff = [i for i in qq if (xinter-buffer) <= posmap.data.flat[i] <= (xinter+buffer)] # get slice pos and data for buffer fitting xbuff = [posmap.data.flat[i] for i in qbff] ybuff = [stacked.data.flat[i] / np.polyval(wavelinfit, wavemap.data.flat[i]-ww0) for i in qbff] # sort on slice position ssp = np.argsort(xbuff) xbuff = [xbuff[i] for i in ssp] ybuff = [ybuff[i] for i in ssp] # fit buffer with low-order poly buffit = np.polyfit(xbuff, ybuff, 3) # plot buffer fit if self.config.instrument.plot_level >= 1: p = figure(title=self.action.args.plotlabel + ' Buffer Region', x_axis_label='Slice Pos (px)', y_axis_label='Ratio', plot_width=self.config.instrument.plot_width, plot_height=self.config.instrument.plot_height) p.circle(xbuff, ybuff) p.line(xbuff, np.polyval(buffit, xbuff), line_color='red') bokeh_plot(p, self.context.bokeh_session) if self.config.instrument.plot_level >= 2: input("Next? <cr>: ") else: time.sleep(self.config.instrument.plot_pause) # get all buffer points in image qbuf = [i for i, v in enumerate(posmap.data.flat) if (xinter-buffer) <= v <= (xinter+buffer)] # apply buffer correction to all buffer points in newflat for i in qbuf: newflat.flat[i] = \ (resflat[1] + resflat[0] * posmap.data.flat[i]) / \ np.polyval(buffit, posmap.data.flat[i]) * newflat.flat[i] self.logger.info("Vignetting correction complete.") self.logger.info("Fitting master illumination") # now fit master flat # get reference slice points qref = [i for i in q if ffleft <= posmap.data.flat[i] <= ffright] xfr = wavemap.data.flat[qref] yfr = newflat.flat[qref] # sort on wavelength s = np.argsort(xfr) xfr = xfr[s] yfr = yfr[s] wavegood0 = wavemap.header['WAVGOOD0'] wavegood1 = wavemap.header['WAVGOOD1'] # correction for BM where we see a ledge if 'BM' in self.action.args.grating: ledge_wave = bm_ledge_position(self.action.args.cwave) self.logger.info("BM ledge calculated wavelength " "for ref slice = %.2f (A)" % ledge_wave) if wavegood0 <= ledge_wave <= wavegood1: self.logger.info("BM grating requires correction") qledge = [i for i, v in enumerate(xfr) if ledge_wave-25 <= v <= ledge_wave+25] xledge = [xfr[i] for i in qledge] yledge = [yfr[i] for i in qledge] s = np.argsort(xledge) xledge = [xledge[i] for i in s] yledge = [yledge[i] for i in s] win = boxcar(250) smyledge = sp.signal.convolve(yledge, win, mode='same') / sum(win) ylmax = np.max(yledge) ylmin = np.min(yledge) fpoints = np.arange(0, 100) / 100. * 50 + (ledge_wave-25) ledgefit, ledgemsk = Bspline.iterfit(np.asarray(xledge), smyledge, fullbkpt=fpoints, upper=1, lower=1) ylfit, _ = ledgefit.value(np.asarray(fpoints)) deriv = -(np.roll(ylfit, 1) - np.roll(ylfit, -1)) / 2.0 # trim edges trm = int(len(deriv)/5) deriv = deriv[trm:-trm] xvals = fpoints[trm:-trm] peaks, _ = find_peaks(deriv, height=100) if len(peaks) != 1: self.logger.warning("Extra peak found!") p = figure(title=self.action.args.plotlabel + ' Ledge', x_axis_label='Wavelength (A)', y_axis_label='Value', plot_width=self.config.instrument.plot_width, plot_height=self.config.instrument.plot_height) p.circle(xledge, smyledge, fill_color='green') p.line(fpoints, ylfit) bokeh_plot(p, self.context.bokeh_session) input("Next? <cr>: ") p = figure(title=self.action.args.plotlabel + ' Deriv', x_axis_label='px', y_axis_label='Value', plot_width=self.config.instrument.plot_width, plot_height=self.config.instrument.plot_height) xx = list(range(len(deriv))) ylim = get_plot_lims(deriv) p.circle(xx, deriv) for pk in peaks: p.line([pk, pk], ylim) bokeh_plot(p, self.context.bokeh_session) print("Please indicate the integer pixel value of the peak") ipk = int(input("Peak? <int>: ")) else: ipk = peaks[0] apk = xvals[ipk] if self.config.instrument.plot_level >= 3: p = figure( title=self.action.args.plotlabel + ' Peak of ledge', x_axis_label='Wave (A)', y_axis_label='Value', plot_width=self.config.instrument.plot_width, plot_height=self.config.instrument.plot_height) p.circle(xvals, deriv, legend_label='Data') p.line([apk, apk], [-50, 200], line_color='red', legend_label='Pk') bokeh_plot(p, self.context.bokeh_session) if self.config.instrument.plot_level >= 2: input("Next? <cr>: ") else: time.sleep(self.config.instrument.plot_pause) xlow = apk - 3 - 5 xhi = apk - 3 zlow = apk + 3 zhi = apk + 3 + 5 qlow = [i for i, v in enumerate(fpoints) if xlow <= v <= xhi] xlf = np.asarray([fpoints[i] for i in qlow]) ylf = np.asarray([ylfit[i] for i in qlow]) lowfit = np.polyfit(xlf, ylf, 1) qhi = [i for i, v in enumerate(fpoints) if zlow <= v <= zhi] xlf = np.asarray([fpoints[i] for i in qhi]) ylf = np.asarray([ylfit[i] for i in qhi]) hifit = np.polyfit(xlf, ylf, 1) ratio = (hifit[1] + hifit[0] * apk) / \ (lowfit[1] + lowfit[0] * apk) self.logger.info("BM ledge ratio: %.3f" % ratio) # correct flat data qcorr = [i for i, v in enumerate(xfr) if v >= apk] for i in qcorr: yfr[i] /= ratio # plot BM ledge if self.config.instrument.plot_level >= 1: p = figure( title=self.action.args.plotlabel + ' BM Ledge Region', x_axis_label='Wave (A)', y_axis_label='Value', plot_width=self.config.instrument.plot_width, plot_height=self.config.instrument.plot_height) # Input data p.circle(xledge, yledge, fill_color='blue', legend_label='Data') # correct input data qcorrect = [i for i, v in enumerate(xledge) if v >= apk] xplt = [] yplt = [] for i in qcorrect: xplt.append(xledge[i]) yplt.append(yledge[i] / ratio) p.circle(xplt, yplt, fill_color='orange', legend_label='Corrected') p.line(fpoints, ylfit, line_color='red', legend_label='Fit') p.line([xlow, xlow], [ylmin, ylmax], line_color='blue') p.line([xhi, xhi], [ylmin, ylmax], line_color='blue') p.line([zlow, zlow], [ylmin, ylmax], line_color='black') p.line([zhi, zhi], [ylmin, ylmax], line_color='black') p.line(fpoints, lowfit[1] + lowfit[0] * fpoints, line_color='purple') p.line(fpoints, hifit[1] + hifit[0] * fpoints, line_color='green') p.line([apk, apk], [ylmin, ylmax], line_color='green', legend_label='Pk') p.y_range = Range1d(ylmin, ylmax) p.legend.location = 'top_left' bokeh_plot(p, self.context.bokeh_session) if self.config.instrument.plot_level >= 2: input("Next? <cr>: ") else: time.sleep(self.config.instrument.plot_pause) # END: handling BM grating ledge # if we are fitting a twilight flat, treat it like a sky image with a # larger number of knots if twiflat: knots = int(ny * knotspp) else: knots = 100 self.logger.info("Using %d knots for bspline fit" % knots) # generate a fit from ref slice points bkpt = np.min(xfr) + np.arange(knots+1) * \ (np.max(xfr) - np.min(xfr)) / knots sftr, _ = Bspline.iterfit(xfr[nrefx:-nrefx], yfr[nrefx:-nrefx], fullbkpt=bkpt) yfitr, _ = sftr.value(xfr) # generate a blue slice spectrum bspline fit blueslice = 12 blueleft = 60 / xbin blueright = 80 / xbin qb = [i for i, v in enumerate(slicemap.data.flat) if v == blueslice] qblue = [i for i in qb if blueleft <= posmap.data.flat[i] <= blueright] xfb = wavemap.data.flat[qblue] yfb = newflat.flat[qblue] s = np.argsort(xfb) xfb = xfb[s] yfb = yfb[s] bkpt = np.min(xfb) + np.arange(knots+1) * \ (np.max(xfb) - np.min(xfb)) / knots sftb, _ = Bspline.iterfit(xfb[nrefx:-nrefx], yfb[nrefx:-nrefx], fullbkpt=bkpt) yfitb, _ = sftb.value(xfb) # generate a red slice spectrum bspline fit redslice = 23 redleft = 60 / xbin redright = 80 / xbin qr = [i for i, v in enumerate(slicemap.data.flat) if v == redslice] qred = [i for i in qr if redleft <= posmap.data.flat[i] <= redright] xfd = wavemap.data.flat[qred] yfd = newflat.flat[qred] s = np.argsort(xfd) xfd = xfd[s] yfd = yfd[s] bkpt = np.min(xfd) + np.arange(knots + 1) * \ (np.max(xfd) - np.min(xfd)) / knots sftd, _ = Bspline.iterfit(xfd[nrefx:-nrefx], yfd[nrefx:-nrefx], fullbkpt=bkpt) yfitd, _ = sftd.value(xfd) # waves minwave = np.min(xfb) maxwave = np.max(xfd) # are we a twilight flat? if twiflat: nwaves = int(ny * knotspp) else: nwaves = 1000 waves = minwave + (maxwave - minwave) * np.arange(nwaves+1) / nwaves if self.config.instrument.plot_level >= 1: # output filename stub rbfnam = "redblue_%05d_%s_%s_%s" % \ (self.action.args.ccddata.header['FRAMENO'], self.action.args.illum, self.action.args.grating, self.action.args.ifuname) if xbin == 1: stride = int(len(xfr) / 8000.) if stride <= 0: stride = 1 else: stride = 1 xrplt = xfr[::stride] yrplt = yfitr[::stride] yrplt_d = yfr[::stride] xbplt = xfb[::stride] ybplt = yfitb[::stride] ybplt_d = yfb[::stride] xdplt = xfd[::stride] ydplt = yfitd[::stride] ydplt_d = yfd[::stride] p = figure( title=self.action.args.plotlabel + ' Blue/Red fits', x_axis_label='Wave (A)', y_axis_label='Flux (e-)', plot_width=self.config.instrument.plot_width, plot_height=self.config.instrument.plot_height) p.line(xrplt, yrplt, line_color='black', legend_label='Ref') p.circle(xrplt, yrplt_d, size=1, line_alpha=0., fill_color='black') p.line(xbplt, ybplt, line_color='blue', legend_label='Blue') p.circle(xbplt, ybplt_d, size=1, line_alpha=0., fill_color='blue') p.line(xdplt, ydplt, line_color='red', legend_label='Red') p.circle(xdplt, ydplt_d, size=1, line_alpha=0., fill_color='red') bokeh_plot(p, self.context.bokeh_session) if self.config.instrument.plot_level >= 2: input("Next? <cr>: ") else: time.sleep(self.config.instrument.plot_pause) save_plot(p, filename=rbfnam+'.png') wavebuffer = 0.1 minrwave = np.min(xfr) maxrwave = np.max(xfr) wavebuffer2 = 0.05 wlb0 = minrwave+(maxrwave-minrwave)*wavebuffer2 wlb1 = minrwave+(maxrwave-minrwave)*wavebuffer wlr0 = minrwave+(maxrwave-minrwave)*(1.-wavebuffer) wlr1 = minrwave+(maxrwave-minrwave)*(1.-wavebuffer2) qbluefit = [i for i, v in enumerate(waves) if wlb0 < v < wlb1] qredfit = [i for i, v in enumerate(waves) if wlr0 < v < wlr1] nqb = len(qbluefit) nqr = len(qredfit) self.logger.info("Wavelength regions: blue = %.1f - %.1f, " "red = %.1f - %.1f" % (wlb0, wlb1, wlr0, wlr1)) self.logger.info("Fit points: blue = %d, red = %d" % (nqb, nqr)) if nqb > 0: bluefit, _ = sftb.value(waves[qbluefit]) refbluefit, _ = sftr.value(waves[qbluefit]) blue_zero_cross = np.nanmin(bluefit) <= 0. or np.nanmin( refbluefit) <= 0. bluelinfit = np.polyfit(waves[qbluefit], refbluefit/bluefit, 1) bluelinfity = bluelinfit[1] + bluelinfit[0] * waves[qbluefit] else: bluefit = None blue_zero_cross = False refbluefit = None bluelinfit = None bluelinfity = None if nqr > 0: redfit, _ = sftd.value(waves[qredfit]) refredfit, _ = sftr.value(waves[qredfit]) red_zero_cross = np.nanmin(redfit) <= 0. or np.nanmin( refredfit) <= 0. redlinfit = np.polyfit(waves[qredfit], refredfit/redfit, 1) redlinfity = redlinfit[1] + redlinfit[0] * waves[qredfit] else: redfit = None red_zero_cross = False refredfit = None redlinfit = None redlinfity = None if blue_zero_cross: self.logger.info("Blue extension zero crossing detected") if red_zero_cross: self.logger.info("Red extension zero crossing detected") if self.config.instrument.plot_level >= 1: if nqb > 1: # plot blue fits p = figure( title=self.action.args.plotlabel + ' Blue fits', x_axis_label='Wave (A)', y_axis_label='Flux (e-)', plot_width=self.config.instrument.plot_width, plot_height=self.config.instrument.plot_height) p.line(waves[qbluefit], refbluefit, line_color='black', legend_label='Ref') p.circle(waves[qbluefit], refbluefit, fill_color='black') p.line(waves[qbluefit], bluefit, line_color='blue', legend_label='Blue') p.circle(waves[qbluefit], bluefit, fill_color='blue') bokeh_plot(p, self.context.bokeh_session) if self.config.instrument.plot_level >= 2: input("Next? <cr>: ") else: time.sleep(self.config.instrument.plot_pause) # plot blue ratios p = figure( title=self.action.args.plotlabel + ' Blue ratios', x_axis_label='Wave (A)', y_axis_label='Ratio', plot_width=self.config.instrument.plot_width, plot_height=self.config.instrument.plot_height) p.line(waves[qbluefit], refbluefit/bluefit, line_color='black', legend_label='Ref') p.circle(waves[qbluefit], refbluefit/bluefit, fill_color='black') p.line(waves[qbluefit], bluelinfity, line_color='blue', legend_label='Blue') p.circle(waves[qbluefit], bluelinfity, fill_color='blue') bokeh_plot(p, self.context.bokeh_session) if self.config.instrument.plot_level >= 2: input("Next? <cr>: ") else: time.sleep(self.config.instrument.plot_pause) if nqr > 1: # plot red fits p = figure( title=self.action.args.plotlabel + ' Red fits', x_axis_label='Wave (A)', y_axis_label='Flux (e-)', plot_width=self.config.instrument.plot_width, plot_height=self.config.instrument.plot_height) p.line(waves[qredfit], refredfit, line_color='black', legend_label='Ref') p.circle(waves[qredfit], refredfit, fill_color='black') p.line(waves[qredfit], redfit, line_color='red', legend_label='Red') p.circle(waves[qredfit], redfit, fill_color='red') bokeh_plot(p, self.context.bokeh_session) if self.config.instrument.plot_level >= 2: input("Next? <cr>: ") else: time.sleep(self.config.instrument.plot_pause) # plot red ratios p = figure( title=self.action.args.plotlabel + ' Red ratios', x_axis_label='Wave (A)', y_axis_label='Ratio', plot_width=self.config.instrument.plot_width, plot_height=self.config.instrument.plot_height) p.line(waves[qredfit], refredfit/redfit, line_color='black', legend_label='Ref') p.circle(waves[qredfit], refredfit/redfit, fill_color='black') p.line(waves[qredfit], redlinfity, line_color='red', legend_label='Red') p.circle(waves[qredfit], redlinfity, fill_color='red') bokeh_plot(p, self.context.bokeh_session) if self.config.instrument.plot_level >= 2: input("Next? <cr>: ") else: time.sleep(self.config.instrument.plot_pause) # at this point we are going to try to merge the points self.logger.info("Correcting points outside %.1f - %.1f A" % (minrwave, maxrwave)) qselblue = [i for i, v in enumerate(xfb) if v <= minrwave] qselred = [i for i, v in enumerate(xfd) if v >= maxrwave] nqsb = len(qselblue) nqsr = len(qselred) blue_all_tie = yfitr[0] red_all_tie = yfitr[-1] self.logger.info("Blue/Red ref tie values: %.3f, %.3f" % (blue_all_tie, red_all_tie)) if nqsb > 0: self.logger.info("Blue ext tie value: %.3f" % yfb[qselblue[-1]]) if blue_zero_cross: blue_offset = yfb[qselblue[-1]] - blue_all_tie bluefluxes = [yfb[i] - blue_offset for i in qselblue] self.logger.info("Blue zero crossing, only applying offset") else: blue_offset = yfb[qselblue[-1]] * \ (bluelinfit[1]+bluelinfit[0]*xfb[qselblue[-1]]) \ - blue_all_tie bluefluxes = [yfb[i] * (bluelinfit[1]+bluelinfit[0]*xfb[i]) - blue_offset for i in qselblue] self.logger.info("Blue linear ratio fit scaling applied") self.logger.info("Blue offset of %.2f applied" % blue_offset) else: bluefluxes = None if nqsr > 0: self.logger.info("Red ext tie value: %.3f" % yfd[qselred[0]]) if red_zero_cross: red_offset = yfd[qselred[0]] - red_all_tie redfluxes = [yfd[i] - red_offset for i in qselred] self.logger.info("Red zero crossing, only applying offset") else: red_offset = yfd[qselred[0]] * \ (redlinfit[1]+redlinfit[0]*xfd[qselred[0]]) \ - red_all_tie redfluxes = [yfd[i] * (redlinfit[1]+redlinfit[0]*xfd[i]) - red_offset for i in qselred] self.logger.info("Red linear ratio fit scaling applied") self.logger.info("Red offset of %.2f applied" % red_offset) else: redfluxes = None allx = xfr allfx = xfr[nrefx:-nrefx] ally = yfr[nrefx:-nrefx] if nqsb > 0: bluex = xfb[qselblue] allx = np.append(bluex, allx) allfx = np.append(bluex[nrefx:], allfx) ally = np.append(bluefluxes[nrefx:], ally) if nqsr > 0: redx = xfd[qselred] allx = np.append(allx, redx) allfx = np.append(allfx, redx[:-nrefx]) ally = np.append(ally, redfluxes[:-nrefx]) s = np.argsort(allx) allx = allx[s] s = np.argsort(allfx) allfx = allfx[s] ally = ally[s] bkpt = np.min(allx) + np.arange(knots+1) * \ (np.max(allx) - np.min(allx)) / knots sftall, _ = Bspline.iterfit(allfx, ally, fullbkpt=bkpt) yfitall, _ = sftall.value(allx) if self.config.instrument.plot_level >= 1: # output filename stub fltfnam = "flat_%05d_%s_%s_%s" % \ (self.action.args.ccddata.header['FRAMENO'], self.action.args.illum, self.action.args.grating, self.action.args.ifuname) if xbin == 1: stride = int(len(allx) / 8000.) else: stride = 1 xplt = allfx[::stride] yplt = ally[::stride] fxplt = allx[::stride] fplt = yfitall[::stride] yran = [np.nanmin(ally), np.nanmax(ally)] p = figure( title=self.action.args.plotlabel + ' Master Illumination', x_axis_label='Wave (A)', y_axis_label='Flux (e-)', plot_width=self.config.instrument.plot_width, plot_height=self.config.instrument.plot_height) p.circle(xplt, yplt, size=1, line_alpha=0., fill_color='black', legend_label='Data') p.line(fxplt, fplt, line_color='red', legend_label='Fit', line_width=2) p.line([minrwave, minrwave], yran, line_color='orange', legend_label='Cor lim') p.line([maxrwave, maxrwave], yran, line_color='orange') p.legend.location = "top_left" bokeh_plot(p, self.context.bokeh_session) if self.config.instrument.plot_level >= 2: input("Next? <cr>: ") else: time.sleep(self.config.instrument.plot_pause) save_plot(p, filename=fltfnam+".png") # OK, Now we have extended to the full range... so... we are going to # make a ratio flat! comflat = np.zeros(newflat.shape, dtype=float) qz = [i for i, v in enumerate(wavemap.data.flat) if v >= 0] comvals = sftall.value(wavemap.data.flat[qz]) comflat.flat[qz] = comvals ratio = np.zeros(newflat.shape, dtype=float) qzer = [i for i, v in enumerate(newflat.flat) if v != 0] ratio.flat[qzer] = comflat.flat[qzer] / newflat.flat[qzer] # trim negative points qq = [i for i, v in enumerate(ratio.flat) if v < 0] if len(qq) > 0: ratio.flat[qq] = 0.0 # trim the high points near edges of slice qq = [i for i, v in enumerate(ratio.flat) if v >= 3. and (posmap.data.flat[i] <= 4/xbin or posmap.data.flat[i] >= 136/xbin)] if len(qq) > 0: ratio.flat[qq] = 0.0 # don't correct low signal points qq = [i for i, v in enumerate(newflat.flat) if v < 30.] if len(qq) > 0: ratio.flat[qq] = 1.0 # get master flat output name mfname = stack_list[0].split('.fits')[0] + '_' + suffix + '.fits' log_string = MakeMasterFlat.__module__ stacked.header['IMTYPE'] = self.action.args.new_type stacked.header['HISTORY'] = log_string stacked.header['MASTFLAT'] = (True, 'master flat image?') stacked.header['WAVMAPF'] = wmf stacked.header['SLIMAPF'] = slf stacked.header['POSMAPF'] = pof # store flat in output frame stacked.data = ratio # output master flat kcwi_fits_writer(stacked, output_file=mfname, output_dir=self.config.instrument.output_directory) self.context.proctab.update_proctab(frame=stacked, suffix=suffix, newtype=self.action.args.new_type, filename=self.action.args.name) self.context.proctab.write_proctab() self.logger.info(log_string) return self.action.args
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): """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): """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