Пример #1
0
 def compute(self, raw_records_aqmon):
     rec = strax.raw_to_records(raw_records_aqmon)
     strax.sort_by_time(rec)
     strax.zero_out_of_bounds(rec)
     strax.baseline(rec,
                    baseline_samples=self.config['baseline_samples_aqmon'],
                    flip=True)
     aqmon_hits = strax.find_hits(
         rec, min_amplitude=self.config['hit_min_amplitude_aqmon'])
     return aqmon_hits
Пример #2
0
    def compute(self, raw_records_coin_nv):
        # Do not trust in DAQ + strax.baseline to leave the
        # out-of-bounds samples to zero.
        r = strax.raw_to_records(raw_records_coin_nv)
        del raw_records_coin_nv

        r = strax.sort_by_time(r)
        strax.zero_out_of_bounds(r)
        strax.baseline(r,
                       baseline_samples=self.baseline_samples,
                       flip=True)

        if self.config['min_samples_alt_baseline_nv']:
            m = r['pulse_length'] > self.config['min_samples_alt_baseline_nv']
            if np.any(m):
                # Correcting baseline after PMT saturated signals
                r[m] = median_baseline(r[m])

        strax.integrate(r)

        strax.zero_out_of_bounds(r)

        hits = strax.find_hits(r, min_amplitude=self.hit_thresholds)

        le, re = self.config['save_outside_hits_nv']
        r = strax.cut_outside_hits(r, hits, left_extension=le, right_extension=re)
        strax.zero_out_of_bounds(r)

        return r
Пример #3
0
def plot_peaks(peaks,
               seconds_range,
               t_reference,
               show_largest=100,
               single_figure=True,
               figsize=(10, 4),
               xaxis=True):
    if single_figure:
        plt.figure(figsize=figsize)
    plt.axhline(0, c='k', alpha=0.2)

    peaks = peaks[np.argsort(-peaks['area'])[:show_largest]]
    peaks = strax.sort_by_time(peaks)

    for p in peaks:
        plot_peak(p,
                  t0=t_reference,
                  color={
                      0: 'gray',
                      1: 'b',
                      2: 'g'
                  }[p['type']])

    if xaxis == 'since_start':
        seconds_range_xaxis(seconds_range, t0=seconds_range[0])
    elif xaxis:
        seconds_range_xaxis(seconds_range)
        plt.xlim(*seconds_range)
    else:
        plt.xticks([])
        plt.xlim(*seconds_range)
    plt.ylabel("Intensity [PE/ns]")
    if single_figure:
        plt.tight_layout()
Пример #4
0
    def compute(self, records):
        r = records
        hits = strax.find_hits(r, threshold=self.config['hit_threshold'])
        hits = strax.sort_by_time(hits)

        peaks = strax.find_peaks(
            hits,
            self.config['to_pe'],
            result_dtype=self.dtype,
            gap_threshold=self.config['peak_gap_threshold'],
            left_extension=self.config['peak_left_extension'],
            right_extension=self.config['peak_right_extension'],
            min_channels=self.config['peak_min_chan'],
            min_area=self.config['peak_min_area'],
            max_duration=self.config['peak_max_duration'])
        strax.sum_waveform(peaks, r, adc_to_pe=self.config['to_pe'])
        peaks = peaks[peaks['dt'] > 0]  # removes strange edge case
        peaks = strax.split_peaks(peaks,
                                  r,
                                  self.config['to_pe'],
                                  min_height=self.config['split_min_height'],
                                  min_ratio=self.config['split_min_ratio'])

        strax.compute_widths(peaks)

        return peaks
Пример #5
0
def test_nveto_event_building(hitlets,
                              coincidence):
    """
    In this test we test the code of
    straxen.plugins.veto_evnets.find_veto_events
    """
    hitlets = strax.sort_by_time(hitlets)

    event_intervals = straxen.plugins.nveto_recorder.find_coincidence(hitlets,
                                                                      coincidence,
                                                                      300)

    mes = 'Found overlapping events returned by "coincidence".'
    assert np.all(event_intervals['endtime'][:-1] - event_intervals['time'][1:] < 0), mes

    # Get hits which touch the event window, this can lead to ambiguities
    # which we will solve subsequently.
    hitlets_ids_in_event = strax.touching_windows(hitlets, event_intervals)
    # First check for empty events, since ambiguity check will merge intervals:
    mes = f'Found an empty event without any hitlets: {hitlets_ids_in_event}.'
    assert np.all(np.diff(hitlets_ids_in_event) != 0), mes

    # Solve ambiguities (merge overlapping intervals)
    interval_truth = _test_ambiguity(hitlets_ids_in_event)
    hitlets_ids_in_event = straxen.plugins.veto_events._solve_ambiguity(hitlets_ids_in_event)

    mes = f'Found ambigious event for {hitlets_ids_in_event} with turth {interval_truth}'
    assert np.all(hitlets_ids_in_event == interval_truth), mes

    # Check if events satisfy the coincidence requirement:
    mes = f'Found an event with less than 3 hitelts. {hitlets_ids_in_event}'
    assert np.all(np.diff(hitlets_ids_in_event) >= coincidence), mes
