LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
ClusterParamsAlg.cxx
Go to the documentation of this file.
1 #include "ClusterParamsAlg.h"
2 
3 // LArSoft includes
4 #include "lardata/Utilities/SimpleFits.h" // LinearFit<>
5 #include "lardataalg/Utilities/StatCollector.h" // StatCollector<>
6 
7 //-----Math-------
8 #include <algorithm>
9 #include <cmath>
10 #include <iostream>
11 #include <vector>
12 
13 #include "TCanvas.h"
14 #include "TH1.h"
15 #include "TLegend.h"
16 #include "TMath.h"
17 #include "TPrincipal.h"
18 #include "TStopwatch.h"
19 
23 
24 #include "cetlib/pow.h"
25 
26 namespace {
27  constexpr double PI{3.14159265};
28 }
29 
30 namespace cluster {
31 
33  {
34  fMinNHits = 10;
35  enableFANN = false;
36  verbose = true;
37  Initialize();
38  }
39 
40  ClusterParamsAlg::ClusterParamsAlg(const std::vector<util::PxHit>& inhitlist)
41  {
42  fMinNHits = 10;
43  enableFANN = false;
44  verbose = true;
45  SetHits(inhitlist);
46  }
47 
48  int ClusterParamsAlg::SetHits(const std::vector<util::PxHit>& inhitlist)
49  {
50  Initialize();
51 
52  // Make default values
53  // Is done by the struct
54  if (!(inhitlist.size())) {
55  throw CRUException("Provided empty hit list!");
56  return -1;
57  }
58 
59  fHitVector.reserve(inhitlist.size());
60  for (auto h : inhitlist)
61  fHitVector.push_back(h);
62 
63  fPlane = fHitVector[0].plane;
64 
65  if (fHitVector.size() < fMinNHits) {
66  if (verbose)
67  std::cout << " the hitlist is too small. Continuing to run may result in crash!!! "
68  << std::endl;
69  return -1;
70  }
71 
72  return fHitVector.size();
73  }
74 
76  {
77  fPlane = p;
78  for (auto& h : fHitVector)
79  h.plane = p;
82  }
83 
84  void ClusterParamsAlg::GetFANNVector(std::vector<float>& data)
85  {
86  unsigned int length = 13;
87  if (data.size() != length) data.resize(length);
88  data[0] = fParams.opening_angle / PI;
90  data[2] = fParams.closing_angle / PI;
92  data[4] = fParams.eigenvalue_principal;
93  data[5] = fParams.eigenvalue_secondary;
94  data[6] = fParams.width / fParams.length;
97  data[9] = fParams.N_Hits_HC / fParams.N_Hits;
98  data[10] = fParams.modified_hit_density;
100  data[12] = log(fParams.sum_charge / fParams.length);
101  return;
102  }
103 
105  {
106  std::vector<float> data;
107  GetFANNVector(data);
108  if (verbose) {
109  std::cout << "Printing FANN input vector:\n"
110  << " Opening Angle (normalized) ... : " << data[0] << "\n"
111  << " Opening Angle charge weight .. : " << data[1] << "\n"
112  << " Closing Angle (normalized) ... : " << data[2] << "\n"
113  << " Closing Angle charge weight .. : " << data[3] << "\n"
114  << " Principal Eigenvalue ......... : " << data[4] << "\n"
115  << " Secondary Eigenvalue ......... : " << data[5] << "\n"
116  << " Width / Length ............... : " << data[6] << "\n"
117  << " Hit Density / M.H.D. ......... : " << data[7] << "\n"
118  << " Percent MultiHit Wires ....... : " << data[8] << "\n"
119  << " Percent High Charge Hits ..... : " << data[9] << "\n"
120  << " Modified Hit Density ......... : " << data[10] << "\n"
121  << " Charge RMS / Mean Charge ...... : " << data[11] << "\n"
122  << " log(Sum Charge / Length) ...... : " << data[12] << "\n";
123  }
124  }
125 
127  {
128  fTimeRecord_ProcName.clear();
129  fTimeRecord_ProcTime.clear();
130 
131  TStopwatch localWatch;
132  localWatch.Start();
133 
134  //--- Initilize attributes values ---//
135  fFinishedGetAverages = false;
136  fFinishedGetRoughAxis = false;
137  fFinishedGetProfileInfo = false;
139  fFinishedRefineDirection = false;
140  fFinishedGetFinalSlope = false;
142  fFinishedTrackShowerSep = false;
143  fFinishedGetEndCharges = false;
144 
145  fRough2DSlope = -999.999; // slope
146  fRough2DIntercept = -999.999; // slope
147 
148  fRoughBeginPoint.w = -999.999;
149  fRoughEndPoint.w = -999.999;
150 
151  fRoughBeginPoint.t = -999.999;
152  fRoughEndPoint.t = -999.999;
153 
154  fProfileIntegralForward = 999.999;
155  fProfileIntegralBackward = 999.999;
156  fProfileMaximumBin = -999;
157 
158  fChargeCutoffThreshold.clear();
159  fChargeCutoffThreshold.reserve(3);
160  fChargeCutoffThreshold.push_back(500);
161  fChargeCutoffThreshold.push_back(500);
162  fChargeCutoffThreshold.push_back(1000);
163 
164  fHitVector.clear();
165 
166  fParams.Clear();
167 
168  fTimeRecord_ProcName.push_back("Initialize");
169  fTimeRecord_ProcTime.push_back(localWatch.RealTime());
170  }
171 
173  {
174  enableFANN = true;
175  }
176 
178  bool override_DoGetAverages,
179  bool override_DoGetRoughAxis,
180  bool override_DoGetProfileInfo,
181  bool override_DoStartPointsAndDirection,
182  bool override_DoGetFinalSlope,
183  bool override_DoTrackShowerSep,
184  bool override_DoEndCharge)
185  {
186  GetAverages(override_DoGetAverages);
187  GetRoughAxis(override_DoGetRoughAxis);
188  GetProfileInfo(gser, override_DoGetProfileInfo);
189  RefineStartPointAndDirection(gser, override_DoStartPointsAndDirection);
190  GetFinalSlope(gser, override_DoGetFinalSlope);
191  GetEndCharges(gser, override_DoEndCharge);
192  TrackShowerSeparation(override_DoTrackShowerSep);
193  }
194 
195  void ClusterParamsAlg::GetAverages(bool override)
196  {
197  if (!override) { //Override being set, we skip all this logic.
198  //OK, no override. Stop if we're already finshed.
199  if (fFinishedGetAverages) return;
200  }
201 
202  TStopwatch localWatch;
203  localWatch.Start();
204 
205  TPrincipal fPrincipal(2, "D");
206 
207  fParams.N_Hits = fHitVector.size();
208 
209  std::map<double, int> wireMap;
210 
211  lar::util::StatCollector<double> charge, sumADC;
212 
213  int uniquewires = 0;
214  int multi_hit_wires = 0;
215  for (auto& hit : fHitVector) {
216  double data[2];
217  data[0] = hit.w;
218  data[1] = hit.t;
219  fPrincipal.AddRow(data);
220  fParams.charge_wgt_x += hit.w * hit.charge;
221  fParams.charge_wgt_y += hit.t * hit.charge;
222  charge.add(hit.charge);
223  sumADC.add(hit.sumADC);
224 
225  if (wireMap[hit.w] == 0) { uniquewires++; }
226  if (wireMap[hit.w] == 1) { multi_hit_wires++; }
227  wireMap[hit.w]++;
228  }
229 
230  fParams.sum_charge = charge.Sum();
231  fParams.mean_charge = charge.Average();
232  fParams.rms_charge = charge.RMS();
233 
234  fParams.sum_ADC = sumADC.Sum();
235  fParams.mean_ADC = sumADC.Average();
236  fParams.rms_ADC = sumADC.RMS();
237 
238  fParams.N_Wires = uniquewires;
239  fParams.multi_hit_wires = multi_hit_wires;
240 
241  if (fPrincipal.GetMeanValues()->GetNrows() < 2) { throw cluster::CRUException(); }
242 
243  fParams.mean_x = (*fPrincipal.GetMeanValues())[0];
244  fParams.mean_y = (*fPrincipal.GetMeanValues())[1];
246 
247  if (fParams.sum_charge != 0.) {
250  }
251  else { // "SNAFU"; use the mean
254  }
255 
256  fPrincipal.MakePrincipals();
257 
258  fParams.eigenvalue_principal = (*fPrincipal.GetEigenValues())[0];
259  fParams.eigenvalue_secondary = (*fPrincipal.GetEigenValues())[1];
260 
261  fFinishedGetAverages = true;
262 
263  fTimeRecord_ProcName.push_back("GetAverages");
264  fTimeRecord_ProcTime.push_back(localWatch.RealTime());
265  }
266 
267  // Also does the high hitlist
268  void ClusterParamsAlg::GetRoughAxis(bool override)
269  {
270  if (!override) { //Override being set, we skip all this logic.
271  //OK, no override. Stop if we're already finshed.
272  if (fFinishedGetRoughAxis) return;
273  //Try to run the previous function if not yet done.
274  if (!fFinishedGetAverages) GetAverages(true);
275  }
276  else {
277  //Try to run the previous function if not yet done.
278  if (!fFinishedGetAverages) GetAverages(true);
279  }
280 
281  TStopwatch localWatch;
282  localWatch.Start();
283 
284  double rmsx = 0.0;
285  double rmsy = 0.0;
286  double rmsq = 0.0;
287  //using the charge weighted coordinates find the axis from slope
288  double ncw = 0;
289  double sumtime = 0; //from sum averages
290  double sumwire = 0; //from sum averages
291  double sumwiretime = 0; //sum over (wire*time)
292  double sumwirewire = 0; //sum over (wire*wire)
293  //next loop over all hits again
294 
295  for (auto& hit : fHitVector) {
296  // First, abuse this loop to calculate rms in x and y
297  rmsx += pow(fParams.mean_x - hit.w, 2) / fParams.N_Hits;
298  rmsy += pow(fParams.mean_y - hit.t, 2) / fParams.N_Hits;
299  rmsq += pow(fParams.mean_charge - hit.charge, 2) / fParams.N_Hits;
300  //if charge is above avg_charge
301 
302  if (hit.charge > fParams.mean_charge) {
303  ncw += 1;
304  sumwire += hit.w;
305  sumtime += hit.t;
306  sumwiretime += hit.w * hit.t;
307  sumwirewire += pow(hit.w, 2);
308  } //for high charge
309  } //For hh loop
310 
311  fParams.rms_x = sqrt(rmsx);
312  fParams.rms_y = sqrt(rmsy);
313  fParams.RMS_charge = sqrt(rmsq);
314 
315  fParams.N_Hits_HC = ncw;
316  //Looking for the slope and intercept of the line above avg_charge hits
317 
318  if ((ncw * sumwirewire - sumwire * sumwire) > 0.00001)
319  fRough2DSlope = (ncw * sumwiretime - sumwire * sumtime) /
320  (ncw * sumwirewire - sumwire * sumwire); //slope for cw
321  else
322  fRough2DSlope = 999;
325  fParams.charge_wgt_y - fRough2DSlope * (fParams.charge_wgt_x) : //intercept for cw
326  0.;
327 
328  //Getthe 2D_angle
329  fParams.cluster_angle_2d = atan(fRough2DSlope) * 180 / PI;
330 
331  fFinishedGetRoughAxis = true;
332 
333  fTimeRecord_ProcName.push_back("GetRoughAxis");
334  fTimeRecord_ProcTime.push_back(localWatch.RealTime());
335  }
336 
338  {
339  if (!override) { //Override being set, we skip all this logic.
340  //OK, no override. Stop if we're already finshed.
341  if (fFinishedGetProfileInfo) return;
342  //Try to run the previous function if not yet done.
344  }
345  else {
347  }
348 
349  TStopwatch localWatch;
350  localWatch.Start();
351 
352  bool drawOrtHistos = false;
353 
354  //these variables need to be initialized to other values?
355  if (fRough2DSlope == -999.999 || fRough2DIntercept == -999.999) GetRoughAxis(true);
356 
357  //get slope of lines orthogonal to those found crossing the shower.
358  double inv_2d_slope = 0;
359  if (fabs(fRough2DSlope) > 0.001)
360  inv_2d_slope = -1. / fRough2DSlope;
361  else
362  inv_2d_slope = -999999.;
363 
364  double InterHigh = -999999;
365  double InterLow = 999999;
366  double WireHigh = -999999;
367  double WireLow = 999999;
368  fInterHigh_side = -999999;
369  fInterLow_side = 999999;
370  double min_ortdist(999), max_ortdist(-999); // needed to calculate width
371  //loop over all hits. Create coarse and fine profiles
372  // of the charge weight to help refine the start/end
373  // points and have a first guess of direction
374 
375  for (auto const& hit : fHitVector) {
376 
377  //calculate intercepts along
378  double intercept = hit.t - inv_2d_slope * (double)(hit.w);
379  double side_intercept = hit.t - fRough2DSlope * (double)(hit.w);
380 
381  util::PxPoint OnlinePoint;
382  // get coordinates of point on axis.
383  gser.GetPointOnLine(fRough2DSlope, fRough2DIntercept, &hit, OnlinePoint);
384 
385  double ortdist = gser.Get2DDistance(&OnlinePoint, &hit);
386  if (ortdist < min_ortdist) min_ortdist = ortdist;
387  if (ortdist > max_ortdist) max_ortdist = ortdist;
388 
389  if (inv_2d_slope != -999999) //almost all cases
390  {
391  if (intercept > InterHigh) { InterHigh = intercept; }
392 
393  if (intercept < InterLow) { InterLow = intercept; }
394  }
395  else //slope is practically horizontal. Care only about wires.
396  {
397  if (hit.w > WireHigh) { WireHigh = hit.w; }
398  if (hit.w < WireLow) { WireLow = hit.w; }
399  }
400 
401  if (side_intercept > fInterHigh_side) { fInterHigh_side = side_intercept; }
402 
403  if (side_intercept < fInterLow_side) { fInterLow_side = side_intercept; }
404  }
405  // end of first HitIter loop, at this point we should
406  // have the extreme intercepts
407 
409  // Second loop. Fill profiles.
411 
412  // get projected points at the beginning and end of the axis.
413 
414  util::PxPoint HighOnlinePoint, LowOnlinePoint, BeginOnlinePoint, EndOnlinePoint;
415 
416  if (inv_2d_slope != -999999) //almost all cases
417  {
418  gser.GetPointOnLineWSlopes(fRough2DSlope, fRough2DIntercept, InterHigh, HighOnlinePoint);
419  gser.GetPointOnLineWSlopes(fRough2DSlope, fRough2DIntercept, InterLow, LowOnlinePoint);
420  }
421  else //need better treatment of horizontal events.
422  {
423  util::PxPoint ptemphigh(fPlane, WireHigh, (fInterHigh_side + fInterLow_side) / 2);
424  util::PxPoint ptemplow(fPlane, WireLow, (fInterHigh_side + fInterLow_side) / 2);
425  gser.GetPointOnLine(fRough2DSlope, fRough2DIntercept, &ptemphigh, HighOnlinePoint);
426  gser.GetPointOnLine(fRough2DSlope, fRough2DIntercept, &ptemplow, LowOnlinePoint);
427  }
428  if (verbose) {
429  std::cout << " Low online point: " << LowOnlinePoint.w << ", " << LowOnlinePoint.t
430  << " High: " << HighOnlinePoint.w << ", " << HighOnlinePoint.t << std::endl;
431  //define BeginOnlinePoint as the one with lower wire number (for now), adjust intercepts accordingly
432  }
433  if (HighOnlinePoint.w >= LowOnlinePoint.w) {
434  BeginOnlinePoint = LowOnlinePoint;
435  fBeginIntercept = InterLow;
436  EndOnlinePoint = HighOnlinePoint;
437  fEndIntercept = InterHigh;
438  }
439  else {
440  BeginOnlinePoint = HighOnlinePoint;
441  fBeginIntercept = InterHigh;
442  EndOnlinePoint = LowOnlinePoint;
443  fEndIntercept = InterLow;
444  }
445 
446  fProjectedLength = gser.Get2DDistance(&BeginOnlinePoint, &EndOnlinePoint);
447 
449  fCoarseNbins = 2;
450 
452  if (fProfileNbins < 10) fProfileNbins = 10;
453 
454  fChargeProfile.clear();
455  fCoarseChargeProfile.clear();
456  fChargeProfile.resize(fProfileNbins, 0);
458 
460  // Some fitting variables to make a histogram:
461 
462  // TODO this is nonsense for small clusters
463  std::vector<double> ort_profile;
464  const int NBINS = 100;
465  ort_profile.resize(NBINS);
466 
467  std::vector<double> ort_dist_vect;
468  ort_dist_vect.reserve(fHitVector.size());
469 
470  double current_maximum = 0;
471  for (auto& hit : fHitVector) {
472 
473  util::PxPoint OnlinePoint;
474  // get coordinates of point on axis.
475  gser.GetPointOnLine(fRough2DSlope, &BeginOnlinePoint, &hit, OnlinePoint);
476  double ortdist = gser.Get2DDistance(&OnlinePoint, &hit);
477 
478  double linedist = gser.Get2DDistance(&OnlinePoint, &BeginOnlinePoint);
479  ort_dist_vect.push_back(ortdist);
480  int ortbin;
481  if (ortdist == 0)
482  ortbin = 0;
483  else if (max_ortdist == min_ortdist)
484  ortbin = 0;
485  else
486  ortbin = (int)(ortdist - min_ortdist) / (max_ortdist - min_ortdist) * (NBINS - 1);
487 
488  ort_profile.at(ortbin) += hit.charge;
489 
491  //calculate the weight along the axis, this formula is based on rough guessology.
492  // there is no physics motivation behind the particular numbers, A.S.
493  // \todo: switch to something that is motivated and easier to
494  // spell than guessology. C.A.
496  double weight = (ortdist < 1.) ? 10 * (hit.charge) : 5 * (hit.charge) / ortdist;
497 
498  int fine_bin =
499  fProjectedLength ? (int)(linedist / fProjectedLength * (fProfileNbins - 1)) : 0;
500  int coarse_bin =
501  fProjectedLength ? (int)(linedist / fProjectedLength * (fCoarseNbins - 1)) : 0;
502 
503  if (fine_bin < fProfileNbins) //only fill if bin number is in range
504  {
505  fChargeProfile.at(fine_bin) += weight;
506 
507  //find maximum bin on the fly:
508  if (fChargeProfile.at(fine_bin) > current_maximum && fine_bin != 0 &&
509  fine_bin != fProfileNbins - 1) {
510  current_maximum = fChargeProfile.at(fine_bin);
511  fProfileMaximumBin = fine_bin;
512  }
513  }
514 
515  if (coarse_bin < fCoarseNbins) //only fill if bin number is in range
516  fCoarseChargeProfile.at(coarse_bin) += weight;
517 
518  } // end second loop on hits. Now should have filled profile vectors.
519 
520  if (verbose) std::cout << "end second loop " << std::endl;
521 
522  double integral = 0;
523  int ix = 0;
524  for (ix = 0; ix < NBINS; ix++) {
525  integral += ort_profile.at(ix);
526  if (integral >= 0.95 * fParams.sum_charge) { break; }
527  }
528 
529  fParams.width =
530  2 * (double)ix / (double)(NBINS - 1) *
531  (double)(max_ortdist -
532  min_ortdist); // multiply by two because ortdist is folding in both sides.
533 
534  if (verbose) std::cout << " after width " << std::endl;
535 
536  if (drawOrtHistos) {
537  TH1F* ortDistHist = new TH1F(
538  "ortDistHist", "Orthogonal Distance to axis;Distance (cm)", 100, min_ortdist, max_ortdist);
539  TH1F* ortDistHistCharge =
540  new TH1F("ortDistHistCharge",
541  "Orthogonal Distance to axis (Charge Weighted);Distance (cm)",
542  100,
543  min_ortdist,
544  max_ortdist);
545  TH1F* ortDistHistAndrzej = new TH1F("ortDistHistAndrzej",
546  "Orthogonal Distance Andrzej weighting",
547  100,
548  min_ortdist,
549  max_ortdist);
550 
551  for (int index = 0; index < fParams.N_Hits; index++) {
552  ortDistHist->Fill(ort_dist_vect.at(index));
553  ortDistHistCharge->Fill(ort_dist_vect.at(index), fHitVector.at(index).charge);
554  double weight = (ort_dist_vect.at(index) < 1.) ?
555  10 * (fHitVector.at(index).charge) :
556  (5 * (fHitVector.at(index).charge) / ort_dist_vect.at(index));
557  ortDistHistAndrzej->Fill(ort_dist_vect.at(index), weight);
558  }
559  ortDistHist->Scale(1.0 / ortDistHist->Integral());
560  ortDistHistCharge->Scale(1.0 / ortDistHistCharge->Integral());
561  ortDistHistAndrzej->Scale(1.0 / ortDistHistAndrzej->Integral());
562 
563  TCanvas* ortCanv = new TCanvas("ortCanv", "ortCanv", 600, 600);
564  ortCanv->cd();
565  ortDistHistAndrzej->SetLineColor(3);
566  ortDistHistAndrzej->Draw("");
567  ortDistHistCharge->Draw("same");
568  ortDistHist->SetLineColor(2);
569  ortDistHist->Draw("same");
570 
571  TLegend* leg = new TLegend(.4, .6, .8, .9);
572  leg->SetHeader("Charge distribution");
573  leg->AddEntry(ortDistHist, "Unweighted Hits");
574  leg->AddEntry(ortDistHistCharge, "Charge Weighted Hits");
575  leg->AddEntry(ortDistHistAndrzej, "Charge Weighted by Guessology");
576  leg->Draw();
577 
578  ortCanv->Update();
579  }
580 
583 
584  //calculate the forward and backward integrals counting int the maximum bin.
585 
586  for (int ibin = 0; ibin < fProfileNbins; ibin++) {
589  }
590 
591  // now, we have the forward and backward integral so start
592  // stepping forward and backward to find the trunk of the
593  // shower. This is done making sure that the running
594  // integral until less than 1% is left and the bin is above
595  // a set theshold many empty bins.
596 
597  //forward loop
598  double running_integral = fProfileIntegralForward;
599  int startbin, endbin;
600  for (startbin = fProfileMaximumBin; startbin > 1 && startbin < (int)(fChargeProfile.size());
601  startbin--) {
602  running_integral -= fChargeProfile.at(startbin);
603  if (fChargeProfile.at(startbin) < fChargeCutoffThreshold.at(fPlane) &&
604  running_integral / fProfileIntegralForward < 0.01)
605  break;
606  else if (fChargeProfile.at(startbin) < fChargeCutoffThreshold.at(fPlane) &&
607  startbin - 1 > 0 &&
608  fChargeProfile.at(startbin - 1) < fChargeCutoffThreshold.at(fPlane) &&
609  startbin - 2 > 0 &&
610  fChargeProfile.at(startbin - 2) < fChargeCutoffThreshold.at(fPlane))
611  break;
612  }
613 
614  //backward loop
615  running_integral = fProfileIntegralBackward;
616  for (endbin = fProfileMaximumBin; endbin > 0 && endbin < fProfileNbins - 1; endbin++) {
617  running_integral -= fChargeProfile.at(endbin);
618  if (fChargeProfile.at(endbin) < fChargeCutoffThreshold.at(fPlane) &&
619  running_integral / fProfileIntegralBackward < 0.01)
620  break;
621  else if (fChargeProfile.at(endbin) < fChargeCutoffThreshold.at(fPlane) &&
622  endbin + 1 < fProfileNbins - 1 && endbin + 2 < fProfileNbins - 1 &&
623  fChargeProfile.at(endbin + 1) < fChargeCutoffThreshold.at(fPlane) &&
624  fChargeProfile.at(endbin + 2) < fChargeCutoffThreshold.at(fPlane))
625  break;
626  }
627 
628  // now have profile start and endpoints. Now translate to wire/time.
629  // Will use wire/time that are on the rough axis.
630  // fProjectedLength is the length from fInterLow to interhigh a
631  // long the rough_2d_axis on bin distance is:
632  // util::PxPoint OnlinePoint;
633 
634  if (inv_2d_slope != -999999) //almost all cases
635  {
636  double ort_intercept_begin =
637  fBeginIntercept + (fEndIntercept - fBeginIntercept) / fProfileNbins * startbin;
639  fRough2DSlope, fRough2DIntercept, ort_intercept_begin, fRoughBeginPoint);
640 
641  double ort_intercept_end =
642  fBeginIntercept + (fEndIntercept - fBeginIntercept) / fProfileNbins * endbin;
644 
646  fRough2DSlope, fRough2DIntercept, ort_intercept_end, fRoughEndPoint);
648  }
649  else {
650  double wire_begin = WireLow + (WireHigh - WireLow) / fProfileNbins * startbin;
651 
652  util::PxPoint ptemphigh(fPlane, wire_begin, (fInterHigh_side + fInterLow_side) / 2);
655 
656  double wire_end = WireLow + (WireHigh - WireLow) / fProfileNbins * endbin;
657 
658  util::PxPoint ptemplow(fPlane, wire_end, (fInterHigh_side + fInterLow_side) / 2);
661  }
662 
663  if (verbose)
664  std::cout << " rough start points " << fRoughBeginPoint.w << ", " << fRoughBeginPoint.t
665  << " end: " << fRoughEndPoint.w << " " << fRoughEndPoint.t << std::endl;
666 
667  // ok. now have fRoughBeginPoint and fRoughEndPoint. No decision about direction has been made yet.
670 
672 
673  fTimeRecord_ProcName.push_back("GetProfileInfo");
674  fTimeRecord_ProcTime.push_back(localWatch.RealTime());
675  }
676 
687  {
688  TStopwatch localWatch;
689  localWatch.Start();
690 
691  // need to define physical direction with openind angles and pass that to Ryan's line finder.
692 
693  // Direction decision has been moved entirely to Refine Direction()
694  // opening and closing angles are already set
695  // they are associated with the start and endpoints.
696  // If refine direction opted to switch the shower direction,
697  // it also switched opening and closing angles.
698 
700  // fParams.direction= ...
701 
702  // Direction is decided by RefineDirection()
703  util::PxPoint startHit, endHit;
704  startHit = fRoughBeginPoint;
705  endHit = fRoughEndPoint;
706 
708  //Ryan's Shower Strip finder work here.
709  //First we need to define the strip width that we want
710  double d = 0.6; //this is the width of the strip.... this needs to be tuned to something.
711  //===============================================================================================================
712  // Will need to feed in the set of hits that we want.
713  // const std::vector<util::PxHit*> whole;
714  std::vector<const util::PxHit*> subhit;
715  double linearlimit = 8;
716  double ortlimit = 12;
717  double lineslopetest(fRough2DSlope);
718  util::PxHit averageHit;
719  //also are we sure this is actually doing what it is supposed to???
720  //are we sure this works?
721  //is anybody even listening? Were they eaten by a grue?
722  gser.SelectLocalHitlist(
723  fHitVector, subhit, startHit, linearlimit, ortlimit, lineslopetest, averageHit);
724 
725  if (!(subhit.size()) || subhit.size() < 3) {
726  if (verbose)
727  std::cout << "Subhit list is empty or too small. Using rough start/end points..."
728  << std::endl;
731 
733 
734  fTimeRecord_ProcName.push_back("RefineStartPoints");
735  fTimeRecord_ProcTime.push_back(localWatch.RealTime());
736 
737  return;
738  }
739 
740  double avgwire = averageHit.w;
741  double avgtime = averageHit.t;
742  //vertex in tilda-space pair(x-til,y-til)
743  std::vector<std::pair<double, double>> vertil;
744  //vector of the sum of the distance of a vector to every vertex in tilda-space
745  std::vector<double> vs;
746  // $$This needs to be corrected//this is the good hits that are between strip
747  std::vector<const util::PxHit*> ghits;
748  ghits.reserve(subhit.size());
749  double fardistcurrent = 0;
750  util::PxHit startpoint;
751  //KAZU!!! I NEED this too//this will need to come from somewhere...
752  //This is some hit that is from the way far side of the entire shower cluster...
753  util::PxPoint farhit = fRoughEndPoint;
754 
755  //=============building the local vector===================
756  //this is clumsy... but just want to get something to work for now.
757  //THIS is REAL Horrible and CLUMSY... We can make things into helper functions soon.
758  std::vector<util::PxHit> returnhits;
759  std::vector<double> radiusofhit;
760  std::vector<int> closehits;
761  //==============================================================================
762  //Now we need to do the transformation into "tilda-space"
763  for (unsigned int a = 0; a < subhit.size(); a++) {
764  for (unsigned int b = a + 1; b < subhit.size(); b++) {
765  if (subhit.at(a)->w != subhit.at(b)->w) {
766  double xtil = ((subhit.at(a)->t - avgtime) - (subhit.at(b)->t - avgtime));
767  xtil /= ((subhit.at(a)->w - avgwire) - (subhit.at(b)->w - avgwire));
768  double ytil = (subhit.at(a)->w - avgwire) * xtil - (subhit.at(a)->t - avgtime);
769  //push back the tilda vertex point on the pair
770  std::pair<double, double> tv(xtil, ytil);
771  vertil.push_back(tv);
772  } //if Wires are not the same
773  } //for over b
774  } //for over a
775  // look at the distance from a tilda-vertex to all other tilda-verticies
776  for (unsigned int z = 0; z < vertil.size(); z++) {
777  double vd = 0; //the distance for vertex... just needs to be something 0
778  for (unsigned int c = 0; c < vertil.size(); c++)
779  vd += std::hypot(vertil.at(z).first - vertil.at(c).first,
780  vertil.at(z).second - vertil.at(c).second);
781  vs.push_back(vd);
782  vd = 0.0;
783  } //for z loop
784 
785  if (vs.size() == 0) //al hits on same wire?!
786  {
787  if (verbose)
788  std::cout << "vertil list is empty. all subhits are on the same wire?" << std::endl;
791 
793 
794  fTimeRecord_ProcName.push_back("RefineStartPoints");
795  fTimeRecord_ProcTime.push_back(localWatch.RealTime());
796  return;
797  }
798  //need to find the min of the distance of vertex in tilda-space
799  //this will get me the area where things are most linear
800  int minvs = std::min_element(vs.begin(), vs.end()) - vs.begin();
801  // now use the min position to find the vertex in tilda-space
802  //now need to look a which hits are between the tilda lines from the points
803  //in the tilda space everything in wire time is based on the new origin which is at the average wire/time
804  double tilwire = vertil.at(minvs).first; //slope
805  double tiltimet = -vertil.at(minvs).second +
806  d * sqrt(1 + pow(tilwire, 2)); //negative cept is accounted for top strip
807  double tiltimeb = -vertil.at(minvs).second -
808  d * sqrt(1 + pow(tilwire, 2)); //negative cept is accounted for bottom strip
809  // look over the subhit list and ask for which are inside of the strip
810  for (unsigned int a = 0; a < subhit.size(); a++) {
811  double dtstrip =
812  (-tilwire * (subhit.at(a)->w - avgwire) + (subhit.at(a)->t - avgtime) - tiltimet) /
813  sqrt(tilwire * tilwire + 1);
814  double dbstrip =
815  (-tilwire * (subhit.at(a)->w - avgwire) + (subhit.at(a)->t - avgtime) - tiltimeb) /
816  sqrt(tilwire * tilwire + 1);
817 
818  if ((dtstrip < 0.0 && dbstrip > 0.0) || (dbstrip < 0.0 && dtstrip > 0.0)) {
819  ghits.push_back(subhit.at(a));
820  } //if between strip
821  } //for a loop
822  //=======================Do stuff with the good hits============================
823 
824  //of these small set of hits just fit a simple line.
825  //then we will need to see which of these hits is farthest away
826 
827  for (unsigned int g = 0; g < ghits.size(); g++) {
828  // should call the helper funtion to do the fit
829  //but for now since there is no helper function I will just write it here......again
830  // now work on calculating the distance in wire time space from the far point
831  //farhit needs to be a hit that is given to me
832  double fardist = std::hypot(ghits.at(g)->w - farhit.w, ghits.at(g)->t - farhit.t);
833  //come back to this... there is a better way to do this probably in the loop
834  //there should also be a check that the hit that is farthest away has subsequent hits after it on a few wires
835  if (fardist > fardistcurrent) {
836  fardistcurrent = fardist;
837  //if fardist... this is the point to use for the start point
838  startpoint.t = ghits.at(g)->t;
839  startpoint.w = ghits.at(g)->w;
840  startpoint.plane = ghits.at(g)->plane;
841  startpoint.charge = ghits.at(g)->charge;
842  }
843  } //for ghits loop
844  //This can be the new start point
845 
846  //should this be here? Id argue this might be done once outside.
847  fParams.length = gser.Get2DDistance((util::PxPoint*)&startpoint, &endHit);
848  if (fParams.length)
850  else
852 
853  if (fParams.length && fParams.width)
855  else
857 
858  fParams.start_point = startpoint;
859 
861 
862  fTimeRecord_ProcName.push_back("RefineStartPoints");
863  fTimeRecord_ProcTime.push_back(localWatch.RealTime());
864  }
865 
869 
871  {
872  if (!override) { //Override being set, we skip all this logic.
873  //OK, no override. Stop if we're already finshed.
874  if (fFinishedGetFinalSlope) return;
875  //Try to run the previous function if not yet done.
877  }
878  else {
879  //Try to run the previous function if not yet done.
881  }
882 
883  TStopwatch localWatch;
884  localWatch.Start();
891  const int NBINS = 720;
892  std::vector<int> fh_omega_single(NBINS, 0); //720,-180., 180.
893 
894  double current_maximum = 0;
895  double curr_max_bin = -1;
896 
897  for (auto hit : fHitVector) {
898 
899  double omx = gser.Get2Dangle((util::PxPoint*)&hit,
900  &fParams.start_point); // in rad and assuming cm/cm space.
901  int nbin = (omx + TMath::Pi()) * (NBINS - 1) / (2 * TMath::Pi());
902  if (nbin >= NBINS) nbin = NBINS - 1;
903  if (nbin < 0) nbin = 0;
904  fh_omega_single[nbin] += hit.charge;
905 
906  if (fh_omega_single[nbin] > current_maximum) {
907  current_maximum = fh_omega_single[nbin];
908  curr_max_bin = nbin;
909  }
910  }
911  // FIXME: using two different definitions of PI in the same calculation?
912  // 2022-04-18 CHG
913  fParams.angle_2d = (curr_max_bin / 720 * (2 * TMath::Pi())) - TMath::Pi();
914  fParams.angle_2d *= 180 / PI;
915  if (verbose) std::cout << " Final 2D angle: " << fParams.angle_2d << " degrees " << std::endl;
916 
917  double mod_angle =
918  (fabs(fParams.angle_2d) <= 90) ?
919  fabs(fParams.angle_2d) :
920  180 - fabs(fParams.angle_2d); //want to transfer angle to radians and from 0 to 90.
921 
922  if (
923  cos(
924  mod_angle * PI /
925  180.)) { //values here based on fit of hit_density_1D vs. mod_angle defined as above (in ArgoNeuT).
926  if (mod_angle <= 75.)
928  fParams.hit_density_1D / (2.45 * cos(mod_angle * PI / 180.) + 0.2595);
929  else
931  fParams.hit_density_1D / (1.036 * mod_angle * PI / 180. - 0.2561);
932 
933  //calculate modified mean_charge:
934  fParams.modmeancharge = fParams.mean_charge / (1264 - 780 * cos(mod_angle * PI / 180.));
935  }
936  else
938 
940  // do not use for now.
941  bool drawProfileHisto = false;
942  if (drawProfileHisto) {
943 
945  double corr_factor = 1;
946  if (
947  cos(
948  mod_angle * PI /
949  180.)) { //values here based on fit of hit_density_1D vs. mod_angle defined as above (in ArgoNeuT).
950 
951  if (mod_angle <= 75.)
952  corr_factor *= (2.45 * cos(mod_angle * PI / 180.) + 0.2595);
953  else
954  corr_factor *= (1.036 * mod_angle * PI / 180. - 0.2561);
955  }
956 
957  fProfileNbins =
958  (int)(fProfileNbins / 2 * corr_factor + 0.5); // +0.5 to round to nearest sensible value
959 
960  if (verbose) std::cout << " number of final profile bins " << fProfileNbins << std::endl;
961 
962  fChargeProfile.clear();
963  fCoarseChargeProfile.clear();
964  fChargeProfile.resize(fProfileNbins, 0);
966 
967  fChargeProfileNew.clear();
968  fChargeProfileNew.resize(fProfileNbins, 0);
969 
970  util::PxPoint BeginOnlinePoint;
972 
973  for (auto hit : fHitVector) {
974 
975  util::PxPoint OnlinePoint;
976  // get coordinates of point on axis.
977  gser.GetPointOnLine(fRough2DSlope, &BeginOnlinePoint, &hit, OnlinePoint);
978 
979  double linedist = gser.Get2DDistance(&OnlinePoint, &BeginOnlinePoint);
980  int fine_bin = (int)(linedist / fProjectedLength * (fProfileNbins - 1));
981 
982  if (fine_bin < fProfileNbins) //only fill if bin number is in range
983  {
984  fChargeProfileNew.at(fine_bin) += hit.charge;
985  }
986  }
987 
988  TH1F* charge_histo = new TH1F("charge dist", "charge dist", fProfileNbins, 0, fProfileNbins);
989 
990  TH1F* charge_diff = new TH1F("charge diff", "charge diff", fProfileNbins, 0, fProfileNbins);
991 
992  for (int ix = 0; ix < fProfileNbins - 1; ix++) {
993  charge_histo->SetBinContent(ix, fChargeProfileNew[ix]);
994 
995  if (ix > 2 && ix < fProfileNbins - 3) {
996  double diff =
997  (1. / 12. * fChargeProfileNew[ix - 2] - 2. / 3. * fChargeProfileNew[ix - 1] +
998  2. / 3. * fChargeProfileNew[ix + 1] - 1. / 12. * fChargeProfileNew[ix + 2]) /
999  (double)fProfileNbins;
1000  charge_diff->SetBinContent(ix, diff);
1001  }
1002  }
1003 
1004  TCanvas* chCanv = new TCanvas("chCanv", "chCanv", 600, 600);
1005  chCanv->cd();
1006  charge_histo->SetLineColor(3);
1007  charge_histo->Draw("");
1008  charge_diff->SetLineColor(2);
1009  charge_diff->Draw("same");
1010  chCanv->Update();
1011  }
1012 
1014 
1015  fTimeRecord_ProcName.push_back("GetFinalSlope");
1016  fTimeRecord_ProcTime.push_back(localWatch.RealTime());
1017 
1018  fFinishedGetFinalSlope = true;
1019  return;
1020  }
1021 
1029  {
1030  //
1031  // We don't use "override"? Should we remove? 05/01/14
1032  //
1033  if (!override) override = true;
1034 
1035  TStopwatch localWatch;
1036  localWatch.Start();
1037 
1038  // if(!override) { //Override being set, we skip all this logic.
1039  // //OK, no override. Stop if we're already finshed.
1040  // if (fFinishedRefineDirection) return;
1041  // //Try to run the previous function if not yet done.
1042  // if (!fFinishedGetProfileInfo) GetProfileInfo(true);
1043  // } else {
1044  // //Try to run the previous function if not yet done.
1045  // if (!fFinishedGetProfileInfo) GetProfileInfo(true);
1046  // }
1047 
1048  // double wire_2_cm = gser.WireToCm();
1049  // double time_2_cm = gser.TimeToCm();
1050 
1051  // Removing the conversion since these points are set above in cm-cm space
1052  // -- Corey
1053 
1054  util::PxPoint this_startPoint, this_endPoint;
1055 
1057  this_startPoint = fParams.start_point;
1058  this_endPoint = fParams.end_point;
1059  }
1060  else {
1061  this_startPoint = fRoughBeginPoint;
1062  this_endPoint = fRoughEndPoint;
1063  }
1064  if (verbose) {
1065  std::cout << "Angle: Start point: (" << this_startPoint.w << ", " << this_startPoint.t
1066  << ")\n";
1067  std::cout << "Angle: End point : (" << this_endPoint.w << ", " << this_endPoint.t << ")\n";
1068  }
1069  double endStartDiff_x = (this_endPoint.w - this_startPoint.w);
1070  double endStartDiff_y = (this_endPoint.t - this_startPoint.t);
1071  double hit_counter_forward = 0;
1072  double hit_counter_backward = 0;
1073 
1074  if (verbose && endStartDiff_y == 0 && endStartDiff_x == 0) {
1075  std::cerr << "Error: end point and start point are the same!\n";
1076 
1077  fTimeRecord_ProcName.push_back("RefineDirection");
1078  fTimeRecord_ProcTime.push_back(localWatch.RealTime());
1079  return;
1080  }
1081 
1082  double percentage = 0.90;
1083  double percentage_HC = 0.90 * fParams.N_Hits_HC / fParams.N_Hits;
1084  const int NBINS = 200;
1085  const double wgt = 1.0 / fParams.N_Hits;
1086 
1087  // Containers for the angle histograms
1088  std::vector<float> opening_angle_bin(NBINS, 0.0);
1089  std::vector<float> closing_angle_bin(NBINS, 0.0);
1090  std::vector<float> opening_angle_highcharge_bin(NBINS, 0.0);
1091  std::vector<float> closing_angle_highcharge_bin(NBINS, 0.0);
1092  std::vector<float> opening_angle_chargeWgt_bin(NBINS, 0.0);
1093  std::vector<float> closing_angle_chargeWgt_bin(NBINS, 0.0);
1094  //hard coding this for now, should use SetRefineDirectionQMin function
1095  fQMinRefDir = 25;
1096 
1097  int index = -1;
1098  for (auto& hit : fHitVector) {
1099  index++;
1100  //skip this hit if below minimum cutoff param
1101  if (hit.charge < fQMinRefDir) continue;
1102 
1103  //weight_total = hit.charge;
1104 
1105  // Compute forward mean
1106  double hitStartDiff_x = (hit.w - this_startPoint.w);
1107  double hitStartDiff_y = (hit.t - this_startPoint.t);
1108 
1109  if (hitStartDiff_x == 0 && hitStartDiff_y == 0) continue;
1110 
1111  double cosangle_start = (endStartDiff_x * hitStartDiff_x + endStartDiff_y * hitStartDiff_y);
1112  cosangle_start /= (pow(pow(endStartDiff_x, 2) + pow(endStartDiff_y, 2), 0.5) *
1113  pow(pow(hitStartDiff_x, 2) + pow(hitStartDiff_y, 2), 0.5));
1114 
1115  if (cosangle_start > 0) {
1116  // Only take into account for hits that are in "front"
1117  //no weighted average, works better as flat average w/ min charge cut
1118  hit_counter_forward++;
1119  }
1120 
1121  // Compute backward mean
1122  double hitEndDiff_x = (hit.w - this_endPoint.w);
1123  double hitEndDiff_y = (hit.t - this_endPoint.t);
1124  if (hitEndDiff_x == 0 && hitEndDiff_y == 0) continue;
1125 
1126  double cosangle_end = (endStartDiff_x * hitEndDiff_x + endStartDiff_y * hitEndDiff_y) * (-1.);
1127  cosangle_end /= (pow(pow(endStartDiff_x, 2) + pow(endStartDiff_y, 2), 0.5) *
1128  pow(pow(hitEndDiff_x, 2) + pow(hitEndDiff_y, 2), 0.5));
1129 
1130  if (cosangle_end > 0) {
1131  //no weighted average, works better as flat average w/ min charge cut
1132  hit_counter_backward++;
1133  }
1134 
1135  int N_bins_OPEN = (NBINS - 1) * acos(cosangle_start) / PI;
1136  int N_bins_CLOSE = (NBINS - 1) * acos(cosangle_end) / PI;
1137  if (N_bins_OPEN < 0) N_bins_OPEN = 0;
1138  if (N_bins_CLOSE < 0) N_bins_CLOSE = 0;
1139 
1140  opening_angle_chargeWgt_bin[N_bins_OPEN] += hit.charge / fParams.sum_charge;
1141  closing_angle_chargeWgt_bin[N_bins_CLOSE] += hit.charge / fParams.sum_charge;
1142  opening_angle_bin[N_bins_OPEN] += wgt;
1143  closing_angle_bin[N_bins_CLOSE] += wgt;
1144 
1145  //Also fill bins for high charge hits
1146  if (hit.charge > fParams.mean_charge) {
1147  opening_angle_highcharge_bin[N_bins_OPEN] += wgt;
1148  closing_angle_highcharge_bin[N_bins_CLOSE] += wgt;
1149  }
1150  }
1151 
1152  //initialize iterators for the angles
1153  int iBin(0), jBin(0), kBin(0), lBin(0), mBin(0), nBin(0);
1154  double percentage_OPEN(0.0), percentage_CLOSE(0.0), percentage_OPEN_HC(0.0),
1155  percentage_CLOSE_HC(0.0), //The last 2 are for High Charge (HC)
1156  percentage_OPEN_CHARGEWGT(0.0), percentage_CLOSE_CHARGEWGT(0.0);
1157 
1158  for (iBin = 0; percentage_OPEN <= percentage && iBin < NBINS; iBin++) {
1159  percentage_OPEN += opening_angle_bin[iBin];
1160  }
1161 
1162  for (jBin = 0; percentage_CLOSE <= percentage && jBin < NBINS; jBin++) {
1163  percentage_CLOSE += closing_angle_bin[jBin];
1164  }
1165 
1166  for (kBin = 0; percentage_OPEN_HC <= percentage_HC && kBin < NBINS; kBin++) {
1167  percentage_OPEN_HC += opening_angle_highcharge_bin[kBin];
1168  }
1169 
1170  for (lBin = 0; percentage_CLOSE_HC <= percentage_HC && lBin < NBINS; lBin++) {
1171  percentage_CLOSE_HC += closing_angle_highcharge_bin[lBin];
1172  }
1173 
1174  for (mBin = 0; percentage_OPEN_CHARGEWGT <= percentage && mBin < NBINS; mBin++) {
1175  percentage_OPEN_CHARGEWGT += opening_angle_chargeWgt_bin[mBin];
1176  }
1177 
1178  for (nBin = 0; percentage_CLOSE_CHARGEWGT <= percentage && nBin < NBINS; nBin++) {
1179  percentage_CLOSE_CHARGEWGT += closing_angle_chargeWgt_bin[nBin];
1180  }
1181 
1182  double opening_angle = iBin * PI / NBINS;
1183  double closing_angle = jBin * PI / NBINS;
1184  double opening_angle_highcharge = kBin * PI / NBINS;
1185  double closing_angle_highcharge = lBin * PI / NBINS;
1186  double opening_angle_charge_wgt = mBin * PI / NBINS;
1187  double closing_angle_charge_wgt = nBin * PI / NBINS;
1188 
1189  double value_1 = closing_angle / opening_angle - 1;
1190  double value_2 = (fCoarseChargeProfile[0] / fCoarseChargeProfile[1]);
1191  if (value_2 < 100.0 && value_2 > 0.01)
1192  value_2 = log(value_2);
1193  else
1194  value_2 = 0.0;
1195  double value_3 = closing_angle_charge_wgt / opening_angle_charge_wgt - 1;
1196 
1197  // Using a sigmoid function to determine flipping.
1198  // I am going to weigh all of the values above (1, 2, 3) equally.
1199  // But, since value 2 in particular, I'm going to cut things off at 5.
1200 
1201  if (value_1 > 3) value_1 = 3.0;
1202  if (value_1 < -3) value_1 = -3.0;
1203  if (value_2 > 3) value_2 = 3.0;
1204  if (value_2 < -3) value_2 = -3.0;
1205  if (value_3 > 3) value_3 = 3.0;
1206  if (value_3 < -3) value_3 = -3.0;
1207 
1208  double Exp = exp(value_1 + value_2 + value_3);
1209  double sigmoid = (Exp - 1) / (Exp + 1);
1210 
1211  bool flip = false;
1212  if (sigmoid < 0) flip = true;
1213  if (flip) {
1214  if (verbose) std::cout << "Flipping!" << std::endl;
1215  std::swap(opening_angle, closing_angle);
1216  std::swap(opening_angle_highcharge, closing_angle_highcharge);
1217  std::swap(opening_angle_charge_wgt, closing_angle_charge_wgt);
1218  std::swap(fParams.start_point, fParams.end_point);
1219  std::swap(fRoughBeginPoint, fRoughEndPoint);
1220  }
1221  else if (verbose) {
1222  std::cout << "Not Flipping!\n";
1223  }
1224 
1225  fParams.opening_angle = opening_angle;
1226  fParams.opening_angle_charge_wgt = opening_angle_charge_wgt;
1227  fParams.closing_angle = closing_angle;
1228  fParams.closing_angle_charge_wgt = closing_angle_charge_wgt;
1229 
1230  fFinishedRefineDirection = true;
1231 
1232  fTimeRecord_ProcName.push_back("RefineDirection");
1233  fTimeRecord_ProcTime.push_back(localWatch.RealTime());
1234  } //end RefineDirection
1235 
1237  {
1238  TStopwatch localWatch;
1239  localWatch.Start();
1240 
1241  if (fHitVector.size()) {
1242  std::vector<const util::PxHit*> container_polygon;
1243  gser.SelectPolygonHitList(fHitVector, container_polygon);
1244  //now making Polygon Object
1245  std::pair<float, float> tmpvertex;
1246  //make Polygon Object as in mac/PolyOverlap.cc
1247  std::vector<std::pair<float, float>> vertices;
1248  for (unsigned int i = 0; i < container_polygon.size(); i++) {
1249  tmpvertex = std::make_pair(container_polygon.at(i)->w, container_polygon.at(i)->t);
1250  vertices.push_back(tmpvertex);
1251  }
1252  fParams.PolyObject = Polygon2D(vertices);
1253  }
1254 
1255  fTimeRecord_ProcName.push_back("FillPolygon");
1256  fTimeRecord_ProcTime.push_back(localWatch.RealTime());
1257  }
1258 
1260 
1262  bool override)
1263  {
1264  // This function is meant to pick the direction.
1265  // It refines both the start and end point, and then asks
1266  // if it should flip.
1267 
1268  TStopwatch localWatch;
1269  localWatch.Start();
1270 
1271  if (verbose) std::cout << " here!!! " << std::endl;
1272 
1273  if (!override) { //Override being set, we skip all this logic.
1274  //OK, no override. Stop if we're already finshed.
1276  fTimeRecord_ProcName.push_back("RefineStartPointAndDirection");
1277  fTimeRecord_ProcTime.push_back(localWatch.RealTime());
1278  return;
1279  }
1280  //Try to run the previous function if not yet done.
1281  if (!fFinishedGetProfileInfo) GetProfileInfo(gser, true);
1282  }
1283  else {
1284  //Try to run the previous function if not yet done.
1285  if (!fFinishedGetProfileInfo) GetProfileInfo(gser, true);
1286  }
1287  if (verbose) {
1288  std::cout << "REFINING .... " << std::endl;
1289  std::cout << " Rough start and end point: " << std::endl;
1290  std::cout << " s: (" << fParams.start_point.w << ", " << fParams.start_point.t << ")"
1291  << std::endl;
1292  std::cout << " e: (" << fParams.end_point.w << ", " << fParams.end_point.t << ")"
1293  << std::endl;
1294  }
1295  RefineStartPoints(gser);
1296  if (verbose) {
1297  std::cout << " Once Refined start and end point: " << std::endl;
1298  std::cout << " s: (" << fParams.start_point.w << ", " << fParams.start_point.t << ")"
1299  << std::endl;
1300  std::cout << " e: (" << fParams.end_point.w << ", " << fParams.end_point.t << ")"
1301  << std::endl;
1302  std::swap(fParams.start_point, fParams.end_point);
1303  std::swap(fRoughBeginPoint, fRoughEndPoint);
1304  }
1305  RefineStartPoints(gser);
1306  if (verbose) {
1307  std::cout << " Twice Refined start and end point: " << std::endl;
1308  std::cout << " s: (" << fParams.start_point.w << ", " << fParams.start_point.t << ")"
1309  << std::endl;
1310  std::cout << " e: (" << fParams.end_point.w << ", " << fParams.end_point.t << ")"
1311  << std::endl;
1312  std::swap(fParams.start_point, fParams.end_point);
1313  std::swap(fRoughBeginPoint, fRoughEndPoint);
1314  }
1315  RefineDirection();
1316  if (verbose) {
1317  std::cout << " Final start and end point: " << std::endl;
1318  std::cout << " s: (" << fParams.start_point.w << ", " << fParams.start_point.t << ")"
1319  << std::endl;
1320  std::cout << " e: (" << fParams.end_point.w << ", " << fParams.end_point.t << ")"
1321  << std::endl;
1322  }
1324 
1325  fTimeRecord_ProcName.push_back("RefineStartPointAndDirection");
1326  fTimeRecord_ProcTime.push_back(localWatch.RealTime());
1327  }
1328 
1330  {
1331  if (!override) return;
1332  if (verbose) std::cout << " ---- Trying T/S sep. ------ \n";
1333  if (enableFANN) {
1334  if (verbose) std::cout << " ---- Doing T/S sep. ------- \n";
1335  std::vector<float> FeatureVector, outputVector;
1336  GetFANNVector(FeatureVector);
1337  }
1338  else {
1339  if (verbose) std::cout << " ---- Failed T/S sep. ------ \n";
1340  }
1341  }
1342 
1343  //----------------------------------------------------------------------------
1345  bool override_ /* = false */)
1346  {
1347  if (!override_) { //Override being set, we skip all this logic.
1348  //OK, no override. Stop if we're already finshed.
1349  if (fFinishedGetEndCharges) return;
1350  }
1351 
1352  TStopwatch localWatch;
1353  localWatch.Start();
1354 
1356  fParams.end_charge = EndCharge(gser);
1357 
1358  fFinishedGetEndCharges = true;
1359 
1360  fTimeRecord_ProcName.push_back("GetEndCharges");
1361  fTimeRecord_ProcTime.push_back(localWatch.RealTime());
1362  } // ClusterParamsAlg::GetEndCharges()
1363 
1364  //----------------------------------------------------------------------------
1365  double ClusterParamsAlg::LinearIntegral(double m, double q, double x1, double x2)
1366  {
1367  return m / 2. * (cet::square(x2) - cet::square(x1)) + q * (x2 - x1);
1368  }
1369 
1370  //----------------------------------------------------------------------------
1372  double from_length,
1373  double to_length,
1374  unsigned int fit_first_bin,
1375  unsigned int fit_end_bin)
1376  {
1377  // first compute the information on the charge profile
1378  GetProfileInfo(gser);
1379 
1380  // first things first
1381  if (fit_first_bin > fit_end_bin) std::swap(fit_first_bin, fit_end_bin);
1382 
1383  // how many bins can we use?
1384  const unsigned int nbins =
1385  std::min(fit_end_bin - fit_first_bin, (unsigned int)fChargeProfile.size());
1386  if (nbins == 0) return 0;
1387 
1388  // move the specified range within reason
1389  if (fit_end_bin > fChargeProfile.size()) {
1390  fit_end_bin = fChargeProfile.size();
1391  fit_first_bin = fit_end_bin - nbins;
1392  }
1393 
1394  // if we have to use only one bin, so be it
1395  if (nbins < 2) return fChargeProfile[fit_first_bin];
1396 
1397  // fit the charge profile vs. bin number;
1398  // we assume that the first bin (#0) starts from the very beginning of the
1399  // cluster, that is at axis coordinate 0.
1401  for (unsigned int iBin = fit_first_bin; iBin < fit_end_bin; ++iBin) {
1402  // should we use a Poisson error instead of no error?
1403  fitter.add((double)iBin, fChargeProfile[iBin]);
1404  } // for
1405 
1406  // get the linear fit parameters; [0] intercept [1] slope
1408  try {
1409  fit = fitter.FitParameters();
1410  }
1411  catch (std::range_error const&) { // oops...
1412  // this is actually unexpected, since the only reason for the fit to fail
1413  // would be a singular fit matrix, that should be pretty much impossible
1414  // given that the bin coordinates are well behaved and there are at least
1415  // two of them
1416  std::cerr << "IntegrateFitCharge(): linear fit failed!" << std::endl;
1417  return 0.;
1418  }
1419 
1420  // now determine the bins corresponding to the length to integrate;
1421  // note that length can be pathologic (0, negative...); not our problem!
1422  const double length_to_bin = (double(fChargeProfile.size() - 1) / fProjectedLength);
1423  const double from_bin = from_length * length_to_bin, to_bin = to_length * length_to_bin;
1424 
1425  // we want the integral between from_bin and to_bin now:
1426  return LinearIntegral(fit[1] /* m */, fit[0] /* q */, from_bin, to_bin);
1427  } // ClusterParamsAlg::IntegrateFitCharge()
1428 
1429  //----------------------------------------------------------------------------
1431  float length /* = 1. */,
1432  unsigned int nbins /* = 10 */)
1433  {
1434  switch (fHitVector.size()) {
1435  case 0: return 0.;
1436  case 1:
1437  return fHitVector.back().charge;
1438  // the "default" is the rest of the function
1439  } // switch
1440 
1441  // this is the range of the fit:
1442  const unsigned int fit_first_bin = 0, fit_last_bin = nbins;
1443 
1444  return IntegrateFitCharge(gser, 0., length, fit_first_bin, fit_last_bin);
1445  } // ClusterParamsAlg::StartCharge()
1446 
1447  //----------------------------------------------------------------------------
1449  float length /* = 1. */,
1450  unsigned int nbins /* = 10 */)
1451  {
1452  switch (fHitVector.size()) {
1453  case 0: return 0.;
1454  case 1:
1455  return fHitVector.back().charge;
1456  // the "default" is the rest of the function
1457  } // switch
1458 
1459  // need the available number of bins and the axis length
1460  GetProfileInfo(gser);
1461  const unsigned int MaxBins = fChargeProfile.size();
1462 
1463  // this is the range of the fit:
1464  const unsigned int fit_first_bin = MaxBins > nbins ? MaxBins - nbins : 0,
1465  fit_last_bin = MaxBins;
1466 
1467  // now determine the integration range, in bin units;
1468  // get to the end, and go length backward;
1469  // note that length can be pathologic (0, negative...); not our problem!
1470  const double from = fProjectedLength - length, to = fProjectedLength;
1471 
1472  return IntegrateFitCharge(gser, from, to, fit_first_bin, fit_last_bin);
1473  } // ClusterParamsAlg::EndCharge()
1474 
1475  //------------------------------------------------------------------------------
1477  {
1478  if (fHitVector.size() < 2) return 0.0F;
1479 
1480  // compute all the averages
1481  GetAverages();
1482 
1483  // return the relevant information
1484  return std::isnormal(fParams.N_Wires) ? fParams.multi_hit_wires / fParams.N_Wires : 0.;
1485  } // ClusterParamsAlg::MultipleHitWires()
1486 
1487  //------------------------------------------------------------------------------
1489  {
1490  if (fHitVector.size() < 2) return 0.0F;
1491 
1492  // compute all the averages
1493  GetAverages();
1494  RefineStartPoints(gser); // fParams.length
1495 
1496  // return the relevant information
1497  return std::isnormal(fParams.length) ? fParams.multi_hit_wires / fParams.length : 0.;
1498  } // ClusterParamsAlg::MultipleHitDensity()
1499 
1500 } //end namespace
double rms_ADC
RMS (standard deviation of sample) of ADC counts of hits in ADC.
Definition: ClusterParams.h:31
void GetRoughAxis(bool override=false)
double Get2DDistance(double wire1, double time1, double wire2, double time2) const
2D polygon object
void GetFinalSlope(util::GeometryUtilities const &gser, bool override=false)
double closing_angle_charge_wgt
Same for charge_wgt.
Definition: ClusterParams.h:43
std::vector< double > fCoarseChargeProfile
std::vector< std::string > fTimeRecord_ProcName
double charge_wgt_y
Mean of hits along y, charge weighted.
Definition: ClusterParams.h:37
void FillPolygon(util::GeometryUtilities const &gser)
double mean_x
Mean of hits along x, peaks only.
Definition: ClusterParams.h:32
double start_charge
Charge at the start of the cluster.
Definition: ClusterParams.h:44
Float_t x1[n_points_granero]
Definition: compare.C:5
double IntegrateFitCharge(util::GeometryUtilities const &gser, double from_length, double to_length, unsigned int fit_first_bin, unsigned int fit_end_bin)
Integrates the charge between two positions in the cluster axis.
Double_t z
Definition: plot.C:276
double rms_charge
RMS (standard deviation of sample) of charge of hits in ADC.
Definition: ClusterParams.h:28
void GetProfileInfo(util::GeometryUtilities const &gser, bool override=false)
Polygon2D PolyObject
Polygon Object...see Polygon2D.hh.
Definition: ClusterParams.h:21
std::vector< util::PxHit > fHitVector
double eigenvalue_principal
the principal eigenvalue from PCA
Definition: ClusterParams.h:46
double mean_ADC
Mean (average) of ADC counts of hits, in ADC.
Definition: ClusterParams.h:30
int GetPointOnLine(double slope, double intercept, double wire1, double time1, double &wireout, double &timeout) const
Cluster finding and building.
double mean_y
Mean of hits along y, peaks only.
Definition: ClusterParams.h:33
Classes gathering simple statistics.
Weight_t RMS() const
Returns the root mean square.
constexpr double PI
Classes performing simple fits.
void RefineStartPoints(util::GeometryUtilities const &gser)
Performs a linear regression of data.
Definition: SimpleFits.h:833
Weight_t Average() const
Returns the value average.
Class def header for exception classes in ClusterRecoUtil package.
double eigenvalue_secondary
the secondary eigenvalue from PCA
Definition: ClusterParams.h:47
float MultipleHitDensity(util::GeometryUtilities const &gser)
Returns the number of multiple hits per wire.
double sum_charge
Sum charge of hits in ADC.
Definition: ClusterParams.h:26
bool isfinite(Vector const &v)
Returns whether all components of the vector are finite.
cluster::cluster_params fParams
Weight_t Sum() const
Returns the weighted sum of the values.
void GetEndCharges(util::GeometryUtilities const &gser, bool override_=false)
double t
Definition: PxUtils.h:10
Float_t d
Definition: plot.C:235
void SelectLocalHitlist(const std::vector< PxHit > &hitlist, std::vector< const PxHit * > &hitlistlocal, PxPoint &startHit, double &linearlimit, double &ortlimit, double &lineslopetest) const
void TrackShowerSeparation(bool override=false)
std::vector< double > fTimeRecord_ProcTime
util::PxPoint start_point
start point
Definition: ClusterParams.h:23
int GetPointOnLineWSlopes(double slope, double intercept, double ort_intercept, double &wireout, double &timeout) const
double angle_2d
Angle of axis in wire/hit view.
Definition: ClusterParams.h:39
double EndCharge(util::GeometryUtilities const &gser, float length=1., unsigned int nbins=10)
Returns the expected charge at the end of the cluster.
std::array< Data_t, NParams > FitParameters_t
type of set of fit parameters
Definition: SimpleFits.h:850
void GetAverages(bool override=false)
Detector simulation of raw signals on wires.
double w
Definition: PxUtils.h:9
std::vector< double > fChargeProfile
void RefineDirection(bool override=false)
void RefineStartPointAndDirection(util::GeometryUtilities const &gser, bool override=false)
virtual FitParameters_t FitParameters() const override
Computes and returns all the parameters of the fit result.
double weight
Definition: plottest35.C:25
void SelectPolygonHitList(const std::vector< PxHit > &hitlist, std::vector< const PxHit * > &hitlistlocal) const
std::vector< double > fChargeProfileNew
double sum_ADC
Sum charge of ADC counts of hits, in ADC.
Definition: ClusterParams.h:29
util::PxPoint end_point
end point
Definition: ClusterParams.h:24
size_t fMinNHits
Cut value for # hits: below this value clusters are not evaluated.
double mean_charge
Mean (average) charge of hits in ADC.
Definition: ClusterParams.h:27
int SetHits(const std::vector< util::PxHit > &)
double charge_wgt_x
Mean of hits along x, charge weighted.
Definition: ClusterParams.h:36
double opening_angle_charge_wgt
Same for charge_wgt.
Definition: ClusterParams.h:41
TLegend * leg
Definition: compare.C:67
double charge
area charge
Definition: PxUtils.h:40
void FillParams(util::GeometryUtilities const &gser, bool override_DoGetAverages=false, bool override_DoGetRoughAxis=false, bool override_DoGetProfileInfo=false, bool override_DoRefineStartPointsAndDirection=false, bool override_DoGetFinalSlope=false, bool override_DoTrackShowerSep=false, bool override_DoEndCharge=false)
double opening_angle
Width of angular distubtion wrt vertx.
Definition: ClusterParams.h:40
Float_t x2[n_points_geant4]
Definition: compare.C:26
double Get2Dangle(double deltawire, double deltatime) const
double cluster_angle_2d
Linear best fit to high-charge hits in the cluster.
Definition: ClusterParams.h:38
double rms_x
rms of hits along x (wires)
Definition: ClusterParams.h:34
double rms_y
rms of hits along y, (time)
Definition: ClusterParams.h:35
Collects statistics on a single quantity (weighted)
bool add(Data_t x, Data_t y, Data_t sy=Data_t(1.0))
Definition: SimpleFits.h:363
static double LinearIntegral(double m, double q, double x1, double x2)
Returns the integral of f(x) = mx + q defined in [x1, x2].
double closing_angle
Width of angular distubtion wrt endpoint.
Definition: ClusterParams.h:42
double StartCharge(util::GeometryUtilities const &gser, float length=1., unsigned int nbins=10)
Returns the expected charge at the beginning of the cluster.
unsigned int plane
Definition: PxUtils.h:11
std::vector< double > fChargeCutoffThreshold
float MultipleHitWires()
Returns the number of multiple hits per wire.
void add(Data_t value, Weight_t weight=Weight_t(1.0))
Adds one entry with specified value and weight.
void GetFANNVector(std::vector< float > &data)
double end_charge
Charge at the (other) end of the cluster.
Definition: ClusterParams.h:45