Example #1
0
def lines(elem,asIndex=False):
    """
    NAME:
       lines
    PURPOSE:
       return the location of peaks in the window weight ('lines') for a given element
    INPUT:
       elem - element
       asIndx= (False) if yes, return the indices into an apStar-like wavelengthh grid rather than the wavelengths directly 
    OUTPUT:
       wavelengths of peaks in \AA
    HISTORY:
       2015-03-11 - Written - Bovy (IAS)
    """
    # Load the window
    win= read(elem,apStarWavegrid=True)
    import apogee.spec.plot as splot
    wavs= splot.apStarWavegrid()
    # Find peaks
    indx= (numpy.roll(win,1) < win)*(numpy.roll(win,-1) < win)\
        *(win > 0.1)
    if asIndex:
        return indx
    else:
        return wavs[indx]
Example #2
0
File: lsf.py Project: mrawls/apogee
def dummy(dx=1./3.,sparse=False):
    """
    NAME:
       dummy
    PURPOSE:
       return a 'dummy' LSF that is a delta function
    INPUT:
       dx= (1/3) spacing between LSF centers in the apStar grid
       sparse= (False) if True, return a sparse representation that can be passed to apogee.spec.lsf.convolve for easy convolution
    OUTPUT:
       LSF(x|pixel center);
       pixel centers are apStarWavegrid if dx=1, and denser 1/integer versions if dx=1/integer
    HISTORY:
       2015-03-23 - Written - Bovy (IAS)
    """
    # Are the x unit pixels or a fraction 1/hires thereof?
    hires= int(round(1./dx))
    # Setup output
    wav= apStarWavegrid()
    l10wav= numpy.log10(wav)
    dowav= l10wav[1]-l10wav[0]
    # Hi-res wavelength for output
    hireswav= 10.**numpy.arange(l10wav[0],l10wav[-1]+dowav/hires,dowav/hires)
    out= numpy.ones((len(hireswav),1))
    if sparse: out= sparsify(out)
    return out
def binaryModelGen(locationID, apogeeID, params, visit, ipf=None, ipg=None, plot=True):
	'''
	Interpolates the spectra of the individual stars in the binary using some initial parameters and their velocity
	difference.

	:param locationID: The location ID of the binary.
	:param apogeeID: The apogee ID of the binary.
	:param params: The paramters to test against the observed data.
		format: [ [Teff1, ...], [logg1, ...], [metals1, ...], [am1, ...], [nm1, ...], [cm1, ...]]
	:param visit: The visit to test against.
	:param ip: The interpolator instance that keeps running ferre. Should be faster to keep using this instead. (default=None)
	:param plot: If true plots each component in the binary in 'plots/model_gen' (default=True)
	:returns: The binary model flux and the maximum value from the cross correlation function between the modeled
	totalFlux and the continuum-normalized spectrum. (totalFlux, max)
	'''
	# Generate models (flux1, flux2)
	if (np.logical_and(ipf == None, ipg == None)):
		# todo: put in condition for both gk and f libraries
		mspecs = ferre.interpolate(params[0], params[1], params[2],	params[3], params[4], params[5])
	else:
		if (params[0][0] > 6000.):
			mspec1 = ipf(params[0][0], params[1][0], params[2][0], params[3][0], params[4][0], params[5][0])
		else:
			mspec1 = ipg(params[0][0], params[1][0], params[2][0], params[3][0], params[4][0], params[5][0])
		if (params[0][1] > 6000.):
			mspec2 = ipf(params[0][1], params[1][1], params[2][1], params[3][1], params[4][1], params[5][1])
		else:
			mspec2 = ipg(params[0][1], params[1][1], params[2][1], params[3][1], params[4][1], params[5][1])
		
		mspecs = [ mspec1, mspec2 ]
	
	for mspec in mspecs:
		mspec[np.isnan(mspec)] = 0.
	
	# Calculate deltaV
	RVs = getRVs(locationID, apogeeID, visit)
	
	# Generate the wavelength grid
	restLambda = splot.apStarWavegrid()
	# Calculates wavelength grid for the second star with a doppler shift
	shiftLambda = [restLambda * (1. + rv / (const.c / 1000.)) for rv in RVs]
	
	# The fluxes of both stars
	shiftedFlux = np.array([np.interp(restLambda, shiftLambda[i], mspecs[i]) for i in range(len(shiftLambda))])

	# The combined flux of the stars in the modeled binary (params[7][1] defined as flux ratio)
	totalFlux = (shiftedFlux[0] + (shiftedFlux[1] * params[6][1])) / 2.0

	# Make the plots
	if (plot == True):
		BinPlot.plotDeltaVCheck(locationID, apogeeID, visit,
						[[ restLambda, mspecs[0], 'blue', 'rest model specA' ],
						 [ restLambda, mspecs[1], 'green', 'rest model specB' ],
						 [ restLambda, shiftedFlux[0], 'orange', 'shift model specA' ],
						 [ restLambda, shiftedFlux[1], 'purple', 'shift model specB' ]],
						[params[0][0], params[0][1]], 'Binary Model Gen - Prelim. Proc.', folder='model_gen');

	return totalFlux
Example #4
0
def eval(x, fiber='combo', sparse=False):
    """
    NAME:
       eval
    PURPOSE:
       evaluate the LSF for a given fiber
    INPUT:
       x - Array of X values for which to compute the LSF, in pixel offset relative to pixel centers; the LSF is calculated at the x offsets for each pixel center; x need to be 1/integer equally-spaced pixel offsets
       fiber= ('combo') fiber number or 'combo' for an average LSF (uses the same one-based indexing as the APOGEE fibers [i.e., fibers range from 1 to 300])
       sparse= (False) if True, return a sparse representation that can be passed to apogee.spec.lsf.convolve for easy convolution
    OUTPUT:
       LSF(x|pixel center);
       pixel centers are apStarWavegrid if dx=1, and denser 1/integer versions if dx=1/integer
    HISTORY:
       2015-03-12 - Written based on Jon H's code (based on David N's code) - Bovy (IAS)
    """
    # Parse fiber input
    if isinstance(fiber, str) and fiber.lower() == 'combo':
        fiber = [50, 100, 150, 200, 250, 300]
    elif isinstance(fiber, int):
        fiber = [fiber]
    elif not isinstance(fiber, list) and isinstance(fiber[0], int):
        raise ValueError(
            'fiber input to apogee.spec.lsf.eval not understood ...')
    # Are the x unit pixels or a fraction 1/hires thereof?
    hires = int(round(1. / (x[1] - x[0])))
    # Setup output
    wav = apStarWavegrid()
    l10wav = numpy.log10(wav)
    dowav = l10wav[1] - l10wav[0]
    # Hi-res wavelength for output
    hireswav = 10.**numpy.arange(l10wav[0], l10wav[-1] + dowav / hires,
                                 dowav / hires)
    out = numpy.zeros((len(hireswav), len(x)))
    for chip in ['a', 'b', 'c']:
        # Get pixel array for this chip, use fiber[0] for consistency if >1 fib
        pix = wave2pix(hireswav, chip, fiber[0])
        dx = numpy.roll(
            pix,
            -hires,
        ) - pix
        dx[-1] = dx[-1 - hires]
        dx[-2] = dx[-2 - hires]
        dx[-3] = dx[-3 - hires]
        xs= numpy.tile(x,(len(hireswav),1))\
            *numpy.tile(dx,(len(x),1)).T # nwav,nx
        gd = True ^ numpy.isnan(pix)
        # Read LSF file for this chip
        lsfpars = apread.apLSF(chip, ext=0)
        # Loop through the fibers
        for fib in fiber:
            out[gd] += raw(xs[gd], pix[gd], lsfpars[:, 300 - fib])
    out[out < 0.] = 0.
    out /= numpy.tile(numpy.sum(out, axis=1), (len(x), 1)).T
    if sparse: out = sparsify(out)
    return out
Example #5
0
def eval(x,fiber='combo',sparse=False):
    """
    NAME:
       eval
    PURPOSE:
       evaluate the LSF for a given fiber
    INPUT:
       x - Array of X values for which to compute the LSF, in pixel offset relative to pixel centers; the LSF is calculated at the x offsets for each pixel center; x need to be 1/integer equally-spaced pixel offsets
       fiber= ('combo') fiber number or 'combo' for an average LSF (uses the same one-based indexing as the APOGEE fibers [i.e., fibers range from 1 to 300])
       sparse= (False) if True, return a sparse representation that can be passed to apogee.spec.lsf.convolve for easy convolution
    OUTPUT:
       LSF(x|pixel center);
       pixel centers are apStarWavegrid if dx=1, and denser 1/integer versions if dx=1/integer
    HISTORY:
       2015-03-12 - Written based on Jon H's code (based on David N's code) - Bovy (IAS)
    """
    # Parse fiber input
    if isinstance(fiber,str) and fiber.lower() == 'combo':
        fiber= [50,100,150,200,250,300]
    elif isinstance(fiber,int):
        fiber= [fiber]
    elif not isinstance(fiber,list) and isinstance(fiber[0],int):
        raise ValueError('fiber input to apogee.spec.lsf.eval not understood ...')
    # Are the x unit pixels or a fraction 1/hires thereof?
    hires= int(round(1./(x[1]-x[0])))
    # Setup output
    wav= apStarWavegrid()
    l10wav= numpy.log10(wav)
    dowav= l10wav[1]-l10wav[0]
    # Hi-res wavelength for output
    hireswav= 10.**numpy.arange(l10wav[0],l10wav[-1]+dowav/hires,dowav/hires)
    out= numpy.zeros((len(hireswav),len(x)))
    for chip in ['a','b','c']:
        # Get pixel array for this chip, use fiber[0] for consistency if >1 fib
        pix= wave2pix(hireswav,chip,fiber[0])
        dx= numpy.roll(pix,-hires,)-pix
        dx[-1]= dx[-1-hires]
        dx[-2]= dx[-2-hires]
        dx[-3]= dx[-3-hires]
        xs= numpy.tile(x,(len(hireswav),1))\
            *numpy.tile(dx,(len(x),1)).T # nwav,nx       
        gd= True-numpy.isnan(pix)
        # Read LSF file for this chip
        lsfpars= apread.apLSF(chip,ext=0)
        # Loop through the fibers
        for fib in fiber:
            out[gd]+= raw(xs[gd],pix[gd],lsfpars[:,300-fib])
    out[out<0.]= 0.
    out/= numpy.tile(numpy.sum(out,axis=1),(len(x),1)).T
    if sparse: out= sparsify(out)
    return out
Example #6
0
def equishwidth(elem,spec,specerr,refspec=None):
    """
    NAME:
       equishwidth
    PURPOSE:
       return an equivalent-width-ish quantity for a given element:

       equishwidth = \sum_lam \Delta lam_center of window x (refspec-spec)/refspec x window/specerr^2 / \sum_lam window/specerr^2

       or if refspec == 0:

       equishwidth = \sum_lam \Delta lam_center of window x (1-spec) x window/specerr^2 / \sum_lam window/specerr^2


    INPUT:
       elem - element to consider
       spec - spectrum on apStarWavegrid (nwave)
       specerr - error on the spectrum on apStarWavegrid (nwave)
       refspec= reference spectrum (assumed to be zero if absent)
    OUTPUT:
        equivalent-ish-width
    HISTORY:
       2015-02-11 - Written - Bovy (IAS@KITP)
    """
    if refspec is None:
        refspec= numpy.zeros_like(spec)
    # Read windows
    win= read(elem,apStarWavegrid=True)
    startindxs, endindxs= waveregions(elem,asIndex=True,pad=0)
    import apogee.spec.plot as splot
    lams= splot.apStarWavegrid()
    startlams= lams[startindxs]
    endlams= lams[endindxs]
    outval= 0.
    norm= 0.
    for (startindx,endindx,startlam,endlam) \
            in zip(startindxs,endindxs,startlams,endlams):
        norm+= numpy.sum(win[startindx:endindx]\
                                /specerr[startindx:endindx]**2.)
        if not numpy.all(refspec == 0.):
            outval+= (endlam-startlam)/(endindx-startindx)\
                *numpy.sum(win[startindx:endindx]/specerr[startindx:endindx]**2.\
                               *(1.-spec[startindx:endindx]/refspec[startindx:endindx]))
        else:
            outval+= (endlam-startlam)/(endindx-startindx)\
                *numpy.sum(win[startindx:endindx]/specerr[startindx:endindx]**2.\
                               *(1.-spec[startindx:endindx]))
    outval/= norm
    return outval
