DUNE-DAQ
DUNE Trigger and Data Acquisition software
Loading...
Searching...
No Matches
triggeralgs::TAMakerMichelElectronAlgorithm Class Reference

#include <TAMakerMichelElectronAlgorithm.hpp>

Inheritance diagram for triggeralgs::TAMakerMichelElectronAlgorithm:
[legend]
Collaboration diagram for triggeralgs::TAMakerMichelElectronAlgorithm:
[legend]

Classes

class  Window
 

Public Member Functions

void process (const TriggerPrimitive &input_tp, std::vector< TriggerActivity > &output_ta)
 TP processing function that creates & fills TAs.
 
void configure (const nlohmann::json &config)
 
- Public Member Functions inherited from triggeralgs::TriggerActivityMaker
virtual ~TriggerActivityMaker ()=default
 
void operator() (const TriggerPrimitive &input_tp, std::vector< TriggerActivity > &output_ta)
 
virtual bool preprocess (const TriggerPrimitive &input_tp)
 TP pre-processing/filtering.
 
virtual void postprocess (std::vector< TriggerActivity > &output_ta)
 Post-processing/filtering of the TAs, e.g. prescale.
 
virtual void flush (timestamp_t, std::vector< TriggerActivity > &)
 

Private Member Functions

TriggerActivity construct_ta () const
 
std::vector< TriggerPrimitivelongest_activity () const
 
bool check_bragg_peak (std::vector< TriggerPrimitive > trackHits)
 
bool check_kinks (std::vector< TriggerPrimitive > trackHits)
 
void add_window_to_record (Window window)
 
void dump_window_record ()
 
void dump_tp (TriggerPrimitive const &input_tp)
 

Private Attributes

Window m_current_window
 
uint64_t m_primitive_count = 0
 
bool debug = false
 
uint16_t m_adjacency_threshold = 15
 
int m_max_adjacency = 0
 
uint16_t m_adj_tolerance = 3
 
int index = 0
 
uint16_t ta_adc = 0
 
uint16_t ta_channels = 0
 
timestamp_t m_window_length = 50000
 
std::vector< Windowm_window_record
 

Additional Inherited Members

- Public Attributes inherited from triggeralgs::TriggerActivityMaker
std::atomic< uint64_t > m_data_vs_system_time = 0
 
std::atomic< uint64_t > m_initial_offset = 0
 
uint64_t m_prescale = 1
 Configurable prescale factor.
 
uint64_t m_ta_count = 0
 TA made count for prescaling.
 
uint32_t m_max_samples_over_threshold = std::numeric_limits<uint32_t>::max()
 Time-over-threshold TP filtering.
 

Detailed Description

Definition at line 17 of file TAMakerMichelElectronAlgorithm.hpp.

Member Function Documentation

◆ add_window_to_record()

void TAMakerMichelElectronAlgorithm::add_window_to_record ( Window window)
private

Definition at line 304 of file TAMakerMichelElectronAlgorithm.cpp.

305{
306 m_window_record.push_back(window);
307 return;
308}

◆ check_bragg_peak()

bool TAMakerMichelElectronAlgorithm::check_bragg_peak ( std::vector< TriggerPrimitive > trackHits)
private

Definition at line 193 of file TAMakerMichelElectronAlgorithm.cpp.

194{
195 bool bragg = false;
196 std::vector<float> adc_means_list;
197 uint16_t convolve_value = 6;
198
199 // Loop over hits that correspond to high adjacency activity
200 for (uint16_t i = 0; i < trackHits.size(); ++i){
201 float adc_sum = 0;
202 float adc_mean = 0;
203
204 // Calculate running ADC mean of this track
205 for (uint16_t j = i; j < i+convolve_value; ++j){
206 int hit = (j) % trackHits.size();
207 adc_sum += trackHits.at(hit).adc_integral;
208 }
209
210 adc_mean = adc_sum / convolve_value;
211 adc_means_list.push_back(adc_mean);
212 adc_sum = 0;
213 }
214
215 // We now have a list of convolved adc means.
216 float ped = std::accumulate(adc_means_list.begin(), adc_means_list.end(), 0.0) / adc_means_list.size();
217 float charge = 0;
218 std::vector<float> charge_dumps;
219
220 // Now go through the list, picking up clusters of charge above the baseline/ped
221 for (auto a : adc_means_list){
222 if (a > ped){
223 charge += a;
224 }
225 else if( a < ped && charge !=0 ){
226 charge_dumps.push_back(charge);
227 charge = 0;
228 }
229 }
230
231 // If the maximum of that list of charge dumps is near(at?) either end of it
232 float max_charge = *max_element(charge_dumps.begin(), charge_dumps.end());
233 if(max_charge == charge_dumps.front() || max_charge == charge_dumps.back()){ bragg=true; }
234
235 return bragg;
236 }

