def spectral_norm_meas_inv2(x,a,b):
	m1,m2 = x
	aM = measurement(a,0.0)
	bM = measurement(b,0.0)
	arrS = []
	wSum = 0
	for i,m in enumerate(m1):
		#arrS.append(m1[i]*aM.val + (m2[i]*m2[i]/m1[i])*bM.val)
		arrS.append(m1[i]*aM.val + (m1[i]*m1[i]/m2[i])*bM.val)
	return arrS
def extract_paired_runs(runPair, redRun, cfg):
    # If we already have a paired list of runs, this produces lifetimes
    # and peak values
    if cfg.vb:
        print("Combining pre-paired runs")

    runNum = []  # Get a list of runs for indexing
    for r in redRun:
        runNum.append(r.run)

    # This is for scattering lifetime vs. rate and bkg rate.
    lts = []
    rates = []
    bkgs = []
    hlds = []
    norms = []
    for pair in runPair:
        ind1 = np.where(runNum == pair[0])[0]
        ind2 = np.where(runNum == pair[1])[0]
        if not (np.size(ind1) > 0
                and np.size(ind2) > 0):  # Didn't get this pair!
            continue
        else:
            #int(ind1,ind2)
            # Convert to indices
            if redRun[int(ind1)].hold < redRun[int(
                    ind2)].hold:  # Run 1 is short, run 2 is long
                sInd = int(ind1)
                lInd = int(ind2)
            else:
                sInd = int(ind2)
                lInd = int(ind1)
        normS = redRun[sInd].pct_cts(cfg) / redRun[sInd].norm_unl(cfg)
        normL = redRun[lInd].pct_cts(cfg) / redRun[lInd].norm_unl(cfg)
        if cfg.useMeanArr:
            matS = redRun[sInd].mat
            matL = redRun[lInd].mat
        else:
            matS = measurement(redRun[sInd].hold, 0.0)
            matL = measurement(redRun[lInd].hold, 0.0)
        lifetime = ltMeas_corr(normS, normL, matS, matL)
        lts.append(lifetime)
        rateS = redRun[sInd].pct_raw_cts(cfg) / redRun[sInd].len
        rateL = redRun[lInd].pct_raw_cts(cfg) / redRun[lInd].len
        rates.append([rateS, rateL])
        bkgRS = redRun[sInd].bkgSum / redRun[sInd].len
        bkgRL = redRun[lInd].bkgSum / redRun[lInd].len
        bkgs.append([bkgRS, bkgRL])
        norms.append([redRun[sInd].norm_unl(cfg) / redRun[lInd].norm_unl(cfg)])
        hlds.append([
            measurement(redRun[sInd].hold, 0.0),
            measurement(redRun[lInd].hold, 0.0)
        ])

    return lts, rates, bkgs, hlds, norms
def calc_lifetime_paired_sections(ltVec, runPair):
    # Calculate the paired, weighted lifetime.
    # Here I've divided this into majorSections, which are the separation places
    ltval = []  # Generate list of lifetimes
    lterr = []
    for lt in ltVec:
        ltval.append(float(lt.val) +
                     totally_sick_blinding_factor)  # Blind here
        lterr.append(1.0 / float(lt.err**2))  # Value of weighting is 1/err^2

    majorSections = [4200, 4711, 7326, 9600, 11669, 13209, 14517]
    years = [4200, 9600, 14517]
    ltSections = []
    for s in range(len(majorSections) -
                   1):  # Dividing the lifetime into chunks
        ltAvg = 0.0
        ltWts = 0.0
        for i, lt in enumerate(ltval):  # Weighted sum of lifetimes
            if majorSections[s] <= runPair[i][0] < majorSections[s + 1]:
                ltAvg += lt * lterr[i]
                ltWts += lterr[i]

        if ltWts > 0:
            ltFin = ltAvg / ltWts  # Lifetime is weighted sum over sum of weights
            ltErr = np.sqrt(1 / ltWts)  # Uncertainty is sqrt of sum of weights
        else:
            ltFin = ltAvg
            ltErr = np.inf

        print("   For runs", majorSections[s], "to", majorSections[s + 1], ":")
        print("       Lifetime is:", ltFin, "+/-", ltErr)
        ltSections.append(measurement(ltFin, ltErr))

    ltYears = []
    print("   ---------------------------------------------------")
    for s in range(len(years) - 1):  # Dividing the lifetime into chunks
        ltAvg = 0.0
        ltWts = 0.0
        for i, lt in enumerate(ltval):  # Weighted sum of lifetimes
            if years[s] <= runPair[i][0] < years[s + 1]:
                ltAvg += lt * lterr[i]
                ltWts += lterr[i]

        if ltWts > 0:
            ltFin = ltAvg / ltWts  # Lifetime is weighted sum over sum of weights
            ltErr = np.sqrt(1 / ltWts)  # Uncertainty is sqrt of sum of weights
        else:
            ltFin = ltAvg
            ltErr = np.inf

        print("   For runs", years[s], "to", years[s + 1], ":")
        print("       Lifetime is:", ltFin, "+/-", ltErr)
        ltYears.append(measurement(ltFin, ltErr))
    return ltSections, ltYears
def bkgFunc(r_m, hDep, run, pmt, hi=10.0, hf=10.0, ti_f=np.inf, tf_f=np.inf):
    # This is the generic background function.
    # Need to fit to alpha, beta, t1, t2.
    #
    # For a given time, have R = f(h)*(a0 + a1*e^(-t/t1) + a2*e^(-t/t2))
    #
    # End of run thus gives a0 = R_end / f(h_end) - a1*e^(-tf/t1) + a2*e^(-tf/t2)
    #
    # So R(h,t) = f(h)*(R_end / f(h_end))
    #              + a1*(f(h)*e^(-t/t1) - f(h_end)*e^(-t/t1))
    #              + a2*(f(h)*e^(-t/t2) - f(h_end)*e^(-t/t2))
    #
    # Requires measured bkg (r_m), run number, and pmt.
    #
    # ti_f is the time we want to calculate the background at
    # tf_f is the time we measured the background.

    #A_i = height_factor_run(run, hi, pmt)
    #A_f = height_factor_run(run, hf, pmt)
    A_i = hDep.hF(hi, pmt)
    A_f = hDep.hF(hf, pmt)
    a, t1, b, t2 = time_dependence_run(run, pmt, hDep)
    ti = measurement(ti_f, 0.0)
    tf = measurement(tf_f, 0.0)
    if r_m > 0:
        rate_h = (measurement(r_m, np.sqrt(r_m)) / A_f)
    else:
        rate_h = (measurement(0.0, 0.0) / A_f)
    #print(measurement(r_m,np.sqrt(r_m)),rate_h)
    #print(r_m,r_m-(A_i*rate_h).val)
    try:
        rate_t = a * ((-ti / t1).exp() - A_f *
                      (-tf / t1).exp() / A_i) + b * ((-ti / t2).exp() - A_f *
                                                     (-tf / t2).exp() / A_i)
        #rate_t = a*(np.exp(-ti/t1) - A_f*np.exp(-tf/t1)/A_i) + b*(np.exp(-ti/t2) - A_f*np.exp(-tf/t2)/A_i)

        #print(run)
        #print("   ",(-ti/t1).exp(),ti,t1,ti/t1)
        #print("   ",np.exp(-tf/t1),np.exp(-tf/t2))
        #print("   ",a, b)
    except RuntimeWarning:
        print(
            str(ti) + " " + str(t1) + " " + str(tf) + " " + str(t2) + " " +
            str(run))
        rate_t = a * ((-ti / t1).exp() - A_f *
                      (-tf / t1).exp() / A_i) + b * ((-ti / t2).exp() - A_f *
                                                     (-tf / t2).exp() / A_i)
        #rate_t = a*(np.exp(-ti/t1) - A_f*np.exp(-tf/t1)/A_i) + b*(np.exp(-ti/t2) - A_f*np.exp(-tf/t2)/A_i)
    #rate_t = measurement(0.0,0.0)
    #print(A_i*(rate_h+rate_t))
    return A_i * (rate_h + rate_t)
    def hF(self, hgt, pmt):
        # This is our height_factor_run() but in object form.

        f = measurement(1.0, 0.0)  # Default is 1
        for i, h in enumerate(self.hgts):
            # Loop through heights
            if h - 1 < hgt < h + 1:
                if pmt == 0:  # Coinc
                    f = measurement(self.coinc[i], self.coincE[i])
                elif pmt == 1:  # PMT 1
                    f = measurement(self.pmt1[i], self.pmt1E[i])
                elif pmt == 2:  # PMT 2
                    f = measurement(self.pmt2[i], self.pmt2E[i])
                break  # If we got the right hgt don't need to loop.
        return f
