def _gen_photons(self, generator): """ Generate photons for S1, DCR, and merged, and save them to instance variables. """ self.hits1 = self.s1loc + pS1.gen_S1((self.nmc, self.nphotons), self.VL, self.tauV, self.tauL, self.tres, generator) self.hitdcr = dcr.gen_DCR(self.nmc, self.T, self.DCR * self.npdm, generator) self.hitall = np.concatenate([self.hits1, self.hitdcr], axis=-1)
def check_filters(nsignal=100, T=1e5, rate=0.0025, VL=3, tauV=7, tauL=1600, tres=10): """ Plot filters output. """ generator = np.random.default_rng(202012191535) signal_loc = T / 2 hits1 = pS1.gen_S1(nsignal, VL, tauV, tauL, tres, generator) hitdcr = dcr.gen_DCR((), T, rate, generator) - signal_loc hitall = np.concatenate([hits1, hitdcr]) things = [ # ['signal only', hits1], ['noise only', hitdcr], ['all hits', hitall] ] figs = [] for i, (desc, hits) in enumerate(things): figtitle = f'filters.check_filters_{desc.replace(" ", "_")}' fig, ax = plt.subplots(num=figtitle, clear=True, figsize=[10.72, 7.05]) out = filters(hits[None], VL, tauV, tauL, tres, midpoints=4, which=[ 'sample mode', 'cross correlation', 'sample mode cross correlation' ]) out = out[0] kw = dict(alpha=0.5) for k in out.dtype.names: t = out[k]['time'] v = out[k]['value'] ax.plot(t, v / np.max(v), label=k, **kw) ax.plot(hits, np.full_like(hits, 1), '.k', markersize=2) ax.set_title(desc.capitalize()) ax.set_xlabel('Time [ns]') ax.set_ylabel('Filter output (maximum=1)') ax.legend(loc='best', fontsize='small') ax.minorticks_on() ax.grid(True, which='major', linestyle='--') ax.grid(True, which='minor', linestyle=':') fig.tight_layout() figs.append(fig) for fig in figs: fig.show() return tuple(figs)
def __init__( self, nevents=1, # number of events nsignal=10, # number of photons per S1 T=10000, # (ns) length of event rate=0.0025, # (ns^-1) rate of dark count photons VL=3, # fast/slow ratio of S1 tauV=7, # (ns) S1 fast tau tauL=1600, # (ns) S1 slow tau tres=10, # (ns) temporal resolution VLfilter=None, # fast/slow ratio of filter, default same as VL dt=1, # (ns) filter output sampling period offset=0, # (ns) template is transformed as f'(t) = f(t+offset) seed=None, # seed of random generator midpoints=1, # number of points inserted between consecutive hits likelihood=False, # if True, use the likelihood instead of cckde dcr=None, # DCR for the likelihood, default <rate> nph=None, # S1 photons for the likelihood, default <nsignal> sigmakde=0, # (ns) sigma of the KDE ): if VLfilter is None: VLfilter = VL if seed is None: seedgen = np.random.default_rng() seed = seedgen.integers(10001) generator = np.random.default_rng(seed) if dcr is None: dcr = rate if nph is None: nph = nsignal hits1 = pS1.gen_S1((nevents, nsignal), VL, tauV, tauL, tres, generator) signal_loc = (T / 10 + 5 * tres) + T * np.arange(nevents) hits1 += signal_loc[:, None] hits1 = hits1.reshape(-1) hitdcr = gen_DCR((), T * nevents, rate, generator) hits = np.sort(np.concatenate([hits1, hitdcr])) if likelihood: mx = pS1.p_S1_gauss_maximum(VLfilter, tauV, tauL, tres) ampl = pS1.log_likelihood(mx, VLfilter, tauV, tauL, tres, dcr, nph) fun = lambda t: pS1.log_likelihood(t + mx + offset, VLfilter, tauV, tauL, tres, dcr, nph) / ampl fun = numba.njit('f8(f8)')(fun) else: sigma = np.hypot(sigmakde, tres) mx = pS1.p_S1_gauss_maximum(VLfilter, tauV, tauL, sigma) ampl = pS1.p_S1_gauss(mx, VLfilter, tauV, tauL, sigma) fun = lambda t: pS1.p_S1_gauss(t + mx + offset, VLfilter, tauV, tauL, sigma) / ampl fun = numba.njit('f8(f8)')(fun) left = -5 * tres right = 10 * tauL t = np.arange(0, nevents * T, dt) v = filters.filter_cross_correlation(hits[None], t[None], fun, left, right)[0] pidx, _ = signal.find_peaks(v, height=0.9) dx, dy = parabola(v, pidx) tpeak = t[pidx] + dx * dt hpeak = v[pidx] + dy signal_loc_eff = signal_loc + mx + offset s1idx = closenb(signal_loc_eff, tpeak) s1 = np.zeros(len(tpeak), bool) s1[s1idx] = True self.hits1 = hits1 # S1 photons time self.hitdcr = hitdcr # dark count photons time self.t = t # time where the filter is evaluated self.v = v # filter output self.tpeak = tpeak # times of filter output peaks self.hpeak = hpeak # height of filter output peaks self.s1 = s1 # mask for S1 peaks self.mx = mx # point of maximum of p_S1_gauss self.signalloc = signal_loc_eff self.nevents = nevents self.nsignal = nsignal self.T = T self.rate = rate self.VL = VL self.tauV = tauV self.tauL = tauL self.tres = tres self.VLfilter = VLfilter self.dt = dt self.offset = offset self.seed = seed self.midpoints = midpoints self.likelihood = likelihood self.dcr = dcr self.nph = nph self.sigmakde = sigmakde
sigma = 10 loc = 100 T = 2000 rate = 0.005 locs = [loc, loc + 1000] gen = np.random.default_rng(202102151206) fig, ax = plt.subplots(num='slides-2021-02-16.ccexample', clear=True) ax.set_xlabel('Time [ns]') s1hit = loc + ps12.gens12(n, p1, tau1, tau2, 0, sigma, gen) s1hit = s1hit[s1hit < T] dchit = dcr.gen_DCR((), T, rate, gen) hitkw = [ (s1hit, dict(color='#f55', label='S1 photons')), (dchit, dict(color='#000', label='dark count photons')), ] for hit, kw in hitkw: for h in hit: ax.axvline(h, 0, 0.5, **kw) kw.pop('label', None) t = np.arange(0, T) hits = np.concatenate([s1hit, dchit]) params = (p1, tau1, tau2, sigma) for x in locs: s1 = ps12.ps1gauss(t - x, *params)
def simulation( DCR=250e-9, # (ns^-1) Dark count rate per PDM, 25 or 250 Hz VL=3, # fast/slow ratio, ER=0.3, NR=3 tauV=7, # (ns) fast component tau tauL=1600, # (ns) slow component tau T_target=4e6, # (ns) time window T_sim=100e3, # (ns) actually simulated time window npdm=8280, # number of PDMs nphotons=10, # (2-100) number of photons in the S1 signal tres=3, # (ns) temporal resolution (3-10) nmc=10, # number of simulated events deadradius=4000, # (ns) for selecting S1 candidates in filter output matchdist=2000, # (ns) for matching a S1 candidate to the true S1 generator=None # random generator ): info = f"""\ total DCR = {DCR * npdm * 1e3:.2g} $\\mu$s$^{{-1}}$ T (target) = {T_target * 1e-6:.1f} ms T (sim.) = {T_sim * 1e-6:.3f} ms fast/slow = {VL:.1f} nphotons = {nphotons} $\\tau$ = ({tauV:.1f}, {tauL:.0f}) ns temporal res. = {tres:.1f} ns dead radius = {deadradius:.0f} ns match dist. = {matchdist:.0f} ns nevents = {nmc}""" hits1 = pS1.gen_S1((nmc, nphotons), VL, tauV, tauL, tres, generator) hitdcr = dcr.gen_DCR(nmc, T_sim, DCR * npdm, generator) s1loc = T_sim / 2 hitall = np.concatenate([hits1 + s1loc, hitdcr], axis=-1) hitd = dict(all=hitall, dcr=hitdcr) plotkw = { 'all': dict(label='Single S1 events'), 'dcr': dict(label='No S1 events', linestyle='--') } filt = { k: filters.filters(hits, VL, tauV, tauL, tres, midpoints=1, pbar_batch=None) for k, hits in hitd.items() } figs = [] for fname in filt['all'].dtype.names: times = {} values = {} for k, fhits in filt.items(): time = fhits[fname]['time'] value = fhits[fname]['value'] times[k], values[k] = clustersort(time, value, deadradius) figname = 'temps1.simulation_' + fname.replace(" ", "_") fig, axs = plt.subplots(2, 1, num=figname, figsize=[6.4, 7.19], clear=True, sharex=True) figs.append(fig) axs[0].set_title( f'{fname.capitalize()} filter detection performance\n(with explicit threshold)' ) ax = axs[0] ax.set_ylabel('Mean number of S1 candidates per event') xy = { k: [v, (1 + np.arange(len(v)))[::-1] / nmc] for k, v in values.items() } interpkw = dict(kind='next', assume_sorted=True, copy=False, bounds_error=False) interp = { k: interpolate.interp1d(x, y, fill_value=(y[0], 0), **interpkw) for k, (x, y) in xy.items() } x = np.sort(np.concatenate([xy['all'][0], xy['dcr'][0]])) xy['all'][0] = x xy['all'][1] = interp['all']( x) + interp['dcr'](x) * (T_target / T_sim - 1) xy['dcr'][1] *= T_target / T_sim for k, (x, y) in xy.items(): x = np.concatenate([x, x[-1:]]) y = np.concatenate([y, [0]]) ax.plot(x, y, drawstyle='steps-pre', **plotkw[k]) if fname == 'sample mode': ax.set_xscale('log') ax.set_yscale('log') ax.minorticks_on() ax.grid(True, which='major', linestyle='--') ax.grid(True, which='minor', linestyle=':') ax.legend(loc='upper right') ax = axs[1] ax.set_ylabel('True S1 detection probability') ax.set_xlabel('Threshold on filter output') time, value = times['all'], values['all'] close = np.abs(time - s1loc) < matchdist s1value = value[close] x = np.concatenate([s1value, s1value[-1:]]) y = np.arange(len(x))[::-1] / nmc ax.plot(x, y, drawstyle='steps-pre', **plotkw['all']) textbox.textbox(ax, info) ax.minorticks_on() ax.grid(True, which='major', linestyle='--') ax.grid(True, which='minor', linestyle=':') ax.set_ylim(0, max(1, np.max(y))) figname = 'temps1.simulation_' + fname.replace(" ", "_") + '_combined' fig, ax = plt.subplots(num=figname, clear=True) figs.append(fig) ax.set_title(f'{fname.capitalize()} filter detection performance') ax.set_xlabel('S1 candidates per event') ax.set_ylabel('S1 loss probability') time, value = times['all'], values['all'] close = np.abs(time - s1loc) < matchdist xs1 = value[close] ys1 = 1 - (1 + np.arange(len(xs1)))[::-1] / nmc fs1 = interpolate.interp1d(xs1, ys1, fill_value=(1 - ys1[0], 1), **interpkw) x, y = xy['all'] sel = (xs1[0] <= x) & (x <= xs1[-1]) x = x[sel] y = y[sel] s1cand = y s1prob = fs1(x) ax.plot(s1cand, s1prob, **plotkw['all']) textbox.textbox(ax, info) ax.minorticks_on() ax.set_xscale('log') ax.set_yscale('log') ax.grid(True, which='major', linestyle='--') ax.grid(True, which='minor', linestyle=':') l, r = ax.get_xlim() ax.set_xlim(max(0.1, l), r) b, _ = ax.get_ylim() ax.set_ylim(b, max(1, np.max(s1prob))) autolinscale(ax) figname = 'temps1.simulation_' + fname.replace(" ", "_") + '_time' fig, axs = plt.subplots(2, 1, num=figname, clear=True, figsize=[6.4, 7.19]) figs.append(fig) axs[0].set_title( f'{fname.capitalize()} filter\nTemporal distribution of S1 candidates' ) ax = axs[0] ax.set_xlabel('Time relative to true S1 location [ns]') ax.set_ylabel('Inverse of neighbor temporal gap [ns$^{-1}$]') for k, time in times.items(): time = np.sort(time) ddecdf = 1 / np.diff(time) x = time - s1loc y = np.concatenate([ddecdf, ddecdf[-1:]]) ax.plot(x, y, drawstyle='steps-post', **plotkw[k]) ax.axvspan(-deadradius, deadradius, color='#eee', zorder=-9, label='$\\pm$ dead radius') ax.axvspan(-matchdist, matchdist, color='#ccc', zorder=-8, label='$\\pm$ match dist.') ax.legend(loc='upper right') # ax.set_xscale('symlog', linthreshx=deadradius, linscalex=2) ax.set_xlim(3.5 * max(2 * matchdist, deadradius) * np.array([-1, 1])) ax.set_yscale('log') ax.minorticks_on() ax.grid(True, which='major', linestyle='--') ax.grid(True, which='minor', linestyle=':') ax = axs[1] ax.set_xlabel('Time relative to true S1 location [ns]') ax.set_ylabel('Histogram bin density [ns$^{-1}$]') times1 = hits1.reshape(-1) time = times['all'] - s1loc time_match = time[np.abs(time) < matchdist] idx = np.argsort(np.abs(time)) time_close = time[idx][:nmc] # t = np.linspace(left, right, 1000) # ax.plot(t, pS1.p_S1_gauss(t, VL, tauV, tauL, tres), label='S1 pdf') histkw = dict(bins='auto', density=True, histtype='step', zorder=10) ax.hist(times1, label=f'S1 photons ({len(times1)})', linestyle=':', **histkw) ax.hist( time_close, label= f'{nmc} closest candidates ($\\sigma_q$={qsigma.qsigma(time_close):.3g})', linestyle='--', **histkw) ax.hist( time_match, label= f'matching candidates ($\\sigma_q$={qsigma.qsigma(time_match):.3g})', **histkw) ax.axvspan(0, deadradius, color='#eee', zorder=-9, label='dead radius') ax.axvspan(0, matchdist, color='#ccc', zorder=-8, label='match dist.') textbox.textbox(ax, info, loc='upper left', zorder=11) ax.legend(loc='upper right', fontsize='small') ax.set_yscale('log') linthreshx = 10**np.ceil(np.log10(15 * qsigma.qsigma(time_match))) ax.set_xscale('symlog', linthreshx=linthreshx) ax.minorticks_on() ax.xaxis.set_minor_locator( symloglocator.MinorSymLogLocator(linthreshx)) ax.grid(True, which='major', linestyle='--') ax.grid(True, which='minor', linestyle=':') for fig in figs: fig.tight_layout() return figs