def calc_elemvar_table(savename,_mywindows=False,_no_indiv_windows=False): if _mywindows: import window # Restore the file with the synthetic spectra with all variations if os.path.exists(savename): with open(savename,'rb') as savefile: baseline= pickle.load(savefile) elem_synspec= pickle.load(savefile) else: raise IOError("File %s with synthetic spectra does not exists; compute this first with apogee/test/test_windows.py" % savename) # Compute the whole table elems= ['C','N','O','Na','Mg','Al','Si','S','K','Ca','Ti','V','Mn','Fe', 'Ni'] table= [] # Loop through synthetic spectra varying one element for elem in elems: col= [] # Now look at the windows for each element for elemc in elems: if not _no_indiv_windows: si, ei= apwindow.waveregions(elemc,asIndex=True,pad=0,dr='13') if _mywindows: elemWeights= window.read(elemc,dr='13') else: elemWeights= apwindow.read(elemc,dr='13') elemWeights/= numpy.nansum(elemWeights) # Start with total col.append(numpy.sqrt(numpy.nansum((elemWeights\ *(elem_synspec[elem][1]-elem_synspec[elem][0])**2.))/numpy.nansum(elemWeights))) if not _no_indiv_windows: for s,e in zip(si,ei): col.append(numpy.sqrt(numpy.nansum((elemWeights\ *(elem_synspec[elem][1]-elem_synspec[elem][0])**2.)[s:e])/numpy.nansum(elemWeights[s:e]))) table.append(col) return table
def bad(elem): """ NAME: bad PURPOSE: return the index (on the apStar grid) of bad windows INPUT: elem - element OUTPUT: boolean array HISTORY: 2015-09-02 - Written - Bovy (UofT) """ si,ei= apwindow.waveregions(elem,asIndex=True,pad=0,dr='13') out= numpy.zeros(8575,dtype='bool') if elem.lower() == 'k': return out for ii,(s,e) in enumerate(zip(si,ei)): # First do the special cases if elem.lower() == 'c': if ref[elem.lower()][ii] <= 0.01 \ or numpy.any(numpy.array(oth[elem.lower()][ii]) > 100.): out[s:e]= True elif elem.lower() == 'n': if ref[elem.lower()][ii] <= 0.01 \ or numpy.any(numpy.array(oth[elem.lower()][ii]) > 200.): out[s:e]= True elif elem.lower() == 'o': if ref[elem.lower()][ii] <= 0.005 \ or numpy.any(numpy.array(oth[elem.lower()][ii]) > 500.): out[s:e]= True elif elem.lower() == 'na': if numpy.any(numpy.array(oth[elem.lower()][ii]) > 34.): out[s:e]= True elif elem.lower() == 'ti': if numpy.any(numpy.array(oth[elem.lower()][ii]) > 30.): out[s:e]= True elif elem.lower() == 'v': if numpy.any(numpy.array(oth[elem.lower()][ii]) > 40.): out[s:e]= True elif elem.lower() == 'mn': if numpy.any(numpy.array(oth[elem.lower()][ii]) > 25.): out[s:e]= True elif elem.lower() == 'ni': if numpy.any(numpy.array(oth[elem.lower()][ii]) > 50.): out[s:e]= True elif ref[elem.lower()][ii] <= 0.01 \ or numpy.any(numpy.array(oth[elem.lower()][ii]) > 34.): out[s:e]= True 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
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 test_windows(options): elems = [ 'C', 'N', 'O', 'Na', 'Mg', 'Al', 'Si', 'S', 'K', 'Ca', 'Ti', 'V', 'Mn', 'Fe', 'Ni', 'Ce', 'Co', 'Cr', 'Cu', 'Ge', 'Nd', 'P', 'Rb', 'Y' ] if options.savefilename is None or \ not os.path.exists(options.savefilename): # Set default linelist for Turbospectrum or MOOG if options.linelist is None and options.moog: linelist = 'moog.201312161124.vac' elif options.linelist is None: linelist = 'turbospec.201312161124' else: linelist = options.linelist # set up a model atmosphere for the requested atmospheric parameters if options.arcturus: options.teff = 4286. options.logg = 1.66 options.metals = -0.52 options.am = 0.4 options.cm = 0.09 options.vm = 1.7 atm = atlas9.Atlas9Atmosphere(teff=options.teff, logg=options.logg, metals=options.metals, am=options.am, cm=options.cm) # create baseline if options.moog: baseline= \ apogee.modelspec.moog.synth(modelatm=atm, linelist=linelist, lsf='all',cont='aspcap', vmacro=6.,isotopes='arcturus', vmicro=options.vm) else: baseline= \ apogee.modelspec.turbospec.synth(modelatm=atm, linelist=linelist, lsf='all',cont='aspcap', vmacro=6.,isotopes='arcturus', vmicro=options.vm) # Loop through elements elem_synspec = {} # Run through once to simulate all differences for elem in elems: # First check that this element has windows elemPath = apwindow.path(elem, dr=options.dr) if not os.path.exists(elemPath): continue # Simulate deltaAbu up and down print "Working on %s" % (elem.capitalize()) abu = [atomic_number(elem), -options.deltaAbu, options.deltaAbu] if options.moog: synspec= \ apogee.modelspec.moog.synth(abu, modelatm=atm, linelist=linelist, lsf='all',cont='aspcap', vmacro=6., isotopes='arcturus', vmicro=options.vm) else: synspec= \ apogee.modelspec.turbospec.synth(abu, modelatm=atm, linelist=linelist, lsf='all',cont='aspcap', vmacro=6., isotopes='arcturus', vmicro=options.vm) elem_synspec[elem] = synspec if not options.savefilename is None: save_pickles(options.savefilename, baseline, elem_synspec) else: with open(options.savefilename, 'rb') as savefile: baseline = pickle.load(savefile) elem_synspec = pickle.load(savefile) # Now run through the different elements again and plot windows for each # with elements that vary significantly colors = sns.color_palette("colorblind") plotelems = [ elem if not elem in ['C', 'N', 'O', 'Fe'] else '%s1' % elem for elem in elems ] plotelems.extend(['C2', 'N2', 'O2', 'Fe2']) for pelem in plotelems: if '1' in pelem or '2' in pelem: elem = pelem[:-1] else: elem = pelem if not elem in elem_synspec: continue # Figure out which elements have significant variations in these # windows and always plot the element that should vary elemIndx = apwindow.tophat(elem, dr=options.dr) elemWeights = apwindow.read(elem, dr=options.dr) elemWeights /= numpy.nansum(elemWeights) # Start with the element in question splot.windows(1. + options.amplify * (elem_synspec[elem][0] - baseline[0]), pelem, color=colors[0], yrange=[0., 1.4], plot_weights=True, zorder=len(elems)) splot.windows(1. + options.amplify * (elem_synspec[elem][1] - baseline[0]), pelem, color=colors[0], overplot=True, zorder=len(elems)) elem_shown = [elem] # Run through the rest to figure out the order elemVar = numpy.zeros(len(elems)) for ii, altElem in enumerate(elems): if altElem == elem: continue if not altElem in elem_synspec: continue elemVar[ii] = 0.5 * numpy.nansum( (elem_synspec[altElem][0] - baseline[0])**2. * elemWeights) elemVar[ii] += 0.5 * numpy.nansum( (elem_synspec[altElem][1] - baseline[0])**2. * elemWeights) jj = 0 sortindx = numpy.argsort(elemVar)[::-1] for altElem in numpy.array(elems)[sortindx]: if altElem == elem: continue if not altElem in elem_synspec: continue if numpy.fabs(\ numpy.nanmax([(elem_synspec[altElem][0]-baseline[0])[elemIndx], (elem_synspec[altElem][1]-baseline[0])[elemIndx]]))\ > options.varthreshold: jj += 1 if jj >= len(colors): jj = len(colors) - 1 elem_shown.append(altElem) splot.windows(1. + options.amplify * (elem_synspec[altElem][0] - baseline[0]), pelem, color=colors[jj], overplot=True, zorder=len(elems) - jj) splot.windows(1. + options.amplify * (elem_synspec[altElem][1] - baseline[0]), pelem, color=colors[jj], overplot=True, zorder=len(elems) - jj) t = pyplot.gca().transData fig = pyplot.gcf() for s, c in zip(elem_shown, colors[:jj + 1]): xc = 0.05 if elem == 'K' or elem == 'Ce' or elem == 'Ge' or elem == 'Nd' \ or elem == 'Rb': xc = apwindow.waveregions(elem, dr=options.dr, pad=3)[0][0] - 15000. + 1. text = pyplot.text(xc, 1.2, " " + (r"$\mathrm{%s}$" % s) + " ", color=c, transform=t, size=16., backgroundcolor='w') text.draw(fig.canvas.get_renderer()) ex = text.get_window_extent() t = transforms.offset_copy(text._transform, x=1.5 * ex.width, units='dots') # Save bovy_plot.bovy_end_print(options.plotfilename.replace('ELEM', pelem)) return None
def windows(*args, **kwargs): """ NAME: windows PURPOSE: plot the spectral windows for a given element INPUT: Either: (a) wavelength, spectrum (\AA,spectrum units) (b) spectrum (assumed on standard APOGEE re-sampled wavelength grid) (c) location ID, APOGEE ID (default loads aspcapStar, loads extension ext(=1); apStar=True loads apStar spectrum) +element string (e.g., 'Al'); Adding 1 and 2 splits the windows into two KEYWORDS: plot_weights= (False) if True, also plot the weights for the windows (assumes that the spectrum is on the apStarWavegrid) markLines= mark the location of 'lines' (see apogee.spec.window.lines) apogee.spec.plot.waveregions keywords OUTPUT: plot to output The final axes allow one to put additional labels on the plot, e.g., for adding the APOGEE ID: bovy_plot.bovy_text(r'$\mathrm{%s}$' % '2M02420597+0837017',top_left=True) Note that an ID (e.g., the apogee ID) and Teff, logg, metallicity, and alpha-enhancement labels can be added using the keywords label* above HISTORY: 2015-01-26 - Written (based on older code) - Bovy (IAS) """ pad = kwargs.pop('pad', 3) try: si, ei = apwindow.waveregions(args[2], pad=pad, asIndex=True) except IOError: try: si, ei = apwindow.waveregions(args[2][:-1], pad=pad, asIndex=True) except IOError: raise IOError( "Windows for element %s could not be loaded, please specify an existing APOGEE element" % ((args[2].lower().capitalize()))) if args[2][-1] == '1': si = si[:len(si) // 2] ei = ei[:len(ei) // 2] else: si = si[len(si) // 2:] ei = ei[len(ei) // 2:] # Remove the number from the element newargs = (args[0], args[1], args[2][:-1]) for ii in range(len(args) - 3): newargs = newargs + (args[ii + 3], ) args = newargs # Also get the number and total width of all of the windows dlam = apwindow.total_dlambda(args[2], pad=pad) numw = apwindow.num(args[2]) # Set spacing between windows if numw > 20: kwargs['skipdx'] = 0.003 kwargs['_noskipdiags'] = True elif numw > 15: kwargs['skipdx'] = 0.01 # Set initial space to zero kwargs['_startendskip'] = 0 # Set initial figure width if not kwargs.get('overplot', False) and not 'fig_width' in kwargs: if dlam > 150.: kwargs['fig_width'] = 8.4 else: kwargs['fig_width'] = 4.2 # Don't tick x kwargs['_noxticks'] = True # Label the largest wavelength in angstrom kwargs['_labelwav'] = True # Don't label the lines unless explicitly asked for kwargs['labelLines'] = kwargs.get('labelLines', False) # Plot the weights as well if kwargs.pop('plot_weights', False): kwargs['_plotw'] = apwindow.read(args[2], apStarWavegrid=True) if kwargs.get('apStar', False): kwargs['yrange'] = kwargs.get('yrange', [0., 1.1 * numpy.nanmax(args[1])]) else: kwargs['yrange'] = kwargs.get('yrange', [0., 1.2]) # mark the 'lines' markLines = kwargs.get('markLines', not 'overplot' in kwargs) if markLines and not '_markwav' in kwargs: kwargs['_markwav'] = apwindow.lines(args[2]) # Plot waveregions(args[0], args[1], startindxs=si, endindxs=ei, *args[3:], **kwargs) # Add label bovy_plot.bovy_text(r'$\mathrm{%s}$' % ((args[2].lower().capitalize())), top_left=True, fontsize=10, backgroundcolor='w') return None
def windows(*args,**kwargs): """ NAME: windows PURPOSE: plot the spectral windows for a given element INPUT: Either: (a) wavelength, spectrum (\AA,spectrum units) (b) spectrum (assumed on standard APOGEE re-sampled wavelength grid) (c) location ID, APOGEE ID (default loads aspcapStar, loads extension ext(=1); apStar=True loads apStar spectrum) +element string (e.g., 'Al'); Adding 1 and 2 splits the windows into two KEYWORDS: plot_weights= (False) if True, also plot the weights for the windows (assumes that the spectrum is on the apStarWavegrid) markLines= mark the location of 'lines' (see apogee.spec.window.lines) apogee.spec.plot.waveregions keywords OUTPUT: plot to output The final axes allow one to put additional labels on the plot, e.g., for adding the APOGEE ID: bovy_plot.bovy_text(r'$\mathrm{%s}$' % '2M02420597+0837017',top_left=True) Note that an ID (e.g., the apogee ID) and Teff, logg, metallicity, and alpha-enhancement labels can be added using the keywords label* above HISTORY: 2015-01-26 - Written (based on older code) - Bovy (IAS) """ pad= kwargs.pop('pad',3) try: si,ei= apwindow.waveregions(args[2],pad=pad,asIndex=True) except IOError: try: si, ei= apwindow.waveregions(args[2][:-1],pad=pad,asIndex=True) except IOError: raise IOError("Windows for element %s could not be loaded, please specify an existing APOGEE element" % ((args[2].lower().capitalize()))) if args[2][-1] == '1': si= si[:len(si)//2] ei= ei[:len(ei)//2] else: si= si[len(si)//2:] ei= ei[len(ei)//2:] # Remove the number from the element newargs= (args[0],args[1],args[2][:-1]) for ii in range(len(args)-3): newargs= newargs+(args[ii+3],) args= newargs # Also get the number and total width of all of the windows dlam= apwindow.total_dlambda(args[2],pad=pad) numw= apwindow.num(args[2]) # Set spacing between windows if numw > 20: kwargs['skipdx']= 0.003 kwargs['_noskipdiags']= True elif numw > 15: kwargs['skipdx']= 0.01 # Set initial space to zero kwargs['_startendskip']= 0 # Set initial figure width if not kwargs.get('overplot',False) and not 'fig_width' in kwargs: if dlam > 150.: kwargs['fig_width']= 8.4 else: kwargs['fig_width']= 4.2 # Don't tick x kwargs['_noxticks']= True # Label the largest wavelength in angstrom kwargs['_labelwav']= True # Don't label the lines unless explicitly asked for kwargs['labelLines']= kwargs.get('labelLines',False) # Plot the weights as well if kwargs.pop('plot_weights',False): kwargs['_plotw']= apwindow.read(args[2],apStarWavegrid=True) if kwargs.get('apStar',False): kwargs['yrange']= kwargs.get('yrange', [0.,1.1*numpy.nanmax(args[1])]) else: kwargs['yrange']= kwargs.get('yrange',[0.,1.2]) # mark the 'lines' markLines= kwargs.get('markLines',not 'overplot' in kwargs) if markLines and not '_markwav' in kwargs: kwargs['_markwav']= apwindow.lines(args[2]) # Plot waveregions(args[0],args[1],startindxs=si,endindxs=ei, *args[3:],**kwargs) # Add label bovy_plot.bovy_text(r'$\mathrm{%s}$' % ((args[2].lower().capitalize())), top_left=True,fontsize=10,backgroundcolor='w') return None
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 test_windows(options): elems= ['C','N','O','Na','Mg','Al','Si','S','K','Ca','Ti','V','Mn','Fe', 'Ni','Ce','Co','Cr','Cu','Ge','Nd','P','Rb','Y'] if options.savefilename is None or \ not os.path.exists(options.savefilename): # Set default linelist for Turbospectrum or MOOG if options.linelist is None and options.moog: linelist= 'moog.201312161124.vac' elif options.linelist is None: linelist= 'turbospec.201312161124' else: linelist= options.linelist # set up a model atmosphere for the requested atmospheric parameters if options.arcturus: options.teff= 4286. options.logg= 1.66 options.metals= -0.52 options.am= 0.4 options.cm= 0.09 options.vm= 1.7 atm= atlas9.Atlas9Atmosphere(teff=options.teff,logg=options.logg, metals=options.metals, am=options.am,cm=options.cm) # create baseline if options.moog: baseline= \ apogee.modelspec.moog.synth(modelatm=atm, linelist=linelist, lsf='all',cont='aspcap', vmacro=6.,isotopes='arcturus', vmicro=options.vm) else: baseline= \ apogee.modelspec.turbospec.synth(modelatm=atm, linelist=linelist, lsf='all',cont='aspcap', vmacro=6.,isotopes='arcturus', vmicro=options.vm) # Loop through elements elem_synspec= {} # Run through once to simulate all differences for elem in elems: # First check that this element has windows elemPath= apwindow.path(elem,dr=options.dr) if not os.path.exists(elemPath): continue # Simulate deltaAbu up and down print "Working on %s" % (elem.capitalize()) abu= [atomic_number(elem),-options.deltaAbu,options.deltaAbu] if options.moog: synspec= \ apogee.modelspec.moog.synth(abu, modelatm=atm, linelist=linelist, lsf='all',cont='aspcap', vmacro=6., isotopes='arcturus', vmicro=options.vm) else: synspec= \ apogee.modelspec.turbospec.synth(abu, modelatm=atm, linelist=linelist, lsf='all',cont='aspcap', vmacro=6., isotopes='arcturus', vmicro=options.vm) elem_synspec[elem]= synspec if not options.savefilename is None: save_pickles(options.savefilename,baseline,elem_synspec) else: with open(options.savefilename,'rb') as savefile: baseline= pickle.load(savefile) elem_synspec= pickle.load(savefile) # Now run through the different elements again and plot windows for each # with elements that vary significantly colors= sns.color_palette("colorblind") plotelems= [elem if not elem in ['C','N','O','Fe'] else '%s1' % elem for elem in elems] plotelems.extend(['C2','N2','O2','Fe2']) for pelem in plotelems: if '1' in pelem or '2' in pelem: elem = pelem[:-1] else: elem= pelem if not elem in elem_synspec: continue # Figure out which elements have significant variations in these # windows and always plot the element that should vary elemIndx= apwindow.tophat(elem,dr=options.dr) elemWeights= apwindow.read(elem,dr=options.dr) elemWeights/= numpy.nansum(elemWeights) # Start with the element in question splot.windows(1.+options.amplify*(elem_synspec[elem][0]-baseline[0]), pelem, color=colors[0], yrange=[0.,1.4], plot_weights=True, zorder=len(elems)) splot.windows(1.+options.amplify*(elem_synspec[elem][1]-baseline[0]), pelem, color=colors[0],overplot=True, zorder=len(elems)) elem_shown= [elem] # Run through the rest to figure out the order elemVar= numpy.zeros(len(elems)) for ii,altElem in enumerate(elems): if altElem == elem: continue if not altElem in elem_synspec: continue elemVar[ii]= 0.5*numpy.nansum((elem_synspec[altElem][0]-baseline[0])**2.*elemWeights) elemVar[ii]+= 0.5*numpy.nansum((elem_synspec[altElem][1]-baseline[0])**2.*elemWeights) jj= 0 sortindx= numpy.argsort(elemVar)[::-1] for altElem in numpy.array(elems)[sortindx]: if altElem == elem: continue if not altElem in elem_synspec: continue if numpy.fabs(\ numpy.nanmax([(elem_synspec[altElem][0]-baseline[0])[elemIndx], (elem_synspec[altElem][1]-baseline[0])[elemIndx]]))\ > options.varthreshold: jj+= 1 if jj >= len(colors): jj= len(colors)-1 elem_shown.append(altElem) splot.windows(1.+options.amplify*(elem_synspec[altElem][0]-baseline[0]), pelem, color=colors[jj],overplot=True, zorder=len(elems)-jj) splot.windows(1.+options.amplify*(elem_synspec[altElem][1]-baseline[0]), pelem, color=colors[jj],overplot=True, zorder=len(elems)-jj) t = pyplot.gca().transData fig= pyplot.gcf() for s,c in zip(elem_shown,colors[:jj+1]): xc= 0.05 if elem == 'K' or elem == 'Ce' or elem == 'Ge' or elem == 'Nd' \ or elem == 'Rb': xc= apwindow.waveregions(elem,dr=options.dr, pad=3)[0][0]-15000.+1. text = pyplot.text(xc,1.2," "+(r"$\mathrm{%s}$" % s)+" ",color=c, transform=t,size=16.,backgroundcolor='w') text.draw(fig.canvas.get_renderer()) ex= text.get_window_extent() t= transforms.offset_copy(text._transform,x=1.5*ex.width, units='dots') # Save bovy_plot.bovy_end_print(options.plotfilename.replace('ELEM',pelem)) return None