def __init__(self, xcoef, ycoef, wavemin, wavemax, npix_y, xsigcoef = None, ysigcoef = None) : """ Lightweight wrapper for trace coordinates and wavelength solution Args: xcoef: 2D[ntrace, ncoef] Legendre coefficient of x as a function of wavelength ycoef: 2D[ntrace, ncoef] Legendre coefficient of y as a function of wavelength wavemin : float wavemax : float. wavemin and wavemax are used to define a reduced variable legx(wave,wavemin,wavemax)=2*(wave-wavemin)/(wavemax-wavemin)-1 used to compute the traces, xccd=legval(legx(wave,wavemin,wavemax),xtrace[fiber]) """ assert(xcoef.shape[0] == ycoef.shape[0]) if xsigcoef is not None : assert(xcoef.shape[0] == xsigcoef.shape[0]) if ysigcoef is not None : assert(xcoef.shape[0] == ysigcoef.shape[0]) self.nspec = xcoef.shape[0] self.wavemin = wavemin self.wavemax = wavemax self.npix_y = npix_y self.x_vs_wave_traceset = TraceSet(xcoef,[wavemin,wavemax]) self.y_vs_wave_traceset = TraceSet(ycoef,[wavemin,wavemax]) self.xsig_vs_wave_traceset = None self.ysig_vs_wave_traceset = None if xsigcoef is not None : self.xsig_vs_wave_traceset = TraceSet(xsigcoef,[wavemin,wavemax]) if ysigcoef is not None : self.ysig_vs_wave_traceset = TraceSet(ysigcoef,[wavemin,wavemax]) self.wave_vs_y_traceset = None
def write_psffile(infile, wcoeffs, wcoeffs_wavemin, wcoeffs_wavemax, outfile, wavestepsize=None): """ extract psf file, add wcoeffs, and make a new psf file preserving the traces etc. psf module will load this """ tset = read_xytraceset(infile) # convert wsigma to ysig ... nfiber = wcoeffs.shape[0] ncoef = wcoeffs.shape[1] nw = 100 # need a larger number than ncoef to get an accurate dydw from the gradients # wcoeffs and tset do not necessarily have the same wavelength range wave = np.linspace(tset.wavemin, tset.wavemax, nw) wsig_set = TraceSet(wcoeffs, [wcoeffs_wavemin, wcoeffs_wavemax]) wsig_vals = np.zeros((nfiber, nw)) for f in range(nfiber): y_vals = tset.y_vs_wave(f, wave) dydw = np.gradient(y_vals) / np.gradient(wave) wsig_vals[f] = wsig_set.eval(f, wave) * dydw tset.ysig_vs_wave_traceset = fit_traces(wave, wsig_vals, deg=ncoef - 1, domain=(tset.wavemin, tset.wavemax)) write_xytraceset(outfile, tset)
def write_psffile(infile,wcoeffs,wcoeffs_wavemin,wcoeffs_wavemax,outfile,wavestepsize=None): """ extract psf file, add wcoeffs, and make a new psf file preserving the traces etc. psf module will load this """ tset = read_xytraceset(infile) # convert wsigma to ysig ... nfiber = wcoeffs.shape[0] ncoef = wcoeffs.shape[1] nw = 100 # need a larger number than ncoef to get an accurate dydw from the gradients # wcoeffs and tset do not necessarily have the same wavelength range wave = np.linspace(tset.wavemin,tset.wavemax,nw) wsig_set = TraceSet(wcoeffs,[wcoeffs_wavemin,wcoeffs_wavemax]) wsig_vals = np.zeros((nfiber,nw)) for f in range(nfiber) : y_vals = tset.y_vs_wave(f,wave) dydw = np.gradient(y_vals)/np.gradient(wave) wsig_vals[f]=wsig_set.eval(f,wave)*dydw tset.ysig_vs_wave_traceset = fit_traces(wave, wsig_vals, deg=ncoef-1, domain=(tset.wavemin,tset.wavemax)) write_xytraceset(outfile,tset)
def __init__(self, xcoef, ycoef, wavemin, wavemax, npix_y, xsigcoef=None, ysigcoef=None, meta=None): """ Lightweight wrapper for trace coordinates and wavelength solution Args: xcoef: 2D[ntrace, ncoef] Legendre coefficient of x as a function of wavelength ycoef: 2D[ntrace, ncoef] Legendre coefficient of y as a function of wavelength wavemin : float wavemax : float. wavemin and wavemax are used to define a reduced variable legx(wave,wavemin,wavemax)=2*(wave-wavemin)/(wavemax-wavemin)-1 used to compute the traces, xccd=legval(legx(wave,wavemin,wavemax),xtrace[fiber]) """ from specter.util.traceset import TraceSet assert (xcoef.shape[0] == ycoef.shape[0]) if xsigcoef is not None: assert (xcoef.shape[0] == xsigcoef.shape[0]) if ysigcoef is not None: assert (xcoef.shape[0] == ysigcoef.shape[0]) self.nspec = xcoef.shape[0] self.wavemin = wavemin self.wavemax = wavemax self.npix_y = npix_y self.x_vs_wave_traceset = TraceSet(xcoef, [wavemin, wavemax]) self.y_vs_wave_traceset = TraceSet(ycoef, [wavemin, wavemax]) self.xsig_vs_wave_traceset = None self.ysig_vs_wave_traceset = None if xsigcoef is not None: self.xsig_vs_wave_traceset = TraceSet(xsigcoef, [wavemin, wavemax]) if ysigcoef is not None: self.ysig_vs_wave_traceset = TraceSet(ysigcoef, [wavemin, wavemax]) self.wave_vs_y_traceset = None self.meta = meta
def read_xytraceset(filename) : """ Reads traces in PSF fits file Args: filename : Path to input fits file which has to contain XTRACE and YTRACE HDUs Returns: XYTraceSet object """ log=get_logger() xcoef=None ycoef=None xsigcoef=None ysigcoef=None wsigmacoef=None wavemin=None wavemax=None log.info("reading traces in '%s'"%filename) fits_file = fits.open(filename) # npix_y, needed for boxcar extractions npix_y=0 for hdu in [0,"XTRACE","PSF"] : if npix_y > 0 : break if hdu in fits_file : head = fits_file[hdu].header if "NPIX_Y" in head : npix_y=int(head["NPIX_Y"]) if npix_y == 0 : raise KeyError("Didn't find head entry NPIX_Y in hdu 0, XTRACE or PSF") log.info("npix_y={}".format(npix_y)) try : psftype=fits_file[0].header["PSFTYPE"] except KeyError : psftype="" # now read trace coefficients log.info("psf is a '%s'"%psftype) if psftype == "bootcalib" : xcoef,wavemin,wavemax =_traceset_from_image(wavemin,wavemax,fits_file[0],"xcoef") ycoef,wavemin,wavemax =_traceset_from_image(wavemin,wavemax,fits_file[1],"ycoef") else : for k in ["XTRACE","XCOEF","XCOEFF"] : if k in fits_file : xcoef,wavemin,wavemax =_traceset_from_image(wavemin,wavemax,fits_file[k],"xcoef") for k in ["YTRACE","YCOEF","YCOEFF"] : if k in fits_file : ycoef,wavemin,wavemax =_traceset_from_image(wavemin,wavemax,fits_file[k],"ycoef") for k in ["XSIG"] : if k in fits_file : xsigcoef,wavemin,wavemax =_traceset_from_image(wavemin,wavemax,fits_file[k],"xsigcoef") for k in ["YSIG"] : if k in fits_file : ysigcoef,wavemin,wavemax =_traceset_from_image(wavemin,wavemax,fits_file[k],"ysigcoef") if "WSIGMA" in fits_file : wsigmacoef = fits_file["WSIGMA"].data if psftype == "GAUSS-HERMITE" : # older version where XTRACE and YTRACE are not saved in separate HDUs hdu=fits_file["PSF"] if xcoef is None : xcoef,wavemin,wavemax =_traceset_from_table(wavemin,wavemax,hdu,"X") if ycoef is None : ycoef,wavemin,wavemax =_traceset_from_table(wavemin,wavemax,hdu,"Y") if xsigcoef is None : xsigcoef,wavemin,wavemax =_traceset_from_table(wavemin,wavemax,hdu,"GHSIGX") if ysigcoef is None : ysigcoef,wavemin,wavemax =_traceset_from_table(wavemin,wavemax,hdu,"GHSIGY") log.info("wavemin={} wavemax={}".format(wavemin,wavemax)) if xcoef is None or ycoef is None : raise ValueError("could not find xcoef and ycoef in psf file %s"%filename) if xcoef.shape[0] != ycoef.shape[0] : raise ValueError("XCOEF and YCOEF don't have same number of fibers %d %d"%(xcoef.shape[0],ycoef.shape[0])) fits_file.close() if wsigmacoef is not None : log.warning("Converting deprecated WSIGMA coefficents (in Ang.) into YSIG (in CCD pixels)") nfiber = wsigmacoef.shape[0] ncoef = wsigmacoef.shape[1] nw = 100 # to get accurate dydw wave = np.linspace(wavemin,wavemax,nw) wsig_set = TraceSet(wsigmacoef,[wavemin,wavemax]) y_set = TraceSet(ycoef,[wavemin,wavemax]) wsig_vals = np.zeros((nfiber,nw)) for f in range(nfiber) : y_vals = y_set.eval(f,wave) dydw = np.gradient(y_vals)/np.gradient(wave) wsig_vals[f]=wsig_set.eval(f,wave)*dydw tset = fit_traces(wave, wsig_vals, deg=ncoef-1, domain=(wavemin,wavemax)) ysigcoef = tset._coeff return XYTraceSet(xcoef,ycoef,wavemin,wavemax,npix_y,xsigcoef=xsigcoef,ysigcoef=ysigcoef)
def process_arc(qframe, xytraceset, linelist=None, npoly=2, nbins=2): """ qframe: desispec.qframe.QFrame object xytraceset : desispec.xytraceset.XYTraceSet object linelist: line list to fit npoly: polynomial order for sigma expansion nbins: no of bins for the half of the fitting window return: xytraceset (with ysig vs wave) """ log = get_logger() if linelist is None: if qframe.meta is None or "CAMERA" not in qframe.meta: log.error( "no information about camera in qframe so I don't know which lines to use" ) raise RuntimeError( "no information about camera in qframe so I don't know which lines to use" ) camera = qframe.meta["CAMERA"] #- load arc lines from desispec.bootcalib import load_arcline_list, load_gdarc_lines, find_arc_lines llist = load_arcline_list(camera) dlamb, gd_lines = load_gdarc_lines(camera, llist) linelist = gd_lines log.info( "No line list configured. Fitting for lines {}".format(linelist)) tset = xytraceset assert (qframe.nspec == tset.nspec) tset.ysig_vs_wave_traceset = TraceSet(np.zeros((tset.nspec, npoly + 1)), [tset.wavemin, tset.wavemax]) for spec in range(tset.nspec): spec_wave = qframe.wave[spec] spec_linelist = linelist[(linelist > spec_wave[0]) & (linelist < spec_wave[-1])] meanwaves, emeanwaves, sigmas, esigmas = sigmas_from_arc( spec_wave, qframe.flux[spec], qframe.ivar[spec], spec_linelist, n=nbins) # convert from wavelength A unit to CCD pixel for consistency with specex PSF y = tset.y_vs_wave(spec, spec_wave) dydw = np.interp(meanwaves, spec_wave, np.gradient(y) / np.gradient(spec_wave)) sigmas *= dydw # A -> pixels esigmas *= dydw # A -> pixels ok = (sigmas > 0) & (esigmas > 0) try: thislegfit = Legendre.fit(meanwaves[ok], sigmas[ok], npoly, domain=[tset.wavemin, tset.wavemax], w=1. / esigmas[ok]**2) tset.ysig_vs_wave_traceset._coeff[spec] = thislegfit.coef except: log.error("legfit of psf width failed for spec {}".format(spec)) wave = np.linspace(tset.wavemin, tset.wavemax, 20) #plt.plot(wave,tset.ysig_vs_wave(spec,wave)) #plt.show() return xytraceset
def read_xytraceset(filename): """ Reads traces in PSF fits file Args: filename : Path to input fits file which has to contain XTRACE and YTRACE HDUs Returns: XYTraceSet object """ #- specter import isolated within function so specter only loaded if #- really needed from specter.util.traceset import TraceSet, fit_traces log = get_logger() xcoef = None ycoef = None xsigcoef = None ysigcoef = None wsigmacoef = None wavemin = None wavemax = None log.info("reading traces in '%s'" % filename) fits_file = fits.open(filename) # npix_y, needed for boxcar extractions npix_y = 0 for hdu in [0, "XTRACE", "PSF"]: if npix_y > 0: break if hdu in fits_file: head = fits_file[hdu].header if "NPIX_Y" in head: npix_y = int(head["NPIX_Y"]) if npix_y == 0: raise KeyError("Didn't find head entry NPIX_Y in hdu 0, XTRACE or PSF") log.debug("npix_y={}".format(npix_y)) try: psftype = fits_file[0].header["PSFTYPE"] except KeyError: psftype = "" # now read trace coefficients log.debug("psf is a '%s'" % psftype) if psftype == "bootcalib": xcoef, wavemin, wavemax = _traceset_from_image(wavemin, wavemax, fits_file[0], "xcoef") ycoef, wavemin, wavemax = _traceset_from_image(wavemin, wavemax, fits_file[1], "ycoef") else: for k in ["XTRACE", "XCOEF", "XCOEFF"]: if k in fits_file: xcoef, wavemin, wavemax = _traceset_from_image( wavemin, wavemax, fits_file[k], "xcoef") for k in ["YTRACE", "YCOEF", "YCOEFF"]: if k in fits_file: ycoef, wavemin, wavemax = _traceset_from_image( wavemin, wavemax, fits_file[k], "ycoef") for k in ["XSIG"]: if k in fits_file: xsigcoef, wavemin, wavemax = _traceset_from_image( wavemin, wavemax, fits_file[k], "xsigcoef") for k in ["YSIG"]: if k in fits_file: ysigcoef, wavemin, wavemax = _traceset_from_image( wavemin, wavemax, fits_file[k], "ysigcoef") if "WSIGMA" in fits_file: wsigmacoef = fits_file["WSIGMA"].data if psftype == "GAUSS-HERMITE": # older version where XTRACE and YTRACE are not saved in separate HDUs hdu = fits_file["PSF"] if xcoef is None: xcoef, wavemin, wavemax = _traceset_from_table( wavemin, wavemax, hdu, "X") if ycoef is None: ycoef, wavemin, wavemax = _traceset_from_table( wavemin, wavemax, hdu, "Y") if xsigcoef is None: xsigcoef, wavemin, wavemax = _traceset_from_table( wavemin, wavemax, hdu, "GHSIGX") if ysigcoef is None: ysigcoef, wavemin, wavemax = _traceset_from_table( wavemin, wavemax, hdu, "GHSIGY") log.debug("wavemin={} wavemax={}".format(wavemin, wavemax)) if xcoef is None or ycoef is None: raise ValueError("could not find xcoef and ycoef in psf file %s" % filename) if xcoef.shape[0] != ycoef.shape[0]: raise ValueError( "XCOEF and YCOEF don't have same number of fibers %d %d" % (xcoef.shape[0], ycoef.shape[0])) fits_file.close() if wsigmacoef is not None: log.warning( "Converting deprecated WSIGMA coefficents (in Ang.) into YSIG (in CCD pixels)" ) nfiber = wsigmacoef.shape[0] ncoef = wsigmacoef.shape[1] nw = 100 # to get accurate dydw wave = np.linspace(wavemin, wavemax, nw) wsig_set = TraceSet(wsigmacoef, [wavemin, wavemax]) y_set = TraceSet(ycoef, [wavemin, wavemax]) wsig_vals = np.zeros((nfiber, nw)) for f in range(nfiber): y_vals = y_set.eval(f, wave) dydw = np.gradient(y_vals) / np.gradient(wave) wsig_vals[f] = wsig_set.eval(f, wave) * dydw tset = fit_traces(wave, wsig_vals, deg=ncoef - 1, domain=(wavemin, wavemax)) ysigcoef = tset._coeff return XYTraceSet(xcoef, ycoef, wavemin, wavemax, npix_y, xsigcoef=xsigcoef, ysigcoef=ysigcoef)
class XYTraceSet(object): def __init__(self, xcoef, ycoef, wavemin, wavemax, npix_y, xsigcoef = None, ysigcoef = None) : """ Lightweight wrapper for trace coordinates and wavelength solution Args: xcoef: 2D[ntrace, ncoef] Legendre coefficient of x as a function of wavelength ycoef: 2D[ntrace, ncoef] Legendre coefficient of y as a function of wavelength wavemin : float wavemax : float. wavemin and wavemax are used to define a reduced variable legx(wave,wavemin,wavemax)=2*(wave-wavemin)/(wavemax-wavemin)-1 used to compute the traces, xccd=legval(legx(wave,wavemin,wavemax),xtrace[fiber]) """ assert(xcoef.shape[0] == ycoef.shape[0]) if xsigcoef is not None : assert(xcoef.shape[0] == xsigcoef.shape[0]) if ysigcoef is not None : assert(xcoef.shape[0] == ysigcoef.shape[0]) self.nspec = xcoef.shape[0] self.wavemin = wavemin self.wavemax = wavemax self.npix_y = npix_y self.x_vs_wave_traceset = TraceSet(xcoef,[wavemin,wavemax]) self.y_vs_wave_traceset = TraceSet(ycoef,[wavemin,wavemax]) self.xsig_vs_wave_traceset = None self.ysig_vs_wave_traceset = None if xsigcoef is not None : self.xsig_vs_wave_traceset = TraceSet(xsigcoef,[wavemin,wavemax]) if ysigcoef is not None : self.ysig_vs_wave_traceset = TraceSet(ysigcoef,[wavemin,wavemax]) self.wave_vs_y_traceset = None def x_vs_wave(self,fiber,wavelength) : return self.x_vs_wave_traceset.eval(fiber,wavelength) def y_vs_wave(self,fiber,wavelength) : return self.y_vs_wave_traceset.eval(fiber,wavelength) def xsig_vs_wave(self,fiber,wavelength) : if self.xsig_vs_wave_traceset is None : raise RuntimeError("no xsig coefficents were read in the PSF") return self.xsig_vs_wave_traceset.eval(fiber,wavelength) def ysig_vs_wave(self,fiber,wavelength) : if self.ysig_vs_wave_traceset is None : raise RuntimeError("no ysig coefficents were read in the PSF") return self.ysig_vs_wave_traceset.eval(fiber,wavelength) def wave_vs_y(self,fiber,y) : if self.wave_vs_y_traceset is None : self.wave_vs_y_traceset = self.y_vs_wave_traceset.invert() return self.wave_vs_y_traceset.eval(fiber,y) def x_vs_y(self,fiber,y) : return self.x_vs_wave(fiber,self.wave_vs_y(fiber,y)) def xsig_vs_y(self,fiber,y) : return self.xsig_vs_wave(fiber,self.wave_vs_y(fiber,y)) def ysig_vs_y(self,fiber,y) : return self.ysig_vs_wave(fiber,self.wave_vs_y(fiber,y)) """
class XYTraceSet(object): def __init__(self, xcoef, ycoef, wavemin, wavemax, npix_y, xsigcoef = None, ysigcoef = None, meta = None) : """ Lightweight wrapper for trace coordinates and wavelength solution Args: xcoef: 2D[ntrace, ncoef] Legendre coefficient of x as a function of wavelength ycoef: 2D[ntrace, ncoef] Legendre coefficient of y as a function of wavelength wavemin : float wavemax : float. wavemin and wavemax are used to define a reduced variable legx(wave,wavemin,wavemax)=2*(wave-wavemin)/(wavemax-wavemin)-1 used to compute the traces, xccd=legval(legx(wave,wavemin,wavemax),xtrace[fiber]) """ assert(xcoef.shape[0] == ycoef.shape[0]) if xsigcoef is not None : assert(xcoef.shape[0] == xsigcoef.shape[0]) if ysigcoef is not None : assert(xcoef.shape[0] == ysigcoef.shape[0]) self.nspec = xcoef.shape[0] self.wavemin = wavemin self.wavemax = wavemax self.npix_y = npix_y self.x_vs_wave_traceset = TraceSet(xcoef,[wavemin,wavemax]) self.y_vs_wave_traceset = TraceSet(ycoef,[wavemin,wavemax]) self.xsig_vs_wave_traceset = None self.ysig_vs_wave_traceset = None if xsigcoef is not None : self.xsig_vs_wave_traceset = TraceSet(xsigcoef,[wavemin,wavemax]) if ysigcoef is not None : self.ysig_vs_wave_traceset = TraceSet(ysigcoef,[wavemin,wavemax]) self.wave_vs_y_traceset = None self.meta = meta def x_vs_wave(self,fiber,wavelength) : return self.x_vs_wave_traceset.eval(fiber,wavelength) def y_vs_wave(self,fiber,wavelength) : return self.y_vs_wave_traceset.eval(fiber,wavelength) def xsig_vs_wave(self,fiber,wavelength) : if self.xsig_vs_wave_traceset is None : raise RuntimeError("no xsig coefficents were read in the PSF") return self.xsig_vs_wave_traceset.eval(fiber,wavelength) def ysig_vs_wave(self,fiber,wavelength) : if self.ysig_vs_wave_traceset is None : raise RuntimeError("no ysig coefficents were read in the PSF") return self.ysig_vs_wave_traceset.eval(fiber,wavelength) def wave_vs_y(self,fiber,y) : if self.wave_vs_y_traceset is None : self.wave_vs_y_traceset = self.y_vs_wave_traceset.invert() return self.wave_vs_y_traceset.eval(fiber,y) def x_vs_y(self,fiber,y) : return self.x_vs_wave(fiber,self.wave_vs_y(fiber,y)) def xsig_vs_y(self,fiber,y) : return self.xsig_vs_wave(fiber,self.wave_vs_y(fiber,y)) def ysig_vs_y(self,fiber,y) : return self.ysig_vs_wave(fiber,self.wave_vs_y(fiber,y)) """