Beispiel #1
0
def seeds_and_bounds(bins, spec, ped_vals):

    norm_seed = spec.sum()

    GSeed = 0
    GSSeed = 0
    pDL = find_peaks_cwt(spec, np.arange(4, 20), min_snr=1, noise_perc=5)
    p1pe = pDL[(bins[pDL] > 10) & (bins[pDL] < 20)]
    if len(p1pe) == 0:
        p1pe = np.argwhere(bins == 15)[0][0]
    else:
        p1pe = p1pe[spec[p1pe].argmax()]
    p1 = fitf.fit(fitf.gauss,
                  bins[p1pe - 5:p1pe + 5],
                  spec[p1pe - 5:p1pe + 5],
                  seed=(spec[p1pe], bins[p1pe], 3.))
    GSeed = p1.values[1] - ped_vals[1]
    if p1.values[2] <= ped_vals[2]:
        GSSeed = 0.5
    else:
        GSSeed = np.sqrt(p1.values[2]**2 - ped_vals[2]**2)

    dscale = spec[bins < 5].sum() / fitf.gauss(bins[bins < 5], *ped_vals).sum()
    errs = np.sqrt(spec[bins < 5])
    errs[errs == 0] = 1
    fscale = fitf.fit(scaler,
                      bins[bins < 5],
                      spec[bins < 5], (dscale),
                      sigma=errs)
    muSeed = -np.log(fscale.values[0])
    if muSeed < 0: muSeed = 0.001

    sd0 = (norm_seed, muSeed, GSeed, GSSeed)
    bd0 = [(0, 0, 0, 0.001), (1e10, 10000, 10000, 10000)]
    return sd0, bd0
Beispiel #2
0
def seeds_and_bounds(indx, func, bins, spec, ped_vals, ped_errs, lim_ped):

    global GainSeeds, SigSeeds, useSavedSeeds, scalerChis
    
    norm_seed = spec.sum()

    GSeed  = 0
    GSSeed = 0
    if useSavedSeeds:
        GSeed  = GainSeeds[indx]
        GSSeed = SigSeeds[indx]
    else:
        pDL = find_peaks_cwt(spec, np.arange(4, 20), min_snr=1, noise_perc=5)
        p1pe = pDL[(bins[pDL]>10) & (bins[pDL]<20)]
        if len(p1pe) == 0:
            p1pe = np.argwhere(bins==15)[0][0]
        else:
            p1pe = p1pe[spec[p1pe].argmax()]
        p1 = fitf.fit(fitf.gauss, bins[p1pe-5:p1pe+5], spec[p1pe-5:p1pe+5], seed=(spec[p1pe], bins[p1pe], 3.))
        GSeed = p1.values[1] - ped_vals[1]
        if p1.values[2] <= ped_vals[2]:
            GSSeed = 0.5
        else:
            GSSeed = np.sqrt(p1.values[2]**2 - ped_vals[2]**2)

    dscale = spec[bins<5].sum() / fitf.gauss(bins[bins<5], *ped_vals).sum()
    errs = np.sqrt(spec[bins<5])
    errs[errs==0] = 1
    fscale = fitf.fit(scaler, bins[bins<5], spec[bins<5], (dscale), sigma=errs)
    scalerChis.append(fscale.chi2)
    if scalerChis[-1] >= 500:
        print('Suspect channel index ', indx)
    muSeed = -np.log(fscale.values[0])
    if muSeed < 0: muSeed = 0.001

    if 'gau' in func:
        ped_seed = ped_vals[1]
        ped_min  = ped_seed - lim_ped * ped_errs[1]
        ped_max  = ped_seed + lim_ped * ped_errs[1]
        ped_sig_seed = ped_vals[2]
        ped_sig_min  = max(0.001, ped_sig_seed - lim_ped * ped_errs[2])
        ped_sig_max  = ped_sig_seed + lim_ped * ped_errs[2]
        sd0 = (norm_seed, muSeed, ped_seed, ped_sig_seed, GSeed, GSSeed)
        bd0 = [(0, 0, ped_min, ped_sig_min, 0, 0.001), (1e10, 10000, ped_max, ped_sig_max, 10000, 10000)]
        #print('Seed check: ', sd0)
        return sd0, bd0

    sd0 = (norm_seed, muSeed, GSeed, GSSeed)
    bd0 = [(0, 0, 0, 0.001), (1e10, 10000, 10000, 10000)]
    print('Seed check: ', sd0)
    return sd0, bd0
Beispiel #3
0
def lt(z, v, znbins, zrange, vnbins, vrange, plot=True):
    """ compute the lifetime of v-variable (S2e, Se1, E) vs Z
    """
    zbins = np.linspace(*zrange, znbins + 1)
    vbins = np.linspace(*vrange, vnbins + 1)
    x, y, yu = fitf.profileX(z, v, znbins, zrange)
    seed = expo_seed(x, y)
    f = fitf.fit(fitf.expo, x, y, seed, sigma=yu)

    if (not plot): return f

    #print('energy_0', f.values[0], ' +- ', f.errors[0] )
    #print('lifetime', f.values[1], ' +- ', f.errors[1] )

    frame_data = plt.gcf().add_axes((.1, .3, .8, .6))
    plt.hist2d(z, v, (zbins, vbins))
    x, y, yu = fitf.profileX(z, v, znbins, zrange)
    plt.errorbar(x, y, yu, np.diff(x)[0] / 2, fmt="kp", ms=7, lw=3)
    plt.plot(x, f.fn(x), "r-", lw=4)
    frame_data.set_xticklabels([])
    labels("", "Energy (pes)", "Lifetime fit")
    lims = plt.xlim()
    frame_res = plt.gcf().add_axes((.1, .1, .8, .2))
    plt.errorbar(x, (f.fn(x) - y) / yu, 1, np.diff(x)[0] / 2, fmt="p", c="k")
    plt.plot(lims, (0, 0), "g--")
    plt.xlim(*lims)
    plt.ylim(-5, +5)
    labels("Drift time (µs)", "Standarized residual")
    return f
Beispiel #4
0
def lifetimes_in_TRange(kre: KrEvent, krnb: KrNBins, krb: KrBins,
                        krr: KrRanges, TL) -> List[KrFit]:
    """ Plots lifetime fitted to a range of T values"""

    # Specify the range and number of bins in Z
    Znbins = krnb.Z
    Zrange = krr.Z

    kfs = []
    for tlim in TL:

        # select data
        kre_t = select_in_TRange(kre, *tlim)
        z, e = kre_t.Z, kre_t.E

        x, y, yu = fitf.profileX(z, e, Znbins, Zrange)
        # Fit profile to an exponential
        seed = expo_seed(x, y)
        f = fitf.fit(fitf.expo, x, y, seed, sigma=yu)

        kf = KrFit(par=np.array(f.values),
                   err=np.array(f.errors),
                   chi2=chi2(f, x, y, yu))

        #krf.print_fit(kf)
        kfs.append(kf)

    return kfs
def test_lt_profile_yields_same_result_expo_fit():

    Nevt  = int(1e5)
    e0 = 1e+4 # pes
    std = 0.05 * e0
    lt = 2000 # lifetime in mus
    nbins_z = 12
    range_z = (1, 500)
    z, es = energy_lt_experiment(Nevt, e0, lt, std)

    x, y, yu     = fitf.profileX(z, es, nbins_z, range_z)
    valid_points = ~np.isnan(yu)

    x    = x [valid_points]
    y    = y [valid_points]
    yu   = yu[valid_points]
    seed = expo_seed(x, y)
    f    = fitf.fit(fitf.expo, x, y, seed, sigma=yu)

    par  = np.array(f.values)
    err  = np.array(f.errors)
    e0   = par[0]
    lt   = - par[1]
    e0_u = err[0]
    lt_u = err[1]

    _, _,  fr = fit_lifetime_profile(z, es, nbins_z, range_z)
    assert e0   == approx(fr.par[0],  rel=0.05)
    assert lt   == approx(fr.par[1],  rel=0.05)
    assert e0_u == approx(fr.err[0],  rel=0.05)
    assert lt_u == approx(fr.err[1],  rel=0.05)