Example #7
0
def equishwidth(elem,spec,specerr,refspec=None):
    """
    NAME:
       equishwidth
    PURPOSE:
       return an equivalent-width-ish quantity for a given element:

       equishwidth = \sum_lam \Delta lam_center of window x (refspec-spec)/refspec x window/specerr^2 / \sum_lam window/specerr^2

       or if refspec == 0:

       equishwidth = \sum_lam \Delta lam_center of window x (1-spec) x window/specerr^2 / \sum_lam window/specerr^2


    INPUT:
       elem - element to consider
       spec - spectrum on apStarWavegrid (nwave)
       specerr - error on the spectrum on apStarWavegrid (nwave)
       refspec= reference spectrum (assumed to be zero if absent)
    OUTPUT:
        equivalent-ish-width
    HISTORY:
       2015-02-11 - Written - Bovy (IAS@KITP)
    """
    if refspec is None:
        refspec= numpy.zeros_like(spec)
    # Read windows
    win= read(elem,apStarWavegrid=True)
    startindxs, endindxs= waveregions(elem,asIndex=True,pad=0)
    import apogee.spec.plot as splot
    lams= splot.apStarWavegrid()
    startlams= lams[startindxs]
    endlams= lams[endindxs]
    outval= 0.
    norm= 0.
    for (startindx,endindx,startlam,endlam) \
            in zip(startindxs,endindxs,startlams,endlams):
        norm+= numpy.sum(win[startindx:endindx]\
                                /specerr[startindx:endindx]**2.)
        if not numpy.all(refspec == 0.):
            outval+= (endlam-startlam)/(endindx-startindx)\
                *numpy.sum(win[startindx:endindx]/specerr[startindx:endindx]**2.\
                               *(1.-spec[startindx:endindx]/refspec[startindx:endindx]))
        else:
            outval+= (endlam-startlam)/(endindx-startindx)\
                *numpy.sum(win[startindx:endindx]/specerr[startindx:endindx]**2.\
                               *(1.-spec[startindx:endindx]))
    outval/= norm
    return outval
Example #8
0
def measure_apogee(allStar, linelist_obj, output_fits=False, *args, **kwargs):
    if isinstance(linelist_obj, str):
        linelist_obj = linelist.Linelist(linelist_obj)
    loc_ids, apogee_ids = make_speclist(allStar)
    lams = splot.apStarWavegrid()
    ews = np.empty([np.shape(allStar)[0], np.shape(linelist_obj.labels)[0]])
    errs = np.empty([np.shape(allStar)[0], np.shape(linelist_obj.labels)[0]])
    try:
        dr = int(_DEFAULT_DR)
    except ValueError:
        dr = 16
    if dr <= 13:
        lockey = 'LOCATION_ID'
    else:
        lockey = 'FIELD'
    for i in tqdm(range(len(apogee_ids))):
        try:
            try:
                loc, id = str(allStar[lockey][i],
                              'utf-8'), str(allStar['APOGEE_ID'][i], 'utf-8')
            except (UnicodeDecodeError, AttributeError, TypeError):
                if isinstance(allStar[lockey][i], str):
                    loc, id = allStar[lockey][i], allStar['APOGEE_ID'][i]
                else:
                    raise IOError(
                        'Input allStar file seems to be incorrectly formatted, please check that the FIELD and APOGEE_ID fields are readable, and read in as either bytes or string'
                    )
            scope = allStar['TELESCOPE'][i]
            specs, hdr = apread.aspcapStar(loc, id, ext=1, telescope=scope)
            errspec, hdr = apread.aspcapStar(loc, id, ext=2, telescope=scope)
            spec = np.dstack([lams, specs, errspec])[0]
            out = equivalentwidths.measurelinelist(spec,
                                                   linelist_obj,
                                                   error=True,
                                                   *args,
                                                   **kwargs)
            ews[i], errs[i] = out[0], out[1]
        except IOError:
            print(
                'Spectrum missing from SAS? Check that your RESULTS_VERS is set correctly.'
            )
            ews[i], errs[i] = np.ones(np.shape(
                linelist_obj.labels)[0]) * np.nan, np.ones(
                    np.shape(linelist_obj.labels)[0]) * np.nan
    return ews, errs
Example #9
0
def lines(elem):
    """
    NAME:
       lines
    PURPOSE:
       return the location of peaks in the window weight ('lines') for a given element
    INPUT:
       elem - element
    OUTPUT:
       wavelengths of peaks in \AA
    HISTORY:
       2015-03-11 - Written - Bovy (IAS)
    """
    # Load the window
    win= read(elem,apStarWavegrid=True)
    import apogee.spec.plot as splot
    wavs= splot.apStarWavegrid()
    # Find peaks
    indx= (numpy.roll(win,1) < win)*(numpy.roll(win,-1) < win)\
        *(win > 0.1)
    return wavs[indx]
Example #10
0
def vmacro(x, vmacro=6., sparse=False, norm=True):
    """
    NAME:
       vmacro
    PURPOSE:
       compute the proper macroturbulence kernel
    INPUT:
       x - Array of X values for which to compute the macroturbulence kernel, in pixel offset relative to pixel centers; the kernel is calculated at the x offsets for each pixel center; x need to be 1/integer equally-spaced pixel offsets
       vmacro= (6.) macroturbulence in km/s (FWHM)
       sparse= (False) if True, return a sparse representation that can be passed to apogee.spec.lsf.convolve for easy convolution      
       norm= (True) if False, don't normalize to sum to 1 (useful to check whether the kernel actually integrates to 1)
    OUTPUT:
       LSF-like array of the macroturbulence
    HISTORY:
       2015-03-23 - Written - Bovy (IAS)
    """
    from apogee.spec.lsf import sparsify
    # Convert vmacro to Gaussian sigma / c
    sigvm = vmacro / 3. / 10.**5. / 2. / numpy.sqrt(2. * numpy.log(2.))
    # Are the x unit pixels or a fraction 1/hires thereof?
    hires = int(1. / (x[1] - x[0]))
    # Setup output
    wav = apStarWavegrid()
    l10wav = numpy.log10(wav)
    dowav = l10wav[1] - l10wav[0]
    # Hi-res wavelength for output
    hireswav = 10.**numpy.arange(l10wav[0], l10wav[-1] + dowav / hires,
                                 dowav / hires)
    # Calculate kernel
    lam = numpy.tile(hireswav, (len(x), 1)).T
    dlam= 10.**(numpy.tile(numpy.log10(hireswav),(len(x),1)).T\
                    +numpy.tile(x,(len(hireswav),1))*dowav)/lam-1.
    u = numpy.fabs(dlam / sigvm)
    out= 2./numpy.sqrt(numpy.pi)*u\
        *(numpy.exp(-u**2.)/u-numpy.sqrt(numpy.pi)*special.erfc(u))
    out[dlam == 0.] = 2. / numpy.sqrt(numpy.pi)
    out *= (1. + dlam) * numpy.log(10.) / sigvm
    if norm: out /= numpy.tile(numpy.sum(out, axis=1), (len(x), 1)).T
    if sparse: out = sparsify(out)
    return out
Example #11
0
def vmacro(x,vmacro=6.,sparse=False,norm=True):
    """
    NAME:
       vmacro
    PURPOSE:
       compute the proper macroturbulence kernel
    INPUT:
       x - Array of X values for which to compute the macroturbulence kernel, in pixel offset relative to pixel centers; the kernel is calculated at the x offsets for each pixel center; x need to be 1/integer equally-spaced pixel offsets
       vmacro= (6.) macroturbulence in km/s (FWHM)
       sparse= (False) if True, return a sparse representation that can be passed to apogee.spec.lsf.convolve for easy convolution      
       norm= (True) if False, don't normalize to sum to 1 (useful to check whether the kernel actually integrates to 1)
    OUTPUT:
       LSF-like array of the macroturbulence
    HISTORY:
       2015-03-23 - Written - Bovy (IAS)
    """
    from apogee.spec.lsf import sparsify
    # Convert vmacro to Gaussian sigma / c
    sigvm= vmacro/3./10.**5./2./numpy.sqrt(2.*numpy.log(2.))
    # Are the x unit pixels or a fraction 1/hires thereof?
    hires= int(1./(x[1]-x[0]))
    # Setup output
    wav= apStarWavegrid()
    l10wav= numpy.log10(wav)
    dowav= l10wav[1]-l10wav[0]
    # Hi-res wavelength for output
    hireswav= 10.**numpy.arange(l10wav[0],l10wav[-1]+dowav/hires,dowav/hires)
    # Calculate kernel
    lam= numpy.tile(hireswav,(len(x),1)).T
    dlam= 10.**(numpy.tile(numpy.log10(hireswav),(len(x),1)).T\
                    +numpy.tile(x,(len(hireswav),1))*dowav)/lam-1.
    u= numpy.fabs(dlam/sigvm)
    out= 2./numpy.sqrt(numpy.pi)*u\
        *(numpy.exp(-u**2.)/u-numpy.sqrt(numpy.pi)*special.erfc(u))
    out[dlam == 0.]= 2./numpy.sqrt(numpy.pi)
    out*= (1.+dlam)*numpy.log(10.)/sigvm
    if norm: out/= numpy.tile(numpy.sum(out,axis=1),(len(x),1)).T
    if sparse: out= sparsify(out)
    return out
def shiftFlux(spec, vel):
	'''
	Shifts flux provided.

	May want to allow user to pass restLambda in so we don't generate it again...
	:param spec: The spectrum to shift
	:param vel: The velocity to shift by
	:return: The shifted spectrum
	'''
	# Generate the wavelength grid
	restLambda = splot.apStarWavegrid()

	if (len(spec.shape) > 1):
		# Calculates wavelength grid for the second star with a doppler shift
		shiftLambda = [restLambda * (1. + v / (const.c / 1000.)) for v in vel]
		# The fluxes of both stars
		shiftedFlux = [np.interp(restLambda, shiftLambda[i], spec[i]) for i in range(len(shiftLambda))]
	else:
		# Calculates wavelength grid for the second star with a doppler shift
		shiftLambda = restLambda * (1. + vel / (const.c / 1000.))
		# The fluxes of both stars
		shiftedFlux = np.interp(restLambda, shiftLambda, spec)
	return shiftedFlux