Пример #6
0
def create_hitlets_from_hits(
    hits,
    save_outside_hits,
    channel_range,
    chunk_start=0,
    chunk_end=np.inf,
):
    """
    Function which creates hitlets from a bunch of hits.

    :param hits: Hits found in records.
    :param save_outside_hits: Tuple with left and right hit extension.
    :param channel_range: Detectors change range from channel map.
    :param chunk_start: (optional) start time of a chunk. Ensures that
        no hitlet is earlier than this timestamp.
    :param chunk_end: (optional) end time of a chunk. Ensures that
        no hitlet ends later than this timestamp.

    :return: Hitlets with temporary fields (data, max_goodness_of_split...)
    """
    # Merge concatenate overlapping  within a channel. This is important
    # in case hits were split by record boundaries. In case we
    # accidentally concatenate two PMT signals we split them later again.
    hits = strax.concat_overlapping_hits(
        hits,
        save_outside_hits,
        channel_range,
        chunk_start,
        chunk_end,
    )
    hits = strax.sort_by_time(hits)

    hitlets = np.zeros(len(hits), strax.hitlet_dtype())
    strax.copy_to_buffer(hits, hitlets, '_refresh_hit_to_hitlets')
    return hitlets
Пример #7
0
    def compute(self, raw_records_coin_nv):
        # Do not trust in DAQ + strax.baseline to leave the
        # out-of-bounds samples to zero.
        r = strax.raw_to_records(raw_records_coin_nv)
        del raw_records_coin_nv

        r = strax.sort_by_time(r)
        strax.zero_out_of_bounds(r)
        strax.baseline(r,
                       baseline_samples=self.config['baseline_samples_nv'],
                       flip=True)
        strax.integrate(r)

        strax.zero_out_of_bounds(r)

        hits = strax.find_hits(
            r, min_amplitude=self.config['hit_min_amplitude_nv'])

        le, re = self.config['save_outside_hits_nv']
        r = strax.cut_outside_hits(r,
                                   hits,
                                   left_extension=le,
                                   right_extension=re)
        strax.zero_out_of_bounds(r)

        rlinks = strax.record_links(r)
        r = clean_up_empty_records(r, rlinks, only_last=True)
        return r
Пример #8
0
    def __call__(self, peaks, records, to_pe, data_type,
                 next_ri=None, do_iterations=1, min_area=0, **kwargs):
        if not len(records) or not len(peaks) or not do_iterations:
            return peaks

        # Build the *args tuple for self.find_split_points from kwargs
        # since numba doesn't support **kwargs
        args_options = []
        for i, (k, value) in enumerate(self.find_split_args_defaults):
            if k in kwargs:
                value = kwargs[k]
            if k == 'threshold':
                # The 'threshold' option is a user-specified function
                value = value(peaks)
            args_options.append(value)
        args_options = tuple(args_options)

        # Check for spurious options
        argnames = [k for k, _ in self.find_split_args_defaults]
        for k in kwargs:
            if k not in argnames:
                raise TypeError(f"Unknown argument {k} for {self.__class__}")

        is_split = np.zeros(len(peaks), dtype=np.bool_)

        split_function = {'peaks': self._split_peaks,
                          'hitlets': self._split_hitlets}
        if data_type not in split_function:
            raise ValueError(f'Data_type "{data_type}" is not supported.')

        new_peaks = split_function[data_type](
            # Numba doesn't like self as argument, but it's ok with functions...
            split_finder=self.find_split_points,
            peaks=peaks,
            is_split=is_split,
            orig_dt=records[0]['dt'],
            min_area=min_area,
            args_options=tuple(args_options),
            result_dtype=peaks.dtype)

        if is_split.sum() != 0:
            # Found new peaks: compute basic properties
            if data_type == 'peaks':
                strax.sum_waveform(new_peaks, records, to_pe)
            elif data_type == 'hitlets':
                # Add record fields here
                strax.update_new_hitlets(new_peaks, records, next_ri, to_pe)

            strax.compute_widths(new_peaks)

            # ... and recurse (if needed)
            new_peaks = self(new_peaks, records, to_pe, data_type, next_ri,
                             do_iterations=do_iterations - 1,
                             min_area=min_area, **kwargs)
            peaks = strax.sort_by_time(np.concatenate([peaks[~is_split],
                                                       new_peaks]))

        return peaks
Пример #9
0
    def final_results(self):
        records = self.record_buffer[:self.
                                     blevel]  # No copying the records from buffer
        maska = records['time'] <= self.last_digitized_right * self.config[
            'sample_duration']
        records = records[maska]

        records = strax.sort_by_time(records)  # Do NOT remove this line
        # strax.baseline(records) Will be done w/ pulse processing
        strax.integrate(records)

        # Yield an appropriate amount of stuff from the truth buffer
        # and mark it as available for writing again

        maskb = (
            self.truth_buffer['fill'] &
            # This condition will always be false if self.truth_buffer['t_first_photon'] == np.nan
            ((self.truth_buffer['t_first_photon'] <=
              self.last_digitized_right * self.config['sample_duration']) |
             # Hence, we need to use this trick to also save these cases (this
             # is what we set the end time to for np.nans)
             (np.isnan(self.truth_buffer['t_first_photon']) &
              (self.truth_buffer['time'] <=
               self.last_digitized_right * self.config['sample_duration']))))
        truth = self.truth_buffer[maskb]  # This is a copy, not a view!

        # Careful here: [maskb]['fill'] = ... does not work
        # numpy creates a copy of the array on the first index.
        # The assignment then goes to the (unused) copy.
        # ['fill'][maskb] leads to a view first, then the advanced
        # assignment works into the original array as expected.
        self.truth_buffer['fill'][maskb] = False

        truth.sort(order='time')
        # Return truth without 'fill' field
        _truth = np.zeros(len(truth),
                          dtype=instruction_dtype + truth_extra_dtype)
        for name in _truth.dtype.names:
            _truth[name] = truth[name]
        _truth['time'][~np.isnan(_truth['t_first_photon'])] = \
            _truth['t_first_photon'][~np.isnan(_truth['t_first_photon'])].astype(int)
        _truth.sort(order='time')
        if self.config['detector'] == 'XENON1T':
            yield dict(raw_records=records, truth=_truth)
        else:
            yield dict(
                raw_records=records[records['channel'] < self.
                                    config['channels_top_high_energy'][0]],
                raw_records_he=records[
                    (records['channel'] >=
                     self.config['channels_top_high_energy'][0])
                    & (records['channel'] <=
                       self.config['channels_top_high_energy'][-1])],
                raw_records_aqmon=records[records['channel'] == 800],
                truth=_truth)
        self.record_buffer[:np.sum(~maska)] = self.record_buffer[:self.blevel][
            ~maska]
        self.blevel = np.sum(~maska)