◆ check_kinks()

bool TAMakerMichelElectronAlgorithm::check_kinks ( std::vector< TriggerPrimitive > trackHits)
private

Definition at line 239 of file TAMakerMichelElectronAlgorithm.cpp.

240{
241 bool kinks = false; // We actually required two kinks in the coldbox, the michel kink and the wes kink
242 std::vector<float> runningGradient;
243 std::vector<float> runningMeanGradient;
244
245 // Choice to be made here. Do we want to scane in collection (z) or time (x) direction when calculating gradient between hits. I
246 // would say if we have already made the request to pass a track of length specific threshold which is longer than the drift
247 // direction for the coldbox, it makes sense to scan across channels a little more.
248 std::sort(finalHits.begin(), finalHits.end(), [](TriggerPrimitive a, TriggerPrimitive b) { return a.channel < b.channel; });
249
250 // Populate the runningGradient with the track hits. Do this between ith and i+kth TPs, to small scale fluctuations of the track
251 // Yet k should be kept small, so that enough gradient information is preserved at the end of the track to identify kinks
252 for (int i=0 ; i < finalHits.size()-2; i++){
253
254 // Skip same channel hits or if the start times are the same - no div by zero!
255 if (finalHits.at(i+2).channel == finalHits.at(i).channel || (finalHits.at(i+2).time_start == finalHits.at(i).time_start) ) { continue; }
256
257 // Check that the next TP is closeby; enough in space and time directions so as to avoid obtaining a gradient value from
258 // same channel hits at large time difference or vice versa due to kink topology or showers. Clearly we shouldn't be very far in
259 // channel number, but since we might later try to do this check in the time direction, leave the condition in.
260 int diff = finalHits.at(i+2).time_start - finalHits.at(i).time_start;
261 if((std::abs(diff) > 1000) || ((std::abs(channel_diff_t(finalHits.at(i+2).channel) - channel_diff_t(finalHits.at(i).channel)) > 6))) { continue; }
262
263 // Gradient is just change in z (collection) over change in x (drift). x is admitedly roughly converted from
264 // hit start time, but I don't think diffusion effects are a huge concern over 20cm. Using mm for readability/visualisation
265 float dz = (finalHits.at(i+2).channel - finalHits.at(i).channel)*4.67; // Change in collection wire z to separation in mm
266 long long int dt = finalHits.at(i+2).time_start - finalHits.at(i).time_start;
267 float dx = dt*0.028; // Change time to separation in x mm
268 float g = dz/dx;
269
270 runningGradient.push_back(g);
271 }
272
273 // Require a decent length of the gradients vector. Otherwise some adjacent events are showers and the conditions above mean
274 // we don't get enough entries. In essence, this provides some confidence that it's track-like rather than shower-like. Which
275 // is what we want for a Michel event.
276 if ( runningGradient.size() > 10 ){
277
278 // Now lets take a running mean of the gradients between TPs, less susceptible to wild changes due to deltas/etc
279 for(int g=0 ; g < runningGradient.size()-1 ; g++){
280 float gsum = runningGradient.at(g) + runningGradient.at(g+1);
281 runningMeanGradient.push_back(gsum/2);
282 }
283
284 // We have a list of gradients, now just demand that the two ends have gradients that differ significantly
285 // from the mean gradient of the activity at both ends. This aims to pick out wesKinks and michelKinks
286 if(runningMeanGradient.size() > 10 ){
287
288 float mean = (std::abs(std::accumulate(runningMeanGradient.begin(), runningMeanGradient.end(), 0.0)))/(runningMeanGradient.size());
289
290 // If you're testing on simulation or december data, you won't see the wes kink, so use an || instead of &&
291 if((std::abs(runningMeanGradient.front()) + mean > 2.5*mean) || ((std::abs(runningMeanGradient.back() + mean)) > 2.5*mean)){ kinks=true; }
292 }
293 }
294
295 return kinks;
296}
dunedaq::trgdataformats::channel_diff_t channel_diff_t
Definition Types.hpp:21
A single energy deposition on a TPC or PDS channel.

