def plotspec(spec,spec2=None,model=None,figsize=10): """ Plot a spectrum.""" norder = spec.norder fig = plt.gcf() # get current graphics window fig.clf() # clear # Single-order plot if norder==1: ax = fig.subplots() fig.set_figheight(figsize*0.5) fig.set_figwidth(figsize) plt.plot(spec.wave,spec.flux,'b',label='Spectrum 1',linewidth=1) if spec2 is not None: plt.plot(spec2.wave,spec2.flux,'green',label='Spectrum 2',linewidth=1) if model is not None: plt.plot(model.wave,model.flux,'r',label='Model',linewidth=1,alpha=0.8) leg = ax.legend(loc='upper left', frameon=False) plt.xlabel('Wavelength (Angstroms)') plt.ylabel('Normalized Flux') xr = dln.minmax(spec.wave) allflux = spec.flux.flatten() if spec2 is not None: allflux = np.concatenate((allflux,spec2.flux.flatten())) if model is not None: allflux = np.concatenate((allflux,model.flux.flatten())) yr = [np.min(allflux), np.max(allflux)] yr = [yr[0]-dln.valrange(yr)*0.15,yr[1]+dln.valrange(yr)*0.005] yr = [np.max([yr[0],-0.2]), np.min([yr[1],2.0])] plt.xlim(xr) plt.ylim(yr) # Multi-order plot else: ax = fig.subplots(norder) fig.set_figheight(figsize) fig.set_figwidth(figsize) for i in range(norder): ax[i].plot(spec.wave[:,i],spec.flux[:,i],'b',label='Spectrum 1',linewidth=1) if spec2 is not None: ax[i].plot(spec2.wave[:,i],spec2.flux[:,i],'green',label='Spectrum 2',linewidth=1) if model is not None: ax[i].plot(model.wave[:,i],model.flux[:,i],'r',label='Model',linewidth=1,alpha=0.8) if i==0: leg = ax[i].legend(loc='upper left', frameon=False) ax[i].set_xlabel('Wavelength (Angstroms)') ax[i].set_ylabel('Normalized Flux') xr = dln.minmax(spec.wave[:,i]) allflux = spec.flux[:,i].flatten() if spec2 is not None: allflux = np.concatenate((allflux,spec2.flux[:,i].flatten())) if model is not None: allflux = np.concatenate((allflux,model.flux[:,i].flatten())) yr = [np.min(allflux), np.max(allflux)] yr = [yr[0]-dln.valrange(yr)*0.05,yr[1]+dln.valrange(yr)*0.05] if i==0: yr = [yr[0]-dln.valrange(yr)*0.15,yr[1]+dln.valrange(yr)*0.05] yr = [np.max([yr[0],-0.2]), np.min([yr[1],2.0])] ax[i].set_xlim(xr) ax[i].set_ylim(yr)
def plotfit(cat, pars, cov, savefig=None): """ Plot a figure of the data and the proper motion/parallax fit.""" plt.rcParams.update({'font.size': 12}) # Compute relative positions cenra = np.mean(cat['ra']) cendec = np.mean(cat['dec']) lon, lat = dcoords.rotsphcen(cat['ra'], cat['dec'], cenra, cendec, gnomic=True) lon *= d2a lat *= d2a # Array of MJDs for model curve mjd = np.linspace(np.min(cat['mjd']), np.max(cat['mjd']), 100) out = astrometryfunc([cenra, cendec, mjd], pars[0], pars[1], pars[2], pars[3], pars[4]) ll = out[0:100] bb = out[100:] # Plot the model and data plt.plot(ll, bb) plt.errorbar(lon, lat, xerr=cat['raerr'], yerr=cat['decerr'], fmt='o', color='black', markersize=5, ecolor='lightgray', elinewidth=2, linestyle='none', capsize=0) plt.xlabel('dRA (arcsec)') plt.ylabel('dDEC (arcsec)') xr = dln.minmax(np.concatenate((lon, ll))) xr = [xr[0] - 0.05 * dln.valrange(xr), xr[1] + 0.05 * dln.valrange(xr)] yr = dln.minmax(np.concatenate((lat, bb))) yr = [yr[0] - 0.05 * dln.valrange(yr), yr[1] + 0.05 * dln.valrange(yr)] plt.xlim(xr) plt.ylim(yr) perr = np.sqrt(np.diag(cov)) plt.annotate( r'$\mu_\alpha$ = %5.3f $\pm$ %5.3f mas/yr' % (pars[2] * 1e3, perr[2] * 1e3) + '\n' + r'$\mu_\delta$ = %5.3f $\pm$ %5.3f mas/yr' % (pars[3] * 1e3, perr[3] * 1e3) + '\n' + r'$\pi$ = %5.3f $\pm$ %5.3f mas' % (pars[4] * 1e3, perr[4] * 1e3), xy=(xr[0] + 0.05 * dln.valrange(xr), yr[1] - 0.20 * dln.valrange(yr)), ha='left') if savefig is not None: plt.savefig(savefig)
def load_cannon_model(files): """ Load a single (or list of) Cannon models from file and manipulate as needed. Returns ------- files : string File name (or list of filenames) of Cannon models to load. Examples -------- model = load_cannon_model() """ # Convert single string into a list if type(files) is not list: files = list(np.atleast_1d(files)) model = [] for f in files: if os.path.exists(f) == False: raise ValueError(f + ' not found') model1 = tc.CannonModel.read(f) # Generate the model ranges ranges = np.zeros([3, 2]) for i in range(3): ranges[i, :] = dln.minmax(model1._training_set_labels[:, i]) model1.ranges = ranges # Rename _fwhm to fwhm and _wavevac to wavevac if hasattr(model1, '_fwhm'): setattr(model1, 'fwhm', model1._fwhm) delattr(model1, '_fwhm') if hasattr(model1, '_wavevac'): setattr(model1, 'wavevac', model1._wavevac) delattr(model1, '_wavevac') # Create continuum model if the information is there if hasattr(model1, '_continuum'): contmodel = readfromdata(model1._continuum) model1.continuum = contmodel delattr(model1, '_continuum') # Convert to DopplerCannonModel model.append(DopplerCannonModel(model1)) if len(model) == 1: model = model[0] return model
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 make_logwave_scale(wave, vel=1000.0): """ Create a logarithmic wavelength scale for a given wavelength array. This is used by rv.fit() to create a logarithmic wavelength scale for cross-correlation. If the input wavelength array is 2D with multiple orders (trailing dimension), then the output wavelength array will extend beyond the boundaries of some of the orders. These pixels will have to be "padded" with dummy/masked-out pixels. Parameters ---------- wave : array Input wavelength array, 1D or 2D with multiple orders. vel : float, optional Maximum velocity shift to allow for. Default is 1000. Returns ------- fwave : array New logarithmic wavelength array. Example ------- .. code-block:: python fwave = make_logwave_scale(wave) """ # If the existing wavelength scale is logarithmic then use it, just extend # on either side vel = np.abs(vel) wave = np.float64(wave) nw = len(wave) wr = dln.minmax(wave) # Multi-order wavelength array if wave.ndim == 2: norder = wave.shape[1] # Ranges wr = np.empty((2, norder), np.float64) wlo = np.empty(norder, np.float64) whi = np.empty(norder, np.float64) for i in range(norder): wr[:, i] = dln.minmax(wave[:, i]) wlo[i] = wr[0, i] - vel / cspeed * wr[0, i] whi[i] = wr[1, i] + vel / cspeed * wr[1, i] # For multi-order wavelengths, the final wavelength solution will extend # beyond the boundary of the original wavelength solution (at the end). # The extra pixels will have to be "padded" with masked out values. # We want the same logarithmic step for all order dw = np.log10(np.float64(wave[1:, :])) - np.log10( np.float64(wave[0:-1, :])) dwlog = np.median(np.abs(dw)) # positive # Extend at the ends if vel > 0: nlo = np.empty(norder, int) nhi = np.empty(norder, int) for i in range(norder): nlo[i] = np.int( np.ceil((np.log10(np.float64(wr[0, i])) - np.log10(np.float64(wlo[i]))) / dwlog)) nhi[i] = np.int( np.ceil((np.log10(np.float64(whi[i])) - np.log10(np.float64(wr[1, i]))) / dwlog)) # Use the maximum over all orders nlo = np.max(nlo) nhi = np.max(nhi) else: nlo = 0 nhi = 0 # Number of pixels for the input wavelength range n = np.empty(norder, int) for i in range(norder): if vel == 0: n[i] = np.int((np.log10(np.float64(wr[1, i])) - np.log10(np.float64(wr[0, i]))) / dwlog) else: n[i] = np.int( np.ceil((np.log10(np.float64(wr[1, i])) - np.log10(np.float64(wr[0, i]))) / dwlog)) # maximum over all orders n = np.max(n) # Final number of pixels nf = n + nlo + nhi # Final wavelength array fwave = np.empty((nf, norder), np.float64) for i in range(norder): fwave[:, i] = 10**((np.arange(nf) - nlo) * dwlog + np.log10(np.float64(wr[0, i]))) # Make sure the element that's associated with the first input wavelength is identical for i in range(norder): # Increasing input wavelength arrays if np.median(dw[:, i]) > 0: fwave[:, i] -= fwave[nlo, i] - wave[0, i] # Decreasing input wavelength arrays else: fwave[:, i] -= fwave[nlo, i] - wave[-1, i] # Single-order wavelength array else: # extend wavelength range by +/-vel km/s wlo = wr[0] - vel / cspeed * wr[0] whi = wr[1] + vel / cspeed * wr[1] # logarithmic step dwlog = np.median(dln.slope(np.log10(np.float64(wave)))) # extend at the ends if vel > 0: nlo = np.int( np.ceil( (np.log10(np.float64(wr[0])) - np.log10(np.float64(wlo))) / dwlog)) nhi = np.int( np.ceil( (np.log10(np.float64(whi)) - np.log10(np.float64(wr[1]))) / dwlog)) else: nlo = 0 nhi = 0 # Number of pixels if vel == 0.0: n = np.int( (np.log10(np.float64(wr[1])) - np.log10(np.float64(wr[0]))) / dwlog) else: n = np.int( np.ceil( (np.log10(np.float64(wr[1])) - np.log10(np.float64(wr[0]))) / dwlog)) nf = n + nlo + nhi fwave = 10**((np.arange(nf) - nlo) * dwlog + np.log10(np.float64(wr[0]))) # Make sure the element that's associated with the first input wavelength is identical fwave -= fwave[nlo] - wave[0] # w=10**(w0log+i*dwlog) return fwave
# expand by a fraction, it's not an extact boundary but good enough buffsize = 10.0 / 3600. # in deg radbound = np.sqrt(lonbound**2 + latbound**2) frac = 1.0 + 1.5 * np.max(buffsize / radbound) lonbuff = lonbound * frac latbuff = latbound * frac rabuff, decbuff = coords.rotsphcen(lonbuff, latbuff, cenra, cendec, gnomic=True, reverse=True) if (np.max(rabuff) - np.min(rabuff)) > 100: # deal with RA=0 wraparound bd, = np.where(rabuff > 180) if len(bd) > 0: rabuff[bd] -= 360.0 buffdict = {'cenra':cenra,'cendec':cendec,'rar':utils.minmax(rabuff),'decr':utils.minmax(decbuff),'ra':rabuff,'dec':decbuff,\ 'lon':lonbuff,'lat':latbuff,'lr':utils.minmax(lonbuff),'br':utils.minmax(latbuff)} # Initialize the ID structure # this will contain the MeasID, Exposure name, ObjectID dtype_idstr = np.dtype([('measid', np.str, 200), ('exposure', np.str, 200), ('expnum', np.str, 200), ('objectid', np.str, 200), ('objectindex', int)]) idstr = np.zeros(1000000, dtype=dtype_idstr) nidstr = len(idstr) idcnt = 0 # Initialize the object structure dtype_obj = np.dtype([('objectid', np.str, 100), ('pix', int), ('ra', np.float64), ('dec', np.float64), ('raerr', float), ('decerr', float), ('pmra', float),
def starcube(cat,image,npix=51,fillvalue=np.nan): """ Produce a cube of cutouts of stars. Parameters ---------- cat : table The catalog of stars to use. This should have "x" and "y" columns and preferably also "amp". image : CCDData object The image to use to generate the stellar images. fillvalue : float, optional The fill value to use for pixels that are bad are off the image. Default is np.nan. Returns ------- cube : numpy array Two-dimensional cube (Npix,Npix,Nstars) of the star images. Example ------- cube = starcube(cat,image) """ # Get the residuals data nstars = len(cat) nhpix = npix//2 cube = np.zeros((npix,npix,nstars),float) xx,yy = np.meshgrid(np.arange(npix)-nhpix,np.arange(npix)-nhpix) rr = np.sqrt(xx**2+yy**2) x = xx[0,:] y = yy[:,0] for i in range(nstars): xcen = cat['x'][i] ycen = cat['y'][i] bbox = models.starbbox((xcen,ycen),image.shape,nhpix) im = image[bbox.slices] flux = image.data[bbox.slices]-image.sky[bbox.slices] err = image.error[bbox.slices] if 'amp' in cat.columns: amp = cat['amp'][i] elif 'peak' in cat.columns: amp = cat['peak'][i] else: amp = flux[int(np.round(ycen)),int(np.round(xcen))] xim,yim = np.meshgrid(im.x,im.y) xim = xim.astype(float)-xcen yim = yim.astype(float)-ycen # We need to interpolate this onto the grid f = RectBivariateSpline(yim[:,0],xim[0,:],flux/amp) im2 = np.zeros((npix,npix),float)+np.nan xcover = (x>=bbox.ixmin-xcen) & (x<=bbox.ixmax-1-xcen) xmin,xmax = dln.minmax(np.where(xcover)[0]) ycover = (y>=bbox.iymin-ycen) & (y<=bbox.iymax-1-ycen) ymin,ymax = dln.minmax(np.where(ycover)[0]) im2[ymin:ymax+1,xmin:xmax+1] = f(y[ycover],x[xcover],grid=True) # Stuff it into 3D array cube[:,:,i] = im2 return cube
cfile = cfile.replace('/net/mss1/','') cfile = cfile.replace('/mss1/','') # Fixing very negative RAs print('FIXING NEGATIVE RAs in CALSTR and CHSTR') #bdra, = np.where(chstr.cenra lt -180,nbdra) bdra,nbdra = dln.where(chstr['cenra']<0) dum,uibd = np.unique(chstr['expdir'][bdra],return_indices=True) ind1,ind2 = dln.match(calstr['expdir'],chstr['expdir'][bdra[uibd]]) nmatch = len(ind1) for i in range(nmatch): ind3,ind4 = dln.match(chstr['expdir'][bdra],calstr['expdir'][ind1[i]]) # Fix CALSTR RA chra = chstr['cenra'][bdra[ind3]] bd1,nbd1 = dln.where(chra < -180) if nbd1>0: chra[bd1]+=360 cenra = np.mean(dln.minmax(chra)) if cenra<0: cenra+=360 calstr['ra'][ind1[i]] = cenra # Fix CHSTR CENRA bd2,nbd2 = dln.where(chra<0) if nbd2>0: chra[bd2]+=360 chstr['cenra'][bdra[ind3]] = chra # Fix CHSTR VRA vra = chstr['vra'][bdra[ind3]] bd3,nbd3 = dln.where(vra<0) if nbd3>0: vra[bd3]+=360 chstr['vra'][bdra[ind3]] = vra # Fix instrument in STR and CHSTR print('FIXING INSTRUMENT IN STR AND CHSTR') type = ['c4d','k4m','ksb']