def lt(sCts, lCts, sDD, lDD): # Lifetime, no mean arr error
	# Lifetime pair measurement calculation -- need short/long counts and short/long times
	tau = measurement(0.0, np.inf)
	if sCts.val <= 0 or lCts.val <= 0:
		return tau
	tau.val = (lDD-sDD)/(log(sCts.val/lCts.val))
	tau.err = sqrt(pow(sCts.err/sCts.val,2)+pow(lCts.err/lCts.val,2))*(lDD-sDD)/pow((log(sCts.val/lCts.val)),2)
	return tau
def spectral_norm_meas_inv(x,a,b):
	#Change this later
	m1,m2 = x
	aM = measurement(a,0.0)
	bM = measurement(b,0.0)
	
	arrS = []
	wSum = 0
		
	for i,m in enumerate(m1):
		# Find "main detector"
		#if m1[i] > m2[i]:
		arrS.append(m1[i]*aM.val+ (m2[i]/m1[i])*bM.val)
		#else:
		#	arrS.append(m2[i]*aM.val+(m1[i]/m2[i])*bM.val)

	return arrS
 def tF(self, hgt, pmt):
     # This is our time_dependence_factor() but in object form.
     # Technically this is hardcoded towards 2 as an output
     fac = []
     t = []
     for i in range(self.consts):  # Initialization loop
         fac.append(measurement(0.0, 0.0))  # Default is 0
         t.append(measurement(np.inf, 0.0))  # Default is inf
     for i in range(self.consts):  # Forced to scale to 490
         if pmt == 0:
             fac[i] = self.coinc[i] * hgt.hF(490., 0)
             t[i] = self.coincT[i]
         if pmt == 1:
             fac[i] = self.pmt1[i] * hgt.hF(490., 1)
             t[i] = self.pmt1T[i]
         if pmt == 2:
             fac[i] = self.pmt2[i] * hgt.hF(490., 2)
             t[i] = self.pmt2T[i]
     # Here's the hardcode to do least work
     return fac[0], t[0], fac[1], t[1]
def ltMeas(sCts, lCts, sDD, lDD): # Lifetime, propagate mean arr. error
	# Lifetime pair measurement calculation including uncertainty in MAT
	
	# Error lifetime is -1 with infinite uncertainty
	tau = measurement(-1.0, np.inf)
	
	if (sCts.val <= 0.0) or (lCts.val <= 0.0) or (sDD.val <=0) or (lDD.val <= 0): # Check that we got data into all four values
		return tau
	if (sCts.val <= lCts.val) or (sDD.val >= lDD.val): # Check that short/long counts are actually short/long
		return tau
	
	dt = lDD - sDD # Difference in lifetime
	try:
		sCts = measurement(float(sCts.val),float(sCts.err))
		lCts = measurement(float(lCts.val),float(lCts.err))
	except AttributeError:
		sCts = measurement(float(sCts),0.0)
		lCts = measurement(float(lCts),0.0)
	ctsR = sCts/lCts # Ratio of counts
		
	tau = dt / ctsR.log()
		
	#print tau
	return tau
def spectral_norm_meas(x,a,b):
	
	m1,m2 = x
	aM = measurement(a,0.0)
	bM = measurement(b,0.0)
	
	#arr = aM*m1+bM*m2
	arrS = []
	wSum = 0	
	for i,m in enumerate(m1):
		#arrS.append(arr.val)
	#	if isinstance(m1[i],measurement):
			#arrS.append(float(m1[i]*aM + m2[i]*aM))
	#		weight = 1.0 / ((m1[i].err/m1[i].val)**2 + (m2[i].err/m2[i].val)**2)
	#	else:
		arrS.append(m1[i]*aM.val+m2[i]*bM.val)
	#		weight = 1.0
		#arrS.append(aM.val*(float(m1[i])+bM.val*float(m2[i])))
		#wSum += weight
		
	#val = a*m1+b*m2
	#print val
	
	return arrS
def ltMeas_corr(sCts, lCts, sDD, lDD): # Lifetime, propagate mean arr. error
	# Lifetime pair measurement calculation including uncertainty in MAT
	
	# Error lifetime is -1 with infinite uncertainty
	tau = measurement(-1.0, np.inf)
	
	if (sCts.val <= 0.0) or (lCts.val <= 0.0) or (sDD.val <=0) or (lDD.val <= 0): # Check that we got data into all four values
		return tau
	if (sCts.val <= lCts.val) or (sDD.val >= lDD.val): # Check that short/long counts are actually short/long
		return tau
	
	dt = lDD - sDD # Difference in lifetime
	try:
		sCts = measurement(float(sCts.val),float(sCts.err))
		lCts = measurement(float(lCts.val),float(lCts.err))
	except AttributeError:
		sCts = measurement(float(sCts),0.0)
		lCts = measurement(float(lCts),0.0)
	ctsR = (sCts/lCts)
	
	corr1 = measurement((sCts/lCts).val * (lCts.err/lCts.val)*(lCts.err/lCts.val),0.)
	#corr3 = (lCts.err/(lCts.val*lCts.val))*((sCts.val/lCts.val)*lCts.err + sCts.err)
	ctsR -=	corr1
	
	#ctsR.err = np.sqrt(( (sCts.err/lCts.err)*(1 - (lCts.err/lCts.val)**2))**2 \
				#+ ((sCts.val*lCts.err)/(lCts.val*lCts.val)*(1 - 3*(lCts.err/lCts.val)**2))**2)
	#print(corr1,  corr3)
	#ctsR = sCts/lCts + (lCts.err/(lCts.val*lCts.val))*((sCts.val/lCts.val)*lCts.err + sCts.err)
	#ctsR = sCts/lCts # Ratio of counts
		
	tau = dt / ctsR.log()# * (1 - (1 - 0.5*ctsR.log().val)*((ctsR.err)/(ctsR.val*ctsR.log().val))**2)
	#corr = 0
	#corr = -0.5 * (dt *(ctsR.log() + 2) / (ctsR.log()*ctsR.log()*ctsR.log()) ).val * (ctsR.err*ctsR.err)/(ctsR.val*ctsR.val)
	
	#tau.err = dt.val/((ctsR.val**3)*((ctsR.log()).val**4))*((ctsR.val**2 - ctsR.err**2)*(ctsR.log().val**2) \
	#			 - 3*ctsR.err*ctsR.err*(1 + ctsR.log().val) )*ctsR.err
	corr = -0.5 * (dt *(ctsR.log() + 2) / (ctsR.log()*ctsR.log()*ctsR.log()) ).val * \
					((sCts.err/sCts.val)*(sCts.err/sCts.val) \
					-(lCts.err/lCts.val)*(lCts.err/lCts.val) \
					- 2*(sCts.err*lCts.err)/(sCts.val*lCts.val*(ctsR.log().val+2)))
					
					
					
	tau = measurement(float(tau.val+corr),float(tau.err))
	
	#print(tau)
	return tau