Example #13
0
def windows(*args,**kwargs):
    """
    NAME:
       windows
    PURPOSE:
       Generate model APOGEE spectra using Turbospectrum in selected wavelength windows (but the whole APOGEE spectral range is returned): this is a general routine that generates the non-continuum-normalized spectrum, convolves with the LSF and macrotubulence, and optionally continuum normalizes the output; use 'turbosynth' for a direct interface to Turbospectrum
    INPUT ARGUMENTS:
       Windows specification: Provide one of
          (1) Element string: the APOGEE windows for this element will be loaded
          (2) startindxs, endindxs= start and end indexes of the windows on the apStar wavelength grid
          (3) startlams, endlams= start and end wavelengths in \AA
       lists with abundance differences wrt the atmosphere (they don't all have to have the same length, missing ones are filled in with zeros):
          [Atomic number1,diff1_1,diff1_2,diff1_3,...,diff1_N]
          [Atomic number2,diff2_1,diff2_2,diff2_3,...,diff2_N]
          ...
          [Atomic numberM,diffM_1,diffM_2,diffM_3,...,diffM_N]
    INPUT KEYWORDS:
       BASELINE: you can specify the baseline spectrum and the continuous opacity to not always re-compute it
          baseline= baseline c-normalized spectrum on Turbospectrum wavelength grid (obtained from turbosynth)
          mwav= Turbospectrum wavelength grid (obtained from turbosynth)
          cflux= continuum flux from Turbospectrum
          modelopac= (None) 
                     (a) if set to an existing filename: assume babsma_lu has already been run and use this continuous opacity in bsyn_lu
                     (b) if set to a non-existing filename: store the continuous opacity in this file
          Typically, you can obtain these three keywords by doing (kwargs are the keywords you provide to this function as well, and includes modelopac='SOME FILENAME')
          >>> baseline= turbosynth(**kwargs)
          >>> mwav= baseline[0]
          >>> cflux= baseline[2]/baseline[1]
          >>> baseline= baseline[1]
       LSF:
          lsf= ('all') LSF to convolve with; output of apogee.spec.lsf.eval; sparsify for efficiency; if 'all' or 'combo' a pre-computed version will be downloaded from the web
          Either:
             xlsf= (None) pixel offset grid on which the LSF is computed (see apogee.spec.lsf.eval); unnecessary if lsf=='all' or 'combo'
             dxlsf= (None) spacing of pixel offsets
          vmacro= (6.) macroturbulence to apply
       CONTINUUM:
          cont= ('aspcap') continuum-normalization to apply:
             None: no continuum normalization
             'true': Use the true continuum
             'aspcap': Use the continuum normalization method of ASPCAP DR12
             'cannon': Normalize using continuum pixels derived from the Cannon
       SYNTHESIS:
          air= (True) if True, perform the synthesis in air wavelengths (output is still in vacuum); set to False at your own risk, as Turbospectrum expects the linelist in air wavelengths!)
          Hlinelist= (None) Hydrogen linelists to use; can be set to the path of a linelist file or to the name of an APOGEE linelist; if None, then we first search for the Hlinedata.vac in the APOGEE linelist directory (if air=False) or we use the internal Turbospectrum Hlinelist (if air=True)
          linelist= (None) molecular and atomic linelists to use; can be set to the path of a linelist file or to the name of an APOGEE linelist, or lists of such files; if a single filename is given, the code will first search for files with extensions '.atoms', '.molec' or that start with 'turboatoms.' and 'turbomolec.'
          wmin, wmax, dw= (15000.000, 17000.000, 0.10000000, 7.0000000) spectral synthesis limits, step, and width of calculation (see MOOG)
          costheta= (1.) cosine of the viewing angle
       MODEL ATMOSPHERE PARAMETERS:
          Specify one of the following:
             (a) modelatm= (None) model-atmosphere instance
             (b) parameters of a KURUCZ model atmosphere:
                 (1) teff= (4500) Teff
                     logg= (2.5) logg
                     metals= (0.) metallicity
                     cm= (0.) carbon-enhancement
                     am= (0.) alpha-enhancement
                 (2) fparam= standard ASPCAP output format
                 lib= ('kurucz_filled') model atmosphere library
          vmicro= (2.) microturbulence (only used if the MOOG-formatted atmosphere is not found) (can also be part of fparam)
       MISCELLANEOUS:
          dr= return the path corresponding to this data release
          raw= (False) if True, return the raw turbosynth output
    OUTPUT:
       spectra (nspec,nwave)
       (wavelengths,cont-norm. spectrum, spectrum (nwave)) if raw == True
    HISTORY:
       2015-04-17 - Written - Bovy (IAS)
    """
    # Pop some kwargs
    baseline= kwargs.pop('baseline',None)
    mwav= kwargs.pop('mwav',None)
    cflux= kwargs.pop('cflux',None)
    raw= kwargs.pop('raw',False)
    # Check that we have the LSF and store the relevant keywords
    lsf= kwargs.pop('lsf','all')
    if isinstance(lsf,str):
        xlsf, lsf= aplsf._load_precomp(dr=kwargs.get('dr',None),fiber=lsf)
        dxlsf= None
    else:
        xlsf= kwargs.pop('xlsf',None)
        dxlsf= kwargs.pop('dxlsf',None)
        if xlsf is None and dxlsf is None: raise ValueError('xlsf= or dxlsf= input needs to be given if the LSF is given as an array')
    vmacro= kwargs.pop('vmacro',6.)
    # Parse continuum-normalization keywords
    cont= kwargs.pop('cont','aspcap')
    # Parse the wavelength regions
    apWave= apStarWavegrid()
    if isinstance(args[0],str): #element string given
        si,ei= apwindow.waveregions(args[0],pad=3,asIndex=True)
        args= args[1:]
    else:
        if isinstance(args[0][0],int): # assume index
            si,ei= args[0], args[1]
        else: # assume wavelengths in \AA
            sl,el= args[0], args[1]
            # Convert to index
            si, ei= [], []
            for s,e in zip(sl,el):
                # Find closest index into apWave
                si.append(numpy.argmin(numpy.fabs(s-apWave)))
                ei.append(numpy.argmin(numpy.fabs(e-apWave)))
        args= args[2:]
    # Setup the model atmosphere
    modelatm= kwargs.pop('modelatm',None)
    # Parse fparam, if present
    fparam= kwargs.pop('fparam',None)
    if not fparam is None:
        kwargs['teff']= fparam[0,paramIndx('TEFF')]
        kwargs['logg']= fparam[0,paramIndx('LOGG')]
        kwargs['metals']= fparam[0,paramIndx('METALS')]
        kwargs['am']= fparam[0,paramIndx('ALPHA')]
        kwargs['cm']= fparam[0,paramIndx('C')]
        kwargs['vmicro']= 10.**fparam[0,paramIndx('LOG10VDOP')]        
    # Need to pass a model atmosphere instance to turbosynth (needs to be made
    # more efficient, because now turbosynth always write the atmosphere
    if modelatm is None: # Setup a model atmosphere
        modelatm= atlas9.Atlas9Atmosphere(teff=kwargs.get('teff',4500.),
                                          logg=kwargs.get('logg',2.5),
                                          metals=kwargs.get('metals',0.),
                                          am=kwargs.get('am',0.),
                                          cm=kwargs.get('cm',0.),
                                          dr=kwargs.get('dr',None))
    if isinstance(modelatm,str) and os.path.exists(modelatm):
        raise ValueError('modelatm= input is an existing filename, but you need to give an Atmosphere object instead')
    elif isinstance(modelatm,str):
        raise ValueError('modelatm= input needs to be an Atmosphere instance')
    # Check temperature
    if modelatm._teff > 7000.:
        warnings.warn('Turbospectrum does not include all necessary physics to model stars hotter than about 7000 K; proceed with caution',RuntimeWarning)
    kwargs['modelatm']= modelatm
    try:
        rmModelopac= False
        if not 'modelopac' in kwargs:
            rmModelopac= True
            kwargs['modelopac']= tempfile.mktemp('mopac')
            # Make sure opacity is first calculated over the full wav. range
            kwargs['babsma_wmin']= 15000.
            kwargs['babsma_wmax']= 17000.
        elif 'modelopac' in kwargs and not isinstance(kwargs['modelopac'],str):
            raise ValueError('modelopac needs to be set to a filename')
        # Run synth for the whole wavelength range as a baseline
        if baseline is None or mwav is None or cflux is None:
            baseline= turbosynth(**kwargs)
            mwav= baseline[0]
            cflux= baseline[2]/baseline[1]
            baseline= baseline[1]
        elif isinstance(baseline,tuple): #probably accidentally gave the entire output of turbosynth
            mwav= baseline[0]
            cflux= baseline[2]/baseline[1]
            baseline= baseline[1]
        # Convert the apStarWavegrid windows to turboWavegrid regions
        sm,em= [], []
        for start,end in zip(si,ei):
            if kwargs.get('air',True):
                sm.append(numpy.argmin(numpy.fabs(vac2air(apWave[start])-mwav)))
                em.append(numpy.argmin(numpy.fabs(vac2air(apWave[end])-mwav)))
            else:
                sm.append(numpy.argmin(numpy.fabs(apWave[start]-mwav)))
                em.append(numpy.argmin(numpy.fabs(apWave[end]-mwav)))
        # Run Turbospectrum synth for all abundances and all windows
        if len(args) == 0: #special case that there are *no* differences
            args= ([26,0.],)
        nsynths= numpy.array([len(args[ii])-1 for ii in range(len(args))])
        nsynth= numpy.amax(nsynths) #Take the longest abundance list
        out= numpy.tile(baseline,(nsynth,1))
        # Run all windows
        for start, end in zip(sm,em):
            kwargs['wmin']= mwav[start]
            kwargs['wmax']= mwav[end]+0.001
            for ii in range(nsynth):
                newargs= ()
                for jj in range(len(args)):
                    tab= [args[jj][0]]
                    if len(args[jj]) > ii+1:
                        tab.append(args[jj][ii+1])
                        newargs= newargs+(tab,)
                tmpOut= turbosynth(*newargs,**kwargs)
                if numpy.isnan(tmpOut[1][-1]): 
                    # NaN returned for reasons that I don't understand
                    out[ii,start:end]= tmpOut[1][:-1]
                else:
                    out[ii,start:end+1]= tmpOut[1]
    except: raise
    finally:
        if rmModelopac and os.path.exists(kwargs['modelopac']):
            os.remove(kwargs['modelopac'])
            kwargs.pop('modelopac')
    # Now multiply each continuum-normalized spectrum with the continuum
    out*= numpy.tile(cflux,(nsynth,1))
    if raw: return (mwav,out/numpy.tile(cflux,(nsynth,1)),out)
    # If the synthesis was done in air, convert wavelength array
    if kwargs.get('air',True):
        mwav= numpy.array([air2vac(w) for w in list(mwav)])
    # Now convolve with the LSF
    out= aplsf.convolve(mwav,out,
                        lsf=lsf,xlsf=xlsf,dxlsf=dxlsf,vmacro=vmacro)
    # Now continuum-normalize
    if cont.lower() == 'true':
        # Get the true continuum on the apStar wavelength grid
        apWave= apStarWavegrid()
        baseline= numpy.polynomial.Polynomial.fit(mwav,cflux,4)
        ip= interpolate.InterpolatedUnivariateSpline(mwav,
                                                     cflux/baseline(mwav),
                                                     k=3)
        cflux= baseline(apWave)*ip(apWave)
        # Divide it out
        out/= numpy.tile(cflux,(nsynth,1))
    elif not cont is None:
        cflux= apcont.fit(out,numpy.ones_like(out),type=cont)
        out[cflux > 0.]/= cflux[cflux > 0.]
        out[cflux <= 0.]= numpy.nan
    return out
Example #14
0
File: lsf.py Project: mrawls/apogee
def convolve(wav,spec,
             lsf=None,xlsf=None,dxlsf=None,fiber='combo',
             vmacro=6.):
    """
    NAME:
       convolve
    PURPOSE:
       convolve with the APOGEE LSF and resample to APOGEE's apStar wavelength grid
    INPUT:
       wav - wavelength array (linear in wavelength in \AA)
       spec - spectrum on wav wavelength grid [nspec,nwave]
       lsf= (None) pre-calculated LSF array from apogee.spec.lsf.eval
       Either:
          xlsf= (None) 1/integer equally-spaced pixel offsets at which the lsf=lsf input is calculated
          dxlsf= (None) spacing of pixel offsets
       fiber= if lsf is None, the LSF is calculated for this fiber
       vmacro= (6.) Gaussian macroturbulence smoothing to apply as well (FWHM or a [sparse] matrix like lsf on the same x grid; can be computed with apogee.modelspec.vmacro)
    OUTPUT:
       spectrum on apStar wavelength grid
    HISTORY:
       2015-03-14 - Written - Bovy (IAS)
    """
    # Parse LSF input
    if lsf is None:
        xlsf= numpy.linspace(-7.,7.,43)
        lsf= eval(xlsf,fiber=fiber)
    if not isinstance(lsf,sparse.dia_matrix):
        lsf= sparsify(lsf)
    if dxlsf is None:
        dx= xlsf[1]-xlsf[0]
    else:
        dx= dxlsf
    hires= int(round(1./dx))
    l10wav= numpy.log10(apStarWavegrid())
    dowav= l10wav[1]-l10wav[0]
    tmpwav= 10.**numpy.arange(l10wav[0],l10wav[-1]+dowav/hires,dowav/hires)
    tmp= numpy.empty(len(l10wav)*hires)   
    # Setup vmacro
    if not vmacro is None and isinstance(vmacro,float):
        sigvm= vmacro/3./10.**5./numpy.log(10.)*hires/dowav\
            /2./numpy.sqrt(2.*numpy.log(2.))
    # Interpolate the input spectrum, starting from a polynomial baseline
    if len(spec.shape) == 1: spec= numpy.reshape(spec,(1,len(spec)))
    nspec= spec.shape[0]
    tmp= numpy.empty((nspec,len(tmpwav)))
    for ii in range(nspec):
        baseline= numpy.polynomial.Polynomial.fit(wav,spec[ii],4)
        ip= interpolate.InterpolatedUnivariateSpline(wav,
                                                     spec[ii]/baseline(wav),
                                                     k=3)
        tmp[ii]= baseline(tmpwav)*ip(tmpwav)
    # Add macroturbulence
    if not vmacro is None and isinstance(vmacro,float):
        tmp= ndimage.gaussian_filter1d(tmp,sigvm,mode='constant',axis=1)
    elif not vmacro is None: 
        # Use sparse representations to quickly calculate the convolution
        tmp= sparse.csr_matrix(tmp)
        if isinstance(vmacro,numpy.ndarray):
            vmacro= sparsify(vmacro)
        tmp= vmacro.dot(tmp.T).T
    if not isinstance(lsf,sparse.csr_matrix):
        # Use sparse representations to quickly calculate the convolution
        tmp= sparse.csr_matrix(tmp)
    return lsf.dot(tmp.T).T.toarray()[:,::hires]
