Exemple #1
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
Exemple #2
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
Exemple #3
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
Exemple #4
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
def gaussian_fit(x: np.array, y: np.array, seed: GaussPar,
                 n_sigma: int) -> Tuple[FitPar, FitResult]:
    """Gaussian fit to x,y variables, with fit range defined by n_sigma"""

    mu = seed.mu.value
    std = seed.std.value
    amp = seed.amp.value
    fit_range = mu - n_sigma * std, mu + n_sigma * std

    x, y = x[in_range(x, *fit_range)], y[in_range(x, *fit_range)]
    yu = poisson_sigma(y)
    fseed = (amp, mu, std)

    par, err = par_and_err_from_seed(seed)
    fr = FitResult(par=par, err=err, chi2=NN, valid=False)
    fp = None

    with warnings.catch_warnings():
        warnings.filterwarnings(
            'error')  # in order to handle fit failures here
        try:
            fp, fr = gfit(x, y, yu, fseed)
        except RuntimeWarning:  # this is the most usual failure, and usually solved trying fitx
            # with a different seed
            print(
                f' fit failed for seed  = {seed}, due to RunTimeWarning, retry fit '
            )
            fseed = (10 * fseed[0], fseed[1], fseed[2])
            try:
                fp, fr = gfit(x, y, yu, fseed)
            except RuntimeWarning:  #  Give up on second failure
                print(
                    f' fit failed for seed  = {seed}, due to RunTimeWarning, give up '
                )
        except OptimizeWarning:
            print(
                f' OptimizeWarning was raised for seed  = {seed} due to OptimizeWarning'
            )
        except RuntimeError:
            print(f' fit failed for seed  = {seed}  due to RunTimeError')
        except TypeError:
            print(f' fit failed for seed  = {seed}  due to TypeError')

    return fp, fr
Exemple #6
0
def get_chi2_and_pvalue(ydata, yfit, ndf, sigma=None):
    """
    Gets reduced chi2 and p-value

    Parameters
    ----------
    ydata : np.ndarray
        Data points.
    yfit : np.ndarray
        Fit values corresponding to ydata array.
    sigma : np.ndarray
        Data errors. If sigma is not given, it takes the poisson case:
            sigma = sqrt(ydata)
    ndf : int
        Number of degrees of freedom
        (number of data points - number of parameters).

    Returns
    -------
    chi2 : float
        Reduced chi2 computed as:
            chi2 = [sum(ydata - yfit)**2 / sigma**2] / ndf
    pvalue : float
        Fit p-value.
    """

    if sigma is None:
        sigma = poisson_sigma(ydata)

    chi2   = np.sum(((ydata - yfit) / sigma)**2)
    pvalue = scipy.stats.chi2.sf(chi2, ndf)

    if ndf > 0:
        return chi2 / ndf, pvalue
    else:
        print(f'Warning in fit_functions_ic:chi2: ndf =0: chi2 = {chi2}')
        return chi2, pvalue
Exemple #7
0
    peaks_dark = find_peaks_cwt(dar, np.arange(2, 20), min_snr=2)
    if len(peaks_dark) == 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([channs[ich], [0, 0, 0, 0], [0, 0, 0, 0], 0, 0])
            print('no peaks in dark spectrum, spec ', ich)
            continue
        else:
            peaks_dark = 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)
    sel = np.arange(peaks_dark[0] - 5, peaks_dark[0] + 5)
    errs = poisson_sigma(dar[sel], default=0.1)

    gauss_fit_dark = fitf.fit(fitf.gauss,
                              bins[sel],
                              dar[sel],
                              sd0,
                              sigma=errs,
                              bounds=gb0)
    outDict[cpio.generic_params[2]] = (gauss_fit_dark.values[1],
                                       gauss_fit_dark.errors[1])
    outDict[cpio.generic_params[3]] = (gauss_fit_dark.values[2],
                                       gauss_fit_dark.errors[2])

    ## Scale just in case we lost a different amount of integrals in dark and led
    ## scale = led.sum() / dar.sum()
    scale = 1.5
