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