All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
SlidingWindowPatternAlg.cxx
Go to the documentation of this file.
1 /**
2  * @file icaruscode/PMT/Trigger/Algorithms/SlidingWindowPatternAlg.cxx
3  * @brief Applies sliding window trigger patterns.
4  * @author Gianluca Petrillo (petrillo@slac.stanford.edu)
5  * @date January 29, 2021
6  * @see icaruscode/PMT/Trigger/Algorithms/SlidingWindowPatternAlg.h
7  */
8 
9 
10 // library header
12 
13 
14 // ICARUS libraries
18 
19 // LArSoft libraries
22 
23 // framework libraries
24 #include "messagefacility/MessageLogger/MessageLogger.h"
25 
26 // C/C++ standard libraries
27 #include <algorithm> // std::binary_search()
28 #include <utility> // std::pair<>, std::move()
29 #include <optional>
30 #include <cassert>
31 
32 
33 //------------------------------------------------------------------------------
35  WindowTopology_t windowTopology,
36  WindowPattern_t windowPattern,
38  std::string const& logCategory /* = "SlidingWindowPatternAlg" */
39  )
40  : icarus::ns::util::mfLoggingClass(logCategory)
41  , fWindowTopology(std::move(windowTopology))
42  , fWindowPattern(std::move(windowPattern))
43  , fBeamGate(std::move(beamGate))
44  {}
45 
46 
47 //------------------------------------------------------------------------------
49  WindowTopology_t windowTopology,
50  WindowPattern_t windowPattern,
51  std::string const& logCategory /* = "SlidingWindowPatternAlg" */
52  )
53  : icarus::ns::util::mfLoggingClass(logCategory)
54  , fWindowTopology(std::move(windowTopology))
55  , fWindowPattern(std::move(windowPattern))
56  {}
57 
58 
59 //------------------------------------------------------------------------------
61  (TriggerGates_t const& gates) const -> AllTriggerInfo_t
62 {
63 
64  // ensures input gates are in the same order as the configured windows
65  verifyInputTopology(gates);
66 
67  auto const& inBeamGates = fBeamGate? fBeamGate->applyToAll(gates): gates;
68 
69  //
70  // 2. apply pattern:
71  //
72  std::size_t const nWindows = fWindowTopology.nWindows();
73 
74  //
75  // 2.1. for each main window, apply the pattern
76  //
77  WindowTriggerInfo_t triggerInfo; // start empty
78  for (std::size_t const iWindow: util::counter(nWindows)) {
79 
80  TriggerInfo_t const windowResponse
81  = applyWindowPattern(fWindowPattern, iWindow, inBeamGates);
82 
83  if (!windowResponse) continue;
84 
85  assert(windowResponse.hasLocation());
86  assert(windowResponse.location() == iWindow);
87  if (windowResponse.nTriggers() == 1U) {
88  mfLogTrace() << "Pattern fired on window #" << iWindow
89  << " at tick " << windowResponse.atTick();
90  }
91  else {
92  std::vector<TriggerInfo_t::OpeningInfo_t> const& triggers
93  = windowResponse.all();
94  TriggerInfo_t::OpeningInfo_t const* pMain = &(windowResponse.main());
95  auto log = mfLogTrace();
96  log << "Pattern fired " << triggers.size()
97  << " times on window #" << iWindow << ":";
98  for (auto const& [ iTrigger, trigger ]: util::enumerate(triggers)) {
99  log << " [#" << iTrigger << "] " << " on " << trigger.tick
100  << " (";
101  if (&trigger == pMain) log << "main; ";
102  log << "lvl=" << trigger.level << ")";
103  } // for
104  }
105 
106  //
107  // 2.2. pick the main window with the earliest successful response, if any;
108  // that defines location and time of the trigger
109  //
110  if (!triggerInfo || triggerInfo.info.atTick() > windowResponse.atTick()) {
111  if (!triggerInfo) mfLogTrace() << " (new global trigger)";
112  triggerInfo.emplace(iWindow, windowResponse);
113  }
114 
115  } // main window choice
116 
117  return { std::move(triggerInfo.info), MoreInfo_t{ triggerInfo.windowIndex } };
118 } // icarus::trigger::SlidingWindowPatternAlg::simulateResponse()
119 
120 
121 //------------------------------------------------------------------------------
123  { return fBeamGate.has_value(); }
124 
125 
126 //------------------------------------------------------------------------------
129  { fBeamGate.emplace(std::move(beamGate)); }
130 
131 
132 //------------------------------------------------------------------------------
134  { fBeamGate.reset(); }
135 
136 
137 //------------------------------------------------------------------------------
139  (TriggerGates_t const& gates) const
140 {
141  /*
142  * Verifies that the `gates` are in the expected order and have
143  * the expected channel content.
144  */
145 
146  std::string errorMsg; // if this stays `empty()` there is no error
147  for (auto const& [ iWindow, gate ]: util::enumerate(gates)) {
148 
149  std::string windowError; // if this stays `empty()` there is no error
150 
151  // more input gates than windows?
152  if (iWindow >= fWindowTopology.nWindows()) {
153  windowError = "unexpected input gate #" + std::to_string(iWindow) + " (";
154  for (raw::Channel_t const channel: gate.channels())
155  windowError += " " + std::to_string(channel);
156  windowError += " )";
157  errorMsg += windowError + '\n';
158  continue; // we collect info of all spurious gates
159  }
160 
161  WindowTopology_t::WindowInfo_t const& windowInfo
162  = fWindowTopology.info(iWindow);
163 
164  auto const channelInWindow
165  = [begin=windowInfo.channels.cbegin(),end=windowInfo.channels.cend()]
166  (raw::Channel_t channel)
167  { return std::binary_search(begin, end, channel); }
168  ;
169 
170  for (raw::Channel_t const channel: gate.channels()) {
171  if (channelInWindow(channel)) continue;
172  if (windowError.empty()) {
173  windowError =
174  "channels not in window #" + std::to_string(windowInfo.index)
175  + ":";
176  } // if first error
177  windowError += " " + std::to_string(channel);
178  } // for all channels in gate
179 
180  if (!windowError.empty()) errorMsg += windowError + '\n';
181  } // for gates
182 
183  // more input gates than windows?
184  if (fWindowTopology.nWindows() > size(gates)) {
185  errorMsg +=
186  "Not enough input gates: " + std::to_string(size(gates)) + " gates for "
187  + std::to_string(fWindowTopology.nWindows()) + " windows\n";
188  }
189 
190  if (errorMsg.empty()) return;
191 
192  // put together the exception message and throw it.
193  throw cet::exception("SlidingWindowPatternAlg")
194  << "Some channels from trigger gates do not match"
195  " the configured window allocation:\n"
196  << errorMsg
197  << "\n" // empty line
198  << "Window configuration: "
199  << fWindowTopology << "\n";
200 
201 } // icarus::trigger::SlidingWindowPatternAlg::verifyInputTopology()
202 
203 
204 //------------------------------------------------------------------------------
206  WindowTopology_t::WindowInfo_t const& windowInfo,
207  WindowPattern_t const& pattern,
208  TriggerGates_t const& gates
209  ) const -> TriggerInfo_t
210 {
211 
212  /*
213  * 1. check that the pattern can be applied; if not, return no trigger
214  * 2. discriminate all the relevant gates against their required minimum count
215  * 3. combine them in AND
216  * 4. find the trigger time, fill the trigger information accordingly
217  */
218  TriggerInfo_t res; // no trigger by default
219  assert(!res);
220 
221  // undress the gate to get its data
222  auto const gateAt = [&gates](std::size_t index) -> TriggerGateData_t const&
223  { return gateIn(gates[index]); };
224 
225  //
226  // 1. check that the pattern can be applied; if not, return no trigger
227  //
228 
229  // check that the pattern centered into iWindow has all it needs:
230  if (pattern.requireUpstreamWindow && !windowInfo.hasUpstreamWindow())
231  return res;
232  if (pattern.requireDownstreamWindow && !windowInfo.hasDownstreamWindow())
233  return res;
234 
235 
236  //
237  // 2. discriminate all the relevant gates against their required minimum count
238  // 3. combine them in AND
239  //
240 
241  mfLogTrace()
242  << "Window info #" << windowInfo.index << " pattern " << pattern.tag();
243 
244  // we have two different "modes" depending on whether we are constraining
245  // the sum of main and opposite window, or not;
246  std::optional<TriggerGateData_t> const mainPlusOpposite
247  = (pattern.minSumInOppositeWindows > 0U)
248  ? std::optional{
249  windowInfo.hasOppositeWindow()
250  ? sumGates(gateAt(windowInfo.index), gateAt(windowInfo.opposite))
251  : gateAt(windowInfo.index)
252  }
253  : std::nullopt
254  ;
255 
256  // the basic trigger primitive gate has the levels of the main or
257  // main+opposite window depending on the requirements
258  TriggerGateData_t trigPrimitive
259  = mainPlusOpposite? *mainPlusOpposite: gateAt(windowInfo.index);
260 
261  mfLogTrace() << " base: " << compactdump(trigPrimitive);
262 
263  // main window
264  if (pattern.minInMainWindow > 0U) {
265  trigPrimitive.Mul
266  (discriminate(gateAt(windowInfo.index), pattern.minInMainWindow));
267  mfLogTrace()
268  << " main >= " << pattern.minInMainWindow << ": "
269  << compactdump(trigPrimitive);
270  } // if
271 
272  // add opposite window requirement (if any)
273  if ((pattern.minInOppositeWindow > 0U) && windowInfo.hasOppositeWindow()) {
274  trigPrimitive.Mul
275  (discriminate(gateAt(windowInfo.opposite), pattern.minInOppositeWindow));
276  mfLogTrace() << " opposite [#" << windowInfo.opposite << "]: "
277  << compactdump(gateAt(windowInfo.opposite))
278  << "\n => " << compactdump(trigPrimitive);
279  } // if
280 
281  // add main plus opposite window requirement (if any)
282  if (pattern.minSumInOppositeWindows > 0U) {
283  assert(mainPlusOpposite.has_value());
284  trigPrimitive.Mul
285  (discriminate(*mainPlusOpposite, pattern.minSumInOppositeWindows));
286  mfLogTrace() << " sum [+ #" << windowInfo.opposite << "]: "
287  << compactdump(*mainPlusOpposite)
288  << "\n => " << compactdump(trigPrimitive);
289 
290  } // if
291 
292  // add upstream window requirement (if any)
293  if ((pattern.minInUpstreamWindow > 0U) && windowInfo.hasUpstreamWindow()) {
294  trigPrimitive.Mul
295  (discriminate(gateAt(windowInfo.upstream), pattern.minInUpstreamWindow));
296  } // if
297 
298  // add downstream window requirement (if any)
299  if ((pattern.minInDownstreamWindow > 0U) && windowInfo.hasDownstreamWindow())
300  {
301  trigPrimitive.Mul(
302  discriminate(gateAt(windowInfo.downstream), pattern.minInDownstreamWindow)
303  );
304  } // if
305 
306  mfLogTrace() << " final: " << compactdump(trigPrimitive);
307 
308  //
309  // 4. find the trigger time, fill the trigger information accordingly
310  //
312  { trigPrimitive };
313 
314  extractOpeningInfo.setLocation
315  (TriggerInfo_t::LocationID_t{ windowInfo.index });
316 
317  while (extractOpeningInfo) {
318  auto info = extractOpeningInfo();
319  if (info) res.add(info.value());
320  } // while
321 
322  return res;
323 
324 } // icarus::trigger::SlidingWindowTriggerEfficiencyPlots::applyWindowPattern()
325 
326 
327 //------------------------------------------------------------------------------
329  WindowPattern_t const& pattern,
330  std::size_t iWindow,
331  TriggerGates_t const& gates
332  ) const -> TriggerInfo_t
333 {
334  WindowTopology_t::WindowInfo_t const& windowInfo
335  = fWindowTopology.info(iWindow);
336  assert(windowInfo.index == iWindow);
337 
338  return applyWindowPattern(windowInfo, pattern, gates);
339 } // icarus::trigger::SlidingWindowTriggerEfficiencyPlots::applyWindowPattern()
340 
341 
342 //------------------------------------------------------------------------------
Data structure to communicate internally a trigger response.
AllTriggerInfo_t simulateResponse(TriggerGates_t const &gates) const
Returns the trigger response from the specified gates.
void setBeamGate(icarus::trigger::ApplyBeamGateClass beamGate)
Changes the beam gate to the specified value.
ReadoutTriggerGate & Mul(ReadoutTriggerGate const &other)
Combines with a gate, keeping the product of openings of the two.
TriggerInfo_t applyWindowPattern(WindowTopology_t::WindowInfo_t const &windowInfo, WindowPattern_t const &pattern, TriggerGates_t const &gates) const
Returns the trigger response for the specified window pattern.
double std(const std::vector< short > &wf, const double ped_mean, size_t start, size_t nsample)
Definition: UtilFunc.cxx:42
void verifyInputTopology(TriggerGates_t const &gates) const
Checks gates are compatible with the current window configuration.
std::vector< OpeningInfo_t > const & all() const
Utilities for the conversion of trigger gate data formats.
Definition of util::enumerate().
LocationID_t location() const
Returns the ID of the location of the trigger (undefined if !fired()).
Utilities for TriggerGateData printout.
std::size_t size(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:561
OpeningInfo_t const & main() const
Returns the full data (undefined if !fired()).
Information about composition and topology of trigger sliding windows.
bool hasLocation() const
Returns if the location of the trigger is set (undefined if !fired()).
SlidingWindowPatternAlg(WindowTopology_t windowTopology, WindowPattern_t windowPattern, icarus::trigger::ApplyBeamGateClass beamGate, std::string const &logCategory="SlidingWindowPatternAlg")
Constructor: configures window topology and times.
Helper applying a beam gate to any gate.
Definition: ApplyBeamGate.h:85
Helper data structure to store transient trigger result.
Definition: TriggerInfo_t.h:50
auto enumerate(Iterables &&...iterables)
Range-for loop helper tracking the number of iteration.
Definition: enumerate.h:69
Specification of the requirement of sliding window firing pattern.
Definition: WindowPattern.h:51
void clearBeamGate()
Do not apply any beam gate.
auto counter(T begin, T end)
Returns an object to iterate values from begin to end in a range-for loop.
Definition: counter.h:285
Logical multi-level gate associated to one or more readout channels.
auto end(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:585
std::size_t LocationID_t
Type for ID of trigger location.
Definition: TriggerInfo_t.h:58
Utilities for the conversion of trigger gate data formats.
Test of util::counter and support utilities.
auto sumGates(GateColl const &gates)
Sums all the gates in a collection.
auto begin(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:573
bool hasBeamGate() const
Returns whether a beam gate is being applied.
optical_tick atTick() const
Returns the time of the trigger (undefined if !fired()).
std::string to_string(WindowPattern const &pattern)
std::vector< raw::Channel_t > channels
Optical detector channels covered by this window.
std::vector< InputTriggerGate_t > TriggerGates_t
A list of trigger gates from input.
WindowIndex_t index
Index of the window this information is about.
GateObj discriminate(GateObj const &gate, typename GateObj::OpeningCount_t threshold=1U, typename GateObj::OpeningCount_t pass=1U, typename GateObj::OpeningCount_t fail=0U)
Returns a discriminated version of gate.
Complete information from this algorithm, standard + non-standard (extra).
Applies sliding window trigger patterns.
Helper to extract OpeningInfo_t from a trigger gate.
Definition: TriggerInfo_t.h:30
std::size_t nTriggers() const
Returns the number of registered triggers.
auto compactdump(ReadoutTriggerGate< Tick, TickInterval, ChannelIDType > const &gate) -> details::CompactFormatter< ReadoutTriggerGate< Tick, TickInterval, ChannelIDType >>
Manipulator-like function for compact format of trigger gates.
decltype(auto) gateIn(Gate &&gate)
Returns the trigger gate (a ReadoutTriggerGate) from the specified gate.