Example #15
0
File: lsf.py Project: mrawls/apogee
def deconvolve(spec,specerr,
               lsf=None,eps=2500.,smooth=None):
    """
    NAME:
       deconvolve
    PURPOSE:
       deconvolve the LSF
    INPUT:
       spec - spectrum (nwave)
       specerr - spectrum uncertainty array (nwave)
       lsf= (None) LSF to deconvolve, needs to be specified in non-sparse format
       eps= (2500.) smoothness parameter
       smooth= (None) if set to a resolution, smooth with a FWHM resolution of 'smooth' and return the spectrum on the apStar wavelength grid
    OUTPUT:
       high-resolution deconvolved spectrum or smoothed deconvolved spectrum on apStar wavelength grid is smooth= is set
    HISTORY:
       2015-04-24 - Written - Bovy (IAS)
    """
    # Parse LSF input
    if lsf is None:
        raise ValueError("lsf= keyword with LSF in non-sparse format required for apogee.spec.lsf.deconvolve")
    if isinstance(lsf,sparse.dia_matrix):
        raise ValueError("lsf= keyword with LSF needs to be in non-sparse format")
    lsf[numpy.isnan(lsf)]= 0.
    # How much higher resolution is the LSF than the data?
    hires= int(round(lsf.shape[0]/8575.))
    # Setup output
    out= numpy.zeros(lsf.shape[0])
    # Loop through the detectors and analyze each one separately
    for sindx, eindx in zip([140,3450,6250],[3370,6200,8450]):
        # Get the LSF for this detector
        slsf= sparsify(lsf[hires*sindx:hires*eindx])
        # Parse the spectrum and its error for this detector, normalize
        tspec= numpy.ones((eindx-sindx)*hires)
        tinvspecerr= numpy.zeros((eindx-sindx)*hires)
        norm= numpy.nanmean(spec[sindx:eindx])
        tspec[::hires]= spec[sindx:eindx]/norm
        tinvspecerr[::hires]= norm/specerr[sindx:eindx]
        # Deal with NaNs
        tinvspecerr[numpy.isnan(tspec)]= 0.
        tspec[numpy.isnan(tspec)]= 1.
        # Set up the necessary sparse matrices
        Cinv= sparse.diags([tinvspecerr**2.],[0])
        CinvL= Cinv.dot(slsf)
        LTCinvL= (slsf.T).dot(CinvL)
        # P smoothness matrix
        diags1= -numpy.ones(slsf.shape[1])
        diags1[-1]= 0.
        diags2= numpy.ones(slsf.shape[1]-1)
        P= sparse.diags([diags1,diags2],[0,1])
        A= LTCinvL+eps*(P.T).dot(P)
        # b
        Cinvs= Cinv.dot(tspec)
        b= (slsf.T).dot(Cinvs)
        tmp= scipy.sparse.linalg.bicg(A,b)
        if tmp[1] == 0:
            tmp= tmp[0]
        else:
            raise RuntimeError("Deconvolution did not converge")
        out[sindx*hires:eindx*hires]= tmp*norm
    if not smooth is None:
        wav= apStarWavegrid()
        l10wav= numpy.log10(wav)
        dowav= l10wav[1]-l10wav[0]
        sigvm= hires/dowav/smooth/numpy.log(10.)\
            /2./numpy.sqrt(2.*numpy.log(2.))
        out= ndimage.gaussian_filter1d(out,sigvm,mode='constant')[::hires]
    return out
Example #16
0
def waveregions(elem,asIndex=False,pad=0,dr=None):
    """
    NAME:
       waveregions
    PURPOSE:
       return the wavelength regions corresponding to different elements
    INPUT:
       elem - element
       asIndx= (False) if yes, return the indices into an apStar-like wavelength grid rather than the wavelengths directly
       pad= (0) pad on each side by this many log10 wavelengths in 6e-6 (changes how windows are combined)
       dr= read the window corresponding to this data release       
    OUTPUT:
       (startlams,endlams) or (startindxs, endindxs)
    BUGS:
       range that comes out of asIndex=True is (or can be) different from that of asIndex=False
    HISTORY:
       2015-01-26 - Written - Bovy (IAS@KITP)
    """
    # Load the window
    win= read(elem,apStarWavegrid=True,dr=dr)
    # Calculate number of contiguous regions, assume this is not at the edge
    mask= ((win > 0.)*(True-numpy.isnan(win))).astype('int')
    dmaskp= numpy.roll(mask,-1)-mask
    dmaskn= numpy.roll(mask,1)-mask
    # Calculate the distance between adjacent windows and combine them if close
    import apogee.spec.plot as splot
    l10wavs= numpy.log10(splot.apStarWavegrid())
    indices= numpy.arange(len(l10wavs))
    if asIndex:
        startindxs= indices[dmaskp == 1.]
        endindxs= indices[dmaskn == 1.]
    startl10lams= l10wavs[dmaskp == 1.]
    endl10lams= l10wavs[dmaskn == 1.]
    if pad > 0:
        if asIndex:
            startindxs= [si-pad for si in startindxs]
            endindxs= [ei+pad for ei in endindxs]
        startl10lams-= pad*splot._DLOG10LAMBDA
        endl10lams+= pad*splot._DLOG10LAMBDA
    # Check that each window is at least _MINWIDTH wide
    width= 10.**endl10lams-10.**startl10lams
    for ii in range(len(startl10lams)):
        if width[ii] < _MINWIDTH:
            if asIndex: # Approximate
                dindx= int(numpy.ceil((_MINWIDTH-width[ii])/2.\
                                          /(10.**startl10lams[ii]\
                                                +10.**endl10lams[ii])/2.\
                                          /numpy.log(10.)/splot._DLOG10LAMBDA))
                startindxs[ii]-= dindx
                endindxs[ii]+= dindx                   
            startl10lams[ii]= numpy.log10(10.**startl10lams[ii]\
                                              -(_MINWIDTH-width[ii])/2.)
            endl10lams[ii]= numpy.log10(10.**endl10lams[ii]\
                                            +(_MINWIDTH-width[ii])/2.)
    diff= numpy.roll(startl10lams,-1)-endl10lams
    if asIndex:
        newStartindxs, newEndindxs= [startindxs[0]], [endindxs[0]]
    newStartl10lams, newEndl10lams= [startl10lams[0]], [endl10lams[0]]
    winIndx= 0
    for ii in range(len(startl10lams)-1):
        if diff[ii] < 10.*splot._DLOG10LAMBDA:
            if asIndex:
                newEndindxs[winIndx]= endindxs[ii+1]
            newEndl10lams[winIndx]= endl10lams[ii+1]
        else:
            if asIndex:
                newStartindxs.append(startindxs[ii+1])
                newEndindxs.append(endindxs[ii+1])
            newStartl10lams.append(startl10lams[ii+1])
            newEndl10lams.append(endl10lams[ii+1])
            winIndx+= 1
    if asIndex:
        return (newStartindxs,newEndindxs)
    else:
        return (10.**numpy.array(newStartl10lams),
                10.**numpy.array(newEndl10lams))
Example #17
0
def waveregions(elem,asIndex=False,pad=0,dr=None):
    """
    NAME:
       waveregions
    PURPOSE:
       return the wavelength regions corresponding to different elements
    INPUT:
       elem - element
       asIndx= (False) if yes, return the indices into an apStar-like wavelength grid rather than the wavelengths directly
       pad= (0) pad on each side by this many log10 wavelengths in 6e-6 (changes how windows are combined)
       dr= read the window corresponding to this data release       
    OUTPUT:
       (startlams,endlams) or (startindxs, endindxs)
    BUGS:
       range that comes out of asIndex=True is (or can be) different from that of asIndex=False
    HISTORY:
       2015-01-26 - Written - Bovy (IAS@KITP)
    """
    # Load the window
    win= read(elem,apStarWavegrid=True,dr=dr)
    # Calculate number of contiguous regions, assume this is not at the edge
    mask= ((win > 0.)*(True-numpy.isnan(win))).astype('int')
    dmaskp= numpy.roll(mask,-1)-mask
    dmaskn= numpy.roll(mask,1)-mask
    # Calculate the distance between adjacent windows and combine them if close
    import apogee.spec.plot as splot
    l10wavs= numpy.log10(splot.apStarWavegrid())
    indices= numpy.arange(len(l10wavs))
    if asIndex:
        startindxs= indices[dmaskp == 1.]
        endindxs= indices[dmaskn == 1.]
    startl10lams= l10wavs[dmaskp == 1.]
    endl10lams= l10wavs[dmaskn == 1.]
    if pad > 0:
        if asIndex:
            startindxs= [si-pad for si in startindxs]
            endindxs= [ei+pad for ei in endindxs]
        startl10lams-= pad*splot._DLOG10LAMBDA
        endl10lams+= pad*splot._DLOG10LAMBDA
    # Check that each window is at least _MINWIDTH wide
    width= 10.**endl10lams-10.**startl10lams
    for ii in range(len(startl10lams)):
        if width[ii] < _MINWIDTH:
            if asIndex: # Approximate
                dindx= int(numpy.ceil((_MINWIDTH-width[ii])/2.\
                                          /(10.**startl10lams[ii]\
                                                +10.**endl10lams[ii])/2.\
                                          /numpy.log(10.)/splot._DLOG10LAMBDA))
                startindxs[ii]-= dindx
                endindxs[ii]+= dindx                   
            startl10lams[ii]= numpy.log10(10.**startl10lams[ii]\
                                              -(_MINWIDTH-width[ii])/2.)
            endl10lams[ii]= numpy.log10(10.**endl10lams[ii]\
                                            +(_MINWIDTH-width[ii])/2.)
    diff= numpy.roll(startl10lams,-1)-endl10lams
    if asIndex:
        newStartindxs, newEndindxs= [startindxs[0]], [endindxs[0]]
    newStartl10lams, newEndl10lams= [startl10lams[0]], [endl10lams[0]]
    winIndx= 0
    for ii in range(len(startl10lams)-1):
        if diff[ii] < 10.*splot._DLOG10LAMBDA:
            if asIndex:
                newEndindxs[winIndx]= endindxs[ii+1]
            newEndl10lams[winIndx]= endl10lams[ii+1]
        else:
            if asIndex:
                newStartindxs.append(startindxs[ii+1])
                newEndindxs.append(endindxs[ii+1])
            newStartl10lams.append(startl10lams[ii+1])
            newEndl10lams.append(endl10lams[ii+1])
            winIndx+= 1
    if asIndex:
        return (newStartindxs,newEndindxs)
    else:
        return (10.**numpy.array(newStartl10lams),
                10.**numpy.array(newEndl10lams))