Пример #10
0
 def finish_results():
     nonlocal results
     records = np.concatenate(results)
     # In strax data, records are always stored
     # sorted, baselined and integrated
     records = strax.sort_by_time(records)
     print("Returning %d records" % len(records))
     results = []
     return records
Пример #11
0
def test_concat_overlapping_hits(hits0, hits1, le, re):
    # combining fake hits of the two channels:
    hits1['channel'] = 1
    hits = np.concatenate([hits0, hits1])

    if not len(hits):
        # In case there are no hitlets there is not much to do:
        concat_hits = strax.concat_overlapping_hits(hits, (le, re), (0, 1), 0,
                                                    float('inf'))
        assert not len(
            concat_hits), 'Concatenated hits not empty although hits are empty'

    else:
        hits = strax.sort_by_time(hits)

        # Additional offset to time since le > hits['time'].min() does not
        # make sense:
        hits['time'] += 100

        # Now we are ready for the tests:
        # Creating for each channel a dummy array.
        tmax = strax.endtime(
            hits).max()  # Since dt is one this is the last sample
        tmax += re

        dummy_array = np.zeros((2, tmax), np.int64)
        for h in hits:
            # Filling samples with 1 if inside a hit:
            st = h['time'] - le
            et = strax.endtime(h) + re
            dummy_array[h['channel'], st:et] = 1

        # Now we concatenate the hits and check whether their length matches
        # with the total sum of our dummy arrays.
        concat_hits = strax.concat_overlapping_hits(hits, (le, re), (0, 1), 0,
                                                    float('inf'))

        assert len(concat_hits) <= len(
            hits), 'Somehow we have more hits than before ?!?'

        for ch in [0, 1]:
            dummy_sum = np.sum(dummy_array[ch])

            # Computing total length of concatenated hits:
            diff = strax.endtime(concat_hits) - concat_hits['time']
            m = concat_hits['channel'] == ch
            concat_sum = np.sum(diff[m])

            assert concat_sum == dummy_sum, f'Total length of concatenated hits deviates from hits for channel {ch}'

            if len(concat_hits[m]) > 1:
                # Checking if new hits do not overlapp or touch anymore:
                mask = strax.endtime(
                    concat_hits[m])[:-1] - concat_hits[m]['time'][1:]
                assert np.all(
                    mask < 0
                ), f'Found two hits within {ch} which are touching or overlapping'
Пример #12
0
    def compute(self, raw_records_nv, start, end):
        strax.zero_out_of_bounds(raw_records_nv)

        # First we have to split rr into records and lone records:
        # Please note that we consider everything as a lone record which
        # does not satisfy the coincidence requirement
        intervals = coincidence(raw_records_nv,
                                self.config['coincidence_level_recorder_nv'],
                                self.config['resolving_time_recorder_nv'])
        # Always save the first and last resolving_time nanoseconds (e.g. 600 ns)  since we cannot guarantee the gap
        # size to be larger. (We cannot use an OverlapingWindow plugin either since it requires disjoint objects.)
        if len(intervals):
            intervals_with_bounds = np.zeros((len(intervals) + 2, 2),
                                             dtype=np.int64)
            intervals_with_bounds[1:-1, :] = intervals
            intervals_with_bounds[0, :] = start, min(
                start + self.config['resolving_time_recorder_nv'],
                intervals[0, 0])
            intervals_with_bounds[-1, :] = max(
                end - self.config['resolving_time_recorder_nv'],
                intervals[-1, 1]), end
            del intervals
        else:
            intervals_with_bounds = np.zeros((0, 2), dtype=np.int64)

        neighbors = strax.record_links(raw_records_nv)
        mask = pulse_in_interval(raw_records_nv, neighbors,
                                 *np.transpose(intervals_with_bounds))
        rr, lone_records = straxen.mask_and_not(raw_records_nv, mask)

        # Compute some properties of the lone_records:
        # We compute only for lone_records baseline etc. since
        # raw_records_nv will be deleted, otherwise we could not change
        # the settings and reprocess the data in case of raw_records_nv
        lr = strax.raw_to_records(lone_records)
        del lone_records

        lr = strax.sort_by_time(lr)
        strax.zero_out_of_bounds(lr)
        strax.baseline(
            lr,
            baseline_samples=self.config['nbaseline_samples_lone_records_nv'],
            flip=True)
        strax.integrate(lr)
        lrs, lr = compute_lone_records(lr, self.config['channel_map']['nveto'],
                                       self.config['n_lone_records_nv'])
        lrs['time'] = start
        lrs['endtime'] = end

        return {
            'raw_records_coin_nv': rr,
            'lone_raw_records_nv': lr,
            'lone_raw_record_statistics_nv': lrs
        }