Beispiel #6
0
def fit_s2_energy_in_z_bins_within_XY_limits(
    kre: KrEvent,
    kL: KrRanges,
    kNB: KrNBins,
    kB: KrBins,
    eR: Ranges,
    figsize=(12, 12)) -> KrFit:
    fig = plt.figure(figsize=figsize)  # Creates a new figure
    EMU = []
    ES = []
    CHI2 = []

    for j, i in enumerate(range(kNB.Z)):
        ax = fig.add_subplot(5, 2, i + 1)
        ax.set_xlabel('S2 energy (pes)', fontsize=11)  #xlabel
        ax.set_ylabel('Number of events', fontsize=11)  #ylabel

        zlim = kB.Z[i], kB.Z[i + 1]

        sel = in_range(kre.X, *kL.XY) & in_range(kre.Y, *kL.XY) & in_range(
            kre.Z, *zlim)
        e = kre.E[sel]

        print(
            f'bin : {j}, energy range for fit: lower = {eR.lower[j]}, upper = {eR.upper[j]}'
        )
        sel = in_range(e, eR.lower[j], eR.upper[j])
        er = e[sel]

        y, b, _ = ax.hist(er,
                          bins=kB.E,
                          histtype='step',
                          edgecolor='black',
                          linewidth=1.5)

        x = shift_to_bin_centers(b)

        df = pd.DataFrame(dict(y=y, x=x))
        df = df[df.y > 0]
        fit_range = (df.x.values[0], df.x.values[-1])
        x = df.x.values
        y = df.y.values
        yu = np.sqrt(y)

        seed = gauss_seed(x, y)
        f = fitf.fit(fitf.gauss, x, y, seed, fit_range=fit_range, sigma=yu)
        plt.plot(x, f.fn(x), "r-", lw=4)

        EMU.append(f.values[1])
        ES.append(f.errors[1])
        CHI2.append(chi2(f, x, y, yu))

        plt.grid(True)
    plt.tight_layout()

    return KrFit(par=np.array(EMU),
                 err=np.array(ES),
                 chi2=np.array(CHI2),
                 valid=np.ones(len(EMU)))
Beispiel #7
0
 def _line_cut(sign):
     x = Zcenters[ok]
     y = mean.value[ok] + sign * nsigma * sigma.value[ok]
     yu = mean.uncertainty[ok]
     seed = expo_seed(x, y)
     efit = fitf.fit(fitf.expo, x, y, seed, sigma=yu)
     assert np.all(efit.values != seed)
     return efit.fn
Beispiel #8
0
def fit_lifetime(kB, kf, title="Lifetime Fit"):
    x = shift_to_bin_centers(kB.Z)
    y = kf.par
    yu = kf.err
    plt.errorbar(x, y, yu, np.diff(x)[0] / 2, fmt="kp", ms=7, lw=3)
    seed = expo_seed(x, y)
    f = fitf.fit(fitf.expo, x, y, seed, sigma=yu)
    plt.plot(x, f.fn(x), "r-", lw=4)
    print_fit(f)
    print('chi2 = {}'.format(chi2(f, x, y, yu)))
Beispiel #9
0
def quick_gauss_fit(data, bins):
    """
    Histogram input data and fit it to a gaussian with the parameters
    automatically estimated.
    """
    y, x  = np.histogram(data, bins)
    x     = shift_to_bin_centers(x)
    seed  = gauss_seed(x, y)
    f     = fitf.fit(fitf.gauss, x, y, seed)
    assert np.all(f.values != seed)
    return f
Beispiel #10
0
def test_chi2f_str_line():
    def line(x, m, n):
        return m * x + n

    y = np.array([9.108e3, 10.34e3, 1.52387e5, 1.6202e5])
    ey = np.array([3.17, 13.5, 70, 21])
    x = np.array([29.7, 33.8, 481, 511])

    fit = fitf.fit(line, x, y, seed=(1, 1), sigma=ey)
    f = lambda x: line(x, *fit.values)
    assert chi2f(f, 2, x, y, ey) == approx(14, rel=1e-02)
def lifetime_calculation(z, zrange, energy, erange, elim, slope, axes):
    """
    Measure the lifetime for the given events and plots the raw energy distribution and the energy vs drift time in a separate axes instance.

    Parameters:
    z = Array of events' drift time
    zrange = Range in drift time for the fit.
    energy = Array of events' energy.
    erange = Range in energy for plotting.
    elim = Limits for the energy at z=0.
    slope = slope of the exponential, used in the selection of the events, should be the inverse of the expected lifetime (1/[expected lifetime])
    axes = Axes object from a subplot, should have length >= 2.
    """

    axes[0].hist(energy, 50, erange, rasterized=True)
    histFun.labels("S2 energy (pes)", "Entries",
                   "Fiducialized energy spectrum")

    low_cut = elim[0] * np.exp(-slope * z)
    high_cut = elim[1] * np.exp(-slope * z)
    sel = coref.in_range(energy, low_cut,
                         high_cut)  # remove low and high E background

    axes[1].hist2d(z,
                   energy, (100, 50),
                   range=(zrange, erange),
                   rasterized=True)
    x, y, u_y = fitf.profileX(z[sel],
                              energy[sel],
                              100,
                              xrange=zrange,
                              yrange=erange)

    axes[1].plot(x, y, "--k", rasterized=True)
    axes[1].plot(z[z < 500], low_cut[z < 500], "k.", rasterized=True)
    axes[1].plot(z[z < 500], high_cut[z < 500], "k.", rasterized=True)

    Zrange_LT = zrange

    seed = np.max(y), (x[15] - x[5]) / np.log(y[15] / y[5])
    f = fitf.fit(fitf.expo, x, y, seed, fit_range=Zrange_LT, sigma=u_y)

    axes[1].plot(x, f.fn(x), "r", lw=3, rasterized=True)
    print("Energy at z=0 = {:.1f} +- {:.1f}".format(f.values[0], f.errors[0]))
    print("Lifetime      = {:.1f} +- {:.1f}".format(-f.values[1], f.errors[1]))
    print("Chi2          = {:.2f}          \n".format(f.chi2))

    #    axes[1].text(zrange[0] + 0.05*(zrange[1]-zrange[0]), erange[0] + 1000, \
    #        "Lifetime = {:.1f} $\pm$ {:.1f}".format(-f.values[1], f.errors[1]), color = "white")#, fontsize = 14)

    histFun.labels("Drift time ($\mu$s)", "S2 energy (pes)", "")

    return np.array([-f.values[1], f.errors[1]
                     ]), corrf.LifetimeCorrection(-f.values[1], f.errors[1])
Beispiel #12
0
def compute_drift_v(zdata: np.array,
                    nbins: int = 35,
                    zrange: Tuple[float, float] = (500, 640),
                    seed: Tuple[float, float, float, float] = None,
                    detector: str = 'new') -> Tuple[float, float]:
    """
    Computes the drift velocity for a given distribution
    using the sigmoid function to get the cathode edge.

    Parameters
    ----------
    zdata: array_like
        Values of Z coordinate.
    nbins: int (optional)
        The number of bins in the z coordinate for the binned fit.
    zrange: length-2 tuple (optional)
        Fix the range in z.
    seed: length-4 tuple (optional)
        Seed for the fit.
    detector: string (optional)
        Used to get the cathode position from DB.
    plot_fit: boolean (optional)
        Flag for plotting the results.

    Returns
    -------
    dv: float
        Drift velocity.
    dvu: float
        Drift velocity uncertainty.
    """

    y, x = np.histogram(zdata, nbins, zrange)
    x = shift_to_bin_centers(x)

    if seed is None: seed = np.max(y), np.mean(zrange), 0.5, np.min(y)

    z_cathode = DB.DetectorGeo(detector).ZMAX[0]
    try:
        f = fitf.fit(sigmoid,
                     x,
                     y,
                     seed,
                     sigma=poisson_sigma(y),
                     fit_range=zrange)
        dv = z_cathode / f.values[1]
        dvu = dv / f.values[1] * f.errors[1]
    except RuntimeError:
        print(
            "WARNING: Sigmoid fit for dv computation fails. NaN value will be set in its place."
        )
        dv, dvu = np.nan, np.nan

    return dv, dvu
Beispiel #13
0
def plot_and_fit(data: List,
                 title: str = '',
                 xlabel: str = 'Charge (pes)',
                 ylabel: str = 'Entries / bin',
                 num_bins: int = 100) -> Tuple[float, float, float]:

    # Fitting function
    def gauss(x, amplitude, mu, sigma):
        return amplitude / (2 * np.pi)**.5 / sigma * np.exp(
            -0.5 * (x - mu)**2. / sigma**2.)

    # Plotting the data
    fig = plt.figure(figsize=(8, 5))
    mean = np.mean(data)
    max_range = 0.1
    plt_range = ((1. - max_range) * mean, (1. + max_range) * mean)

    y, x, _ = plt.hist(data, bins=num_bins, range=plt_range)
    x = shift_to_bin_centers(x)

    # Fitting data
    seed = 1000, mean, mean * 0.01
    sigma = poisson_sigma(y)
    max_range = 0.05
    fit_range = ((1. - max_range) * mean, (1. + max_range) * mean)
    f = fitf.fit(gauss, x, y, seed, fit_range=fit_range, sigma=sigma)

    amplitude, mu, sigma = f.values[0], f.values[1], f.values[2]
    mu_err, sigma_err = f.errors[1], f.errors[2]
    fwhm = 2.35 * sigma / mu

    # Plotting the gauss fit
    mx = np.linspace(fit_range[0], fit_range[1], 1000)

    legend = f"$\mu$ = {mu:.3f}\n"
    legend += f"$\sigma$ = {sigma:.3f}\n"
    legend += f"fwhm = {fwhm/units.perCent:.2f} %"

    plt.plot(mx, f.fn(mx), 'r-')
    plt.plot(mx, gauss(mx, amplitude, mu, sigma), 'r-', label=legend)
    plt.title(title, size=14)
    plt.xlabel(xlabel, size=14)
    plt.ylabel(ylabel, size=14)
    plt.legend(loc=1)
    plt.show()

    # Verbosing
    print(f"DATA from {title} ...\n" + \
          f"mu    = {mu:10.3f} +- {mu_err:.3f}\n" +\
          f"sigma = {sigma:10.3f} +- {sigma_err:.3f}  ->  " + \
          f"fwhm  = {fwhm/units.perCent:.3f} %\n"
          f"Chi2  = {f.chi2:10.3f}\n")

    return mu, sigma, fwhm
