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