Пример #13
0
    def compute(self, records_nv, start, end):
        # Search again for hits in records:
        hits = strax.find_hits(
            records_nv, min_amplitude=self.config['hit_min_amplitude_nv'])

        # Merge concatenate overlapping  within a channel. This is important
        # in case hits were split by record boundaries. In case we
        # accidentally concatenate two PMT signals we split them later again.
        hits = strax.concat_overlapping_hits(
            hits, self.config['save_outside_hits_nv'],
            self.config['channel_map']['nveto'], start, end)
        hits = strax.sort_by_time(hits)

        # Now convert hits into temp_hitlets including the data field:
        if len(hits):
            nsamples = hits['length'].max()
        else:
            nsamples = 0
        temp_hitlets = np.zeros(
            len(hits), strax.hitlet_with_data_dtype(n_samples=nsamples))

        # Generating hitlets and copying relevant information from hits to hitlets.
        # These hitlets are not stored in the end since this array also contains a data
        # field which we will drop later.
        strax.refresh_hit_to_hitlets(hits, temp_hitlets)
        del hits

        # Get hitlet data and split hitlets:
        strax.get_hitlets_data(temp_hitlets, records_nv, to_pe=self.to_pe)

        temp_hitlets = strax.split_peaks(
            temp_hitlets,
            records_nv,
            self.to_pe,
            data_type='hitlets',
            algorithm='local_minimum',
            min_height=self.config['min_split_nv'],
            min_ratio=self.config['min_split_ratio_nv'])

        # Compute other hitlet properties:
        # We have to loop here 3 times over all hitlets...
        strax.hitlet_properties(temp_hitlets)
        entropy = strax.conditional_entropy(temp_hitlets,
                                            template='flat',
                                            square_data=False)
        temp_hitlets['entropy'][:] = entropy
        strax.compute_widths(temp_hitlets)

        # Remove data field:
        hitlets = np.zeros(len(temp_hitlets), dtype=strax.hitlet_dtype())
        drop_data_field(temp_hitlets, hitlets)

        return hitlets
Пример #14
0
    def compute(self, even_recs, odd_recs):
        n_even = len(even_recs)
        n_odd = len(odd_recs)
        res = np.zeros(n_even + n_odd, self.dtype)
        res['dt'][:n_even] = even_recs['dt']
        res['time'][:n_even] = even_recs['time']
        res['length'][:n_even] = even_recs['length']
        res['channel'][:n_even] = even_recs['channel']

        res['dt'][n_even:] = odd_recs['dt']
        res['time'][n_even:] = odd_recs['time']
        res['length'][n_even:] = odd_recs['length']
        res['channel'][n_even:] = odd_recs['channel']

        return strax.sort_by_time(res)
Пример #15
0
    def final_results(self, record_j):
        records = self.record_buffer[:record_j]  # Copy the records from buffer
        records = strax.sort_by_time(
            records)  # Must keep this for sorted output
        strax.baseline(records)
        strax.integrate(records)

        _truth = self.truth_buffer[self.truth_buffer['fill']]
        # Return truth without 'fill' field
        truth = np.zeros(len(_truth),
                         dtype=instruction_dtype + truth_extra_dtype)
        for name in truth.dtype.names:
            truth[name] = _truth[name]

        return dict(raw_records=records, truth=truth)
Пример #16
0
    def compute(self, peaklets, lone_hits):
        if self.config['merge_without_s1']:
            peaklets = peaklets[peaklets['type'] != 1]

        if len(peaklets) <= 1:
            return np.zeros(0, dtype=peaklets.dtype)

        gap_thresholds = self.config['s2_merge_gap_thresholds']
        max_gap = gap_thresholds[0][1]
        max_area = 10**gap_thresholds[-1][0]

        if max_gap < 0:
            # Do not merge at all
            return np.zeros(0, dtype=peaklets.dtype)
        else:
            # Max gap and area should be set by the gap thresholds
            # to avoid contradictions
            start_merge_at, end_merge_at = self.get_merge_instructions(
                peaklets['time'],
                strax.endtime(peaklets),
                areas=peaklets['area'],
                types=peaklets['type'],
                gap_thresholds=gap_thresholds,
                max_duration=self.config['s2_merge_max_duration'],
                max_gap=max_gap,
                max_area=max_area,
            )
            merged_s2s = strax.merge_peaks(
                peaklets,
                start_merge_at,
                end_merge_at,
                max_buffer=int(self.config['s2_merge_max_duration'] //
                               np.gcd.reduce(peaklets['dt'])),
            )
            merged_s2s['type'] = 2

            # Updated time and length of lone_hits and sort again:
            lh = np.copy(lone_hits)
            del lone_hits
            lh_time_shift = (lh['left'] - lh['left_integration']) * lh['dt']
            lh['time'] = lh['time'] - lh_time_shift
            lh['length'] = (lh['right_integration'] - lh['left_integration'])
            lh = strax.sort_by_time(lh)
            strax.add_lone_hits(merged_s2s, lh, self.to_pe)

            strax.compute_widths(merged_s2s)

        return merged_s2s
Пример #17
0
    def compute(self, records, peaks):
        r = records
        p = peaks
        hits = strax.find_hits(r)
        hits = strax.sort_by_time(hits)

        hit_mean_times = hits['time'] + (hits['length'] / 2.0
                                         )  # hit "mean" time
        peak_mean_times = p['time'] + (p['length'] / 2.0)  # peak "mean" time

        tight_coin = self.get_tight_coin(
            hit_mean_times, peak_mean_times,
            self.config['tight_coincidence_window_left'],
            self.config['tight_coincidence_window_right'])

        return dict(tight_coincidence=tight_coin)