Beispiel #14
0
def compute_drift_v(zdata: np.array,
                    nbins: int = 35,
                    zrange: Tuple[float, float] = (500, 640),
                    seed: Tuple[float, float, float, float] = None,
                    detector: str = 'new',
                    plot_fit: bool = False) -> Tuple[float, float]:
    """
    Computes the drift velocity for a given distribution
    using the sigmoid function to get the cathode edge.

    Parameters
    ----------
    zdata: array_like
        Values of Z coordinate.
    nbins: int (optional)
        The number of bins in the z coordinate for the binned fit.
    zrange: length-2 tuple (optional)
        Fix the range in z.
    seed: length-4 tuple (optional)
        Seed for the fit.
    detector: string (optional)
        Used to get the cathode position from DB.
    plot_fit: boolean (optional)
        Flag for plotting the results.

    Returns
    -------
    dv: float
        Drift velocity.
    dvu: float
        Drift velocity uncertainty.
    """

    y, x = np.histogram(zdata, nbins, zrange)
    x = shift_to_bin_centers(x)

    if seed is None: seed = np.max(y), np.mean(zrange), 0.5, np.min(y)

    f = fitf.fit(sigmoid, x, y, seed, sigma=poisson_sigma(y), fit_range=zrange)

    z_cathode = DB.DetectorGeo(detector).ZMAX[0]
    dv = z_cathode / f.values[1]
    dvu = dv / f.values[1] * f.errors[1]

    if plot_fit:
        plt.figure()
        plt.hist(zdata, nbins, zrange)
        xx = np.linspace(zrange[0], zrange[1], nbins)
        plt.plot(xx, sigmoid(xx, *f[1]), color='red')

    return dv, dvu
Beispiel #15
0
def lifetimes_in_XYRange(kre: KrEvent,
                         krnb: KrNBins,
                         krb: KrBins,
                         krr: KrRanges,
                         xyr: XYRanges,
                         XL=[(-125, -75), (-125, -75), (75, 125), (75, 125)],
                         YL=[(-125, -75), (75, 125), (75, 125), (-125, -75)],
                         nx=2,
                         ny=2,
                         figsize=(8, 8)) -> KrFit:
    """ Plots lifetime fitted to a range of XY values"""

    # Specify the range and number of bins in Z
    Znbins = krnb.Z
    Zrange = krr.Z

    fig = plt.figure(figsize=figsize)

    # XL = [(-125, -75), (-125, -75), (75, 125),(75, 125)]
    # YL = [(-125, -75), (75, 125), (75, 125),(-125, -75)]
    KF = []
    for i, pair in enumerate(zip(XL, YL)):
        xlim = pair[0]
        ylim = pair[1]
        print(f'xlim = {xlim}, ylim ={ylim}')

        # select data in region defined by xyr
        xyr = XYRanges(X=xlim, Y=ylim)
        kre_xy = select_in_XYRange(kre, xyr)
        z, e = kre_xy.Z, kre_xy.E

        ax = fig.add_subplot(nx, ny, i + 1)
        x, y, yu = fitf.profileX(z, e, Znbins, Zrange)
        plt.errorbar(x, y, yu, np.diff(x)[0] / 2, fmt="kp", ms=7, lw=3)

        # Fit profile to an exponential
        seed = expo_seed(x, y)
        f = fitf.fit(fitf.expo, x, y, seed, sigma=yu)

        # plot fitted value
        plt.plot(x, f.fn(x), "r-", lw=4)

        labels("", "Energy (pes)", "Lifetime fit")

        kf = KrFit(par=np.array(f.values),
                   err=np.array(f.errors),
                   chi2=chi2(f, x, y, yu))
        KF.append(kf)
    return KF
Beispiel #16
0
def fit_profile_1d_expo(xdata, ydata, nbins, *args, **kwargs):
    """
    Make a profile of the input data and fit it to an exponential
    function with the parameters automatically estimated.
    """
    x, y, yu     = fitf.profileX(xdata, ydata, nbins, *args, **kwargs)
    valid_points = yu > 0

    x    = x [valid_points]
    y    = y [valid_points]
    yu   = yu[valid_points]
    seed = expo_seed(x, y)
    f    = fitf.fit(fitf.expo, x, y, seed, sigma=yu)
    assert np.all(f.values != seed)
    return f
Beispiel #17
0
def fit_intensity(DF, sigma, imax=200, figsize=(10, 10)):
    I = avg_intensity(DF)
    X = np.arange(len(I))
    seed = expo_seed(X, I)
    f = fitf.fit(fitf.expo, X, I, seed, sigma=sigma * np.ones(len(I)))

    fig = plt.figure(figsize=figsize)
    plt.errorbar(X, I, fmt="kp", yerr=sigma * np.ones(len(I)), ms=7, ls='none')
    plt.plot(X, f.fn(X), lw=3)
    plt.ylim(0, imax)
    plt.xlabel('shot number')
    plt.ylabel('I (a.u.)')
    plt.show()
    print(f'Fit function -->{f}')
    return f.values, f.errors
Beispiel #18
0
def gfit(x     : np.array,
         y     : np.array,
         yu    : np.array,
         fseed : Tuple[float, float, float]) ->Tuple[FitPar, FitResult]:

    f     = fitf.fit(fitf.gauss, x, y, fseed, sigma=yu)
    c2    = chi2(f, x, y, yu)
    par  = np.array(f.values)
    err  = np.array(f.errors)
    xu   = np.diff(x) * 0.5

    fr = FitResult(par = par,
                   err = err,
                   chi2 = c2,
                   valid = True)
    fp = FitPar(x  = x, y  = y, xu = xu, yu = yu, f  = f.fn)

    return fp, fr
Beispiel #19
0
def profile_and_fit(X, Y, xrange, yrange, nbins, fitpar, label):
    fitOpt = "r"
    xe = (xrange[1] - xrange[0]) / nbins

    x, y, sy = fitf.profileX(X,
                             Y,
                             nbins=nbins,
                             xrange=xrange,
                             yrange=yrange,
                             drop_nan=True)
    sel = in_range(x, xrange[0], xrange[1])
    x, y, sy = x[sel], y[sel], sy[sel]
    f = fitf.fit(fitf.expo, x, y, fitpar, sigma=sy)

    plt.errorbar(x=x, xerr=xe, y=y, yerr=sy, linestyle='none', marker='.')
    plt.plot(x, f.fn(x), fitOpt)
    #set_plot_labels(xlabel=label[0], ylabel=label[1], grid=True)
    return f, x, y, sy
Beispiel #20
0
def lifetime_in_XYRange(kre: KrEvent, krnb: KrNBins, krb: KrBins,
                        krr: KrRanges, xyr: XYRanges) -> KrFit:
    """ Fits lifetime to a range of XY values"""

    # select data in region defined by xyr
    kre_xy = select_in_XYRange(kre, xyr)
    z, e = kre_xy.Z, kre_xy.E

    # Specify the range and number of bins in Z
    Znbins = krnb.Z
    Zrange = krr.Z

    # create a figure and plot 2D histogram and profile
    frame_data = plt.gcf().add_axes((.1, .3, .8, .6))
    plt.hist2d(z, e, (krb.Z, krb.E))
    x, y, yu = fitf.profileX(z, e, Znbins, Zrange)
    plt.errorbar(x, y, yu, np.diff(x)[0] / 2, fmt="kp", ms=7, lw=3)

    # Fit profile to an exponential
    seed = expo_seed(x, y)
    f = fitf.fit(fitf.expo, x, y, seed, sigma=yu)

    # plot fitted value
    plt.plot(x, f.fn(x), "r-", lw=4)

    # labels and ticks
    frame_data.set_xticklabels([])
    labels("", "Energy (pes)", "Lifetime fit")

    # add a second frame

    lims = plt.xlim()
    frame_res = plt.gcf().add_axes((.1, .1, .8, .2))
    # Plot (y - f(x)) / sigma(y) as a function of x
    plt.errorbar(x, (f.fn(x) - y) / yu, 1, np.diff(x)[0] / 2, fmt="p", c="k")
    plt.plot(lims, (0, 0), "g--")
    plt.xlim(*lims)
    plt.ylim(-5, +5)
    labels("Drift time (µs)", "Standarized residual")

    return KrFit(par=np.array(f.values),
                 err=np.array(f.errors),
                 chi2=chi2(f, x, y, yu))