def measureDriftVelocity(run, drift_field, z, zrange_dv, bins_dv, buffersize,
                         z_cathode, el_time):
    """
    Given a Z distribution, returns the drift velocity with its error.
    The drift velocity is obtained through an smooth Heavyside fit or logistic function (https://en.wikipedia.org/wiki/Logistic_function).

    Parameters:
    run = Run number, to add it to the figure title
    drift_field = Drift field of the run, to add it to the figure title.
    groupDST = Grouped DST (by event id).
    zrange_dv = Range in drift time for the fit.
    bins_dv = Bins for plotting the region of interest.
    buffersize = Buffersize of the run, to plot the drift time distribution.
    z_cathode = Cathode z-position in mm.
    """

    fig, axes = plt.subplots(1, 3, figsize=(24, 6))
    st = fig.suptitle(
        "Run {0} (drift field = {1:.0f} V/cm/bar) Z distribution".format(
            run, drift_field[0]),
        fontsize=16)

    axes[0].hist(z, 100, [0, buffersize], rasterized=True)
    axes[0].set_xlabel("Z (mm)")
    axes[0].set_ylabel("")

    axes[1].hist(z, 100, [0, buffersize], rasterized=True)
    axes[1].set_xlabel("Z (mm)")
    axes[1].set_ylabel("")
    axes[1].set_yscale('log')

    plt.axes(axes[2])
    y, x = h1d(z, bins_dv, zrange_dv, "Z (mm)", "Number of events", ax=axes[2])
    axes[2].set_yscale('log')

    sigmoid = lambda x, A, B, C, D: A / (1 + np.exp(-C * (x - B))) + D
    seed = np.max(y), np.mean(zrange_dv), np.diff(zrange_dv)[0] / 100, np.min(
        y)
    f = fitf.fit(sigmoid,
                 x,
                 y,
                 seed,
                 fit_range=zrange_dv,
                 sigma=statf.poisson_sigma(y))

    plt.plot(x, f.fn(x), "r", lw=1.25, rasterized=True)
    histFun.labels("Drift time ($\mu$s)", "Number of events", "")

    dv = z_cathode[0] / (f.values[1] - el_time[0])
    dv_e_stat = z_cathode[0] / (
        (f.values[1] - el_time[0])**
        2) * f.errors[1]  # Statistic error derived from fit.
    dv_e_syst = (z_cathode[0]/((f.values[1] - el_time[0])**2) * 1.)**2 + \
                (z_cathode[1]/(f.values[1] - el_time[0]))**2 + \
                (z_cathode[0]/((f.values[1] - el_time[0])**2) * el_time[1])**2 # Systematic error derived from drift time measurement (asumed to be 1 mus), drift length and drifted time in EL.
    dv_e_syst = np.sqrt(dv_e_syst)

    dv = np.array([dv, dv_e_stat, dv_e_syst])

    print("Max drift time = {:.3f} +- {:.3f}".format(f.values[1], f.errors[1]))
    print(
        "Drift velocity   = {:.5f} +- {:.5f} (stat.) +- {:.5f} (syst.)".format(
            dv[0], dv[1], dv[2]))
    print("======================================")
    axes[2].annotate("Drift time = \n{:.3f} $\pm$ {:.3f}".format(
        f.values[1], f.errors[1]),
                     xy=(0.665, 0.9),
                     xycoords='axes fraction',
                     color="black")  #, fontsize = 14)

    extent = axes[0].get_tightbbox(fig.canvas.get_renderer()).transformed(
        fig.dpi_scale_trans.inverted())
    fig.savefig("DriftVel_R{}.pdf".format(run), bbox_inches=extent)
    extent = axes[1].get_tightbbox(fig.canvas.get_renderer()).transformed(
        fig.dpi_scale_trans.inverted())
    fig.savefig("DriftVel_Log_R{}.pdf".format(run), bbox_inches=extent)
    extent = axes[2].get_tightbbox(fig.canvas.get_renderer()).transformed(
        fig.dpi_scale_trans.inverted())
    fig.savefig("DriftVel_LogZoom_R{}.pdf".format(run), bbox_inches=extent)
    plt.close(fig)

    return dv