Example #18
0
def windows(*args,**kwargs):
    """
    NAME:
       windows
    PURPOSE:
       Generate model APOGEE spectra using MOOG in selected wavelength windows (but the whole APOGEE spectral range is returned): this is a general routine that generates the non-continuum-normalized spectrum, convolves with the LSF and macrotubulence, and optionally continuum normalizes the output; use 'moogsynth' for a direct interface to MOOG
    INPUT ARGUMENTS:
       Windows specification: Provide one of
          (1) Element string: the APOGEE windows for this element will be loaded
          (2) startindxs, endindxs= start and end indexes of the windows on the apStar wavelength grid
          (3) startlams, endlams= start and end wavelengths in \AA
       lists with abundance differences wrt the atmosphere (they don't all have to have the same length, missing ones are filled in with zeros):
          [Atomic number1,diff1_1,diff1_2,diff1_3,...,diff1_N]
          [Atomic number2,diff2_1,diff2_2,diff2_3,...,diff2_N]
          ...
          [Atomic numberM,diffM_1,diffM_2,diffM_3,...,diffM_N]
    INPUT KEYWORDS:
       BASELINE: you can specify the baseline spectrum to not always re-compute it
          baseline= baseline c-normalized spectrum on MOOG wavelength grid (obtained from moogsynth)
          mwav= MOOG wavelength grid (obtained from moogsynth)
          cflux= continuum flux from MOOG
          Typically, you can obtain these three keywords by doing (kwargs are the keywords you provide to this function as well)
          >>> baseline= moogsynth(**kwargs)[1]
          >>> mwav, cflux= moogsynth(doflux=True,**kwargs)
       LSF:
          lsf= ('all') LSF to convolve with; output of apogee.spec.lsf.eval; sparsify for efficiency; if 'all' or 'combo' a pre-computed version will be downloaded from the web
          Either:
             xlsf= (None) pixel offset grid on which the LSF is computed (see apogee.spec.lsf.eval); unnecessary if lsf=='all' or 'combo'
             dxlsf= (None) spacing of pixel offsets
          vmacro= (6.) macroturbulence to apply
       CONTINUUM:
          cont= ('aspcap') continuum-normalization to apply:
             None: no continuum normalization
             'true': Use the true continuum
             'aspcap': Use the continuum normalization method of ASPCAP DR12
             'cannon': Normalize using continuum pixels derived from the Cannon
       SYNTHESIS:
          linelist= (None) linelist to use; if this is None, the code looks for a weed-out version of the linelist appropriate for the given model atmosphere
          run_weedout= (False) if True, run MOOG weedout on the linelist first
          wmin, wmax, dw, width= (15000.000, 17000.000, 0.10000000, 7.0000000) spectral synthesis limits *for the whole spectrum* (not just the windows), step, and width of calculation (see MOOG)
       MODEL ATMOSPHERE PARAMETERS:
          Specify one of the following:
             (a) modelatm= (None) can be set to the filename of a model atmosphere or to a model-atmosphere instance (if filename, needs to end in .mod)
             (b) parameters of a KURUCZ model atmosphere:
                 (1) teff= (4500) Teff
                     logg= (2.5) logg
                     metals= (0.) metallicity
                     cm= (0.) carbon-enhancement
                     am= (0.) alpha-enhancement
                 (2) fparam= standard ASPCAP output format (
                 lib= ('kurucz_filled') model atmosphere library
                 dr= (None) use model atmospheres from this data release
          vmicro= (2.) microturbulence (only used if the MOOG-formatted atmosphere is not found) (can also be part of fparam)
       MISCELLANEOUS:
          dr= return the path corresponding to this data release
    OUTPUT:
       spectra (nspec,nwave)
    HISTORY:
       2015-03-18 - Written - Bovy (IAS)
    """
    # Pop some kwargs
    run_weedout= kwargs.pop('run_weedout',False)
    baseline= kwargs.pop('baseline',None)
    mwav= kwargs.pop('mwav',None)
    cflux= kwargs.pop('cflux',None)
    # Check that we have the LSF and store the relevant keywords
    lsf= kwargs.pop('lsf','all')
    if isinstance(lsf,str):
        xlsf, lsf= aplsf._load_precomp(dr=kwargs.get('dr',None),fiber=lsf)
        dxlsf= None
    else:
        xlsf= kwargs.pop('xlsf',None)
        dxlsf= kwargs.pop('dxlsf',None)
        if xlsf is None and dxlsf is None: raise ValueError('xlsf= or dxlsf= input needs to be given if the LSF is given as an array')
    vmacro= kwargs.pop('vmacro',6.)
    # Parse continuum-normalization keywords
    cont= kwargs.pop('cont','aspcap')
    # Parse the wavelength regions
    apWave= apStarWavegrid()
    if isinstance(args[0],str): #element string given
        si,ei= apwindow.waveregions(args[0],pad=3,asIndex=True)
        args= args[1:]
    else:
        if isinstance(args[0][0],int): # assume index
            si,ei= args[0], args[1]
        else: # assume wavelengths in \AA
            sl,el= args[0], args[1]
            # Convert to index
            si, ei= [], []
            for s,e in zip(sl,el):
                # Find closest index into apWave
                si.append(numpy.argmin(numpy.fabs(s-apWave)))
                ei.append(numpy.argmin(numpy.fabs(e-apWave)))
        args= args[2:]
    # Setup the model atmosphere
    modelatm= kwargs.pop('modelatm',None)
    tmpModelAtmDir= False
    # Parse fparam, if present
    fparam= kwargs.pop('fparam',None)
    if not fparam is None:
        kwargs['teff']= fparam[0,paramIndx('TEFF')]
        kwargs['logg']= fparam[0,paramIndx('LOGG')]
        kwargs['metals']= fparam[0,paramIndx('METALS')]
        kwargs['am']= fparam[0,paramIndx('ALPHA')]
        kwargs['cm']= fparam[0,paramIndx('C')]
        kwargs['vm']= 10.**fparam[0,paramIndx('LOG10VDOP')]        
    if modelatm is None: # Setup a model atmosphere
        modelatm= atlas9.Atlas9Atmosphere(teff=kwargs.get('teff',4500.),
                                          logg=kwargs.get('logg',2.5),
                                          metals=kwargs.get('metals',0.),
                                          am=kwargs.get('am',0.),
                                          cm=kwargs.get('cm',0.),
                                          dr=kwargs.get('dr',None))
    if isinstance(modelatm,str) and os.path.exists(modelatm):
        modelfilename= modelatm
    elif isinstance(modelatm,str):
        raise ValueError('modelatm= input is a non-existing filename')
    else: # model atmosphere instance
        # Need to write this instance to a file; we will run in a temp 
        # subdirectory of the current directory
        tmpDir= tempfile.mkdtemp(dir=os.getcwd())
        tmpModelAtmDir= True # need to remove this later
        modelfilename= os.path.join(tmpDir,'modelatm.mod')
        modelatm.writeto(modelfilename)
    kwargs['modelatm']= modelfilename
    try:
        # Check whether a MOOG version of the model atmosphere exists
        if not os.path.exists(modelfilename.replace('.mod','.org')):
            # Convert to MOOG format
            convert_modelAtmosphere(**kwargs)
        # Run weedout on the linelist first if requested
        if run_weedout:
            linelistfilename= modelfilename.replace('.mod','.lines')
            if not os.path.exists(linelistfilename):
                weedout(**kwargs)
            kwargs['linelist']= linelistfilename
        # Run MOOG synth for the whole wavelength range as a baseline, also contin
        if baseline is None:
            baseline= moogsynth(**kwargs)[1] 
        elif isinstance(baseline,tuple): #probably accidentally gave wav as well
            baseline= baseline[1]
        if mwav is None or cflux is None:
            mwav, cflux= moogsynth(doflux=True,**kwargs)
        # Convert the apStarWavegrid windows to moogWavegrid regions
        sm,em= [], []
        for start,end in zip(si,ei):
            sm.append(numpy.argmin(numpy.fabs(apWave[start]-mwav)))
            em.append(numpy.argmin(numpy.fabs(apWave[end]-mwav)))
        # Run MOOG synth for all abundances and all windows
        if len(args) == 0: #special case that there are *no* differences
            args= ([26,0.],)
        nsynths= numpy.array([len(args[ii])-1 for ii in range(len(args))])
        nsynth= numpy.amax(nsynths) #Take the longest abundance list
        out= numpy.tile(baseline,(nsynth,1))
        # Run all windows
        for start, end in zip(sm,em):
            kwargs['wmin']= mwav[start]
            kwargs['wmax']= mwav[end]
            # Check whether the number of syntheses is > 5 and run multiple 
            # MOOG instances if necessary, bc MOOG only does 5 at a time
            ninstances= int(numpy.ceil(nsynth/5.))
            for ii in range(ninstances):
                newargs= ()
                for jj in range(len(args)):
                    tab= [args[jj][0]]
                    if len(args[jj][5*ii+1:5*(ii+1)+1]) > 0:
                        tab.extend(args[jj][5*ii+1:5*(ii+1)+1])
                        newargs= newargs+(tab,)
                out[5*ii:5*(ii+1),start:end+1]= moogsynth(*newargs,**kwargs)[1] 
    except: raise
    finally:
        if tmpModelAtmDir: # need to remove this temporary directory
            os.remove(modelfilename)
        moogmodelfilename= modelfilename.replace('.mod','.org')
        if os.path.exists(moogmodelfilename):
            os.remove(moogmodelfilename)
        if run_weedout:
            os.remove(modelfilename.replace('.mod','.lines'))
        os.rmdir(tmpDir)
    # Now multiply each continuum-normalized spectrum with the continuum
    out*= numpy.tile(cflux,(nsynth,1))
    # Now convolve with the LSF
    out= aplsf.convolve(mwav,out,
                        lsf=lsf,xlsf=xlsf,dxlsf=dxlsf,vmacro=vmacro)
    # Now continuum-normalize
    if cont.lower() == 'true':
        # Get the true continuum on the apStar wavelength grid
        apWave= apStarWavegrid()
        baseline= numpy.polynomial.Polynomial.fit(mwav,cflux,4)
        ip= interpolate.InterpolatedUnivariateSpline(mwav,
                                                     cflux/baseline(mwav),
                                                     k=3)
        cflux= baseline(apWave)*ip(apWave)
        # Divide it out
        out/= numpy.tile(cflux,(nsynth,1))
    elif not cont is None:
        cflux= apcont.fit(out,numpy.ones_like(out),type=cont)
        out[cflux > 0.]/= cflux[cflux > 0.]
        out[cflux <= 0.]= numpy.nan
    return out
Example #19
0
def synth(*args,**kwargs):
    """
    NAME:
       synth
    PURPOSE:
       Generate model APOGEE spectra using MOOG: this is a general routine that generates the non-continuum-normalized spectrum, convolves with the LSF and macrotubulence, and optionally continuum normalizes the output; use 'moogsynth' for a direct interface to MOOG
    INPUT ARGUMENTS:
       lists with abundances wrt the atmosphere (they don't all have to have the same length, missing ones are filled in with zeros):
          [Atomic number1,diff1_1,diff1_2,diff1_3,...,diff1_N]
          [Atomic number2,diff2_1,diff2_2,diff2_3,...,diff2_N]
          ...
          [Atomic numberM,diffM_1,diffM_2,diffM_3,...,diffM_N]
    INPUT KEYWORDS:
       LSF:
          lsf= ('all') LSF to convolve with; output of apogee.spec.lsf.eval; sparsify for efficiency; if 'all' or 'combo' a pre-computed version will be downloaded from the web
          Either:
             xlsf= (None) pixel offset grid on which the LSF is computed (see apogee.spec.lsf.eval); unnecessary if lsf=='all' or 'combo'
             dxlsf= (None) spacing of pixel offsets
          vmacro= (6.) macroturbulence to apply
       CONTINUUM:
          cont= ('aspcap') continuum-normalization to apply:
             None: no continuum normalization
             'true': Use the true continuum
             'aspcap': Use the continuum normalization method of ASPCAP DR12
             'cannon': Normalize using continuum pixels derived from the Cannon
       SYNTHESIS:
          linelist= (None) linelist to use; can be set to the path of a linelist file or to the name of an APOGEE linelist
          run_weedout= (False) if True, run MOOG weedout on the linelist first
          wmin, wmax, dw, width= (15000.000, 17000.000, 0.10000000, 7.0000000) spectral synthesis limits, step, and width of calculation (see MOOG)
          lib= ('kurucz_filled') spectral library
       MODEL ATMOSPHERE PARAMETERS:
          Specify one of the following:
             (a) modelatm= (None) can be set to the filename of a model atmosphere or to a model-atmosphere instance (if filename, needs to end in .mod)
             (b) parameters of a KURUCZ model atmosphere:
                 (1) teff= (4500) Teff
                     logg= (2.5) logg
                     metals= (0.) metallicity
                     cm= (0.) carbon-enhancement
                     am= (0.) alpha-enhancement
                 (2) fparam= standard ASPCAP output format
                 lib= ('kurucz_filled') model atmosphere library
                 dr= (None) use model atmospheres from this data release
          vmicro= (2.) microturbulence (only used if the MOOG-formatted atmosphere is not found) (can also be part of fparam)
       MISCELLANEOUS:
          dr= return the path corresponding to this data release
    OUTPUT:
       spectra (nspec,nwave)
    HISTORY:
       2015-03-15 - Written - Bovy (IAS)
    """
    run_weedout= kwargs.pop('run_weedout',False)
    # Check that we have the LSF and store the relevant keywords
    lsf= kwargs.pop('lsf','all')
    if isinstance(lsf,str):
        xlsf, lsf= aplsf._load_precomp(dr=kwargs.get('dr',None),fiber=lsf)
        dxlsf= None
    else:
        xlsf= kwargs.pop('xlsf',None)
        dxlsf= kwargs.pop('dxlsf',None)
        if xlsf is None and dxlsf is None: raise ValueError('xlsf= or dxlsf= input needs to be given if the LSF is given as an array')
    vmacro= kwargs.pop('vmacro',6.)
    # Parse continuum-normalization keywords
    cont= kwargs.pop('cont','aspcap')
    # Setup the model atmosphere
    modelatm= kwargs.pop('modelatm',None)
    tmpModelAtmDir= False
    # Parse fparam, if present
    fparam= kwargs.pop('fparam',None)
    if not fparam is None:
        kwargs['teff']= fparam[paramIndx('TEFF')]
        kwargs['logg']= fparam[paramIndx('LOGG')]
        kwargs['metals']= fparam[paramIndx('METALS')]
        kwargs['am']= fparam[paramIndx('ALPHA')]
        kwargs['cm']= fparam[paramIndx('C')]
        kwargs['vm']= 10.**fparam[paramIndx('LOG10VDOP')]        
    if modelatm is None: # Setup a model atmosphere
        modelatm= atlas9.Atlas9Atmosphere(teff=kwargs.get('teff',4500.),
                                          logg=kwargs.get('logg',2.5),
                                          metals=kwargs.get('metals',0.),
                                          am=kwargs.get('am',0.),
                                          cm=kwargs.get('cm',0.),
                                          dr=kwargs.get('dr',None))
    if isinstance(modelatm,str) and os.path.exists(modelatm):
        modelfilename= modelatm
    elif isinstance(modelatm,str):
        raise ValueError('modelatm= input is a non-existing filename')
    else: # model atmosphere instance
        # Need to write this instance to a file; we will run in a temp 
        # subdirectory of the current directory
        tmpDir= tempfile.mkdtemp(dir=os.getcwd())
        tmpModelAtmDir= True # need to remove this later
        modelfilename= os.path.join(tmpDir,'modelatm.mod')
        modelatm.writeto(modelfilename)
    kwargs['modelatm']= modelfilename
    try:
        # Check whether a MOOG version of the model atmosphere exists
        if not os.path.exists(modelfilename.replace('.mod','.org')):
            # Convert to MOOG format
            convert_modelAtmosphere(**kwargs)
        # Run weedout on the linelist first if requested
        if run_weedout:
            linelistfilename= modelfilename.replace('.mod','.lines')
            if not os.path.exists(linelistfilename):
                weedout(**kwargs)
            kwargs['linelist']= linelistfilename
        # Run MOOG synth for all abundances
        if len(args) == 0: #special case that there are *no* differences
            args= ([26,0.],)
        nsynths= numpy.array([len(args[ii])-1 for ii in range(len(args))])
        nsynth= numpy.amax(nsynths) #Take the longest abundance list
        nmoogwav= int((kwargs.get('wmax',_WMAX_DEFAULT)\
                           -kwargs.get('wmin',_WMIN_DEFAULT))\
                          /kwargs.get('dw',_DW_DEFAULT)+1)
        out= numpy.empty((nsynth,nmoogwav))
        # Check whether the number of syntheses is > 5 and run multiple 
        # MOOG instances if necessary, bc MOOG only does 5 at a time
        ninstances= int(numpy.ceil(nsynth/5.))
        for ii in range(ninstances):
            newargs= ()
            for jj in range(len(args)):
                tab= [args[jj][0]]
                if len(args[jj][5*ii+1:5*(ii+1)+1]) > 0:
                    tab.extend(args[jj][5*ii+1:5*(ii+1)+1])
                    newargs= newargs+(tab,)
            out[5*ii:5*(ii+1)]= moogsynth(*newargs,**kwargs)[1] 
            # We'll grab the wavelength grid from the continuum below
        # Now compute the continuum and multiply each c-norm spectrum with it
        mwav, cflux= moogsynth(doflux=True,**kwargs)
    except: raise
    finally:
        if tmpModelAtmDir: # need to remove this temporary directory
            os.remove(modelfilename)
        moogmodelfilename= modelfilename.replace('.mod','.org')
        if os.path.exists(moogmodelfilename):
            os.remove(moogmodelfilename)
        if run_weedout:
            os.remove(modelfilename.replace('.mod','.lines'))
        os.rmdir(tmpDir)
    out*= numpy.tile(cflux,(nsynth,1))
    # Now convolve with the LSF
    out= aplsf.convolve(mwav,out,
                        lsf=lsf,xlsf=xlsf,dxlsf=dxlsf,vmacro=vmacro)
    # Now continuum-normalize
    if cont.lower() == 'true':
        # Get the true continuum on the apStar wavelength grid
        apWave= apStarWavegrid()
        baseline= numpy.polynomial.Polynomial.fit(mwav,cflux,4)
        ip= interpolate.InterpolatedUnivariateSpline(mwav,
                                                     cflux/baseline(mwav),
                                                     k=3)
        cflux= baseline(apWave)*ip(apWave)
        # Divide it out
        out/= numpy.tile(cflux,(nsynth,1))
    elif not cont is None:
        cflux= apcont.fit(out,numpy.ones_like(out),type=cont)
        out[cflux > 0.]/= cflux[cflux > 0.]
        out[cflux <= 0.]= numpy.nan
    return out
