Пример #1
0
def calibrate_pass2(ds, mode, write_db=False):
    """
    Load first-pass constants from the calDB for this DataSet, and the list of
    peaks we want to fit from the runDB, and fit the PPC peakshape to each one.

    Apply pygama fit functions developed in pygama.analysis.peak_fitting

    TODO:
    Make a new table in the calDB for each DataSet, "cal_pass2", that
    holds fit results, etc.  These should be used as inputs for the
    MultiPeakFitter calibration code (pass 3).
    """
    etype, ecal = "e_ftp", "e_cal"

    # load calibration database file with tinyDB and convert to pandas
    calDB = ds.calDB
    query = db.Query()
    table = calDB.table("cal_pass1").all()
    df_cal = pd.DataFrame(table) # <<---- omg awesome

    # apply calibration from db to tier 2 dataframe
    df_cal = df_cal.loc[df_cal.ds.isin(ds.ds_list)]
    p1cal = df_cal.iloc[0]["p1cal"]
    t2df = ds.get_t2df()
    t2df[ecal] = t2df[etype] * p1cal # create a new column

    # get additional options from the config file
    cal_opts = ds.get_p1cal_pars(etype)
    pk_lim = cal_opts["peak_lim_keV"]
    # pk_thresh = cal_opts["peakdet_thresh"]

    fits = {}
    pk_names = ds.config["pks"]

    # loop over a list of peaks we assume are always present
    for e_peak in sorted(ds.config["main_peaks"], reverse=True):

        # histogram the spectrum near the peak
        xlo, xhi, xpb = e_peak - pk_lim, e_peak + pk_lim, 1
        hE, xE, vE = ph.get_hist(t2df[ecal], range=(xlo, xhi), dx=xpb, trim=False)

        # run peakdet and measure the difference between expected & calibrated
        # maxes, mins = pu.peakdet(hE, pk_thresh, xE)
        # diffs = [e_peak - pk_val[0] for pk_val in maxes]
        # pk_min, i_min = min((v, i) for (i, v) in enumerate(diffs))
        # print(e_peak, pk_min, i_min)

        # -- run gaussian fit (gauss + linear bkg term) --
        if mode == 0:

            # mu, sigma, a, b, m
            # TODO: could set initial sigma w. some simple linear function
            x0 = [e_peak, 5, np.sum(hE), np.mean(hE[:50]), 1]

            xF, xF_cov = pf.fit_hist(pf.gauss_lin, hE, xE, var=np.ones(len(hE)),
                                     guess=x0)
            results = {
                "e_fit" : xF[0],
                "e_unc" : np.sqrt(xF_cov[0][0]),
                "fwhm" : xF[1] * 2.355,
                "fwhm_unc" : np.sqrt(xF_cov[1][1]) * 2.355,
                "resid" : abs(e_peak - xF[0]),
                "bkg0" : xF[3],
                "bkg1" : xF[4]
                }
            chisq = []
            for i, h in enumerate(hE):
                diff = (pf.gauss_lin(xE[i], *xF) - hE[i])**2 / pf.gauss_lin(xE[i], *xF)
                chisq.append(abs(diff))
            results["chisq_ndf"] = sum(np.array(chisq) / len(hE))

            # update DB results
            fits[pk_names[str(e_peak)]] = results

        # -- run peakshape function fit (+ linear bkg term) --
        elif mode == 1:

            # peakshape parameters: mu, sigma, hstep, htail, tau, bg0, a=1
            hstep = 0.001 # fraction that the step contributes
            htail = 0.1
            amp = np.sum(hE)
            tau = 10
            bg0 = np.mean(hE[:20])
            x0 = [e_peak, 5, hstep, htail, tau, bg0, amp]

            xF, xF_cov = pf.fit_hist(pf.radford_peak, hE, xE, var=vE, guess=x0)

            results = {
                "e_fit" : xF[0],
                "fwhm" : xF[1] * 2.355
                # ...
            }
            chisq = []
            for i, h in enumerate(hE):
                diff = (pf.radford_peak(xE[i], *xF) - hE[i])**2 / pf.radford_peak(xE[i], *xF)
                chisq.append(abs(diff))
            results["chisq_ndf"] = sum(np.array(chisq) / len(hE))

            # update DB results
            fits[pk_names[str(e_peak)]] = results


        # -- plot the fit --
        plt.axvline(e_peak, c='g')

        if mode==0:
            # gaussian fit
            # plt.plot(xE, pf.gauss_lin(xE, *x0), c='orange', label='guess')
            plt.plot(xE, pf.gauss_lin(xE, *xF), c='r', label='fit')

        if mode==1:
            # peakshape function
            # plt.plot(xE, pf.radford_peak(xE, *x0), c='orange', label='guess')
            plt.plot(xE, pf.radford_peak(xE, *xF), c='r', label='peakshape')

            # plot individual components

            # consts - tail_hi & bg
            tail_hi, gaus, bg, step, tail_lo = pf.radford_peak(xE, *xF, components=True)
            gaus = np.array(gaus)
            step = np.array(step)
            tail_lo = np.array(tail_lo)

            plt.plot(xE, gaus * tail_hi, ls="--", lw=2, c='g', label="gaus+hi_tail")
            plt.plot(xE, step + bg, ls='--', lw=2, c='m', label='step + bg')
            plt.plot(xE, tail_lo, ls='--', lw=2, c='k', label='tail_lo')

        plt.plot(xE[1:], hE, ls='steps', lw=1, c='b', label="data")
        plt.plot(np.nan, np.nan, c='w', label=f"fwhm = {results['fwhm']:.2f} keV")

        plt.xlabel("Energy (keV)", ha='right', x=1)
        plt.ylabel("Counts", ha='right', y=1)
        plt.legend()
        # plt.show()
        plt.savefig("./plots/cage_ds3_pass2cal.pdf")


    if write_db:
        calDB = ds.calDB
        query = db.Query()
        table = calDB.table("cal_pass2")

        # collapse data to 1 row
        row = {}
        for pk in fits:
            for key, val in fits[pk].items():
                row[f"{key}_{pk}"] = val

        # write an entry for every dataset.  if we've chained together
        # multiple datasets, the values will be the same.
        # use "upsert" to avoid writing duplicate entries.
        for dset in ds.ds_list:
            table.upsert(row, query.ds == dset)

        print("wrote results to DB.")
