LArSoft  v06_85_00
Liquid Argon Software toolkit - http://larsoft.org/
CandHitDerivative_tool.cc
Go to the documentation of this file.
1 
8 
13 #include "cetlib_except/exception.h"
16 
17 #include "TH1F.h"
18 #include "TProfile.h"
19 
20 #include <cmath>
21 #include <fstream>
22 
23 namespace reco_tool
24 {
25 
27 {
28 public:
29  explicit CandHitDerivative(const fhicl::ParameterSet& pset);
30 
32 
33  void configure(const fhicl::ParameterSet& pset) override;
34 
35  void findHitCandidates(const Waveform&,
36  size_t,
37  size_t,
38  size_t,
39  HitCandidateVec&) const override;
40 
41  void MergeHitCandidates(const Waveform&,
42  const HitCandidateVec&,
43  MergeHitCandidateVec&) const override;
44 
45 private:
46  // Internal functions
49  size_t,
50  int,
51  float,
52  HitCandidateVec&) const;
53 
54  // Finding the nearest maximum/minimum from current point
57 
58  // handle finding the "start" and "stop" of a candidate hit
61 
62  // some control variables
63  size_t fPlane; //< Identifies the plane this tool is meant to operate on
64  int fMinDeltaTicks; //< minimum ticks from max to min to consider
65  int fMaxDeltaTicks; //< maximum ticks from max to min to consider
66  float fMinDeltaPeaks; //< minimum maximum to minimum peak distance
67  float fMinHitHeight; //< Drop candidate hits with height less than this
68  size_t fNumInterveningTicks; //< Number ticks between candidate hits to merge
69  bool fOutputHistograms; //< If true will generate a very large file of hists!
70 
72 
73  // Global histograms
77 
78  mutable std::map<size_t,int> fChannelCntMap;
79 
80  // Member variables from the fhicl file
81  std::unique_ptr<reco_tool::IWaveformTool> fWaveformTool;
82 
83  const geo::GeometryCore* fGeometry = lar::providerFrom<geo::Geometry>();
84 };
85 
86 //----------------------------------------------------------------------
87 // Constructor.
89 {
90  configure(pset);
91 }
92 
94 {
95 }
96 
98 {
99  // Recover our parameters
100  fPlane = pset.get< size_t >("Plane", 0);
101  fMinDeltaTicks = pset.get< int >("MinDeltaTicks", 0);
102  fMaxDeltaTicks = pset.get< int >("MaxDeltaTicks", 30);
103  fMinDeltaPeaks = pset.get< float >("MinDeltaPeaks", 0.025);
104  fMinHitHeight = pset.get< float >("MinHitHeight", 2.0);
105  fNumInterveningTicks = pset.get< size_t >("NumInterveningTicks", 6);
106  fOutputHistograms = pset.get< bool >("OutputHistograms", false);
107 
108  // Recover the baseline tool
109  fWaveformTool = art::make_tool<reco_tool::IWaveformTool> (pset.get<fhicl::ParameterSet>("WaveformAlgs"));
110 
111  // If asked, define the global histograms
112  if (fOutputHistograms)
113  {
114  // Access ART's TFileService, which will handle creating and writing
115  // histograms and n-tuples for us.
117 
118  fHistDirectory = tfs.get();
119 
120  // Make a directory for these histograms
121  art::TFileDirectory dir = fHistDirectory->mkdir(Form("HitPlane_%1zu",fPlane));
122 
123  fDStopStartHist = dir.make<TH1F>(Form("DStopStart_%1zu", fPlane), ";Delta Stop/Start;", 200, 0., 200.);
124  fDMaxTickMinTickHist = dir.make<TH1F>(Form("DMaxTMinT_%1zu", fPlane), ";Delta Max/Min Tick;", 200, 0., 200.);
125  fDMaxDerivMinDerivHist = dir.make<TH1F>(Form("DMaxDMinD_%1zu", fPlane), ";Delta Max/Min Deriv;", 200, 0., 200.);
126  }
127 
128  return;
129 }
130 
132  size_t roiStartTick,
133  size_t channel,
134  size_t eventCount,
135  HitCandidateVec& hitCandidateVec) const
136 {
137  // In this case we want to find hit candidates based on the derivative of of the input waveform
138  // We get this from our waveform algs too...
139  Waveform rawDerivativeVec;
140  Waveform derivativeVec;
141 
142  fWaveformTool->firstDerivative(waveform, rawDerivativeVec);
143  fWaveformTool->triangleSmooth(rawDerivativeVec, derivativeVec);
144 
145  std::vector<geo::WireID> wids = fGeometry->ChannelToWire(channel);
146  size_t plane = wids[0].Plane;
147  size_t cryo = wids[0].Cryostat;
148  size_t tpc = wids[0].TPC;
149  size_t wire = wids[0].Wire;
150 
151  // Just make sure the input candidate hit vector has been cleared
152  hitCandidateVec.clear();
153 
154  // Now find the hits
155  findHitCandidates(derivativeVec.begin(),derivativeVec.end(),roiStartTick,fMinDeltaTicks,fMinDeltaPeaks,hitCandidateVec);
156 
157  if (hitCandidateVec.empty())
158  {
159  if (plane == 0)
160  {
161  std::cout << "** C/T/P: " << cryo << "/" << tpc << "/" << plane << ", wire: " << wire << " has not hits with input size: " << waveform.size() << std::endl;
162  }
163  }
164 
165  // Reset the hit height from the input waveform
166  for(auto& hitCandidate : hitCandidateVec)
167  {
168  size_t centerIdx = hitCandidate.hitCenter;
169 
170  hitCandidate.hitHeight = waveform.at(centerIdx);
171  }
172 
173  // Keep track of histograms if requested
174  if (fOutputHistograms)
175  {
176  // Recover the details...
177 // std::vector<geo::WireID> wids = fGeometry->ChannelToWire(channel);
178 // size_t plane = wids[0].Plane;
179 // size_t cryo = wids[0].Cryostat;
180 // size_t tpc = wids[0].TPC;
181 // size_t wire = wids[0].Wire;
182 
183  size_t channelCnt = fChannelCntMap[channel]++;
184 
185  // Make a directory for these histograms
186  art::TFileDirectory dir = fHistDirectory->mkdir(Form("HitPlane_%1zu/ev%04zu/c%1zut%1zuwire_%05zu",plane,eventCount,cryo,tpc,wire));
187 
188  size_t waveformSize = waveform.size();
189  int waveStart = roiStartTick;
190  int waveStop = waveStart + waveformSize;
191 
192  TProfile* waveHist = dir.make<TProfile>(Form("HWfm_%03zu_ctw%01zu-%01zu-%01zu-%05zu",channelCnt,cryo,tpc,plane,wire), "Waveform", waveformSize, waveStart, waveStop, -500., 500.);
193  TProfile* derivHist = dir.make<TProfile>(Form("HDer_%03zu_ctw%01zu-%01zu-%01zu-%05zu",channelCnt,cryo,tpc,plane,wire), "Derivative", waveformSize, waveStart, waveStop, -500., 500.);
194  TProfile* candHitHist = dir.make<TProfile>(Form("HCan_%03zu_ctw%01zu-%01zu-%01zu-%05zu",channelCnt,cryo,tpc,plane,wire), "Cand Hits", waveformSize, waveStart, waveStop, -500., 500.);
195  TProfile* maxDerivHist = dir.make<TProfile>(Form("HMax_%03zu_ctw%01zu-%01zu-%01zu-%05zu",channelCnt,cryo,tpc,plane,wire), "Maxima", waveformSize, waveStart, waveStop, -500., 500.);
196 
197  // Fill wave/derivative
198  for(size_t idx = 0; idx < waveform.size(); idx++)
199  {
200  waveHist->Fill(roiStartTick + idx, waveform.at(idx));
201  derivHist->Fill(roiStartTick + idx, derivativeVec.at(idx));
202  }
203 
204  // Fill hits
205  for(const auto& hitCandidate : hitCandidateVec)
206  {
207  candHitHist->Fill(hitCandidate.hitCenter, hitCandidate.hitHeight);
208  maxDerivHist->Fill(hitCandidate.maxTick, hitCandidate.maxDerivative);
209  maxDerivHist->Fill(hitCandidate.minTick, hitCandidate.minDerivative);
210 
211  fDStopStartHist->Fill(hitCandidate.stopTick - hitCandidate.startTick, 1.);
212  fDMaxTickMinTickHist->Fill(hitCandidate.minTick - hitCandidate.maxTick, 1.);
213  fDMaxDerivMinDerivHist->Fill(hitCandidate.maxDerivative - hitCandidate.minDerivative, 1.);
214  }
215  }
216 
217  return;
218 }
219 
221  Waveform::const_iterator stopItr,
222  size_t roiStartTick,
223  int dTicksThreshold,
224  float dPeakThreshold,
225  HitCandidateVec& hitCandidateVec) const
226 {
227  // Search for candidate hits...
228  // The idea will be to find the largest deviation in the input derivative waveform as the starting point. Depending
229  // on if a maximum or minimum, we search forward or backward to find the minimum or maximum that our extremum
230  // corresponds to.
231  std::pair<Waveform::const_iterator, Waveform::const_iterator> minMaxPair = std::minmax_element(startItr, stopItr);
232 
233  Waveform::const_iterator maxItr = minMaxPair.second;
234  Waveform::const_iterator minItr = minMaxPair.first;
235 
236  // Use the larger of the two as the starting point and recover the nearest max or min
237  if (std::fabs(*maxItr) > std::fabs(*minItr)) minItr = findNearestMin(maxItr, stopItr);
238  else maxItr = findNearestMax(minItr,startItr);
239 
240  int deltaTicks = std::distance(maxItr,minItr);
241  float range = *maxItr - *minItr;
242 
243  // At some point small rolling oscillations on the waveform need to be ignored...
244  if (deltaTicks >= dTicksThreshold && range > dPeakThreshold)
245  {
246  // Need to back up to find zero crossing, this will be the starting point of our
247  // candidate hit but also the endpoint of the pre sub-waveform we'll search next
248  Waveform::const_iterator newEndItr = findStartTick(maxItr, startItr);
249 
250  int startTick = std::distance(startItr,newEndItr);
251 
252  // Now need to go forward to again get close to zero, this will then be the end point
253  // of our candidate hit and the starting point for the post sub-waveform to search
254  Waveform::const_iterator newStartItr = findStopTick(minItr, stopItr);
255 
256  int stopTick = std::distance(startItr,newStartItr);
257 
258  // Find hits in the section of the waveform leading up to this candidate hit
259  if (startTick > dTicksThreshold)
260  {
261  // Special handling for merged hits
262  if (*(newEndItr-1) > 0.) {dTicksThreshold = 2; dPeakThreshold = 0.; }
263  else {dTicksThreshold = fMinDeltaTicks; dPeakThreshold = fMinDeltaPeaks;}
264 
265  findHitCandidates(startItr,newEndItr+1,roiStartTick,dTicksThreshold,dPeakThreshold,hitCandidateVec);
266  }
267 
268  // Create a new hit candidate and store away
269  HitCandidate_t hitCandidate;
270 
271  Waveform::const_iterator peakItr = std::min_element(maxItr,minItr,[](const auto& left, const auto& right){return std::fabs(left) < std::fabs(right);});
272 
273  // Check balance
274  if (2 * std::distance(peakItr,minItr) < std::distance(maxItr,peakItr)) peakItr--;
275  else if (2 * std::distance(maxItr,peakItr) < std::distance(peakItr,minItr)) peakItr++;
276 
277  hitCandidate.startTick = roiStartTick + startTick;
278  hitCandidate.stopTick = roiStartTick + stopTick;
279  hitCandidate.maxTick = roiStartTick + std::distance(startItr,maxItr);
280  hitCandidate.minTick = roiStartTick + std::distance(startItr,minItr);
281  hitCandidate.maxDerivative = maxItr != stopItr ? *maxItr : 0.;
282  hitCandidate.minDerivative = minItr != stopItr ? *minItr : 0.;
283  hitCandidate.hitCenter = roiStartTick + std::distance(startItr,peakItr) + 0.5;
284  hitCandidate.hitSigma = 0.5 * float(hitCandidate.minTick - hitCandidate.maxTick);
285  hitCandidate.hitHeight = hitCandidate.hitSigma * (hitCandidate.maxDerivative - hitCandidate.minDerivative) / 1.2130;
286 
287  hitCandidateVec.push_back(hitCandidate);
288 
289  // Finally, search the section of the waveform following this candidate for more hits
290  if (std::distance(newStartItr,stopItr) > dTicksThreshold)
291  {
292  // Special handling for merged hits
293  if (*(newStartItr+1) < 0.) {dTicksThreshold = 2; dPeakThreshold = 0.; }
294  else {dTicksThreshold = fMinDeltaTicks; dPeakThreshold = fMinDeltaPeaks;}
295 
296  findHitCandidates(newStartItr,stopItr,roiStartTick + stopTick,dTicksThreshold,dPeakThreshold,hitCandidateVec);
297  }
298  }
299 
300  return;
301 }
302 
304  const HitCandidateVec& hitCandidateVec,
305  MergeHitCandidateVec& mergedHitsVec) const
306 {
307  // If nothing on the input end then nothing to do
308  if (hitCandidateVec.empty()) return;
309 
310  // The idea is to group hits that "touch" so they can be part of common fit, those that
311  // don't "touch" are fit independently. So here we build the output vector to achieve that
312  // Get a container for the hits...
313  HitCandidateVec groupedHitVec;
314 
315  // Initialize the end of the last hit which we'll set to the first input hit's stop
316  size_t lastStopTick = hitCandidateVec.front().stopTick;
317 
318  // Step through the input hit candidates and group them by proximity
319  for(const auto& hitCandidate : hitCandidateVec)
320  {
321  // Small pulse height hits should not be considered?
322  if (hitCandidate.hitHeight > fMinHitHeight)
323  {
324  // Check condition that we have a new grouping
325  if (hitCandidate.startTick > lastStopTick + fNumInterveningTicks && !groupedHitVec.empty())
326  {
327  mergedHitsVec.emplace_back(groupedHitVec);
328 
329  groupedHitVec.clear();
330  }
331 
332  // Add the current hit to the current group
333  groupedHitVec.emplace_back(hitCandidate);
334 
335  lastStopTick = hitCandidate.stopTick;
336  }
337  }
338 
339  // Check end condition
340  if (!groupedHitVec.empty()) mergedHitsVec.emplace_back(groupedHitVec);
341 
342  return;
343 }
344 
346 {
347  // reset the min iterator and search forward to find the nearest minimum
348  Waveform::const_iterator lastItr = maxItr;
349 
350  // The strategy is simple...
351  // We are at a maximum so we search forward until we find the lowest negative point
352  while((lastItr + 1) != stopItr)
353  {
354  if (*(lastItr + 1) > *lastItr) break;
355 
356  lastItr++;
357  }
358 
359  // The minimum will be the last iterator value...
360  return lastItr;
361 }
362 
364 {
365  // Set the internal loop variable...
366  Waveform::const_iterator lastItr = minItr;
367 
368  // One extra condition to watch for here, make sure we can actually back up!
369  if (std::distance(startItr,minItr) > 0)
370  {
371  // Similar to searching for a maximum, we loop backward over ticks looking for the waveform to start decreasing
372  while((lastItr - 1) != startItr)
373  {
374  if (*(lastItr - 1) < *lastItr) break;
375 
376  lastItr--;
377  }
378  }
379 
380  return lastItr;
381 }
382 
384 {
385  Waveform::const_iterator lastItr = maxItr;
386 
387  // If we can't back up then there is nothing to do
388  if (std::distance(startItr,lastItr) > 0)
389  {
390  // In theory, we are starting at a maximum and want to find the "start" of the candidate peak
391  // Ideally we would look to search backward to the point where the (derivative) waveform crosses zero again.
392  // However, the complication is that we need to watch for the case where two peaks are merged together and
393  // we might run through another peak before crossing zero...
394  // So... loop until we hit the startItr...
395  Waveform::const_iterator loopItr = lastItr - 1;
396 
397  while(loopItr != startItr)
398  {
399  // Ideal world case, we cross zero... but we might encounter a minimum... or an inflection point
400  if (*loopItr < 0. || !(*loopItr < *lastItr)) break;
401 
402  lastItr = loopItr--;
403  }
404  }
405  else lastItr = startItr;
406 
407  return lastItr;
408 }
409 
411 {
412  Waveform::const_iterator lastItr = minItr;
413 
414  // If we can't go forward then there is really nothing to do
415  if (std::distance(minItr,stopItr) > 1)
416  {
417  // Pretty much the same strategy as for finding the start tick...
418  Waveform::const_iterator loopItr = lastItr + 1;
419 
420  while(loopItr != stopItr)
421  {
422  // Ideal case that we have crossed zero coming from a minimum... but watch for a maximum as well
423  if (*loopItr > 0. || !(*loopItr > *lastItr)) break;
424 
425  lastItr = loopItr++;
426  }
427  }
428 
429  return lastItr;
430 }
431 
433 }
void MergeHitCandidates(const Waveform &, const HitCandidateVec &, MergeHitCandidateVec &) const override
constexpr auto const & right(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:112
#define DEFINE_ART_CLASS_TOOL(tool)
Definition: ToolMacros.h:45
T * get() const
Definition: ServiceHandle.h:71
This is the interface class for tools/algorithms that perform various operations on waveforms...
Waveform::const_iterator findNearestMax(Waveform::const_iterator, Waveform::const_iterator) const
const geo::GeometryCore * fGeometry
std::vector< HitCandidate_t > HitCandidateVec
std::vector< geo::WireID > ChannelToWire(raw::ChannelID_t const channel) const
Returns a list of wires connected to the specified TPC channel.
struct HitCandidate{size_t startTick HitCandidate_t
void findHitCandidates(const Waveform &, size_t, size_t, size_t, HitCandidateVec &) const override
void configure(const fhicl::ParameterSet &pset) override
TFileDirectory mkdir(std::string const &dir, std::string const &descr="")
intermediate_table::const_iterator const_iterator
T get(std::string const &key) const
Definition: ParameterSet.h:231
std::unique_ptr< reco_tool::IWaveformTool > fWaveformTool
Description of geometry of one entire detector.
std::map< size_t, int > fChannelCntMap
Waveform::const_iterator findStopTick(Waveform::const_iterator, Waveform::const_iterator) const
constexpr auto const & left(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:104
T * make(ARGS...args) const
TDirectory * dir
Definition: macro.C:5
CandHitDerivative(const fhicl::ParameterSet &pset)
This provides an interface for tools which are tasked with finding candidate hits on input waveforms...
Waveform::const_iterator findStartTick(Waveform::const_iterator, Waveform::const_iterator) const
art::TFileDirectory * fHistDirectory
std::vector< HitCandidateVec > MergeHitCandidateVec
art framework interface to geometry description
Waveform::const_iterator findNearestMin(Waveform::const_iterator, Waveform::const_iterator) const