Beispiel #21
0
def fit_lifetime_from_profile(kre: KrEvent,
                              kR: KrRanges,
                              kNB: KrNBins,
                              kB: KrBins,
                              kL: KrRanges,
                              title="Lifetime Fit") -> KrFit:

    sel = in_range(kre.X, *kL.XY) & in_range(kre.Y, *kL.XY)
    z, e = kre.Z[sel], kre.E[sel]

    frame_data = plt.gcf().add_axes((.1, .35, .8, .6))
    plt.hist2d(z, e, (kB.Z, kB.E))

    x, y, yu = fitf.profileX(z, e, kNB.Z, kR.Z, kR.E)
    plt.errorbar(x, y, yu, np.diff(x)[0] / 2, fmt="kp", ms=7, lw=3)

    seed = expo_seed(x, y)
    f = fitf.fit(fitf.expo, x, y, seed, sigma=yu)

    plt.plot(x, f.fn(x), "r-", lw=4)

    frame_data.set_xticklabels([])
    labels("", "Energy (pes)", title)
    lims = plt.xlim()

    frame_res = plt.gcf().add_axes((.1, .1, .8, .2))
    plt.errorbar(x, (f.fn(x) - y) / yu, 1, np.diff(x)[0] / 2, fmt="p", c="k")
    plt.plot(lims, (0, 0), "g--")
    plt.xlim(*lims)
    plt.ylim(-5, +5)
    labels("Drift time (µs)", "Standarized residual")
    print_fit(f)
    print('chi2 = {}'.format(chi2(f, x, y, yu)))

    return KrFit(par=np.array(f.values[1]),
                 err=np.array(f.errors[1]),
                 chi2=np.array(chi2(f, x, y, yu)),
                 valid=np.ones(1))
Beispiel #22
0
def energy_in_XYRange(kre: KrEvent, xr: Tuple[float], yr: Tuple[float],
                      ernb: ExyzNBins) -> KrFit:

    sel = in_range(kre.X, *xr) & in_range(kre.Y, *yr)
    e = kre.E[sel]
    bins = ernb.E
    frame_data = plt.gcf().add_axes((.1, .3, .8, .6))

    y, b, _ = plt.hist(e,
                       bins=bins,
                       histtype='step',
                       edgecolor='black',
                       linewidth=1.5)
    x = shift_to_bin_centers(b)

    seed = gauss_seed(x, y)
    fit_range = seed[1] - 2.0 * seed[2], seed[1] + 2.0 * seed[2]
    x, y = x[in_range(x, *fit_range)], y[in_range(x, *fit_range)]
    f = fitf.fit(fitf.gauss, x, y, seed, sigma=poisson_sigma(y))

    yu = poisson_sigma(y)
    plt.plot(x, f.fn(x), "r-", lw=4)

    frame_data.set_xticklabels([])
    labels("", "Entries", "Energy fit example")
    lims = plt.xlim()

    frame_res = plt.gcf().add_axes((.1, .1, .8, .2))
    plt.errorbar(x, (f.fn(x) - y) / yu, 1, np.diff(x)[0] / 2, fmt="p", c="k")
    plt.plot(lims, (0, 0), "g--")
    plt.xlim(*lims)
    plt.ylim(-5, +5)

    kf = KrFit(par=np.array(f.values),
               err=np.array(f.errors),
               chi2=chi2(f, x, y, yu))
    return kf
Beispiel #23
0
def main():
    """ Fitting for pmt response to ~spe led pulses """

    file_name = sys.argv[1]
    func_name = sys.argv[2]
    min_stat = 0
    fix_ped = False
    if len(sys.argv) > 3:
        use_db_gain_seeds = True if 'true' in sys.argv[3] else False
        min_stat = int(sys.argv[4])

    dats = tb.open_file(file_name, 'r')
    bins = np.array(dats.root.HIST.pmt_dark_bins)
    specsD = np.array(dats.root.HIST.pmt_dark).sum(axis=0)
    specsL = np.array(dats.root.HIST.pmt_spe).sum(axis=0)

    run_no = get_run_number(dats)
    sensor_type = SensorType.SIPM if 'sipm' in file_name else SensorType.PMT

    ffuncs = {
        'ngau': speR.poisson_scaled_gaussians(n_gaussians=7),
        'intgau': speR.poisson_scaled_gaussians(min_integral=100),
        'dfunc': partial(speR.scaled_dark_pedestal, min_integral=100),
        'conv': partial(speR.dark_convolution, min_integral=100)
    }

    pOrders = {
        'ngau': 'norm err poismu err ped err pedSig err gain err 1peSig err',
        'intgau': 'norm err poismu err ped err pedSig err gain err 1peSig err',
        'dfunc': 'norm err poismu err gain err 1peSig err',
        'conv': 'norm err poismu err gain err 1peSig err'
    }

    fnam = {
        'ngau': 'poisson_scaled_gaussians_ngau',
        'intgau': 'poisson_scaled_gaussians_min',
        'dfunc': 'scaled_dark_pedestal',
        'conv': 'dark_convolution'
    }

    ## pOut = open('pmtCalParOut_R'+file_name[-7:-3]+'_F'+func_name+'.dat', 'w')
    posRunNo = file_name.find('R')
    pOut = tb.open_file(
        'pmtCalParOut_R' + file_name[posRunNo + 1:posRunNo + 5] + '_F' +
        func_name + '.h5', 'w')

    param_writer = pIO.channel_param_writer(pOut,
                                            sensor_type='pmt',
                                            func_name=fnam[func_name],
                                            param_names=pIO.generic_params)
    test_names = ['normalization', 'Pedestal', 'Pedestal_sig']
    pTest_writer = pIO.channel_param_writer(pOut,
                                            sensor_type='pmt',
                                            func_name='Pedestal_gaussian',
                                            param_names=test_names,
                                            covariance=(3, 3))
    outDict = {}
    testDict = {}

    for ich, (dspec, lspec) in enumerate(zip(specsD, specsL)):

        b1 = 0
        b2 = len(dspec)
        if min_stat != 0:
            valid_bins = np.argwhere(lspec >= min_stat)
            b1 = valid_bins[0][0]
            b2 = valid_bins[-1][0]

        outDict[pIO.generic_params[-2]] = (bins[b1],
                                           bins[min(len(bins) - 1, b2)])

        ## Fit the dark spectrum with a Gaussian (not really necessary for the conv option)
        gb0 = [(0, -100, 0), (1e99, 100, 10000)]
        av, rms = weighted_av_std(bins[dspec > 100], dspec[dspec > 100])
        sd0 = (dspec.sum(), av, rms)
        errs = np.sqrt(dspec[dspec > 100])
        errs[errs == 0] = 0.0001
        gfitRes = fitf.fit(fitf.gauss,
                           bins[dspec > 100],
                           dspec[dspec > 100],
                           sd0,
                           sigma=errs,
                           bounds=gb0)
        outDict[pIO.generic_params[2]] = (gfitRes.values[1], gfitRes.errors[1])
        outDict[pIO.generic_params[3]] = (gfitRes.values[2], gfitRes.errors[2])

        testDict[test_names[0]] = (gfitRes.values[0], gfitRes.errors[0])
        testDict[test_names[1]] = (gfitRes.values[1], gfitRes.errors[1])
        testDict[test_names[2]] = (gfitRes.values[2], gfitRes.errors[2])
        testDict["covariance"] = gfitRes.cov
        pTest_writer(ich, testDict)

        ## Scale just in case we lost a different amount of integrals in dark and led
        scale = lspec.sum() / dspec.sum()
        print('Scale check: ', scale)

        if 'dfunc' in func_name:
            respF = ffuncs[func_name](dark_spectrum=dspec[b1:b2] * scale,
                                      pedestal_mean=gfitRes.values[1],
                                      pedestal_sigma=gfitRes.values[2])
        elif 'conv' in func_name:
            respF = ffuncs[func_name](dark_spectrum=dspec[b1:b2] * scale,
                                      bins=bins[b1:b2])
        elif fix_ped:
            respF = partial(ffuncs[func_name],
                            pedestal_mean=gfitRes.values[1],
                            pedestal_sigma=gfitRes.values[2])
        else:
            respF = ffuncs[func_name]

        ped_vals = np.array(
            [gfitRes.values[0] * scale, gfitRes.values[1], gfitRes.values[2]])

        dark = dspec[b1:b2]
        scaler_func = dark_scaler(dark[bins[b1:b2] < 0])

        seeds, bounds = seeds_and_bounds(sensor_type,
                                         run_no,
                                         ich,
                                         scaler_func,
                                         bins[b1:b2],
                                         lspec[b1:b2],
                                         ped_vals,
                                         'new',
                                         gfitRes.errors,
                                         func='dfunc',
                                         use_db_gain_seeds=True)

        ## The fit
        errs = np.sqrt(lspec[b1:b2])
        if not 'gau' in func_name:
            errs = np.sqrt(errs**2 + np.exp(-2 * seeds[1]) * dspec[b1:b2])
        errs[errs == 0] = 1  #0.001
        rfit = fitf.fit(respF,
                        bins[b1:b2],
                        lspec[b1:b2],
                        seeds,
                        sigma=errs,
                        bounds=bounds)

        ## plot the result
        plt.errorbar(bins,
                     lspec,
                     xerr=0.5 * np.diff(bins)[0],
                     yerr=np.sqrt(lspec),
                     fmt='b.')
        plt.plot(bins[b1:b2], rfit.fn(bins[b1:b2]), 'r')
        plt.plot(bins[b1:b2], respF(bins[b1:b2], *seeds), 'g')
        plt.title('Spe response fit to channel ' + str(ich))
        plt.xlabel('ADC')
        plt.ylabel('AU')

        outDict[pIO.generic_params[0]] = (rfit.values[0], rfit.errors[0])
        outDict[pIO.generic_params[1]] = (rfit.values[1], rfit.errors[1])
        gIndx = 2
        if 'gau' in func_name:
            gIndx = 4
        outDict[pIO.generic_params[4]] = (rfit.values[gIndx],
                                          rfit.errors[gIndx])
        outDict[pIO.generic_params[5]] = (rfit.values[gIndx + 1],
                                          rfit.errors[gIndx + 1])
        outDict[pIO.generic_params[-1]] = (respF.n_gaussians, rfit.chi2)
        param_writer(ich, outDict)
        plt.show(block=False)
        next_plot = input('press enter to move to next fit')
        if 's' in next_plot:
            plt.savefig('FitPMTCh' + str(ich) + '.png')
        plt.clf()
        plt.close()

    pOut.close()