Пример #2
0
def optimize_trap(rise_times, test=False):
    """
    duplicate the plot from Figure 2.7 of Kris Vorren's thesis.
    need to fit the e_ftp peak to the HPGe peakshape function (same as in
    calibration.py) and plot the resulting FWHM^2 vs. the ramp time.
    """
    out_dir = "~/Data/cage"
    opt_file = f"{out_dir}/cage_ds3_optimize.h5"
    print("input file:", opt_file)
    
    # match keys to settings; should maybe do this in prev function as attrs.
    with pd.HDFStore(opt_file, 'r') as store:
        keys = [key[1:] for key in store.keys()]  # remove leading '/'
        settings = {keys[i] : rise_times[i] for i in range(len(keys))}
    
    # loop over the keys and fit each e_ftp spectrum to the peakshape function
    fwhms = {}
    for key, rt in settings.items():
        
        t2df = pd.read_hdf(opt_file, key=key)
        
        # histogram spectrum near the uncalibrated peak -- have to be careful here
        xlo, xhi, xpb = 2550, 2660, 1
        hE, xE, vE = ph.get_hist(t2df["e_ftp"], range=(xlo, xhi), dx=xpb, trim=False)
        
        # set initial guesses for the peakshape function.  most are pretty rough
        mu = xE[np.argmax(hE)]
        sigma = 5
        hstep = 0.001
        htail = 0.5
        tau = 10
        bg0 = np.mean(hE[:20])
        amp = np.sum(hE)
        x0 = [mu, sigma, hstep, htail, tau, bg0, amp]
        
        xF, xF_cov = pf.fit_hist(pf.radford_peak, hE, xE, var=vE, guess=x0)
        
        fwhms[key] = xF[1] * 2.355

        if test:
            plt.cla()
            
            # peakshape function
            plt.plot(xE, pf.radford_peak(xE, *x0), c='orange', label='guess')
            plt.plot(xE, pf.radford_peak(xE, *xF), c='r', label='peakshape')
            
            plt.axvline(mu, c='g')
            
            # plot individual components
            # tail_hi, gaus, bg, step, tail_lo = pf.radford_peak(xE, *xF, components=True)
            # gaus = np.array(gaus)
            # step = np.array(step)
            # tail_lo = np.array(tail_lo)
            # plt.plot(xE, gaus * tail_hi, ls="--", lw=2, c='g', label="gaus+hi_tail")
            # plt.plot(xE, step + bg, ls='--', lw=2, c='m', label='step + bg')
            # plt.plot(xE, tail_lo, ls='--', lw=2, c='k', label='tail_lo')
        
            plt.plot(xE[1:], hE, ls='steps', lw=1, c='b', label="data")
            plt.plot(np.nan, np.nan, c='w', label=f"fwhm = {results['fwhm']:.2f} uncal.")
        
            plt.xlabel("Energy (uncal.)", ha='right', x=1)
            plt.ylabel("Counts", ha='right', y=1)
            plt.legend(loc=2)
            
            plt.show()
