Example #1
0
 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)
Example #2
0
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)
Example #3
0
    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
Example #4
0
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)
Example #5
0
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