All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
TimedTrackSelector_module.cc
Go to the documentation of this file.
1 /**
2  * @file icaruscode/Analysis/trigger/TimedTrackSelector_module.cc
3  * @date September 17, 2021
4  * @authors Animesh Chatterjee (ANC238@pitt.edu),
5  * Gianluca Petrillo (petrillo@slac.stanford.edu),
6  * Jacob Zettlemoyer (jzettle@fnal.gov)
7  *
8  */
9 
10 #define MF_DEBUG
11 
12 // SBN and ICARUS libraries
13 #ifdef USE_ATOMICPASSCOUNTER
15 #else // !USE_ATOMICPASSCOUNTER
17 #endif // USE_ATOMICPASSCOUNTER
18 
19 // LArSoft libraries
22 
23 
24 // framework libraries
25 #include "art/Framework/Core/SharedFilter.h"
26 #include "art/Framework/Core/ModuleMacros.h"
27 #include "art/Framework/Principal/Event.h"
28 #include "art/Framework/Principal/Handle.h"
29 #include "canvas/Persistency/Common/Assns.h"
30 #include "canvas/Persistency/Common/Ptr.h"
31 #include "canvas/Utilities/InputTag.h"
32 #include "fhiclcpp/types/Sequence.h"
33 #include "fhiclcpp/types/Atom.h"
34 #include "messagefacility/MessageLogger/MessageLogger.h"
35 
36 // C/C++ standard library
37 #include <vector>
38 #include <string>
39 #include <atomic>
40 #include <memory>
41 #include <limits>
42 
43 // -----------------------------------------------------------------------------
44 namespace sbn { class TimedTrackSelector; }
45 /**
46  * @brief Selects tracks with time information.
47  *
48  * This module produces a list of "tracks" that are associated to a time.
49  * Optionally, it can select only the tracks with that time in a specified
50  * range.
51  *
52  * This module is a filter that will return `true` for an event if at least one
53  * of its tracks is selected. This threshold can be adjusted by configuration.
54  *
55  *
56  * Input
57  * ------
58  *
59  * The input file is expected to contain the time information and association
60  * to "tracks", which are actually represented by `recob::PFParticle` rather
61  * than `recob::Track`.
62  * Note that the track data products are not explicitly required.
63  *
64  *
65  * Output
66  * -------
67  *
68  * The filter _produces_ a list of pointers to the selected tracks; the list
69  * will mix tracks from different data products if multiple input collections
70  * are specified. A track is selected if all the following apply:
71  *
72  * 1. the track is associated to a time;
73  * 2. only if a time range is specified, that track time must fall within.
74  *
75  * The filter passes the event if:
76  * * the number of selected tracks is within the configured range of requested
77  * tracks per event.
78  *
79  *
80  * Configuration options
81  * ----------------------
82  *
83  * * `TrackTimeTags` (list of data product tags, required): data product of the
84  * times and their associations tracks.
85  * * `MinT0` (real, optional): if specified, tracks are selected only if their
86  * associated time is not earlier than this value. Time is in the same time
87  * scale as the associated track time, which is expected to be the
88  * @ref DetectorClocksTriggerTime "trigger time scale" but in nanoseconds.
89  * * `MaxT0` (real, optional): if specified, tracks are selected only if their
90  * associated time is earlier than this value. This time is in the same
91  * time scale as `MinT0`.
92  * * `MinTracks` (integer, default: `1`): the filter "passes" the event only if
93  * at least these many tracks are selected; disable this by setting it to
94  * `0`.
95  * * `MaxTracks` (integer, default: a large number): if specified, filter
96  * "passes" the event only if at most these many tracks are selected.
97  * * `OnlyPandoraTracks` (flag, default: `true`): saves only the particles which
98  * have been recognised as track-like. The definition was taken from
99  * Pandora (`larpandoracontent` `v03_26_01`) and includes charged pions and
100  * kaons, protons and muons. Note that Pandora itself does not do a full
101  * particle type identification and it uses only the codes for muons and
102  * electrons, to distinguish track-like and shower-like topologies, plus
103  * neutrino codes.
104  * * `SaveTracks` (flag, default: `true`): produces a list of _art_ pointers
105  * to the selected particles. This is the default behaviour of the module.
106  * On the other end, the module can be used solely for its filtering
107  * capability, in which case saving the list is not necessary and this
108  * option allows omitting it.
109  *
110  */
111 class sbn::TimedTrackSelector: public art::SharedFilter {
112 
113  public:
114 
115  static constexpr double NoMinTime { std::numeric_limits<double>::lowest() };
116  static constexpr double NoMaxTime { std::numeric_limits<double>::max() };
117  static constexpr unsigned int NoMinTracks { 0U };
118  static constexpr unsigned int NoMaxTracks
119  { std::numeric_limits<unsigned int>::max() };
120 
121 
122  /// Module configuration parameters.
123  struct Config {
124 
125  using Name = fhicl::Name;
126  using Comment = fhicl::Comment;
127 
128 
129  fhicl::Sequence<art::InputTag> TrackTimeTags {
130  Name{ "TrackTimeTags" },
131  Comment{ "data products with trach time information (and associations)" }
132  };
133 
134  fhicl::Atom<double> MinT0 {
135  Name{ "MinT0" },
136  Comment{ "Select only tracks not earlier than this time [ns]" },
137  NoMinTime
138  };
139 
140  fhicl::Atom<double> MaxT0 {
141  Name{ "MaxT0" },
142  Comment{ "Select only tracks earlier than this time [ns]" },
143  NoMaxTime
144  };
145 
146  fhicl::Atom<unsigned int> MinTracks {
147  Name{ "MinTracks" },
148  Comment{ "Pass only events with at least these many selected tracks" },
149  1U
150  };
151 
152  fhicl::Atom<unsigned int> MaxTracks {
153  Name{ "MaxTracks" },
154  Comment{ "Pass only events with at most these many selected tracks" },
156  };
157 
158  fhicl::Atom<bool> OnlyPandoraTracks {
159  Name{ "OnlyPandoraTracks" },
160  Comment{
161  "Select a \"track\" only if its particle flow object"
162  " has the particle ID of a track"
163  },
164  true
165  };
166 
167  fhicl::Atom<bool> SaveTracks {
168  Name{ "SaveTracks" },
169  Comment{ "Whether to write the list of selected tracks" },
170  true
171  };
172 
173  fhicl::Atom<std::string> LogCategory {
174  Name{ "LogCategory" },
175  Comment{ "name of a message facility stream for this module" },
176  "TimedTrackSelector"
177  };
178 
179  }; // Config
180 
181 
182  using Parameters = art::SharedFilter::Table<Config>;
183 
184  explicit TimedTrackSelector
185  (Parameters const& params, art::ProcessingFrame const&);
186 
187  bool filter(art::Event& event, art::ProcessingFrame const&) override;
188 
189  /// Prints end-job summary.
190  void endJob(art::ProcessingFrame const&) override;
191 
192 
193  private:
194 
195  // --- BEGIN -- Configuration parameters -------------------------------------
196 
197  /// List of track-time association input tags.
198  std::vector<art::InputTag> const fTrackTimeTags;
199 
200  double const fMinT0; ///< Minimum track time for track selection.
201  double const fMaxT0; ///< Maximum track time for track selection.
202 
203  /// Minimum selected tracks for event selection.
204  unsigned int const fMinTracks;
205  /// Maximum selected tracks for event selection.
206  unsigned int const fMaxTracks;
207 
208  bool const fOnlyPandoraTracks; ///< Save only tracks identified as such.
209  bool const fSaveTracks; ///< Whether to save selected tracks into the event.
210 
211  std::string const fLogCategory; ///< Message facility stream name.
212 
213  // --- END ---- Configuration parameters -------------------------------------
214 
215  /// Counter of passed events (not thread-safe).
216 #ifdef USE_ATOMICPASSCOUNTER
218 #else // !USE_ATOMICPASSCOUNTER
220 #endif // USE_ATOMICPASSCOUNTER
221 
222 
223  /**
224  * @brief Adds to `selectedTracks` qualifying tracks from `timeTracks`.
225  * @param timeTracks time/track associations
226  * @param[out] selectedTracks collection to expand with the qualifying tracks
227  * @return the number of qualifying tracks found in `timeTracks` and added
228  */
229  unsigned int selectTracks(
230  art::Assns<anab::T0, recob::PFParticle> const& timeTracks, std::vector<art::Ptr<recob::PFParticle>>& selectedTracks
231  ) const;
232 
233 
234  /// Returns whether the specified track (with specified time) qualifies.
235  bool isTrackSelected
236  (recob::PFParticle const& track, anab::T0 const& time) const;
237 
238  /// Returns whether the specified `particle` flow object is a track.
239  static bool isTrack(recob::PFParticle const& particle);
240 
241  /// Returns if the number of tracks `nTracks` satisfies filter requirements.
242  bool selectedTracksRequirement(unsigned int nTracks) const;
243 
244 
245 }; // sbn::TimedTrackSelector
246 
247 
248 // -----------------------------------------------------------------------------
249 // --- Implementation
250 // -----------------------------------------------------------------------------
251 
252 int fTotalTracks = 0;
253 
255  (Parameters const& params, art::ProcessingFrame const&)
256  : art::SharedFilter{ params }
257  , fTrackTimeTags{ params().TrackTimeTags() }
258  , fMinT0{ params().MinT0() }
259  , fMaxT0{ params().MaxT0() }
260  , fMinTracks{ params().MinTracks() }
261  , fMaxTracks{ params().MaxTracks() }
262  , fOnlyPandoraTracks{ params().OnlyPandoraTracks() }
263  , fSaveTracks{ params().SaveTracks() }
264  , fLogCategory{ params().LogCategory() }
265 {
266  async<art::InEvent>();
267 
268  if (fSaveTracks)
269  produces<std::vector<art::Ptr<recob::PFParticle>>>();
270 
271  //
272  // configuration dump
273  //
274  {
275  mf::LogInfo log{ fLogCategory };
276  log << "Configuration:"
277  << "\n * tracks required to be associated to a time (cathode-crossers)"
278  ;
279  log << "\n * track time:";
280  if (fMinT0 == NoMinTime) {
281  if (fMaxT0 == NoMaxTime) log << " any";
282  else log << " before " << fMaxT0;
283  }
284  else {
285  log << " from " << fMinT0;
286  if (fMaxT0 == NoMaxTime) log << " on";
287  else log << " to " << fMaxT0;
288  }
289 
290  log << "\n * selected tracks per event: ";
291  if (fMinTracks == NoMinTracks) {
292  if (fMaxTracks == NoMaxTracks) log << "any";
293  else log << fMaxTracks << " or less";
294  }
295  else {
296  if (fMaxTracks == NoMaxTracks) log << fMinTracks << " or more";
297  else log << "between " << fMinTracks << " and " << fMaxTracks;
298  }
299  log << "\n * selected tracks will" << (fSaveTracks? "": " not")
300  << " be saved";
301  } // end local scope
302 
303 } // sbn::TimedTrackSelector::TimedTrackSelector()
304 
305 
306 // -----------------------------------------------------------------------------
308  (art::Event& event, art::ProcessingFrame const&)
309 {
310 
311  mf::LogDebug(fLogCategory) << "Processing " << event.id();
312 
313  //
314  // select tracks from each of the input tags
315  //
316  std::vector<art::Ptr<recob::PFParticle>> selectedTracks;
317  for (art::InputTag const& inputTag: fTrackTimeTags) {
318  auto const& T0toTrack
319  = event.getProduct<art::Assns<anab::T0, recob::PFParticle>>(inputTag);
320  unsigned int const newTracks = selectTracks(T0toTrack, selectedTracks);
321 
322  mf::LogTrace(fLogCategory)
323  << "From '" << inputTag.encode() << "': "
324  << newTracks << " tracks selected"
325  ;
326 
327  } // for
328 
329  unsigned int const nSelectedTracks = selectedTracks.size();
330 
331 
332  //
333  // save track list in the event
334  //
335 
336  // after this, selectedTracks may be empty. JCZ: Not quite, I think the access to selectedTracks[0] in the log statement throws a segfault it it's empty, now fixed
337  if (fSaveTracks) {
338  event.put(
339  std::make_unique<std::vector<art::Ptr<recob::PFParticle>>>(selectedTracks)
340  );
341  if(selectedTracks.size() > 0)
342  {
343  mf::LogTrace(fLogCategory)
344  << "InputTag for this product is: "
345  << event.getProductDescription(selectedTracks[0].id())->inputTag();
346  }
347  }
348 
349 
350  //
351  // filter logic
352  //
353 
354  bool const passed = selectedTracksRequirement(nSelectedTracks);
355  fPassRate.add(passed);
356  mf::LogTrace(fLogCategory) << event.id()
357  << ' ' << (passed? "passed": "rejected") << " (" << nSelectedTracks
358  << " selected tracks)."; // funny fact: we don't know the total track count, JCZ: I think I fixed that fact although I could be completely wrong
359 
360 
361  mf::LogDebug(fLogCategory) << "Completed " << event.id();
362 
363  mf::LogDebug(fLogCategory) << "There are now " << fTotalTracks << " total tracks selected.";
364 
365  return passed;
366 
367 } // sbn::TimedTrackSelector::filter()
368 
369 
370 // -----------------------------------------------------------------------------
371 void sbn::TimedTrackSelector::endJob(art::ProcessingFrame const&) {
372 
373  mf::LogInfo(fLogCategory) << "Selected " << fPassRate.passed()
374  << '/' << fPassRate.total() << " events with qualifying tracks.";
375 
376 } // sbn::TimedTrackSelector::endJob()
377 
378 
379 // -----------------------------------------------------------------------------
381  art::Assns<anab::T0, recob::PFParticle> const& timeTracks, std::vector<art::Ptr<recob::PFParticle>>& selectedTracks
382 ) const {
383 
384  unsigned int nSelectedTracks { 0U };
385  for (auto const& [ t0Ptr, trackPtr ]: timeTracks) {
386 
387  // if fOnlyPandoraTracks is false, we don't need the track, so we avoid
388  // dereferencing the pointer: a reference from a null pointer will be
389  // passed around and will cause a segmentation violation at the first access
390  recob::PFParticle const* track = fOnlyPandoraTracks? &*trackPtr: nullptr;
391 
392  if (!isTrackSelected(*track, *t0Ptr)) continue;
393 
394  MF_LOG_TRACE(fLogCategory) << "Track #" << trackPtr.key() << " selected.";
395 
396  selectedTracks.push_back(trackPtr);
397  ++nSelectedTracks;
398  ++fTotalTracks;
399  } // for
400 
401  return nSelectedTracks;
402 
403 } // sbn::TimedTrackSelector::selectTracks()
404 
405 
406 // -----------------------------------------------------------------------------
408  (recob::PFParticle const& track, anab::T0 const& time) const
409 {
410 
411  double const T0 = time.Time();
412  MF_LOG_TRACE(fLogCategory) << "Track time: " << T0;
413 
414  if (fOnlyPandoraTracks && !isTrack(track)) {
415  MF_LOG_TRACE(fLogCategory) << "Not a track (ID=" << track.PdgCode()
416  << ") => discarded!";
417  return false;
418  }
419 
420  if ((T0 < fMinT0) || (T0 >= fMaxT0)) {
421  MF_LOG_TRACE(fLogCategory) << "Time out of range [ " << fMinT0 << "; "
422  << fMaxT0 << " ] => discarded!";
423  return false;
424  }
425 
426  return true;
427 } // sbn::TimedTrackSelector::isTrackSelected()
428 
429 
430 // -----------------------------------------------------------------------------
432 
433  switch (std::abs(particle.PdgCode())) {
434  case 13: // muon
435  case 211: // charged pion
436  case 321: // charged kaon
437  case 2212: // proton
438  return true;
439  default:
440  return false;
441  } // switch
442 
443 } // sbn::TimedTrackSelector::isTrack()
444 
445 
446 // -----------------------------------------------------------------------------
448  (unsigned int nTracks) const
449 {
450  return (nTracks >= fMinTracks) && (nTracks <= fMaxTracks);
451 } // sbn::TimedTrackSelector::selectedTracks()
452 
453 
454 // -----------------------------------------------------------------------------
455 DEFINE_ART_MODULE(sbn::TimedTrackSelector)
456 
457 
458 // -----------------------------------------------------------------------------
std::string const fLogCategory
Message facility stream name.
icarus::ns::util::PassCounter fPassRate
Counter of passed events (not thread-safe).
bool filter(art::Event &event, art::ProcessingFrame const &) override
static constexpr unsigned int NoMinTracks
const double & Time() const
Definition: T0.h:44
bool selectedTracksRequirement(unsigned int nTracks) const
Returns if the number of tracks nTracks satisfies filter requirements.
unsigned int const fMaxTracks
Maximum selected tracks for event selection.
void endJob(art::ProcessingFrame const &) override
Prints end-job summary.
int PdgCode() const
Return the type of particle as a PDG ID.
Definition: PFParticle.h:83
process_name use argoneut_mc_hitfinder track
Definition: T0.h:16
Class to keep count of a pass/fail result.
bool isTrackSelected(recob::PFParticle const &track, anab::T0 const &time) const
Returns whether the specified track (with specified time) qualifies.
Count_t total() const
Returns the total number of registered events.
Definition: PassCounter.h:60
Selects tracks with time information.
fDetProps &fDetProps fDetProps &fDetProps fLogCategory
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:265
double const fMinT0
Minimum track time for track selection.
T abs(T value)
double const fMaxT0
Maximum track time for track selection.
std::vector< art::InputTag > const fTrackTimeTags
List of track-time association input tags.
bool const fOnlyPandoraTracks
Save only tracks identified as such.
fhicl::Sequence< art::InputTag > TrackTimeTags
static bool isTrack(recob::PFParticle const &particle)
Returns whether the specified particle flow object is a track.
bool const fSaveTracks
Whether to save selected tracks into the event.
Module configuration parameters.
BEGIN_PROLOG vertical distance to the surface Name
Class to keep count of a pass/fail result (thread-safe).
static constexpr double NoMaxTime
Hierarchical representation of particle flow.
Definition: PFParticle.h:44
TimedTrackSelector(Parameters const &params, art::ProcessingFrame const &)
unsigned int const fMinTracks
Minimum selected tracks for event selection.
static constexpr double NoMinTime
Count_t passed() const
Returns the number of events which &quot;passed&quot;.
Definition: PassCounter.h:54
unsigned int selectTracks(art::Assns< anab::T0, recob::PFParticle > const &timeTracks, std::vector< art::Ptr< recob::PFParticle >> &selectedTracks) const
Adds to selectedTracks qualifying tracks from timeTracks.
Class counting pass/fail events.
Definition: PassCounter.h:15
static constexpr unsigned int NoMaxTracks
art::SharedFilter::Table< Config > Parameters
Class counting pass/fail events.
process_name opdaq physics producers generator physics producers generator physics producers generator physics producers generator physics producers generator physics producers generator physics producers generator physics producers generator T0
Definition: gen_protons.fcl:45