◆ configure()

void TAMakerMichelElectronAlgorithm::configure ( const nlohmann::json & config)
virtual

Reimplemented from triggeralgs::TriggerActivityMaker.

Definition at line 66 of file TAMakerMichelElectronAlgorithm.cpp.

67{
69
70 // FIX ME: Use some schema here. Also can't work out how to pass booleans.
71 if (config.is_object()) {
72 if (config.contains("window_length"))
73 m_window_length = config["window_length"];
74 if (config.contains("adjacency_tolerance"))
75 m_adj_tolerance = config["adjacency_tolerance"];
76 if (config.contains("adjacency_threshold"))
77 m_adjacency_threshold = config["adjacency_threshold"];
78 }
79
80}
virtual void configure(const nlohmann::json &config)

◆ construct_ta()

TriggerActivity TAMakerMichelElectronAlgorithm::construct_ta ( ) const
private

Definition at line 83 of file TAMakerMichelElectronAlgorithm.cpp.

84{
85
86 TriggerPrimitive latest_tp_in_window = m_current_window.inputs.back();
87
90 ta.time_end = latest_tp_in_window.time_start + latest_tp_in_window.samples_over_threshold * 32; // FIXME: Replace the hard-coded SOT to TOT scaling.
91 ta.time_peak = latest_tp_in_window.samples_to_peak * 32 + latest_tp_in_window.time_start; // FIXME: Replace STP to `time_peak` conversion.
93 ta.channel_start = latest_tp_in_window.channel;
94 ta.channel_end = latest_tp_in_window.channel;
95 ta.channel_peak = latest_tp_in_window.channel;
97 ta.adc_peak = latest_tp_in_window.adc_peak;
98 ta.detid = latest_tp_in_window.detid;
102
103 return ta;
104}
std::vector< TriggerPrimitive > inputs

◆ dump_tp()

void TAMakerMichelElectronAlgorithm::dump_tp ( TriggerPrimitive const & input_tp)
private

Definition at line 340 of file TAMakerMichelElectronAlgorithm.cpp.

341{
342 std::ofstream outfile;
343 outfile.open("coldbox_tps.txt", std::ios_base::app);
344
345 // Output relevant TP information to file
346 outfile << input_tp.time_start << " "; // Start time of TP
347 outfile << input_tp.samples_over_threshold << " "; // in multiples of 25
348 outfile << input_tp.samples_to_peak << " "; //
349 outfile << input_tp.channel << " "; // Offline channel ID
350 outfile << input_tp.adc_integral << " "; // ADC Sum
351 outfile << input_tp.adc_peak << " "; // ADC Peak Value
352 outfile << input_tp.detid << " "; // Det ID - Identifies detector element, APA or PDS part etc...
353 outfile.close();
354
355 return;
356}

◆ dump_window_record()

void TAMakerMichelElectronAlgorithm::dump_window_record ( )
private

Definition at line 313 of file TAMakerMichelElectronAlgorithm.cpp.

314{
315 // FIX ME: Need to index this outfile in the name by detid or something similar.
316 std::ofstream outfile;
317 outfile.open("window_record_tam.csv", std::ios_base::app);
318
319 for (auto window : m_window_record) {
320 outfile << window.time_start << ",";
321 outfile << window.inputs.back().time_start << ",";
322 outfile << window.inputs.back().time_start - window.time_start << ","; // window_length - from TP start times
323 outfile << window.adc_integral << ",";
324 outfile << window.n_channels_hit() << ","; // Number of unique channels with hits
325 outfile << window.inputs.size() << ","; // Number of TPs in Window
326 outfile << window.inputs.back().channel << ","; // Last TP Channel ID
327 outfile << window.inputs.front().channel << ","; // First TP Channel ID
328 outfile << longest_activity().size() << std::endl; // New adjacency value for the window
329 }
330
331 outfile.close();
332
333 m_window_record.clear();
334
335 return;
336}
std::vector< TriggerPrimitive > longest_activity() const

