def calibrate_pass0(ds, df, etype="e_ftp", write_db=False, test=False): print("Calibration on single peak") epeaks = sorted(ds.config["cal_peaks"], reverse=True) pu = epeaks[0] # get initial parameters for this energy estimator calpars = ds.get_p1cal_pars("e_ftp") pk_thresh = calpars["peakdet_thresh"] match_thresh = calpars["match_thresh"] xlo, xhi, xpb = calpars["xlims"] ene = df[etype] nb = int((xhi - xlo) / xpb) h, bins = np.histogram(ene, nb, (xlo, xhi)) b = (bins[:-1] + bins[1:]) / 2. # run peakdet to identify the uncalibrated maxima maxes, mins = pgu.peakdet(h, pk_thresh, b) calVal = maxes[-1][0] fcal = pu / calVal firstcal = ene * fcal h1, bins1 = np.histogram(firstcal, 1600, (1400, 3000)) b1 = (bins1[:-1] + bins1[1:]) / 2. maxes1, mins1 = pgu.peakdet(h1, pk_thresh, b1) print(maxes1) # plt.plot(h1) # plt.show() iter = 0 if len(maxes1) == len(epeaks): peaks = sorted(maxes1[:, 0], reverse=True) else: print( "found more or less peaks then expected! Have a look at the raw spectrum again..." ) peaks = maxes1[:, 0] peaks = np.array(peaks, dtype=float) print("Peaks = ", peaks) def pol1(x, a, b): return a * x + b pars1, cov1 = opt.curve_fit(pol1, peaks, epeaks) errs1 = np.sqrt(np.diag(cov1)) print("Calibration curve: ", pars1) ecal = firstcal * pars1[0] + pars1[1] hcal, bins2 = np.histogram(ecal, 3000, (0, 3000)) plt.plot(hcal) plt.show()
def spectrum_medfilt_peaks(): with open("runDB.json") as f: runDB = json.load(f) tier_dir = os.path.expandvars(runDB["tier_dir"]) meta_dir = os.path.expandvars(runDB["meta_dir"]) df = pd.read_hdf('{}/t2_run{}.h5'.format(tier_dir, sys.argv[1])) m = np.array(df['e_ftp']) xlo, xhi, xpb = 0, 10000, 10 nbins = int((xhi - xlo) / xpb) hist, bins = np.histogram(m, nbins, (xlo, xhi)) #bins = bins + (bins[1] - bins[0])/2 #bins = bins[0:(len(bins)-1)] hist = np.append(hist, 0) hmed = medfilt(hist, 5) hpks = hist - hmed plt.plot(bins, hist, '-k', ls="steps", label='uncalibrated energy spectrum, run ' + str(sys.argv[1])) plt.plot(bins, hmed, '-r', ls='steps', label="peakless spectrum (medfilt)") plt.plot(bins, hpks, '-b', ls='steps', label='peaks only (spectrum - medfilt)') thresholds = np.arange(5, 10000, 5, dtype=int) for i in range(len(thresholds)): maxes, mins = pgu.peakdet(hpks, thresholds[i], bins) if len(maxes) == 4: break for pk in maxes: plt.plot(pk[0], pk[1], '.m', ms=10) plt.xlim(0, 9000) plt.ylim(0, plt.ylim()[1]) #plt.semilogy() colors = ['black', 'red', 'blue'] lines = [Line2D([0], [0], color=c) for c in colors] labels = [ 'uncalibrated energy spectrum, run ' + str(sys.argv[1]), 'peakless spectrum (medfilt)', 'peaks only (spectrum - medfilt)' ] plt.legend(lines, labels, frameon=True, loc='upper right', fontsize='x-small') plt.show()
def find_peaks(hE, xE, var): """ run peakdet routine (use a JSON config file to set thresholds) """ maxes, mins = pu.peakdet(hE, 100, xE[1:]) umaxes = np.array(sorted([x[0] for x in maxes])) print(f"{umaxes}") for peak in umaxes: plt.axvline(peak, linestyle="--", lw=1) plt.semilogy(xE[1:], hE, ls='steps', lw=1, c='r') plt.xlabel("Energy (uncal.)", ha='right', x=1) plt.ylabel("Counts", ha='right', y=1) plt.show()
def calibrate_pass1(ds, df, etype="e_ftp", write_db=False, test=False): """ Run a "first guess" calibration of an arbitrary energy estimator. Uses a peak matching algorithm based on finding ratios of uncalibrated and "true" (keV) energies. We run peakdet to find the maxima in the spectrum, then compute all ratios e1/e2, u1/u2, ..., u29/u30 etc. We find the subset of uncalibrated ratios (u7/u8, ... etc) that match the "true" ratios, and compute a calibration constant for each. Then for each uncalibrated ratio, we assume it to be true, then loop over the expected peak positions. We shift the uncalibrated peaks so that the true peak would be very close to 0, and calculate its distance from 0. The "true" calibration constant will minimize this value for all ratios, and this is the one we select. Writes first-pass calibration results to a database, for access by the second pass, and other codes. """ print("Start first Calibration!") # get a list of peaks we assume are always present epeaks = sorted(ds.config["cal_peaks"], reverse=True) # get initial parameters for this energy estimator calpars = ds.get_p1cal_pars(etype) pk_thresh = calpars["peakdet_thresh"] match_thresh = calpars["match_thresh"] xlo, xhi, xpb = calpars["xlims"] # make energy histogram ene = df[etype] nb = int((xhi - xlo) / xpb) h, bins = np.histogram(ene, nb, (xlo, xhi)) b = (bins[:-1] + bins[1:]) / 2. print(pk_thresh, " / ", b) print("") print(bins) plt.plot(h) plt.show() # run peakdet to identify the uncalibrated maxima maxes, mins = pgu.peakdet(h, pk_thresh, b) print(maxes) umaxes = np.array(sorted([x[0] for x in maxes], reverse=True)) # compute all ratios ecom = [c for c in it.combinations(epeaks, 2)] ucom = [c for c in it.combinations(umaxes, 2)] eratios = np.array([x[0] / x[1] for x in ecom]) # assumes x[0] > x[1] uratios = np.array([x[0] / x[1] for x in ucom]) # match peaks to true energies cals = {} for i, er in enumerate(eratios): umatch = np.where(np.isclose(uratios, er, rtol=match_thresh)) e1, e2 = ecom[i][0], ecom[i][1] if test: print(f"\nratio {i} -- e1 {e1:.0f} e2 {e2:.0f} -- {er:.3f}") if len(umatch[0]) == 0: continue caldists = [] for ij, j in enumerate(umatch[0]): u1, u2 = ucom[j][0], ucom[j][1] cal = (e2 - e1) / (u2 - u1) cal_maxes = cal * umaxes # shift peaks by the amount we would expect if this const were true. # compute the distance (in "keV") of the peak that minimizes this. dist = 0 for e_true in epeaks: idx = np.abs(cal_maxes - e_true).argmin() dist += np.abs(cal_maxes[idx] - e_true) caldists.append([cal, dist]) if test: dev = er - uratios[j] # set by match_thresh parameter print( f"{ij} {u1:-5.0f} {u2:-5.0f} {dev:-7.3f} {cal:-5.2f}") # get the cal ratio with the smallest total dist caldists = np.array(caldists) imin = caldists[:, 1].argmin() cals[i] = caldists[imin, :] if test: print( f"best: {imin} {caldists[imin, 0]:.4f} {caldists[imin, 1]:.4f}" ) if test: print("\nSummary:") for ipk in cals: e1, e2 = ecom[ipk][0], ecom[ipk][1] print(f"{ipk} {e1:-6.1f} {e2:-6.1f} cal {cals[ipk][0]:.5f}") # get first-pass const for this DataSet cal_vals = np.array([c[1][0] for c in cals.items()]) ds_cal = np.median(cal_vals) ds_std = np.std(cal_vals) print(f"Pass-1 cal for {etype}: {ds_cal:.5e} pm {ds_std:.5e}") if test: plt.semilogy(b * ds_cal, h, ls='steps', lw=1.5, c='b', label=f"{etype}, {sum(h)} cts") for x, y in maxes: plt.plot(x * ds_cal, y, "m.", ms=10) pks = ds.config["pks"] cmap = plt.cm.get_cmap('jet', len(pks) + 1) for i, pk in enumerate(pks): plt.axvline(float(pk), c=cmap(i), linestyle="--", lw=1, label=f"{pks[pk]}: {pk} keV") plt.xlabel("Energy (keV)", ha='right', x=1) plt.ylabel("Counts", ha='right', y=1) plt.legend(fontsize=9) # plt.show() if write_db: calDB = ds.calDB query = db.Query() table = calDB.table("cal_pass1") # 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: row = {"ds": dset, "p1cal": ds_cal, "p1std": ds_std} table.upsert(row, query.ds == dset)
def linear_calibration(): with open("runDB.json") as f: runDB = json.load(f) tier_dir = os.path.expandvars(runDB["tier_dir"]) meta_dir = os.path.expandvars(runDB["meta_dir"]) pks_lit = [238.6, 583.2] pks_lit = [238.6, 583.2] df = pd.read_hdf('{}/t2_run{}.h5'.format(tier_dir, sys.argv[1])) # ds = DataSet(runlist=[278, 279], md='./runDB.json', tier_dir=tier_dir) # df = ds.get_t2df() m = np.array(df['e_ftp']) xlo, xhi, xpb = 0, 10000, 1 nbins = int((xhi - xlo) / xpb) hist, bins = np.histogram(m, nbins, (xlo, xhi)) hist = np.pad(hist, (1, 0), 'constant') bins = bins + (bins[1] - bins[0]) / 2 hmed = medfilt(hist, 51) hpks = hist - hmed thresholds = np.arange(5, 10000, 5, dtype=int) for i in range(len(thresholds)): maxes, mins = pgu.peakdet(hpks, thresholds[i], bins) if len(maxes) == 5: break x_maxes = [] for i in range(len(maxes)): x_value = maxes[i][0] x_maxes.append(x_value) ratios = [] for i in range(len(maxes)): for j in range(len(maxes)): ratios.append(x_maxes[i] / x_maxes[j]) #another way to do the block above #import itertools #ratios = [] #for x_i, x_j in itertools.product(x_maxes, x_maxes): #ratios.append(x_i/x_j) real_ratio = [] for i in range(len(ratios)): real_ratio.append(pks_lit[1] / pks_lit[0]) ratios_array = np.array(ratios) real_ratio_array = np.array(real_ratio) closeness = np.absolute(ratios_array - real_ratio_array) relevant_entry = int(np.where(closeness == np.amin(closeness))[0]) adc_2_peak_combinations = [] for i in range(len(x_maxes)): for j in range(len(x_maxes)): adc_2_peak_combinations.append([x_maxes[j], x_maxes[i]]) #ADC Values Corresponding to Energy Peaks 1460.820 keV and 2614.511 keV adc_values = adc_2_peak_combinations[relevant_entry] #Now we model a linear equation to go from ADC value (e_ftp) to real energy using the points (adc_values[0], 1460.820) and (adc_values[1], 2614.511 keV) # E = A(e_ftp) + B A = float((pks_lit[1] - pks_lit[0]) / (adc_values[1] - adc_values[0])) B = float((pks_lit[1] - adc_values[1] * A)) #Now we will add a column to df that represents the energy measured (rather than only having the adc (e_ftp) value measured as the df currently does) df['e_cal'] = df['e_ftp'] * A + B pks_lit_all = [ 238.6, 338.3, 463.0, 511, 0, 583.2, 727.3, 794.9, 860.6, 911.2, 969, 1460.8, 1592.5, 2103.5, 2614.5 ] plt.axvline(x=238.6, ymin=0, ymax=30, color='red', linestyle='--', lw=1, zorder=1) plt.axvline(x=338.3, ymin=0, ymax=30, color='aqua', linestyle='--', lw=1, zorder=1) plt.axvline(x=463.0, ymin=0, ymax=30, color='teal', linestyle='--', lw=1, zorder=1) plt.axvline(x=511.0, ymin=0, ymax=30, color='darkgreen', linestyle='--', lw=1, zorder=1) plt.axvline(x=583.2, ymin=0, ymax=30, color='darkorange', linestyle='--', lw=1, zorder=1) plt.axvline(x=727.3, ymin=0, ymax=30, color='gray', linestyle='--', lw=1, zorder=1) plt.axvline(x=794.9, ymin=0, ymax=30, color='brown', linestyle='--', lw=1, zorder=1) plt.axvline(x=860.6, ymin=0, ymax=30, color='purple', linestyle='--', lw=1, zorder=1) plt.axvline(x=911.2, ymin=0, ymax=30, color='fuchsia', linestyle='--', lw=1, zorder=1) plt.axvline(x=969.0, ymin=0, ymax=30, color='saddlebrown', linestyle='--', lw=1, zorder=1) plt.axvline(x=1460.8, ymin=0, ymax=30, color='navy', linestyle='--', lw=1, zorder=1) plt.axvline(x=1592.5, ymin=0, ymax=30, color='limegreen', linestyle='--', lw=1, zorder=1) plt.axvline(x=2103.5, ymin=0, ymax=30, color='olive', linestyle='--', lw=1, zorder=1) plt.axvline(x=2614.5, ymin=0, ymax=30, color='indigo', linestyle='--', lw=1, zorder=1) n = np.array(df['e_cal']) plt.hist(n, np.arange(0, 9500, 1.5), histtype='step', color='black', zorder=2, label='{} entries'.format(len(n))) plt.xlim(0, 4000) plt.ylim(0, plt.ylim()[1]) plt.xlabel('Energy (keV)', ha='right', x=1.0) plt.ylabel('Counts', ha='right', y=1.0) E_cal = 'calibrated energy spectrum, run ' + str(sys.argv[1]) E_1 = 'E=238.6 keV (212Pb peak)*' E_2 = 'E=338.3 keV (228Ac peak)' E_3 = 'E=463.0 keV (228Ac peak)' E_4 = 'E=511.0 keV (beta+ peak)' E_5 = 'E=583.2 keV (208Tl peak)*' E_6 = 'E=727.3 keV (212Bi peak)' E_7 = 'E=794.9 keV (228Ac peak)' E_8 = 'E=860.6 keV (208Tl peak)' E_9 = 'E=911.2 keV (228Ac peak)' E_10 = 'E=969 keV (228Ac peak)' E_11 = 'E=1460.8 keV (40K peak)' E_12 = 'E=1592.5 keV (208Tl DE)' E_13 = 'E=2103.5 keV (208Tl SE)' E_14 = 'E=2614.5 keV (208Tl peak)' colors = [ 'black', 'red', 'aqua', 'teal', 'darkgreen', 'darkorange', 'gray', 'brown', 'purple', 'fuchsia', 'saddlebrown', 'navy', 'limegreen', 'olive', 'indigo' ] lines = [Line2D([0], [0], color=c) for c in colors] labels = [ E_cal, E_1, E_2, E_3, E_4, E_5, E_6, E_7, E_8, E_9, E_10, E_11, E_12, E_13, E_14 ] plt.legend(lines, labels, frameon=True, loc='upper right', fontsize='x-small') plt.tight_layout() #plt.semilogy() plt.show()
def calibrate_pass2(ds, test=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 radford peak to each one. make a new table in the calDB, "cal_pass2" that holds all the important results, like mu, sigma, errors, etc. """ """ This function is mainly being used to estimate the FWHM of the calibration peaks """ epeaks = sorted(ds.config["expected_peaks"]) calpars = ds.get_p1cal_pars("e_ftp") xlo, xhi, xpb = calpars["xlims"] pk_thresh = calpars["width_thresh"] width_lo, width_hi, wlo1, whi1, wlo2, whi2 = calpars["width_lims"] calDB = ds.calDB query = db.Query() table = calDB.table("cal_pass1") vals = table.all() df_cal = pd.DataFrame(vals) # <<---- omg awesome df_cal = df_cal.loc[df_cal.ds.isin(ds.ds_list)] p1cal = df_cal.iloc[0]["p1cal"] t2 = ds.get_t2df() ene = t2["e_ftp"] * p1cal for i in range(len(epeaks)): ehi = epeaks[i] + width_hi elo = epeaks[i] + width_lo xpb = 1 nb = int((ehi - elo) / xpb) h, bins = np.histogram(ene, nb, (elo, ehi)) b = (bins[:-1] + bins[1:]) / 2 # subract background mean_upper = np.mean(np.array(h[wlo1:whi1])) mean_lower = np.mean(np.array(h[wlo2:whi2])) h = h - (mean_upper + mean_lower) / 2 max, min = peakdet(h, pk_thresh, b) print(max) binr = np.where(b == max[0][0]) binl = np.where(b == max[0][0]) binr, binl = binr[0], binl[0] peakh = max[0][1] fwhmr = h[binr] fwhml = h[binl] while fwhmr > 0.5 * peakh or fwhml > 0.5 * peakh: binr += 1 binl += -1 fwhmr = h[binr] fwhml = h[binl] print("FWHM is kind of in the ball park of: ", (b[binr] - b[binl])) if test: plt.plot(b, h, ls="steps", linewidth=1.5) plt.axvline(float(max[0][0]), c='red', linestyle="--", lw=1) plt.axvline(float(b[binr]), c='black', linestyle="--", lw=1) plt.axvline(float(b[binl]), c='green', linestyle="--", lw=1) plt.title("Peak: {}".format(epeaks[i])) plt.xlabel("keV") plt.ylabel("Counts/keV") plt.show() exit()
def linear_calibration(): if (len(sys.argv) != 2): print('Usage: campaign.py [run number]') sys.exit() pks_lit = [609.3, 1460.8] with open("runDB.json") as f: runDB = json.load(f) tier_dir = os.path.expandvars(runDB["tier_dir"]) meta_dir = os.path.expandvars(runDB["meta_dir"]) df = pd.read_hdf('{}/t2_run{}.h5'.format(tier_dir, sys.argv[1])) print(df.keys()) exit() m = np.array(df['e_ftp']) xlo, xhi, xpb = 0, 10000, 10 nbins = int((xhi - xlo) / xpb) hist, bins = np.histogram(m, nbins, (xlo, xhi)) bins = bins + (bins[1] - bins[0]) / 2 bins = bins[0:(len(bins) - 1)] hmed = medfilt(hist, 5) hpks = hist - hmed thresholds = np.arange(50, 1000000, 10, dtype=int) for i in range(len(thresholds)): maxes, mins = pgu.peakdet(hpks, thresholds[i], bins) if len(maxes) == 4: break x_maxes = [] for i in range(len(maxes)): x_value = maxes[i][0] x_maxes.append(x_value) y_maxes = [] for i in range(len(maxes)): y_value = maxes[i][1] y_maxes.append(y_value) ## if for whatever reason the background calibration does not seem to work, these values seem to work more often than not. #x_maxes = [15.0, 345.0, 585.0, 725.0, 835.0, 865.0, 1255.0, 1435.0, 1495.0, 1785.0, 2235.0, 2375.0, 2755.0, 3595.0, 4335.0, 6425.0] ratios = [] for i in range(len(x_maxes)): for j in range(len(x_maxes)): ratios.append(x_maxes[i] / x_maxes[j]) #another way to do the block above #import itertools #ratios = [] #for x_i, x_j in itertools.product(x_maxes, x_maxes): #ratios.append(x_i/x_j) real_ratio = [] for i in range(len(ratios)): real_ratio.append(pks_lit[1] / pks_lit[0]) ratios_array = np.array(ratios) real_ratio_array = np.array(real_ratio) closeness = np.absolute(ratios_array - real_ratio_array) relevant_entry = np.where(closeness == np.amin(closeness))[0] relevant_entry = int(relevant_entry[len(relevant_entry) - 1]) adc_2_peak_combinations = [] for i in range(len(x_maxes)): for j in range(len(x_maxes)): adc_2_peak_combinations.append([x_maxes[j], x_maxes[i]]) #ADC Values Corresponding to Energy Peaks 1460.820 keV and 2614.511 keV adc_values = adc_2_peak_combinations[relevant_entry] #Now we model a linear equation to go from ADC value (e_ftp) to real energy using the points (adc_values[0], 1460.820) and (adc_values[1], 2614.511 keV) # E = A(e_ftp) + B A = float((pks_lit[1] - pks_lit[0]) / (adc_values[1] - adc_values[0])) B = float((pks_lit[1] - adc_values[1] * A)) #Now we will add a column to df that represents the energy measured (rather than only having the adc (e_ftp) value measured as the df currently does) df["e_cal"] = A * df['e_ftp'] + B df.to_hdf('{}/Spectrum_{}.hdf5'.format(meta_dir, sys.argv[1]), key='df', mode='w') pks_lit_all = [ 238.6, 351.9, 511.0, 583.2, 609.3, 911.2, 969, 1120.3, 1460.8, 1764.5, 2614.5 ] plt.axvline(x=238.6, ymin=0, ymax=30, color='red', linestyle='--', lw=1, zorder=1) plt.axvline(x=351.9, ymin=0, ymax=30, color='aqua', linestyle='--', lw=1, zorder=1) plt.axvline(x=511.0, ymin=0, ymax=30, color='darkgreen', linestyle='--', lw=1, zorder=1) plt.axvline(x=583.2, ymin=0, ymax=30, color='darkorange', linestyle='--', lw=1, zorder=1) plt.axvline(x=609.3, ymin=0, ymax=30, color='gray', linestyle='--', lw=1, zorder=1) plt.axvline(x=911.2, ymin=0, ymax=30, color='brown', linestyle='--', lw=1, zorder=1) plt.axvline(x=969.0, ymin=0, ymax=30, color='saddlebrown', linestyle='--', lw=1, zorder=1) plt.axvline(x=1120.3, ymin=0, ymax=30, color='navy', linestyle='--', lw=1, zorder=1) plt.axvline(x=1460.8, ymin=0, ymax=30, color='olive', linestyle='--', lw=1, zorder=1) plt.axvline(x=1764.5, ymin=0, ymax=30, color='indigo', linestyle='--', lw=1, zorder=1) plt.axvline(x=2614.5, ymin=0, ymax=30, color='limegreen', linestyle='--', lw=1, zorder=1) n = np.array(df['e_cal']) plt.hist(n, np.arange(0, 9500, 0.75), histtype='step', color='black', zorder=2, label='{} entries'.format(len(n))) plt.xlim(0, 4000) plt.ylim(0, plt.ylim()[1]) plt.xlabel('Energy (keV)', ha='right', x=1.0) plt.ylabel('Counts', ha='right', y=1.0) E_cal = 'calibrated energy spectrum, run ' + str(sys.argv[1]) E_1 = 'E=238.6 keV (212Pb peak)' E_2 = 'E=351.93 keV (214Pb peak)' E_3 = 'E=511.0 keV (beta+ peak)' E_4 = 'E=583.19 keV (208Tl peak)' E_5 = 'E=609.32 keV (214Bi peak)*' E_6 = 'E=911.2 keV (228Ac peak)' E_7 = 'E=969 keV (228Ac peak)' E_8 = 'E=1120.3 keV (214Bi peak)' E_9 = 'E=1460.82 keV (40K peak)*' E_10 = 'E=1764.49 keV (214Bi peak)' E_11 = 'E=2614.51 keV (208Tl peak)' colors = [ 'black', 'red', 'aqua', 'darkgreen', 'darkorange', 'gray', 'brown', 'saddlebrown', 'navy', 'olive', 'indigo', 'limegreen' ] lines = [Line2D([0], [0], color=c) for c in colors] labels = [E_cal, E_1, E_2, E_3, E_4, E_5, E_6, E_7, E_8, E_9, E_10, E_11] #plt.title('Energy Spectrum') plt.legend(lines, labels, frameon=True, loc='upper right', fontsize='x-small') plt.tight_layout() #plt.semilogy() plt.show()