Ejemplo n.º 1
0
    def plot_temporal_distribution(self, fname=None):
        fname = self._fname(fname)

        figname = 'temps1.Simulation.plot_temporal_distribution.' + fname.replace(
            " ", "_")
        fig, axs = plt.subplots(2,
                                1,
                                num=figname,
                                clear=True,
                                figsize=[6.4, 7.19])
        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 in ['all', 'dcr']:
            time = self.times[fname][k]
            time = np.sort(time)
            ddecdf = 1 / np.diff(time)
            x = time - self.s1loc
            y = np.concatenate([ddecdf, ddecdf[-1:]])
            ax.plot(x, y, drawstyle='steps-post', **self.plotkw[k])

        ax.axvspan(-self.deadradius,
                   self.deadradius,
                   color='#eee',
                   zorder=-9,
                   label='$\\pm$ dead radius')
        ax.axvspan(-self.matchdist,
                   self.matchdist,
                   color='#ccc',
                   zorder=-8,
                   label='$\\pm$ match dist.')

        ax.legend(loc='upper right')
        ax.set_xlim(3.5 * max(2 * self.matchdist, self.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 = self.hits1.reshape(-1) - self.s1loc
        time = self.times[fname]['all'] - self.s1loc
        time_match = time[np.abs(time) < self.matchdist]
        idx = np.argsort(np.abs(time))
        time_close = time[idx][:self.nmc]

        # t = np.linspace(..., ..., 1000)
        # ax.plot(t, pS1.p_S1_gauss(t, self.VL, self.tauV, self.tauL, self.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'{self.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,
                   self.deadradius,
                   color='#eee',
                   zorder=-9,
                   label='dead radius')
        ax.axvspan(0,
                   self.matchdist,
                   color='#ccc',
                   zorder=-8,
                   label='match dist.')

        textbox.textbox(ax, self.infotext(), 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=':')

        fig.tight_layout()

        return fig
Ejemplo n.º 2
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