Пример #3
0
def get_fwhm(f_grid, f_opt, verbose=False):
    """
    duplicate the plot from Figure 2.7 of Kris Vorren's thesis (and much more!)
    
    this code fits the e_ftp peak to the HPGe peakshape function (same as in
    calibration.py) and writes a new column to df_grid, "fwhm".
    """
    df_grid = pd.read_hdf(f_grid)

    # declare some new columns for df_grid
    cols = ["fwhm", "rchi2"]
    for col in cols:
        df_grid[col] = np.nan

    # loop over the keys and fit each e_ftp spectrum to the peakshape function
    print("i  rise  flat  rc  fwhm  rchi2")

    for i, row in df_grid.iterrows():

        key = f"opt_{i}"
        t2df = pd.read_hdf(f_opt, key=f"opt_{i}")

        # auto-histogram spectrum near the uncalibrated peak
        hE, xE, vE = ph.get_hist(t2df["e_ftp"], bins=1000, trim=False)

        # shift the histogram to be roughly centered at 0 and symmetric
        mu = xE[np.argmax(hE)]
        xE -= mu
        imax = np.argmax(hE)
        hmax = hE[imax]
        idx = np.where(hE > hmax / 2)  # fwhm
        ilo, ihi = idx[0][0], idx[0][-1]
        sig = (xE[ihi] - xE[ilo]) / 2.355
        idx = np.where((xE > -8 * sig) & (xE < 8 * sig))
        ilo, ihi = idx[0][0], idx[0][-1] - 1

        xE = xE[ilo - 1:ihi]
        hE, vE = hE[ilo:ihi], vE[ilo:ihi]

        # plt.plot(xE[1:], hE, ls='steps', c='r', lw=3)
        # plt.show()
        # exit()

        # set initial guesses for the peakshape function.  could all be improved
        mu = 0
        sigma = 5  # radford uses an input linear function
        hstep = 0.001
        htail = 0.5
        tau = 10
        bg0 = np.mean(hE[:20])
        amp = np.sum(hE)
        x0 = [mu, sigma, hstep, htail, tau, bg0, amp]

        xF, xF_cov = pf.fit_hist(pf.radford_peak, hE, xE, var=vE, guess=x0)

        # goodness of fit
        chisq = []
        for j, h in enumerate(hE):
            model = pf.radford_peak(xE[j], *xF)
            diff = (model - h)**2 / model
            chisq.append(abs(diff))

        # update the master dataframe
        fwhm = xF[1] * 2.355
        rchi2 = sum(np.array(chisq) / len(hE))

        df_grid.at[i, "fwhm"] = fwhm
        df_grid.at[i, "rchi2"] = rchi2

        rise, flat, rc = row[:3]
        label = f"{i} {rise:.2f} {flat:.2f} {rc:.0f} {fwhm:.2f} {rchi2:.2f}"
        print(label)

        if verbose:

            # plot every dang fit
            plt.cla()

            # peakshape function
            plt.plot(xE, pf.radford_peak(xE, *x0), c='orange', label='guess')
            plt.plot(xE, pf.radford_peak(xE, *xF), c='r', label='peakshape')

            plt.axvline(mu, c='g')

            # plot individual components
            # tail_hi, gaus, bg, step, tail_lo = pf.radford_peak(xE, *xF, components=True)
            # gaus = np.array(gaus)
            # step = np.array(step)
            # tail_lo = np.array(tail_lo)
            # plt.plot(xE, gaus * tail_hi, ls="--", lw=2, c='g', label="gaus+hi_tail")
            # plt.plot(xE, step + bg, ls='--', lw=2, c='m', label='step + bg')
            # plt.plot(xE, tail_lo, ls='--', lw=2, c='k', label='tail_lo')

            plt.plot(xE[1:], hE, ls='steps', lw=1, c='b', label="data")
            plt.plot(np.nan, np.nan, c='w', label=f"fwhm = {fwhm:.2f} uncal.")
            plt.plot(np.nan, np.nan, c='w', label=label)

            plt.xlabel("Energy (uncal.)", ha='right', x=1)
            plt.ylabel("Counts", ha='right', y=1)
            plt.legend(loc=2, fontsize=12)

            plt.show()

    # write the updated df_grid to the output file.
    if not verbose:
        df_grid.to_hdf(f_grid, key="pygama_optimization")
        print("wrote output file")