def compute(self, events, peaks): split_peaks = strax.split_by_containment(peaks, events) result = np.zeros(len(events), self.dtype) self.set_nan_defaults(result) # 1. Assign peaks features to main S1 and main S2 in the event for event_i, (event, sp) in enumerate(zip(events, split_peaks)): res_i = result[event_i] # Fetch the features of main S1 and main S2 for idx, main_peak in zip([event['s1_index'], event['s2_index']], ['s1_', 's2_']): if idx >= 0: for key in [ 's1_time_shadow', 's2_time_shadow', 's2_position_shadow' ]: type_str = key.split('_')[0] res_i[f'{main_peak}shadow_{key}'] = sp[ f'shadow_{key}'][idx] res_i[f'{main_peak}dt_{key}'] = sp[f'dt_{key}'][idx] if 'time' in key: res_i[f'{main_peak}nearest_dt_{type_str}'] = sp[ f'nearest_dt_{type_str}'][idx] if 's2' in key: res_i[f'{main_peak}x_{key}'] = sp[f'x_{key}'][idx] res_i[f'{main_peak}y_{key}'] = sp[f'y_{key}'][idx] # Record the PDF of HalfCauchy res_i[f'{main_peak}pdf_s2_position_shadow'] = sp[ 'pdf_s2_position_shadow'][idx] # 2. Set time and endtime for events result['time'] = events['time'] result['endtime'] = strax.endtime(events) return result
def compute(self, events, peaks): split_peaks = strax.split_by_containment(peaks, events) res = np.zeros(len(events), self.dtype) res['shadow_index'] = -1 res['pre_s2_x'] = np.nan res['pre_s2_y'] = np.nan for event_i, (event, sp) in enumerate(zip(events, split_peaks)): if event['s1_index'] >= 0: res['s1_shadow'][event_i] = sp['shadow'][event['s1_index']] if event['s2_index'] >= 0: res['s2_shadow'][event_i] = sp['shadow'][event['s2_index']] if (sp['type'] == 2).sum() > 0: # Define event shadow as the first S2 peak shadow first_s2_index = np.argwhere(sp['type'] == 2)[0] res['shadow_index'][event_i] = first_s2_index res['shadow'][event_i] = sp['shadow'][first_s2_index] res['pre_s2_area'][event_i] = sp['pre_s2_area'][first_s2_index] res['shadow_dt'][event_i] = sp['shadow_dt'][first_s2_index] res['pre_s2_x'][event_i] = sp['pre_s2_x'][first_s2_index] res['pre_s2_y'][event_i] = sp['pre_s2_y'][first_s2_index] res['shadow_distance'] = ((res['pre_s2_x'] - events['s2_x'])**2 + (res['pre_s2_y'] - events['s2_y'])**2)**0.5 res['time'] = events['time'] res['endtime'] = strax.endtime(events) return res
def compute(self, events, peaks): split_tags = strax.split_by_containment(peaks, events) result = np.zeros(len(events), self.dtype) result['time'] = events['time'] result['endtime'] = events['endtime'] get_veto_tags(events, split_tags, result) return result
def compute(self, events, peaks): result = np.zeros(len(events), dtype=self.dtype) self.set_nan_defaults(result) split_peaks = strax.split_by_containment(peaks, events) result['time'] = events['time'] result['endtime'] = events['endtime'] result['event_number'] = events['event_number'] self.fill_events(result, events, split_peaks) return result
def test_split_by_containment(things, containers): result = strax.split_by_containment(things, containers) assert len(result) == len(containers) for container_i, things_in in enumerate(result): for t in things: assert ((t in things_in) == _is_contained(t, containers[container_i])) if len(result) and len(np.concatenate(result)) > 1: assert np.diff(np.concatenate(result)['time']) >= 0, "Sorting broken"
def compute(self, **kwargs): # If not otherwise specified, data kind to loop over # is that of the first dependency (e.g. events) # Can't be in __init__: deps not initialized then if hasattr(self, 'loop_over'): loop_over = self.loop_over else: loop_over = self.deps[self.depends_on[0]].data_kind if not isinstance(loop_over, str): raise TypeError("Please add \"loop_over = <base>\"" " to your plugin definition") # Group into lists of things (e.g. peaks) # contained in the base things (e.g. events) base = kwargs[loop_over] if len(base) > 1: assert np.all(base[1:]['time'] >= strax.endtime(base[:-1])), \ f'{base}s overlap' for k, things in kwargs.items(): # Check for sorting difs = np.diff(things['time']) if difs.min(initial=0) < 0: i_bad = np.argmin(difs) examples = things[i_bad - 1:i_bad + 3] t0 = examples['time'].min() raise ValueError(f'Expected {k} to be sorted, but found ' + str([(x['time'] - t0, strax.endtime(x) - t0) for x in examples])) if k != loop_over: r = strax.split_by_containment(things, base) if len(r) != len(base): raise RuntimeError(f"Split {k} into {len(r)}, " f"should be {len(base)}!") kwargs[k] = r results = np.zeros(len(base), dtype=self.dtype) deps_by_kind = self.dependencies_by_kind() for i in range(len(base)): r = self.compute_loop( base[i], **{k: kwargs[k][i] for k in deps_by_kind if k != loop_over}) # Convert from dict to array row: for k, v in r.items(): results[i][k] = v return results
def __init__(self, events=None, hitlets=None, run_id=0, channel_range=(2000, 2119), pmt_map='nveto_pmt_position.csv', plot_extension='bokeh'): """ Class to plot an interactive nveto display. :param events: Events which should be plot. Can also be none in case the hitlet matrix and/or pattern map should be plotted separately. :param hitlets: Same as events, but hitlets_nv. :param run_id: Run_id which should be displayed in the title. :param channel_range: Channel range of the detector. :param pmt_map: PMT map which is loaded via straxen.get_resource. The map has to contain the channel number, and xyz coordinates. :param plot_extension: Extension which should be used for rendering can be either bokeh or matpltolib. Default is bokeh to support dynamic plots. """ self.import_holoviews() self.hv.extension(plot_extension) self.df_event_time = None self.df_event_properties = None self.hitlets = hitlets self.channel_range = channel_range self.run_id = run_id # Load PMT data: if isinstance(pmt_map, str): self.pmt_positions = straxen.get_resource(pmt_map, fmt='csv') elif isinstance(pmt_map, np.ndarray): self.pmt_positions = pmt_map else: raise ValueError('pmt_map not understood, has either to be ' f'a string or a numpy array, got "{pmt_map}".') if events is not None: self.event_df = straxen.convert_array_to_df(events) else: self.event_df = None if events is not None and hitlets is not None: self.hitlets_per_event = strax.split_by_containment( hitlets, events)
def compute(self, events, peaks): split_peaks = strax.split_by_containment(peaks, events) # 1. Initialization, ambience is set to be the lowest possible value result = np.zeros(len(events), self.dtype) # 2. Assign peaks features to main S1, main S2 in the event for event_i, (event, sp) in enumerate(zip(events, split_peaks)): for idx, main_peak in zip([event['s1_index'], event['s2_index']], ['s1_', 's2_']): if idx >= 0: for ambience in self.origin_dtype: result[f'{main_peak}n_{ambience}'][event_i] = sp[ f'n_{ambience}'][idx] # 3. Set time and endtime for events result['time'] = events['time'] result['endtime'] = strax.endtime(events) return result
def compute(self, events_nv, hitlets_nv): event_angles = np.zeros(len(events_nv), dtype=self.dtype) # Split hitlets by containment, works since we updated event start/end in # compute_event_properties. hits_in_events = strax.split_by_containment(hitlets_nv, events_nv) # Compute hitlets within the first x ns of event: hits_in_events, n_prompt = first_hitlets( hits_in_events, self.config['position_max_time_nv']) event_angles['n_prompt_hitlets'] = n_prompt # Compute azimuthal angle and xyz positions: angle = get_average_angle(hits_in_events, self.pmt_properties) event_angles['angle'] = angle compute_positions(event_angles, hits_in_events, self.pmt_properties) strax.copy_to_buffer(events_nv, event_angles, f'_copy_events_nv') return event_angles
def compute(self, **kwargs): # If not otherwise specified, data kind to loop over # is that of the first dependency (e.g. events) # Can't be in __init__: deps not initialized then if hasattr(self, 'loop_over'): loop_over = self.loop_over else: loop_over = self.deps[self.depends_on[0]].data_kind # Group into lists of things (e.g. peaks) # contained in the base things (e.g. events) base = kwargs[loop_over] if len(base) > 1: assert np.all(base[1:]['time'] >= strax.endtime(base[:-1])), \ f'{base}s overlap' for k, things in kwargs.items(): if len(things) > 1: assert np.diff(things['time']).min() > 0, f'{k} not sorted' if k != loop_over: r = strax.split_by_containment(things, base) if len(r) != len(base): raise RuntimeError(f"Split {k} into {len(r)}, " f"should be {len(base)}!") kwargs[k] = r results = np.zeros(len(base), dtype=self.dtype) for i in range(len(base)): r = self.compute_loop( base[i], **{ k: kwargs[k][i] for k in self.dependencies_by_kind() if k != loop_over }) # Convert from dict to array row: for k, v in r.items(): results[i][k] = v return results
def peak_plot(self, index): print(index) if not index: index = [0] events = self.event_table.iloc[index].data all_peaks = np.load("181028_0045_peaks.npy") peaks = strax.split_by_containment(all_peaks, events.to_records()) # print(peaks[0]) plots = [] for i, peak in enumerate(peaks): if len(peak["data"]) and len(peak["data"]) < 30: ymax = np.max(peak["data"]) overlay = hv.NdOverlay({ j: hv.Curve(peak["data"][k], kdims='digtizer index', vdims="counts").opts(width=500, interpolation="steps-mid", xlim=(0, 200), ylim=(0, ymax)) for j, k in enumerate(reversed(np.argsort(peak["area"]))) }) plots.append(overlay) return hv.Layout(plots)
def test_event_info_double_w_double_peaks(self: PluginTestCase, trigger_min_area=10): """ Try building event-info double with very long events """ st = self.st.new_context() ev = st.get_array(self.run_id, 'events') if not len(ev): return ev_time_diff = np.median(np.diff(ev['time'])) # increase the event_extension such that we start merging several events st.set_config(dict(event_right_extension=ev_time_diff)) st.get_array(self.run_id, 'event_info_double') distinct_channels = st.get_single_plugin(self.run_id, 'distinct_channels') events = st.get_array(self.run_id, 'event_basics') # Make alt == main just to test that we are able to compute that # all have no distinct channels events['alt_s1_index'] = events['s1_index'] peaks = st.get_array(self.run_id, 'peaks') split_peaks = strax.split_by_containment(peaks, events) for event, split_peak in zip(events, split_peaks): res = distinct_channels.compute_loop(event, split_peak) assert res['alt_s1_distinct_channels'] == 0
def test_nveto_event_plugin(hitlets, area): hitlets['area'] = area hitlets = strax.sort_by_time(hitlets) events, hitlets_ids_in_event = straxen.find_veto_events(hitlets, 3, 300, 0) straxen.plugins.veto_events.compute_nveto_event_properties( events, hitlets, hitlets_ids_in_event, start_channel=2000) # Test some of the parameters: for e, hit_ids in zip(events, hitlets_ids_in_event): hits = hitlets[hit_ids[0]:hit_ids[1]] assert e['time'] == np.min( hits['time']), f'Event start is wrong (hit_ids: hit_ids)' assert e['endtime'] == np.max( strax.endtime(hits)), f'Event end is wrong (hit_ids: hit_ids)' assert np.isclose( e['area'], np.sum(hits['area']) ), f'Event area is wrong for {e["area"]}, {hits["area"]}' mes = f'Event n_contributing_pmt is wrong for {e["n_contributing_pmt"]}, {hits["channel"]}' assert e['n_contributing_pmt'] == len(np.unique(hits['channel'])), mes assert e['n_hits'] == len( hits), f'Event n_hits is wrong for {e["n_hits"]}, {hits}' # ----------------------- # Check if updated events # have the correct boundaries: # ----------------------- if len(events) > 1: mes = f'Updated event boundaries overlap! {events}' assert (events['endtime'][:-1] - events['time'][1:]) > 0, mes split_hitlets = strax.split_by_containment(hitlets, events) for sbc_hitlets, tw_hitlet_id in zip(split_hitlets, hitlets_ids_in_event): h = hitlets[tw_hitlet_id[0]:tw_hitlet_id[1]] mes = ( 'Touching windows and split_by_containment yield different hitlets' ' after updating the event boundaries. This should not have happened.' ) assert np.all(sbc_hitlets == h), mes # Test event positions: try: npmt_pos = straxen.get_resource('nveto_pmt_position.csv', fmt='csv') npmt_pos = npmt_pos.to_records(index=False) except FileNotFoundError: npmt_pos = np.ones(120, dtype=[('x', np.float64), ('y', np.float64), ('z', np.float64)]) events_angle = np.zeros( len(events), dtype=straxen.plugins.veto_events.veto_event_positions_dtype()) straxen.plugins.veto_events.compute_positions(events_angle, events, split_hitlets, npmt_pos, start_channel=2000) angle = straxen.plugins.veto_events.compute_average_angle( split_hitlets, npmt_pos, start_channel=2000) # Compute truth angles: truth_angle = np.angle(events_angle['pos_x'] + events_angle['pos_y'] * 1j) # Replace not defined angles, into zeros to match np.angles return # and to simplify comparison m = (events_angle['pos_x'] == 0) & (events_angle['pos_y'] == 0) angle[m] = 0 # Fixing +2pi issue and np.angle [-180, 180] and [0, 360) convention # issue. angle = angle % (2 * np.pi) truth_angle = truth_angle % (2 * np.pi) # Compare angle, also indirectly tests average x/y/z mes = f'Event angle did not match expected {truth_angle}, got {angle}.' assert np.isclose(angle, truth_angle), mes
def compute(self, **kwargs): # If not otherwise specified, data kind to loop over # is that of the first dependency (e.g. events) # Can't be in __init__: deps not initialized then if hasattr(self, 'loop_over'): loop_over = self.loop_over else: loop_over = self.deps[self.depends_on[0]].data_kind if not isinstance(loop_over, str): raise TypeError("Please add \"loop_over = <base>\"" " to your plugin definition") # Group into lists of things (e.g. peaks) # contained in the base things (e.g. events) base = kwargs[loop_over] if len(base) > 1: assert np.all(base[1:]['time'] >= strax.endtime(base[:-1])), \ f'{base}s overlap' for k, things in kwargs.items(): # Check for sorting difs = np.diff(things['time']) if difs.min(initial=0) < 0: i_bad = np.argmin(difs) examples = things[i_bad - 1:i_bad + 3] t0 = examples['time'].min() raise ValueError(f'Expected {k} to be sorted, but found ' + str([(x['time'] - t0, strax.endtime(x) - t0) for x in examples])) if k != loop_over: if self.time_selection == 'fully_contained': r = strax.split_by_containment(things, base) elif self.time_selection == 'touching': # Experimental feature that should be handled with care: # github.com/AxFoundation/strax/pull/424 warn( f'{self.__class__.__name__} has a touching time ' f'selection. This may lead to ambiguous results as two ' f'{loop_over}\'s may contain the same {k}, thereby a ' f'given {k} can be included multiple times.') window = 0 if hasattr(self, 'touching_window'): window = self.touching_window r = strax.split_touching_windows(things, base, window=window) else: raise RuntimeError('Unknown time_selection') if len(r) != len(base): raise RuntimeError(f"Split {k} into {len(r)}, " f"should be {len(base)}!") kwargs[k] = r if self.multi_output: # This is the a-typical case. Most of the time you just have # one output. Just doing the same as below but this time we # need to create a dict for the outputs. # NB: both outputs will need to have the same length as the # base! results = { k: np.zeros(len(base), dtype=self.dtype[k]) for k in self.provides } deps_by_kind = self.dependencies_by_kind() for i, base_chunk in enumerate(base): res = self.compute_loop( base_chunk, **{ k: kwargs[k][i] for k in deps_by_kind if k != loop_over }) if not isinstance(res, (dict, immutabledict)): raise AttributeError('Please provide result in ' 'compute loop as dict') # Convert from dict to array row: for provides, r in res.items(): for k, v in r.items(): if np.shape(v) != np.shape(results[provides][i][k]): # Make sure that the buffer length as # defined by the base matches the output of # the compute argument. raise ValueError( f'{provides} returned an improper length array ' f'that is not equal to the {loop_over} ' 'data-kind! Are you sure a LoopPlugin is the ' 'right Plugin for your application?') results[provides][i][k] = v else: # Normally you end up here were we are going to loop over # base and add the results to the right format. results = np.zeros(len(base), dtype=self.dtype) deps_by_kind = self.dependencies_by_kind() for i, base_chunk in enumerate(base): r = self.compute_loop( base_chunk, **{ k: kwargs[k][i] for k in deps_by_kind if k != loop_over }) if not isinstance(r, (dict, immutabledict)): raise AttributeError('Please provide result in ' 'compute loop as dict') # Convert from dict to array row: for k, v in r.items(): results[i][k] = v return results