Line data Source code
1 : /**
2 : * @file TAMakerChannelAdjacencyAlgorithm.cpp
3 : *
4 : * This is part of the DUNE DAQ Application Framework, copyright 2021.
5 : * Licensing/copyright details are in the COPYING file that you should have
6 : * received with this code.
7 : */
8 :
9 : #include "triggeralgs/ChannelAdjacency/TAMakerChannelAdjacencyAlgorithm.hpp"
10 : #include "TRACE/trace.h"
11 : #include "triggeralgs/Logging.hpp"
12 : #define TRACE_NAME "TAMakerChannelAdjacencyAlgorithm"
13 : #include <math.h>
14 : #include <vector>
15 :
16 : using namespace triggeralgs;
17 :
18 : using Logging::TLVL_DEBUG_LOW;
19 :
20 : void
21 0 : TAMakerChannelAdjacencyAlgorithm::process(const TriggerPrimitive& input_tp,
22 : std::vector<TriggerActivity>& output_ta)
23 : {
24 :
25 : // Add useful info about recived TPs here for FW and SW TPG guys.
26 0 : if (m_print_tp_info) {
27 0 : TLOG_DEBUG(TLVL_DEBUG_LOW) << " ########## m_current_window is reset ##########\n"
28 0 : << " TP Start Time: " << input_tp.time_start << ", TP ADC Sum: " << input_tp.adc_integral
29 0 : << ", TP SOT: " << input_tp.samples_over_threshold << ", TP ADC Peak: " << input_tp.adc_peak
30 0 : << ", TP Offline Channel ID: " << input_tp.channel << "\n";
31 : }
32 :
33 : // 0) FIRST TP =====================================================================
34 : // The first time process() is called, reset the window object.
35 0 : if (m_current_window.is_empty()) {
36 0 : m_current_window.reset(input_tp);
37 0 : return;
38 : }
39 :
40 : // If the difference between the current TP's start time and the start of the window
41 : // is less than the specified window size, add the TP to the window.
42 0 : bool adj_pass = 0; // sets to true when adjacency logic is satisfied
43 0 : bool window_filled = 1; // sets to true when window is ready to test the adjacency logic
44 0 : if ((input_tp.time_start - m_current_window.time_start) < m_window_length) {
45 0 : m_current_window.add(input_tp);
46 0 : window_filled = 0;
47 0 : TLOG_DEBUG(TLVL_DEBUG_LOW) << "m_current_window.time_start " << m_current_window.time_start << "\n";
48 : }
49 :
50 : else {
51 0 : TPWindow win_adj_max;
52 :
53 0 : bool ta_found = 1;
54 0 : while (ta_found) {
55 :
56 : // make m_current_window_tmp a copy of m_current_window and clear m_current_window
57 0 : TPWindow m_current_window_tmp = m_current_window;
58 0 : m_current_window.clear();
59 :
60 : // make m_current_window a new window of non-overlapping tps (of m_current_window_tmp and win_adj_max)
61 0 : for (auto tp : m_current_window_tmp.inputs) {
62 0 : bool new_tp = 1;
63 0 : for (auto tp_sel : win_adj_max.inputs) {
64 0 : if (tp.channel == tp_sel.channel) {
65 : new_tp = 0;
66 : break;
67 : }
68 : }
69 0 : if (new_tp)
70 0 : m_current_window.add(tp);
71 : }
72 :
73 : // check adjacency -> win_adj_max now contains only those tps that make the track
74 0 : win_adj_max = check_adjacency();
75 0 : if (win_adj_max.inputs.size() > 0) {
76 :
77 0 : adj_pass = 1;
78 0 : ta_found = 1;
79 0 : output_ta.push_back(construct_ta(win_adj_max));
80 : } else
81 : ta_found = 0;
82 0 : }
83 0 : if (adj_pass)
84 0 : m_current_window.reset(input_tp);
85 0 : }
86 :
87 : // if adjacency logic is not true, slide the window along using the current TP.
88 0 : if (window_filled && !adj_pass) {
89 0 : m_current_window.move(input_tp, m_window_length);
90 : }
91 :
92 : return;
93 : }
94 :
95 : void
96 0 : TAMakerChannelAdjacencyAlgorithm::configure(const nlohmann::json& config)
97 : {
98 0 : TriggerActivityMaker::configure(config);
99 0 : if (config.is_object()) {
100 0 : if (config.contains("window_length"))
101 0 : m_window_length = config["window_length"];
102 0 : if (config.contains("adjacency_tolerance"))
103 0 : m_adj_tolerance = config["adjacency_tolerance"];
104 0 : if (config.contains("adjacency_threshold"))
105 0 : m_adjacency_threshold = config["adjacency_threshold"];
106 0 : if (config.contains("print_tp_info"))
107 0 : m_print_tp_info = config["print_tp_info"];
108 : }
109 0 : }
110 :
111 : TriggerActivity
112 0 : TAMakerChannelAdjacencyAlgorithm::construct_ta(TPWindow win_adj_max) const
113 : {
114 :
115 0 : TriggerActivity ta;
116 :
117 0 : TriggerPrimitive last_tp = win_adj_max.inputs.back();
118 :
119 0 : ta.time_start = last_tp.time_start;
120 0 : ta.time_end = last_tp.time_start;
121 0 : ta.time_peak = last_tp.samples_to_peak * 32 + last_tp.time_start; // FIXME: Replace STP to `time_peak` conversion.
122 0 : ta.time_activity = ta.time_peak;
123 0 : ta.channel_start = last_tp.channel;
124 0 : ta.channel_end = last_tp.channel;
125 0 : ta.channel_peak = last_tp.channel;
126 0 : ta.adc_integral = win_adj_max.adc_integral;
127 0 : ta.adc_peak = last_tp.adc_peak;
128 0 : ta.detid = last_tp.detid;
129 0 : ta.type = TriggerActivity::Type::kTPC;
130 0 : ta.algorithm = TriggerActivity::Algorithm::kChannelAdjacency;
131 0 : ta.inputs = win_adj_max.inputs;
132 :
133 0 : for (const auto& tp : ta.inputs) {
134 0 : ta.time_start = std::min(ta.time_start, tp.time_start);
135 0 : ta.time_end = std::max(ta.time_end, tp.time_start);
136 0 : ta.channel_start = std::min(ta.channel_start, channel_t(tp.channel));
137 0 : ta.channel_end = std::max(ta.channel_end, channel_t(tp.channel));
138 0 : if (tp.adc_peak > ta.adc_peak) {
139 0 : ta.time_peak = tp.samples_to_peak * 32 + tp.time_start; // FIXME: Replace STP to `time_peak` conversion.
140 0 : ta.adc_peak = tp.adc_peak;
141 0 : ta.channel_peak = tp.channel;
142 : }
143 : }
144 :
145 0 : return ta;
146 0 : }
147 :
148 : // std::vector<TriggerPrimitive>
149 : TPWindow
150 0 : TAMakerChannelAdjacencyAlgorithm::check_adjacency()
151 : {
152 : // This function deals with tp window (m_current_window), select adjacent tps (with a channel gap from 0 to 5; sum of
153 : // all gaps < m_adj_tolerance), checks if track length > m_adjacency_threshold: return the tp window (win_adj_max,
154 : // which is subset of the input tp window)
155 :
156 0 : unsigned int channel = 0; // Current channel ID
157 0 : unsigned int next_channel = 0; // Next channel ID
158 0 : unsigned int next = 0; // The next position in the hit channels vector
159 0 : unsigned int tol_count = 0; // Tolerance count, should not pass adj_tolerance
160 :
161 : // Generate a channelID ordered list of hit channels for this window; second element of pair is tps
162 0 : std::vector<std::pair<int, TriggerPrimitive>> chanTPList;
163 0 : for (auto tp : m_current_window.inputs) {
164 0 : chanTPList.push_back(std::make_pair(channel_t(tp.channel), tp));
165 : }
166 0 : std::sort(chanTPList.begin(),
167 : chanTPList.end(),
168 0 : [](const std::pair<int, TriggerPrimitive>& a, const std::pair<int, TriggerPrimitive>& b) {
169 0 : return (a.first < b.first);
170 : });
171 :
172 : // ADAJACENCY LOGIC ====================================================================
173 : // =====================================================================================
174 : // Adjcancency Tolerance = Number of times prepared to skip missed hits before resetting
175 : // the adjacency count (win_adj). This accounts for things like dead channels / missed TPs.
176 :
177 : // add first tp, and then if tps are on next channels (check code below to understand the definition)
178 0 : TPWindow win_adj;
179 0 : TPWindow win_adj_max; // if track length > m_adjacency_threshold, set win_adj_max = win_adj; return win_adj_max;
180 :
181 0 : for (int i = 0; i < chanTPList.size(); ++i) {
182 :
183 0 : win_adj_max.clear();
184 :
185 0 : next = (i + 1) % chanTPList.size(); // Loops back when outside of channel list range
186 0 : channel = chanTPList.at(i).first;
187 0 : next_channel = chanTPList.at(next).first; // Next channel with a hit
188 :
189 : // End of vector condition.
190 0 : if (next == 0) {
191 0 : next_channel = channel - 1;
192 : }
193 :
194 : // Skip same channel hits.
195 0 : if (next_channel == channel)
196 0 : continue;
197 :
198 : // If win_adj size == zero, add current tp
199 0 : if (win_adj.inputs.size() == 0)
200 0 : win_adj.add(chanTPList[i].second);
201 :
202 : // If next hit is on next channel, increment the adjacency count
203 0 : if (next_channel - channel == 1) {
204 0 : win_adj.add(chanTPList[next].second);
205 : }
206 :
207 : // Allow a max gap of 5 channels (e.g., 45 and 50; 46, 47, 48, 49 are missing); increment the adjacency count
208 : // Sum of gaps should be < adj_tolerance (e.g., if toleance is 30, the max total gap can vary from 0 to 29+4 = 33)
209 0 : else if (next_channel - channel > 0 && next_channel - channel <= 5 && tol_count < m_adj_tolerance) {
210 0 : win_adj.add(chanTPList[next].second);
211 0 : tol_count += next_channel - channel - 1;
212 : }
213 :
214 : // if track length > m_adjacency_threshold, set win_adj_max = win_adj;
215 0 : else if (win_adj.inputs.size() > m_adjacency_threshold) {
216 0 : win_adj_max = win_adj;
217 : break;
218 : }
219 :
220 : // If track length < m_adjacency_threshold, reset variables for next iteration.
221 : else {
222 0 : tol_count = 0;
223 0 : win_adj.clear();
224 : }
225 : }
226 :
227 0 : return win_adj_max;
228 0 : }
229 :
230 : // =====================================================================================
231 : // Functions below this line are for debugging purposes.
232 : // =====================================================================================
233 : void
234 0 : TAMakerChannelAdjacencyAlgorithm::add_window_to_record(TPWindow window)
235 : {
236 0 : m_window_record.push_back(window);
237 0 : return;
238 : }
239 :
240 : // Register algo in TA Factory
241 12 : REGISTER_TRIGGER_ACTIVITY_MAKER(TRACE_NAME, TAMakerChannelAdjacencyAlgorithm)
|