All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
BasicWireAnalysis_tool.cc
Go to the documentation of this file.
2 
3 #include "fhiclcpp/ParameterSet.h"
4 #include "art/Utilities/ToolMacros.h"
5 #include "art/Framework/Services/Registry/ServiceHandle.h"
6 #include "art_root_io/TFileService.h"
7 #include "art/Framework/Core/ModuleMacros.h"
8 #include "art/Utilities/make_tool.h"
9 #include "art_root_io/TFileDirectory.h"
10 #include "messagefacility/MessageLogger/MessageLogger.h"
11 
13 #include "larcore/CoreUtils/ServiceUtil.h" // lar::providerFrom()
17 
19 
20 #include "TH1.h"
21 #include "TH2.h"
22 #include "TProfile.h"
23 #include "TProfile2D.h"
24 #include "TF1.h"
25 #include "TVirtualFFT.h"
26 
27 #include <cmath>
28 #include <algorithm>
29 
31 {
32  ////////////////////////////////////////////////////////////////////////
33  //
34  // Class: BasicWireAnalysis
35  // Module Type: producer
36  // File: BasicWireAnalysis.h
37  //
38  // The intent of this module is to provide methods for
39  // "analyzing" hits on waveforms
40  //
41  // Configuration parameters:
42  //
43  // TruncMeanFraction - the fraction of waveform bins to discard when
44  //
45  // Created by Tracy Usher (usher@slac.stanford.edu) on February 19, 2016
46  //
47  ////////////////////////////////////////////////////////////////////////
48 
49 class BasicWireAnalysis : virtual public IWireHistogramTool
50 {
51 public:
52  /**
53  * @brief Constructor
54  *
55  * @param pset
56  */
57  explicit BasicWireAnalysis(fhicl::ParameterSet const & pset);
58 
59  /**
60  * @brief Destructor
61  */
63 
64  // provide for initialization
65  void configure(fhicl::ParameterSet const & pset) override;
66 
67  /**
68  * @brief Interface for initializing the histograms to be filled
69  *
70  * @param TFileService handle to the TFile service
71  * @param string subdirectory to store the hists in
72  */
73  void initializeHists(art::ServiceHandle<art::TFileService>&, const std::string&) override;
74 
75  /**
76  * @brief Interface for method to executve at the end of run processing
77  *
78  * @param int number of events to use for normalization
79  */
80  void endJob(int numEvents) override;
81 
82  /**
83  * @brief Interface for filling histograms
84  */
86 
87 private:
88 
89  // Define a structure to contain hits
90  using HitCandidate_t = struct HitCandidate
91  {
92  size_t startTick;
93  size_t stopTick;
94  size_t maxTick;
95  size_t minTick;
98  float hitCenter;
99  float hitSigma;
100  float hitHeight;
101  };
102 
103  using HitCandidateVec = std::vector<HitCandidate_t>;
104 // using MergeHitCandidateVec = std::vector<HitCandidateVec>;
105 
106  using Waveform = std::vector<float>;
107 
108  // Internal functions
109  void findHitCandidates(Waveform::const_iterator,
110  Waveform::const_iterator,
111  size_t,
112  size_t,
113  HitCandidateVec&) const;
114  void findHitCandidates(Waveform::const_iterator,
115  Waveform::const_iterator,
116  Waveform::const_iterator,
117  Waveform::const_iterator,
118  size_t,
119  size_t,
120  HitCandidateVec&) const;
121 
122  // Finding the nearest maximum/minimum from current point
123  Waveform::const_iterator findNearestMax(Waveform::const_iterator, Waveform::const_iterator) const;
124  Waveform::const_iterator findNearestMin(Waveform::const_iterator, Waveform::const_iterator) const;
125 
126  // handle finding the "start" and "stop" of a candidate hit
127  Waveform::const_iterator findStartTick(Waveform::const_iterator, Waveform::const_iterator) const;
128  Waveform::const_iterator findStopTick(Waveform::const_iterator, Waveform::const_iterator) const;
129 
130  // some control variables
131  std::vector<int> fMinDeltaTicks; //< minimum ticks from max to min to consider
132  std::vector<int> fMaxDeltaTicks; //< maximum ticks from max to min to consider
133  std::vector<float> fMinDeltaPeaks; //< minimum maximum to minimum peak distance
134  float fMinHitHeight; //< Drop candidate hits with height less than this
135  size_t fNumInterveningTicks; //< Number ticks between candidate hits to merge
136  int fStructuringElement; //< Window size for morphologcial filter
137 
138  // Member variables from the fhicl file
139  std::unique_ptr<reco_tool::IWaveformTool> fWaveformTool;
140 
141  // Pointers to the histograms we'll create.
142  std::vector<TH1D*> fTruncMeanHist;
143  std::vector<TH1D*> fTruncRmsHist;
144  std::vector<TH1D*> fFullRmsHist;
145  std::vector<TH1D*> fNumTruncHist;
146 
147  std::vector<TH1D*> fDeltaTicksHist;
148  std::vector<TH1D*> fRangeHist;
149 
150  art::TFileDirectory* fHistDirectory;
151 
152  // Useful services, keep copies for now (we can update during begin run periods)
153  const geo::GeometryCore& fGeometry; ///< pointer to Geometry service
154  icarusutil::SignalShapingICARUSService& fSignalServices; ///< The signal shaping service
155  const lariov::DetPedestalProvider& fPedestalRetrievalAlg; ///< Keep track of an instance to the pedestal retrieval alg
156 };
157 
158 //----------------------------------------------------------------------------
159 /// Constructor.
160 ///
161 /// Arguments:
162 ///
163 /// pset - Fcl parameters.
164 ///
165 BasicWireAnalysis::BasicWireAnalysis(fhicl::ParameterSet const & pset) :
166  fGeometry(*lar::providerFrom<geo::Geometry>()),
167  fSignalServices(*art::ServiceHandle<icarusutil::SignalShapingICARUSService>()),
168  fPedestalRetrievalAlg(*lar::providerFrom<lariov::DetPedestalService>())
169 {
170  configure(pset);
171 
172  // Report.
173  mf::LogInfo("BasicWireAnalysis") << "BasicWireAnalysis configured\n";
174 }
175 
176 //----------------------------------------------------------------------------
177 /// Destructor.
178 BasicWireAnalysis::~BasicWireAnalysis()
179 {}
180 
181 //----------------------------------------------------------------------------
182 /// Reconfigure method.
183 ///
184 /// Arguments:
185 ///
186 /// pset - Fcl parameter set.
187 ///
188 void BasicWireAnalysis::configure(fhicl::ParameterSet const & pset)
189 {
190  fMinDeltaTicks = pset.get<std::vector<int> >("MinDeltaTicks", std::vector<int>() = { 0, 0, 0});
191  fMaxDeltaTicks = pset.get<std::vector<int> >("MaxDeltaTicks", std::vector<int>() = { 30, 30, 30});
192  fMinDeltaPeaks = pset.get<std::vector<float> >("MinDeltaPeaks", std::vector<float>() = {0.025, 0.025, 0.025});
193  fMinHitHeight = pset.get< float >("MinHitHeight", 2.0);
194  fNumInterveningTicks = pset.get< size_t >("NumInterveningTicks", 6 );
195  fStructuringElement = pset.get< int >("StructuringElement", 20);
196 
197  // Recover an instance of the waveform tool
198  fhicl::ParameterSet waveformToolParams;
199 
200  waveformToolParams.put<std::string>("tool_type","WaveformTools");
201 
202  fWaveformTool = art::make_tool<reco_tool::IWaveformTool>(waveformToolParams);
203 }
204 
205 //----------------------------------------------------------------------------
206 /// Begin job method.
207 void BasicWireAnalysis::initializeHists(art::ServiceHandle<art::TFileService>& tfs, const std::string& dirName)
208 {
209  fHistDirectory = tfs.get();
210 
211  // Make a directory for these histograms
212  art::TFileDirectory dir = tfs->mkdir(dirName.c_str());
213 
214  // Define the histograms. Putting semi-colons around the title
215  // causes it to be displayed as the x-axis label if the histogram
216  // is drawn.
217 
218  fTruncMeanHist.resize(3);
219  fTruncRmsHist.resize(3);
220  fFullRmsHist.resize(3);
221  fNumTruncHist.resize(3);
222  fDeltaTicksHist.resize(3);
223  fRangeHist.resize(3);
224 
225  for(size_t plane = 0; plane < fGeometry.Nplanes(); plane++)
226  {
227  std::string histName = "TruncMean_" + std::to_string(plane);
228 
229  fTruncMeanHist[plane] = dir.make<TH1D>(histName.c_str(), ";ADC", 200, -50., 50.);
230 
231  histName = "TruncRMS_" + std::to_string(plane);
232 
233  fTruncRmsHist[plane] = dir.make<TH1D>(histName.c_str(), ";ADC", 100, 0., 20.);
234 
235  histName = "FullRMS_" + std::to_string(plane);
236 
237  fFullRmsHist[plane] = dir.make<TH1D>(histName.c_str(), ";ADC", 100, 0., 20.);
238 
239  histName = "NTrunc_" + std::to_string(plane);
240 
241  fNumTruncHist[plane] = dir.make<TH1D>(histName.c_str(), ";Truncated Fraction", 100, 0., 1.);
242 
243  histName = "DeltaTicks_" + std::to_string(plane);
244 
245  fDeltaTicksHist[plane] = dir.make<TH1D>(histName.c_str(), ";Delta", 500, 0., 500.);
246 
247  histName = "Range_" + std::to_string(plane);
248 
249  fRangeHist[plane] = dir.make<TH1D>(histName.c_str(), ";Range", 200, 0., 200.);
250  }
251 
252  return;
253 }
254 
255 void BasicWireAnalysis::fillHistograms(const IWireHistogramTool::WirePtrVec& wirePtrVec,
256  const IWireHistogramTool::SimChannelMap& channelMap,
257  int eventNum) const
258 {
259  // Sadly, the RawDigits come to us in an unsorted condition which is not optimal for
260  // what we want to do here. So we make a vector of pointers to the input raw digits and sort them
261  std::vector<const recob::Wire*> wireVec;
262 
263  // Ugliness to fill the pointer vector...
264  for(size_t idx = 0; idx < wirePtrVec.size(); idx++) wireVec.push_back(wirePtrVec.at(idx).get());
265 
266  // Sort (use a lambda to sort by channel id)
267  std::sort(wireVec.begin(),wireVec.end(),[](const recob::Wire* left, const recob::Wire* right) {return left->Channel() < right->Channel();});
268 
269  // Commence looping over raw digits
270  for(const auto& wire : wireVec)
271  {
272  raw::ChannelID_t channel = wire->Channel();
273 
274  // Try to limit to the wire number (since we are already segregated by plane)
275  std::vector<geo::WireID> wids = fGeometry.ChannelToWire(channel);
276  size_t cryo = wids[0].Cryostat;
277  size_t tpc = wids[0].TPC;
278  size_t plane = wids[0].Plane;
279  size_t wireNum = wids[0].Wire;
280 
281  // Make a directory for these histograms
282  art::TFileDirectory dir = fHistDirectory->mkdir(Form("WavePlane_%1zu/c%1zu/c%1zut%1zuwire_%05zu",plane,size_t(eventNum),cryo,tpc,wireNum));
283 
284  // If MC, does this channel have signal?
285  bool hasSignal = channelMap.find(channel) != channelMap.end();
286 
287  if (!hasSignal) continue;
288 
289  const recob::Wire::RegionsOfInterest_t& signalROI = wire->SignalROI();
290 
291  for(const auto& range : signalROI.get_ranges())
292  {
293  const Waveform& waveform = range.data();
294 
295  // Get mean rms and stuff
296  float truncMean(0.);
297  float truncRMS(0.);
298  float fullRMS(0.);
299  int nTrunc(0);
300 
301  fWaveformTool->getTruncatedMeanRMS(waveform, truncMean, fullRMS, truncRMS, nTrunc);
302 
303  fTruncMeanHist.at(plane)->Fill(truncMean, 1.);
304  fTruncRmsHist.at(plane)->Fill(truncRMS, 1.);
305  fFullRmsHist.at(plane)->Fill(fullRMS, 1.);
306  fNumTruncHist.at(plane)->Fill(float(nTrunc)/float(waveform.size()),1.);
307 
308  // ROI start time
309  raw::TDCtick_t roiStartTick = range.begin_index();
310 
311  HitCandidateVec hitCandidateVec;
312 
313  // In this case we want to find hit candidates based on the derivative of of the input waveform
314  // We get this from our waveform algs too...
315  Waveform rawDerivativeVec;
316  Waveform derivativeVec;
317 
318  fWaveformTool->firstDerivative(waveform, rawDerivativeVec);
319  fWaveformTool->triangleSmooth(rawDerivativeVec,derivativeVec);
320 
321  // We keep track of the waveform and derivative:
322  TProfile* waveHist =
323  dir.make<TProfile>(Form("WWfm_%03zu_ctw%01zu-%01zu-%01zu-%05zu-%05zu",size_t(eventNum),cryo,tpc,plane,wireNum,size_t(roiStartTick)), "Waveform", waveform.size(), 0, waveform.size(), -500., 500.);
324  TProfile* derivHist =
325  dir.make<TProfile>(Form("WDer_%03zu_ctw%01zu-%01zu-%01zu-%05zu-%05zu",size_t(eventNum),cryo,tpc,plane,wireNum,size_t(roiStartTick)), "Derivative", waveform.size(), 0, waveform.size(), -500., 500.);
326  TProfile* candHitHist =
327  dir.make<TProfile>(Form("WPeak_%03zu_ctw%01zu-%01zu-%01zu-%05zu-%05zu",size_t(eventNum),cryo,tpc,plane,wireNum,size_t(roiStartTick)), "Peaks", waveform.size(), 0, waveform.size(), -500., 500.);
328  TProfile* edgeHitHist =
329  dir.make<TProfile>(Form("WEdge_%03zu_ctw%01zu-%01zu-%01zu-%05zu-%05zu",size_t(eventNum),cryo,tpc,plane,wireNum,size_t(roiStartTick)), "Edges", waveform.size(), 0, waveform.size(), -500., 500.);
330 
331  for(size_t idx = 0; idx < waveform.size(); idx++)
332  {
333  waveHist->Fill(idx, waveform.at(idx), 1.);
334  derivHist->Fill(idx, derivativeVec.at(idx), 1.);
335  }
336 
337  // Now find the hits
338  findHitCandidates(derivativeVec.begin(),derivativeVec.end(),0,plane,hitCandidateVec);
339 
340  // Reset the hit height from the input waveform
341  for(auto& hitCandidate : hitCandidateVec)
342  {
343  size_t centerIdx = hitCandidate.hitCenter;
344 
345  candHitHist->Fill(hitCandidate.maxTick, hitCandidate.maxDerivative, 1.);
346  candHitHist->Fill(hitCandidate.minTick, hitCandidate.minDerivative, 1.);
347  edgeHitHist->Fill(hitCandidate.startTick, 1.);
348  edgeHitHist->Fill(hitCandidate.stopTick, 1.);
349 
350  hitCandidate.hitHeight = waveform.at(centerIdx);
351  }
352 
353  // We make lots of vectors... erosion, dilation, average and difference
354  Waveform erosionVec;
355  Waveform dilationVec;
356  Waveform averageVec;
357  Waveform differenceVec;
358  Waveform closingVec;
359  Waveform openingVec;
360 
361  // Define histograms for this particular channel?
362  reco_tool::HistogramMap histogramMap;
363 
364  histogramMap[reco_tool::WAVEFORM] = waveHist;
365  histogramMap[reco_tool::EROSION] =
366  dir.make<TProfile>(Form("WEro_%03zu_ctw%01zu-%01zu-%01zu-%05zu-%05zu",size_t(eventNum),cryo,tpc,plane,wireNum,size_t(roiStartTick)), "Erosion", waveform.size(), 0, waveform.size(), -500., 500.);
367  histogramMap[reco_tool::DILATION] =
368  dir.make<TProfile>(Form("WDil_%03zu_ctw%01zu-%01zu-%01zu-%05zu-%05zu",size_t(eventNum),cryo,tpc,plane,wireNum,size_t(roiStartTick)), "Dilation", waveform.size(), 0, waveform.size(), -500., 500.);
369  histogramMap[reco_tool::AVERAGE] =
370  dir.make<TProfile>(Form("WAve_%03zu_ctw%01zu-%01zu-%01zu-%05zu-%05zu",size_t(eventNum),cryo,tpc,plane,wireNum,size_t(roiStartTick)), "Average", waveform.size(), 0, waveform.size(), -500., 500.);
371  histogramMap[reco_tool::DIFFERENCE] =
372  dir.make<TProfile>(Form("WDif_%03zu_ctw%01zu-%01zu-%01zu-%05zu-%05zu",size_t(eventNum),cryo,tpc,plane,wireNum,size_t(roiStartTick)), "Difference", waveform.size(), 0, waveform.size(), -500., 500.);
373  histogramMap[reco_tool::CLOSING] =
374  dir.make<TProfile>(Form("WClo_%03zu_ctw%01zu-%01zu-%01zu-%05zu-%05zu",size_t(eventNum),cryo,tpc,plane,wireNum,size_t(roiStartTick)), "Closing", waveform.size(), 0, waveform.size(), -500., 500.);
375  histogramMap[reco_tool::OPENING] =
376  dir.make<TProfile>(Form("WOpe_%03zu_ctw%01zu-%01zu-%01zu-%05zu-%05zu",size_t(eventNum),cryo,tpc,plane,wireNum,size_t(roiStartTick)), "Opening", waveform.size(), 0, waveform.size(), -500., 500.);
377  histogramMap[reco_tool::DOPENCLOSING] =
378  dir.make<TProfile>(Form("WDOC_%03zu_ctw%01zu-%01zu-%01zu-%05zu-%05zu",size_t(eventNum),cryo,tpc,plane,wireNum,size_t(roiStartTick)), "Difference", waveform.size(), 0, waveform.size(), -500., 500.);
379 
380  // Compute the morphological filter vectors
381  fWaveformTool->getErosionDilationAverageDifference(waveform, fStructuringElement, histogramMap, erosionVec, dilationVec, averageVec, differenceVec);
382 
383  fWaveformTool->getOpeningAndClosing(erosionVec, dilationVec, fStructuringElement, histogramMap, closingVec, openingVec);
384 
385  // Initialial hit finding
386  HitCandidateVec hitCandidateMorphVec;
387 
388  // Now find the hits
389 // findHitCandidates(erosionVec.begin(),erosionVec.end(),differenceVec.begin(),differenceVec.end(),0,plane,hitCandidateMorphVec);
390  findHitCandidates(openingVec.begin(),openingVec.end(),differenceVec.begin(),differenceVec.end(),0,plane,hitCandidateMorphVec);
391 
392  TProfile* candMorphHist =
393  dir.make<TProfile>(Form("MPeak_%03zu_ctw%01zu-%01zu-%01zu-%05zu-%05zu",size_t(eventNum),cryo,tpc,plane,wireNum,size_t(roiStartTick)), "Peaks", waveform.size(), 0, waveform.size(), -500., 500.);
394  TProfile* edgeMorphHist =
395  dir.make<TProfile>(Form("MEdge_%03zu_ctw%01zu-%01zu-%01zu-%05zu-%05zu",size_t(eventNum),cryo,tpc,plane,wireNum,size_t(roiStartTick)), "Edges", waveform.size(), 0, waveform.size(), -500., 500.);
396 
397  // Reset the hit height from the input waveform
398  for(auto& hitCandidate : hitCandidateMorphVec)
399  {
400  size_t centerIdx = hitCandidate.hitCenter;
401 
402  candMorphHist->Fill(hitCandidate.maxTick, hitCandidate.maxDerivative, 1.);
403  candMorphHist->Fill(hitCandidate.minTick, hitCandidate.minDerivative, 1.);
404  candMorphHist->Fill(hitCandidate.hitCenter, waveform.at(hitCandidate.hitCenter), 1.);
405  edgeMorphHist->Fill(hitCandidate.startTick, 1.);
406  edgeMorphHist->Fill(hitCandidate.stopTick, 1.);
407 
408  hitCandidate.hitHeight = waveform.at(centerIdx);
409  }
410  }
411  }
412 
413  return;
414 }
415 
416 void BasicWireAnalysis::findHitCandidates(Waveform::const_iterator startItr,
417  Waveform::const_iterator stopItr,
418  size_t roiStartTick,
419  size_t planeIdx,
420  HitCandidateVec& hitCandidateVec) const
421 {
422  // Search for candidate hits...
423  // The idea will be to find the largest deviation in the input derivative waveform as the starting point. Depending
424  // on if a maximum or minimum, we search forward or backward to find the minimum or maximum that our extremum
425  // corresponds to.
426  std::pair<Waveform::const_iterator, Waveform::const_iterator> minMaxPair = std::minmax_element(startItr, stopItr);
427 
428  Waveform::const_iterator maxItr = minMaxPair.second;
429  Waveform::const_iterator minItr = minMaxPair.first;
430 
431  // Use the larger of the two as the starting point and recover the nearest max or min
432  if (std::fabs(*maxItr) > std::fabs(*minItr)) minItr = findNearestMin(maxItr, stopItr);
433  else maxItr = findNearestMax(minItr,startItr);
434 
435  int deltaTicks = std::distance(maxItr,minItr);
436  float range = *maxItr - *minItr;
437 
438  fDeltaTicksHist.at(planeIdx)->Fill(deltaTicks, 1.);
439  fRangeHist.at(planeIdx)->Fill(range, 1.);
440 
441  // std::cout << "** max at tick: " << std::distance(startItr,maxItr) << ", val: " << *maxItr << ", min at tick: " << std::distance(startItr,minItr) << ", val: " << *minItr << ", delta: " << deltaTicks << ", range: " << range << std::endl;
442 
443  // At some point small rolling oscillations on the waveform need to be ignored...
444  if (deltaTicks >= fMinDeltaTicks.at(planeIdx) && range > fMinDeltaPeaks.at(planeIdx))
445  {
446  // Need to back up to find zero crossing, this will be the starting point of our
447  // candidate hit but also the endpoint of the pre sub-waveform we'll search next
448  Waveform::const_iterator newEndItr = findStartTick(maxItr, startItr);
449 
450  int startTick = std::distance(startItr,newEndItr);
451 
452  // Now need to go forward to again get close to zero, this will then be the end point
453  // of our candidate hit and the starting point for the post sub-waveform to search
454  Waveform::const_iterator newStartItr = findStopTick(minItr, stopItr);
455 
456  int stopTick = std::distance(startItr,newStartItr);
457 
458  // Find hits in the section of the waveform leading up to this candidate hit
459  if (startTick > 2) findHitCandidates(startItr,newEndItr,roiStartTick,planeIdx,hitCandidateVec);
460 
461  // Create a new hit candidate and store away
462  HitCandidate_t hitCandidate;
463 
464  Waveform::const_iterator peakItr = std::min_element(maxItr,minItr,[](const auto& left, const auto& right){return std::fabs(left) < std::fabs(right);});
465 
466  // Check balance
467  if (2 * std::distance(peakItr,minItr) < std::distance(maxItr,peakItr)) peakItr--;
468  else if (2 * std::distance(maxItr,peakItr) < std::distance(peakItr,minItr)) peakItr++;
469 
470  hitCandidate.startTick = roiStartTick + startTick;
471  hitCandidate.stopTick = roiStartTick + stopTick;
472  hitCandidate.maxTick = roiStartTick + std::distance(startItr,maxItr);
473  hitCandidate.minTick = roiStartTick + std::distance(startItr,minItr);
474  hitCandidate.maxDerivative = maxItr != stopItr ? *maxItr : 0.;
475  hitCandidate.minDerivative = minItr != stopItr ? *minItr : 0.;
476  hitCandidate.hitCenter = roiStartTick + std::distance(startItr,peakItr) + 0.5;
477  hitCandidate.hitSigma = 0.5 * float(hitCandidate.minTick - hitCandidate.maxTick);
478  hitCandidate.hitHeight = hitCandidate.hitSigma * (hitCandidate.maxDerivative - hitCandidate.minDerivative) / 1.2130;
479 
480  hitCandidateVec.push_back(hitCandidate);
481 
482  // Finally, search the section of the waveform following this candidate for more hits
483  if (std::distance(newStartItr,stopItr) > 2) findHitCandidates(newStartItr,stopItr,roiStartTick + stopTick,planeIdx,hitCandidateVec);
484  }
485 
486  return;
487 }
488 
489 void BasicWireAnalysis::findHitCandidates(Waveform::const_iterator eroStartItr,
490  Waveform::const_iterator eroStopItr,
491  Waveform::const_iterator diffStartItr,
492  Waveform::const_iterator diffStopItr,
493  size_t roiStartTick,
494  size_t planeIdx,
495  HitCandidateVec& hitCandidateVec) const
496 {
497  // Search for candidate hits...
498  // First task is to recover the maximum and minimum difference and reject waveform if no chance for an actual hit
499  std::pair<Waveform::const_iterator,Waveform::const_iterator> diffMinMaxPair = std::minmax_element(diffStartItr,diffStopItr);
500 
501  float deltaDiff = *diffMinMaxPair.second - *diffMinMaxPair.first;
502 
503  if (deltaDiff < 7) return;
504 
505  // The idea will be to find the largest deviation in the input derivative waveform as the starting point. Depending
506  // on if a maximum or minimum, we search forward or backward to find the minimum or maximum that our extremum
507  // corresponds to.
508  Waveform::const_iterator peakLeftItr = std::max_element(eroStartItr,eroStopItr);
509  Waveform::const_iterator peakRightItr = peakLeftItr;
510  Waveform::const_iterator diffLeftItr = diffStartItr;
511 
512  std::advance(diffLeftItr, std::distance(eroStartItr,peakLeftItr));
513 
514  Waveform::const_iterator diffRightItr = diffLeftItr;
515 
516  // Look for the case of a large waveform, handle differently
517  if (*diffLeftItr < *peakLeftItr)
518  {
519  // Find the position where the erosion vector is clearly less than the difference
520  // should this be a ratio test?
521  while(std::distance(eroStartItr,peakLeftItr) > 0 && *diffLeftItr - 1. < *peakLeftItr) {diffLeftItr--; peakLeftItr--;}
522  while(std::distance(peakRightItr,eroStopItr) > 0 && *diffRightItr - 1. < *peakRightItr) {diffRightItr++; peakRightItr++;}
523  }
524 
525  // Find maximum in difference to left of peak
526  Waveform::const_iterator leftMaxItr = diffLeftItr;
527  float leftMaxVal = *leftMaxItr;
528 
529  while(std::distance(diffStartItr,leftMaxItr) > 0 && *(leftMaxItr - 1) >= leftMaxVal) leftMaxVal = *(--leftMaxItr);
530 
531  // Find the rise point in the difference distribution
532  Waveform::const_iterator leftRiseItr = leftMaxItr;
533  float leftRiseVal = *leftRiseItr;
534 
535  while(std::distance(diffStartItr,leftRiseItr) >= 0 && *(leftRiseItr - 1) < leftRiseVal) leftRiseVal = *(--leftRiseItr);
536 
537  // Switch gears and look to the right of the peak
538  Waveform::const_iterator rightMaxItr = diffRightItr;
539  float rightMaxVal = *rightMaxItr;
540 
541  while(std::distance(rightMaxItr,diffStopItr) > 0 && *(rightMaxItr + 1) >= rightMaxVal) rightMaxVal = *(++rightMaxItr);
542 
543  // Find the rise point in the difference distribution
544  Waveform::const_iterator rightRiseItr = rightMaxItr;
545  float rightRiseVal = *rightRiseItr;
546 
547  while(std::distance(rightRiseItr,diffStopItr) > 0 && *(rightRiseItr + 1) < rightRiseVal) rightRiseVal = *(++rightRiseItr);
548 
549  // Find hits in the section of the waveform leading up to this candidate hit
550  if (std::distance(diffStartItr,leftRiseItr) > 4)
551  {
552  Waveform::const_iterator newEroStopItr = eroStartItr;
553 
554  std::advance(newEroStopItr,std::distance(diffStartItr,leftRiseItr));
555 
556  findHitCandidates(eroStartItr,newEroStopItr,diffStartItr,leftRiseItr,roiStartTick,planeIdx,hitCandidateVec);
557  }
558 
559  // Fill the data structure
560  HitCandidate_t hitCandidate;
561 
562  hitCandidate.startTick = roiStartTick + std::distance(diffStartItr,leftRiseItr);
563  hitCandidate.stopTick = roiStartTick + std::distance(diffStartItr,rightRiseItr);
564  hitCandidate.maxTick = roiStartTick + std::distance(diffStartItr,leftMaxItr);
565  hitCandidate.minTick = roiStartTick + std::distance(diffStartItr,rightMaxItr);
566  hitCandidate.maxDerivative = *leftMaxItr;
567  hitCandidate.minDerivative = *rightMaxItr;
568  hitCandidate.hitCenter = roiStartTick + (std::distance(eroStartItr,peakLeftItr) + std::distance(eroStartItr,peakRightItr) + 0.5)/2;
569  hitCandidate.hitSigma = 0.5 * float(hitCandidate.minTick - hitCandidate.maxTick);
570  hitCandidate.hitHeight = hitCandidate.hitSigma * (hitCandidate.maxDerivative - hitCandidate.minDerivative) / 1.2130;
571 
572  hitCandidateVec.push_back(hitCandidate);
573 
574  // Finally, search the section of the waveform following this candidate for more hits
575  if (std::distance(rightRiseItr,diffStopItr) > 4)
576  {
577  Waveform::const_iterator newEroStartItr = eroStartItr;
578  int newStartTick = std::distance(diffStartItr,rightRiseItr);
579 
580  std::advance(newEroStartItr, newStartTick);
581 
582  findHitCandidates(newEroStartItr,eroStopItr,rightRiseItr,diffStopItr,roiStartTick + newStartTick,planeIdx,hitCandidateVec);
583  }
584 
585  return;
586 }
587 
588 // Useful for normalizing histograms
589 void BasicWireAnalysis::endJob(int numEvents)
590 {
591  // A task to complete is to fit the average power displays with aim to develop a "good" filter function and
592  // get the signal to noise ratio
593 
594  return;
595 }
596 
597 BasicWireAnalysis::Waveform::const_iterator BasicWireAnalysis::findNearestMin(Waveform::const_iterator maxItr, Waveform::const_iterator stopItr) const
598 {
599  // reset the min iterator and search forward to find the nearest minimum
600  Waveform::const_iterator lastItr = maxItr;
601 
602  // The strategy is simple, loop forward over ticks until we find the point where the waveform is increasing again
603  while((lastItr + 1) != stopItr)
604  {
605  if (*(lastItr + 1) > *lastItr) break;
606 
607  lastItr++;
608  }
609 
610  // The minimum will be the last iterator value...
611  return lastItr;
612 }
613 
614 BasicWireAnalysis::Waveform::const_iterator BasicWireAnalysis::findNearestMax(Waveform::const_iterator minItr, Waveform::const_iterator startItr) const
615 {
616  // Set the internal loop variable...
617  Waveform::const_iterator lastItr = minItr;
618 
619  // One extra condition to watch for here, make sure we can actually back up!
620  if (std::distance(startItr,minItr) > 0)
621  {
622  // Similar to searching for a maximum, we loop backward over ticks looking for the waveform to start decreasing
623  while((lastItr - 1) != startItr)
624  {
625  if (*(lastItr - 1) < *lastItr) break;
626 
627  lastItr--;
628  }
629  }
630 
631  return lastItr;
632 }
633 
634 BasicWireAnalysis::Waveform::const_iterator BasicWireAnalysis::findStartTick(Waveform::const_iterator maxItr, Waveform::const_iterator startItr) const
635 {
636  Waveform::const_iterator lastItr = maxItr;
637 
638  // If we can't back up then there is nothing to do
639  if (std::distance(startItr,lastItr) > 0)
640  {
641  // In theory, we are starting at a maximum and want to find the "start" of the candidate peak
642  // Ideally we would look to search backward to the point where the (derivative) waveform crosses zero again.
643  // However, the complication is that we need to watch for the case where two peaks are merged together and
644  // we might run through another peak before crossing zero...
645  // So... loop until we hit the startItr...
646  Waveform::const_iterator loopItr = lastItr - 1;
647 
648  while(loopItr != startItr)
649  {
650  // Ideal world case, we cross zero... but we might encounter a minimum... or an inflection point
651  if (*loopItr < 0. || !(*loopItr < *lastItr)) break;
652 
653  lastItr = loopItr--;
654  }
655  }
656  else lastItr = startItr;
657 
658  return lastItr;
659 }
660 
661 BasicWireAnalysis::Waveform::const_iterator BasicWireAnalysis::findStopTick(Waveform::const_iterator minItr, Waveform::const_iterator stopItr) const
662 {
663  Waveform::const_iterator lastItr = minItr;
664 
665  // If we can't go forward then there is really nothing to do
666  if (std::distance(minItr,stopItr) > 1)
667  {
668  // Pretty much the same strategy as for finding the start tick...
669  Waveform::const_iterator loopItr = lastItr + 1;
670 
671  while(loopItr != stopItr)
672  {
673  // Ideal case that we have crossed zero coming from a minimum... but watch for a maximum as well
674  if (*loopItr > 0. || !(*loopItr > *lastItr)) break;
675 
676  lastItr = loopItr++;
677  }
678  }
679 
680  return lastItr;
681 }
682 
683 DEFINE_ART_CLASS_TOOL(BasicWireAnalysis)
684 }
Utilities related to art service access.
BasicWireAnalysis(fhicl::ParameterSet const &pset)
Constructor.
std::vector< HitCandidate_t > HitCandidateVec
T::provider_type const * providerFrom()
Returns a constant pointer to the provider of specified service.
Definition: ServiceUtil.h:77
This is the interface class for tools/algorithms that perform various operations on waveforms...
walls no right
Definition: selectors.fcl:105
std::vector< geo::WireID > ChannelToWire(raw::ChannelID_t const channel) const
Returns a list of wires connected to the specified TPC channel.
const range_list_t & get_ranges() const
Returns the internal list of non-void ranges.
Waveform::const_iterator findNearestMax(Waveform::const_iterator, Waveform::const_iterator) const
void configure(fhicl::ParameterSet const &pset) override
icarusutil::SignalShapingICARUSService & fSignalServices
The signal shaping service.
int TDCtick_t
Type representing a TDC tick.
Definition: RawTypes.h:25
void initializeHists(art::ServiceHandle< art::TFileService > &, const std::string &) override
Interface for initializing the histograms to be filled.
unsigned int Nplanes(unsigned int tpc=0, unsigned int cstat=0) const
Returns the total number of wire planes in the specified TPC.
double distance(geo::Point_t const &point, CathodeDesc_t const &cathode)
Returns the distance of a point from the cathode.
raw::ChannelID_t Channel() const
Returns the ID of the channel (or InvalidChannelID)
Definition: Wire.h:231
std::unique_ptr< reco_tool::IWaveformTool > fWaveformTool
std::vector< art::Ptr< recob::Wire >> WirePtrVec
Interface for filling histograms.
const lariov::DetPedestalProvider & fPedestalRetrievalAlg
Keep track of an instance to the pedestal retrieval alg.
walls no left
Definition: selectors.fcl:105
Description of geometry of one entire detector.
void endJob(int numEvents) override
Interface for method to executve at the end of run processing.
tuple dir
Definition: dropbox.py:28
std::map< int, TProfile * > HistogramMap
Definition: IWaveformTool.h:41
Waveform::const_iterator findNearestMin(Waveform::const_iterator, Waveform::const_iterator) const
struct HitCandidate{size_t startTick HitCandidate_t
Waveform::const_iterator findStopTick(Waveform::const_iterator, Waveform::const_iterator) const
std::string to_string(WindowPattern const &pattern)
std::map< raw::ChannelID_t, const sim::SimChannel * > SimChannelMap
void fillHistograms(const IWireHistogramTool::WirePtrVec &, const IWireHistogramTool::SimChannelMap &, int) const override
Interface for filling histograms.
Class holding the regions of interest of signal from a channel.
Definition: Wire.h:118
const geo::GeometryCore & fGeometry
pointer to Geometry service
art::ServiceHandle< art::TFileService > tfs
void findHitCandidates(Waveform::const_iterator, Waveform::const_iterator, size_t, size_t, HitCandidateVec &) const
unsigned int ChannelID_t
Type representing the ID of a readout channel.
Definition: RawTypes.h:28
art framework interface to geometry description
Waveform::const_iterator findStartTick(Waveform::const_iterator, Waveform::const_iterator) const