◆ longest_activity()

std::vector< TriggerPrimitive > TAMakerMichelElectronAlgorithm::longest_activity ( ) const
private

Definition at line 107 of file TAMakerMichelElectronAlgorithm.cpp.

108{
109 // This function attempts to return a vector of hits that correspond to the longest
110 // piece of activity in the current window. The logic follows that from the HMA
111 // check_adjacency() function and further details can be found there.
112 std::vector<TriggerPrimitive> trackHits;
113 std::vector<TriggerPrimitive> finalHits; // The vector of track hits, which we return
114
115 uint16_t adj = 1; // Initialise adjacency, 1 for the first wire.
116 uint16_t max = 0;
117 unsigned int channel = 0; // Current channel ID
118 unsigned int next_channel = 0; // Next channel ID
119 unsigned int next = 0; // The next position in the hit channels vector
120 unsigned int tol_count = 0; // Tolerance count, should not pass adj_tolerance
121
122 // Generate a channelID ordered list of hit channels for this window
123 std::vector<TriggerPrimitive> hitList;
124 for (auto tp : m_current_window.inputs) {
125 hitList.push_back(tp);
126 }
127 std::sort(hitList.begin(), hitList.end(), [](TriggerPrimitive a, TriggerPrimitive b)
128 { return a.channel < b.channel; });
129
130 // ADAJACENCY LOGIC ====================================================================
131 // =====================================================================================
132 // Adjcancency Tolerance = Number of times prepared to skip missed hits before resetting
133 // the adjacency count. This accounts for things like dead channels / missed TPs. The
134 // maximum gap is 4 which comes from tuning on December 2021 coldbox data, and June 2022
135 // coldbox runs.
136 for (int i = 0; i < hitList.size(); ++i) {
137
138 next = (i + 1) % hitList.size(); // Loops back when outside of channel list range
139 channel = hitList.at(i).channel;
140 next_channel = hitList.at(next).channel; // Next channel with a hit
141
142 if (trackHits.size() == 0 ){ trackHits.push_back(hitList.at(i)); }
143
144 // End of vector condition.
145 if (next_channel == 0) { next_channel = channel - 1; }
146
147 // Skip same channel hits for adjacency counting, but add to the track!
148 if (next_channel == channel) {
149 trackHits.push_back(hitList.at(next));
150 continue; }
151
152 // If next hit is on next channel, increment the adjacency count.
153 else if (next_channel == channel + 1){
154 trackHits.push_back(hitList.at(next));
155 ++adj; }
156
157 // If next channel is not on the next hit, but the 'second next', increase adjacency
158 // but also tally up with the tolerance counter.
159 else if (((next_channel == channel + 2) || (next_channel == channel + 3) ||
160 (next_channel == channel + 4) || (next_channel == channel + 5))
161 && (tol_count < m_adj_tolerance)) {
162 trackHits.push_back(hitList.at(next));
163 ++adj;
164 for (int i = 0 ; i < next_channel-channel ; ++i){ ++tol_count; }
165 }
166
167 // If next hit isn't within reach, end the adjacency count and check for a new max.
168 // Reset variables for next iteration.
169 else {
170 if (adj > max) {
171 max = adj;
172 finalHits.clear(); // Clear previous track
173 for (auto h : trackHits){
174 finalHits.push_back(h);
175 }
176 }
177 adj = 1;
178 tol_count = 0;
179 trackHits.clear();
180 }
181 }
182
183 return finalHits;
184}

◆ process()

void TAMakerMichelElectronAlgorithm::process ( const TriggerPrimitive & input_tp,
std::vector< TriggerActivity > & output_ta )
virtual

TP processing function that creates & fills TAs.

