def ConvolveSmooth(self, numiters=10, lowreject=2, highreject=3): done = False data = self.smoothing_data.copy() # data.y /= data.cont iterations = 0 if self.window_size % 2 == 0: self.window_size += 1 while not done and iterations < numiters: iterations += 1 done = True y = FittingUtilities.savitzky_golay(data.y, self.window_size, 5) #s = np.r_[data.y[self.window_size/2:0:-1], data.y, data.y[-1:-self.window_size/2:-1]] #y = np.convolve(window/window.sum(), s, mode='valid') reduced = data.y / y sigma = np.std(reduced) mean = np.mean(reduced) badindices = \ np.where(np.logical_or((reduced - mean) / sigma < -lowreject, (reduced - mean) / sigma > highreject))[0] if badindices.size > 0: done = False data.y[badindices] = y[badindices] return DataStructures.xypoint(x=self.smoothing_data.x, y=y / self.smoothing_data.cont)
def ReadFile(fname, blaze=None): try: orders = HelperFunctions.ReadFits(fname) except ValueError: orders = HelperFunctions.ReadFits(fname, errors=2) orders = orders[::-1] # Reverse order so the bluest order is first #Need to blaze-correct the later data if int(os.path.split(fname)[1][2:7]) > 50400 and blaze is not None: print "\tBlaze correcting!" try: blaze_orders = HelperFunctions.ReadFits(blaze) except ValueError: blaze_orders = HelperFunctions.ReadFits(blaze, errors=2) blaze_orders = blaze_orders[:: -1] # Reverse order so the bluest order is first blazecorrect = True else: blazecorrect = False for i, order in enumerate(orders): if blazecorrect: b = blaze_orders[i].y / blaze_orders[i].y.mean() #plt.plot(order.x, b, 'g-', alpha=0.4) order.y /= b order.cont = FittingUtilities.Continuum(order.x, order.y, fitorder=2, lowreject=2, highreject=5) #plt.plot(order.x, order.y, 'k-', alpha=0.4) #plt.plot(order.x, order.cont, 'r-', alpha=0.4) orders[i] = order.copy() #plt.show() return orders
def fit_rv_shift(template, orders): template_lines = [] order_lines = [] for t, o in zip(template, orders): plt.plot(t.x, t.y/t.cont, 'k-', alpha=0.4) plt.plot(o.x, o.y/o.cont, 'r-', alpha=0.5) tlines = FittingUtilities.FindLines(t, tol=0.8) olines = FittingUtilities.FindLines(o, tol=0.8) if len(tlines) == 0 or len(tlines) != len(olines): continue template_lines.append(t.x[tlines]) order_lines.append(o.x[olines]) template_lines = np.hstack(template_lines) order_lines = np.hstack(order_lines) rv = (np.median(order_lines/template_lines) - 1.0)*c #plt.scatter(template_lines, order_lines) #plt.plot(plt.xlim(), plt.xlim(), 'r--') plt.scatter(template_lines, order_lines - template_lines*(1.+rv/c)) plt.show() return rv
def FitErrorFunction(self, fitpars): """ The error function for the fitter. This should never be called directly! """ if self.return_resolution: model, resolution = self.GenerateModel(fitpars, return_resolution=True) else: model = self.GenerateModel(fitpars) outfile = open("chisq_summary.dat", 'a') weights = 1.0 / self.data.err**2 #Find the regions to use (ignoring the parts that were defined as bad) good = numpy.arange(self.data.x.size, dtype=numpy.int32) for region in self.ignore: x0 = min(region) x1 = max(region) tmp1 = [self.data.x[i] in self.data.x[good] for i in range(self.data.x.size)] tmp2 = numpy.logical_or(self.data.x<x0, self.data.x>x1) good = numpy.where(numpy.logical_and(tmp1, tmp2))[0] return_array = (self.data.y - self.data.cont*model.y)[good]**2 * weights[good] #Evaluate bound conditions and output the parameter value to the logfile. fit_idx = 0 for i in range(len(self.bounds)): if self.fitting[i]: if len(self.bounds[i]) == 2: return_array += FittingUtilities.bound(self.bounds[i], fitpars[fit_idx]) outfile.write("%.12g\t" %fitpars[fit_idx]) self.parvals[i].append(fitpars[fit_idx]) fit_idx += 1 elif len(self.bounds[i]) == 2 and self.parnames[i] != "resolution": return_array += FittingUtilities.bound(self.bounds[i], self.const_pars[i]) outfile.write("%g\n" %(numpy.sum(return_array)/float(weights.size))) self.chisq_vals.append(numpy.sum(return_array)/float(weights.size)) print "X^2 = ", numpy.sum(return_array)/float(weights.size) outfile.close() return return_array
def ResolutionFitError(self, resolution, data, model): """ This function gets called by scipy.optimize.fminbound in FitResolution. Not meant to be called directly by the user! """ resolution = max(1000.0, float(int(float(resolution) + 0.5))) if self.debug and self.debug_level >= 5: print "Saving inputs for R = ", resolution print " to Debug_ResFit.log and Debug_ResFit2.log" numpy.savetxt("Debug_ResFit.log", numpy.transpose((data.x, data.y, data.cont))) numpy.savetxt("Debug_Resfit2.log", numpy.transpose((model.x, model.y))) newmodel = FittingUtilities.ReduceResolution(model, resolution, extend=False) newmodel = FittingUtilities.RebinData(newmodel, data.x, synphot=False) #Find the regions to use (ignoring the parts that were defined as bad) good = numpy.arange(self.data.x.size, dtype=numpy.int32) for region in self.ignore: x0 = min(region) x1 = max(region) tmp1 = [self.data.x[i] in self.data.x[good] for i in range(self.data.x.size)] tmp2 = numpy.logical_or(self.data.x<x0, self.data.x>x1) good = numpy.where(numpy.logical_and(tmp1, tmp2))[0] weights = 1.0/data.err**2 returnvec = (data.y - data.cont*newmodel.y)[good]**2 * weights[good] + FittingUtilities.bound(self.resolution_bounds, resolution) if self.debug: print "Resolution-fitting X^2 = ", numpy.sum(returnvec)/float(good.size), "at R = ", resolution if numpy.isnan(numpy.sum(returnvec**2)): print "Error! NaN found in ResolutionFitError!" outfile=open("ResolutionFitError.log", "a") outfile.write("#Error attempting R = %g\n" %(resolution)) numpy.savetxt(outfile, numpy.transpose((data.x, data.y, data.cont, newmodel.x, newmodel.y)), fmt="%.10g") outfile.write("\n\n\n\n") numpy.savetxt(outfile, numpy.transpose((model.x, model.y)), fmt="%.10g") outfile.write("\n\n\n\n") outfile.close() raise ValueError return returnvec
print "\n***************************\nFitting order %i: " % (i) order = orders[i] fitter.AdjustValue({ "wavestart": order.x[0] - 20.0, "waveend": order.x[-1] + 20.0, "o2": 0.0, "h2o": humidity, "resolution": resolution }) fitpars = [ fitter.const_pars[j] for j in range(len(fitter.parnames)) if fitter.fitting[j] ] order.cont = FittingUtilities.Continuum(order.x, order.y, fitorder=3, lowreject=1.5, highreject=10) fitter.ImportData(order) fitter.resolution_fit_mode = "gauss" fitter.fit_source = False fitter.fit_primary = False model = fitter.GenerateModel(fitpars, separate_source=False, return_resolution=False) # Find the best scale factor model.cont = np.ones(model.size()) lines = FittingUtilities.FindLines(model, tol=0.95).astype(int) if len(lines) > 5: scale = np.median(
def make_matrices(): # Find all the relevant files #allfiles = [f for f in os.listdir("./") if f.startswith("RV") and f.endswith("-0.fits")] allfiles = [f for f in os.listdir("./") if f.startswith("RV") and '-0' in f and f.endswith("corrected.fits")] # Read in the measured RV data bjd, rv = np.loadtxt("psi1draa_100_120_mcomb1.dat", usecols=(0,1), unpack=True) bjd2, vbary_arr = np.loadtxt("psi1draa_100p_28_37_ASW.dat", usecols=(0,5), unpack=True) # Put all the data in one giant list alldata = [] print "Reading all data" for i, fname in enumerate(allfiles): header = fits.getheader(fname) jd = header['jd'] #vbary = GenericSearch.HelCorr(header, observatory="McDonald") idx = np.argmin(abs(bjd2 - jd)) bjd2_i = bjd2[idx] vbary = vbary_arr[idx] / 1000.0 idx = np.argmin(abs(bjd - jd)) bjd_i = bjd[idx] vstar = rv[idx]/1e3 print(fname, vbary, vstar) vel = vbary - vstar #Closest... but still not great #vel = -vbary + vstar #NO #vel = vbary + vstar #NO #vel = -vbary - vstar #NO #vel = vstar #NO #vel = -vstar #NO #vel = vbary #Surprisingly not bad... orders = HelperFunctions.ReadExtensionFits(fname)[:-2] for j in badorders[::-1]: orders.pop(j) if i == 0: template = [] for j, order in enumerate(orders): order.x *= (1.0+vel/c) orders[j] = order.copy() template.append(order.copy()) alldata.append([order]) #template = [o.copy() for o in orders] else: vel = fit_rv_shift_old(template, orders) print('RV adjustment = {}'.format(vel)) for j, order in enumerate(orders): order.x *= (1.0+vel/c) orders[j] = order.copy() if i == 0: alldata.append([order]) #template.append(order.copy()) else: alldata[j].append(order) #plt.plot(order.x, order.y/order.cont, 'g-', alpha=0.5) #plt.show() # Interpolate each order to a single wavelength grid print "Interpolating to wavelength grid" xgrids = [] c_cycler = itertools.cycle(('r', 'g', 'b', 'k')) ls_cycler = itertools.cycle(('-', '--', ':', '-.')) for i, order in enumerate(alldata): print "Order ", i+1 firstwaves = [d.x[0] for d in order] lastwaves = [d.x[-1] for d in order] size = np.mean([d.size() for d in order]) xgrid = np.linspace(max(firstwaves), min(lastwaves), size) for j, data in enumerate(order): tmp = FittingUtilities.RebinData(data, xgrid) order[j] = tmp.y/tmp.cont #if i == 20: # col = c_cycler.next() # if j%4 == 0: # ls = ls_cycler.next() # plt.plot(xgrid, order[j], color=col, ls=ls, alpha=0.8, label=allfiles[j]) #plt.plot(xgrid, order[j], 'k-', alpha=0.2) alldata[i] = np.array(order) xgrids.append(xgrid) #plt.legend(loc='best') #plt.show() return allfiles, xgrids, alldata
def get_ccfs(T=4000, vsini=5, logg=4.5, metal=0.5, hdf_file='Cross_correlations/CCF.hdf5', xgrid=np.arange(-400, 400, 1), addmode='simple'): """ Get the cross-correlation functions for the given parameters, for all stars """ ccfs = [] filenames = [] rv_shift = {} if T > 6000 else pickle.load(open('rvs.pkl')) with h5py.File(hdf_file) as f: starname = 'psi1 Dra A' date_list = f[starname].keys() for date in date_list: datasets = f[starname][date].keys() for ds_name in datasets: ds = f[starname][date][ds_name] if (ds.attrs['T'] == T and ds.attrs['vsini'] == vsini and ds.attrs['logg'] == logg and ds.attrs['[Fe/H]'] == metal and ds.attrs['addmode'] == addmode): vel, corr = ds.value #ccf = spline(vel[::-1]*-1, (1.0-corr[::-1])) ccf = spline(vel[::-1] * -1, corr[::-1]) #ccf = spline(vel, corr) fname = ds.attrs['fname'] vbary = get_rv_correction(fname) #cont = FittingUtilities.Continuum(xgrid, ccf(xgrid-vbary), fitorder=2, lowreject=2.5, highreject=5) #normed_ccf = ccf(xgrid-vbary)/cont cont = FittingUtilities.Continuum(xgrid, ccf(xgrid - vbary), fitorder=2, lowreject=5, highreject=2.5) normed_ccf = ccf(xgrid - vbary) - cont if T <= 6000: centroid = rv_shift[fname] #centroid = xgrid[np.argmax(normed_ccf)] #top = 1.0 #amp = 1.0 - min(normed_ccf) top = 0.0 amp = max(normed_ccf) #idx = np.argmin(np.abs(xgrid-centroid)) #amp = normed_ccfs[idx] else: gauss_pars = fit_gaussian(xgrid, normed_ccf) centroid = gauss_pars[2] amp = gauss_pars[1] top = gauss_pars[0] amp = 0.5 print(centroid, fname) #cont = FittingUtilities.Continuum(xgrid, ccf(xgrid-vbary+centroid), fitorder=2, lowreject=2.5, highreject=5) #normed_ccf = (ccf(xgrid-vbary+centroid) / cont - top) * 0.5/abs(amp) + top cont = FittingUtilities.Continuum(xgrid, ccf(xgrid - vbary + centroid), fitorder=2, lowreject=5, highreject=2.5) normed_ccf = (ccf(xgrid - vbary + centroid) - cont) * 0.5 / abs(amp) filenames.append(fname) ccfs.append(normed_ccf) rv_shift[fname] = centroid if T > 6000: pickle.dump(rv_shift, open('rvs.pkl', 'w')) return np.array(ccfs), filenames
orders.pop(numorders - 1 - i) else: order.cont = FittingUtilities.Continuum(order.x, order.y, lowreject=3, highreject=3) orders[numorders - 1 - i] = order.copy() """ for i, order in enumerate(orders): plt.plot(order.x, order.y/order.cont+i) plt.show() sys.exit() """ #Smooth data if smooth: for order in orders: order.y /= FittingUtilities.savitzky_golay(order.y, 91, 5) output_dir = "Cross_correlations/" outfilebase = fname.split(".fits")[0] if "/" in fname: dirs = fname.split("/") output_dir = "" outfilebase = dirs[-1].split(".fits")[0] for directory in dirs[:-1]: output_dir = output_dir + directory + "/" output_dir = output_dir + "Cross_correlations/" #Do the cross-correlation for vsini in [10, 20, 30, 40]: Correlate.PyCorr2(orders, resolution=60000, outdir=output_dir, models=model_data, stars=star_list, temps=temp_list, gravities=gravity_list, metallicities=metal_list, vsini=vsini * units.km.to(units.cm), debug=False, outfilebase=outfilebase)
def MakeModel(self, pressure=795.0, temperature=283.0, lowfreq=4000, highfreq=4600, angle=45.0, humidity=50.0, co2=368.5, o3=3.9e-2, n2o=0.32, co=0.14, ch4=1.8, o2=2.1e5, no=1.1e-19, so2=1e-4, no2=1e-4, nh3=1e-4, hno3=5.6e-4, lat=30.6, alt=2.1, wavegrid=None, resolution=None, save=False, libfile=None, vac2air=True): """ Here is the important function! All of the variables have default values, which you will want to override for any realistic use. :param pressure: Pressure at telescope altitude (hPa) :param temperature: Temperature at telescope altitude (Kelvin) :param lowfreq: The starting wavenumber (cm^-1) :param highfreq: The ending wavenumber (cm^-1) :param angle: The zenith distance of the telescope (degrees). This is related to the airmass (z) through z = sec(angle) :param humidity: Percent relative humidity at the telescope altitude. :param co2: Mixing ratio of this molecule (parts per million by volumne) :param o3: Mixing ratio of this molecule (parts per million by volumne) :param n2o: Mixing ratio of this molecule (parts per million by volumne) :param co: Mixing ratio of this molecule (parts per million by volumne) :param ch4: Mixing ratio of this molecule (parts per million by volumne) :param o2: Mixing ratio of this molecule (parts per million by volumne) :param no: Mixing ratio of this molecule (parts per million by volumne) :param so2: Mixing ratio of this molecule (parts per million by volumne) :param no2: Mixing ratio of this molecule (parts per million by volumne) :param nh3: Mixing ratio of this molecule (parts per million by volumne) :param hno3: Mixing ratio of this molecule (parts per million by volumne) :param lat: The latitude of the observatory (degrees) :param alt: The altitude of the observatory above sea level (km) :param wavegrid: If given, the model will be resampled to this grid. Should be a 1D np array :param resolution: If given, it will reduce the resolution by convolving with a gaussian of appropriate width. Should be a float with R=lam/dlam :param save: If true, the generated model is saved. The filename will be printed to the screen. :param libfile: Useful if generating a telluric library. The filename of the saved file will be written to this filename. Should be a string variable. Ignored if save==False :param vac2air: If True (default), it converts the wavelengths from vacuum to air :return: DataStructures.xypoint instance with the telluric model. The x-axis is in nanometers and the y-axis is in fractional transmission. """ self.FindWorkingDirectory() #Make the class variables local TelluricModelingDir = self.TelluricModelingDir debug = self.debug lock = self.lock layers = np.array(self.layers) ModelDir = self.ModelDir #Make a deep copy of atmosphere so that I don't repeatedly modify it Atmosphere = copy.deepcopy(self.Atmosphere) #Convert from relative humidity to concentration (ppm) h2o = humidity_to_ppmv(humidity, temperature, pressure) #Start by scaling the abundances from those at 'alt' km # (linearly interpolate) keys = sorted(Atmosphere.keys()) lower = max(0, np.searchsorted(keys, alt) - 1) upper = min(lower + 1, len(keys) - 1) if lower == upper: raise ZeroDivisionError( "Observatory altitude of %g results in the surrounding layers being the same!" % alt) scale_values = list(Atmosphere[lower]) scale_values[2] = list(Atmosphere[lower][2]) scale_values[0] = (Atmosphere[upper][0] - Atmosphere[lower][0]) / ( keys[upper] - keys[lower]) * (alt - keys[lower]) + Atmosphere[lower][0] scale_values[1] = (Atmosphere[upper][1] - Atmosphere[lower][1]) / ( keys[upper] - keys[lower]) * (alt - keys[lower]) + Atmosphere[lower][1] for mol in range(len(scale_values[2])): scale_values[2][mol] = ( Atmosphere[upper][2][mol] - Atmosphere[lower][2][mol]) / (keys[upper] - keys[lower]) * ( alt - keys[lower]) + Atmosphere[lower][2][mol] #Do the actual scaling pressure_scalefactor = (scale_values[0] - pressure) * np.exp( -(layers - alt)**2 / (2.0 * 10.0**2)) temperature_scalefactor = (scale_values[1] - temperature) * np.exp( -(layers - alt)**2 / (2.0 * 10.0**2)) for i, layer in enumerate(layers): # Atmosphere[layer][0] -= pressure_scalefactor[i] Atmosphere[layer][0] *= pressure / scale_values[0] Atmosphere[layer][1] -= temperature_scalefactor[i] Atmosphere[layer][2][0] *= h2o / scale_values[2][0] Atmosphere[layer][2][1] *= co2 / scale_values[2][1] Atmosphere[layer][2][2] *= o3 / scale_values[2][2] Atmosphere[layer][2][3] *= n2o / scale_values[2][3] Atmosphere[layer][2][4] *= co / scale_values[2][4] Atmosphere[layer][2][5] *= ch4 / scale_values[2][5] Atmosphere[layer][2][6] *= o2 / scale_values[2][6] Atmosphere[layer][2][7] *= no / scale_values[2][7] Atmosphere[layer][2][8] *= so2 / scale_values[2][8] Atmosphere[layer][2][9] *= no2 / scale_values[2][9] Atmosphere[layer][2][10] *= nh3 / scale_values[2][10] Atmosphere[layer][2][11] *= hno3 / scale_values[2][11] #Now, Read in the ParameterFile and edit the necessary parameters parameters = MakeTape5.ReadParFile(parameterfile=TelluricModelingDir + "ParameterFile") parameters[48] = "%.1f" % lat parameters[49] = "%.1f" % alt parameters[51] = "%.5f" % angle parameters[17] = lowfreq freq, transmission = np.array([]), np.array([]) #Need to run lblrtm several times if the wavelength range is too large. maxdiff = 1999.9 if (highfreq - lowfreq > maxdiff): while lowfreq + maxdiff <= highfreq: parameters[18] = lowfreq + maxdiff MakeTape5.WriteTape5(parameters, output=TelluricModelingDir + "TAPE5", atmosphere=Atmosphere) #Run lblrtm cmd = "cd " + TelluricModelingDir + ";sh runlblrtm_v3.sh" try: if self.print_lblrtm_output: command = subprocess.check_call(cmd, shell=True) if not self.print_lblrtm_output: command = subprocess.check_call( cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) except subprocess.CalledProcessError: raise subprocess.CalledProcessError( "Error: Command '{}' failed in directory {}".format( cmd, TelluricModelingDir)) #Read in TAPE12, which is the output of LBLRTM freq, transmission = self.ReadTAPE12(TelluricModelingDir, appendto=(freq, transmission)) lowfreq = lowfreq + 2000.00001 parameters[17] = lowfreq parameters[18] = highfreq MakeTape5.WriteTape5(parameters, output=TelluricModelingDir + "TAPE5", atmosphere=Atmosphere) #Run lblrtm for the last time cmd = "cd " + TelluricModelingDir + ";sh runlblrtm_v3.sh" try: if self.print_lblrtm_output: command = subprocess.check_call(cmd, shell=True) if not self.print_lblrtm_output: command = subprocess.check_call(cmd, shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) except subprocess.CalledProcessError: raise subprocess.CalledProcessError( "Error: Command '{}' failed in directory {}".format( cmd, TelluricModelingDir)) #Read in TAPE12, which is the output of LBLRTM freq, transmission = self.ReadTAPE12(TelluricModelingDir, appendto=(freq, transmission)) #Convert from frequency to wavelength units wavelength = units.cm.to(units.nm) / freq #Correct for index of refraction of air (use IAU standard conversion from # Morton, D. C. 1991, ApJS, 77, 119 if vac2air: wave_A = wavelength * units.nm.to( units.angstrom) # Wavelength in angstroms n = 1.0 + 2.735182e-4 + 131.4182 / wave_A**2 + 2.76249e8 / wave_A**4 wavelength /= n if save: #Output filename model_name = ModelDir + "transmission" + "-%.2f" % pressure + "-%.2f" % temperature + "-%.1f" % humidity + "-%.1f" % angle + "-%.2f" % ( co2) + "-%.2f" % (o3 * 100) + "-%.2f" % ch4 + "-%.2f" % (co * 10) logging.info( "All done! Output Transmission spectrum is located in the file below:\n\t\t{}" .format(model_name)) np.savetxt(model_name, np.transpose((wavelength[::-1], transmission[::-1])), fmt="%.8g") if libfile != None: infile = open(libfile, "a") infile.write(model_name + "\n") infile.close() self.Cleanup() #Un-lock the working directory if wavegrid != None: model = DataStructures.xypoint(x=wavelength[::-1], y=transmission[::-1]) return FittingUtilities.RebinData(model, wavegrid) return DataStructures.xypoint(x=wavelength[::-1], y=transmission[::-1])
def GenerateModel(self, pars, nofit=False, separate_primary=False, return_resolution=False): """ This function does the actual work of generating a model with the given parameters, fitting the continuum, making sure the model and data are well aligned in wavelength, and fitting the detector resolution. In general, it is not meant to be called directly by the user. However, the 'nofit' keyword turns this into a wrapper to MakeModel.Modeler().MakeModel() with all the appropriate parameters. """ data = self.data #Update self.const_pars to include the new values in fitpars # I know, it's confusing that const_pars holds some non-constant parameters... fit_idx = 0 for i in range(len(self.parnames)): if self.fitting[i]: self.const_pars[i] = pars[fit_idx] fit_idx += 1 self.DisplayVariables(fitonly=True) #Extract parameters from pars and const_pars. They will have variable # names set from self.parnames fit_idx = 0 for i in range(len(self.parnames)): #Assign to local variables by the parameter name if self.fitting[i]: exec("%s = %g" %(self.parnames[i], pars[fit_idx])) fit_idx += 1 else: exec("%s = %g" %(self.parnames[i], self.const_pars[i])) #Make sure everything is within its bounds if len(self.bounds[i]) > 0: lower = self.bounds[i][0] upper = self.bounds[i][1] exec("%s = %g if %s < %g else %s" %(self.parnames[i], lower, self.parnames[i], lower, self.parnames[i])) exec("%s = %g if %s > %g else %s" %(self.parnames[i], upper, self.parnames[i], upper, self.parnames[i])) wavenum_start = 1e7/waveend wavenum_end = 1e7/wavestart lat = self.observatory["latitude"] alt = self.observatory["altitude"] #Generate the model: model = self.Modeler.MakeModel(pressure, temperature, wavenum_start, wavenum_end, angle, h2o, co2, o3, n2o, co, ch4, o2, no, so2, no2, nh3, hno3, lat=lat, alt=alt, wavegrid=None, resolution=None) #Shift the x-axis, using the shift from previous iterations if self.debug: print "Shifting by %.4g before fitting model" %self.shift if self.adjust_wave == "data": data.x += self.shift elif self.adjust_wave == "model": model.x -= self.shift #Save each model if debugging if self.debug and self.debug_level >= 5: FittingUtilities.ensure_dir("Models/") model_name = "Models/transmission"+"-%.2f" %pressure + "-%.2f" %temperature + "-%.1f" %h2o + "-%.1f" %angle + "-%.2f" %(co2) + "-%.2f" %(o3*100) + "-%.2f" %ch4 + "-%.2f" %(co*10) numpy.savetxt(model_name, numpy.transpose((model.x, model.y)), fmt="%.8f") #Interpolate to constant wavelength spacing xgrid = numpy.linspace(model.x[0], model.x[-1], model.x.size) model = FittingUtilities.RebinData(model, xgrid) #Use nofit if you want a model with reduced resolution. Probably easier # to go through MakeModel directly though... if data == None or nofit: return FittingUtilities.ReduceResolution(model, resolution) model_original = model.copy() #Reduce to initial guess resolution if (resolution - 10 < self.resolution_bounds[0] or resolution+10 > self.resolution_bounds[1]): resolution = numpy.mean(self.resolution_bounds) model = FittingUtilities.ReduceResolution(model, resolution) model = FittingUtilities.RebinData(model, data.x) #Shift the data (or model) by a constant offset. This gets the wavelength calibration close shift = FittingUtilities.CCImprove(data, model, tol=0.1) if self.adjust_wave == "data": data.x += shift elif self.adjust_wave == "model": model_original.x -= shift # In this case, we need to adjust the resolution again model = FittingUtilities.ReduceResolution(model_original.copy(), resolution) model = FittingUtilities.RebinData(model, data.x) else: sys.exit("Error! adjust_wave parameter set to invalid value: %s" %self.adjust_wave) self.shift += shift resid = data.y/model.y nans = numpy.isnan(resid) resid[nans] = data.cont[nans] #As the model gets better, the continuum will be less affected by # telluric lines, and so will get better data.cont = FittingUtilities.Continuum(data.x, resid, fitorder=self.continuum_fit_order, lowreject=2, highreject=3) if separate_primary or self.fit_source: print "Generating Primary star model" primary_star = data.copy() primary_star.y = FittingUtilities.Iterative_SV(resid/data.cont, 61, 4, lowreject=2, highreject=3) data.cont *= primary_star.y if self.debug and self.debug_level >= 4: print "Saving data and model arrays right before fitting the wavelength" print " and resolution to Debug_Output1.log" numpy.savetxt("Debug_Output1.log", numpy.transpose((data.x, data.y, data.cont, model.x, model.y))) #Fine-tune the wavelength calibration by fitting the location of several telluric lines modelfcn, mean = self.FitWavelength(data, model.copy(), fitorder=self.wavelength_fit_order) if self.adjust_wave == "data": test = data.x - modelfcn(data.x - mean) xdiff = [test[j] - test[j-1] for j in range(1, len(test)-1)] if min(xdiff) > 0 and numpy.max(numpy.abs(test - data.x)) < 0.1 and min(test) > 0: print "Adjusting data wavelengths by at most %.8g nm" %numpy.max(test - model.x) data.x = test.copy() else: print "Warning! Wavelength calibration did not succeed!" elif self.adjust_wave == "model": test = model_original.x + modelfcn(model_original.x - mean) test2 = model.x + modelfcn(model.x - mean) xdiff = [test[j] - test[j-1] for j in range(1, len(test)-1)] if min(xdiff) > 0 and numpy.max(numpy.abs(test2 - model.x)) < 0.1 and min(test) > 0 and abs(test[0] - data.x[0]) < 50 and abs(test[-1] - data.x[-1]) < 50: print "Adjusting wavelength calibration by at most %.8g nm" %max(test2 - model.x) model_original.x = test.copy() model.x = test2.copy() else: print "Warning! Wavelength calibration did not succeed!" else: sys.exit("Error! adjust_wave set to an invalid value: %s" %self.adjust_wave) if self.debug and self.debug_level >= 4: print "Saving data and model arrays after fitting the wavelength" print " and before the resolution fit to Debug_Output2.log" numpy.savetxt("Debug_Output2.log", numpy.transpose((data.x, data.y, data.cont, model.x, model.y))) #Fit instrumental resolution done = False while not done: done = True if "SVD" in self.resolution_fit_mode and min(model.y) < 0.95: model, self.broadstuff = self.Broaden(data.copy(), model_original.copy(), full_output=True) elif "gauss" in self.resolution_fit_mode: model, resolution = self.FitResolution(data.copy(), model_original.copy(), resolution) else: done = False print "Resolution fit mode set to an invalid value: %s" %self.resolution_fit_mode self.resolution_fit_mode = raw_input("Enter a valid mode (SVD or guass): ") self.data = data self.first_iteration = False if separate_primary: if return_resolution: return primary_star, model, resolution else: return primary_star, model else: #if self.fit_source: # data.cont /= primary_star.y if return_resolution: return model, resolution return model
def Filter(fname, vsini=100, numiters=100, lowreject=3, highreject=3): orders = FitsUtils.MakeXYpoints(fname, extensions=True, x="wavelength", y="flux", errors="error") column_list = [] for i, order in enumerate(orders): smoothed = HelperFunctions.IterativeLowPass( order, vsini * units.km.to(units.cm), numiter=numiters, lowreject=lowreject, highreject=highreject ) plt.plot(order.x, order.y) plt.plot(order.x, smoothed) plt.show() continue done = False x = order.x.copy() y = order.y.copy() indices = np.array([True] * x.size) iteration = 0 while not done and iteration < numiters: done = True iteration += 1 smoothed = FittingUtilities.savitzky_golay(y, window_size, smoothorder) residuals = y / smoothed if iteration == 1: # Save the first and last points, for interpolation first, last = smoothed[0], smoothed[-1] mean = residuals.mean() std = residuals.std() print residuals.size, x.size, y.size # plt.plot((residuals - mean)/std) # plt.show() badindices = np.where( np.logical_or((residuals - mean) / std > highreject, (residuals - mean) / std < -lowreject) )[0] if badindices.size > 1 and y.size - badindices.size > 2 * window_size and iteration < numiters: done = False x = np.delete(x, badindices) y = np.delete(y, badindices) print "iter = %i" % iteration if x[0] > order.x[0]: x = np.append(np.array([order.x[0]]), x) smoothed = np.append(np.array([first]), smoothed) if x[-1] < order.x[-1]: x = np.append(x, np.array([order.x[-1]])) smoothed = np.append(smoothed, np.array([last])) print x.size, y.size, smoothed.size smooth_fcn = interp(x, smoothed, k=1) smoothed = smooth_fcn(order.x) order.cont = FittingUtilities.Continuum(order.x, order.y) plt.figure(1) plt.plot(order.x, order.y / order.cont, "k-") plt.plot(order.x, smoothed / order.cont, "r-") # plt.figure(2) # plt.plot(order.x, order.y/smoothed) # plt.plot(order.x, smoothed) # orders[i].y /= smoothed column = { "wavelength": order.x, "flux": order.y / smoothed, "continuum": np.ones(order.x.size), "error": order.err, } column_list.append(column) plt.show() outfilename = "%s_smoothed.fits" % (fname.split(".fits")[0]) print "Outputting to %s" % outfilename