예제 #1
0
def convolveTelluric(lsf, airmass, pwv, telluric_data):
    """
    Return a convolved, normalized 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 = smart.broaden(wave=modelwave,
                              flux=modelflux,
                              vbroad=lsf,
                              rotate=False,
                              gaussian=True)
    # resample
    modelflux = np.array(
        smart.integralResample(xh=modelwave,
                               yh=modelflux,
                               xl=telluric_data.wave))
    modelwave = telluric_data.wave
    telluric_model = smart.Model()
    telluric_model.flux = modelflux
    telluric_model.wave = modelwave

    return telluric_model
예제 #2
0
def convolveTelluric(lsf, telluric_data, alpha=1.0, airmass='1.0', 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 = smart.getTelluric(wavelow=wavelow,
                                       wavehigh=wavehigh,
                                       airmass=airmass,
                                       pwv=pwv)
    telluric_model.flux **= alpha
    # lsf
    telluric_model.flux = smart.broaden(wave=telluric_model.wave,
                                        flux=telluric_model.flux,
                                        vbroad=lsf,
                                        rotate=False,
                                        gaussian=True)
    # resample
    telluric_model.flux = np.array(
        smart.integralResample(xh=telluric_model.wave,
                               yh=telluric_model.flux,
                               xl=telluric_data.wave))
    telluric_model.wave = telluric_data.wave
    return telluric_model
예제 #3
0
def applyTelluric(model, tell_alpha=1.0, airmass=1.5, pwv=0.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 = smart.getTelluric(wavelow=wavelow, wavehigh=wavehigh, alpha=alpha, airmass=airmass)

    telluric_model = smart.Model()
    telluric_model.wave, telluric_model.flux = smart.InterpTelluricModel(
        wavelow=wavelow, wavehigh=wavehigh, airmass=airmass, pwv=pwv)

    # apply the telluric alpha parameter
    telluric_model.flux = telluric_model.flux**(tell_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(smart.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(
        smart.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
예제 #4
0
def telluric_mask(data, sigma=2.5, lsf=4.8, pwv=None, pixel_start=10, pixel_end=-30, outlier_rejection=2.5, diagnostic=True, save_to_path='./'):
	"""
	Routine to generate a mask for tellurics as the MCMC initialization.

	Parameters
	----------
	sigma 				: 	float; default 2.5
							The sigma-clipping method to reject the outliers.

	lsf 				:	float; default 4.8
							The value of line spread function. 
							The default is the normal value of LSF for Keck/NIRSPEC.

	pwv 				: 	float; default None
							precitable water vapor.
							The default is to run the chi2 grids of pwv to obtain the best pwv.

	pixel_start			: 	int; default 10
							The starting pixel to compute the mask.

	pixel_end 			: 	int; default -30
							The ending pixel to compute the mask.

	outlier_rejection 	: 	float; default 2.5
							The value for number * sigma to reject outliers

	diagnostic 			: 	bool; default True
							Generate the diagnostic plots for pwv-ch2 and model-data before/after masks

	save_to_path 		: 	str; default the current working directory


	Returns
	-------
	mask 				: numpy array

	pwv 				: float 

	airmass 			: float


	Example
	-------
	>>> from smart.forward_model import mask
	>>> tell_mask, pwv, airmass = mask.telluric_mask(data, diagnostic=False)

	"""

	data.flux  = data.flux[pixel_start:pixel_end]
	data.wave  = data.wave[pixel_start:pixel_end]
	data.noise = data.noise[pixel_start:pixel_end]

	data0 = copy.deepcopy(data)

	# take the closest airmass from the header
	airmass = float(round(data.header['AIRMASS']*2)/2)
	if airmass > 3.0: airmass = 3.0

	# simple chi2 comparison with different pwv
	if pwv is None:
		pwvs = [0.5, 1.0, 1.5, 2.5, 3.5, 5.0, 7.5, 10.0, 20.0]
		pwv_chi2 = []
		
		for pwv in pwvs:
			data_tmp       = copy.deepcopy(data)
	
			#data_tmp       = smart.continuumTelluric(data=data_tmp, model=model_tmp)
			model_tmp      = tellurics.makeTelluricModel(lsf=lsf, airmass=airmass, pwv=pwv, flux_offset=0, wave_offset=0, data=data_tmp, deg=10)
	
			model_tmp.flux = np.array(smart.integralResample(xh=model_tmp.wave, yh=model_tmp.flux, xl=data_tmp.wave))
			model_tmp.wave = data_tmp.wave

			#plt.plot(data_tmp.wave, data_tmp.flux, 'k-')
			#plt.plot(model_tmp.wave, model_tmp.flux, 'r-')
			#plt.show()
			#plt.close()
	
			pwv_chi2.append(smart.chisquare(data_tmp, model_tmp))
		# find the pwv with minimum chisquare
		pwv_chi2_array = np.array(pwv_chi2)
		
		if diagnostic:
			plt.plot(pwvs, pwv_chi2)
			plt.xlabel('pwv (mm)', fontsize=15)
			plt.ylabel('$\chi^2$', fontsize=15)
			plt.tight_layout()
			plt.savefig(save_to_path+'pwv_chi2.png')
			#plt.show()
			plt.close()

	pwv_min_index = np.where(pwv_chi2_array == np.min(pwv_chi2_array))[0][0]
	pwv           = pwvs[pwv_min_index]

	data_tmp  = copy.deepcopy(data)

	model      = tellurics.makeTelluricModel(lsf=lsf, airmass=airmass, pwv=pwv, flux_offset=0, wave_offset=0, data=data_tmp)

	model_0 = copy.deepcopy(model)

	# generate the mask based on sigma clipping
	pixel = np.delete(np.arange(len(data.oriWave)), data.mask)[pixel_start: pixel_end]
	#pixel = np.delete(np.arange(len(data_tmp.oriWave)),data_tmp.mask)
	mask  = pixel[np.where(np.abs(data_tmp.flux-model.flux) > outlier_rejection*np.std(data_tmp.flux-model.flux))]

	#plt.plot(data_tmp.wave, data_tmp.flux, 'k-')
	#plt.plot(model.wave, model.flux, 'r-')
	#plt.show()
	#plt.close()

	data_tmp.mask_custom(mask)
	data_tmp.flux  = data_tmp.flux[pixel_start:pixel_end]
	data_tmp.wave  = data_tmp.wave[pixel_start:pixel_end]
	data_tmp.noise = data_tmp.noise[pixel_start:pixel_end]

	#plt.plot(data_tmp.wave, data_tmp.flux, 'k-')
	#plt.plot(model.wave, model.flux, 'r-')
	#plt.show()
	#plt.close()

	# use curve_fit
	def tell_model_fit(wave, airmass, pwv, flux_offset, wave_offset):
		model      = tellurics.makeTelluricModel(lsf=lsf, airmass=airmass, pwv=pwv, flux_offset=flux_offset, wave_offset=wave_offset, data=data_tmp)
		return model.flux

	print('initial airmass and pwv', airmass, pwv)
	flux_med = np.median(data_tmp.flux)
	p0 = [airmass, pwv, 0, 0]
	bounds = ([airmass-0.5, pwv-0.5, -flux_med*0.05, -0.05], [airmass+0.5, pwv+0.5, flux_med*0.05, 0.05])

	popt, pcov = curve_fit(tell_model_fit, data_tmp.wave, data_tmp.flux, p0=p0, bounds=bounds)

	airmass, pwv, flux_offset, wave_offset = popt[0], popt[1], popt[2], popt[3]
	print('best-fit airmass, pwv, flux_offset, wave_offset', airmass, pwv, flux_offset, wave_offset)
	model      = tellurics.makeTelluricModel(lsf=lsf, airmass=airmass, pwv=pwv, flux_offset=0, wave_offset=0, data=data_tmp)
	print('old telluric mask', mask)
	pixel = np.delete(np.arange(len(data_tmp.oriWave)), mask)[pixel_start: pixel_end]
	#print('len pixel, data, model', len(pixel), len(data_tmp.wave), len(model.wave))
	mask  = pixel[np.where(np.abs(data_tmp.flux-model.flux) > outlier_rejection*np.std(data_tmp.flux-model.flux))]
	# combine the masks
	mask = np.union1d(mask,np.array(data_tmp.mask))
	print('new telluric mask', mask)

	data.mask_custom(mask)
	data.flux  = data.flux[pixel_start:pixel_end]
	data.wave  = data.wave[pixel_start:pixel_end]
	data.noise = data.noise[pixel_start:pixel_end]
	print(data.mask)

	#plt.plot(data.wave, data.flux, 'k-')
	#plt.plot(model.wave, model.flux, 'r-')
	#plt.plot(model_0.wave, model_0.flux, 'b-', alpha=0.5)
	#plt.show()
	#plt.close()

	if diagnostic:
		data.flux  = data.flux[pixel_start:pixel_end]
		data.wave  = data.wave[pixel_start:pixel_end]
		data.noise = data.noise[pixel_start:pixel_end]
		
		model      = tellurics.makeTelluricModel(lsf=lsf, airmass=airmass, pwv=pwv, flux_offset=0, wave_offset=0, data=data)
		
		plt.plot(data0.wave, data0.flux, 'k-', label='original data', alpha=0.5)
		plt.plot(data.wave, data.flux, 'k-', label='masked data')
		plt.plot(model.wave, model.flux, 'r-', alpha=0.7)
		plt.plot(data.wave, data.flux-model.flux, 'r-')
		plt.xlabel('$\lambda (\AA)$')
		plt.ylabel('$F_{\lambda}$')
		plt.savefig(save_to_path+'telluric_data_model_mask.png')
		#plt.show()
		plt.close()

	return mask.tolist(), pwv, airmass
예제 #5
0
def continuumTelluric(data, model=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 smart
    >>> smart.continuumTelluric(data)

    >>> smart.continuumTelluric(data,model)

    """
    if model is None:
        wavelow = data.wave[0] - 20
        wavehigh = data.wave[-1] + 20
        model = smart.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 = smart.broaden(wave=model2.wave,
                                    flux=model2.flux,
                                    vbroad=4.8,
                                    rotate=False,
                                    gaussian=True)
        model2.flux = np.array(
            smart.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
예제 #6
0
def makeModel(teff,
              logg=5,
              metal=0,
              vsini=1,
              rv=0,
              tell_alpha=1.0,
              airmass=1.0,
              pwv=0.5,
              wave_offset=0,
              flux_offset=0,
              **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')
    veiling = kwargs.get('veiling', 0)  # flux veiling parameter
    lsf = kwargs.get('lsf', 4.5)  # instrumental LSF
    if instrument == 'apogee':
        try:
            import apogee_tools as ap
        except ImportError:
            print(
                'Need to install the package "apogee_tools" (https://github.com/jbirky/apogee_tools) \n'
            )
        xlsf = kwargs.get('xlsf',
                          np.linspace(-7., 7.,
                                      43))  # APOGEE instrumental LSF sampling
        wave_off1 = kwargs.get('wave_off1')  # wavelength offset for chip a
        wave_off2 = kwargs.get('wave_off2')  # wavelength offset for chip b
        wave_off3 = kwargs.get('wave_off3')  # wavelength offset for chip c
        c0_1 = kwargs.get('c0_1')  # constant flux offset for chip a
        c0_2 = kwargs.get('c0_2')  # linear flux offset for chip a
        c1_1 = kwargs.get('c1_1')  # constant flux offset for chip b
        c1_2 = kwargs.get('c1_2')  # linear flux offset for chip b
        c2_1 = kwargs.get('c2_1')  # constant flux offset for chip c
        c2_2 = kwargs.get('c2_2')  # linear flux offset for chip c

    tell = kwargs.get('tell', True)  # apply telluric
    #tell_alpha = kwargs.get('tell_alpha', 1.0) # Telluric alpha power
    binary = kwargs.get('binary', False)  # make a binary model

    # assume the secondary has the same metallicity
    if binary:
        teff2 = kwargs.get('teff2')
        logg2 = kwargs.get('logg2')
        rv2 = kwargs.get('rv2')
        vsini2 = kwargs.get('vsini2')
        flux_scale = kwargs.get('flux_scale', 0.8)

    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 = smart.Model(teff=teff,
                            logg=logg,
                            metal=metal,
                            order=str(order),
                            modelset=modelset,
                            instrument=instrument)

    #elif data is not None and instrument == 'apogee':
    elif instrument == 'apogee':
        model = smart.Model(teff=teff,
                            logg=logg,
                            metal=metal,
                            modelset=modelset,
                            instrument=instrument)
        # Dirty fix here
        model.wave = model.wave[np.where(model.flux != 0)]
        model.flux = model.flux[np.where(model.flux != 0)]

        # apply vmicro
        vmicro = 2.478 - 0.325 * logg
        model.flux = smart.broaden(wave=model.wave,
                                   flux=model.flux,
                                   vbroad=vmicro,
                                   rotate=False,
                                   gaussian=True)

    elif data is None and instrument == 'nirspec':
        model = smart.Model(teff=teff,
                            logg=logg,
                            metal=metal,
                            order=str(order),
                            modelset=modelset,
                            instrument=instrument)

    # wavelength offset
    #model.wave += wave_offset

    # apply vsini
    model.flux = smart.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)

    # flux veiling
    model.flux += veiling

    ## if binary is True: make a binary model
    if binary:
        model2 = smart.Model(teff=teff2,
                             logg=logg2,
                             metal=metal,
                             order=str(order),
                             modelset=modelset,
                             instrument=instrument)
        # apply vsini
        model2.flux = smart.broaden(wave=model2.wave,
                                    flux=model2.flux,
                                    vbroad=vsini2,
                                    rotate=True,
                                    gaussian=False)
        # apply rv (including the barycentric correction)
        model2.wave = rvShift(model2.wave, rv=rv2)
        # linearly interpolate the model2 onto the model1 grid
        fit = interp1d(model2.wave, model2.flux)

        select_wavelength = np.where((model.wave < model2.wave[-1])
                                     & (model.wave > model2.wave[0]))
        model.flux = model.flux[select_wavelength]
        model.wave = model.wave[select_wavelength]

        # combine the models together and scale the secondary flux
        model.flux += flux_scale * fit(model.wave)

    if output_stellar_model:
        stellar_model = copy.deepcopy(model)
        if binary:
            model2.flux = flux_scale * fit(model.wave)

    # apply telluric
    if tell is True:
        model = smart.applyTelluric(model=model,
                                    tell_alpha=tell_alpha,
                                    airmass=airmass,
                                    pwv=pwv)

    # instrumental LSF
    if instrument == 'nirspec':
        model.flux = smart.broaden(wave=model.wave,
                                   flux=model.flux,
                                   vbroad=lsf,
                                   rotate=False,
                                   gaussian=True)
    elif instrument == 'apogee':
        model.flux = ap.apogee_hack.spec.lsf.convolve(model.wave,
                                                      model.flux,
                                                      lsf=lsf,
                                                      xlsf=xlsf).flatten()
        model.wave = ap.apogee_hack.spec.lsf.apStarWavegrid()
        # Remove the NANs
        model.wave = model.wave[~np.isnan(model.flux)]
        model.flux = model.flux[~np.isnan(model.flux)]

    if output_stellar_model:
        stellar_model.flux = smart.broaden(wave=stellar_model.wave,
                                           flux=stellar_model.flux,
                                           vbroad=lsf,
                                           rotate=False,
                                           gaussian=True)
        if binary:
            model2.flux = smart.broaden(wave=model2.wave,
                                        flux=model2.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
        if binary:
            model2.wave = stellar_model.wave

    # integral resampling
    if data is not None:
        if instrument == 'nirspec':
            model.flux = np.array(
                smart.integralResample(xh=model.wave,
                                       yh=model.flux,
                                       xl=data.wave))
            model.wave = data.wave

            if output_stellar_model:
                stellar_model.flux = np.array(
                    smart.integralResample(xh=stellar_model.wave,
                                           yh=stellar_model.flux,
                                           xl=data.wave))
                stellar_model.wave = data.wave
                if binary:
                    model2.flux = np.array(
                        smart.integralResample(xh=model2.wave,
                                               yh=model2.flux,
                                               xl=data.wave))
                    model2.wave = data.wave

        # contunuum correction
        if data.instrument == 'nirspec':
            niter = 5  # continuum iteration
            if output_stellar_model:
                model, cont_factor = smart.continuum(data=data,
                                                     mdl=model,
                                                     prop=True)
                for i in range(niter):
                    model, cont_factor2 = smart.continuum(data=data,
                                                          mdl=model,
                                                          prop=True)
                    cont_factor *= cont_factor2
                stellar_model.flux *= cont_factor
                if binary:
                    model2.flux *= cont_factor
            else:
                model = smart.continuum(data=data, mdl=model)
                for i in range(niter):
                    model = smart.continuum(data=data, mdl=model)
        elif data.instrument == 'apogee':
            ## 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)

            # wavelength offset
            model0.wave += wave_off1

            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]
            if data0.wave[0] > data0.wave[-1]:
                data0.wave = data0.wave[::-1]
                data0.flux = data0.flux[::-1]
            model0.flux = np.array(
                smart.integralResample(xh=model0.wave,
                                       yh=model0.flux,
                                       xl=data0.wave))
            model0.wave = data0.wave
            model0 = smart.continuum(data=data0, mdl=model0, deg=deg)
            # flux corrections
            model0.flux = (model0.flux + c0_1) * np.e**(-c0_2)

            data1 = copy.deepcopy(data)
            model1 = copy.deepcopy(model)

            # wavelength offset
            model1.wave += wave_off2

            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]
            if data1.wave[0] > data1.wave[-1]:
                data1.wave = data1.wave[::-1]
                data1.flux = data1.flux[::-1]
            model1.flux = np.array(
                smart.integralResample(xh=model1.wave,
                                       yh=model1.flux,
                                       xl=data1.wave))
            model1.wave = data1.wave
            model1 = smart.continuum(data=data1, mdl=model1, deg=deg)

            # flux corrections
            model1.flux = (model1.flux + c1_1) * np.e**(-c1_2)

            data2 = copy.deepcopy(data)
            model2 = copy.deepcopy(model)

            # wavelength offset
            model2.wave += wave_off3

            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]
            if data2.wave[0] > data2.wave[-1]:
                data2.wave = data2.wave[::-1]
                data2.flux = data2.flux[::-1]

            model2.flux = np.array(
                smart.integralResample(xh=model2.wave,
                                       yh=model2.flux,
                                       xl=data2.wave))
            model2.wave = data2.wave
            model2 = smart.continuum(data=data2, mdl=model2, deg=deg)
            # flux corrections
            model2.flux = (model2.flux + c2_1) * np.e**(-c2_2)

            ## scale the flux to be the same as the data
            #model0.flux *= (np.std(data0.flux)/np.std(model0.flux))
            #model0.flux -= np.median(model0.flux) - np.median(data0.flux)

            #model1.flux *= (np.std(data1.flux)/np.std(model1.flux))
            #model1.flux -= np.median(model1.flux) - np.median(data1.flux)

            #model2.flux *= (np.std(data2.flux)/np.std(model2.flux))
            #model2.flux -= np.median(model2.flux) - np.median(data2.flux)

            model.flux = np.array(
                list(model2.flux) + list(model1.flux) + list(model0.flux))
            model.wave = np.array(
                list(model2.wave) + list(model1.wave) + list(model0.wave))

    if instrument == 'nirspec':
        # flux offset
        model.flux += flux_offset
        if output_stellar_model:
            stellar_model.flux += flux_offset
            if binary:
                model2.flux += flux_offset
    #model.flux **= (1 + flux_exponent_offset)

    if output_stellar_model:
        if not binary:
            return model, stellar_model
        else:
            return model, stellar_model, model2
    else:
        return model