Example #20
0
def synth(*args, **kwargs):
    """
    NAME:
       synth
    PURPOSE:
       Generate model APOGEE spectra using MOOG: this is a general routine that generates the non-continuum-normalized spectrum, convolves with the LSF and macrotubulence, and optionally continuum normalizes the output; use 'moogsynth' for a direct interface to MOOG
    INPUT ARGUMENTS:
       lists with abundances wrt the atmosphere (they don't all have to have the same length, missing ones are filled in with zeros):
          [Atomic number1,diff1_1,diff1_2,diff1_3,...,diff1_N]
          [Atomic number2,diff2_1,diff2_2,diff2_3,...,diff2_N]
          ...
          [Atomic numberM,diffM_1,diffM_2,diffM_3,...,diffM_N]
    INPUT KEYWORDS:
       LSF:
          lsf= ('all') LSF to convolve with; output of apogee.spec.lsf.eval; sparsify for efficiency; if 'all' or 'combo' a pre-computed version will be downloaded from the web
          Either:
             xlsf= (None) pixel offset grid on which the LSF is computed (see apogee.spec.lsf.eval); unnecessary if lsf=='all' or 'combo'
             dxlsf= (None) spacing of pixel offsets
          vmacro= (6.) macroturbulence to apply
       CONTINUUM:
          cont= ('aspcap') continuum-normalization to apply:
             None: no continuum normalization
             'true': Use the true continuum
             'aspcap': Use the continuum normalization method of ASPCAP DR12
             'cannon': Normalize using continuum pixels derived from the Cannon
       SYNTHESIS:
          linelist= (None) linelist to use; can be set to the path of a linelist file or to the name of an APOGEE linelist
          run_weedout= (False) if True, run MOOG weedout on the linelist first
          wmin, wmax, dw, width= (15000.000, 17000.000, 0.10000000, 7.0000000) spectral synthesis limits, step, and width of calculation (see MOOG)
          lib= ('kurucz_filled') spectral library
       MODEL ATMOSPHERE PARAMETERS:
          Specify one of the following:
             (a) modelatm= (None) can be set to the filename of a model atmosphere or to a model-atmosphere instance (if filename, needs to end in .mod)
             (b) parameters of a KURUCZ model atmosphere:
                 (1) teff= (4500) Teff
                     logg= (2.5) logg
                     metals= (0.) metallicity
                     cm= (0.) carbon-enhancement
                     am= (0.) alpha-enhancement
                 (2) fparam= standard ASPCAP output format
                 lib= ('kurucz_filled') model atmosphere library
                 dr= (None) use model atmospheres from this data release
          vmicro= (2.) microturbulence (only used if the MOOG-formatted atmosphere is not found) (can also be part of fparam)
       MISCELLANEOUS:
          dr= return the path corresponding to this data release
    OUTPUT:
       spectra (nspec,nwave)
    HISTORY:
       2015-03-15 - Written - Bovy (IAS)
    """
    run_weedout = kwargs.pop('run_weedout', False)
    # Check that we have the LSF and store the relevant keywords
    lsf = kwargs.pop('lsf', 'all')
    if isinstance(lsf, str):
        xlsf, lsf = aplsf._load_precomp(dr=kwargs.get('dr', None), fiber=lsf)
        dxlsf = None
    else:
        xlsf = kwargs.pop('xlsf', None)
        dxlsf = kwargs.pop('dxlsf', None)
        if xlsf is None and dxlsf is None:
            raise ValueError(
                'xlsf= or dxlsf= input needs to be given if the LSF is given as an array'
            )
    vmacro = kwargs.pop('vmacro', 6.)
    # Parse continuum-normalization keywords
    cont = kwargs.pop('cont', 'aspcap')
    # Setup the model atmosphere
    modelatm = kwargs.pop('modelatm', None)
    tmpModelAtmDir = False
    # Parse fparam, if present
    fparam = kwargs.pop('fparam', None)
    if not fparam is None:
        kwargs['teff'] = fparam[paramIndx('TEFF')]
        kwargs['logg'] = fparam[paramIndx('LOGG')]
        kwargs['metals'] = fparam[paramIndx('METALS')]
        kwargs['am'] = fparam[paramIndx('ALPHA')]
        kwargs['cm'] = fparam[paramIndx('C')]
        kwargs['vm'] = 10.**fparam[paramIndx('LOG10VDOP')]
    if modelatm is None:  # Setup a model atmosphere
        modelatm = atlas9.Atlas9Atmosphere(teff=kwargs.get('teff', 4500.),
                                           logg=kwargs.get('logg', 2.5),
                                           metals=kwargs.get('metals', 0.),
                                           am=kwargs.get('am', 0.),
                                           cm=kwargs.get('cm', 0.),
                                           dr=kwargs.get('dr', None))
    if isinstance(modelatm, str) and os.path.exists(modelatm):
        modelfilename = modelatm
    elif isinstance(modelatm, str):
        raise ValueError('modelatm= input is a non-existing filename')
    else:  # model atmosphere instance
        # Need to write this instance to a file; we will run in a temp
        # subdirectory of the current directory
        tmpDir = tempfile.mkdtemp(dir=os.getcwd())
        tmpModelAtmDir = True  # need to remove this later
        modelfilename = os.path.join(tmpDir, 'modelatm.mod')
        modelatm.writeto(modelfilename)
    kwargs['modelatm'] = modelfilename
    try:
        # Check whether a MOOG version of the model atmosphere exists
        if not os.path.exists(modelfilename.replace('.mod', '.org')):
            # Convert to MOOG format
            convert_modelAtmosphere(**kwargs)
        # Run weedout on the linelist first if requested
        if run_weedout:
            linelistfilename = modelfilename.replace('.mod', '.lines')
            if not os.path.exists(linelistfilename):
                weedout(**kwargs)
            kwargs['linelist'] = linelistfilename
        # Run MOOG synth for all abundances
        if len(args) == 0:  #special case that there are *no* differences
            args = ([26, 0.], )
        nsynths = numpy.array([len(args[ii]) - 1 for ii in range(len(args))])
        nsynth = numpy.amax(nsynths)  #Take the longest abundance list
        nmoogwav= int((kwargs.get('wmax',_WMAX_DEFAULT)\
                           -kwargs.get('wmin',_WMIN_DEFAULT))\
                          /kwargs.get('dw',_DW_DEFAULT)+1)
        out = numpy.empty((nsynth, nmoogwav))
        # Check whether the number of syntheses is > 5 and run multiple
        # MOOG instances if necessary, bc MOOG only does 5 at a time
        ninstances = int(numpy.ceil(nsynth / 5.))
        for ii in range(ninstances):
            newargs = ()
            for jj in range(len(args)):
                tab = [args[jj][0]]
                if len(args[jj][5 * ii + 1:5 * (ii + 1) + 1]) > 0:
                    tab.extend(args[jj][5 * ii + 1:5 * (ii + 1) + 1])
                    newargs = newargs + (tab, )
            out[5 * ii:5 * (ii + 1)] = moogsynth(*newargs, **kwargs)[1]
            # We'll grab the wavelength grid from the continuum below
        # Now compute the continuum and multiply each c-norm spectrum with it
        mwav, cflux = moogsynth(doflux=True, **kwargs)
    except:
        raise
    finally:
        if tmpModelAtmDir:  # need to remove this temporary directory
            os.remove(modelfilename)
        moogmodelfilename = modelfilename.replace('.mod', '.org')
        if os.path.exists(moogmodelfilename):
            os.remove(moogmodelfilename)
        if run_weedout:
            os.remove(modelfilename.replace('.mod', '.lines'))
        os.rmdir(tmpDir)
    out *= numpy.tile(cflux, (nsynth, 1))
    # Now convolve with the LSF
    out = aplsf.convolve(mwav,
                         out,
                         lsf=lsf,
                         xlsf=xlsf,
                         dxlsf=dxlsf,
                         vmacro=vmacro)
    # Now continuum-normalize
    if cont.lower() == 'true':
        # Get the true continuum on the apStar wavelength grid
        apWave = apStarWavegrid()
        baseline = numpy.polynomial.Polynomial.fit(mwav, cflux, 4)
        ip = interpolate.InterpolatedUnivariateSpline(mwav,
                                                      cflux / baseline(mwav),
                                                      k=3)
        cflux = baseline(apWave) * ip(apWave)
        # Divide it out
        out /= numpy.tile(cflux, (nsynth, 1))
    elif not cont is None:
        cflux = apcont.fit(out, numpy.ones_like(out), type=cont)
        out[cflux > 0.] /= cflux[cflux > 0.]
        out[cflux <= 0.] = numpy.nan
    return out
