All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
PMTWaveformBaselinesFromReadoutConfiguration_module.cc
Go to the documentation of this file.
1 /**
2  * @file PMTWaveformBaselinesFromReadoutConfiguration_module.cc
3  * @brief Extracts PMT channel baselines from PMT readout configuration.
4  * @author Gianluca Petrillo (petrillo@slac.stanford.edu)
5  * @date February 24, 2021
6  */
7 
8 // ICARUS libraries
13 // #include "icaruscode/Utilities/DataProductPointerMap.h"
14 
15 // LArSoft libraries
17 #include "larcore/CoreUtils/ServiceUtil.h" // lar::providerFrom()
21 
22 // framework libraries
23 #include "art_root_io/TFileService.h"
24 #include "art/Framework/Core/ModuleMacros.h"
25 #include "art/Framework/Core/EDProducer.h"
26 #include "art/Framework/Principal/Event.h"
27 #include "art/Framework/Principal/Run.h"
28 #include "art/Framework/Principal/Handle.h"
29 #include "art/Persistency/Common/PtrMaker.h"
30 #include "canvas/Persistency/Common/Assns.h"
31 #include "canvas/Persistency/Common/Ptr.h"
32 #include "canvas/Utilities/InputTag.h"
33 #include "cetlib_except/exception.h"
34 #include "messagefacility/MessageLogger/MessageLogger.h"
35 #include "fhiclcpp/types/Atom.h"
36 
37 // ROOT libraries
38 #include "TH2F.h"
39 
40 // C/C++ standard libraries
41 #include <vector>
42 #include <memory> // std::make_unique()
43 #include <string>
44 #include <tuple> // std::tie()
45 #include <utility> // std::move(), std::pair<>
46 #include <limits> // std::numeric_limits<>
47 #include <cassert>
48 
49 //------------------------------------------------------------------------------
50 namespace icarus { class PMTWaveformBaselinesFromReadoutConfiguration; }
51 
52 /**
53  * @brief Extracts PMT baseline settings from PMT readout configuration.
54  *
55  * This module produces a baseline data product for each optical detector
56  * waveform. The content is the same for all the events in each run, and it is
57  * read from the specified PMT configuration data product.
58  *
59  * Each waveform is associated with the baseline configured for its channel.
60  * If there is no information for a given channel and that channel is requested,
61  * an exception is thrown.
62  *
63  * This module is interchangeable with `PMTWaveformBaselines`, which instead
64  * extracts the baseline dynamically from each waveform.
65  *
66  *
67  * Output data products
68  * =====================
69  *
70  * * a data product of type `std::vector<icarus::WaveformBaseline>`, with one
71  * baseline per input waveform; the baselines are guaranteed to be in the same
72  * order as the waveforms in the input collection;
73  * * an _art_ association, one baseline per input optical waveform; normally
74  * this is not needed as long as the original PMT waveform data product is
75  * available; the type of the association is
76  * `art::Assns<icarus::WaveformBaseline, raw::OpDetWaveform>`.
77  *
78  *
79  * Output plots
80  * -------------
81  *
82  * * `Baselines`: baseline distribution, per channel; one entry [ADC]
83  * per configured channel per run.
84  *
85  *
86  * Input data products
87  * ====================
88  *
89  * * `std::vector<raw::OpDetWaveform>`: a single waveform for each recorded
90  * optical detector activity; the activity belongs to a single channel, but
91  * there may be multiple waveforms on the same channel.
92  *
93  * @note This module needs only the information of the channel of each waveform;
94  * because _art_ is not capable of selective readout, though, the whole
95  * optical detector readout data is loaded. When this module is the first
96  * one in the job accessing the PMT waveforms, the data loading time is
97  * accounted to it; the time the module needs to perform its specific
98  * operations is tiny in comparison. Anyway, since it is likely that
99  * another module in the job will then use PMT waveform data, this loading
100  * time is not wasted.
101  *
102  *
103  * Service requirements
104  * ---------------------
105  *
106  * * `TFileService` and `Geometry` if `PlotBaselines` is enabled
107  *
108  *
109  * Configuration parameters
110  * =========================
111  *
112  * A terse description of the parameters is printed by running
113  * `lar --print-description PMTWaveformBaselinesFromReadoutConfiguration`.
114  *
115  * * `OpticalWaveforms` (input tag, mandatory): the data product containing all
116  * optical detector waveforms.
117  * * `PMTconfigurationTag` (input tag, mandatory): the run-level data product
118  * containing the full PMT readout configuration (including baseline per
119  * channel).
120  * * `OutputCategory` (string, default:
121  * `"PMTWaveformBaselinesFromReadoutConfiguration"`): label
122  * for the category of messages in the console output; this is the label
123  * that can be used for filtering messages via MessageFacility service
124  * configuration.
125  * * `PlotBaselines` (flag, default: `true`): whether to produce distributions
126  * of the configured baselines (_not implemented yet_).
127  * * `PrintBaselines` (flag, default: `true`): if set to `true`, on each run it
128  * will print the baseline of all the configured channels with a LArSoft
129  * channel ID.
130  *
131  */
133  : public art::EDProducer
134 {
135 
136  public:
137 
138  // --- BEGIN Configuration ---------------------------------------------------
139  struct Config {
140 
141  using Name = fhicl::Name;
142  using Comment = fhicl::Comment;
143 
144  fhicl::Atom<art::InputTag> OpticalWaveforms {
145  Name("OpticalWaveforms"),
146  Comment("label of input digitized optical waveform data product")
147  };
148 
149  fhicl::Atom<art::InputTag> PMTconfigurationTag {
150  Name("PMTconfigurationTag"),
151  Comment("label of PMT readout configuration run-level data product")
152  };
153 
154  fhicl::Atom<bool> PlotBaselines {
155  Name("PlotBaselines"),
156  Comment("produce plots on the extracted baseline"),
157  true
158  };
159 
160  fhicl::Atom<bool> PrintBaselines {
161  Name("PrintBaselines"),
162  Comment("prints baseline values for each configured channel on each run"),
163  false
164  };
165 
166  fhicl::Atom<std::string> OutputCategory {
167  Name("OutputCategory"),
168  Comment("tag of the module output to console via message facility"),
169  "PMTWaveformBaselinesFromReadoutConfiguration"
170  };
171 
172  }; // struct Config
173 
174  using Parameters = art::EDProducer::Table<Config>;
175  // --- END Configuration -----------------------------------------------------
176 
177 
179  (Parameters const& config);
180 
181 
182 
183  // --- BEGIN Framework hooks -------------------------------------------------
184  /// Prepares the plots to be filled.
185  virtual void beginJob() override;
186 
187  /// Reads the PMT readout configuration.
188  virtual void beginRun(art::Run& run) override;
189 
190  /// Creates the data products.
191  virtual void produce(art::Event& event) override;
192 
193  // --- END Framework hooks ---------------------------------------------------
194 
195 
196  private:
197 
198  /// Type for baseline (same as V1730channelConfiguration::baseline)
199  using Baseline_t = signed short int;
200 
201  /// Mnemonic value for channels without baseline information.
202  static constexpr Baseline_t NoBaseline
203  = std::numeric_limits<Baseline_t>::min();
204 
205 
206  // --- BEGIN Configuration variables -----------------------------------------
207 
208  art::InputTag const fOpDetWaveformTag; ///< Input optical waveform tag.
209  art::InputTag const fPMTconfigurationTag; ///< Input PMT readout config tag.
210  bool const fPlotBaselines; ///< Whether to produce plots.
211  bool const fPrintBaselines; ///< Whether to print baselines on each run.
212 
213  std::string const fLogCategory; ///< Category name for the console output stream.
214 
215  // --- END Configuration variables -------------------------------------------
216 
217  // --- BEGIN Service variables -----------------------------------------------
218 
219  // --- END Service variables -------------------------------------------------
220 
221  unsigned int fConfigured = 0U; ///< Number of channels in PMT configuration.
222 
223  /// PMT baselines configured in the current run, indexed by channel.
224  std::vector<short signed int> fBaselines;
225 
226 
227  // --- BEGIN Algorithms ------------------------------------------------------
228 
229  // --- END Algorithms --------------------------------------------------------
230 
231  TH2* fHBaselines = nullptr; ///< All baselines, per channel.
232 
233 
234  /// Creates all the plots to be filled by the module.
235  void setupPlots();
236 
237 
238  /// Returns the configured baseline.
239  /// @throw cet::exception (category: "PMTWaveformBaselinesFromReadoutConfiguration")
240  /// if `channel` is not present in the configuration
241  Baseline_t getBaseline(raw::Channel_t channel) const;
242 
243  /// Returns the number of channels in configuration and a baseline map.
244  std::pair<unsigned int, std::vector<Baseline_t>>
246  (sbn::PMTconfiguration const& PMTconfig) const;
247 
248  /// Returns the number of channels currently configured with a baseline.
249  unsigned int nChannelsWithBaseline() const;
250 
251  /// Prints the current baselines on maesage facility (INFO level).
252  void printBaselines() const;
253 
254 }; // icarus::PMTWaveformBaselinesFromReadoutConfiguration
255 
256 
257 
258 //------------------------------------------------------------------------------
259 //--- Implementation
260 //------------------------------------------------------------------------------
261 namespace {
262 
263  /// Extracts the median of the collection between the specified iterators.
264  template <typename BIter, typename EIter>
265  auto median(BIter begin, EIter end) {
266 
267  using value_type = typename BIter::value_type;
268 
269  std::vector<value_type> data{ begin, end };
270  assert(!data.empty());
271 
272  auto const middle = data.begin() + data.size() / 2;
273  std::nth_element(data.begin(), middle, data.end());
274 
275  return *middle;
276 
277  } // median()
278 
279 } // local namespace
280 
281 
282 //------------------------------------------------------------------------------
283 //--- icarus::PMTWaveformBaselinesFromReadoutConfiguration
284 //------------------------------------------------------------------------------
286  (Parameters const& config)
287  : art::EDProducer(config)
288  // configuration
289  , fOpDetWaveformTag(config().OpticalWaveforms())
290  , fPMTconfigurationTag(config().PMTconfigurationTag())
291  , fPlotBaselines(config().PlotBaselines())
292  , fPrintBaselines(config().PrintBaselines())
293  , fLogCategory(config().OutputCategory())
294 {
295  //
296  // optional configuration parameters
297  //
298 
299  //
300  // configuration report (currently, more like a placeholder)
301  //
302  mf::LogInfo(fLogCategory)
303  << "Using configured baselines from '" << fPMTconfigurationTag.encode()
304  << "', waveform by waveform, on '" << fOpDetWaveformTag.encode() << "'.";
305 
306  //
307  // declaration of input
308  //
309  consumes<sbn::PMTconfiguration, art::InRun>(fPMTconfigurationTag);
310  consumes<std::vector<raw::OpDetWaveform>>(fOpDetWaveformTag);
311 
312  //
313  // declaration of output
314  //
315  produces<std::vector<icarus::WaveformBaseline>>();
316  produces<art::Assns<icarus::WaveformBaseline, raw::OpDetWaveform>>();
317 
318 } // icarus::PMTWaveformBaselinesFromReadoutConfiguration::PMTWaveformBaselinesFromReadoutConfiguration()
319 
320 
321 //------------------------------------------------------------------------------
323 
324  //
325  // set up the plots, if needed
326  //
327  if (fPlotBaselines) setupPlots();
328 
329 } // icarus::PMTWaveformBaselinesFromReadoutConfiguration::beginJob()
330 
331 
332 //------------------------------------------------------------------------------
334  (art::Run& run)
335 {
336  auto const& PMTconfig
337  = run.getProduct<sbn::PMTconfiguration>(fPMTconfigurationTag);
338 
339  std::vector<Baseline_t> newBaselines;
340  std::tie(fConfigured, newBaselines)
341  = extractBaselinesFromConfiguration(PMTconfig);
342 
343  bool const changed = (fBaselines != newBaselines);
344  fBaselines = std::move(newBaselines);
345 
346  if (fHBaselines) {
347  for (auto const& [ channel, baseline ]: util::enumerate(fBaselines)) {
348  if (baseline != NoBaseline)
349  fHBaselines->Fill(double(channel), double(baseline));
350  } // for
351  } // if plots
352 
353  if (fPrintBaselines && changed) printBaselines();
354 
355 } // icarus::PMTWaveformBaselinesFromReadoutConfiguration::beginRun()
356 
357 
358 //------------------------------------------------------------------------------
360  (art::Event& event)
361 {
362 
363  //
364  // fetch input
365  //
366  auto const& waveformHandle
367  = event.getValidHandle<std::vector<raw::OpDetWaveform>>(fOpDetWaveformTag);
368  auto const& waveforms = *waveformHandle;
369 
370  /*
371  * this may be needed in a future where processing happens per channel
372  * rather than per waveform:
373  // map address of waveform to art pointer to that waveform
374  auto const& opDetWavePtrs
375  = util::mapDataProductPointers(event, waveformHandle);
376  */
377 
378 
379  //
380  // compute all the baselines
381  //
382  std::vector<icarus::WaveformBaseline> baselines;
383  baselines.reserve(waveforms.size());
384 
385  art::Assns<icarus::WaveformBaseline, raw::OpDetWaveform> baselineToWaveforms;
386 
387  art::PtrMaker<icarus::WaveformBaseline> const makeBaselinePtr(event);
388 
389  for (auto const& [ iWaveform, waveform ]: util::enumerate(waveforms)) {
390  assert(iWaveform == baselines.size());
391 
393  { static_cast<float>(getBaseline(waveform.ChannelNumber())) };
394 
395  baselines.push_back(baseline);
396 
397  baselineToWaveforms.addSingle(
398  makeBaselinePtr(iWaveform),
399  art::Ptr<raw::OpDetWaveform>(waveformHandle, iWaveform)
400  );
401 
402  } // for waveforms
403 
404 
405  //
406  // output
407  //
408  event.put(
409  std::make_unique<std::vector<icarus::WaveformBaseline>>
410  (std::move(baselines))
411  );
412  event.put(
413  std::make_unique<art::Assns<icarus::WaveformBaseline, raw::OpDetWaveform>>
414  (std::move(baselineToWaveforms))
415  );
416 
417 
418 } // icarus::PMTWaveformBaselinesFromReadoutConfiguration::produce()
419 
420 
421 //------------------------------------------------------------------------------
423 
424  auto const& tfs = *(art::ServiceHandle<art::TFileService>());
425  auto const& geom = *(lar::providerFrom<geo::Geometry const>());
426 
427  unsigned int const nChannels = geom.NOpChannels();
428 
429  fHBaselines = tfs.make<TH2F>(
430  "Baselines",
431  "PMT baseline;channel;baseline per channel [ / 8 ADC ]",
432  nChannels, 0.0, double(nChannels),
433  256, 13312.0, 15360.0
434  );
435 
436 } // icarus::PMTWaveformBaselinesFromReadoutConfiguration::setupPlots()
437 
438 
439 //------------------------------------------------------------------------------
441  (raw::Channel_t channel) const -> Baseline_t
442 {
443 
444  auto const channelSlot = static_cast<std::size_t>(channel);
445  if (channelSlot < fBaselines.size()) {
446  Baseline_t const baseline = fBaselines[channelSlot];
447  if (baseline != NoBaseline) return baseline;
448  }
449  throw cet::exception("PMTWaveformBaselinesFromReadoutConfiguration")
450  << "No baseline configured for channel " << channel << ".\n";
451 
452 } // icarus::PMTWaveformBaselinesFromReadoutConfiguration::getBaseline()
453 
454 
455 
456 //------------------------------------------------------------------------------
457 auto
460  -> std::pair<unsigned int, std::vector<Baseline_t>>
461 {
462  constexpr auto NoChannelID = sbn::V1730channelConfiguration::NoChannelID;
463 
464  unsigned int nConfigured = 0U;
465  std::vector<Baseline_t> baselines;
466 
467  for (sbn::V1730Configuration const& boardConfig: PMTconfig.boards) {
468  for (sbn::V1730channelConfiguration const& channelConfig
469  : boardConfig.channels
470  ) {
471 
472  ++nConfigured;
473 
474  raw::Channel_t const channelID = channelConfig.channelID;
475  if (channelID == NoChannelID) continue;
476 
477  auto const channelSlot = static_cast<std::size_t>(channelID);
478  if (baselines.size() <= channelSlot)
479  baselines.resize(channelSlot + 1U, NoBaseline);
480 
481  Baseline_t const baseline = channelConfig.baseline;
482  if (baseline == NoBaseline) {
483  // if this exception is thrown, it means we need a different way than
484  // a special baseline value (`NoBaseline`) to track the missing channels
485  // (e.g. a map or a std::vector<std::optional<...>>)
486  throw art::Exception(art::errors::LogicError)
487  << "A configured baseline actually matches the special value "
488  << "`NoBaseline` (" << NoBaseline << ")\n";
489  } // if bad luck
490 
491  baselines[channelSlot] = baseline;
492 
493  } // for all channels on a board
494  } // for all boards
495 
496  return { nConfigured, std::move(baselines) };
497 } // icarus::PMTWaveformBaselinesFromReadoutConfiguration::extractBaselinesFromConfiguration()
498 
499 
500 
501 //------------------------------------------------------------------------------
502 unsigned int
504  () const
505 {
506  return fBaselines.size()
507  - std::count(fBaselines.begin(), fBaselines.end(), NoBaseline);
508 }
509 
510 //------------------------------------------------------------------------------
512  const
513 {
514  mf::LogInfo log { fLogCategory };
515 
516  unsigned int const nChannels = nChannelsWithBaseline();
517  assert(nChannels <= fConfigured);
518  unsigned int const nMissing = fConfigured - nChannels;
519 
520  log << "Configuration has " << nChannels << " channels";
521  if (nMissing > 0U)
522  log << " (plus " << nMissing << " not associated to any channel ID)";
523  log << ":";
524  for (auto [ channelID, baseline ]: util::enumerate(fBaselines)) {
525  if (baseline == NoBaseline) continue;
526  log << "\n channel " << channelID << ": " << baseline;
527  } // for all channels
528 
529 } // icarus::PMTWaveformBaselinesFromReadoutConfiguration::printBaselines()
530 
531 //------------------------------------------------------------------------------
533 
534 
535 //------------------------------------------------------------------------------
Utilities related to art service access.
signed short int Baseline_t
Type for baseline (same as V1730channelConfiguration::baseline)
std::pair< unsigned int, std::vector< Baseline_t > > extractBaselinesFromConfiguration(sbn::PMTconfiguration const &PMTconfig) const
Returns the number of channels in configuration and a baseline map.
Definition of util::enumerate().
std::string const fLogCategory
Category name for the console output stream.
virtual void beginRun(art::Run &run) override
Reads the PMT readout configuration.
std::vector< short signed int > fBaselines
PMT baselines configured in the current run, indexed by channel.
static constexpr Baseline_t NoBaseline
Mnemonic value for channels without baseline information.
auto enumerate(Iterables &&...iterables)
Range-for loop helper tracking the number of iteration.
Definition: enumerate.h:69
fDetProps &fDetProps fDetProps &fDetProps fLogCategory
Information from the configuration of a V1730 PMT readout board.
Access the description of detector geometry.
void printBaselines() const
Prints the current baselines on maesage facility (INFO level).
process_name PMTconfig
auto end(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:585
BEGIN_PROLOG baseline
BEGIN_PROLOG vertical distance to the surface Name
Information from the configuration of PMT readout.
Extracts PMT baseline settings from PMT readout configuration.
Information from the configuration of a V1730 PMT readout board.
unsigned int nChannelsWithBaseline() const
Returns the number of channels currently configured with a baseline.
auto begin(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:573
physics producers discrimopdaq OpticalWaveforms
short signed int baseline
Baseline (BaselineCh&lt;N+1&gt;).
virtual void produce(art::Event &event) override
Creates the data products.
Class containing a waveform baseline value.
art::ServiceHandle< art::TFileService > tfs
static constexpr auto NoChannelID
Special value for unassigned channel ID.
raw::Channel_t channelID
Offline channel ID.
std::size_t count(Cont const &cont)
std::vector< sbn::V1730channelConfiguration > channels
Configuration of each channel.
Class containing configuration for a V1730 channel.
Class containing configuration for PMT readout.
art framework interface to geometry description
std::vector< sbn::V1730Configuration > boards
Configuration of all PMT readout boards.
Class containing configuration for a V1730 board.