def calc_lifetime_ODR(nCtsVec, holdVec, useMeanArr=True):

    # TEST of Orthogonal Distance Regression (allows errors in X and Y)
    #print len(timeV), len(timeE), len(rawCts), len(rawErr)
    rawCts = []
    rawErr = []
    for x in nCtsVec:  #separate out counts
        rawCts.append(float(x.val))
        rawErr.append(float(x.err))

    expModel = Model(lnlt)
    if useMeanArr:
        odrData = RealData(timeV, rawCts, sx=timeE, sy=rawErr)
        testODR = ODR(odrData, expModel, beta0=[1, 880.0])
        output = testODR.run()
        print("ODR lifetime is: " + str(output.beta[1]) + " +/- " +
              str(output.sd_beta[1]))

    return measurement(output.beta[1], output.sd_beta[1])
def calc_lifetime_paired(ltVec, runPair=[], vb=True):
    # Calculate the paired, weighted lifetime.
    # This should track well with an exponential, but is subject to
    # statistical bias (it'll undershoot lifetime for low statistics)
    if vb:
        print("Calculating Lifetime (Paired)...")

    ltval = []  # Generate list of lifetimes
    lterr = []
    for lt in ltVec:
        ltval.append(float(lt.val) +
                     totally_sick_blinding_factor)  # Blind here
        lterr.append(1.0 / float(lt.err**2))  # Value of weighting is 1/err^2

    ltAvg = 0.0
    ltWts = 0.0
    for i, lt in enumerate(ltval):  # Weighted sum of lifetimes
        ltAvg += lt * lterr[i]
        ltWts += lterr[i]

    if ltWts > 0:
        ltFin = ltAvg / ltWts  # Lifetime is weighted sum over sum of weights
        ltErr = np.sqrt(1 / ltWts)  # Uncertainty is sqrt of sum of weights
    else:
        print("Error! Unable to predict uncertainty on lifetime!")
        ltFin = ltAvg
        ltErr = np.inf

    ltval = np.array(ltval)
    unc = 1. / np.sqrt(np.array(lterr))

    chi2 = np.sum(np.power((ltval - ltFin) / unc, 2))
    chi2NDF = chi2 / (len(ltval) - 1)
    ltErr2 = ltErr * np.sqrt(chi2NDF)

    if vb:
        print("Paired (weighted) lifetime is:", ltFin, "+/-", ltErr)
        print("Chi2 is:", chi2, "(NDF):", chi2NDF)
        print("Scale Err = ", ltErr2)

    return measurement(ltFin, ltErr2)
def calc_lifetime_unw_paired(ltVec, runPair):
    # Calculate the paired, weighted lifetime.
    # This should be less statistically biased than weighted.
    # However it might still overshoot the lifetime a bit.
    #
    # Additionally it's not a great use of statistics and will give
    # inflated error bars

    print("Calculating Lifetime (Paired but Unweighted)...")

    ltval = []  # Generate list of lifetimes
    for lt in ltVec:  # Just look at the value of lifetimes -- ignore errors
        ltval.append(float(lt) + totally_sick_blinding_factor
                     )  # float fcn auto-casts .val for measurement class

    ltAvg = np.mean(ltval)
    ltStd = np.std(ltval)

    print("Paired (unweighted) lifetime is: " + str(ltAvg) + " +/- " +
          str(ltStd / np.sqrt(len(ltVec))))
    return measurement(ltAvg, ltStd / np.sqrt(len(ltVec)))  #, runPair,ltVec
def calc_lifetime_exp(rRed, cfg):
    # Single normalization exponential lifetime

    if cfg.vb:
        print("Calculating Lifetime (Exponential Fit)...")
    rawCts = []
    rawErr = []
    timeV = []
    for x in rRed:

        if x.normalize_cts().err > 0:  # Avoid divide by zero errors
            rawCts.append(x.normalize_cts().val)
            rawErr.append(x.normalize_cts().err)
            if cfg.useMeanArr:
                timeV.append(x.mat)  # Assume perfect knowledge of time
            else:
                timeV.append(x.hold)

    pFitE, pVarE = curve_fit(explt,
                             timeV,
                             rawCts,
                             p0=(1, 880.0),
                             sigma=rawErr,
                             absolute_sigma=False)

    print("Exponential lifetime (floating) is: " + str(pFitE[1]) + " +/- " +
          str(np.sqrt(np.diag(pVarE))[1]))
    print(str(pVarE))
    varT = np.sqrt(
        pFitE[1] * pFitE[1] * pVarE[1][1] * pVarE[1][1] +
        2 * pFitE[0] * pFitE[1] * pVarE[0][1])  # Attempt to correct for cov.
    print("Uncertainty of t given fixed a is " + str(varT / len(rawCts)))

    #return measurement(pFitE[1], np.sqrt(np.diag(pVarE))[1]), measurement(pFitE[0], np.sqrt(np.diag(pVarE))[0])

    #pFitE, pVarE = curve_fit(explt_fix, timeV, rawCts, p0=(880.0), sigma=rawErr,absolute_sigma=True)
    #print("Exponential lifetime (fixed) is: "+str(pFitE[0]+totally_sick_blinding_factor)+" +/- "+str(np.sqrt(np.diag(pVarE))[0]))
    return measurement(pFitE[0], np.sqrt(np.diag(pVarE))[0])
    def __init__(self, r_min, r_max):
        self.rMin = r_min
        self.rMax = r_max

        # How many time dependent curves we want to load
        # That would also require modifying dataIO.
        self.consts = 2

        # Time constants as "measurement" values
        self.pmt1 = []
        self.pmt1T = []
        self.pmt2 = []
        self.pmt2T = []
        self.coinc = []
        self.coincT = []
        for i in range(self.consts):
            # Load factors as 0
            self.pmt1.append(measurement(0.0, 0.0))
            self.pmt2.append(measurement(0.0, 0.0))
            self.coinc.append(measurement(0.0, 0.0))
            # and time constants infinite
            self.pmt1T.append(measurement(np.inf, 0.0))
            self.pmt2T.append(measurement(np.inf, 0.0))
            self.coincT.append(measurement(np.inf, 0.0))