Example #21
0
def windows(*args, **kwargs):
    """
    NAME:
       windows
    PURPOSE:
       Generate model APOGEE spectra using MOOG in selected wavelength windows (but the whole APOGEE spectral range is returned): this is a general routine that generates the non-continuum-normalized spectrum, convolves with the LSF and macrotubulence, and optionally continuum normalizes the output; use 'moogsynth' for a direct interface to MOOG
    INPUT ARGUMENTS:
       Windows specification: Provide one of
          (1) Element string: the APOGEE windows for this element will be loaded
          (2) startindxs, endindxs= start and end indexes of the windows on the apStar wavelength grid
          (3) startlams, endlams= start and end wavelengths in \AA
       lists with abundance differences wrt the atmosphere (they don't all have to have the same length, missing ones are filled in with zeros):
          [Atomic number1,diff1_1,diff1_2,diff1_3,...,diff1_N]
          [Atomic number2,diff2_1,diff2_2,diff2_3,...,diff2_N]
          ...
          [Atomic numberM,diffM_1,diffM_2,diffM_3,...,diffM_N]
    INPUT KEYWORDS:
       BASELINE: you can specify the baseline spectrum to not always re-compute it
          baseline= baseline c-normalized spectrum on MOOG wavelength grid (obtained from moogsynth)
          mwav= MOOG wavelength grid (obtained from moogsynth)
          cflux= continuum flux from MOOG
          Typically, you can obtain these three keywords by doing (kwargs are the keywords you provide to this function as well)
          >>> baseline= moogsynth(**kwargs)[1]
          >>> mwav, cflux= moogsynth(doflux=True,**kwargs)
       LSF:
          lsf= ('all') LSF to convolve with; output of apogee.spec.lsf.eval; sparsify for efficiency; if 'all' or 'combo' a pre-computed version will be downloaded from the web
          Either:
             xlsf= (None) pixel offset grid on which the LSF is computed (see apogee.spec.lsf.eval); unnecessary if lsf=='all' or 'combo'
             dxlsf= (None) spacing of pixel offsets
          vmacro= (6.) macroturbulence to apply
       CONTINUUM:
          cont= ('aspcap') continuum-normalization to apply:
             None: no continuum normalization
             'true': Use the true continuum
             'aspcap': Use the continuum normalization method of ASPCAP DR12
             'cannon': Normalize using continuum pixels derived from the Cannon
       SYNTHESIS:
          linelist= (None) linelist to use; if this is None, the code looks for a weed-out version of the linelist appropriate for the given model atmosphere
          run_weedout= (False) if True, run MOOG weedout on the linelist first
          wmin, wmax, dw, width= (15000.000, 17000.000, 0.10000000, 7.0000000) spectral synthesis limits *for the whole spectrum* (not just the windows), step, and width of calculation (see MOOG)
       MODEL ATMOSPHERE PARAMETERS:
          Specify one of the following:
             (a) modelatm= (None) can be set to the filename of a model atmosphere or to a model-atmosphere instance (if filename, needs to end in .mod)
             (b) parameters of a KURUCZ model atmosphere:
                 (1) teff= (4500) Teff
                     logg= (2.5) logg
                     metals= (0.) metallicity
                     cm= (0.) carbon-enhancement
                     am= (0.) alpha-enhancement
                 (2) fparam= standard ASPCAP output format (
                 lib= ('kurucz_filled') model atmosphere library
                 dr= (None) use model atmospheres from this data release
          vmicro= (2.) microturbulence (only used if the MOOG-formatted atmosphere is not found) (can also be part of fparam)
       MISCELLANEOUS:
          dr= return the path corresponding to this data release
    OUTPUT:
       spectra (nspec,nwave)
    HISTORY:
       2015-03-18 - Written - Bovy (IAS)
    """
    # Pop some kwargs
    run_weedout = kwargs.pop('run_weedout', False)
    baseline = kwargs.pop('baseline', None)
    mwav = kwargs.pop('mwav', None)
    cflux = kwargs.pop('cflux', None)
    # Check that we have the LSF and store the relevant keywords
    lsf = kwargs.pop('lsf', 'all')
    if isinstance(lsf, str):
        xlsf, lsf = aplsf._load_precomp(dr=kwargs.get('dr', None), fiber=lsf)
        dxlsf = None
    else:
        xlsf = kwargs.pop('xlsf', None)
        dxlsf = kwargs.pop('dxlsf', None)
        if xlsf is None and dxlsf is None:
            raise ValueError(
                'xlsf= or dxlsf= input needs to be given if the LSF is given as an array'
            )
    vmacro = kwargs.pop('vmacro', 6.)
    # Parse continuum-normalization keywords
    cont = kwargs.pop('cont', 'aspcap')
    # Parse the wavelength regions
    apWave = apStarWavegrid()
    if isinstance(args[0], str):  #element string given
        si, ei = apwindow.waveregions(args[0], pad=3, asIndex=True)
        args = args[1:]
    else:
        if isinstance(args[0][0], int):  # assume index
            si, ei = args[0], args[1]
        else:  # assume wavelengths in \AA
            sl, el = args[0], args[1]
            # Convert to index
            si, ei = [], []
            for s, e in zip(sl, el):
                # Find closest index into apWave
                si.append(numpy.argmin(numpy.fabs(s - apWave)))
                ei.append(numpy.argmin(numpy.fabs(e - apWave)))
        args = args[2:]
    # Setup the model atmosphere
    modelatm = kwargs.pop('modelatm', None)
    tmpModelAtmDir = False
    # Parse fparam, if present
    fparam = kwargs.pop('fparam', None)
    if not fparam is None:
        kwargs['teff'] = fparam[0, paramIndx('TEFF')]
        kwargs['logg'] = fparam[0, paramIndx('LOGG')]
        kwargs['metals'] = fparam[0, paramIndx('METALS')]
        kwargs['am'] = fparam[0, paramIndx('ALPHA')]
        kwargs['cm'] = fparam[0, paramIndx('C')]
        kwargs['vm'] = 10.**fparam[0, paramIndx('LOG10VDOP')]
    if modelatm is None:  # Setup a model atmosphere
        modelatm = atlas9.Atlas9Atmosphere(teff=kwargs.get('teff', 4500.),
                                           logg=kwargs.get('logg', 2.5),
                                           metals=kwargs.get('metals', 0.),
                                           am=kwargs.get('am', 0.),
                                           cm=kwargs.get('cm', 0.),
                                           dr=kwargs.get('dr', None))
    if isinstance(modelatm, str) and os.path.exists(modelatm):
        modelfilename = modelatm
    elif isinstance(modelatm, str):
        raise ValueError('modelatm= input is a non-existing filename')
    else:  # model atmosphere instance
        # Need to write this instance to a file; we will run in a temp
        # subdirectory of the current directory
        tmpDir = tempfile.mkdtemp(dir=os.getcwd())
        tmpModelAtmDir = True  # need to remove this later
        modelfilename = os.path.join(tmpDir, 'modelatm.mod')
        modelatm.writeto(modelfilename)
    kwargs['modelatm'] = modelfilename
    try:
        # Check whether a MOOG version of the model atmosphere exists
        if not os.path.exists(modelfilename.replace('.mod', '.org')):
            # Convert to MOOG format
            convert_modelAtmosphere(**kwargs)
        # Run weedout on the linelist first if requested
        if run_weedout:
            linelistfilename = modelfilename.replace('.mod', '.lines')
            if not os.path.exists(linelistfilename):
                weedout(**kwargs)
            kwargs['linelist'] = linelistfilename
        # Run MOOG synth for the whole wavelength range as a baseline, also contin
        if baseline is None:
            baseline = moogsynth(**kwargs)[1]
        elif isinstance(baseline,
                        tuple):  #probably accidentally gave wav as well
            baseline = baseline[1]
        if mwav is None or cflux is None:
            mwav, cflux = moogsynth(doflux=True, **kwargs)
        # Convert the apStarWavegrid windows to moogWavegrid regions
        sm, em = [], []
        for start, end in zip(si, ei):
            sm.append(numpy.argmin(numpy.fabs(apWave[start] - mwav)))
            em.append(numpy.argmin(numpy.fabs(apWave[end] - mwav)))
        # Run MOOG synth for all abundances and all windows
        if len(args) == 0:  #special case that there are *no* differences
            args = ([26, 0.], )
        nsynths = numpy.array([len(args[ii]) - 1 for ii in range(len(args))])
        nsynth = numpy.amax(nsynths)  #Take the longest abundance list
        out = numpy.tile(baseline, (nsynth, 1))
        # Run all windows
        for start, end in zip(sm, em):
            kwargs['wmin'] = mwav[start]
            kwargs['wmax'] = mwav[end]
            # Check whether the number of syntheses is > 5 and run multiple
            # MOOG instances if necessary, bc MOOG only does 5 at a time
            ninstances = int(numpy.ceil(nsynth / 5.))
            for ii in range(ninstances):
                newargs = ()
                for jj in range(len(args)):
                    tab = [args[jj][0]]
                    if len(args[jj][5 * ii + 1:5 * (ii + 1) + 1]) > 0:
                        tab.extend(args[jj][5 * ii + 1:5 * (ii + 1) + 1])
                        newargs = newargs + (tab, )
                out[5 * ii:5 * (ii + 1),
                    start:end + 1] = moogsynth(*newargs, **kwargs)[1]
    except:
        raise
    finally:
        if tmpModelAtmDir:  # need to remove this temporary directory
            os.remove(modelfilename)
        moogmodelfilename = modelfilename.replace('.mod', '.org')
        if os.path.exists(moogmodelfilename):
            os.remove(moogmodelfilename)
        if run_weedout:
            os.remove(modelfilename.replace('.mod', '.lines'))
        os.rmdir(tmpDir)
    # Now multiply each continuum-normalized spectrum with the continuum
    out *= numpy.tile(cflux, (nsynth, 1))
    # Now convolve with the LSF
    out = aplsf.convolve(mwav,
                         out,
                         lsf=lsf,
                         xlsf=xlsf,
                         dxlsf=dxlsf,
                         vmacro=vmacro)
    # Now continuum-normalize
    if cont.lower() == 'true':
        # Get the true continuum on the apStar wavelength grid
        apWave = apStarWavegrid()
        baseline = numpy.polynomial.Polynomial.fit(mwav, cflux, 4)
        ip = interpolate.InterpolatedUnivariateSpline(mwav,
                                                      cflux / baseline(mwav),
                                                      k=3)
        cflux = baseline(apWave) * ip(apWave)
        # Divide it out
        out /= numpy.tile(cflux, (nsynth, 1))
    elif not cont is None:
        cflux = apcont.fit(out, numpy.ones_like(out), type=cont)
        out[cflux > 0.] /= cflux[cflux > 0.]
        out[cflux <= 0.] = numpy.nan
    return out
        if params[1][0] >= 5.0:
            params[1][0] = params[1][2] = params[1][1] = 4.9

            # Generate models (flux1, flux2)
        mspecs = ferre.interpolate(params[0], params[1], params[2], params[3], params[4], params[5])
        # for mspec in mspecs:
        # 	mspec[np.where(np.isnan(mspec))] = 0.0

        # prep obs spec
        """aspec= np.reshape(spec,(1,len(spec)))
		aspecerr= np.reshape(specerr,(1,len(specerr)))
		cont= spec / continuum.fit(aspec,aspecerr,type='aspcap')[0]"""
        """spec[np.where(np.isnan(spec))] = 0.0
		cont = spec / max(spec)"""
        # Generate the wavelength grid
        restLambda = splot.apStarWavegrid()

        ccfs = []
        velocityShift = 0

        for mspec in mspecs:
            print(mspec.shape)
            nan_vals = np.where(np.isnan(mspec))[0]
            # nan_vals = np.where(spec == 0.0)[0]
            chip_ranges = [
                (nan_vals[i] + 1, nan_vals[i + 1])
                for i in range(len(nan_vals) - 1)
                if nan_vals[i + 1] != nan_vals[i] + 1
            ]
            mspec[np.where(np.isnan(mspec))[0]] = 0.0
            ycorr = np.array([])