Beispiel #24
0
def selection_in_band(E,
                      Z,
                      Erange,
                      Zrange,
                      Zfitrange,
                      nsigma=3.5,
                      Znbins=50,
                      Enbins=100,
                      plot=True):
    """ This returns a selection of the events that are inside the Kr E vz Z
    returns: np.array(bool)
        If plot=True, it draws E vs Z and the band
    """
    Zfit = Zfitrange

    Zbins = np.linspace(*Zrange, Znbins + 1)
    Ebins = np.linspace(*Erange, Enbins + 1)

    Zcenters = shift_to_bin_centers(Zbins)
    Zerror = np.diff(Zbins) * 0.5

    sel_e = in_range(E, *Erange)
    mean, sigma, chi2, ok = fit_slices_1d_gauss(Z[sel_e],
                                                E[sel_e],
                                                Zbins,
                                                Ebins,
                                                min_entries=5e2)
    ok = ok & in_range(Zcenters, *Zfit)

    def _line_cut(sign):
        x = Zcenters[ok]
        y = mean.value[ok] + sign * nsigma * sigma.value[ok]
        yu = mean.uncertainty[ok]
        seed = expo_seed(x, y)
        efit = fitf.fit(fitf.expo, x, y, seed, sigma=yu)
        assert np.all(efit.values != seed)
        return efit.fn

    lowE_cut = _line_cut(-1.)
    highE_cut = _line_cut(+1.)

    sel_inband = in_range(E, lowE_cut(Z), highE_cut(Z))

    if (plot == False): return sel_inband

    plt.hist2d(Z, E, (Zbins, Ebins), cmap=default_cmap)
    plt.errorbar(Zcenters[ok],
                 mean.value[ok],
                 sigma.value[ok],
                 Zerror[ok],
                 "kp",
                 label="Kr peak energy $\pm 1 \sigma$")
    f = fitf.fit(fitf.expo, Zcenters[ok], mean.value[ok], (1e4, -1e3))
    plt.plot(Zcenters, f.fn(Zcenters), "r-")
    print(f.values)
    plt.plot(Zbins,
             lowE_cut(Zbins),
             "m",
             lw=2,
             label="$\pm " + str(nsigma) + " \sigma$ region")
    plt.plot(Zbins, highE_cut(Zbins), "m", lw=2)
    plt.legend()
    labels("Drift time (µs)", "S2 energy (pes)", "Energy vs drift")

    return sel_inband
Beispiel #25
0
def fit_and_plot_slices_2d_expo(
    kre: KrEvent,
    krnb: KrNBins,
    krb: KrBins,
    krr: KrRanges,
    fit_var="E",
    min_entries=1e2,
    figsize=(12, 12)) -> KrLTSlices:
    """
    Slice the data in x and y, make the profile in z of E,
    fit it to a exponential and return the relevant values.

    """

    xybins = krb.XY
    nbins_xy = np.size(xybins) - 1
    nbins_z = krnb.Z
    nbins = nbins_xy, nbins_xy
    const = np.zeros(nbins)
    slope = np.zeros(nbins)
    constu = np.zeros(nbins)
    slopeu = np.zeros(nbins)
    chi2 = np.zeros(nbins)
    valid = np.zeros(nbins, dtype=bool)
    zrange = krr.Z

    fig = plt.figure(figsize=figsize)  # Creates a new figure
    k = 0
    index = 0
    for i in range(nbins_xy):
        sel_x = in_range(kre.X, *xybins[i:i + 2])
        for j in range(nbins_xy):
            index += 1
            #print(f' bin =({i},{j});  index = {index}')
            if k % 25 == 0:
                k = 0
                fig = plt.figure(figsize=figsize)
            ax = fig.add_subplot(5, 5, k + 1)
            k += 1
            sel_y = in_range(kre.Y, *xybins[j:j + 2])
            sel = sel_x & sel_y
            entries = np.count_nonzero(sel)
            if entries < min_entries:
                print(
                    f'entries ={entries} not enough  to fit bin (i,j) =({i},{j})'
                )
                valid[i, j] = False
                continue

            try:
                z = kre.Z[sel]
                t = kre.E[sel]
                if fit_var == "Q":
                    t = kre.Q[sel]

                x, y, yu = fitf.profileX(z, t, nbins_z, zrange)
                ax.errorbar(x, y, yu, np.diff(x)[0] / 2, fmt="kp", ms=7, lw=3)
                seed = expo_seed(x, y)
                f = fitf.fit(fitf.expo, x, y, seed, sigma=yu)
                plt.plot(x, f.fn(x), "r-", lw=4)
                plt.grid(True)
                re = np.abs(f.errors[1] / f.values[1])
                #print(f' E +- Eu = {f.values[0]} +- {f.errors[0]}')
                #print(f' LT +- LTu = {-f.values[1]} +- {f.errors[1]}')
                #print(f' LTu/LT = {re} chi2 = {f.chi2}')

                if re > 0.5:
                    print(
                        f'Relative error to large, re ={re} for bin (i,j) =({i},{j})'
                    )
                    print(f' LT +- LTu = {-f.values[1]} +- {f.errors[1]}')
                    print(f' LTu/LT = {re} chi2 = {f.chi2}')
                    valid[i, j] = False

                const[i, j] = f.values[0]
                constu[i, j] = f.errors[0]
                slope[i, j] = -f.values[1]
                slopeu[i, j] = f.errors[1]
                chi2[i, j] = f.chi2
                valid[i, j] = True
            except:
                print(f'fit failed for bin (i,j) =({i},{j})')
                pass
    plt.tight_layout()

    return KrLTSlices(Ez0=Measurement(const, constu),
                      LT=Measurement(slope, slopeu),
                      chi2=chi2,
                      valid=valid)
Beispiel #26
0
def fit_lifetime_slices(kre: KrEvent,
                        krnb: KrNBins,
                        krb: KrBins,
                        krr: KrRanges,
                        fit_var="E",
                        min_entries=1e2) -> KrLTSlices:
    """
    Slice the data in x and y, make the profile in z of E,
    fit it to a exponential and return the relevant values.

    """

    xybins = krb.XY
    nbins_xy = np.size(xybins) - 1
    nbins_z = krnb.Z
    nbins = nbins_xy, nbins_xy
    const = np.zeros(nbins)
    slope = np.zeros(nbins)
    constu = np.zeros(nbins)
    slopeu = np.zeros(nbins)
    chi2 = np.zeros(nbins)
    valid = np.zeros(nbins, dtype=bool)
    zrange = krr.Z

    for i in range(nbins_xy):
        sel_x = in_range(kre.X, *xybins[i:i + 2])
        for j in range(nbins_xy):
            #print(f' bin =({i},{j});  index = {index}')
            sel_y = in_range(kre.Y, *xybins[j:j + 2])
            sel = sel_x & sel_y
            entries = np.count_nonzero(sel)
            if entries < min_entries:
                #print(f'entries ={entries} not enough  to fit bin (i,j) =({i},{j})')
                valid[i, j] = False
                continue

            try:
                z = kre.Z[sel]
                t = kre.E[sel]
                if fit_var == "Q":
                    t = kre.Q[sel]

                x, y, yu = fitf.profileX(z, t, nbins_z, zrange)

                seed = expo_seed(x, y)
                f = fitf.fit(fitf.expo, x, y, seed, sigma=yu)
                re = np.abs(f.errors[1] / f.values[1])
                #print(f' E +- Eu = {f.values[0]} +- {f.errors[0]}')
                #print(f' LT +- LTu = {-f.values[1]} +- {f.errors[1]}')
                #print(f' LTu/LT = {re} chi2 = {f.chi2}')

                const[i, j] = f.values[0]
                constu[i, j] = f.errors[0]
                slope[i, j] = -f.values[1]
                slopeu[i, j] = f.errors[1]
                chi2[i, j] = f.chi2
                valid[i, j] = True

                if re > 0.5:
                    # print(f'Relative error to large, re ={re} for bin (i,j) =({i},{j})')
                    # print(f' LT +- LTu = {-f.values[1]} +- {f.errors[1]}')
                    # print(f' LTu/LT = {re} chi2 = {f.chi2}')
                    valid[i, j] = False

            except:
                print(f'fit failed for bin (i,j) =({i},{j})')
                pass

    return KrLTSlices(Es=Measurement(const, constu),
                      LT=Measurement(slope, slopeu),
                      chi2=chi2,
                      valid=valid)