def calc_lifetime_ODR_meanFit(ctsVec, holdVec, mArrVec=[]):
    # TEST of Orthogonal Distance Regression (allows errors in X and Y)
    # meanFit averages all the points before doing a fit.

    # If we're not doing mean arrival time, assume mean arrival is just hold
    if len(mArrVec) == 0:
        mArrVec = holdVec

    fixTimes = []  # Generate the total number of time bins
    for t in holdVec:
        if round(t.val) not in fixTimes:
            fixTimes.append(round(t.val))

    # Count our buffers
    tBuff = np.zeros(len(fixTimes))
    tEBuff = np.zeros(len(fixTimes))
    ctsBuff = np.zeros(len(fixTimes))
    ctsEBuff = np.zeros(len(fixTimes))
    #tBuff = np.array([measurement(0.0,0.0) for y in range(len(fixTimes))])
    #ctsBuff = np.array([measurement(0.0,0.0) for y in range(len(fixTimes))])

    num = np.zeros(len(fixTimes))
    for i, t in enumerate(holdVec):
        ind = int(np.argwhere(np.array(fixTimes) == round(t.val)))
        ctsBuff[ind] += ctsVec[i].val / (ctsVec[i].err * ctsVec[i].err)
        ctsEBuff[ind] += 1 / (ctsVec[i].err * ctsVec[i].err)
        if t.err > 0:
            tBuff[ind] += t.val / (t.err * t.err)
            tEBuff[ind] += 1 / (t.err * t.err)
        else:
            tBuff[ind] += t.val
            tEBuff[ind] += 1
        #tBuff[ind] += t
        num[ind] += 1

    meanCts = []
    meanCtsE = []
    meanT = []
    meanTE = []
    for i, x in enumerate(fixTimes):  #separate out counts
        meanCts.append(float(ctsBuff[i] / ctsEBuff[i]))
        meanCtsE.append(1 / np.sqrt(ctsEBuff[i]))
        meanT.append(float(tBuff[i] / tEBuff[i]))
        meanTE.append(1 / np.sqrt(tEBuff[i]))

    expModel = Model(lnlt)
    if len(meanTE) > 0:
        odrData = RealData(meanT, meanCts, sx=meanTE, sy=meanCtsE)
    else:
        odrData = RealData(meanT, meanCts, sy=meanCtsE)
    testODR = ODR(odrData, expModel, beta0=[1, 880.0])
    output = testODR.run()
    print("ODR (mean fit) lifetime is: " + str(output.beta[1]) + " +/- " +
          str(output.sd_beta[1]))
    linetxt = np.linspace(0, 5000, 5000)
    ltTest = output.beta[0] * np.exp(-linetxt / output.beta[1])
    #plt.figure(9001)
    #if len(meanTE) > 0:
    #	plt.errorbar(meanT,meanCts,xerr=meanTE,yerr=meanCtsE,fmt='b.')
    #else:
    #	plt.errorbar(meanT,meanCts,yerr=meanCtsE,fmt='b.')
    #plt.plot(linetxt,ltTest)
    #plt.yscale('log')
    #plt.show()
    return measurement(output.beta[1], output.sd_beta[1]), meanT, meanCts
def pair_runs_summed(runBreaks, rN, unld, time, normFac=[], corr=0.9):
    # Redoing the pairing algorithm but with summed ext. counts
    # If we sum up the counts before taking lifetimes, we should still get
    # the right lifetime. There's a little bit of weirdness required here.
    #
    # Namely, we have to be careful with the long runs since they're paired
    # with multiple short lengths (and vice versa)

    #print runBreaks

    runsS = []  # Separate run list into short and long
    nCtsS = []
    hldTS = []
    runsL = []
    nCtsL = []
    hldTL = []

    hldTVecS = []  # This is the list of short run times
    hldTVecL = []  # This is the list of long run times
    if len(normFac) != len(rN):
        normFac = []
        for r in rN:
            normFac.append(1.0)

    nFS = []
    nFL = []
    for i, run in enumerate(rN):
        if unld[i].err == np.inf or unld[
                i].err == 0.0:  # Weird error state catch
            continue
        t = time[i].val

        if (t < 500.0):
            runsS.append(run)
            nCtsS.append(unld[i])
            hldTS.append(time[i])
            nFS.append(normFac[i])
            if round(t) not in hldTVecS:
                hldTVecS.append(round(t))
        else:
            runsL.append(run)
            nCtsL.append(unld[i])
            hldTL.append(time[i])
            nFL.append(normFac[i])
            if round(t) not in hldTVecL:
                hldTVecL.append(round(t))
    hldTVecS.sort()
    hldTVecL.sort()

    nRunsMat = np.zeros(
        (len(hldTVecS),
         len(hldTVecL)))  # Create matrices for runs (number of runs)
    sCtsMat = np.array([[measurement(0.0, 0.0) for y in range(len(hldTVecL))]
                        for x in range(len(hldTVecS))
                        ])  # Create matrices for runs (short cts)
    lCtsMat = np.array([[measurement(0.0, 0.0) for y in range(len(hldTVecL))]
                        for x in range(len(hldTVecS))
                        ])  # Create matrices for runs (long cts)
    #lCtsMat  = np.empty((len(hldTVecS),len(hldTVecL)),dtype=measurement) # Create matrices for runs (long cts)
    #lCtsMat  = np.empty((len(hldTVecS),len(hldTVecL)),dtype=measurement) # Create matrices for runs (long cts)

    #print sCtsMat, hldTVecS, hldTVecL
    # Pairing
    scMax = 16  # Max separation
    lInd = 0
    bCount = 0
    runPair = []
    #print hldTVecS
    #print hldTVecL
    for gap in range(
            1, scMax
    ):  # Pairing algorithm looks to "minimize" spacing between short/long pairs
        bCount = 0
        for sInd, sr in enumerate(runsS):  # Loop through short
            paired = False  # For breaking this loop
            while runBreaks[bCount + 1] < sr:  # Find runBreaks region
                bCount += 1
            for lInd, lr in enumerate(runsL):  # and through long
                #print sr,lr, gap, runBreaks[bCount],runBreaks[bCount+1]
                if abs(
                        sr - lr
                ) < gap:  # We're in possible range, let's now check other things
                    # First, check if this is really the "best" pair
                    if lInd != len(runsL) - 1:
                        if runBreaks[bCount] <= runsL[lInd + 1] < runBreaks[
                                bCount +
                                1]:  # Make sure we don't cross a break
                            if abs(sr - runsL[lInd + 1]) < abs(
                                    sr - lr
                            ):  # We have another pair that's closer in range!
                                continue
                            elif abs(sr - runsL[lInd + 1]) == abs(
                                    sr -
                                    lr):  # tiebreaker should be normalization.
                                if abs(1.0 -
                                       float(nFS[sInd] / nFL[lInd])) > abs(
                                           1.0 -
                                           float(nFS[sInd] / nFL[lInd + 1])):
                                    continue  # I'll let this continue -- you could beat this if you auto-paired this here.

                    if (
                        (runBreaks[bCount] <= sr < runBreaks[bCount + 1]
                         and runBreaks[bCount] <= lr < runBreaks[bCount + 1]
                         )  # check break regions
                            and
                        (corr < float(nFS[sInd] / nFL[lInd]) < 1.0 / corr)
                    ):  # Check that the normalization is roughly the same too

                        tvIndS = int(
                            np.argwhere(
                                np.array(hldTVecS) == round(hldTS[sInd].val))
                        )  # figure out the short index
                        tvIndL = int(
                            np.argwhere(
                                np.array(hldTVecL) == round(hldTL[lInd].val))
                        )  # figure out the long index
                        # We're good! Now just calculate the lifetime
                        nRunsMat[tvIndS][tvIndL] += 1
                        sCtsMat[tvIndS][tvIndL] += measurement(
                            float(nCtsS[sInd].val), float(nCtsS[sInd].err))
                        lCtsMat[tvIndS][tvIndL] += measurement(
                            float(nCtsL[lInd].val), float(nCtsL[lInd].err))
                        #	runPair.append([sr,lr])
                        #	lts.append(lifetime) # output vector
                        runPair.append([sr, lr])

                        # And remove these runs, since we've successfully paired them
                        runsL.pop(lInd)
                        nCtsL.pop(lInd)
                        hldTL.pop(lInd)
                        nFL.pop(lInd)
                        paired = True  #For breaking out of the other loop
                        break
                elif lr - sr > scMax:  # If lr is scMax more than sr, we can assume lr is sorted and break
                    break
            if paired:  # If we found a long run for this short run, take this run out for the future!
                runsS.pop(sInd)
                nCtsS.pop(sInd)
                hldTS.pop(sInd)
                nFS.pop(sInd)

    # for sInd, sr in enumerate(runsS):

    # if nCtsS[sInd].err == np.inf: # If we have an infinite errorbar, continue.
    # #print sr
    # continue

    # tvIndS = int(np.argwhere(np.array(hldTVecS) == round(hldTS[sInd].val))) # figure out the short index

    # while runBreaks[bCount+1] < sr: # If the short index is in the right "break" region it's OK
    # bCount += 1
    # if runBreaks[bCount] <= sr < runBreaks[bCount+1]:
    # for lInd, lr in enumerate(runsL): # Loop through long to see if we're in the right "break" region
    # if runBreaks[bCount] <= lr < runBreaks[bCount+1]:
    # if abs(sr - lr) < scMax:
    # #print np.argwhere(np.array(hldTVecL)) == hldTL[lInd]
    # tvIndL = int(np.argwhere(np.array(hldTVecL) == round(hldTL[lInd].val))) # figure out the long index
    # #try:
    # if not nCtsL[lInd].err == np.inf: # If we have an infinite errorbar, continue.
    # # add counts to the proper position our summing matrices
    # nRunsMat[tvIndS][tvIndL] += 1
    # sCtsMat[tvIndS][tvIndL]  += measurement(float(nCtsS[sInd].val),float(nCtsS[sInd].err))
    # lCtsMat[tvIndS][tvIndL]  += measurement(float(nCtsL[lInd].val),float(nCtsL[lInd].err))
    # runPair.append([sr,lr])
    # #except:
    # #	continue
    # runsL.pop(lInd)
    # nCtsL.pop(lInd)
    # hldTL.pop(lInd)
    # break

    lts = []
    wts = []
    for tvIndS, tS in enumerate(hldTVecS):
        for tvIndL, tL in enumerate(hldTVecL):
            #lifetime = ltMeas(sCtsMat[tvIndS][tvIndL]/nRunsMat[tvIndS][tvIndL],lCtsMat[tvIndS][tvIndL]/nRunsMat[tvIndS][tvIndL],measurement(tS,0.0),measurement(tL,0.0))
            lifetime = ltMeas(sCtsMat[tvIndS][tvIndL], lCtsMat[tvIndS][tvIndL],
                              measurement(tS, 0.0), measurement(tL, 0.0))

            #	lifetime = measurement(0.0,np.inf)
            #print sCtsMat[tvIndS][tvIndL]
            #print lCtsMat[tvIndS][tvIndL]

            #print lifetime, tS, tL, nRunsMat[tvIndS][tvIndL]
            lifetime.err = lifetime.err / np.sqrt(
                nRunsMat[tvIndS][tvIndL])  # This is a sqrt then another sqrt.
            lts.append(lifetime)
            wts.append(nRunsMat[tvIndS][tvIndL])

    if len(lts) == 0:
        print("Something went wrong, no pairs created!")
    else:
        print("Summing " + str(len(runPair)) + " short-long pairs!")

    lt_meas = measurement(0.0, 0.0)
    for i, x in enumerate(lts):
        if x.val > 0.0:
            lt_meas += x * measurement(wts[i], 0.0)
        else:
            wts[i] = 0.0
    print("Summed Lifetime is: " +
          str(lt_meas / measurement(np.sum(wts), 0.0)))
    #print lts

    return lts, runPair
