def GetCoGLimit(starData, ion=26.0, modelAtms=None, pradks=None): # Returns the parameters for a line (using the same return parameters as # scipy.stats.linregress), representing the maximum limit for an EQW measurement # assuming that the non-linear portion of the Curve of Growth (CoG) begins # around a value of -4.60, as measured by Log(EQW/Wavelength). # The return parameters are (in order): # slope, intercept, r-value, p-value, stderr starName = k.MaxDetFakeStarName clustName = k.CalibClusterName abdict, lines, unusedMins, unusedMaxes = AB.CalcAbsAndLines( clustName + ' ' + starName, starData, ionList=[ion], modelAtms=modelAtms, pradks=pradks) if ion not in lines.keys(): # Hack for N if ion == 7.0: return 0, 0, 0, 0, 0 else: print( 'Ion:{0:2.1f} will not use CoG limits (possibly HFS).'.format( ion)) return 0, 0, 0, 0, 0 Xs = [line[5] for line in sorted(lines[ion], key=lambda l: l[0])] Ys = [ el.STR(line[2], line[1], starData[0]) for line in sorted(lines[ion], key=lambda l: l[0]) ] return sp.stats.linregress(Xs, Ys)
def GetSolarCorrections(ionList=[], modelAtms=None, pradks=None, useDAOSpec=False): # Returns a dictionary of lines (for the passed ion list) with their # abundance corrections, based on our line measurement of Solar spectra, # and the accepted abundance (from Asplund 2009). # If the passed ion list is empty, all corrected lines are returned. # # The three returned dictionaries: # solarCorrs:{ion:{wl:correction,...},...} # solarLines:{ion:[[wl, Ex.Pot., logGf, eqw, logRW, abund],...]} # weights: {ion:{wl:weight,...},...} solarCorrs = {} weights = {} # First, check to see if we already have calculated Solar abs allLines = LL.LookupSolarAbundances(ionList=ionList) if len(allLines.keys()) > 0: solarLines = allLines else: # Don't have them. So, calculate, and enter them. We have the line list # now. if modelAtms is None or pradks is None: modelPath = k.DwarfModelPath modelFiles = mk.findDataFiles(modelPath) modelAtms, pradks = mk.LoadModels(modelFiles) allLines = LL.getSolarLines(ionList, [(4500.,8750.)],\ useDAOSpec=useDAOSpec) # We need a slightly different form if we have to calculate the abs. compositeLines = sorted([[l[0], l[1], 'filler', 'filler', l[2]] \ for l in allLines], key = lambda l:(l[1], l[0])) unused1, solarLines, unused2, unused3 = AB.CalcAbsFromLines(\ compositeLines, k.SolarRefParms, star='SolarRef',\ ionList=ionList, modelAtms=modelAtms, pradks=pradks) # MOOG can fail, in which case, our dictionary will be empty: if len(solarLines.keys()) == 0: return {}, {}, {} # Now, put them into our DB, so we don't have to do this again. newLines = [] for ion in sorted(solarLines.keys()): newLines.extend([[k.SolarRefStarName, ion, l[0], l[1], l[2], l[3], l[4], l[5]] for l in solarLines[ion]]) LL.EnterCalibrationAbundances(newLines) if len(ionList) == 0: ionList = solarLines.keys() for ion in ionList: solarAb = PA.ptoe[int(ion)-1][PA.abIdx] solarCorrs[ion] = {} weights[ion] = {} if ion not in solarLines.keys(): continue for line in solarLines[ion]: correction = solarAb-line[5] solarCorrs[ion][line[0]] = correction weights[ion][line[0]] = GetRefLineWeight(correction) return solarCorrs, solarLines, weights
def GetDetectionLimit(starData, ion=26.0, modelAtms=None, pradks=None): # Returns the parameters for a line (using the same return parameters as # scipy.stats.linregress), representing the minimum detection limit for the # passed ion (default: FeI). Measurements below, and to the left of the line # are below the calculated detection limit of the spectra, and should be # ignored. # Since detection limit is affected by parameters, passing a stellar parameter # tuple of the form: (Teff, LogG, Vturb, [Fe/H], Giant mass (=0.0 for dwarfs)) # is needed. # The return parameters are (in order): # slope, intercept, r-value, p-value, stderr starName = k.MinDetFakeStarName clustName = k.CalibClusterName abdict, lines, unusedMin, unusedMax = AB.CalcAbsAndLines( clustName + ' ' + starName, starData, ionList=[ion], modelAtms=modelAtms, pradks=pradks) if ion not in lines.keys(): # Hack for N if ion == 7.0: return 0, 0, 0, 0, 0 else: print('Ion:{0:2.1f} will not use detection limits (possibly HFS).'. format(ion)) return 0, 0, 0, 0, 0 Xs = [line[5] for line in sorted(lines[ion], key=lambda l: l[0])] Ys = [ el.STR(line[2], line[1], starData[0]) for line in sorted(lines[ion], key=lambda l: l[0]) ] return sp.stats.linregress(Xs, Ys)
def PlotXPAbs(starData, clusterName='NGC-0752', ionList=kAllIonList, fileTag='', labelPlot=True, labelPoints=False, showTrendLine=False, modelAtms=None, pradks=None, referenceCorrect=False): # Make XP vs. Ab for the passed star # One element per plot. starName = starData[0] starParmTuple = tuple(starData[1:]) isGiant = RC.isGiantStar(starParmTuple) if isGiant: modelPath = k.GiantModelPath else: modelPath = k.DwarfModelPath if modelAtms == None or pradks == None: modelFiles = mk.findDataFiles(modelPath) modelAtms, pradks = mk.LoadModels(modelFiles) abdict, uncorrLines, unusedMin, unusedMax = \ AB.CalcAbsAndLines(clusterName+' '+starName, tuple(starData[1:6]), ionList=ionList, modelAtms=modelAtms, pradks=pradks) # uncorrLines: # # {elem.ion:[[Wavelength, Ex.Pot., logGf, eqw, logRW, abund],...]} if referenceCorrect: if isGiant: # Obligatory comment on bad Giant corrections, and using # Solar instead. correctDict, referenceLines, lineWeights = \ RC.GetSolarCorrections(ionList=ionList, modelAtms=modelAtms, pradks=pradks) else: correctDict, referenceLines, lineWeights = \ RC.GetDwarfCorrections(ionList=ionList, modelAtms=modelAtms, pradks=pradks) correctionsAvailable = False if len(correctDict) > 0 and len(referenceLines) > 0: correctionsAvailable = True for ion in ionList: if ion not in uncorrLines.keys(): continue # Does this element have NLTE corrections available? Note: we do # this BEFORE Solar corrections, which assumes that the same # NLTE corrections are already applied to any Solar corrections # we use. if ion in NLTEIons: LTELines = AB.CorrectNLTEAbs(ion, uncorrLines[ion], tuple(starData[1:6])) else: if ion in uncorrLines.keys(): LTELines = uncorrLines[ion] else: # Either synthesized lines, or none available LTELines = np.array([]) # Do we want the "reference corrected" abundances? if referenceCorrect and correctionsAvailable: tempAdj,tempAll = \ RC.SortAndFilterLines(LTELines, ion, tuple(starData[1:6]), solarCorrect=referenceCorrect, solarLines=referenceLines[ion], solarCorrs=correctDict[ion], lineWeights=lineWeights[ion]) # correctedLines: # [[ab, line STR score, wl, "quality"], ...] # allLines: # [[ab, line STR score, wl],...] # We want to use np.arrays, so... allLines = np.array(tempAll) correctedLines = np.array(tempAdj) if len(allLines) == 0 or len(correctedLines) == 0: correctionsAvailable = False elif len(allLines) == 1 or len(correctedLines) == 1: print('Single Line determination:{0}'.format(starData[0])) print(allLines, correctedLines) # One plot per ion. if labelPlot: plotLabel = 'XP vs Ab for [{2}/H] in {0} {1}.'.\ format(clusterName, starName, el.getIonName(ion)) else: plotLabel = '' if referenceCorrect and correctionsAvailable: tempPoints = [] for line in uncorrLines[ion]: correctedAbs = [l[0] for l in correctedLines if \ u.in_range(l[2],line[0]-0.05, line[0]+0.05)] if len(correctedAbs) > 0: tempPoints.append( [line[1], np.mean(correctedAbs), line[3], line[0]]) else: tempPoints.append([line[1], line[5], line[3], line[0]]) XPAbPoints = np.array(tempPoints) else: XPAbPoints = np.array([[line[1],line[5],line[3],line[0]]\ for line in uncorrLines[ion]]) if labelPoints: # Label the points with the wavelength pointLabels = ['{0:2.3f}'.format(point[3]) \ for point in XPAbPoints] else: pointLabels = None ps.XPAbPlot(XPAbPoints, starName, ion, fileTag=fileTag + 'XPAb', plotTitle=plotLabel, pointLabels=pointLabels, showTrendLine=showTrendLine)
def GetAbsForStar(starData, clusterName='NGC-0752', ions=kAllIonList, \ filterBlends=False, refCorrect=False,\ refLines=None, refDict=None, lineWeights=None, useDAOSpec=False, modelAtms=None, pradks=None): # Get all the elemental abundances for the single passed star (params): # starData: (starName, Teff, LogG, VTurb, Met, Mass) # # Returns a dictionary of {ion:(Ab, std, #lines, avg Quality)...} starName = starData[0] starParms = tuple(starData[1:6]) abdict, uncorrLines, unusedMins, unusedMaxes = \ AB.CalcAbsAndLines(clusterName+' '+starName, starParms, ionList=ions, modelAtms=modelAtms, pradks=pradks, useDAOlines=useDAOSpec) # uncorrLines: # # {elem.ion:[[Wavelength, Ex.Pot., logGf, eqw, logRW, abund],...]} starAbDict = {} ionLUT = sorted(list(abdict.keys())) ionLUT = sorted(set(ionLUT + [ion for ion in SynthAbsDict.keys()\ if starName in SynthAbsDict[ion].keys()])) if refCorrect and (refLines is None or refDict is None): refCorrect = False for ion in ionLUT: # We'll make one line per ion. Note, synth elements won't # necessarily have lines in the table/dict. if ion not in list(uncorrLines.keys()) + list(SynthAbsDict.keys()): continue if refCorrect and ion not in refLines.keys(): correctionsAvail = False else: correctionsAvail = True # Does this element have NLTE corrections available? Note: we do # this BEFORE Solar corrections, which assumes that the same # NLTE corrections are already applied to any Solar corrections # we use. if ion in NLTEIons: LTELines = AB.CorrectNLTEAbs(ion, uncorrLines[ion], starParms) else: if ion in uncorrLines.keys(): LTELines = uncorrLines[ion] else: # Either synthesized lines, or none available LTELines = np.array([]) # Now apply "Solar" or "Astrophysical Log gf" corrections, if requested if correctionsAvail and (refCorrect or filterBlends) and ion in uncorrLines.keys(): tempAdj,tempAll = \ RC.SortAndFilterLines(LTELines, ion, starParms, filterBlends=filterBlends, solarCorrect=refCorrect, solarLines=refLines[ion], solarCorrs=refDict[ion], lineWeights=lineWeights[ion]) # correctedLines: # [[ab, line STR score, wl, "quality"], ...] # allLines: # [[ab, line STR score, wl],...] # We want to use np.arrays, so... allLines = np.array(tempAll) correctedLines = np.array(tempAdj) else: if len(LTELines) > 0: allLines = np.array(\ [[l[5], el.STR(l[2], l[1], starParms[0]), l[0]] \ for l in LTELines]) correctedLines = np.array(\ [[l[0], l[1], l[2], k.AbWeights[-1]] \ for l in allLines]) else: allLines = correctedLines = np.array([]) if ion in SynthAbsDict.keys() and \ starName in SynthAbsDict[ion].keys(): # We have a C I or N I synthesis: if ion == 6.0: nonSynthLines = np.array([l for l in correctedLines\ if l[2]<7110 or l[2]>7120]) elif ion == 7.0: # N I synthesis uses both the 7115 region (giants) # and the 7442 region (all). Only the 7442 region # has a potentially measured line, tho. nonSynthLines = np.array([l for l in correctedLines \ if np.floor(l[2]) != 7442]) else: nonSynthLines = [] synthArray = GetSynthLines(starName, ion,\ clusterName=clusterName) # Returned tuple: (synthMean, synthStd, synthCount) # Include our synthesis as the number of lines measured if len(nonSynthLines) == 0: starAbDict[ion] = [synthArray[0], synthArray[1],\ synthArray[2],k.NoAbWeight] else: mean = np.mean(nonSynthLines[:, 0]) std = np.std(nonSynthLines[:, 0]) ct = len(nonSynthLines) totalCt = ct + synthArray[2] totalMean = ((mean*ct)+(synthArray[0]*synthArray[2]))/\ (totalCt) totalStd = np.sqrt(\ (ct*(totalMean-mean)**2+ synthArray[2]*(synthArray[0]-mean)**2)/\ (totalCt-1)) starAbDict[ion] = [totalMean, totalStd, totalCt, k.NoAbWeight] # We have already created the abundance dictionary for this ion, # so skip out on the next part. continue else: # No synthesized lines need to be added. pass if refCorrect and len(correctedLines) > 0: linesToCount = correctedLines avgQ = np.mean(correctedLines[:, 3]) else: linesToCount = allLines avgQ = k.NoAbWeight if len(linesToCount) > 0: starAbDict[ion] = [ np.mean(linesToCount[:, 0]), np.std(linesToCount[:, 0]), len(linesToCount), avgQ ] else: # No lines to count. Make sure we don't have an entry: if ion in starAbDict.keys(): del starAbDict[ion] return starAbDict
def PlotAbs(clusterName, starData, ions, fileTag='', plotTitle='', filterBlends=False, referenceCorrect=False, labelPoints=False, useDAOSpec=False, modelAtms=None, pradks=None): pointLabels = None # Lookup the abundance(s) for the passed star, and plot them # (into a .png file). One element per plot. starName = starData[0] starParmTuple = tuple(starData[1:6]) isGiant = RC.isGiantStar(starParmTuple) if isGiant: modelPath = k.GiantModelPath else: modelPath = k.DwarfModelPath if modelAtms == None or pradks == None: modelFiles = mk.findDataFiles(modelPath) modelAtms, pradks = mk.LoadModels(modelFiles) # uncorrLines format: # {elem.ion:[[Wavelength, Ex.Pot., logGf, eqw, logRW, abund],...]} # abDict format: # {elem.ion:[abundance mean, ab. std.dev., # lines]} abdict, uncorrLines, unusedMin, unusedMax = \ AB.CalcAbsAndLines(clusterName+' '+starName, starParmTuple, ionList=ions, modelAtms=modelAtms, pradks=pradks, useDAOlines=useDAOSpec) if isGiant: # Obtain the reference corrections for a giant star - Note: this is # badly broken! So, we're just going to use the Solar corrections for # now: correctDict, referenceLines, lineWeights = \ RC.GetDwarfCorrections(ionList=ions, modelAtms=modelAtms, pradks=pradks, useDAOSpec=useDAOSpec) # correctDict, referenceLines, lineWeights = \ # GetGiantCorrections(ionList=ions, # modelAtms=modelAtms, # pradks=pradks) else: # ...or for a dwarf star. correctDict, referenceLines, lineWeights = \ RC.GetDwarfCorrections(ionList=ions, modelAtms=modelAtms, pradks=pradks, useDAOSpec=useDAOSpec) for ion in ions: # We'll make one plot per ion. pointLabels = [] redData = greenData = blueData = [] if ion not in uncorrLines.keys(): print('No {0:2.1f} lines for {1}.'.format(ion, starName)) continue if (referenceCorrect or filterBlends) and \ ion in referenceLines.keys() and ion in correctDict.keys() and \ ion in lineWeights.keys(): adjustedLines, dataPoints = \ RC.SortAndFilterLines(uncorrLines[ion], ion, starParmTuple, filterBlends=filterBlends, solarCorrect=referenceCorrect, solarLines=referenceLines[ion], solarCorrs=correctDict[ion], lineWeights=lineWeights[ion]) tempData = [] for line in uncorrLines[ion]: corrLine = u.find(lambda l: l[0] == line[0], dataPoints) if corrLine is not None: tempData.append(corrLine) else: tempData.append([line[5], el.STR(line[2], line[1], \ starParmTuple[0]),line[0]]) redData = np.array(tempData) else: dataPoints = np.array([[line[5], el.STR(line[2], line[1], starParmTuple[0]), line[0]] \ for line in uncorrLines[ion]]) if labelPoints: pointLabels = ['{0:4.3f}'.format(line[2]) for line in dataPoints] pointLabels.extend(['{0:4.3f}'.\ format(line[2]) for line in redData]) pointLabels.extend(['{0:4.3f}'.\ format(line[2]) for line in greenData]) pointLabels.extend(['{0:4.3f}'.\ format(line[2]) for line in blueData]) loSlope, loIntercept, rv, pv, err = \ ps.GetDetectionLimit(starParmTuple, ion, modelAtms=modelAtms, pradks=pradks) hiSlope, hiIntercept, rv, pv, err = \ ps.GetCoGLimit(starParmTuple, ion, modelAtms=modelAtms, pradks=pradks) ps.AbSTRPlot(starName, ion, dataPoints, redSet=redData, greenSet=greenData, blueSet=blueData, lowLimit=(loSlope, loIntercept), hiLimit=(hiSlope, hiIntercept), fileTag=fileTag, plotTitle=plotTitle, labelPoints=pointLabels)
def COGPlot(clusterName, starData, ions, fileTag='', labelPlot=True, labelPoints=False, modelAtms=None, pradks=None, makeQualityPlot=False): # Make Curve of Growth (COG) plots for the passed star # One element per plot. # If the makeQualityPlot flag is set, a second plot will be made, with the # points shaded relative to how well they correspond to the linear fit of # measurements with similar excitation potentials. starName = starData[0] starParmTuple = tuple(starData[1:]) isGiant = RC.isGiantStar(starParmTuple) if isGiant: modelPath = k.GiantModelPath else: modelPath = k.DwarfModelPath if modelAtms == None or pradks == None: modelFiles = mk.findDataFiles(modelPath) modelAtms, pradks = mk.LoadModels(modelFiles) abdict, uncorrLines, unusedMin, unusedMax = \ AB.CalcAbsAndLines(clusterName+' '+starName, tuple(starData[1:6]), ionList=ions, modelAtms=modelAtms, pradks=pradks) for ion in ions: # One plot per ion. if ion not in uncorrLines.keys(): print('No {0:2.1f} lines for {1}.'.format(ion, starName)) continue if labelPlot: plotLabel = 'Curve of Growth for [{2}/H] in {0} {1}.'.\ format(clusterName, starName, el.getIonName(ion)) else: plotLabel = '' if labelPoints: # Label the points with the excitation potential pointLabels = ['{0:2.3f}'.format(point[1]) \ for point in np.array(uncorrLines[ion])] else: pointLabels = None ps.COGPlot(np.array(uncorrLines[ion]), starName, ion, fileTag=fileTag, plotTitle=plotLabel, pointLabels=pointLabels) if makeQualityPlot: if labelPlot: plotLabel = 'Measurement quality for [{2}/H] in {0} {1}.'.\ format(clusterName, starName, el.getIonName(ion)) else: plotLabel = '' ps.QualityPlot(np.array(uncorrLines[ion]), starName, ion, fileTag=fileTag + 'QP', plotTitle=plotLabel, pointLabels=pointLabels)
def MakeTeffPlots(clusterName='NGC-0752', starDataList=None, fileTag='', referenceCorrect=False): # Function plots Teff vs. slope of (XP vs. [Fe/H]) for a range of temperatures. # Intended to provide a spectroscopic confirmation/adjustment # for photometrically-determined parameters. We assume that at the "correct" # temperature, the slope of XP vs. [Fe/H] would be zero. if starDataList is None: starDataList = STP.GetAllStarParms(clusterName=clusterName) dModelFiles = mk.findDataFiles(k.DwarfModelPath) dModelAtms, dPradks = mk.LoadModels(dModelFiles) gModelFiles = mk.findDataFiles(k.GiantModelPath) gModelAtms, gPradks = mk.LoadModels(gModelFiles) # Map Fe I Ab vs. Ex pot. ions = [26.0] abDict = {} dTeffRange = np.linspace(5000, 7000, 81) gTeffRange = np.linspace(4000, 6000, 81) for star in starDataList: starName = star[0] isGiant = RC.isGiantStar(star[1:]) if isGiant: modelAtms = gModelAtms pradks = gPradks tRange = gTeffRange if referenceCorrect: correctDict, referenceLines, lineWeights = \ RC.GetGiantCorrections(ionList=ions, modelAtms=modelAtms, pradks=pradks) else: modelAtms = dModelAtms pradks = dPradks tRange = dTeffRange if referenceCorrect: correctDict, referenceLines, lineWeights = \ RC.GetDwarfCorrections(ionList=ions, modelAtms=modelAtms, pradks=pradks) logG = star[2] vTurb = star[3] met = star[4] slopes = [] for Teff in tRange: unusedDict, uncorrLines, unusedMin, unusedMax = \ AB.CalcAbsAndLines(clusterName+' '+starName, tuple([Teff, star[2], star[3], star[4], star[5]]),\ ionList=ions, modelAtms=modelAtms, pradks=pradks) XPs = [] Abs = [] if referenceCorrect: adjLines, allLines = RC.SortAndFilterLines( uncorrLines[26.0], 26.0, tuple([Teff, star[2], star[3], star[4], star[5]]), solarCorrect=True, solarLines=referenceLines[26.0], solarCorrs=correctDict[26.0], lineWeights=lineWeights[26.0]) if len(adjLines) > 10: # Assume 10 Fe I lines needed for nice plots for line in adjLines: # XP isn't returned, so have to do a lookup into the # original list. XPs.append( u.find(lambda l: l[0] == line[2], uncorrLines[26.0])[1]) Abs.append(line[0]) if len(XPs) == 0 or len(Abs) == 0: # Either no corrections, or corrections failed XPs = [line[1] for line in uncorrLines[26.0]] Abs = [line[5] for line in uncorrLines[26.0]] slope, intercept, rVal, pVal, err = sp.stats.linregress(XPs, Abs) slopes.append(slope) # We have to flip because interp expects the xvalues to be # monotomically increasing interpTs = np.interp([-0.02, 0., 0.02], slopes[::-1], tRange[::-1]) if interpTs[0] > interpTs[1]: minusR = interpTs[1] - interpTs[2] plusR = interpTs[0] - interpTs[1] else: minusR = interpTs[1] - interpTs[0] plusR = interpTs[2] - interpTs[1] fig = pyplot.figure() TeffLabel = r'$T_{{eff}} = {0:4.0f}^{{+{1:4.0f}}}_{{-{2:4.0f}}}$)'.\ format(interpTs[1],minusR,plusR) pyplot.scatter(tRange, slopes, label=TeffLabel) pyplot.axhline(0.0, linestyle=':') pyplot.legend() pyplot.savefig(k.ParmPlotDir + 'Teff/' + star[0] + fileTag + '_FeSlope.png') pyplot.close()