Пример #18
0
 def _load_chunk(self, path, kind='central'):
     records = [
         strax.load_file(fn, compressor='blosc', dtype=strax.record_dtype())
         for fn in glob.glob(f'{path}/reader_*')
     ]
     records = np.concatenate(records)
     records = strax.sort_by_time(records)
     if kind == 'central':
         return records
     result = strax.from_break(
         records,
         safe_break=int(1e3),  # TODO config?
         left=kind == 'post',
         tolerant=True)
     if self.config['erase']:
         shutil.rmtree(path)
     return result
Пример #19
0
 def _load_chunk(self, path, kind='central'):
     records = [
         strax.load_file(fn, compressor='blosc', dtype=strax.record_dtype())
         for fn in sorted(glob.glob(f'{path}/*'))
     ]
     records = np.concatenate(records)
     records = strax.sort_by_time(records)
     if kind == 'central':
         result = records
     else:
         result = strax.from_break(
             records,
             safe_break=self.config['safe_break_in_pulses'],
             left=kind == 'post',
             tolerant=True)
     if self.config['erase']:
         shutil.rmtree(path)
     return result
Пример #20
0
def split_peaks(peaks, records, to_pe, min_height=25, min_ratio=4):
    """Return peaks after splitting at prominent sum waveform minima
    'Prominent' means: on either side of a split point, local maxima are:
      - larger than minimum + min_height
      - larger than minimum * min_ratio
    (this is related to topographical prominence for mountains)

    Min_height is in pe/ns (NOT pe/bin!)
    """
    is_split = np.zeros(len(peaks), dtype=np.bool_)

    new_peaks = _split_peaks(peaks,
                             min_height=min_height,
                             min_ratio=min_ratio,
                             orig_dt=records[0]['dt'],
                             is_split=is_split,
                             result_dtype=peaks.dtype)
    strax.sum_waveform(new_peaks, records, to_pe)
    return strax.sort_by_time(np.concatenate([peaks[~is_split], new_peaks]))
Пример #21
0
    def compute(self, records):
        r = records
        hits = strax.find_hits(r)  # TODO: Duplicate work
        hits = strax.sort_by_time(hits)

        peaks = strax.find_peaks(hits, to_pe, result_dtype=self.dtype)
        strax.sum_waveform(peaks, r, to_pe)

        peaks = strax.split_peaks(peaks, r, to_pe)

        strax.compute_widths(peaks)

        if self.config['diagnose_sorting']:
            assert np.diff(r['time']).min() >= 0, "Records not sorted"
            assert np.diff(hits['time']).min() >= 0, "Hits not sorted"
            assert np.all(peaks['time'][1:] >= strax.endtime(peaks)[:-1]
                          ), "Peaks not disjoint"

        return peaks
Пример #22
0
def test_sum_waveform(records):
    # Make a single big peak to contain all the records
    n_ch = 100

    rlinks = strax.record_links(records)
    hits = strax.find_hits(records, np.ones(n_ch))
    hits['left_integration'] = hits['left']
    hits['right_integration'] = hits['right']
    hits = strax.sort_by_time(hits)

    peaks = strax.find_peaks(hits,
                             np.ones(n_ch),
                             gap_threshold=6,
                             left_extension=2,
                             right_extension=3,
                             min_area=0,
                             min_channels=1,
                             max_duration=10_000_000)
    strax.sum_waveform(peaks, hits, records, rlinks, np.ones(n_ch))

    for p in peaks:
        # Area measures must be consistent
        area = p['area']
        assert area >= 0
        assert p['data'].sum() == area
        assert p['area_per_channel'].sum() == area

        sum_wv = np.zeros(p['length'], dtype=np.float32)
        for r in records:
            (rs, re), (ps, pe) = strax.overlap_indices(r['time'], r['length'],
                                                       p['time'], p['length'])
            sum_wv[ps:pe] += r['data'][rs:re]

        assert np.all(p['data'][:p['length']] == sum_wv)

        # Finally check that we also can use a selection of peaks to sum
        strax.sum_waveform(peaks,
                           hits,
                           records,
                           rlinks,
                           np.ones(n_ch),
                           select_peaks_indices=np.array([0]))
Пример #23
0
 def load_chunk(self, folder, kind='central'):
     records = np.concatenate([
         strax.load_file(os.path.join(folder, f),
                         compressor='blosc',
                         dtype=strax.record_dtype())
         for f in os.listdir(folder)
     ])
     records = strax.sort_by_time(records)
     if kind == 'central':
         result = records
     else:
         if self.config['do_breaks']:
             result = strax.from_break(records,
                                       safe_break=self.config['safe_break'],
                                       left=kind == 'post',
                                       tolerant=True)
         else:
             result = records
     result['time'] += self.config['run_start']
     return result
Пример #24
0
    def compute(self, raw_records_aqmon):
        not_allowed_channels = (set(np.unique(raw_records_aqmon['channel'])) -
                                set(self.aqmon_channels))
        if not_allowed_channels:
            raise ValueError(
                f'Unknown channel {not_allowed_channels}. Only know {self.aqmon_channels}'
            )

        if self.check_raw_record_aqmon_overlaps:
            straxen.check_overlaps(raw_records_aqmon,
                                   n_channels=max(AqmonChannels).value + 1)

        records = strax.raw_to_records(raw_records_aqmon)
        strax.zero_out_of_bounds(records)
        strax.baseline(records,
                       baseline_samples=self.baseline_samples_aqmon,
                       flip=True)
        aqmon_hits = self.find_aqmon_hits_per_channel(records)
        aqmon_hits = strax.sort_by_time(aqmon_hits)
        return aqmon_hits
