All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
DiscriminatePMTwaveformsByChannel_module.cc
Go to the documentation of this file.
1 /**
2  * @file DiscriminatePMTwaveformsByChannel_module.cc
3  * @brief Module producing discriminated waveforms.
4  * @author Gianluca Petrillo (petrillo@slac.stanford.edu)
5  * @date February 25, 2021
6  */
7 
8 
9 // ICARUS libraries
13 #include "icaruscode/PMT/Algorithms/OpDetWaveformMetaUtils.h" // OpDetWaveformMetaMaker
19 #include "icarusalg/Utilities/FHiCLutils.h" // util::fhicl::getOptionalValue()
20 
21 // LArSoft libraries
24 #include "larcore/CoreUtils/ServiceUtil.h" // lar::providerFrom()
26 #include "lardataalg/Utilities/quantities_fhicl.h" // for ADCCounts_t parameters
28 #include "larcorealg/CoreUtils/values.h" // util::const_values()
31 #include "larcorealg/CoreUtils/StdUtils.h" // util::to_string()
32 
33 // framework libraries
34 #include "art/Framework/Core/ModuleMacros.h"
35 #include "art/Framework/Core/EDProducer.h"
36 #include "art/Framework/Principal/Event.h"
37 #include "art/Framework/Principal/Run.h"
38 #include "art/Framework/Principal/Handle.h"
39 #include "art/Persistency/Common/PtrMaker.h"
40 #include "art/Utilities/make_tool.h"
41 #include "canvas/Persistency/Common/Assns.h"
42 #include "canvas/Persistency/Common/Ptr.h"
43 #include "canvas/Utilities/InputTag.h"
44 #include "cetlib_except/exception.h"
45 #include "messagefacility/MessageLogger/MessageLogger.h"
46 #include "fhiclcpp/types/OptionalSequence.h"
47 #include "fhiclcpp/types/OptionalAtom.h"
48 #include "fhiclcpp/types/Table.h"
49 #include "fhiclcpp/types/TableAs.h"
50 #include "fhiclcpp/types/Sequence.h"
51 #include "fhiclcpp/types/Atom.h"
52 #include "fhiclcpp/types/DelegatedParameter.h"
53 #include "fhiclcpp/ParameterSet.h"
54 
55 // C/C++ standard libraries
56 #include <map>
57 #include <optional>
58 #include <vector>
59 #include <string>
60 
61 
62 //------------------------------------------------------------------------------
63 namespace icarus::trigger { class DiscriminatePMTwaveformsByChannel; }
64 
65 /**
66  * @brief Produces discriminated optical waveforms.
67  *
68  * This module produces a "discriminated waveform" for each optical detector
69  * channel and for a single set of discrimination thresholds.
70  * The thresholds are specified one per channel.
71  *
72  *
73  * Output data products
74  * =====================
75  *
76  * The output data products can be assigned an instance name
77  * (`OutputInstanceName` configuration parameter) if desired. They are:
78  *
79  * * `std::vector<icarus::trigger::OpticalTriggerGate::GateData_t>`,
80  * with one entry per optical channel, including _all_ optical channels
81  * (even when no signal above threshold is ever present); each gate object
82  * has as index in the vector the number of the optical channel;
83  * * an _art_ association, one trigger gate data to many optical waveforms,
84  * of each trigger gate data (as above) with all the optical waveforms
85  * which contributed to it; the order of the association pairs is the same
86  * as the one of the trigger gate data, i.e. by channel, and some trigger
87  * gate data entries may be not associated to any waveform and therefore
88  * they can not be listed in the association; association pairs within
89  * the same optical channel are sorted by optical waveform timestamp;
90  * the type of the association is
91  * `art::Assns<icarus::trigger::OpticalTriggerGate::GateData_t, raw::OpDetWaveform>`;
92  * similarly, if `SavePMTcoverage` parameter was set, also an association
93  * `art::Assns<icarus::trigger::OpticalTriggerGate::GateData_t, sbn::OpDetWaveformMeta>`
94  * is produced.
95  * * `std::vector<sbn::OpDetWaveformMeta>` parallel to the input optical
96  * detector waveforms, if `SavePMTcoverage` parameter is set; each entry in
97  * the collection is matched with the corresponding one in the input waveform
98  * collection; an association
99  * `art::Assns<sbn::OpDetWaveformMeta, raw::OpDetWaveform>` is also produced.
100  *
101  *
102  * Input data products
103  * ====================
104  *
105  * * `std::vector<raw::OpDetWaveform>`: a single waveform for each recorded
106  * optical detector activity; the activity belongs to a single channel, but
107  * there may be multiple waveforms on the same channel. The time stamp is
108  * expected to be from the
109  * @ref DetectorClocksElectronicsTime "electronics time scale"
110  * and therefore expressed in microseconds.
111  *
112  *
113  * Service requirements
114  * ---------------------
115  *
116  * The following services are _required_:
117  *
118  * * an implementation of `detinfo::DetectorClocksService`
119  * * _art_ message facility
120  *
121  *
122  * Configuration parameters
123  * =========================
124  *
125  * There are several (too many?) ways to specify the discrimination thresholds.
126  * In the end, all PMT channels must be assigned a threshold: if at any time,
127  * on any event, a channel is found whose threshold was not set, an exception
128  * is thrown.
129  * All threshold specifications are relative to the baseline. There are
130  * currently four levels of threshold setting:
131  *
132  * 1. if `NChannels` (positive integer) and `DefaultThreshold` (integral number)
133  * are both specified, the first `NChannels` PMT channels are assigned the
134  * `DefaultThreshold`; note that they both need to be specified at the same
135  * time, and when they are, channels with ID equal or larger than
136  * `NChannels` are still not configured, i.e. they will not be assigned the
137  * `DefaultThreshold` and if encountered an exception will be thrown;
138  * 2. `ThresholdList` (list of integers) will define a threshold for each
139  * channel starting with ID `0` on; thresholds already set with the previous
140  * step will be overridden;
141  * 3. `ThresholdsFromPMTconfig` (input tag) points to a data product with
142  * a PMT configuration (`sbn::PMTconfiguration`) which contains
143  * information on each channel, including the thresholds; thresholds are set
144  * for each channel from the value returned by
145  * `sbn::V1730channelConfiguration::relativeThreshold()` for that channel;
146  * only channel configuration entries that have a offline channel ID
147  * (`sbn::V1730channelConfiguration::hasChannelID()`) are configured in this
148  * way; thresholds already set with the previous steps will be overridden;
149  * when using this parameter, `NChannels` is also mandatory and channels
150  * with ID greater or equal than `NChannels` will trigger an exception.
151  * 4. `Thresholds` (list of tables): in each entry, a threshold is specified for
152  * a single channel; the format of the entry is:
153  * * `Channel` (integral, mandatory): channel number;
154  * * `Threshold` (integral, mandatory): the threshold for that channel.
155  * .
156  * Thresholds already set with the previous steps will be overridden.
157  *
158  * All these are optional (but if the configuration has none, not much can be
159  * done since there will be no channel configured).
160  *
161  * A terse description of the remaining parameters follows.
162  *
163  * * `OpticalWaveforms` (input tag): the data product containing all optical
164  * detector waveforms.
165  * * `Baselines` (input tag): the data product containing one baseline per
166  * waveform in data product (from `OpticalWaveforms`).
167  * * `NChannels` (integral): if specified, only channels with ID from `0` to
168  * `NChannels - 1` are processed; waveforms from channels with higher ID will
169  * be ignored. If `DefaultThreshold` or `ThresholdsFromConfiguration`
170  * parameters are specified (see above), then `NChannels` is mandatory;
171  * otherwise, its value is deduced from the highest channel configured with
172  * one of the other threshold parameters.
173  * * `TriggerGateBuilder` (tool configuration): template configuration of the
174  * _art_ tool used to discriminate the optional waveforms; the tool interface
175  * is `icarus::trigger::TriggerGateBuilder`. Note that the threshold settings
176  * of this configuration is going to be ignored.
177  * * `SavePMTcoverage` (flag, default: `true`): also produces a collection of
178  * `sbn::OpDetWaveformMeta` representing each of the input waveforms; trigger
179  * tools can use this information in place for the more space-hungry waveforms
180  * for further processing.
181  * * `OutputCategory` (string, default: `"DiscriminatePMTwaveformsByChannel"`):
182  * label for the category of messages in the console output; this is the label
183  * that can be used for filtering messages via MessageFacility service.
184  * configuration.
185  *
186  *
187  */
189 {
190 
191  public:
192 
193  /// The type of data produced for a single channel.
195 
196  /// Collection of settings for one channel.
197  struct ChannelInfo_t {
199  std::optional<raw::ADC_Count_t> baseline;
200  std::optional<raw::ADC_Count_t> threshold;
201 
202  bool operator== (ChannelInfo_t const& other) const;
203  bool operator< (ChannelInfo_t const& other) const;
204  }; // ChannelInfo_t
205 
206 
207  // --- BEGIN Configuration ---------------------------------------------------
208 
209  struct ChannelConfig {
210 
211  using Name = fhicl::Name;
212  using Comment = fhicl::Comment;
213 
214  fhicl::Atom<raw::Channel_t> Channel {
215  Name("Channel"),
216  Comment("off-line channel ID")
217  };
218 
219  fhicl::OptionalAtom<raw::ADC_Count_t> Baseline {
220  Name("Baseline"),
221  Comment("the baseline for this channel [ADC]")
222  };
223 
224  fhicl::OptionalAtom<raw::ADC_Count_t> Threshold {
225  Name("Threshold"),
226  Comment("threshold relative to the baseline for this channel [ADC]")
227  };
228 
229  }; // ChannelConfig
230 
231  struct Config {
232 
233  using Name = fhicl::Name;
234  using Comment = fhicl::Comment;
235 
236  fhicl::Atom<art::InputTag> OpticalWaveforms {
237  Name("OpticalWaveforms"),
238  Comment("label of input digitized optical waveform data product"),
239  "opdaq" // tradition demands
240  };
241 
242  fhicl::OptionalAtom<art::InputTag> Baselines {
243  Name("Baselines"),
244  Comment("label of input waveform baselines (parallel to the waveforms)")
245  };
246 
247  fhicl::OptionalAtom<float> Baseline {
248  Name("Baseline"),
249  Comment("constant baseline for all waveforms, in ADC counts")
250  };
251 
252  fhicl::DelegatedParameter TriggerGateBuilder_ {
253  Name("TriggerGateBuilder"),
254  Comment
255  ("parameters for generating trigger gates from optical channel output")
256  };
257 
258  fhicl::OptionalAtom<raw::ADC_Count_t> DefaultThreshold {
259  Name("DefaultThreshold"),
260  Comment("threshold for all channels up to NChannel [ADC]")
261  };
262 
263  fhicl::Sequence<raw::ADC_Count_t> ThresholdList {
264  Name("ThresholdList"),
265  Comment("relative thresholds for each channel from #0 on [ADC]"),
266  std::vector<raw::ADC_Count_t>{}
267  };
268 
269  fhicl::OptionalAtom<art::InputTag> ThresholdsFromPMTconfig {
270  Name("ThresholdsFromPMTconfig"),
271  Comment("read thresholds from this PMT configuration run by run")
272  };
273 
274  fhicl::Sequence<fhicl::TableAs<ChannelInfo_t, ChannelConfig>> Thresholds {
275  Name("Thresholds"),
276  Comment
277  ("thresholds for the specified channels; overrides `ThresholdList`"),
278  std::vector<ChannelInfo_t>{}
279  };
280 
281 
282  fhicl::OptionalAtom<unsigned int> NChannels {
283  Name("NChannels"),
284  Comment("minimum number of channels to provide (default: all)")
285  };
286 
287  fhicl::Atom<bool> SavePMTcoverage {
288  Name("SavePMTcoverage"),
289  Comment("write also a sbn::OpDetWaveformMeta collection"),
290  true
291  };
292 
293  fhicl::Atom<std::string> OutputInstanceName {
294  Name("OutputInstanceName"),
295  Comment("instance name for the output products (none by default)"),
296  "" // default
297  };
298 
299  fhicl::Atom<std::string> OutputCategory {
300  Name("OutputCategory"),
301  Comment("tag of the module output to console via message facility"),
302  "DiscriminatePMTwaveformsByChannel"
303  };
304 
305  }; // struct Config
306 
307  using Parameters = art::EDProducer::Table<Config>;
308  // --- END Configuration -----------------------------------------------------
309 
310 
311  // --- BEGIN Constructors ----------------------------------------------------
312 
313  explicit DiscriminatePMTwaveformsByChannel(Parameters const& config);
314 
315  // --- END Constructors ------------------------------------------------------
316 
317 
318  // --- BEGIN Framework hooks -------------------------------------------------
319  /// Prepares the plots to be filled.
320  /// Prepares the plots to be filled.
321  virtual void beginRun(art::Run& run) override;
322 
323  /// Creates the data products.
324  virtual void produce(art::Event& event) override;
325 
326  // --- END Framework hooks ---------------------------------------------------
327 
328 
329  private:
330 
332 
333  // --- BEGIN Configuration variables -----------------------------------------
334 
335  art::InputTag const fOpDetWaveformTag; ///< Input optical waveform tag.
336 
337  ///< Input waveform baseline tag.
338  std::optional<art::InputTag> const fBaselineTag;
339 
340  std::optional<float> const fBaseline; ///< A constant baseline level.
341 
342  unsigned int const fNOpDetChannels; ///< Number of optical detector channels.
343 
344  /// Default threshold.
345  std::optional<raw::ADC_Count_t> const fDefaultThreshold;
346 
347  /// The thresholds configured by a sequence parameter.
348  std::vector<raw::ADC_Count_t> const fThresholdList;
349 
350  /// Tag of `PMTconfiguration` data product to read thresholds from.
351  std::optional<art::InputTag> const fThresholdsFromPMTconfig;
352 
353  /// A map of thresholds: channel ID -> relative threshold value [ADC]
354  std::vector<ChannelInfo_t> const fChannelInfos;
355 
356  std::string const fOutputInstanceName; ///< Instance name for output.
357 
358  bool const fSavePMTcoverage; ///< Whether to save also `sbn::OpDetWaveformMeta`.
359 
360  /// Category name for the console output stream.
361  std::string const fLogCategory;
362 
363  // --- END Configuration variables -------------------------------------------
364 
365 
366  // --- BEGIN Service variables -----------------------------------------------
367 
368  // --- END Service variables -------------------------------------------------
369 
370 
371  // --- BEGIN Algorithms ------------------------------------------------------
372 
373  /// PMT readout configuration for the current run.
374  std::optional<sbn::PMTconfiguration> fPMTconfig;
375 
376  /// Current baseline list from run (one per channel).
377  std::vector<icarus::WaveformBaseline> fBaselinesRun;
378 
379  enum class Source_t
381 
382  /// Information on the current setting of a threshold.
384  Source_t source = Source_t::Unset; ///< Where the value comes from.
385  icarus::trigger::ADCCounts_t value {}; ///< Threshold value.
386  bool isUnset() const noexcept { return source == Source_t::Unset; }
387  }; // ADCvalueSetting_t
388 
389  /// A map of current thresholds: channel ID -> relative threshold value [ADC]
390  std::vector<ADCvalueSetting_t> fCurrentThresholds;
391 
392  /// A map of current baselines: channel ID -> baseline [ADC]
393  std::vector<ADCvalueSetting_t> fCurrentBaselines;
394 
395  /// Algorithms to simulate trigger gates out of optical channel output.
396  std::unique_ptr<icarus::trigger::TriggerGateBuilder> fTriggerGateBuilder;
397 
398  /// Updates `fCurrentThresholds` with the current status of the object.
400 
401  /// Updates `fCurrentBaselines` with the current status of the object.
403 
404  /// Prints the current thresholds to the message logger.
405  template <typename Logger = mf::LogInfo>
407 
408  // --- END Algorithms --------------------------------------------------------
409 
410 
411  /**
412  * @brief Creates a collection with one gate per channel and no gaps.
413  * @param gates the trigger gates to be put in the collection
414  * @return a collection with with one gate per channel and no gaps
415  *
416  * The returned collection includes one gate per channel, and at least
417  * `fNOpDetChannels` channels (more if there are channels with higher number
418  * than that).
419  * For each channel, a gate is set in the item of the collection with index
420  * the channel ID (i.e. the first gate in the collection will be the one for
421  * channel `0`, the next the one for channel `1` and so on). It is assumed
422  * that there is only at most one gate per channel.
423  * The `gates` specified in the argument are _moved_ to the proper item in the
424  * returned collection. The other gates are assigned the proper channel number
425  * but are closed gates and associated with no waveform.
426  */
427  /// Fetches the optical detector count from geometry unless in `param`.
428  static unsigned int getNOpDetChannels
429  (fhicl::OptionalAtom<unsigned int> const& param);
430 
431  static std::vector<ChannelInfo_t> readChannelInfoSpecs
432  (std::vector<ChannelInfo_t> const& config);
433 
434 }; // icarus::trigger::DiscriminatePMTwaveformsByChannel
435 
436 
437 //------------------------------------------------------------------------------
438 //--- Implementation
439 //------------------------------------------------------------------------------
440 //--- icarus::trigger::DiscriminatePMTwaveformsByChannel::ChannelInfo_t
441 //------------------------------------------------------------------------------
442 bool icarus::trigger::DiscriminatePMTwaveformsByChannel::ChannelInfo_t::operator==
443  (ChannelInfo_t const& other) const
444 {
445  return (channel == other.channel)
446  && (baseline == other.baseline)
447  && (threshold == other.threshold)
448  ;
449 } // icarus::...::DiscriminatePMTwaveformsByChannel::ChannelInfo_t::operator==()
450 
451 
452 //------------------------------------------------------------------------------
453 namespace icarus::trigger {
454  // conversion function: ChannelConfig -> ChannelInfo_t
457  {
459  config.Channel(), // channel
460  util::fhicl::getOptionalValue(config.Baseline), // baseline
461  util::fhicl::getOptionalValue(config.Threshold) // threshold
462  };
463  } // icarus::trigger::convert()
464 } // namespace icarus::trigger
465 
466 
467 //------------------------------------------------------------------------------
468 bool icarus::trigger::DiscriminatePMTwaveformsByChannel::ChannelInfo_t::operator<
469  (ChannelInfo_t const& other) const
470  { return channel < other.channel; }
471 
472 
473 //------------------------------------------------------------------------------
474 //--- icarus::trigger::DiscriminatePMTwaveformsByChannel
475 //------------------------------------------------------------------------------
477  (Parameters const& config)
478  : art::EDProducer(config)
479  // configuration
480  , fOpDetWaveformTag(config().OpticalWaveforms())
481  , fBaselineTag(util::fhicl::getOptionalValue(config().Baselines))
482  , fBaseline(util::fhicl::getOptionalValue(config().Baseline))
483  , fNOpDetChannels(getNOpDetChannels(config().NChannels))
484  , fDefaultThreshold(util::fhicl::getOptionalValue(config().DefaultThreshold))
485  , fThresholdList(config().ThresholdList())
486  , fThresholdsFromPMTconfig
487  (util::fhicl::getOptionalValue(config().ThresholdsFromPMTconfig))
488  , fChannelInfos(readChannelInfoSpecs(config().Thresholds()))
489  , fOutputInstanceName(config().OutputInstanceName())
490  , fSavePMTcoverage(config().SavePMTcoverage())
491  , fLogCategory(config().OutputCategory())
492  , fTriggerGateBuilder
493  (
494  art::make_tool<icarus::trigger::TriggerGateBuilder>
495  (config().TriggerGateBuilder_.get<fhicl::ParameterSet>())
496  )
497 {
498 
499  if (config().DefaultThreshold.hasValue()
500  || config().ThresholdsFromPMTconfig.hasValue()
501  ) {
502  if (!config().NChannels.hasValue()) {
503  throw art::Exception(art::errors::Configuration)
504  << "Configuration parameter `" << config().NChannels.name()
505  << "` not specified: it is mandatory when either `"
506  << config().DefaultThreshold.name() << "` or `"
507  << config().ThresholdsFromPMTconfig.name() << "` are specified.\n";
508  }
509  }
510 
511  //
512  // optional configuration parameters
513  //
514  if (bool(fBaseline) + bool(fBaselineTag) != 1)
515  {
516  throw art::Exception(art::errors::Configuration)
517  << "Exactly one among options `" << config().Baselines.name() << "` ("
518  << (fBaselineTag? ("'" + fBaselineTag->encode() + "'"): "not set")
519  << ") and `" << config().Baseline.name() << "` ("
520  << (fBaseline? ("'" + std::to_string(*fBaseline) + "'"): "not set")
521  << ") must be specified.\n";
522  }
523 
524 
525  refreshCurrentThresholds();
526  refreshCurrentBaselines();
527  if (!fThresholdsFromPMTconfig) printCurrentThresholdsAndBaselines();
528 
529  //
530  // declaration of input
531  //
532  consumes<std::vector<raw::OpDetWaveform>>(fOpDetWaveformTag);
533 
534  if (fThresholdsFromPMTconfig)
535  consumes<sbn::PMTconfiguration, art::InRun>(*fThresholdsFromPMTconfig);
536 
537 
538  //
539  // declaration of output
540  //
541 
542  produces<std::vector<TriggerGateData_t>>(fOutputInstanceName);
543  produces<art::Assns<TriggerGateData_t, raw::OpDetWaveform>>
544  (fOutputInstanceName);
545  if (fSavePMTcoverage) {
546  produces<std::vector<sbn::OpDetWaveformMeta>>(fOutputInstanceName);
547  produces<art::Assns<raw::OpDetWaveform, sbn::OpDetWaveformMeta>>
548  (fOutputInstanceName);
549  produces<art::Assns<sbn::OpDetWaveformMeta, TriggerGateData_t>>
550  (fOutputInstanceName);
551  }
552 
553 } // icarus::trigger::DiscriminatePMTwaveformsByChannel::DiscriminatePMTwaveformsByChannel()
554 
555 
556 //------------------------------------------------------------------------------
558  (art::Run& run)
559 {
560  if (fThresholdsFromPMTconfig) {
561  auto const& PMTconfig
562  = run.getProduct<sbn::PMTconfiguration>(fThresholdsFromPMTconfig.value());
563 
564  if (!fPMTconfig || (fPMTconfig != PMTconfig)) {
565  fPMTconfig = PMTconfig;
566  refreshCurrentThresholds();
567  printCurrentThresholdsAndBaselines();
568  }
569  } // if thresholds from config
570 
571 } // icarus::trigger::DiscriminatePMTwaveformsByChannel::beginRun()
572 
573 
574 //------------------------------------------------------------------------------
576 
577  //
578  // set up the algorithm to create the trigger gates
579  //
581  art::ServiceHandle<detinfo::DetectorClocksService const>()->DataFor(event)
582  };
583 
584  //
585  // fetch input
586  //
587  auto const& waveformHandle
588  = event.getValidHandle<std::vector<raw::OpDetWaveform>>(fOpDetWaveformTag);
589  auto const& waveforms = *waveformHandle;
590 
591  // map address of waveform to art pointer to that waveform
592  auto const& opDetWavePtrs
593  = util::mapDataProductPointers(event, waveformHandle);
594 
595  //
596  // retrieve the baseline information if provided event by event
597  //
598  std::vector<icarus::WaveformBaseline> baselines;
599  if (fBaselineTag) {
600  // read and assign from the data product; configured baselines are ignored
601 
602  baselines = event.getProduct<std::vector<icarus::WaveformBaseline>>
603  (fBaselineTag.value());
604 
605  } // if baselines from event
606  else {
607  // assign baselines from configuration
608  baselines.reserve(waveforms.size());
609  for (auto const& waveform: waveforms) {
610  auto const channelSlot
611  = static_cast<std::size_t>(waveform.ChannelNumber());
612  if (channelSlot >= fCurrentBaselines.size()) {
613  throw cet::exception("DiscriminatePMTwaveformsByChannel")
614  << "Waveforms on channel " << waveform.ChannelNumber()
615  << " have no baseline configured.\n";
616  }
617  baselines.emplace_back(fCurrentBaselines[channelSlot].value.value());
618  } // for all waveforms
619  } // if ... else
620 
621  {
622  mf::LogDebug log { fLogCategory };
623  log << "Discrimination of " << waveforms.size() << " PMT waveforms from '"
624  << fOpDetWaveformTag.encode() << "'";
625  if (fBaselineTag)
626  log << " with baselines from '" << fBaselineTag->encode() << "'";
627  }
628 
629 
630  // We use an algorithm designed for applying to all channels the same
631  // thresholds, and many of them. But here we have a different threshold per
632  // channel, and a single threshold in that.
633  // So we'll reconfigure the algorithm for each channel, and process only
634  // the waveforms (and all of them) on that channel
635  // (a possible alternative is to set all the thresholds that appear in any
636  // of the channels, and after processing pick the right one for each channel;
637  // this normally works well because the threshold values are actually very few
638  // (a nominal one, and a few exceptions) but it may not scale.
639 
640  // group the waveforms by channel; ignore the channels we'll not process:
641  // channel ID -> all input waveforms and baselines on that channel
642  std::vector<std::vector<icarus::trigger::WaveformWithBaseline>>
643  waveformInfoPerChannel(fNOpDetChannels);
644 
645  for (auto const& [ waveform, baseline ]: util::zip(waveforms, baselines)) {
646 
647  auto const channelSlot = static_cast<std::size_t>(waveform.ChannelNumber());
648  if (channelSlot >= waveformInfoPerChannel.size()) continue; // ignore
649 
650  waveformInfoPerChannel[channelSlot].emplace_back
651  (&waveform, &baseline);
652 
653  } // for all channels
654 
655  assert(fCurrentThresholds.size() >= fNOpDetChannels);
657  for (auto const& [ channelSlot, waveInfo ]:
658  util::enumerate(waveformInfoPerChannel)
659  ) {
660  auto const channel = static_cast<raw::Channel_t>(channelSlot);
661  if (waveInfo.empty()) {
662  // gate associated to channel, always closed
663  gates.emplace_back(icarus::trigger::OpticalTriggerGateData_t{ channel });
664  continue;
665  }
666 
667  // set the threshold
668  auto const& threshold = fCurrentThresholds[channelSlot];
669  if (threshold.isUnset()) {
670  throw cet::exception("DiscriminatePMTwaveformsByChannel")
671  << "No threshold set up for PMT channel #" << channel << ".\n";
672  }
673  fTriggerGateBuilder->resetup
674  (detTimings, { ADCCounts_t::castFrom(threshold.value) });
675  assert(fTriggerGateBuilder->nChannelThresholds() == 1U);
676  mf::LogTrace(fLogCategory)
677  << "Processing PMT channel #" << channel
678  << " (first waveform baseline: " << waveInfo.front().baseline()
679  << ") and threshold " << fTriggerGateBuilder->channelThresholds().front()
680  ;
681 
682  // if there are waveforms, run the algorithm;
683  // save the first collection, i.e. the first (and only) threshold;
684  // extract the trigger gates from TriggerGates object immediately,
685  // GateData_t is a collection of TrackedTriggerGate objects,
686  // one per channel; that is, only one entry in this case
688  = std::move(fTriggerGateBuilder->build(waveInfo).front()).gates();
689  assert(channelGates.size() == 1);
690  gates.push_back(std::move(channelGates.front()));
691 
692  } // for all channels
693 
694  { // nameless block
695  mf::LogTrace log(fLogCategory);
696  log << "Trigger gates:\n";
697  unsigned int nOpenGates = gates.size();
698  for (auto const& gate: gates) if (gate.gate().alwaysClosed()) --nOpenGates;
699  log << nOpenGates << "/" << gates.size() << " trigger gates";
700  if (nOpenGates) {
701  log << ":";
702  for (auto const& gate: gates) {
703  if (gate.gate().alwaysClosed()) continue;
704  log << "\n " << gate;
705  }
706  }
707  } // nameless block
708 
709  //
710  // prepare output
711  //
712  art::PtrMaker<TriggerGateData_t> const makeGatePtr
713  { event, fOutputInstanceName };
714 
715  //
716  // reformat the results for the threshold
717  //
719  (std::move(gates), makeGatePtr, opDetWavePtrs);
720 
721  if (fSavePMTcoverage) {
722 
723  art::PtrMaker<sbn::OpDetWaveformMeta> const makePMTinfoPtr
724  { event, fOutputInstanceName };
725 
726  // PMT info and associations to input waveforms
727  sbn::OpDetWaveformMetaMaker const makePMTinfo { detTimings };
728 
729  std::vector<sbn::OpDetWaveformMeta> PMTinfo;
730  art::Assns<raw::OpDetWaveform, sbn::OpDetWaveformMeta> WavePMTinfoAssns;
731  art::PtrMaker<raw::OpDetWaveform> const makeWavePtr
732  { event, waveformHandle.id() };
733 
734  for (auto const& [ iWaveform, waveform ]: util::enumerate(waveforms)) {
735 
736  PMTinfo.push_back(makePMTinfo(waveform));
737 
738  WavePMTinfoAssns.addSingle
739  (makeWavePtr(iWaveform), makePMTinfoPtr(iWaveform));
740 
741  } // for
742 
743  // replica of discriminated gate-waveform association replacing the latter
744  // with the PMT coverage with the same index as the waveform
745  art::Assns<sbn::OpDetWaveformMeta, TriggerGateData_t> PMTinfoGateAssns;
746  for (auto [ gatePtr, wavePtr ]: assns)
747  PMTinfoGateAssns.addSingle(makePMTinfoPtr(wavePtr.key()), gatePtr);
748 
749  event.put(
750  std::make_unique<std::vector<sbn::OpDetWaveformMeta>>(std::move(PMTinfo)),
752  );
753  event.put(
754  std::make_unique<art::Assns<raw::OpDetWaveform, sbn::OpDetWaveformMeta>>
755  (std::move(WavePMTinfoAssns)),
757  );
758  event.put(
759  std::make_unique<art::Assns<sbn::OpDetWaveformMeta, TriggerGateData_t>>
760  (std::move(PMTinfoGateAssns)),
762  );
763 
764  } // if save PMT coverage
765 
766  //
767  // move the reformatted data into the event
768  //
769  event.put(
770  std::make_unique<std::vector<TriggerGateData_t>>(std::move(data)),
772  );
773  event.put(
774  std::make_unique<art::Assns<TriggerGateData_t, raw::OpDetWaveform>>
775  (std::move(assns)),
777  );
778 
779 } // icarus::trigger::DiscriminatePMTwaveformsByChannel::produce()
780 
781 
782 //------------------------------------------------------------------------------
783 void
785 {
786  //
787  // start from scratch
788  //
789  fCurrentThresholds.clear();
790 
791  //
792  // first set the default values for the first time
793  //
794  if (fNOpDetChannels > 0U) {
795  fCurrentThresholds.resize(
796  fNOpDetChannels,
797  fDefaultThreshold.has_value()
799  { Source_t::Default, ADCCounts_t::castFrom(fDefaultThreshold.value()) }
801  );
802  } // if
803 
804  //
805  // override from the list
806  //
807  if (fCurrentThresholds.size() < fThresholdList.size())
808  fCurrentThresholds.resize(fThresholdList.size());
809  for (auto [ iThr, thr ]: util::enumerate(fThresholdList))
810  fCurrentThresholds[iThr] = { Source_t::List, ADCCounts_t::castFrom(thr) };
811 
812  //
813  // override from PMT configuration
814  //
815  if (fPMTconfig) {
816  for (sbn::V1730Configuration const& boardConfig: fPMTconfig->boards) {
817  for (sbn::V1730channelConfiguration const& channelConfig
818  : boardConfig.channels
819  ) {
820  if (!channelConfig.hasChannelID()) continue;
821 
822  auto const channelSlot
823  = static_cast<std::size_t>(channelConfig.channelID);
824  if (fCurrentThresholds.size() <= channelSlot)
825  fCurrentThresholds.resize(channelSlot + 1U);
826 
827  fCurrentThresholds[channelSlot] = {
830  };
831 
832  } // for channel
833  } // for board
834  } // if we have PMT configuration
835 
836  //
837  // override from channel list
838  //
839  for (auto const& chInfo: fChannelInfos) {
840  if (!chInfo.threshold.has_value()) continue;
841  auto const channelSlot = static_cast<std::size_t>(chInfo.channel);
842  if (fCurrentThresholds.size() <= channelSlot) // make room
843  fCurrentThresholds.resize(channelSlot + 1U);
844  fCurrentThresholds[channelSlot] = {
845  Source_t::ChannelSpec, ADCCounts_t::castFrom(chInfo.threshold.value())
846  };
847 
848  } // for
849 
850 } // icarus::trigger::DiscriminatePMTwaveformsByChannel::refreshCurrentThresholds()
851 
852 //------------------------------------------------------------------------------
853 void
855 {
856  //
857  // start from scratch
858  //
859  fCurrentBaselines.clear();
860 
861  //
862  // first set the default values for the first time
863  //
864  if ((fNOpDetChannels > 0U) && fBaseline.has_value()) {
865  fCurrentBaselines.resize(
866  fNOpDetChannels,
869  );
870  } // if
871 
872  //
873  // override from channel list
874  //
875  for (auto const& chInfo: fChannelInfos) {
876  if (!chInfo.baseline.has_value()) continue;
877  auto const channelSlot = static_cast<std::size_t>(chInfo.channel);
878  if (fCurrentBaselines.size() <= channelSlot) // make room
879  fCurrentBaselines.resize(channelSlot + 1U);
880  fCurrentBaselines[channelSlot] = {
881  Source_t::ChannelSpec, ADCCounts_t::castFrom(chInfo.baseline.value())
882  };
883 
884  } // for
885 
886 } // icarus::trigger::DiscriminatePMTwaveformsByChannel::refreshCurrentBaselines()
887 
888 
889 //------------------------------------------------------------------------------
890 template <typename Logger /* mf::LogInfo */>
891 void
893  () const
894 {
895  Logger log { fLogCategory };
896 
897  auto const printSourceTag = [&log](Source_t source)
898  {
899  switch (source) {
900  case Source_t::Default: log << "(default value)";
901  break;
902  case Source_t::List: log << "(from threshold list)";
903  break;
904  case Source_t::Unset: log << "not set";
905  break;
906  case Source_t::PMTconfig: log << "(from PMT readout configuration)";
907  break;
908  case Source_t::Run: log << "(from run information)";
909  break;
910  case Source_t::Event: log << "(from event information)";
911  break;
912  case Source_t::ChannelSpec: log << "(specified for this channel)";
913  break;
914  } // switch
915  };
916 
917  std::size_t const nChannels
918  = std::max(fCurrentThresholds.size(), fCurrentBaselines.size());
919  log << "discrimination settings for " << nChannels << " channels";
920  if ((fNOpDetChannels > 0U) && (fNOpDetChannels < nChannels))
921  log << " (but only the first " << fNOpDetChannels << " will be processed)";
922  log << ':';
923  for (std::size_t const channel: util::counter(nChannels)) {
924 
925  log << "\n channel " << channel
926  << ": threshold ";
927  if (channel < fCurrentThresholds.size()) {
928  auto const& thrInfo = fCurrentThresholds[channel];
929  if (thrInfo.source != Source_t::Unset) log << thrInfo.value << ' ';
930  printSourceTag(thrInfo.source);
931  }
932  else log << "n/a";
933 
934  log << "; baseline ";
935  if (channel < fCurrentBaselines.size()) {
936  auto const& blineInfo = fCurrentBaselines[channel];
937  if (blineInfo.source != Source_t::Unset) log << blineInfo.value << ' ';
938  printSourceTag(blineInfo.source);
939  }
940  else log << "n/a";
941 
942  } // for channel
943 
944 } // icarus::trigger::DiscriminatePMTwaveformsByChannel::printCurrentThresholdsAndBaselines()
945 
946 
947 //------------------------------------------------------------------------------
949  (fhicl::OptionalAtom<unsigned int> const& param)
950 {
951  unsigned int nChannels;
952  return
953  param(nChannels)? nChannels: lar::providerFrom<geo::Geometry>()->NOpDets();
954 } // icarus::trigger::DiscriminatePMTwaveformsByChannel::getNOpDetChannels()
955 
956 
957 //------------------------------------------------------------------------------
959  (std::vector<ChannelInfo_t> const& config) -> std::vector<ChannelInfo_t>
960 {
961  std::vector<ChannelInfo_t> chMap = config;
962 
963  auto const compareByChannel
964  = [](ChannelInfo_t const& a, ChannelInfo_t const& b)
965  { return a.channel == b.channel; }
966  ;
967 
968  // sort and check for duplicates
969  std::sort(chMap.begin(), chMap.end());
970  auto iDup = std::unique(chMap.begin(), chMap.end(), compareByChannel);
971  if (iDup != chMap.end()) {
972  art::Exception e { art::errors::Configuration };
973  e << "Found " << std::distance(iDup, chMap.end())
974  << " duplicate channels in the configuration:";
975  while (iDup != chMap.end()) e << " " << (iDup++)->channel;
976  throw e;
977  } // if duplicates
978 
979  return chMap;
980 } // icarus::trigger::DiscriminatePMTwaveformsByChannel::readChannelInfoSpecs()
981 
982 
983 //------------------------------------------------------------------------------
985 
986 
987 //------------------------------------------------------------------------------
BEGIN_PROLOG BeamGateDuration pmtthr physics producers trigtilewindowORS Thresholds
unsigned int const fNOpDetChannels
Number of optical detector channels.
Utilities related to art service access.
DiscriminatePMTwaveformsByChannel::ChannelInfo_t convert(DiscriminatePMTwaveformsByChannel::ChannelConfig const &config)
Writes a collection of sbn::OpDetWaveformMeta from PMT waveforms.
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
void refreshCurrentBaselines()
Updates fCurrentBaselines with the current status of the object.
Definition of util::enumerate().
Algorithm to produce trigger gates out of optical readout waveforms.
do source
virtual void produce(art::Event &event) override
Creates the data products.
void printCurrentThresholdsAndBaselines() const
Prints the current thresholds to the message logger.
Derivative information from raw::OpDetWaveform data.
std::vector< icarus::trigger::OpticalTriggerGateData_t > transformIntoOpticalTriggerGate(Gates &&gates)
Returns the trigger gates in serializable format.
static constexpr quantity_t castFrom(U value)
Returns a new quantity initialized with the specified value.
Definition: quantities.h:825
std::vector< icarus::WaveformBaseline > fBaselinesRun
Current baseline list from run (one per channel).
std::optional< raw::ADC_Count_t > const fDefaultThreshold
Default threshold.
bool const fSavePMTcoverage
Whether to save also sbn::OpDetWaveformMeta.
auto enumerate(Iterables &&...iterables)
Range-for loop helper tracking the number of iteration.
Definition: enumerate.h:69
Interface to detinfo::DetectorClocks.
process_name gaushit a
fDetProps &fDetProps fDetProps &fDetProps fLogCategory
util::quantities::counts_as< raw::ADC_Count_t > ADCCounts_t
ADC counts for optical detector readout.
Definition: TriggerTypes.h:40
auto counter(T begin, T end)
Returns an object to iterate values from begin to end in a range-for loop.
Definition: counter.h:285
Logical multi-level gate associated to one or more readout channels.
double distance(geo::Point_t const &point, CathodeDesc_t const &cathode)
Returns the distance of a point from the cathode.
Utilities to read and write quantities in FHiCL configuration.
A logical multilevel gate for triggering.
A value measured in the specified unit.
Definition: quantities.h:566
Simple type definitions for trigger algorithms.
std::optional< sbn::PMTconfiguration > fPMTconfig
PMT readout configuration for the current run.
process_name PMTconfig
BEGIN_PROLOG baseline
std::unique_ptr< icarus::trigger::TriggerGateBuilder > fTriggerGateBuilder
Algorithms to simulate trigger gates out of optical channel output.
std::vector< raw::ADC_Count_t > const fThresholdList
The thresholds configured by a sequence parameter.
BEGIN_PROLOG vertical distance to the surface Name
Information from the configuration of PMT readout.
Test of util::counter and support utilities.
short signed int relativeThreshold() const
Threshold relative to the baseline (ticks).
void refreshCurrentThresholds()
Updates fCurrentThresholds with the current status of the object.
std::vector< ChannelInfo_t > const fChannelInfos
A map of thresholds: channel ID -&gt; relative threshold value [ADC].
static std::vector< ChannelInfo_t > readChannelInfoSpecs(std::vector< ChannelInfo_t > const &config)
physics producers discrimopdaq OpticalWaveforms
std::string to_string(WindowPattern const &pattern)
Converter from raw::OpDetWaveform into sbn::OpDetWaveformMeta.
std::vector< ADCvalueSetting_t > fCurrentBaselines
A map of current baselines: channel ID -&gt; baseline [ADC].
A class exposing an upgraded interface of detinfo::DetectorClocksData.
static unsigned int getNOpDetChannels(fhicl::OptionalAtom< unsigned int > const &param)
Creates a collection with one gate per channel and no gaps.
Functions pulling in STL customization if available.
DataProductPointerMap_t< ArtHandleData_t< Handle > > mapDataProductPointers(art::Event const &event, Handle const &handle)
Creates a map from address of data product element to art pointer to it.
do i e
std::string const fLogCategory
Category name for the console output stream.
std::optional< art::InputTag > const fThresholdsFromPMTconfig
Tag of PMTconfiguration data product to read thresholds from.
Simple helper functions to deal with FHiCL.
Sample_t fBaseline
Waveform baseline [ADC counts].
auto zip(Iterables &&...iterables)
Range-for loop helper iterating across many collections at the same time.
Definition: zip.h:295
std::vector< ADCvalueSetting_t > fCurrentThresholds
A map of current thresholds: channel ID -&gt; relative threshold value [ADC].
temporary value
std::optional< float > const fBaseline
A constant baseline level.
fDetProps &fDetProps fDetProps &fDetProps detTimings
raw::Channel_t channelID
Offline channel ID.
Definition of util::values() and util::const_values().
then echo find_global_symbol finds mangled or demangled symbols in libraries echo within LD_LIBRARY_PATH match any symbol that echo contains name echo Default
bool hasChannelID() const
Returns whether the channel ID is set.
std::vector< sbn::V1730channelConfiguration > channels
Configuration of each channel.
Class containing configuration for a V1730 channel.
OpticalTriggerGateData_t GateData_t
Type for gate data access.
fhicl::Sequence< fhicl::TableAs< ChannelInfo_t, ChannelConfig > > Thresholds
Class containing configuration for PMT readout.
constexpr BitMask< Storage > Unset(Flag_t< Storage > flag)
Returns a bit mask which unsets the specified flag.
art framework interface to geometry description
Utilities to map data pointer elements to their art::Ptr.
Class containing configuration for a V1730 board.