Beispiel #27
0
def fit_dataset(dataF=None, funcName=None, minStat=None, limitPed=None):

    """ Check new fit function on SiPM spectra """
    global useSavedSeeds, GainSeeds, SigSeeds

    file_name = dataF
    func_name = funcName
    min_stat = minStat
    limit_ped = limitPed
    optimise = True
    if not file_name:
        optimise = False
        file_name = sys.argv[1]
        func_name = sys.argv[2]
        min_stat  = 0
        limit_ped = 10000.
        if len(sys.argv) > 3:
            useSavedSeeds = True if 'true' in sys.argv[3] else False
            min_stat = int(sys.argv[4])
            limit_ped = int(sys.argv[5])

    run_no = file_name[file_name.find('R')+1:file_name.find('R')+5]
    run_no = int(run_no)
    chNos = DB.DataSiPM(run_no).SensorID.values
    if useSavedSeeds:
        dodgy = DB.DataSiPM(run_no).index[DB.DataSiPM(run_no).Active==0].values
        GainSeeds = DB.DataSiPM(run_no).adc_to_pes.values
        SigSeeds  = DB.DataSiPM(run_no).Sigma.values
        ## Give generic values to previously dead or dodgy channels
        GainSeeds[dodgy] = 15
        SigSeeds[dodgy] = 2

    sipmIn = tb.open_file(file_name, 'r')

    ## Bins are the same for dark and light, just use light for now
    bins = np.array(sipmIn.root.HIST.sipm_spe_bins)
    ## LED correlated and anticorrelated spectra:
    specsL = np.array(sipmIn.root.HIST.sipm_spe).sum(axis=0)
    specsD = np.array(sipmIn.root.HIST.sipm_dark).sum(axis=0)
    
    ffuncs = {'ngau':speR.poisson_scaled_gaussians(n_gaussians=7),
              'intgau':speR.poisson_scaled_gaussians(min_integral=100),
              'dfunc':partial(speR.scaled_dark_pedestal, min_integral=100),
              'conv':partial(speR.dark_convolution, min_integral=100)}

    ## Loop over the specra:
    outData = []
    outDict = {}
    llchans = []
    if not optimise:
        fnam = {'ngau':'poisson_scaled_gaussians_ngau', 'intgau':'poisson_scaled_gaussians_min', 'dfunc':'scaled_dark_pedestal', 'conv':'dark_convolution'}
        pOut = tb.open_file('sipmCalParOut_R'+str(run_no)+'_F'+func_name+'.h5', 'w')
        param_writer = pIO.channel_param_writer(pOut,
                                                sensor_type='sipm',
                                                func_name=fnam[func_name],
                                                param_names=pIO.generic_params)
    ## Extra protection since 3065 is weird
    knownDead = [ 3056, 11009, 12058, 14010, 22028, 22029, 25049 ]
    specialCheck = [1006, 1007, 3000, 3001, 5010, 7000, 22029, 28056, 28057]
    for ich, (led, dar) in enumerate(zip(specsL, specsD)):
        if chNos[ich] in knownDead:
            outData.append([chNos[ich], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], 0, 0])
            if not optimise:
                for kname in pIO.generic_params:
                    outDict[kname] = (0, 0)
                param_writer(chNos[ich], outDict)
            print('no peaks in dark spectrum, spec ', ich)
            continue
        ## Limits for safe fit
        b1 = 0
        b2 = len(dar)
        if min_stat != 0:
            valid_bins = np.argwhere(led>=min_stat)
            b1 = valid_bins[0][0]
            b2 = valid_bins[-1][0]
        outDict[pIO.generic_params[-2]] = (bins[b1], bins[min(len(bins)-1, b2)])
        # Seed finding
        pD = find_peaks_cwt(dar, np.arange(2, 20), min_snr=2)
        if len(pD) == 0:
            ## Try to salvage in case not a masked channel
            ## Masked channels have al entries in one bin.
            if led[led>0].size == 1:
                outData.append([0., 0., 0., 0., 0., 0., 0.])
                print('no peaks in dark spectrum, spec ', ich)
                continue
            else:
                pD = np.array([dar.argmax()])
        ## Fit the dark spectrum with a Gaussian (not really necessary for the conv option)
        gb0 = [(0, -100, 0), (1e99, 100, 10000)]
        sd0 = (dar.sum(), 0, 2)
        errs = np.sqrt(dar[pD[0]-5:pD[0]+5])
        errs[errs==0] = 0.1
        gfitRes = fitf.fit(fitf.gauss, bins[pD[0]-5:pD[0]+5], dar[pD[0]-5:pD[0]+5], sd0, sigma=errs, bounds=gb0)
        outDict[pIO.generic_params[2]] = (gfitRes.values[1], gfitRes.errors[1])
        outDict[pIO.generic_params[3]] = (gfitRes.values[2], gfitRes.errors[2])

        ## Scale just in case we lost a different amount of integrals in dark and led
        ## scale = led.sum() / dar.sum()
        scale = 1

        ## Take into account the scale in seed finding (could affect Poisson mu)????
        ped_vals = np.array([gfitRes.values[0] * scale, gfitRes.values[1], gfitRes.values[2]])

        binR = bins[b1:b2]
        global darr
        darr = dar[b1:b2] * scale
        darr = darr[binR<5]
        seeds, bounds = seeds_and_bounds(ich, func_name, bins[b1:b2], led[b1:b2],
                                         ped_vals, gfitRes.errors, limit_ped)      
        ## Protect low light channels
        if seeds[1] < 0.2:
            llchans.append(chNos[ich])
            ## Dodgy setting of high charge dark bins to zero
            dar[bins>gfitRes.values[1] + 3*gfitRes.values[2]] = 0
        ##
        if 'dfunc' in func_name:
            respF = ffuncs[func_name](dark_spectrum=dar[b1:b2] * scale,
                                     pedestal_mean=gfitRes.values[1],
                                     pedestal_sigma=gfitRes.values[2])
        elif 'conv' in func_name:
            respF = ffuncs[func_name](dark_spectrum=dar[b1:b2] * scale,
                                     bins=bins[b1:b2])
        else:
            respF = ffuncs[func_name]
        
        ## The fit
        errs = np.sqrt(led)
        if not 'gau' in func_name:
            errs = np.sqrt(errs**2 + np.exp(-2 * seeds[1]) * dar)
        errs[errs==0] = 0.001
        print('About to fit channel ', chNos[ich])
        rfit = fitf.fit(respF, bins[b1:b2], led[b1:b2], seeds, sigma=errs[b1:b2], bounds=bounds)
        chi = rfit.chi2
        ## Attempt to catch bad fits and refit (currently only valid for dfunc and conv)
        if chi >= 7 or rfit.values[3] >= 2.5 or rfit.values[3] <= 1:
            ## The offending parameter seems to be the sigma in most cases
            nseed = rfit.values
            nseed[3] = 1.7
            nbound = [(bounds[0][0], bounds[0][1], bounds[0][2], 1),
                      (bounds[1][0], bounds[1][1], bounds[1][2], 2.5)]
            rfit = fitf.fit(respF, bins[b1:b2], led[b1:b2], nseed, sigma=errs[b1:b2], bounds=nbound)
            chi = rfit.chi2
        if not optimise:
            if chNos[ich] in specialCheck or chi >= 10 or rfit.values[2] < 12 or rfit.values[2] > 19 or rfit.values[3] > 3:
                if chNos[ich] in specialCheck: print('Special check channel '+str(chNos[ich]))
                print('Channel fit: ', rfit.values, chi)
                plt.errorbar(bins, led, xerr=0.5*np.diff(bins)[0], yerr=errs, fmt='b.')
                plt.plot(bins[b1:b2], respF(bins[b1:b2], *rfit.values), 'r')
                plt.plot(bins[b1:b2], respF(bins[b1:b2], *seeds), 'g')
                plt.title('Spe response fit to channel '+str(chNos[ich]))
                plt.xlabel('ADC')
                plt.ylabel('AU')
                plt.show()
        outData.append([chNos[ich], rfit.values, rfit.errors, respF.n_gaussians, chi])
        outDict[pIO.generic_params[0]] = (rfit.values[0], rfit.errors[0])
        outDict[pIO.generic_params[1]] = (rfit.values[1], rfit.errors[1])
        gIndx = 2
        if 'gau' in func_name:
            gaIndx = 4
        outDict[pIO.generic_params[4]] = (rfit.values[gIndx], rfit.errors[gIndx])
        outDict[pIO.generic_params[5]] = (rfit.values[gIndx+1], rfit.errors[gIndx+1])
        outDict[pIO.generic_params[-1]] = (respF.n_gaussians, rfit.chi2)
        if not optimise:
            param_writer(chNos[ich], outDict)

    ## Couple of plots
    gainIndx = 2
    if 'gau' in func_name:
        gainIndx = 4

    plot_names = ["Gain", "1pe sigma", "Poisson mu", "chi2"]
    pVals = [np.fromiter((ch[1][gainIndx] for ch in outData), np.float),
             np.fromiter((ch[1][gainIndx+1] for ch in outData), np.float),
             np.fromiter((ch[1][1] for ch in outData), np.float),
             np.fromiter((ch[4] for ch in outData), np.float)]
    if optimise:
        sipmIn.close()
        return pVals
    pOut.close()

    #global scalerChis
    pos_x = DB.DataSiPM(run_no).X.values
    pos_y = DB.DataSiPM(run_no).Y.values
    chNos = DB.DataSiPM(run_no).SensorID.values
    ## vals2D = np.zeros((int((pos_x.max()-pos_x.min())/10)+1, int((pos_y.max()-pos_y.min())/10)+1))
    ## print('shape: ', vals2D.shape)
    ## *_, chis = display_matrix(pos_x, pos_y, pVals[3])
    ## Trampa
    #pVals[3][pVals[3]>10] = 0
    plt.scatter(pos_x, pos_y, c=pVals[3])
    plt.title("Fit chi^2 map")
    plt.xlabel("X (mm)")
    plt.ylabel("Y (mm)")
    plt.colorbar()
    plt.show()
    #mask = np.argwhere((pVals[2]>=2) & (pVals[2]<8))
    #mask = np.argwhere((chNos<9000) | (chNos>=11000))
    #plt.scatter(pos_x[mask], pos_y[mask], c=pVals[2][mask])
    plt.scatter(pos_x, pos_y, c=pVals[2])
    plt.title("Fit poisson mu")
    plt.xlabel("X (mm)")
    plt.ylabel("Y (mm)")
    plt.colorbar()
    plt.show()
    ## fg2, ax2 = plt.subplots()
    ## p = ax2.pcolor(pos_x, pos_y, scalerChis, cmap=cm.Spectral, vmin=np.abs(scalerChis).min(), vmax=np.abs(scalerChis).max())
    ## plt.colorbar(p, ax=ax2)
    ## fg2.show()
    #plt.hist(scalerChis, bins=1000)
    #plt.show()
    fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(20,6))
    chiVs = pVals[3]
    for ax, val, nm in zip(axes.flatten(), pVals, plot_names):
        ax.hist(val[(chiVs<10) & (chiVs!=0)], bins=100)
        ax.set_title(nm)
    fig.show()
    input('finished with plots?')

    print('Low light chans:', llchans)
