def convolveTelluric(lsf, telluric_data, alpha=1.0, airmass='1.5', pwv='1.5'): """ Return a convolved telluric transmission model given a telluric data and lsf. """ # get a telluric standard model wavelow = telluric_data.wave[0] - 50 wavehigh = telluric_data.wave[-1] + 50 telluric_model = nsp.getTelluric(wavelow=wavelow,wavehigh=wavehigh, airmass=airmass, pwv=pwv) telluric_model.flux **= alpha # lsf telluric_model.flux = nsp.broaden(wave=telluric_model.wave, flux=telluric_model.flux, vbroad=lsf, rotate=False, gaussian=True) # resample telluric_model.flux = np.array(nsp.integralResample(xh=telluric_model.wave, yh=telluric_model.flux, xl=telluric_data.wave)) telluric_model.wave = telluric_data.wave return telluric_model
def applyTelluric(model, alpha=1, airmass='1.5'): """ Apply the telluric model on the science model. Parameters ---------- model : model object BT Settl model alpha : float telluric scaling factor (the power on the flux) Returns ------- model : model object BT Settl model times the corresponding model """ # read in a telluric model wavelow = model.wave[0] - 10 wavehigh = model.wave[-1] + 10 telluric_model = nsp.getTelluric(wavelow=wavelow, wavehigh=wavehigh, alpha=alpha, airmass=airmass) # apply the telluric alpha parameter #telluric_model.flux = telluric_model.flux**(alpha) #if len(model.wave) > len(telluric_model.wave): # print("The model has a higher resolution ({}) than the telluric model ({})."\ # .format(len(model.wave),len(telluric_model.wave))) # model.flux = np.array(nsp.integralResample(xh=model.wave, # yh=model.flux, xl=telluric_model.wave)) # model.wave = telluric_model.wave # model.flux *= telluric_model.flux #elif len(model.wave) < len(telluric_model.wave): ## This should be always true telluric_model.flux = np.array(nsp.integralResample(xh=telluric_model.wave, yh=telluric_model.flux, xl=model.wave)) telluric_model.wave = model.wave model.flux *= telluric_model.flux #elif len(model.wave) == len(telluric_model.wave): # model.flux *= telluric_model.flux return model
def convolveTelluric(lsf, airmass, pwv, telluric_data): """ Return a convolved telluric transmission model given a telluric data and lsf. """ # get a telluric standard model wavelow = telluric_data.wave[0] - 50 wavehigh = telluric_data.wave[-1] + 50 modelwave, modelflux = InterpTelluricModel(wavelow=wavelow, wavehigh=wavehigh, airmass=airmass, pwv=pwv) #modelflux **= alpha # lsf modelflux = nsp.broaden(wave=modelwave, flux=modelflux, vbroad=lsf, rotate=False, gaussian=True) # resample modelflux = np.array(nsp.integralResample(xh=modelwave, yh=modelflux, xl=telluric_data.wave)) modelwave = telluric_data.wave telluric_model = nsp.Model() telluric_model.flux = modelflux telluric_model.wave = modelwave return telluric_model
def makeModel(teff, logg, z, vsini, rv, alpha, wave_offset, flux_offset, **kwargs): """ Return a forward model. Parameters ---------- params : a dictionary that specifies the parameters such as teff, logg, z. data : an input science data used for continuum correction Returns ------- model: a synthesized model """ # read in the parameters order = kwargs.get('order', 33) modelset = kwargs.get('modelset', 'btsettl08') lsf = kwargs.get('lsf', 6.0) # instrumental LSF tell = kwargs.get('tell', True) # apply telluric data = kwargs.get('data', None) # for continuum correction and resampling if data is not None: order = data.order # read in a model #model = nsp.Model(teff=teff, logg=logg, feh=z, order=order, modelset=modelset) model = nsp.Model() model.wave, model.flux = InterpModel(teff, logg, modelset=modelset, order=order) model.wave *= 10000 # wavelength offset #model.wave += wave_offset # apply vsini model.flux = nsp.broaden(wave=model.wave, flux=model.flux, vbroad=vsini, rotate=True, gaussian=False) # apply rv (including the barycentric correction) model.wave = nsp.rvShift(model.wave, rv=rv) # apply telluric if tell is True: model = nsp.applyTelluric(model=model, alpha=alpha, airmass='1.5') # NIRSPEC LSF model.flux = nsp.broaden(wave=model.wave, flux=model.flux, vbroad=lsf, rotate=False, gaussian=True) # add a fringe pattern to the model #model.flux *= (1+amp*np.sin(freq*(model.wave-phase))) # wavelength offset model.wave += wave_offset # integral resampling if data is not None: model.flux = np.array( nsp.integralResample(xh=model.wave, yh=model.flux, xl=data.wave)) model.wave = data.wave # contunuum correction model = nsp.continuum(data=data, mdl=model) # flux offset model.flux += flux_offset #model.flux **= (1 + flux_exponent_offset) return model
# apply rv (including the barycentric correction) model.wave = nsp.rvShift(model.wave, rv=rv) model_notell = copy.deepcopy(model) # apply telluric model = nsp.applyTelluric(model=model, alpha=alpha) # NIRSPEC LSF model.flux = nsp.broaden(wave=model.wave, flux=model.flux, vbroad=lsf, rotate=False, gaussian=True) # integral resampling model.flux = np.array( nsp.integralResample(xh=model.wave, yh=model.flux, xl=data.wave)) model.wave = data.wave # contunuum correction model, cont_factor = nsp.continuum(data=data, mdl=model, prop=True) # NIRSPEC LSF model_notell.flux = nsp.broaden(wave=model_notell.wave, flux=model_notell.flux, vbroad=lsf, rotate=False, gaussian=True) # integral resampling model_notell.flux = np.array( nsp.integralResample(xh=model_notell.wave,
def makeModel(teff, logg, z, vsini, rv, wave_offset, flux_offset, **kwargs): """ Return a forward model. Parameters ---------- teff : effective temperature data : an input science data used for continuum correction Optional Parameters ------------------- Returns ------- model: a synthesized model """ # read in the parameters order = kwargs.get('order', 33) modelset = kwargs.get('modelset', 'btsettl08') instrument = kwargs.get('instrument', 'nirspec') lsf = kwargs.get('lsf', 6.0) # instrumental LSF tell = kwargs.get('tell', True) # apply telluric data = kwargs.get('data', None) # for continuum correction and resampling if data is not None and instrument == 'nirspec': order = data.order # read in a model #print('teff ',teff,'logg ',logg, 'z', z, 'order', order, 'modelset', modelset) #print('teff ',type(teff),'logg ',type(logg), 'z', type(z), 'order', type(order), 'modelset', type(modelset)) model = nsp.Model(teff=teff, logg=logg, feh=z, order=order, modelset=modelset, instrument=instrument) elif data is not None and instrument == 'apogee': model = nsp.Model(teff=teff, logg=logg, feh=z, modelset=modelset, instrument=instrument) # wavelength offset #model.wave += wave_offset # apply vsini model.flux = nsp.broaden(wave=model.wave, flux=model.flux, vbroad=vsini, rotate=True, gaussian=False) # apply rv (including the barycentric correction) model.wave = nsp.rvShift(model.wave, rv=rv) # instrumental LSF model.flux = nsp.broaden(wave=model.wave, flux=model.flux, vbroad=lsf, rotate=False, gaussian=True) # add a fringe pattern to the model #model.flux *= (1+amp*np.sin(freq*(model.wave-phase))) # wavelength offset model.wave += wave_offset # integral resampling if data is not None: model.flux = np.array( nsp.integralResample(xh=model.wave, yh=model.flux, xl=data.wave)) model.wave = data.wave # contunuum correction model = nsp.continuum(data=data, mdl=model) # flux offset model.flux += flux_offset #model.flux **= (1 + flux_exponent_offset) return model
def makeModel(teff,logg,z,vsini,rv,alpha,wave_offset,flux_offset,**kwargs): """ Return a forward model. Parameters ---------- teff : effective temperature data : an input science data used for continuum correction Optional Parameters ------------------- Returns ------- model: a synthesized model """ # read in the parameters order = kwargs.get('order', 33) modelset = kwargs.get('modelset', 'btsettl08') instrument = kwargs.get('instrument', 'nirspec') lsf = kwargs.get('lsf', 6.0) # instrumental LSF tell = kwargs.get('tell', True) # apply telluric data = kwargs.get('data', None) # for continuum correction and resampling output_stellar_model = kwargs.get('output_stellar_model', False) if data is not None and instrument == 'nirspec': order = data.order # read in a model #print('teff ',teff,'logg ',logg, 'z', z, 'order', order, 'modelset', modelset) #print('teff ',type(teff),'logg ',type(logg), 'z', type(z), 'order', type(order), 'modelset', type(modelset)) model = nsp.Model(teff=teff, logg=logg, feh=z, order=order, modelset=modelset, instrument=instrument) #elif data is not None and instrument == 'apogee': elif instrument == 'apogee': model = nsp.Model(teff=teff, logg=logg, feh=z, modelset=modelset, instrument=instrument) elif data is None and instrument == 'nirspec': model = nsp.Model(teff=teff, logg=logg, feh=z, order=order, modelset=modelset, instrument=instrument) # wavelength offset #model.wave += wave_offset # apply vsini model.flux = nsp.broaden(wave=model.wave, flux=model.flux, vbroad=vsini, rotate=True, gaussian=False) # apply rv (including the barycentric correction) model.wave = rvShift(model.wave, rv=rv) if output_stellar_model: stellar_model = copy.deepcopy(model) # apply telluric if tell is True: model = nsp.applyTelluric(model=model, alpha=alpha, airmass='1.5') # instrumental LSF model.flux = nsp.broaden(wave=model.wave, flux=model.flux, vbroad=lsf, rotate=False, gaussian=True) if output_stellar_model: stellar_model.flux = nsp.broaden(wave=stellar_model.wave, flux=stellar_model.flux, vbroad=lsf, rotate=False, gaussian=True) # add a fringe pattern to the model #model.flux *= (1+amp*np.sin(freq*(model.wave-phase))) # wavelength offset model.wave += wave_offset if output_stellar_model: stellar_model.wave += wave_offset # integral resampling if data is not None: model.flux = np.array(nsp.integralResample(xh=model.wave, yh=model.flux, xl=data.wave)) model.wave = data.wave if output_stellar_model: stellar_model.flux = np.array(nsp.integralResample(xh=stellar_model.wave, yh=stellar_model.flux, xl=data.wave)) stellar_model.wave = data.wave # contunuum correction if data.instrument == 'nirspec': if output_stellar_model: model, cont_factor = nsp.continuum(data=data, mdl=model, prop=True) stellar_model.flux *= cont_factor else: model = nsp.continuum(data=data, mdl=model) elif data.instrument == 'apogee' and data.datatype =='apvisit': ## set the order in the continuum fit deg = 5 ## because of the APOGEE bands, continuum is corrected from three pieces of the spectra data0 = copy.deepcopy(data) model0 = copy.deepcopy(model) range0 = np.where((data0.wave >= data.oriWave0[0][-1]) & (data0.wave <= data.oriWave0[0][0])) data0.wave = data0.wave[range0] data0.flux = data0.flux[range0] data0.noise = data0.noise[range0] model0.wave = model0.wave[range0] model0.flux = model0.flux[range0] model0 = nsp.continuum(data=data0, mdl=model0, deg=deg) data1 = copy.deepcopy(data) model1 = copy.deepcopy(model) range1 = np.where((data1.wave >= data.oriWave0[1][-1]) & (data1.wave <= data.oriWave0[1][0])) data1.wave = data1.wave[range1] data1.flux = data1.flux[range1] data1.noise = data1.noise[range1] model1.wave = model1.wave[range1] model1.flux = model1.flux[range1] model1 = nsp.continuum(data=data1, mdl=model1, deg=deg) data2 = copy.deepcopy(data) model2 = copy.deepcopy(model) range2 = np.where((data2.wave >= data.oriWave0[2][-1]) & (data2.wave <= data.oriWave0[2][0])) data2.wave = data2.wave[range2] data2.flux = data2.flux[range2] data2.noise = data2.noise[range2] model2.wave = model2.wave[range2] model2.flux = model2.flux[range2] model2 = nsp.continuum(data=data2, mdl=model2, deg=deg) model.flux = np.array( list(model0.flux) + list(model1.flux) + list(model2.flux) ) model.wave = np.array( list(model0.wave) + list(model1.wave) + list(model2.wave) ) elif data.instrument == 'apogee' and data.datatype =='apstar': model = nsp.continuum(data=data, mdl=model) # flux offset model.flux += flux_offset if output_stellar_model: stellar_model.flux += flux_offset #model.flux **= (1 + flux_exponent_offset) if output_stellar_model: return model, stellar_model else: return model
def continuumTelluric(data, model=None, order=None): """ Return a continnum telluric standard data. Default: return a telluric flux of mean 1. Parameters ---------- data: spectrum object The input telluric data to be continuum corrected model: (optional) model object The telluric model to obtain the mean flux Instead of 1 as in default, it returns a constant shift by the difference between the mean flux of the telluric data and that of the telluric model Returns ------- data: spectrum object continuum corrected telluric data Examples -------- >>> import nirspec_pip as nsp >>> nsp.continuumTelluric(data) >>> nsp.continuumTelluric(data,model) """ if model is None: wavelow = data.wave[0] - 20 wavehigh = data.wave[-1] + 20 model = nsp.getTelluric(wavelow, wavehigh) if not data.applymask: data2 = copy.deepcopy(data) data.maskBySigmas(sigma=1.5) else: data2 = copy.deepcopy(data) data.wave = data.wave[10:-30] data.flux = data.flux[10:-30] data.noise = data.noise[10:-30] # plt.plot(data.wave,data.flux, alpha=0.5) # plt.plot(data2.wave,data2.flux, alpha=0.5) # plt.show() # plt.close() if data.order == 35: # O35 has a voigt absorption profile popt, pcov = curve_fit( voigt_profile, data.wave[20:-20], data.flux[20:-20], p0=[21660, 2000, 0.1, 0.1, 0.01, 0.1, 10000, 1000], maxfev=10000) #plt.plot(data.wave,data.flux,'k-',alpha=0.5) #plt.plot(data.wave,voigt_profile(data.wave,*popt),'r-',alpha=0.5) #plt.show() #plt.close() const = np.mean(data.flux/voigt_profile(data.wave, *popt))\ -np.mean(model.flux) data.flux = data.flux / voigt_profile(data.wave, *popt) - const data.noise = data.noise / voigt_profile(data.wave, *popt) #if not data.applymask: data2.flux = data2.flux / voigt_profile(data2.wave, *popt) - const data2.noise = data2.noise / voigt_profile(data2.wave, *popt) data = data2 elif data.order == 38 or data.order == 30: # O38 has rich absorption features def fit_continuum_O38(x, a, b, **kwargs): flux = kwargs.get('flux', data.flux) linear = a * x + b return flux / linear model2 = copy.deepcopy(model) model2.flux = nsp.broaden(wave=model2.wave, flux=model2.flux, vbroad=4.8, rotate=False, gaussian=True) model2.flux = np.array( nsp.integralResample(xh=model2.wave, yh=model2.flux, xl=data.wave)) model2.wave = data.wave popt, pcov = curve_fit(fit_continuum_O38, data.wave, model2.flux, p0=[8.54253062e+00, -166000]) #const = np.mean(data.flux/linear_fit(data.wave, *popt))-np.mean(model.flux) #data.flux = data.flux/linear_fit(data.wave, *popt) - const data.flux = data.flux / linear_fit(data.wave, *popt) data.noise = data.noise / linear_fit(data.wave, *popt) #if not data.applymask: data2.flux /= linear_fit(data2.wave, *popt) data2.noise /= linear_fit(data2.wave, *popt) data = data2 elif data.order == 55: popt, pcov = curve_fit(_continuumFit, data.wave, data.flux) #if data.applymask: # data.flux = data.flux/_continuumFit(data.wave, *popt) # data.noise = data.noise/_continuumFit(data.wave, *popt) # # factor = np.max(data.flux) # data.flux /= factor # data.noise /= factor # data.flux /= 0.93 # data.noise /= 0.93 #elif not data.applymask: data2.flux = data2.flux / _continuumFit(data2.wave, *popt) data2.noise = data2.noise / _continuumFit(data2.wave, *popt) factor = np.max(data2.flux) data2.flux = data2.flux / factor data2.noise = data2.noise / factor data2.flux /= 0.93 data2.noise /= 0.93 data = data2 elif data.order == 56: # select the highest points to fit a polynomial x1 = np.max(data.flux[0:100]) x2 = np.max(data.flux[100:150]) x3 = np.max(data.flux[200:300]) x4 = np.max(data.flux[300:400]) x5 = np.max(data.flux[600:700]) x6 = np.max(data.flux[700:800]) a = [ float(data.wave[np.where(data.flux == x1)]), float(data.wave[np.where(data.flux == x2)]), float(data.wave[np.where(data.flux == x3)]), float(data.wave[np.where(data.flux == x4)]), float(data.wave[np.where(data.flux == x5)]), float(data.wave[np.where(data.flux == x6)]) ] b = [x1, x2, x3, x4, x5, x6] popt, pcov = curve_fit(_continuumFit, a, b) data.flux = data.flux / _continuumFit(data.wave, *popt) * 0.85 data.noise = data.noise / _continuumFit(data.wave, *popt) * 0.85 #if not data.applymask: data2.flux = data2.flux / _continuumFit(data2.wave, *popt) * 0.85 data2.noise = data2.noise / _continuumFit(data2.wave, *popt) * 0.85 data = data2 elif data.order == 59: #wave0 = int(data.wave[np.where(data.flux==np.min(data.flux))]) popt, pcov = curve_fit( voigt_profile, data.wave, data.flux, p0=[12820, 2000, 0.1, 0.1, 0.01, 0.1, 10000, 1000], maxfev=100000) #p0=[wave0,2000,0.1,0.1,0.01,0.1,10000,1000], #maxfev=10000) data.flux /= voigt_profile(data.wave, *popt) data.noise /= voigt_profile(data.wave, *popt) #if not data.applymask: data2.flux /= voigt_profile(data2.wave, *popt) data2.noise /= voigt_profile(data2.wave, *popt) data = data2 #plt.plot(data.wave,data.flux) #plt.show() #plt.close() ## this is not true in general!! elif data.order == 65: # O65 is best mateched by a gaussian absorption feature popt, pcov = curve_fit(gaus_absorption_only, data.wave, data.flux, p0=[11660, 50, 2000, 2000], maxfev=100000) const = np.mean( data.flux / gaus_absorption_only(data.wave, *popt)) - np.mean( model.flux) data.flux = data.flux / gaus_absorption_only(data.wave, *popt) - const data.noise /= gaus_absorption_only(data.wave, *popt) #if not data.applymask: data2.flux = data2.flux / gaus_absorption_only(data2.wave, * popt) - const data2.noise /= gaus_absorption_only(data2.wave, *popt) data = data2 else: # this second order polynomial continnum correction # works for the O33, O34, O36, and O37 popt, pcov = curve_fit(_continuumFit, data.wave, data.flux) const = np.mean(data.flux / _continuumFit(data.wave, *popt)) - np.mean( model.flux) if data.order == 57: const = 0 data.flux = data.flux / _continuumFit(data.wave, *popt) - const data.noise = data.noise / _continuumFit(data.wave, *popt) #if not data.applymask: data2.flux = data2.flux / _continuumFit(data2.wave, *popt) - const data2.noise = data2.noise / _continuumFit(data2.wave, *popt) data = data2 return data