CandHitMorphological_tool.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 /// \file CandHitMorphological.cc
3 /// \author T. Usher
4 // MT note: This implementation is not thread-safe.
5 ////////////////////////////////////////////////////////////////////////
6 
9 
12 #include "art/Utilities/Globals.h"
13 #include "art_root_io/TFileService.h"
14 #include "cetlib_except/exception.h"
16 
17 #include "TProfile.h"
18 
19 #include <cmath>
20 
21 namespace reco_tool {
22 
24  public:
25  explicit CandHitMorphological(const fhicl::ParameterSet& pset);
26 
27  void findHitCandidates(const recob::Wire::RegionsOfInterest_t::datarange_t&,
28  const size_t,
29  const size_t,
30  const size_t,
31  HitCandidateVec&) const override;
32 
33  void MergeHitCandidates(const recob::Wire::RegionsOfInterest_t::datarange_t&,
34  const HitCandidateVec&,
35  MergeHitCandidateVec&) const override;
36 
37  private:
38  // Internal functions
39  //< Top level hit finding using erosion/dilation vectors
41  Waveform::const_iterator, //< derivative
43  Waveform::const_iterator, //< erosion
45  Waveform::const_iterator, //< dilation
46  const size_t,
47  float,
48  HitCandidateVec&) const;
49 
50  //< Fine grain hit finding within candidate peak regions using derivative method
53  const size_t,
54  int,
55  float,
56  HitCandidateVec&) const;
57 
58  //< For a given range, return the list of max/min pairs
59  using MaxMinPair = std::pair<Waveform::const_iterator, Waveform::const_iterator>;
60  using CandHitParams = std::tuple<Waveform::const_iterator,
63  Waveform::const_iterator>;
64  using CandHitParamsVec = std::vector<CandHitParams>;
65 
66  bool getListOfHitCandidates(Waveform::const_iterator,
67  Waveform::const_iterator,
68  int,
69  float,
70  CandHitParamsVec&) const;
71 
72  // Finding the nearest maximum/minimum from current point
73  Waveform::const_iterator findNearestMax(Waveform::const_iterator,
74  Waveform::const_iterator) const;
75  Waveform::const_iterator findNearestMin(Waveform::const_iterator,
76  Waveform::const_iterator) const;
77 
78  // handle finding the "start" and "stop" of a candidate hit
79  Waveform::const_iterator findStartTick(Waveform::const_iterator,
80  Waveform::const_iterator) const;
81  Waveform::const_iterator findStopTick(Waveform::const_iterator, Waveform::const_iterator) const;
82 
83  // some fhicl control variables
84  const size_t fPlane; //< Identifies the plane this tool is meant to operate on
85  const float fDilationThreshold; //< Dilation threshold
86  const float fDilationFraction; //< Fraction of max dilation to set for min dilation
87  const float fErosionFraction; //< Fraction of max dilation value to set min erosion
88  const int fMinDeltaTicks; //< minimum ticks from max to min to consider
89  const float fMinDeltaPeaks; //< minimum maximum to minimum peak distance
90  const float fMinHitHeight; //< Drop candidate hits with height less than this
91  const size_t fNumInterveningTicks; //< Number ticks between candidate hits to merge
92  const int fStructuringElement; //< Window size for morphologcial filter
93  const bool fOutputHistograms; //< If true will generate summary style histograms
94  const bool
95  fOutputWaveforms; //< If true will output waveform related info <<< very big output file!
96  const float
97  fFitNSigmaFromCenter; //< Limit ticks to fit to NSigma from hit center; not applied if zero or negative
98 
99  art::TFileDirectory* fHistDirectory;
100 
101  // Global histograms
102  TH1F* fDStopStartHist; //< Basically keeps track of the length of hit regions
103  TH1F* fDMaxTickMinTickHist; //< This will be a measure of the width of candidate hits
104  TH1F* fDMaxDerivMinDerivHist; //< This is the difference peak to peak of derivative for cand hit
105  TH1F* fMaxErosionHist; //< Keep track of the maximum erosion
106  TH1F* fMaxDilationHist; //< Keep track of the maximum dilation
107  TH1F* fMaxDilEroRatHist; //< Ratio of the maxima of the two
108 
109  //MT note: The mutable data members are only used in the histogram filling functions
110  //and histogram filling can only be done in single-threaded mode.
111  //Will need to consider design changes if this behavior changes.
112  mutable size_t
113  fLastChannel; //< Kludge to keep track of last channel when histogramming in effect
114  mutable size_t fChannelCnt; //< Counts the number of times a channel is used (assumed in order)
115 
116  //< All of the real work is done in the waveform tool
117  std::unique_ptr<reco_tool::IWaveformTool> fWaveformTool;
118 
119  const geo::GeometryCore* fGeometry = lar::providerFrom<geo::Geometry>();
120  };
121 
122  //----------------------------------------------------------------------
123  // Constructor.
125  : fPlane(pset.get<size_t>("Plane", 0))
126  , fDilationThreshold(pset.get<float>("DilationThreshold", 4.))
127  , fDilationFraction(pset.get<float>("DilationFraction", 0.75))
128  , fErosionFraction(pset.get<float>("ErosionFraction", 0.2))
129  , fMinDeltaTicks(pset.get<int>("MinDeltaTicks", 0))
130  , fMinDeltaPeaks(pset.get<float>("MinDeltaPeaks", 0.025))
131  , fMinHitHeight(pset.get<float>("MinHitHeight", 1.0))
132  , fNumInterveningTicks(pset.get<size_t>("NumInterveningTicks", 6))
133  , fStructuringElement(pset.get<int>("StructuringElement", 20))
134  , fOutputHistograms(pset.get<bool>("OutputHistograms", false))
135  , fOutputWaveforms(pset.get<bool>("OutputWaveforms", false))
136  , fFitNSigmaFromCenter(pset.get<float>("FitNSigmaFromCenter", 5.))
137  {
138 
139  if (art::Globals::instance()->nthreads() > 1u) {
140  if (fOutputHistograms) {
142  << "Cannot fill histograms when multiple threads configured, please set "
143  "fOutputHistograms to false or change number of threads to 1\n";
144  }
145 
146  if (fOutputWaveforms) {
148  << "Cannot write output waveforms when multiple threads configured, please set "
149  "fOutputHistograms to false or change number of threads to 1\n";
150  }
151  }
152  // Recover the baseline tool
153  fWaveformTool =
154  art::make_tool<reco_tool::IWaveformTool>(pset.get<fhicl::ParameterSet>("WaveformAlgs"));
155 
156  // Set the last channel to some nonsensical value
158  fChannelCnt = 0;
159 
160  // If asked, define the global histograms
161  if (fOutputHistograms) {
162  // Access ART's TFileService, which will handle creating and writing
163  // histograms and n-tuples for us.
165 
166  fHistDirectory = tfs.get();
167 
168  // Make a directory for these histograms
169  art::TFileDirectory dir = fHistDirectory->mkdir(Form("HitPlane_%1zu", fPlane));
170 
172  dir.make<TH1F>(Form("DStopStart_%1zu", fPlane), ";Delta Stop/Start;", 100, 0., 100.);
174  dir.make<TH1F>(Form("DMaxTMinT_%1zu", fPlane), ";Delta Max/Min Tick;", 100, 0., 100.);
176  dir.make<TH1F>(Form("DMaxDMinD_%1zu", fPlane), ";Delta Max/Min Deriv;", 200, 0., 100.);
178  dir.make<TH1F>(Form("MaxErosion_%1zu", fPlane), ";Max Erosion;", 200, -50., 150.);
180  dir.make<TH1F>(Form("MaxDilation_%1zu", fPlane), ";Max Dilation;", 200, -50., 150.);
182  dir.make<TH1F>(Form("MaxDilEroRat_%1zu", fPlane), ";Max Dil/Ero;", 200, -1., 1.);
183  }
184 
185  return;
186  }
187 
188  void
190  const recob::Wire::RegionsOfInterest_t::datarange_t& dataRange,
191  const size_t roiStartTick,
192  const size_t channel,
193  const size_t eventCount,
194  HitCandidateVec& hitCandidateVec) const
195  {
196  // In this case we want to find hit candidates based on the derivative of of the input waveform
197  // We get this from our waveform algs too...
198  Waveform rawDerivativeVec;
199  Waveform derivativeVec;
200 
201  // Recover the actual waveform
202  const Waveform& waveform = dataRange.data();
203 
204  fWaveformTool->firstDerivative(waveform, rawDerivativeVec);
205  fWaveformTool->triangleSmooth(rawDerivativeVec, derivativeVec);
206 
207  // Now we get the erosion/dilation vectors
208  Waveform erosionVec;
209  Waveform dilationVec;
210  Waveform averageVec;
211  Waveform differenceVec;
212 
213  reco_tool::HistogramMap histogramMap;
214 
215  // Compute the morphological filter vectors
216  fWaveformTool->getErosionDilationAverageDifference(waveform,
218  histogramMap,
219  erosionVec,
220  dilationVec,
221  averageVec,
222  differenceVec);
223 
224  // Now find the hits
225  findHitCandidates(derivativeVec.begin(),
226  derivativeVec.end(),
227  erosionVec.begin(),
228  erosionVec.end(),
229  dilationVec.begin(),
230  dilationVec.end(),
231  roiStartTick,
233  hitCandidateVec);
234 
235  // Limit start and stop tick to the neighborhood of the peak
236  if (fFitNSigmaFromCenter > 0) {
237  for (auto& hc : hitCandidateVec) {
238  auto startCand = hc.hitCenter - fFitNSigmaFromCenter * hc.hitSigma;
239  if (startCand >= 0) hc.startTick = std::max(hc.startTick, size_t(startCand));
240  hc.stopTick =
241  std::min(hc.stopTick, size_t(hc.hitCenter + fFitNSigmaFromCenter * hc.hitSigma));
242  }
243  }
244 
245  // Reset the hit height from the input waveform
246  for (auto& hitCandidate : hitCandidateVec) {
247  size_t centerIdx = hitCandidate.hitCenter;
248 
249  hitCandidate.hitHeight = waveform.at(centerIdx);
250  }
251 
252  // Keep track of histograms if requested
253  if (fOutputWaveforms) {
254  // Recover the details...
255  std::vector<geo::WireID> wids = fGeometry->ChannelToWire(channel);
256  size_t plane = wids[0].Plane;
257  size_t cryo = wids[0].Cryostat;
258  size_t tpc = wids[0].TPC;
259  size_t wire = wids[0].Wire;
260 
261  if (channel != fLastChannel) fChannelCnt = 0;
262 
263  // Make a directory for these histograms
264  art::TFileDirectory dir = fHistDirectory->mkdir(
265  Form("Event%04zu/c%1zuT%1zuP%1zu/Wire_%05zu", eventCount, cryo, tpc, plane, wire));
266 
267  size_t waveformSize = waveform.size();
268  size_t waveStart = dataRange.begin_index();
269 
270  TProfile* waveHist =
271  dir.make<TProfile>(Form("HWfm_%03zu_roiStart-%05zu", fChannelCnt, waveStart),
272  "Waveform",
273  waveformSize,
274  0,
275  waveformSize,
276  -500.,
277  500.);
278  TProfile* derivHist =
279  dir.make<TProfile>(Form("HDer_%03zu_roiStart-%05zu", fChannelCnt, waveStart),
280  "Derivative",
281  waveformSize,
282  0,
283  waveformSize,
284  -500.,
285  500.);
286  TProfile* erosionHist =
287  dir.make<TProfile>(Form("HEro_%03zu_roiStart-%05zu", fChannelCnt, waveStart),
288  "Erosion",
289  waveformSize,
290  0,
291  waveformSize,
292  -500.,
293  500.);
294  TProfile* dilationHist =
295  dir.make<TProfile>(Form("HDil_%03zu_roiStart-%05zu", fChannelCnt, waveStart),
296  "Dilation",
297  waveformSize,
298  0,
299  waveformSize,
300  -500.,
301  500.);
302  TProfile* candHitHist =
303  dir.make<TProfile>(Form("HCan_%03zu_roiStart-%05zu", fChannelCnt, waveStart),
304  "Cand Hits",
305  waveformSize,
306  0,
307  waveformSize,
308  -500.,
309  500.);
310  TProfile* maxDerivHist =
311  dir.make<TProfile>(Form("HMax_%03zu_roiStart-%05zu", fChannelCnt, waveStart),
312  "Maxima",
313  waveformSize,
314  0,
315  waveformSize,
316  -500.,
317  500.);
318  TProfile* strtStopHist =
319  dir.make<TProfile>(Form("HSSS_%03zu_roiStart-%05zu", fChannelCnt, waveStart),
320  "Start/Stop",
321  waveformSize,
322  0,
323  waveformSize,
324  -500.,
325  500.);
326 
327  // Fill wave/derivative
328  for (size_t idx = 0; idx < waveform.size(); idx++) {
329  waveHist->Fill(roiStartTick + idx, waveform.at(idx));
330  derivHist->Fill(roiStartTick + idx, derivativeVec.at(idx));
331  erosionHist->Fill(roiStartTick + idx, erosionVec.at(idx));
332  dilationHist->Fill(roiStartTick + idx, dilationVec.at(idx));
333  }
334 
335  // Fill hits
336  for (const auto& hitCandidate : hitCandidateVec) {
337  candHitHist->Fill(hitCandidate.hitCenter, hitCandidate.hitHeight);
338  maxDerivHist->Fill(hitCandidate.maxTick, hitCandidate.maxDerivative);
339  maxDerivHist->Fill(hitCandidate.minTick, hitCandidate.minDerivative);
340  strtStopHist->Fill(hitCandidate.startTick, waveform.at(hitCandidate.startTick));
341  strtStopHist->Fill(hitCandidate.stopTick, waveform.at(hitCandidate.stopTick));
342  }
343 
345  fChannelCnt++;
346  }
347 
348  if (fOutputHistograms) {
349  // Fill hits
350  for (const auto& hitCandidate : hitCandidateVec) {
351  fDStopStartHist->Fill(hitCandidate.stopTick - hitCandidate.startTick, 1.);
352  fDMaxTickMinTickHist->Fill(hitCandidate.minTick - hitCandidate.maxTick, 1.);
353  fDMaxDerivMinDerivHist->Fill(hitCandidate.maxDerivative - hitCandidate.minDerivative, 1.);
354  }
355 
356  // Get the max dilation/erosion
357  Waveform::const_iterator maxDilationItr =
358  std::max_element(dilationVec.begin(), dilationVec.end());
359  Waveform::const_iterator maxErosionItr =
360  std::max_element(erosionVec.begin(), erosionVec.end());
361 
362  float dilEroRat(1.);
363 
364  if (std::abs(*maxDilationItr) > 0.) dilEroRat = *maxErosionItr / *maxDilationItr;
365 
366  fMaxErosionHist->Fill(*maxErosionItr, 1.);
367  fMaxDilationHist->Fill(*maxDilationItr, 1.);
368  fMaxDilEroRatHist->Fill(dilEroRat, 1.);
369  }
370 
371  return;
372  }
373 
374  void
376  Waveform::const_iterator derivStopItr,
377  Waveform::const_iterator erosionStartItr,
378  Waveform::const_iterator erosionStopItr,
379  Waveform::const_iterator dilationStartItr,
380  Waveform::const_iterator dilationStopItr,
381  const size_t roiStartTick,
382  float dilationThreshold,
383  HitCandidateVec& hitCandidateVec) const
384  {
385  // This function aims to use the erosion/dilation vectors to find candidate hit regions
386  // Once armed with a region then the "standard" differential approach is used to return the candidate peaks
387 
388  // Don't do anything if not enough ticks
389  int ticksInInputWaveform = std::distance(derivStartItr, derivStopItr);
390 
391  if (ticksInInputWaveform < fMinDeltaTicks) return;
392 
393  // This function is recursive, we start by finding the largest element of the dilation vector
394  Waveform::const_iterator maxItr = std::max_element(dilationStartItr, dilationStopItr);
395  float maxVal = *maxItr;
396 
397  // Check that the peak is of reasonable height...
398  if (maxVal < dilationThreshold) return;
399 
400  int maxBin = std::distance(dilationStartItr, maxItr);
401 
402  // The candidate hit region we want lies between the two nearest minima to the maximum we just found
403  // subject to the condition that the erosion vector has return to less than zero
404  Waveform::const_iterator firstDerItr = derivStartItr + maxBin;
405  Waveform::const_iterator erosionItr = erosionStartItr + maxBin;
406 
407  float firstDerivValue = -1.;
408  float erosionCutValue = fErosionFraction * maxVal;
409 
410  // Search for starting point
411  while (firstDerItr != derivStartItr) {
412  // Look for the turnover point
413  if (*erosionItr-- < erosionCutValue) {
414  // We are looking for the zero crossing signifying a minimum value in the waveform
415  // (so the previous derivative < 0 while current is > 0)
416  // We are moving "backwards" so the current value <= 0, the previous value > 0
417  if (*firstDerItr * firstDerivValue <= 0. && firstDerivValue > 0.) break;
418  }
419 
420  firstDerivValue = *firstDerItr--;
421  }
422 
423  // Set the start bin
424  int hitRegionStart = std::distance(derivStartItr, firstDerItr);
425 
426  // Now go the other way
427  Waveform::const_iterator lastDerItr = derivStartItr + maxBin;
428 
429  // Reset the local variables
430  float lastDerivValue = 1.;
431 
432  erosionItr = erosionStartItr + maxBin;
433 
434  // Search for starting point
435  while (lastDerItr != derivStopItr) {
436  if (*erosionItr++ <= erosionCutValue) {
437  // We are again looking for the zero crossing signifying a minimum value in the waveform
438  // This time we are moving forward, so test is that previous value < 0, new value >= 0
439  if (*lastDerItr * lastDerivValue <= 0. && lastDerivValue < 0.) break;
440  }
441 
442  lastDerivValue = *lastDerItr++;
443  }
444 
445  // Set the stop bin
446  int hitRegionStop = std::distance(derivStartItr, lastDerItr);
447 
448  // Recursive call to find any hits in front of where we are now
449  if (hitRegionStart > fMinDeltaTicks)
450  findHitCandidates(derivStartItr,
451  derivStartItr + hitRegionStart,
452  erosionStartItr,
453  erosionStartItr + hitRegionStart,
454  dilationStartItr,
455  dilationStartItr + hitRegionStart,
456  roiStartTick,
458  hitCandidateVec);
459 
460  // Call the differential hit finding to get the actual hits within the region
461  findHitCandidates(derivStartItr + hitRegionStart,
462  derivStartItr + hitRegionStop,
463  roiStartTick + hitRegionStart,
466  hitCandidateVec);
467 
468  // Now call ourselves again to find any hits trailing the region we just identified
469  if (std::distance(lastDerItr, derivStopItr) > fMinDeltaTicks)
470  findHitCandidates(derivStartItr + hitRegionStop,
471  derivStopItr,
472  erosionStartItr + hitRegionStop,
473  erosionStopItr,
474  dilationStartItr + hitRegionStop,
475  dilationStopItr,
476  roiStartTick + hitRegionStop,
478  hitCandidateVec);
479 
480  return;
481  }
482 
483  void
485  Waveform::const_iterator stopItr,
486  const size_t roiStartTick,
487  int dTicksThreshold,
488  float dPeakThreshold,
489  HitCandidateVec& hitCandidateVec) const
490  {
491  // Search for candidate hits...
492  // Strategy is to get the list of all possible max/min pairs of the input derivative vector and then
493  // look for candidate hits in that list
494  CandHitParamsVec candHitParamsVec;
495 
497  startItr, stopItr, dTicksThreshold, dPeakThreshold, candHitParamsVec)) {
498  // We've been given a list of candidate hits so now convert to hits
499  // Version one... simply convert all the candidates
500  for (const auto& tuple : candHitParamsVec) {
501  // Create a new hit candidate and store away
502  HitCandidate hitCandidate;
503 
504  Waveform::const_iterator candStartItr = std::get<0>(tuple);
505  Waveform::const_iterator maxItr = std::get<1>(tuple);
506  Waveform::const_iterator minItr = std::get<2>(tuple);
507  Waveform::const_iterator candStopItr = std::get<3>(tuple);
508 
509  Waveform::const_iterator peakItr =
510  std::min_element(maxItr, minItr, [](const auto& left, const auto& right) {
511  return std::fabs(left) < std::fabs(right);
512  });
513 
514  // Check balance
515  if (2 * std::distance(peakItr, minItr) < std::distance(maxItr, peakItr))
516  peakItr--;
517  else if (2 * std::distance(maxItr, peakItr) < std::distance(peakItr, minItr))
518  peakItr++;
519 
520  // Special handling of the start tick for multiple hits
521  size_t hitCandidateStartTick = roiStartTick + std::distance(startItr, candStartItr);
522 
523  if (!hitCandidateVec.empty()) {
524  int deltaTicks = hitCandidateStartTick - hitCandidateVec.back().stopTick;
525 
526  if (deltaTicks > 0) {
527  hitCandidateStartTick -= deltaTicks / 2;
528  hitCandidateVec.back().stopTick += deltaTicks / 2;
529  }
530  }
531 
532  hitCandidate.startTick = hitCandidateStartTick;
533  hitCandidate.stopTick = roiStartTick + std::distance(startItr, candStopItr);
534  hitCandidate.maxTick = roiStartTick + std::distance(startItr, maxItr);
535  hitCandidate.minTick = roiStartTick + std::distance(startItr, minItr);
536  hitCandidate.maxDerivative = maxItr != stopItr ? *maxItr : 0.;
537  hitCandidate.minDerivative = minItr != stopItr ? *minItr : 0.;
538  hitCandidate.hitCenter = roiStartTick + std::distance(startItr, peakItr) + 0.5;
539  hitCandidate.hitSigma = 0.5 * float(hitCandidate.minTick - hitCandidate.maxTick);
540  hitCandidate.hitHeight = hitCandidate.hitSigma *
541  (hitCandidate.maxDerivative - hitCandidate.minDerivative) / 1.2130;
542 
543  hitCandidateVec.push_back(hitCandidate);
544  }
545  }
546 
547  // // The idea will be to find the largest deviation in the input derivative waveform as the starting point. Depending
548  // // on if a maximum or minimum, we search forward or backward to find the minimum or maximum that our extremum
549  // // corresponds to.
550  // std::pair<Waveform::const_iterator, Waveform::const_iterator> minMaxPair = std::minmax_element(startItr, stopItr);
551  //
552  // Waveform::const_iterator maxItr = minMaxPair.second;
553  // Waveform::const_iterator minItr = minMaxPair.first;
554  //
555  // // Use the larger of the two as the starting point and recover the nearest max or min
556  // if (std::fabs(*maxItr) > std::fabs(*minItr)) minItr = findNearestMin(maxItr, stopItr);
557  // else maxItr = findNearestMax(minItr,startItr);
558  //
559  // int deltaTicks = std::distance(maxItr,minItr);
560  // float range = *maxItr - *minItr;
561  //
562  // // At some point small rolling oscillations on the waveform need to be ignored...
563  // if (deltaTicks >= dTicksThreshold && range > dPeakThreshold)
564  // {
565  // // Need to back up to find zero crossing, this will be the starting point of our
566  // // candidate hit but also the endpoint of the pre sub-waveform we'll search next
567  // Waveform::const_iterator newEndItr = findStartTick(maxItr, startItr);
568  //
569  // int startTick = std::distance(startItr,newEndItr);
570  //
571  // // Now need to go forward to again get close to zero, this will then be the end point
572  // // of our candidate hit and the starting point for the post sub-waveform to search
573  // Waveform::const_iterator newStartItr = findStopTick(minItr, stopItr);
574  //
575  // int stopTick = std::distance(startItr,newStartItr);
576  //
577  // // Find hits in the section of the waveform leading up to this candidate hit
578  // if (startTick > 2)
579  // {
580  // // Special handling for merged hits
581  // if (*(newEndItr-1) > 0.) {dTicksThreshold = 2; dPeakThreshold = 0.; }
582  // else {dTicksThreshold = fMinDeltaTicks; dPeakThreshold = fMinDeltaPeaks;}
583  //
584  // findHitCandidates(startItr,newEndItr+1,roiStartTick,dTicksThreshold,dPeakThreshold,hitCandidateVec);
585  // }
586  //
587  // // Create a new hit candidate and store away
588  // HitCandidate_t hitCandidate;
589  //
590  // Waveform::const_iterator peakItr = std::min_element(maxItr,minItr,[](const auto& left, const auto& right){return std::fabs(left) < std::fabs(right);});
591  //
592  // // Check balance
593  // if (2 * std::distance(peakItr,minItr) < std::distance(maxItr,peakItr)) peakItr--;
594  // else if (2 * std::distance(maxItr,peakItr) < std::distance(peakItr,minItr)) peakItr++;
595  //
596  // // Special handling of the start tick for multiple hits
597  // size_t hitCandidateStartTick = roiStartTick + startTick;
598  //
599  // if (!hitCandidateVec.empty())
600  // {
601  // int deltaTicks = hitCandidateStartTick - hitCandidateVec.back().stopTick;
602  //
603  // if (deltaTicks > 0)
604  // {
605  // hitCandidateStartTick -= deltaTicks / 2;
606  // hitCandidateVec.back().stopTick += deltaTicks / 2;
607  // }
608  // }
609  //
610  // hitCandidate.startTick = hitCandidateStartTick;
611  // hitCandidate.stopTick = roiStartTick + stopTick;
612  // hitCandidate.maxTick = roiStartTick + std::distance(startItr,maxItr);
613  // hitCandidate.minTick = roiStartTick + std::distance(startItr,minItr);
614  // hitCandidate.maxDerivative = maxItr != stopItr ? *maxItr : 0.;
615  // hitCandidate.minDerivative = minItr != stopItr ? *minItr : 0.;
616  // hitCandidate.hitCenter = roiStartTick + std::distance(startItr,peakItr) + 0.5;
617  // hitCandidate.hitSigma = 0.5 * float(hitCandidate.minTick - hitCandidate.maxTick);
618  // hitCandidate.hitHeight = hitCandidate.hitSigma * (hitCandidate.maxDerivative - hitCandidate.minDerivative) / 1.2130;
619  //
620  // hitCandidateVec.push_back(hitCandidate);
621  //
622  // // Finally, search the section of the waveform following this candidate for more hits
623  // if (std::distance(newStartItr,stopItr) > 2)
624  // {
625  // // Special handling for merged hits
626  // if (*(newStartItr+1) < 0.) {dTicksThreshold = 2; dPeakThreshold = 0.; }
627  // else {dTicksThreshold = fMinDeltaTicks; dPeakThreshold = fMinDeltaPeaks;}
628  //
629  // findHitCandidates(newStartItr,stopItr,roiStartTick + stopTick,dTicksThreshold,dPeakThreshold,hitCandidateVec);
630  // }
631  // }
632 
633  return;
634  }
635 
636  bool
638  Waveform::const_iterator stopItr,
639  int dTicksThreshold,
640  float dPeakThreshold,
641  CandHitParamsVec& candHitParamsVec) const
642  {
643  // We'll check if any of our candidates meet the requirements so declare the result here
644  bool foundCandidate(false);
645 
646  int dTicks = std::distance(startItr, stopItr);
647 
648  // Search for candidate hits...
649  // But only if enough ticks
650  if (dTicks < fMinDeltaTicks) return foundCandidate;
651 
652  // Generally, the mission is simple... the goal is to find all possible combinations of maximum/minimum pairs in
653  // the input (presumed) derivative waveform. We can do this with a divice and conquer approach where we start by
654  // finding the largerst max or min and start from there
655  MaxMinPair minMaxPair = std::minmax_element(startItr, stopItr);
656 
657  Waveform::const_iterator maxItr = minMaxPair.second;
658  Waveform::const_iterator minItr = minMaxPair.first;
659 
660  // Use the larger of the two as the starting point and recover the nearest max or min
661  if (std::fabs(*maxItr) > std::fabs(*minItr))
662  minItr = findNearestMin(maxItr, stopItr);
663  else
664  maxItr = findNearestMax(minItr, startItr);
665 
666  int deltaTicks = std::distance(maxItr, minItr);
667  float range = *maxItr - *minItr;
668 
669  if (deltaTicks < 2) return foundCandidate;
670 
671  // Check if this particular max/min pair would meet the requirements...
672  if (deltaTicks >= dTicksThreshold && range > dPeakThreshold) foundCandidate = true;
673 
674  // Need to back up to find zero crossing, this will be the starting point of our
675  // candidate hit but also the endpoint of the pre sub-waveform we'll search next
676  Waveform::const_iterator candStartItr = findStartTick(maxItr, startItr);
677 
678  // Now need to go forward to again get close to zero, this will then be the end point
679  // of our candidate hit and the starting point for the post sub-waveform to search
680  Waveform::const_iterator candStopItr = findStopTick(minItr, stopItr);
681 
682  // Call ourself to find hit candidates preceding this one
683  bool prevTicks = getListOfHitCandidates(
684  startItr, candStartItr, dTicksThreshold, dPeakThreshold, candHitParamsVec);
685 
686  // The above call will have populated the list of candidate max/min pairs preceding this one, so now add our contribution
687  candHitParamsVec.emplace_back(candStartItr, maxItr, minItr, candStopItr);
688 
689  // Now catch any that might follow this one
690  bool postTicks = getListOfHitCandidates(
691  candStopItr, stopItr, dTicksThreshold, dPeakThreshold, candHitParamsVec);
692 
693  return foundCandidate || prevTicks || postTicks;
694  }
695 
696  void
698  const recob::Wire::RegionsOfInterest_t::datarange_t& rangeData,
699  const HitCandidateVec& hitCandidateVec,
700  MergeHitCandidateVec& mergedHitsVec) const
701  {
702  // If nothing on the input end then nothing to do
703  if (hitCandidateVec.empty()) return;
704 
705  // The idea is to group hits that "touch" so they can be part of common fit, those that
706  // don't "touch" are fit independently. So here we build the output vector to achieve that
707  // Get a container for the hits...
708  HitCandidateVec groupedHitVec;
709 
710  // Initialize the end of the last hit which we'll set to the first input hit's stop
711  size_t lastStopTick = hitCandidateVec.front().stopTick;
712 
713  // Step through the input hit candidates and group them by proximity
714  for (const auto& hitCandidate : hitCandidateVec) {
715  // Small pulse height hits should not be considered?
716  if (hitCandidate.hitHeight > fMinHitHeight) {
717  // Check condition that we have a new grouping
718  if (hitCandidate.startTick > lastStopTick + fNumInterveningTicks &&
719  !groupedHitVec.empty()) {
720  mergedHitsVec.emplace_back(groupedHitVec);
721 
722  groupedHitVec.clear();
723  }
724 
725  // Add the current hit to the current group
726  groupedHitVec.emplace_back(hitCandidate);
727 
728  lastStopTick = hitCandidate.stopTick;
729  }
730  }
731 
732  // Check end condition
733  if (!groupedHitVec.empty()) mergedHitsVec.emplace_back(groupedHitVec);
734 
735  return;
736  }
737 
740  Waveform::const_iterator stopItr) const
741  {
742  // reset the min iterator and search forward to find the nearest minimum
743  Waveform::const_iterator lastItr = maxItr;
744 
745  // The strategy is simple...
746  // We are at a maximum so we search forward until we find the lowest negative point
747  while ((lastItr + 1) != stopItr) {
748  if (*(lastItr + 1) > *lastItr) break;
749 
750  lastItr++;
751  }
752 
753  // The minimum will be the last iterator value...
754  return lastItr;
755  }
756 
759  Waveform::const_iterator startItr) const
760  {
761  // Set the internal loop variable...
762  Waveform::const_iterator lastItr = minItr;
763 
764  // One extra condition to watch for here, make sure we can actually back up!
765  if (std::distance(startItr, minItr) > 0) {
766  // Similar to searching for a maximum, we loop backward over ticks looking for the waveform to start decreasing
767  while ((lastItr - 1) != startItr) {
768  if (*(lastItr - 1) < *lastItr) break;
769 
770  lastItr--;
771  }
772  }
773 
774  return lastItr;
775  }
776 
779  Waveform::const_iterator startItr) const
780  {
781  Waveform::const_iterator lastItr = maxItr;
782 
783  // If we can't back up then there is nothing to do
784  if (std::distance(startItr, lastItr) > 0) {
785  // In theory, we are starting at a maximum and want to find the "start" of the candidate peak
786  // Ideally we would look to search backward to the point where the (derivative) waveform crosses zero again.
787  // However, the complication is that we need to watch for the case where two peaks are merged together and
788  // we might run through another peak before crossing zero...
789  // So... loop until we hit the startItr...
790  Waveform::const_iterator loopItr = lastItr - 1;
791 
792  while (loopItr != startItr) {
793  // Ideal world case, we cross zero... but we might encounter a minimum... or an inflection point
794  if (*loopItr < 0. || !(*loopItr < *lastItr)) break;
795 
796  lastItr = loopItr--;
797  }
798  }
799  else
800  lastItr = startItr;
801 
802  return lastItr;
803  }
804 
807  Waveform::const_iterator stopItr) const
808  {
809  Waveform::const_iterator lastItr = minItr;
810 
811  // If we can't go forward then there is really nothing to do
812  if (std::distance(minItr, stopItr) > 1) {
813  // Pretty much the same strategy as for finding the start tick...
814  Waveform::const_iterator loopItr = lastItr + 1;
815 
816  while (loopItr != stopItr) {
817  // Ideal case that we have crossed zero coming from a minimum... but watch for a maximum as well
818  if (*loopItr > 0. || !(*loopItr > *lastItr)) break;
819 
820  lastItr = loopItr++;
821  }
822  }
823 
824  return lastItr;
825  }
826 
828 }
Waveform::const_iterator findNearestMin(Waveform::const_iterator, Waveform::const_iterator) const
#define DEFINE_ART_CLASS_TOOL(tool)
Definition: ToolMacros.h:42
std::vector< CandHitParams > CandHitParamsVec
T * get() const
Definition: ServiceHandle.h:63
This is the interface class for tools/algorithms that perform various operations on waveforms...
Waveform::const_iterator findStopTick(Waveform::const_iterator, Waveform::const_iterator) const
std::vector< geo::WireID > ChannelToWire(raw::ChannelID_t const channel) const
Returns a list of wires connected to the specified TPC channel.
intermediate_table::const_iterator const_iterator
uint8_t channel
Definition: CRTFragment.hh:201
string dir
CandHitMorphological(const fhicl::ParameterSet &pset)
art framework interface to geometry description
void findHitCandidates(const recob::Wire::RegionsOfInterest_t::datarange_t &, const size_t, const size_t, const size_t, HitCandidateVec &) const override
T abs(T value)
Waveform::const_iterator findStartTick(Waveform::const_iterator, Waveform::const_iterator) const
std::tuple< Waveform::const_iterator, Waveform::const_iterator, Waveform::const_iterator, Waveform::const_iterator > CandHitParams
std::unique_ptr< reco_tool::IWaveformTool > fWaveformTool
bool getListOfHitCandidates(Waveform::const_iterator, Waveform::const_iterator, int, float, CandHitParamsVec &) const
T get(std::string const &key) const
Definition: ParameterSet.h:271
double distance(double x1, double y1, double z1, double x2, double y2, double z2)
static int max(int a, int b)
Description of geometry of one entire detector.
std::map< int, TProfile * > HistogramMap
Definition: IWaveformTool.h:37
cet::coded_exception< errors::ErrorCodes, ExceptionDetail::translate > Exception
Definition: Exception.h:66
T min(sqlite3 *const db, std::string const &table_name, std::string const &column_name)
Definition: statistics.h:55
std::pair< Waveform::const_iterator, Waveform::const_iterator > MaxMinPair
This provides an interface for tools which are tasked with finding candidate hits on input waveforms...
Waveform::const_iterator findNearestMax(Waveform::const_iterator, Waveform::const_iterator) const
void MergeHitCandidates(const recob::Wire::RegionsOfInterest_t::datarange_t &, const HitCandidateVec &, MergeHitCandidateVec &) const override
static Globals * instance()
Definition: Globals.cc:17
auto const & get(AssnsNode< L, R, D > const &r)
Definition: AssnsNode.h:115
std::vector< HitCandidateVec > MergeHitCandidateVec
int bool
Definition: qglobal.h:345
std::vector< HitCandidate > HitCandidateVec