def pair_runs_from_file(rRed, cfg, inFName='/home/frank/run_pairs.csv'):

    #-------------------------------------------------------------------
    # This script loads pairs of runs from a file
    #-------------------------------------------------------------------
    print("Loading list of pairs from " + inFName)

    datatype = [('r1', 'i4'), ('r2', 'i4')]
    pairs = np.loadtxt(inFName, delimiter=",", dtype=datatype)

    runsS = []  # Separate run list into short and long
    nCtsS = []
    hldTS = []
    matTS = []
    runsL = []
    nCtsL = []
    matTL = []
    hldTL = []
    #if len(normFac) != len(rN):
    normFac = []
    for r in rRed:  # This doesn't mean anything now
        normFac.append(1.0)

    nFS = []
    nFL = []
    rUse = []
    for r in rRed:
        if r.run in pairs['r1'] or r.run in pairs['r2']:
            rUse.append(r)
        if (r.hold > 2000.0) and not (cfg.useLong):
            continue
        #unld = (r.ctsSum-r.bkgSum)/r.norm_unl(cfg)
        unld = r.pct_cts(cfg) / r.norm_unl(cfg)
        #unld /= (r.eff[0] + r.eff[1])
        if (not np.isfinite(
                unld.err)) or unld.err == 0.0:  # Weird error state catch
            if cfg.vb:
                print("%d has infinite unload error" % r.run)
            continue

        if (r.hold < 500.0):
            runsS.append(r.run)
            nCtsS.append(unld)
            hldTS.append(measurement(r.hold, 0.0))
            if cfg.useMeanArr:
                matTS.append(r.mat)
            else:
                matTS.append(measurement(r.hold, 0.0))
            #nFS.append(normFac[i])
            nFS.append(r.norm_unl(cfg))

        else:
            runsL.append(r.run)
            nCtsL.append(unld)
            hldTL.append(measurement(r.hold, 0.0))
            if cfg.useMeanArr:
                matTL.append(r.mat)
            else:
                matTL.append(measurement(r.hold, 0.0))
            nFL.append(r.norm_unl(cfg))
    runsS = np.array(runsS)
    runsL = np.array(runsL)
    lts = []
    runPair = []
    for pair in pairs:
        runS = pair['r1']
        runL = pair['r2']

        sInd = np.where(runsS == runS)[0]
        lInd = np.where(runsL == runL)[0]

        if not (np.size(sInd) > 0 and np.size(lInd) > 0):
            continue
        else:
            sInd = int(sInd)
            lInd = int(lInd)
        if (float(matTS[sInd]) < float(
                matTL[lInd])):  # Run 1 is short, run 2 is long
            lifetime = ltMeas_corr(nCtsS[sInd], nCtsL[lInd], matTS[sInd],
                                   matTL[lInd])
            lts.append(lifetime)
            runPair.append([runS, runL])
        else:  # Run 2 is short, run 1 is long
            lifetime = ltMeas_corr(nCtsS[lInd], nCtsL[sInd], matTS[lInd],
                                   matTL[sInd])
            lts.append(lifetime)
            runPair.append([runS, runL])
        #print (str(lifetime))

    return lts, runPair, rUse