Example #23
0
def synth(*args,**kwargs):
    """
    NAME:
       synth
    PURPOSE:
       Generate model APOGEE spectra using Turbospectrum: this is a general routine that generates the non-continuum-normalized spectrum, convolves with the LSF and macrotubulence, and optionally continuum normalizes the output; use 'turbosynth' for a direct interface to Turbospectrum
    INPUT ARGUMENTS:
       lists with abundances differences wrt the atmosphere (they don't all have to have the same length, missing ones are filled in with zeros):
          [Atomic number1,diff1_1,diff1_2,diff1_3,...,diff1_N]
          [Atomic number2,diff2_1,diff2_2,diff2_3,...,diff2_N]
          ...
          [Atomic numberM,diffM_1,diffM_2,diffM_3,...,diffM_N]
    INPUT KEYWORDS:
       LSF:
          lsf= ('all') LSF to convolve with; output of apogee.spec.lsf.eval; sparsify for efficiency; if 'all' or 'combo' a pre-computed version will be downloaded from the web
          Either:
             xlsf= (None) pixel offset grid on which the LSF is computed (see apogee.spec.lsf.eval); unnecessary if lsf=='all' or 'combo'
             dxlsf= (None) spacing of pixel offsets
          vmacro= (6.) macroturbulence to apply
       CONTINUUM:
          cont= ('aspcap') continuum-normalization to apply:
             None: no continuum normalization
             'true': Use the true continuum
             'aspcap': Use the continuum normalization method of ASPCAP DR12
             'cannon': Normalize using continuum pixels derived from the Cannon
       SYNTHESIS:
          air= (True) if True, perform the synthesis in air wavelengths (output is still in vacuum); set to False at your own risk, as Turbospectrum expects the linelist in air wavelengths!)
          Hlinelist= (None) Hydrogen linelists to use; can be set to the path of a linelist file or to the name of an APOGEE linelist; if None, then we first search for the Hlinedata.vac in the APOGEE linelist directory (if air=False) or we use the internal Turbospectrum Hlinelist (if air=True)
          linelist= (None) molecular and atomic linelists to use; can be set to the path of a linelist file or to the name of an APOGEE linelist, or lists of such files; if a single filename is given, the code will first search for files with extensions '.atoms', '.molec' or that start with 'turboatoms.' and 'turbomolec.'
          wmin, wmax, dw= (15000.000, 17000.000, 0.10000000) spectral synthesis limits and step
          costheta= (1.) cosine of the viewing angle
          lib= ('kurucz_filled') spectral library
       MODEL ATMOSPHERE PARAMETERS:
          Specify one of the following:
             (a) modelatm= (None) model-atmosphere instance
             (b) parameters of a KURUCZ model atmosphere:
                 (1) teff= (4500) Teff
                     logg= (2.5) logg
                     metals= (0.) metallicity
                     cm= (0.) carbon-enhancement
                     am= (0.) alpha-enhancement
                 (2) fparam= standard ASPCAP output format
                 lib= ('kurucz_filled') model atmosphere library
          vmicro= (2.) microturbulence (only used if the MOOG-formatted atmosphere is not found) (can also be part of fparam)
       MISCELLANEOUS:
          dr= return the path corresponding to this data release
    OUTPUT:
       spectra (nspec,nwave)
    HISTORY:
       2015-04-16 - Written - Bovy (IAS)
    """
    # Check that we have the LSF and store the relevant keywords
    lsf= kwargs.pop('lsf','all')
    if isinstance(lsf,str):
        xlsf, lsf= aplsf._load_precomp(dr=kwargs.get('dr',None),fiber=lsf)
        dxlsf= None
    else:
        xlsf= kwargs.pop('xlsf',None)
        dxlsf= kwargs.pop('dxlsf',None)
        if xlsf is None and dxlsf is None: raise ValueError('xlsf= or dxlsf= input needs to be given if the LSF is given as an array')
    vmacro= kwargs.pop('vmacro',6.)
    # Parse continuum-normalization keywords
    cont= kwargs.pop('cont','aspcap')
    # Setup the model atmosphere
    modelatm= kwargs.pop('modelatm',None)
    # Parse fparam, if present
    fparam= kwargs.pop('fparam',None)
    if not fparam is None:
        kwargs['teff']= fparam[paramIndx('TEFF')]
        kwargs['logg']= fparam[paramIndx('LOGG')]
        kwargs['metals']= fparam[paramIndx('METALS')]
        kwargs['am']= fparam[paramIndx('ALPHA')]
        kwargs['cm']= fparam[paramIndx('C')]
        kwargs['vmicro']= 10.**fparam[paramIndx('LOG10VDOP')]        
    # Need to pass a model atmosphere instance to turbosynth (needs to be made
    # more efficient, because now turbosynth always write the atmosphere
    if modelatm is None: # Setup a model atmosphere
        modelatm= atlas9.Atlas9Atmosphere(teff=kwargs.get('teff',4500.),
                                          logg=kwargs.get('logg',2.5),
                                          metals=kwargs.get('metals',0.),
                                          am=kwargs.get('am',0.),
                                          cm=kwargs.get('cm',0.),
                                          dr=kwargs.get('dr',None))
    if isinstance(modelatm,str) and os.path.exists(modelatm):
        raise ValueError('modelatm= input is an existing filename, but you need to give an Atmosphere object instead')
    elif isinstance(modelatm,str):
        raise ValueError('modelatm= input needs to be an Atmosphere instance')
    # Check temperature
    if modelatm._teff > 7000.:
        warnings.warn('Turbospectrum does not include all necessary physics to model stars hotter than about 7000 K; proceed with caution',RuntimeWarning)
    kwargs['modelatm']= modelatm
    try:
        # Run turbosynth for all abundances
        if len(args) == 0: #special case that there are *no* differences
            args= ([26,0.],)
        nsynths= numpy.array([len(args[ii])-1 for ii in range(len(args))])
        nsynth= numpy.amax(nsynths) #Take the longest abundance list
        nturbowav= int((kwargs.get('wmax',_WMAX_DEFAULT)\
                            -kwargs.get('wmin',_WMIN_DEFAULT))\
                           /kwargs.get('dw',_DW_DEFAULT)+1)
        out= numpy.empty((nsynth,nturbowav))
        for ii in range(nsynth):
            newargs= ()
            for jj in range(len(args)):
                tab= [args[jj][0]]
                if len(args[jj]) > ii+1:
                    tab.append(args[jj][ii+1])
                    newargs= newargs+(tab,)
            tmpOut= turbosynth(*newargs,**kwargs)
            out[ii]= tmpOut[2] # incl. continuum
        # wavelength grid from final one
        mwav= tmpOut[0]
    except: raise
    # If the synthesis was done in air, convert wavelength array
    if kwargs.get('air',True):
        mwav= numpy.array([air2vac(w) for w in list(mwav)])
    # Now convolve with the LSF
    out= aplsf.convolve(mwav,out,
                        lsf=lsf,xlsf=xlsf,dxlsf=dxlsf,vmacro=vmacro)
    # Now continuum-normalize
    if cont.lower() == 'true':
        # Get the true continuum on the apStar wavelength grid
        apWave= apStarWavegrid()
        baseline= numpy.polynomial.Polynomial.fit(mwav,tmpOut[2]/tmpOut[1],4)
        ip= interpolate.InterpolatedUnivariateSpline(mwav,
                                                     tmpOut[2]/tmpOut[1]\
                                                         /baseline(mwav),
                                                     k=3)
        cflux= baseline(apWave)*ip(apWave)
        # Divide it out
        out/= numpy.tile(cflux,(nsynth,1))
    elif not cont is None:
        cflux= apcont.fit(out,numpy.ones_like(out),type=cont)
        out[cflux > 0.]/= cflux[cflux > 0.]
        out[cflux <= 0.]= numpy.nan
    return out
Example #24
0
def targetGrid(gridParam, minimizedVisitParams, plot=True):
	'''
	The grid tests against ranging effective temperatures for both stars and the flux ratio of the
	secondary component. This is done by target.

	:param gridParam: [in/out] The GridParam of the target
	:param gridRes: [out] The visits that have the same paramters as the minimized chi2 visit
	:param plot: [in] If true makes plots to see intermediate steps (default=True)
	'''
	locationID = gridParam.locationID
	apogeeID = gridParam.apogeeID

	badheader, header = apread.apStar(locationID, apogeeID, ext=0, header=True)
	specs = apread.apStar(locationID, apogeeID, ext=1, header=False)
	specerrs = apread.apStar(locationID, apogeeID, ext=2, header=False)
	nvisits = header['NVISITS']
	
	# chi2 = np.full((nvisits, nrangeTeffA, nrangeTeffB, nrangeFluxRatio), -1.)
	#chi2 = np.full((nvisits, nrangeTeffA, nrangeTeffB, nrangeFluxRatio, nrangeRVA, nrangeRVB), -1.)
	ipg = ferre.Interpolator(lib='GK')
	ipf = ferre.Interpolator(lib='F')

	# Create file to store all the chi2 values
	path = 'lists/all_chi2/' + str(locationID) + '/'
	if not os.path.exists(path):
		os.makedirs(path)
	fn = open(path + apogeeID + '.lis', 'w')
	fn.write(gridParam.toStringHeader())
	timer = Timer()
	timeSum = 0.0
	allChi2 = []
	visitGridParamsBuffer = []
	for visit in range(1, nvisits + 1):
		timer.start()
		if (nvisits != 1):
			spec = specs[1+visit]
			specerr = specerrs[1+visit]
		else:
			spec = specs
			specerr = specerrs
		
		if (len(minimizedVisitParams) == 0):
			gridParam = GridParam(locationID, apogeeID)
			gridParam.constructParams()
			gridParam.getRVs(visit)
		else:
			gridParam = minimizedVisitParams[visit - 1]
		visitGridParamsBuffer.append(gridParam)
		
		# Prepare grid ranges
		rangeTeffA = np.arange(gridParam.minTeffA, gridParam.maxTeffA, gridParam.teffStepA)
		rangeTeffB = np.arange(gridParam.minTeffB, gridParam.maxTeffB, gridParam.teffStepB)
		rangeFluxRatio = np.arange(gridParam.minFluxRatio, gridParam.maxFluxRatio, gridParam.fluxStep)
		rangeRVA = np.arange(gridParam.minRVA, gridParam.maxRVA, gridParam.rvAStep)
		rangeRVB = np.arange(gridParam.minRVB, gridParam.maxRVB, gridParam.rvBStep)
		nrangeTeffA = len(rangeTeffA)
		nrangeTeffB = len(rangeTeffB)
		nrangeFluxRatio = len(rangeFluxRatio)
		nrangeRVA =len(rangeRVA)
		nrangeRVB =len(rangeRVB)

		chi2 = np.full((nrangeTeffA, nrangeTeffB, nrangeFluxRatio, nrangeRVA, nrangeRVB), -1.)
		print('Visit: ' + str(visit) ,'Grid dimensions: ' + str(chi2.shape))
		# Prep Spectra
		aspec= np.reshape(spec,(1, len(spec)))
		aspecerr= np.reshape(specerr,(1, len(specerr)))
		cont= spec / continuum.fit(aspec, aspecerr, type='aspcap')[0]
		conterr = specerr / continuum.fit(aspec, aspecerr, type='aspcap')[0]
		shiftedSpec = bm.shiftFlux(cont, header['VHELIO' + str(visit)])

		# Run grid
		for i in range(nrangeTeffA):
			gridParam.modelParamA.teff = rangeTeffA[i]
			componentA = bm.genComponent(gridParam.modelParamA, ipf, ipg)
			for j in range(nrangeTeffB):
				gridParam.modelParamB.teff = rangeTeffB[j]
				componentB = bm.genComponent(gridParam.modelParamB, ipf, ipg)
				for k in range(nrangeFluxRatio):
					gridParam.modelParamB.fluxRatio = rangeFluxRatio[k]
					componentBR = componentB * rangeFluxRatio[k]
					for l in range(nrangeRVA):
						gridParam.modelParamA.rv = rangeRVA[l]
						componentAS = bm.shiftFlux(componentA, rangeRVA[l])
						for m in range(nrangeRVB):
							gridParam.modelParamB.rv = rangeRVB[m]
							componentBS = bm.shiftFlux(componentBR, rangeRVB[m])
							binaryFlux = bm.combineFlux(componentAS, componentBS)
							chi2[i][j][k][l][m] = calcChi2(binaryFlux, shiftedSpec, conterr) / (len(binaryFlux) - 5.0)
							gridParam.chi2 = chi2[i][j][k][l][m]
							fn.write(gridParam.toString())
							if (plot is True):
								restLambda = splot.apStarWavegrid()
								BinPlot.plotDeltaVCheck(locationID, apogeeID, visit,
													[	[ restLambda, binaryFlux, 'blue', 'model' ],
														[ restLambda, cont, 'orange', 'unshifted' ],
														[ restLambda, shiftedSpec, 'green', 'shifted' ]],
														[gridParam.modelParamA.teff,gridParam.modelParamB.teff, gridParam.modelParamB.fluxRatio],
														'Delta V Shift', folder='grid_deltaVCheck')

		timeSum+=timer.end()
		allChi2.append(chi2)
	fn.close()

	print('Average visit time: ' + str(round(timeSum/nvisits, 2)) + str('s'))

	# Get minized values for each visit
	indices = None
	for i in range(nvisits):
		inds = getMinIndicies(allChi2[i])
		rangeTeffA = np.arange(visitGridParamsBuffer[i].minTeffA, visitGridParamsBuffer[i].maxTeffA, visitGridParamsBuffer[i].teffStepA)
		rangeTeffB = np.arange(visitGridParamsBuffer[i].minTeffB, visitGridParamsBuffer[i].maxTeffB, visitGridParamsBuffer[i].teffStepB)
		rangeFluxRatio = np.arange(visitGridParamsBuffer[i].minFluxRatio, visitGridParamsBuffer[i].maxFluxRatio, visitGridParamsBuffer[i].fluxStep)
		rangeRVA = np.arange(visitGridParamsBuffer[i].minRVA, visitGridParamsBuffer[i].maxRVA, visitGridParamsBuffer[i].rvAStep)
		rangeRVB = np.arange(visitGridParamsBuffer[i].minRVB, visitGridParamsBuffer[i].maxRVB, visitGridParamsBuffer[i].rvBStep)
		nrangeTeffA = len(rangeTeffA)
		nrangeTeffB = len(rangeTeffB)
		nrangeFluxRatio = len(rangeFluxRatio)
		nrangeRVA =len(rangeRVA)
		nrangeRVB =len(rangeRVB)
		visitGridParamsBuffer[i].setParams(i + 1, rangeTeffA[inds[0]], rangeTeffB[inds[1]], rangeFluxRatio[inds[2]],
					rangeRVA[inds[3]], rangeRVB[inds[4]], allChi2[i][inds[0]][inds[1]][inds[2]][inds[3]][inds[4]])

		if (indices is None):
			indices = [i + 1, inds, allChi2[i][inds[0]][inds[1]][inds[2]][inds[3]][inds[4]]]
		if (allChi2[i][inds[0]][inds[1]][inds[2]][inds[3]][inds[4]] < indices[2]):
			indices = [i + 1, inds, allChi2[i][inds[0]][inds[1]][inds[2]][inds[3]][inds[4]]]
	
	inds = getMinIndicies(allChi2)
	gridParam = visitGridParamsBuffer[inds[0]]
	return gridParam, visitGridParamsBuffer