Пример #25
0
    def compute(self, peaklets, merged_s2s):
        # Remove fake merged S2s from dirty hack, see above
        merged_s2s = merged_s2s[merged_s2s['type'] != FAKE_MERGED_S2_TYPE]

        if self.config['merge_without_s1']:
            is_s1 = peaklets['type'] == 1
            peaks = strax.replace_merged(peaklets[~is_s1], merged_s2s)
            peaks = strax.sort_by_time(np.concatenate([peaklets[is_s1],
                                                       peaks]))
        else:
            peaks = strax.replace_merged(peaklets, merged_s2s)

        if self.config['diagnose_sorting']:
            assert np.all(np.diff(peaks['time']) >= 0), "Peaks not sorted"
            if self.config['merge_without_s1']:
                to_check = peaks['type'] != 1
            else:
                to_check = peaks['type'] != FAKE_MERGED_S2_TYPE

            assert np.all(peaks['time'][to_check][1:] >= strax.endtime(peaks)
                          [to_check][:-1]), "Peaks not disjoint"
        return peaks
Пример #26
0
    def compute(self, records):
        r = records

        hits = strax.find_hits(r)

        # Remove hits in zero-gain channels
        # they should not affect the clustering!
        hits = hits[self.to_pe[hits['channel']] != 0]

        hits = strax.sort_by_time(hits)

        peaks = strax.find_peaks(
            hits,
            self.to_pe,
            gap_threshold=self.config['peak_gap_threshold'],
            left_extension=self.config['peak_left_extension'],
            right_extension=self.config['peak_right_extension'],
            min_channels=self.config['peak_min_pmts'],
            result_dtype=self.dtype)
        strax.sum_waveform(peaks, r, self.to_pe)

        peaks = strax.split_peaks(
            peaks,
            r,
            self.to_pe,
            min_height=self.config['peak_split_min_height'],
            min_ratio=self.config['peak_split_min_ratio'])

        strax.compute_widths(peaks)

        if self.config['diagnose_sorting']:
            assert np.diff(r['time']).min() >= 0, "Records not sorted"
            assert np.diff(hits['time']).min() >= 0, "Hits not sorted"
            assert np.all(peaks['time'][1:] >= strax.endtime(peaks)[:-1]
                          ), "Peaks not disjoint"

        return peaks
Пример #27
0
    def compute(self, raw_records_coin_nv):
        # Do not trust in DAQ + strax.baseline to leave the
        # out-of-bounds samples to zero.
        r = strax.raw_to_records(raw_records_coin_nv)
        del raw_records_coin_nv

        r = strax.sort_by_time(r)
        strax.zero_out_of_bounds(r)
        strax.baseline(r, baseline_samples=self.baseline_samples, flip=True)

        strax.integrate(r)

        strax.zero_out_of_bounds(r)

        hits = strax.find_hits(r, min_amplitude=self.hit_thresholds)

        le, re = self.config['save_outside_hits_nv']
        r = strax.cut_outside_hits(r,
                                   hits,
                                   left_extension=le,
                                   right_extension=re)
        strax.zero_out_of_bounds(r)

        return r