Exemple #20
0
        tdPmt1_raw = tCts['pmt1'][21:] * 0.
    global bkgExp
    bkgExp = muCts[r].pmt1  #s* hDCts[r].pmt1
    #tdPmt1 -= muCts[r].pmt1 * hDCts[r].pmt1 # Background Subtraction
    if tCts['pmt2'][0] > 0:
        tdPmt2_raw = tCts['pmt2'][21:] / tCts['pmt2'][0]
    else:
        tdPmt2_raw = tCts['pmt1'][21:] * 0.
    #tdPmt2 -= muCts[r].pmt2 * hDCts[r].pmt2
    if tCts['coinc'][0] > 0:
        tdPmtC_raw = tCts['coinc'][21:] / tCts['coinc'][0]
    else:
        tdPmtC_raw = tCts['coinc'][21:] * 0.

    # Extract the expected background rates
    pmt1_avg = measurement(muCts[r].pmt1/hDCts[r].pmt1,\
            muCts[r].pmt1/hDCts[r].pmt1*np.sqrt((muStd[r].pmt1/muCts[r].pmt1)**2+(hDStd[r].pmt1/hDCts[r].pmt1)**2))
    pmt2_avg = measurement(muCts[r].pmt2/hDCts[r].pmt2,\
            muCts[r].pmt2/hDCts[r].pmt2*np.sqrt((muStd[r].pmt2/muCts[r].pmt2)**2+(hDStd[r].pmt2/hDCts[r].pmt2)**2))
    pmtC_avg = measurement(muCts[r].coinc/hDCts[r].coinc,\
            muCts[r].coinc/hDCts[r].coinc*np.sqrt((muStd[r].coinc/muCts[r].coinc)**2+(hDStd[r].coinc/hDCts[r].coinc)**2))
    avg_bin = 10
    bins = np.zeros(int(len(bins_raw) / avg_bin))
    tdPmt1 = np.zeros(int(len(bins_raw) / avg_bin))
    tdPmt2 = np.zeros(int(len(bins_raw) / avg_bin))
    tdPmtC = np.zeros(int(len(bins_raw) / avg_bin))
    for i in range(int(len(bins_raw) / avg_bin)):
        bins[i] = np.mean(bins_raw[10 * i:10 * (i + 1) - 1])
        tdPmt1[i] = np.mean(tdPmt1_raw[10 * i:10 * (i + 1) - 1])
        tdPmt2[i] = np.mean(tdPmt2_raw[10 * i:10 * (i + 1) - 1])
        tdPmtC[i] = np.mean(tdPmtC_raw[10 * i:10 * (i + 1) - 1])
def time_dependence_run(run, pmt, hDep):
    # Again this is a lookup table for time dependence runs
    # There are now 4 factors to look up here for each run/PMT.
    #
    # These numbers are "raw" meaning un-height-corrected for the time dep.
    # We height-correct the scaling factor at the end.
    #
    # I'm also assuming there's no time dependence in coincidences (for now.)
    # To turn on coincidences I'd just add an else for PMTs.
    #
    # I should probably re-do the math on these

    a = measurement(0.0, 0.0)
    t1 = measurement(np.inf, 0.0)
    b = measurement(0.0, 0.0)
    t2 = measurement(np.inf, 0.0)

    if 4200 <= run < 7327:
        if pmt == 1:
            a = measurement(0.0009514, 0.0002624)
            t1 = measurement(35.58, 8.35)
            b = measurement(0.002028, 0.000052)
            t2 = measurement(491.2, 14.0)
        elif pmt == 2:
            a = measurement(0.000304, 0.000019)
            t1 = measurement(136.0, 18.2)
            b = measurement(0.0007963, 0.0000155)
            t2 = measurement(2306.0, 101.0)
    elif 7327 <= run < 9545:
        if pmt == 1:
            a = measurement(0.0001638, 0.0000042)
            t1 = measurement(110.2, 6.8)
            b = measurement(0.0006133, 0.0000026)
            t2 = measurement(5490, 123.7)
        elif pmt == 2:
            a = measurement(0.0003004, 0.000079)
            t1 = measurement(70.54, 3.58)
            b = measurement(0.0007014, 0.000031)
            t2 = measurement(3107, 46.5)
    elif 9545 <= run < 13309:
        if pmt == 1:
            a = measurement(0.000611, 0.0000035)
            t1 = measurement(31.36, 2.54)
            b = measurement(0.001348, 0.000010)
            t2 = measurement(851.8, 9.4)
        elif pmt == 2:
            a = measurement(0.001744, 0.000105)
            t1 = measurement(33.23, 2.36)
            b = measurement(0.0027538, 0.000046)
            t2 = measurement(364.3, 6.4) * 3
    elif 13309 <= run < 15000:  # These numbers should be re-calculated
        if pmt == 1:
            a = measurement(0.0006849, 0.00000428)
            t1 = measurement(98.27, 7.91)
            b = measurement(0.001191, 0.000027)
            t2 = measurement(940.1, 26.6)
        elif pmt == 2:
            a = measurement(0.0006847, 0.0000428)
            t1 = measurement(98.26, 7.91)
            b = measurement(0.001191, 0.000027)
            t2 = measurement(939.2, 26.6)

    # Need to correct for height factor here and scale to Hz
    #a = a * height_factor_run(run,490.0,pmt) * measurement(1550.0,0.0)
    #b = b * height_factor_run(run,490.0,pmt) * measurement(1550.0,0.0)
    a = a * hDep.hF(490.0, pmt) * measurement(1550.0, 0.0)
    b = b * hDep.hF(490.0, pmt) * measurement(1550.0, 0.0)

    return a, t1, b, t2
