Exemple #1
0
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
Exemple #3
0
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)
Exemple #5
0
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
Exemple #6
0
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()
Exemple #7
0
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