Пример #28
0
    def compute(self, records, start, end):
        r = records

        hits = strax.find_hits(r, min_amplitude=self.hit_thresholds)

        # Remove hits in zero-gain channels
        # they should not affect the clustering!
        hits = hits[self.to_pe[hits['channel']] != 0]

        hits = strax.sort_by_time(hits)

        # Use peaklet gap threshold for initial clustering
        # based on gaps between hits
        peaklets = strax.find_peaks(
            hits,
            self.to_pe,
            gap_threshold=self.config['peaklet_gap_threshold'],
            left_extension=self.config['peak_left_extension'],
            right_extension=self.config['peak_right_extension'],
            min_channels=self.config['peak_min_pmts'],
            result_dtype=self.dtype_for('peaklets'),
            max_duration=self.config['peaklet_max_duration'],
        )

        # Make sure peaklets don't extend out of the chunk boundary
        # This should be very rare in normal data due to the ADC pretrigger
        # window.
        self.clip_peaklet_times(peaklets, start, end)

        # Get hits outside peaklets, and store them separately.
        # fully_contained is OK provided gap_threshold > extension,
        # which is asserted inside strax.find_peaks.
        is_lone_hit = strax.fully_contained_in(hits, peaklets) == -1
        lone_hits = hits[is_lone_hit]
        strax.integrate_lone_hits(
            lone_hits,
            records,
            peaklets,
            save_outside_hits=(self.config['peak_left_extension'],
                               self.config['peak_right_extension']),
            n_channels=len(self.to_pe))

        # Compute basic peak properties -- needed before natural breaks
        hits = hits[~is_lone_hit]
        # Define regions outside of peaks such that _find_hit_integration_bounds
        # is not extended beyond a peak.
        outside_peaks = self.create_outside_peaks_region(peaklets, start, end)
        strax.find_hit_integration_bounds(
            hits,
            outside_peaks,
            records,
            save_outside_hits=(self.config['peak_left_extension'],
                               self.config['peak_right_extension']),
            n_channels=len(self.to_pe),
            allow_bounds_beyond_records=True,
        )

        # Transform hits to hitlets for naming conventions. A hit refers
        # to the central part above threshold a hitlet to the entire signal
        # including the left and right extension.
        # (We are not going to use the actual hitlet data_type here.)
        hitlets = hits
        del hits

        hitlet_time_shift = (hitlets['left'] -
                             hitlets['left_integration']) * hitlets['dt']
        hitlets['time'] = hitlets['time'] - hitlet_time_shift
        hitlets['length'] = (hitlets['right_integration'] -
                             hitlets['left_integration'])
        hitlets = strax.sort_by_time(hitlets)
        rlinks = strax.record_links(records)

        strax.sum_waveform(peaklets, hitlets, r, rlinks, self.to_pe)

        strax.compute_widths(peaklets)

        # Split peaks using low-split natural breaks;
        # see https://github.com/XENONnT/straxen/pull/45
        # and https://github.com/AxFoundation/strax/pull/225
        peaklets = strax.split_peaks(
            peaklets,
            hitlets,
            r,
            rlinks,
            self.to_pe,
            algorithm='natural_breaks',
            threshold=self.natural_breaks_threshold,
            split_low=True,
            filter_wing_width=self.config['peak_split_filter_wing_width'],
            min_area=self.config['peak_split_min_area'],
            do_iterations=self.config['peak_split_iterations'])

        # Saturation correction using non-saturated channels
        # similar method used in pax
        # see https://github.com/XENON1T/pax/pull/712
        # Cases when records is not writeable for unclear reason
        # only see this when loading 1T test data
        # more details on https://numpy.org/doc/stable/reference/generated/numpy.ndarray.flags.html
        if not r['data'].flags.writeable:
            r = r.copy()

        if self.config['saturation_correction_on']:
            peak_list = peak_saturation_correction(
                r,
                rlinks,
                peaklets,
                hitlets,
                self.to_pe,
                reference_length=self.config['saturation_reference_length'],
                min_reference_length=self.
                config['saturation_min_reference_length'])

            # Compute the width again for corrected peaks
            strax.compute_widths(peaklets, select_peaks_indices=peak_list)

        # Compute tight coincidence level.
        # Making this a separate plugin would
        # (a) doing hitfinding yet again (or storing hits)
        # (b) increase strax memory usage / max_messages,
        #     possibly due to its currently primitive scheduling.
        hit_max_times = np.sort(
            hitlets['time'] +
            hitlets['dt'] * hit_max_sample(records, hitlets) +
            hitlet_time_shift  # add time shift again to get correct maximum
        )
        peaklet_max_times = (
            peaklets['time'] +
            np.argmax(peaklets['data'], axis=1) * peaklets['dt'])
        tight_coincidence_channel = get_tight_coin(
            hit_max_times, hitlets['channel'], peaklet_max_times,
            self.config['tight_coincidence_window_left'],
            self.config['tight_coincidence_window_right'], self.channel_range)

        peaklets['tight_coincidence'] = tight_coincidence_channel

        if self.config['diagnose_sorting'] and len(r):
            assert np.diff(r['time']).min(initial=1) >= 0, "Records not sorted"
            assert np.diff(
                hitlets['time']).min(initial=1) >= 0, "Hits/Hitlets not sorted"
            assert np.all(peaklets['time'][1:] >= strax.endtime(peaklets)[:-1]
                          ), "Peaks not disjoint"

        # Update nhits of peaklets:
        counts = strax.touching_windows(hitlets, peaklets)
        counts = np.diff(counts, axis=1).flatten()
        peaklets['n_hits'] = counts

        return dict(peaklets=peaklets, lone_hits=lone_hits)
