diff --git a/straxen/plugins/events/event_basics_som.py b/straxen/plugins/events/event_basics_som.py index faf5529fc..ed653fd96 100644 --- a/straxen/plugins/events/event_basics_som.py +++ b/straxen/plugins/events/event_basics_som.py @@ -25,7 +25,3 @@ def _set_dtype_requirements(self): ("loc_y_som", np.int16, "y location of the peak(let) in the SOM"), ] self.peak_properties = tuple(self.peak_properties) - - def compute(self, events, peaks): - result = super().compute(events, peaks) - return result diff --git a/straxen/plugins/events/event_basics_vanilla.py b/straxen/plugins/events/event_basics_vanilla.py index 7c25748d5..fae03179b 100644 --- a/straxen/plugins/events/event_basics_vanilla.py +++ b/straxen/plugins/events/event_basics_vanilla.py @@ -2,6 +2,7 @@ import numba import strax import straxen +from .events import is_triggering export, __all__ = strax.exporter() @@ -17,16 +18,41 @@ class EventBasicsVanilla(strax.Plugin): """ - __version__ = "1.3.3" + __version__ = "1.4.0" depends_on = ("events", "peak_basics", "peak_positions", "peak_proximity") provides = "event_basics" data_kind = "events" - electron_drift_velocity = straxen.URLConfig( - default="cmt://electron_drift_velocity?version=ONLINE&run_id=plugin.run_id", - cache=True, - help="Vertical electron drift velocity in cm/ns (1e4 m/ms)", + trigger_min_area = straxen.URLConfig( + default=100, + type=(int, float), + help="Peaks must have more area (PE) than this to cause events", + ) + + trigger_max_competing = straxen.URLConfig( + default=7, + type=int, + help="Peaks must have FEWER nearby larger or slightly smaller peaks to cause events", + ) + + exclude_s1_as_triggering_peaks = straxen.URLConfig( + default=True, + type=bool, + help="If true exclude S1s as triggering peaks.", + ) + + event_s1_min_coincidence = straxen.URLConfig( + default=2, + infer_type=False, + help=( + "Event level S1 min coincidence. Should be >= " + "s1_min_coincidence in the peaklet classification" + ), + ) + + s1_min_coincidence = straxen.URLConfig( + default=2, type=int, help="Minimum tight coincidence necessary to make an S1" ) allow_posts2_s1s = straxen.URLConfig( @@ -47,21 +73,18 @@ class EventBasicsVanilla(strax.Plugin): help="Make sure alt_s2 is in max drift time starting from main S1", ) - event_s1_min_coincidence = straxen.URLConfig( - default=2, - infer_type=False, - help=( - "Event level S1 min coincidence. Should be >= s1_min_coincidence " - "in the peaklet classification" - ), - ) - max_drift_length = straxen.URLConfig( default=straxen.tpc_z, infer_type=False, help="Total length of the TPC from the bottom of gate to the top of cathode wires [cm]", ) + electron_drift_velocity = straxen.URLConfig( + default="cmt://electron_drift_velocity?version=ONLINE&run_id=plugin.run_id", + cache=True, + help="Vertical electron drift velocity in cm/ns (1e4 m/ms)", + ) + def infer_dtype(self): # Basic event properties self._set_posrec_save() @@ -133,6 +156,11 @@ def _set_dtype_requirements(self): ) def setup(self): + if self.s1_min_coincidence > self.event_s1_min_coincidence: + raise ValueError( + "Peak s1 coincidence requirement should be smaller " + "or equal to event_s1_min_coincidence" + ) self.drift_time_max = int(self.max_drift_length / self.electron_drift_velocity) @staticmethod @@ -237,7 +265,15 @@ def fill_result_i(self, event, peaks): """For a single event with the result_buffer.""" # Consider S2s first, then S1s (to enable allow_posts2_s1s = False) # number_of_peaks=0 selects all available s2 and sort by area - largest_s2s, s2_idx = self.get_largest_sx_peaks(peaks, s_i=2, number_of_peaks=0) + largest_s2s, s2_idx = self.get_largest_sx_peaks( + peaks, + s_i=2, + trigger_min_area=self.trigger_min_area, + trigger_max_competing=self.trigger_max_competing, + exclude_s1_as_triggering_peaks=self.exclude_s1_as_triggering_peaks, + event_s1_min_coincidence=self.event_s1_min_coincidence, + number_of_peaks=0, + ) if not self.allow_posts2_s1s and len(largest_s2s): s1_latest_time = largest_s2s[0]["time"] @@ -247,8 +283,11 @@ def fill_result_i(self, event, peaks): largest_s1s, s1_idx = self.get_largest_sx_peaks( peaks, s_i=1, + trigger_min_area=self.trigger_min_area, + trigger_max_competing=self.trigger_max_competing, + exclude_s1_as_triggering_peaks=self.exclude_s1_as_triggering_peaks, + event_s1_min_coincidence=self.event_s1_min_coincidence, s1_before_time=s1_latest_time, - s1_min_coincidence=self.event_s1_min_coincidence, ) if self.force_alt_s2_in_max_drift_time: @@ -329,17 +368,24 @@ def set_event_properties(result, largest_s1s, largest_s2s, peaks): peaks_before_ms2 = peaks[peaks["time"] < largest_s2s[0]["time"]] result["area_before_main_s2"] = np.sum(peaks_before_ms2["area"]) - s2peaks_before_ms2 = peaks_before_ms2[peaks_before_ms2["type"] == 2] - if len(s2peaks_before_ms2) == 0: + s2_peaks_before_ms2 = peaks_before_ms2[peaks_before_ms2["type"] == 2] + if len(s2_peaks_before_ms2) == 0: result["large_s2_before_main_s2"] = 0 else: - result["large_s2_before_main_s2"] = np.max(s2peaks_before_ms2["area"]) + result["large_s2_before_main_s2"] = np.max(s2_peaks_before_ms2["area"]) return result @staticmethod # @numba.njit <- works but slows if fill_events is not numbafied def get_largest_sx_peaks( - peaks, s_i, s1_before_time=np.inf, s1_min_coincidence=0, number_of_peaks=2 + peaks, + s_i, + trigger_min_area, + trigger_max_competing, + exclude_s1_as_triggering_peaks=True, + event_s1_min_coincidence=0, + s1_before_time=np.inf, + number_of_peaks=2, ): """Get the largest S1/S2. @@ -348,9 +394,19 @@ def get_largest_sx_peaks( """ # Find all peaks of this type (S1 or S2) s_mask = peaks["type"] == s_i + # Exclude non-triggering peaks if S2 or if (S1 and not exclude_s1_as_triggering_peaks) + if s_i == 2 or not exclude_s1_as_triggering_peaks: + s_mask &= is_triggering( + peaks, + trigger_min_area, + trigger_max_competing, + exclude_s1_as_triggering_peaks, + event_s1_min_coincidence, + ) + # Extra condition for S1s if s_i == 1: s_mask &= peaks["time"] < s1_before_time - s_mask &= peaks["tight_coincidence"] >= s1_min_coincidence + s_mask &= peaks["tight_coincidence"] >= event_s1_min_coincidence selected_peaks = peaks[s_mask] s_index = np.arange(len(peaks))[s_mask] diff --git a/straxen/plugins/events/events.py b/straxen/plugins/events/events.py index 3696ea31c..c77cb4fa0 100644 --- a/straxen/plugins/events/events.py +++ b/straxen/plugins/events/events.py @@ -39,12 +39,6 @@ class Events(strax.OverlapWindowPlugin): events_seen = 0 - electron_drift_velocity = straxen.URLConfig( - default="cmt://electron_drift_velocity?version=ONLINE&run_id=plugin.run_id", - cache=True, - help="Vertical electron drift velocity in cm/ns (1e4 m/ms)", - ) - trigger_min_area = straxen.URLConfig( default=100, type=(int, float), @@ -57,20 +51,23 @@ class Events(strax.OverlapWindowPlugin): help="Peaks must have FEWER nearby larger or slightly smaller peaks to cause events", ) - left_event_extension = straxen.URLConfig( - default=int(0.25e6), - type=(int, float), + exclude_s1_as_triggering_peaks = straxen.URLConfig( + default=True, + type=bool, + help="If true exclude S1s as triggering peaks.", + ) + + event_s1_min_coincidence = straxen.URLConfig( + default=2, + infer_type=False, help=( - "Extend events this many ns to the left from each " - "triggering peak. This extension is added to the maximum " - "drift time." + "Event level S1 min coincidence. Should be >= " + "s1_min_coincidence in the peaklet classification" ), ) - right_event_extension = straxen.URLConfig( - default=int(0.25e6), - type=(int, float), - help="Extend events this many ns to the right from each triggering peak.", + s1_min_coincidence = straxen.URLConfig( + default=2, type=int, help="Minimum tight coincidence necessary to make an S1" ) max_drift_length = straxen.URLConfig( @@ -79,23 +76,26 @@ class Events(strax.OverlapWindowPlugin): help="Total length of the TPC from the bottom of gate to the top of cathode wires [cm]", ) - exclude_s1_as_triggering_peaks = straxen.URLConfig( - default=True, - type=bool, - help="If true exclude S1s as triggering peaks.", + electron_drift_velocity = straxen.URLConfig( + default="cmt://electron_drift_velocity?version=ONLINE&run_id=plugin.run_id", + cache=True, + help="Vertical electron drift velocity in cm/ns (1e4 m/ms)", ) - event_s1_min_coincidence = straxen.URLConfig( - default=2, - infer_type=False, + left_event_extension = straxen.URLConfig( + default=int(0.25e6), + type=(int, float), help=( - "Event level S1 min coincidence. Should be >= " - "s1_min_coincidence in the peaklet classification" + "Extend events this many ns to the left from each " + "triggering peak. This extension is added to the maximum " + "drift time." ), ) - s1_min_coincidence = straxen.URLConfig( - default=2, type=int, help="Minimum tight coincidence necessary to make an S1" + right_event_extension = straxen.URLConfig( + default=int(0.25e6), + type=(int, float), + help="Extend events this many ns to the right from each triggering peak.", ) diagnose_overlapping = straxen.URLConfig( @@ -118,19 +118,14 @@ def get_window_size(self): # Take a large window for safety, events can have long tails return 10 * (self.left_event_extension + self.drift_time_max + self.right_event_extension) - def _is_triggering(self, peaks): - _is_triggering = peaks["area"] > self.trigger_min_area - _is_triggering &= peaks["n_competing"] <= self.trigger_max_competing - if self.exclude_s1_as_triggering_peaks: - _is_triggering &= peaks["type"] == 2 - else: - is_not_s1 = peaks["type"] != 1 - has_tc_large_enough = peaks["tight_coincidence"] >= self.event_s1_min_coincidence - _is_triggering &= is_not_s1 | has_tc_large_enough - return _is_triggering - def compute(self, peaks, start, end): - _is_triggering = self._is_triggering(peaks) + _is_triggering = is_triggering( + peaks, + self.trigger_min_area, + self.trigger_max_competing, + self.exclude_s1_as_triggering_peaks, + self.event_s1_min_coincidence, + ) triggers = peaks[_is_triggering] @@ -164,3 +159,23 @@ def compute(self, peaks, start, end): self.events_seen += len(result) return result + + +@export +def is_triggering( + peaks, + trigger_min_area, + trigger_max_competing, + exclude_s1_as_triggering_peaks, + event_s1_min_coincidence, +): + _is_triggering = np.isin(peaks["type"], [0, 1, 2]) + _is_triggering &= peaks["area"] > trigger_min_area + _is_triggering &= peaks["n_competing"] <= trigger_max_competing + if exclude_s1_as_triggering_peaks: + _is_triggering &= peaks["type"] == 2 + else: + is_not_s1 = peaks["type"] != 1 + has_tc_large_enough = peaks["tight_coincidence"] >= event_s1_min_coincidence + _is_triggering &= is_not_s1 | has_tc_large_enough + return _is_triggering