Beispiel #28
0
def fit_lifetime_profile(
        z: np.array, e: np.array, nbins_z: int,
        range_z: Tuple[float, float]) -> Tuple[FitPar, FitPar, FitResult]:
    """
    Make a profile of the input data and fit it to an exponential
    function.
    Parameters
    ----------
        z
            Array of z values.
        e
            Array of energy values.
        nbins_z
            Number of bins in Z for the profile fit.
        range_z
            Range in Z for fit.


    Returns
    -------
        A Tuple with:
            FitPar : Fit parameters (arrays of fitted values and errors, fit function)
            FitPar : Fit parameters (duplicated to make it compatible with fit_liftime_unbined)
            FirResults: Fit results (lt, e0, errors, chi2)

    @dataclass
    class ProfilePar:
        x  : np.array
        y  : np.array
        xu : np.array
        yu : np.array

    @dataclass
    class FitPar(ProfilePar):
        f     : FitFunction

    @dataclass
    class FitResult:
        par  : np.array
        err  : np.array
        chi2 : float
        valid : bool

    """

    logging.debug(' fit_liftime_profile')
    logging.debug(f' len (z) ={len(z)}, len (e) ={len(e)} ')
    logging.debug(f' nbins_z ={nbins_z}, range_z ={range_z} ')
    fp = None
    valid = True
    c2 = NN
    par = NN * np.ones(2)
    err = NN * np.ones(2)

    x, y, yu = profile1d(z, e, nbins_z, range_z)
    xu = np.diff(x) * 0.5
    seed = expo_seed(x, y)

    logging.debug(f' after profile: len (x) ={len(x)}, len (y) ={len(y)} ')
    try:
        f = fit(expo, x, y, seed, sigma=yu)
        c2 = f.chi2
        par = np.array(f.values)
        par[1] = -par[1]
        err = np.array(f.errors)

        logging.debug(f' e0z ={par[0]} +- {err[0]} ')
        logging.debug(f' lt ={par[1]} +- {err[1]} ')
        logging.debug(f' c2 ={c2} ')
        fp = FitPar(x=x, y=y, xu=xu, yu=yu, f=f.fn)
    except:
        warnings.warn(
            f' fit failed for seed  = {seed} in fit_lifetime_profile',
            UserWarning)
        valid = False
        raise

    fr = FitResult(par=par, err=err, chi2=c2, valid=valid)

    return fp, fp, fr
