All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PMTWaveformBaselinesFromChannelData_module.cc
Go to the documentation of this file.
1 /**
2  * @file PMTWaveformBaselinesFromChannelData_module.cc
3  * @brief Extracts and writes PMT waveform baselines.
4  * @author Gianluca Petrillo (petrillo@slac.stanford.edu)
5  * @date September 14, 2020
6  */
7 
8 // ICARUS libraries
14 
15 // LArSoft libraries
19 #include "lardataalg/DetectorInfo/DetectorTimingTypes.h" // electronics_time
20 #include "lardataalg/Utilities/quantities/spacetime.h" // nanoseconds
21 #include "lardataalg/Utilities/StatCollector.h" // lar::util::MinMaxCollector
26 
27 // framework libraries
28 #include "art_root_io/TFileService.h"
29 #include "art_root_io/TFileDirectory.h"
30 #include "art/Framework/Core/ModuleMacros.h"
31 #include "art/Framework/Core/SharedProducer.h"
32 #include "art/Framework/Core/ProcessingFrame.h"
33 #include "art/Framework/Principal/Run.h"
34 #include "art/Framework/Principal/Event.h"
35 #include "art/Framework/Principal/Handle.h"
36 #include "art/Persistency/Common/PtrMaker.h"
37 #include "canvas/Persistency/Common/Assns.h"
38 #include "canvas/Persistency/Common/Ptr.h"
39 #include "canvas/Utilities/InputTag.h"
40 #include "canvas/Utilities/Exception.h"
41 #include "cetlib_except/exception.h"
42 #include "messagefacility/MessageLogger/MessageLogger.h"
43 #include "fhiclcpp/types/TableAs.h"
44 #include "fhiclcpp/types/OptionalAtom.h"
45 #include "fhiclcpp/types/Atom.h"
46 
47 // ROOT libraries
48 #include "TH2F.h"
49 #include "TGraph.h"
50 #include "TProfile.h"
51 
52 // C/C++ standard libraries
53 #include <vector>
54 #include <algorithm> // std::remove_if(), std::sort()
55 #include <iterator> // std::distance()
56 #include <memory> // std::make_unique()
57 #include <utility> // std::pair, std::move()
58 #include <string>
59 #include <optional>
60 #include <limits>
61 #include <cmath> // std::round(), std::max(), std::ceil()
62 #include <cstdint> // std::size_t
63 #include <cassert>
64 
65 
66 // -----------------------------------------------------------------------------
67 namespace icarus { class PMTWaveformBaselinesFromChannelData; }
68 
69 /**
70  * @class icarus::PMTWaveformBaselinesFromChannelData
71  * @brief Extracts a baseline from PMT waveforms.
72  *
73  * This module produces a baseline data product for each optical detector
74  * waveform.
75  * The algorithm extracts a baseline per channel per event, considering all the
76  * waveforms from one channel together, under the assumptions that:
77  *
78  * 1. the baseline is not going to change during the few milliseconds of readout
79  * 2. the beginning of the waveform is on its baseline level
80  *
81  *
82  * The algorithm
83  * ==============
84  *
85  * The core of the algorithm is described in its own class,
86  * `opdet::SharedWaveformBaseline`.
87  *
88  * On each event independently, the waveforms are grouped by channel.
89  * For each waveform, `opdet::SharedWaveformBaseline` considers their first part
90  * for baseline calculation. The number of samples of that part is specified as
91  * a fraction of the pre-trigger buffer. The pre-trigger buffer includes the
92  * samples that were collected before the physics activity that causes the data
93  * acquisition happened, and as such is expected to be almost completely free
94  * of physics signal and to be made of just electronics noise. The size of this
95  * prebuffer is determined in data by the readout configuration, which can be
96  * accessed by specifying the parameter `PMTconfigurationTag`, and in simulation
97  * by a digitization module parameter, which can be replicated here by the
98  * parameter `PretriggerBufferSize`. The fraction of such buffer used for the
99  * baseline estimation is also specified by a configuration parameter
100  * (`PretriggerBufferFractionForBaseline`). Depending on the value of the
101  * configuration parameter `ExcludeSpillTimeIfMoreThan`, the one waveform that
102  * covers the trigger time may be excluded and not used; the rationale is that
103  * there is a conspicuous number of events triggered by the late light of a
104  * cosmic ray happening before the beam spill, in which case the activity may
105  * contaminate the pre-trigger buffer and could bias the estimation of the
106  * baseline.
107  *
108  * The result of the `opdet::SharedWaveformBaseline` is currently used directly
109  * as the baseline for all the waveforms on the channel on that event.
110  *
111  * @todo Add run-level information and checks.
112  *
113  *
114  * Output data products
115  * =====================
116  *
117  * * a data product of type `std::vector<icarus::WaveformBaseline>`, with one
118  * baseline per input waveform; the baselines are guaranteed to be in the same
119  * order as the waveforms in the input collection;
120  * * a data product of type `std::vector<icarus::WaveformRMS>`, with one
121  * baseline RMS per input waveform; the baselines are guaranteed to be in the
122  * same order as the waveforms in the input collection;
123  * * two _art_ associations, with one baseline and one RMS per input optical
124  * waveform; normally this is not needed as long as the original PMT waveform
125  * data product is available; the type of the associations are
126  * `art::Assns<icarus::WaveformBaseline, raw::OpDetWaveform>` and
127  * `art::Assns<icarus::WaveformRMS, raw::OpDetWaveform>`.
128  *
129  *
130  * Output plots
131  * -------------
132  *
133  * * `Baselines`: baseline distribution, per channel; average baseline [ADC] per
134  * event per channel; all waveforms on the same channels in a single event
135  * contribute to the average, and channels with no waveforms in an event do
136  * not contribute an entry for that event.
137  *
138  *
139  *
140  * Input data products
141  * ====================
142  *
143  * * `std::vector<raw::OpDetWaveform>`: a single waveform for each recorded
144  * optical detector activity; the activity belongs to a single channel, but
145  * there may be multiple waveforms on the same channel.
146  *
147  *
148  * Service requirements
149  * ---------------------
150  *
151  * * `DetectorClocksService` for the determination of the optical tick duration
152  * and the trigger time (if the exclusion of waveform with trigger is
153  * enabled).
154  * * `TFileService` and `Geometry` if `PlotBaselines` is enabled.
155  *
156  *
157  * Configuration parameters
158  * =========================
159  *
160  * A terse description of the parameters is printed by running
161  * `lar --print-description PMTWaveformBaselinesFromChannelData`.
162  *
163  * * `OpticalWaveforms` (input tag, mandatory): the data product containing all
164  * optical detector waveforms;
165  * * `PretriggerBufferSize` (positive integer, optional): the number of samples
166  * collected by the PMT readout before the PMT signal. This number is used
167  * as base for the size of the initial part of the waveform to be used in
168  * baseline calculation. This and `PMTconfigurationTag` parameters are
169  * exclusive. This one is expected to be preferred for simulated samples.
170  * * `PMTconfigurationTag` (input tag): the (run) data product containing the
171  * PMT readout configuration. This is extracted from the run configuration
172  * for data events. The pre-trigger buffer size is read from this data
173  * product, and used as base for the size of the initial part of the
174  * waveform to be used in baseline calculation. This and
175  * `PretriggerBufferSize` parameters are exclusive. This one is expected
176  * to be preferred for data samples.
177  * * `PretriggerBufferFractionForBaseline` (real, default: `0.5`): the fraction
178  * of the pre-trigger buffer (see `PretriggerBufferSize` and
179  * `PMTconfigurationTag` parameters) to be used to extract the baseline.
180  * * `ExcludeSpillTimeIfMoreThan` (integer, default: disable): the minimum
181  * number of PMT waveforms on a single channel of a single event, in order
182  * for the exclusion of the waveform containing the global trigger to be
183  * enabled. By default (huge number) the feature is disabled.
184  * * `AlgoParams` (table): configuration of the core algorithm extracting the
185  * baseline from a set of waveforms on the same channel. The configuration
186  * is as follows:
187  * * `AcceptedSampleRangeRMS` (real): for a waveform to be considered for
188  * the baseline, the values of the samples must stay within a certain
189  * range, which is defined as this number of RMS away in either
190  * directions from a central value that is a rougher estimation of the
191  * baseline.
192  * * `ExcessSampleLimit` (integer): for a waveform to be considered for
193  * the baseline, there must be less than this number of samples in a
194  * row that are outside of the range defined by
195  * `AcceptedSampleRangeRMS` parameter.
196  * * `PlotBaselines` (flag, default: `true`): whether to produce distributions
197  * of the extracted baselines.
198  * * `BaselineTimeAverage` (real number, default: `600.0`): binning of the
199  * baseline profile vs. time, in seconds. Requires `PlotBaselines` to be set.
200  * * `OutputCategory` (string, default: `"PMTWaveformBaselinesFromChannelData"`):
201  * label for the category of messages in the console output; this is the label
202  * that can be used for filtering messages via MessageFacility service
203  * configuration.
204  *
205  */
206 class icarus::PMTWaveformBaselinesFromChannelData: public art::SharedProducer {
207 
208  public:
209 
210  // --- BEGIN Configuration ---------------------------------------------------
211  struct Config {
212 
213  using Name = fhicl::Name;
214  using Comment = fhicl::Comment;
215 
216  /// Configuration of the algorithm parametes.
217  struct AlgoConfig {
218 
219  fhicl::Atom<double> AcceptedSampleRangeRMS {
220  Name{ "AcceptedSampleRangeRMS" },
221  Comment{
222  "The range of samples accepted for baseline, expressed in number of"
223  " RMS units from the first baseline estimation"
224  }
225  };
226 
227  fhicl::Atom<unsigned int> ExcessSampleLimit {
228  Name{ "ExcessSampleLimit" },
229  Comment{
230  "If there is this number of samples outside the RMS limit,"
231  " the waveform is not used"
232  }
233  };
234 
235  }; // AlgoConfig
236 
237 
238  fhicl::Atom<art::InputTag> OpticalWaveforms {
239  Name{ "OpticalWaveforms" },
240  Comment{ "label of input digitized optical waveform data product" }
241  };
242 
243  fhicl::OptionalAtom<unsigned int> PretriggerBufferSize {
244  Name{ "PretriggerBufferSize" },
245  Comment{ "the number of samples in the pre-trigger readout buffer" }
246  };
247 
248  fhicl::OptionalAtom<art::InputTag> PMTconfigurationTag {
249  Name{ "PMTconfigurationTag" },
250  Comment{ "PMT readout configuration object" }
251  };
252 
254  Name{ "PretriggerBufferFractionForBaseline" },
255  Comment
256  { "fraction of the pretrigger buffer used for baseline determination" },
257  0.5f
258  };
259 
260  fhicl::Atom<unsigned int> ExcludeSpillTimeIfMoreThan {
261  Name{ "ExcludeSpillTimeIfMoreThan" },
262  Comment{
263  "do not include the waveform at spill time"
264  " if there are at least this number of off-spill PMT waveforms"
265  },
266  std::numeric_limits<unsigned int>::max()
267  };
268 
269  fhicl::TableAs<opdet::SharedWaveformBaseline::Params_t, AlgoConfig>
270  AlgoParams
271  {
272  Name{ "AlgoParams" },
273  Comment{ "baseline algorithm parameters" }
274  };
275 
276  fhicl::Atom<bool> PlotBaselines {
277  Name{ "PlotBaselines" },
278  Comment{ "produce plots on the extracted baseline" },
279  true
280  };
281 
282  fhicl::Atom<double> BaselineTimeAverage {
283  Name{ "BaselineTimeAverage" },
284  Comment{ "binning of the baseline profile vs. time [s]" },
285  [this](){ return PlotBaselines(); }, // enabled if `PlotBaselines` is set
286  600.0
287  };
288 
289  fhicl::Atom<std::string> OutputCategory {
290  Name{ "OutputCategory" },
291  Comment{ "tag of the module output to console via message facility" },
292  "PMTWaveformBaselinesFromChannelData"
293  };
294 
295  }; // struct Config
296 
297  using Parameters = art::SharedProducer::Table<Config>;
298 
299  // --- END Configuration -----------------------------------------------------
300 
301 
302  // --- BEGIN Constructors ----------------------------------------------------
303 
305  (Parameters const& config, art::ProcessingFrame const&);
306 
307  // --- END Constructors ------------------------------------------------------
308 
309 
310  // --- BEGIN Framework hooks -------------------------------------------------
311  /// Prepares the plots to be filled.
312  virtual void beginJob(art::ProcessingFrame const& frame) override;
313 
314  /// Reads needed PMT configuration.
315  virtual void beginRun(art::Run& run, art::ProcessingFrame const&) override;
316 
317  /// Creates the data products.
318  virtual void produce
319  (art::Event& event, art::ProcessingFrame const&) override;
320 
321  /// Remove empty plots.
322  virtual void endJob(art::ProcessingFrame const& frame) override;
323 
324  // --- END Framework hooks ---------------------------------------------------
325 
326 
327  private:
328 
329  // --- BEGIN Configuration variables -----------------------------------------
330 
331  art::InputTag const fOpDetWaveformTag; ///< Input optical waveform tag.
332 
333  /// Minimum number of waveforms when excluding trigger one is allowed.
335 
336  // this may be updated during the job:
337  unsigned int fPretriggerSamples; ///< Samples in the pre-trigger buffer.
338 
339  /// Tag for PMT configuration data product.
340  std::optional<art::InputTag> fPMTconfigTag;
341 
342  float const fSampleFraction; ///< Fraction of pretrigger buffer to use.
343 
344  // this may be updated during the job:
345  /// Parameters for the baseline algorithm.
347 
348  bool const fPlotBaselines; ///< Whether to produce plots.
349 
350  /// Width of baseline time profile binning [s]
351  double const fBaselineTimeAverage { 0.0 };
352 
353  std::string const fLogCategory; ///< Category name for the console output stream.
354 
355  // --- END Configuration variables -------------------------------------------
356 
357 
358  // --- BEGIN Service variables -----------------------------------------------
359 
360  /// Duration of a PMT digitizer tick.
362 
363  // --- END Service variables -------------------------------------------------
364 
365 
366  // --- BEGIN Algorithms ------------------------------------------------------
367 
368  // --- END Algorithms --------------------------------------------------------
369 
370  /// Record of baseline information to be written.
371  struct BaselineInfo_t {
374  }; // BaselineInfo_t
375 
376  std::size_t fNPlotChannels = 0U; ///< Number of plotted channels
377  TH2* fHBaselines = nullptr; ///< All baselines, per channel.
378 
379  /// For each channel, all event times and their baselines.
380  std::vector<std::vector<std::pair<double, double>>> fBaselinesVsTime;
381 
382  /// Removes `waveforms` containing `time`, retuning how many were removed.
383  unsigned int removeWaveformsAround
384  (std::vector<raw::OpDetWaveform const*>& waveforms, double time) const;
385 
386  /// Returns the smallest pre-trigger buffer size available among all boards.
387  unsigned int getPretriggerBuffer
388  (sbn::PMTconfiguration const& PMTconfig) const;
389 
390  /// Creates all the plots to be filled by the module.
391  void setupPlots(art::ProcessingFrame const& frame);
392 
393  /// Removes the empty plots.
394  void buildBaselineGraphs(art::ProcessingFrame const& frame);
395 
396  /// Returns a common baseline for all the specified `waveforms`.
397  /// Returns a vector of waveforms per channel (which is the index).
398  static std::vector<std::vector<raw::OpDetWaveform const*>> groupByChannel
399  (std::vector<raw::OpDetWaveform> const& waveforms);
400 
401 
402 }; // icarus::PMTWaveformBaselinesFromChannelData
403 
404 
405 
406 //------------------------------------------------------------------------------
407 //--- Implementation
408 //------------------------------------------------------------------------------
409 namespace icarus {
410 
413  {
414  return {
415  0U // nSample (to be changed run by run)
416  , config.AcceptedSampleRangeRMS() // nRMS
417  , config.ExcessSampleLimit() // nExcessSamples
418  };
419  } // convert(icarus::PMTWaveformBaselinesFromChannelData::Config::AlgoConfig)
420 
421 } // namespace icarus
422 
423 
424 //------------------------------------------------------------------------------
425 namespace {
426 
427  template <typename Vect, typename Index = typename Vect::size_type>
428  class VectorExpander {
429  public:
430  using Vector_t = Vect; ///< Type of bound vector.
431  using Index_t = Index; ///< Type used for indexing the vector.
432  using value_type = typename Vector_t::value_type;
433  using reference = typename Vector_t::reference;
434 
435  /// Constructor: binds to the specified vector (forever).
436  VectorExpander(Vector_t& v, value_type defaultValue = value_type{})
437  : fVector{ &v }, fDefValue{ std::move(defaultValue) } {}
438 
439  /// Returns a reference to the element `index` (created as needed).
440  reference operator() (Index_t index) const
441  {
442  auto const ix = static_cast<typename Vector_t::size_type>(index);
443  if (fVector->size() <= ix) fVector->resize(ix + 1, fDefValue);
444  return (*fVector)[ix];
445  }
446 
447 
448  private:
449  Vector_t* fVector; ///< Bound vector.
450  value_type fDefValue; ///< Value used when resizing.
451 
452  }; // VectorExpander
453 
454 } // local namespace
455 
456 
457 //------------------------------------------------------------------------------
458 //--- icarus::PMTWaveformBaselinesFromChannelData
459 //------------------------------------------------------------------------------
460 
462  (Parameters const& config, art::ProcessingFrame const& frame)
463  : art::SharedProducer(config)
464  // configuration
465  , fOpDetWaveformTag(config().OpticalWaveforms())
466  , fExcludeSpillTimeIfMoreThan(config().ExcludeSpillTimeIfMoreThan())
467  , fPretriggerSamples(config().PretriggerBufferSize().value_or(0U))
468  , fPMTconfigTag(config().PMTconfigurationTag())
469  , fSampleFraction(config().PretriggerBufferFractionForBaseline())
470  , fAlgoParams(config().AlgoParams())
471  , fPlotBaselines(config().PlotBaselines())
472  , fBaselineTimeAverage(config().BaselineTimeAverage())
473  , fLogCategory(config().OutputCategory())
474  // service caching
475  , fOpticalTick(
477  { frame.serviceHandle<detinfo::DetectorClocksService>()->DataForJob() }
478  .OpticalClockPeriod()
479  )
480  // algorithms
481 {
482 
483  if (fPlotBaselines)
484 // serialize<art::InEvent>(art::TFileService::resource_name()); // TODO isn't art supposed to provide this method?
485  serializeExternal<art::InEvent>(std::string{ "TFileService" });
486  else
487  async<art::InEvent>();
488 
489 
490  //
491  // optional configuration parameters
492  //
493  if (fPMTconfigTag.has_value() == (fPretriggerSamples != 0)) {
494  throw art::Exception(art::errors::Configuration)
495  << "Exactly one between parameters '"
496  << config().PMTconfigurationTag.name() << "' and '"
497  << config().PretriggerBufferFractionForBaseline.name()
498  << "' (with a positive value) must be specified!\n";
499  }
500 
501  //
502  // configuration report
503  //
504  {
505  mf::LogInfo log{ fLogCategory };
506  log << "Using the standard (median) algorithm, waveform by waveform, on '"
507  << fOpDetWaveformTag.encode() << "'";
508  }
509 
510  //
511  // declaration of input
512  //
513  consumes<std::vector<raw::OpDetWaveform>>(fOpDetWaveformTag);
514  if (fPMTconfigTag) consumes<sbn::PMTconfiguration>(*fPMTconfigTag);
515 
516  //
517  // declaration of output
518  //
519  produces<std::vector<icarus::WaveformBaseline>>();
520  produces<std::vector<icarus::WaveformRMS>>();
521  produces<art::Assns<icarus::WaveformBaseline, raw::OpDetWaveform>>();
522  produces<art::Assns<icarus::WaveformRMS, raw::OpDetWaveform>>();
523 
524 } // icarus::PMTWaveformBaselinesFromChannelData::PMTWaveformBaselinesFromChannelData()
525 
526 
527 //------------------------------------------------------------------------------
529  (art::ProcessingFrame const& frame)
530 {
531 
532  //
533  // set up the plots, if needed
534  //
535  if (fPlotBaselines) setupPlots(frame);
536 
537 } // icarus::PMTWaveformBaselinesFromChannelData::beginJob()
538 
539 
540 //------------------------------------------------------------------------------
542  (art::Run& run, art::ProcessingFrame const&)
543 {
544 
545  if (fPMTconfigTag) { // update the pretrigger samples number
546  fPretriggerSamples = getPretriggerBuffer
547  (run.getProduct<sbn::PMTconfiguration>(*fPMTconfigTag));
548  }
549  assert(fPretriggerSamples > 0U);
550 
551  fAlgoParams.nSample = std::max(
552  1U,
553  static_cast<unsigned int>(std::round(fSampleFraction * fPretriggerSamples))
554  );
555 
556  {
557  // not clear whether this is debug or info level
558  mf::LogInfo log{ fLogCategory };
559  log << run.id() << ": baseline algorithm configuration:\n";
560  fAlgoParams.dump(log, " - ");
561  }
562 
563 } // icarus::PMTWaveformBaselinesFromChannelData::beginRun()
564 
565 
566 //------------------------------------------------------------------------------
568  (art::Event& event, art::ProcessingFrame const& frame)
569 {
570 
572  frame.serviceHandle<detinfo::DetectorClocksService const>()->DataFor(event)
573  };
574 
575  detinfo::timescales::electronics_time const triggerTime
576  = detTimings.TriggerTime();
577 
578  mf::LogDebug{ fLogCategory }
579  << event.id() << " trigger time: " << triggerTime;
580 
581  //
582  // fetch input
583  //
584  auto const& waveformHandle
585  = event.getValidHandle<std::vector<raw::OpDetWaveform>>(fOpDetWaveformTag);
586  auto const& waveforms = *waveformHandle;
587 
588  //
589  // compute all the baselines (and plot them)
590  //
591  opdet::SharedWaveformBaseline const sharedWaveformBaselineAlgo
592  { fAlgoParams, fLogCategory };
593 
594  double const eventTime = static_cast<double>(event.time().timeHigh())
595  + static_cast<double>(event.time().timeHigh()) * 1e-9;
596 
597  std::vector<BaselineInfo_t> channelBaselines;
598  VectorExpander baselineForChannel { channelBaselines };
599 
600  auto waveformsByChannel = groupByChannel(waveforms);
601 
602  for (auto const& [ channel, waveforms ]: util::enumerate(waveformsByChannel))
603  {
604  if (waveforms.empty()) continue;
605 
606  mf::LogTrace{ fLogCategory }
607  << "Processing " << waveforms.size() << " waveforms for channel "
608  << channel;
609 
610  //
611  // remove global trigger waveform
612  //
613  if (waveforms.size() >= fExcludeSpillTimeIfMoreThan) {
614 
615  unsigned int const nExcluded
616  = removeWaveformsAround(waveforms, triggerTime.value());
617  if (nExcluded > 0U) {
618  mf::LogTrace{ fLogCategory }
619  << "Removed " << nExcluded << "/" << (waveforms.size() + nExcluded)
620  << " waveforms at trigger time " << triggerTime;
621  }
622 
623  } // if many waveforms
624 
625  //
626  // extract baseline
627  //
629  = sharedWaveformBaselineAlgo(waveforms);
630 
631  mf::LogTrace{ fLogCategory }
632  << "Channel " << channel << ": baseline " << baseline.baseline
633  << " ADC# from " << baseline.nSamples << " samples in "
634  << baseline.nWaveforms << "/" << waveforms.size()
635  << " waveforms; found RMS=" << baseline.RMS << " ADC#";
636 
637  auto const chIndex = static_cast<std::size_t>(channel);
638  baselineForChannel(chIndex) = { baseline.baseline, baseline.RMS };
639 
640  if (fHBaselines) fHBaselines->Fill(double(channel), baseline.baseline);
641  if (chIndex < fBaselinesVsTime.size())
642  fBaselinesVsTime[chIndex].emplace_back(eventTime, baseline.baseline);
643 
644  } // for grouped waveforms
645 
646  //
647  // assign baselines to waveforms
648  //
649 
650  std::vector<icarus::WaveformBaseline> baselines;
651  baselines.reserve(waveforms.size());
652  art::Assns<icarus::WaveformBaseline, raw::OpDetWaveform> baselineToWaveforms;
653  art::PtrMaker<icarus::WaveformBaseline> const makeBaselinePtr(event);
654 
655  std::vector<icarus::WaveformRMS> RMSs;
656  RMSs.reserve(waveforms.size());
657  art::Assns<icarus::WaveformRMS, raw::OpDetWaveform> RMStoWaveforms;
658  art::PtrMaker<icarus::WaveformRMS> const makeRMSPtr(event);
659 
660  for (auto const& [ iWaveform, waveform ]: util::enumerate(waveforms)) {
661 
662  BaselineInfo_t baselineInfo
663  = channelBaselines[static_cast<std::size_t>(waveform.ChannelNumber())];
664 
665  art::Ptr<raw::OpDetWaveform> const waveformPtr{ waveformHandle, iWaveform };
666 
667  baselines.push_back(baselineInfo.baseline);
668  RMSs.push_back(baselineInfo.RMS);
669  baselineToWaveforms.addSingle(makeBaselinePtr(iWaveform), waveformPtr);
670  RMStoWaveforms.addSingle(makeRMSPtr(iWaveform), waveformPtr);
671 
672  } // for
673 
674  //
675  // output
676  //
677  event.put(
678  std::make_unique<std::vector<icarus::WaveformBaseline>>
679  (std::move(baselines))
680  );
681  event.put
682  (std::make_unique<std::vector<icarus::WaveformRMS>>(std::move(RMSs)));
683  event.put(
684  std::make_unique<art::Assns<icarus::WaveformBaseline, raw::OpDetWaveform>>
685  (std::move(baselineToWaveforms))
686  );
687  event.put(
688  std::make_unique<art::Assns<icarus::WaveformRMS, raw::OpDetWaveform>>
689  (std::move(RMStoWaveforms))
690  );
691 
692 
693 } // icarus::PMTWaveformBaselinesFromChannelData::produce()
694 
695 
696 //------------------------------------------------------------------------------
698  (art::ProcessingFrame const& frame)
699 {
700 
701  if (fPlotBaselines) buildBaselineGraphs(frame);
702 
703 } // icarus::PMTWaveformBaselinesFromChannelData::endJob()
704 
705 
706 //------------------------------------------------------------------------------
708  (art::ProcessingFrame const& frame)
709 {
710 
711  auto const& tfs = *(frame.serviceHandle<art::TFileService>());
712  auto const& geom = *(frame.serviceHandle<geo::Geometry>()->provider());
713 
714  fNPlotChannels = geom.NOpChannels();
715 
716  fHBaselines = tfs.make<TH2F>(
717  "Baselines",
718  "PMT baseline;channel;baseline per channel [ / 8 ADC ]",
719  fNPlotChannels, 0.0, double(fNPlotChannels),
720  256, 13312.0, 15360.0
721  );
722 
723  // these are graphs, and it is more convenient to carry around their data
724  // in a vector than carrying around the graphs themselves;
725  // `buildBaselineGraphs()` will turn that data into graph at end of the job;
726  // here we just declare which channels we are going to plot.
727  fBaselinesVsTime.resize(fNPlotChannels);
728 
729 } // icarus::PMTWaveformBaselinesFromChannelData::setupPlots()
730 
731 
732 //------------------------------------------------------------------------------
735 {
737  for (sbn::V1730Configuration const& board: PMTconfig.boards)
738  prebuffer.add(board.preTriggerTicks());
739  if (!prebuffer.has_data()) {
740  throw art::Exception{ art::errors::Unknown }
741  << "No boards found in the PMT configuration!\n";
742  }
743  else if (prebuffer.max() > prebuffer.min()) {
744  throw art::Exception{ art::errors::Unknown }
745  << "Found different pre-trigger readout buffer sizes between "
746  << prebuffer.min() << " and " << prebuffer.max() << " ticks.\n";
747  }
748  return prebuffer.min();
749 } // icarus::PMTWaveformBaselinesFromChannelData::getPretriggerBuffer()
750 
751 
752 //------------------------------------------------------------------------------
753 std::vector<std::vector<raw::OpDetWaveform const*>>
755  (std::vector<raw::OpDetWaveform> const& waveforms)
756 {
757 
758  std::vector<std::vector<raw::OpDetWaveform const*>> groups;
759  VectorExpander groupForChannel { groups };
760 
761  for (raw::OpDetWaveform const& waveform: waveforms) {
762  groupForChannel(waveform.ChannelNumber()).push_back(&waveform);
763  }
764 
765  return groups;
766 } // icarus::PMTWaveformBaselinesFromChannelData::groupByChannel()
767 
768 
769 //------------------------------------------------------------------------------
771  (std::vector<raw::OpDetWaveform const*>& waveforms, double time) const
772 {
773 
774  // tick duration in the same unit as the electronics time scale times
775  // (that is microseconds, actually)
776  double const tickDuration
777  = detinfo::timescales::electronics_time::interval_t{ fOpticalTick }.value();
778 
779  auto const hasTriggerTime
780  = [tickDuration,time](raw::OpDetWaveform const* waveform)
781  {
782  double const relTime = time - waveform->TimeStamp();
783  return (relTime >= 0.0) && (relTime < (waveform->size() * tickDuration));
784  };
785 
786  auto const iLast
787  = std::remove_if(waveforms.begin(), waveforms.end(), hasTriggerTime);
788 
789  unsigned int const nRemoved = std::distance(iLast, waveforms.end());
790  waveforms.erase(iLast, waveforms.end());
791 
792  return nRemoved;
793 
794 } // icarus::PMTWaveformBaselinesFromChannelData::removeWaveformsAround()
795 
796 
797 //------------------------------------------------------------------------------
799  (art::ProcessingFrame const& frame)
800 {
801 
802  auto& tfs = *(frame.serviceHandle<art::TFileService>());
803 
804  art::TFileDirectory graphDir = tfs.mkdir("graphs", "Baseline vs. time");
805  art::TFileDirectory profileDir
806  = tfs.mkdir("profiles", "Baseline profiles vs. time");
807 
808  for (auto const channel: util::counter(fBaselinesVsTime.size())) {
809 
810  auto& timeAndBaselines = fBaselinesVsTime[channel];
811  if (timeAndBaselines.empty()) continue;
812 
813  // sort by time (entries with the same time would be sorted by baseline,
814  // but that does not really happen nor it mattered if it happened)
815  std::sort(timeAndBaselines.begin(), timeAndBaselines.end());
816 
817  // graph, one point per event
818  auto* const graph = graphDir.makeAndRegister<TGraph>(
819  "BaselineCh" + std::to_string(channel),
820  "PMT channel #" + std::to_string(channel) + ": baseline vs. run time",
821  timeAndBaselines.size()
822  );
823  assert(graph->GetXaxis());
824  assert(graph->GetYaxis());
825  graph->GetXaxis()->SetTitle("event time");
826  graph->GetYaxis()->SetTitle("baseline [ ADC ]");
827 
828 
829  // profile, one point every 10 minutes (or best offer)
830  double const startTime = timeAndBaselines.front().first;
831  double const totalTime = timeAndBaselines.back().first - startTime;
832  auto const nBins = std::max(1U,
833  static_cast<unsigned int>(std::ceil(totalTime / fBaselineTimeAverage))
834  );
835  double const endTime = startTime + nBins * fBaselineTimeAverage;
836 
837  auto* const profile = profileDir.make<TProfile>(
838  ("BaselineCh" + std::to_string(channel) + "profile").c_str(),
839  ("PMT channel #" + std::to_string(channel) + ": baseline vs. run time")
840  .c_str(),
841  nBins, startTime, endTime
842  );
843  assert(profile->GetXaxis());
844  assert(profile->GetYaxis());
845  profile->GetXaxis()->SetTitle(
846  ("event time [ / " + std::to_string(fBaselineTimeAverage) + " s ]")
847  .c_str()
848  );
849  profile->GetYaxis()->SetTitle("average baseline [ ADC ]");
850 
851  for (auto const& [ i, data ]: util::enumerate(timeAndBaselines)) {
852  auto const [ time, baseline ] = data;
853  graph->SetPoint(i, time, baseline);
854  profile->Fill(time, baseline);
855  } // for
856 
857  } // for
858 
859 } // icarus::PMTWaveformBaselinesFromChannelData::buildBaselineGraphs()
860 
861 
862 //------------------------------------------------------------------------------
864 
865 
866 //------------------------------------------------------------------------------
Data_t max() const
Returns the accumulated maximum, or a very small number if no values.
float const fSampleFraction
Fraction of pretrigger buffer to use.
art::InputTag const fOpDetWaveformTag
Input optical waveform tag.
bool has_data() const
Returns whether at least one datum has been added.
unsigned int getPretriggerBuffer(sbn::PMTconfiguration const &PMTconfig) const
Returns the smallest pre-trigger buffer size available among all boards.
std::vector< std::vector< std::pair< double, double > > > fBaselinesVsTime
For each channel, all event times and their baselines.
This_t & add(Data_t value)
Include a single value in the statistics.
double RMS
The RMS found during the extraction.
fhicl::TableAs< opdet::SharedWaveformBaseline::Params_t, AlgoConfig > AlgoParams
A baseline RMS for a waveform.
Definition of util::enumerate().
static std::vector< std::vector< raw::OpDetWaveform const * > > groupByChannel(std::vector< raw::OpDetWaveform > const &waveforms)
unsigned int preTriggerTicks() const
Ticks in the waveform before the trigger.
opdet::SharedWaveformBaseline::Params_t fAlgoParams
Parameters for the baseline algorithm.
unsigned int nSamples
Number of samples used for the extraction.
Classes gathering simple statistics.
unsigned int Index_t
Type to denote the index of the flag.
Definition: BitMask.h:60
auto enumerate(Iterables &&...iterables)
Range-for loop helper tracking the number of iteration.
Definition: enumerate.h:69
Interface to detinfo::DetectorClocks.
util::quantities::intervals::nanoseconds fOpticalTick
Duration of a PMT digitizer tick.
fDetProps &fDetProps fDetProps &fDetProps fLogCategory
Keeps track of the minimum and maximum value we observed.
Information from the configuration of a V1730 PMT readout board.
Access the description of detector geometry.
Data_t min() const
Returns the accumulated minimum, or a very large number if no values.
Algorithm configuration parameters.
PMTWaveformBaselinesFromChannelData(Parameters const &config, art::ProcessingFrame const &)
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
double distance(geo::Point_t const &point, CathodeDesc_t const &cathode)
Returns the distance of a point from the cathode.
daq::details::BoardSetup_t convert(DaqDecoderICARUSPMT::BoardSetupConfig const &config)
Special function fhicl::TableAs uses to convert BoardSetupConfig.
Extracts a common baseline from waveforms.
virtual void beginRun(art::Run &run, art::ProcessingFrame const &) override
Reads needed PMT configuration.
std::string const fLogCategory
Category name for the console output stream.
process_name PMTconfig
BEGIN_PROLOG baseline
BEGIN_PROLOG vertical distance to the surface Name
Information from the configuration of PMT readout.
Test of util::counter and support utilities.
The geometry of one entire detector, as served by art.
Definition: Geometry.h:181
std::optional< art::InputTag > fPMTconfigTag
Tag for PMT configuration data product.
An interval (duration, length, distance) between two quantity points.
Definition: intervals.h:114
Extracts and writes PMT waveform baselines.
virtual void beginJob(art::ProcessingFrame const &frame) override
Prepares the plots to be filled.
physics producers discrimopdaq OpticalWaveforms
virtual void endJob(art::ProcessingFrame const &frame) override
Remove empty plots.
Class containing a waveform baseline RMS value.
Definition: WaveformRMS.h:51
std::string to_string(WindowPattern const &pattern)
Dimensioned variables representing space or time quantities.
A class exposing an upgraded interface of detinfo::DetectorClocksData.
Data types for detinfo::DetectorTimings.
double const fBaselineTimeAverage
Width of baseline time profile binning [s].
Class containing a waveform baseline value.
do i e
unsigned int nWaveforms
Number of waveforms used for the extraction.
unsigned int fExcludeSpillTimeIfMoreThan
Minimum number of waveforms when excluding trigger one is allowed.
fDetProps &fDetProps fDetProps &fDetProps detTimings
art::ServiceHandle< art::TFileService > tfs
void buildBaselineGraphs(art::ProcessingFrame const &frame)
Removes the empty plots.
void setupPlots(art::ProcessingFrame const &frame)
Creates all the plots to be filled by the module.
unsigned int fPretriggerSamples
Samples in the pre-trigger buffer.
virtual void produce(art::Event &event, art::ProcessingFrame const &) override
Creates the data products.
recob::tracking::Vector_t Vector_t
double baseline
Value of the baseline [ADC#].
Class containing configuration for PMT readout.
timescale_traits< ElectronicsTimeCategory >::time_point_t electronics_time
A point in time on the electronics time scale.
art framework interface to geometry description
std::vector< sbn::V1730Configuration > boards
Configuration of all PMT readout boards.
Class containing configuration for a V1730 board.
unsigned int removeWaveformsAround(std::vector< raw::OpDetWaveform const * > &waveforms, double time) const
Removes waveforms containing time, retuning how many were removed.