def moogsynth(*args, **kwargs): """ NAME: moogsynth PURPOSE: Run a MOOG synthesis (direct interface to the MOOG code; use 'synth' for a general routine that generates the non-continuum-normalized spectrum, convolves withe LSF and macrotubulence, and optionally continuum normalizes the output) INPUT ARGUMENTS: lists with abundances (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] SYNTHEIS KEYWORDS: isotopes= ('solar') use 'solar' or 'arcturus' isotope ratios; can also be a dictionary with isotope ratios (e.g., isotopes= {'108.00116':'1.001','606.01212':'1.01'}) wmin, wmax, dw, width= (15000.000, 17000.000, 0.10000000, 7.0000000) spectral synthesis limits, step, and width of calculation (see MOOG) doflux= (False) if True, calculate the continuum flux instead LINELIST KEYWORDS: 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; otherwise can be set to the path of a linelist file or to the name of an APOGEE linelist ATMOSPHERE KEYWORDS: Either: (a) modelatm= (None) can be set to the filename of a model atmosphere (needs to end in .mod) (b) specify the stellar parameters for a grid point in model atm by - lib= ('kurucz_filled') spectral library - teff= (4500) grid-point Teff - logg= (2.5) grid-point logg - metals= (0.) grid-point metallicity - cm= (0.) grid-point carbon-enhancement - am= (0.) grid-point alpha-enhancement - dr= return the path corresponding to this data release vmicro= (2.) microturbulence (km/s) (only used if the MOOG-formatted atmosphere file doesn't already exist) OUTPUT: (wavelengths,spectra (nspec,nwave)) for synth driver (wavelengths,continuum spectr (nwave)) for doflux driver HISTORY: 2015-02-13 - Written - Bovy (IAS) """ doflux = kwargs.pop('doflux', False) # Get the spectral synthesis limits wmin = kwargs.pop('wmin', _WMIN_DEFAULT) wmax = kwargs.pop('wmax', _WMAX_DEFAULT) dw = kwargs.pop('dw', _DW_DEFAULT) width = kwargs.pop('width', _WIDTH_DEFAULT) linelist = kwargs.pop('linelist', None) # Parse isotopes isotopes = kwargs.pop('isotopes', 'solar') if isinstance(isotopes, str) and isotopes.lower() == 'solar': isotopes = { '108.00116': '1.001', '606.01212': '1.01', '606.01213': '90', '606.01313': '180', '607.01214': '1.01', '607.01314': '90', '607.01215': '273', '608.01216': '1.01', '608.01316': '90', '608.01217': '1101', '608.01218': '551', '114.00128': '1.011', '114.00129': '20', '114.00130': '30', '101.00101': '1.001', '101.00102': '1000', '126.00156': '1.00' } elif isinstance(isotopes, str) and isotopes.lower() == 'arcturus': isotopes = { '108.00116': '1.001', '606.01212': '0.91', '606.01213': '8', '606.01313': '81', '607.01214': '0.91', '607.01314': '8', '607.01215': '273', '608.01216': '0.91', '608.01316': '8', '608.01217': '1101', '608.01218': '551', '114.00128': '1.011', '114.00129': '20', '114.00130': '30', '101.00101': '1.001', '101.00102': '1000', '126.00156': '1.00' } elif not isinstance(isotopes, dict): raise ValueError( "'isotopes=' input not understood, should be 'solar', 'arcturus', or a dictionary" ) # Get the filename of the model atmosphere modelatm = kwargs.pop('modelatm', None) if not modelatm is 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: raise ValueError( 'modelatm= in moogsynth should be set to the name of a file') else: modelfilename = appath.modelAtmospherePath(**kwargs) # Check whether a MOOG version exists if not os.path.exists(modelfilename.replace('.mod', '.org')): # Convert to MOOG format convert_modelAtmosphere(modelatm=modelfilename, **kwargs) modeldirname = os.path.dirname(modelfilename) modelbasename = os.path.basename(modelfilename) # Get the name of the linelist if linelist is None: linelistfilename = modelbasename.replace('.mod', '.lines') if not os.path.exists(os.path.join(modeldirname, linelistfilename)): raise IOError( 'No linelist given and no weed-out version found for this atmosphere; either specify a linelist or run weedout first' ) linelistfilename = os.path.join(modeldirname, linelistfilename) elif os.path.exists(linelist): linelistfilename = linelist else: linelistfilename = appath.linelistPath(linelist, dr=kwargs.get('dr', None)) if not os.path.exists(linelistfilename): raise RuntimeError( "Linelist %s not found; download linelist w/ apogee.tools.download.linelist (if you have access)" % linelistfilename) # We will run in a subdirectory of the relevant model atmosphere tmpDir = tempfile.mkdtemp(dir=modeldirname) shutil.copy(linelistfilename, tmpDir) # Cut the linelist to the desired wavelength range with open(os.path.join(tmpDir, 'cutlines.awk'), 'w') as awkfile: awkfile.write('$1>%.3f && $1<%.3f\n' % (wmin - width, wmax + width)) keeplines = open(os.path.join(tmpDir, 'lines.tmp'), 'w') stderr = open('/dev/null', 'w') try: subprocess.check_call( ['awk', '-f', 'cutlines.awk', os.path.basename(linelistfilename)], cwd=tmpDir, stdout=keeplines, stderr=stderr) keeplines.close() shutil.copy(os.path.join(tmpDir, 'lines.tmp'), os.path.join(tmpDir, os.path.basename(linelistfilename))) except subprocess.CalledProcessError: print("Removing unnecessary linelist entries failed ...") finally: os.remove(os.path.join(tmpDir, 'cutlines.awk')) os.remove(os.path.join(tmpDir, 'lines.tmp')) stderr.close() # Also copy the strong lines stronglinesfilename = appath.linelistPath('stronglines.vac', dr=kwargs.get('dr', None)) if not os.path.exists(stronglinesfilename): try: download.linelist('stronglines.vac', dr=kwargs.get('dr', None)) except: raise RuntimeError( "Linelist stronglines.vac not found or downloading failed; download linelist w/ apogee.tools.download.linelist (if you have access)" ) finally: if os.path.exists(os.path.join(tmpDir, 'synth.par')): os.remove(os.path.join(tmpDir, 'synth.par')) if os.path.exists(os.path.join(tmpDir, 'std.out')): os.remove(os.path.join(tmpDir, 'std.out')) if os.path.exists( os.path.join(tmpDir, os.path.basename(linelistfilename))): os.remove( os.path.join(tmpDir, os.path.basename(linelistfilename))) if os.path.exists(os.path.join(tmpDir, 'stronglines.vac')): os.remove(os.path.join(tmpDir, 'stronglines.vac')) os.rmdir(tmpDir) shutil.copy(stronglinesfilename, tmpDir) # Now write the script file 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 if nsynth > 5: raise ValueError( "MOOG only allows five syntheses to be run at the same time; please reduce the number of abundance values in the apogee.modelspec.moog.moogsynth input" ) nabu = len(args) with open(os.path.join(tmpDir, 'synth.par'), 'w') as parfile: if doflux: parfile.write('doflux\n') else: parfile.write('synth\n') parfile.write('terminal x11\n') parfile.write('plot 1\n') parfile.write("standard_out std.out\n") parfile.write("summary_out '../synth.out'\n") parfile.write("smoothed_out '/dev/null'\n") parfile.write("strong 1\n") parfile.write("damping 0\n") parfile.write("stronglines_in stronglines.vac\n") parfile.write("model_in '../%s'\n" % modelbasename.replace('.mod', '.org')) parfile.write("lines_in %s\n" % os.path.basename(linelistfilename)) parfile.write("atmosphere 1\n") parfile.write("molecules 2\n") parfile.write("lines 1\n") parfile.write("flux/int 0\n") # Write the isotopes niso = len(isotopes) parfile.write("isotopes %i %i\n" % (niso, nsynth)) for iso in isotopes: isotopestr = iso for ii in range(nsynth): isotopestr += ' ' + isotopes[iso] parfile.write(isotopestr + '\n') # Abundances parfile.write("abundances %i %i\n" % (nabu, nsynth)) for ii in range(nabu): abustr = '%i' % args[ii][0] for jj in range(nsynth): try: abustr += ' %.3f' % args[ii][jj + 1] except IndexError: abustr += ' 0.0' parfile.write(abustr + "\n") # Synthesis limits parfile.write("synlimits\n") # Add 0.001 to make sure wmax is included parfile.write("%.3f %.3f %.3f %.3f\n" % (wmin, wmax + 0.001, dw, width)) # Now run synth sys.stdout.write('\r' + "Running MOOG synth ...\r") sys.stdout.flush() try: p = subprocess.Popen(['moogsilent'], cwd=tmpDir, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) p.stdin.write('synth.par\n') stdout, stderr = p.communicate() except subprocess.CalledProcessError: print("Running synth failed ...") finally: if os.path.exists(os.path.join(tmpDir, 'synth.par')): os.remove(os.path.join(tmpDir, 'synth.par')) if os.path.exists(os.path.join(tmpDir, 'std.out')): os.remove(os.path.join(tmpDir, 'std.out')) if os.path.exists( os.path.join(tmpDir, os.path.basename(linelistfilename))): os.remove(os.path.join(tmpDir, os.path.basename(linelistfilename))) if os.path.exists(os.path.join(tmpDir, 'stronglines.vac')): os.remove(os.path.join(tmpDir, 'stronglines.vac')) os.rmdir(tmpDir) sys.stdout.write('\r' + download._ERASESTR + '\r') sys.stdout.flush() # Now read the output wavs = numpy.arange(wmin, wmax + dw, dw) if wavs[-1] > wmax + dw / 2.: wavs = wavs[:-1] if doflux: contdata = numpy.loadtxt(os.path.join(modeldirname, 'synth.out'), converters={ 0: lambda x: x.replace('D', 'E'), 1: lambda x: x.replace('D', 'E') }, usecols=[0, 1]) # Wavelength in summary file appears to be wrong from comparing to # the standard output file out = contdata[:, 1] out /= numpy.nanmean(out) # Make the numbers more manageable else: with open(os.path.join(modeldirname, 'synth.out')) as summfile: out = numpy.empty((nsynth, len(wavs))) for ii in range(nsynth): # Skip to beginning of synthetic spectrum while True: line = summfile.readline() if line[0] == 'M': break summfile.readline() tout = [] while True: line = summfile.readline() if not line or line[0] == 'A': break tout.extend([float(s) for s in line.split()]) out[ii] = numpy.array(tout) os.remove(os.path.join(modeldirname, 'synth.out')) if doflux: return (wavs, out) else: return (wavs, 1. - out)
def moogsynth(*args,**kwargs): """ NAME: moogsynth PURPOSE: Run a MOOG synthesis (direct interface to the MOOG code; use 'synth' for a general routine that generates the non-continuum-normalized spectrum, convolves withe LSF and macrotubulence, and optionally continuum normalizes the output) INPUT ARGUMENTS: lists with abundances (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] SYNTHEIS KEYWORDS: isotopes= ('solar') use 'solar' or 'arcturus' isotope ratios; can also be a dictionary with isotope ratios (e.g., isotopes= {'108.00116':'1.001','606.01212':'1.01'}) wmin, wmax, dw, width= (15000.000, 17000.000, 0.10000000, 7.0000000) spectral synthesis limits, step, and width of calculation (see MOOG) doflux= (False) if True, calculate the continuum flux instead LINELIST KEYWORDS: 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; otherwise can be set to the path of a linelist file or to the name of an APOGEE linelist ATMOSPHERE KEYWORDS: Either: (a) modelatm= (None) can be set to the filename of a model atmosphere (needs to end in .mod) (b) specify the stellar parameters for a grid point in model atm by - lib= ('kurucz_filled') spectral library - teff= (4500) grid-point Teff - logg= (2.5) grid-point logg - metals= (0.) grid-point metallicity - cm= (0.) grid-point carbon-enhancement - am= (0.) grid-point alpha-enhancement - dr= return the path corresponding to this data release vmicro= (2.) microturbulence (km/s) (only used if the MOOG-formatted atmosphere file doesn't already exist) OUTPUT: (wavelengths,spectra (nspec,nwave)) for synth driver (wavelengths,continuum spectr (nwave)) for doflux driver HISTORY: 2015-02-13 - Written - Bovy (IAS) """ doflux= kwargs.pop('doflux',False) # Get the spectral synthesis limits wmin= kwargs.pop('wmin',_WMIN_DEFAULT) wmax= kwargs.pop('wmax',_WMAX_DEFAULT) dw= kwargs.pop('dw',_DW_DEFAULT) width= kwargs.pop('width',_WIDTH_DEFAULT) linelist= kwargs.pop('linelist',None) # Parse isotopes isotopes= kwargs.pop('isotopes','solar') if isinstance(isotopes,str) and isotopes.lower() == 'solar': isotopes= {'108.00116':'1.001', '606.01212':'1.01', '606.01213':'90', '606.01313':'180', '607.01214':'1.01', '607.01314':'90', '607.01215':'273', '608.01216':'1.01', '608.01316':'90', '608.01217':'1101', '608.01218':'551', '114.00128':'1.011', '114.00129':'20', '114.00130':'30', '101.00101':'1.001', '101.00102':'1000', '126.00156':'1.00'} elif isinstance(isotopes,str) and isotopes.lower() == 'arcturus': isotopes= {'108.00116':'1.001', '606.01212':'0.91', '606.01213':'8', '606.01313':'81', '607.01214':'0.91', '607.01314':'8', '607.01215':'273', '608.01216':'0.91', '608.01316':'8', '608.01217':'1101', '608.01218':'551', '114.00128':'1.011', '114.00129':'20', '114.00130':'30', '101.00101':'1.001', '101.00102':'1000', '126.00156':'1.00'} elif not isinstance(isotopes,dict): raise ValueError("'isotopes=' input not understood, should be 'solar', 'arcturus', or a dictionary") # Get the filename of the model atmosphere modelatm= kwargs.pop('modelatm',None) if not modelatm is 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: raise ValueError('modelatm= in moogsynth should be set to the name of a file') else: modelfilename= appath.modelAtmospherePath(**kwargs) # Check whether a MOOG version exists if not os.path.exists(modelfilename.replace('.mod','.org')): # Convert to MOOG format convert_modelAtmosphere(modelatm=modelfilename,**kwargs) modeldirname= os.path.dirname(modelfilename) modelbasename= os.path.basename(modelfilename) # Get the name of the linelist if linelist is None: linelistfilename= modelbasename.replace('.mod','.lines') if not os.path.exists(os.path.join(modeldirname,linelistfilename)): raise IOError('No linelist given and no weed-out version found for this atmosphere; either specify a linelist or run weedout first') linelistfilename= os.path.join(modeldirname,linelistfilename) elif os.path.exists(linelist): linelistfilename= linelist else: linelistfilename= appath.linelistPath(linelist, dr=kwargs.get('dr',None)) # We will run in a subdirectory of the relevant model atmosphere tmpDir= tempfile.mkdtemp(dir=modeldirname) shutil.copy(linelistfilename,tmpDir) # Cut the linelist to the desired wavelength range with open(os.path.join(tmpDir,'cutlines.awk'),'w') as awkfile: awkfile.write('$1>%.3f && $1<%.3f\n' %(wmin-width,wmax+width)) keeplines= open(os.path.join(tmpDir,'lines.tmp'),'w') stderr= open('/dev/null','w') try: subprocess.check_call(['awk','-f','cutlines.awk', os.path.basename(linelistfilename)], cwd=tmpDir,stdout=keeplines,stderr=stderr) keeplines.close() shutil.copy(os.path.join(tmpDir,'lines.tmp'), os.path.join(tmpDir,os.path.basename(linelistfilename))) except subprocess.CalledProcessError: print("Removing unnecessary linelist entries failed ...") finally: os.remove(os.path.join(tmpDir,'cutlines.awk')) os.remove(os.path.join(tmpDir,'lines.tmp')) stderr.close() # Also copy the strong lines stronglinesfilename= appath.linelistPath('stronglines.vac', dr=kwargs.get('dr',None)) if not os.path.exists(stronglinesfilename): download.linelist('stronglines.vac',dr=kwargs.get('dr',None)) shutil.copy(stronglinesfilename,tmpDir) # Now write the script file 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 if nsynth > 5: raise ValueError("MOOG only allows five syntheses to be run at the same time; please reduce the number of abundance values in the apogee.modelspec.moog.moogsynth input") nabu= len(args) with open(os.path.join(tmpDir,'synth.par'),'w') as parfile: if doflux: parfile.write('doflux\n') else: parfile.write('synth\n') parfile.write('terminal x11\n') parfile.write('plot 1\n') parfile.write("standard_out std.out\n") parfile.write("summary_out '../synth.out'\n") parfile.write("smoothed_out '/dev/null'\n") parfile.write("strong 1\n") parfile.write("damping 0\n") parfile.write("stronglines_in stronglines.vac\n") parfile.write("model_in '../%s'\n" % modelbasename.replace('.mod','.org')) parfile.write("lines_in %s\n" % os.path.basename(linelistfilename)) parfile.write("atmosphere 1\n") parfile.write("molecules 2\n") parfile.write("lines 1\n") parfile.write("flux/int 0\n") # Write the isotopes niso= len(isotopes) parfile.write("isotopes %i %i\n" % (niso,nsynth)) for iso in isotopes: isotopestr= iso for ii in range(nsynth): isotopestr+= ' '+isotopes[iso] parfile.write(isotopestr+'\n') # Abundances parfile.write("abundances %i %i\n" % (nabu,nsynth)) for ii in range(nabu): abustr= '%i' % args[ii][0] for jj in range(nsynth): try: abustr+= ' %.3f' % args[ii][jj+1] except IndexError: abustr+= ' 0.0' parfile.write(abustr+"\n") # Synthesis limits parfile.write("synlimits\n") # Add 0.001 to make sure wmax is included parfile.write("%.3f %.3f %.3f %.3f\n" % (wmin,wmax+0.001,dw,width)) # Now run synth sys.stdout.write('\r'+"Running MOOG synth ...\r") sys.stdout.flush() try: p= subprocess.Popen(['moogsilent'], cwd=tmpDir, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) p.stdin.write('synth.par\n') stdout, stderr= p.communicate() except subprocess.CalledProcessError: print("Running synth failed ...") finally: if os.path.exists(os.path.join(tmpDir,'synth.par')): os.remove(os.path.join(tmpDir,'synth.par')) if os.path.exists(os.path.join(tmpDir,'std.out')): os.remove(os.path.join(tmpDir,'std.out')) if os.path.exists(os.path.join(tmpDir, os.path.basename(linelistfilename))): os.remove(os.path.join(tmpDir,os.path.basename(linelistfilename))) if os.path.exists(os.path.join(tmpDir,'stronglines.vac')): os.remove(os.path.join(tmpDir,'stronglines.vac')) os.rmdir(tmpDir) sys.stdout.write('\r'+download._ERASESTR+'\r') sys.stdout.flush() # Now read the output wavs= numpy.arange(wmin,wmax+dw,dw) if wavs[-1] > wmax+dw/2.: wavs= wavs[:-1] if doflux: contdata= numpy.loadtxt(os.path.join(modeldirname,'synth.out'), converters={0:lambda x: x.replace('D','E'), 1:lambda x: x.replace('D','E')}, usecols=[0,1]) # Wavelength in summary file appears to be wrong from comparing to # the standard output file out= contdata[:,1] out/= numpy.nanmean(out) # Make the numbers more manageable else: with open(os.path.join(modeldirname,'synth.out')) as summfile: out= numpy.empty((nsynth,len(wavs))) for ii in range(nsynth): # Skip to beginning of synthetic spectrum while True: line= summfile.readline() if line[0] == 'M': break summfile.readline() tout= [] while True: line= summfile.readline() if not line or line[0] == 'A': break tout.extend([float(s) for s in line.split()]) out[ii]= numpy.array(tout) os.remove(os.path.join(modeldirname,'synth.out')) if doflux: return (wavs,out) else: return (wavs,1.-out)
def turbosynth(*args,**kwargs): """ NAME: turbosynth PURPOSE: Run a Turbospectrum synthesis (direct interface to the Turbospectrum code; use 'synth' for a general routine that generates the non-continuum-normalized spectrum, convolves withe LSF and macrotubulence, and optionally continuum normalizes the output) INPUT ARGUMENTS: lists with abundances: [Atomic number1,diff1] [Atomic number2,diff2] ... [Atomic numberM,diffM] SYNTHEIS KEYWORDS: isotopes= ('solar') use 'solar' or 'arcturus' isotope ratios; can also be a dictionary with isotope ratios (e.g., isotopes= {'6.012':'0.9375','6.013':'0.0625'}) wmin, wmax, dw, width= (15000.000, 17000.000, 0.10000000) spectral synthesis limits and step of calculation (see MOOG) babsma_wmin, babsma_wmax= (wmin,wmax)) allows opacity limits to be different (broader) than for the synthesis itself costheta= (1.) cosine of the viewing angle LINELIST KEYWORDS: air= (True) if True, perform the synthesis in air wavelengths (affects the default Hlinelist, nothing else; output is in air if air, vacuum otherwise); 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.' ATMOSPHERE KEYWORDS: modelatm= (None) model-atmosphere instance vmicro= (2.) microturbulence (km/s) 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 MISCELLANEOUS KEYWORDS: dr= data release saveTurboInput= if set to a string, the input to and output from Turbospectrum will be saved as a tar.gz file with this name; can be a filename in the current directory or a full path OUTPUT: (wavelengths,cont-norm. spectrum, spectrum (nwave)) HISTORY: 2015-04-13 - Written - Bovy (IAS) """ # Get the spectral synthesis limits wmin= kwargs.pop('wmin',_WMIN_DEFAULT) wmax= kwargs.pop('wmax',_WMAX_DEFAULT) dw= kwargs.pop('dw',_DW_DEFAULT) babsma_wmin= kwargs.pop('babsma_wmin',wmin) babsma_wmax= kwargs.pop('babsma_wmax',wmax) if babsma_wmin > wmin or babsma_wmax < wmax: raise ValueError("Opacity wavelength range must encompass the synthesis range") if int(numpy.ceil((wmax-wmin)/dw > 150000)): raise ValueError('Too many wavelengths for Turbospectrum synthesis, reduce the wavelength step dw (to, e.g., 0.016)') costheta= kwargs.pop('costheta',1.) # Linelists Hlinelist= kwargs.pop('Hlinelist',None) linelist= kwargs.pop('linelist',None) # Parse isotopes isotopes= kwargs.pop('isotopes','solar') if isinstance(isotopes,str) and isotopes.lower() == 'solar': isotopes= {} elif isinstance(isotopes,str) and isotopes.lower() == 'arcturus': isotopes= {'6.012':'0.9375', '6.013':'0.0625'} elif not isinstance(isotopes,dict): raise ValueError("'isotopes=' input not understood, should be 'solar', 'arcturus', or a dictionary") # We will run in a subdirectory of the current directory tmpDir= tempfile.mkdtemp(dir=os.getcwd()) # Get the model atmosphere modelatm= kwargs.pop('modelatm',None) if not modelatm is 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') else: # 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) # Write atmosphere to file modelfilename= os.path.join(tmpDir,'atm.mod') modelatm.writeto(modelfilename,turbo=True) modeldirname= os.path.dirname(modelfilename) modelbasename= os.path.basename(modelfilename) # Get the name of the linelists if Hlinelist is None: if kwargs.get('air',True): Hlinelist= 'DATA/Hlinedata' # will be symlinked else: Hlinelist= appath.linelistPath('Hlinedata.vac', dr=kwargs.get('dr',None)) if not os.path.exists(Hlinelist) and not Hlinelist == 'DATA/Hlinedata': Hlinelist= appath.linelistPath(Hlinelist, dr=kwargs.get('dr',None)) if not os.path.exists(Hlinelist) and not kwargs.get('air',True): print("Hlinelist in vacuum linelist not found, using Turbospectrum's, which is in air...") Hlinelist= 'DATA/Hlinedata' # will be symlinked linelistfilenames= [Hlinelist] if isinstance(linelist,str): if os.path.exists(linelist): linelistfilenames.append(linelist) else: # Try finding the linelist atomlinelistfilename= appath.linelistPath(\ '%s.atoms' % linelist, dr=kwargs.get('dr',None)) moleclinelistfilename= appath.linelistPath(\ '%s.molec' % linelist, dr=kwargs.get('dr',None)) if os.path.exists(atomlinelistfilename) \ and os.path.exists(moleclinelistfilename): linelistfilenames.append(atomlinelistfilename) linelistfilenames.append(moleclinelistfilename) else: atomlinelistfilename= appath.linelistPath(\ 'turboatoms.%s' % linelist, dr=kwargs.get('dr',None)) moleclinelistfilename= appath.linelistPath(\ 'turbomolec.%s' % linelist, dr=kwargs.get('dr',None)) if not os.path.exists(atomlinelistfilename) \ and '201404080919' in atomlinelistfilename \ and kwargs.get('air',True): download.linelist(os.path.basename(atomlinelistfilename), dr=kwargs.get('dr',None)) if not os.path.exists(moleclinelistfilename) \ and '201404080919' in moleclinelistfilename \ and kwargs.get('air',True): download.linelist(os.path.basename(moleclinelistfilename), dr=kwargs.get('dr',None)) if os.path.exists(atomlinelistfilename) \ and os.path.exists(moleclinelistfilename): linelistfilenames.append(atomlinelistfilename) linelistfilenames.append(moleclinelistfilename) if linelist is None or len(linelistfilenames) == 1: os.remove(modelfilename) os.rmdir(tmpDir) raise ValueError('linelist= must be set (see documentation) and given linelist must exist (either as absolute path or in the linelist directory)') # Link the Turbospectrum DATA directory os.symlink(os.getenv('TURBODATA'),os.path.join(tmpDir,'DATA')) # Cut the linelist to the desired wavelength range, if necessary, # Skipped because it is unnecessary, but left in case we still want to # use it rmLinelists= False for ll, linelistfilename in enumerate(linelistfilenames[1:]): if not _CUTLINELIST: continue #SKIP if wmin == _WMIN_DEFAULT and wmax == _WMAX_DEFAULT: continue rmLinelists= True with open(os.path.join(tmpDir,'cutlines.awk'),'w') as awkfile: awkfile.write('($1>%.3f && $1<%.3f) || ( substr($1,1,1) == "' %(wmin-7.,wmax+7.) +"'"+'")\n') keeplines= open(os.path.join(tmpDir,'lines.tmp'),'w') stderr= open('/dev/null','w') try: subprocess.check_call(['awk','-f','cutlines.awk', linelistfilename], cwd=tmpDir,stdout=keeplines,stderr=stderr) keeplines.close() except subprocess.CalledProcessError: os.remove(os.path.join(tmpDir,'lines.tmp')) os.remove(os.path.join(tmpDir,'DATA')) raise RuntimeError("Removing unnecessary linelist entries failed ...") finally: os.remove(os.path.join(tmpDir,'cutlines.awk')) stderr.close() # Remove elements that aren't used altogether, adjust nlines with open(os.path.join(tmpDir,'lines.tmp'),'r') as infile: lines= infile.readlines() nl_list= [l[0] == "'" for l in lines] nl= numpy.array(nl_list,dtype='int') nl_list.append(True) nl_list.append(True) nlines= [numpy.sum(1-nl[ii:nl_list[ii+2:].index(True)+ii+2]) for ii in range(len(nl))] with open(os.path.join(tmpDir,os.path.basename(linelistfilename)), 'w') \ as outfile: for ii, line in enumerate(lines): if ii < len(lines)-2: if not lines[ii][0] == "'": outfile.write(lines[ii]) elif not (lines[ii+2][0] == "'" and lines[ii+1][0] == "'"): if lines[ii+1][0] == "'": # Adjust nlines outfile.write(lines[ii].replace(lines[ii].split()[-1]+'\n', '%i\n' % nlines[ii])) else: outfile.write(lines[ii]) else: if not lines[ii][0] == "'": outfile.write(lines[ii]) os.remove(os.path.join(tmpDir,'lines.tmp')) # cp the linelists to the temporary directory shutil.copy(linelistfilename,tmpDir) linelistfilenames[ll]= os.path.basename(linelistfilename) # Parse the abundances if len(args) == 0: #special case that there are *no* differences args= ([26,0.],) indiv_abu= {} for arg in args: indiv_abu[arg[0]]= arg[1]+solarabundances._ASPLUND05[arg[0]]\ +modelatm._metals if arg[0] == 6: indiv_abu[arg[0]]+= modelatm._cm if arg[0] in [8,10,12,14,16,18,20,22]: indiv_abu[arg[0]]+= modelatm._am modelopac= kwargs.get('modelopac',None) if modelopac is None or \ (isinstance(modelopac,str) and not os.path.exists(modelopac)): # Now write the script file for babsma_lu scriptfilename= os.path.join(tmpDir,'babsma.par') modelopacname= os.path.join(tmpDir,'mopac') _write_script(scriptfilename, babsma_wmin,babsma_wmax,dw, None, modelfilename, None, modelopacname, modelatm._metals, modelatm._am, indiv_abu, kwargs.get('vmicro',2.), None,None,None,bsyn=False) # Run babsma sys.stdout.write('\r'+"Running Turbospectrum babsma_lu ...\r") sys.stdout.flush() if kwargs.get('verbose',False): stdout= None stderr= None else: stdout= open('/dev/null', 'w') stderr= subprocess.STDOUT try: p= subprocess.Popen(['babsma_lu'], cwd=tmpDir, stdin=subprocess.PIPE, stdout=stdout, stderr=stderr) with open(os.path.join(tmpDir,'babsma.par'),'r') as parfile: for line in parfile: p.stdin.write(line) stdout, stderr= p.communicate() except subprocess.CalledProcessError: for linelistfilename in linelistfilenames: os.remove(linelistfilename,tmpDir) if os.path.exists(os.path.join(tmpDir,'DATA')): os.remove(os.path.join(tmpDir,'DATA')) raise RuntimeError("Running babsma_lu failed ...") finally: if os.path.exists(os.path.join(tmpDir,'babsma.par')) \ and not 'saveTurboInput' in kwargs: os.remove(os.path.join(tmpDir,'babsma.par')) sys.stdout.write('\r'+download._ERASESTR+'\r') sys.stdout.flush() if isinstance(modelopac,str): shutil.copy(modelopacname,modelopac) else: shutil.copy(modelopac,tmpDir) modelopacname= os.path.join(tmpDir,os.path.basename(modelopac)) # Now write the script file for bsyn_lu scriptfilename= os.path.join(tmpDir,'bsyn.par') outfilename= os.path.join(tmpDir,'bsyn.out') _write_script(scriptfilename, wmin,wmax,dw, costheta, modelfilename, None, modelopacname, modelatm._metals, modelatm._am, indiv_abu, None, outfilename, isotopes, linelistfilenames, bsyn=True) # Run bsyn sys.stdout.write('\r'+"Running Turbospectrum bsyn_lu ...\r") sys.stdout.flush() if kwargs.get('verbose',False): stdout= None stderr= None else: stdout= open('/dev/null', 'w') stderr= subprocess.STDOUT try: p= subprocess.Popen(['bsyn_lu'], cwd=tmpDir, stdin=subprocess.PIPE, stdout=stdout, stderr=stderr) with open(os.path.join(tmpDir,'bsyn.par'),'r') as parfile: for line in parfile: p.stdin.write(line) stdout, stderr= p.communicate() except subprocess.CalledProcessError: raise RuntimeError("Running bsyn_lu failed ...") finally: if 'saveTurboInput' in kwargs: turbosavefilename= kwargs['saveTurboInput'] if os.path.dirname(turbosavefilename) == '': turbosavefilename= os.path.join(os.getcwd(),turbosavefilename) try: subprocess.check_call(['tar','cvzf',turbosavefilename, os.path.basename(os.path.normpath(tmpDir))]) except subprocess.CalledProcessError: raise RuntimeError("Tar-zipping the Turbospectrum input and output failed; you will have to manually delete the temporary directory ...") # Need to remove babsma.par, bc not removed above if os.path.exists(os.path.join(tmpDir,'babsma.par')): os.remove(os.path.join(tmpDir,'babsma.par')) if os.path.exists(os.path.join(tmpDir,'bsyn.par')): os.remove(os.path.join(tmpDir,'bsyn.par')) if os.path.exists(modelopacname): os.remove(modelopacname) if os.path.exists(modelopacname+'.mod'): os.remove(modelopacname+'.mod') if os.path.exists(os.path.join(tmpDir,'DATA')): os.remove(os.path.join(tmpDir,'DATA')) if os.path.exists(os.path.join(tmpDir,'dummy-output.dat')): os.remove(os.path.join(tmpDir,'dummy-output.dat')) if os.path.exists(modelfilename): os.remove(modelfilename) if rmLinelists: for linelistfilename in linelistfilenames[1:]: os.remove(linelistfilename) sys.stdout.write('\r'+download._ERASESTR+'\r') sys.stdout.flush() # Now read the output turboOut= numpy.loadtxt(outfilename) # Clean up os.remove(outfilename) os.rmdir(tmpDir) # Return wav, cont-norm, full spectrum return (turboOut[:,0],turboOut[:,1],turboOut[:,2])