Пример #29
0
    def compute(self, records, start, end):
        r = records

        hits = strax.find_hits(r,
                               min_amplitude=straxen.hit_min_amplitude(
                                   self.config['hit_min_amplitude']))

        # Remove hits in zero-gain channels
        # they should not affect the clustering!
        hits = hits[self.to_pe[hits['channel']] != 0]

        hits = strax.sort_by_time(hits)

        # Use peaklet gap threshold for initial clustering
        # based on gaps between hits
        peaklets = strax.find_peaks(
            hits,
            self.to_pe,
            gap_threshold=self.config['peaklet_gap_threshold'],
            left_extension=self.config['peak_left_extension'],
            right_extension=self.config['peak_right_extension'],
            min_channels=self.config['peak_min_pmts'],
            result_dtype=self.dtype_for('peaklets'))

        # Make sure peaklets don't extend out of the chunk boundary
        # This should be very rare in normal data due to the ADC pretrigger
        # window.
        self.clip_peaklet_times(peaklets, start, end)

        # Get hits outside peaklets, and store them separately.
        # fully_contained is OK provided gap_threshold > extension,
        # which is asserted inside strax.find_peaks.
        lone_hits = hits[strax.fully_contained_in(hits, peaklets) == -1]
        strax.integrate_lone_hits(
            lone_hits,
            records,
            peaklets,
            save_outside_hits=(self.config['peak_left_extension'],
                               self.config['peak_right_extension']),
            n_channels=len(self.to_pe))

        # Compute basic peak properties -- needed before natural breaks
        strax.sum_waveform(peaklets, r, self.to_pe)
        strax.compute_widths(peaklets)

        # Split peaks using low-split natural breaks;
        # see https://github.com/XENONnT/straxen/pull/45
        # and https://github.com/AxFoundation/strax/pull/225
        peaklets = strax.split_peaks(
            peaklets,
            r,
            self.to_pe,
            algorithm='natural_breaks',
            threshold=self.natural_breaks_threshold,
            split_low=True,
            filter_wing_width=self.config['peak_split_filter_wing_width'],
            min_area=self.config['peak_split_min_area'],
            do_iterations=self.config['peak_split_iterations'])

        # Saturation correction using non-saturated channels
        # similar method used in pax
        # see https://github.com/XENON1T/pax/pull/712
        if self.config['saturation_correction_on']:
            peak_saturation_correction(
                r,
                peaklets,
                self.to_pe,
                reference_length=self.config['saturation_reference_length'],
                min_reference_length=self.
                config['saturation_min_reference_length'])

        # Compute tight coincidence level.
        # Making this a separate plugin would
        # (a) doing hitfinding yet again (or storing hits)
        # (b) increase strax memory usage / max_messages,
        #     possibly due to its currently primitive scheduling.
        hit_max_times = np.sort(hits['time'] +
                                hits['dt'] * hit_max_sample(records, hits))
        peaklet_max_times = (
            peaklets['time'] +
            np.argmax(peaklets['data'], axis=1) * peaklets['dt'])
        peaklets['tight_coincidence'] = get_tight_coin(
            hit_max_times, peaklet_max_times,
            self.config['tight_coincidence_window_left'],
            self.config['tight_coincidence_window_right'])

        if self.config['diagnose_sorting'] and len(r):
            assert np.diff(r['time']).min(initial=1) >= 0, "Records not sorted"
            assert np.diff(hits['time']).min(initial=1) >= 0, "Hits not sorted"
            assert np.all(peaklets['time'][1:] >= strax.endtime(peaklets)[:-1]
                          ), "Peaks not disjoint"

        # Update nhits of peaklets:
        counts = strax.touching_windows(hits, peaklets)
        counts = np.diff(counts, axis=1).flatten()
        counts += 1
        peaklets['n_hits'] = counts

        return dict(peaklets=peaklets, lone_hits=lone_hits)
Пример #30
0
    def compute(self, raw_records_nv, start, end):
        if self.config['check_raw_record_overlaps_nv']:
            straxen.check_overlaps(raw_records_nv, n_channels=3000)
        # Cover the case if we do not want to have any coincidence:
        if self.config['coincidence_level_recorder_nv'] <= 1:
            rr = raw_records_nv
            lr = np.zeros(0, dtype=self.dtype['lone_raw_records_nv'])
            lrs = np.zeros(0,
                           dtype=self.dtype['lone_raw_record_statistics_nv'])
            return {
                'raw_records_coin_nv': rr,
                'lone_raw_records_nv': lr,
                'lone_raw_record_statistics_nv': lrs
            }

        # Search for hits to define coincidence intervals:
        temp_records = strax.raw_to_records(raw_records_nv)
        temp_records = strax.sort_by_time(temp_records)
        strax.zero_out_of_bounds(temp_records)
        strax.baseline(temp_records,
                       baseline_samples=self.baseline_samples,
                       flip=True)
        hits = strax.find_hits(temp_records, min_amplitude=self.hit_thresholds)
        del temp_records

        # First we have to split rr into records and lone records:
        # Please note that we consider everything as a lone record which
        # does not satisfy the coincidence requirement
        intervals = find_coincidence(
            hits, self.config['coincidence_level_recorder_nv'],
            self.config['resolving_time_recorder_nv'],
            self.config['pre_trigger_time_nv'])
        del hits

        # Always save the first and last resolving_time nanoseconds (e.g. 600 ns)  since we cannot guarantee the gap
        # size to be larger. (We cannot use an OverlapingWindow plugin either since it requires disjoint objects.)
        if len(intervals):
            intervals_with_bounds = np.zeros(len(intervals) + 2,
                                             dtype=strax.time_fields)
            intervals_with_bounds['time'][1:-1] = intervals['time']
            intervals_with_bounds['endtime'][1:-1] = intervals['endtime']
            intervals_with_bounds['time'][0] = start
            intervals_with_bounds['endtime'][0] = min(
                start + self.config['resolving_time_recorder_nv'],
                intervals['time'][0])
            intervals_with_bounds['time'][-1] = max(
                end - self.config['resolving_time_recorder_nv'],
                intervals['endtime'][-1])
            intervals_with_bounds['endtime'][-1] = end
            del intervals
        else:
            intervals_with_bounds = np.zeros((0, 2), dtype=strax.time_fields)

        neighbors = strax.record_links(raw_records_nv)
        mask = pulse_in_interval(
            raw_records_nv,
            neighbors,
            intervals_with_bounds['time'],
            intervals_with_bounds['endtime'],
        )

        rr, lone_records = straxen.mask_and_not(raw_records_nv, mask)

        # Compute some properties of the lone_records:
        # We compute only for lone_records baseline etc. since
        # raw_records_nv will be deleted, otherwise we could not change
        # the settings and reprocess the data in case of raw_records_nv
        lr = strax.raw_to_records(lone_records)
        del lone_records

        lr = strax.sort_by_time(lr)
        strax.zero_out_of_bounds(lr)
        strax.baseline(lr, baseline_samples=self.baseline_samples, flip=True)
        strax.integrate(lr)
        lrs, lr = compute_lone_records(lr, self.config['channel_map']['nveto'],
                                       self.config['n_lone_records_nv'])
        lrs['time'] = start
        lrs['endtime'] = end

        return {
            'raw_records_coin_nv': rr,
            'lone_raw_records_nv': lr,
            'lone_raw_record_statistics_nv': lrs
        }