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