Beispiel #29
0
def main():
    """ Fitting for pmt response to ~spe led pulses """

    fileName = sys.argv[1]
    funcName = sys.argv[2]
    min_stat = 0
    limit_ped = 10000.
    fix_ped = False
    if len(sys.argv) > 3:
        min_stat = int(sys.argv[3])
        limit_ped = int(sys.argv[4])
        if limit_ped == 0:
            fix_ped = True
            limit_ped = 10000

    dats = tb.open_file(fileName, 'r')
    bins = np.array(dats.root.HIST.pmt_dark_bins)
    specsD = np.array(dats.root.HIST.pmt_dark).sum(axis=0)
    specsL = np.array(dats.root.HIST.pmt_spe).sum(axis=0)
    ## bins = np.array(dats.root.HIST.pmtdar_bins)
    ## specsD = np.array(dats.root.HIST.pmtdar).sum(axis=0)
    ## specsL = np.array(dats.root.HIST.pmtspe).sum(axis=0)

    #respF = fitf.SensorSpeResponse(bins)
    #ffuncs = {'ngau':respF.set_gaussians, 'intgau':respF.min_integ_gaussians, 'dfunc': respF.scaled_dark_pedestal, 'conv':respF.dark_convolution}
    #pOrders = {'ngau':'norm err ped err gain err poismu err pedSig err 1peSig err', 'intgau':'norm err ped err gain err poismu err pedSig err 1peSig err', 'dfunc':'norm err gain err poismu err 1peSig err', 'conv':'norm err gain err poismu err 1peSig err'}
    ffuncs = {
        'ngau': speR.poisson_scaled_gaussians(n_gaussians=7),
        'intgau': speR.poisson_scaled_gaussians(min_integral=100),
        'dfunc': partial(speR.scaled_dark_pedestal, min_integral=100),
        'conv': partial(speR.dark_convolution, min_integral=100)
    }

    pOrders = {
        'ngau': 'norm err poismu err ped err pedSig err gain err 1peSig err',
        'intgau': 'norm err poismu err ped err pedSig err gain err 1peSig err',
        'dfunc': 'norm err poismu err gain err 1peSig err',
        'conv': 'norm err poismu err gain err 1peSig err'
    }
    ## Not ideal...
    fnam = {
        'ngau': 'poisson_scaled_gaussians_ngau',
        'intgau': 'poisson_scaled_gaussians_min',
        'dfunc': 'scaled_dark_pedestal',
        'conv': 'dark_convolution'
    }

    ## pOut = open('pmtCalParOut_R'+fileName[-7:-3]+'_F'+funcName+'.dat', 'w')
    posRunNo = fileName.find('R')
    pOut = tb.open_file(
        'pmtCalParOut_R' + fileName[posRunNo + 1:posRunNo + 5] + '_F' +
        funcName + '.h5', 'w')
    ## pOut.write('InputFile: '+fileName+'\n')
    ## pOut.write('FuncName: '+funcName+'\n')
    ## pOut.write('Minimum stats: '+str(min_stat)+'\n')
    ## pOut.write('Pedestal nsig limits: +/-'+str(limit_ped)+'\n')
    ## pOut.write('\n \n')
    ## pOut.write('Parameter order: '+pOrders[funcName]+'\n')
    param_writer = pIO.channel_param_writer(pOut,
                                            sensor_type='pmt',
                                            func_name=fnam[funcName],
                                            param_names=pIO.generic_params)
    test_names = ['normalization', 'Pedestal', 'Pedestal_sig']
    pTest_writer = pIO.channel_param_writer(pOut,
                                            sensor_type='pmt',
                                            func_name='Pedestal_gaussian',
                                            param_names=test_names,
                                            covariance=(3, 3))
    outDict = {}
    testDict = {}

    for i, (dspec, lspec) in enumerate(zip(specsD, specsL)):

        b1 = 0
        b2 = len(dspec)
        if min_stat != 0:
            valid_bins = np.argwhere(lspec >= min_stat)
            b1 = valid_bins[0][0]
            b2 = valid_bins[-1][0]

        outDict[pIO.generic_params[-2]] = (bins[b1],
                                           bins[min(len(bins) - 1, b2)])

        ## Fit the dark spectrum with a Gaussian (not really necessary for the conv option)
        gb0 = [(0, -100, 0), (1e99, 100, 10000)]
        av, rms = weighted_av_std(bins[dspec > 100], dspec[dspec > 100])
        sd0 = (dspec.sum(), av, rms)
        errs = np.sqrt(dspec[dspec > 100])
        errs[errs == 0] = 0.0001
        gfitRes = fitf.fit(fitf.gauss,
                           bins[dspec > 100],
                           dspec[dspec > 100],
                           sd0,
                           sigma=errs,
                           bounds=gb0)
        outDict[pIO.generic_params[2]] = (gfitRes.values[1], gfitRes.errors[1])
        outDict[pIO.generic_params[3]] = (gfitRes.values[2], gfitRes.errors[2])

        testDict[test_names[0]] = (gfitRes.values[0], gfitRes.errors[0])
        testDict[test_names[1]] = (gfitRes.values[1], gfitRes.errors[1])
        testDict[test_names[2]] = (gfitRes.values[2], gfitRes.errors[2])
        testDict["covariance"] = gfitRes.cov
        pTest_writer(i, testDict)

        ## Scale just in case we lost a different amount of integrals in dark and led
        scale = lspec.sum() / dspec.sum()
        #print('Scale check: ', scale)
        #respF.set_dark_func(dspec[b1:b2], cent=gfitRes.values[1], sig=gfitRes.values[2], scale=scale)
        #respF.redefine_bins(bins[b1:b2])
        if 'dfunc' in funcName:
            respF = ffuncs[funcName](dark_spectrum=dspec[b1:b2] * scale,
                                     pedestal_mean=gfitRes.values[1],
                                     pedestal_sigma=gfitRes.values[2])
        elif 'conv' in funcName:
            respF = ffuncs[funcName](dark_spectrum=dspec[b1:b2] * scale,
                                     bins=bins[b1:b2])
        elif fix_ped:
            respF = partial(ffuncs[funcName],
                            pedestal_mean=gfitRes.values[1],
                            pedestal_sigma=gfitRes.values[2])
        else:
            respF = ffuncs[funcName]

        ## Take into account the scale in seed finding (could affect Poisson mu)????
        ped_vals = np.array(
            [gfitRes.values[0] * scale, gfitRes.values[1], gfitRes.values[2]])

        binR = bins[b1:b2]
        global darr
        darr = dspec[b1:b2] * scale
        darr = darr[binR < 0]
        seeds, bounds = seeds_and_bounds(i, funcName, bins[b1:b2],
                                         lspec[b1:b2], ped_vals,
                                         gfitRes.errors, limit_ped)

        print(seeds)
        #print(bounds)

        ## The fit
        errs = np.sqrt(lspec[b1:b2])
        if not 'gau' in funcName:
            errs = np.sqrt(errs**2 + np.exp(-2 * seeds[1]) * dspec[b1:b2])
        errs[errs == 0] = 1  #0.001
        ## rfit = fitf.fit(ffuncs[funcName], bins[b1:b2], lspec[b1:b2], seeds, sigma=errs, bounds=bounds)
        rfit = fitf.fit(respF,
                        bins[b1:b2],
                        lspec[b1:b2],
                        seeds,
                        sigma=errs,
                        bounds=bounds)
        ## plot the result
        plt.errorbar(bins,
                     lspec,
                     xerr=0.5 * np.diff(bins)[0],
                     yerr=np.sqrt(lspec),
                     fmt='b.')
        ## plt.plot(bins[b1:b2], rfit.fn(bins[b1:b2]), 'r')
        ## plt.plot(bins[b1:b2], ffuncs[funcName](bins[b1:b2], *seeds), 'g')
        plt.plot(bins[b1:b2], rfit.fn(bins[b1:b2]), 'r')
        plt.plot(bins[b1:b2], respF(bins[b1:b2], *seeds), 'g')
        plt.title('Spe response fit to channel ' + str(i))
        plt.xlabel('ADC')
        plt.ylabel('AU')
        #print('Sensor index: ', i)
        #print('Fit values: ', rfit.values)
        #print('Fit errors: ', rfit.errors)
        ## print('Number of Gaussians: ', respF.nGau)
        #print('Number of Gaussians: ', respF.n_gaussians)
        #print('Fit chi2: ', rfit.chi2)
        ## pOut.write('Indx: '+str(i)+', params: '+str(np.vstack((rfit.values, rfit.errors)).reshape((-1,), order='F'))+', ngaus = '+str(respF.nGau)+', chi2 = '+str(rfit.chi2)+'\n')
        ##pOut.write('Indx: '+str(i)+', params: '+str(np.vstack((rfit.values, rfit.errors)).reshape((-1,), order='F'))+', ngaus = '+str(respF.n_gaussians)+', chi2 = '+str(rfit.chi2)+'\n')
        outDict[pIO.generic_params[0]] = (rfit.values[0], rfit.errors[0])
        outDict[pIO.generic_params[1]] = (rfit.values[1], rfit.errors[1])
        gIndx = 2
        if 'gau' in funcName:
            gIndx = 4
        outDict[pIO.generic_params[4]] = (rfit.values[gIndx],
                                          rfit.errors[gIndx])
        outDict[pIO.generic_params[5]] = (rfit.values[gIndx + 1],
                                          rfit.errors[gIndx + 1])
        outDict[pIO.generic_params[-1]] = (respF.n_gaussians, rfit.chi2)
        param_writer(i, outDict)

        next_plot = input('press enter to move to next fit')
        if 's' in next_plot:
            plt.savefig('FitPMTCh' + str(i) + '.png')
        plt.clf()
        plt.close()

    pOut.close()
Beispiel #30
0
def seeds_and_bounds(indx, func, bins, spec, ped_vals, ped_errs, lim_ped):

    norm_seed = spec.sum()

    ped_seed = ped_vals[1]
    ped_min = ped_seed - lim_ped * ped_errs[1]
    ped_max = ped_seed + lim_ped * ped_errs[1]
    #print('ped check: ', ped_seed, ped_min, ped_max)

    ped_sig_seed = ped_vals[2]
    ped_sig_min = max(0.001, ped_sig_seed - lim_ped * ped_errs[2])
    ped_sig_max = ped_sig_seed + lim_ped * ped_errs[2]
    #print('rms check: ', ped_sig_seed, ped_sig_min, ped_sig_max)

    ## Remove the ped prediction and check try to get seeds for 1pe
    # first scale the dark pedestal
    dscale = spec[bins < 0].sum() / fitf.gauss(bins[bins < 0], *ped_vals).sum()
    GSeed = 0
    GSSeed = 0

    if not useSavedSeeds:  # Case of not having seeds
        l_subtract_d = spec - fitf.gauss(bins, *ped_vals) * dscale
        pDL = find_peaks_cwt(l_subtract_d,
                             np.arange(10, 20),
                             min_snr=1,
                             noise_perc=5)
        print('pDL', pDL)
        p1pe = pDL[(bins[pDL] > 15) & (bins[pDL] < 50)]
        p1pe = p1pe[spec[p1pe].argmax()]  # The maximum is taken
        ## Now fit a Gaussian
        fgaus = fitf.fit(
            fitf.gauss,
            bins[p1pe - 10:p1pe + 10],
            l_subtract_d[p1pe - 10:p1pe + 10],
            (l_subtract_d[p1pe - 10:p1pe + 10].max(), bins[p1pe], 7),
            sigma=np.sqrt(l_subtract_d[p1pe - 10:p1pe + 10]))
        #print('1pe fit check: ', fgaus.values, fgaus.errors)
        GSeed = fgaus.values[1] - ped_vals[1]
        GSSeed = np.sqrt(fgaus.values[2]**2 - ped_vals[2]**2)
    else:
        GSeed = GainSeeds[indx]
        GSSeed = SigSeeds[indx]

    ## Test scale
    ftest = fitf.fit(scaler, bins[bins < 0], spec[bins < 0], (dscale))
    print(ftest)
    print(dscale)
    #print('ftest par = ', ftest.values[0], -np.log(ftest.values[0]))

    if 'gau' in func:
        # There are 6 variables: normalization, pedestal pos., spe mean, poisson mean, pedestal sigma, 1pe sigma
        sd0 = (norm_seed, -np.log(ftest.values[0]), ped_seed, ped_sig_seed,
               GSeed, GSSeed)
        bd0 = [(0, 0, ped_min, ped_sig_min, 0, 0.001),
               (1e10, 10000, ped_max, ped_sig_max, 10000, 10000)]
        #print('Seed check: ', sd0)
        return sd0, bd0
    ## The other functions only have four parameters: normalization, spe mean, poisson mean, 1pe sigma
    sd0 = (norm_seed, -np.log(ftest.values[0]), GSeed, GSSeed)
    bd0 = [(0, 0, 0, 0.001), (1e10, 10000, 10000, 10000)]
    #print('Seed check: ', sd0)
    return sd0, bd0