def pair_runs_from_list(rRed, pairsList, cfg):
    # Pairing algorithm. I wrote this with a bunch of individual lists
    # so it could be optimized better.
    runsS = []  # Separate run list into short and long
    nCtsS = []
    hldTS = []
    matTS = []
    runsL = []
    nCtsL = []
    matTL = []
    hldTL = []
    #if len(normFac) != len(rN):
    normFac = []
    for r in rRed:  # This doesn't mean anything now
        normFac.append(1.0)

    pairs = np.zeros(len(pairsList), dtype=[('r1', 'f8'), ('r2', 'f8')])
    for i in range(len(pairsList)):
        try:
            pairs[i]['r1'] = pairsList[i][0]
            pairs[i]['r2'] = pairsList[i][1]
        except ValueError:
            continue
    nFS = []
    nFL = []
    rUse = []
    for r in rRed:
        if r.run in pairs['r1'] or r.run in pairs['r2']:
            rUse.append(r)
        else:
            continue
        unld = r.cts / r.norm
        if (not np.isfinite(
                unld.err)) or unld.err == 0.0:  # Weird error state catch
            if cfg.vb:
                print("%d has infinite unload error" % r.run)
            continue

        if (r.hold < 500.0):
            runsS.append(r.run)
            nCtsS.append(unld)
            hldTS.append(measurement(r.hold, 0.0))
            if cfg.useMeanArr:
                matTS.append(r.mat)
            else:
                matTS.append(measurement(r.hold, 0.0))
            nFS.append(r.norm)

        else:
            runsL.append(r.run)
            nCtsL.append(unld)
            hldTL.append(measurement(r.hold, 0.0))
            if cfg.useMeanArr:
                matTL.append(r.mat)
            else:
                matTL.append(measurement(r.hold, 0.0))
            nFL.append(r.norm)

    runsS = np.array(runsS)
    runsL = np.array(runsL)
    lts = []
    ltsC = []
    hTPair = []
    runPair = []

    for pair in pairs:
        runS = pair['r1']
        runL = pair['r2']

        sInd = np.where(runsS == runS)[0]
        lInd = np.where(runsL == runL)[0]

        if not (np.size(sInd) > 0 and np.size(lInd) > 0):
            continue
        else:
            sInd = int(sInd)
            lInd = int(lInd)
        #if (float(matTS[sInd]) < float(matTL[lInd])): # Run 1 is short, run 2 is long
        # At one point I was concerned about short/long, but I don't think(?) that's an issue
        lifetime = ltMeas(nCtsS[sInd], nCtsL[lInd], matTS[sInd], matTL[lInd])
        lifetime_C = ltMeas_corr(nCtsS[sInd], nCtsL[lInd], matTS[sInd],
                                 matTL[lInd])
        lts.append(lifetime)
        ltsC.append(lifetime_C)
        hTPair.append([hldTS[sInd], hldTL[lInd]])
        runPair.append([runS, runL])
        #else: # Run 2 is short, run 1 is long
        #	lifetime  = ltMeas(nCtsS[lInd], nCtsL[sInd], matTS[lInd], matTL[sInd])
        #	lifetime_C = ltMeas_corr(nCtsS[lInd], nCtsL[sInd], matTS[sInd], matTL[lInd])
        #	lts.append(lifetime)
        #	ltsC.append(lifetime_C)
        #	hTPair.append([hldTS[sInd],hldTL[lInd]])
        #	runPair.append([runS,runL])
        #print (str(lifetime))
    return lts, runPair, hTPair, ltsC
def bkgFunc_obj(bkg, pmt=0, h_i=10.0, ti_f=np.inf):
    # This is the generic background function.
    # Need to fit to alpha, beta, t1, t2.
    #
    # For a given time, have R = f(h)*(a0 + a1*e^(-t/t1) + a2*e^(-t/t2))
    #
    # End of run thus gives a0 = R_end / f(h_end) - a1*e^(-tf/t1) + a2*e^(-tf/t2)
    #
    # So R(h,t) = f(h)*(R_end / f(h_end))
    #              + a1*(f(h)*e^(-t/t1) - f(h_end)*e^(-t/t1))
    #              + a2*(f(h)*e^(-t/t2) - f(h_end)*e^(-t/t2))
    #
    # Requires measured bkg (r_m), run number, and pmt.
    #
    # ti_f is the time we want to calculate the background at
    # tf_f is the time we measured the background.

    # Extract factors for measured point
    a, t1, b, t2 = bkg.tDep.tF(bkg.hDep, pmt)
    #a, t1, b, t2 = time_dependence_run(bkg.run, pmt,bkg.hDep)
    A_i = bkg.hDep.hF(h_i, pmt)  # height dependence stored in object
    #	A_i = height_factor_run(bkg.run, h_i, pmt)
    ti = measurement(ti_f, 0.0)  # Convert time to meas.

    # Scale background to point
    A_f = bkg.hDep.hF(bkg.hgt, pmt)
    #A_f = height_factor_run(bkg.run, bkg.hgt, pmt)
    tf = measurement(bkg.time, 0.0)
    if pmt == 1:
        r_m = bkg.pmt1
    elif pmt == 2:
        r_m = bkg.pmt2
    elif pmt == 0:
        r_m = bkg.coinc
    else:
        sys.exit("ERROR! You're scaling backgrounds for a non-existent PMT!")
    rate_h = (measurement(r_m, np.sqrt(r_m)) / A_f)

    #try:
    if np.isfinite(t1.val) and (
            t1.val > 0):  # numpy doesn't like infinite time constants
        a_val = a * ((-ti / t1).exp() - A_f * (-tf / t1).exp() / A_i)
    else:
        a_val = a * (measurement(1., 0.) - A_f / A_i)
    if np.isfinite(
            t2.val) and (t2.val > 0):  # Again, avoid infinite time constants.
        b_val = b * ((-ti / t2).exp() - A_f * (-tf / t2).exp() / A_i)
    else:
        b_val = b * (measurement(1., 0.) - A_f / A_i)
    rate_t = a_val + b_val  # Time dependence is function of these two
    #else:
    #	rate_t = measurement(0.0,0.0)
    #except RuntimeWarning:
    #	print("Warning! Possible Overflow Error!",str(run))
    # Error here is probably an overflow error
    #	rate_t = measurement(0.0,np.inf)\
    bkgTot = A_i * (rate_h + rate_t)
    bkgPos = A_i / A_f * measurement(r_m, np.sqrt(r_m)) - measurement(
        r_m, np.sqrt(r_m))
    bkgPos.err = bkgPos.val * np.sqrt(r_m + (A_i / A_f).err * (A_i / A_f).err)
    bkgTime = A_i * rate_t
    bkgTime.err = np.sqrt(bkgTime.err *
                          bkgTime.val)  # Fractional time dependence

    return bkgTot, bkgPos, bkgTime
def height_factor_run(run, height=10.0, pmt=0):
    # This is just a lookup table of height dependence factors.
    # Calculated for PMT1/2. Coinc is "unknown" case [0 = coinc]
    #
    # If in doubt there's no height dependence.
    #if height == 250.0:
    #	height = 490.0
    #elif height == 490.0:
    #	height = 250.0
    if 9.0 < height < 11.0:
        return measurement(1.0, 0.0)

    factor = []
    if 4200 <= run < 7327:
        if 489.0 < height < 491.0:
            factor.append(measurement(0.923, 0.017))  # Coinc
            factor.append(measurement(1.0221, 0.0013))  # PMT 1
            factor.append(measurement(1.0594, 0.0020))  # PMT 2
        elif 379.0 < height < 381.0:
            factor.append(measurement(1.007, 0.018))  # Coinc
            factor.append(measurement(1.005, 0.0012))  # PMT 1
            factor.append(measurement(1.0406, 0.0021))  # PMT 2
        elif 249.0 < height < 251.0:
            factor.append(measurement(1.013, 0.0021))  # Coinc
            factor.append(measurement(0.9935, 0.0014))  # PMT 1
            factor.append(measurement(1.0196, 0.0018))  # PMT 2
    elif 7327 <= run < 9545:
        if 489.0 < height < 491.0:
            factor.append(measurement(0.940, 0.016))  # Coinc
            factor.append(measurement(1.1536, 0.0042))  # PMT 1
            factor.append(measurement(1.1220, 0.0028))  # PMT2
        elif 379.0 < height < 381.0:
            factor.append(measurement(0.997, 0.016))  # Coinc
            factor.append(measurement(1.0210, 0.0019))  # PMT 1
            factor.append(measurement(1.0691, 0.0025))  # PMT 2
        elif 249.0 < height < 251.0:
            factor.append(measurement(0.985, 0.017))  # Coinc
            factor.append(measurement(1.0172, 0.0016))  # PMT 1
            factor.append(measurement(1.0428, 0.0022))  # PMT 2
    elif 9545 <= run < 13309:
        if 489.0 < height < 491.0:
            factor.append(measurement(0.9353, 0.0075))  # Coinc
            factor.append(measurement(1.0180, 0.0023))  # PMT 1
            factor.append(measurement(1.0098, 0.0011))  # PMT 2
        elif 379.0 < height < 381.0:
            factor.append(measurement(0.9525, 0.0081))  # Coinc
            factor.append(measurement(1.0150, 0.0024))  # PMT 1
            factor.append(measurement(1.0051, 0.0013))  # PMT 2
        elif 249.0 < height < 251.0:
            factor.append(measurement(0.9435, 0.0074))  # Coinc
            factor.append(measurement(1.0043, 0.0019))  # PMT 1
            factor.append(measurement(0.9833, 0.0011))  # PMT 2
    elif 13309 <= run < 15000:
        if 489.0 < height < 491.0:
            factor.append(measurement(0.9862, 0.0148))  # Coinc
            factor.append(measurement(1.0251, 0.0019))  # PMT 1
            factor.append(measurement(1.0259, 0.0016))  # PMT 2
        elif 379.0 < height < 381.0:
            factor.append(measurement(0.9983, 0.0148))  # Coinc
            factor.append(measurement(1.0203, 0.0017))  # PMT 1
            factor.append(measurement(1.0284, 0.0016))  # PMT 2
        elif 249.0 < height < 251.0:
            factor.append(measurement(1.0005, 0.0133))  # Coinc
            factor.append(measurement(0.9988, 0.0017))  # PMT 1
            factor.append(measurement(1.0002, 0.0014))  # PMT 2

    if pmt >= len(
            factor):  # Unknown case assumes at bottom, which is always 1.0.
        return measurement(1.0, 0.0)
    else:
        #print (run,height,pmt,factor[pmt])
        #f_test = measurement(1.0,0.0) + (measurement(1.0,0.0) - factor[pmt])*measurement(249.0,0.0)
        #return measurement(1.0,0.0)/factor[pmt]
        return factor[pmt]
