def test_params_named(): #Test that the named tags correspond to the correct values in param according to PARAM_SYMBOL assert numpy.all( numpy.fabs(_DATA['PARAM'][:, paramIndx('teff')] - _DATA['TEFF']) < 10. **-10.), 'PARAM TEFF does not correspond to tag TEFF' assert numpy.all( numpy.fabs(_DATA['PARAM'][:, paramIndx('logg')] - _DATA['LOGG']) < 10. **-10.), 'PARAM LOGG does not correspond to tag LOGG' cnanIndx = (True - numpy.isnan( numpy.sqrt(_DATA['PARAM_COV'][:, paramIndx('teff'), paramIndx('teff')]))) if numpy.sum(cnanIndx) > 0: assert numpy.all( numpy.fabs( numpy.sqrt(_DATA['PARAM_COV'][cnanIndx, paramIndx('teff'), paramIndx('teff')]) - _DATA['TEFF_ERR'][cnanIndx]) < 10.**-10. ), 'PARAM_COV TEFF does not correspond to tag TEFF_ERR' cnanIndx = (True - numpy.isnan( numpy.sqrt(_DATA['PARAM_COV'][:, paramIndx('logg'), paramIndx('logg')]))) if numpy.sum(cnanIndx) > 0: assert numpy.all( numpy.fabs( numpy.sqrt(_DATA['PARAM_COV'][cnanIndx, paramIndx('logg'), paramIndx('logg')]) - _DATA['LOGG_ERR'][cnanIndx]) < 10.**-10. ), 'PARAM_COV LOGG does not correspond to tag LOGG_ERR' return None
def test_elem_named(): #Test that the named tags for the elements correspond to the correct values in elem according to ELEM_SYMBOL elems= ['C','N','O','Mg','Si','S','Ca','Ti', 'Ni','Fe','Al','K','Na','V','Mn'] ferreOverM= ['C','N','O','Mg','Si','S','Ca','Ti'] for ii,elem in enumerate(elems): elemval= copy.copy(_DATA['ELEM'][:,elemIndx(elem)]) if elem in ferreOverM: elemval+= _DATA['FPARAM'][:,paramIndx('metals')] #BOVY: What about the following? goodIndx= _DATA['FPARAM'][:,paramIndx('metals')] != -9999. assert numpy.all(numpy.fabs(elemval[goodIndx]-_DATA[elem.upper()+'_H'][goodIndx]) < 10.**-10.), 'ELEM value for %s_H does not agree with named tag' % elem return None
def test_elem_named(): #Test that the named tags for the elements correspond to the correct values in elem according to ELEM_SYMBOL from apogee.tools import _ELEM_SYMBOL elems= [e.capitalize() for e in _ELEM_SYMBOL if e != 'ci' and e != 'tiii'] ferreOverM= ['C','N','O','Mg','Si','S','Ca','Ti'] for ii,elem in enumerate(elems): if elem == 'C' or elem == 'N' or elem == 'O': continue elemval= copy.copy(_DATA['ELEM'][:,elemIndx(elem)]) if elem in ferreOverM: elemval+= _DATA['FPARAM'][:,paramIndx('metals')] #BOVY: What about the following? goodIndx= (_DATA['FPARAM'][:,paramIndx('metals')] != -9999.)\ *(_DATA[elem.upper()+'_H'] != -9999.) assert numpy.all(numpy.fabs(elemval[goodIndx]-_DATA[elem.upper()+'_H'][goodIndx]) < 10.**-10.), 'ELEM value for %s_H does not agree with named tag' % elem return None
def test_elem_calib_outsiderange_dwarfs(): #Test that the elem calibration does not extend outside of the calibration #temperature range from apogee.tools import _ELEM_SYMBOL elems = [e.capitalize() for e in _ELEM_SYMBOL if e != 'ci' and e != 'tiii'] TeffMin = 3800. TeffMax = 7500. dwarfs= (_DATA['FPARAM'][:,paramIndx('logg')] >= (2./1300.\ *(_DATA['FPARAM'][:,paramIndx('teff')]-3500.)+2.))\ +(_DATA['FPARAM'][:,paramIndx('logg')] >= 4.)\ +(_DATA['FPARAM'][:,paramIndx('teff')] >= 7000.) for elem in elems: calibDiff= _DATA['FELEM'][:,elemIndx(elem)]\ -_DATA['ELEM'][:,elemIndx(elem)] #Only consider good stars for this element indx= ((_DATA['ASPCAPFLAG'] & 2**23) == 0)\ *(_DATA['FPARAM'][:,paramIndx('teff')] > -1000.)\ *dwarfs\ *(_DATA['FELEM'][:,elemIndx(elem)] > -1000.)\ *(_DATA['ELEM'][:,elemIndx(elem)] > -1000.) try: loTIndx = numpy.argmin( numpy.fabs(_DATA['FPARAM'][indx, paramIndx('teff')] - TeffMin)) except ValueError: pass else: assert numpy.all( numpy.fabs(calibDiff[indx][ _DATA['FPARAM'][indx, paramIndx('teff')] < TeffMin] - calibDiff[indx][loTIndx]) < 10.**-3. ), 'Calibration offset does not saturate below the minimum calibration temperature of %i for element %s' % ( TeffMin, elem) try: hiTIndx = numpy.argmin( numpy.fabs(_DATA['FPARAM'][indx, paramIndx('teff')] - TeffMax)) except ValueError: pass else: assert numpy.all( numpy.fabs(calibDiff[indx][ _DATA['FPARAM'][indx, paramIndx('teff')] > TeffMax] - calibDiff[indx][hiTIndx]) < 10.**-2. ), 'Calibration offset does not saturate above the maximum calibration temperature of %i for element %s' % ( TeffMax, elem) return None
def test_params_named(): #Test that the named tags correspond to the correct values in param according to PARAM_SYMBOL assert numpy.all(numpy.fabs(_DATA['PARAM'][:,paramIndx('teff')] -_DATA['TEFF']) < 10.**-10.), 'PARAM TEFF does not correspond to tag TEFF' assert numpy.all(numpy.fabs(_DATA['PARAM'][:,paramIndx('logg')] -_DATA['LOGG']) < 10.**-10.), 'PARAM LOGG does not correspond to tag LOGG' cnanIndx= (True-numpy.isnan(numpy.sqrt(_DATA['PARAM_COV'][:,paramIndx('teff'),paramIndx('teff')]))) if numpy.sum(cnanIndx) > 0: assert numpy.all(numpy.fabs(numpy.sqrt(_DATA['PARAM_COV'][cnanIndx,paramIndx('teff'),paramIndx('teff')]) -_DATA['TEFF_ERR'][cnanIndx]) < 10.**-10.), 'PARAM_COV TEFF does not correspond to tag TEFF_ERR' cnanIndx= (True-numpy.isnan(numpy.sqrt(_DATA['PARAM_COV'][:,paramIndx('logg'),paramIndx('logg')]))) if numpy.sum(cnanIndx) > 0: assert numpy.all(numpy.fabs(numpy.sqrt(_DATA['PARAM_COV'][cnanIndx,paramIndx('logg'),paramIndx('logg')]) -_DATA['LOGG_ERR'][cnanIndx]) < 10.**-10.), 'PARAM_COV LOGG does not correspond to tag LOGG_ERR' return None
def test_elem_calib_outsiderange(): #Test that the elem calibration does not extend outside of the calibration #temperature range elems= ['C','N','O','Mg','Si','S','Ca','Ti', 'Ni','Fe','Al','K','Na','V','Mn'] TeffMin= 3800. TeffMax= 5250. for elem in elems: calibDiff= _DATA['FELEM'][:,elemIndx(elem)]\ -_DATA['ELEM'][:,elemIndx(elem)] #Only consider good stars for this element indx= ((_DATA['ASPCAPFLAG'] & 2**23) == 0)\ *(_DATA['FPARAM'][:,paramIndx('teff')] > -1000.)\ *(_DATA['FELEM'][:,elemIndx(elem)] > -1000.)\ *(_DATA['ELEM'][:,elemIndx(elem)] > -1000.) loTIndx= numpy.argmin(numpy.fabs(_DATA['FPARAM'][indx, paramIndx('teff')] -TeffMin)) hiTIndx= numpy.argmin(numpy.fabs(_DATA['FPARAM'][indx, paramIndx('teff')] -TeffMax)) assert numpy.all(numpy.fabs(calibDiff[indx][_DATA['FPARAM'][indx,paramIndx('teff')] < TeffMin]-calibDiff[indx][loTIndx]) < 10.**-3.), 'Calibration offset does not saturate below the minimum calibration temperature of %i for element %s' % (TeffMin,elem) assert numpy.all(numpy.fabs(calibDiff[indx][_DATA['FPARAM'][indx,paramIndx('teff')] > TeffMax]-calibDiff[indx][hiTIndx]) < 10.**-2.), 'Calibration offset does not saturate above the maximum calibration temperature of %i for element %s' % (TeffMax,elem) return None
def test_elem_calib_outsiderange_dwarfs(): #Test that the elem calibration does not extend outside of the calibration #temperature range from apogee.tools import _ELEM_SYMBOL elems= [e.capitalize() for e in _ELEM_SYMBOL if e != 'ci' and e != 'tiii'] TeffMin= 3800. TeffMax= 7500. dwarfs= (_DATA['FPARAM'][:,paramIndx('logg')] >= (2./1300.\ *(_DATA['FPARAM'][:,paramIndx('teff')]-3500.)+2.))\ +(_DATA['FPARAM'][:,paramIndx('logg')] >= 4.)\ +(_DATA['FPARAM'][:,paramIndx('teff')] >= 7000.) for elem in elems: calibDiff= _DATA['FELEM'][:,elemIndx(elem)]\ -_DATA['ELEM'][:,elemIndx(elem)] #Only consider good stars for this element indx= ((_DATA['ASPCAPFLAG'] & 2**23) == 0)\ *(_DATA['FPARAM'][:,paramIndx('teff')] > -1000.)\ *dwarfs\ *(_DATA['FELEM'][:,elemIndx(elem)] > -1000.)\ *(_DATA['ELEM'][:,elemIndx(elem)] > -1000.) try: loTIndx= numpy.argmin(numpy.fabs(_DATA['FPARAM'][indx, paramIndx('teff')] -TeffMin)) except ValueError: pass else: assert numpy.all(numpy.fabs(calibDiff[indx][_DATA['FPARAM'][indx,paramIndx('teff')] < TeffMin]-calibDiff[indx][loTIndx]) < 10.**-3.), 'Calibration offset does not saturate below the minimum calibration temperature of %i for element %s' % (TeffMin,elem) try: hiTIndx= numpy.argmin(numpy.fabs(_DATA['FPARAM'][indx, paramIndx('teff')] -TeffMax)) except ValueError: pass else: assert numpy.all(numpy.fabs(calibDiff[indx][_DATA['FPARAM'][indx,paramIndx('teff')] > TeffMax]-calibDiff[indx][hiTIndx]) < 10.**-2.), 'Calibration offset does not saturate above the maximum calibration temperature of %i for element %s' % (TeffMax,elem) return None
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 make_rcsample(parser): options, args = parser.parse_args() savefilename = options.savefilename if savefilename is None: #Create savefilename if not given savefilename = os.path.join( appath._APOGEE_DATA, 'rcsample_' + appath._APOGEE_REDUX + '.fits') print("Saving to %s ..." % savefilename) #Read the base-sample data = apread.allStar(adddist=_ADDHAYDENDIST, rmdups=options.rmdups) #Remove a bunch of fields that we do not want to keep data = esutil.numpy_util.remove_fields(data, [ 'TARGET_ID', 'FILE', 'AK_WISE', 'SFD_EBV', 'SYNTHVHELIO_AVG', 'SYNTHVSCATTER', 'SYNTHVERR', 'SYNTHVERR_MED', 'RV_TEFF', 'RV_LOGG', 'RV_FEH', 'RV_ALPHA', 'RV_CARB', 'RV_CCFWHM', 'RV_AUTOFWHM', 'SYNTHSCATTER', 'STABLERV_CHI2', 'STABLERV_RCHI2', 'STABLERV_CHI2_PROB', 'CHI2_THRESHOLD', 'APSTAR_VERSION', 'ASPCAP_VERSION', 'RESULTS_VERSION', 'WASH_M', 'WASH_M_ERR', 'WASH_T2', 'WASH_T2_ERR', 'DDO51', 'DDO51_ERR', 'IRAC_3_6', 'IRAC_3_6_ERR', 'IRAC_4_5', 'IRAC_4_5_ERR', 'IRAC_5_8', 'IRAC_5_8_ERR', 'IRAC_8_0', 'IRAC_8_0_ERR', 'WISE_4_5', 'WISE_4_5_ERR', 'TARG_4_5', 'TARG_4_5_ERR', 'WASH_DDO51_GIANT_FLAG', 'WASH_DDO51_STAR_FLAG', 'REDUCTION_ID', 'SRC_H', 'PM_SRC' ]) # More if appath._APOGEE_REDUX.lower() == 'l33': data = esutil.numpy_util.remove_fields(data, [ 'GAIA_SOURCE_ID', 'GAIA_PARALLAX', 'GAIA_PARALLAX_ERROR', 'GAIA_PMRA', 'GAIA_PMRA_ERROR', 'GAIA_PMDEC', 'GAIA_PMDEC_ERROR', 'GAIA_PHOT_G_MEAN_MAG', 'GAIA_PHOT_BP_MEAN_MAG', 'GAIA_PHOT_RP_MEAN_MAG', 'GAIA_RADIAL_VELOCITY', 'GAIA_RADIAL_VELOCITY_ERROR', 'GAIA_R_EST', 'GAIA_R_LO', 'GAIA_R_HI', 'TEFF_SPEC', 'LOGG_SPEC' ]) if not appath._APOGEE_REDUX.lower() == 'current' \ and not 'l3' in appath._APOGEE_REDUX \ and int(appath._APOGEE_REDUX[1:]) < 500: data = esutil.numpy_util.remove_fields(data, ['ELEM']) #Select red-clump stars jk = data['J0'] - data['K0'] z = isodist.FEH2Z(data['METALS'], zsolar=0.017) if 'l31' in appath._APOGEE_REDUX: logg = data['LOGG'] elif 'l30' in appath._APOGEE_REDUX: logg = data['LOGG'] elif appath._APOGEE_REDUX.lower() == 'current' \ or int(appath._APOGEE_REDUX[1:]) > 600: if False: #Use my custom logg calibration that's correct for the RC logg = (1. - 0.042) * data['FPARAM'][:, paramIndx('logg')] - 0.213 lowloggindx = data['FPARAM'][:, paramIndx('logg')] < 1. logg[lowloggindx] = data['FPARAM'][lowloggindx, paramIndx('logg')] - 0.255 hiloggindx = data['FPARAM'][:, paramIndx('logg')] > 3.8 logg[hiloggindx] = data['FPARAM'][hiloggindx, paramIndx('logg')] - 0.3726 else: #Use my custom logg calibration that's correct on average logg = (1. + 0.03) * data['FPARAM'][:, paramIndx('logg')] - 0.37 lowloggindx = data['FPARAM'][:, paramIndx('logg')] < 1. logg[lowloggindx] = data['FPARAM'][lowloggindx, paramIndx('logg')] - 0.34 hiloggindx = data['FPARAM'][:, paramIndx('logg')] > 3.8 logg[hiloggindx] = data['FPARAM'][hiloggindx, paramIndx('logg')] - 0.256 else: logg = data['LOGG'] indx= (jk < 0.8)*(jk >= 0.5)\ *(z <= 0.06)\ *(z <= rcmodel.jkzcut(jk,upper=True))\ *(z >= rcmodel.jkzcut(jk))\ *(logg >= rcmodel.loggteffcut(data['TEFF'],z,upper=False))\ *(logg+0.1*('l31' in appath._APOGEE_REDUX or 'l33' in appath._APOGEE_REDUX) \ <= rcmodel.loggteffcut(data['TEFF'],z,upper=True)) data = data[indx] #Add more aggressive flag cut data = esutil.numpy_util.add_fields(data, [('ADDL_LOGG_CUT', numpy.int32)]) data['ADDL_LOGG_CUT'] = ( (data['TEFF'] - 4800.) / 1000. + 2.75) > data['LOGG'] if options.loggcut: data = data[data['ADDL_LOGG_CUT'] == 1] print("Making catalog of %i objects ..." % len(data)) #Add distances data = esutil.numpy_util.add_fields(data, [('RC_DIST', float), ('RC_DM', float), ('RC_GALR', float), ('RC_GALPHI', float), ('RC_GALZ', float)]) rcd = rcmodel.rcdist() jk = data['J0'] - data['K0'] z = isodist.FEH2Z(data['METALS'], zsolar=0.017) data['RC_DIST'] = rcd(jk, z, appmag=data['K0']) * options.distfac data['RC_DM'] = 5. * numpy.log10(data['RC_DIST']) + 10. XYZ = bovy_coords.lbd_to_XYZ(data['GLON'], data['GLAT'], data['RC_DIST'], degree=True) RphiZ = bovy_coords.XYZ_to_galcencyl(XYZ[:, 0], XYZ[:, 1], XYZ[:, 2], Xsun=8.15, Zsun=0.0208) R = RphiZ[:, 0] phi = RphiZ[:, 1] Z = RphiZ[:, 2] data['RC_GALR'] = R data['RC_GALPHI'] = phi data['RC_GALZ'] = Z #Save fitswrite(savefilename, data, clobber=True) # Add Tycho-2 matches if options.tyc2: data = esutil.numpy_util.add_fields(data, [('TYC2MATCH', numpy.int32), ('TYC1', numpy.int32), ('TYC2', numpy.int32), ('TYC3', numpy.int32)]) data['TYC2MATCH'] = 0 data['TYC1'] = -1 data['TYC2'] = -1 data['TYC3'] = -1 # Write positions posfilename = tempfile.mktemp('.csv', dir=os.getcwd()) resultfilename = tempfile.mktemp('.csv', dir=os.getcwd()) with open(posfilename, 'w') as csvfile: wr = csv.writer(csvfile, delimiter=',', quoting=csv.QUOTE_MINIMAL) wr.writerow(['RA', 'DEC']) for ii in range(len(data)): wr.writerow([data[ii]['RA'], data[ii]['DEC']]) # Send to CDS for matching result = open(resultfilename, 'w') try: subprocess.check_call([ 'curl', '-X', 'POST', '-F', 'request=xmatch', '-F', 'distMaxArcsec=2', '-F', 'RESPONSEFORMAT=csv', '-F', 'cat1=@%s' % os.path.basename(posfilename), '-F', 'colRA1=RA', '-F', 'colDec1=DEC', '-F', 'cat2=vizier:Tycho2', 'http://cdsxmatch.u-strasbg.fr/xmatch/api/v1/sync' ], stdout=result) except subprocess.CalledProcessError: os.remove(posfilename) if os.path.exists(resultfilename): result.close() os.remove(resultfilename) result.close() # Directly match on input RA ma = numpy.loadtxt(resultfilename, delimiter=',', skiprows=1, usecols=(1, 2, 7, 8, 9)) iis = numpy.arange(len(data)) mai = [iis[data['RA'] == ma[ii, 0]][0] for ii in range(len(ma))] data['TYC2MATCH'][mai] = 1 data['TYC1'][mai] = ma[:, 2] data['TYC2'][mai] = ma[:, 3] data['TYC3'][mai] = ma[:, 4] os.remove(posfilename) os.remove(resultfilename) if not options.nostat: #Determine statistical sample and add flag apo = apogee.select.apogeeSelect() statIndx = apo.determine_statistical(data) mainIndx = apread.mainIndx(data) data = esutil.numpy_util.add_fields(data, [('STAT', numpy.int32), ('INVSF', float)]) data['STAT'] = 0 data['STAT'][statIndx * mainIndx] = 1 for ii in range(len(data)): if (statIndx * mainIndx)[ii]: data['INVSF'][ii] = 1. / apo(data['LOCATION_ID'][ii], data['H'][ii]) else: data['INVSF'][ii] = -1. if options.nopm: fitswrite(savefilename, data, clobber=True) return None data = _add_proper_motions(data, savefilename) # Save fitswrite(savefilename, data, clobber=True) return None
def calibrate_logg_dr12(rgb=False): """Calibrate, using RC or RGV when rgb=True (the latter should reproduce the official calibration""" #Read the calibration file, APOKASC, and match them caldata= fitsio.read(os.path.join(os.getenv('APOGEE_DATA'),'cal_%s.fits' % os.getenv('APOGEE_REDUX')),1) apokasc= apread.apokasc() h=esutil.htm.HTM() m1,m2,d12 = h.match(caldata['RA'],caldata['DEC'], apokasc['RA'],apokasc['DEC'], 2./3600.,maxmatch=1) caldata= caldata[m1] apokasc= apokasc[m2] #Select a decent calibration sample # Use stars that are definitely RGB or RC seismoState= numpy.char.strip(apokasc['SEISMO EVOL']) if rgb: indx= (seismoState == 'RGB') \ + (seismoState == 'DWARF/SUBGIANT') else: indx= (seismoState == 'CLUMP') #Add low-logg giants of any kind indx+= (apokasc['KASC_RG_LOGG_SCALE_2'] < 2.) #rm bad data indx= (caldata['FPARAM'][:,paramIndx('logg')] > -1000.) indx*= (apokasc['KASC_RG_LOGG_SCALE_2'] > -1000.) #Apply limits indx*= (caldata['FPARAM'][:,paramIndx('logg')] > 1.)\ *(caldata['FPARAM'][:,paramIndx('logg')] < 3.8) print "Using %i stars to calibrate logg ..." % numpy.sum(indx) #Now fit the difference fitOut= numpy.polyfit(caldata['FPARAM'][indx,paramIndx('logg')], caldata['FPARAM'][indx,paramIndx('logg')]\ -apokasc['KASC_RG_LOGG_SCALE_2'][indx], 1) print fitOut if True: bovy_plot.bovy_print() bovy_plot.bovy_plot(caldata['FPARAM'][indx,paramIndx('logg')], caldata['FPARAM'][indx,paramIndx('logg')]\ -apokasc['KASC_RG_LOGG_SCALE_2'][indx], 'k.', xrange=[0.,5.],yrange=[-0.5,1.], xlabel=r'$\log g_{\mathrm{ASPCAP}}$', ylabel=r'$\log g_{\mathrm{ASPCAP}}-\log g_{\mathrm{seismo}}$') if not rgb: plotindx= (seismoState == 'RGB') \ + (seismoState == 'DWARF/SUBGIANT') else: plotindx= (seismoState == 'CLUMP') plotindx*= (caldata['FPARAM'][:,paramIndx('logg')] > -1000.) plotindx*= (apokasc['KASC_RG_LOGG_SCALE_2'] > -1000.) bovy_plot.bovy_plot(caldata['FPARAM'][plotindx,paramIndx('logg')], caldata['FPARAM'][plotindx,paramIndx('logg')]\ -apokasc['KASC_RG_LOGG_SCALE_2'][plotindx], '.',color='0.65',overplot=True) xs= numpy.linspace(1.,3.8,1001) bovy_plot.bovy_plot(xs,fitOut[0]*xs+fitOut[1],'k-',overplot=True) bovy_plot.bovy_plot(xs,-0.14*xs+0.588,'k--',overplot=True) print numpy.amax(numpy.fabs(fitOut[0]*xs+fitOut[1]-(-0.14*xs+0.588))) print numpy.amax(numpy.fabs(fitOut[0]*xs+fitOut[1]-(-0.14*xs+0.588))[xs < 2.]) bovy_plot.bovy_text(r'$\mathrm{diff} = %.3f \log g_{\mathrm{ASPCAP}} + %.3f$' % (fitOut[0],fitOut[1]), bottom_left=True,fontsize=14.) bovy_plot.bovy_end_print('/Users/bovy/Desktop/test.png') return None
def make_rcsample(parser): options, args = parser.parse_args() savefilename = options.savefilename if savefilename is None: #Create savefilename if not given savefilename = os.path.join( appath._APOGEE_DATA, 'rcsample_' + appath._APOGEE_REDUX + '.fits') print("Saving to %s ..." % savefilename) #Read the base-sample data = apread.allStar(adddist=_ADDHAYDENDIST, rmdups=options.rmdups) #Remove a bunch of fields that we do not want to keep data = esutil.numpy_util.remove_fields(data, [ 'TARGET_ID', 'FILE', 'AK_WISE', 'SFD_EBV', 'SYNTHVHELIO_AVG', 'SYNTHVSCATTER', 'SYNTHVERR', 'SYNTHVERR_MED', 'RV_TEFF', 'RV_LOGG', 'RV_FEH', 'RV_ALPHA', 'RV_CARB', 'RV_CCFWHM', 'RV_AUTOFWHM', 'SYNTHSCATTER', 'STABLERV_CHI2', 'STABLERV_RCHI2', 'STABLERV_CHI2_PROB', 'CHI2_THRESHOLD', 'APSTAR_VERSION', 'ASPCAP_VERSION', 'RESULTS_VERSION', 'WASH_M', 'WASH_M_ERR', 'WASH_T2', 'WASH_T2_ERR', 'DDO51', 'DDO51_ERR', 'IRAC_3_6', 'IRAC_3_6_ERR', 'IRAC_4_5', 'IRAC_4_5_ERR', 'IRAC_5_8', 'IRAC_5_8_ERR', 'IRAC_8_0', 'IRAC_8_0_ERR', 'WISE_4_5', 'WISE_4_5_ERR', 'TARG_4_5', 'TARG_4_5_ERR', 'WASH_DDO51_GIANT_FLAG', 'WASH_DDO51_STAR_FLAG', 'REDUCTION_ID', 'SRC_H', 'PM_SRC' ]) if not appath._APOGEE_REDUX.lower() == 'current' \ and not 'l30' in appath._APOGEE_REDUX \ and int(appath._APOGEE_REDUX[1:]) < 500: data = esutil.numpy_util.remove_fields(data, ['ELEM']) #Select red-clump stars jk = data['J0'] - data['K0'] z = isodist.FEH2Z(data['METALS'], zsolar=0.017) if 'l30' in appath._APOGEE_REDUX: logg = data['LOGG'] elif appath._APOGEE_REDUX.lower() == 'current' \ or int(appath._APOGEE_REDUX[1:]) > 600: from apogee.tools import paramIndx if False: #Use my custom logg calibration that's correct for the RC logg = (1. - 0.042) * data['FPARAM'][:, paramIndx('logg')] - 0.213 lowloggindx = data['FPARAM'][:, paramIndx('logg')] < 1. logg[lowloggindx] = data['FPARAM'][lowloggindx, paramIndx('logg')] - 0.255 hiloggindx = data['FPARAM'][:, paramIndx('logg')] > 3.8 logg[hiloggindx] = data['FPARAM'][hiloggindx, paramIndx('logg')] - 0.3726 else: #Use my custom logg calibration that's correct on average logg = (1. + 0.03) * data['FPARAM'][:, paramIndx('logg')] - 0.37 lowloggindx = data['FPARAM'][:, paramIndx('logg')] < 1. logg[lowloggindx] = data['FPARAM'][lowloggindx, paramIndx('logg')] - 0.34 hiloggindx = data['FPARAM'][:, paramIndx('logg')] > 3.8 logg[hiloggindx] = data['FPARAM'][hiloggindx, paramIndx('logg')] - 0.256 else: logg = data['LOGG'] indx= (jk < 0.8)*(jk >= 0.5)\ *(z <= 0.06)\ *(z <= rcmodel.jkzcut(jk,upper=True))\ *(z >= rcmodel.jkzcut(jk))\ *(logg >= rcmodel.loggteffcut(data['TEFF'],z,upper=False))\ *(logg <= rcmodel.loggteffcut(data['TEFF'],z,upper=True)) data = data[indx] #Add more aggressive flag cut data = esutil.numpy_util.add_fields(data, [('ADDL_LOGG_CUT', numpy.int32)]) data['ADDL_LOGG_CUT'] = ( (data['TEFF'] - 4800.) / 1000. + 2.75) > data['LOGG'] if options.loggcut: data = data[data['ADDL_LOGG_CUT'] == 1] print("Making catalog of %i objects ..." % len(data)) #Add distances data = esutil.numpy_util.add_fields(data, [('RC_DIST', float), ('RC_DM', float), ('RC_GALR', float), ('RC_GALPHI', float), ('RC_GALZ', float)]) rcd = rcmodel.rcdist() jk = data['J0'] - data['K0'] z = isodist.FEH2Z(data['METALS'], zsolar=0.017) data['RC_DIST'] = rcd(jk, z, appmag=data['K0']) * options.distfac data['RC_DM'] = 5. * numpy.log10(data['RC_DIST']) + 10. XYZ = bovy_coords.lbd_to_XYZ(data['GLON'], data['GLAT'], data['RC_DIST'], degree=True) R, phi, Z = bovy_coords.XYZ_to_galcencyl(XYZ[:, 0], XYZ[:, 1], XYZ[:, 2], Xsun=8., Zsun=0.025) data['RC_GALR'] = R data['RC_GALPHI'] = phi data['RC_GALZ'] = Z #Save fitsio.write(savefilename, data, clobber=True) # Add Tycho-2 matches if options.tyc2: data = esutil.numpy_util.add_fields(data, [('TYC2MATCH', numpy.int32), ('TYC1', numpy.int32), ('TYC2', numpy.int32), ('TYC3', numpy.int32)]) data['TYC2MATCH'] = 0 data['TYC1'] = -1 data['TYC2'] = -1 data['TYC3'] = -1 # Write positions posfilename = tempfile.mktemp('.csv', dir=os.getcwd()) resultfilename = tempfile.mktemp('.csv', dir=os.getcwd()) with open(posfilename, 'w') as csvfile: wr = csv.writer(csvfile, delimiter=',', quoting=csv.QUOTE_MINIMAL) wr.writerow(['RA', 'DEC']) for ii in range(len(data)): wr.writerow([data[ii]['RA'], data[ii]['DEC']]) # Send to CDS for matching result = open(resultfilename, 'w') try: subprocess.check_call([ 'curl', '-X', 'POST', '-F', 'request=xmatch', '-F', 'distMaxArcsec=2', '-F', 'RESPONSEFORMAT=csv', '-F', 'cat1=@%s' % os.path.basename(posfilename), '-F', 'colRA1=RA', '-F', 'colDec1=DEC', '-F', 'cat2=vizier:Tycho2', 'http://cdsxmatch.u-strasbg.fr/xmatch/api/v1/sync' ], stdout=result) except subprocess.CalledProcessError: os.remove(posfilename) if os.path.exists(resultfilename): result.close() os.remove(resultfilename) result.close() # Directly match on input RA ma = numpy.loadtxt(resultfilename, delimiter=',', skiprows=1, usecols=(1, 2, 7, 8, 9)) iis = numpy.arange(len(data)) mai = [iis[data['RA'] == ma[ii, 0]][0] for ii in range(len(ma))] data['TYC2MATCH'][mai] = 1 data['TYC1'][mai] = ma[:, 2] data['TYC2'][mai] = ma[:, 3] data['TYC3'][mai] = ma[:, 4] os.remove(posfilename) os.remove(resultfilename) if not options.nostat: #Determine statistical sample and add flag apo = apogee.select.apogeeSelect() statIndx = apo.determine_statistical(data) mainIndx = apread.mainIndx(data) data = esutil.numpy_util.add_fields(data, [('STAT', numpy.int32), ('INVSF', float)]) data['STAT'] = 0 data['STAT'][statIndx * mainIndx] = 1 for ii in range(len(data)): if (statIndx * mainIndx)[ii]: data['INVSF'][ii] = 1. / apo(data['LOCATION_ID'][ii], data['H'][ii]) else: data['INVSF'][ii] = -1. if options.nopm: fitsio.write(savefilename, data, clobber=True) return None #Get proper motions, in a somewhat roundabout way pmfile = savefilename.split('.')[0] + '_pms.fits' if os.path.exists(pmfile): pmdata = fitsio.read(pmfile, 1) else: pmdata = numpy.recarray( len(data), formats=['f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'i4'], names=[ 'RA', 'DEC', 'PMRA', 'PMDEC', 'PMRA_ERR', 'PMDEC_ERR', 'PMMATCH' ]) # Write positions, again ... posfilename = tempfile.mktemp('.csv', dir=os.getcwd()) resultfilename = tempfile.mktemp('.csv', dir=os.getcwd()) with open(posfilename, 'w') as csvfile: wr = csv.writer(csvfile, delimiter=',', quoting=csv.QUOTE_MINIMAL) wr.writerow(['RA', 'DEC']) for ii in range(len(data)): wr.writerow([data[ii]['RA'], data[ii]['DEC']]) # Send to CDS for matching result = open(resultfilename, 'w') try: subprocess.check_call([ 'curl', '-X', 'POST', '-F', 'request=xmatch', '-F', 'distMaxArcsec=4', '-F', 'RESPONSEFORMAT=csv', '-F', 'cat1=@%s' % os.path.basename(posfilename), '-F', 'colRA1=RA', '-F', 'colDec1=DEC', '-F', 'cat2=vizier:UCAC4', 'http://cdsxmatch.u-strasbg.fr/xmatch/api/v1/sync' ], stdout=result) except subprocess.CalledProcessError: os.remove(posfilename) if os.path.exists(resultfilename): result.close() os.remove(resultfilename) result.close() # Match back and only keep the closest one ma = numpy.loadtxt(resultfilename, delimiter=',', skiprows=1, converters={ 15: lambda s: float(s.strip() or -9999), 16: lambda s: float(s.strip() or -9999), 17: lambda s: float(s.strip() or -9999), 18: lambda s: float(s.strip() or -9999) }, usecols=(4, 5, 15, 16, 17, 18)) h = esutil.htm.HTM() m1, m2, d12 = h.match(data['RA'], data['DEC'], ma[:, 0], ma[:, 1], 4. / 3600., maxmatch=1) pmdata['PMMATCH'] = 0 pmdata['RA'] = data['RA'] pmdata['DEC'] = data['DEC'] pmdata['PMMATCH'][m1] = 1 pmdata['PMRA'][m1] = ma[m2, 2] pmdata['PMDEC'][m1] = ma[m2, 3] pmdata['PMRA_ERR'][m1] = ma[m2, 4] pmdata['PMDEC_ERR'][m1] = ma[m2, 5] pmdata['PMMATCH'][(pmdata['PMRA'] == -9999) \ +(pmdata['PMDEC'] == -9999) \ +(pmdata['PMRA_ERR'] == -9999) \ +(pmdata['PMDEC_ERR'] == -9999)]= 0 fitsio.write(pmfile, pmdata, clobber=True) #To make sure we're using the same format below pmdata = fitsio.read(pmfile, 1) os.remove(posfilename) os.remove(resultfilename) #Match proper motions try: #These already exist currently, but may not always exist data = esutil.numpy_util.remove_fields(data, ['PMRA', 'PMDEC']) except ValueError: pass data = esutil.numpy_util.add_fields(data, [('PMRA', numpy.float), ('PMDEC', numpy.float), ('PMRA_ERR', numpy.float), ('PMDEC_ERR', numpy.float), ('PMMATCH', numpy.int32)]) data['PMMATCH'] = 0 h = esutil.htm.HTM() m1, m2, d12 = h.match(pmdata['RA'], pmdata['DEC'], data['RA'], data['DEC'], 2. / 3600., maxmatch=1) data['PMRA'][m2] = pmdata['PMRA'][m1] data['PMDEC'][m2] = pmdata['PMDEC'][m1] data['PMRA_ERR'][m2] = pmdata['PMRA_ERR'][m1] data['PMDEC_ERR'][m2] = pmdata['PMDEC_ERR'][m1] data['PMMATCH'][m2] = pmdata['PMMATCH'][m1].astype(numpy.int32) pmindx = data['PMMATCH'] == 1 data['PMRA'][True - pmindx] = -9999.99 data['PMDEC'][True - pmindx] = -9999.99 data['PMRA_ERR'][True - pmindx] = -9999.99 data['PMDEC_ERR'][True - pmindx] = -9999.99 #Calculate Galactocentric velocities data = esutil.numpy_util.add_fields(data, [('GALVR', numpy.float), ('GALVT', numpy.float), ('GALVZ', numpy.float)]) lb = bovy_coords.radec_to_lb(data['RA'], data['DEC'], degree=True) XYZ = bovy_coords.lbd_to_XYZ(lb[:, 0], lb[:, 1], data['RC_DIST'], degree=True) pmllpmbb = bovy_coords.pmrapmdec_to_pmllpmbb(data['PMRA'], data['PMDEC'], data['RA'], data['DEC'], degree=True) vxvyvz = bovy_coords.vrpmllpmbb_to_vxvyvz(data['VHELIO_AVG'], pmllpmbb[:, 0], pmllpmbb[:, 1], lb[:, 0], lb[:, 1], data['RC_DIST'], degree=True) vR, vT, vZ = bovy_coords.vxvyvz_to_galcencyl( vxvyvz[:, 0], vxvyvz[:, 1], vxvyvz[:, 2], 8. - XYZ[:, 0], XYZ[:, 1], XYZ[:, 2] + 0.025, vsun=[-11.1, 30.24 * 8., 7.25]) #Assumes proper motion of Sgr A* and R0=8 kpc, zo= 25 pc data['GALVR'] = vR data['GALVT'] = vT data['GALVZ'] = vZ data['GALVR'][True - pmindx] = -9999.99 data['GALVT'][True - pmindx] = -9999.99 data['GALVZ'][True - pmindx] = -9999.99 #Get PPMXL proper motions, in a somewhat roundabout way pmfile = savefilename.split('.')[0] + '_pms_ppmxl.fits' if os.path.exists(pmfile): pmdata = fitsio.read(pmfile, 1) else: pmdata = numpy.recarray( len(data), formats=['f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'i4'], names=[ 'RA', 'DEC', 'PMRA', 'PMDEC', 'PMRA_ERR', 'PMDEC_ERR', 'PMMATCH' ]) # Write positions, again ... posfilename = tempfile.mktemp('.csv', dir=os.getcwd()) resultfilename = tempfile.mktemp('.csv', dir=os.getcwd()) with open(posfilename, 'w') as csvfile: wr = csv.writer(csvfile, delimiter=',', quoting=csv.QUOTE_MINIMAL) wr.writerow(['RA', 'DEC']) for ii in range(len(data)): wr.writerow([data[ii]['RA'], data[ii]['DEC']]) # Send to CDS for matching result = open(resultfilename, 'w') try: subprocess.check_call([ 'curl', '-X', 'POST', '-F', 'request=xmatch', '-F', 'distMaxArcsec=4', '-F', 'RESPONSEFORMAT=csv', '-F', 'cat1=@%s' % os.path.basename(posfilename), '-F', 'colRA1=RA', '-F', 'colDec1=DEC', '-F', 'cat2=vizier:PPMXL', 'http://cdsxmatch.u-strasbg.fr/xmatch/api/v1/sync' ], stdout=result) except subprocess.CalledProcessError: os.remove(posfilename) if os.path.exists(resultfilename): result.close() os.remove(resultfilename) result.close() # Match back and only keep the closest one ma = numpy.loadtxt(resultfilename, delimiter=',', skiprows=1, converters={ 15: lambda s: float(s.strip() or -9999), 16: lambda s: float(s.strip() or -9999), 17: lambda s: float(s.strip() or -9999), 18: lambda s: float(s.strip() or -9999) }, usecols=(4, 5, 15, 16, 19, 20)) h = esutil.htm.HTM() m1, m2, d12 = h.match(data['RA'], data['DEC'], ma[:, 0], ma[:, 1], 4. / 3600., maxmatch=1) pmdata['PMMATCH'] = 0 pmdata['RA'] = data['RA'] pmdata['DEC'] = data['DEC'] pmdata['PMMATCH'][m1] = 1 pmdata['PMRA'][m1] = ma[m2, 2] pmdata['PMDEC'][m1] = ma[m2, 3] pmdata['PMRA_ERR'][m1] = ma[m2, 4] pmdata['PMDEC_ERR'][m1] = ma[m2, 5] pmdata['PMMATCH'][(pmdata['PMRA'] == -9999) \ +(pmdata['PMDEC'] == -9999) \ +(pmdata['PMRA_ERR'] == -9999) \ +(pmdata['PMDEC_ERR'] == -9999)]= 0 fitsio.write(pmfile, pmdata, clobber=True) #To make sure we're using the same format below pmdata = fitsio.read(pmfile, 1) os.remove(posfilename) os.remove(resultfilename) #Match proper motions to ppmxl data = esutil.numpy_util.add_fields(data, [('PMRA_PPMXL', numpy.float), ('PMDEC_PPMXL', numpy.float), ('PMRA_ERR_PPMXL', numpy.float), ('PMDEC_ERR_PPMXL', numpy.float), ('PMMATCH_PPMXL', numpy.int32)]) data['PMMATCH_PPMXL'] = 0 h = esutil.htm.HTM() m1, m2, d12 = h.match(pmdata['RA'], pmdata['DEC'], data['RA'], data['DEC'], 2. / 3600., maxmatch=1) data['PMRA_PPMXL'][m2] = pmdata['PMRA'][m1] data['PMDEC_PPMXL'][m2] = pmdata['PMDEC'][m1] data['PMRA_ERR_PPMXL'][m2] = pmdata['PMRA_ERR'][m1] data['PMDEC_ERR_PPMXL'][m2] = pmdata['PMDEC_ERR'][m1] data['PMMATCH_PPMXL'][m2] = pmdata['PMMATCH'][m1].astype(numpy.int32) pmindx = data['PMMATCH_PPMXL'] == 1 data['PMRA_PPMXL'][True - pmindx] = -9999.99 data['PMDEC_PPMXL'][True - pmindx] = -9999.99 data['PMRA_ERR_PPMXL'][True - pmindx] = -9999.99 data['PMDEC_ERR_PPMXL'][True - pmindx] = -9999.99 #Calculate Galactocentric velocities data = esutil.numpy_util.add_fields(data, [('GALVR_PPMXL', numpy.float), ('GALVT_PPMXL', numpy.float), ('GALVZ_PPMXL', numpy.float)]) lb = bovy_coords.radec_to_lb(data['RA'], data['DEC'], degree=True) XYZ = bovy_coords.lbd_to_XYZ(lb[:, 0], lb[:, 1], data['RC_DIST'], degree=True) pmllpmbb = bovy_coords.pmrapmdec_to_pmllpmbb(data['PMRA_PPMXL'], data['PMDEC_PPMXL'], data['RA'], data['DEC'], degree=True) vxvyvz = bovy_coords.vrpmllpmbb_to_vxvyvz(data['VHELIO_AVG'], pmllpmbb[:, 0], pmllpmbb[:, 1], lb[:, 0], lb[:, 1], data['RC_DIST'], degree=True) vR, vT, vZ = bovy_coords.vxvyvz_to_galcencyl( vxvyvz[:, 0], vxvyvz[:, 1], vxvyvz[:, 2], 8. - XYZ[:, 0], XYZ[:, 1], XYZ[:, 2] + 0.025, vsun=[-11.1, 30.24 * 8., 7.25]) #Assumes proper motion of Sgr A* and R0=8 kpc, zo= 25 pc data['GALVR_PPMXL'] = vR data['GALVT_PPMXL'] = vT data['GALVZ_PPMXL'] = vZ data['GALVR_PPMXL'][True - pmindx] = -9999.99 data['GALVT_PPMXL'][True - pmindx] = -9999.99 data['GALVZ_PPMXL'][True - pmindx] = -9999.99 #Save fitsio.write(savefilename, data, clobber=True) return None
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 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 allStar(rmcommissioning=True, main=False, exclude_star_bad=False, exclude_star_warn=False, ak=True, akvers='targ', rmnovisits=False, adddist=False, distredux=None, rmdups=False, raw=False): """ NAME: allStar PURPOSE: read the allStar file INPUT: rmcommissioning= (default: True) if True, only use data obtained after commissioning main= (default: False) if True, only select stars in the main survey exclude_star_bad= (False) if True, remove stars with the STAR_BAD flag set in ASPCAPFLAG exclude_star_warn= (False) if True, remove stars with the STAR_WARN flag set in ASPCAPFLAG ak= (default: True) only use objects for which dereddened mags exist akvers= 'targ' (default) or 'wise': use target AK (AK_TARG) or AK derived from all-sky WISE (AK_WISE) rmnovisits= (False) if True, remove stars with no good visits (to go into the combined spectrum); shouldn't be necessary adddist= (default: False) add distances (DR10/11 Hayden distances, DR12 combined distances) distredux= (default: DR default) reduction on which the distances are based rmdups= (False) if True, remove duplicates (very slow) raw= (False) if True, just return the raw file, read w/ fitsio OUTPUT: allStar data HISTORY: 2013-09-06 - Written - Bovy (IAS) """ filePath = path.allStarPath() if not os.path.exists(filePath): download.allStar() #read allStar file data = fitsio.read(path.allStarPath()) if raw: return data #Remove duplicates, cache if rmdups: dupsFilename = path.allStarPath().replace('.fits', '-nodups.fits') if os.path.exists(dupsFilename): data = fitsio.read(dupsFilename) else: sys.stdout.write( '\r' + "Removing duplicates (might take a while) and caching the duplicate-free file ...\r" ) sys.stdout.flush() data = remove_duplicates(data) #Cache this file for subsequent use of rmdups fitsio.write(dupsFilename, data, clobber=True) sys.stdout.write('\r' + _ERASESTR + '\r') sys.stdout.flush() #Some cuts if rmcommissioning: indx = numpy.array( ['apogee.n.c'.encode('utf-8') in s for s in data['APSTAR_ID']]) indx += numpy.array( ['apogee.s.c'.encode('utf-8') in s for s in data['APSTAR_ID']]) data = data[True - indx] if rmnovisits: indx = numpy.array([s.strip() != '' for s in data['VISITS']]) data = data[indx] if main: indx = mainIndx(data) data = data[indx] if akvers.lower() == 'targ': aktag = 'AK_TARG' elif akvers.lower() == 'wise': aktag = 'AK_WISE' if ak: data = data[True - numpy.isnan(data[aktag])] data = data[(data[aktag] > -50.)] if exclude_star_bad: data = data[(data['ASPCAPFLAG'] & 2**23) == 0] if exclude_star_warn: data = data[(data['ASPCAPFLAG'] & 2**7) == 0] #Add dereddened J, H, and Ks aj = data[aktag] * 2.5 ah = data[aktag] * 1.55 if _ESUTIL_LOADED: data = esutil.numpy_util.add_fields(data, [('J0', float), ('H0', float), ('K0', float)]) data['J0'] = data['J'] - aj data['H0'] = data['H'] - ah data['K0'] = data['K'] - data[aktag] data['J0'][(data[aktag] <= -50.)] = -9999.9999 data['H0'][(data[aktag] <= -50.)] = -9999.9999 data['K0'][(data[aktag] <= -50.)] = -9999.9999 else: warnings.warn( "Extinction-corrected J,H,K not added because esutil is not installed", RuntimeWarning) #Add distances if adddist and _ESUTIL_LOADED: dist = fitsio.read(path.distPath(), 1) h = esutil.htm.HTM() m1, m2, d12 = h.match(dist['RA'], dist['DEC'], data['RA'], data['DEC'], 2. / 3600., maxmatch=1) data = data[m2] dist = dist[m1] distredux = path._redux_dr() if distredux.lower() == 'v302' or distredux.lower() == path._DR10REDUX: data = esutil.numpy_util.add_fields(data, [('DM05', float), ('DM16', float), ('DM50', float), ('DM84', float), ('DM95', float), ('DMPEAK', float), ('DMAVG', float), ('SIG_DM', float), ('DIST_SOL', float), ('SIG_DISTSOL', float)]) data['DM05'] = dist['DM05'] data['DM16'] = dist['DM16'] data['DM50'] = dist['DM50'] data['DM84'] = dist['DM84'] data['DM95'] = dist['DM95'] data['DMPEAK'] = dist['DMPEAK'] data['DMAVG'] = dist['DMAVG'] data['SIG_DM'] = dist['SIG_DM'] data['DIST_SOL'] = dist['DIST_SOL'] / 1000. data['SIG_DISTSOL'] = dist['SIG_DISTSOL'] / 1000. elif distredux.lower() == path._DR11REDUX: data = esutil.numpy_util.add_fields(data, [('DISO', float), ('DMASS', float), ('DISO_GAL', float), ('DMASS_GAL', float)]) data['DISO'] = dist['DISO'][:, 1] data['DMASS'] = dist['DMASS'][:, 1] data['DISO_GAL'] = dist['DISO_GAL'][:, 1] data['DMASS_GAL'] = dist['DMASS_GAL'][:, 1] elif distredux.lower() == path._DR12REDUX: data = esutil.numpy_util.add_fields( data, [('HIP_PLX', float), ('HIP_E_PLX', float), ('RC_DIST', float), ('APOKASC_DIST_DIRECT', float), ('BPG_DIST1_MEAN', float), ('HAYDEN_DIST_PEAK', float), ('SCHULTHEIS_DIST', float)]) data['HIP_PLX'] = dist['HIP_PLX'] data['HIP_E_PLX'] = dist['HIP_E_PLX'] data['RC_DIST'] = dist['RC_dist_pc'] data[ 'APOKASC_DIST_DIRECT'] = dist['APOKASC_dist_direct_pc'] / 1000. data['BPG_DIST1_MEAN'] = dist['BPG_dist1_mean'] data['HAYDEN_DIST_PEAK'] = 10.**(dist['HAYDEN_distmod_PEAK'] / 5. - 2.) data['SCHULTHEIS_DIST'] = dist['SCHULTHEIS_dist'] elif adddist: warnings.warn( "Distances not added because matching requires the uninstalled esutil module", RuntimeWarning) if _ESUTIL_LOADED and (path._APOGEE_REDUX.lower() == 'current' \ or 'l30' in path._APOGEE_REDUX.lower() \ or int(path._APOGEE_REDUX[1:]) > 600): data = esutil.numpy_util.add_fields(data, [('METALS', float), ('ALPHAFE', float)]) data['METALS'] = data['PARAM'][:, paramIndx('metals')] data['ALPHAFE'] = data['PARAM'][:, paramIndx('alpha')] return data
def fit(spec,specerr, teff=4750.,logg=2.5,metals=0.,am=0.,nm=0.,cm=0.,vm=None, fixteff=False,fixlogg=False,fixmetals=False,fixam=False,fixcm=False, fixnm=False,fixvm=False, lib='GK',pca=True,sixd=True,dr=None, offile=None, inter=3,f_format=1,f_access=None, errbar=1,indini=[1,1,1,2,2,3],init=1,initcannon=False, verbose=False): """ NAME: fit PURPOSE: Fit a model spectrum to a given data spectrum INPUT: Either: (1) location ID - single or list/array of location IDs APOGEE ID - single or list/array of APOGEE IDs; loads aspcapStar (2) spec - spectrum: can be (nwave) or (nspec,nwave) specerr - spectrum errors: can be (nwave) or (nspec,nwave) Input parameters (can be 1D arrays); only used when init=0 teff= (4750.) Effective temperature (K) logg= (2.5) log10 surface gravity / cm s^-2 metals= (0.) overall metallicity am= (0.) [alpha/M] nm= (0.) [N/M] cm= (0.) [C/M] vm= if using the 7D library, also specify the microturbulence Fit options: fixteff= (False) if True, fix teff at the input value fixlogg= (False) if True, fix logg at the input value fixmetals= (False) if True, fix metals at the input value fixam= (False) if True, fix am at the input value fixcm= (False) if True, fix cm at the input value fixnm= (False) if True, fix nm at the input value fixvm= (False) if True, fix vm at the input value (only if sixd is False) Library options: lib= ('GK') spectral library pca= (True) if True, use a PCA compressed library sixd= (True) if True, use the 6D library (w/o vm) dr= data release FERRE options: inter= (3) order of the interpolation errbar= (1) method for calculating the error bars indini= ([1,1,1,2,2,3]) how to initialize the search (int or array/list with ndim entries) init= (1) if 0, initialize the search at the parameters in the pfile f_format= (1) file format (0=ascii, 1=unf) f_access= (None) 0: load whole library, 1: use direct access (for small numbers of interpolations), None: automatically determine a good value (currently, 1) Other options: initcannon= (False) If True, initialize a single run by first running the Cannon using the default Cannon fit (sets init=0) Output options: offile= (None) if offile is set, the FERRE OFFILE is saved to this file, otherwise this file is removed verbose= (False) if True, run FERRE in verbose mode OUTPUT: best-fit parameters (nspec,nparams); in the same order as the FPARAM APOGEE data product HISTORY: 2015-01-29 - Written - Bovy (IAS) """ # Initialize using the Cannon? if initcannon: init= 0 # just run one fit using the Cannon as a starting guess initparams= cannon.polylabels(spec,specerr) if not fixteff: teff= initparams[:,0] if not fixlogg: logg= initparams[:,1] if not fixmetals: metals= initparams[:,2] if not fixam: am= initparams[:,3] if not fixcm: cm= numpy.zeros_like(initparams[:,0]) if not fixnm: nm= numpy.zeros_like(initparams[:,0]) # Make sure the Teff etc. have the right dimensionality if len(spec.shape) == 1: nspec= 1 else: nspec= spec.shape[0] if nspec > 1 and isinstance(teff,float): teff= teff*numpy.ones(nspec) if nspec > 1 and isinstance(logg,float): logg= logg*numpy.ones(nspec) if nspec > 1 and isinstance(metals,float): metals= metals*numpy.ones(nspec) if nspec > 1 and isinstance(am,float): am= am*numpy.ones(nspec) if nspec > 1 and isinstance(nm,float): nm= nm*numpy.ones(nspec) if nspec > 1 and isinstance(cm,float): cm= cm*numpy.ones(nspec) if nspec > 1 and not vm is None and isinstance(vm,float): vm= vm*numpy.ones(nspec) if dr is None: dr= appath._default_dr() # Fix any of the parameters? indv= [] indini= copy.copy(indini) # need to copy bc passed by reference if isinstance(indini,numpy.ndarray) and \ ((not sixd and fixvm) or fixcm or fixnm or fixam or fixmetals \ or fixlogg or fixteff): indini= list(indini) if not sixd and not fixvm: indv.append(1) elif not sixd: if isinstance(indini,list): indini[0]= -1 if not fixcm: indv.append(2-sixd) else: if isinstance(indini,list): indini[1-sixd]= -1 if not fixnm: indv.append(3-sixd) else: if isinstance(indini,list): indini[2-sixd]= -1 if not fixam: indv.append(4-sixd) else: if isinstance(indini,list): indini[3-sixd]= -1 if not fixmetals: indv.append(5-sixd) else: if isinstance(indini,list): indini[4-sixd]= -1 if not fixlogg: indv.append(6-sixd) else: if isinstance(indini,list): indini[5-sixd]= -1 if not fixteff: indv.append(7-sixd) else: if isinstance(indini,list): indini[6-sixd]= -1 if isinstance(indini,list): while -1 in indini: indini.remove(-1) # Setup temporary directory to run FERRE from tmpDir= tempfile.mkdtemp(dir='./') try: # First write the ipf file with the parameters write_ipf(tmpDir,teff,logg,metals,am,nm,cm,vm=vm) # Write the file with the fluxes and the flux errors write_ffile(tmpDir,spec,specerr=specerr) # Now write the input.nml file if f_access is None: f_access= 1 write_input_nml(tmpDir,'input.ipf','output.dat',ndim=7-sixd, nov=7-sixd-fixcm-fixnm-fixam-fixmetals\ -fixlogg-fixteff, indv=indv, synthfile=appath.ferreModelLibraryPath\ (lib=lib,pca=pca,sixd=sixd,dr=dr, header=True,unf=False), ffile='input.frd',erfile='input.err', opfile='output.opf', inter=inter,f_format=f_format, errbar=errbar,indini=indini,init=init, f_access=f_access) # Run FERRE run_ferre(tmpDir,verbose=verbose) # Read the output cols= (1,2,3,4,5,6) tmpOut= numpy.loadtxt(os.path.join(tmpDir,'output.opf'),usecols=cols) if len(spec.shape) == 1 or spec.shape[0] == 1: out= numpy.zeros((1,7)) tmpOut= numpy.reshape(tmpOut,(1,7-sixd)) else: out= numpy.zeros((nspec,7)) out[:,paramIndx('TEFF')]= tmpOut[:,-1] out[:,paramIndx('LOGG')]= tmpOut[:,-2] out[:,paramIndx('METALS')]= tmpOut[:,-3] out[:,paramIndx('ALPHA')]= tmpOut[:,-4] out[:,paramIndx('N')]= tmpOut[:,-5] out[:,paramIndx('C')]= tmpOut[:,-6] if sixd and dr == '12': out[:,paramIndx('LOG10VDOP')]=\ numpy.log10(2.478-0.325*out[:,paramIndx('LOGG')]) else: out[:,paramIndx('LOG10VDOP')]= tmpOut[:,0] if not offile is None: os.rename(os.path.join(tmpDir,'output.dat'),offile) finally: # Clean up if os.path.exists(os.path.join(tmpDir,'input.ipf')): os.remove(os.path.join(tmpDir,'input.ipf')) if os.path.exists(os.path.join(tmpDir,'input.frd')): os.remove(os.path.join(tmpDir,'input.frd')) if os.path.exists(os.path.join(tmpDir,'input.err')): os.remove(os.path.join(tmpDir,'input.err')) if os.path.exists(os.path.join(tmpDir,'input.nml')): os.remove(os.path.join(tmpDir,'input.nml')) if os.path.exists(os.path.join(tmpDir,'output.dat')): os.remove(os.path.join(tmpDir,'output.dat')) if os.path.exists(os.path.join(tmpDir,'output.opf')): os.remove(os.path.join(tmpDir,'output.opf')) os.rmdir(tmpDir) return out
def make_rcsample(parser): options,args= parser.parse_args() savefilename= options.savefilename if savefilename is None: #Create savefilename if not given savefilename= os.path.join(appath._APOGEE_DATA, 'rcsample_'+appath._APOGEE_REDUX+'.fits') print("Saving to %s ..." % savefilename) #Read the base-sample data= apread.allStar(adddist=_ADDHAYDENDIST,rmdups=options.rmdups) #Remove a bunch of fields that we do not want to keep data= esutil.numpy_util.remove_fields(data, ['TARGET_ID', 'FILE', 'AK_WISE', 'SFD_EBV', 'SYNTHVHELIO_AVG', 'SYNTHVSCATTER', 'SYNTHVERR', 'SYNTHVERR_MED', 'RV_TEFF', 'RV_LOGG', 'RV_FEH', 'RV_ALPHA', 'RV_CARB', 'RV_CCFWHM', 'RV_AUTOFWHM', 'SYNTHSCATTER', 'STABLERV_CHI2', 'STABLERV_RCHI2', 'STABLERV_CHI2_PROB', 'CHI2_THRESHOLD', 'APSTAR_VERSION', 'ASPCAP_VERSION', 'RESULTS_VERSION', 'WASH_M', 'WASH_M_ERR', 'WASH_T2', 'WASH_T2_ERR', 'DDO51', 'DDO51_ERR', 'IRAC_3_6', 'IRAC_3_6_ERR', 'IRAC_4_5', 'IRAC_4_5_ERR', 'IRAC_5_8', 'IRAC_5_8_ERR', 'IRAC_8_0', 'IRAC_8_0_ERR', 'WISE_4_5', 'WISE_4_5_ERR', 'TARG_4_5', 'TARG_4_5_ERR', 'WASH_DDO51_GIANT_FLAG', 'WASH_DDO51_STAR_FLAG', 'REDUCTION_ID', 'SRC_H', 'PM_SRC']) if not appath._APOGEE_REDUX.lower() == 'current' \ and not 'l30' in appath._APOGEE_REDUX \ and int(appath._APOGEE_REDUX[1:]) < 500: data= esutil.numpy_util.remove_fields(data, ['ELEM']) #Select red-clump stars jk= data['J0']-data['K0'] z= isodist.FEH2Z(data['METALS'],zsolar=0.017) if 'l30' in appath._APOGEE_REDUX: logg= data['LOGG'] elif appath._APOGEE_REDUX.lower() == 'current' \ or int(appath._APOGEE_REDUX[1:]) > 600: from apogee.tools import paramIndx if False: #Use my custom logg calibration that's correct for the RC logg= (1.-0.042)*data['FPARAM'][:,paramIndx('logg')]-0.213 lowloggindx= data['FPARAM'][:,paramIndx('logg')] < 1. logg[lowloggindx]= data['FPARAM'][lowloggindx,paramIndx('logg')]-0.255 hiloggindx= data['FPARAM'][:,paramIndx('logg')] > 3.8 logg[hiloggindx]= data['FPARAM'][hiloggindx,paramIndx('logg')]-0.3726 else: #Use my custom logg calibration that's correct on average logg= (1.+0.03)*data['FPARAM'][:,paramIndx('logg')]-0.37 lowloggindx= data['FPARAM'][:,paramIndx('logg')] < 1. logg[lowloggindx]= data['FPARAM'][lowloggindx,paramIndx('logg')]-0.34 hiloggindx= data['FPARAM'][:,paramIndx('logg')] > 3.8 logg[hiloggindx]= data['FPARAM'][hiloggindx,paramIndx('logg')]-0.256 else: logg= data['LOGG'] indx= (jk < 0.8)*(jk >= 0.5)\ *(z <= 0.06)\ *(z <= rcmodel.jkzcut(jk,upper=True))\ *(z >= rcmodel.jkzcut(jk))\ *(logg >= rcmodel.loggteffcut(data['TEFF'],z,upper=False))\ *(logg <= rcmodel.loggteffcut(data['TEFF'],z,upper=True)) data= data[indx] #Add more aggressive flag cut data= esutil.numpy_util.add_fields(data,[('ADDL_LOGG_CUT',numpy.int32)]) data['ADDL_LOGG_CUT']= ((data['TEFF']-4800.)/1000.+2.75) > data['LOGG'] if options.loggcut: data= data[data['ADDL_LOGG_CUT'] == 1] print("Making catalog of %i objects ..." % len(data)) #Add distances data= esutil.numpy_util.add_fields(data,[('RC_DIST', float), ('RC_DM', float), ('RC_GALR', float), ('RC_GALPHI', float), ('RC_GALZ', float)]) rcd= rcmodel.rcdist() jk= data['J0']-data['K0'] z= isodist.FEH2Z(data['METALS'],zsolar=0.017) data['RC_DIST']= rcd(jk,z,appmag=data['K0'])*options.distfac data['RC_DM']= 5.*numpy.log10(data['RC_DIST'])+10. XYZ= bovy_coords.lbd_to_XYZ(data['GLON'], data['GLAT'], data['RC_DIST'], degree=True) R,phi,Z= bovy_coords.XYZ_to_galcencyl(XYZ[:,0], XYZ[:,1], XYZ[:,2], Xsun=8.,Zsun=0.025) data['RC_GALR']= R data['RC_GALPHI']= phi data['RC_GALZ']= Z #Save fitsio.write(savefilename,data,clobber=True) # Add Tycho-2 matches if options.tyc2: data= esutil.numpy_util.add_fields(data,[('TYC2MATCH',numpy.int32), ('TYC1',numpy.int32), ('TYC2',numpy.int32), ('TYC3',numpy.int32)]) data['TYC2MATCH']= 0 data['TYC1']= -1 data['TYC2']= -1 data['TYC3']= -1 # Write positions posfilename= tempfile.mktemp('.csv',dir=os.getcwd()) resultfilename= tempfile.mktemp('.csv',dir=os.getcwd()) with open(posfilename,'w') as csvfile: wr= csv.writer(csvfile,delimiter=',',quoting=csv.QUOTE_MINIMAL) wr.writerow(['RA','DEC']) for ii in range(len(data)): wr.writerow([data[ii]['RA'],data[ii]['DEC']]) # Send to CDS for matching result= open(resultfilename,'w') try: subprocess.check_call(['curl', '-X','POST', '-F','request=xmatch', '-F','distMaxArcsec=2', '-F','RESPONSEFORMAT=csv', '-F','cat1=@%s' % os.path.basename(posfilename), '-F','colRA1=RA', '-F','colDec1=DEC', '-F','cat2=vizier:Tycho2', 'http://cdsxmatch.u-strasbg.fr/xmatch/api/v1/sync'], stdout=result) except subprocess.CalledProcessError: os.remove(posfilename) if os.path.exists(resultfilename): result.close() os.remove(resultfilename) result.close() # Directly match on input RA ma= numpy.loadtxt(resultfilename,delimiter=',',skiprows=1, usecols=(1,2,7,8,9)) iis= numpy.arange(len(data)) mai= [iis[data['RA'] == ma[ii,0]][0] for ii in range(len(ma))] data['TYC2MATCH'][mai]= 1 data['TYC1'][mai]= ma[:,2] data['TYC2'][mai]= ma[:,3] data['TYC3'][mai]= ma[:,4] os.remove(posfilename) os.remove(resultfilename) if not options.nostat: #Determine statistical sample and add flag apo= apogee.select.apogeeSelect() statIndx= apo.determine_statistical(data) mainIndx= apread.mainIndx(data) data= esutil.numpy_util.add_fields(data,[('STAT',numpy.int32), ('INVSF',float)]) data['STAT']= 0 data['STAT'][statIndx*mainIndx]= 1 for ii in range(len(data)): if (statIndx*mainIndx)[ii]: data['INVSF'][ii]= 1./apo(data['LOCATION_ID'][ii], data['H'][ii]) else: data['INVSF'][ii]= -1. if options.nopm: fitsio.write(savefilename,data,clobber=True) return None #Get proper motions, in a somewhat roundabout way pmfile= savefilename.split('.')[0]+'_pms.fits' if os.path.exists(pmfile): pmdata= fitsio.read(pmfile,1) else: pmdata= numpy.recarray(len(data), formats=['f8','f8','f8','f8','f8','f8','i4'], names=['RA','DEC','PMRA','PMDEC', 'PMRA_ERR','PMDEC_ERR','PMMATCH']) # Write positions, again ... posfilename= tempfile.mktemp('.csv',dir=os.getcwd()) resultfilename= tempfile.mktemp('.csv',dir=os.getcwd()) with open(posfilename,'w') as csvfile: wr= csv.writer(csvfile,delimiter=',',quoting=csv.QUOTE_MINIMAL) wr.writerow(['RA','DEC']) for ii in range(len(data)): wr.writerow([data[ii]['RA'],data[ii]['DEC']]) # Send to CDS for matching result= open(resultfilename,'w') try: subprocess.check_call(['curl', '-X','POST', '-F','request=xmatch', '-F','distMaxArcsec=4', '-F','RESPONSEFORMAT=csv', '-F','cat1=@%s' % os.path.basename(posfilename), '-F','colRA1=RA', '-F','colDec1=DEC', '-F','cat2=vizier:UCAC4', 'http://cdsxmatch.u-strasbg.fr/xmatch/api/v1/sync'], stdout=result) except subprocess.CalledProcessError: os.remove(posfilename) if os.path.exists(resultfilename): result.close() os.remove(resultfilename) result.close() # Match back and only keep the closest one ma= numpy.loadtxt(resultfilename,delimiter=',',skiprows=1, converters={15: lambda s: float(s.strip() or -9999), 16: lambda s: float(s.strip() or -9999), 17: lambda s: float(s.strip() or -9999), 18: lambda s: float(s.strip() or -9999)}, usecols=(4,5,15,16,17,18)) h=esutil.htm.HTM() m1,m2,d12 = h.match(data['RA'],data['DEC'], ma[:,0],ma[:,1],4./3600.,maxmatch=1) pmdata['PMMATCH']= 0 pmdata['RA']= data['RA'] pmdata['DEC']= data['DEC'] pmdata['PMMATCH'][m1]= 1 pmdata['PMRA'][m1]= ma[m2,2] pmdata['PMDEC'][m1]= ma[m2,3] pmdata['PMRA_ERR'][m1]= ma[m2,4] pmdata['PMDEC_ERR'][m1]= ma[m2,5] pmdata['PMMATCH'][(pmdata['PMRA'] == -9999) \ +(pmdata['PMDEC'] == -9999) \ +(pmdata['PMRA_ERR'] == -9999) \ +(pmdata['PMDEC_ERR'] == -9999)]= 0 fitsio.write(pmfile,pmdata,clobber=True) #To make sure we're using the same format below pmdata= fitsio.read(pmfile,1) os.remove(posfilename) os.remove(resultfilename) #Match proper motions try: #These already exist currently, but may not always exist data= esutil.numpy_util.remove_fields(data,['PMRA','PMDEC']) except ValueError: pass data= esutil.numpy_util.add_fields(data,[('PMRA', numpy.float), ('PMDEC', numpy.float), ('PMRA_ERR', numpy.float), ('PMDEC_ERR', numpy.float), ('PMMATCH',numpy.int32)]) data['PMMATCH']= 0 h=esutil.htm.HTM() m1,m2,d12 = h.match(pmdata['RA'],pmdata['DEC'], data['RA'],data['DEC'], 2./3600.,maxmatch=1) data['PMRA'][m2]= pmdata['PMRA'][m1] data['PMDEC'][m2]= pmdata['PMDEC'][m1] data['PMRA_ERR'][m2]= pmdata['PMRA_ERR'][m1] data['PMDEC_ERR'][m2]= pmdata['PMDEC_ERR'][m1] data['PMMATCH'][m2]= pmdata['PMMATCH'][m1].astype(numpy.int32) pmindx= data['PMMATCH'] == 1 data['PMRA'][True-pmindx]= -9999.99 data['PMDEC'][True-pmindx]= -9999.99 data['PMRA_ERR'][True-pmindx]= -9999.99 data['PMDEC_ERR'][True-pmindx]= -9999.99 #Calculate Galactocentric velocities data= esutil.numpy_util.add_fields(data,[('GALVR', numpy.float), ('GALVT', numpy.float), ('GALVZ', numpy.float)]) lb= bovy_coords.radec_to_lb(data['RA'],data['DEC'],degree=True) XYZ= bovy_coords.lbd_to_XYZ(lb[:,0],lb[:,1],data['RC_DIST'],degree=True) pmllpmbb= bovy_coords.pmrapmdec_to_pmllpmbb(data['PMRA'],data['PMDEC'], data['RA'],data['DEC'], degree=True) vxvyvz= bovy_coords.vrpmllpmbb_to_vxvyvz(data['VHELIO_AVG'], pmllpmbb[:,0], pmllpmbb[:,1], lb[:,0],lb[:,1],data['RC_DIST'], degree=True) vR, vT, vZ= bovy_coords.vxvyvz_to_galcencyl(vxvyvz[:,0], vxvyvz[:,1], vxvyvz[:,2], 8.-XYZ[:,0], XYZ[:,1], XYZ[:,2]+0.025, vsun=[-11.1,30.24*8.,7.25])#Assumes proper motion of Sgr A* and R0=8 kpc, zo= 25 pc data['GALVR']= vR data['GALVT']= vT data['GALVZ']= vZ data['GALVR'][True-pmindx]= -9999.99 data['GALVT'][True-pmindx]= -9999.99 data['GALVZ'][True-pmindx]= -9999.99 #Get PPMXL proper motions, in a somewhat roundabout way pmfile= savefilename.split('.')[0]+'_pms_ppmxl.fits' if os.path.exists(pmfile): pmdata= fitsio.read(pmfile,1) else: pmdata= numpy.recarray(len(data), formats=['f8','f8','f8','f8','f8','f8','i4'], names=['RA','DEC','PMRA','PMDEC', 'PMRA_ERR','PMDEC_ERR','PMMATCH']) # Write positions, again ... posfilename= tempfile.mktemp('.csv',dir=os.getcwd()) resultfilename= tempfile.mktemp('.csv',dir=os.getcwd()) with open(posfilename,'w') as csvfile: wr= csv.writer(csvfile,delimiter=',',quoting=csv.QUOTE_MINIMAL) wr.writerow(['RA','DEC']) for ii in range(len(data)): wr.writerow([data[ii]['RA'],data[ii]['DEC']]) # Send to CDS for matching result= open(resultfilename,'w') try: subprocess.check_call(['curl', '-X','POST', '-F','request=xmatch', '-F','distMaxArcsec=4', '-F','RESPONSEFORMAT=csv', '-F','cat1=@%s' % os.path.basename(posfilename), '-F','colRA1=RA', '-F','colDec1=DEC', '-F','cat2=vizier:PPMXL', 'http://cdsxmatch.u-strasbg.fr/xmatch/api/v1/sync'], stdout=result) except subprocess.CalledProcessError: os.remove(posfilename) if os.path.exists(resultfilename): result.close() os.remove(resultfilename) result.close() # Match back and only keep the closest one ma= numpy.loadtxt(resultfilename,delimiter=',',skiprows=1, converters={15: lambda s: float(s.strip() or -9999), 16: lambda s: float(s.strip() or -9999), 17: lambda s: float(s.strip() or -9999), 18: lambda s: float(s.strip() or -9999)}, usecols=(4,5,15,16,19,20)) h=esutil.htm.HTM() m1,m2,d12 = h.match(data['RA'],data['DEC'], ma[:,0],ma[:,1],4./3600.,maxmatch=1) pmdata['PMMATCH']= 0 pmdata['RA']= data['RA'] pmdata['DEC']= data['DEC'] pmdata['PMMATCH'][m1]= 1 pmdata['PMRA'][m1]= ma[m2,2] pmdata['PMDEC'][m1]= ma[m2,3] pmdata['PMRA_ERR'][m1]= ma[m2,4] pmdata['PMDEC_ERR'][m1]= ma[m2,5] pmdata['PMMATCH'][(pmdata['PMRA'] == -9999) \ +(pmdata['PMDEC'] == -9999) \ +(pmdata['PMRA_ERR'] == -9999) \ +(pmdata['PMDEC_ERR'] == -9999)]= 0 fitsio.write(pmfile,pmdata,clobber=True) #To make sure we're using the same format below pmdata= fitsio.read(pmfile,1) os.remove(posfilename) os.remove(resultfilename) #Match proper motions to ppmxl data= esutil.numpy_util.add_fields(data,[('PMRA_PPMXL', numpy.float), ('PMDEC_PPMXL', numpy.float), ('PMRA_ERR_PPMXL', numpy.float), ('PMDEC_ERR_PPMXL', numpy.float), ('PMMATCH_PPMXL',numpy.int32)]) data['PMMATCH_PPMXL']= 0 h=esutil.htm.HTM() m1,m2,d12 = h.match(pmdata['RA'],pmdata['DEC'], data['RA'],data['DEC'], 2./3600.,maxmatch=1) data['PMRA_PPMXL'][m2]= pmdata['PMRA'][m1] data['PMDEC_PPMXL'][m2]= pmdata['PMDEC'][m1] data['PMRA_ERR_PPMXL'][m2]= pmdata['PMRA_ERR'][m1] data['PMDEC_ERR_PPMXL'][m2]= pmdata['PMDEC_ERR'][m1] data['PMMATCH_PPMXL'][m2]= pmdata['PMMATCH'][m1].astype(numpy.int32) pmindx= data['PMMATCH_PPMXL'] == 1 data['PMRA_PPMXL'][True-pmindx]= -9999.99 data['PMDEC_PPMXL'][True-pmindx]= -9999.99 data['PMRA_ERR_PPMXL'][True-pmindx]= -9999.99 data['PMDEC_ERR_PPMXL'][True-pmindx]= -9999.99 #Calculate Galactocentric velocities data= esutil.numpy_util.add_fields(data,[('GALVR_PPMXL', numpy.float), ('GALVT_PPMXL', numpy.float), ('GALVZ_PPMXL', numpy.float)]) lb= bovy_coords.radec_to_lb(data['RA'],data['DEC'],degree=True) XYZ= bovy_coords.lbd_to_XYZ(lb[:,0],lb[:,1],data['RC_DIST'],degree=True) pmllpmbb= bovy_coords.pmrapmdec_to_pmllpmbb(data['PMRA_PPMXL'], data['PMDEC_PPMXL'], data['RA'],data['DEC'], degree=True) vxvyvz= bovy_coords.vrpmllpmbb_to_vxvyvz(data['VHELIO_AVG'], pmllpmbb[:,0], pmllpmbb[:,1], lb[:,0],lb[:,1],data['RC_DIST'], degree=True) vR, vT, vZ= bovy_coords.vxvyvz_to_galcencyl(vxvyvz[:,0], vxvyvz[:,1], vxvyvz[:,2], 8.-XYZ[:,0], XYZ[:,1], XYZ[:,2]+0.025, vsun=[-11.1,30.24*8.,7.25])#Assumes proper motion of Sgr A* and R0=8 kpc, zo= 25 pc data['GALVR_PPMXL']= vR data['GALVT_PPMXL']= vT data['GALVZ_PPMXL']= vZ data['GALVR_PPMXL'][True-pmindx]= -9999.99 data['GALVT_PPMXL'][True-pmindx]= -9999.99 data['GALVZ_PPMXL'][True-pmindx]= -9999.99 #Save fitsio.write(savefilename,data,clobber=True) return None
def allStar(rmcommissioning=True, main=False, exclude_star_bad=False, exclude_star_warn=False, ak=True, akvers='targ', rmnovisits=False, adddist=False, distredux=None, rmdups=False, raw=False): """ NAME: allStar PURPOSE: read the allStar file INPUT: rmcommissioning= (default: True) if True, only use data obtained after commissioning main= (default: False) if True, only select stars in the main survey exclude_star_bad= (False) if True, remove stars with the STAR_BAD flag set in ASPCAPFLAG exclude_star_warn= (False) if True, remove stars with the STAR_WARN flag set in ASPCAPFLAG ak= (default: True) only use objects for which dereddened mags exist akvers= 'targ' (default) or 'wise': use target AK (AK_TARG) or AK derived from all-sky WISE (AK_WISE) rmnovisits= (False) if True, remove stars with no good visits (to go into the combined spectrum); shouldn't be necessary adddist= (default: False) add distances (DR10/11 Hayden distances, DR12 combined distances) distredux= (default: DR default) reduction on which the distances are based rmdups= (False) if True, remove duplicates (very slow) raw= (False) if True, just return the raw file, read w/ fitsio OUTPUT: allStar data HISTORY: 2013-09-06 - Written - Bovy (IAS) """ filePath= path.allStarPath() if not os.path.exists(filePath): download.allStar() #read allStar file data= fitsio.read(path.allStarPath()) if raw: return data #Remove duplicates, cache if rmdups: dupsFilename= path.allStarPath().replace('.fits','-nodups.fits') if os.path.exists(dupsFilename): data= fitsio.read(dupsFilename) else: sys.stdout.write('\r'+"Removing duplicates (might take a while) and caching the duplicate-free file ...\r") sys.stdout.flush() data= remove_duplicates(data) #Cache this file for subsequent use of rmdups fitsio.write(dupsFilename,data,clobber=True) sys.stdout.write('\r'+_ERASESTR+'\r') sys.stdout.flush() #Some cuts if rmcommissioning: indx= numpy.array(['apogee.n.c' in s for s in data['APSTAR_ID']]) indx+= numpy.array(['apogee.s.c' in s for s in data['APSTAR_ID']]) data= data[True-indx] if rmnovisits: indx= numpy.array([s.strip() != '' for s in data['VISITS']]) data= data[indx] if main: indx= mainIndx(data) data= data[indx] if akvers.lower() == 'targ': aktag= 'AK_TARG' elif akvers.lower() == 'wise': aktag= 'AK_WISE' if ak: data= data[True-numpy.isnan(data[aktag])] data= data[(data[aktag] > -50.)] if exclude_star_bad: data= data[(data['ASPCAPFLAG'] & 2**23) == 0] if exclude_star_warn: data= data[(data['ASPCAPFLAG'] & 2**7) == 0] #Add dereddened J, H, and Ks aj= data[aktag]*2.5 ah= data[aktag]*1.55 data= esutil.numpy_util.add_fields(data,[('J0', float), ('H0', float), ('K0', float)]) data['J0']= data['J']-aj data['H0']= data['H']-ah data['K0']= data['K']-data[aktag] data['J0'][(data[aktag] <= -50.)]= -9999.9999 data['H0'][(data[aktag] <= -50.)]= -9999.9999 data['K0'][(data[aktag] <= -50.)]= -9999.9999 #Add distances if adddist: dist= fitsio.read(path.distPath(),1) h=esutil.htm.HTM() m1,m2,d12 = h.match(dist['RA'],dist['DEC'], data['RA'],data['DEC'], 2./3600.,maxmatch=1) data= data[m2] dist= dist[m1] distredux= path._redux_dr() if distredux.lower() == 'v302' or distredux.lower() == path._DR10REDUX: data= esutil.numpy_util.add_fields(data,[('DM05', float), ('DM16', float), ('DM50', float), ('DM84', float), ('DM95', float), ('DMPEAK', float), ('DMAVG', float), ('SIG_DM', float), ('DIST_SOL', float), ('SIG_DISTSOL', float)]) data['DM05']= dist['DM05'] data['DM16']= dist['DM16'] data['DM50']= dist['DM50'] data['DM84']= dist['DM84'] data['DM95']= dist['DM95'] data['DMPEAK']= dist['DMPEAK'] data['DMAVG']= dist['DMAVG'] data['SIG_DM']= dist['SIG_DM'] data['DIST_SOL']= dist['DIST_SOL']/1000. data['SIG_DISTSOL']= dist['SIG_DISTSOL']/1000. elif distredux.lower() == path._DR11REDUX: data= esutil.numpy_util.add_fields(data,[('DISO', float), ('DMASS', float), ('DISO_GAL', float), ('DMASS_GAL', float)]) data['DISO']= dist['DISO'][:,1] data['DMASS']= dist['DMASS'][:,1] data['DISO_GAL']= dist['DISO_GAL'][:,1] data['DMASS_GAL']= dist['DMASS_GAL'][:,1] elif distredux.lower() == path._DR12REDUX: data= esutil.numpy_util.add_fields(data,[('HIP_PLX', float), ('HIP_E_PLX', float), ('RC_DIST', float), ('APOKASC_DIST_DIRECT', float), ('BPG_DIST1_MEAN', float), ('HAYDEN_DIST_PEAK', float), ('SCHULTHEIS_DIST', float)]) data['HIP_PLX']= dist['HIP_PLX'] data['HIP_E_PLX']= dist['HIP_E_PLX'] data['RC_DIST']= dist['RC_dist_pc'] data['APOKASC_DIST_DIRECT']= dist['APOKASC_dist_direct_pc']/1000. data['BPG_DIST1_MEAN']= dist['BPG_dist1_mean'] data['HAYDEN_DIST_PEAK']= 10.**(dist['HAYDEN_distmod_PEAK']/5.-2.) data['SCHULTHEIS_DIST']= dist['SCHULTHEIS_dist'] if path._APOGEE_REDUX.lower() == 'current' \ or int(path._APOGEE_REDUX[1:]) > 600: data= esutil.numpy_util.add_fields(data,[('METALS', float), ('ALPHAFE', float)]) data['METALS']= data['PARAM'][:,paramIndx('metals')] data['ALPHAFE']= data['PARAM'][:,paramIndx('alpha')] return data
def allStar(rmcommissioning=True, main=False, exclude_star_bad=False, exclude_star_warn=False, ak=True, akvers='targ', rmnovisits=False, use_astroNN=False, use_astroNN_abundances=False, use_astroNN_distances=False, use_astroNN_ages=False, adddist=False, distredux=None, rmdups=False, raw=False, mjd=58104, xmatch=None,**kwargs): """ NAME: allStar PURPOSE: read the allStar file INPUT: rmcommissioning= (default: True) if True, only use data obtained after commissioning main= (default: False) if True, only select stars in the main survey exclude_star_bad= (False) if True, remove stars with the STAR_BAD flag set in ASPCAPFLAG exclude_star_warn= (False) if True, remove stars with the STAR_WARN flag set in ASPCAPFLAG ak= (default: True) only use objects for which dereddened mags exist akvers= 'targ' (default) or 'wise': use target AK (AK_TARG) or AK derived from all-sky WISE (AK_WISE) rmnovisits= (False) if True, remove stars with no good visits (to go into the combined spectrum); shouldn't be necessary use_astroNN= (False) if True, swap in astroNN (Leung & Bovy 2019a) parameters (get placed in, e.g., TEFF and TEFF_ERR), astroNN distances (Leung & Bovy 2019b), and astroNN ages (Mackereth, Bovy, Leung, et al. (2019) use_astroNN_abundances= (False) only swap in astroNN parameters and abundances, not distances and ages use_astroNN_distances= (False) only swap in astroNN distances, not parameters and abundances and ages use_astroNN_ages= (False) only swap in astroNN ages, not parameters and abundances and distances adddist= (default: False) add distances (DR10/11 Hayden distances, DR12 combined distances) distredux= (default: DR default) reduction on which the distances are based rmdups= (False) if True, remove duplicates (very slow) raw= (False) if True, just return the raw file, read w/ fitsio mjd= (58104) MJD of version for monthly internal pipeline runs xmatch= (None) uses gaia_tools.xmatch.cds to x-match to an external catalog (eg., Gaia DR2 for xmatch='vizier:I/345/gaia2') and caches the result for re-use; requires jobovy/gaia_tools +gaia_tools.xmatch.cds keywords OUTPUT: allStar data[,xmatched table] HISTORY: 2013-09-06 - Written - Bovy (IAS) 2018-01-22 - Edited for new monthly pipeline runs - Bovy (UofT) 2018-05-09 - Add xmatch - Bovy (UofT) 2018-10-20 - Add use_astroNN option - Bovy (UofT) 2018-02-15 - Add astroNN distances and corresponding options - Bovy (UofT) 2018-02-16 - Add astroNN ages and corresponding options - Bovy (UofT) """ filePath= path.allStarPath(mjd=mjd) if not os.path.exists(filePath): download.allStar(mjd=mjd) #read allStar file data= fitsread(path.allStarPath(mjd=mjd)) #Add astroNN? astroNN file matched line-by-line to allStar, so match here # [ages file not matched line-by-line] if use_astroNN or kwargs.get('astroNN',False) or use_astroNN_abundances: _warn_astroNN_abundances() astroNNdata= astroNN() data= _swap_in_astroNN(data,astroNNdata) if use_astroNN or kwargs.get('astroNN',False) or use_astroNN_distances: _warn_astroNN_distances() astroNNdata= astroNNDistances() data= _add_astroNN_distances(data,astroNNdata) if use_astroNN or kwargs.get('astroNN',False) or use_astroNN_ages: _warn_astroNN_ages() astroNNdata= astroNNAges() data= _add_astroNN_ages(data,astroNNdata) if raw: return data #Remove duplicates, cache if rmdups: dupsFilename= path.allStarPath(mjd=mjd).replace('.fits','-nodups.fits') if os.path.exists(dupsFilename): data= fitsread(dupsFilename) else: sys.stdout.write('\r'+"Removing duplicates (might take a while) and caching the duplicate-free file ...\r") sys.stdout.flush() data= remove_duplicates(data) #Cache this file for subsequent use of rmdups fitswrite(dupsFilename,data,clobber=True) sys.stdout.write('\r'+_ERASESTR+'\r') sys.stdout.flush() if not xmatch is None: from gaia_tools.load import _xmatch_cds if rmdups: matchFilePath= dupsFilename else: matchFilePath= filePath if use_astroNN_ages: matchFilePath= matchFilePath.replace('rc-','rc-astroNN-ages-') ma,mai= _xmatch_cds(data,xmatch,filePath,**kwargs) data= data[mai] #Some cuts if rmcommissioning: try: indx= numpy.array(['apogee.n.c'.encode('utf-8') in s for s in data['APSTAR_ID']]) indx+= numpy.array(['apogee.s.c'.encode('utf-8') in s for s in data['APSTAR_ID']]) except TypeError: indx= numpy.array(['apogee.n.c' in s for s in data['APSTAR_ID']]) indx+= numpy.array(['apogee.s.c' in s for s in data['APSTAR_ID']]) data= data[True^indx] if not xmatch is None: ma= ma[True^indx] if rmnovisits: indx= numpy.array([s.strip() != '' for s in data['VISITS']]) data= data[indx] if not xmatch is None: ma= ma[indx] if main: indx= mainIndx(data) data= data[indx] if not xmatch is None: ma= ma[indx] if akvers.lower() == 'targ': aktag= 'AK_TARG' elif akvers.lower() == 'wise': aktag= 'AK_WISE' if ak: if not xmatch is None: ma= ma[True^numpy.isnan(data[aktag])] data= data[True^numpy.isnan(data[aktag])] if not xmatch is None: ma= ma[(data[aktag] > -50.)] data= data[(data[aktag] > -50.)] if exclude_star_bad: if not xmatch is None: ma= ma[(data['ASPCAPFLAG'] & 2**23) == 0] data= data[(data['ASPCAPFLAG'] & 2**23) == 0] if exclude_star_warn: if not xmatch is None: ma= ma[(data['ASPCAPFLAG'] & 2**7) == 0] data= data[(data['ASPCAPFLAG'] & 2**7) == 0] #Add dereddened J, H, and Ks aj= data[aktag]*2.5 ah= data[aktag]*1.55 if _ESUTIL_LOADED: data= esutil.numpy_util.add_fields(data,[('J0', float), ('H0', float), ('K0', float)]) data['J0']= data['J']-aj data['H0']= data['H']-ah data['K0']= data['K']-data[aktag] data['J0'][(data[aktag] <= -50.)]= -9999.9999 data['H0'][(data[aktag] <= -50.)]= -9999.9999 data['K0'][(data[aktag] <= -50.)]= -9999.9999 else: warnings.warn("Extinction-corrected J,H,K not added because esutil is not installed",RuntimeWarning) #Add distances if adddist and _ESUTIL_LOADED: dist= fitsread(path.distPath(),1) h=esutil.htm.HTM() m1,m2,d12 = h.match(dist['RA'],dist['DEC'], data['RA'],data['DEC'], 2./3600.,maxmatch=1) data= data[m2] if not xmatch is None: ma= ma[m2] dist= dist[m1] distredux= path._redux_dr() if distredux.lower() == 'v302' or distredux.lower() == path._DR10REDUX: data= esutil.numpy_util.add_fields(data,[('DM05', float), ('DM16', float), ('DM50', float), ('DM84', float), ('DM95', float), ('DMPEAK', float), ('DMAVG', float), ('SIG_DM', float), ('DIST_SOL', float), ('SIG_DISTSOL', float)]) data['DM05']= dist['DM05'] data['DM16']= dist['DM16'] data['DM50']= dist['DM50'] data['DM84']= dist['DM84'] data['DM95']= dist['DM95'] data['DMPEAK']= dist['DMPEAK'] data['DMAVG']= dist['DMAVG'] data['SIG_DM']= dist['SIG_DM'] data['DIST_SOL']= dist['DIST_SOL']/1000. data['SIG_DISTSOL']= dist['SIG_DISTSOL']/1000. elif distredux.lower() == path._DR11REDUX: data= esutil.numpy_util.add_fields(data,[('DISO', float), ('DMASS', float), ('DISO_GAL', float), ('DMASS_GAL', float)]) data['DISO']= dist['DISO'][:,1] data['DMASS']= dist['DMASS'][:,1] data['DISO_GAL']= dist['DISO_GAL'][:,1] data['DMASS_GAL']= dist['DMASS_GAL'][:,1] elif distredux.lower() == path._DR12REDUX: data= esutil.numpy_util.add_fields(data,[('HIP_PLX', float), ('HIP_E_PLX', float), ('RC_DIST', float), ('APOKASC_DIST_DIRECT', float), ('BPG_DIST1_MEAN', float), ('HAYDEN_DIST_PEAK', float), ('SCHULTHEIS_DIST', float)]) data['HIP_PLX']= dist['HIP_PLX'] data['HIP_E_PLX']= dist['HIP_E_PLX'] data['RC_DIST']= dist['RC_dist_pc'] data['APOKASC_DIST_DIRECT']= dist['APOKASC_dist_direct_pc']/1000. data['BPG_DIST1_MEAN']= dist['BPG_dist1_mean'] data['HAYDEN_DIST_PEAK']= 10.**(dist['HAYDEN_distmod_PEAK']/5.-2.) data['SCHULTHEIS_DIST']= dist['SCHULTHEIS_dist'] elif adddist: warnings.warn("Distances not added because matching requires the uninstalled esutil module",RuntimeWarning) if _ESUTIL_LOADED and (path._APOGEE_REDUX.lower() == 'current' \ or 'l3' in path._APOGEE_REDUX.lower() \ or int(path._APOGEE_REDUX[1:]) > 600): data= esutil.numpy_util.add_fields(data,[('METALS', float), ('ALPHAFE', float)]) data['METALS']= data['PARAM'][:,paramIndx('metals')] data['ALPHAFE']= data['PARAM'][:,paramIndx('alpha')] if not xmatch is None: return (data,ma) else: return data
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 plot_afe_spectra(savename,plotname): # Load the data data= define_rcsample.get_rcsample() data= data[data['SNR'] > 200.] fehindx= (data['FE_H'] <= -0.35)*(data['FE_H'] > -0.45) fehdata= data[fehindx] # First compute the residuals and do the EM-PCA smoothing if not os.path.exists(savename): nspec= len(fehdata) spec= numpy.zeros((nspec,7214)) specerr= numpy.zeros((nspec,7214)) for ii in range(nspec): sys.stdout.write('\r'+"Loading spectrum %i / %i ...\r" % (ii+1,nspec)) sys.stdout.flush() spec[ii]= apread.aspcapStar(fehdata['LOCATION_ID'][ii], fehdata['APOGEE_ID'][ii], ext=1,header=False,aspcapWavegrid=True) specerr[ii]= apread.aspcapStar(fehdata['LOCATION_ID'][ii], fehdata['APOGEE_ID'][ii], ext=2,header=False, aspcapWavegrid=True) teffs= fehdata['FPARAM'][:,paramIndx('teff')] loggs= fehdata['FPARAM'][:,paramIndx('logg')] metals= fehdata[define_rcsample._FEHTAG] cf, s, r= apcannon.quadfit(spec,specerr, teffs-4800.,loggs-2.85,metals+0.3, return_residuals=True) pr= numpy.zeros_like(r) # Deal w/ bad data _MAXERR= 0.02 npca= 8 pca_input= r pca_weights= (1./specerr**2.) pca_weights[pca_weights < 1./_MAXERR**2.]= 0. nanIndx= numpy.isnan(pca_input) + numpy.isnan(pca_weights) pca_weights[nanIndx]= 0. pca_input[nanIndx]= 0. # Run EM-PCA m= empca.empca(pca_input,pca_weights,nvec=npca,niter=25)#,silent=False) for jj in range(nspec): for kk in range(npca): pr[jj]+= m.coeff[jj,kk]*m.eigvec[kk] save_pickles(savename,pr,r,cf) else: with open(savename,'rb') as savefile: pr= pickle.load(savefile) # Now plot the various elements colormap= cm.seismic colorFunc= lambda afe: afe/0.25 widths= [3.5,2.] yranges= [[-0.05,0.02],[-0.03,0.01]] for ee, elem in enumerate(['S','Ca1']): for ii in range(5): tindx= (fehdata[define_rcsample._AFETAG] > ii*0.05-0.025)\ *(fehdata[define_rcsample._AFETAG] <= (ii+1)*0.05-0.025) args= (apstack.median(pr[tindx][:12]),elem,) kwargs= {'markLines':ii==4, 'yrange':yranges[ee], 'ylabel':'', 'cleanZero':False, 'zorder':int(numpy.floor(numpy.random.uniform()*5)), 'color':colormap(colorFunc(ii*0.05)), 'overplot':ii>0, 'fig_width':widths[ee]} if ii>0: kwargs.pop('fig_width') splot.windows(*args,**kwargs) bovy_plot.bovy_end_print(plotname.replace('ELEM', elem.lower().capitalize())) # Also do Mg for ii in range(5): tindx= (fehdata[define_rcsample._AFETAG] > ii*0.05-0.025)\ *(fehdata[define_rcsample._AFETAG] <= (ii+1)*0.05-0.025) args= (apstack.median(pr[tindx][:12]),) kwargs={'startindxs':[3012,3120,3990], 'endindxs':[3083,3158,4012], 'yrange':[-0.05,0.02], 'ylabel':'', 'cleanZero':False, '_markwav':[15745.017,15753.189,15770.055,15958.836], 'zorder':int(numpy.floor(numpy.random.uniform()*5)), 'color':colormap(colorFunc(ii*0.05)), 'overplot':ii>0, 'fig_width':4.5, 'markLines':True} if ii>0: kwargs.pop('fig_width') if ii != 4: kwargs.pop('_markwav') kwargs.pop('markLines') kwargs['_startendskip']= 0 kwargs['_noxticks']= True kwargs['_labelwav']= True splot.waveregions(*args,**kwargs) bovy_plot.bovy_text(r'$\mathrm{Mg}$', top_left=True,fontsize=10,backgroundcolor='w') bovy_plot.bovy_end_print(plotname.replace('ELEM','Mg')) # Also do Si for ii in range(5): tindx= (fehdata[define_rcsample._AFETAG] > ii*0.05-0.025)\ *(fehdata[define_rcsample._AFETAG] <= (ii+1)*0.05-0.025) args= (apstack.median(pr[tindx][:12]),) kwargs={'startindxs':[4469, 4624,5171, 7205, 7843], 'endindxs':[4488, 4644,5182, 7243, 7871], 'yrange':[-0.05,0.02], 'ylabel':'', 'cleanZero':False, '_markwav':apwindow.lines('Si'), 'zorder':int(numpy.floor(numpy.random.uniform()*5)), 'color':colormap(colorFunc(ii*0.05)), 'overplot':ii>0, 'fig_width':6., 'markLines':True} if ii>0: kwargs.pop('fig_width') if ii != 4: kwargs.pop('_markwav') kwargs.pop('markLines') kwargs['_startendskip']= 0 kwargs['_noxticks']= True kwargs['_labelwav']= True splot.waveregions(*args,**kwargs) bovy_plot.bovy_text(r'$\mathrm{Si}$', top_left=True,fontsize=10,backgroundcolor='w') bovy_plot.bovy_end_print(plotname.replace('ELEM','Si2')) # Also do Oxygen for ii in range(5): tindx= (fehdata[define_rcsample._AFETAG] > ii*0.05-0.025)\ *(fehdata[define_rcsample._AFETAG] <= (ii+1)*0.05-0.025) args= (apstack.median(pr[tindx][:12]),) kwargs={'startlams':[15558,16242,16536,16720], 'endlams':[15566,16250,16544,16728], 'yrange':[-0.05,0.02], 'ylabel':'', 'cleanZero':False, '_markwav':[15562,16246,16539,16723.5], 'zorder':int(numpy.floor(numpy.random.uniform()*5)), 'color':colormap(colorFunc(ii*0.05)), 'overplot':ii>0, 'fig_width':5., 'markLines':True} if ii>0: kwargs.pop('fig_width') if ii != 4: kwargs.pop('_markwav') kwargs.pop('markLines') kwargs['_startendskip']= 0 kwargs['_noxticks']= True kwargs['_labelwav']= True splot.waveregions(*args,**kwargs) bovy_plot.bovy_text(r'$\mathrm{O}$', top_left=True,fontsize=10,backgroundcolor='w') bovy_plot.bovy_end_print(plotname.replace('ELEM','O')) # Also do Ti for ii in range(5): tindx= (fehdata[define_rcsample._AFETAG] > ii*0.05-0.025)\ *(fehdata[define_rcsample._AFETAG] <= (ii+1)*0.05-0.025) args= (apstack.median(pr[tindx][:12]),) kwargs={'startindxs':[1116,2100,2899], 'endindxs':[1146,2124,2922], 'yrange':[-0.05,0.02], 'ylabel':'', 'cleanZero':False, '_markwav':apwindow.lines('Ti'), 'zorder':int(numpy.floor(numpy.random.uniform()*5)), 'color':colormap(colorFunc(ii*0.05)), 'overplot':ii>0, 'fig_width':3.5, 'markLines':True} if ii>0: kwargs.pop('fig_width') if ii != 4: kwargs.pop('_markwav') kwargs.pop('markLines') kwargs['_startendskip']= 0 kwargs['_noxticks']= True kwargs['_labelwav']= True splot.waveregions(*args,**kwargs) bovy_plot.bovy_text(r'$\mathrm{Ti}$', top_left=True,fontsize=10,backgroundcolor='w') bovy_plot.bovy_end_print(plotname.replace('ELEM','Ti')) return None
def allStar(rmcommissioning=True, main=False, exclude_star_bad=False, exclude_star_warn=False, ak=True, akvers='targ', rmnovisits=False, use_astroNN=False, use_astroNN_abundances=False, use_astroNN_distances=False, use_astroNN_ages=False, adddist=False, distredux=None, rmdups=False, raw=False, mjd=58104, xmatch=None, **kwargs): """ NAME: allStar PURPOSE: read the allStar file INPUT: rmcommissioning= (default: True) if True, only use data obtained after commissioning main= (default: False) if True, only select stars in the main survey exclude_star_bad= (False) if True, remove stars with the STAR_BAD flag set in ASPCAPFLAG exclude_star_warn= (False) if True, remove stars with the STAR_WARN flag set in ASPCAPFLAG ak= (default: True) only use objects for which dereddened mags exist akvers= 'targ' (default) or 'wise': use target AK (AK_TARG) or AK derived from all-sky WISE (AK_WISE) rmnovisits= (False) if True, remove stars with no good visits (to go into the combined spectrum); shouldn't be necessary use_astroNN= (False) if True, swap in astroNN (Leung & Bovy 2019a) parameters (get placed in, e.g., TEFF and TEFF_ERR), astroNN distances (Leung & Bovy 2019b), and astroNN ages (Mackereth, Bovy, Leung, et al. (2019) use_astroNN_abundances= (False) only swap in astroNN parameters and abundances, not distances and ages use_astroNN_distances= (False) only swap in astroNN distances, not parameters and abundances and ages use_astroNN_ages= (False) only swap in astroNN ages, not parameters and abundances and distances adddist= (default: False) add distances (DR10/11 Hayden distances, DR12 combined distances) distredux= (default: DR default) reduction on which the distances are based rmdups= (False) if True, remove duplicates (very slow) raw= (False) if True, just return the raw file, read w/ fitsio mjd= (58104) MJD of version for monthly internal pipeline runs xmatch= (None) uses gaia_tools.xmatch.cds to x-match to an external catalog (eg., Gaia DR2 for xmatch='vizier:I/345/gaia2') and caches the result for re-use; requires jobovy/gaia_tools +gaia_tools.xmatch.cds keywords OUTPUT: allStar data[,xmatched table] HISTORY: 2013-09-06 - Written - Bovy (IAS) 2018-01-22 - Edited for new monthly pipeline runs - Bovy (UofT) 2018-05-09 - Add xmatch - Bovy (UofT) 2018-10-20 - Add use_astroNN option - Bovy (UofT) 2018-02-15 - Add astroNN distances and corresponding options - Bovy (UofT) 2018-02-16 - Add astroNN ages and corresponding options - Bovy (UofT) """ filePath = path.allStarPath(mjd=mjd) if not os.path.exists(filePath): download.allStar(mjd=mjd) #read allStar file data = fitsread(path.allStarPath(mjd=mjd)) #Add astroNN? astroNN file matched line-by-line to allStar, so match here # [ages file not matched line-by-line] if use_astroNN or kwargs.get('astroNN', False) or use_astroNN_abundances: _warn_astroNN_abundances() astroNNdata = astroNN() data = _swap_in_astroNN(data, astroNNdata) if use_astroNN or kwargs.get('astroNN', False) or use_astroNN_distances: _warn_astroNN_distances() astroNNdata = astroNNDistances() data = _add_astroNN_distances(data, astroNNdata) if use_astroNN or kwargs.get('astroNN', False) or use_astroNN_ages: _warn_astroNN_ages() astroNNdata = astroNNAges() data = _add_astroNN_ages(data, astroNNdata) if raw: return data #Remove duplicates, cache if rmdups: dupsFilename = path.allStarPath(mjd=mjd).replace( '.fits', '-nodups.fits') if os.path.exists(dupsFilename): data = fitsread(dupsFilename) else: sys.stdout.write( '\r' + "Removing duplicates (might take a while) and caching the duplicate-free file ...\r" ) sys.stdout.flush() data = remove_duplicates(data) #Cache this file for subsequent use of rmdups fitswrite(dupsFilename, data, clobber=True) sys.stdout.write('\r' + _ERASESTR + '\r') sys.stdout.flush() if not xmatch is None: from gaia_tools.load import _xmatch_cds if rmdups: matchFilePath = dupsFilename else: matchFilePath = filePath if use_astroNN_ages: matchFilePath = matchFilePath.replace('rc-', 'rc-astroNN-ages-') ma, mai = _xmatch_cds(data, xmatch, filePath, **kwargs) data = data[mai] #Some cuts if rmcommissioning: try: indx = numpy.array( ['apogee.n.c'.encode('utf-8') in s for s in data['APSTAR_ID']]) indx += numpy.array( ['apogee.s.c'.encode('utf-8') in s for s in data['APSTAR_ID']]) except TypeError: indx = numpy.array(['apogee.n.c' in s for s in data['APSTAR_ID']]) indx += numpy.array(['apogee.s.c' in s for s in data['APSTAR_ID']]) data = data[True ^ indx] if not xmatch is None: ma = ma[True ^ indx] if rmnovisits: indx = numpy.array([s.strip() != '' for s in data['VISITS']]) data = data[indx] if not xmatch is None: ma = ma[indx] if main: indx = mainIndx(data) data = data[indx] if not xmatch is None: ma = ma[indx] if akvers.lower() == 'targ': aktag = 'AK_TARG' elif akvers.lower() == 'wise': aktag = 'AK_WISE' if ak: if not xmatch is None: ma = ma[True ^ numpy.isnan(data[aktag])] data = data[True ^ numpy.isnan(data[aktag])] if not xmatch is None: ma = ma[(data[aktag] > -50.)] data = data[(data[aktag] > -50.)] if exclude_star_bad: if not xmatch is None: ma = ma[(data['ASPCAPFLAG'] & 2**23) == 0] data = data[(data['ASPCAPFLAG'] & 2**23) == 0] if exclude_star_warn: if not xmatch is None: ma = ma[(data['ASPCAPFLAG'] & 2**7) == 0] data = data[(data['ASPCAPFLAG'] & 2**7) == 0] #Add dereddened J, H, and Ks aj = data[aktag] * 2.5 ah = data[aktag] * 1.55 if _ESUTIL_LOADED: data = esutil.numpy_util.add_fields(data, [('J0', float), ('H0', float), ('K0', float)]) data['J0'] = data['J'] - aj data['H0'] = data['H'] - ah data['K0'] = data['K'] - data[aktag] data['J0'][(data[aktag] <= -50.)] = -9999.9999 data['H0'][(data[aktag] <= -50.)] = -9999.9999 data['K0'][(data[aktag] <= -50.)] = -9999.9999 else: warnings.warn( "Extinction-corrected J,H,K not added because esutil is not installed", RuntimeWarning) #Add distances if adddist and _ESUTIL_LOADED: dist = fitsread(path.distPath(), 1) h = esutil.htm.HTM() m1, m2, d12 = h.match(dist['RA'], dist['DEC'], data['RA'], data['DEC'], 2. / 3600., maxmatch=1) data = data[m2] if not xmatch is None: ma = ma[m2] dist = dist[m1] distredux = path._redux_dr() if distredux.lower() == 'v302' or distredux.lower() == path._DR10REDUX: data = esutil.numpy_util.add_fields(data, [('DM05', float), ('DM16', float), ('DM50', float), ('DM84', float), ('DM95', float), ('DMPEAK', float), ('DMAVG', float), ('SIG_DM', float), ('DIST_SOL', float), ('SIG_DISTSOL', float)]) data['DM05'] = dist['DM05'] data['DM16'] = dist['DM16'] data['DM50'] = dist['DM50'] data['DM84'] = dist['DM84'] data['DM95'] = dist['DM95'] data['DMPEAK'] = dist['DMPEAK'] data['DMAVG'] = dist['DMAVG'] data['SIG_DM'] = dist['SIG_DM'] data['DIST_SOL'] = dist['DIST_SOL'] / 1000. data['SIG_DISTSOL'] = dist['SIG_DISTSOL'] / 1000. elif distredux.lower() == path._DR11REDUX: data = esutil.numpy_util.add_fields(data, [('DISO', float), ('DMASS', float), ('DISO_GAL', float), ('DMASS_GAL', float)]) data['DISO'] = dist['DISO'][:, 1] data['DMASS'] = dist['DMASS'][:, 1] data['DISO_GAL'] = dist['DISO_GAL'][:, 1] data['DMASS_GAL'] = dist['DMASS_GAL'][:, 1] elif distredux.lower() == path._DR12REDUX: data = esutil.numpy_util.add_fields( data, [('HIP_PLX', float), ('HIP_E_PLX', float), ('RC_DIST', float), ('APOKASC_DIST_DIRECT', float), ('BPG_DIST1_MEAN', float), ('HAYDEN_DIST_PEAK', float), ('SCHULTHEIS_DIST', float)]) data['HIP_PLX'] = dist['HIP_PLX'] data['HIP_E_PLX'] = dist['HIP_E_PLX'] data['RC_DIST'] = dist['RC_dist_pc'] data[ 'APOKASC_DIST_DIRECT'] = dist['APOKASC_dist_direct_pc'] / 1000. data['BPG_DIST1_MEAN'] = dist['BPG_dist1_mean'] data['HAYDEN_DIST_PEAK'] = 10.**(dist['HAYDEN_distmod_PEAK'] / 5. - 2.) data['SCHULTHEIS_DIST'] = dist['SCHULTHEIS_dist'] elif adddist: warnings.warn( "Distances not added because matching requires the uninstalled esutil module", RuntimeWarning) if _ESUTIL_LOADED and (path._APOGEE_REDUX.lower() == 'current' \ or 'l3' in path._APOGEE_REDUX.lower() \ or int(path._APOGEE_REDUX[1:]) > 600): data = esutil.numpy_util.add_fields(data, [('METALS', float), ('ALPHAFE', float)]) data['METALS'] = data['PARAM'][:, paramIndx('metals')] data['ALPHAFE'] = data['PARAM'][:, paramIndx('alpha')] if not xmatch is None: return (data, ma) else: return data
def mcmc(spec,specerr, fparam=None, teff=4750.,logg=2.5,metals=0.,am=0.,nm=0.,cm=0.,vm=None, nsamples=1000,nwalkers=40, initsig=[20.,0.05,0.025,0.025,0.025,0.025,0.025], fixteff=False,fixlogg=False,fixmetals=False,fixam=False,fixcm=False, fixnm=False,fixvm=False, lib='GK',pca=True,sixd=True,dr=None, inter=3,f_format=1,f_access=None, verbose=False): """ NAME: mcmc PURPOSE: Perform MCMC of the 6/7 parameter global fit of model spectra to a given data spectrum INPUT: Either: (1) location ID - single or list/array of location IDs APOGEE ID - single or list/array of APOGEE IDs; loads aspcapStar (2) spec - spectrum: can be (nwave) or (nspec,nwave) specerr - spectrum errors: can be (nwave) or (nspec,nwave) Input parameters (can be 1D arrays) Either: (1) fparam= (None) output of ferre.fit (2) teff= (4750.) Effective temperature (K) logg= (2.5) log10 surface gravity / cm s^-2 metals= (0.) overall metallicity am= (0.) [alpha/M] nm= (0.) [N/M] cm= (0.) [C/M] vm= if using the 7D library, also specify the microturbulence MCMC options: nsamples= (1000) number of samples to get nwalkers= (40) number of ensemble walkers to use in the MCMC initsig= std. dev. of the initial ball of walkers around [Teff,logg,log10vmicro,metals,cm,nm,am] specify log10vmicro even when using a 6D library fixteff= (True) if True, fix teff at the input value fixlogg= (True) if True, fix logg at the input value fixvm= (True) if True, fix vm at the input value (only if sixd is False) fixmetals= (None) if True, fix metals at the input value fixam= (None) if True, fix am at the input value fixcm= (None) if True, fix cm at the input value fixnm= (None) if True, fix nm at the input value Library options: lib= ('GK') spectral library pca= (True) if True, use a PCA compressed library sixd= (True) if True, use the 6D library (w/o vm) dr= data release FERRE options: inter= (3) order of the interpolation f_format= (1) file format (0=ascii, 1=unf) f_access= (None) 0: load whole library, 1: use direct access (for small numbers of interpolations), None: automatically determine a good value (currently, 1) verbose= (False) if True, run FERRE in verbose mode OUTPUT: TBD HISTORY: 2015-04-08 - Written - Bovy (IAS) """ # Parse fparam if not fparam is None: teff= fparam[:,paramIndx('TEFF')] logg= fparam[:,paramIndx('LOGG')] metals= fparam[:,paramIndx('METALS')] am= fparam[:,paramIndx('ALPHA')] nm= fparam[:,paramIndx('N')] cm= fparam[:,paramIndx('C')] if sixd: vm= None else: vm= fparam[:,paramIndx('LOG10VDOP')] # Make sure the Teff etc. have the right dimensionality if len(spec.shape) == 1: nspec= 1 else: raise ValueError("apogee.modelspec.ferre.mcmc only works for a single spectrum") nspec= spec.shape[0] if nspec > 1 and isinstance(teff,float): teff= teff*numpy.ones(nspec) if nspec > 1 and isinstance(logg,float): logg= logg*numpy.ones(nspec) if nspec > 1 and isinstance(metals,float): metals= metals*numpy.ones(nspec) if nspec > 1 and isinstance(am,float): am= am*numpy.ones(nspec) if nspec > 1 and isinstance(nm,float): nm= nm*numpy.ones(nspec) if nspec > 1 and isinstance(cm,float): cm= cm*numpy.ones(nspec) if nspec > 1 and not vm is None and isinstance(vm,float): vm= vm*numpy.ones(nspec) if dr is None: dr= appath._default_dr() # Setup the walkers ndim= 7-sixd-fixvm+sixd*fixvm-fixteff-fixlogg-fixmetals-fixam-fixcm-fixnm p0= [] for ii in range(nwalkers): tp0= [] # Teff if not fixteff: tteff= teff+numpy.random.normal()*initsig[0] if tteff > 6000.: tteff= 6000. if tteff < 3500.: tteff= 3500. tp0.append(tteff) # logg if not fixlogg: tlogg= logg+numpy.random.normal()*initsig[1] if logg > 5.: tlogg= 5. if logg < 0.: tlogg= 0. tp0.append(tlogg) # log10vmicro if not (fixvm or sixd): tvm= numpy.log10(vm)+numpy.random.normal()*initsig[2] if tvm > numpy.log10(8.0): tvm= numpy.log10(8.0) if tvm < numpy.log10(0.5): tvm= numpy.log10(0.5) tp0.append(tvm) # metals if not fixmetals: tmetals= metals+numpy.random.normal()*initsig[3] if metals > 0.5: tmetals= 0.5 if metals < -2.5: tmetals= -2.5 tp0.append(tmetals) # cm if not fixcm: tcm= cm+numpy.random.normal()*initsig[4] if cm > 1.0: tcm= 1.0 if cm < -1.0: tcm= -1. tp0.append(tcm) # nm if not fixnm: tnm= nm+numpy.random.normal()*initsig[5] if nm > 0.5: tnm= 1.0 if nm < -1.0: tnm= -1.0 tp0.append(tnm) # am if not fixam: tam= am+numpy.random.normal()*initsig[6] if am > 0.5: tam= 1.0 if am < -1.0: tam= -1.0 tp0.append(tam) p0.append(numpy.array(tp0)[:,0]) p0= numpy.array(p0) # Prepare the data dspec= numpy.tile(spec,(nwalkers,1)) dspecerr= numpy.tile(specerr,(nwalkers,1)) # Run MCMC sampler= apogee.util.emcee.EnsembleSampler(nwalkers,ndim,_mcmc_lnprob, args=[dspec,dspecerr, teff,logg,vm,metals, cm,nm,am, fixteff,fixlogg,fixvm, fixmetals,fixcm,fixnm, fixam,sixd,pca, dr,lib, inter,f_format,f_access]) # Burn-in 10% of nsamples pos, prob, state = sampler.run_mcmc(p0,nsamples//10) sampler.reset() # Run main MCMC sampler.run_mcmc(pos,nsamples) return sampler.flatchain
def elemfitall(*args,**kwargs): """ NAME: elemfitall PURPOSE: Fit a model spectrum to a given data spectrum for all element windows INPUT: Either: (1) location ID - single or list/array of location IDs APOGEE ID - single or list/array of APOGEE IDs; loads aspcapStar (2) spec - spectrum: can be (nwave) or (nspec,nwave) specerr - spectrum errors: can be (nwave) or (nspec,nwave) estimate_err= (False) if True, estimate the error from Delta chi^2=1; only works for errors <~ 0.3 dex, code returns numpy.nan when error is larger Input parameters (can be 1D arrays); only used when init=0 Either: (1) fparam= (None) output of ferre.fit (2) teff= (4750.) Effective temperature (K) logg= (2.5) log10 surface gravity / cm s^-2 metals= (0.) overall metallicity am= (0.) [alpha/M] nm= (0.) [N/M] cm= (0.) [C/M] vm= if using the 7D library, also specify the microturbulence Fit options: fixteff= (True) if True, fix teff at the input value fixlogg= (True) if True, fix logg at the input value fixvm= (True) if True, fix vm at the input value (only if sixd is False) The following are set to False based on the element being fit (C -> fixcm=False, N -> fixnm=False, O,Mg,S,Si,Ca,Ti -> fixam=False, rest -> fixmetals=False) fixmetals= (None) if True, fix metals at the input value fixam= (None) if True, fix am at the input value fixcm= (None) if True, fix cm at the input value fixnm= (None) if True, fix nm at the input value Library options: lib= ('GK') spectral library pca= (True) if True, use a PCA compressed library sixd= (True) if True, use the 6D library (w/o vm) dr= data release FERRE options: inter= (3) order of the interpolation errbar= (1) method for calculating the error bars indini= ([1,1,1,2,2,3]) how to initialize the search (int or array/list with ndim entries) init= (0) if 0, initialize the search at the parameters in the pfile f_format= (1) file format (0=ascii, 1=unf) f_access= (None) 0: load whole library, 1: use direct access (for small numbers of interpolations), None: automatically determine a good value (currently, 1) Output options: offile= (None) if offile is set, the FERRE OFFILE is saved to this file, otherwise this file is removed verbose= (False) if True, run FERRE in verbose mode OUTPUT: dictionary with best-fit ELEM_H (nspec), contains e_ELEM when estimate_err; this does not include the (potentially correlated) error on METALS for those elements fit relative to Fe HISTORY: 2015-03-01 - Written - Bovy (IAS) """ # METALS for normalization if not kwargs.get('fparam',None) is None: metals= kwargs.get('fparam')[:,paramIndx('METALS')] else: metals= kwargs.get('metals',0.) # Run through and fit all elements out= {} for elem in _ELEM_SYMBOL: targs= args+(elem.capitalize(),) tefit= elemfit(*targs,**kwargs) if kwargs.get('estimate_err',False): tefit, terr= tefit out['e_'+elem.capitalize()]= terr if elem.lower() == 'c': tout= tefit[:,paramIndx('C')]+metals elif elem.lower() == 'n': tout= tefit[:,paramIndx('N')]+metals elif elem.lower() in ['o','mg','s','si','ca','ti']: tout= tefit[:,paramIndx('ALPHA')]+metals else: tout= tefit[:,paramIndx('METALS')] out[elem.capitalize()]= tout return out
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 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 elemfit(spec,specerr,elem, fparam=None, teff=4750.,logg=2.5,metals=0.,am=0.,nm=0.,cm=0.,vm=None, fixteff=True,fixlogg=True,fixmetals=None,fixam=None, fixcm=None, fixnm=None,fixvm=True, estimate_err=False, lib='GK',pca=True,sixd=True,dr=None, offile=None, inter=3,f_format=1,f_access=None, errbar=1,indini=[1,1,1,2,2,3],init=0, verbose=False): """ NAME: elemfit PURPOSE: Fit a model spectrum to a given data spectrum for a given element window INPUT: Either: (1) location ID - single or list/array of location IDs APOGEE ID - single or list/array of APOGEE IDs; loads aspcapStar (2) spec - spectrum: can be (nwave) or (nspec,nwave) specerr - spectrum errors: can be (nwave) or (nspec,nwave) elem - element to fit (e.g., 'Al') estimate_err= (False) if True, estimate the error from Delta chi^2=1; only works for errors <~ 0.3 dex, code returns numpy.nan when error is larger Input parameters (can be 1D arrays); only used when init=0 Either: (1) fparam= (None) output of ferre.fit (2) teff= (4750.) Effective temperature (K) logg= (2.5) log10 surface gravity / cm s^-2 metals= (0.) overall metallicity am= (0.) [alpha/M] nm= (0.) [N/M] cm= (0.) [C/M] vm= if using the 7D library, also specify the microturbulence Fit options: fixteff= (True) if True, fix teff at the input value fixlogg= (True) if True, fix logg at the input value fixvm= (True) if True, fix vm at the input value (only if sixd is False) The following are set to False based on the element being fit (C -> fixcm=False, N -> fixnm=False, O,Mg,S,Si,Ca,Ti -> fixam=False, rest -> fixmetals=False) fixmetals= (None) if True, fix metals at the input value fixam= (None) if True, fix am at the input value fixcm= (None) if True, fix cm at the input value fixnm= (None) if True, fix nm at the input value Library options: lib= ('GK') spectral library pca= (True) if True, use a PCA compressed library sixd= (True) if True, use the 6D library (w/o vm) dr= data release FERRE options: inter= (3) order of the interpolation errbar= (1) method for calculating the error bars indini= ([1,1,1,2,2,3]) how to initialize the search (int or array/list with ndim entries) init= (0) if 0, initialize the search at the parameters in the pfile f_format= (1) file format (0=ascii, 1=unf) f_access= (None) 0: load whole library, 1: use direct access (for small numbers of interpolations), None: automatically determine a good value (currently, 1) Output options: offile= (None) if offile is set, the FERRE OFFILE is saved to this file, otherwise this file is removed verbose= (False) if True, run FERRE in verbose mode OUTPUT: best-fit parameters (nspec,nparams); in the same order as the FPARAM APOGEE data product (fixed inputs are repeated in the output) if estimate_err: tuple with best-fit (see above) and error on the element abundance HISTORY: 2015-02-27 - Written - Bovy (IAS) """ # Parse fparam if not fparam is None: teff= fparam[:,paramIndx('TEFF')] logg= fparam[:,paramIndx('LOGG')] metals= fparam[:,paramIndx('METALS')] am= fparam[:,paramIndx('ALPHA')] nm= fparam[:,paramIndx('N')] cm= fparam[:,paramIndx('C')] if sixd: vm= None else: vm= fparam[:,paramIndx('LOG10VDOP')] # Make sure the Teff etc. have the right dimensionality if len(spec.shape) == 1: nspec= 1 else: nspec= spec.shape[0] if nspec > 1 and isinstance(teff,float): teff= teff*numpy.ones(nspec) if nspec > 1 and isinstance(logg,float): logg= logg*numpy.ones(nspec) if nspec > 1 and isinstance(metals,float): metals= metals*numpy.ones(nspec) if nspec > 1 and isinstance(am,float): am= am*numpy.ones(nspec) if nspec > 1 and isinstance(nm,float): nm= nm*numpy.ones(nspec) if nspec > 1 and isinstance(cm,float): cm= cm*numpy.ones(nspec) if nspec > 1 and not vm is None and isinstance(vm,float): vm= vm*numpy.ones(nspec) if dr is None: dr= appath._default_dr() # Set fixXX based on element being fit, first set None to True if fixcm is None: fixcm= True if fixnm is None: fixnm= True if fixam is None: fixam= True if fixmetals is None: fixmetals= True if elem.lower() == 'c': fixcm= False elif elem.lower() == 'n': fixnm= False elif elem.lower() in ['o','mg','s','si','ca','ti']: fixam= False else: fixmetals= False # Fix any of the parameters? indv= [] indini= copy.copy(indini) # need to copy bc passed by reference if isinstance(indini,numpy.ndarray) and \ ((not sixd and fixvm) or fixcm or fixnm or fixam or fixmetals \ or fixlogg or fixteff): indini= list(indini) if not sixd and not fixvm: indv.append(1) elif not sixd: if isinstance(indini,list): indini[0]= -1 if not fixcm: indv.append(2-sixd) else: if isinstance(indini,list): indini[1-sixd]= -1 if not fixnm: indv.append(3-sixd) else: if isinstance(indini,list): indini[2-sixd]= -1 if not fixam: indv.append(4-sixd) else: if isinstance(indini,list): indini[3-sixd]= -1 if not fixmetals: indv.append(5-sixd) else: if isinstance(indini,list): indini[4-sixd]= -1 if not fixlogg: indv.append(6-sixd) else: if isinstance(indini,list): indini[5-sixd]= -1 if not fixteff: indv.append(7-sixd) else: if isinstance(indini,list): indini[6-sixd]= -1 if isinstance(indini,list): while -1 in indini: indini.remove(-1) if init == 0: indini= 0 # Setup temporary directory to run FERRE from tmpDir= tempfile.mkdtemp(dir='./') try: # First write the ipf file with the parameters write_ipf(tmpDir,teff,logg,metals,am,nm,cm,vm=vm) # Write the file with the fluxes and the flux errors write_ffile(tmpDir,spec,specerr=specerr) # Now write the input.nml file if f_access is None: f_access= 1 write_input_nml(tmpDir,'input.ipf','output.dat',ndim=7-sixd, nov=7-sixd-fixcm-fixnm-fixam-fixmetals\ -fixlogg-fixteff, indv=indv, synthfile=appath.ferreModelLibraryPath\ (lib=lib,pca=pca,sixd=sixd,dr=dr, header=True,unf=False), ffile='input.frd',erfile='input.err', opfile='output.opf', inter=inter,f_format=f_format, errbar=errbar,indini=indini,init=init, f_access=f_access, filterfile=apwindow.path(elem,dr=dr)) # Run FERRE run_ferre(tmpDir,verbose=verbose) # Read the output cols= (1,2,3,4,5,6) tmpOut= numpy.loadtxt(os.path.join(tmpDir,'output.opf'),usecols=cols) if len(spec.shape) == 1 or spec.shape[0] == 1: out= numpy.zeros((1,7)) tmpOut= numpy.reshape(tmpOut,(1,7-sixd)) else: out= numpy.zeros((nspec,7)) out[:,paramIndx('TEFF')]= tmpOut[:,-1] out[:,paramIndx('LOGG')]= tmpOut[:,-2] out[:,paramIndx('METALS')]= tmpOut[:,-3] out[:,paramIndx('ALPHA')]= tmpOut[:,-4] out[:,paramIndx('N')]= tmpOut[:,-5] out[:,paramIndx('C')]= tmpOut[:,-6] if sixd and dr == '12': out[:,paramIndx('LOG10VDOP')]=\ numpy.log10(2.478-0.325*out[:,paramIndx('LOGG')]) else: out[:,paramIndx('LOG10VDOP')]= tmpOut[:,0] if not offile is None: os.rename(os.path.join(tmpDir,'output.dat'),offile) finally: # Clean up if os.path.exists(os.path.join(tmpDir,'input.ipf')): os.remove(os.path.join(tmpDir,'input.ipf')) if os.path.exists(os.path.join(tmpDir,'input.frd')): os.remove(os.path.join(tmpDir,'input.frd')) if os.path.exists(os.path.join(tmpDir,'input.err')): os.remove(os.path.join(tmpDir,'input.err')) if os.path.exists(os.path.join(tmpDir,'input.nml')): os.remove(os.path.join(tmpDir,'input.nml')) if os.path.exists(os.path.join(tmpDir,'output.dat')): os.remove(os.path.join(tmpDir,'output.dat')) if os.path.exists(os.path.join(tmpDir,'output.opf')): os.remove(os.path.join(tmpDir,'output.opf')) os.rmdir(tmpDir) if estimate_err: # Determine the chi2 and the error elem_linspace= (-0.5,0.5,51) elems= numpy.linspace(*elem_linspace) c2= elemchi2(spec,specerr,elem, elem_linspace=elem_linspace, fparam=out,lib=lib,pca=pca,sixd=sixd,dr=dr, inter=inter,f_format=f_format,f_access=f_access, verbose=verbose) from scipy import interpolate, optimize outerr= numpy.empty(nspec) for ii in range(nspec): spl= \ interpolate.InterpolatedUnivariateSpline(elems, c2[ii]-numpy.amin(c2[ii])) try: ul= optimize.brentq(lambda x: spl(x)-1., 0.,elem_linspace[1]-0.01) ll= optimize.brentq(lambda x: spl(x)-1., elem_linspace[0]+0.01,0.) outerr[ii]= (ul-ll)/2. except ValueError: outerr[ii]= numpy.nan return (out,outerr) else: return out
def make_rcsample(parser): options,args= parser.parse_args() savefilename= options.savefilename if savefilename is None: #Create savefilename if not given savefilename= os.path.join(appath._APOGEE_DATA, 'rcsample_'+appath._APOGEE_REDUX+'.fits') print "Saving to %s ..." % savefilename #Read the base-sample data= apread.allStar(adddist=_ADDHAYDENDIST,rmdups=options.rmdups) #Remove a bunch of fields that we do not want to keep data= esutil.numpy_util.remove_fields(data, ['TARGET_ID', 'FILE', 'AK_WISE', 'SFD_EBV', 'SYNTHVHELIO_AVG', 'SYNTHVSCATTER', 'SYNTHVERR', 'SYNTHVERR_MED', 'RV_TEFF', 'RV_LOGG', 'RV_FEH', 'RV_CCFWHM', 'RV_AUTOFWHM', 'SYNTHSCATTER', 'CHI2_THRESHOLD', 'APSTAR_VERSION', 'ASPCAP_VERSION', 'RESULTS_VERSION', 'REDUCTION_ID', 'SRC_H', 'PM_SRC']) if not appath._APOGEE_REDUX.lower() == 'current' \ and int(appath._APOGEE_REDUX[1:]) < 500: data= esutil.numpy_util.remove_fields(data, ['ELEM']) #Select red-clump stars jk= data['J0']-data['K0'] z= isodist.FEH2Z(data['METALS'],zsolar=0.017) if appath._APOGEE_REDUX.lower() == 'current' \ or int(appath._APOGEE_REDUX[1:]) > 600: from apogee.tools import paramIndx if False: #Use my custom logg calibration that's correct for the RC logg= (1.-0.042)*data['FPARAM'][:,paramIndx('logg')]-0.213 lowloggindx= data['FPARAM'][:,paramIndx('logg')] < 1. logg[lowloggindx]= data['FPARAM'][lowloggindx,paramIndx('logg')]-0.255 hiloggindx= data['FPARAM'][:,paramIndx('logg')] > 3.8 logg[hiloggindx]= data['FPARAM'][hiloggindx,paramIndx('logg')]-0.3726 else: #Use my custom logg calibration that's correct on average logg= (1.+0.03)*data['FPARAM'][:,paramIndx('logg')]-0.37 lowloggindx= data['FPARAM'][:,paramIndx('logg')] < 1. logg[lowloggindx]= data['FPARAM'][lowloggindx,paramIndx('logg')]-0.34 hiloggindx= data['FPARAM'][:,paramIndx('logg')] > 3.8 logg[hiloggindx]= data['FPARAM'][hiloggindx,paramIndx('logg')]-0.256 else: logg= data['LOGG'] indx= (jk < 0.8)*(jk >= 0.5)\ *(z <= 0.06)\ *(z <= rcmodel.jkzcut(jk,upper=True))\ *(z >= rcmodel.jkzcut(jk))\ *(logg >= rcmodel.loggteffcut(data['TEFF'],z,upper=False))\ *(logg <= rcmodel.loggteffcut(data['TEFF'],z,upper=True)) data= data[indx] #Add more aggressive flag cut data= esutil.numpy_util.add_fields(data,[('ADDL_LOGG_CUT',numpy.int32)]) data['ADDL_LOGG_CUT']= ((data['TEFF']-4800.)/1000.+2.75) > data['LOGG'] if options.loggcut: data= data[data['ADDL_LOGG_CUT'] == 1] print "Making catalog of %i objects ..." % len(data) #Add distances data= esutil.numpy_util.add_fields(data,[('RC_DIST', float), ('RC_DM', float), ('RC_GALR', float), ('RC_GALPHI', float), ('RC_GALZ', float)]) rcd= rcmodel.rcdist() jk= data['J0']-data['K0'] z= isodist.FEH2Z(data['METALS'],zsolar=0.017) data['RC_DIST']= rcd(jk,z,appmag=data['K0'])*options.distfac data['RC_DM']= 5.*numpy.log10(data['RC_DIST'])+10. XYZ= bovy_coords.lbd_to_XYZ(data['GLON'], data['GLAT'], data['RC_DIST'], degree=True) R,phi,Z= bovy_coords.XYZ_to_galcencyl(XYZ[:,0], XYZ[:,1], XYZ[:,2], Xsun=8.,Zsun=0.025) data['RC_GALR']= R data['RC_GALPHI']= phi data['RC_GALZ']= Z #Save fitsio.write(savefilename,data,clobber=True) if not options.nostat: #Determine statistical sample and add flag apo= apogee.select.apogeeSelect() statIndx= apo.determine_statistical(data) mainIndx= apread.mainIndx(data) data= esutil.numpy_util.add_fields(data,[('STAT',numpy.int32), ('INVSF',float)]) data['STAT']= 0 data['STAT'][statIndx*mainIndx]= 1 for ii in range(len(data)): if (statIndx*mainIndx)[ii]: data['INVSF'][ii]= 1./apo(data['LOCATION_ID'][ii], data['H'][ii]) else: data['INVSF'][ii]= -1. if options.nopm: fitsio.write(savefilename,data,clobber=True) return None #Get proper motions from astroquery.vizier import Vizier import astroquery from astropy import units as u import astropy.coordinates as coord pmfile= savefilename.split('.')[0]+'_pms.fits' if os.path.exists(pmfile): pmdata= fitsio.read(pmfile,1) else: pmdata= numpy.recarray(len(data), formats=['f8','f8','f8','f8','f8','f8','i4'], names=['RA','DEC','PMRA','PMDEC', 'PMRA_ERR','PMDEC_ERR','PMMATCH']) rad= u.Quantity(4./3600.,u.degree) v= Vizier(columns=['RAJ2000','DEJ2000','pmRA','pmDE','e_pmRA','e_pmDE']) for ii in range(len(data)): #if ii > 100: break sys.stdout.write('\r'+"Getting pm data for point %i / %i" % (ii+1,len(data))) sys.stdout.flush() pmdata.RA[ii]= data['RA'][ii] pmdata.DEC[ii]= data['DEC'][ii] co= coord.ICRS(ra=data['RA'][ii], dec=data['DEC'][ii], unit=(u.degree, u.degree)) trying= True while trying: try: tab= v.query_region(co,rad,catalog='I/322') #UCAC-4 catalog except astroquery.exceptions.TimeoutError: pass else: trying= False if len(tab) == 0: pmdata.PMMATCH[ii]= 0 print "Didn't find a match for %i ..." % ii continue else: pmdata.PMMATCH[ii]= len(tab) if len(tab[0]['pmRA']) > 1: print "Found more than 1 match for %i ..." % ii try: pmdata.PMRA[ii]= float(tab[0]['pmRA']) except TypeError: jj= 1 while len(tab[0]['pmRA']) > 1 and jj < 4: trad= u.Quantity((4.-jj)/3600.,u.degree) trying= True while trying: try: tab= v.query_region(co,trad,catalog='I/322') #UCAC-4 catalog except astroquery.exceptions.TimeoutError: pass else: trying= False jj+= 1 if len(tab) == 0: pmdata.PMMATCH[ii]= 0 print "Didn't find a unambiguous match for %i ..." % ii continue pmdata.PMRA[ii]= float(tab[0]['pmRA']) pmdata.PMDEC[ii]= float(tab[0]['pmDE']) pmdata.PMRA_ERR[ii]= float(tab[0]['e_pmRA']) pmdata.PMDEC_ERR[ii]= float(tab[0]['e_pmDE']) if numpy.isnan(float(tab[0]['pmRA'])): pmdata.PMMATCH[ii]= 0 sys.stdout.write('\r'+_ERASESTR+'\r') sys.stdout.flush() fitsio.write(pmfile,pmdata,clobber=True) #To make sure we're using the same format below pmdata= fitsio.read(pmfile,1) #Match proper motions try: #These already exist currently, but may not always exist data= esutil.numpy_util.remove_fields(data,['PMRA','PMDEC']) except ValueError: pass data= esutil.numpy_util.add_fields(data,[('PMRA', numpy.float), ('PMDEC', numpy.float), ('PMRA_ERR', numpy.float), ('PMDEC_ERR', numpy.float), ('PMMATCH',numpy.int32)]) data['PMMATCH']= 0 h=esutil.htm.HTM() m1,m2,d12 = h.match(pmdata['RA'],pmdata['DEC'], data['RA'],data['DEC'], 2./3600.,maxmatch=1) data['PMRA'][m2]= pmdata['PMRA'][m1] data['PMDEC'][m2]= pmdata['PMDEC'][m1] data['PMRA_ERR'][m2]= pmdata['PMRA_ERR'][m1] data['PMDEC_ERR'][m2]= pmdata['PMDEC_ERR'][m1] data['PMMATCH'][m2]= pmdata['PMMATCH'][m1].astype(numpy.int32) pmindx= data['PMMATCH'] == 1 data['PMRA'][True-pmindx]= -9999.99 data['PMDEC'][True-pmindx]= -9999.99 data['PMRA_ERR'][True-pmindx]= -9999.99 data['PMDEC_ERR'][True-pmindx]= -9999.99 #Calculate Galactocentric velocities data= esutil.numpy_util.add_fields(data,[('GALVR', numpy.float), ('GALVT', numpy.float), ('GALVZ', numpy.float)]) lb= bovy_coords.radec_to_lb(data['RA'],data['DEC'],degree=True) XYZ= bovy_coords.lbd_to_XYZ(lb[:,0],lb[:,1],data['RC_DIST'],degree=True) pmllpmbb= bovy_coords.pmrapmdec_to_pmllpmbb(data['PMRA'],data['PMDEC'], data['RA'],data['DEC'], degree=True) vxvyvz= bovy_coords.vrpmllpmbb_to_vxvyvz(data['VHELIO_AVG'], pmllpmbb[:,0], pmllpmbb[:,1], lb[:,0],lb[:,1],data['RC_DIST'], degree=True) vR, vT, vZ= bovy_coords.vxvyvz_to_galcencyl(vxvyvz[:,0], vxvyvz[:,1], vxvyvz[:,2], 8.-XYZ[:,0], XYZ[:,1], XYZ[:,2]+0.025, vsun=[-11.1,30.24*8.,7.25])#Assumes proper motion of Sgr A* and R0=8 kpc, zo= 25 pc data['GALVR']= vR data['GALVT']= vT data['GALVZ']= vZ data['GALVR'][True-pmindx]= -9999.99 data['GALVT'][True-pmindx]= -9999.99 data['GALVZ'][True-pmindx]= -9999.99 #Get proper motions pmfile= savefilename.split('.')[0]+'_pms_ppmxl.fits' if os.path.exists(pmfile): pmdata= fitsio.read(pmfile,1) else: pmdata= numpy.recarray(len(data), formats=['f8','f8','f8','f8','f8','f8','i4'], names=['RA','DEC','PMRA','PMDEC', 'PMRA_ERR','PMDEC_ERR','PMMATCH']) rad= u.Quantity(4./3600.,u.degree) v= Vizier(columns=['RAJ2000','DEJ2000','pmRA','pmDE','e_pmRA','e_pmDE']) for ii in range(len(data)): #if ii > 100: break sys.stdout.write('\r'+"Getting pm data for point %i / %i" % (ii+1,len(data))) sys.stdout.flush() pmdata.RA[ii]= data['RA'][ii] pmdata.DEC[ii]= data['DEC'][ii] co= coord.ICRS(ra=data['RA'][ii], dec=data['DEC'][ii], unit=(u.degree, u.degree)) trying= True while trying: try: tab= v.query_region(co,rad,catalog='I/317') #PPMXL catalog except astroquery.exceptions.TimeoutError: pass else: trying= False if len(tab) == 0: pmdata.PMMATCH[ii]= 0 print "Didn't find a match for %i ..." % ii continue else: pmdata.PMMATCH[ii]= len(tab) if len(tab[0]['pmRA']) > 1: pass #print "Found more than 1 match for %i ..." % ii try: pmdata.PMRA[ii]= float(tab[0]['pmRA']) except TypeError: #Find nearest cosdists= numpy.zeros(len(tab[0]['pmRA'])) for jj in range(len(tab[0]['pmRA'])): cosdists[jj]= cos_sphere_dist(tab[0]['RAJ2000'][jj], tab[0]['DEJ2000'][jj], data['RA'][ii], data['DEC'][ii]) closest= numpy.argmax(cosdists) pmdata.PMRA[ii]= float(tab[0]['pmRA'][closest]) pmdata.PMDEC[ii]= float(tab[0]['pmDE'][closest]) pmdata.PMRA_ERR[ii]= float(tab[0]['e_pmRA'][closest]) pmdata.PMDEC_ERR[ii]= float(tab[0]['e_pmDE'][closest]) if numpy.isnan(float(tab[0]['pmRA'][closest])): pmdata.PMMATCH[ii]= 0 else: pmdata.PMDEC[ii]= float(tab[0]['pmDE']) pmdata.PMRA_ERR[ii]= float(tab[0]['e_pmRA']) pmdata.PMDEC_ERR[ii]= float(tab[0]['e_pmDE']) if numpy.isnan(float(tab[0]['pmRA'])): pmdata.PMMATCH[ii]= 0 sys.stdout.write('\r'+_ERASESTR+'\r') sys.stdout.flush() fitsio.write(pmfile,pmdata,clobber=True) #To make sure we're using the same format below pmdata= fitsio.read(pmfile,1) #Match proper motions to ppmxl data= esutil.numpy_util.add_fields(data,[('PMRA_PPMXL', numpy.float), ('PMDEC_PPMXL', numpy.float), ('PMRA_ERR_PPMXL', numpy.float), ('PMDEC_ERR_PPMXL', numpy.float), ('PMMATCH_PPMXL',numpy.int32)]) data['PMMATCH_PPMXL']= 0 h=esutil.htm.HTM() m1,m2,d12 = h.match(pmdata['RA'],pmdata['DEC'], data['RA'],data['DEC'], 2./3600.,maxmatch=1) data['PMRA_PPMXL'][m2]= pmdata['PMRA'][m1] data['PMDEC_PPMXL'][m2]= pmdata['PMDEC'][m1] data['PMRA_ERR_PPMXL'][m2]= pmdata['PMRA_ERR'][m1] data['PMDEC_ERR_PPMXL'][m2]= pmdata['PMDEC_ERR'][m1] data['PMMATCH_PPMXL'][m2]= pmdata['PMMATCH'][m1].astype(numpy.int32) pmindx= data['PMMATCH_PPMXL'] == 1 data['PMRA_PPMXL'][True-pmindx]= -9999.99 data['PMDEC_PPMXL'][True-pmindx]= -9999.99 data['PMRA_ERR_PPMXL'][True-pmindx]= -9999.99 data['PMDEC_ERR_PPMXL'][True-pmindx]= -9999.99 #Calculate Galactocentric velocities data= esutil.numpy_util.add_fields(data,[('GALVR_PPMXL', numpy.float), ('GALVT_PPMXL', numpy.float), ('GALVZ_PPMXL', numpy.float)]) lb= bovy_coords.radec_to_lb(data['RA'],data['DEC'],degree=True) XYZ= bovy_coords.lbd_to_XYZ(lb[:,0],lb[:,1],data['RC_DIST'],degree=True) pmllpmbb= bovy_coords.pmrapmdec_to_pmllpmbb(data['PMRA_PPMXL'], data['PMDEC_PPMXL'], data['RA'],data['DEC'], degree=True) vxvyvz= bovy_coords.vrpmllpmbb_to_vxvyvz(data['VHELIO_AVG'], pmllpmbb[:,0], pmllpmbb[:,1], lb[:,0],lb[:,1],data['RC_DIST'], degree=True) vR, vT, vZ= bovy_coords.vxvyvz_to_galcencyl(vxvyvz[:,0], vxvyvz[:,1], vxvyvz[:,2], 8.-XYZ[:,0], XYZ[:,1], XYZ[:,2]+0.025, vsun=[-11.1,30.24*8.,7.25])#Assumes proper motion of Sgr A* and R0=8 kpc, zo= 25 pc data['GALVR_PPMXL']= vR data['GALVT_PPMXL']= vT data['GALVZ_PPMXL']= vZ data['GALVR_PPMXL'][True-pmindx]= -9999.99 data['GALVT_PPMXL'][True-pmindx]= -9999.99 data['GALVZ_PPMXL'][True-pmindx]= -9999.99 #Save fitsio.write(savefilename,data,clobber=True) return None
def elemchi2(spec,specerr, elem,elem_linspace=(-0.5,0.5,11),tophat=False, fparam=None, teff=4750.,logg=2.5,metals=0.,am=0.,nm=0.,cm=0.,vm=None, lib='GK',pca=True,sixd=True,dr=None, offile=None, inter=3,f_format=1,f_access=None, verbose=False): """ NAME: elemchi2 PURPOSE: Calculate the chi^2 for a given element INPUT: Either: (1) location ID - single or list/array of location IDs APOGEE ID - single or list/array of APOGEE IDs; loads aspcapStar (2) spec - spectrum: can be (nwave) or (nspec,nwave) specerr - spectrum errors: can be (nwave) or (nspec,nwave) elem - element to consider (e.g., 'Al') elem_linspace= ((-0.5,0.5,11)) numpy.linspace range of abundance, relative to the relevant value in fparam / metals,am,nm,cm tophat= (False) if True, don't use the value of weights, just use them to define windows that have weight equal to one Input parameters (can be 1D arrays) Either: (1) fparam= (None) output of ferre.fit (2) teff= (4750.) Effective temperature (K) logg= (2.5) log10 surface gravity / cm s^-2 metals= (0.) overall metallicity am= (0.) [alpha/M] nm= (0.) [N/M] cm= (0.) [C/M] vm= if using the 7D library, also specify the microturbulence Library options: lib= ('GK') spectral library pca= (True) if True, use a PCA compressed library sixd= (True) if True, use the 6D library (w/o vm) dr= data release FERRE options: inter= (3) order of the interpolation f_format= (1) file format (0=ascii, 1=unf) f_access= (None) 0: load whole library, 1: use direct access (for small numbers of interpolations), None: automatically determine a good value (currently, 1) verbose= (False) if True, run FERRE in verbose mode OUTPUT: chi^2 HISTORY: 2015-03-12 - Written - Bovy (IAS) """ # Parse fparam if not fparam is None: teff= fparam[:,paramIndx('TEFF')] logg= fparam[:,paramIndx('LOGG')] metals= fparam[:,paramIndx('METALS')] am= fparam[:,paramIndx('ALPHA')] nm= fparam[:,paramIndx('N')] cm= fparam[:,paramIndx('C')] if sixd: vm= None else: vm= fparam[:,paramIndx('LOG10VDOP')] # parse spec, specerr input nspec= len(teff) if len(spec.shape) == 1: spec= numpy.reshape(spec,(1,spec.shape[0])) specerr= numpy.reshape(specerr,(1,specerr.shape[0])) # Read the weights if tophat: weights= apwindow.tophat(elem,apStarWavegrid=False,dr=dr) else: weights= apwindow.read(elem,apStarWavegrid=False,dr=dr) weights/= numpy.sum(weights) # Decide which parameter to vary nvelem= elem_linspace[2] var_elem= numpy.tile(numpy.linspace(*elem_linspace),(nspec,1)) if elem.lower() == 'c': cm= var_elem+numpy.tile(cm,(nvelem,1)).T elif elem.lower() == 'n': nm= var_elem+numpy.tile(nm,(nvelem,1)).T elif elem.lower() in ['o','mg','s','si','ca','ti']: am= var_elem+numpy.tile(am,(nvelem,1)).T else: metals= var_elem+numpy.tile(metals,(nvelem,1)).T # Upgrade dimensionality of other parameters for interpolate input teff= numpy.tile(teff,(nvelem,1)).T logg= numpy.tile(logg,(nvelem,1)).T if not sixd: vm= numpy.tile(vm,(nvelem,1)).T.flatten() if not elem.lower() == 'c': cm= numpy.tile(cm,(nvelem,1)).T if not elem.lower() == 'n': nm= numpy.tile(nm,(nvelem,1)).T if not elem.lower() in ['o','mg','s','si','ca','ti']: am= numpy.tile(am,(nvelem,1)).T if elem.lower() in ['c','n','o','mg','s','si','ca','ti']: metals= numpy.tile(metals,(nvelem,1)).T # Get interpolated spectra, [nspec,nwave] ispec= interpolate(teff.flatten(),logg.flatten(),metals.flatten(), am.flatten(),nm.flatten(),cm.flatten(),vm=vm, lib=lib,pca=pca,sixd=sixd,dr=dr, inter=inter,f_format=f_format,f_access=f_access, verbose=verbose,apStarWavegrid=False) dspec= numpy.tile(spec,(1,nvelem)).reshape((nspec*nvelem,spec.shape[1])) dspecerr= numpy.tile(specerr, (1,nvelem)).reshape((nspec*nvelem,spec.shape[1])) tchi2= _chi2(ispec,dspec,dspecerr,numpy.tile(weights,(nspec*nvelem,1))) return numpy.reshape(tchi2,(nspec,nvelem))