All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
TriggerEfficiencyPlotsBase.cxx
Go to the documentation of this file.
1 /**
2  * @file icaruscode/PMT/Trigger/TriggerEfficiencyPlotsBase.cxx
3  * @brief Base class for _art_modules plotting trigger efficiencies.
4  * @author Gianluca Petrillo (petrillo@slac.stanford.edu)
5  * @date March 30, 2020
6  * @see icaruscode/PMT/Trigger/TriggerEfficiencyPlotsBase.h
7  */
8 
9 // library header
11 
12 
13 // ICARUS libraries
15 #include "icaruscode/PMT/Trigger/Utilities/TriggerDataUtils.h" // FillTriggerGates()
17 #include "icaruscode/Utilities/DetectorClocksHelpers.h" // makeDetTimings()
19 #include "icarusalg/Utilities/ROOTutils.h" // util::ROOT
20 #include "icarusalg/Utilities/FHiCLutils.h" // util::fhicl::getOptionalValue()
21 
22 // LArSoft libraries
26 #include "larcore/CoreUtils/ServiceUtil.h" // lar::providerFrom()
27 #include "lardataalg/DetectorInfo/DetectorTimingTypes.h" // optical_time_ticks..
32 #include "larcorealg/CoreUtils/values.h" // util::const_values()
34 #include "larcorealg/CoreUtils/StdUtils.h" // util::to_string()
37 // #include "larcorealg/CoreUtils/DebugUtils.h" // lar::debug::::static_assert_on<>
38 
39 // nutools libraries
40 #include "nusimdata/SimulationBase/MCTruth.h"
41 #include "nusimdata/SimulationBase/MCNeutrino.h" // also simb::kCC, ...
42 #include "nusimdata/SimulationBase/MCParticle.h"
43 
44 // framework libraries
45 #include "art_root_io/TFileService.h"
46 #include "art/Framework/Services/Registry/ServiceHandle.h"
47 #include "art/Framework/Core/ConsumesCollector.h"
48 #include "art/Framework/Principal/Event.h"
49 #include "art/Framework/Principal/Handle.h"
50 #include "canvas/Persistency/Common/Assns.h"
51 #include "canvas/Utilities/InputTag.h"
52 #include "messagefacility/MessageLogger/MessageLogger.h" // mf namespace
53 #include "cetlib_except/exception.h"
54 
55 // ROOT libraries
56 #include "TEfficiency.h"
57 #include "TGraph.h"
58 #include "TH1F.h"
59 #include "TH1I.h"
60 #include "TH2F.h"
61 #include "TTree.h"
62 
63 // C/C++ standard libraries
64 #include <ostream>
65 #include <vector>
66 #include <array>
67 #include <string>
68 #include <memory> // std::make_unique()
69 #include <utility> // std::pair<>, std::move()
70 #include <cassert>
71 
72 
73 //------------------------------------------------------------------------------
74 namespace {
75 
76  /// Returns a sorted copy of the specified collection.
77  template <typename Coll>
78  Coll sortCollection(Coll cont) {
79  std::sort(cont.begin(), cont.end());
80  return cont;
81  }
82 
83 } // local namespace
84 
85 //------------------------------------------------------------------------------
86 //--- icarus::trigger::details::TriggerPassCounters
87 //------------------------------------------------------------------------------
89  (Threshold_t const& threshold, std::string const& patternName) -> IndexPair_t
90 {
91 
92  std::size_t thrIndex = thresholdIndex(threshold);
93  if (thrIndex == NoIndex) thrIndex = registerThreshold(threshold);
94 
95  std::size_t patIndex = patternIndex(patternName);
96  if (patIndex == NoIndex) patIndex = registerPattern(patternName);
97 
98  if (thrIndex >= nThresholds())
99  fCounters.resize(thrIndex + 1U, std::vector<Counter_t>{ nPatterns() });
100 
101  if (patIndex >= nPatterns()) {
102  for (auto& thrCounters: fCounters) thrCounters.resize(patIndex + 1U);
103  }
104 
105  assert(hasThreshold(thrIndex));
106  assert(hasPattern(patIndex));
107  return { thrIndex, patIndex };
108 
109 } // icarus::trigger::details::TriggerPassCounters::create()
110 
111 
112 //------------------------------------------------------------------------------
114  { return fCounters.size(); }
115 
116 
117 //------------------------------------------------------------------------------
119  { return fCounters.empty()? 0U: fCounters.front().size(); }
120 
121 
122 //------------------------------------------------------------------------------
124  (Threshold_t const& threshold)
125 {
126  assert(!hasThreshold(threshold));
127 
128  std::size_t const newIndex = nThresholds();
129  fThresholdIndex[threshold] = newIndex;
130 
131  assert(hasThreshold(threshold));
132  return newIndex;
133 } // icarus::trigger::details::TriggerPassCounters::registerThreshold()
134 
135 
136 //------------------------------------------------------------------------------
138  (std::string const& name)
139 {
140  assert(!hasPattern(name));
141 
142  std::size_t const newIndex = nPatterns();
143  fPatternIndex[name] = newIndex;
144 
145  assert(hasPattern(name));
146  return newIndex;
147 } // icarus::trigger::details::TriggerPassCounters::registerPattern()
148 
149 
150 //------------------------------------------------------------------------------
152  (Threshold_t const& threshold, std::string const& patternName) const
153  -> Counter_t const&
154 {
155  IndexPair_t const index
156  { thresholdIndex(threshold), patternIndex(patternName) };
157  if (index.first == NoIndex)
158  throw std::out_of_range{ threshold };
159  if (index.second == NoIndex)
160  throw std::out_of_range{ patternName };
161  return counter(index);
162 } // icarus::trigger::details::TriggerPassCounters::counter(Threshold_t, string)
163 
164 
165 //------------------------------------------------------------------------------
167  (std::size_t threshold, std::string const& patternName) const
168  -> Counter_t const&
169 {
170  IndexPair_t const index { threshold, patternIndex(patternName) };
171  if (index.first == NoIndex)
172  throw std::out_of_range{ std::to_string(threshold) };
173  if (index.second == NoIndex)
174  throw std::out_of_range{ patternName };
175  return counter(index);
176 } // icarus::trigger::details::TriggerPassCounters::counter(size_t, string)
177 
178 
179 //------------------------------------------------------------------------------
181  (Threshold_t const& threshold, std::size_t pattern) const
182  -> Counter_t const&
183 {
184  IndexPair_t const index
185  { thresholdIndex(threshold), pattern };
186  if (index.first == NoIndex)
187  throw std::out_of_range{ threshold };
188  if (index.second == NoIndex)
189  throw std::out_of_range{ std::to_string(pattern) };
190  return counter(index);
191 } // icarus::trigger::details::TriggerPassCounters::counter(Threshold_t, size_t)
192 
193 
194 //------------------------------------------------------------------------------
196  (std::size_t threshold, std::size_t pattern) const
197  -> Counter_t const&
198 {
199  IndexPair_t const index { threshold, pattern };
200  if (index.first == NoIndex)
201  throw std::out_of_range{ std::to_string(threshold) };
202  if (index.second == NoIndex)
203  throw std::out_of_range{ std::to_string(pattern) };
204  return counter(index);
205 } // icarus::trigger::details::TriggerPassCounters::counter(size_t, size_t)
206 
207 
208 //------------------------------------------------------------------------------
210  (IndexPair_t indices) const -> Counter_t const&
211 {
212  if (!hasThreshold(indices.first))
213  throw std::out_of_range{ std::to_string(indices.first) };
214 
215  auto const& thrCounters = fCounters[indices.first];
216  if (indices.second >= thrCounters.size())
217  throw std::out_of_range{ std::to_string(indices.second) };
218 
219  return thrCounters[indices.second];
220 } // icarus::trigger::details::TriggerPassCounters::counter(IndexPair_t)
221 
222 
223 //------------------------------------------------------------------------------
225  (Threshold_t const& threshold, std::string const& patternName) -> Counter_t&
226 {
227  IndexPair_t const index
228  { thresholdIndex(threshold), patternIndex(patternName) };
229  if (index.first == NoIndex)
230  throw std::out_of_range{ threshold };
231  if (index.second == NoIndex)
232  throw std::out_of_range{ patternName };
233  return counter(index);
234 } // icarus::trigger::details::TriggerPassCounters::counter(Threshold_t, string)
235 
236 
237 //------------------------------------------------------------------------------
239  (std::size_t threshold, std::string const& patternName) -> Counter_t&
240 {
241  IndexPair_t const index { threshold, patternIndex(patternName) };
242  if (index.first == NoIndex)
243  throw std::out_of_range{ std::to_string(threshold) };
244  if (index.second == NoIndex)
245  throw std::out_of_range{ patternName };
246  return counter(index);
247 } // icarus::trigger::details::TriggerPassCounters::counter(size_t, string)
248 
249 
250 //------------------------------------------------------------------------------
252  (Threshold_t const& threshold, std::size_t pattern) -> Counter_t&
253 {
254  IndexPair_t const index
255  { thresholdIndex(threshold), pattern };
256  if (index.first == NoIndex)
257  throw std::out_of_range{ threshold };
258  if (index.second == NoIndex)
259  throw std::out_of_range{ std::to_string(pattern) };
260  return counter(index);
261 } // icarus::trigger::details::TriggerPassCounters::counter(Threshold_t, size_t)
262 
263 
264 //------------------------------------------------------------------------------
266  (std::size_t threshold, std::size_t pattern) -> Counter_t&
267 {
268  IndexPair_t const index { threshold, pattern };
269  if (index.first == NoIndex)
270  throw std::out_of_range{ std::to_string(threshold) };
271  if (index.second == NoIndex)
272  throw std::out_of_range{ std::to_string(pattern) };
273  return counter(index);
274 } // icarus::trigger::details::TriggerPassCounters::counter(size_t, size_t)
275 
276 
277 //------------------------------------------------------------------------------
279  -> Counter_t&
280 {
281  if (!hasThreshold(indices.first))
282  throw std::out_of_range{ std::to_string(indices.first) };
283 
284  auto& thrCounters = fCounters[indices.first];
285  if (indices.second >= thrCounters.size())
286  throw std::out_of_range{ std::to_string(indices.second) };
287 
288  return thrCounters[indices.second];
289 } // icarus::trigger::details::TriggerPassCounters::counter(IndexPair_t)
290 
291 
292 //------------------------------------------------------------------------------
294  (Threshold_t const& threshold) const
295  { return fThresholdIndex.find(threshold) != fThresholdIndex.end(); }
296 
297 
298 //------------------------------------------------------------------------------
300  (std::size_t thresholdIndex) const
301  { return thresholdIndex < nThresholds(); }
302 
303 
304 //------------------------------------------------------------------------------
306  (std::string const& patternName) const
307  { return fPatternIndex.find(patternName) != fPatternIndex.end(); }
308 
309 
310 //------------------------------------------------------------------------------
312  (std::size_t patternIndex) const
313  { return patternIndex < nPatterns(); }
314 
315 
316 //------------------------------------------------------------------------------
318  (Threshold_t const& threshold) const
319 {
320  auto const iIndex = fThresholdIndex.find(threshold);
321  return (iIndex == fThresholdIndex.end())? NoIndex: iIndex->second;
322 } // icarus::trigger::details::TriggerPassCounters::thresholdIndex()
323 
324 
325 //------------------------------------------------------------------------------
327  (std::string const& patternName) const
328 {
329  auto const iIndex = fPatternIndex.find(patternName);
330  return (iIndex == fPatternIndex.end())? NoIndex: iIndex->second;
331 } // icarus::trigger::details::TriggerPassCounters::patternIndex()
332 
333 
334 //------------------------------------------------------------------------------
336  (std::size_t index) const -> Threshold_t const&
337 {
338  // reverse lookup: slow...
339  for (auto const& [ threshold, thrIndex ]: fThresholdIndex)
340  if (thrIndex == index) return threshold;
341  throw std::out_of_range{ std::to_string(index) };
342 } // icarus::trigger::details::TriggerPassCounters::threshold()
343 
344 
345 //------------------------------------------------------------------------------
347  (std::size_t index) const -> std::string const&
348 {
349  // reverse lookup: slow...
350  for (auto const& [ name, patIndex ]: fPatternIndex)
351  if (patIndex == index) return name;
352  throw std::out_of_range{ std::to_string(index) };
353 } // icarus::trigger::details::TriggerPassCounters::patternName()
354 
355 
356 //------------------------------------------------------------------------------
358  (std::ostream& out) const
359 {
360  out << "Triggers for " << nThresholds() << " thresholds and " << nPatterns()
361  << " patterns:";
362  for (auto const iThr: util::counter(nThresholds())) {
363 
364  assert(hasThreshold(iThr));
365  out << "\n threshold " << threshold(iThr) << " [#" << iThr << "]:";
366  unsigned int nonEmptyPatterns = 0U;
367  for (auto const iPat: util::counter(nPatterns())) {
368  assert(hasPattern(iPat));
369  auto const& counts = counter(iThr, iPat);
370  if (counts.empty()) continue;
371  out << "\n " << patternName(iPat) << " [#" << iPat << "]: "
372  << counts.passed() << " / " << counts.total();
373  ++nonEmptyPatterns;
374  } // for patterns
375  if (nonEmptyPatterns == 0) out << " no events";
376 
377  } // for threshold
378  out << "\n";
379 } // icarus::trigger::details::TriggerPassCounters::dump()
380 
381 
382 //------------------------------------------------------------------------------
383 std::ostream& icarus::trigger::details::operator<<
384  (std::ostream& out, TriggerPassCounters const& counters)
385  { counters.dump(out); return out; }
386 
387 
388 //------------------------------------------------------------------------------
389 //--- icarus::trigger::details::PlotInfoTree
390 //------------------------------------------------------------------------------
392  : TreeHolder(tree)
393 {
394 
395  this->tree().Branch("InPlots", &fInPlots);
396 
397 } // icarus::trigger::details::PlotInfoTree::PlotInfoTree()
398 
399 
400 //------------------------------------------------------------------------------
402 
403  fInPlots = static_cast<Bool_t>(inPlots);
404 
405 } // icarus::trigger::details::PlotInfoTree::assignEvent()
406 
407 
408 //------------------------------------------------------------------------------
409 /**
410  * @brief List of event categories.
411  *
412  * category name | condition
413  * -------------- | ------------------------------------------------------------
414  * `All` | any event
415  * ---Nu_mu |
416  * ---Nu_e |
417  * `NuCC` | at least one generated charged current neutrino interaction
418  * ---Nu_mu |
419  * ---Nu_e |
420  * `NuNC` | at least one generated neutral current neutrino interaction
421  * ---Nu_mu |
422  * ---Nu_e |
423  *
424  *
425  */
428 
429  PlotCategory{
430  "All"
431  },
432 
433  PlotCategory{
434  "All nu_mu", "nu_mu",
435  [](EventInfo_t const& info){ return info.hasGenerated() && info.isNu_mu(); }
436  },
437 
438  PlotCategory{
439  "All nu_e", "nu_e",
440  [](EventInfo_t const& info){ return info.hasGenerated() && info.isNu_e(); }
441  },
442 
443  PlotCategory{
444  "NuCC", "CC",
445  [](EventInfo_t const& info)
446  { return info.hasGenerated() && info.isWeakChargedCurrent(); }
447  },
448 
449  PlotCategory{
450  "NuCC_mu", "CC_mu",
451  [](EventInfo_t const& info)
452  { return info.hasGenerated() && info.isWeakChargedCurrent() && info.isNu_mu(); }
453  },
454 
455  PlotCategory{
456  "NuCC_e", "CC_e",
457  [](EventInfo_t const& info)
458  { return info.hasGenerated() && info.isWeakChargedCurrent() && info.isNu_e(); }
459  },
460 
461  PlotCategory{
462  "NuNC", "NC",
463  [](EventInfo_t const& info)
464  { return info.hasGenerated() && info.isWeakNeutralCurrent(); }
465  },
466 
467  PlotCategory{
468  "NuNC_mu", "NC_mu",
469  [](EventInfo_t const& info)
470  { return info.hasGenerated() && info.isWeakNeutralCurrent() && info.isNu_mu(); }
471  },
472 
473  PlotCategory{
474  "NuNC_e", "NC_e",
475  [](EventInfo_t const& info)
476  { return info.hasGenerated() && info.isWeakNeutralCurrent() && info.isNu_e(); }
477  },
478 
479  PlotCategory{
480  "NoActivity", "no energy deposited in active volume during beam gate",
481  [](EventInfo_t const& info)
482  {
483  using namespace util::quantities::energy_literals;
484  return info.hasDepEnergy()
485  && (info.DepositedEnergyInSpillInActiveVolume() == 0.0_GeV);
486  }
487  }
488 
489 }; // icarus::trigger::TriggerEfficiencyPlotsBase::DefaultPlotCategories[]
490 
491 
492 //------------------------------------------------------------------------------
494  (Config const& config, art::ConsumesCollector& consumer)
495  // configuration
496  : fDetectorParticleTag (config.DetectorParticleTag())
497  , fBeamGateDuration (config.BeamGateDuration())
498  , fBeamGateStart (config.BeamGateStart())
499  , fPreSpillWindow (config.PreSpillWindow())
500  , fPreSpillStart
501  (fBeamGateStart - config.PreSpillWindowGap() - fPreSpillWindow)
502  , fTriggerTimeResolution(config.TriggerTimeResolution())
503  , fPlotOnlyActiveVolume (config.PlotOnlyActiveVolume())
504  , fOnlyPlotCategories (sortCollection(config.OnlyPlotCategories()))
505  , fLogCategory (config.LogCategory())
506  // services
507  , fGeom (*lar::providerFrom<geo::Geometry>())
508  , fOutputDir (*art::ServiceHandle<art::TFileService>())
509  // cached
510  , fEventInfoExtractorMaker(
511  config.GeneratorTags(), // truthTags
512  makeEdepTag(config.EnergyDepositTags, config.EnergyDepositSummaryTag),
513  // edepTags
514  fGeom, // geom
515  nullptr, // detProps
516  nullptr, // detTimings
517  fLogCategory, // logCategory
518  consumer // consumesCollector
519  )
520  , fChannelCryostat(makeChannelCryostatMap(fGeom))
521 {
522  //
523  // more complex parameter parsing
524  //
525  if (config.EventDetailsLogCategory(fLogEventDetails)) {
526  // the parameter is somehow set, so fLogEventDetails won't be empty;
527  // but if the parameter value is specified empty, we set it to fLogCategory
528  if (fLogEventDetails.empty()) fLogEventDetails = fLogCategory;
529  } // if EventDetailsLogCategory is specified
530 
531  std::string const discrModuleLabel = config.TriggerGatesTag();
532  for (std::string const& threshold: config.Thresholds())
533  fADCthresholds[threshold] = art::InputTag{ discrModuleLabel, threshold };
534 
535 
536  if (config.EventTreeName.hasValue()) {
537 
538  std::string treeName;
539  config.EventTreeName(treeName);
540 
541  fIDTree = std::make_unique<details::EventIDTree>
542  (*(fOutputDir.make<TTree>(treeName.c_str(), "Event information")));
543  fEventTree = std::make_unique<details::EventInfoTree>
544  (fIDTree->tree(), useGen(), useEDep());
545  fPlotTree = std::make_unique<details::PlotInfoTree>(fIDTree->tree());
546 
547  } // if make tree
548 
549  //
550  // input data declaration
551  //
552  using icarus::trigger::OpticalTriggerGateData_t; // for convenience
553 
554  // trigger primitives
555  for (art::InputTag const& inputDataTag: util::const_values(fADCthresholds)) {
556  consumer.consumes<std::vector<OpticalTriggerGateData_t>>(inputDataTag);
557  consumer.consumes<art::Assns<OpticalTriggerGateData_t, raw::OpDetWaveform>>
558  (inputDataTag);
559  } // for
560 
561  {
562  mf::LogInfo log(fLogCategory);
563  log << "\nConfigured " << fADCthresholds.size() << " thresholds (ADC):";
564  for (auto const& [ thresholdTag, dataTag ]: fADCthresholds)
565  log << "\n * " << thresholdTag << " (from '" << dataTag.encode() << "')";
566 
567  } // local block
568 
569 
570  if (!useGen()) {
571  mf::LogVerbatim(fLogCategory)
572  << "Generation information will not be produced.";
573  }
574  if (!useEDep()) {
575  mf::LogVerbatim(fLogCategory)
576  << "Energy deposition information will not be produced.";
577  }
578 
579 } // icarus::trigger::TriggerEfficiencyPlots::TriggerEfficiencyPlots()
580 
581 
582 //------------------------------------------------------------------------------
584  (art::Event const& event)
585 {
586 
587  /*
588  * 1. find out the features of the event and the categories it belongs to
589  * 2. for each threshold:
590  * 1. read the trigger primitives
591  * 2. pick the plots to be filled
592  * 3. combine the trigger primitives, apply the beam gate,
593  * generate the trigger response, add the response to all the plots
594  * (delegated)
595  *
596  */
597 
598  ++nEvents;
599 
600  //
601  // 1. find out the features of the event and the categories it belongs to
602  //
603 
604  auto const [ detTimings, beamGate, preSpillWindow ] = makeGatePack(&event);
605 
606  if (auto oldGate = fBeamGateChangeCheck(beamGate); oldGate) {
607  mf::LogDebug(fLogCategory)
608  << "Beam gate has changed from " << oldGate->asOptTickRange()
609  << " to " << beamGate.asOptTickRange() << " (optical tick)!";
610  }
611 
612  EventInfo_t const eventInfo = fEventInfoExtractorMaker
613  (beamGate.asSimulationRange(), preSpillWindow.asSimulationRange())(event);
614 
615 
616  bool const bPlot = shouldPlotEvent(eventInfo);
617  if (bPlot) ++nPlottedEvents;
618 
619  if (fIDTree) fIDTree->assignID(event.id());
620  if (fPlotTree) fPlotTree->assign(bPlot);
621  if (fEventTree) fEventTree->assignEvent(eventInfo);
622 
623  std::vector<std::string> selectedPlotCategories
624  = selectPlotCategories(eventInfo, fPlotCategories);
625  {
626  mf::LogTrace log(fLogCategory);
627  log
628  << "Event " << event.id() << " falls in " << selectedPlotCategories.size()
629  << " categories:"
630  ;
631  for (std::string const& name: selectedPlotCategories)
632  log << " \"" << name << "\"";
633  // print the information on the event
634  } // local block
635  if (!fLogEventDetails.empty()) {
636  mf::LogTrace(fLogEventDetails)
637  << "Event " << event.id() << ": " << eventInfo;
638  }
639 
640  //
641  // 2. for each PMT threshold:
642  //
643  auto const clockData
644  = art::ServiceHandle<detinfo::DetectorClocksService const>()->DataFor(event);
645  for (auto&& [ iThr, thrPair, thrPlots ]
646  : util::enumerate(fADCthresholds, fThresholdPlots)
647  ) {
648 
649  auto const& [ thresholdTag, dataTag ] = thrPair;
650 
651  //
652  // 2.1. read the trigger primitives
653  //
654 
655  TriggerGatesPerCryostat_t const& cryoGates
656  = splitByCryostat(readTriggerGates(event, dataTag));
657 
658  //
659  // 2.2. pick the plots to be filled
660  //
661  PlotSandboxRefs_t selectedPlots;
662 
663  if (bPlot) {
664  for (std::string const& name: selectedPlotCategories)
665  selectedPlots.emplace_back(*(thrPlots.findSandbox(name)));
666  }
667 
668  //
669  // 2.3. combine the trigger primitives, apply the beam gate,
670  // generate the trigger response, add the response to all the plots
671  //
672  simulateAndPlot(
673  iThr, // settings index
674  cryoGates,
675  eventInfo,
676  clockData,
677  selectedPlots
678  );
679 
680  } // for thresholds
681 
682  //
683  // store information in output tree if any
684  //
685  if (fIDTree) fIDTree->tree().Fill();
686 
687 } // icarus::trigger::TriggerEfficiencyPlotsBase::process()
688 
689 
690 //------------------------------------------------------------------------------
692 
693  mf::LogInfo log(fLogCategory);
694  log << nPlottedEvents << "/" << nEvents << " events plotted.";
695 
696  log << "\n" << fPassCounters;
697 
698 } // icarus::trigger::TriggerEfficiencyPlotsBase::printSummary()
699 
700 
701 //------------------------------------------------------------------------------
703  (PlotCategories_t categories, std::vector<SettingsInfo_t> const& settings)
704 {
705  using namespace std::string_literals;
706 
707  auto const beamGate = icarus::trigger::makeBeamGateStruct
708  (icarus::ns::util::makeDetTimings(), fBeamGateDuration, fBeamGateStart);
709 
710  fBeamGateChangeCheck(beamGate);
711 
712  if (fOnlyPlotCategories.empty()) fPlotCategories = std::move(categories);
713  else {
714  auto const plotThisCategory = [this](std::string const& name)
715  {
716  return std::binary_search
717  (fOnlyPlotCategories.begin(), fOnlyPlotCategories.end(), name);
718  };
719 
720  fPlotCategories.clear();
721  for (auto&& plotCategory: categories) {
722  if (!plotThisCategory(plotCategory.name())) continue;
723  fPlotCategories.push_back(std::move(plotCategory));
724  } // for
725  }
726 
727 
728  {
729  mf::LogTrace(fLogCategory)
730  << "Beam gate:"
731  << "\n - electronics time: " << beamGate.asGate()
732  << "\n - simulation time: " << beamGate.asSimulationRange()
733  << "\n - optical ticks: " << beamGate.asOptTickRange()
734  ;
735 
736  mf::LogInfo log(fLogCategory);
737  log << "\nConfigured " << fADCthresholds.size() << " thresholds (ADC):";
738  for (auto const& [ thresholdTag, dataTag ]: fADCthresholds)
739  log << "\n * " << thresholdTag << " (from '" << dataTag.encode() << "')";
740  log << "\nBeam gate for plots is " << beamGate.asSimulationRange();
741 
742  log << "\nConfigured " << fPlotCategories.size() << " plot categories"
743  << (fPlotCategories.empty()? '.': ':');
744  for (auto const& plotCategory: fPlotCategories) {
745  log << "\n ['" << plotCategory.name() << "'] "
746  << plotCategory.description();
747  } // for
748 
749  } // local block
750 
751 
752  for (std::string const& thresholdTag: util::get_elements<0U>(fADCthresholds))
753  {
754  // create a plot sandbox inside `fOutputDir` with a name/prefix `Thr###`
756  { fOutputDir, "Thr"s + thresholdTag, "(thr: "s + thresholdTag + ")"s };
757 
758  // create a subbox for each plot category
759  for (PlotCategory const& category: fPlotCategories) {
760  PlotSandbox& plots = thrPlots.addSubSandbox(
761  category.name(),
762  category.description()
763  );
764 
765  initializePlotSet(plots, settings);
766  } // for plot category
767  fThresholdPlots.push_back(std::move(thrPlots));
768  } // for thresholds
769 
770  mf::LogTrace log(fLogCategory);
771  log << "Created " << fThresholdPlots.size() << " plot boxes:\n";
772  for (auto const& box: fThresholdPlots) {
773  box.dump(log, " ");
774  } // for
775 
776 } // icarus::trigger::TriggerEfficiencyPlotsBase::initializePlots()
777 
778 
779 //------------------------------------------------------------------------------
781  (PlotSandbox& plots, std::vector<SettingsInfo_t> const& settings) const
782 {
783 
784  //
785  // Selection-related plots
786  //
787  initializeEventPlots(plots);
788 
789  initializePMTplots(plots);
790 
791  //
792  // Plots per trigger setting, split in triggering and not triggering events;
793  // the plot set is the same as the "global" one.
794  //
795  using SS_t = std::pair<std::string, std::string>;
796  std::array<SS_t, 2U> const classes {
797  SS_t{ "triggering", "triggering events" },
798  SS_t{ "nontriggering", "non-triggering events" }
799  };
800  for (auto const& settingsDesc: settings) {
801 
802  // this defines a specific trigger, with its thresholds and settings
803  PlotSandbox& reqBox
804  = plots.addSubSandbox(settingsDesc.tag, settingsDesc.description);
805 
806  initializeEfficiencyPerTriggerPlots(reqBox);
807 
808  for (auto const& [ name, desc ]: classes) {
809 
810  PlotSandbox& box = reqBox.addSubSandbox(name, desc);
811 
812  initializeEventPlots(box);
813 
814  initializePMTplots(box);
815 
816  } // for triggering requirement
817  } // for triggering classes
818 
819 
820 } // icarus::trigger::TriggerEfficiencyPlotsBase::initializePlotSet()
821 
822 
823 //------------------------------------------------------------------------------
824 void
826  (PlotSandbox& plots) const
827 {
828 
829  auto const [ detTimings, beamGate, preSpillWindow ] = makeGatePack();
830 
831  detinfo::timescales::optical_time_ticks const triggerResolutionTicks
832  { detTimings.toOpticalTicks(fTriggerTimeResolution) };
833 
834  auto const PreSpillDuration = preSpillWindow.asSimulationRange().duration();
835 
836  //
837  // Triggering efficiency vs. something else
838  //
839  if (useEDep()) {
840  plots.make<TEfficiency>(
841  "EffVsEnergyInSpill",
842  "Efficiency of triggering vs. energy deposited in spill"
843  ";energy deposited in spill [ GeV ]"
844  ";trigger efficiency [ / 50 MeV ]",
845  120, 0.0, 6.0 // 6 GeV should be enough for a MIP crossing 20 m of detector
846  );
847 
848  plots.make<TEfficiency>(
849  "EffVsEnergyInSpillActive",
850  "Efficiency of triggering vs. energy deposited in active volume"
851  ";energy deposited in active volume in spill [ GeV ]"
852  ";trigger efficiency [ / 50 MeV ]",
853  120, 0.0, 6.0 // 6 GeV should be enough for a MIP crossing 20 m of detector
854  );
855 
856  plots.make<TEfficiency>(
857  "EffVsEnergyInPreSpill",
858  (
859  "Efficiency of triggering vs. energy deposited in pre-spill ("
860  + to_string(PreSpillDuration) + ")"
861  ";energy deposited in pre-spill [ GeV ]"
862  ";trigger efficiency [ / 100 MeV ]"
863  ).c_str(),
864  120, 0.0, 12.0
865  );
866 
867  plots.make<TEfficiency>(
868  "EffVsEnergyInPreSpillActive",
869  (
870  "Efficiency of triggering vs. energy deposited in active volume"
871  " (pre-spill: " + to_string(PreSpillDuration) + ")"
872  ";energy deposited in active volume in pre-spill [ GeV ]"
873  ";trigger efficiency [ / 100 MeV ]"
874  ).c_str(),
875  120, 0.0, 12.0 // 6 GeV should be enough for a MIP crossing 20 m of detector
876  );
877  } // if plots with deposited energy
878 
879  if (useGen()) {
880  plots.make<TEfficiency>(
881  "EffVsNeutrinoEnergy",
882  "Efficiency of triggering vs. neutrino energy"
883  ";neutrino true energy [ GeV ]"
884  ";trigger efficiency [ / 50 MeV ]",
885  120, 0.0, 6.0 // 6 GeV is not that much for NuMI, but we should be ok
886  );
887 
888  plots.make<TEfficiency>(
889  "EffVsLeptonEnergy",
890  "Efficiency of triggering vs. outgoing lepton energy"
891  ";final state lepton true energy [ GeV ]"
892  ";trigger efficiency [ / 50 MeV ]",
893  120, 0.0, 6.0
894  );
895  } // if plots with generated info
896 
897  auto const& beamGateOpt = beamGate.asOptTickRange();
898  plots.make<TH1F>(
899  "TriggerTick",
900  "Trigger time tick"
901  ";optical time tick [ /" + util::to_string(triggerResolutionTicks) + " ]",
902  beamGateOpt.duration() / triggerResolutionTicks,
903  beamGateOpt.start().value(), beamGateOpt.end().value()
904  );
905 
906  // plots will be relative to the beam gate:
907  constexpr util::quantities::intervals::microseconds beamPlotPadding { 4_us };
908  icarus::ns::util::BinningSpecs const beamGateBinning = alignBinningTo(
910  (
911  std::min(
912  preSpillWindow.asElectronicsTimeRange().start(),
913  beamGate.asElectronicsTimeRange().start()
914  )
915  - beamGate.asElectronicsTimeRange().start()
916  - beamPlotPadding
917  ).value(),
918  (std::max(
919  preSpillWindow.asElectronicsTimeRange().end()
920  - beamGate.asElectronicsTimeRange().start(),
921  beamGate.duration()
922  ) + beamPlotPadding).value(),
923  fTriggerTimeResolution.value()
924  },
925  0.0
926  );
927 
928  plots.make<TH1F>(
929  "OpeningTimes",
930  "Times at which trigger logic was satisfied"
931  ";trigger time (relative to beam gate opening) [ us ]"
932  ";opened trigger gates",
933  beamGateBinning.nBins(), beamGateBinning.lower(), beamGateBinning.upper()
934  );
935 
936  plots.make<TH1F>(
937  "TriggerTime",
938  "Time of the trigger"
939  ";trigger time (relative to beam gate opening) [ us ]"
940  ";opened trigger gates",
941  beamGateBinning.nBins(), beamGateBinning.lower(), beamGateBinning.upper()
942  );
943 
944  //
945  // plots independent of the trigger primitive requirements
946  //
947 
948 } // icarus::trigger::TriggerEfficiencyPlotsBase::initializeEfficiencyPerTriggerPlots()
949 
950 
951 //------------------------------------------------------------------------------
953  (PlotSandbox& plots) const
954 {
955 
956  auto const [ detTimings, beamGate, preSpillWindow ] = makeGatePack();
957 
958  auto const BeamGateDuration = beamGate.asSimulationRange().duration();
959  auto const PreSpillDuration = preSpillWindow.asSimulationRange().duration();
960 
961  //
962  // Selection-related plots
963  //
964  if (useGen()) {
965  plots.make<TH1F>(
966  "NeutrinoEnergy",
967  "True Neutrino Energy"
968  ";neutrino energy [GeV]"
969  ";events",
970  120, 0.0, 6.0 // GeV
971  );
972  }
973 
974  if (useEDep()) {
975  plots.make<TH1F>(
976  "EnergyInSpill",
977  "Energy deposited during the beam gate opening"
978  ";energy deposited in spill [ GeV ]"
979  ";events [ / 50 MeV ]",
980  120, 0.0, 6.0 // 6 GeV should be enough for a MIP crossing 20 m of detector
981  );
982  plots.make<TH1F>(
983  "EnergyInPreSpill",
984  (
985  "Energy deposited during the pre-spill window ("
986  + to_string(PreSpillDuration) + ")"
987  ";energy deposited in pre-spill [ GeV ]"
988  ";events [ / 100 MeV ]"
989  ).c_str(),
990  120, 0.0, 12.0
991  );
992  plots.make<TH1F>(
993  "EnergyInSpillActive",
994  "Energy deposited during the beam gate opening in active volume"
995  ";energy deposited in active volume in spill [ GeV ]"
996  ";events [ / 50 MeV ]",
997  120, 0.0, 6.0 // 6 GeV should be enough for a MIP crossing 20 m of detector
998  );
999  plots.make<TH1F>(
1000  "EnergyInPreSpillActive",
1001  (
1002  "Energy deposited in active volume during the pre-spill window ("
1003  + to_string(PreSpillDuration) + ")"
1004  ";energy deposited in active volume in pre-spill [ GeV ]"
1005  ";events [ / 100 MeV ]"
1006  ).c_str(),
1007  120, 0.0, 12.0
1008  );
1009  plots.make<TH2F>(
1010  "EnergyInPreSpillVsSpillActive",
1011  (
1012  "Energy deposited in active volume"
1013  ";energy in spill window (" + to_string(BeamGateDuration) + ") [ GeV ]"
1014  ";energy in pre-spill window (" + to_string(PreSpillDuration)
1015  + ") [ GeV ]"
1016  ).c_str(),
1017  120, 0.0, 6.0, 120, 0.0, 12.0
1018  );
1019  } // if use energy deposition
1020 
1021  if (useGen()) {
1022  plots.make<TH1I>(
1023  "InteractionType",
1024  "Interaction type"
1025  ";Interaction Type"
1026  ";events",
1027  200, 999.5, 1199.5
1028  );
1029  plots.make<TH1F>(
1030  "LeptonEnergy",
1031  "Energy of outgoing lepton"
1032  ";deposited energy [ GeV ]"
1033  ";events [ / 50 MeV ]",
1034  120, 0.0, 6.0
1035  );
1036  plots.make<TH2F>(
1037  "InteractionVertexYZ",
1038  "Vertex of triggered interaction"
1039  ";beam direction (z) [ / 20 cm ]"
1040  ";vertical direction (y) [ / 5 cm ]",
1041  120, -1200., +1200.,
1042  100, -250., +250.
1043  );
1044 
1045  plots.make<TH2F>(
1046  "InteractionTypeNeutrinoEnergy",
1047  "Interaction Type vs Neutrino Energy"
1048  ";InteractionType"
1049  ";Neutrino Energy",
1050  200,999.5,1199.5,
1051  120, 0.0, 6.0
1052  );
1053 
1054  } // if generated information
1055 
1056 } // icarus::trigger::TriggerEfficiencyPlotsBase::initializeEventPlots()
1057 
1058 
1059 //------------------------------------------------------------------------------
1061  (PlotSandbox& plots) const
1062 {
1063 
1064  unsigned int const nOpChannels = fGeom.NOpChannels();
1065 
1066  //
1067  // plots independent of the trigger primitive requirements
1068  //
1069  plots.make<TH1I>(
1070  "ActivePMT",
1071  "PMT channels contributing to the trigger"
1072  ";channel with opened trigger gate"
1073  ";events",
1074  nOpChannels, // large number, zoom in presentations!
1075  0.0, static_cast<double>(nOpChannels)
1076  );
1077 
1078 } // icarus::trigger::TriggerEfficiencyPlotsBase::initializePMTplots()
1079 
1080 
1081 //------------------------------------------------------------------------------
1083  (EventInfo_t const& eventInfo) const
1084 {
1085  if (fPlotOnlyActiveVolume
1086  && eventInfo.hasVertex() && !eventInfo.isInActiveVolume())
1087  {
1088  return false;
1089  }
1090 
1091  return true;
1092 } // icarus::trigger::TriggerEfficiencyPlotsBase::shouldPlotEvent()
1093 
1094 
1095 //------------------------------------------------------------------------------
1097  (EventInfo_t const& eventInfo, PlotSandbox const& plots) const
1098 {
1099 
1100  using namespace std::string_literals;
1101 
1102  HistGetter const getTrig { plots };
1103 
1104  if (useEDep()) {
1105  assert(eventInfo.hasDepEnergy());
1106  getTrig.Hist("EnergyInSpill"s).Fill(double(eventInfo.DepositedEnergyInSpill()));
1107  getTrig.Hist("EnergyInSpillActive"s).Fill(double(eventInfo.DepositedEnergyInSpillInActiveVolume()));
1108  getTrig.Hist("EnergyInPreSpill"s)
1109  .Fill(double(eventInfo.DepositedEnergyInPreSpill()));
1110  getTrig.Hist("EnergyInPreSpillActive"s)
1111  .Fill(double(eventInfo.DepositedEnergyInPreSpillInActiveVolume()));
1112  getTrig.Hist2D("EnergyInPreSpillVsSpillActive"s).Fill(
1113  double(eventInfo.DepositedEnergyInSpillInActiveVolume()),
1114  double(eventInfo.DepositedEnergyInPreSpillInActiveVolume())
1115  );
1116  }
1117  if (useGen()) {
1118  if (eventInfo.isNeutrino()) {
1119  assert(eventInfo.hasGenerated());
1120  getTrig.Hist("NeutrinoEnergy"s).Fill(double(eventInfo.NeutrinoEnergy()));
1121  getTrig.Hist("InteractionType"s).Fill(eventInfo.InteractionType());
1122  getTrig.Hist("LeptonEnergy"s).Fill(double(eventInfo.LeptonEnergy()));
1123  getTrig.Hist("InteractionTypeNeutrinoEnergy"s).Fill(double(eventInfo.InteractionType()), double(eventInfo.NeutrinoEnergy()));
1124  } // if neutrino event
1125  TH2& vertexHist = getTrig.Hist2D("InteractionVertexYZ"s);
1126  for (auto const& point: eventInfo.Vertices())
1127  vertexHist.Fill(point.Z(), point.Y());
1128  } // if use generated information
1129 
1130 } // icarus::trigger::TriggerEfficiencyPlotsBase::fillEventPlots()
1131 
1132 
1133 //------------------------------------------------------------------------------
1135  (PMTInfo_t const& PMTinfo, PlotSandbox const& plots) const
1136 {
1137 
1138  using namespace std::string_literals;
1139 
1140  HistGetter const getTrig { plots };
1141 
1142  auto& activePMThist = getTrig.Hist("ActivePMT"s);
1143  for (raw::Channel_t const channel: PMTinfo.activeChannels())
1144  activePMThist.Fill(channel);
1145 
1146 } // icarus::trigger::TriggerEfficiencyPlotsBase::fillPMTplots()
1147 
1148 
1149 //------------------------------------------------------------------------------
1151  EventInfo_t const& eventInfo,
1152  TriggerInfo_t const& triggerInfo,
1153  PlotSandbox const& plots
1154 ) const {
1155 
1156  using namespace std::string_literals;
1158 
1160 
1161  HistGetter const getTrigEff { plots };
1162 
1163  bool const fired = triggerInfo.fired();
1164 
1165  // efficiency plots
1166  if (useEDep()) {
1167  getTrigEff.Eff("EffVsEnergyInSpill"s).Fill
1168  (fired, double(eventInfo.DepositedEnergyInSpill()));
1169  getTrigEff.Eff("EffVsEnergyInPreSpill"s).Fill
1170  (fired, double(eventInfo.DepositedEnergyInPreSpill()));
1171  getTrigEff.Eff("EffVsEnergyInSpillActive"s).Fill
1172  (fired, double(eventInfo.DepositedEnergyInSpillInActiveVolume()));
1173  getTrigEff.Eff("EffVsEnergyInPreSpillActive"s).Fill
1174  (fired, double(eventInfo.DepositedEnergyInPreSpillInActiveVolume()));
1175  } // if use energy deposits
1176  if (useGen()) {
1177  if (eventInfo.isNeutrino()) {
1178  getTrigEff.Eff("EffVsNeutrinoEnergy"s).Fill
1179  (fired, double(eventInfo.NeutrinoEnergy()));
1180  getTrigEff.Eff("EffVsLeptonEnergy"s).Fill
1181  (fired, double(eventInfo.LeptonEnergy()));
1182  }
1183  } // if use generated information
1184 
1185  if (fired) {
1186  detinfo::timescales::electronics_time const beamGateTime
1187  = detTimings.BeamGateTime();
1188 
1189  getTrigEff.Hist("TriggerTick"s).Fill(triggerInfo.atTick().value());
1190 
1191  // converts the tick in the argument into electronics time:
1192  auto openingTime = [&detTimings](OpeningInfo_t const& info)
1193  { return detTimings.toElectronicsTime(info.tick); };
1194 
1195  getTrigEff.Hist("TriggerTime"s).Fill
1196  ((openingTime(triggerInfo.main()) - beamGateTime).value());
1197 
1198  std::vector<OpeningInfo_t> const& allTriggerOpenings = triggerInfo.all();
1199 
1200  for (OpeningInfo_t const& opening : allTriggerOpenings) {
1201  getTrigEff.Hist("OpeningTimes"s).Fill
1202  ((openingTime(opening) - beamGateTime).value());
1203  } // for all trigger openings
1204 
1205  } // if fired
1206 
1207 } // icarus::trigger::TriggerEfficiencyPlotsBase::fillEfficiencyPlots()
1208 
1209 
1210 //------------------------------------------------------------------------------
1212  EventInfo_t const& eventInfo,
1213  PMTInfo_t const& PMTinfo,
1214  TriggerInfo_t const& triggerInfo,
1215  PlotSandbox const& plots
1216 ) const {
1217 
1218  fillEfficiencyPlots(eventInfo, triggerInfo, plots);
1219 
1220  // plotting split for triggering/not triggering events
1221  fillEventPlots(
1222  eventInfo,
1223  plots.demandSandbox(triggerInfo.fired()? "triggering": "nontriggering")
1224  );
1225 
1226  fillPMTplots(
1227  PMTinfo,
1228  plots.demandSandbox(triggerInfo.fired()? "triggering": "nontriggering")
1229  );
1230 
1231 } // icarus::trigger::TriggerEfficiencyPlotsBase::fillAllEfficiencyPlots()
1232 
1233 
1234 //------------------------------------------------------------------------------
1236 {
1237 
1238  for (auto& thrPlots: fThresholdPlots) deleteEmptyPlots(thrPlots);
1239 
1240 } // icarus::trigger::TriggerEfficiencyPlotsBase::deleteEmptyPlots()
1241 
1242 
1243 //------------------------------------------------------------------------------
1245  (std::string const& patternName) -> std::size_t
1246 {
1247 
1248  std::size_t patternIndex = fPassCounters.NoIndex;
1249  std::size_t iThr [[maybe_unused]] = 0U;
1250  for (std::string const& thresholdTag: util::get_elements<0U>(fADCthresholds))
1251  {
1252 
1253  auto const indices = fPassCounters.create(thresholdTag, patternName);
1254  if (patternIndex == fPassCounters.NoIndex) patternIndex = indices.second;
1255  else assert(indices.second == patternIndex);
1256 
1257  } // for thresholds
1258 
1259  return patternIndex;
1260 } // icarus::trigger::TriggerEfficiencyPlotsBase::createCountersForPattern()
1261 
1262 
1263 //------------------------------------------------------------------------------
1265  (std::size_t threshold, std::size_t pattern, bool fired)
1266  { fPassCounters(threshold, pattern).add(fired); }
1267 
1268 
1269 //------------------------------------------------------------------------------
1271  (std::size_t threshold, std::size_t pattern, TriggerInfo_t const& triggerInfo)
1272  { registerTriggerResult(threshold, pattern, triggerInfo.fired()); }
1273 
1274 
1275 //------------------------------------------------------------------------------
1277  (art::Event const* event /* = nullptr */) const -> GatePack_t
1278 {
1279  auto const detTimings = icarus::ns::util::makeDetTimings(event);
1280  return GatePack_t{
1281  detTimings,
1283  (detTimings, fBeamGateDuration, fBeamGateStart),
1285  (detTimings, fPreSpillStart, fPreSpillStart + fPreSpillWindow)
1286  };
1287 
1288 } // icarus::trigger::TriggerEfficiencyPlotsBase::makeGatePack()
1289 
1290 
1291 //------------------------------------------------------------------------------
1292 std::vector<std::string>
1294  (EventInfo_t const& info, PlotCategories_t const& categories) const
1295 {
1296  std::vector<std::string> selected;
1297 
1298  for (auto const& category: categories)
1299  if (category(info)) selected.push_back(category);
1300 
1301  return selected;
1302 
1303 } // icarus::trigger::TriggerEfficiencyPlotsBase::selectPlotCategories()
1304 
1305 
1306 //------------------------------------------------------------------------------
1308  (art::Event const& event, art::InputTag const& dataTag) const
1309  -> TriggerGates_t
1310 {
1311 
1312  using icarus::trigger::OpticalTriggerGateData_t; // for convenience
1313 
1314  // currently the associations are a waste of time memory...
1315  auto const& gates
1316  = event.getProduct<std::vector<OpticalTriggerGateData_t>>(dataTag);
1317  auto const& gateToWaveforms = event.getProduct
1318  <art::Assns<OpticalTriggerGateData_t, sbn::OpDetWaveformMeta>>(dataTag);
1319 
1320  try {
1321  return icarus::trigger::FillTriggerGates(gates, gateToWaveforms);
1322  }
1323  catch (cet::exception const& e) {
1324  throw cet::exception("TriggerEfficiencyPlots", "", e)
1325  << "Error encountered while reading data products from '"
1326  << dataTag.encode() << "'\n";
1327  }
1328 
1329 } // icarus::trigger::TriggerEfficiencyPlotsBase::readTriggerGates()
1330 
1331 
1332 //------------------------------------------------------------------------------
1335 {
1336 
1337  TriggerGatesPerCryostat_t gatesPerCryostat{ fGeom.Ncryostats() };
1338 
1339  for (auto& gate: gates) {
1340  gatesPerCryostat[fChannelCryostat.at(gate.channels().front()).Cryostat]
1341  .push_back(std::move(gate));
1342  } // for gates
1343 
1344  return gatesPerCryostat;
1345 
1346 } // icarus::trigger::TriggerEfficiencyPlotsBase::splitByCryostat()
1347 
1348 
1349 //------------------------------------------------------------------------------
1351  (TriggerGatesPerCryostat_t const& cryoGates) -> std::vector<ChannelID_t>
1352 {
1353 
1354  //
1355  // get channels contributing to gates in a fired event
1356  //
1357 
1358  std::vector<ChannelID_t> channelList;
1359  for (auto const& gates: cryoGates) {
1360  for (auto const& gate: icarus::trigger::gatesIn(gates)) {
1361 
1362  if (gate.alwaysClosed()) continue;
1363  for (auto const channel: gate.channels()) {
1364  channelList.push_back(channel);
1365  }
1366  } // for gates
1367  } // for
1368 
1369  // remove duplicates
1370  std::sort(channelList.begin(), channelList.end());
1371  auto const firstDuplicate
1372  = std::unique(channelList.begin(), channelList.end());
1373  channelList.erase(firstDuplicate, channelList.end());
1374  return channelList;
1375 
1376 } // icarus::trigger::TriggerEfficiencyPlotsBase::extractActiveChannels()
1377 
1378 
1379 //------------------------------------------------------------------------------
1381  (PlotSandbox& plots) const
1382 {
1383  TDirectory* baseDir = plots.getDirectory();
1384  if (!baseDir) return true; // no content, nothing to do
1385 
1386  // our plots first
1387  unsigned int nEntries = 0U, nDirectories = 0U, nDeleted = 0U;
1388  for (TObject* obj: *(baseDir->GetList())) {
1389 
1390  // we do not deal with directories (except for subboxes below)
1391  if (dynamic_cast<TDirectory*>(obj)) {
1392  ++nDirectories;
1393  continue;
1394  }
1395 
1396  ++nEntries;
1397 
1398  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1399  if (auto hist = dynamic_cast<TH1 const*>(obj)) {
1400  if (hist->GetEntries() > 0) continue;
1401  }
1402  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1403  else if (auto graph = dynamic_cast<TGraph const*>(obj)) {
1404  if (graph->GetN() > 0) continue;
1405  }
1406  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1407  else if (auto* eff = dynamic_cast<TEfficiency const*>(obj)) {
1408  auto const* hist = eff->GetTotalHistogram();
1409  if (hist && hist->GetEntries() > 0) continue;
1410  }
1411  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1412  else if (auto* tree = dynamic_cast<TTree const*>(obj)) {
1413  if (tree->GetEntries() > 0) continue;
1414  }
1415  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1416  // add here more supported object types
1417  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1418  else continue; // we don't delete unknown objects
1419 
1420  mf::LogTrace(fLogCategory)
1421  << "Deleting empty " << obj->IsA()->GetName() << "['" << obj->GetName()
1422  << "'] from " << plots.name();
1423  delete obj;
1424  ++nDeleted;
1425 
1426  } // for objects in directory
1427 
1428  // if we have found no more directories than the ones expected
1429  // from the subboxes and all the other entries have been deleted,
1430  // this box might be empty
1431 
1432  bool empty
1433  = (nDeleted == nEntries) && (nDirectories <= plots.nSubSandboxes());
1434 
1435  // we can't delete the sandboxes while iterating on them...
1436  std::vector<std::string> toBeDeleted;
1437  for (PlotSandbox& subbox: plots.subSandboxes()) {
1438  if (!deleteEmptyPlots(subbox)) continue;
1439  toBeDeleted.push_back(subbox.name());
1440  mf::LogTrace(fLogCategory)
1441  << "Scheduling empty " << plots.name() << "/" << toBeDeleted.back() << " for deletion";
1442  } // for subboxes
1443  if (toBeDeleted.size() != plots.nSubSandboxes()) empty = false;
1444  for (std::string const& subName: toBeDeleted) {
1445  if (!plots.deleteSubSandbox(subName)) continue;
1446  mf::LogTrace(fLogCategory)
1447  << "Deleted box " << plots.name() << "/" << subName;
1448  } // for
1449 
1450  return empty;
1451 } // icarus::trigger::TriggerEfficiencyPlotsBase::deleteEmptyPlots()
1452 
1453 
1454 //------------------------------------------------------------------------------
1456  (geo::GeometryCore const& geom) -> std::vector<geo::CryostatID>
1457 {
1458 
1459  auto const nOpChannels = geom.NOpChannels();
1460 
1461  std::vector<geo::CryostatID> channelCryostatMap(nOpChannels);
1462 
1463  for (auto const opChannel: util::counter(nOpChannels)) {
1464  if (!geom.IsValidOpChannel(opChannel)) continue;
1465  channelCryostatMap.at(opChannel)
1466  = geom.OpDetGeoFromOpChannel(opChannel).ID();
1467  } // for all channels
1468 
1469  return channelCryostatMap;
1470 
1471 } // icarus::trigger::TriggerEfficiencyPlotsBase::makeChannelCryostatMap()
1472 
1473 
1474 //------------------------------------------------------------------------------
1477  fhicl::Sequence<art::InputTag> const& EnergyDepositTags,
1478  fhicl::OptionalAtom<art::InputTag> const& EnergyDepositSummaryTag
1479 ) {
1480 
1481  if (auto summaryTag = util::fhicl::getOptionalValue(EnergyDepositSummaryTag))
1482  {
1483  return {
1485  { *summaryTag }
1486  };
1487  }
1488  else {
1489 
1490  return { EnergyDepositTags() };
1491 
1492  }
1493 
1494 } // icarus::trigger::MakeTriggerSimulationTree::makeEdepTag()
1495 
1496 
1497 //------------------------------------------------------------------------------
TriggerEfficiencyPlotsBase(Config const &config, art::ConsumesCollector &consumer)
Constructor; requires a configuration and module&#39;s consumesCollector().
bool fired() const
Returns whether the trigger fired.
bool isInActiveVolume() const
Returns whether there is an interaction within the active volume.
Definition: EventInfo_t.h:124
Obj * make(std::string const &name, std::string const &title, Args &&...args)
Creates a new ROOT object with the specified name and title.
Definition: PlotSandbox.h:701
std::pair< std::size_t, std::size_t > IndexPair_t
Definition of util::zip().
std::vector< OpeningInfo_t > const & all() const
std::size_t nThresholds() const
Returns the number of thresholds currently registered.
Utilities related to art service access.
Utilities for the conversion of trigger gate data formats.
std::optional< typename Optional::value_type > getOptionalValue(Optional const &parameter)
Returns the value of an optional parameter as std::optional.
Definition: FHiCLutils.h:188
Tracks pass rate by discrimination threshold and trigger pattern name.
static PlotCategories_t const DefaultPlotCategories
List of event categories.
Definition of util::enumerate().
Simple utility for human-friendly binning.
virtual void initializeEventPlots(PlotSandbox &plots) const
Initializes a single, trigger-independent plot set into plots.
A collection of useful beam gates. Make one with makeGatePack().
virtual void fillEventPlots(EventInfo_t const &eventInfo, PlotSandbox const &plots) const
Fills the plots (initializeEventPlots()) with info from eventInfo.
std::vector< std::string > selectPlotCategories(EventInfo_t const &info, PlotCategories_t const &categories) const
Returns the names of the plot categories event qualifies for.
std::vector< std::vector< Counter_t > > fCounters
All counters; indices: [threshold][pattern].
void printSummary() const
Prints end-of-job summaries.
GeV NeutrinoEnergy() const
Returns the neutrino energy [GeV].
Definition: EventInfo_t.h:103
decltype(auto) subSandboxes() const
Returns an object proper to iterate through all contained sand boxes.
Definition: PlotSandbox.h:369
auto gatesIn(TrackingGateColl &trackingGates)
void dump(std::ostream &out) const
Dump all the counters on the specified stream.
decltype(auto) const_values(Coll &&coll)
Range-for loop helper iterating across the constant values of the specified collection.
virtual void fillAllEfficiencyPlots(EventInfo_t const &eventInfo, PMTInfo_t const &PMTinfo, TriggerInfo_t const &triggerInfo, PlotSandbox const &plots) const
OpeningInfo_t const & main() const
Returns the full data (undefined if !fired()).
static std::vector< geo::CryostatID > makeChannelCryostatMap(geo::GeometryCore const &geom)
Fills and returns a map of cryostat ID for each optical detector channel.
Counter_t const & counter(Threshold_t const &threshold, std::string const &patternName) const
std::string const & name() const
Returns the sandbox name.
Definition: PlotSandbox.h:196
std::variant< std::vector< art::InputTag >, SimEnergyDepositSummaryInputTag, SimChannelsInputTag > EDepTags_t
Type to specify the source of energy deposition information, if any.
IndexPair_t create(Threshold_t const &threshold, std::string const &patternName)
Creates and returns a new counter.
double lower() const
Returns the value of the lower end of the first bin.
Definition: BinningSpecs.h:187
void deleteEmptyPlots()
Deletes plots with no entries, and directories which became empty.
unsigned int NOpChannels() const
Number of electronics channels for all the optical detectors.
GeV LeptonEnergy() const
Returns the lepton energy [GeV].
Definition: EventInfo_t.h:106
pure virtual base interface for detector clocks
std::size_t thresholdIndex(Threshold_t const &threshold) const
Returns the index of the specified threshold (max() if not registered).
bool hasDepEnergy() const
Returns whether generator information is available.
Definition: EventInfo_t.h:140
bool deleteSubSandbox(std::string const &name)
Deletes the subbox with the specified name and its directory.
bool hasVertex() const
Returns whether this type of event has a known vertex.
Definition: EventInfo_t.h:115
TriggerGatesPerCryostat_t splitByCryostat(TriggerGates_t &&gates) const
Moves the data in gates in a collection of gates by cryostat.
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
bool hasPattern(std::string const &patternName) const
Returns whether the specified pattern is registered.
BinningSpecs alignBinningTo(BinningSpecs const &binning, double boundary, bool extendCoverage=true)
Returns a binning shifted to align with the specified boundary.
TriggerGates_t readTriggerGates(art::Event const &event, art::InputTag const &dataTag) const
timescale_traits< OpticalTimeCategory >::tick_interval_t optical_time_ticks
fDetProps &fDetProps fDetProps &fDetProps fLogCategory
Access the description of detector geometry.
Information about the event.
std::size_t registerPattern(std::string const &name)
Registers a new pattern in the index and returns its index (unchecked).
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
Data structure holding binning information.
Definition: BinningSpecs.h:170
virtual void initializePMTplots(PlotSandbox &plots) const
virtual bool shouldPlotEvent(EventInfo_t const &eventInfo) const
Definitions of geometry vector data types.
decltype(auto) constexpr to_string(T &&obj)
ADL-aware version of std::to_string.
virtual void initializePlotSet(PlotSandbox &plots, std::vector< SettingsInfo_t > const &settings) const
Initializes full set of plots for (ADC threshold + category) into plots.
Simple functions to streamline the creation of DetectorClocksData.
void assign(bool inPlots)
Fills the information of the specified event.
counts_as<> counts
Number of ADC counts, represented by signed short int.
Definition: electronics.h:116
DirObj * getDirectory() const
Fetches the base directory of the sandbox.
Definition: PlotSandbox.h:683
constexpr std::array< std::size_t, geo::vect::dimension< Vector >)> indices()
Returns a sequence of indices valid for a vector of the specified type.
icarus::trigger::ReadoutTriggerGate< TriggerGateTick_t, TriggerGateTicks_t, raw::Channel_t > OpticalTriggerGateData_t
Type of trigger gate data serialized into art data products.
std::size_t nSubSandboxes() const
Returns the number of contained sand boxes.
Definition: PlotSandbox.h:341
A bunch of diverse utilities and futilities related to ROOT.
std::vector< PlotDef > plots
Definition: demo.h:54
GatePack_t makeGatePack(art::Event const *event=nullptr) const
Creates a GatePack_t from the specified event.
Helper data structure to store PMT activity information in the event.
Definition: PMTInfo_t.h:25
static std::vector< ChannelID_t > extractActiveChannels(TriggerGatesPerCryostat_t const &cryoGates)
Returns all channels contributing to the trigger gates.
Simple class holding a tree.
Definition: TreeHolder.h:24
PlotInfoTree(TTree &tree)
Creates the required branches and assigns addresses to them.
Description of geometry of one entire detector.
GeV DepositedEnergyInSpill() const
Returns the total energy deposited in the detector during beam [GeV].
Definition: EventInfo_t.h:146
An interval (duration, length, distance) between two quantity points.
Definition: intervals.h:114
std::vector< ChannelID_t > const & activeChannels() const
Returns the list of channels with activity above threshold.
Definition: PMTInfo_t.h:49
std::size_t patternIndex(std::string const &patternName) const
Returns the index of the specified pattern (max() if not registered).
Base class for _art_modules plotting trigger efficiencies.
virtual void fillPMTplots(PMTInfo_t const &PMTinfo, PlotSandbox const &plots) const
Fill the plots (initializePMTplots()) with info from PMTinfo.
GeV DepositedEnergyInSpillInActiveVolume() const
Returns the energy deposited in the active volume during the beam [GeV].
Definition: EventInfo_t.h:156
double upper() const
Returns the value of the upper end of the last bin.
Definition: BinningSpecs.h:190
std::size_t registerThreshold(Threshold_t const &threshold)
Registers a new threshold in the index and returns its index (unchecked).
bool hasGenerated() const
Returns whether generator information is available.
Definition: EventInfo_t.h:66
GeV DepositedEnergyInPreSpillInActiveVolume() const
Definition: EventInfo_t.h:161
void process(art::Event const &event)
Fills the plots. Also extracts the information to fill them with.
optical_tick atTick() const
Returns the time of the trigger (undefined if !fired()).
std::size_t createCountersForPattern(std::string const &patternName)
Creates counters for all the thresholds of the specified trigger.
bool isNeutrino() const
Returns whether the event is generated as a neutrino interaction.
Definition: EventInfo_t.h:92
BeamGateStruct makeBeamGateStruct(detinfo::DetectorTimings const &detTimings, util::quantities::intervals::microseconds duration, util::quantities::intervals::microseconds delay=util::quantities::intervals::microseconds{0.0})
Creates a BeamGateStruct object of specified duration and start.
std::string to_string(WindowPattern const &pattern)
then echo File list $list not found else cat $list while read file do echo $file sed s
Definition: file_to_url.sh:60
contains information for a single step in the detector simulation
Data types for detinfo::DetectorTimings.
Functions pulling in STL customization if available.
Class to create an object representing a beam gate.
SandboxType & addSubSandbox(std::string const &name, std::string const &desc, Args &&...args)
Creates a new sandbox contained in this one.
Definition: PlotSandbox.h:723
do i e
static auto & demandSandbox(SandboxType &sandbox, std::string const &name)
Helper function for demandSandbox() implementations.
Definition: PlotSandbox.h:750
std::vector< icarus::trigger::TrackedOpticalTriggerGate< OpDetInfo > > FillTriggerGates(std::vector< icarus::trigger::OpticalTriggerGateData_t > const &gates, art::Assns< icarus::trigger::OpticalTriggerGateData_t, OpDetInfo > const &gateToWaveformInfo)
Creates a gate object out of trigger gate data products.
Selected information about the event.
Definition: EventInfo_t.h:45
Threshold_t const & threshold(std::size_t index) const
void registerTriggerResult(std::size_t threshold, std::size_t pattern, bool fired)
Registers the outcome of the specified trigger.
std::size_t nPatterns() const
Returns the number of patterns currently registered.
then echo fcl name
Simple helper functions to deal with FHiCL.
geo::OpDetID const & ID() const
Returns the geometry ID of this optical detector.
Definition: OpDetGeo.h:72
std::vector< std::reference_wrapper< PlotSandbox const >> PlotSandboxRefs_t
List of references to plot sandboxes.
temporary value
fDetProps &fDetProps fDetProps &fDetProps detTimings
Definition of util::values() and util::const_values().
Utility tag to identify a parameter as a specific type of tag.
unsigned long nBins() const
Returns the number of bins.
Definition: BinningSpecs.h:196
virtual void initializePlots(PlotCategories_t categories, std::vector< SettingsInfo_t > const &settings)
Initializes all the plot sets, one per PMT threshold.
bool hasThreshold(Threshold_t const &threshold) const
Returns whether the specified threshold is registered.
std::vector< TriggerGates_t > TriggerGatesPerCryostat_t
Type of lists of gates, one list per cryostat (outer index: cryostat no).
bool empty(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:555
OpDetGeo const & OpDetGeoFromOpChannel(unsigned int OpChannel) const
Returns the geo::OpDetGeo object for the given channel number.
Class counting pass/fail events.
Definition: PassCounter.h:15
bool IsValidOpChannel(int opChannel) const
Is this a valid OpChannel number?
timescale_traits< ElectronicsTimeCategory >::time_point_t electronics_time
A point in time on the electronics time scale.
A helper to manage ROOT objects in a art::TFileDirectory.
static icarus::trigger::details::EventInfoExtractor::EDepTags_t makeEdepTag(fhicl::Sequence< art::InputTag > const &EnergyDepositTags, fhicl::OptionalAtom< art::InputTag > const &EnergyDepositSummaryTag)
Creates a EDepTags_t out of the module configuration.
art framework interface to geometry description
std::vector< InputTriggerGate_t > TriggerGates_t
A list of trigger gates from input.
std::vector< geo::Point_t > const & Vertices() const
Returns the list of a known interaction vertex.
Definition: EventInfo_t.h:121
std::string const & patternName(std::size_t index) const
detinfo::DetectorTimings makeDetTimings(art::Event const *event)
Returns a detinfo::DetectorTimings from DetectorClocksService.
Encapsulate the construction of a single detector plane.
virtual void initializeEfficiencyPerTriggerPlots(PlotSandbox &plots) const
Initializes set of plots per complete trigger definition into plots.
A helper to manage ROOT objects with consistent naming.
Definition: PlotSandbox.h:95
virtual void fillEfficiencyPlots(EventInfo_t const &eventInfo, TriggerInfo_t const &triggerInfo, PlotSandbox const &plots) const
int InteractionType() const
Returns the interaction type.
Definition: EventInfo_t.h:109