All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
RawDataDrawer.cxx
Go to the documentation of this file.
1 /**
2  * @file RawDataDrawer.cxx
3  * @brief Class to aid in the rendering of RawData objects
4  * @author messier@indiana.edu
5  *
6  * This class prepares the rendering of the raw digits content for the 2D view
7  * of the display. In particular, it fills the 2D view of detected charge
8  * vs. time and wire number for a single TPC.
9  * The code is not ready to support an arbitrary TPC from the detector;
10  * it can be fixed to support that, but a good deal of the calling code also
11  * has to change.
12  *
13  * Notes from Gianluca Petrillo (petrillo@fnal.gov) on August 19, 2015
14  * --------------------------------------------------------------------
15  *
16  * As of August 2015, this class performs some preprocessing for the data to
17  * be displayed.
18  * The main argument here is that the display assigns to each plane a space
19  * of 600x250 pixels, while the amound of data is typically 500x5000 elements.
20  * These numbers vary wildly: while the first number is close to the current
21  * default size for a new window, that window can be resized and windows many
22  * times larger can be obtained; for the data content, a MicroBooNE plane
23  * contains 2400x9600 elements (that's roughly 25 millions, and there is three
24  * of them), each of the two LArIAT planes contains 240x3200 (less than 1
25  * million), and a single TPC of DUNE's 35t prototype about 350x4800 (but it
26  * is larger when extended readout window modes are used).
27  * The code produces TBox'es to be rendered by the nuevdb event display
28  * infrastructure. The code before August 2015 would produce one box for each
29  * of the aggregated charge; charge could be aggregated in time by FHiCL
30  * configuration to merge TDC ticks. This typically bloats rendering time,
31  * since rendering of all boxes is attempted.
32  * The new code performs dynamic aggregation after discovering the actual size
33  * of the graphical viewport, and it submits at most one TBox per pixel.
34  * Additional improvement is caching of the uncompressed raw data, so that
35  * following zooming is faster, and especially a way to bypass the decompression
36  * when the original data is not compressed in the first place, that saves
37  * a bit of time and quite some memory.
38  *
39  * @todo There are probably a number of glitches and shortcuts in the current
40  * preprocessing implementation. If they become a problem, they can probably be
41  * fixed on demand. Examples include:
42  * - no alignment of the boxes with the wire and tick numbers
43  * - possible border effects
44  * - the fact that only the maximum charge is displayed on each box , and no
45  * dynamic charge range detection is in place
46  * - the first drawing is performed with a grid that is not the final one
47  * (because for some reason the frame starts larger than it should and is
48  * resized later)
49  * - the drawing honours the zoom region the user selected, even if the viewport
50  * ends up being larger (that is, if upstream decides that the zoom region is
51  * too small and a larger area will be drawn, the additional area will be
52  * blank)
53  */
54 #include <algorithm> // std::fill(), std::find_if(), ...
55 #include <cmath> // std::abs(), ...
56 #include <cstddef> // std::ptrdiff_t
57 #include <limits> // std::numeric_limits<>
58 #include <memory> // std::unique_ptr()
59 #include <tuple>
60 #include <type_traits> // std::add_const_t<>, ...
61 #include <typeinfo> // to use typeid()
62 #include <utility> // std::move()
63 
64 #include "TBox.h"
65 #include "TFrame.h"
66 #include "TH1F.h"
67 #include "TVirtualPad.h"
68 
71 #include "lardataalg/Utilities/StatCollector.h" // lar::util::MinMaxCollector<>
73 #include "lardataobj/RawData/raw.h"
74 #include "lareventdisplay/EventDisplay/ChangeTrackers.h" // util::PlaneDataChangeTracker_t
82 #include "nuevdb/EventDisplayBase/View2D.h"
83 
84 #include "art/Framework/Principal/Event.h"
85 #include "art/Framework/Principal/Handle.h"
86 #include "art/Framework/Services/Registry/ServiceHandle.h"
87 #include "canvas/Persistency/Common/Ptr.h"
88 #include "canvas/Utilities/Exception.h"
89 #include "canvas/Utilities/InputTag.h"
90 #include "cetlib_except/demangle.h"
91 #include "messagefacility/MessageLogger/MessageLogger.h"
92 
93 namespace {
94  template <typename Stream, typename T>
95  void
96  PrintRange(Stream&& out, std::string header, lar::util::MinMaxCollector<T> const& range)
97  {
98  out << header << ": " << range.min() << " -- " << range.max() << " ("
99  << (range.has_data() ? "valid" : "invalid") << ")";
100  } // PrintRange()
101 } // local namespace
102 
103 // internal use classes declaration;
104 // it can't live in the header because it uses C++11/14
105 namespace details {
106  /// @todo Document this code and make it into a library
107  template <typename T>
109  public:
110  using value_type = T;
111  using const_value_type = std::add_const_t<value_type>;
112  using reference = std::add_lvalue_reference_t<value_type>;
113  using const_reference = std::add_lvalue_reference_t<const_value_type>;
114  using pointer = std::add_pointer_t<value_type>;
115  using const_pointer = std::add_pointer_t<const_value_type>;
116 
117  /// Destructor: gets rid of the owned data
119 
120  /// @name Dereferencing
121  /// @{
122  const_reference operator*() const { return *pData; }
123  reference operator*() { return *pData; }
124 
125  const_pointer operator->() const { return pData; }
126  pointer operator->() { return pData; }
127  /// @}
128 
129  /// Returns whether we point to something
130  operator bool() const { return hasData(); }
131 
132  /// Returns whether we point to nothing
133  bool operator!() const { return !hasData(); }
134 
135  /// Returns whether we have data
136  bool
137  hasData() const
138  {
139  return bool(pData);
140  }
141 
142  /// Returns whether we have data and we own it
143  bool
144  owned() const
145  {
146  return bOwned && hasData();
147  }
148 
149  /// Sets the data and the ownership
150  void
151  SetData(pointer data, bool owned)
152  {
153  Clear();
154  bOwned = owned;
155  pData = data;
156  }
157  /// Acquire ownership of the specified data
158  void
160  {
161  SetData(data, true);
162  }
163  /// Point to the specified data, not acquiring ownership
164  void
166  {
167  SetData(data, false);
168  }
169  /// Point to the specified data, not acquiring ownership
170  void
172  {
173  SetData(&data, false);
174  }
175  /// Move data from the specified object, and own it
176  void
177  StealData(std::remove_const_t<T>&& data)
178  {
179  AcquireData(new T(std::move(data)));
180  }
181  /// Create a owned copy of the specified object
182  void
183  NewData(T const& data)
184  {
185  AcquireData(new T(data));
186  }
187  /// Stop pointing to the data; if owned, delete it
188  void
190  {
191  if (bOwned) delete pData;
192  pData = nullptr;
193  bOwned = false;
194  } // Clear()
195 
196  protected:
197  bool bOwned = false; ///< whether we own our data
198  pointer pData = nullptr; ///< pointer to data
199  }; // class PointerToData_t<>
200 }
201 
202 namespace evd {
203  namespace details {
204 
205  /// Information about a RawDigit; may contain uncompressed duplicate of data
207  public:
208  /// Returns an art pointer to the actual digit
209  art::Ptr<raw::RawDigit>
210  DigitPtr() const
211  {
212  return digit;
213  }
214 
215  /// Returns an art pointer to the actual digit
216  raw::RawDigit const&
217  Digit() const
218  {
219  return *digit;
220  }
221 
222  /// Returns the channel of this digit (for convenience)
224  Channel() const
225  {
226  return digit ? digit->Channel() : raw::InvalidChannelID;
227  }
228 
229  /// minimum charge
230  short
231  MinCharge() const
232  {
233  return SampleInfo().min_charge;
234  }
235 
236  /// maximum charge
237  short
238  MaxCharge() const
239  {
240  return SampleInfo().max_charge;
241  }
242 
243  /// average charge
244  // short AverageCharge() const { return SampleInfo().average_charge; }
245 
246  /// Returns the uncompressed data
247  raw::RawDigit::ADCvector_t const& Data() const;
248 
249  /// Parses the specified digit
250  void Fill(art::Ptr<raw::RawDigit> const& src);
251 
252  /// Deletes the data
253  void Clear();
254 
255  /// Dumps the content of the digit info
256  template <typename Stream>
257  void Dump(Stream&& out) const;
258 
259  private:
260  typedef struct {
261  short min_charge = std::numeric_limits<short>::max(); ///< minimum charge
262  short max_charge = std::numeric_limits<short>::max(); ///< maximum charge
263  // float average_charge = 0.; ///< average charge
264  } SampleInfo_t; // SampleInfo_t
265 
266  art::Ptr<raw::RawDigit> digit; ///< a pointer to the actual digit
267 
268  /// Uncompressed data
269  mutable ::details::PointerToData_t<raw::RawDigit::ADCvector_t const> data;
270 
271  /// Information collected from the uncompressed data
272  mutable std::unique_ptr<SampleInfo_t> sample_info;
273 
274  /// Fills the uncompressed data cache
275  void UncompressData() const;
276 
277  /// Fills the sample info cache
278  void CollectSampleInfo() const;
279 
280  /// Returns the uncompressed data
281  SampleInfo_t const& SampleInfo() const;
282 
283  }; // class RawDigitInfo_t
284 
285  /// Cached set of RawDigitInfo_t
287  public:
288  /// Returns the list of digit info
289  std::vector<RawDigitInfo_t> const&
290  Digits() const
291  {
292  return digits;
293  }
294 
295  /// Returns a pointer to the digit info of given channel, nullptr if none
296  RawDigitInfo_t const* FindChannel(raw::ChannelID_t channel) const;
297 
298  /// Returns the largest number of samples in the unpacked raw digits
299  size_t
300  MaxSamples() const
301  {
302  return max_samples;
303  }
304 
305  /// Returns whether the cache is empty() (STL-like interface)
306  bool
307  empty() const
308  {
309  return digits.empty();
310  }
311 
312  /// Empties the cache
313  void Clear();
314 
315  /// Fills the cache from the specified raw digits product handle
316  void Refill(art::Handle<std::vector<raw::RawDigit>>& rdcol);
317 
318  /// Clears the cache and marks it as invalid (use Update() to fill it)
319  void Invalidate();
320 
321  /// Updates the cache for new_timestamp using the specified event
322  /// @return true if it needed to update (that might have failed)
323  bool Update(art::Event const& evt, CacheID_t const& new_timestamp);
324 
325  /// Dump the content of the cache
326  template <typename Stream>
327  void Dump(Stream&& out) const;
328 
329  private:
331 
332  bool bUpToDate = false;
333  std::vector<raw::RawDigit> const* digits = nullptr;
334 
335  BoolWithUpToDateMetadata() = default;
336  BoolWithUpToDateMetadata(bool uptodate, std::vector<raw::RawDigit> const* newdigits)
337  : bUpToDate(uptodate), digits(newdigits)
338  {}
339 
340  operator bool() const { return bUpToDate; }
341  }; // struct BoolWithUpToDateMetadata
342 
343  std::vector<RawDigitInfo_t> digits; ///< vector of raw digit information
344 
345  CacheID_t timestamp; ///< object expressing validity range of cached data
346 
347  size_t max_samples = 0; ///< the largest number of ticks in any digit
348 
349  /// Checks whether an update is needed; can load digits in the process
351  art::Event const* evt = nullptr) const;
352 
353  static std::vector<raw::RawDigit> const* ReadProduct(art::Event const& evt,
354  art::InputTag label);
355 
356  }; // struct RawDigitCacheDataClass
357 
358  std::vector<evd::details::RawDigitInfo_t>::const_iterator
360  {
361  return cache.Digits().cbegin();
362  }
363  std::vector<evd::details::RawDigitInfo_t>::const_iterator
365  {
366  return cache.Digits().cend();
367  }
368 
369  /// Manages a cell-like division of a coordinate
371  public:
372  /// Default constructor: an invalid range
373  GridAxisClass() { Init(0, 0., 0.); }
374 
375  /// Constructor: sets the limits and the number of cells
376  GridAxisClass(size_t nDiv, float new_min, float new_max) { Init(nDiv, new_min, new_max); }
377 
378  //@{
379  /// Returns the index of the specified cell
380  std::ptrdiff_t GetCell(float coord) const;
381  std::ptrdiff_t
382  operator()(float coord) const
383  {
384  return GetCell(coord);
385  }
386  //@}
387 
388  /// Returns whether the cell is present or not
389  bool
390  hasCell(std::ptrdiff_t iCell) const
391  {
392  return (iCell >= 0) && ((size_t)iCell < NCells());
393  }
394 
395  /// Returns whether the coordinate is included in the range or not
396  bool
397  hasCoord(float coord) const
398  {
399  return (coord >= Min()) && (coord < Max());
400  }
401 
402  //@{
403  /// Returns the extremes of the axis
404  float
405  Min() const
406  {
407  return min;
408  }
409  float
410  Max() const
411  {
412  return max;
413  }
414  //@}
415 
416  /// Returns the length of the axis
417  float
418  Length() const
419  {
420  return max - min;
421  }
422 
423  /// Returns the length of the axis
424  size_t
425  NCells() const
426  {
427  return n_cells;
428  }
429 
430  /// Returns whether minimum and maximum match
431  bool
432  isEmpty() const
433  {
434  return max == min;
435  }
436 
437  /// Returns the cell size
438  float
439  CellSize() const
440  {
441  return cell_size;
442  }
443 
444  /// Returns the lower edge of the cell
445  float
446  LowerEdge(std::ptrdiff_t iCell) const
447  {
448  return Min() + CellSize() * iCell;
449  }
450 
451  /// Returns the upper edge of the cell
452  float
453  UpperEdge(std::ptrdiff_t iCell) const
454  {
455  return LowerEdge(iCell + 1);
456  }
457 
458  /// Initialize the axis, returns whether cell size is finite
459  bool Init(size_t nDiv, float new_min, float new_max);
460 
461  /// Initialize the axis limits, returns whether cell size is finite
462  bool SetLimits(float new_min, float new_max);
463 
464  /// Expands the cell (at fixed range) to meet minimum cell size
465  /// @return Whether the cell size was changed
466  bool SetMinCellSize(float min_size);
467 
468  /// Expands the cell (at fixed range) to meet maximum cell size
469  /// @return Whether the cell size was changed
470  bool SetMaxCellSize(float max_size);
471 
472  /// Expands the cell (at fixed range) to meet maximum cell size
473  /// @return Whether the cell size was changed
474  bool
475  SetCellSizeBoundary(float min_size, float max_size)
476  {
477  return SetMinCellSize(min_size) || SetMaxCellSize(max_size);
478  }
479 
480  template <typename Stream>
481  void Dump(Stream&& out) const;
482 
483  private:
484  size_t n_cells; ///< number of cells in the axis
485  float min, max; ///< extremes of the axis
486 
487  float cell_size; ///< size of each cell
488 
489  }; // GridAxisClass
490 
491  /// Manages a grid-like division of 2D space
493  public:
494  /// Default constructor: invalid ranges
496 
497  /// Constructor: sets the extremes and assumes one cell for each element
498  CellGridClass(unsigned int nWires, unsigned int nTDC);
499 
500  /// Constructor: sets the wire and TDC ranges in detail
501  CellGridClass(float min_wire,
502  float max_wire,
503  unsigned int nWires,
504  float min_tdc,
505  float max_tdc,
506  unsigned int nTDC);
507 
508  /// Returns the total number of cells in the grid
509  size_t
510  NCells() const
511  {
512  return wire_axis.NCells() * tdc_axis.NCells();
513  }
514 
515  /// Return the information about the wires
516  GridAxisClass const&
517  WireAxis() const
518  {
519  return wire_axis;
520  }
521 
522  /// Return the information about the TDCs
523  GridAxisClass const&
524  TDCAxis() const
525  {
526  return tdc_axis;
527  }
528 
529  /// Returns the index of specified cell, or -1 if out of range
530  std::ptrdiff_t GetCell(float wire, float tick) const;
531 
532  /// Returns the coordinates { w1, t1, w2, t2 } of specified cell
533  std::tuple<float, float, float, float> GetCellBox(std::ptrdiff_t iCell) const;
534 
535  //@{
536  /// Returns whether the range includes the specified wire
537  bool
538  hasWire(float wire) const
539  {
540  return wire_axis.hasCoord(wire);
541  }
542  bool
543  hasWire(int wire) const
544  {
545  return hasWire((float)wire);
546  }
547  //@}
548 
549  //@{
550  /// Returns whether the range includes the specified wire
551  bool
552  hasTick(float tick) const
553  {
554  return tdc_axis.hasCoord(tick);
555  }
556  bool
557  hasTick(int tick) const
558  {
559  return hasTick((float)tick);
560  }
561  //@}
562 
563  /// Increments the specified cell of cont with the value v
564  /// @return whether there was such a cell
565  template <typename CONT>
566  bool
567  Add(CONT& cont, float wire, float tick, typename CONT::value_type v)
568  {
569  std::ptrdiff_t cell = GetCell(wire, tick);
570  if (cell < 0) return false;
571  cont[(size_t)cell] += v;
572  return true;
573  } // Add()
574 
575  /// @name Setters
576  /// @{
577  /// Sets a simple wire range: all the wires, one cell per wire
578  void
579  SetWireRange(unsigned int nWires)
580  {
581  SetWireRange(0., (float)nWires, nWires);
582  }
583 
584  /// Sets the wire range, leaving the number of wire cells unchanged
585  void
586  SetWireRange(float min_wire, float max_wire)
587  {
588  wire_axis.SetLimits(min_wire, max_wire);
589  }
590 
591  /// Sets the complete wire range
592  void
593  SetWireRange(float min_wire, float max_wire, unsigned int nWires)
594  {
595  wire_axis.Init(nWires, min_wire, max_wire);
596  }
597 
598  /// Sets the complete wire range, with minimum cell size
599  void
600  SetWireRange(float min_wire, float max_wire, unsigned int nWires, float min_size)
601  {
602  wire_axis.Init(nWires, min_wire, max_wire);
603  wire_axis.SetMinCellSize(min_size);
604  }
605 
606  /// Sets a simple TDC range: all the ticks, one cell per tick
607  void
608  SetTDCRange(unsigned int nTDC)
609  {
610  SetTDCRange(0., (float)nTDC, nTDC);
611  }
612 
613  /// Sets the complete TDC range
614  void
615  SetTDCRange(float min_tdc, float max_tdc, unsigned int nTDC)
616  {
617  tdc_axis.Init(nTDC, min_tdc, max_tdc);
618  }
619 
620  /// Sets the TDC range, leaving the number of ticks unchanged
621  void
622  SetTDCRange(float min_tdc, float max_tdc)
623  {
624  tdc_axis.SetLimits(min_tdc, max_tdc);
625  }
626 
627  /// Sets the complete TDC range, with minimum cell size
628  void
629  SetTDCRange(float min_tdc, float max_tdc, unsigned int nTDC, float min_size)
630  {
631  tdc_axis.Init(nTDC, min_tdc, max_tdc);
632  tdc_axis.SetMinCellSize(min_size);
633  }
634 
635  /// @}
636 
637  /// Sets the minimum size for wire cells
638  bool
639  SetMinWireCellSize(float min_size)
640  {
641  return wire_axis.SetMinCellSize(min_size);
642  }
643 
644  /// Sets the minimum size for TDC cells
645  bool
646  SetMinTDCCellSize(float min_size)
647  {
648  return tdc_axis.SetMinCellSize(min_size);
649  }
650 
651  /// Prints the current axes on the specified stream
652  template <typename Stream>
653  void Dump(Stream&& out) const;
654 
655  private:
658  }; // CellGridClass
659 
660  //--------------------------------------------------------------------------
661  /// Applies Birks correction
663  public:
665  : detProp{dp}
666  , wirePitch{art::ServiceHandle<geo::Geometry const>()->WirePitch(pid)}
667  , electronsToADC{dp.ElectronsToADC()}
668  {}
669 
670  /// Applies Birks correction to the specified pedestal-subtracted charge
671  double
672  operator()(float adc) const
673  {
674  if (adc < 0.) return 0.;
675  double const dQdX = adc / wirePitch / electronsToADC;
676  return detProp.BirksCorrection(dQdX);
677  }
678 
679  private:
681  double wirePitch; ///< wire pitch
682  double electronsToADC; ///< conversion constant
683 
684  }; // ADCCorrectorClass
685  //--------------------------------------------------------------------------
686  } // namespace details
687 } // namespace evd
688 
689 namespace evd {
690 
691  // empty vector
692  std::vector<raw::RawDigit> const RawDataDrawer::EmptyRawDigits;
693 
694  //......................................................................
696  : digit_cache(new details::RawDigitCacheDataClass)
697  , fStartTick(0)
698  , fTicks(2048)
699  , fCacheID(new details::CacheID_t)
700  , fDrawingRange(new details::CellGridClass)
701  {
702  art::ServiceHandle<geo::Geometry const> geo;
703 
704  art::ServiceHandle<evd::RawDrawingOptions const> rawopt;
705  geo::TPCID tpcid(rawopt->fCryostat, rawopt->fTPC);
706 
707  fStartTick = rawopt->fStartTick;
708  fTicks = rawopt->fTicks;
709 
710  // set the list of bad channels in this detector
711  unsigned int nplanes = geo->Nplanes(tpcid);
712  fWireMin.resize(nplanes, -1);
713  fWireMax.resize(nplanes, -1);
714  fTimeMin.resize(nplanes, -1);
715  fTimeMax.resize(nplanes, -1);
716  fRawCharge.resize(nplanes, 0);
717  fConvertedCharge.resize(nplanes, 0);
718  }
719 
720  //......................................................................
722  {
723  delete digit_cache;
724  delete fDrawingRange;
725  delete fCacheID;
726  }
727 
728  //......................................................................
729  void
730  RawDataDrawer::SetDrawingLimits(float low_wire, float high_wire, float low_tdc, float high_tdc)
731  {
732  MF_LOG_DEBUG("RawDataDrawer") << __func__ << "() setting drawing range as wires ( " << low_wire
733  << " - " << high_wire << " ), ticks ( " << low_tdc << " - "
734  << high_tdc << " )";
735 
736  // we need to set the minimum cell size to 1, otherwise some cell will not
737  // cover any wire/tick and they will be always empty
738  if (PadResolution) {
739  // TODO implement support for swapping axes here
740  unsigned int wire_pixels = PadResolution.width;
741  unsigned int tdc_pixels = PadResolution.height;
742  fDrawingRange->SetWireRange(low_wire, high_wire, wire_pixels, 1.F);
743  fDrawingRange->SetTDCRange(low_tdc, high_tdc, tdc_pixels, 1.F);
744  }
745  else {
746  MF_LOG_DEBUG("RawDataDrawer") << "Pad size not available -- using existing cell size";
747  fDrawingRange->SetWireRange(low_wire, high_wire);
749  fDrawingRange->SetTDCRange(low_tdc, high_tdc);
751  }
752 
753  } // RawDataDrawer::SetDrawingLimits()
754 
755  void
757  {
758  SetDrawingLimits(fWireMin[plane], fWireMax[plane], fTimeMin[plane], fTimeMax[plane]);
759  } // RawDataDrawer::SetDrawingLimitsFromRoI()
760 
761  void
762  RawDataDrawer::ExtractRange(TVirtualPad* pPad, std::vector<double> const* zoom /* = nullptr */)
763  {
764  mf::LogDebug log("RawDataDrawer");
765  log << "ExtractRange() on pad '" << pPad->GetName() << "'";
766 
767  TFrame const* pFrame = pPad->GetFrame();
768  if (pFrame) {
769  // these coordinates are used to find the actual extent of pad in pixels
770  double low_wire = pFrame->GetX1(), high_wire = pFrame->GetX2();
771  double low_tdc = pFrame->GetY1(), high_tdc = pFrame->GetY2();
772  double const wire_pixels = pPad->XtoAbsPixel(high_wire) - pPad->XtoAbsPixel(low_wire);
773  double const tdc_pixels = -(pPad->YtoAbsPixel(high_tdc) - pPad->YtoAbsPixel(low_tdc));
774 
775  PadResolution.width = (unsigned int)wire_pixels;
776  PadResolution.height = (unsigned int)tdc_pixels;
777 
778  log << "\n frame window is " << PadResolution.width << "x" << PadResolution.height
779  << " pixel big and";
780  // those coordinates also are a (unreliable) estimation of the zoom;
781  // if we have a better one, let's use it
782  // (this does not change the size of the window in terms of pixels)
783  if (zoom) {
784  log << ", from external source,";
785  low_wire = (*zoom)[0];
786  high_wire = (*zoom)[1];
787  low_tdc = (*zoom)[2];
788  high_tdc = (*zoom)[3];
789  }
790 
791  log << " spans wires " << low_wire << "-" << high_wire << " and TDC " << low_tdc << "-"
792  << high_tdc;
793 
794  // TODO support swapping axes here:
795  // if (rawopt.fAxisOrientation < 1) { normal ; } else { swapped; }
796 
797  fDrawingRange->SetWireRange(low_wire, high_wire, (unsigned int)wire_pixels, 1.0);
798  fDrawingRange->SetTDCRange(low_tdc, high_tdc, (unsigned int)tdc_pixels, 1.0);
799  }
800  else {
801  // keep the old frame (if any)
802  log << "\n no frame!";
803  }
804 
805  } // RawDataDrawer::ExtractRange()
806 
807  //......................................................................
809  public:
810  OperationBaseClass(geo::PlaneID const& pid, RawDataDrawer* data_drawer = nullptr)
811  : pRawDataDrawer(data_drawer), planeID(pid)
812  {}
813 
814  virtual ~OperationBaseClass() = default;
815 
816  virtual bool
818  {
819  return true;
820  }
821 
822  virtual bool
824  {
825  return true;
826  }
827  virtual bool ProcessTick(size_t) { return true; }
828 
829  virtual bool Operate(geo::WireID const& wireID, size_t tick, float adc) = 0;
830 
831  virtual bool
833  {
834  return true;
835  }
836 
837  virtual std::string
838  Name() const
839  {
840  return cet::demangle_symbol(typeid(*this).name());
841  }
842 
843  bool
844  operator()(geo::WireID const& wireID, size_t tick, float adc)
845  {
846  return Operate(wireID, tick, adc);
847  }
848 
849  geo::PlaneID const&
850  PlaneID() const
851  {
852  return planeID;
853  }
856  {
857  return pRawDataDrawer;
858  }
859 
860  protected:
862 
863  private:
865  }; // class RawDataDrawer::OperationBaseClass
866 
867  //......................................................................
869  std::vector<std::unique_ptr<RawDataDrawer::OperationBaseClass>> operations;
870 
871  public:
872  ManyOperations(geo::PlaneID const& pid, RawDataDrawer* data_drawer = nullptr)
873  : OperationBaseClass(pid, data_drawer)
874  {}
875 
876  bool
877  Initialize() override
878  {
879  bool bAllOk = true;
880  for (std::unique_ptr<OperationBaseClass> const& op : operations)
881  if (!op->Initialize()) bAllOk = false;
882  return bAllOk;
883  }
884 
885  bool
886  ProcessWire(geo::WireID const& wireID) override
887  {
888  for (std::unique_ptr<OperationBaseClass> const& op : operations)
889  if (op->ProcessWire(wireID)) return true;
890  return false;
891  }
892  bool
893  ProcessTick(size_t tick) override
894  {
895  for (std::unique_ptr<OperationBaseClass> const& op : operations)
896  if (op->ProcessTick(tick)) return true;
897  return false;
898  }
899 
900  bool
901  Operate(geo::WireID const& wireID, size_t tick, float adc) override
902  {
903  for (std::unique_ptr<OperationBaseClass> const& op : operations)
904  if (!op->Operate(wireID, tick, adc)) return false;
905  return true;
906  }
907 
908  bool
909  Finish() override
910  {
911  bool bAllOk = true;
912  for (std::unique_ptr<OperationBaseClass> const& op : operations)
913  if (!op->Finish()) bAllOk = false;
914  return bAllOk;
915  }
916 
917  std::string
918  Name() const override
919  {
920  std::string msg = cet::demangle_symbol(typeid(*this).name());
921  msg += (" [running " + std::to_string(operations.size()) + " operations:");
922  for (auto const& op : operations) { // it's unique_ptr<OperationBaseClass>
923  if (op)
924  msg += " " + op->Name();
925  else
926  msg += " <invalid>";
927  }
928  return msg + " ]";
929  }
930 
932  Operator(size_t iOp)
933  {
934  return operations.at(iOp).get();
935  }
936  OperationBaseClass const*
937  Operator(size_t iOp) const
938  {
939  return operations.at(iOp).get();
940  }
941 
942  void
943  AddOperation(std::unique_ptr<OperationBaseClass> new_op)
944  {
945  if (!new_op) return;
946  if (PlaneID() != new_op->PlaneID()) {
947  throw art::Exception(art::errors::LogicError)
948  << "RawDataDrawer::ManyOperations(): trying to run operations on "
949  << std::string(PlaneID()) << " and " << std::string(new_op->PlaneID())
950  << " at the same time";
951  }
952  if (RawDataDrawerPtr() && (RawDataDrawerPtr() != new_op->RawDataDrawerPtr())) {
953  throw art::Exception(art::errors::LogicError)
954  << "RawDataDrawer::ManyOperations(): "
955  "trying to run operations on different RawDataDrawer"; // possible, but very unlikely
956  }
957  operations.emplace_back(std::move(new_op));
958  }
959 
960  }; // class RawDataDrawer::ManyOperations
961 
962  //......................................................................
963  bool
964  RawDataDrawer::RunOperation(art::Event const& evt, OperationBaseClass* operation)
965  {
966  geo::PlaneID const& pid = operation->PlaneID();
967  art::ServiceHandle<evd::RawDrawingOptions const> rawopt;
968 
969  if (digit_cache->empty()) return true;
970 
971  MF_LOG_DEBUG("RawDataDrawer") << "RawDataDrawer::RunOperation() running " << operation->Name();
972 
973  // if we have an initialization failure, return false immediately;
974  // but it's way better if the failure throws an exception
975  if (!operation->Initialize()) return false;
976 
977  lariov::ChannelStatusProvider const& channelStatus =
978  art::ServiceHandle<lariov::ChannelStatusService const>()->GetProvider();
979 
980  //get pedestal conditions
981  const lariov::DetPedestalProvider& pedestalRetrievalAlg =
982  *(lar::providerFrom<lariov::DetPedestalService>());
983 
984  geo::GeometryCore const& geom = *(lar::providerFrom<geo::Geometry>());
985 
986  // loop over all the channels/raw digits
987  for (evd::details::RawDigitInfo_t const& digit_info : *digit_cache) {
988  raw::RawDigit const& hit = digit_info.Digit();
989  raw::ChannelID_t const channel = hit.Channel();
990 
991  // skip the bad channels
992  if (!channelStatus.IsPresent(channel)) continue;
993  // The following test is meant to be temporary until the "correct" solution is implemented
994  if (!ProcessChannelWithStatus(channelStatus.Status(channel))) continue;
995 
996  // we have a list of all channels, but we are drawing only on one plane;
997  // most of the channels will not contribute to this plane,
998  // and before we start querying databases, unpacking data etc.
999  // we want to know it's for something
1000 
1001  std::vector<geo::WireID> WireIDs = geom.ChannelToWire(channel);
1002 
1003  bool bDrawChannel = false;
1004  for (geo::WireID const& wireID : WireIDs) {
1005  if (wireID.planeID() != pid) continue; // not us!
1006  bDrawChannel = true;
1007  break;
1008  } // for wires
1009  if (!bDrawChannel) continue;
1010 
1011  // collect bad channels
1012  bool const bGood = rawopt->fSeeBadChannels || !channelStatus.IsBad(channel);
1013 
1014  // nothing else to be done if the channel is not good:
1015  // cells are marked bad by default and if any good channel falls in any of
1016  // them, they become good
1017  if (!bGood) continue;
1018 
1019  // at this point we know we have to process this channel
1020  raw::RawDigit::ADCvector_t const& uncompressed = digit_info.Data();
1021 
1022  // recover the pedestal
1023  float pedestal = 0;
1024  if (rawopt->fPedestalOption == 0) { pedestal = pedestalRetrievalAlg.PedMean(channel); }
1025  else if (rawopt->fPedestalOption == 1) {
1026  pedestal = hit.GetPedestal();
1027  }
1028  else if (rawopt->fPedestalOption == 2) {
1029  pedestal = 0;
1030  }
1031  else {
1032  mf::LogWarning("RawDataDrawer")
1033  << " PedestalOption is not understood: " << rawopt->fPedestalOption
1034  << ". Pedestals not subtracted.";
1035  }
1036 
1037  // loop over all the wires that are covered by this channel;
1038  // without knowing better, we have to draw into all of them
1039  for (geo::WireID const& wireID : WireIDs) {
1040  // check that the plane and tpc are the correct ones to draw
1041  if (wireID.planeID() != pid) continue; // not us!
1042 
1043  // do we have anything to do with this wire?
1044  if (!operation->ProcessWire(wireID)) continue;
1045 
1046  // get an iterator over the adc values
1047  // accumulate all the data of this wire in our "cells"
1048  size_t const max_tick = std::min({uncompressed.size(), size_t(fStartTick + fTicks)});
1049 
1050  for (size_t iTick = fStartTick; iTick < max_tick; ++iTick) {
1051 
1052  // do we have anything to do with this wire?
1053  if (!operation->ProcessTick(iTick)) continue;
1054 
1055  float const adc = uncompressed[iTick] - pedestal;
1056  //std::cout << "adc, pedestal: " << adc << " " << pedestal << std::endl;
1057 
1058  if (!operation->Operate(wireID, iTick, adc)) return false;
1059 
1060  } // if good
1061  } // for wires
1062  } // for channels
1063 
1064  return operation->Finish();
1065  } // ChannelLooper()
1066 
1067  //......................................................................
1069  public:
1071  geo::PlaneID const& pid,
1072  RawDataDrawer* dataDrawer,
1073  evdb::View2D* new_view)
1074  : OperationBaseClass(pid, dataDrawer)
1075  , view(new_view)
1076  , rawCharge(0.)
1077  , convertedCharge(0.)
1078  , drawingRange(*(dataDrawer->fDrawingRange))
1079  , ADCCorrector(detProp, PlaneID())
1080  {}
1081 
1082  bool
1083  Initialize() override
1084  {
1085  art::ServiceHandle<evd::RawDrawingOptions const> rawopt;
1086 
1087  // set up the size of the grid to be visualized;
1088  // the information on the size has to be already there:
1089  // caller should have user ExtractRange(), or similar, first.
1090  // set the minimum cell in ticks to at least match fTicksPerPoint
1091  drawingRange.SetMinTDCCellSize((float)rawopt->fTicksPerPoint);
1092  // also set the minimum wire cell size to 1,
1093  // otherwise there will be cells represented by no wire.
1095  boxInfo.clear();
1096  boxInfo.resize(drawingRange.NCells());
1097  return true;
1098  }
1099 
1100  bool
1101  ProcessWire(geo::WireID const& wire) override
1102  {
1103  return drawingRange.hasWire((int)wire.Wire);
1104  }
1105 
1106  bool
1107  ProcessTick(size_t tick) override
1108  {
1109  return drawingRange.hasTick((float)tick);
1110  }
1111 
1112  bool
1113  Operate(geo::WireID const& wireID, size_t tick, float adc) override
1114  {
1115  geo::WireID::WireID_t const wire = wireID.Wire;
1116  std::ptrdiff_t cell = drawingRange.GetCell(wire, tick);
1117  if (cell < 0) return true;
1118 
1119  BoxInfo_t& info = boxInfo[cell];
1120  info.good = true; // if in range, we mark this cell as good
1121 
1122  rawCharge += adc;
1123  convertedCharge += ADCCorrector(adc);
1124 
1125  // draw maximum digit in the cell
1126  if (std::abs(info.adc) <= std::abs(adc)) info.adc = adc;
1127 
1128  return true;
1129  }
1130 
1131  bool
1132  Finish() override
1133  {
1134  // write the information back
1135  geo::PlaneID::PlaneID_t const plane = PlaneID().Plane;
1138 
1139  // the cell size might have changed because of minimum size settings
1140  // from configuration (see Initialize())
1142 
1143  // complete the drawing
1145 
1146  return true;
1147  }
1148 
1149  private:
1150  evdb::View2D* view;
1151 
1152  double rawCharge = 0., convertedCharge = 0.;
1154  std::vector<BoxInfo_t> boxInfo;
1156  }; // class RawDataDrawer::BoxDrawer
1157 
1158  void
1160  geo::PlaneID const& pid,
1161  std::vector<BoxInfo_t> const& BoxInfo)
1162  {
1163  //
1164  // All the information is now collected in BoxInfo.
1165  // Make boxes out of it.
1166  //
1167  evd::RawDrawingOptions const& rawopt = *art::ServiceHandle<evd::RawDrawingOptions const>();
1168 
1169  MF_LOG_DEBUG("RawDataDrawer") << "Filling " << BoxInfo.size() << " boxes to be rendered";
1170 
1171  // drawing options:
1172  float const MinSignal = rawopt.fMinSignal;
1173  bool const bScaleDigitsByCharge = rawopt.fScaleDigitsByCharge;
1174 
1175  art::ServiceHandle<evd::ColorDrawingOptions const> cst;
1176 
1177  geo::GeometryCore const& geom = *art::ServiceHandle<geo::Geometry const>();
1178  geo::SigType_t const sigType = geom.SignalType(pid);
1179  evdb::ColorScale const& ColorSet = cst->RawQ(sigType);
1180  size_t const nBoxes = BoxInfo.size();
1181  unsigned int nDrawnBoxes = 0;
1182  for (size_t iBox = 0; iBox < nBoxes; ++iBox) {
1183  BoxInfo_t const& info = BoxInfo[iBox];
1184 
1185  // too little signal, don't bother drawing
1186  if (info.good && (std::abs(info.adc) < MinSignal)) continue;
1187 
1188  // skip the bad cells
1189  if (!info.good) continue;
1190 
1191  // box color, proportional to the ADC count
1192  int const color = ColorSet.GetColor(info.adc);
1193 
1194  // scale factor, proportional to ADC count (optional)
1195  constexpr float q0 = 1000.;
1196  float const sf = bScaleDigitsByCharge ? std::min(std::sqrt((float)info.adc / q0), 1.0F) : 1.;
1197 
1198  // coordinates of the cell box
1199  float min_wire, max_wire, min_tick, max_tick;
1200  std::tie(min_wire, min_tick, max_wire, max_tick) = fDrawingRange->GetCellBox(iBox);
1201  /*
1202  MF_LOG_TRACE("RawDataDrawer")
1203  << "Wires ( " << min_wire << " - " << max_wire << " ) ticks ("
1204  << min_tick << " - " << max_tick << " ) for cell " << iBox;
1205  */
1206  if (sf != 1.) { // need to shrink the box
1207  float const nsf = 1. - sf; // negation of scale factor
1208  float const half_box_wires = (max_wire - min_wire) / 2.,
1209  half_box_ticks = (max_tick - min_tick) / 2.;
1210 
1211  // shrink the box:
1212  min_wire += nsf * half_box_wires;
1213  max_wire -= nsf * half_box_wires;
1214  min_tick += nsf * half_box_ticks;
1215  max_tick -= nsf * half_box_ticks;
1216  } // if scaling
1217 
1218  // allocate the box on the view;
1219  // the order of the coordinates depends on the orientation
1220  TBox* pBox;
1221  if (rawopt.fAxisOrientation < 1)
1222  pBox = &(view->AddBox(min_wire, min_tick, max_wire, max_tick));
1223  else
1224  pBox = &(view->AddBox(min_tick, min_wire, max_tick, max_wire));
1225 
1226  pBox->SetFillStyle(1001);
1227  pBox->SetFillColor(color);
1228  pBox->SetBit(kCannotPick);
1229 
1230  ++nDrawnBoxes;
1231  } // for (iBox)
1232 
1233  MF_LOG_DEBUG("RawDataDrawer") << "Sent " << nDrawnBoxes << "/" << BoxInfo.size()
1234  << " boxes to be rendered";
1235  } // RawDataDrawer::QueueDrawingBoxes()
1236 
1237  void
1240  evdb::View2D* view,
1241  unsigned int plane)
1242  {
1243 
1244  // Check if we're supposed to draw raw hits at all
1245  art::ServiceHandle<evd::RawDrawingOptions const> rawopt;
1246  if (rawopt->fDrawRawDataOrCalibWires == 1) return;
1247 
1248  geo::PlaneID const pid(rawopt->CurrentTPC(), plane);
1249  BoxDrawer drawer(detProp, pid, this, view);
1250  if (!RunOperation(evt, &drawer)) {
1251  throw art::Exception(art::errors::Unknown) << "RawDataDrawer::RunDrawOperation(): "
1252  "somewhere something went somehow wrong";
1253  }
1254 
1255  } // RawDataDrawer::RunDrawOperation()
1256 
1257  //......................................................................
1259  public:
1260  float const RoIthreshold;
1261 
1263  : OperationBaseClass(pid, data_drawer)
1264  , RoIthreshold(art::ServiceHandle<evd::RawDrawingOptions const>()->RoIthreshold(PlaneID()))
1265  {}
1266 
1267  bool
1268  Operate(geo::WireID const& wireID, size_t tick, float adc) override
1269  {
1270  if (std::abs(adc) < RoIthreshold) return true;
1271  WireRange.add(wireID.Wire);
1272  TDCrange.add(tick);
1273  return true;
1274  } // Operate()
1275 
1276  bool
1277  Finish() override
1278  {
1279  geo::PlaneID::PlaneID_t const plane = PlaneID().Plane;
1280  int& WireMin = pRawDataDrawer->fWireMin[plane];
1281  int& WireMax = pRawDataDrawer->fWireMax[plane];
1282  int& TimeMin = pRawDataDrawer->fTimeMin[plane];
1283  int& TimeMax = pRawDataDrawer->fTimeMax[plane];
1284 
1285  if ((WireMin == WireMax) && WireRange.has_data()) {
1286  geo::GeometryCore const& geom = *art::ServiceHandle<geo::Geometry const>();
1287  mf::LogInfo("RawDataDrawer")
1288  << "Region of interest for " << std::string(PlaneID()) << " detected to be within wires "
1289  << WireRange.min() << " to " << WireRange.max() << " (plane has "
1290  << geom.Nwires(PlaneID()) << " wires)";
1291  WireMax = WireRange.max() + 1;
1292  WireMin = WireRange.min();
1293  }
1294  if ((TimeMin == TimeMax) && TDCrange.has_data()) {
1295  mf::LogInfo("RawDataDrawer")
1296  << "Region of interest for " << std::string(PlaneID()) << " detected to be within ticks "
1297  << TDCrange.min() << " to " << TDCrange.max();
1298  TimeMax = TDCrange.max() + 1;
1299  TimeMin = TDCrange.min();
1300  }
1301  return true;
1302  } // Finish()
1303 
1304  private:
1306  }; // class RawDataDrawer::RoIextractorClass
1307 
1308  void
1309  RawDataDrawer::RunRoIextractor(art::Event const& evt, unsigned int plane)
1310  {
1311  art::ServiceHandle<evd::RawDrawingOptions const> rawopt;
1312  geo::PlaneID const pid(rawopt->CurrentTPC(), plane);
1313 
1314  // if we have no region of interest, prepare to extract it
1315  bool const bExtractRoI = !hasRegionOfInterest(plane);
1316  MF_LOG_TRACE("RawDataDrawer") << "Region of interest for " << pid
1317  << (bExtractRoI ? " extracted" : " not extracted")
1318  << " on this draw";
1319 
1320  if (!bExtractRoI) return;
1321 
1322  RoIextractorClass Extractor(pid, this);
1323  if (!RunOperation(evt, &Extractor)) {
1324  throw std::runtime_error(
1325  "RawDataDrawer::RunRoIextractor(): somewhere something went somehow wrong");
1326  }
1327 
1328  } // RawDataDrawer::RunRoIextractor()
1329 
1330  //......................................................................
1331 
1332  void
1333  RawDataDrawer::RawDigit2D(art::Event const& evt,
1335  evdb::View2D* view,
1336  unsigned int plane,
1337  bool bZoomToRoI /* = false */
1338  )
1339  {
1340  art::ServiceHandle<evd::RawDrawingOptions const> rawopt;
1341  geo::PlaneID const pid(rawopt->CurrentTPC(), plane);
1342 
1343  bool const bDraw = (rawopt->fDrawRawDataOrCalibWires != 1);
1344  // if we don't need to draw, don't bother doing anything;
1345  // if the region of interest is required, RunRoIextractor() should be called
1346  // (ok, now it's private, but it could be exposed)
1347  if (!bDraw) return;
1348 
1349  art::ServiceHandle<geo::Geometry const> geom;
1350 
1351  // Need to loop over the labels, but we don't want to zap existing cached RawDigits that are valid
1352  // So... do the painful search to make sure the RawDigits we recover at those we are searching for.
1353  bool theDroidIAmLookingFor = false;
1354 
1355  // Loop over labels
1356  for (const auto& rawDataLabel : rawopt->fRawDataLabels) {
1357  // make sure we reset what needs to be reset
1358  // before the operations are initialized;
1359  // we call for reading raw digits; they will be cached, so it's not a waste
1360  details::CacheID_t NewCacheID(evt, rawDataLabel, pid);
1361  GetRawDigits(evt, NewCacheID);
1362 
1363  // Painful check to see if these RawDigits contain the droids we are looking for
1364  for (const auto& rawDigit : digit_cache->Digits()) {
1365  std::vector<geo::WireID> WireIDs = geom->ChannelToWire(rawDigit.Channel());
1366 
1367  for (geo::WireID const& wireID : WireIDs) {
1368  if (wireID.planeID() != pid) continue; // not us!
1369  theDroidIAmLookingFor = true;
1370  break;
1371  } // for wires
1372 
1373  if (theDroidIAmLookingFor) break;
1374  }
1375 
1376  if (theDroidIAmLookingFor) break;
1377  }
1378 
1379  if (!theDroidIAmLookingFor) return;
1380 
1381  bool const hasRoI = hasRegionOfInterest(plane);
1382 
1383  // - if we don't have a RoI yet, we want to get it while we draw
1384  // * if we are zooming into it now, we have to extract it first, then draw
1385  // * if we are not zooming, we can do both at the same time
1386  // - if we have a RoI, we don't want to extract it again
1387  if (!bZoomToRoI) { // we are not required to zoom to the RoI
1388 
1389  std::unique_ptr<OperationBaseClass> operation;
1390 
1391  // we will do the drawing in one pass
1392  MF_LOG_DEBUG("RawDataDrawer") << __func__ << "() setting up one-pass drawing";
1393  operation.reset(new BoxDrawer(detProp, pid, this, view));
1394 
1395  if (!hasRoI) { // we don't have any RoI; since it's cheap, let's get it
1396  MF_LOG_DEBUG("RawDataDrawer") << __func__ << "() adding RoI extraction";
1397 
1398  // swap cards: operation becomes a multiple operation:
1399  // - prepare the two operations (one is there already, somehow)
1400  std::unique_ptr<OperationBaseClass> drawer(std::move(operation));
1401  std::unique_ptr<OperationBaseClass> extractor(new RoIextractorClass(pid, this));
1402  // - create a new composite operation and give it the sub-ops
1403  operation.reset(new ManyOperations(pid, this));
1404  ManyOperations* pManyOps = static_cast<ManyOperations*>(operation.get());
1405  pManyOps->AddOperation(std::move(drawer));
1406  pManyOps->AddOperation(std::move(extractor));
1407  }
1408 
1409  if (!RunOperation(evt, operation.get())) {
1410  throw art::Exception(art::errors::Unknown) << "RawDataDrawer::RunDrawOperation(): "
1411  "somewhere something went somehow wrong";
1412  }
1413  }
1414  else { // we are zooming to RoI
1415  // first, we want the RoI extracted; the extractor will update this object
1416  if (!hasRoI) {
1417  MF_LOG_DEBUG("RawDataDrawer") << __func__ << "() setting up RoI extraction for " << pid;
1418  RoIextractorClass extractor(pid, this);
1419  if (!RunOperation(evt, &extractor)) {
1420  throw art::Exception(art::errors::Unknown)
1421  << "RawDataDrawer::RunDrawOperation():"
1422  " something went somehow wrong while extracting RoI";
1423  }
1424  }
1425  else {
1426  MF_LOG_DEBUG("RawDataDrawer")
1427  << __func__ << "() using existing RoI for " << pid << ": wires ( " << fWireMin[plane]
1428  << " - " << fWireMax[plane] << " ), ticks ( " << fTimeMin[plane] << " - "
1429  << fTimeMax[plane] << " )";
1430  }
1431 
1432  // adopt the drawing limits information from the wire/time limits
1434 
1435  // then we draw
1436  MF_LOG_DEBUG("RawDataDrawer") << __func__ << "() setting up drawing";
1437  BoxDrawer drawer(detProp, pid, this, view);
1438  if (!RunOperation(evt, &drawer)) {
1439  throw art::Exception(art::errors::Unknown) << "RawDataDrawer::RunDrawOperation():"
1440  " something went somehow wrong while drawing";
1441  }
1442  }
1443  } // RawDataDrawer::RawDigit2D()
1444 
1445  //........................................................................
1446  int
1447  RawDataDrawer::GetRegionOfInterest(int plane, int& minw, int& maxw, int& mint, int& maxt)
1448  {
1449  art::ServiceHandle<geo::Geometry const> geo;
1450 
1451  if ((unsigned int)plane >= fWireMin.size()) {
1452  mf::LogWarning("RawDataDrawer")
1453  << " Requested plane " << plane << " is larger than those available " << std::endl;
1454  return -1;
1455  }
1456 
1457  minw = fWireMin[plane];
1458  maxw = fWireMax[plane];
1459  mint = fTimeMin[plane];
1460  maxt = fTimeMax[plane];
1461 
1462  if ((minw == maxw) || (mint == maxt)) return 1;
1463 
1464  //make values a bit larger, but make sure they don't go out of bounds
1465  minw = (minw - 30 < 0) ? 0 : minw - 30;
1466  mint = (mint - 10 < 0) ? 0 : mint - 10;
1467 
1468  maxw = (maxw + 10 > (int)geo->Nwires(plane)) ? geo->Nwires(plane) : maxw + 10;
1469  maxt = (maxt + 10 > TotalClockTicks()) ? TotalClockTicks() : maxt + 10;
1470 
1471  return 0;
1472  }
1473 
1474  //......................................................................
1475  void
1476  RawDataDrawer::GetChargeSum(int plane, double& charge, double& convcharge)
1477  {
1478  charge = fRawCharge[plane];
1479  convcharge = fConvertedCharge[plane];
1480  }
1481 
1482  //......................................................................
1483  void
1484  RawDataDrawer::FillQHisto(const art::Event& evt, unsigned int plane, TH1F* histo)
1485  {
1486 
1487  // Check if we're supposed to draw raw hits at all
1488  art::ServiceHandle<evd::RawDrawingOptions const> rawopt;
1489  if (rawopt->fDrawRawDataOrCalibWires == 1) return;
1490 
1491  art::ServiceHandle<geo::Geometry const> geo;
1492 
1493  geo::PlaneID const pid(rawopt->CurrentTPC(), plane);
1494 
1495  for (const auto& rawDataLabel : rawopt->fRawDataLabels) {
1496  details::CacheID_t NewCacheID(evt, rawDataLabel, pid);
1497  GetRawDigits(evt, NewCacheID);
1498 
1499  lariov::ChannelStatusProvider const& channelStatus =
1500  art::ServiceHandle<lariov::ChannelStatusService const>()->GetProvider();
1501 
1502  //get pedestal conditions
1503  const lariov::DetPedestalProvider& pedestalRetrievalAlg =
1504  art::ServiceHandle<lariov::DetPedestalService const>()->GetPedestalProvider();
1505 
1506  for (evd::details::RawDigitInfo_t const& digit_info : *digit_cache) {
1507  raw::RawDigit const& hit = digit_info.Digit();
1508  raw::ChannelID_t const channel = hit.Channel();
1509 
1510  if (!channelStatus.IsPresent(channel)) continue;
1511 
1512  // The following test is meant to be temporary until the "correct" solution is implemented
1513  if (!ProcessChannelWithStatus(channelStatus.Status(channel))) continue;
1514 
1515  // to be explicit: we don't cound bad channels in
1516  if (!rawopt->fSeeBadChannels && channelStatus.IsBad(channel)) continue;
1517 
1518  std::vector<geo::WireID> wireids = geo->ChannelToWire(channel);
1519  for (auto const& wid : wireids) {
1520  // check that the plane and tpc are the correct ones to draw
1521  if (wid.planeID() != pid) continue;
1522 
1523  raw::RawDigit::ADCvector_t const& uncompressed = digit_info.Data();
1524 
1525  //float const pedestal = pedestalRetrievalAlg.PedMean(channel);
1526  // recover the pedestal
1527  float pedestal = 0;
1528  if (rawopt->fPedestalOption == 0) { pedestal = pedestalRetrievalAlg.PedMean(channel); }
1529  else if (rawopt->fPedestalOption == 1) {
1530  pedestal = hit.GetPedestal();
1531  }
1532  else if (rawopt->fPedestalOption == 2) {
1533  pedestal = 0;
1534  }
1535  else {
1536  mf::LogWarning("RawDataDrawer")
1537  << " PedestalOption is not understood: " << rawopt->fPedestalOption
1538  << ". Pedestals not subtracted.";
1539  }
1540 
1541  for (short d : uncompressed)
1542  histo->Fill(float(d) - pedestal); //pedestals[plane]); //hit.GetPedestal());
1543 
1544  // this channel is on the correct plane, don't double count the raw signal
1545  // if there are more than one wids for the channel
1546  break;
1547  } // end loop over wids
1548  } //end loop over raw hits
1549  } //end loop over labels
1550  }
1551 
1552  //......................................................................
1553  void
1554  RawDataDrawer::FillTQHisto(const art::Event& evt,
1555  unsigned int plane,
1556  unsigned int wire,
1557  TH1F* histo)
1558  {
1559 
1560  // Check if we're supposed to draw raw hits at all
1561  art::ServiceHandle<evd::RawDrawingOptions const> rawopt;
1562  if (rawopt->fDrawRawDataOrCalibWires == 1) return;
1563 
1564  // make sure we have the raw digits cached
1565  geo::PlaneID const pid(rawopt->CurrentTPC(), plane);
1566 
1567  // loop over labels
1568  for (const auto& rawDataLabel : rawopt->fRawDataLabels) {
1569  details::CacheID_t NewCacheID(evt, rawDataLabel, pid);
1570  GetRawDigits(evt, NewCacheID);
1571 
1572  if (digit_cache->empty()) return;
1573 
1574  geo::WireID const wireid(pid, wire);
1575 
1576  // find the channel
1577  art::ServiceHandle<geo::Geometry const> geom;
1578  raw::ChannelID_t const channel = geom->PlaneWireToChannel(wireid);
1579  if (!raw::isValidChannelID(channel)) { // no channel, empty histogram
1580  mf::LogError("RawDataDrawer")
1581  << __func__ << ": no channel associated to " << std::string(wireid);
1582  return;
1583  } // if no channel
1584 
1585  // check the channel status; bad channels are still ok.
1586  lariov::ChannelStatusProvider const& channelStatus =
1587  art::ServiceHandle<lariov::ChannelStatusService const>()->GetProvider();
1588 
1589  if (!channelStatus.IsPresent(channel)) return;
1590 
1591  // The following test is meant to be temporary until the "correct" solution is implemented
1592  if (!ProcessChannelWithStatus(channelStatus.Status(channel))) return;
1593 
1594  // we accept to see the content of a bad channel, so this is commented out:
1595  if (!rawopt->fSeeBadChannels && channelStatus.IsBad(channel)) return;
1596 
1597  //get pedestal conditions
1598  const lariov::DetPedestalProvider& pedestalRetrievalAlg =
1599  art::ServiceHandle<lariov::DetPedestalService const>()->GetPedestalProvider();
1600 
1601  // find the raw digit
1602  // (iDigit is an iterator to a evd::details::RawDigitInfo_t)
1603  evd::details::RawDigitInfo_t const* pDigit = digit_cache->FindChannel(channel);
1604 
1605  if (
1606  !pDigit) { // this is weird... actually no, this can happen if the RawDigits are per TPC (or something)
1607  // mf::LogWarning("RawDataDrawer") << __func__
1608  // << ": can't find raw digit for channel #" << channel
1609  // << " (" << std::string(wireid) << ")";
1610  continue;
1611  }
1612 
1613  raw::RawDigit::ADCvector_t const& uncompressed = pDigit->Data();
1614 
1615  // recover the pedestal
1616  float pedestal = 0;
1617  if (rawopt->fPedestalOption == 0) { pedestal = pedestalRetrievalAlg.PedMean(channel); }
1618  else if (rawopt->fPedestalOption == 1) {
1619  pedestal = pDigit->DigitPtr()->GetPedestal();
1620  }
1621  else if (rawopt->fPedestalOption == 2) {
1622  pedestal = 0;
1623  }
1624  else {
1625  mf::LogWarning("RawDataDrawer")
1626  << " PedestalOption is not understood: " << rawopt->fPedestalOption
1627  << ". Pedestals not subtracted.";
1628  }
1629 
1630  for (size_t j = 0; j < uncompressed.size(); ++j)
1631  histo->Fill(float(j),
1632  float(uncompressed[j]) - pedestal); //pedestals[plane]); //hit.GetPedestal());
1633  }
1634 
1635  } // RawDataDrawer::FillTQHisto()
1636 
1637  //......................................................................
1638 
1639  // void RawDataDrawer::RawDigit3D(const art::Event& evt,
1640  // evdb::View3D* view)
1641  // {
1642  // // Check if we're supposed to draw raw hits at all
1643  // art::ServiceHandle<evd::RawDrawingOptions const> rawopt;
1644  // if (rawopt->fDrawRawOrCalibHits!=0) return;
1645 
1646  // art::ServiceHandle<geo::Geometry const> geom;
1647 
1648  // HitTower tower;
1649  // tower.fQscale = 0.01;
1650 
1651  // for (unsigned int imod=0; imod<rawopt->fRawDigitModules.size(); ++imod) {
1652  // const char* which = rawopt->fRawDigitModules[imod].c_str();
1653 
1654  // std::vector<raw::RawDigit> rawhits;
1655  // GetRawDigits(evt, which, rawhits);
1656 
1657  // for (unsigned int i=0; i<rawhits.size(); ++i) {
1658  // double t = 0;
1659  // double q = 0;
1660  // t = rawhits[i]->fTDC[0];
1661  // for (unsigned int j=0; j<rawhits[i]->NADC(); ++j) {
1662  // q += rawhits[i]->ADC(j);
1663  // }
1664  // // Hack for now...
1665  // if (q<=0.0) q = 1+i%10;
1666 
1667  // // Get the cell geometry for the hit
1668  // int iplane = cmap->GetPlane(rawhits[i].get());
1669  // int icell = cmap->GetCell(rawhits[i].get());
1670  // double xyz[3];
1671  // double dpos[3];
1672  // geo::View_t v;
1673  // geom->CellInfo(iplane, icell, &v, xyz, dpos);
1674 
1675  // switch (rawopt->fRawDigit3DStyle) {
1676  // case 1:
1677  // //
1678  // // Render digits as towers
1679  // //
1680  // if (v==geo::kX) {
1681  // tower.AddHit(v, iplane, icell, xyz[0], xyz[2], q, t);
1682  // }
1683  // else if (v==geo::kY) {
1684  // tower.AddHit(v, iplane, icell, xyz[1], xyz[2], q, t);
1685  // }
1686  // else abort();
1687  // break;
1688  // default:
1689  // //
1690  // // Render Digits as boxes
1691  // //
1692  // TPolyLine3D& p = view->AddPolyLine3D(5,kGreen+1,1,2);
1693  // double sf = std::sqrt(0.01*q);
1694  // if (v==geo::kX) {
1695  // double x1 = xyz[0] - sf*dpos[0];
1696  // double x2 = xyz[0] + sf*dpos[0];
1697  // double z1 = xyz[2] - sf*dpos[2];
1698  // double z2 = xyz[2] + sf*dpos[2];
1699  // p.SetPoint(0, x1, geom->DetHalfHeight(), z1);
1700  // p.SetPoint(1, x2, geom->DetHalfHeight(), z1);
1701  // p.SetPoint(2, x2, geom->DetHalfHeight(), z2);
1702  // p.SetPoint(3, x1, geom->DetHalfHeight(), z2);
1703  // p.SetPoint(4, x1, geom->DetHalfHeight(), z1);
1704  // }
1705  // else if (v==geo::kY) {
1706  // double y1 = xyz[1] - sf*dpos[1];
1707  // double y2 = xyz[1] + sf*dpos[1];
1708  // double z1 = xyz[2] - sf*dpos[2];
1709  // double z2 = xyz[2] + sf*dpos[2];
1710  // p.SetPoint(0, geom->DetHalfWidth(), y1, z1);
1711  // p.SetPoint(1, geom->DetHalfWidth(), y2, z1);
1712  // p.SetPoint(2, geom->DetHalfWidth(), y2, z2);
1713  // p.SetPoint(3, geom->DetHalfWidth(), y1, z2);
1714  // p.SetPoint(4, geom->DetHalfWidth(), y1, z1);
1715  // }
1716  // else abort();
1717  // break;
1718  // } // switch fRawDigit3DStyle
1719  // }//end loop over raw digits
1720  // }// end loop over RawDigit modules
1721 
1722  // // Render the towers for that style choice
1723  // if (rawopt->fRawDigit3DStyle==1) tower.Draw(view);
1724  // }
1725 
1726  //......................................................................
1727  bool
1729  {
1730 
1731  return (fWireMax[plane] != -1) && (fTimeMax[plane] != -1);
1732 
1733  } // RawDataDrawer::hasRegionOfInterest()
1734 
1735  //......................................................................
1736  void
1738  {
1739 
1740  MF_LOG_DEBUG("RawDataDrawer") << "RawDataDrawer[" << ((void*)this)
1741  << "]: resetting the region of interest";
1742 
1743  std::fill(fWireMin.begin(), fWireMin.end(), -1);
1744  std::fill(fWireMax.begin(), fWireMax.end(), -1);
1745  std::fill(fTimeMin.begin(), fTimeMin.end(), -1);
1746  std::fill(fTimeMax.begin(), fTimeMax.end(), -1);
1747 
1748  } // RawDataDrawer::ResetRegionOfInterest()
1749 
1750  //......................................................................
1751 
1752  void
1753  RawDataDrawer::GetRawDigits(art::Event const& evt, details::CacheID_t const& new_timestamp)
1754  {
1755  MF_LOG_DEBUG("RawDataDrawer") << "GetRawDigits() for " << new_timestamp
1756  << " (last for: " << *fCacheID << ")";
1757 
1758  // update cache
1759  digit_cache->Update(evt, new_timestamp);
1760 
1761  // if time stamp is changing, we want to reconsider which region is
1762  // interesting
1763  if (!fCacheID->sameTPC(new_timestamp)) ResetRegionOfInterest();
1764 
1765  // all the caches have been properly updated or invalidated;
1766  // we are now on a new cache state
1767  *fCacheID = new_timestamp;
1768 
1769  } // RawDataDrawer::GetRawDigits()
1770 
1771  //......................................................................
1772  bool
1775  {
1776  // if we don't have a valid status, we can't reject the channel
1777  if (!lariov::ChannelStatusProvider::IsValidStatus(channel_status)) return true;
1778 
1779  // is the status "too bad"?
1780  art::ServiceHandle<evd::RawDrawingOptions const> drawopt;
1781  if (channel_status > drawopt->fMaxChannelStatus) return false;
1782  if (channel_status < drawopt->fMinChannelStatus) return false;
1783 
1784  // no reason to reject it...
1785  return true;
1786  } // RawDataDrawer::ProcessChannel()
1787 
1788  //----------------------------------------------------------------------------
1789  namespace details {
1790 
1791  //--------------------------------------------------------------------------
1792  //--- RawDigitInfo_t
1793  //---
1796  {
1797  if (!data.hasData()) UncompressData();
1798  return *data;
1799  } // RawDigitInfo_t::Data()
1800 
1801  void
1802  RawDigitInfo_t::Fill(art::Ptr<raw::RawDigit> const& src)
1803  {
1804  data.Clear();
1805  digit = src;
1806  } // RawDigitInfo_t::Fill()
1807 
1808  void
1810  {
1811  data.Clear();
1812  sample_info.reset();
1813  }
1814 
1815  void
1817  {
1818  data.Clear();
1819 
1820  if (!digit) return; // no original data, can't do anything
1821 
1822  art::ServiceHandle<evd::RawDrawingOptions const> drawopt;
1823 
1824  if (digit->Compression() == kNone) {
1825  // no compression, we can refer to the original data directly
1826  data.PointToData(digit->ADCs());
1827  }
1828  else {
1829  // data is compressed, need to do the real work
1830  if (drawopt->fUncompressWithPed) { //Use pedestal in uncompression
1831  int pedestal = (int)digit->GetPedestal();
1833  samples.resize(digit->Samples());
1834  Uncompress(digit->ADCs(), samples, pedestal, digit->Compression());
1835  data.StealData(std::move(samples));
1836  }
1837  else {
1839  samples.resize(digit->Samples());
1840  Uncompress(digit->ADCs(), samples, digit->Compression());
1841  data.StealData(std::move(samples));
1842  }
1843  }
1844  } // RawDigitInfo_t::UncompressData()
1845 
1846  void
1848  {
1849  raw::RawDigit::ADCvector_t const& samples = Data();
1850 
1851  // lar::util::StatCollector<double> stat;
1852  // stat.add(samples.begin(), samples.end());
1853 
1855  samples.end());
1856 
1857  sample_info.reset(new SampleInfo_t);
1858  sample_info->min_charge = stat.min();
1859  sample_info->max_charge = stat.max();
1860  // sample_info->average_charge = stat.Average();
1861 
1862  } // RawDigitInfo_t::CollectSampleInfo()
1863 
1866  {
1868  return *sample_info;
1869  } // SampleInfo()
1870 
1871  template <typename Stream>
1872  void
1874  {
1875  out << " digit at " << ((void*)digit.get()) << " on channel #" << digit->Channel()
1876  << " with " << digit->NADC();
1877  if (digit->Compression() == kNone)
1878  out << " uncompressed data";
1879  else
1880  out << " data items compressed with <" << digit->Compression() << ">";
1881  if (data.hasData())
1882  out << " with data (" << data->size() << " samples)";
1883  else
1884  out << " without data";
1885  } // RawDigitInfo_t::Dump()
1886 
1887  //--------------------------------------------------------------------------
1888  //--- RawDigitCacheDataClass
1889  //---
1890 
1891  RawDigitInfo_t const*
1893  {
1894  auto iDigit = std::find_if(
1895  digits.cbegin(), digits.cend(), [channel](evd::details::RawDigitInfo_t const& digit) {
1896  return digit.Channel() == channel;
1897  });
1898  return (iDigit == digits.cend()) ? nullptr : &*iDigit;
1899  } // RawDigitCacheDataClass::FindChannel()
1900 
1901  std::vector<raw::RawDigit> const*
1902  RawDigitCacheDataClass::ReadProduct(art::Event const& evt, art::InputTag label)
1903  {
1904  art::Handle<std::vector<raw::RawDigit>> rdcol;
1905  if (!evt.getByLabel(label, rdcol)) return nullptr;
1906  return &*rdcol;
1907  } // RawDigitCacheDataClass::ReadProduct()
1908 
1909  void
1910  RawDigitCacheDataClass::Refill(art::Handle<std::vector<raw::RawDigit>>& rdcol)
1911  {
1912  digits.resize(rdcol->size());
1913  for (size_t iDigit = 0; iDigit < rdcol->size(); ++iDigit) {
1914  art::Ptr<raw::RawDigit> pDigit(rdcol, iDigit);
1915  digits[iDigit].Fill(pDigit);
1916  size_t samples = pDigit->Samples();
1917  if (samples > max_samples) max_samples = samples;
1918  } // for
1919  } // RawDigitCacheDataClass::Refill()
1920 
1921  void
1923  {
1924  timestamp.clear();
1925  } // RawDigitCacheDataClass::Invalidate()
1926 
1927  void
1929  {
1930  Invalidate();
1931  digits.clear();
1932  max_samples = 0;
1933  } // RawDigitCacheDataClass::Clear()
1934 
1937  art::Event const* evt /* = nullptr */) const
1938  {
1939  BoolWithUpToDateMetadata res{false, nullptr};
1940 
1941  // normally only if either the event or the product label have changed,
1942  // cache becomes invalid:
1943  if (!ts.sameProduct(timestamp)) return res; // outdated cache
1944 
1945  // But: our cache stores pointers to the original data, and on a new TPC
1946  // the event display may reload the event anew, removing the "old" data
1947  // from memory.
1948  // Since TPC can change with or without the data being invalidated,
1949  // a more accurate verification is needed.
1950 
1951  // if the cache is empty, well, it does not make much difference;
1952  // we invalidate it to be sure
1953  if (empty()) return res; // outdated cache
1954 
1955  if (!evt) return res; // outdated, since we can't know better without the event
1956 
1957  // here we force reading of the product
1958  res.digits = ReadProduct(*evt, ts.inputLabel());
1959  if (!res.digits) return res; // outdated cache; this is actually an error
1960 
1961  if (res.digits->empty())
1962  return res; // outdated; no digits (strange!), invalidate just in case
1963 
1964  // use the first digit as test
1965  raw::ChannelID_t channel = res.digits->front().Channel();
1966  RawDigitInfo_t const* pInfo = FindChannel(channel);
1967  if (!pInfo) return res; // outdated: we don't even have this channel in cache!
1968 
1969  if (&(pInfo->Digit()) != &(res.digits->front()))
1970  return res; // outdated: different memory address for data
1971 
1972  res.bUpToDate = true;
1973  return res; // cache still valid
1974  } // RawDigitCacheDataClass::CheckUpToDate()
1975 
1976  bool
1977  RawDigitCacheDataClass::Update(art::Event const& evt, CacheID_t const& new_timestamp)
1978  {
1979  BoolWithUpToDateMetadata update_info = CheckUpToDate(new_timestamp, &evt);
1980 
1981  if (update_info) return false; // already up to date: move on!
1982 
1983  MF_LOG_DEBUG("RawDataDrawer") << "Refilling raw digit cache RawDigitCacheDataClass["
1984  << ((void*)this) << "] for " << new_timestamp;
1985 
1986  Clear();
1987 
1988  art::Handle<std::vector<raw::RawDigit>> rdcol;
1989  if (!evt.getByLabel(new_timestamp.inputLabel(), rdcol)) {
1990  mf::LogWarning("RawDataDrawer")
1991  << "no RawDigit collection '" << new_timestamp.inputLabel() << "' found";
1992  return true;
1993  }
1994 
1995  Refill(rdcol);
1996 
1997  timestamp = new_timestamp;
1998  return true;
1999  } // RawDigitCacheDataClass::Update()
2000 
2001  template <typename Stream>
2002  void
2004  {
2005  out << "Cache at " << ((void*)this) << " with time stamp " << std::string(timestamp)
2006  << " and " << digits.size() << " entries (maximum sample: " << max_samples << ");"
2007  << " data at " << ((void*)digits.data());
2008  for (RawDigitInfo_t const& digitInfo : digits) {
2009  out << "\n ";
2010  digitInfo.Dump(out);
2011  } // for
2012  out << "\n";
2013  } // RawDigitCacheDataClass::Dump()
2014 
2015  //--------------------------------------------------------------------------
2016  //--- GridAxisClass
2017  //---
2018  std::ptrdiff_t
2020  {
2021  return std::ptrdiff_t((coord - min) / cell_size); // truncate
2022  } // GridAxisClass::GetCell()
2023 
2024  //--------------------------------------------------------------------------
2025  bool
2026  GridAxisClass::Init(size_t nDiv, float new_min, float new_max)
2027  {
2028 
2029  n_cells = std::max(nDiv, size_t(1));
2030  return SetLimits(new_min, new_max);
2031 
2032  } // GridAxisClass::Init()
2033 
2034  //--------------------------------------------------------------------------
2035  bool
2036  GridAxisClass::SetLimits(float new_min, float new_max)
2037  {
2038  min = new_min;
2039  max = new_max;
2040  cell_size = Length() / float(n_cells);
2041 
2042  return std::isnormal(cell_size);
2043  } // GridAxisClass::SetLimits()
2044 
2045  //--------------------------------------------------------------------------
2046  bool
2048  {
2049  if (cell_size >= min_size) return false;
2050 
2051  // n_cells gets truncated
2052  n_cells = (size_t)std::max(std::floor(Length() / min_size), 1.0F);
2053 
2054  // reevaluate cell size, that might be different than min_size
2055  // because of n_cells truncation or minimum value
2056  cell_size = Length() / float(n_cells);
2057  return true;
2058  } // GridAxisClass::SetMinCellSize()
2059 
2060  //--------------------------------------------------------------------------
2061  bool
2063  {
2064  if (cell_size <= max_size) return false;
2065 
2066  // n_cells gets rounded up
2067  n_cells = (size_t)std::max(std::ceil(Length() / max_size), 1.0F);
2068 
2069  // reevaluate cell size, that might be different than max_size
2070  // because of n_cells rounding or minimum value
2071  cell_size = Length() / float(n_cells);
2072  return true;
2073  } // GridAxisClass::SetMaxCellSize()
2074 
2075  //--------------------------------------------------------------------------
2076  template <typename Stream>
2077  void
2079  {
2080  out << NCells() << " cells from " << Min() << " to " << Max() << " (length: " << Length()
2081  << ")";
2082  } // GridAxisClass::Dump()
2083 
2084  //--------------------------------------------------------------------------
2085  //--- CellGridClass
2086  //---
2087  CellGridClass::CellGridClass(unsigned int nWires, unsigned int nTDC)
2088  : wire_axis((size_t)nWires, 0., float(nWires)), tdc_axis((size_t)nTDC, 0., float(nTDC))
2089  {} // CellGridClass::CellGridClass(int, int)
2090 
2091  //--------------------------------------------------------------------------
2093  float max_wire,
2094  unsigned int nWires,
2095  float min_tdc,
2096  float max_tdc,
2097  unsigned int nTDC)
2098  : wire_axis((size_t)nWires, min_wire, max_wire), tdc_axis((size_t)nTDC, min_tdc, max_tdc)
2099  {} // CellGridClass::CellGridClass({ float, float, int } x 2)
2100 
2101  //--------------------------------------------------------------------------
2102  std::ptrdiff_t
2103  CellGridClass::GetCell(float wire, float tick) const
2104  {
2105  std::ptrdiff_t iWireCell = wire_axis.GetCell(wire);
2106  if (!wire_axis.hasCell(iWireCell)) return std::ptrdiff_t(-1);
2107  std::ptrdiff_t iTDCCell = tdc_axis.GetCell(tick);
2108  if (!tdc_axis.hasCell(iTDCCell)) return std::ptrdiff_t(-1);
2109  return iWireCell * TDCAxis().NCells() + iTDCCell;
2110  } // CellGridClass::GetCell()
2111 
2112  //--------------------------------------------------------------------------
2113  std::tuple<float, float, float, float>
2114  CellGridClass::GetCellBox(std::ptrdiff_t iCell) const
2115  {
2116  // { w1, t1, w2, t2 }
2117  size_t const nTDCCells = TDCAxis().NCells();
2118  std::ptrdiff_t iWireCell = (std::ptrdiff_t)(iCell / nTDCCells),
2119  iTDCCell = (std::ptrdiff_t)(iCell % nTDCCells);
2120 
2121  return std::tuple<float, float, float, float>(WireAxis().LowerEdge(iWireCell),
2122  TDCAxis().LowerEdge(iTDCCell),
2123  WireAxis().UpperEdge(iWireCell),
2124  TDCAxis().UpperEdge(iTDCCell));
2125  } // CellGridClass::GetCellBox()
2126 
2127  //--------------------------------------------------------------------------
2128  template <typename Stream>
2129  void
2131  {
2132  out << "Wire axis: ";
2133  WireAxis().Dump(out);
2134  out << "; time axis: ";
2135  TDCAxis().Dump(out);
2136  } // CellGridClass::Dump()
2137 
2138  //--------------------------------------------------------------------------
2139 
2140  } // details
2141 
2142 } // namespace evd
2143 
2144 ////////////////////////////////////////////////////////////////////////
int GetRegionOfInterest(int plane, int &minw, int &maxw, int &mint, int &maxt)
float GetPedestal() const
Definition: RawDigit.h:214
Data_t max() const
Returns the accumulated maximum, or a very small number if no values.
const_pointer operator->() const
bool Operate(geo::WireID const &wireID, size_t tick, float adc) override
details::CellGridClass drawingRange
short MaxCharge() const
maximum charge
bool has_data() const
Returns whether at least one datum has been added.
~PointerToData_t()
Destructor: gets rid of the owned data.
int fScaleDigitsByCharge
scale the size of the digit by the charge
bool Update(art::Event const &evt, CacheID_t const &new_timestamp)
BoxDrawer(detinfo::DetectorPropertiesData const &detProp, geo::PlaneID const &pid, RawDataDrawer *dataDrawer, evdb::View2D *new_view)
bool sameProduct(PlaneDataChangeTracker_t const &as) const
Returns whether we are in the same event (the rest could differ)
BoolWithUpToDateMetadata(bool uptodate, std::vector< raw::RawDigit > const *newdigits)
bool SetCellSizeBoundary(float min_size, float max_size)
This_t & add(Data_t value)
Include a single value in the statistics.
size_t max_samples
the largest number of ticks in any digit
bool ProcessTick(size_t tick) override
Collection of charge vs time digitized from a single readout channel.
Definition: RawDigit.h:69
void SetWireRange(float min_wire, float max_wire)
Sets the wire range, leaving the number of wire cells unchanged.
void FillTQHisto(const art::Event &evt, unsigned int plane, unsigned int wire, TH1F *histo)
int adc
total ADC count in this box
raw::RawDigit const & Digit() const
Returns an art pointer to the actual digit.
void SetTDCRange(float min_tdc, float max_tdc, unsigned int nTDC, float min_size)
Sets the complete TDC range, with minimum cell size.
void SetWireRange(float min_wire, float max_wire, unsigned int nWires, float min_size)
Sets the complete wire range, with minimum cell size.
bool Operate(geo::WireID const &wireID, size_t tick, float adc) override
virtual bool IsBad(raw::ChannelID_t channel) const =0
Returns whether the specified channel is bad in the current run.
OperationBaseClass(geo::PlaneID const &pid, RawDataDrawer *data_drawer=nullptr)
void RunDrawOperation(art::Event const &evt, detinfo::DetectorPropertiesData const &detProp, evdb::View2D *view, unsigned int plane)
void SetWireRange(unsigned int nWires)
Display parameters for the raw data.
float Min() const
Returns the extremes of the axis.
const_reference operator*() const
auto coord(Vector &v, unsigned int n) noexcept
Returns an object to manage the coordinate n of a vector.
void Dump(Stream &&out) const
Prints the current axes on the specified stream.
void SetDrawingLimitsFromRoI(geo::PlaneID::PlaneID_t plane)
double fStartTick
low tick
unsigned int PlaneID_t
Type for the ID number.
Definition: geo_types.h:473
The data type to uniquely identify a Plane.
Definition: geo_types.h:472
details::CacheID_t * fCacheID
information about the last processed plane
GridAxisClass const & WireAxis() const
Return the information about the wires.
std::unique_ptr< SampleInfo_t > sample_info
Information collected from the uncompressed data.
CacheID_t timestamp
object expressing validity range of cached data
ChannelID_t Channel() const
DAQ channel this raw data was read from.
Definition: RawDigit.h:212
std::vector< std::unique_ptr< RawDataDrawer::OperationBaseClass > > operations
void Dump(Stream &&out) const
Dump the content of the cache.
Classes detecting configuration changes.
float cell_size
size of each cell
std::vector< short > ADCvector_t
Type representing a (compressed) vector of ADC counts.
Definition: RawDigit.h:73
bool SetMinTDCCellSize(float min_size)
Sets the minimum size for TDC cells.
std::ptrdiff_t GetCell(float wire, float tick) const
Returns the index of specified cell, or -1 if out of range.
GridAxisClass()
Default constructor: an invalid range.
std::vector< geo::WireID > ChannelToWire(raw::ChannelID_t const channel) const
Returns a list of wires connected to the specified TPC channel.
void SetTDCRange(float min_tdc, float max_tdc)
Sets the TDC range, leaving the number of ticks unchanged.
static constexpr bool
std::vector< BoxInfo_t > boxInfo
Definition of basic raw digits.
SampleInfo_t const & SampleInfo() const
Returns the uncompressed data.
void RawDigit2D(art::Event const &evt, detinfo::DetectorPropertiesData const &detProp, evdb::View2D *view, unsigned int plane, bool bZoomToRoI=false)
Draws raw digit content in 2D wire plane representation.
bool hasTick(int tick) const
bool operator!() const
Returns whether we point to nothing.
WireID_t Wire
Index of the wire within its plane.
Definition: geo_types.h:580
ManyOperations(geo::PlaneID const &pid, RawDataDrawer *data_drawer=nullptr)
bool hasTick(float tick) const
Returns whether the range includes the specified wire.
process_name hit
Definition: cheaterreco.fcl:51
SigType_t SignalType(geo::PlaneID const &pid) const
Returns the type of signal on the channels of specified TPC plane.
void PointToData(pointer data)
Point to the specified data, not acquiring ownership.
raw::ChannelID_t Channel() const
Returns the channel of this digit (for convenience)
lar::util::MinMaxCollector< float > WireRange
bool ProcessChannelWithStatus(lariov::ChannelStatusProvider::Status_t channel_status) const
Returns whether a channel with the specified status should be processed.
Classes gathering simple statistics.
std::vector< double > fRawCharge
Sum of Raw Charge.
BEGIN_PROLOG channel_status
unsigned int Nwires(unsigned int p, unsigned int tpc=0, unsigned int cstat=0) const
Returns the total number of wires in the specified plane.
std::vector< int > fWireMin
lowest wire in interesting region for each plane
double electronsToADC
conversion constant
Manages a cell-like division of a coordinate.
std::add_pointer_t< value_type > pointer
void SetDrawingLimits(float low_wire, float high_wire, float low_tdc, float high_tdc)
Fills the viewport borders from the specified extremes.
size_t n_cells
number of cells in the axis
friend class RoIextractorClass
details::ADCCorrectorClass ADCCorrector
unsigned short Status_t
type representing channel status
std::vector< int > fTimeMax
highest time in interesting region for each plane
The color scales used by the event display.
Keeps track of the minimum and maximum value we observed.
Information about a RawDigit; may contain uncompressed duplicate of data.
art::InputTag const & inputLabel() const
T abs(T value)
OperationBaseClass const * Operator(size_t iOp) const
std::add_lvalue_reference_t< value_type > reference
void Refill(art::Handle< std::vector< raw::RawDigit >> &rdcol)
Fills the cache from the specified raw digits product handle.
virtual bool IsPresent(raw::ChannelID_t channel) const =0
Returns whether the specified channel is physical and connected to wire.
Data_t min() const
Returns the accumulated minimum, or a very large number if no values.
constexpr ChannelID_t InvalidChannelID
ID of an invalid channel.
Definition: RawTypes.h:32
detinfo::DetectorPropertiesData const & detProp
float Length() const
Returns the length of the axis.
double fTicks
number of ticks of the clock
mutable::details::PointerToData_t< raw::RawDigit::ADCvector_t const > data
Uncompressed data.
bool ProcessWire(geo::WireID const &wire) override
void AcquireData(pointer data)
Acquire ownership of the specified data.
PadResolution_t PadResolution
stored pad resolution
short MinCharge() const
minimum charge
raw::RawDigit::ADCvector_t const & Data() const
average charge
bool SetMinWireCellSize(float min_size)
Sets the minimum size for wire cells.
double operator()(float adc) const
Applies Birks correction to the specified pedestal-subtracted charge.
bool Add(CONT &cont, float wire, float tick, typename CONT::value_type v)
enum geo::_plane_sigtype SigType_t
bool hasWire(int wire) const
std::ptrdiff_t operator()(float coord) const
ADCCorrectorClass(detinfo::DetectorPropertiesData const &dp, geo::PlaneID const &pid)
bool SetLimits(float new_min, float new_max)
Initialize the axis limits, returns whether cell size is finite.
Cached set of RawDigitInfo_t.
Collect all the RawData header files together.
std::vector< evd::details::RawDigitInfo_t >::const_iterator begin(RawDigitCacheDataClass const &cache)
float UpperEdge(std::ptrdiff_t iCell) const
Returns the upper edge of the cell.
float LowerEdge(std::ptrdiff_t iCell) const
Returns the lower edge of the cell.
void FillQHisto(const art::Event &evt, unsigned int plane, TH1F *histo)
void GetChargeSum(int plane, double &charge, double &convcharge)
double fMinSignal
minimum ADC count to display a time bin
lar::util::MinMaxCollector< float > TDCrange
void RunRoIextractor(art::Event const &evt, unsigned int plane)
std::vector< int > fWireMax
highest wire in interesting region for each plane
std::add_lvalue_reference_t< const_value_type > const_reference
bool Init(size_t nDiv, float new_min, float new_max)
Initialize the axis, returns whether cell size is finite.
tick_as<> tick
Tick number, represented by std::ptrdiff_t.
Definition: electronics.h:75
constexpr bool isValidChannelID(raw::ChannelID_t channel)
Definition: RawTypes.h:37
void GetRawDigits(art::Event const &evt)
Reads raw::RawDigits; also triggers Reset()
virtual Status_t Status(raw::ChannelID_t channel) const
Returns a status integer with arbitrary meaning.
void fill(const art::PtrVector< recob::Hit > &hits, int only_plane)
bool hasCell(std::ptrdiff_t iCell) const
Returns whether the cell is present or not.
double TotalClockTicks() const
Definition: RawDataDrawer.h:85
Class providing information about the quality of channels.
void Dump(Stream &&out) const
Dumps the content of the digit info.
The data type to uniquely identify a TPC.
Definition: geo_types.h:386
PlaneID_t Plane
Index of the plane within its TPC.
Definition: geo_types.h:493
Description of geometry of one entire detector.
virtual bool Operate(geo::WireID const &wireID, size_t tick, float adc)=0
virtual float PedMean(raw::ChannelID_t ch) const =0
Retrieve pedestal information.
bool operator()(geo::WireID const &wireID, size_t tick, float adc)
void Invalidate()
Clears the cache and marks it as invalid (use Update() to fill it)
bool isEmpty() const
Returns whether minimum and maximum match.
bool bOwned
whether we own our data
void UncompressData() const
Fills the uncompressed data cache.
void CollectSampleInfo() const
Fills the sample info cache.
Class to aid in the rendering of RawData objects.
void SetTDCRange(float min_tdc, float max_tdc, unsigned int nTDC)
Sets the complete TDC range.
float CellSize() const
Returns the cell size.
Detects the presence of a new event, data product or wire plane.
std::vector< RawDigitInfo_t > const & Digits() const
Returns the list of digit info.
CellGridClass()
Default constructor: invalid ranges.
bool empty() const
Returns whether the cache is empty() (STL-like interface)
geo::PlaneID const & PlaneID() const
void NewData(T const &data)
Create a owned copy of the specified object.
RawDigitInfo_t const * FindChannel(raw::ChannelID_t channel) const
Returns a pointer to the digit info of given channel, nullptr if none.
virtual bool ProcessWire(geo::WireID const &)
bool hasCoord(float coord) const
Returns whether the coordinate is included in the range or not.
bool hasRegionOfInterest(geo::PlaneID::PlaneID_t plane) const
std::string to_string(WindowPattern const &pattern)
bool SetMaxCellSize(float max_size)
friend class BoxDrawer
void SetWireRange(float min_wire, float max_wire, unsigned int nWires)
Sets the complete wire range.
static std::vector< raw::RawDigit > const * ReadProduct(art::Event const &evt, art::InputTag label)
double BirksCorrection(double dQdX) const
dQ/dX in electrons/cm, returns dE/dX in MeV/cm.
float max
extremes of the axis
void PointToData(reference data)
Point to the specified data, not acquiring ownership.
pointer pData
pointer to data
bool hasWire(float wire) const
Returns whether the range includes the specified wire.
size_t NCells() const
Returns the length of the axis.
void QueueDrawingBoxes(evdb::View2D *view, geo::PlaneID const &pid, std::vector< BoxInfo_t > const &BoxInfo)
int fAxisOrientation
0 = TDC values on y-axis, wire number on x-axis, 1 = swapped
void Clear()
Stop pointing to the data; if owned, delete it.
Aid in the rendering of RawData objects.
Definition: RawDataDrawer.h:43
Interface for experiment-specific channel quality info provider.
art::Ptr< raw::RawDigit > digit
a pointer to the actual digit
GridAxisClass(size_t nDiv, float new_min, float new_max)
Constructor: sets the limits and the number of cells.
bool ProcessTick(size_t tick) override
bool good
whether the channel is not bad
void Dump(Stream &&out) const
BoolWithUpToDateMetadata CheckUpToDate(CacheID_t const &ts, art::Event const *evt=nullptr) const
Checks whether an update is needed; can load digits in the process.
unsigned int WireID_t
Type for the ID number.
Definition: geo_types.h:561
bool owned() const
Returns whether we have data and we own it.
static std::vector< raw::RawDigit > const EmptyRawDigits
Empty collection, used in return value of invalid digits.
RawDataDrawer * RawDataDrawerPtr() const
size_t MaxSamples() const
Returns the largest number of samples in the unpacked raw digits.
std::vector< evd::details::RawDigitInfo_t >::const_iterator end(RawDigitCacheDataClass const &cache)
RoIextractorClass(geo::PlaneID const &pid, RawDataDrawer *data_drawer)
void ResetRegionOfInterest()
Forgets about the current region of interest.
details::CellGridClass * fDrawingRange
information about the viewport
std::vector< int > fTimeMin
lowest time in interesting region for each plane
::util::PlaneDataChangeTracker_t CacheID_t
Definition: RawDataDrawer.h:38
std::add_const_t< value_type > const_value_type
Manages a grid-like division of 2D space.
TCEvent evt
Definition: DataStructs.cxx:8
bool ProcessWire(geo::WireID const &wireID) override
unsigned int ChannelID_t
Type representing the ID of a readout channel.
Definition: RawTypes.h:28
Applies Birks correction.
Interface for experiment-specific service for channel quality info.
OperationBaseClass * Operator(size_t iOp)
virtual std::string Name() const
void Uncompress(const std::vector< short > &adc, std::vector< short > &uncompressed, raw::Compress_t compress)
Uncompresses a raw data buffer.
Definition: raw.cxx:776
evd::details::RawDigitCacheDataClass * digit_cache
Cache of raw digits.
void SetTDCRange(unsigned int nTDC)
Sets a simple TDC range: all the ticks, one cell per tick.
void Fill(art::Ptr< raw::RawDigit > const &src)
Parses the specified digit.
void StealData(std::remove_const_t< T > &&data)
Move data from the specified object, and own it.
std::vector< RawDigitInfo_t > digits
vector of raw digit information
GridAxisClass const & TDCAxis() const
Return the information about the TDCs.
bool hasData() const
Returns whether we have data.
art::Ptr< raw::RawDigit > DigitPtr() const
Returns an art pointer to the actual digit.
bool RunOperation(art::Event const &evt, OperationBaseClass *operation)
std::tuple< float, float, float, float > GetCellBox(std::ptrdiff_t iCell) const
Returns the coordinates { w1, t1, w2, t2 } of specified cell.
art framework interface to geometry description
size_t NCells() const
Returns the total number of cells in the grid.
void SetData(pointer data, bool owned)
Sets the data and the ownership.
auto const detProp
bool Operate(geo::WireID const &wireID, size_t tick, float adc) override
bnb BNB Stream
std::add_pointer_t< const_value_type > const_pointer
static bool IsValidStatus(Status_t status)
Returns whether the specified status is a valid one.
std::ptrdiff_t GetCell(float coord) const
Returns the index of the specified cell.
bool SetMinCellSize(float min_size)
void AddOperation(std::unique_ptr< OperationBaseClass > new_op)
std::string Name() const override
std::vector< double > fConvertedCharge
Sum of Charge Converted using Birks&#39; formula.
void ExtractRange(TVirtualPad *pPad, std::vector< double > const *zoom=nullptr)
Fills the viewport information from the specified pad.
void Clear()
Deletes the data.