All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MajorityTriggerEfficiencyPlots_module.cc
Go to the documentation of this file.
1 /**
2  * @file MajorityTriggerEfficiencyPlots_module.cc
3  * @brief Plots of efficiency for triggers based on PMT channel global count.
4  * @author Gianluca Petrillo (petrillo@slac.stanford.edu)
5  * @date January 9, 2020
6  * @see icaruscode/PMT/Trigger/TriggerEfficiencyPlotsBase.h
7  */
8 
9 
10 // ICARUS libraries
15 #include "icarusalg/Utilities/ROOTutils.h" // util::ROOT
16 
17 // LArSoft libraries
19 #include "lardata/Utilities/TensorIndices.h" // util::MatrixIndices
20 #include "lardataalg/DetectorInfo/DetectorTimingTypes.h" // optical_time_ticks..
23 #include "larcorealg/CoreUtils/StdUtils.h" // util::to_string()
24 
25 // framework libraries
26 #include "art/Framework/Core/EDAnalyzer.h"
27 #include "art/Framework/Core/ModuleMacros.h"
28 #include "art/Framework/Principal/Event.h"
29 #include "canvas/Utilities/Exception.h"
30 #include "messagefacility/MessageLogger/MessageLogger.h"
31 #include "fhiclcpp/types/Sequence.h"
32 
33 // ROOT libraries
34 #include "TTree.h"
35 #include "TH2F.h"
36 
37 // C/C++ standard libraries
38 #include <algorithm> // std::sort()
39 #include <vector>
40 #include <memory> // std::unique_ptr
41 #include <optional>
42 #include <utility> // std::pair<>, std::move()
43 #include <cstddef> // std::size_t
44 
45 
46 //------------------------------------------------------------------------------
47 
48 // --- BEGIN -- ROOT tree helpers ----------------------------------------------
49 /**
50  * @brief Class managing the serialization of trigger responses in a simple ROOT
51  * tree.
52  *
53  * The tree is supplied by the caller.
54  * This object will create the proper branches into the tree and assign
55  * addresses to them. Then it will assume they will stay assigned.
56  *
57  * On `assignResponse()`, the proper branch address is assigned the specified
58  * trigger response (`true` or `false`).
59  *
60  * The branch structure is: a `RespTxxRxx/O` branch for each threshold (numeric)
61  * and requirement (also numeric).
62  * with a single branch per element.
63  *
64  */
66 
67  // `std::vector<bool>` is too special for us. Let's pack it to go.
69  std::unique_ptr<bool[]> RespTxxRxx;
70 
71 
72  /// Constructor: accommodates that many thresholds and requirements.
73  template <typename Thresholds, typename Requirements>
75  (TTree& tree, Thresholds const& thresholds, Requirements const& minReqs);
76 
77  /// Assigns the response for the specified trigger.
78  void assignResponse(std::size_t iThr, std::size_t iReq, bool resp);
79 
80 }; // struct ResponseTree
81 
82 
83 // --- END -- ROOT tree helpers ------------------------------------------------
84 
85 
86 //------------------------------------------------------------------------------
87 namespace icarus::trigger { class MajorityTriggerEfficiencyPlots; }
88 /**
89  * @brief Produces plots about trigger simulation and trigger efficiency.
90  *
91  * This module is an implementation of `TriggerEfficiencyPlotsBase`
92  * for a trigger defined as a minimum number of trigger primitives beyond
93  * threshold.
94  *
95  * A trigger primitive is a two-level function of time which describes when
96  * that primitive is on and when it is off. Trigger primitives are given as
97  * input to this module and their origin may vary, but the standard source in
98  * ICARUS is the pairing with AND or OR of two optical detector channels
99  * discriminated against a certain ADC count threshold.
100  *
101  *
102  * Trigger logic algorithm
103  * ========================
104  *
105  * @anchor MajorityTriggerEfficiencyPlots_Algorithm
106  *
107  * This section describes the trigger logic algorithm used in
108  * `icarus::trigger::MajorityTriggerEfficiencyPlots` and its assumptions.
109  *
110  * The algorithm keeps the trigger primitives from the different cryostats
111  * separate for the most time.
112  * Within each cryostat, all trigger primitives are treated equally, whether
113  * they originate from one or from two channels (or 10 or 30), and wherever
114  * their channels are in the cryostat.
115  * The trigger primitives in each cryostat are combined in a multi-level gate by
116  * adding them, so that the level of the resulting gate matches at any time how
117  * many trigger primitives are on at that time.
118  * Finally, the maximum number of trigger primitives open in any of the
119  * cryostats at each time is the level to be compared to the trigger
120  * requirements.
121  *
122  * This multi-level gate is set in coincidence with the beam gate by multiplying
123  * the multi-level and the beam gates.
124  * The beam gate opens at a time configured in `DetectorClocks` service provider
125  * (`detinfo::DetectorClocks::BeamGateTime()`) and has a duration configured
126  * in this module (`BeamGateDuration`).
127  *
128  * At this point, the trigger gate is a multi-level gate suppressed everywhere
129  * except than during the beam gate.
130  * The algorithm handles multiple trigger primitive requirements.
131  * Each requirement is simply how many trigger primitives must be open at the
132  * same time in a single cryostat for the trigger to fire. The values of these
133  * requirements are set in the configuration (`MinimumPrimitives`).
134  * To determine whether a trigger with a given requirement, i.e. with a required
135  * minimum number of trigger primitives open at the same time, has fired, the
136  * gate combined as described above is scanned to find _the first tick_ where
137  * the level of the gate reaches or passes this minimum required number. If such
138  * tick exists, the trigger is considered to have fired, and at that time.
139  *
140  * As a consequence, there are for each event as many different trigger
141  * responses as how many different requirements are configured in
142  * `MinimumPrimitives`, _times_ how many ADC thresholds are provided in input,
143  * configured in `Thresholds`.
144  *
145  * While there _is_ a parameter describing the time resolution of the trigger
146  * (`TriggerTimeResolution`), this is currently only used for aesthetic purposes
147  * to choose the binning of some plots: the resolution is _not_ superimposed
148  * to the gates (yet).
149  *
150  * The combination algorithm is implemented in `combineTriggerPrimitives()`
151  * while the requirement evaluation and plotting are implemented in
152  * `plotResponses()`.
153  *
154  * The set of plots and their organization are described in the documentation of
155  * `icarus::trigger::TriggerEfficiencyPlotsBase`.
156  * In the following documentation only the additions are described.
157  *
158  *
159  * Output plots
160  * -------------
161  *
162  * A generic "setting" of `icarus::trigger::TriggerEfficiencyPlotsBase` is
163  * in this module represented by the single trigger primitive requirement _N_
164  * (configuration parameter: `MinimumPrimitives`). The folders and plots will
165  * identify each requirement with the tag `ReqN` (e.g. `Req5` when requesting
166  * at least 5 trigger primitives for an event trigger).
167  *
168  * There are a few plots that are produced by this module in addition to the
169  * ones in `TriggerEfficiencyPlotsBase`.
170  *
171  * There are different "types" of plots. Some
172  * @ref MajorityTriggerEfficiencyPlots_SelectionPlots "do not depend on triggering at all",
173  * like the deposited energy distribution. Others
174  * @ref MajorityTriggerEfficiencyPlots_MultiTriggerPlots "cross different trigger definitions",
175  * like the trigger efficiency as function of trigger requirement. Others still
176  * @ref MajorityTriggerEfficiencyPlots_SingleTriggerPlots "assume a single trigger definition":
177  * this is the case of trigger efficiency plots versus energy. Finally, there are
178  * @ref MajorityTriggerEfficiencyPlots_SingleTriggerResponsePlots "plots that depend on a specific trigger definition and outcome":
179  * this is the case of all the plots including only triggering or non-triggering
180  * events.
181  *
182  * A list of additional plots follows for each plot type.
183  * All the plots are always relative to a specific optical detector channel
184  * threshold (ADC) and a broad event category.
185  *
186  *
187  * ### Plots independent of the triggers (selection plots)
188  *
189  * @anchor MajorityTriggerEfficiencyPlots_SelectionPlots
190  *
191  * Only @ref TriggerEfficiencyPlotsBase_SelectionPlots "the standard plots"
192  * from `TriggerEfficiencyPlotsBase` are produced in this category.
193  *
194  *
195  * ### Plots including different trigger requirements
196  *
197  * @anchor MajorityTriggerEfficiencyPlots_MultiTriggerPlots
198  *
199  * In addition to @ref TriggerEfficiencyPlotsBase_MultiTriggerPlots "the plots"
200  * from `TriggerEfficiencyPlotsBase`, the following plots are also produced:
201  *
202  * * `Eff`: trigger efficiency defined as number of triggered events over the
203  * total number of events, as function of the minimum number of trigger
204  * primitives (`MinimumPrimitives`) to define a firing trigger; uncertainties
205  * are managed by `TEfficiency`.
206  * * `TriggerTick`: distribution of the time of the earliest trigger for the
207  * event, as function of the minimum number of trigger primitives (as in
208  * `Eff`). It may happen that the event is such that there is e.g. a
209  * 20-primitive flash, then subsiding, and then another 30-primitive flash.
210  * In such a case, in the trigger requirement "&geq; 15 primitives" such event
211  * will show at the time of the 20-primitive flash, while in the trigger
212  * requirement "&geq; 25 primitives" it will show at the time of the
213  * 30-primitive flash. Each event appears at most once for each trigger
214  * requirement, and it may not appear at all if does not fire a trigger.
215  * * `NPrimitives`: the maximum number of primitives "on" at any time.
216  *
217  *
218  * ### Plots depending on a specific trigger definition
219  *
220  * @anchor MajorityTriggerEfficiencyPlots_SingleTriggerPlots
221  *
222  * Only @ref TriggerEfficiencyPlotsBase_SingleTriggerPlots "the standard plots"
223  * from `TriggerEfficiencyPlotsBase` are produced in this category.
224  *
225  *
226  * ### Plots depending on a specific trigger definition and response
227  *
228  * @anchor MajorityTriggerEfficiencyPlots_SingleTriggerResponsePlots
229  *
230  * Only @ref TriggerEfficiencyPlotsBase_SingleTriggerResponsePlots "the standard plots"
231  * from `TriggerEfficiencyPlotsBase` are produced in this category.
232  *
233  *
234  * Configuration parameters
235  * =========================
236  *
237  * @anchor MajorityTriggerEfficiencyPlots_Configuration
238  *
239  * In addition to
240  * @ref TriggerEfficiencyPlotsBase_Configuration "all the configuration parameters"
241  * from `TriggerEfficiencyPlotsBase`, the following one is also present:
242  *
243  * * `MinimumPrimitives` (list of integers, _mandatory_): a list of alternative
244  * requirements for the definition of a trigger; each value is the number
245  * of trigger primitives needed to be "on" at the same time for the trigger
246  * to fire;
247  *
248  * An example job configuration is provided as `maketriggerplots_icarus.fcl`.
249  *
250  *
251  * Technical description of the module
252  * ====================================
253  *
254  * This module class is derived from
255  * `icarus::trigger::TriggerEfficiencyPlotsBase`, which provides a backbone to
256  * perform the simulation of triggers and plotting of their efficiency.
257  *
258  * There is not any superior design involved in this separation, but just the
259  * desire to share most code possible between different modules which simulate
260  * different trigger patterns and as a consequence might have specific plots to
261  * fill.
262  *
263  * This module redefines:
264  *
265  * * `initializePlotSet()` to define the
266  * @ref MajorityTriggerEfficiencyPlots_MultiTriggerPlots "few additional plots"
267  * needed;
268  * * `simulateAndPlot()`, which must always be defined, and which connects
269  * the simulation pieces and the plotting.
270  *
271  * It does not redefine `initializeEfficiencyPerTriggerPlots()` nor
272  * `initializeEventPlots()` because there are no additional plots of the types
273  * these functions deal with.
274  *
275  * The event categories are from the default list (`DefaultPlotCategories`) too.
276  *
277  */
279  : public art::EDAnalyzer
281 {
282 
283  public:
284 
285  // --- BEGIN Configuration ---------------------------------------------------
287 
288  using Name = fhicl::Name;
289  using Comment = fhicl::Comment;
290 
291  fhicl::Sequence<unsigned int> MinimumPrimitives {
292  Name("MinimumPrimitives"),
293  Comment("minimum required number of trigger primitives for a trigger")
294  };
295 
296  }; // struct Config
297 
298  using Parameters = art::EDAnalyzer::Table<Config>;
299  // --- END Configuration -----------------------------------------------------
300 
301 
302  // --- BEGIN Constructors ----------------------------------------------------
303  explicit MajorityTriggerEfficiencyPlots(Parameters const& config);
304 
305  // Plugins should not be copied or assigned.
310 
311  // --- END Constructors ------------------------------------------------------
312 
313 
314  // --- BEGIN Framework hooks -------------------------------------------------
315 
316  /// Initializes the plots.
317  virtual void beginJob() override;
318 
319  /// Fills the plots. Also extracts the information to fill them with.
320  virtual void analyze(art::Event const& event) override;
321 
322  /// Prints end-of-job summaries.
323  virtual void endJob() override;
324 
325  // --- END Framework hooks ---------------------------------------------------
326 
327 
328  private:
329 
330  /// Type of gate data used for internal processing.
332 
333 
334  // --- BEGIN Configuration variables -----------------------------------------
335 
336  /// Minimum number of trigger primitives for a trigger to happen.
337  std::vector<unsigned int> fMinimumPrimitives;
338 
339  // --- END Configuration variables -------------------------------------------
340 
341 
342  // --- BEGIN Internal variables ----------------------------------------------
343 
344  std::unique_ptr<ResponseTree> fResponseTree; ///< Handler of ROOT tree output.
345 
346  // --- END Internal variables ------------------------------------------------
347 
348 
349  // @{
350  /// Access to the helper.
351  MajorityTriggerEfficiencyPlots const& helper() const { return *this; }
353  // @}
354 
355 
356  // --- BEGIN Derived class methods -------------------------------------------
357  /**
358  * @brief Initializes full set of plots for (ADC threshold + category).
359  *
360  * This customization of `TriggerEfficiencyPlotsBase::initializePlotSet()`
361  * adds some trigger-definition specific plots and some overview plots
362  * across different trigger definitions.
363  */
364  virtual void initializePlotSet
365  (PlotSandbox& plots, std::vector<SettingsInfo_t> const& settings) const
366  override;
367 
368  /**
369  * @brief Simulates all trigger minimum requirements plots the results.
370  * @param thresholdIndex the index of the PMT threshold of input primitives
371  * @param gates the trigger primitives used to simulate the trigger response
372  * @param eventInfo general information about the event being simulated
373  * @param selectedPlots list of boxes containing plots to be filled
374  *
375  * This method is expected to perform the following steps for each trigger
376  * primitive requirement in `MinimumPrimitives`:
377  *
378  * 1. combine the trigger primitives: `combineTriggerPrimitives()`;
379  * 2. apply the beam gate: `applyBeamGate()` on the combined primitives;
380  * 3. generate the trigger response: in `plotResponses()`;
381  * 4. fill all plots: also in in `plotResponses()`.
382  *
383  * Details are in the documentation of the relevant methods.
384  *
385  * This method is invoked once per PMT threshold.
386  */
387  virtual void simulateAndPlot(
388  std::size_t const thresholdIndex,
389  TriggerGatesPerCryostat_t const& gates,
390  EventInfo_t const& eventInfo,
391  detinfo::DetectorClocksData const& clockData,
392  PlotSandboxRefs_t const& selectedPlots
393  ) override;
394 
395  // --- END Derived class methods ---------------------------------------------
396 
397 
398  /**
399  * @brief Completes the event trigger simulation and fills the plots.
400  * @param iThr index of PMT threshold (used in tree output)
401  * @param threshold PMT threshold in ADC counts (for printing)
402  * @param plotSets set of plot boxes to fill (from `initializePlotSet()`)
403  * @param eventInfo event information for plotting
404  * @param combinedCounts combined trigger primitives, per cryostat
405  *
406  * For each of the trigger requirements (`MinimumPrimitives`), this method:
407  *
408  * 1. applies the requirement to the `combinedCounts` trigger primitive
409  * 2. computes the event triggers (including the main one)
410  * 3. fills all plots in all the plot sets for this requirement accordingly
411  *
412  * The input combined trigger primitives contain the maximum number of
413  * trigger primitives active at each optical clock tick, one entry for each
414  * cryostat.
415  * It is assumed that the beam gate has already been "applied" so that outside
416  * it no trigger primitive is considered open.
417  *
418  * A trigger with requirement of minimum trigger primitives _N_ is fired if
419  * this combined primitive reaches or passes _N_, i.e. if there are at least
420  * _N_ open trigger primitives at any time.
421  * The time of the trigger is taken as the first tick at which the requirement
422  * is met.
423  *
424  * Extra plots are an overview of the trigger efficiency for different
425  * requirements, per threshold, the distribution of the trigger time for
426  * different requirements, and maximum number of open primitive in a cryostat,
427  * per event.
428  *
429  * Note that there is no information about which cryostat is meeting the
430  * trigger requirement.
431  */
432  void plotResponses(
433  std::size_t iThr, std::string const& threshold,
434  PlotSandboxRefs_t const& plotSets, EventInfo_t const& eventInfo,
435  detinfo::DetectorClocksData const& clockData,
436  std::vector<WorkingTriggerGate_t> const& combinedCounts,
437  std::vector<ChannelID_t> const& channelList
438  );
439 
440  /**
441  * @brief Computes the trigger response from primitives with the given
442  * `threshold`.
443  * @param cryoGates collections of trigger primitives, one coll. per cryostat
444  * @param threshold PMT threshold of the primitives (for printing purposes)
445  * @return a list of combined trigger primitives, one combination per cryostat
446  *
447  * The input trigger primitives are already grouped by cryostat.
448  * For each cryostat, the primitives are combined and one combination is
449  * returned. The combination is just the "total" of the primitives opened at
450  * each tick.
451  *
452  * The event trigger is not finalized here, and the cryostat trigger
453  * primitives are all returned.
454  */
455  std::vector<WorkingTriggerGate_t> combineTriggerPrimitives(
456  TriggerGatesPerCryostat_t const& cryoGates,
457  std::string const& threshold
458  ) const;
459 
460 }; // icarus::trigger::MajorityTriggerEfficiencyPlots
461 
462 
463 
464 //------------------------------------------------------------------------------
465 //--- Implementation
466 //------------------------------------------------------------------------------
467 //--- ResponseTree
468 //------------------------------------------------------------------------------
469 template <typename Thresholds, typename Requirements>
471  (TTree& tree, Thresholds const& thresholds, Requirements const& minReqs)
472  : TreeHolder(tree)
473  , indices(std::size(thresholds), std::size(minReqs))
474  , RespTxxRxx{ std::make_unique<bool[]>(indices.size()) }
475 {
476 
477  for (auto [ iThr, thresholdTag]: util::enumerate(thresholds)) {
478 
479  for (auto [ iReq, req ]: util::enumerate(minReqs)) {
480 
481  std::string const branchName
482  = "RespT" + thresholdTag + "R" + util::to_string(req);
483 
484  this->tree().Branch
485  (branchName.c_str(), &(RespTxxRxx[indices(iThr, iReq)]));
486 
487  } // for all requirements
488 
489  } // for all thresholds
490 
491 } // ResponseTree::ResponseTree()
492 
493 
494 //------------------------------------------------------------------------------
496  (std::size_t iThr, std::size_t iReq, bool resp)
497 {
498  RespTxxRxx[indices(iThr, iReq)] = resp;
499 } // ResponseTree::assignResponse()
500 
501 
502 //------------------------------------------------------------------------------
503 //--- icarus::trigger::MajorityTriggerEfficiencyPlots
504 //------------------------------------------------------------------------------
506  (Parameters const& config)
507  : art::EDAnalyzer (config)
509  // configuration
510  , fMinimumPrimitives(config().MinimumPrimitives())
511 {
512  std::sort(fMinimumPrimitives.begin(), fMinimumPrimitives.end());
513 
514  //
515  // more complex parameter parsing
516  //
517  if (helper().eventTree()) {
518 
519  fResponseTree = std::make_unique<ResponseTree>
520  (*(helper().eventTree()), helper().ADCthresholds(), fMinimumPrimitives);
521 
522  } // if make tree
523 
524  if (fMinimumPrimitives.empty()) {
525  throw art::Exception(art::errors::Configuration)
526  << "At least one 'MinimumPrimitives' requirement... required.";
527  }
528 
529  std::size_t iPattern [[maybe_unused]] = 0U; // NOTE: incremented only in DEBUG
530  for (auto const& req: fMinimumPrimitives) {
531  std::size_t const index [[maybe_unused]]
532  = createCountersForPattern("Req" + std::to_string(req));
533  assert(index == iPattern++);
534  } // for requirements
535 
536  {
537  mf::LogInfo log(helper().logCategory());
538  log
539  << "Requirement of minimum trigger primitives ("
540  << fMinimumPrimitives.size() << "):";
541  for (auto const& req: fMinimumPrimitives) log << " " << req;
542  log << ".";
543  }
544 
545 } // icarus::trigger::MajorityTriggerEfficiencyPlots::MajorityTriggerEfficiencyPlots()
546 
547 
548 //------------------------------------------------------------------------------
550 
551  // hook helper and framework;
552  // actual action is in the (overridden) virtual functions `initializeXxx()`.
553 
554  // NOTE this action can't happen in constructor because relies on polymorphism
555  std::vector<SettingsInfo_t> settings;
556  for (auto [ iReq, minCount ]: util::enumerate(fMinimumPrimitives)) {
557  std::string const minCountStr { std::to_string(minCount) };
558  settings.emplace_back(
559  iReq, // index
560  "Req" + minCountStr, // tag
561  minCountStr + " channels required" // description
562  );
563  } // for
564 
565  // we use the default plot categories defined in
566  // `TriggerEfficiencyPlotsBase::DefaultPlotCategories`
567  helper().initializePlots(settings);
568 
569 } // icarus::trigger::MajorityTriggerEfficiencyPlots::beginJob()
570 
571 
572 //------------------------------------------------------------------------------
574  (art::Event const& event)
575 {
576 
577  // hook helper and framework;
578  // actual action is in the (overridden) virtual function `plotResponses()`
579  // and `fillXxxPlots()`.
580  helper().process(event);
581 
582 } // icarus::trigger::MajorityTriggerEfficiencyPlots::analyze()
583 
584 
585 //------------------------------------------------------------------------------
587 
588  // hook helper and framework
589  helper().printSummary();
590 
591 } // icarus::trigger::MajorityTriggerEfficiencyPlots::endJob()
592 
593 
594 //------------------------------------------------------------------------------
596  (PlotSandbox& plots, std::vector<SettingsInfo_t> const& settings) const
597 {
598 
599  //
600  // Selection-related plots
601  //
602 
603  // (inherited)
604  helper().TriggerEfficiencyPlotsBase::initializePlotSet(plots, settings);
605 
606  //
607  // overview plots with different settings
608  //
609 
610  // a variable binning for the required number of trigger primitives
611  auto [ minimumPrimBinning, minimumPrimBinningLabels ]
612  = util::ROOT::makeVariableBinningAndLabels(fMinimumPrimitives);
613  assert(minimumPrimBinning.size() == minimumPrimBinningLabels.size() + 1U);
614 
615  {
616  mf::LogTrace log(helper().logCategory());
617  log << "MajorityTriggerEfficiencyPlots (plots '"
618  << plots.name() << "') variable binning including the "
619  << fMinimumPrimitives.size() << " points {";
620  for (auto value: fMinimumPrimitives) log << " " << value;
621  log << " } => " << minimumPrimBinningLabels.size() << " bins: ";
622  for (auto const& [ value, label ]
623  : util::zip<1U>(minimumPrimBinning, minimumPrimBinningLabels))
624  {
625  log << " " << value << " (\"" << label << "\") =>";
626  } // for
627  log << " " << minimumPrimBinning.back();
628  } // debug output block
629 
630  //
631  // Triggering efficiency vs. requirements.
632  //
633 
634  auto const [ detTimings, beamGate, preSpillWindow ] = makeGatePack();
635 
636  detinfo::timescales::optical_time_ticks const triggerResolutionTicks
637  { detTimings.toOpticalTicks(helper().triggerTimeResolution()) };
638 
639  auto const& beamGateOpt = beamGate.asOptTickRange();
640 
641  auto* TrigTime = plots.make<TH2F>(
642  "TriggerTick",
643  "Trigger time tick"
644  ";minimum requested number of trigger primitives"
645  ";optical time tick [ /" + util::to_string(triggerResolutionTicks) + " ]",
646  minimumPrimBinning.size() - 1U, minimumPrimBinning.data(),
647 // fMinimumPrimitives.back(), 0, fMinimumPrimitives.back() + 1
648  beamGateOpt.duration() / triggerResolutionTicks,
649  beamGateOpt.first.value(), beamGateOpt.second.value()
650  );
651 
652  util::ROOT::applyAxisLabels(TrigTime->GetXaxis(), minimumPrimBinningLabels);
653 
654  auto* Eff = plots.make<TEfficiency>(
655  "Eff",
656  "Efficiency of triggering"
657  ";minimum requested number of trigger primitives"
658  ";trigger efficiency",
659  minimumPrimBinning.size() - 1U, minimumPrimBinning.data()
660 // fMinimumPrimitives.back(), 0, fMinimumPrimitives.back() + 1
661  );
662 
663  // people are said to have earned hell for things like this;
664  // but TEfficiency really does not expose the interface to assign labels to
665  // its axes, which supposedly could be done had we chosen to create it by
666  // histograms instead of directly as recommended.
668  const_cast<TH1*>(Eff->GetTotalHistogram())->GetXaxis(),
669  minimumPrimBinningLabels
670  );
671 
672  //
673  // plots independent of the trigger primitive requirements
674  //
675  plots.make<TH1F>(
676  "NPrimitives",
677  "Number of trigger primitives (\"channels firing at once\")"
678  ";maximum trigger primitives at the same time on a single cryostat"
679  ";events",
680  192, 0.0, 192.0 // large number, zoom in presentations!
681  );
682 
683 } // icarus::trigger::MajorityTriggerEfficiencyPlots::initializePlotSet()
684 
685 
686 //------------------------------------------------------------------------------
688  std::size_t const thresholdIndex,
689  TriggerGatesPerCryostat_t const& gates,
690  EventInfo_t const& eventInfo,
691  detinfo::DetectorClocksData const& clockData,
692  PlotSandboxRefs_t const& selectedPlots
693 ) {
694 
695  auto const threshold = helper().ADCthresholdTag(thresholdIndex);
696 
697  auto const& beamGate = helper().makeMyBeamGate(clockData);
698 
699  /*
700  * 1. combine trigger primitives per cryostat (`combineTriggerPrimitives()`)
701  * 2. apply the beam gate on the combination (`applyBeamGate()`)
702  * 3. and compute the trigger response (`plotResponses()`)
703  * 4. fill plots with the result (also `plotResponses()`)
704  */
705  plotResponses(
706  thresholdIndex, threshold, selectedPlots, eventInfo,
707  clockData,
708  beamGate.applyToAll(combineTriggerPrimitives(gates, threshold)),
709  helper().extractActiveChannels(gates)
710  );
711 
712 } // icarus::trigger::MajorityTriggerEfficiencyPlots::simulateAndPlot()
713 
714 
715 //------------------------------------------------------------------------------
717  std::size_t iThr,
718  std::string const& threshold,
719  PlotSandboxRefs_t const& plotSets,
720  EventInfo_t const& eventInfo,
721  detinfo::DetectorClocksData const& clockData,
722  std::vector<WorkingTriggerGate_t> const& combinedCounts,
723  std::vector<ChannelID_t> const& channelList
724 ) {
725 
726  /*
727  * This function plots according to the configured minimum number of trigger
728  * primitives: for each requirement of minimum number of primitives, the
729  * earliest time where that requirement is met is found, and that is
730  * considered as the trigger time.
731  *
732  * The following quantities are drawn per ADC threshold and per plot category:
733  *
734  * * per minimum number of primitives:
735  * * trigger efficiency (i.e. whether there was _any time_ a number of
736  * primitives fulfilling the requirement)
737  * * trigger time in ticks (distribution as 2D histogram)
738  * * maximum number of trigger primitives present at any time
739  * * deposited energy during beam spill
740  *
741  */
742  using namespace std::string_literals;
743 
744  using ClockTick_t = WorkingTriggerGate_t::TriggerGate_t::ClockTick_t;
745  using OpeningCount_t = WorkingTriggerGate_t::TriggerGate_t::OpeningCount_t;
746 
747  using PrimitiveCount_t = std::pair<ClockTick_t, OpeningCount_t>;
748 
749  PrimitiveCount_t maxPrimitives { ClockTick_t{ 0 } /* dummy */, 0U };
750  for (auto const& [ iCryo, combinedCount ]
751  : util::enumerate(gatesIn(combinedCounts)))
752  {
753  auto const maxPrimitiveTime { combinedCount.findMaxOpen() };
754  PrimitiveCount_t const maxPrimitivesInCryo
755  { maxPrimitiveTime, combinedCount.openingCount(maxPrimitiveTime) };
756  if (maxPrimitivesInCryo.second > maxPrimitives.second)
757  maxPrimitives = maxPrimitivesInCryo;
758 
759  mf::LogTrace log { helper().logCategory() };
760  log << "Max primitive count in " << threshold << " for C:" << iCryo << ": "
761  << maxPrimitivesInCryo.second;
762  if (maxPrimitivesInCryo.second > 0) {
763  log << " at tick " << maxPrimitivesInCryo.first << " ("
765  (detinfo::DetectorTimings::optical_tick{ maxPrimitivesInCryo.first })
766  << ")"
767  ;
768  } // if
769  } // for
770 
771  PMTInfo_t const PMTinfo { threshold, channelList };
772 
773  /*
774  * Fill all the histograms for all the minimum primitive requirements
775  * (filling the information whether or not the trigger fired),
776  * for all the qualifying categories.
777  * Note that in this type of plots each event appears in all bins
778  * (may be with "fired" or "not fired" on each bin)
779  */
780 
781  // PrimitiveCount_t lastMinCount { WorkingTriggerGate_t::MinTick, 0 };
782 
783  bool fired = true; // the final trigger response (changes with requirement)
784 
785  for (auto [ iReq, minCount ]: util::enumerate(fMinimumPrimitives)) {
786 
787  TriggerInfo_t triggerInfo;
788  if (fired) { // this is still the previous requirement
789  for (auto const& [ iCryo, cryoGate ]: util::enumerate(combinedCounts)) {
790 
792  { gateIn(cryoGate), minCount };
793  extractOpeningInfo.setLocation(iCryo);
794  while (extractOpeningInfo) {
795  auto info = extractOpeningInfo();
796  if (info) triggerInfo.add(info.value());
797  } // while
798  } // for all cryostats
799  triggerInfo.sortOpenings(); // sort in time
800  fired = triggerInfo.fired();
801  } // if previous fired
802 
803  if (fired) {
804  mf::LogTrace log(helper().logCategory());
805  log
806  << " => fired (>" << minCount << ") at " << triggerInfo.atTick()
807  << " (level " << triggerInfo.level() << ") from"
808  ;
809  if (triggerInfo.hasLocation()) log << " C:" << triggerInfo.location();
810  else log << " unknown location";
811  log << ", " << triggerInfo.nTriggers() << " triggers total:";
812  for (auto const& [iTrigger, info ]: util::enumerate(triggerInfo.all())) {
813  log << " [" << iTrigger << "] at " << info.tick;
814  if (info.hasLocation()) log << " of C:" << info.locationID;
815  else log << " [unknown location]";
816  log << " (level=" << info.level << ")";
817  } // for
818 
819  } // if fired
820  else {
821  mf::LogTrace(helper().logCategory())
822  << " => not fired (>" << minCount << ")";
823  }
824 
825  // at this point we know we have minCount or more trigger primitives,
826  // and the time of this one is in lastMinCount.first (just in case)
827 
828  if (fResponseTree) fResponseTree->assignResponse(iThr, iReq, fired);
829 
830  registerTriggerResult(iThr, iReq, fired);
831 
832  std::string const minCountStr { "Req" + std::to_string(minCount) };
833 
834  // go through all the plot categories this event qualifies for
835  // (for example: charged currents, muon neutrinos, ...)
836  for (icarus::trigger::PlotSandbox const& plotSet: plotSets) {
837 
838  //
839  // overview plots from different thresholds
840  //
841 
842  HistGetter const get { plotSet };
843 
844  // simple efficiency
845  get.Eff("Eff"s).Fill(fired, minCount);
846 
847  // trigger time (if any)
848  if (fired) {
849  get.Hist2D("TriggerTick"s).Fill(minCount, triggerInfo.atTick().value());
850  }
851 
852  //
853  // plots depending on the trigger response
854  // (but not caring of the trigger definition details)
855  //
856 
857  // efficiency plots
858  // (including event plots in the triggered or non-triggered category)
859  helper().fillAllEfficiencyPlots
860  (eventInfo, PMTinfo, triggerInfo, plotSet.demandSandbox(minCountStr));
861 
862  //
863  // add here further trigger-specific plots
864  //
865 
866  } // for all qualifying plot categories
867 
868  } // for all requirements
869 
870  /*
871  * Now fill the plots independent of the trigger response:
872  * the same value is plotted in all plot sets.
873  * (again for all pertinent event categories, e.g. charged currents, etc.)
874  */
875  for (PlotSandbox const& plotSet: plotSets) {
876 
877  //
878  // general plots, independent of trigger definition details
879  //
880  fillEventPlots(eventInfo, plotSet);
881 
882  //
883  // general plots, independent of trigger definition but dependent on
884  // threshold
885  //
886  fillPMTplots(PMTinfo, plotSet);
887 
888  //
889  // trigger-definition specific plots
890  //
891  HistGetter const get(plotSet);
892 
893  // number of primitives
894  get.Hist("NPrimitives"s).Fill(maxPrimitives.second);
895 
896  } // for
897 
898 } // icarus::trigger::MajorityTriggerEfficiencyPlots::plotResponses()
899 
900 
901 //------------------------------------------------------------------------------
903  TriggerGatesPerCryostat_t const& cryoGates,
904  std::string const& threshold
905 ) const -> std::vector<WorkingTriggerGate_t> {
906 
907  //
908  // simple count
909  //
910  std::vector<WorkingTriggerGate_t> cryoCombinedGate;
911  cryoCombinedGate.reserve(cryoGates.size());
912 
913  for (auto const& [ iCryo, gates ]: util::enumerate(cryoGates)) {
914  geo::CryostatID const cryoID(iCryo);
915 
916  mf::LogTrace(helper().logCategory())
917  << "Simulating trigger response with ADC threshold " << threshold
918  << " for " << cryoID << " (" << gates.size() << " primitives)";
919 
920  cryoCombinedGate.push_back(icarus::trigger::sumGates(gates));
921  } // for
922 
923  //
924  // largest number of trigger primitives at any time, per cryostat
925  //
926  return cryoCombinedGate;
927 
928 } // icarus::trigger::MajorityTriggerEfficiencyPlots::combineTriggerPrimitives()
929 
930 
931 //------------------------------------------------------------------------------
933 
934 
935 //------------------------------------------------------------------------------
BEGIN_PROLOG BeamGateDuration pmtthr physics producers trigtilewindowORS Thresholds
bool fired() const
Returns whether the trigger fired.
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
Definition of util::zip().
Helper class to produce plots about trigger simulation and trigger efficiency.
std::vector< OpeningInfo_t > const & all() const
std::vector< unsigned int > fMinimumPrimitives
Minimum number of trigger primitives for a trigger to happen.
virtual void initializePlotSet(PlotSandbox &plots, std::vector< SettingsInfo_t > const &settings) const override
Initializes full set of plots for (ADC threshold + category).
std::pair< std::vector< double >, std::vector< std::string > > makeVariableBinningAndLabels(Coll const &centralPoints)
Returns a variable size binning for the points.
void sortOpenings()
Sorts all openings by time.
Definition of util::enumerate().
A wrapper to trigger gate objects tracking the input of operations.
virtual void endJob() override
Prints end-of-job summaries.
LocationID_t location() const
Returns the ID of the location of the trigger (undefined if !fired()).
Class managing the serialization of trigger responses in a simple ROOT tree.
ResponseTree(TTree &tree, Thresholds const &thresholds, Requirements const &minReqs)
Constructor: accommodates that many thresholds and requirements.
void assignResponse(std::size_t iThr, std::size_t iReq, bool resp)
Assigns the response for the specified trigger.
auto gatesIn(TrackingGateColl &trackingGates)
std::size_t size(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:561
void applyAxisLabels(TAxis *pAxis, std::vector< std::string > const &labels, int first=1)
Sets all the labels starting with the bin first (1 by default).
Definition: ROOTutils.h:175
std::unique_ptr< ResponseTree > fResponseTree
Handler of ROOT tree output.
TensorIndices class to flatten multi-dimension indices into linear.
bool hasLocation() const
Returns if the location of the trigger is set (undefined if !fired()).
std::string const & name() const
Returns the sandbox name.
Definition: PlotSandbox.h:196
std::unique_ptr< bool[]> RespTxxRxx
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
timescale_traits< OpticalTimeCategory >::tick_interval_t optical_time_ticks
icarus::trigger::TrackedOpticalTriggerGate< sbn::OpDetWaveformMeta > InputTriggerGate_t
Type of trigger gate extracted from the input event.
std::vector< WorkingTriggerGate_t > combineTriggerPrimitives(TriggerGatesPerCryostat_t const &cryoGates, std::string const &threshold) const
Computes the trigger response from primitives with the given threshold.
Information about the event.
Converts a tensor element specification into a linear index.
Definition: TensorIndices.h:46
electronics_time toElectronicsTime(FromTime time) const
Converts a time point into electronics time scale.
decltype(auto) constexpr to_string(T &&obj)
ADL-aware version of std::to_string.
void plotResponses(std::size_t iThr, std::string const &threshold, PlotSandboxRefs_t const &plotSets, EventInfo_t const &eventInfo, detinfo::DetectorClocksData const &clockData, std::vector< WorkingTriggerGate_t > const &combinedCounts, std::vector< ChannelID_t > const &channelList)
Completes the event trigger simulation and fills the plots.
constexpr std::array< std::size_t, geo::vect::dimension< Vector >)> indices()
Returns a sequence of indices valid for a vector of the specified type.
A bunch of diverse utilities and futilities related to ROOT.
virtual void simulateAndPlot(std::size_t const thresholdIndex, TriggerGatesPerCryostat_t const &gates, EventInfo_t const &eventInfo, detinfo::DetectorClocksData const &clockData, PlotSandboxRefs_t const &selectedPlots) override
Simulates all trigger minimum requirements plots the results.
std::vector< PlotDef > plots
Definition: demo.h:54
Utilities for the conversion of trigger gate data formats.
BEGIN_PROLOG vertical distance to the surface Name
Helper data structure to store PMT activity information in the event.
Definition: PMTInfo_t.h:25
Simple class holding a tree.
Definition: TreeHolder.h:24
auto sumGates(GateColl const &gates)
Sums all the gates in a collection.
Base class for _art_modules plotting trigger efficiencies.
virtual void analyze(art::Event const &event) override
Fills the plots. Also extracts the information to fill them with.
MajorityTriggerEfficiencyPlots const & helper() const
Access to the helper.
A wrapper to trigger gate objects tracking the contributions.
optical_tick atTick() const
Returns the time of the trigger (undefined if !fired()).
Contains all timing reference information for the detector.
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
A class exposing an upgraded interface of detinfo::DetectorClocksData.
Data types for detinfo::DetectorTimings.
Functions pulling in STL customization if available.
Selected information about the event.
Definition: EventInfo_t.h:45
std::vector< std::reference_wrapper< PlotSandbox const >> PlotSandboxRefs_t
List of references to plot sandboxes.
temporary value
fDetProps &fDetProps fDetProps &fDetProps detTimings
Opening_t level() const
Returns the opening level of the trigger (undefined if !fired()).
virtual void initializePlots(PlotCategories_t categories, std::vector< SettingsInfo_t > const &settings)
Initializes all the plot sets, one per PMT threshold.
std::vector< TriggerGates_t > TriggerGatesPerCryostat_t
Type of lists of gates, one list per cryostat (outer index: cryostat no).
MajorityTriggerEfficiencyPlots & operator=(MajorityTriggerEfficiencyPlots const &)=delete
detinfo::timescales::optical_tick optical_tick
Helper to extract OpeningInfo_t from a trigger gate.
Definition: TriggerInfo_t.h:30
A helper to manage ROOT objects in a art::TFileDirectory.
std::size_t nTriggers() const
Returns the number of registered triggers.
The data type to uniquely identify a cryostat.
Definition: geo_types.h:190
fDetProps &fDetProps fDetProps &fDetProps consumesCollector())
A helper to manage ROOT objects with consistent naming.
Definition: PlotSandbox.h:95
decltype(auto) gateIn(Gate &&gate)
Returns the trigger gate (a ReadoutTriggerGate) from the specified gate.
Produces plots about trigger simulation and trigger efficiency.