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]
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
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
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
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
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
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]
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 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
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
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]
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
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))
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
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
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
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([])
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
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