def get_continuum(flux): # Fit continuum x = np.arange(len(flux)) ybin,bin_edges,binnumber = bindata.binned_statistic(x,flux,statistic='median',bins=10) xbin = bin_edges[0:-1]+(bin_edges[1]-bin_edges[0])*0.5 cont = dln.interp(xbin,ybin,x,extrapolate=True) return cont
def interp(self, x=None, xtype='wave', order=None): """ Interpolate onto a new wavelength scale and/or shift by a velocity.""" # if x is 2D and has multiple dimensions and the spectrum does as well # (with the same number of dimensions), and order=None, then it is assumed # that the input and output orders are "matched". # Check input xtype if (xtype.lower().find('wave') == -1) & (xtype.lower().find('pix') == -1): raise ValueError(xtype + ' not supported. Must be wave or pixel') # Convert pixels to wavelength if (xtype.lower().find('pix') > -1): wave = self.pix2wave(x, order=order) else: wave = x.copy() # How many orders in output wavelength if (wave.ndim == 1): nwpix = len(wave) nworder = 1 else: nwpix, nworder = wave.shape wave = wave.reshape(nwpix, nworder) # make 2D for order indexing # Loop over orders in final wavelength oflux = np.zeros((nwpix, nworder), float) oerr = np.zeros((nwpix, nworder), float) omask = np.zeros((nwpix, nworder), bool) osigma = np.zeros((nwpix, nworder), float) for i in range(nworder): # Interpolate onto the final wavelength scale wave1 = wave[:, i] wr1 = dln.minmax(wave1) # Make spectrum arrays 2D for order indexing, [Npix,Norder] swave = self.wave.reshape(self.npix, self.norder) sflux = self.flux.reshape(self.npix, self.norder) serr = self.err.reshape(self.npix, self.norder) # convert mask to integer 0 or 1 if hasattr(self, 'mask'): smask = np.zeros((self.npix, self.norder), int) smask_bool = self.err.reshape(self.npix, self.norder) smask[smask_bool == True] = 1 else: smask = np.zeros((self.npix, self.norder), int) # The orders are "matched", one input for one output order if (nworder == self.norder) & (order is None): swave1 = swave[:, i] sflux1 = sflux[:, i] serr1 = serr[:, i] ssigma1 = self.lsf.sigma(order=i) smask1 = smask[:, i] # Some overlap if (np.min(swave1) < wr1[1]) & (np.max(swave1) > wr1[0]): # Fix NaN pixels bd, nbd = dln.where(np.isfinite(sflux1) == False) if nbd > 0: sflux1[bd] = 1.0 serr1[bd] = 1e30 smask1[bd] = 1 ind, nind = dln.where((wave1 > np.min(swave1)) & (wave1 < np.max(swave1))) oflux[ind, i] = dln.interp(swave1, sflux1, wave1[ind], extrapolate=False, assume_sorted=False) oerr[ind, i] = dln.interp(swave1, serr1, wave1[ind], extrapolate=False, assume_sorted=False, kind='linear') osigma[ind, i] = dln.interp(swave1, ssigma1, wave1[ind], extrapolate=False, assume_sorted=False) # Gauss-Hermite, convert to wavelength units if self.lsf.lsftype == 'Gauss-Hermite': sdw1 = np.abs(swave1[1:] - swave1[0:-1]) sdw1 = np.hstack((sdw1, sdw1[-1])) dw = dln.interp(swave1, sdw1, wave1[ind], extrapolate=False, assume_sorted=False) osigma[ind, i] *= dw # in Ang mask_interp = dln.interp(swave1, smask1, wave1[ind], extrapolate=False, assume_sorted=False) mask_interp_bool = np.zeros(nind, bool) mask_interp_bool[mask_interp > 0.4] = True omask[ind, i] = mask_interp_bool # Loop over all spectrum orders else: # Loop over spectrum orders for j in range(self.norder): swave1 = swave[:, j] sflux1 = sflux[:, j] serr1 = serr[:, j] ssigma1 = self.lsf.sigma(order=j) smask1 = smask[:, j] # Some overlap if (np.min(swave1) < wr1[1]) & (np.max(swave1) > wr1[0]): ind, nind = dln.where((wave1 > np.min(swave1)) & (wave1 < np.max(swave1))) oflux[ind, i] = dln.interp(swave1, sflux1, wave1[ind], extrapolate=False, assume_sorted=False) oerr[ind, i] = dln.interp(swave1, serr1, wave1[ind], extrapolate=False, assume_sorted=False, kind='linear') osigma[ind, i] = dln.interp(swave1, ssigma1, wave1[ind], extrapolate=False, assume_sorted=False) mask_interp = dln.interp(swave1, smask1, wave1[ind], extrapolate=False, assume_sorted=False) mask_interp_bool = np.zeros(nind, bool) mask_interp_bool[mask_interp > 0.4] = True omask[ind, i] = mask_interp_bool # Currently this does NOT deal with the overlap of multiple orders (e.g. averaging) # Flatten if 1D if (x.ndim == 1): wave = wave.flatten() oflux = oflux.flatten() oerr = oerr.flatten() osigma = osigma.flatten() omask = omask.flatten() # Create output spectrum object if self.lsf.lsftype == 'Gauss-Hermite': # Can't interpolate Gauss-Hermite LSF yet # instead convert to a Gaussian approximation in wavelength units #print('Cannot interpolate Gauss-Hermite LSF yet') lsfxtype = 'wave' else: lsfxtype = self.lsf.xtype ospec = Spec1D(oflux, wave=wave, err=oerr, mask=omask, lsftype='Gaussian', lsfxtype=lsfxtype, lsfsigma=osigma) return ospec
def continuum(spec, norder=6, perclevel=90.0, binsize=0.1, interp=True): """ Measure the continuum of a spectrum. Parameters ---------- spec : Spec1D object A spectrum object. This at least needs to have a FLUX and WAVE attribute. norder : float, optional Polynomial order to use for the continuum fitting. The default is 6. perclevel : float, optional Percent level to use for the continuum value in large bins. Default is 90. binsize : float, optional Fraction of the wavelength range (scaled to -1 to +1) to bin. Default is 0.1. interp : bool, optional Use interpolation of the binned values instead of a polynomial fit. Default is True. Returns ------- cont : numpy array The continuum array, in the same shape as the input flux. Examples -------- .. code-block:: python cont = continuum(spec) """ wave = spec.wave.copy().reshape(spec.npix, spec.norder) # make 2D flux = spec.flux.copy().reshape(spec.npix, spec.norder) # make 2D err = spec.err.copy().reshape(spec.npix, spec.norder) # make 2D mask = spec.mask.copy().reshape(spec.npix, spec.norder) # make 2D cont = err.copy() * 0.0 + 1 for o in range(spec.norder): w = wave[:, o].copy() wr = [np.min(w), np.max(w)] x = (w - np.mean(wr)) / dln.valrange(wr) * 2 # -1 to +1 y = flux[:, o].copy() m = mask[:, o].copy() # Divide by median medy = np.nanmedian(y) if medy <= 0.0: medy = 1.0 y /= medy # Perform sigma clipping out large positive outliers coef = dln.poly_fit(x[~m], y[~m], 2, robust=True) sig = dln.mad(y - dln.poly(x, coef)) bd, nbd = dln.where((y - dln.poly(x, coef)) > 5 * sig) if nbd > 0: m[bd] = True gdmask = (y > 0) & (m == False) # need positive fluxes and no mask set if np.sum(gdmask) == 0: continue # Bin the data points xr = [np.nanmin(x), np.nanmax(x)] bins = int(np.ceil((xr[1] - xr[0]) / binsize)) ybin, bin_edges, binnumber = bindata.binned_statistic( x[gdmask], y[gdmask], statistic='percentile', percentile=perclevel, bins=bins, range=None) xbin = bin_edges[0:-1] + 0.5 * binsize # Interpolate to full grid if interp is True: fnt = np.isfinite(ybin) cont1 = dln.interp(xbin[fnt], ybin[fnt], x, kind='quadratic', extrapolate=True, exporder=1) else: coef = dln.poly_fit(xbin, ybin, norder) cont1 = dln.poly(x, coef) cont1 *= medy cont[:, o] = cont1 # Flatten to 1D if norder=1 if spec.norder == 1: cont = cont.flatten() return cont
def __call__(self, labels, order=None, norm=True, fluxonly=False, wave=None, rv=None): # Default is to return all orders # order can also be a list or array of orders # Orders to loop over if order is None: orders = np.arange(self.norder) else: orders = list(np.atleast_1d(order)) norders = dln.size(orders) # Get maximum number of pixels over all orders npix = 0 for i in range(self.norder): npix = np.maximum(npix, self._data[i].dispersion.shape[0]) # Wavelength array input if wave is not None: if wave.ndim == 1: wnorders = 1 else: wnorders = wave.shape[1] if wnorders != norders: raise ValueError( "Number of orders in WAVE must match orders in the model") npix = wave.shape[0] # Initialize output arrays oflux = np.zeros((npix, norders), np.float32) + np.nan owave = np.zeros((npix, norders), np.float64) omask = np.zeros((npix, norders), bool) + True # Order loop for i in orders: if wave is None: owave1 = self._data[ i].dispersion # final wavelength array for this order else: if wave.ndim == 1: owave1 = wave.copy() else: owave1 = wave[:, i] # Get model and add radial velocity if necessary if (rv is None) & (wave is None): m = self._data[i] f = m(labels) zfactor = 1 else: m = self._data_nointerp[i] f0 = m(labels) zfactor = 1 + rv / cspeed # redshift factor zwave = m.dispersion * zfactor # redshift the wavelengths f = np.zeros(len(owave1), np.float32) + np.nan gind, ngind = dln.where( (owave1 >= np.min(zwave)) & (owave1 <= np.max(zwave))) # wavelengths we can cover if ngind > 0: f[gind] = dln.interp(zwave, f0, owave1[gind]) # Get Continuum if (norm is False): if hasattr(m, 'continuum'): contmodel = m.continuum smallcont = contmodel(labels) if contmodel._logflux is True: smallcont = 10**smallcont # Interpolate to the full spectrum wavelength array # with any redshift cont = dln.interp(contmodel.dispersion * zfactor, smallcont, owave1) # Now mulitply the flux array by the continuum f *= cont else: raise ValueError( "Model does not have continuum information") # Stuff in the array oflux[0:len(f), i] = f owave[0:len(f), i] = owave1 omask[0:len(f), i] = False # Only return the flux if fluxonly is True: return oflux # Change single order 2D arrays to 1D if norders == 1: oflux = oflux.flatten() owave = owave.flatten() omask = omask.flatten() # Create Spec1D object mspec = Spec1D(oflux, err=oflux * 0.0, wave=owave, mask=omask, lsfsigma=None, instrument='Model') mspec.teff = labels[0] mspec.logg = labels[1] mspec.feh = labels[2] #mspec.rv = rv mspec.snr = np.inf return mspec
def prepare_cannon_model(model, spec, dointerp=False): """ This prepares a Cannon model or list of models for an observed spectrum. The models are trimmed, rebinned, convolved to the spectrum LSF's and finally interpolated onto the spectrum's wavelength scale. The final interpolation can be omitted by setting dointerp=False (the default). This can be useful if the model is to interpolated multiple times on differen wavelength scales (e.g. for multiple velocities or logarithmic wavelength scales for cross-correlation). Parameters ---------- model : Cannon model or list The Cannon model(s) to prepare for "spec". spec : Spec1D object The observed spectrum for which to prepare the Cannon model(s). dointerp : bool, optional Do the interpolation onto the observed wavelength scale. The default is False. Returns ------- omodel : Cannon model or list The Cannon model or list of models prepared for "spec". Examples -------- omodel = prepare_cannon_model(model,spec) """ if spec.wave is None: raise Exception('No wavelength in observed spectrum') if spec.lsf is None: raise Exception('No LSF in observed spectrum') if type(model) is not list: if model.dispersion is None: raise Exception('No model wavelength information') if type(model) is list: outmodel = [] for i in range(len(model)): model1 = model[i] omodel1 = prepare_cannon_model(model1, spec, dointerp=dointerp) outmodel.append(omodel1) else: # Convert wavelength from air->vacuum or vice versa if model.wavevac != spec.wavevac: # Air -> Vacuum if spec.wavevac is True: model.dispersion = astro.airtovac(model.dispersion) model.wavevac = True # Vacuum -> Air else: model.dispersion = astro.vactoair(model.dispersion) model.wavevac = False # Observed spectrum values wave = spec.wave ndim = wave.ndim if ndim == 2: npix, norder = wave.shape if ndim == 1: norder = 1 npix = len(wave) wave = wave.reshape(npix, norder) # Loop over the orders outmodel = [] for o in range(norder): w = wave[:, o] w0 = np.min(w) w1 = np.max(w) dw = dln.slope(w) dw = np.hstack((dw, dw[-1])) dw = np.abs(dw) meddw = np.median(dw) npix = len(w) if (np.min(model.dispersion) > w0) | (np.max(model.dispersion) < w1): raise Exception( 'Model does not cover the observed wavelength range') # Trim nextend = int(np.ceil(len(w) * 0.25)) # extend 25% on each end nextend = np.maximum(nextend, 200) # or 200 pixels if (np.min(model.dispersion) < (w0 - nextend * meddw)) | (np.max(model.dispersion) > (w1 + nextend * meddw)): tmodel = trim_cannon_model(model, w0=w0 - nextend * meddw, w1=w1 + nextend * meddw) #if (np.min(model.dispersion)<(w0-50*meddw)) | (np.max(model.dispersion)>(w1+50*meddw)): # tmodel = trim_cannon_model(model,w0=w0-20*meddw,w1=w1+20*meddw) #if (np.min(model.dispersion)<(w0-1000.0*w0/cspeed)) | (np.max(model.dispersion)>(w1+1000.0*w1/cspeed)): # # allow for up to 1000 km/s offset on each end # tmodel = trim_cannon_model(model,w0=w0-1000.0*w0/cspeed,w1=w1+1000.0*w1/cspeed) tmodel.trim = True else: tmodel = cannon_copy(model) tmodel.trim = False # Rebin # get LSF FWHM (A) for a handful of positions across the spectrum xp = np.arange(npix // 20) * 20 fwhm = spec.lsf.fwhm(w[xp], xtype='Wave', order=o) # FWHM is in units of lsf.xtype, convert to wavelength/angstroms, if necessary if spec.lsf.xtype.lower().find('pix') > -1: dw1 = dln.interp(w, dw, w[xp], assume_sorted=False) fwhm *= dw1 # convert FWHM (A) in number of model pixels at those positions dwmod = dln.slope(tmodel.dispersion) dwmod = np.hstack((dwmod, dwmod[-1])) xpmod = interp1d(tmodel.dispersion, np.arange(len(tmodel.dispersion)), kind='cubic', bounds_error=False, fill_value=(np.nan, np.nan), assume_sorted=False)(w[xp]) xpmod = np.round(xpmod).astype(int) fwhmpix = fwhm / dwmod[xpmod] # need at least ~4 pixels per LSF FWHM across the spectrum # using 3 affects the final profile shape nbin = np.round(np.min(fwhmpix) // 4).astype(int) if nbin == 0: import pdb nbin = 1 print(spec.filename, 'Model has lower resolution than the observed spectrum', fwhmpix.min()) #raise Exception('Model has lower resolution than the observed spectrum',spec.filename,fwhmpix.min()) if nbin > 1: rmodel = rebin_cannon_model(tmodel, nbin) rmodel.rebin = True else: rmodel = cannon_copy(tmodel) rmodel.rebin = False # Convolve lsf = spec.lsf.anyarray(rmodel.dispersion, xtype='Wave', order=o) #import pdb; pdb.set_trace() cmodel = convolve_cannon_model(rmodel, lsf) cmodel.convolve = True # Interpolate if dointerp is True: omodel = interp_cannon_model(cmodel, wout=spec.wave) omodel.interp = True else: omodel = cannon_copy(cmodel) omodel.interp = False # Order information omodel.norder = norder omodel.order = o # # Copy continuum information # if hasattr(model,'continuum'): # omodel.continuum = cannon_copy(model.continuum) # Append to final output outmodel.append(omodel) # Single-element list if (type(outmodel) is list) & (len(outmodel) == 1): outmodel = outmodel[0] return outmodel
def normalize(self, ncorder=6, perclevel=0.95): """ Normalize the spectrum. Parameters ---------- ncorder : float, optional Polynomial order to use for the continuum fitting. The default is 6. perclevel : float, optional Percent level (1.0 for 100%) to use for the continuum value in large bins. Default is 0.95. Returns ------- The flux and err arrays will normalized (divided) by the continuum and the continuum saved in cont. The normalized property is set to True. Examples -------- spec.normalize() """ self._flux = self.flux # Save the original #nspec, cont, masked = normspec(self,ncorder=ncorder,perclevel=perclevel) binsize = 0.10 perclevel = 90.0 wave = self.wave.copy().reshape(self.npix, self.norder) # make 2D flux = self.flux.copy().reshape(self.npix, self.norder) # make 2D err = self.err.copy().reshape(self.npix, self.norder) # make 2D mask = self.mask.copy().reshape(self.npix, self.norder) # make 2D cont = err.copy() * 0.0 + 1 for o in range(self.norder): w = wave[:, o].copy() x = (w - np.median(w)) / (np.max(w * 0.5) - np.min(w * 0.5) ) # -1 to +1 y = flux[:, o].copy() m = mask[:, o].copy() # Divide by median medy = np.nanmedian(y) y /= medy # Perform sigma clipping out large positive outliers coef = dln.poly_fit(x, y, 2, robust=True) sig = dln.mad(y - dln.poly(x, coef)) bd, nbd = dln.where((y - dln.poly(x, coef)) > 5 * sig) if nbd > 0: m[bd] = True gdmask = (y > 0) & (m == False ) # need positive fluxes and no mask set # Bin the data points xr = [np.nanmin(x), np.nanmax(x)] bins = np.ceil((xr[1] - xr[0]) / binsize) + 1 ybin, bin_edges, binnumber = bindata.binned_statistic( x[gdmask], y[gdmask], statistic='percentile', percentile=perclevel, bins=bins, range=None) xbin = bin_edges[0:-1] + 0.5 * binsize # Interpolate to full grid fnt = np.isfinite(ybin) cont1 = dln.interp(xbin[fnt], ybin[fnt], x, extrapolate=True) cont1 *= medy flux[:, o] /= cont1 err[:, o] /= cont1 cont[:, o] = cont1 # Flatten to 1D if norder=1 if self.norder == 1: flux = flux.flatten() err = err.flatten() cont = cont.flatten() # Stuff back in self.flux = flux self.err = err self.cont = cont self.normalized = True return
def model_interp(teff, logg, metal, mtype='odfnew'): """ Interpolate Kurucz model.""" availteff = np.arange(27) * 250 + 3500.0 availlogg = np.arange(11) * .5 + 0. availmetal = np.arange(7) * 0.5 - 2.5 if mtype is None: mtype = 'odfnew' if mtype == 'old': availmetal = np.arange(13) * 0.5 - 5.0 if mtype == 'old': ntau = 64 else: ntau = 72 if mtype == 'odfnew' and teff > 10000: avail = Table.read(modeldir() + 'tefflogg.txt', format='ascii') avail['col1'].name = 'teff' avail['col2'].name = 'logg' availteff = avail['teff'].data availlogg = avail['logg'].data v1, nv1 = dln.where((abs(availteff - teff) < 0.1) & (abs(availlogg - logg) <= 0.001)) v2 = v1 v3, nv3 = dln.where(abs(availmetal - metal) <= 0.001) else: v1, nv1 = dln.where(abs(availteff - teff) <= .1) v2, nv2 = dln.where(abs(availlogg - logg) <= 0.001) v3, nv3 = dln.where(abs(availmetal - metal) <= 0.001) # Linear Interpolation teffimif = max(np.where(availteff <= teff)[0]) # immediately inferior Teff loggimif = max(np.where(availlogg <= logg)[0]) # immediately inferior logg metalimif = max( np.where(availmetal <= metal)[0]) #immediately inferior [Fe/H] teffimsu = min(np.where(availteff >= teff)[0]) # immediately superior Teff loggimsu = min(np.where(availlogg >= logg)[0]) # immediately superior logg metalimsu = min( np.where(availmetal >= metal)[0]) #immediately superior [Fe/H] if mtype == 'old': ncols = 7 else: ncols = 10 grid = np.zeros((2, 2, 2, ncols), dtype=np.float64) tm1 = availteff[teffimif] tp1 = availteff[teffimsu] lm1 = availlogg[loggimif] lp1 = availlogg[loggimsu] mm1 = availmetal[metalimif] mp1 = availmetal[metalimsu] if (tp1 != tm1): mapteff = (teff - tm1) / (tp1 - tm1) else: mapteff = 0.5 if (lp1 != lm1): maplogg = (logg - lm1) / (lp1 - lm1) else: maplogg = 0.5 if (mp1 != mm1): mapmetal = (metal - mm1) / (mp1 - mm1) else: mapmetal = 0.5 # Reading the corresponding models for i in np.arange(8) + 1: if i == 1: model, header, tail = read_kurucz(tm1, lm1, mm1, mtype=mtype) if i == 2: model, h, t = read_kurucz(tm1, lm1, mp1, mtype=mtype) if i == 3: model, h, t = read_kurucz(tm1, lp1, mm1, mtype=mtype) if i == 4: model, h, t = read_kurucz(tm1, lp1, mp1, mtype=mtype) if i == 5: model, h, t = read_kurucz(tp1, lm1, mm1, mtype=mtype) if i == 6: model, h, t = read_kurucz(tp1, lm1, mp1, mtype=mtype) if i == 7: model, h, t = read_kurucz(tp1, lp1, mm1, mtype=mtype) if i == 8: model, h, t = read_kurucz(tp1, lp1, mp1, mtype=mtype) if (len(model[0, :]) > ntau): m2 = np.zeros((ncols, ntau), dtype=np.float64) m2[0, :] = interpol(model[0, :], ntau) for j in range(ncols): m2[j, :] = interpol(model[j, :], model[0, :], m2[0, :]) model = m2 # getting the tauross scale rhox = model[0, :] kappaross = model[4, :] tauross = np.zeros(ntau, dtype=np.float64) tauross[0] = rhox[0] * kappaross[0] for ii in np.arange(ntau - 1) + 1: tauross[ii] = trapz(rhox[0:ii + 1], kappaross[0:ii + 1]) if i == 1: model1 = model tauross1 = tauross elif i == 2: model2 = model tauross2 = tauross elif i == 3: model3 = model tauross3 = tauross elif i == 4: model4 = model tauross4 = tauross elif i == 5: model5 = model tauross5 = tauross elif i == 6: model6 = model tauross6 = tauross elif i == 7: model7 = model tauross7 = tauross elif i == 8: model8 = model tauross8 = tauross else: print('% KMOD: i should be 1--8!') model = np.zeros((ncols, ntau), dtype=np.float64) # cleaning up for re-using the matrix # Defining the mass (RHOX#gr cm-2) sampling tauross = tauross1 # re-using the vector tauross bot_tauross = min([ tauross1[ntau - 1], tauross2[ntau - 1], tauross3[ntau - 1], tauross4[ntau - 1], tauross5[ntau - 1], tauross6[ntau - 1], tauross7[ntau - 1], tauross8[ntau - 1] ]) top_tauross = max([ tauross1[0], tauross2[0], tauross3[0], tauross4[0], tauross5[0], tauross6[0], tauross7[0], tauross8[0] ]) g, = np.where((tauross >= top_tauross) & (tauross <= bot_tauross)) tauross_new = dln.interp(np.linspace(0, 1, len(g)), tauross[g], np.linspace(0, 1, ntau), kind='linear') # Let's interpolate for every depth points = (np.arange(2), np.arange(2), np.arange(2)) for i in np.arange(ntau - 1) + 1: for j in range(ncols): grid[0, 0, 0, j] = dln.interp(tauross1[1:], model1[j, 1:], tauross_new[i], kind='linear') grid[0, 0, 1, j] = dln.interp(tauross2[1:], model2[j, 1:], tauross_new[i], kind='linear') grid[0, 1, 0, j] = dln.interp(tauross3[1:], model3[j, 1:], tauross_new[i], kind='linear') grid[0, 1, 1, j] = dln.interp(tauross4[1:], model4[j, 1:], tauross_new[i], kind='linear') grid[1, 0, 0, j] = dln.interp(tauross5[1:], model5[j, 1:], tauross_new[i], kind='linear') grid[1, 0, 1, j] = dln.interp(tauross6[1:], model6[j, 1:], tauross_new[i], kind='linear') grid[1, 1, 0, j] = dln.interp(tauross7[1:], model7[j, 1:], tauross_new[i], kind='linear') grid[1, 1, 1, j] = dln.interp(tauross8[1:], model8[j, 1:], tauross_new[i], kind='linear') model[j, i] = interpn(points, grid[:, :, :, j], (mapteff, maplogg, mapmetal), method='linear') for j in range(ncols): model[j, 0] = model[j, 1] * 0.999 # Editing the header header[0] = strput(header[0], '%7.0f' % teff, 4) header[0] = strput(header[0], '%8.5f' % logg, 21) tmpstr1 = header[1] tmpstr2 = header[4] if (metal < 0.0): if type == 'old': header[1] = strput(header[1], '-%3.1f' % abs(metal), 18) else: header[1] = strput(header[1], '-%3.1f' % abs(metal), 8) header[4] = strput(header[4], '%9.5f' % 10**metal, 16) else: if type == 'old': header[1] = strput(header[1], '+%3.1f' % abs(metal), 18) else: header[1] = strput(header[1], '+%3.1f' % abs(metal), 8) header[4] = strput(header[4], '%9.5f' % 10**metal, 16) header[22] = strput(header[22], '%2i' % ntau, 11) return model, header, tail
lag = np.arange(2 * maxlag + 1) - maxlag rflux = refspec.spec1d_flux.value rflux[refspec.spec1d_uncertainty.quantity.value > 900] = np.nan sflux = spec.spec1d_flux.value sflux[spec.spec1d_uncertainty.quantity.value > 900] = np.nan # cross-correlate chunks nchunks = 5 xshift = np.zeros(nchunks, float) mnx = np.zeros(nchunks, float) nbin = npix // nchunks for k in range(nchunks): mnx[k] = np.mean([k * nbin, (k + 1) * nbin - 1]) cc = ccorrelate(rflux[k * nbin:(k + 1) * nbin], sflux[k * nbin:(k + 1) * nbin], lag) lag2 = np.linspace(-maxlag, maxlag, 1000) cc2 = dln.interp(lag, cc, lag2) bestshift = lag2[np.argmax(cc2)] xshift[k] = bestshift print('shifts = ' + str(xshift)) cc = ccorrelate(rflux, sflux, lag) lag2 = np.linspace(-maxlag, maxlag, 1000) cc2 = dln.interp(lag, cc, lag2) bestshift = lag2[np.argmax(cc2)] print('best shift = ' + str(bestshift)) # Shift reference wavelength array refwav = refap.func(x) drefwav = refwav[1:] - refwav[0:-1] drefwav = np.append(drefwav, drefwav[-1]) xshiftall = dln.interp(mnx, xshift, np.arange(npix)) #wav0 = refwav.copy() - drefwav*xshiftall wav0 = refwav.copy() - drefwav * bestshift
def getaplines(idlines,apinfo,ap,diag=False): #aperture = [id.aperture for id in idlines] #aperture = np.array(aperture) apind, = np.where(apinfo['aperture']==ap) apind = apind[0] idline1 = idlines[apind] goodap, = np.where(apinfo['good']==True) # This is a "good" aperture if apind in goodap: #print(str(ap)+' is a good aperture') nlines = len(idline1.fit_lines.fit) linecat = np.zeros(nlines,dtype=np.dtype([('aperture',int),('num',int),('height',float),('center',float), ('sigma',float),('wave0',float),('wave',float),('match',int),('outlier',bool)])) linecat['aperture'][:] = idline1.aperture linecat['num'][:] = np.arange(nlines)+1 for j in range(nlines): linecat['height'][j] = idline1.fit_lines.fit[j].parameters[0] linecat['center'][j] = idline1.fit_lines.fit[j].parameters[1] linecat['sigma'][j] = idline1.fit_lines.fit[j].parameters[2] wav = idline1.wav[j] if isinstance(wav,float): linecat['wave'][j] = wav linecat['match'][j] = 1 return linecat print(str(ap)+' is a bad aperture') # This is a "bad" aperture spec = extract1d_array[apind] # Find a neighboring good aperture bestind = goodap[np.argmin(np.abs(apinfo['aperture'][apind]-apinfo['aperture'][goodap]))] #if idline1.aperture==61: # bestind = 62 print('Best reference aperture: '+str(apinfo['aperture'][bestind])) refap = idlines[bestind] refspec = extract1d_array[bestind] # Cross-correlate the spectra to get shift maxlag = 10 lag = np.arange(2*maxlag+1)-maxlag rflux = refspec.spec1d_flux.value rflux[refspec.spec1d_uncertainty.quantity.value>900] = np.nan sflux = spec.spec1d_flux.value sflux[spec.spec1d_uncertainty.quantity.value>900] = np.nan # cross-correlate chunks nchunks = 5 xshift = np.zeros(nchunks,float) mnx = np.zeros(nchunks,float) nbin = npix//nchunks for k in range(nchunks): mnx[k] = np.mean([k*nbin,(k+1)*nbin-1]) cc = ccorrelate(rflux[k*nbin:(k+1)*nbin],sflux[k*nbin:(k+1)*nbin],lag) lag2 = np.linspace(-maxlag,maxlag,1000) cc2 = dln.interp(lag,cc,lag2) bestshift = lag2[np.argmax(cc2)] xshift[k] = bestshift print('shifts = '+str(xshift)) cc = ccorrelate(rflux,sflux,lag) lag2 = np.linspace(-maxlag,maxlag,1000) cc2 = dln.interp(lag,cc,lag2) bestshift = lag2[np.argmax(cc2)] print('best shift = '+str(bestshift)) #if ap==61: # #plt.plot(x,rflux) # #plt.plot(x+bestshift,sflux) # #plt.show() # #import pdb; pdb.set_trace() # print('KLUDGE for aperture 61') # bestshift = 0.0 if diag==True: plt.plot(x,rflux) plt.plot(x-bestshift,sflux) plt.show() # Shift reference wavelength array refwav = refap.func(x) drefwav = refwav[1:]-refwav[0:-1] drefwav = np.append(drefwav,drefwav[-1]) xshiftall = dln.interp(mnx,xshift,np.arange(npix)) #wav0 = refwav.copy() - drefwav*xshiftall wav0 = refwav.copy() - drefwav*bestshift # Get initial wavelengths for the lines nlines = len(idline1.fit_lines.fit) linecat = np.zeros(nlines,dtype=np.dtype([('aperture',int),('num',int),('height',float),('center',float), ('sigma',float),('wave0',float),('wave',float),('match',int),('outlier',bool)])) linecat['aperture'][:] = idline1.aperture linecat['num'][:] = np.arange(nlines)+1 for j in range(nlines): linecat['height'][j] = idline1.fit_lines.fit[j].parameters[0] linecat['center'][j] = idline1.fit_lines.fit[j].parameters[1] linecat['sigma'][j] = idline1.fit_lines.fit[j].parameters[2] linecat['wave0'][:] = dln.interp(x,wav0,linecat['center']) # Try to ID them using the template lines match = np.zeros(ntlines,int)-1 dwave = np.zeros(ntlines,float)-1 for j in range(ntlines): bestline = np.argmin(np.abs(tlinecat['wave'][j]-linecat['wave0'])) maxdiff = 0.5 #1.0 if np.abs(tlinecat['wave'][j]-linecat['wave0'][bestline])<maxdiff: match[j] = bestline dwave[j] = tlinecat['wave'][j]-linecat['wave0'][bestline] gmatch, = np.where(match>-1) linecat['wave'][match[gmatch]] = tlinecat['wave'][gmatch] linecat['match'][match[gmatch]] = 1 return linecat
def skytweak(extfile,skyfile,plugfile,diag=False): """ Tweak the Sky subtraction.""" #extfile = 'ut20131123/b0236_extract1d_array.pickle' #skyfile = 'ut20131123/b0236_sky_array.pickle' #plugfile = 'ut20131123/b0236_plugmap.pickle' print('Tweaking sky subtraction') print('extract1d file = '+extfile) print('sky file = '+skyfile) print('plugmap file = '+plugfile) if os.path.exists(extfile)==False: raise ValueError(extfile+' NOT FOUND') if os.path.exists(skyfile)==False: raise ValueError(skyfile+' NOT FOUND') if os.path.exists(plugfile)==False: raise ValueError(plugfile+' NOT FOUND') ext = pickle.load(open(extfile,'rb')) sky = pickle.load(open(skyfile,'rb')) plugmap = pickle.load(open(plugfile,'rb')) #skysub = pickle.load(open('ut20131123/b0236_skysubtract_array.pickle','rb')) #ext1d = pickle.load(open('ut20131123/b0236_extract1d_array.pickle','rb')) #ext = pickle.load(open('ut20131123/b0236_throughputcorr_array.pickle','rb')) #thru = pickle.load(open('ut20131123/b0236_throughput_array.pickle','rb')) #plugmap = pickle.load(open('ut20131123/b0236_plugmap.pickle','rb')) nap = len(ext) outsky = [] outskysub = [] outscale = [] # Loop over apertures for i in range(nap): ext1 = ext[i] sky1 = sky[i] if np.nansum(sky1.spec1d_flux.value)>0: skycont = get_continuum(sky1.spec1d_flux.value) mask = ((sky1.spec1d_flux.value-skycont) > 25) & np.isfinite(ext1.spec1d_flux.value) & np.isfinite(sky1.spec1d_flux.value) scaling = np.linspace(0.2,2.0,50) resid = np.zeros(len(scaling),float) for j in range(len(scaling)): resid[j] = skyresid(ext1.spec1d_flux.value,sky1.spec1d_flux.value,scaling[j],mask) scaling2 = np.linspace(0.2,2.0,1000) resid2 = dln.interp(scaling,resid,scaling2) bestind = np.argmin(np.abs(resid2)) bestscale = scaling2[bestind] print(str(i+1)+' '+str(bestscale)) outscale.append(bestscale) outsky1 = deepcopy(sky1) outsky1.spec1d_flux *= bestscale outsky1.spec1d_uncertainty.array *= bestscale outsky.append(outsky1) outskysub1 = deepcopy(ext1) outskysub1.spec1d_flux -= outsky1.spec1d_flux outskysub.append(outskysub1) eflux = ext1.spec1d_flux.value.copy() sflux = sky1.spec1d_flux.value.copy() bestresid = eflux-sflux*bestscale # Fit continuum and subtract bestcont = get_continuum(bestresid) if diag==True: eflux[(~np.isfinite(eflux)) | (ext1.spec1d_uncertainty.quantity.value>900)] = np.nan fig = plt.figure(figsize=(10,8)) #fig,ax = plt.subplots() #fig.set_figheight(8) #fig.set_figwidth(10) plt.plot(eflux) plt.plot(bestcont) plt.plot(outsky1.spec1d_flux.value+bestcont,alpha=0.7) plt.plot([0,2048],[0,0],linestyle='--',c='gray',alpha=0.8) #plt.plot(outsky1.spec1d_flux) plt.plot(outskysub1.spec1d_flux.value-150) plt.plot(bestcont-150) plt.plot([0,2048],[-150,-150],linestyle='--',c='gray',alpha=0.8) plt.xlabel('X') plt.ylabel('Flux') plt.xlim(0,2048) plt.ylim(-400,800) plt.title('Aperture '+str(ext1.aperture)+' Scale=%5.2f' % bestscale) plt.show() else: print(str(i+1)+' skipping') outscale.append(1.0) outsky.append(sky1) outskysub.append(ext1) return outscale,outsky,outskysub