def PrintLineTableForCluster(cluster='NGC-0752'): starNames = SDB.GetStarsForCluster(cluster) allLineDict = {} for starName in starNames: starLines = LL.getLinesForStar(cluster, starName) for line in starLines: if (line[0], line[1]) not in allLineDict.keys(): allLineDict[(line[0], line[1])] = 1 else: allLineDict[(line[0], line[1])] += 1 for line in sorted(allLineDict.keys()): lookup, refs = LL.getLines(elements=[line[0]], \ wavelengthRange=[(line[1]-k.LambdaVarianceLimit, \ line[1]+k.LambdaVarianceLimit)],\ dataFormat=None) if len(lookup) == 0: continue luline = lookup[0] print( k.LLTableFormat.format(line[1], line[0], luline[2], luline[3], allLineDict[(line[0], line[1])], luline[5]))
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 getLinesForApertures(lineFilename, apBoundaries=None): # Function: getLinesForApertures(lineFilename, apBoundaries): # # Inputs: lineFilename: A file containing information on lines to measure - can be "None" # apBoundaries: A list of a spectra's aperture boundaries - used to determine # the spectral range for line lookup into the DB # # Outputs: EQWidths - A list of the measured widths, each tuple of the form: # (wl, ion, ep, gf, c6, eqw, meas. wl., fwhm (gaussian)). # badLines : A list of lines we were unable to measure. Each element is a # tuple of the form: (wl, ion) # dopplerCount : A count of the 'good' lines measured with a Doppler shift # dopplerCheck : A running total of the Doppler shifts of the good lines # # Desription: This function automatically measures the equivalent width of each line # in the passed list. if lineFilename != None: lineData = MI.ReadMOOGLineFile(lineFilename) refs = [ ] # Note: It is POSSIBLE to do line lookups into our DB to find # references for the lines in the passed file, but we have no # way of insuring that the data in the file is actually from our # DB's references....Thus the empty reference list when passed # a line file. elif apBoundaries != None: minWL = min([x[0] for x in apBoundaries]) maxWL = max([x[1] for x in apBoundaries]) lineData, refs = LL.getLines(elements=[], wavelengthRange=[(minWL, maxWL)], dataFormat=None) else: lineData = [] refs = [] return lineData, refs
def MakeTvsEPPlots(fileTag=''): feiFiles = glob.glob( "/home/mikelum/Dropbox/CodeCloud/MyTools/ClusterAnalysis/Tables/FeI_{0}.tab" .format(fileTag)) tiiFile = glob.glob( "/home/mikelum/Dropbox/CodeCloud/MyTools/ClusterAnalysis/Tables/TiI_{0}.tab" .format(fileTag)) ion = 26.0 ionStr = 'FeI' if len(feiFiles) > 0: fp = open(feiFiles[0], 'r') lines = fp.readlines() fp.close() starNames = [l.replace(" ", "") for l in lines[0].split(',')[1:-1]] wls = [float(l.split(',')[0]) for l in lines[3:-1]] abundSTR = [l.split(',')[1:-1] for l in lines[3:-1]] abunds = np.array([[float(b) for b in a] for a in abundSTR]) for ab, starName in zip(abunds.T, starNames): lines = [(w, x) for (w, x) in zip(wls, ab) if x > 0] if len(lines) == 0: continue epAbs = [] for wl, ab in lines: info, refs = LL.getLines( elements=[26.0], wavelengthRange=[[wl - 0.100, wl + 0.100]], dataFormat='') if len(info) > 0: epAbs.append([info[0][2], ab]) # logg = parmLUT[starName][1 # teff = parmLUT[starName][0] fig = pyplot.figure() axes = pyplot.gca() Xs = [p[0] for p in epAbs] Ys = [p[1] for p in epAbs] avg = np.mean(Ys) std = np.std(Ys) axes.scatter(Xs, Ys, marker='.', color='b') axes.axhline(y=avg, ls='dashed', color='b') axes.axhline(y=avg - std, ls='dotted', color='b') axes.axhline(y=avg + std, ls='dotted', color='b') axes.set_ylim([avg - 1.1, avg + 1.1]) axes.set_xlabel(r'[FeI/H]') axes.set_ylabel('Ex.Pot. (eV)') axes.set_title(starName) pyplot.savefig('{0}{1}_{2}{3}.png'.format( '/home/mikelum/Dropbox/CodeCloud/MyTools/ClusterAnalysis/Plots/TvsEP/', starName, ionStr, fileTag), figure=fig) # pyplot.show() pyplot.close(fig)
def MakeMOOGEQWLogFile(lines, logFilename=k.MOOGTempLogName): try: logFile = open(logFilename,'w') logFile.write('# Temporary MOOG log file created: {0}\n'.format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))) except IOError: print('Unable to open MOOG script file: {0} for writing. Exiting.'.format(logFilename)) sys.exit() for line in lines: lineInfo, lineRefs = LL.getLines(elements=[line[0]], wavelengthRange=[(line[1]-0.05, line[1]+0.05)], dataFormat=None) if len(lineInfo) > 0: try: logFile.write(k.MOOGListFormat.\ format(line[1],line[0],lineInfo[0][2],lineInfo[0][3],lineInfo[0][4],line[4],'\n')) except ValueError: # Some unicode strings snuck in as floats...try typecasting logFile.write(k.MOOGListFormat.\ format(float(line[1]),float(line[0]),float(lineInfo[0][2]),float(lineInfo[0][3]),float(lineInfo[0][4]),float(line[4]),'\n')) logFile.close() return logFilename
def MakeStarLineTable(starNames=[], clusterID='NGC-0752', ions=[], \ wls=[4500,8500], outFile=d.TableDir+'Table1.dat'): if len(starNames) == 0: starNames = SDB.GetStarsForCluster(clusterID) fp = open(outFile, 'w') fmt, db = BuildFormatString(table1Format, DataLabels=table1Labels, DataUnits=table1Units, DataExp=table1Desc, MakeBbBDesc=True) fp.write(db) for sn in starNames: lines = LL.getLinesForStar(clusterID, sn, elements=ions, wavelengthRange=wls, dataFormat=None) starID = int(sn[4:]) for l in lines: fp.write(fmt.format(starID, l[0], l[1], l[4]) + '\n') fp.close()
def MakeLineListTable(ions=[], wls=[], outFile=d.TableDir + 'Table2.dat'): lines, refs = LL.getLines(elements=ions, wavelengthRange=wls,\ dataFormat=None) unused1, unused2, weights = RC.GetSolarCorrections() fp = open(outFile, 'w') fmt, db = BuildFormatString(table2Format, DataLabels=table2Labels, DataUnits=table2Units, DataExp=table2Desc, MakeBbBDesc=True) fp.write(db) allIons = sorted(set(np.array(lines)[:, 1])) lineData = [] for i in allIons: ion = np.round(i, decimals=1) (elem, state, unused) = el.getIonState(el.getIonName(ion)) ionLines = np.array( sorted([l for l in lines if np.round(l[1], decimals=1) == ion], key=lambda l: l[0])) for line in ionLines: wl = np.round(line[0], decimals=3) xp = line[2] gf = line[3] if gf < 0: gfs = '-' else: gfs = '' gf = abs(gf) try: qu = int(weights[ion][wl]) except KeyError: qu = 0 rf = line[5] lineData.append([elem, state, wl, xp, gfs, gf, qu, rf]) for line in lineData: fp.write(fmt.format(*line) + '\n') fp.close()
def measureVoigtEQWs(specFilename, lineData=None): global gBaselineSpline objectName, numApertures, apertureSize, apBoundaries = SD.ReadSpectraInfo( specFilename) specList = SD.LoadAndSmoothSpectra(specFilename) measuredLines = [] badLines = [] dopplerCheck = 0. dopplerCount = 0. for smoothSpec in specList: wls = smoothSpec[0] fluxes = smoothSpec[1] if not SD.IsGoodAperture(smoothSpec): continue if lineData == None: lineData, unusedRefs = LL.getLines(wavelengthRange=[(wls[0], wls[-1])], dataFormat=None) for line in lineData: # lineData entries are variable length, depending on how many # optional parameters are included. So, we have to manually assign # the ones we need. wl = line[0] ion = line[1] ep = line[2] gf = line[3] if len(line) > 4: # This is the only optional parameter we might need. c6 = line[4] else: c6 = '' wlUpper = wl + k.HalfWindowWidth wlLower = wl - k.HalfWindowWidth loApPix = u.closestIndex(wls, wlLower) centerPix = u.closestIndex(wls, wl) hiApPix = u.closestIndex(wls, wlUpper) # The algorithm for curve_fit needs values "close" to one. So, # we normalize our wavelength-calibrated spectrum window to be # centered at "0", and range from -2 to +2. We also assume that # our continuum normalized spectrum has amplitudes near 1. x = wls[loApPix:hiApPix + 1] - wls[centerPix] y = fluxes[loApPix:hiApPix + 1] # At some point, I suppose we should use the actual S/N of the spectra # for the error array... e = 0.01 * np.ones(len(x)) if len(x) < 10: # Line is too close to the order boundary, so skip it badLines.append([ wl, ion, k.badSpectralRegion, 'Too close to order boundary' ]) continue try: # Because of the normalization needed for curve fitting, we need to perform # some shenanigans on the baseline spline rawSpline = BL.FitSplineBaseline(loApPix, hiApPix, smoothSpec) gBaselineSpline = interp.UnivariateSpline( x, rawSpline(x + wls[centerPix])) except: # We've had errors when trying to fit bad regions - just skip to the next one print('Fitting error for {0:4.3f}, ({1:2.1f}).'.format( wl, ion)) continue maxIdxs = [ mx for mx in sig.argrelmax(y)[0].tolist() if y[mx] < 2.5 ] maxIdxs.append(0) maxIdxs.sort() maxIdxs.append(-1) minIdxs = [ mn for mn in sig.argrelmin(y)[0].tolist() if y[mn] > 0.05 ] minIdxs.append(0) minIdxs.sort() minIdxs.append(-1) mins = [x[idx] for idx in minIdxs] closestMin = mins.index(u.closest(0, mins)) # Find the two local maxes which bracket the central wavelength (offset=0.) maxRange = u.bracket(0., [x[idx] for idx in maxIdxs]) fitRange = [maxIdxs[maxRange[0]], maxIdxs[maxRange[1]] + 1] # guess that the scale is the difference between the local max and mins, # although we could opt to use the difference between the continuum and the min... scaleGuess = (y[maxIdxs[maxRange[0]]] + y[maxIdxs[maxRange[1]]] ) / 2. - y[minIdxs[closestMin]] # The line center should occur at, or really close to the offset offsetGuess = 0.0 # Gamma and sigma are Lorenzian and Gaussian parms...need a better idea for guesses gammaGuess = 0.10 sigmaGuess = 0.10 # The core fraction is basically how much of the line is "pure" Gaussian coreFractionGuess = 0.65 guessParms = [ scaleGuess, offsetGuess, gammaGuess, sigmaGuess, coreFractionGuess ] curveFitted = False if len(x[fitRange[0]:fitRange[1]]) < 5: # Way too few points to even do an interpolation. Try extending to the # two "maxes" on either side of the current loRange = 0 if maxRange[0] < 2 else maxRange[0] - 1 hiRange = -1 if maxRange[1] > len( maxIdxs) - 2 or maxRange[1] == -1 else maxRange[1] + 1 fitRange = [ maxIdxs[loRange], maxIdxs[hiRange] + 1 if hiRange > 0 else -1 ] xToFit = x[fitRange[0]:fitRange[1]] yToFit = y[fitRange[0]:fitRange[1]] eToFit = e[fitRange[0]:fitRange[1]] if len(x[fitRange[0]:fitRange[1]]) < 5: # STILL too small of a range - Bail, with a bad line. badLines.append( [wl, ion, k.badSpectralRegion, 'Line region too small']) continue if len(x[fitRange[0]:fitRange[1]]) < 20: # Too few points? # Try adding more (interpolated) data points, along a spline if fitRange[1] == -1: ySpline = interp.interp1d(x[fitRange[0]:], y[fitRange[0]:], kind='cubic') eSpline = interp.interp1d(x[fitRange[0]:], e[fitRange[0]:], kind='cubic') xToFit = np.linspace(x[fitRange[0]], x[-1], num=100) yToFit = ySpline(xToFit) eToFit = eSpline(xToFit) else: ySpline = interp.interp1d(x[fitRange[0]:fitRange[1] + 1], y[fitRange[0]:fitRange[1] + 1], kind='cubic') eSpline = interp.interp1d(x[fitRange[0]:fitRange[1] + 1], e[fitRange[0]:fitRange[1] + 1], kind='cubic') xToFit = np.linspace(x[fitRange[0]], x[fitRange[1]], num=100) yToFit = ySpline(xToFit) eToFit = eSpline(xToFit) else: xToFit = x[fitRange[0]:fitRange[1]] yToFit = y[fitRange[0]:fitRange[1]] eToFit = e[fitRange[0]:fitRange[1]] try: popt, pcov = fitCurve(xToFit, yToFit, eToFit, guessParms) if popt[4] > 0. and popt[4] < 1.: # Even though we have a fit, if it's "pure" Lorenzian or Gaussian # pretend we can do better with some data manipulation curveFitted = True # else: # curveFitted = False except RuntimeError: # Occurs when curve_fit cannot converge (Line not fit) # We need to catch it in order to try our own data manipulation # for a good fit. # print('Initial fit failure for line at: %.3f' % wl) pass if not curveFitted: # Try to refit, by addressing typical issue(s) # Assume not enough of the line is visible (no wings) # Add ~10 points along the continuum, in the "wings" loBound = fitRange[0] - 5 if loBound < 0: loBound = 0 if fitRange[1] == -1: hiBound = -1 newY = np.concatenate( (gBaselineSpline(x[loBound:fitRange[0]]), y[fitRange[0]:hiBound])) else: hiBound = fitRange[1] + 5 if hiBound > len(x) - 1: hiBound = -1 newY = np.concatenate( (gBaselineSpline(x[loBound:fitRange[0]]), y[fitRange[0]:fitRange[1] + 1], gBaselineSpline(x[fitRange[1] + 1:hiBound]))) newX = x[loBound:hiBound] newE = e[loBound:hiBound] if len(newX) != len(newY) or len(newX) != len(newE): # Probably just have an indexing problem in the code immediately above, # but just skip the line for now. badLines.append([wl, ion, k.unknownReason, k.unknownStr]) continue try: popt, pcov = fitCurve(newX, newY, newE, guessParms) # As long as something fits, here - we'll take it. curveFitted = True except RuntimeError: # Well, and truly screwed? # pyplot.errorbar(x+smoothSpec.xarr[centerPix], y, yerr=e, linewidth=1, color='black', fmt='none') # pyplot.show() badLines.append([wl, ion, k.unknownReason, k.unknownStr]) continue eqw, eqwError = calcEQW(popt, pcov, x) # showLineFit(popt, pcov, x, y, e, smoothSpec.xarr[centerPix].value) # FWHM of the Gaussian core is sqrt(8*ln(2))*sigma = 2.355*sigma # The factor of 1000 is to convert to miliangstroms. measuredLines.append( [wl, ion, ep, gf, c6, eqw, wl + popt[1], 2355. * popt[3]]) thisDoppler = ((popt[1]) / wl) * k.SpeedofLight dopplerCheck += thisDoppler dopplerCount += 1 # if popt[4] <= 0.: # print('Best fit is a pure Lorenzian') # elif popt[4] >= 1.: # print('Best fit is a pure Gaussian') # nans = len([x for x in measuredLines if np.isnan(x[5])]) badLines.extend([[x[0], x[1], k.badEQWMValue, k.nanStr] for x in measuredLines if np.isnan(x[5])]) badLines.extend( [[x[0], x[1], k.emissionLine, "Emission strength: %.3f" % (abs(x[5]))] for x in measuredLines if x[5] < 0.]) badLines.extend([[ x[0], x[1], k.centerWLOff, "Line center delta: %.3f" % (x[0] - x[6]) ] for x in measuredLines if abs(x[0] - x[6]) > 0.25]) # print("nan eqw: %d - emission: %d - bad offset: %d" % (nans, emiss, bads)) # # for line in sorted(measuredLines, key=lambda x: x[0]): # if not np.isnan(line[5]) and line[5]>0. and abs(line[0]-line[6]) < 0.25: # print("%2.1f :%4.3f(%4.3f) = %.1f (%.1f)" % (line[1], line[0], line[6], line[5], line[7])) # Measured WL within 250mA of expected, non-emission lines, only filteredLines = [ l for l in measuredLines if not np.isnan(l[5]) and l[5] > 0. and abs(l[0] - l[6]) < 0.25 ] # Want ascending wls within ascending ions filteredLines.sort(key=lambda x: x[0]) filteredLines.sort(key=lambda x: x[1]) # print "*****************" # print "*******{0} bad *********".format(len(badLines)) # print "*******{0} measured *********".format(len(measuredLines)) # print "*******{0} filtered **********".format(len(filteredLines)) return filteredLines, badLines, dopplerCount, dopplerCheck
def makeListFiles(spectraFile, listDirectory, remeasure=False): # Function takes the passed spectra, and builds a list of lines # which should lie within the spectra's orders. Due to a problem # with the automated measuring process, the list is split into # multiple files, with no more than 200 in each list. # Output lists are stored in the passed directory, with the full # path name(s) returned. # The remeasure parameter tells whether to re-measure a line for # a given spectra, if it has been measured _FOR THE PASSED FILE_ # previously. lineListFileList = [] # Determine wavelength range coverage of this spectra objectName, numApertures, apertureSize, apBoundaries = SD.ReadSpectraInfo( spectraFile) # Remove the 'bad' regions newRegions = [] for mask in k.BadSpectralRegions: for actual in apBoundaries: if mask[1] < actual[0] or mask[0] > actual[1]: # No overlap pass elif mask[0] < actual[0] and mask[1] > actual[1]: # Complete overlap apBoundaries.remove(actual) elif mask[1] < actual[1]: # Overlap on the low side of the actual: if mask[0] > actual[0]: # Mask is internal to the region - need to split apBoundaries.append((actual[0], mask[0])) apBoundaries.append((mask[1], actual[1])) apBoundaries.remove(actual) else: # mask[0] > actual[0] # Overlap on the high side of the actual: apBoundaries.append((actual[0], mask[0])) apBoundaries.remove(actual) # Do the lookup (return format is in 'MOOG' text format) lineData, refList = ll.getLines([], apBoundaries, dataFormat='') # We do not want to re-measure lines (or maybe we do...?) if remeasure: measuredLines = [] else: measuredLines = ll.getMeasuredLines(spectraFile) unmeasuredLines = [ line for line in lineData if (line[1], line[0]) not in measuredLines ] # Eliminate duplicate lines from overlapping windows and # sort the returned lines by wavelength sortedLines = sorted(set(unmeasuredLines), key=lambda line: line[0]) # Write the list out in 75 line segments. This is done to prevent the memory leak # issue in pyspeckit, when a large number of lines are measured. indexList = range(0, len(sortedLines), k.MaxLineListFileLength) lineListFileList = [] for index, lineIndex in enumerate(indexList): # Note: For mp runs, we'll want to add a time or proc.# stamp to the log file name # File enumeration is 1-based for readability lineListFilename = listDirectory + k.TempListFileHead + '{0}'.format( index + 1) + k.TempListFileExt firstLine = '# Line list ({0}/{1}) for spectra:{2} Created:{3}\n'.format( index + 1, len(indexList), spectraFile, dt.datetime.now().strftime("%Y/%m/%d %H:%M:%S")) try: outfile = open(lineListFilename, 'w') except IOError: errorOutput( 'Unable to open:{0} for writing'.format(lineListFilename)) outfile.write(firstLine) if index < len(sortedLines) - k.MaxLineListFileLength: outputList = sortedLines[lineIndex:lineIndex + k.MaxLineListFileLength] else: outputList = sortedLines[lineIndex:] for lineData in outputList: outfile.write( k.MOOGListFormat.format(lineData[0], lineData[1], lineData[2], lineData[3], lineData[4], 99.9, lineData[5]) + '\n') outfile.close() lineListFileList.append(lineListFilename) # Return our list of list files return lineListFileList
def determineParms(guessTuple, kuruczAtms, pradks, mass, clustMetal = 0.0, clustAge = 4500, logFile=None, starname=None, photoGuess = True, refDict=None): # External entry point to determine the atmospheric parameters (Teff, # LogG, VTurb, [Fe/H]) for a star, given a set of absorption line # measures, and a starting "guess" - usually made # from photometry. If the photoGuess flag is set, we operate on # the assumption that the passed guess is based on tangible # data, and will use a Gaussian distribution around the Teff # value (sigma=200K) as a (quasi-) Baysian prior. Otherwise # we just use the guess as a starting point and have no # prior for the Teff value. # # Params are determined for the star passed, either by name # (In the form "NGC-XXXX IIII-YYYY"), or by a "log" file, containing # the lines for the star. # Needless to say, we need at least one of them to make a determination assert logFile != None or starname != None # Testing parameter visibility error paramScoreList = [] metallicityList = np.array([]) (tGuess, logGGuess, vTurbGuess) = guessTuple bestT = tGuess bestG = logGGuess bestV = vTurbGuess bestM = clustMetal # We're going to give some credit to the original guess. Let's try # a normal distribution around the guess with a sigma of 200K if photoGuess: photoDist = stats.norm(tGuess,k.AtmTGuessSigma) photoGuessPDF = photoDist.pdf(tGuess) # For normalization else: vTurbDist = stats.gamma(2.5) vTurbDistMax = max([vTurbDist.pdf(i) for i in np.arange(0., 6., 0.04)]) # photoProb = 1.0 probKDEs = {} probKDE = getProbKDE(clustMetal, clustAge, tGuess) probKDEs[clustMetal] = probKDE # Something (probably MOOG - FORTRAN is dumb!) is choking on long file path names if logFile!=None and len(logFile) > 32: shortLogFilename = k.MOOGTempLogName if os.path.isfile(shortLogFilename): if not os.path.samefile(shortLogFilename, logFile): os.remove(shortLogFilename) else: shutil.copy2(logFile, shortLogFilename) elif logFile == None: # No log file, so look up the lines in the DB, and make one. clusterID = starname.split()[0] starID = starname.split()[1] lines = LL.getLinesForStar(clusterID, starID, elements=[22.0, 22.1, 26.0, 26.1], dataFormat='MOOG') shortLogFilename = k.MOOGTempLogName with open(shortLogFilename, 'w') as logFile: fileHeader = '# Temporary line log file for {0} {1}\n'.format(clusterID, starID) logFile.write(fileHeader) for line in lines: logFile.write(line+'\n') else: shortLogFilename = logFile # Copy the list by slicing, since we will be using "pop" varLimitList = k.EQWAbVariances[:] # Any line with a starting abundance two orders off from the rest # of the lines needs to be tossed immediately. #currentVarLimit = 2.0 # Initial read and prune of the line log tempFileHead = 'temp_{0:05d}_{1:06d}'.format(os.getpid(),datetime.datetime.now().microsecond) modelFilename=tempFileHead+'.m' with open("mylogFile.txt", 'a') as myLogFile: myLogFile.write('New star: {0} {1}\n'.format(clusterID, starID)) for currentVarLimit in varLimitList: # Calculate starting abundances, based on the current model myLogFile.write(" Starting new abundance limit: % 1.3f\n" % currentVarLimit) thisModel, temppradk = mk.MakeAtmModel(kuruczAtms, pradks, bestT, bestG, bestM, bestV, mass) MI.WriteMOOGAtmModel(thisModel, modelFilename, bestT, bestG, bestM, bestV) # mk.WriteMOOGModel(thisModel, modelFilename, bestT, bestG, bestM, bestV) lines = MI.GetMOOGAbs(shortLogFilename, modelFilename) FeILines, FeIILines, TiILines, TiIILines = selectLines(lines, kuruczAtms, pradks, (bestT, bestG), varLimit=currentVarLimit, enforceMin=True, refDict=refDict) if len(FeILines)+len(FeIILines)+len(TiILines)+len(TiIILines) <1: myLogFile.write(" Excessive abundance {1:1.3f} filtering: {0:3d} lines.\n".format(len(lines), currentVarLimit)) break #currentVarLimit = 0. #continue # Clean up old temp files u.clearFiles(glob.glob(tempFileHead[:-7]+'*')) # Once we pare the line list, don't deal with the other lines, anymore linesToLog = getMOOGLisLines(FeILines, FeIILines, TiILines, TiIILines) headStr = '# Line width measures pared from:{0} on: '.format(shortLogFilename)+datetime.datetime.now().isoformat() # Had an error in the output at one point. This code just left in # "just in case". try: np.savetxt(fname=shortLogFilename, X=linesToLog, fmt=k.MOOGLogFormat, header=headStr) except ValueError: print ("--------- {0:1.3f} --------------".format(currentVarLimit)) print (linesToLog) print ("Error in lines parameter") exit(0) # Take big steps first, then try smaller ones for stepSize in [10., 5., 2., 1.]: myLogFile.write(' New Step: T:+/-{0:3.0f}; LogG: +/-{1:1.3f}; VTurb:+/-{2:1.3f}\n'.format(stepSize*k.TeffStep, stepSize*k.LogGStep, stepSize*k.VTurbStep)) slopeProb, balanceProb = evaluateParams(FeILines, FeIILines, TiILines, TiIILines) # Need to verify that our isochrone is at least close to our # current guess' metallicity. isoMets = probKDEs.keys() closestIsoMet = min(range(len(isoMets)), key=lambda i: abs(isoMets[i]-bestM)) probKDE = probKDEs[isoMets[closestIsoMet]] if abs(isoMets[closestIsoMet]-bestM) > 0.50: # Need a new isochrone: if bestM > k.MaxParsecMetal: if k.MaxParsecMetal in isoMets: probKDE = probKDEs[k.MaxParsecMetal] else: myLogFile.write('***** New Isochrone: T:{0:3.0f}; M:{1:+1.3f}({2:+1.3f})\n'.format(bestT, bestM, k.MaxParsecMetal)) probKDE = getProbKDE(k.MaxParsecMetal, clustAge, bestT) probKDEs[k.MaxParsecMetal] = probKDE else: myLogFile.write('***** New Isochrone: T:{0:3.0f}; M:{1:+1.3f}\n'.format(bestT, bestM)) probKDE = getProbKDE(bestM, clustAge, bestT) probKDEs[bestM] = probKDE isoProb = IP.GetProb((bestT, bestG), probKDE) if photoGuess: photoProb = photoDist.pdf(bestT)/photoGuessPDF else: photoProb = (vTurbDist.pdf(bestV)/vTurbDistMax)**3 scores = [slopeProb, balanceProb, isoProb, photoProb] myLogFile.write(' Current guess: T:{0:4.0f} - G:{1:1.3f} - V:{2:1.3f} - M:{3:+1.3f}: {4:1.4e} * {5:1.4e} * {6:1.3e} * {7:1.3e} = {8:1.3e}\n'.format(bestT, bestG, bestV, bestM, slopeProb, balanceProb, isoProb, photoProb, np.product([x for x in scores if not x==0.]))) bestStep = -1 lastScore = 0. lastParms = (bestT, bestG, bestV, bestM) while bestStep != 0: # When we first enter, the best step is always the first, signified by # a "bestStep" value of "-1", which we need to change to "0". We can't # start the loop with the actual index, for obvious reasons. if bestStep < 0: bestStep = 0 paramScoreList = [] metallicityList = np.array([]) # Get the scores for our current guess and # the seven potential steps around it. guessArray = np.array([(bestT+stepSize*item[0], bestG+stepSize*item[1], bestV+stepSize*item[2]) for item in k.ParmStepArray]) for (thisT, thisG, thisV) in guessArray: # New step in the range of our models? if not mk.parms_in_range(teff=thisT, logg=thisG, vturb=thisV): myLogFile.write(' *** Parmeter out of range: Continuing.\n') paramScoreList.append((-1., -1., -1.)) metallicityList = np.append(metallicityList, thisM) continue tempFileHead = 'temp_{0:05d}_{1:06d}'.format(os.getpid(),datetime.datetime.now().microsecond) modelFilename=tempFileHead+'.m' thisModel, temppradk = mk.MakeAtmModel(kuruczAtms, pradks, thisT, thisG, bestM, thisV) MI.WriteMOOGAtmModel(thisModel, modelFilename, thisT, thisG, bestM, thisV) # We re-read (but do not re-prune!) the lines for every guess, since abundances change # Note: the data format is different from above. lines = MI.GetMOOGAbs(shortLogFilename, modelFilename) FeILines, FeIILines, TiILines, TiIILines = selectLines(lines, kuruczAtms, pradks, (thisT, thisG), varLimit=currentVarLimit, enforceMin=True, refDict=refDict) # Clean up our temp files u.clearFiles(glob.glob(tempFileHead[:-7]+'*')) if len(FeILines) < 7: if len(FeILines) == 0 or not u.is_list(FeILines): # Most frequently, we will have zero FeI lines as a # result of a failed MOOG step. We'll just record a # "bad" probability and continue with our guesses. myLogFile.write(' *** 0/1 FeI Lines: Continuing.\n') paramScoreList.append((-1., -1, -1.)) metallicityList = np.append(metallicityList, thisM) continue elif u.is_list(FeILines): myLogFile.write(' *** {0:1d} FeI Lines: FeI[0]:{1}\n'.format(len(FeILines), repr(FeILines[0]))) else: myLogFile.write(' *** {0:1d} FeI Lines: Continuing.\n'.format(len(FeILines))) # We were getting a bug where the FeILines list # (of lists) only had one member, and wasn't showing as # a list of a single list of lines. Rather it was just # showing up as a single list of line parameters. This # ugliness was desinged to figure out what was going on # and can probably be removed...eventually, once I am # convinced that the original issue is fixed. try: thisM = np.mean(np.array(FeILines)[:,5])-k.SolarFeH except (IndexError, TypeError): myLogFile.write(' Error, in FeILines indexing\n') myLogFile.write(' --------------------\n') for line in FeILines: print (line) print('--------------------') print(FeIILines) print('--------------------') print(TiILines) print('--------------------') print(TiIILines) #raw_input('Hit return to continue') continue # If the new params create a different metallicity value, recalculate # with a "compromise" value if abs(thisM-bestM) > 0.03: newM = bestM + (thisM-bestM)/2 tempFileHead = 'temp_{0:05d}_{1:06d}'.format(os.getpid(),datetime.datetime.now().microsecond) modelFilename=tempFileHead+'.m' thisModel, temppradk = mk.MakeAtmModel(kuruczAtms, pradks, thisT, thisG, newM, thisV) MI.WriteMOOGAtmModel(thisModel, modelFilename, thisT, thisG, newM, thisV) # mk.WriteMOOGModel(thisModel, modelFilename, thisT, thisG, newM, thisV) lines = MI.GetMOOGAbs(shortLogFilename, modelFilename) u.clearFiles(glob.glob(tempFileHead[:-7]+'*')) FeILines, FeIILines, TiILines, TiIILines = selectLines(lines, kuruczAtms, pradks, (thisT, thisG), varLimit=currentVarLimit, enforceMin=True, refDict=refDict) slopeProb, balanceProb = evaluateParams(FeILines, FeIILines, TiILines, TiIILines) isoProb = IP.GetProb((thisT, thisG), probKDE) if photoGuess: photoProb = photoDist.pdf(thisT)/photoGuessPDF else: photoProb = (vTurbDist.pdf(thisV)/vTurbDistMax)**3 scores = [slopeProb, balanceProb, isoProb, photoProb] myLogFile.write(' Evaluating: T:{0:4.0f} - G:{1:1.3f} - V:{2:1.3f} - M:{3:+1.3f}: {4:1.4e} * {5:1.4e} * {6:1.3e} * {7:1.3e} = {8:1.3e}\n'.format(thisT, thisG, thisV, thisM, slopeProb, balanceProb, isoProb, photoProb, np.product([x for x in scores if not x==0.]))) # Place a tuple of "probabilities" (insert P-score discussion, here) # for later evaluation paramScoreList.append((isoProb, slopeProb, balanceProb, photoProb)) metallicityList = np.append(metallicityList, thisM) # Select the guess with the highest score. # If it's our current point, then the loop will continue, # tighten the abundance variance restriction, and re-measure. newT, newG, newV, newM, newStep = evaluatePoints(paramScoreList, metallicityList, guessArray, bestM) if (newT, newG, newV, lastParms[3]) == lastParms: # We're oscillating between two steps. Don't bestStep = 0 else: lastParms = (bestT, bestG, bestV, bestM) bestT, bestG, bestV, bestM, bestStep = newT, newG, newV, newM, newStep try: time.sleep(0.1) myLogFile.write(' Best guess: T:{0:4.0f} - G:{1:1.3f} - V:{2:1.3f} M:{3:+1.3f}\n'.format(bestT, bestG, bestV, bestM)) time.sleep(0.1) myLogFile.flush() except IOError: # We can actually try flushing/writing before the last write has # completed. If this is the case, don't sweat it. We'll just get # it next time. # just "Pass-ing" doesn't seem to be ignoring the exception, so... print("Error - Handling") # pass # If our current point has re-evaluated to be a lower score # than our previous, go back! newScore = paramScoreList[newStep][0]*paramScoreList[newStep][1]*paramScoreList[newStep][2]*paramScoreList[newStep][3] if newScore <= lastScore and newScore > 0.: # Restore the last, best parms, exit this iteration and # either force the next paring, or end the run (bestT, bestG, bestV, bestM) = lastParms try: myLogFile.write(' Going back!: T:{0:4.0f} - G:{1:1.3f} - V:{2:1.3f} - M:{3:+1.3f} - ({4:1.3e} - {5:1.3e})\n'.format(bestT, bestG, bestV, bestM, newScore, lastScore)) except IOError: time.sleep(0.5) myLogFile.flush() myLogFile.write(' Going back!: T:{0:4.0f} - G:{1:1.3f} - V:{2:1.3f} - M:{3:+1.3f} - ({4:1.3e} - {5:1.3e})\n'.format(bestT, bestG, bestV, bestM, newScore, lastScore)) pass bestStep = 0 lastScore = 0. continue else: time.sleep(0.5) lastScore = newScore # This worked most of the time, but occasionally would spawn an IOError, so changed it to a for loop # # Get the next Ab. limit, and carry on. # if currentVarLimit > 0.0: # currentVarLimit = varLimitList.pop(0) return bestT, bestG, bestV, bestM