def pair_runs(runBreaks, rRed, cfg):
    # Pairing algorithm. I wrote this with a bunch of individual lists
    # so it could be optimized better.
    if cfg.vb:
        print("Pairing runs...")

    runsS = []  # Separate run list into short and long
    nCtsS = []
    hldTS = []
    matTS = []
    runsL = []
    nCtsL = []
    matTL = []
    hldTL = []
    #if len(normFac) != len(rN):
    normFac = []
    for r in rRed:  # This doesn't mean anything now
        normFac.append(1.0)

    nFS = []
    nFL = []
    for r in rRed:

        if (r.hold > 2000.0) and not (cfg.useLong):
            continue

        #unld = (r.ctsSum-r.bkgSum)/r.norm_unl(cfg)
        #unld  = r.pct_cts(cfg)/r.norm_unl(cfg)
        #print((r.ctsSum-r.bkgSum)-r.cts,((r.ctsSum-r.ctsSum)-(r.bkgSum-r.bkgSum)))
        unld = r.cts / r.norm
        #unld /= (r.eff[0] + r.eff[1])
        if (not np.isfinite(
                unld.err)) or unld.err == 0.0:  # Weird error state catch
            if cfg.vb:
                print("%d has infinite unload error" % r.run)
            continue

        if (r.hold < 500.0):
            runsS.append(r.run)
            nCtsS.append(unld)
            hldTS.append(measurement(r.hold, 0.0))
            if cfg.useMeanArr:
                matTS.append(r.mat)
            else:
                matTS.append(measurement(r.hold, 0.0))
            #nFS.append(normFac[i])
            nFS.append(r.norm)
            #nFS.append(r.norm_unl(cfg))
        else:
            runsL.append(r.run)
            nCtsL.append(unld)
            hldTL.append(measurement(r.hold, 0.0))
            if cfg.useMeanArr:
                matTL.append(r.mat)
            else:
                matTL.append(measurement(r.hold, 0.0))
            #nFL.append(r.norm_unl(cfg))
            nFL.append(r.norm)

    # Pairing
    scMax = 16  # +/- 2 octets
    lInd = 0
    bCount = 0
    corr = 0.9  # No crazy norm drop-offs
    lts = []
    ltsC = []
    runPair = []
    hTPair = []
    for gap in range(
            1, scMax
    ):  # Pairing algorithm looks to "minimize" spacing between short/long pairs
        bCount = 0
        for sInd, sr in enumerate(runsS):  # Loop through short
            paired = False  # For breaking this loop
            while runBreaks[bCount + 1] < sr:  # Find runBreaks region
                bCount += 1
                if bCount == len(runBreaks) - 2:
                    break
            for lInd, lr in enumerate(runsL):  # and through long
                #print sr,lr, gap, runBreaks[bCount],runBreaks[bCount+1]
                if abs(
                        sr - lr
                ) < gap:  # We're in possible range, let's now check other things
                    # First, check if this is really the "best" pair
                    if lInd != len(runsL) - 1:
                        if runBreaks[bCount] <= runsL[lInd + 1] < runBreaks[
                                bCount +
                                1]:  # Make sure we don't cross a break
                            if abs(sr - runsL[lInd + 1]) < abs(
                                    sr - lr
                            ):  # We have another pair that's closer in range!
                                continue
                            elif abs(sr - runsL[lInd + 1]) == abs(
                                    sr -
                                    lr):  # tiebreaker should be normalization.
                                if abs(1.0 -
                                       float(nFS[sInd] / nFL[lInd])) > abs(
                                           1.0 -
                                           float(nFS[sInd] / nFL[lInd + 1])):
                                    continue  # I'll let this continue -- you could beat this if you auto-paired this here.

                    if (
                        (runBreaks[bCount] <= sr < runBreaks[bCount + 1]
                         and runBreaks[bCount] <= lr < runBreaks[bCount + 1]
                         )  # check break regions
                            and
                        (corr < float(nFS[sInd] / nFL[lInd]) < 1.0 / corr)
                    ):  # Check that the normalization is roughly the same too
                        # We're good! Now just calculate the lifetime
                        lifetime = ltMeas(nCtsS[sInd], nCtsL[lInd],
                                          matTS[sInd], matTL[lInd])
                        lifetimeC = ltMeas_corr(nCtsS[sInd], nCtsL[lInd],
                                                matTS[sInd], matTL[lInd])
                        if lifetime.err < lifetime.val:  # ignore giant errorbars (for plotting)
                            lts.append(lifetime)  # output vector
                            ltsC.append(lifetimeC)
                            runPair.append([sr, lr])
                            hTPair.append([hldTS[sInd], hldTL[lInd]])

                        # And remove these runs, since we've successfully paired them
                        runsL.pop(lInd)
                        nCtsL.pop(lInd)
                        hldTL.pop(lInd)
                        matTL.pop(lInd)
                        nFL.pop(lInd)
                        paired = True  #For breaking out of the other loop
                        break
                elif lr - sr > scMax:  # If lr is scMax more than sr, we can assume lr is sorted and break
                    break
            if paired:  # If we found a long run for this short run, take this run out for the future!
                runsS.pop(sInd)
                nCtsS.pop(sInd)
                hldTS.pop(sInd)
                matTS.pop(sInd)
                nFS.pop(sInd)

    if len(lts) == 0:
        print("Something went wrong, no pairs created!")
    else:
        print("Using " + str(len(lts)) + " short-long pairs!")
        print("   First lifetime in list: " + str(lts[0].val) + " +/- " +
              str(lts[0].err))

    return lts, runPair, hTPair, ltsC