LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
CCHitFinderAlg.cxx
Go to the documentation of this file.
1 
13 // class header
15 
16 // C/C++ standard libraries
17 #include <algorithm> // std::sort(), std::copy()
18 #include <array>
19 #include <cmath> // std::sqrt(), std::abs()
20 #include <iomanip>
21 #include <iostream>
22 #include <sstream>
23 #include <utility> // std::pair<>, std::make_pair()
24 
25 // framework libraries
27 
28 // LArSoft Includes
30 #include "lardata/Utilities/SimpleFits.h" // lar::util::GaussianFit<>
31 #include "larevt/CalibrationDBI/Interface/ChannelStatusProvider.h"
32 #include "larevt/CalibrationDBI/Interface/ChannelStatusService.h"
33 
34 // ROOT Includes
35 #include "TF1.h"
36 #include "TGraph.h"
37 
38 namespace hit {
39 
40  constexpr unsigned int CCHitFinderAlg::MaxGaussians; // definition
41 
42  //------------------------------------------------------------------------------
44  : FitCache(
45  new GausFitCache // run-time, on demand TFormula cache
46  // CompiledGausFitCache<MaxGaussians> // precompiled Gaussian set
47  // CompiledTruncatedGausFitCache<MaxGaussians, 4> // precompiled truncated Gaussian set
48  // CompiledTruncatedGausFitCache<MaxGaussians, 5> // precompiled truncated Gaussian set
49  ("GausFitCache_CCHitFinderAlg"))
50  {
51  this->reconfigure(pset);
52  }
53 
55  {
56  if (pset.has_key("MinSigInd"))
58  << "CCHitFinderAlg: Using no-longer-valid fcl input: MinSigInd, MinSigCol, etc";
59 
60  fMinPeak = pset.get<std::vector<float>>("MinPeak");
61  fMinRMS = pset.get<std::vector<float>>("MinRMS");
62  fMaxBumps = pset.get<unsigned short>("MaxBumps");
63  fMaxXtraHits = pset.get<unsigned short>("MaxXtraHits");
64  fChiSplit = pset.get<float>("ChiSplit");
65  fChiNorms = pset.get<std::vector<float>>("ChiNorms");
66  fUseFastFit = pset.get<bool>("UseFastFit", false);
67  fUseChannelFilter = pset.get<bool>("UseChannelFilter", true);
68  fStudyHits = pset.get<bool>("StudyHits", false);
69  // The following variables are only used in StudyHits mode
70  fUWireRange = pset.get<std::vector<short>>("UWireRange");
71  fUTickRange = pset.get<std::vector<short>>("UTickRange");
72  fVWireRange = pset.get<std::vector<short>>("VWireRange");
73  fVTickRange = pset.get<std::vector<short>>("VTickRange");
74  fWWireRange = pset.get<std::vector<short>>("WWireRange");
75  fWTickRange = pset.get<std::vector<short>>("WTickRange");
76 
77  if (fMinPeak.size() != fMinRMS.size()) {
78  mf::LogError("CCTF") << "MinPeak size != MinRMS size";
79  return;
80  }
81 
82  if (fMaxBumps > MaxGaussians) {
83  // MF_LOG_WARNING will point the user to this line of code.
84  // Any value of MaxGaussians can be used.
85  // That value is defined in the header file.
86  MF_LOG_WARNING("CCHitFinderAlg")
87  << "CCHitFinder algorithm is currently hard-coded to support at most " << MaxGaussians
88  << " bumps per region of interest, but " << fMaxBumps << " have been requested.\n"
89  << "We are forcing the parameter to " << MaxGaussians
90  << ". If this is not acceptable, increase CCHitFinderAlg::MaxGaussians"
91  << " value and recompile.";
92  fMaxBumps = MaxGaussians;
93  } // if too many gaussians
94 
97 
98  // sanity check for StudyHits mode
99  if (fStudyHits) {
100  if (fUWireRange.size() != 2 || fUTickRange.size() != 2 || fVWireRange.size() != 2 ||
101  fVTickRange.size() != 2 || fWWireRange.size() != 2 || fWTickRange.size() != 2) {
102  mf::LogError("CCHF") << "Invalid vector size for StudyHits. Must be 2";
103  return;
104  }
105  } // fStudyHits
106  }
107 
108  //------------------------------------------------------------------------------
110  geo::WireID wid,
111  geo::Geometry const& geom)
112  : wire(w), wireID(wid), sigType(geom.SignalType(w->Channel()))
113  {}
114 
115  //------------------------------------------------------------------------------
116  void CCHitFinderAlg::RunCCHitFinder(std::vector<recob::Wire> const& Wires)
117  {
118 
119  allhits.clear();
120 
121  unsigned short maxticks = 1000;
122  float* ticks = new float[maxticks];
123  // define the ticks array used for fitting
124  for (unsigned short ii = 0; ii < maxticks; ++ii) {
125  ticks[ii] = ii;
126  }
127  float* signl = new float[maxticks];
128  float adcsum = 0;
129  // initialize the vectors for the hit study
130  if (fStudyHits) StudyHits(0);
131  bool first;
132 
133  // prt = false;
134  lariov::ChannelStatusProvider const& channelStatus =
136 
137  for (size_t wireIter = 0; wireIter < Wires.size(); wireIter++) {
138 
139  recob::Wire const& theWire = Wires[wireIter];
140  theChannel = theWire.Channel();
141  // ignore bad channels
142  if (channelStatus.IsBad(theChannel)) continue;
143  /*
144  geo::SigType_t SigType = geom->SignalType(theChannel);
145  minSig = 0.;
146  minRMS = 0.;
147  if(SigType == geo::kInduction){
148  minSig = fMinSigInd;
149  minRMS = fMinRMSInd;
150  }//<-- End if Induction Plane
151  else if(SigType == geo::kCollection){
152  minSig = fMinSigCol;
153  minRMS = fMinRMSCol;
154  }//<-- End if Collection Plane
155 */
156 
157  std::vector<geo::WireID> wids = geom->ChannelToWire(theChannel);
158  thePlane = wids[0].Plane;
159  if (thePlane > fMinPeak.size() - 1) {
160  mf::LogError("CCHF") << "MinPeak vector too small for plane " << thePlane;
161  return;
162  }
163  theWireNum = wids[0].Wire;
164  HitChannelInfo_t WireInfo(&theWire, wids[0], *geom);
165 
166  // minimum number of time samples
167  unsigned short minSamples = 2 * fMinRMS[thePlane];
168 
169  // factor used to normalize the chi/dof fits for each plane
171 
172  // edit this line to debug hit fitting on a particular plane/wire
173  // prt = (thePlane == 1 && theWireNum == 839);
174  std::vector<float> signal(theWire.Signal());
175 
176  unsigned short nabove = 0;
177  unsigned short tstart = 0;
178  unsigned short maxtime = signal.size() - 2;
179  // find the min time when the signal is below threshold
180  unsigned short mintime = 3;
181  for (unsigned short time = 3; time < maxtime; ++time) {
182  if (signal[time] < fMinPeak[thePlane]) {
183  mintime = time;
184  break;
185  }
186  }
187  for (unsigned short time = mintime; time < maxtime; ++time) {
188  if (signal[time] > fMinPeak[thePlane]) {
189  if (nabove == 0) tstart = time;
190  ++nabove;
191  }
192  else {
193  // check for a wide enough signal above threshold
194  if (nabove > minSamples) {
195  // skip this wire if the RAT is too long
196  if (nabove > maxticks)
197  mf::LogError("CCHitFinder")
198  << "Long RAT " << nabove << " " << maxticks << " No signal on wire " << theWireNum
199  << " after time " << time;
200  if (nabove > maxticks) break;
201  unsigned short npt = 0;
202  // look for bumps to inform the fit
203  bumps.clear();
204  adcsum = 0;
205  for (unsigned short ii = tstart; ii < time; ++ii) {
206  signl[npt] = signal[ii];
207  adcsum += signl[npt];
208  if (signal[ii] > signal[ii - 1] && signal[ii - 1] > signal[ii - 2] &&
209  signal[ii] > signal[ii + 1] && signal[ii + 1] > signal[ii + 2])
210  bumps.push_back(npt);
211  // if(prt) mf::LogVerbatim("CCHitFinder")<<"signl "<<ii<<" "<<signl[npt];
212  ++npt;
213  }
214  // decide if this RAT should be studied
215  if (fStudyHits) StudyHits(1, npt, signl, tstart);
216  // just make a crude hit if too many bumps
217  if (bumps.size() > fMaxBumps) {
218  MakeCrudeHit(npt, ticks, signl);
219  StoreHits(tstart, npt, WireInfo, adcsum);
220  nabove = 0;
221  continue;
222  }
223  // start looking for hits with the found bumps
224  unsigned short nHitsFit = bumps.size();
225  unsigned short nfit = 0;
226  chidof = 0.;
227  dof = -1;
228  bool HitStored = false;
229  unsigned short nMaxFit = bumps.size() + fMaxXtraHits;
230  // only used in StudyHits mode
231  first = true;
232  while (nHitsFit <= nMaxFit) {
233 
234  FitNG(nHitsFit, npt, ticks, signl);
235  if (fStudyHits && first && SelRAT) {
236  first = false;
237  StudyHits(2, npt, signl, tstart);
238  }
239  // good chisq so store it
240  if (chidof < fChiSplit) {
241  StoreHits(tstart, npt, WireInfo, adcsum);
242  HitStored = true;
243  break;
244  }
245  // the previous fit was better, so revert to it and
246  // store it
247  ++nHitsFit;
248  ++nfit;
249  } // nHitsFit < fMaxXtraHits
250  if (!HitStored && npt < maxticks) {
251  // failed all fitting. Make a crude hit
252  MakeCrudeHit(npt, ticks, signl);
253  StoreHits(tstart, npt, WireInfo, adcsum);
254  }
255  else if (nHitsFit > 0)
256  FinalFitStats.AddMultiGaus(nHitsFit);
257  } // nabove > minSamples
258  nabove = 0;
259  } // signal < fMinPeak
260  } // time
261  } // wireIter
262 
263  // print out
264  if (fStudyHits) StudyHits(4);
265 
266  delete[] ticks;
267  delete[] signl;
268 
269  } //RunCCHitFinder
270 
272  bool CCHitFinderAlg::FastGaussianFit(unsigned short npt,
273  float const* ticks,
274  float const* signl,
275  std::array<double, 3>& params,
276  std::array<double, 3>& paramerrors,
277  float& chidof)
278  {
279  // parameters: amplitude, mean, sigma
280 
281  lar::util::GaussianFit<double> fitter; // probably "double" is overdoing
282 
283  // apply a time shift so that the center of the time interval is 0
284  const float time_shift = (ticks[npt - 1] + ticks[0]) / 2.F;
285 
286  // fill the input data, no uncertainty
287  for (size_t i = 0; i < npt; ++i) {
288  if (signl[i] <= 0) {
289  MF_LOG_DEBUG("CCHitFinderAlg")
290  << "Non-positive charge encountered. Backing up to ROOT fit.";
291  return false;
292  }
293  // we could freely add a Poisson uncertainty (as third parameter)
294  fitter.add(ticks[i] - time_shift, signl[i]);
295  } // for
296 
297  // we might have found that we don't want the fast fit after all...
298  if (!fitter.FillResults(params, paramerrors)) {
299  // something went wrong...
300  MF_LOG_DEBUG("CCHitFinderAlg") << "Fast Gaussian fit failed.";
301  return false;
302  }
303 
304  // note that this is not the full chi^2, but it is the chi^2 of the
305  // parabolic fit underlying the Gaussian one
306  const double chi2 = fitter.ChiSquare();
307  chidof = chi2 / fitter.NDF();
308 
309  // remove the time shift
310  params[1] += time_shift; // mean
311 
312  // GP: inflate the uncertainties on the fit parameters according to chi2/NDF
313  // (not sure if this is in any way justified)
314  if (chidof > 1.)
315  for (double& par : paramerrors)
316  par *= std::sqrt(chidof);
317 
318  return true;
319  } // FastGaussianFit()
320 
322  void CCHitFinderAlg::FitNG(unsigned short nGaus, unsigned short npt, float* ticks, float* signl)
323  {
324  // Fit the signal to n Gaussians
325 
326  dof = npt - 3 * nGaus;
327 
328  chidof = 9999.;
329 
330  if (dof < 3) return;
331  if (bumps.size() == 0) return;
332 
333  // load the fit into a temp vector
334  std::vector<double> partmp;
335  std::vector<double> partmperr;
336 
337  //
338  // if it is possible, we try first with the quick single Gaussian fit
339  //
341 
342  bool bNeedROOTfit = (nGaus > 1) || !fUseFastFit;
343  if (!bNeedROOTfit) {
344  // so, we need only one puny Gaussian;
345  std::array<double, 3> params, paramerrors;
346 
348 
349  if (FastGaussianFit(npt, ticks, signl, params, paramerrors, chidof)) {
350  // success? copy the results in the proper structures
351  partmp.resize(3);
352  std::copy(params.begin(), params.end(), partmp.begin());
353  partmperr.resize(3);
354  std::copy(paramerrors.begin(), paramerrors.end(), partmperr.begin());
355  }
356  else
357  bNeedROOTfit = true; // if we fail, let's schedule ROOT to back us up
358 
359  if (!bNeedROOTfit) FinalFitStats.AddFast();
360 
361  } // if we don't need ROOT to fit
362 
363  if (bNeedROOTfit) {
364  // we may land here either because the simple Gaussian fit did not work
365  // (either failed, or we chose not to trust it)
366  // or because the fit is multi-Gaussian
367 
368  // define the fit string to pass to TF1
369 
370  std::stringstream numConv;
371  std::string eqn = "gaus";
372  if (nGaus > 1) eqn = "gaus(0)";
373  for (unsigned short ii = 3; ii < nGaus * 3; ii += 3) {
374  eqn.append(" + gaus(");
375  numConv.str("");
376  numConv << ii;
377  eqn.append(numConv.str());
378  eqn.append(")");
379  }
380 
381  auto Gn = std::make_unique<TF1>("gn", eqn.c_str());
382  /*
383  TF1* Gn = FitCache->Get(nGaus);
384  */
385  TGraph* fitn = new TGraph(npt, ticks, signl);
386  /*
387  if(prt) mf::LogVerbatim("CCHitFinder")
388  <<"FitNG nGaus "<<nGaus<<" nBumps "<<bumps.size();
389  */
390  // put in the bump parameters. Assume that nGaus >= bumps.size()
391  for (unsigned short ii = 0; ii < bumps.size(); ++ii) {
392  unsigned short index = ii * 3;
393  unsigned short bumptime = bumps[ii];
394  double amp = signl[bumptime];
395  Gn->SetParameter(index, amp);
396  Gn->SetParLimits(index, 0., 9999.);
397  Gn->SetParameter(index + 1, (double)bumptime);
398  Gn->SetParLimits(index + 1, 0, (double)npt);
399  Gn->SetParameter(index + 2, (double)fMinRMS[thePlane]);
400  Gn->SetParLimits(index + 2, 1., 3 * (double)fMinRMS[thePlane]);
401  /*
402  if(prt) mf::LogVerbatim("CCHitFinder")<<"Bump params "<<ii<<" "<<(short)amp
403  <<" "<<(int)bumptime<<" "<<(int)fMinRMS[thePlane];
404  */
405  } // ii bumps
406 
407  // search for other bumps that may be hidden by the already found ones
408  for (unsigned short ii = bumps.size(); ii < nGaus; ++ii) {
409  // bump height must exceed fMinPeak
410  float big = fMinPeak[thePlane];
411  unsigned short imbig = 0;
412  for (unsigned short jj = 0; jj < npt; ++jj) {
413  float diff = signl[jj] - Gn->Eval((Double_t)jj, 0, 0, 0);
414  if (diff > big) {
415  big = diff;
416  imbig = jj;
417  }
418  } // jj
419  if (imbig > 0) {
420  /*
421  if(prt) mf::LogVerbatim("CCHitFinder")<<"Found bump "<<ii<<" "<<(short)big
422  <<" "<<imbig;
423  */
424  // set the parameters for the bump
425  unsigned short index = ii * 3;
426  Gn->SetParameter(index, (double)big);
427  Gn->SetParLimits(index, 0., 9999.);
428  Gn->SetParameter(index + 1, (double)imbig);
429  Gn->SetParLimits(index + 1, 0, (double)npt);
430  Gn->SetParameter(index + 2, (double)fMinRMS[thePlane]);
431  Gn->SetParLimits(index + 2, 1., 5 * (double)fMinRMS[thePlane]);
432  } // imbig > 0
433  } // ii
434 
435  // W = set weights to 1, N = no drawing or storing, Q = quiet
436  // B = bounded parameters
437  fitn->Fit(&*Gn, "WNQB");
438 
439  for (unsigned short ipar = 0; ipar < 3 * nGaus; ++ipar) {
440  partmp.push_back(Gn->GetParameter(ipar));
441  partmperr.push_back(Gn->GetParError(ipar));
442  }
443  chidof = Gn->GetChisquare() / (dof * chinorm);
444 
445  delete fitn;
446  // delete Gn;
447 
448  } // if ROOT fit
449 
450  // Sort by increasing time if necessary
451  if (nGaus > 1) {
452  std::vector<std::pair<unsigned short, unsigned short>> times;
453  // fill the sort vector
454  for (unsigned short ii = 0; ii < nGaus; ++ii) {
455  unsigned short index = ii * 3;
456  times.push_back(std::make_pair(partmp[index + 1], ii));
457  } // ii
458  std::sort(times.begin(), times.end());
459  // see if re-arranging is necessary
460  bool sortem = false;
461  for (unsigned short ii = 0; ii < nGaus; ++ii) {
462  if (times[ii].second != ii) {
463  sortem = true;
464  break;
465  }
466  } // ii
467  if (sortem) {
468  // temp temp vectors for putting things in the right time order
469  std::vector<double> partmpt;
470  std::vector<double> partmperrt;
471  for (unsigned short ii = 0; ii < nGaus; ++ii) {
472  unsigned short index = times[ii].second * 3;
473  partmpt.push_back(partmp[index]);
474  partmpt.push_back(partmp[index + 1]);
475  partmpt.push_back(partmp[index + 2]);
476  partmperrt.push_back(partmperr[index]);
477  partmperrt.push_back(partmperr[index + 1]);
478  partmperrt.push_back(partmperr[index + 2]);
479  } // ii
480  partmp = partmpt;
481  partmperr = partmperrt;
482  } // sortem
483  } // nGaus > 1
484  /*
485  if(prt) {
486  mf::LogVerbatim("CCHitFinder")<<"Fit "<<nGaus<<" chi "<<chidof
487  <<" npars "<<partmp.size();
488  mf::LogVerbatim("CCHitFinder")<<"pars errs ";
489  for(unsigned short ii = 0; ii < partmp.size(); ++ii) {
490  mf::LogVerbatim("CCHitFinder")<<ii<<" "<<partmp[ii]<<" "
491  <<partmperr[ii];
492  }
493  }
494 */
495  // ensure that the fit is reasonable
496  bool fitok = true;
497  for (unsigned short ii = 0; ii < nGaus; ++ii) {
498  unsigned short index = ii * 3;
499  // ensure that the fitted time is within the signal bounds
500  short fittime = partmp[index + 1];
501  if (fittime < 0 || fittime > npt - 1) {
502  fitok = false;
503  break;
504  }
505  // ensure that the signal peak is large enough
506  if (partmp[index] < fMinPeak[thePlane]) {
507  fitok = false;
508  break;
509  }
510  // ensure that the RMS is large enough but not too large
511  float rms = partmp[index + 2];
512  if (rms < 0.5 * fMinRMS[thePlane] || rms > 5 * fMinRMS[thePlane]) {
513  fitok = false;
514  break;
515  }
516  // ensure that the hits are not too similar in time (< 2 ticks)
517  for (unsigned short jj = 0; jj < nGaus; ++jj) {
518  if (jj == ii) continue;
519  unsigned short jndex = jj * 3;
520  float timediff = std::abs(partmp[jndex + 1] - partmp[index + 1]);
521  if (timediff < 2.) {
522  fitok = false;
523  break;
524  }
525  }
526  if (!fitok) break;
527  }
528 
529  if (fitok) {
530  par = partmp;
531  parerr = partmperr;
532  }
533  else {
534  chidof = 9999.;
535  dof = -1;
536  // if(prt) mf::LogVerbatim("CCHitFinder")<<"Bad fit parameters";
537  }
538 
539  return;
540  } // FitNG
541 
543  void CCHitFinderAlg::MakeCrudeHit(unsigned short npt, float* ticks, float* signl)
544  {
545  // make a single crude hit if fitting failed
546  float sumS = 0.;
547  float sumST = 0.;
548  for (unsigned short ii = 0; ii < npt; ++ii) {
549  sumS += signl[ii];
550  sumST += signl[ii] * ticks[ii];
551  }
552  float mean = sumST / sumS;
553  float rms = 0.;
554  for (unsigned short ii = 0; ii < npt; ++ii) {
555  float arg = ticks[ii] - mean;
556  rms += signl[ii] * arg * arg;
557  }
558  rms = std::sqrt(rms / sumS);
559  float amp = sumS / (Sqrt2Pi * rms);
560  par.clear();
561  /*
562  if(prt) mf::LogVerbatim("CCHitFinder")<<"Crude hit Amp "<<(int)amp<<" mean "
563  <<(int)mean<<" rms "<<rms;
564 */
565  par.push_back(amp);
566  par.push_back(mean);
567  par.push_back(rms);
568  // need to do the errors better
569  parerr.clear();
570  float amperr = npt;
571  float meanerr = std::sqrt(1 / sumS);
572  float rmserr = 0.2 * rms;
573  parerr.push_back(amperr);
574  parerr.push_back(meanerr);
575  parerr.push_back(rmserr);
576  /*
577  if(prt) mf::LogVerbatim("CCHitFinder")<<" errors Amp "<<amperr<<" mean "
578  <<meanerr<<" rms "<<rmserr;
579 */
580  chidof = 9999.;
581  dof = -1;
582  } // MakeCrudeHit
583 
585  void CCHitFinderAlg::StoreHits(unsigned short TStart,
586  unsigned short npt,
587  HitChannelInfo_t info,
588  float adcsum)
589  {
590  // store the hits in the struct
591  size_t nhits = par.size() / 3;
592 
593  if (allhits.max_size() - allhits.size() < nhits) {
594  mf::LogError("CCHitFinder") << "Too many hits: existing " << allhits.size() << " plus new "
595  << nhits << " beyond the maximum " << allhits.max_size();
596  return;
597  }
598 
599  if (nhits == 0) return;
600 
601  // fill RMS for single hits
602  if (fStudyHits) StudyHits(3);
603 
604  const float loTime = TStart;
605  const float hiTime = TStart + npt;
606 
607  // Find sum of the areas of all Gaussians
608  float gsum = 0.;
609  for (size_t hit = 0; hit < nhits; ++hit) {
610  const unsigned short index = 3 * hit;
611  gsum += Sqrt2Pi * par[index] * par[index + 2];
612  }
613  for (size_t hit = 0; hit < nhits; ++hit) {
614  const size_t index = 3 * hit;
615  const float charge = Sqrt2Pi * par[index] * par[index + 2];
616  const float charge_err =
617  SqrtPi * (parerr[index] * par[index + 2] + par[index] * parerr[index + 2]);
618 
619  allhits.emplace_back(info.wire->Channel(), // channel
620  loTime, // start_tick
621  hiTime, // end_tick
622  par[index + 1] + TStart, // peak_time
623  parerr[index + 1], // sigma_peak_time
624  par[index + 2], // rms
625  par[index], // peak_amplitude
626  parerr[index], // sigma_peak_amplitude
627  adcsum * charge / gsum, // summedADC
628  charge, // hit_integral
629  charge_err, // hit_sigma_integral
630  nhits, // multiplicity
631  hit, // local_index
632  chidof, // goodness_of_fit
633  dof, // dof
634  info.wire->View(), // view
635  info.sigType, // signal_type
636  info.wireID // wireID
637  );
638  /*
639  if(prt) {
640  mf::LogVerbatim("CCHitFinder")<<"W:T "<<allhits.back().WireID().Wire
641  <<":"<<(short)allhits.back().PeakTime()
642  <<" Chg "<<(short)allhits.back().Integral()
643  <<" RMS "<<allhits.back().RMS()
644  <<" lo ID "<<allhits.back().LocalIndex()
645  <<" numHits "<<allhits.back().Multiplicity()
646  <<" loTime "<<allhits.back().StartTick()<<" hiTime "<<allhits.back().EndTick()
647  <<" chidof "<<allhits.back().GoodnessOfFit()
648  << " DOF " << allhits.back().DegreesOfFreedom();
649  }
650 */
651  } // hit
652  } // StoreHits
653 
655  void CCHitFinderAlg::StudyHits(unsigned short flag,
656  unsigned short npt,
657  float* signl,
658  unsigned short tstart)
659  {
660  // study hits in user-selected ranges of wires and ticks in each plane. The user should identify
661  // a shallow-angle isolated track, e.g. using the event display, to determine the wire/tick ranges.
662  // One hit should be reconstructed on each wire when the hit finding fcl parameters are set correctly.
663  // The intent of this study is to determine the correct fcl parameters. The flag variable determines
664  // the operation performed.
665  // flag = 0: Initialize study vectors
666  // flag = 1: Set SelRat true if the Region Above Threshold resides within a wire/hit range
667  // flag = 2: Find the maximum signal and calculate the RMS. Also find the low and high ticks of signals
668  // in the wire range to allow a later calculation of the track angle. This isn't strictly
669  // necessary for the study and presumes that the user has selected compatible regions in each plane.
670  // flag = 3: Accumulate the RMS from the first Gaussian fit
671  // flag = 4: Calculate recommended fcl parameters and print the results to the screen
672 
673  // init
674  if (flag == 0) {
675  for (unsigned short ipl = 0; ipl < 3; ++ipl) {
676  // Average chisq of the first fit on a single bump in each plane
677  bumpChi.push_back(0.);
678  // Average RMS of the dump
679  bumpRMS.push_back(0.);
680  // The number of single bumps in each plane
681  bumpCnt.push_back(0.);
682  // number of RATs
683  RATCnt.push_back(0);
684  // The number of single hits found in each plane
685  hitCnt.push_back(0.);
686  // Average reconstructed hit RMS
687  hitRMS.push_back(0.);
688  // lo/hi wire/time
689  loWire.push_back(9999.);
690  loTime.push_back(0.);
691  hiWire.push_back(-1.);
692  hiTime.push_back(0.);
693  } // ii
694  return;
695  } // flag == 0
696 
697  if (flag == 1) {
698  SelRAT = false;
699  if (thePlane == 0) {
700  if (theWireNum > fUWireRange[0] && theWireNum < fUWireRange[1] && tstart > fUTickRange[0] &&
701  tstart < fUTickRange[1]) {
702  SelRAT = true;
703  RATCnt[thePlane] += 1;
704  }
705  return;
706  } // thePlane == 0
707  if (thePlane == 1) {
708  if (theWireNum > fVWireRange[0] && theWireNum < fVWireRange[1] && tstart > fVTickRange[0] &&
709  tstart < fVTickRange[1]) {
710  SelRAT = true;
711  RATCnt[thePlane] += 1;
712  }
713  return;
714  } // thePlane == 1
715  if (thePlane == 2) {
716  if (theWireNum > fWWireRange[0] && theWireNum < fWWireRange[1] && tstart > fWTickRange[0] &&
717  tstart < fWTickRange[1]) {
718  SelRAT = true;
719  RATCnt[thePlane] += 1;
720  }
721  return;
722  } // thePlane == 2
723  } // flag == 1
724 
725  if (flag == 2) {
726  if (!SelRAT) return;
727  // in this section we find the low/hi wire/time for a signal. This can be used to calculate
728  // the slope dT/dW to study hit width, fraction of crude hits, etc vs dT/dW
729  float big = 0.;
730  float imbig = 0.;
731  for (unsigned short ii = 0; ii < npt; ++ii) {
732  if (signl[ii] > big) {
733  big = signl[ii];
734  imbig = ii;
735  }
736  } // ii
737  // require a significant PH
738  if (big > fMinPeak[0]) {
739  // get the Lo info
740  if (theWireNum < loWire[thePlane]) {
742  loTime[thePlane] = tstart + imbig;
743  }
744  // get the Hi info
745  if (theWireNum > hiWire[thePlane]) {
747  hiTime[thePlane] = tstart + imbig;
748  }
749  } // big > fMinPeak[0]
750  if (bumps.size() == 1 && chidof < 9999.) {
751  bumpCnt[thePlane] += bumps.size();
752  bumpChi[thePlane] += chidof;
753  // calculate the average bin
754  float sumt = 0.;
755  float sum = 0.;
756  for (unsigned short ii = 0; ii < npt; ++ii) {
757  sum += signl[ii];
758  sumt += signl[ii] * ii;
759  } // ii
760  float aveb = sumt / sum;
761  // now calculate the RMS
762  sumt = 0.;
763  for (unsigned short ii = 0; ii < npt; ++ii) {
764  float dbin = (float)ii - aveb;
765  sumt += signl[ii] * dbin * dbin;
766  } // ii
767  bumpRMS[thePlane] += std::sqrt(sumt / sum);
768  } // bumps.size() == 1 && chidof < 9999.
769  return;
770  } // flag == 2
771 
772  // fill info for single hits
773  if (flag == 3) {
774  if (!SelRAT) return;
775  if (par.size() == 3) {
776  hitCnt[thePlane] += 1;
777  hitRMS[thePlane] += par[2];
778  }
779  return;
780  }
781 
782  if (flag == 4) {
783  // The goal is to adjust the fcl inputs so that the number of single
784  // hits found is ~equal to the number of single bumps found for shallow
785  // angle tracks. The ChiNorm inputs should be adjusted so the average
786  // chisq/DOF is ~1 in each plane.
787  std::cout << "Check lo and hi W/T for each plane" << std::endl;
788  for (unsigned short ipl = 0; ipl < 3; ++ipl) {
789  std::cout << ipl << " lo " << loWire[ipl] << " " << loTime[ipl] << " hi " << hiWire[ipl]
790  << " " << hiTime[ipl] << std::endl;
791  }
792  std::cout << " ipl nRAT bCnt bChi bRMS hCnt hRMS dT/dW New_ChiNorm" << std::endl;
793  for (unsigned short ipl = 0; ipl < 3; ++ipl) {
794  if (bumpCnt[ipl] > 0) {
795  bumpChi[ipl] = bumpChi[ipl] / (float)bumpCnt[ipl];
796  bumpRMS[ipl] = bumpRMS[ipl] / (float)bumpCnt[ipl];
797  hitRMS[ipl] = hitRMS[ipl] / (float)hitCnt[ipl];
798  // calculate the slope
799  float dTdW = std::abs((hiTime[ipl] - loTime[ipl]) / (hiWire[ipl] - loWire[ipl]));
800  std::cout << ipl << std::right << std::setw(5) << RATCnt[ipl] << std::setw(5)
801  << bumpCnt[ipl] << std::setw(7) << std::fixed << std::setprecision(2)
802  << bumpChi[ipl] << std::setw(7) << bumpRMS[ipl] << std::setw(7) << hitCnt[ipl]
803  << std::setw(7) << std::setprecision(1) << hitRMS[ipl] << std::setw(7) << dTdW
804  << std::setw(7) << std::setprecision(2) << bumpChi[ipl] * fChiNorms[ipl]
805  << std::endl;
806  } //
807  } // ipl
808  std::cout << "nRAT is the number of Regions Above Threshold (RAT) used in the study.\n";
809  std::cout << "bCnt is the number of single bumps that were successfully fitted \n";
810  std::cout << "bChi is the average chisq/DOF of the first fit\n";
811  std::cout << "bRMS is the average calculated RMS of the bumps\n";
812  std::cout << "hCnt is the number of RATs that have a single hit\n";
813  std::cout << "hRMS is the average RMS from the Gaussian fit -> use this value for "
814  "fMinRMS[plane] in the fcl file\n";
815  std::cout << "dTdW is the slope of the track\n";
816  std::cout
817  << "New_ChiNorm is the recommended values of ChiNorm that should be used in the fcl file\n";
818  bumpChi.clear();
819  bumpRMS.clear();
820  bumpCnt.clear();
821  RATCnt.clear();
822  hitRMS.clear();
823  hitCnt.clear();
824  loWire.clear();
825  loTime.clear();
826  hiWire.clear();
827  hiTime.clear();
828  }
829  } // StudyHits
830 
832  void CCHitFinderAlg::FitStats_t::Reset(unsigned int nGaus)
833  {
834  if (nGaus == 0) return;
835  MultiGausFits.resize(nGaus);
836  std::fill(MultiGausFits.begin(), MultiGausFits.end(), 0);
837  FastFits = 0;
838  } // CCHitFinderAlg::FitStats_t::Reset()
839 
841  {
842  ++MultiGausFits[std::min(nGaus, (unsigned int)MultiGausFits.size()) - 1];
843  } // CCHitFinderAlg::FitStats_t::AddMultiGaus()
844 
845 } // namespace hit
unsigned short fMaxBumps
void RunCCHitFinder(std::vector< recob::Wire > const &Wires)
std::vector< short > fWTickRange
constexpr auto const & right(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:102
bool add(Data_t x, Data_t y, Data_t sy=Data_t(1.0))
Clears all the input statistics.
Definition: SimpleFits.h:1719
void AddMultiGaus(unsigned int nGaus)
float fChiSplit
Estimated noise error on the Signal.
static constexpr float SqrtPi
art::ServiceHandle< geo::Geometry const > geom
std::vector< WireID > ChannelToWire(raw::ChannelID_t const channel) const
Returns a list of wires connected to the specified TPC channel.
void FitNG(unsigned short nGaus, unsigned short npt, float *ticks, float *signl)
exchange data about the originating wire
virtual void reconfigure(fhicl::ParameterSet const &pset)
raw::ChannelID_t theChannel
std::vector< float > loWire
void StoreHits(unsigned short TStart, unsigned short npt, HitChannelInfo_t info, float adcsum)
std::vector< float > hiWire
std::vector< unsigned short > bumps
std::vector< float > fChiNorms
std::vector< float > bumpRMS
bool fUseFastFit
whether to attempt using a fast fit on single gauss.
constexpr auto abs(T v)
Returns the absolute value of the argument.
CCHitFinderAlg(fhicl::ParameterSet const &pset)
FitStats_t TriedFitStats
counts of the tried fits
MaybeLogger_< ELseverityLevel::ELsev_error, false > LogError
tick ticks
Alias for common language habits.
Definition: electronics.h:76
Classes performing simple fits.
geo::View_t View() const
Returns the view the channel belongs to.
Definition: Wire.h:219
A set of TF1 linear sum of base functions (Gaussians)
Definition: GausFitCache.h:45
virtual bool FillResults(FitParameters_t &params, FitMatrix_t &Xmat, Data_t &det, FitMatrix_t &Smat) const override
Fills the specified parameters.
Definition: SimpleFits.h:1803
std::vector< short > fUTickRange
std::vector< recob::Hit > allhits
unsigned short thePlane
raw::ChannelID_t Channel() const
Returns the ID of the channel (or InvalidChannelID)
Definition: Wire.h:223
T get(std::string const &key) const
Definition: ParameterSet.h:314
void Reset(unsigned int nGaus)
void fill(const art::PtrVector< recob::Hit > &hits, int only_plane)
"Fast" Gaussian fit
Definition: SimpleFits.h:993
std::vector< float > hiTime
std::vector< short > fUWireRange
virtual int NDF() const override
Returns the degrees of freedom in the determination of the fit.
Definition: SimpleFits.h:1144
bool has_key(std::string const &key) const
The geometry of one entire detector, as served by art.
Definition: Geometry.h:181
std::vector< int > bumpCnt
Detector simulation of raw signals on wires.
std::vector< float > Signal() const
Return a zero-padded full length vector filled with RoI signal.
Definition: Wire.cxx:30
std::vector< float > loTime
cet::coded_exception< errors::ErrorCodes, ExceptionDetail::translate > Exception
Definition: Exception.h:66
static bool FastGaussianFit(unsigned short npt, float const *ticks, float const *signl, std::array< double, 3 > &params, std::array< double, 3 > &paramerrors, float &chidof)
Performs a "fast" fit.
double mean(const std::vector< short > &wf, size_t start, size_t nsample)
Definition: UtilFunc.cxx:13
void StudyHits(unsigned short flag, unsigned short npt=0, float *signl=nullptr, unsigned short tstart=0)
std::vector< short > fWWireRange
unsigned short theWireNum
std::vector< float > fMinPeak
#define MF_LOG_DEBUG(id)
Class holding the regions of interest of signal from a channel.
Definition: Wire.h:116
std::vector< int > hitCnt
static constexpr unsigned int MaxGaussians
static constexpr float Sqrt2Pi
std::vector< short > fVWireRange
std::vector< short > fVTickRange
virtual Data_t ChiSquare() const override
Returns the of the original fit.
Definition: SimpleFits.h:1135
unsigned short fMaxXtraHits
void MakeCrudeHit(unsigned short npt, float *ticks, float *signl)
std::vector< float > hitRMS
#define MF_LOG_WARNING(category)
Double_t sum
Definition: plot.C:31
std::vector< int > RATCnt
second_as<> second
Type of time stored in seconds, in double precision.
Definition: spacetime.h:82
FitStats_t FinalFitStats
counts of the good fits
std::vector< double > parerr
HitChannelInfo_t(recob::Wire const *w, geo::WireID wid, geo::Geometry const &geom)
Float_t w
Definition: plot.C:20
std::vector< float > fMinRMS
std::vector< float > bumpChi
std::vector< double > par
art framework interface to geometry description