All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MajorityTriggerSimulation_module.cc
Go to the documentation of this file.
1 /**
2  * @file MajorityTriggerSimulation_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  */
7 
8 
9 // ICARUS libraries
16 #include "icaruscode/PMT/Trigger/Utilities/TriggerDataUtils.h" // ReadTriggerGates()
20 #include "icaruscode/Utilities/DetectorClocksHelpers.h" // makeDetTimings()...
21 #include "icarusalg/Utilities/ROOTutils.h" // util::ROOT
23 #include "icarusalg/Utilities/ChangeMonitor.h" // ThreadSafeChangeMonitor
24 #include "icarusalg/Utilities/rounding.h" // icarus::ns::util::roundup()
25 
26 // LArSoft libraries
29 #include "larcore/CoreUtils/ServiceUtil.h" // lar::providerFrom()
32 #include "lardataalg/DetectorInfo/DetectorTimingTypes.h" // optical_tick...
33 #include "lardataalg/Utilities/quantities/spacetime.h" // microseconds, ...
34 #include "lardataalg/Utilities/intervals_fhicl.h" // microseconds from FHiCL
38 #include "larcorealg/CoreUtils/values.h" // util::const_values()
39 #include "larcorealg/CoreUtils/get_elements.h" // util::get_elements()
41 #include "larcorealg/CoreUtils/StdUtils.h" // util::to_string()
42 #include "lardataobj/RawData/TriggerData.h" // raw::Trigger
43 #include "lardataobj/RawData/OpDetWaveform.h" // raw::ADC_Count_t
44 #include "larcoreobj/SimpleTypesAndConstants/geo_types.h" // geo::CryostatID
45 
46 // framework libraries
47 #include "art_root_io/TFileService.h"
48 #include "art_root_io/TFileDirectory.h"
49 #include "art/Framework/Services/Registry/ServiceHandle.h"
50 #include "art/Framework/Core/EDProducer.h"
51 #include "art/Framework/Core/ModuleMacros.h"
52 #include "art/Framework/Principal/Event.h"
53 #include "canvas/Utilities/InputTag.h"
54 #include "canvas/Utilities/Exception.h"
55 #include "messagefacility/MessageLogger/MessageLogger.h"
56 #include "fhiclcpp/types/Sequence.h"
57 #include "fhiclcpp/types/Atom.h"
58 
59 // ROOT libraries
60 #include "TEfficiency.h"
61 #include "TH1F.h"
62 #include "TH2F.h"
63 
64 // C/C++ standard libraries
65 #include <ostream>
66 #include <algorithm> // std::fill()
67 #include <map>
68 #include <vector>
69 #include <memory> // std::make_unique()
70 #include <string>
71 #include <atomic>
72 #include <optional>
73 #ifdef __cpp_lib_source_location
74 # include <source_location>
75 #endif // __cpp_lib_source_location
76 #include <utility> // std::pair<>, std::move()
77 #include <cmath> // std::ceil()
78 #include <cstddef> // std::size_t
79 #include <cassert>
80 
81 
82 //------------------------------------------------------------------------------
83 using namespace util::quantities::time_literals;
84 
85 
86 // TODO Sort this mess
87 
88 //------------------------------------------------------------------------------
89 namespace icarus::trigger { class MajorityTriggerCombiner; }
90 
91 /// Combines a group of trigger gates for majority trigger. Glorified `Sum()`.
94 {
95 
96  public:
97 
99  (std::string const& logCategory = "MajorityTriggerCombiner")
100  : icarus::ns::util::mfLoggingClass(logCategory) {}
101 
102  /// Combines all the gates (by cryostat) in a single majority gate.
103  template <typename GateObj>
104  GateObj combine(std::vector<GateObj> const& gates) const
105  { return icarus::trigger::sumGates(gates); }
106 
107 
108  private:
109 
110 }; // class icarus::trigger::MajorityTriggerCombiner
111 
112 
113 //------------------------------------------------------------------------------
114 namespace icarus::trigger { class CryostatTriggerCombiner; }
115 
116 /// Combines cryostat triggers via OR. Glorified `Max()`.
119 {
120 
121  public:
122 
124  (std::string const& logCategory = "CryostatTriggerCombiner")
125  : icarus::ns::util::mfLoggingClass(logCategory) {}
126 
127  /// Combines all the gates (by cryostat) in a single majority gate.
128  template <typename GateObj>
129  GateObj combine(std::vector<GateObj> const& cryoGates) const
130  { return icarus::trigger::maxGates(cryoGates); }
131 
132  private:
133 
134 }; // class icarus::trigger::CryostatTriggerCombiner
135 
136 
137 //------------------------------------------------------------------------------
138 namespace icarus::trigger { class GeometryChannelSplitter; }
139 
140 
141 /// Combines cryostat triggers via OR. Glorified `Max()`.
144 {
145 
146  public:
147 
149  geo::GeometryCore const& geom,
150  std::string const& logCategory = "GeometryChannelSplitter"
151  );
152 
153  /// Splits the gates by cryostat.
154  template <typename GateObj>
155  std::vector<std::vector<GateObj>> byCryostat
156  (std::vector<GateObj>&& gates) const;
157 
158  private:
159 
160  unsigned int const fNCryostats; ///< Number of cryostats in the detector.
161 
162  /// Map: optical channel ID -> number of the cryostat with that channel.
163  std::vector<geo::CryostatID> const fChannelCryostat;
164 
165 
166  /// Creates a map like `fChannelCryostat` from the geometry information.
167  static std::vector<geo::CryostatID> makeChannelCryostatMap
168  (geo::GeometryCore const& geom);
169 
170 }; // class icarus::trigger::GeometryChannelSplitter
171 
172 
173 //------------------------------------------------------------------------------
175  geo::GeometryCore const& geom,
176  std::string const& logCategory /* = "GeometryChannelSplitter" */
177  )
178  : icarus::ns::util::mfLoggingClass(logCategory)
179  , fNCryostats(geom.Ncryostats())
180  , fChannelCryostat(makeChannelCryostatMap(geom))
181 {}
182 
183 
184 //------------------------------------------------------------------------------
185 template <typename GateObj>
186 std::vector<std::vector<GateObj>>
188  (std::vector<GateObj>&& gates) const
189 {
190  std::vector<std::vector<GateObj>> gatesPerCryostat{ fNCryostats };
191 
192  for (auto& gate: gates) {
193  gatesPerCryostat[fChannelCryostat.at(gate.channels().front()).Cryostat]
194  .push_back(std::move(gate));
195  } // for gates
196 
197  return gatesPerCryostat;
198 } // icarus::trigger::GeometryChannelSplitter::byCryostat()
199 
200 
201 //------------------------------------------------------------------------------
203  (geo::GeometryCore const& geom) -> std::vector<geo::CryostatID>
204 {
205 
206  auto const nOpChannels = geom.NOpChannels();
207 
208  std::vector<geo::CryostatID> channelCryostatMap(nOpChannels);
209 
210  for (auto const opChannel: util::counter(nOpChannels)) {
211  if (!geom.IsValidOpChannel(opChannel)) continue;
212  channelCryostatMap.at(opChannel)
213  = geom.OpDetGeoFromOpChannel(opChannel).ID();
214  } // for all channels
215 
216  return channelCryostatMap;
217 
218 } // icarus::trigger::GeometryChannelSplitter::makeChannelCryostatMap()
219 
220 
221 //------------------------------------------------------------------------------
222 namespace icarus::trigger { class MajorityTriggerSimulation; }
223 /**
224  * @brief Simulates a "majority" trigger.
225  *
226  * A trigger primitive is a two-level function of time which describes when
227  * that primitive is on and when it is off. Trigger primitives are given as
228  * input to this module and their origin may vary, but the standard source in
229  * ICARUS is @ref ICARUSPMTTriggerGlossary "single trigger request".
230  *
231  * This module simulates a trigger requesting a minimum number of single trigger
232  * requests in the event, and saves the result as `raw::Trigger` data products.
233  * While only one trigger definition is used, inputs with different thresholds
234  * may be specified to have the different responses.
235  *
236  *
237  * Configuration
238  * ==============
239  *
240  * * `TriggerGatesTag` (string, mandatory): name of the module
241  * instance which produced the trigger primitives to be used as input;
242  * it must not include any instance name, as the instance names will be
243  * automatically added from `Thresholds` parameter.
244  * The typical trigger primitives used as input may be LVDS discriminated
245  * output (e.g. from `icarus::trigger::LVDSgates` module) or combinations
246  * of them (e.g. from `icarus::trigger::SlidingWindowTrigger` module).
247  * * `Thresholds` (list of integers, mandatory): list of the discrimination
248  * thresholds to consider, in ADC counts. A data product containing a
249  * digital signal is read for each one of the thresholds, and the tag of the
250  * data product is expected to be the module label `TriggerGatesTag` with as
251  * instance name the value of the threshold (e.g. for a threshold of 60 ADC
252  * counts the data product tag might be `LVDSgates:60`).
253  * * `MinimumPrimitives` (integer, _mandatory_): the required number of
254  * single trigger requests in order for the trigger to fire;
255  * * `BeamGateDuration` (time, _mandatory_): the duration of the beam
256  * gate; _the time requires the unit to be explicitly specified_: use
257  * `"1.6 us"` for BNB, `9.5 us` for NuMI (also available as
258  * `BNB_settings.spill_duration` and `NuMI_settings.spill_duration` in
259  * `trigger_icarus.fcl`);
260  * * `TriggerTimeResolution` (time, default: `8 ns`): time resolution for the
261  * trigger primitives;
262  * * `LogCategory` (string, default `TriggerEfficiencyPlots`): name of category
263  * used to stream messages from this module into message facility.
264  *
265  * An example job configuration is provided as
266  * `simulatemajoritytriggers_icarus.fcl`.
267  *
268  *
269  * Output data products
270  * =====================
271  *
272  * * `std::vector<raw::Trigger>` (one instance per ADC threshold):
273  * list of triggers fired according to the configured trigger definition;
274  * there is one collection (and data product) per ADC threshold, and the
275  * data product has the same instance name as the input data one
276  * (see `TriggerGatesTag` and `Thresholds` configuration parameters);
277  * currently only at most one trigger is emitted, with time stamp matching
278  * the first time the trigger criteria are satisfied.
279  *
280  *
281  *
282  * Trigger logic algorithm
283  * ========================
284  *
285  * @anchor MajorityTriggerSimulation_Algorithm
286  *
287  * This section describes the trigger logic algorithm used in
288  * `icarus::trigger::MajorityTriggerSimulation` and its assumptions.
289  *
290  * The algorithm keeps the trigger primitives from the different cryostats
291  * separate for the most time.
292  * Within each cryostat, all trigger primitives are treated equally, whether
293  * they originate from one or from two channels (or 10 or 30), and wherever
294  * their channels are in the cryostat.
295  * The trigger primitives in each cryostat are combined in a multi-level gate by
296  * adding them, so that the level of the resulting gate matches at any time how
297  * many trigger primitives are on at that time.
298  * Finally, the maximum number of trigger primitives open in any of the
299  * cryostats at each time is the level to be compared to the trigger
300  * requirements.
301  *
302  * This multi-level gate is set in coincidence with the beam gate by multiplying
303  * the multi-level and the beam gates.
304  * The beam gate opens at a time configured in `DetectorClocks` service provider
305  * (`detinfo::DetectorClocks::BeamGateTime()`) and has a duration configured
306  * in this module (`BeamGateDuration`).
307  *
308  * At this point, the trigger gate is a multi-level gate suppressed everywhere
309  * except than during the beam gate.
310  * The trigger requirement is simply how many trigger primitives must be open at
311  * the same time in a single cryostat for the trigger to fire. The requirement
312  * is set in the configuration (`MinimumPrimitives`).
313  * To determine whether a trigger with the given minimum number of primitives
314  * open at the same time has fired, the gate combined as described above is
315  * scanned to find _the first tick_ where the level of the gate reaches
316  * or passes this minimum required number. If such tick exists, the trigger is
317  * considered to have fired, and at that time.
318  *
319  * While there _is_ a parameter describing the time resolution of the trigger
320  * (`TriggerTimeResolution`), this is currently only used for aesthetic purposes
321  * to choose the binning of some plots: the resolution is _not_ superimposed
322  * to the gates (yet).
323  *
324  *
325  * Output plots
326  * -------------
327  *
328  * Plots are directly stored in the producer folder of the `TFileService` ROOT
329  * output file.
330  *
331  * Summary plots are generated:
332  * * `NTriggers`: total number of triggers, per threshold
333  * * `Eff`: fraction of events with at least one trigger, per threshold
334  * * `TriggerTick`: trigger time distribution (optical tick), per threshold
335  *
336  * The plots marked "per threshold" have a "threshold" axis where each bin
337  * corresponds to the threshold specified in the `Thresholds` configuration
338  * parameter. Note that the numerical value of the axis on that bin does not
339  * match the threshold value.
340  *
341  */
343  : public art::EDProducer
345 {
346 
347  public:
348 
351 
352 
353  // --- BEGIN Configuration ---------------------------------------------------
354  struct Config {
355 
356  using Name = fhicl::Name;
357  using Comment = fhicl::Comment;
358 
359  fhicl::Atom<std::string> TriggerGatesTag {
360  Name("TriggerGatesTag"),
361  Comment("label of the input trigger gate data product (no instance name)")
362  };
363 
364  fhicl::Sequence<raw::ADC_Count_t> Thresholds {
365  Name("Thresholds"),
366  Comment("thresholds to consider [ADC counts]")
367  };
368 
369  fhicl::Atom<unsigned int> MinimumPrimitives {
370  Name("MinimumPrimitives"),
371  Comment("minimum required number of trigger primitives for the trigger")
372  };
373 
374  fhicl::Atom<microseconds> BeamGateDuration {
375  Name("BeamGateDuration"),
376  Comment("length of time interval when optical triggers are accepted")
377  };
378 
379  fhicl::Atom<std::uint32_t> BeamBits {
380  Name("BeamBits"),
381  Comment("bits to be set in the trigger object as beam identified")
382  };
383 
384  fhicl::Atom<nanoseconds> TriggerTimeResolution {
385  Name("TriggerTimeResolution"),
386  Comment("resolution of trigger in time"),
387  8_ns
388  };
389 
390  fhicl::Atom<std::string> LogCategory {
391  Name("LogCategory"),
392  Comment("name of the category used for the output"),
393  "SlidingWindowTrigger" // default
394  };
395 
396  }; // struct Config
397 
398  using Parameters = art::EDProducer::Table<Config>;
399  // --- END Configuration -----------------------------------------------------
400 
401 
402  // --- BEGIN Constructors ----------------------------------------------------
403  explicit MajorityTriggerSimulation(Parameters const& config);
404 /*
405  // Plugins should not be copied or assigned.
406  MajorityTriggerSimulation(MajorityTriggerSimulation const&) = delete;
407  MajorityTriggerSimulation(MajorityTriggerSimulation&&) = delete;
408  MajorityTriggerSimulation& operator=(MajorityTriggerSimulation const&) = delete;
409  MajorityTriggerSimulation& operator=(MajorityTriggerSimulation&&) = delete;
410 */
411  // --- END Constructors ------------------------------------------------------
412 
413 
414  // --- BEGIN Framework hooks -------------------------------------------------
415 
416  /// Initializes the plots.
417  virtual void beginJob() override;
418 
419  /// Runs the simulation and saves the results into the _art_ event.
420  virtual void produce(art::Event& event) override;
421 
422  /// Prints end-of-job summaries.
423  virtual void endJob() override;
424 
425  // --- END Framework hooks ---------------------------------------------------
426 
427 
428  private:
429 
430  using TriggerInfo_t = details::TriggerInfo_t; ///< Type alias.
431 
432  /// Type of trigger gate extracted from the input event.
433  using InputTriggerGate_t
435 
436  /// A list of trigger gates from input.
437  using TriggerGates_t = std::vector<InputTriggerGate_t>;
438 
439 
440  // --- BEGIN Configuration variables -----------------------------------------
441 
442  /// ADC thresholds to read, and the input tag connected to their data.
443  std::map<icarus::trigger::ADCCounts_t, art::InputTag> fADCthresholds;
444 
445  /// Minimum number of trigger primitives for a trigger to happen.
446  unsigned int const fMinimumPrimitives;
447 
448  /// Duration of the gate during with global optical triggers are accepted.
450 
451  nanoseconds fTriggerTimeResolution; ///< Trigger resolution in time.
452 
453  std::uint32_t fBeamBits; ///< Bits for the beam gate being simulated.
454 
455  /// Message facility stream category for output.
456  std::string const fLogCategory;
457 
458  // --- END Configuration variables -------------------------------------------
459 
460  // --- BEGIN Service variables -----------------------------------------------
461 
463 
464  /// ROOT directory where all the plots are written.
465  art::TFileDirectory fOutputDir;
466 
467  // --- END Service variables -------------------------------------------------
468 
469 
470  // --- BEGIN Internal variables ----------------------------------------------
471 
472  MajorityTriggerCombiner const fCombiner; ///< Algorithm to combine primitives.
473 
474  /// Algorithm to sort trigger gates by cryostat or TPC.
476 
477  /// All plots in one practical sandbox.
479 
480  ///< Count of fired triggers, per threshold.
481  std::vector<std::atomic<unsigned int>> fTriggerCount;
482  std::atomic<unsigned int> fTotalEvents { 0U }; ///< Count of fired triggers.
483 
484 
485  // TODO this is not multithread-safe, needs a mutex
486  /// Functor returning whether a gate has changed.
489 
490  // --- END Internal variables ------------------------------------------------
491 
492 
493 
494  // --- BEGIN Derived class methods -------------------------------------------
495 
496  /// @brief Initializes the full set of plots (all ADC thresholds).
497  void initializePlots();
498 
499  /**
500  * @brief Performs the simulation for the specified ADC threshold.
501  * @param event _art_ event to read data from and put results into
502  * @param iThr index of the threshold in the configuration
503  * @param thr value of the threshold (ADC counts)
504  * @return a simple copy of the trigger response information
505  *
506  * For the given threshold, the simulation of the configured trigger is
507  * performed.
508  * The input data is read from the event (the source tag is from the module
509  * configuration), simulation is performed, auxiliary plots are drawn and
510  * a `raw::Trigger` collection is stored into the event.
511  *
512  * The stored collection contains either one or zero `raw::Trigger` elements.
513  *
514  * The simulation itself is performed by the `simulate()` method.
515  */
516  TriggerInfo_t produceForThreshold(
517  art::Event& event,
519  ApplyBeamGateClass const& beamGate,
520  std::size_t const iThr, icarus::trigger::ADCCounts_t const thr
521  );
522 
523  /**
524  * @brief Performs the simulation of the configured trigger on `gates` input.
525  * @param gates the input to the trigger simulation
526  * @return the outcome and details of the trigger simulation
527  *
528  * The simulation is performed using the input single trigger requests
529  * (or trigger primitives) from the `gates` collection.
530  *
531  * The gates are split by cryostat, and the simulation is performed
532  * independently on each cryostat (`simulateCryostat()`).
533  * Finally, the cryostat triggers are combined (OR) into the final trigger
534  * decision, bearing as time the earliest one.
535  */
536  TriggerInfo_t simulate(ApplyBeamGateClass const& clockData,
537  TriggerGates_t const& gates) const;
538 
539  /**
540  * @brief Simulates the trigger response within a single cryostat.
541  * @param beamGate the beam gate to be applied
542  * @param iCryo index of the cryostat being processed
543  * @param gates the trigger primitives to be considered
544  * @return the outcome and details of the trigger simulation
545  *
546  * The simulation computes the count of trigger `gates` open at any time,
547  * sets it in coincidence with the beam gate, and fires a trigger if within
548  * that gate the count of open `gates` is equal or larger than the threshold
549  * configured (`MinimumPrimitives`).
550  * The time is the earliest one when that requirement is met.
551  */
552  TriggerInfo_t simulateCryostat(
553  ApplyBeamGateClass const& beamGate,
554  std::size_t iCryo,
555  TriggerGates_t const& gates
556  ) const;
557 
558 
559  /**
560  * @brief Converts the trigger information into a `raw::Trigger` object.
561  * @param triggerNumber the unique number to assign to this trigger
562  * @param info the information about the fired trigger
563  * @return a `raw::Trigger` object with all the information encoded
564  *
565  * The trigger described by `info` is encoded into a `raw::Trigger` object.
566  * The trigger _must_ have fired.
567  */
568  raw::Trigger triggerInfoToTriggerData
570  unsigned int triggerNumber, TriggerInfo_t const& info) const;
571 
572  /// Fills the plots for threshold index `iThr` with trigger information.
573  void plotTriggerResponse
574  (std::size_t iThr, TriggerInfo_t const& triggerInfo) const;
575 
576 
577  /// Prints the summary of fired triggers on screen.
578  void printSummary() const;
579 
580 
581  //@{
582  /// Shortcut to create an `ApplyBeamGate` with the current configuration.
584  (art::Event const* event = nullptr) const
585  {
586  return makeApplyBeamGate(
587  fBeamGateDuration,
590  );
591  }
593  (art::Event const& event) const { return makeMyBeamGate(&event); }
594  //@}
595 
596 
597 }; // icarus::trigger::MajorityTriggerSimulation
598 
599 
600 
601 //------------------------------------------------------------------------------
602 //--- Implementation
603 //------------------------------------------------------------------------------
605  (Parameters const& config)
606  : art::EDProducer (config)
607  // configuration
608  , fMinimumPrimitives (config().MinimumPrimitives())
609  , fBeamGateDuration (config().BeamGateDuration())
610  , fTriggerTimeResolution(config().TriggerTimeResolution())
611  , fBeamBits (config().BeamBits())
612  , fLogCategory (config().LogCategory())
613  // services
614  , fGeom (*lar::providerFrom<geo::Geometry>())
615  , fOutputDir (*art::ServiceHandle<art::TFileService>())
616  // internal and cached
617  , fCombiner (fLogCategory)
618  , fChannelSplitter(fGeom, fLogCategory)
619  , fPlots(
620  fOutputDir, "", "minimum primitives: " + std::to_string(fMinimumPrimitives)
621  )
622 {
623 
624  //
625  // more complex parameter parsing
626  //
627  std::string const discrModuleLabel = config().TriggerGatesTag();
628  for (raw::ADC_Count_t threshold: config().Thresholds()) {
629  fADCthresholds[icarus::trigger::ADCCounts_t{threshold}]
630  = art::InputTag{ discrModuleLabel, util::to_string(threshold) };
631  }
632 
633  // initialization of a vector of atomic is not as trivial as it sounds...
634  fTriggerCount = std::vector<std::atomic<unsigned int>>(fADCthresholds.size());
635  std::fill(fTriggerCount.begin(), fTriggerCount.end(), 0U);
636 
637 
638  //
639  // input data declaration
640  //
641  using icarus::trigger::OpticalTriggerGateData_t; // for convenience
642 
643  // trigger primitives
644  for (art::InputTag const& inputDataTag: util::const_values(fADCthresholds)) {
645  consumes<std::vector<OpticalTriggerGateData_t>>(inputDataTag);
646  consumes<art::Assns<OpticalTriggerGateData_t, raw::OpDetWaveform>>
647  (inputDataTag);
648  } // for
649 
650  //
651  // output data declaration
652  //
653  for (art::InputTag const& inputDataTag: util::const_values(fADCthresholds))
654  produces<std::vector<raw::Trigger>>(inputDataTag.instance());
655 
656 
657  mf::LogInfo(fLogCategory)
658  << "Requirement of minimum " << fMinimumPrimitives << " primitives.";
659 
660 
661 } // icarus::trigger::MajorityTriggerSimulation::MajorityTriggerSimulation()
662 
663 
664 //------------------------------------------------------------------------------
666 
667  initializePlots();
668 
669 } // icarus::trigger::MajorityTriggerSimulation::beginJob()
670 
671 
672 //------------------------------------------------------------------------------
674 
675  mf::LogDebug log(fLogCategory);
676  log << "Event " << event.id() << ":";
677 
678  auto const clockData
679  = art::ServiceHandle<detinfo::DetectorClocksService const>()->DataFor(event);
680  detinfo::DetectorTimings const detTimings{clockData};
681  auto const beamGate = makeMyBeamGate(event);
682 
683  if (auto oldGate = fGateChangeCheck(beamGate); oldGate) {
684  mf::LogWarning(fLogCategory)
685  << "Beam gate has changed from " << *oldGate << " to " << beamGate << "!";
686  }
687 
688 
689  for (auto const [ iThr, thr ]
690  : util::enumerate(util::get_elements<0U>(fADCthresholds))
691  ) {
692 
693  TriggerInfo_t const triggerInfo = produceForThreshold(event, detTimings, beamGate, iThr, thr);
694 
695  log << "\n * threshold " << thr << ": ";
696  if (triggerInfo) log << "trigger at " << triggerInfo.atTick();
697  else log << "not triggered";
698 
699  } // for
700 
701  ++fTotalEvents;
702 
703 } // icarus::trigger::MajorityTriggerSimulation::produce()
704 
705 
706 //------------------------------------------------------------------------------
708 
709  printSummary();
710 
711 } // icarus::trigger::MajorityTriggerSimulation::endJob()
712 
713 
714 //------------------------------------------------------------------------------
716 
717  //
718  // overview plots with different settings
719  //
720 
721  std::vector<std::string> thresholdLabels;
722  thresholdLabels.reserve(size(fADCthresholds));
723  for (art::InputTag const& inputDataTag: util::const_values(fADCthresholds))
724  thresholdLabels.push_back(inputDataTag.instance());
725 
726  auto const beamGate = makeMyBeamGate();
727  fGateChangeCheck(beamGate);
728  mf::LogInfo(fLogCategory)
729  << "Beam gate for plots: " << beamGate.asSimulationTime()
730  << " (simulation time), " << beamGate.tickRange()
731  << " (optical ticks)"
732  ;
733 
734  //
735  // Triggering efficiency vs. ADC threshold.
736  //
737  auto* NTriggers = fPlots.make<TH1F>(
738  "NTriggers",
739  "Number of triggering events"
740  ";PMT discrimination threshold [ ADC counts ]"
741  ";events",
742  thresholdLabels.size(), 0.0, double(thresholdLabels.size())
743  );
744  util::ROOT::applyAxisLabels(NTriggers->GetXaxis(), thresholdLabels);
745 
746  auto* Eff = fPlots.make<TEfficiency>(
747  "Eff",
748  "Efficiency of triggering"
749  ";PMT discrimination threshold [ ADC counts ]"
750  ";trigger efficiency",
751  thresholdLabels.size(), 0.0, double(thresholdLabels.size())
752  );
753  // people are said to have earned hell for things like this;
754  // but TEfficiency really does not expose the interface to assign labels to
755  // its axes, which supposedly could be done had we chosen to create it by
756  // histograms instead of directly as recommended.
758  (const_cast<TH1*>(Eff->GetTotalHistogram())->GetXaxis(), thresholdLabels);
759 
760  detinfo::timescales::optical_time_ticks const triggerResolutionTicks{
761  icarus::ns::util::makeDetTimings().toOpticalTicks(fTriggerTimeResolution)
762  };
763 
764  auto const& beamGateTicks = beamGate.tickRange();
765  auto* TrigTime = fPlots.make<TH2F>(
766  "TriggerTick",
767  "Trigger time tick"
768  ";optical time tick [ /" + util::to_string(triggerResolutionTicks) + " ]"
769  ";PMT discrimination threshold [ ADC counts ]"
770  ";events",
771  static_cast<int>(std::ceil(beamGate.lengthTicks()/triggerResolutionTicks)),
772  beamGateTicks.start().value(),
774  (beamGateTicks.start() + beamGate.lengthTicks(), triggerResolutionTicks)
775  .value(),
776  thresholdLabels.size(), 0.0, double(thresholdLabels.size())
777  );
778  util::ROOT::applyAxisLabels(TrigTime->GetYaxis(), thresholdLabels);
779 
780 
781 } // icarus::trigger::MajorityTriggerSimulation::initializePlots()
782 
783 
784 //------------------------------------------------------------------------------
786  art::Event& event,
788  ApplyBeamGateClass const& beamGate,
789  std::size_t const iThr, icarus::trigger::ADCCounts_t const thr
790 ) -> TriggerInfo_t {
791 
792  //
793  // get the input
794  //
795  art::InputTag const dataTag = fADCthresholds.at(thr);
796  auto const& gates = icarus::trigger::ReadTriggerGates(event, dataTag);
797 
798  //
799  // simulate the trigger response
800  //
801  TriggerInfo_t const triggerInfo = simulate(beamGate, gates);
802  if (triggerInfo) ++fTriggerCount[iThr]; // keep the unique count
803 
804  //
805  // fill the plots
806  //
807  plotTriggerResponse(iThr, triggerInfo);
808 
809  //
810  // create and store the data product
811  //
812  auto triggers = std::make_unique<std::vector<raw::Trigger>>();
813  if (triggerInfo.fired()) {
814  triggers->push_back
815  (triggerInfoToTriggerData(detTimings, fTriggerCount[iThr], triggerInfo));
816  } // if
817  event.put(std::move(triggers), dataTag.instance());
818 
819  return triggerInfo;
820 
821 } // icarus::trigger::MajorityTriggerSimulation::produceForThreshold()
822 
823 
824 //------------------------------------------------------------------------------
826  (ApplyBeamGateClass const& beamGate,
827  TriggerGates_t const& gates) const -> TriggerInfo_t
828 {
829 
830  /*
831  * 1. split the input by cryostat
832  * 2. simulate the cryostat trigger
833  * 3. combine the responses (earliest wins)
834  */
835 
836  // to use the splitter we need a *copy* of the gates
837  auto const& cryoGates = fChannelSplitter.byCryostat(TriggerGates_t{ gates });
838 
839  TriggerInfo_t triggerInfo; // not fired by default
840  for (auto const& [ iCryo, gatesInCryo ]: util::enumerate(cryoGates)) {
841 
842  triggerInfo.addAndReplaceIfEarlier
843  (simulateCryostat(beamGate, iCryo, gatesInCryo));
844 
845  } // for gates in cryostat
846 
847  triggerInfo.sortOpenings();
848 
849  return triggerInfo;
850 
851 } // icarus::trigger::MajorityTriggerSimulation::simulate()
852 
853 
854 //------------------------------------------------------------------------------
856  ApplyBeamGateClass const& beamGate,
857  std::size_t iCryo, TriggerGates_t const& gates
858  ) const
859  -> TriggerInfo_t
860 {
862 
863  /*
864  * 1. combine the trigger primitives
865  * 2. apply the beam gate on the combination
866  * 3. compute the trigger response
867  */
868 
869  auto const combinedCount = beamGate.apply(fCombiner.combine(gates));
870 
871  TriggerInfo_t triggerInfo;
873  { combinedCount, fMinimumPrimitives };
874  extractOpeningInfo.setLocation(iCryo);
875  while (extractOpeningInfo) {
876  auto info = extractOpeningInfo();
877  if (info) triggerInfo.add(info.value());
878  } // while
879 
880  return triggerInfo;
881 
882 } // icarus::trigger::MajorityTriggerSimulation::simulateCryostat()
883 
884 
885 //------------------------------------------------------------------------------
887  (std::size_t iThr, TriggerInfo_t const& triggerInfo) const
888 {
889 
890  fPlots.demand<TEfficiency>("Eff").Fill(triggerInfo.fired(), iThr);
891 
892  if (triggerInfo.fired()) {
893  fPlots.demand<TH1>("NTriggers").Fill(iThr);
894  fPlots.demand<TH2>("TriggerTick").Fill(triggerInfo.atTick().value(), iThr);
895  }
896 
897 } // icarus::trigger::MajorityTriggerSimulation::plotTriggerResponse()
898 
899 
900 //------------------------------------------------------------------------------
902 
903  //
904  // summary from our internal counters
905  //
906  mf::LogInfo log(fLogCategory);
907  log
908  << "Summary of triggers requiring " << fMinimumPrimitives
909  << "+ primitives for " << fTriggerCount.size() << " ADC thresholds:"
910  ;
911  for (auto const& [ count, thr ]
912  : util::zip(fTriggerCount, util::get_elements<0U>(fADCthresholds)))
913  {
914  log << "\n ADC threshold " << thr
915  << ": " << count << " events triggered";
916  if (fTotalEvents > 0U)
917  log << " (" << (double(count) / fTotalEvents * 100.0) << "%)";
918  } // for
919 
920 } // icarus::trigger::MajorityTriggerSimulation::printSummary()
921 
922 
923 //------------------------------------------------------------------------------
927  unsigned int triggerNumber, TriggerInfo_t const& info) const
928 {
929  assert(info.fired());
930 
931  return {
932  triggerNumber, // counter
933  double(detTimings.toElectronicsTime(info.atTick())), // trigger time
934  double(detTimings.BeamGateTime()), // beam gate in electronics time scale
935  fBeamBits // bits
936  };
937 
938 } // icarus::trigger::MajorityTriggerSimulation::triggerInfoToTriggerData()
939 
940 
941 //------------------------------------------------------------------------------
943 
944 
945 //------------------------------------------------------------------------------
bool addAndReplaceIfEarlier(TriggerInfo_t const &other)
If other has fired, and at an earlier tick, set a new main().
BEGIN_PROLOG BeamGateDuration pmtthr physics producers trigtilewindowORS Thresholds
bool fired() const
Returns whether the trigger fired.
std::vector< std::atomic< unsigned int > > fTriggerCount
TriggerInfo_t simulateCryostat(ApplyBeamGateClass const &beamGate, std::size_t iCryo, TriggerGates_t const &gates) const
Simulates the trigger response within a single cryostat.
electronics_time BeamGateTime() const
auto maxGates(GateColl const &gates)
Computes the gate with the maximum opening of gates from collection.
An empty class that can&#39;t be copied nor moved.
TriggerInfo_t simulate(ApplyBeamGateClass const &clockData, TriggerGates_t const &gates) const
Performs the simulation of the configured trigger on gates input.
Utilities related to art service access.
Utilities for the conversion of trigger gate data formats.
std::string const fLogCategory
Message facility stream category for output.
ApplyBeamGateClass makeApplyBeamGate(util::quantities::intervals::microseconds duration, util::quantities::intervals::microseconds delay, detinfo::DetectorClocksData const &clockData, std::string const &logCategory="ApplyBeamGateClass")
Returns a new ApplyBeamGateClass object with the specified gate.
void sortOpenings()
Sorts all openings by time.
constexpr T roundup(T const value, U const quantum, T const offset=T{})
Returns the value, rounded up.
Definition: rounding.h:112
Definition of util::get_elements() and util::get_const_elements().
Definition of util::enumerate().
A wrapper to trigger gate objects tracking the input of operations.
microseconds_as<> microseconds
Type of time interval stored in microseconds, in double precision.
Definition: spacetime.h:259
Defines classes that can&#39;t be copied nor moved.
art::TFileDirectory fOutputDir
ROOT directory where all the plots are written.
std::map< icarus::trigger::ADCCounts_t, art::InputTag > fADCthresholds
ADC thresholds to read, and the input tag connected to their data.
GeometryChannelSplitter(geo::GeometryCore const &geom, std::string const &logCategory="GeometryChannelSplitter")
Base class facilitating logging to message facility.
Derivative information from raw::OpDetWaveform data.
Helper to manage a beam gate.
decltype(auto) const_values(Coll &&coll)
Range-for loop helper iterating across the constant values of the specified collection.
Utilities for numerical rounding.
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
Helper applying a beam gate to any gate.
Definition: ApplyBeamGate.h:85
Helper to check if an object has changed. Thread-safe.
unsigned int NOpChannels() const
Number of electronics channels for all the optical detectors.
pure virtual base interface for detector clocks
Helper for logging classes.
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
unsigned int const fMinimumPrimitives
Minimum number of trigger primitives for a trigger to happen.
Combines a group of trigger gates for majority trigger. Glorified Sum().
timescale_traits< OpticalTimeCategory >::tick_interval_t optical_time_ticks
Interface to detinfo::DetectorClocks.
fDetProps &fDetProps fDetProps &fDetProps fLogCategory
Access the description of detector geometry.
std::vector< geo::CryostatID > const fChannelCryostat
Map: optical channel ID -&gt; number of the cryostat with that channel.
raw::Trigger triggerInfoToTriggerData(detinfo::DetectorTimings const &detTimings, unsigned int triggerNumber, TriggerInfo_t const &info) const
Converts the trigger information into a raw::Trigger object.
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
electronics_time toElectronicsTime(FromTime time) const
Converts a time point into electronics time scale.
Ticks toOpticalTicks(time_interval time) const
Returns the optical ticks corresponding to a time interval.
decltype(auto) constexpr to_string(T &&obj)
ADL-aware version of std::to_string.
GateObj combine(std::vector< GateObj > const &gates) const
Combines all the gates (by cryostat) in a single majority gate.
Simple functions to streamline the creation of DetectorClocksData.
A value measured in the specified unit.
Definition: quantities.h:566
Simple type definitions for trigger algorithms.
std::vector< std::vector< GateObj > > byCryostat(std::vector< GateObj > &&gates) const
Splits the gates by cryostat.
void plotTriggerResponse(std::size_t iThr, TriggerInfo_t const &triggerInfo) const
Fills the plots for threshold index iThr with trigger information.
A trigger gate data object for optical detector electronics.
icarus::trigger::ReadoutTriggerGate< TriggerGateTick_t, TriggerGateTicks_t, raw::Channel_t > OpticalTriggerGateData_t
Type of trigger gate data serialized into art data products.
A bunch of diverse utilities and futilities related to ROOT.
std::uint32_t fBeamBits
Bits for the beam gate being simulated.
unsigned int const fNCryostats
Number of cryostats in the detector.
virtual void endJob() override
Prints end-of-job summaries.
icarus::ns::util::ThreadSafeChangeMonitor< icarus::trigger::ApplyBeamGateClass > fGateChangeCheck
Functor returning whether a gate has changed.
Utilities for the conversion of trigger gate data formats.
void fill(const art::PtrVector< recob::Hit > &hits, int only_plane)
BEGIN_PROLOG vertical distance to the surface Name
void initializePlots()
Initializes the full set of plots (all ADC thresholds).
Test of util::counter and support utilities.
Simulates a &quot;majority&quot; trigger.
Description of geometry of one entire detector.
static std::vector< geo::CryostatID > makeChannelCryostatMap(geo::GeometryCore const &geom)
Creates a map like fChannelCryostat from the geometry information.
An interval (duration, length, distance) between two quantity points.
Definition: intervals.h:114
Definition of data types for geometry description.
auto sumGates(GateColl const &gates)
Sums all the gates in a collection.
GeometryChannelSplitter fChannelSplitter
Algorithm to sort trigger gates by cryostat or TPC.
physics simulate
nanoseconds_as<> nanoseconds
Type of time interval stored in nanoseconds, in double precision.
Definition: spacetime.h:270
std::vector< InputTriggerGate_t > TriggerGates_t
A list of trigger gates from input.
Utilities to read interval and point quantity FHiCL configuration.
Gate apply(Gate gate) const
Returns a copy of gate in AND with this beam gate.
timescale_traits< OpticalTimeCategory >::tick_t optical_tick
Classes to detect the change of object values.
optical_tick atTick() const
Returns the time of the trigger (undefined if !fired()).
virtual void produce(art::Event &event) override
Runs the simulation and saves the results into the art event.
std::string to_string(WindowPattern const &pattern)
virtual void beginJob() override
Initializes the plots.
Dimensioned variables representing space or time quantities.
Helper class to store transient trigger result.
A class exposing an upgraded interface of detinfo::DetectorClocksData.
Data types for detinfo::DetectorTimings.
Functions pulling in STL customization if available.
Class to create an object representing a beam gate.
GateObj combine(std::vector< GateObj > const &cryoGates) const
Combines all the gates (by cryostat) in a single majority gate.
void printSummary() const
Prints the summary of fired triggers on screen.
A simple alias for a most commonly used TrackedTriggerGate type.
microseconds fBeamGateDuration
Duration of the gate during with global optical triggers are accepted.
geo::OpDetID const & ID() const
Returns the geometry ID of this optical detector.
Definition: OpDetGeo.h:72
auto zip(Iterables &&...iterables)
Range-for loop helper iterating across many collections at the same time.
Definition: zip.h:295
temporary value
fDetProps &fDetProps fDetProps &fDetProps detTimings
Definition of util::values() and util::const_values().
std::size_t count(Cont const &cont)
MajorityTriggerCombiner const fCombiner
Algorithm to combine primitives.
Combines cryostat triggers via OR. Glorified Max().
std::vector< icarus::trigger::TrackedOpticalTriggerGate< OpDetInfo > > ReadTriggerGates(Event const &event, art::InputTag const &dataTag)
Assembles and returns trigger gates from serialized data.
detinfo::DetectorClocksData makeDetClockData(art::Event const *event)
Returns a detinfo::DetectorClocksData from DetectorClocksService.
TriggerInfo_t produceForThreshold(art::Event &event, detinfo::DetectorTimings const &detTimings, ApplyBeamGateClass const &beamGate, std::size_t const iThr, icarus::trigger::ADCCounts_t const thr)
Performs the simulation for the specified ADC threshold.
OpDetGeo const & OpDetGeoFromOpChannel(unsigned int OpChannel) const
Returns the geo::OpDetGeo object for the given channel number.
nanoseconds fTriggerTimeResolution
Trigger resolution in time.
bool IsValidOpChannel(int opChannel) const
Is this a valid OpChannel number?
Helper to extract OpeningInfo_t from a trigger gate.
Definition: TriggerInfo_t.h:30
A helper to manage ROOT objects in a art::TFileDirectory.
art framework interface to geometry description
detinfo::DetectorTimings makeDetTimings(art::Event const *event)
Returns a detinfo::DetectorTimings from DetectorClocksService.
Combines cryostat triggers via OR. Glorified Max().
icarus::trigger::PlotSandbox fPlots
All plots in one practical sandbox.
A helper to manage ROOT objects with consistent naming.
Definition: PlotSandbox.h:95