def echelle_2dfit(self, wv_calib, debug=False, skip_QA=False): """ Evaluate 2-d wavelength solution for echelle data. Unpacks wv_calib for slits to be input into arc.fit2darc Args: wv_calib (dict): Wavelength calibration debug (bool, optional): Show debugging info skip_QA (bool, optional): Skip QA Returns: dict: dictionary containing information from 2-d fit """ msgs.info('Fitting 2-d wavelength solution for echelle....') all_wave = np.array([], dtype=float) all_pixel = np.array([], dtype=float) all_order = np.array([], dtype=float) # Obtain a list of good slits ok_mask = np.where(~self.maskslits)[0] nspec = self.msarc.shape[0] for islit in wv_calib.keys(): if int(islit) not in ok_mask: continue iorder = self.spectrograph.slit2order(islit) mask_now = wv_calib[islit]['mask'] all_wave = np.append(all_wave, wv_calib[islit]['wave_fit'][mask_now]) all_pixel = np.append(all_pixel, wv_calib[islit]['pixel_fit'][mask_now]) all_order = np.append( all_order, np.full_like(wv_calib[islit]['pixel_fit'][mask_now], float(iorder))) fit2d_dict = arc.fit2darc(all_wave, all_pixel, all_order, nspec, nspec_coeff=self.par['ech_nspec_coeff'], norder_coeff=self.par['ech_norder_coeff'], sigrej=self.par['ech_sigrej'], debug=debug) self.steps.append(inspect.stack()[0][3]) # QA if not skip_QA: outfile_global = qa.set_qa_filename(self.master_key, 'arc_fit2d_global_qa', out_dir=self.qa_path) arc.fit2darc_global_qa(fit2d_dict, outfile=outfile_global) outfile_orders = qa.set_qa_filename(self.master_key, 'arc_fit2d_orders_qa', out_dir=self.qa_path) arc.fit2darc_orders_qa(fit2d_dict, outfile=outfile_orders) return fit2d_dict
def wv_calib_from_extern(wave_soln, arc, lamps, outfile = None, sigdetect=5.0, fwhm=4.0, nonlinear_counts=1e10, outroot='./', debug=False): # TODO add array size checking etc. nslits = wave_soln.shape[1] nspec = wave_soln.shape[0] line_lists = wavecal.waveio.load_line_lists(lamps) wv_calib = {} spec_vec = np.arange(nspec) for islit in range(nslits): print(str(islit)) # Find peaks for this slit tcent, ecent, cut_tcent, icut, spec_cont_sub = wavecal.wvutils.arc_lines_from_spec(arc[:,islit], sigdetect=sigdetect, nonlinear_counts=nonlinear_counts, fwhm=fwhm, debug=debug) detections = tcent[icut] wave_det = (scipy.interpolate.interp1d(spec_vec, wave_soln[:, islit], kind='cubic'))(detections) patt_dict = {} patt_dict['mask'] = np.ones_like(detections, dtype=bool) patt_dict['IDs'] = wave_det patt_dict['bdisp'] = (np.max(wave_soln[:,islit]) - np.min(wave_soln[:,islit]))/nspec final_fit = wavecal.fitting.fit_slit(spec_cont_sub, patt_dict, detections, line_lists, vel_tol=300.0, outroot=outroot, verbose=True) wv_calib[str(islit)] = final_fit qa_file = qa.set_qa_filename('GNIRS', 'arc_fit_qa', slit=islit, out_dir=outroot) wavecal.qa.arc_fit_qa(wv_calib[str(islit)],outfile=qa_file) if debug: # Show the QA wavecal.qa.arc_fit_qa(wv_calib[str(islit)]) waveCalib = wavecalib.WaveCalib(None,None) if outfile is not None: waveCalib.save_master(wv_calib, outfile=outfile) return wv_calib
def plot_tilt_2d(tilts_dspat, tilts, tilts_model, tot_mask, rej_mask, spat_order, spec_order, rms, fwhm, slit=0, setup='A', outfile=None, show_QA=False, out_dir=None): plt.rcdefaults() plt.rcParams['font.family']= 'Helvetica' # Outfile method = inspect.stack()[0][3] if (outfile is None): outfile = qa.set_qa_filename(setup, method, slit=slit, out_dir=out_dir) # Show the fit fig, ax = plt.subplots(figsize=(12, 18)) ax.cla() ax.plot(tilts_dspat[tot_mask], tilts[tot_mask], color='black', linestyle=' ', mfc='None', marker='o', markersize=9.0, markeredgewidth=1.0, zorder=4, label='Good Tilt') ax.plot(tilts_dspat[rej_mask], tilts[rej_mask], color='red', linestyle=' ', mfc='None', marker='o', markersize=9.0, markeredgewidth=2.0, zorder=5, label='Rejected') ax.plot(tilts_dspat[tot_mask], tilts_model[tot_mask], color='black', linestyle=' ', marker='o', markersize=2.0, markeredgewidth=1.0, zorder=1, label='2D Model') xmin = 1.1 * tilts_dspat[tot_mask].min() xmax = 1.1 * tilts_dspat[tot_mask].max() ax.set_xlim((xmin, xmax)) ax.set_xlabel('Spatial Offset from Central Trace (pixels)', fontsize=15) ax.set_ylabel('Spectral Pixel', fontsize=15) ax.legend() ax.set_title('Tilts vs Fit (spat_order, spec_order)=({:d},{:d}) for slit={:d}: RMS = {:5.3f}, ' 'RMS/FWHM={:5.3f}'.format(spat_order, spec_order, slit, rms, rms / fwhm), fontsize=15) # Finish #plt.tight_layout(pad=1.0, h_pad=1.0, w_pad=1.0) if outfile is not None: plt.savefig(outfile, dpi=400) if show_QA: plt.show() plt.close() plt.rcdefaults()
def build_wv_calib(self, arccen, method, skip_QA=False): """ Main routine to generate the wavelength solutions in a loop over slits Wrapper to arc.simple_calib or arc.calib_with_arclines self.maskslits is updated for slits that fail Args: method : str 'simple' -- arc.simple_calib 'arclines' -- arc.calib_with_arclines 'holy-grail' -- wavecal.autoid.HolyGrail 'reidentify' -- wavecal.auotid.ArchiveReid 'full_template' -- wavecal.auotid.full_template skip_QA (bool, optional) Returns: dict: self.wv_calib """ # Obtain a list of good slits ok_mask = np.where(~self.maskslits)[0] # Obtain calibration for all slits if method == 'simple': lines = self.par['lamps'] line_lists = waveio.load_line_lists(lines) self.wv_calib = arc.simple_calib_driver( self.msarc, line_lists, arccen, ok_mask, nfitpix=self.par['nfitpix'], IDpixels=self.par['IDpixels'], IDwaves=self.par['IDwaves']) elif method == 'semi-brute': # TODO: THIS IS CURRENTLY BROKEN debugger.set_trace() final_fit = {} for slit in ok_mask: # HACKS BY JXP self.par['wv_cen'] = 8670. self.par['disp'] = 1.524 # ToDO remove these hacks and use the parset in semi_brute best_dict, ifinal_fit \ = autoid.semi_brute(arccen[:, slit], self.par['lamps'], self.par['wv_cen'], self.par['disp'], match_toler=self.par['match_toler'], func=self.par['func'], n_first=self.par['n_first'], sigrej_first=self.par['n_first'], n_final=self.par['n_final'], sigrej_final=self.par['sigrej_final'], sigdetect=self.par['sigdetect'], nonlinear_counts= self.nonlinear_counts) final_fit[str(slit)] = ifinal_fit.copy() elif method == 'basic': final_fit = {} for slit in ok_mask: status, ngd_match, match_idx, scores, ifinal_fit = \ autoid.basic(arccen[:, slit], self.par['lamps'], self.par['wv_cen'], self.par['disp'], nonlinear_counts=self.nonlinear_counts) final_fit[str(slit)] = ifinal_fit.copy() if status != 1: self.maskslits[slit] = True elif method == 'holy-grail': # Sometimes works, sometimes fails arcfitter = autoid.HolyGrail(arccen, par=self.par, ok_mask=ok_mask) patt_dict, final_fit = arcfitter.get_results() elif method == 'reidentify': # Now preferred # Slit positions arcfitter = autoid.ArchiveReid(arccen, self.spectrograph, self.par, ok_mask=ok_mask, slit_spat_pos=self.slit_spat_pos) patt_dict, final_fit = arcfitter.get_results() elif method == 'full_template': # Now preferred if self.binspectral is None: msgs.error( "You must specify binspectral for the full_template method!" ) final_fit = autoid.full_template(arccen, self.par, ok_mask, self.det, self.binspectral, nsnippet=self.par['nsnippet']) else: msgs.error( 'Unrecognized wavelength calibration method: {:}'.format( method)) self.wv_calib = final_fit # Remake mask (*mainly for the QA that follows*) self.maskslits = self.make_maskslits(len(self.maskslits)) ok_mask = np.where(~self.maskslits)[0] # QA if not skip_QA: for slit in ok_mask: outfile = qa.set_qa_filename(self.master_key, 'arc_fit_qa', slit=slit, out_dir=self.qa_path) autoid.arc_fit_qa(self.wv_calib[str(slit)], outfile=outfile) # Return self.steps.append(inspect.stack()[0][3]) return self.wv_calib
def flexure_qa_oldbuggyversion(specobjs, maskslits, basename, det, flex_list, slit_cen=False): """ QA on flexure measurement Parameters ---------- det flex_list : list list of dict containing flexure results slit_cen : bool, optional QA on slit center instead of objects Returns ------- """ plt.rcdefaults() plt.rcParams['font.family'] = 'times new roman' # Grab the named of the method method = inspect.stack()[0][3] # gdslits = np.where(~maskslits)[0] for sl in range(len(specobjs)): if sl not in gdslits: continue if specobjs[sl][0] is None: continue # Setup if slit_cen: nobj = 1 ncol = 1 else: nobj = len(specobjs[sl]) ncol = min(3, nobj) # if nobj == 0: continue nrow = nobj // ncol + ((nobj % ncol) > 0) # Get the flexure dictionary flex_dict = flex_list[sl] # Outfile outfile = qa.set_qa_filename(basename, method + '_corr', det=det, slit=specobjs[sl][0].SLITID) plt.figure(figsize=(8, 5.0)) plt.clf() gs = gridspec.GridSpec(nrow, ncol) # Correlation QA for o in range(nobj): ax = plt.subplot(gs[o // ncol, o % ncol]) # Fit fit = flex_dict['polyfit'][o] xval = np.linspace( -10., 10, 100) + flex_dict['corr_cen'][o] #+ flex_dict['shift'][o] #model = (fit[2]*(xval**2.))+(fit[1]*xval)+fit[0] model = utils.func_val(fit, xval, 'polynomial') mxmod = np.max(model) ylim = [np.min(model / mxmod), 1.3] ax.plot(xval - flex_dict['corr_cen'][o], model / mxmod, 'k-') # Measurements ax.scatter(flex_dict['subpix'][o] - flex_dict['corr_cen'][o], flex_dict['corr'][o] / mxmod, marker='o') # Final shift ax.plot([flex_dict['shift'][o]] * 2, ylim, 'g:') # Label if slit_cen: ax.text(0.5, 0.25, 'Slit Center', transform=ax.transAxes, size='large', ha='center') else: ax.text(0.5, 0.25, '{:s}'.format(specobjs[sl][o].name), transform=ax.transAxes, size='large', ha='center') ax.text(0.5, 0.15, 'flex_shift = {:g}'.format(flex_dict['shift'][o]), transform=ax.transAxes, size='large', ha='center') #, bbox={'facecolor':'white'}) # Axes ax.set_ylim(ylim) ax.set_xlabel('Lag') # Finish plt.tight_layout(pad=0.2, h_pad=0.0, w_pad=0.0) plt.savefig(outfile, dpi=400) plt.close() # Sky line QA (just one object) if slit_cen: o = 0 else: o = 0 specobj = specobjs[sl][o] sky_spec = flex_dict['sky_spec'][o] arx_spec = flex_dict['arx_spec'][o] # Sky lines sky_lines = np.array([ 3370.0, 3914.0, 4046.56, 4358.34, 5577.338, 6300.304, 7340.885, 7993.332, 8430.174, 8919.610, 9439.660, 10013.99, 10372.88 ]) * units.AA dwv = 20. * units.AA gdsky = np.where((sky_lines > sky_spec.wvmin) & (sky_lines < sky_spec.wvmax))[0] if len(gdsky) == 0: msgs.warn("No sky lines for Flexure QA") return if len(gdsky) > 6: idx = np.array( [0, 1, len(gdsky) // 2, len(gdsky) // 2 + 1, -2, -1]) gdsky = gdsky[idx] # Outfile outfile = qa.set_qa_filename(basename, method + '_sky', det=det, slit=specobjs[sl][0].SLITID) # Figure plt.figure(figsize=(8, 5.0)) plt.clf() nrow, ncol = 2, 3 gs = gridspec.GridSpec(nrow, ncol) if slit_cen: plt.suptitle('Sky Comparison for Slit Center', y=1.05) else: plt.suptitle('Sky Comparison for {:s}'.format(specobj.name), y=1.05) for ii, igdsky in enumerate(gdsky): skyline = sky_lines[igdsky] ax = plt.subplot(gs[ii // ncol, ii % ncol]) # Norm pix = np.where(np.abs(sky_spec.wavelength - skyline) < dwv)[0] f1 = np.sum(sky_spec.flux[pix]) f2 = np.sum(arx_spec.flux[pix]) norm = f1 / f2 # Plot ax.plot(sky_spec.wavelength[pix], sky_spec.flux[pix], 'k-', label='Obj', drawstyle='steps-mid') pix2 = np.where(np.abs(arx_spec.wavelength - skyline) < dwv)[0] ax.plot(arx_spec.wavelength[pix2], arx_spec.flux[pix2] * norm, 'r-', label='Arx', drawstyle='steps-mid') # Axes ax.xaxis.set_major_locator(plt.MultipleLocator(dwv.value)) ax.set_xlabel('Wavelength') ax.set_ylabel('Counts') # Legend plt.legend(loc='upper left', scatterpoints=1, borderpad=0.3, handletextpad=0.3, fontsize='small', numpoints=1) # Finish plt.savefig(outfile, dpi=400) plt.close() #plt.close() plt.rcdefaults()
def flexure_qa(specobjs, maskslits, basename, det, flex_list, slit_cen=False, out_dir=None): """ Args: specobjs: maskslits (np.ndarray): basename (str): det (int): flex_list (list): slit_cen: out_dir: """ plt.rcdefaults() plt.rcParams['font.family'] = 'times new roman' # Grab the named of the method method = inspect.stack()[0][3] # gdslits = np.where(np.invert(maskslits))[0] # Loop over slits, and then over objects here for slit in gdslits: indx = specobjs.slitorder_indices(slit) this_specobjs = specobjs[indx] this_flex_dict = flex_list[slit] # Setup if slit_cen: nobj = 1 ncol = 1 else: nobj = np.sum(indx) ncol = min(3, nobj) # if nobj == 0: continue nrow = nobj // ncol + ((nobj % ncol) > 0) # Outfile, one QA file per slit outfile = qa.set_qa_filename(basename, method + '_corr', det=det, slit=(slit + 1), out_dir=out_dir) plt.figure(figsize=(8, 5.0)) plt.clf() gs = gridspec.GridSpec(nrow, ncol) for iobj, specobj in enumerate(this_specobjs): if specobj is None or len(specobj._data.keys()) == 1: continue # Correlation QA ax = plt.subplot(gs[iobj // ncol, iobj % ncol]) # Fit fit = this_flex_dict['polyfit'][iobj] xval = np.linspace(-10., 10, 100) + this_flex_dict['corr_cen'][ iobj] #+ flex_dict['shift'][o] #model = (fit[2]*(xval**2.))+(fit[1]*xval)+fit[0] model = utils.func_val(fit, xval, 'polynomial') mxmod = np.max(model) ylim_min = np.min(model / mxmod) if np.isfinite(np.min(model / mxmod)) else 0.0 ylim = [ylim_min, 1.3] ax.plot(xval - this_flex_dict['corr_cen'][iobj], model / mxmod, 'k-') # Measurements ax.scatter(this_flex_dict['subpix'][iobj] - this_flex_dict['corr_cen'][iobj], this_flex_dict['corr'][iobj] / mxmod, marker='o') # Final shift ax.plot([this_flex_dict['shift'][iobj]] * 2, ylim, 'g:') # Label if slit_cen: ax.text(0.5, 0.25, 'Slit Center', transform=ax.transAxes, size='large', ha='center') else: ax.text(0.5, 0.25, '{:s}'.format(specobj.name), transform=ax.transAxes, size='large', ha='center') ax.text(0.5, 0.15, 'flex_shift = {:g}'.format(this_flex_dict['shift'][iobj]), transform=ax.transAxes, size='large', ha='center') #, bbox={'facecolor':'white'}) # Axes ax.set_ylim(ylim) ax.set_xlabel('Lag') # Finish plt.tight_layout(pad=0.2, h_pad=0.0, w_pad=0.0) plt.savefig(outfile, dpi=400) plt.close() # Sky line QA (just one object) if slit_cen: iobj = 0 else: iobj = 0 specobj = this_specobjs[iobj] if len(this_flex_dict['shift']) == 0: return # Repackage sky_spec = this_flex_dict['sky_spec'][iobj] arx_spec = this_flex_dict['arx_spec'][iobj] # Sky lines sky_lines = np.array([ 3370.0, 3914.0, 4046.56, 4358.34, 5577.338, 6300.304, 7340.885, 7993.332, 8430.174, 8919.610, 9439.660, 10013.99, 10372.88 ]) * units.AA dwv = 20. * units.AA gdsky = np.where((sky_lines > sky_spec.wvmin) & (sky_lines < sky_spec.wvmax))[0] if len(gdsky) == 0: msgs.warn("No sky lines for Flexure QA") return if len(gdsky) > 6: idx = np.array( [0, 1, len(gdsky) // 2, len(gdsky) // 2 + 1, -2, -1]) gdsky = gdsky[idx] # Outfile outfile = qa.set_qa_filename(basename, method + '_sky', det=det, slit=(slit + 1), out_dir=out_dir) # Figure plt.figure(figsize=(8, 5.0)) plt.clf() nrow, ncol = 2, 3 gs = gridspec.GridSpec(nrow, ncol) if slit_cen: plt.suptitle('Sky Comparison for Slit Center', y=1.05) else: plt.suptitle('Sky Comparison for {:s}'.format(specobj.name), y=1.05) for ii, igdsky in enumerate(gdsky): skyline = sky_lines[igdsky] ax = plt.subplot(gs[ii // ncol, ii % ncol]) # Norm pix = np.where(np.abs(sky_spec.wavelength - skyline) < dwv)[0] f1 = np.sum(sky_spec.flux[pix]) f2 = np.sum(arx_spec.flux[pix]) norm = f1 / f2 # Plot ax.plot(sky_spec.wavelength[pix], sky_spec.flux[pix], 'k-', label='Obj', drawstyle='steps-mid') pix2 = np.where(np.abs(arx_spec.wavelength - skyline) < dwv)[0] ax.plot(arx_spec.wavelength[pix2], arx_spec.flux[pix2] * norm, 'r-', label='Arx', drawstyle='steps-mid') # Axes ax.xaxis.set_major_locator(plt.MultipleLocator(dwv.value)) ax.set_xlabel('Wavelength') ax.set_ylabel('Counts') # Legend plt.legend(loc='upper left', scatterpoints=1, borderpad=0.3, handletextpad=0.3, fontsize='small', numpoints=1) # Finish plt.savefig(outfile, dpi=400) plt.close() #plt.close() plt.rcdefaults() return
def build_wv_calib(self, arccen, method, skip_QA=False): """ Main routine to generate the wavelength solutions in a loop over slits Wrapper to arc.simple_calib or arc.calib_with_arclines self.maskslits is updated for slits that fail Args: method : str 'simple' -- arc.simple_calib 'arclines' -- arc.calib_with_arclines 'holy-grail' -- wavecal.autoid.HolyGrail 'reidentify' -- wavecal.auotid.ArchiveReid 'identify' -- wavecal.identify.Identify 'full_template' -- wavecal.auotid.full_template skip_QA (bool, optional) Returns: dict: self.wv_calib """ # Obtain a list of good slits ok_mask = np.where(np.invert(self.maskslits))[0] # Obtain calibration for all slits if method == 'simple': lines = self.par['lamps'] line_lists = waveio.load_line_lists(lines) final_fit = arc.simple_calib_driver( line_lists, arccen, ok_mask, n_final=self.par['n_final'], sigdetect=self.par['sigdetect'], IDpixels=self.par['IDpixels'], IDwaves=self.par['IDwaves']) elif method == 'semi-brute': # TODO: THIS IS CURRENTLY BROKEN embed() final_fit = {} for slit in ok_mask: # HACKS BY JXP self.par['wv_cen'] = 8670. self.par['disp'] = 1.524 # ToDO remove these hacks and use the parset in semi_brute best_dict, ifinal_fit \ = autoid.semi_brute(arccen[:, slit], self.par['lamps'], self.par['wv_cen'], self.par['disp'], match_toler=self.par['match_toler'], func=self.par['func'], n_first=self.par['n_first'], sigrej_first=self.par['n_first'], n_final=self.par['n_final'], sigrej_final=self.par['sigrej_final'], sigdetect=self.par['sigdetect'], nonlinear_counts= self.nonlinear_counts) final_fit[str(slit)] = ifinal_fit.copy() elif method == 'basic': final_fit = {} for slit in ok_mask: status, ngd_match, match_idx, scores, ifinal_fit = \ autoid.basic(arccen[:, slit], self.par['lamps'], self.par['wv_cen'], self.par['disp'], nonlinear_counts=self.nonlinear_counts) final_fit[str(slit)] = ifinal_fit.copy() if status != 1: self.maskslits[slit] = True elif method == 'holy-grail': # Sometimes works, sometimes fails arcfitter = autoid.HolyGrail(arccen, par=self.par, ok_mask=ok_mask) patt_dict, final_fit = arcfitter.get_results() elif method == 'identify': final_fit = {} # Manually identify lines msgs.info("Initializing the wavelength calibration tool") # TODO: Move this loop to the GUI initalise method embed() for slit in ok_mask: arcfitter = gui_identify.initialise(arccen, slit=slit, par=self.par) final_fit[str(slit)] = arcfitter.get_results() if final_fit[str(slit)] is not None: ans = 'y' # ans = '' # while ans != 'y' and ans != 'n': # ans = input("Would you like to store this wavelength solution in the archive? (y/n): ") if ans == 'y' and final_fit[str( slit)]['rms'] < self.par['rms_threshold']: # Store the results in the user reid arxiv specname = self.spectrograph.spectrograph gratname = "UNKNOWN" # input("Please input the grating name: ") dispangl = "UNKNOWN" # input("Please input the dispersion angle: ") templates.pypeit_identify_record( final_fit[str(slit)], self.binspectral, specname, gratname, dispangl) msgs.info("Your wavelength solution has been stored") msgs.info( "Please consider sending your solution to the PYPEIT team!" ) elif method == 'reidentify': # Now preferred # Slit positions arcfitter = autoid.ArchiveReid(arccen, self.spectrograph, self.par, ok_mask=ok_mask, slit_spat_pos=self.slit_spat_pos) patt_dict, final_fit = arcfitter.get_results() elif method == 'full_template': # Now preferred if self.binspectral is None: msgs.error( "You must specify binspectral for the full_template method!" ) final_fit = autoid.full_template(arccen, self.par, ok_mask, self.det, self.binspectral, nsnippet=self.par['nsnippet']) else: msgs.error( 'Unrecognized wavelength calibration method: {:}'.format( method)) self.wv_calib = final_fit # Remake mask (*mainly for the QA that follows*) self.maskslits = self.make_maskslits(len(self.maskslits)) ok_mask = np.where(np.invert(self.maskslits))[0] # QA if not skip_QA: for slit in ok_mask: outfile = qa.set_qa_filename(self.master_key, 'arc_fit_qa', slit=slit, out_dir=self.qa_path) autoid.arc_fit_qa(self.wv_calib[str(slit)], outfile=outfile) # Return self.steps.append(inspect.stack()[0][3]) return self.wv_calib
def echelle_2dfit(self, wv_calib, debug=False, skip_QA=False): """ Fit a two-dimensional wavelength solution for echelle data. Primarily a wrapper for :func:`pypeit.core.arc.fit2darc`, using data unpacked from the ``wv_calib`` dictionary. Args: wv_calib (:class:`pypeit.wavecalib.WaveCalib`): Wavelength calibration object debug (:obj:`bool`, optional): Show debugging info skip_QA (:obj:`bool`, optional): Flag to skip construction of the nominal QA plots. Returns: :class:`pypeit.fitting.PypeItFit`: object containing information from 2-d fit. """ if self.spectrograph.pypeline != 'Echelle': msgs.error( 'Cannot execute echelle_2dfit for a non-echelle spectrograph.') msgs.info('Fitting 2-d wavelength solution for echelle....') all_wave = np.array([], dtype=float) all_pixel = np.array([], dtype=float) all_order = np.array([], dtype=float) # Obtain a list of good slits ok_mask_idx = np.where(np.invert(self.wvc_bpm))[0] ok_mask_order = self.slits.slitord_id[ok_mask_idx] nspec = self.msarc.image.shape[0] # Loop for ii in range(wv_calib.nslits): iorder = self.slits.ech_order[ii] if iorder not in ok_mask_order: continue # Slurp mask_now = wv_calib.wv_fits[ii].pypeitfit.bool_gpm all_wave = np.append(all_wave, wv_calib.wv_fits[ii]['wave_fit'][mask_now]) all_pixel = np.append(all_pixel, wv_calib.wv_fits[ii]['pixel_fit'][mask_now]) all_order = np.append( all_order, np.full_like(wv_calib.wv_fits[ii]['pixel_fit'][mask_now], float(iorder))) # Fit # THIS NEEDS TO BE DEVELOPED fit2d = arc.fit2darc(all_wave, all_pixel, all_order, nspec, nspec_coeff=self.par['ech_nspec_coeff'], norder_coeff=self.par['ech_norder_coeff'], sigrej=self.par['ech_sigrej'], debug=debug) self.steps.append(inspect.stack()[0][3]) # QA # TODO -- TURN QA BACK ON! #skip_QA = True if not skip_QA: outfile_global = qa.set_qa_filename(self.master_key, 'arc_fit2d_global_qa', out_dir=self.qa_path) arc.fit2darc_global_qa(fit2d, nspec, outfile=outfile_global) outfile_orders = qa.set_qa_filename(self.master_key, 'arc_fit2d_orders_qa', out_dir=self.qa_path) arc.fit2darc_orders_qa(fit2d, nspec, outfile=outfile_orders) return fit2d
def build_wv_calib(self, arccen, method, skip_QA=False): """ Main routine to generate the wavelength solutions in a loop over slits Wrapper to arc.simple_calib or arc.calib_with_arclines self.maskslits is updated for slits that fail Args: method : str 'simple' -- arc.simple_calib 'arclines' -- arc.calib_with_arclines 'holy-grail' -- wavecal.autoid.HolyGrail 'reidentify' -- wavecal.auotid.ArchiveReid 'identify' -- wavecal.identify.Identify 'full_template' -- wavecal.auotid.full_template skip_QA (bool, optional) Returns: dict: self.wv_calib """ # Obtain a list of good slits ok_mask_idx = np.where(np.invert(self.wvc_bpm))[0] # Obtain calibration for all slits if method == 'simple': lines = self.par['lamps'] line_lists = waveio.load_line_lists(lines) final_fit = arc.simple_calib_driver( line_lists, arccen, ok_mask_idx, n_final=self.par['n_final'], sigdetect=self.par['sigdetect'], IDpixels=self.par['IDpixels'], IDwaves=self.par['IDwaves']) elif method == 'holy-grail': # Sometimes works, sometimes fails arcfitter = autoid.HolyGrail( arccen, par=self.par, ok_mask=ok_mask_idx, nonlinear_counts=self.nonlinear_counts) patt_dict, final_fit = arcfitter.get_results() elif method == 'identify': final_fit = {} # Manually identify lines msgs.info("Initializing the wavelength calibration tool") embed(header='line 222 wavecalib.py') for slit_idx in ok_mask_idx: arcfitter = Identify.initialise(arccen, self.slits, slit=slit_idx, par=self.par) final_fit[str(slit_idx)] = arcfitter.get_results() arcfitter.store_solution(final_fit[str(slit_idx)], "", self.binspectral, specname=self.spectrograph.name, gratname="UNKNOWN", dispangl="UNKNOWN") elif method == 'reidentify': # Now preferred # Slit positions arcfitter = autoid.ArchiveReid( arccen, self.spectrograph, self.par, ok_mask=ok_mask_idx, #slit_spat_pos=self.spat_coo, orders=self.orders, nonlinear_counts=self.nonlinear_counts) patt_dict, final_fit = arcfitter.get_results() elif method == 'full_template': # Now preferred if self.binspectral is None: msgs.error( "You must specify binspectral for the full_template method!" ) final_fit = autoid.full_template( arccen, self.par, ok_mask_idx, self.det, self.binspectral, nonlinear_counts=self.nonlinear_counts, nsnippet=self.par['nsnippet']) else: msgs.error( 'Unrecognized wavelength calibration method: {:}'.format( method)) # Build the DataContainer # Loop on WaveFit items tmp = [] for idx in range(self.slits.nslits): item = final_fit.pop(str(idx)) if item is None: # Add an empty WaveFit tmp.append(wv_fitting.WaveFit(self.slits.spat_id[idx])) else: # This is for I/O naming item.spat_id = self.slits.spat_id[idx] tmp.append(item) self.wv_calib = WaveCalib( wv_fits=np.asarray(tmp), arc_spectra=arccen, nslits=self.slits.nslits, spat_ids=self.slits.spat_id, PYP_SPEC=self.spectrograph.name, ) # Update mask self.update_wvmask() #TODO For generalized echelle (not hard wired) assign order number here before, i.e. slits.ech_order # QA if not skip_QA: ok_mask_idx = np.where(np.invert(self.wvc_bpm))[0] for slit_idx in ok_mask_idx: outfile = qa.set_qa_filename( self.master_key, 'arc_fit_qa', slit=self.slits.slitord_id[slit_idx], out_dir=self.qa_path) # #autoid.arc_fit_qa(self.wv_calib[str(self.slits.slitord_id[slit_idx])], # outfile=outfile) autoid.arc_fit_qa( self.wv_calib.wv_fits[slit_idx], #str(self.slits.slitord_id[slit_idx]), outfile=outfile) # Return self.steps.append(inspect.stack()[0][3]) return self.wv_calib
def echelle_2dfit(self, wv_calib, debug=False, skip_QA=False): """ Fit a two-dimensional wavelength solution for echelle data. Primarily a wrapper for :func:`pypeit.core.arc.fit2darc`, using data unpacked from the ``wv_calib`` dictionary. Args: wv_calib (:obj:`dict`): Wavelength calibration dictionary. See ?? debug (:obj:`bool`, optional): Show debugging info skip_QA (:obj:`bool`, optional): Flag to skip construction of the nominal QA plots. Returns: :obj:`dict`: Dictionary containing information from 2-d fit. """ if self.spectrograph.pypeline != 'Echelle': msgs.error( 'Cannot execute echelle_2dfit for a non-echelle spectrograph.') msgs.info('Fitting 2-d wavelength solution for echelle....') all_wave = np.array([], dtype=float) all_pixel = np.array([], dtype=float) all_order = np.array([], dtype=float) # Obtain a list of good slits ok_mask_idx = np.where(np.invert(self.wvc_bpm))[0] ok_mask_order = self.slits.slitord_id[ok_mask_idx] nspec = self.msarc.image.shape[0] for iorder in wv_calib.keys(): # Spatial based if int(iorder) not in ok_mask_order: continue #try: # iorder, iindx = self.spectrograph.slit2order(self.spat_coo[self.slits.spatid_to_zero(int(islit))]) #except: # embed() mask_now = wv_calib[iorder]['mask'] all_wave = np.append(all_wave, wv_calib[iorder]['wave_fit'][mask_now]) all_pixel = np.append(all_pixel, wv_calib[iorder]['pixel_fit'][mask_now]) all_order = np.append( all_order, np.full_like(wv_calib[iorder]['pixel_fit'][mask_now], float(iorder))) # Fit fit2d_dict = arc.fit2darc(all_wave, all_pixel, all_order, nspec, nspec_coeff=self.par['ech_nspec_coeff'], norder_coeff=self.par['ech_norder_coeff'], sigrej=self.par['ech_sigrej'], debug=debug) self.steps.append(inspect.stack()[0][3]) # QA if not skip_QA: outfile_global = qa.set_qa_filename(self.master_key, 'arc_fit2d_global_qa', out_dir=self.qa_path) arc.fit2darc_global_qa(fit2d_dict, outfile=outfile_global) outfile_orders = qa.set_qa_filename(self.master_key, 'arc_fit2d_orders_qa', out_dir=self.qa_path) arc.fit2darc_orders_qa(fit2d_dict, outfile=outfile_orders) return fit2d_dict
def pca_plot(setup, inpar, ofit, prefix, maxp=25, pcadesc="", addOne=True, show=False): """ Saves quality control plots for a PCA analysis Parameters ---------- inpar ofit prefix : str prefix for the filenames maxp pcadesc addOne Returns ------- """ plt.rcdefaults() plt.rcParams['font.family']= 'times new roman' # Setup method = inspect.stack()[0][3] if not show: outroot = qa.set_qa_filename(setup, method, prefix=prefix) npc = inpar['npc']+1 pages, npp = qa.get_dimen(npc, maxp=maxp) # x0 = inpar['x0'] ordernum = inpar['x0in'] x0fit = inpar['x0fit'] usetrc = inpar['usetrc'] hidden = inpar['hidden'] high_fit = inpar['high_fit'] nc = np.max(ordernum[usetrc]) # Loop through all pages and plot the results ndone = 0 for i in range(len(pages)): plt.clf() f, axes = plt.subplots(pages[i][1], pages[i][0]) ipx, ipy = 0, 0 if i == 0: if pages[i][1] == 1: ind = (0,) elif pages[i][0] == 1: ind = (0,) else: ind = (0,0) axes[ind].plot(ordernum[usetrc], x0[usetrc], 'bx') axes[ind].plot(ordernum, x0fit, 'k-') amn, amx = np.min(x0fit), np.max(x0fit) diff = x0[usetrc]-x0fit[usetrc] tdiffv = np.median(diff) mdiffv = 1.4826*np.median(np.abs(tdiffv-diff)) amn -= 2.0*mdiffv amx += 2.0*mdiffv mval = amn-0.15*(amx-amn) dmin, dmax = tdiffv-2.0*mdiffv, tdiffv+2.0*mdiffv diff = mval + diff*0.20*(amx-amn)/(dmax-dmin) wign = np.where(np.abs(diff-np.median(diff))<4.0*1.4826*np.median(np.abs(diff-np.median(diff))))[0] dmin, dmax = np.min(diff[wign]), np.max(diff[wign]) axes[ind].plot(ordernum[usetrc], diff, 'rx') if addOne: axes[ind].plot([0, nc+1], [mval,mval], 'k-') axes[ind].axis([0, nc+1, dmin-0.5*(dmax-dmin), amx + 0.05*(amx-amn)]) else: axes[ind].plot([0, nc], [mval, mval], 'k-') axes[ind].axis([0, nc, dmin-0.5*(dmax-dmin), amx + 0.05*(amx-amn)]) axes[ind].set_title("Mean Value") ipx += 1 if ipx == pages[i][0]: ipx = 0 ipy += 1 npp[0] -= 1 for j in range(npp[i]): if pages[i][1] == 1: ind = (ipx,) elif pages[i][0] == 1: ind = (ipy,) else: ind = (ipy, ipx) axes[ind].plot(ordernum[usetrc], hidden[j+ndone,:], 'bx') axes[ind].plot(ordernum, high_fit[:,j+ndone], 'k-') vmin, vmax = np.min(hidden[j+ndone,:]), np.max(hidden[j+ndone,:]) if ofit[1+j+ndone] != -1: cmn, cmx = np.min(high_fit[:,j+ndone]), np.max(high_fit[:,j+ndone]) diff = hidden[j+ndone,:]-high_fit[:,j+ndone][usetrc] tdiffv = np.median(diff) mdiffv = 1.4826*np.median(np.abs(tdiffv-diff)) cmn -= 2.0*mdiffv cmx += 2.0*mdiffv mval = cmn-0.15*(cmx-cmn) dmin, dmax = tdiffv-2.0*mdiffv, tdiffv+2.0*mdiffv #dmin, dmax = np.min(diff), np.max(diff) diff = mval + diff*0.20*(cmx-cmn)/(dmax-dmin) wign = np.where(np.abs(diff-np.median(diff))<4.0*1.4826*np.median(np.abs(diff-np.median(diff))))[0] dmin, dmax = np.min(diff[wign]), np.max(diff[wign]) #vmin, vmax = np.min(hidden[j+ndone,:][wign]), np.max(hidden[j+ndone,:][wign]) axes[ind].plot(ordernum[usetrc], diff, 'rx') axes[ind].plot([0, 1+nc], [mval, mval], 'k-') # ymin = np.min([(3.0*dmin-dmax)/2.0,vmin-0.1*(vmax-dmin),dmin-0.1*(vmax-dmin)]) # ymax = np.max([np.max(high_fit[:,j+ndone]),vmax+0.1*(vmax-dmin),dmax+0.1*(vmax-dmin)]) ymin = dmin-0.5*(dmax-dmin) ymax = cmx + 0.05*(cmx-cmn) if addOne: axes[ind].axis([0, nc+1, ymin, ymax]) else: axes[ind].axis([0, nc, ymin, ymax]) else: if addOne: axes[ind].axis([0, nc+1, vmin-0.1*(vmax-vmin), vmax+0.1*(vmax-vmin)]) else: axes[ind].axis([0, nc, vmin-0.1*(vmax-vmin), vmax+0.1*(vmax-vmin)]) axes[ind].set_title("PC {0:d}".format(j+ndone)) axes[ind].tick_params(labelsize=8) ipx += 1 if ipx == pages[i][0]: ipx = 0 ipy += 1 if i == 0: npp[0] = npp[0] + 1 # Delete the unnecessary axes for j in range(npp[i], axes.size): if pages[i][1] == 1: ind = (ipx,) elif pages[i][0] == 1: ind = (ipy,) else: ind = (ipy, ipx) f.delaxes(axes[ind]) ipx += 1 if ipx == pages[i][0]: ipx = 0 ipy += 1 ndone += npp[i] # Save the figure if pages[i][1] == 1 or pages[i][0] == 1: ypngsiz = 11.0/axes.size else: ypngsiz = 11.0*axes.shape[0]/axes.shape[1] f.set_size_inches(11.0, ypngsiz) if pcadesc != "": pgtxt = "" if len(pages) != 1: pgtxt = ", page {0:d}/{1:d}".format(i+1, len(pages)) f.suptitle(pcadesc + pgtxt, y=1.02, size=16) f.tight_layout() if show: plt.show() else: outfile = outroot+'{:02d}.png'.format(i) f.savefig(outfile, dpi=200) plt.close() f.clf() del f plt.rcdefaults() return
def plot_tilt_spat(tilts_dspat, tilts, tilts_model, tilts_spec_fit, tot_mask, rej_mask,spat_order, spec_order, rms, fwhm, setup='A', slit=0, outfile=None, show_QA=False, out_dir=None): import matplotlib as mpl from matplotlib.lines import Line2D plt.rcdefaults() plt.rcParams['font.family']= 'Helvetica' # Outfil method = inspect.stack()[0][3] if (outfile is None): outfile = qa.set_qa_filename(setup, method, slit=slit, out_dir=out_dir) nspat, nuse = tilts_dspat.shape # Show the fit residuals as a function of spatial position line_indx = np.outer(np.ones(nspat), np.arange(nuse)) lines_spec = tilts_spec_fit[0, :] cmap = mpl.cm.get_cmap('coolwarm', nuse) fig, ax = plt.subplots(figsize=(14, 12)) # dummy mappable shows the spectral pixel dummie_cax = ax.scatter(lines_spec, lines_spec, c=lines_spec, cmap=cmap) ax.cla() for iline in range(nuse): iall = (line_indx == iline) & tot_mask irej = (line_indx == iline) & tot_mask & rej_mask this_color = cmap(iline) # plot the residuals ax.plot(tilts_dspat[iall], tilts[iall] - tilts_model[iall], color=this_color, linestyle='-', linewidth=3.0, marker='None', alpha=0.5) ax.plot(tilts_dspat[irej], tilts[irej] - tilts_model[irej], linestyle=' ', marker='o', color='limegreen', mfc='limegreen', markersize=5.0) xmin = 1.1 * tilts_dspat[tot_mask].min() xmax = 1.1 * tilts_dspat[tot_mask].max() ax.hlines(0.0, xmin, xmax, linestyle='--', linewidth=2.0, color='k', zorder=10) ax.set_xlim((xmin, xmax)) ax.set_xlabel('Spatial Offset from Central Trace (pixels)') ax.set_ylabel('Arc Line Tilt Residual (pixels)') legend_elements = [Line2D([0], [0], color='cornflowerblue', linestyle='-', linewidth=3.0, label='residual'), Line2D([0], [0], color='limegreen', linestyle=' ', marker='o', mfc='limegreen', markersize=7.0, label='rejected')] ax.legend(handles=legend_elements) ax.set_title('Tilts vs Fit (spat_order, spec_order)=({:d},{:d}) for slit={:d}: RMS = {:5.3f}, ' 'RMS/FWHM={:5.3f}'.format(spat_order, spec_order, slit, rms, rms / fwhm), fontsize=15) cb = fig.colorbar(dummie_cax, ticks=lines_spec) cb.set_label('Spectral Pixel') # Finish plt.tight_layout(pad=0.2, h_pad=0.0, w_pad=0.0) if outfile is not None: plt.savefig(outfile, dpi=400) if show_QA: plt.show() plt.close() plt.rcdefaults()
def plot_tilt_spec(tilts_spec_fit, tilts, tilts_model, tot_mask, rej_mask, rms, fwhm, slit=0, setup = 'A', outfile=None, show_QA=False, out_dir=None): """ Generate a QA plot of the residuals for the fit to the tilts in the spectral direction one slit at a time Parameters ---------- """ plt.rcdefaults() plt.rcParams['font.family']= 'Helvetica' # Outfil method = inspect.stack()[0][3] if (outfile is None): outfile = qa.set_qa_filename(setup, method, slit=slit, out_dir=out_dir) # Setup plt.figure(figsize=(14, 6)) plt.clf() ax = plt.gca() # Scatter plot res = (tilts - tilts_model) nspat, nuse = tilts.shape # Show the fit residuals as a function of spatial position line_indx = np.outer(np.ones(nspat), np.arange(nuse)) xmin = 0.90*(tilts_spec_fit.min()) xmax = 1.10*(tilts_spec_fit.max()) ax.hlines(0.0, xmin, xmax,linestyle='--', color='green') for iline in range(nuse): iall = (line_indx == iline) & tot_mask igd = (line_indx == iline) & tot_mask & (rej_mask == False) irej = (line_indx == iline) & tot_mask & rej_mask ax.plot(tilts_spec_fit[igd], (res[igd]), 'ko', mfc='k', markersize=4.0) ax.plot(tilts_spec_fit[irej],(res[irej]), 'ro', mfc='r', markersize=4.0) # Compute the RMS for this line all_rms = np.std(res[iall]) good_rms = np.std(res[igd]) # ToDo show the mean here as well if np.any(igd): ax.plot(tilts_spec_fit[igd][0], all_rms, marker='s',linestyle=' ', color='g', mfc='g', markersize=7.0) ax.plot(tilts_spec_fit[igd][0], good_rms, marker='^', linestyle=' ', color='orange', mfc='orange', markersize=7.0) ax.text(0.90, 0.90, 'Slit {:d}: Residual (pixels) = {:0.5f}'.format(slit, rms), transform=ax.transAxes, size='large', ha='right', color='black',fontsize=16) ax.text(0.90, 0.80, ' Slit {:d}: RMS/FWHM = {:0.5f}'.format(slit, rms/fwhm), transform=ax.transAxes, size='large', ha='right', color='black',fontsize=16) # Label ax.set_xlabel('Spectral Pixel') ax.set_ylabel('RMS (pixels)') ax.set_title('RMS of Each Arc Line Traced') ax.set_xlim((xmin,xmax)) ax.set_ylim((-5.0*rms,5.0*rms)) # Legend legend_elements = [Line2D([0], [0],linestyle=' ', color='k', marker='o', mfc='k', markersize=4.0, label='good'), Line2D([0], [0], linestyle=' ', color='r', marker='o', mfc='r', markersize=4.0, label='rejected'), Line2D([0], [0], linestyle=' ', color='g', marker='s', mfc='g', markersize=7.0, label='all RMS'), Line2D([0], [0], linestyle=' ', color='orange', marker='^', mfc='orange', markersize=7.0, label='good RMS')] ax.legend(handles=legend_elements) # Finish plt.tight_layout(pad=0.2, h_pad=0.0, w_pad=0.0) if outfile is not None: plt.savefig(outfile, dpi=400) if show_QA: plt.show() plt.close() plt.rcdefaults()
def build_wv_calib(self, arccen, method, skip_QA=False): """ Main routine to generate the wavelength solutions in a loop over slits Wrapper to arc.simple_calib or arc.calib_with_arclines self.maskslits is updated for slits that fail Args: method : str 'simple' -- arc.simple_calib 'arclines' -- arc.calib_with_arclines 'holy-grail' -- wavecal.autoid.HolyGrail 'reidentify' -- wavecal.auotid.ArchiveReid 'identify' -- wavecal.identify.Identify 'full_template' -- wavecal.auotid.full_template skip_QA (bool, optional) Returns: dict: self.wv_calib """ # Obtain a list of good slits ok_mask_idx = np.where(np.invert(self.wvc_bpm))[0] # Obtain calibration for all slits if method == 'simple': lines = self.par['lamps'] line_lists = waveio.load_line_lists(lines) final_fit = arc.simple_calib_driver( line_lists, arccen, ok_mask_idx, n_final=self.par['n_final'], sigdetect=self.par['sigdetect'], IDpixels=self.par['IDpixels'], IDwaves=self.par['IDwaves']) # elif method == 'basic': # final_fit = {} # for slit in ok_mask: # status, ngd_match, match_idx, scores, ifinal_fit = \ # autoid.basic(arccen[:, slit], self.par['lamps'], self.par['wv_cen'], # self.par['disp'], nonlinear_counts=self.nonlinear_counts) # final_fit[str(slit)] = ifinal_fit.copy() # if status != 1: # self.maskslits[slit] = True elif method == 'holy-grail': # Sometimes works, sometimes fails arcfitter = autoid.HolyGrail( arccen, par=self.par, ok_mask=ok_mask_idx, nonlinear_counts=self.nonlinear_counts) patt_dict, final_fit = arcfitter.get_results() elif method == 'identify': final_fit = {} # Manually identify lines msgs.info("Initializing the wavelength calibration tool") # TODO: Move this loop to the GUI initalise method embed() for slit_idx in ok_mask_idx: arcfitter = gui_identify.initialise(arccen, slit=slit_idx, par=self.par) final_fit[str(slit_idx)] = arcfitter.get_results() if final_fit[str(slit_idx)] is not None: ans = 'y' # ans = '' # while ans != 'y' and ans != 'n': # ans = input("Would you like to store this wavelength solution in the archive? (y/n): ") if ans == 'y' and final_fit[str( slit_idx)]['rms'] < self.par['rms_threshold']: # Store the results in the user reid arxiv specname = self.spectrograph.spectrograph gratname = "UNKNOWN" # input("Please input the grating name: ") dispangl = "UNKNOWN" # input("Please input the dispersion angle: ") templates.pypeit_identify_record( final_fit[str(slit_idx)], self.binspectral, specname, gratname, dispangl) msgs.info("Your wavelength solution has been stored") msgs.info( "Please consider sending your solution to the PYPEIT team!" ) elif method == 'reidentify': # Now preferred # Slit positions arcfitter = autoid.ArchiveReid( arccen, self.spectrograph, self.par, ok_mask=ok_mask_idx, slit_spat_pos=self.spat_coo, nonlinear_counts=self.nonlinear_counts) patt_dict, final_fit = arcfitter.get_results() elif method == 'full_template': # Now preferred if self.binspectral is None: msgs.error( "You must specify binspectral for the full_template method!" ) final_fit = autoid.full_template( arccen, self.par, ok_mask_idx, self.det, self.binspectral, nonlinear_counts=self.nonlinear_counts, nsnippet=self.par['nsnippet']) else: msgs.error( 'Unrecognized wavelength calibration method: {:}'.format( method)) # Convert keys to spatial system self.wv_calib = {} tmp = copy.deepcopy(final_fit) for idx in range(self.slits.nslits): if str(idx) in final_fit.keys(): self.wv_calib[str(self.slits.slitord_id[idx])] = final_fit.pop( str(idx)) # Update mask self.update_wvmask() #TODO For generalized echelle (not hard wired) assign order number here before, i.e. slits.ech_order # QA if not skip_QA: ok_mask_idx = np.where(np.invert(self.wvc_bpm))[0] for slit_idx in ok_mask_idx: outfile = qa.set_qa_filename( self.master_key, 'arc_fit_qa', slit=self.slits.slitord_id[slit_idx], out_dir=self.qa_path) autoid.arc_fit_qa(self.wv_calib[str( self.slits.slitord_id[slit_idx])], outfile=outfile) # Return self.steps.append(inspect.stack()[0][3]) return self.wv_calib
def wv_calib_from_extern(wave_soln, arc, lamps, outfile=None, sigdetect=5.0, fwhm=4.0, nonlinear_counts=1e10, outroot='./', debug=False): """ Args: wave_soln: arc: lamps: outfile: sigdetect: fwhm: nonlinear_counts: outroot: debug: Returns: """ # TODO add array size checking etc. nslits = wave_soln.shape[1] nspec = wave_soln.shape[0] line_lists = wavecal.waveio.load_line_lists(lamps) wv_calib = {} spec_vec = np.arange(nspec) for islit in range(nslits): print(str(islit)) # Find peaks for this slit tcent, ecent, cut_tcent, icut, spec_cont_sub = wavecal.wvutils.arc_lines_from_spec( arc[:, islit], sigdetect=sigdetect, nonlinear_counts=nonlinear_counts, fwhm=fwhm, debug=debug) detections = tcent[icut] wave_det = (scipy.interpolate.interp1d(spec_vec, wave_soln[:, islit], kind='cubic'))(detections) patt_dict = {} patt_dict['mask'] = np.ones_like(detections, dtype=bool) patt_dict['IDs'] = wave_det patt_dict['bdisp'] = (np.max(wave_soln[:, islit]) - np.min(wave_soln[:, islit])) / nspec final_fit = wavecal.fitting.fit_slit(spec_cont_sub, patt_dict, detections, line_lists, vel_tol=300.0, outroot=outroot, verbose=True) wv_calib[str(islit)] = final_fit qa_file = qa.set_qa_filename('GNIRS', 'arc_fit_qa', slit=islit, out_dir=outroot) wavecal.qa.arc_fit_qa(wv_calib[str(islit)], outfile=qa_file) if debug: # Show the QA wavecal.qa.arc_fit_qa(wv_calib[str(islit)]) waveCalib = wavecalib.WaveCalib(None, None) if outfile is not None: waveCalib.save_master(wv_calib, outfile=outfile) return wv_calib
def spec_flexure_qa(slitords, bpm, basename, det, flex_list, specobjs=None, out_dir=None): """ Args: slitords (`numpy.ndarray`_): Array of slit/order numbers bpm (`numpy.ndarray`_): True = masked slit basename (str): det (int): flex_list (list): specobjs: (:class:`pypeit.specobjs.Specobjs`) Spectrally extracted objects out_dir: """ plt.rcdefaults() plt.rcParams['font.family'] = 'times new roman' # What type of QA are we doing slit_cen = False if specobjs is None: slit_cen = True # Grab the named of the method method = inspect.stack()[0][3] # Mask gdslits = np.where(np.invert(bpm))[0] # Loop over slits, and then over objects here for islit in gdslits: # Slit/order number slitord = slitords[islit] # Parse and Setup if slit_cen: nobj = 1 ncol = 1 else: indx = specobjs.slitorder_indices(slitord) this_specobjs = specobjs[indx] nobj = np.sum(indx) if nobj == 0: continue ncol = min(3, nobj) this_flex_dict = flex_list[islit] # Check that the default was overwritten if len(this_flex_dict['shift']) == 0: continue nrow = nobj // ncol + ((nobj % ncol) > 0) # Outfile, one QA file per slit outfile = qa.set_qa_filename(basename, method + '_corr', det=det, slit=slitord, out_dir=out_dir) plt.figure(figsize=(8, 5.0)) plt.clf() gs = gridspec.GridSpec(nrow, ncol) # TODO -- This cntr is crummy and needs to be replaced by a DataContainer # for flex_dict and flex_list cntr = 0 # Correlation QA if slit_cen: ax = plt.subplot(gs[0, 0]) spec_flexure_corrQA(ax, this_flex_dict, cntr, 'Slit Center') else: for specobj in this_specobjs: if specobj is None or (specobj.BOX_WAVE is None and specobj.OPT_WAVE is None): continue ax = plt.subplot(gs[cntr // ncol, cntr % ncol]) spec_flexure_corrQA(ax, this_flex_dict, cntr, '{:s}'.format(specobj.NAME)) cntr += 1 # Finish plt.tight_layout(pad=0.2, h_pad=0.0, w_pad=0.0) plt.savefig(outfile, dpi=400) plt.close() # Sky line QA (just one object) if slit_cen: iobj = 0 else: iobj = 0 specobj = this_specobjs[iobj] # Repackage sky_spec = this_flex_dict['sky_spec'][iobj] arx_spec = this_flex_dict['arx_spec'][iobj] min_wave = max(np.amin(arx_spec.wavelength.value), np.amin(sky_spec.wavelength.value)) * units.AA max_wave = min(np.amax(arx_spec.wavelength.value), np.amax(sky_spec.wavelength.value)) * units.AA # Sky lines sky_lines = np.array([ 3370.0, 3914.0, 4046.56, 4358.34, 5577.338, 6300.304, 7340.885, 7993.332, 8430.174, 8919.610, 9439.660, 10013.99, 10372.88 ]) * units.AA dwv = 20. * units.AA gdsky = np.where((sky_lines > min_wave) & (sky_lines < max_wave))[0] if len(gdsky) == 0: msgs.warn("No sky lines for Flexure QA") continue if len(gdsky) > 6: idx = np.array( [0, 1, len(gdsky) // 2, len(gdsky) // 2 + 1, -2, -1]) gdsky = gdsky[idx] # Outfile outfile = qa.set_qa_filename(basename, method + '_sky', det=det, slit=slitord, out_dir=out_dir) # Figure plt.figure(figsize=(8, 5.0)) plt.clf() nrow, ncol = 2, 3 gs = gridspec.GridSpec(nrow, ncol) if slit_cen: plt.suptitle('Sky Comparison for Slit Center', y=1.05) else: plt.suptitle('Sky Comparison for {:s}'.format(specobj.NAME), y=1.05) for ii, igdsky in enumerate(gdsky): skyline = sky_lines[igdsky] ax = plt.subplot(gs[ii // ncol, ii % ncol]) # Norm pix1 = np.where(np.abs(sky_spec.wavelength - skyline) < dwv)[0] pix2 = np.where(np.abs(arx_spec.wavelength - skyline) < dwv)[0] f1 = np.sum(sky_spec.flux[pix1]) f2 = np.sum(arx_spec.flux[pix2]) norm = f1 / f2 # Plot ax.plot(sky_spec.wavelength[pix1], sky_spec.flux[pix1], 'k-', label='Obj', drawstyle='steps-mid') ax.plot(arx_spec.wavelength[pix2], arx_spec.flux[pix2] * norm, 'r-', label='Arx', drawstyle='steps-mid') # Axes ax.xaxis.set_major_locator(plt.MultipleLocator(dwv.value)) ax.set_xlabel('Wavelength') ax.set_ylabel('Counts') # Legend plt.legend(loc='upper left', scatterpoints=1, borderpad=0.3, handletextpad=0.3, fontsize='small', numpoints=1) # Finish plt.savefig(outfile, dpi=400) plt.close() msgs.info("Wrote spectral flexure QA: {}".format(outfile)) #plt.close() plt.rcdefaults()