예제 #7
0
    def coadd(self, sp, method='pixel'):
        """
		Coadd individual extractions, either in pixel space or
		wavelength space.

		Parameters
		----------
		sp 		: 	Spectrum object
					spectrum to be coadded

		method 	: 	'pixel' or 'wavelength'
					coadd based on adding pixels or wavelength
					If 'wavelength', the second spectrum would be
					10x supersample and then cross correlated
					to be optimally shifted and coadded

		Returns
		-------
		self 	: 	Spectrum object
					coadded spectra

		"""
        if method == 'pixel':
            w1 = 1 / self.oriNoise**2
            w2 = 1 / sp.oriNoise**2
            self.oriFlux = (self.oriFlux * w1 + sp.oriFlux * w2) / (w1 + w2)
            self.oriNoise = np.sqrt(1 / (w1 + w2))
            ## set up masking criteria
            self.avgFlux = np.mean(self.oriFlux)
            self.stdFlux = np.std(self.oriFlux)
            self.smoothFlux = self.oriFlux
            ## set the outliers as the flux below
            if self.applymask:
                self.smoothFlux[self.smoothFlux <= self.avgFlux -
                                2 * self.stdFlux] = 0
                self.mask = np.where(self.smoothFlux <= 0)
            else:
                self.mask = []
            self.wave = np.delete(self.oriWave, list(self.mask))
            self.flux = np.delete(self.oriFlux, list(self.mask))
            self.noise = np.delete(self.oriNoise, list(self.mask))

        elif method == 'wavelength':
            self_supers = copy.deepcopy(self)
            g = interpolate.interp1d(self.wave, self.flux)
            sp_supers = copy.deepcopy(sp)
            f = interpolate.interp1d(sp.wave, sp.flux)
            ## 10x supersample the average difference of
            ## the wavelength
            #step0 = np.mean(np.diff(self.wave))/10
            #self_supers.wave = np.arange(self.wave[0],
            #	self.wave[-1],step0)
            self_supers.flux = g(self_supers.wave)
            self_supers.oriWave = np.arange(
                self.oriWave[0], self.oriWave[-1],
                (self.oriWave[-1] - self.oriWave[0]) / 10240)
            g1 = interpolate.interp1d(self.oriWave, self.oriFlux)
            self_supers.oriFlux = g1(self_supers.oriWave)

            #step = np.mean(np.diff(sp.wave))/10
            #sp_supers.wave = np.arange(sp.wave[0],sp.wave[-1],step)
            #sp_supers.flux = f(sp_supers.wave)
            sp_supers.oriWave = np.arange(sp.oriWave[0], sp.oriWave[-1],
                                          (sp.oriWave[-1] - sp.oriWave[0]) /
                                          10240)
            f1 = interpolate.interp1d(sp.oriWave, sp.oriFlux)
            sp_supers.oriFlux = f1(sp_supers.oriWave)

            ## calculate the max cross correlation value
            def xcorr(a0, b0, shift):
                """
				Shift is the index number after supersampling 
				both of the spectra.
				"""
                a = copy.deepcopy(a0)
                b = copy.deepcopy(b0)

                ## shift the wavelength of b
                length = b.oriFlux.shape[0]
                if shift >= 0:
                    mask_a = np.arange(0, shift, 1)
                    a.oriFlux = np.delete(a.oriFlux, mask_a)
                    mask_b = np.arange(length - 1, length - shift - 1, -1)
                    b.oriFlux = np.delete(b.oriFlux, mask_b)

                elif shift < 0:
                    mask_a = np.arange(length - 1, length + shift - 1, -1)
                    a.oriFlux = np.delete(a.oriFlux, mask_a)
                    mask_b = np.arange(0, -shift, 1)
                    b.oriFlux = np.delete(b.oriFlux, mask_b)

                ## shift the wavelength of b
                #b.wave += shift * step
                ## discard the points where the wavelength values
                ## are larger
                #condition = (a.wave > b.wave[0]) & (a.wave < b.wave[-1])

                #a.flux = a.flux[np.where(condition)]
                #a.wave = a.wave[np.where(condition)]
                ## resampling the telluric model
                #b.flux = np.array(smart.integralResample(xh=b.wave,
                #	yh=b.flux, xl=a.wave))

                return np.inner(a.oriFlux, b.oriFlux)/\
                (np.average(a.oriFlux)*np.average(b.oriFlux))/a.oriFlux.shape[0]

            xcorr_list = []
            ## mask the ending pixels
            self_supers2 = copy.deepcopy(self_supers)
            sp_supers2 = copy.deepcopy(sp_supers)
            self_supers2.wave = self_supers2.wave[1000:-1000]
            self_supers2.flux = self_supers2.flux[1000:-1000]
            sp_supers2.wave = sp_supers2.wave[1000:-1000]
            sp_supers2.flux = sp_supers2.flux[1000:-1000]
            for shift in np.arange(-10, 10, 1):
                xcorr_list.append(xcorr(self_supers2, sp_supers2, shift))

            ## dignostic plot for cc result
            fig, ax = plt.subplots()
            ax.plot(np.arange(-10, 10, 1), np.array(xcorr_list), 'k-')
            plt.show()
            plt.close()

            step = np.absolute(np.mean(np.diff(sp_supers.wave)))
            bestshift = np.arange(-10 * step, 10 * step,
                                  step)[np.argmax(xcorr_list)]
            sp_supers.oriWave += bestshift
            ## discard the points where the wavelength values
            ## are larger
            condition = (self.oriWave > sp_supers.oriWave[0])\
            & (self.oriWave < sp_supers.oriWave[-1])

            self.oriFlux = self.oriFlux[np.where(condition)]
            self.oriWave = self.oriWave[np.where(condition)]
            self.oriNoise = self.oriNoise[np.where(condition)]
            sp_supers.oriNoise = sp_supers.oriNoise[np.where(condition)]
            sp_supers.oriFlux = np.array(
                smart.integralResample(xh=sp_supers.oriWave,
                                       yh=sp_supers.oriFlux,
                                       xl=self.oriWave))

            w1 = 1 / self.oriNoise**2
            w2 = 1 / sp_supers.oriNoise**2
            self.oriFlux = (self.oriFlux * w1 + sp_supers.oriFlux * w2) / (w1 +
                                                                           w2)
            self.oriNoise = np.sqrt(1 / (w1 + w2))
            ## set up masking criteria
            self.avgFlux = np.mean(self.oriFlux)
            self.stdFlux = np.std(self.oriFlux)
            self.smoothFlux = self.oriFlux
            ## set the outliers as the flux below
            self.smoothFlux[self.smoothFlux <= self.avgFlux -
                            2 * self.stdFlux] = 0
            self.mask = np.where(self.smoothFlux <= 0)
            self.wave = np.delete(self.oriWave, list(self.mask))
            self.flux = np.delete(self.oriFlux, list(self.mask))
            self.noise = np.delete(self.oriNoise, list(self.mask))

        return self