Parameters
input_tp[in]Input TP for the triggering algorithm
output_ta[out]Output vector of TAs to fill by the algorithm

Implements triggeralgs::TriggerActivityMaker.

Definition at line 20 of file TAMakerMichelElectronAlgorithm.cpp.

22{
23
24 // The first time operator() is called, reset the window object.
26 m_current_window.reset(input_tp);
28 return;
29 }
30
31 // If the difference between the current TP's start time and the start of the window
32 // is less than the specified window size, add the TP to the window.
34 m_current_window.add(input_tp);
35 }
36
37 // Check Michel Candidate ========================================================
38 // We've filled the window, now require a sufficient length track AND that the track
39 // has a potential Bragg P, and then a kink.
41
42
43 // We have a good length acitivity, now search for Bragg peak and kinks
44 std::vector<TriggerPrimitive> trackHits = longest_activity();
45
46 if (check_bragg_peak(trackHits)){
47 if (check_kinks(trackHits)){
48 TLOG_DEBUG(TLVL_DEBUG_MEDIUM) << "[TAM:ME] Emitting a trigger for candidate Michel event.";
49 output_ta.push_back(construct_ta());
50 m_current_window.reset(input_tp);
51 } // Kinks
52 } // Bragg peak
53
54 }
55
56 // Otherwise, slide the window along using the current TP.
57 else {
59 }
60
62 return;
63}
void move(TriggerPrimitive const &input_tp, timestamp_t const &window_length)
bool check_kinks(std::vector< TriggerPrimitive > trackHits)
bool check_bragg_peak(std::vector< TriggerPrimitive > trackHits)
#define TLOG_DEBUG(lvl,...)
Definition Logging.hpp:112
FELIX Initialization std::string initerror FELIX queue timed std::string queuename Unexpected chunk size

Member Data Documentation

◆ debug

bool triggeralgs::TAMakerMichelElectronAlgorithm::debug = false
private

Definition at line 116 of file TAMakerMichelElectronAlgorithm.hpp.

◆ index

int triggeralgs::TAMakerMichelElectronAlgorithm::index = 0
private

Definition at line 120 of file TAMakerMichelElectronAlgorithm.hpp.

◆ m_adj_tolerance

uint16_t triggeralgs::TAMakerMichelElectronAlgorithm::m_adj_tolerance = 3
private

Definition at line 119 of file TAMakerMichelElectronAlgorithm.hpp.

◆ m_adjacency_threshold

uint16_t triggeralgs::TAMakerMichelElectronAlgorithm::m_adjacency_threshold = 15
private

Definition at line 117 of file TAMakerMichelElectronAlgorithm.hpp.

◆ m_current_window

Window triggeralgs::TAMakerMichelElectronAlgorithm::m_current_window
private

Definition at line 111 of file TAMakerMichelElectronAlgorithm.hpp.

◆ m_max_adjacency

int triggeralgs::TAMakerMichelElectronAlgorithm::m_max_adjacency = 0
private

Definition at line 118 of file TAMakerMichelElectronAlgorithm.hpp.

◆ m_primitive_count

uint64_t triggeralgs::TAMakerMichelElectronAlgorithm::m_primitive_count = 0
private

Definition at line 112 of file TAMakerMichelElectronAlgorithm.hpp.

◆ m_window_length

timestamp_t triggeralgs::TAMakerMichelElectronAlgorithm::m_window_length = 50000
private

Definition at line 123 of file TAMakerMichelElectronAlgorithm.hpp.

◆ m_window_record

std::vector<Window> triggeralgs::TAMakerMichelElectronAlgorithm::m_window_record
private

Definition at line 129 of file TAMakerMichelElectronAlgorithm.hpp.

◆ ta_adc

uint16_t triggeralgs::TAMakerMichelElectronAlgorithm::ta_adc = 0
private

Definition at line 121 of file TAMakerMichelElectronAlgorithm.hpp.

◆ ta_channels

uint16_t triggeralgs::TAMakerMichelElectronAlgorithm::ta_channels = 0
private

Definition at line 122 of file TAMakerMichelElectronAlgorithm.hpp.


The documentation for this class was generated from the following files: