LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
ClusterMergeAlg_tool.cc
Go to the documentation of this file.
1 
8 // Framework Includes
11 #include "art_root_io/TFileDirectory.h"
12 #include "art_root_io/TFileService.h"
13 #include "cetlib/cpu_timer.h"
14 #include "fhiclcpp/ParameterSet.h"
16 
17 // LArSoft includes
21 
22 // Root includes
23 #include "TH1F.h"
24 #include "TString.h"
25 
26 // std includes
27 #include <iostream>
28 
29 //------------------------------------------------------------------------------------------------------------------------------------------
30 // implementation follows
31 
32 namespace lar_cluster3d {
33 
34  class ClusterMergeAlg : virtual public IClusterModAlg {
35  public:
41  explicit ClusterMergeAlg(const fhicl::ParameterSet&);
42 
47 
48  void configure(fhicl::ParameterSet const& pset) override;
49 
56  void initializeHistograms(art::TFileDirectory&) override;
57 
64  void ModifyClusters(reco::ClusterParametersList&) const override;
65 
69  float getTimeToExecute() const override { return fTimeToProcess; }
70 
71  private:
73 
75 
76  float closestApproach(const Eigen::Vector3f&,
77  const Eigen::Vector3f&,
78  const Eigen::Vector3f&,
79  const Eigen::Vector3f&,
80  Eigen::Vector3f&,
81  Eigen::Vector3f&,
82  Eigen::Vector3f&) const;
83 
84  const reco::ClusterHit3D* findClosestHit3D(const Eigen::Vector3f&,
85  const Eigen::Vector3f&,
86  const reco::HitPairListPtr&) const;
87 
88  const reco::ClusterHit3D* findFurthestHit3D(const Eigen::Vector3f&,
89  const Eigen::Vector3f&,
90  const reco::HitPairListPtr&) const;
91 
99 
100  mutable float fTimeToProcess;
101 
102  std::vector<TH1F*> fFirstEigenValueHists;
103  std::vector<TH1F*> fNextEigenValueHists;
105 
107 
111  TH1F*
113 
117  TH1F*
119 
124 
128 
130  TH1F* fGapRatHist;
131 
132  PrincipalComponentsAlg fPCAAlg; // For running Principal Components Analysis
133  };
134 
136  : fPCAAlg(pset.get<fhicl::ParameterSet>("PrincipalComponentsAlg"))
137  {
138  this->configure(pset);
139  }
140 
141  //------------------------------------------------------------------------------------------------------------------------------------------
142 
144 
145  //------------------------------------------------------------------------------------------------------------------------------------------
146 
148  {
149  fEnableMonitoring = pset.get<bool>("EnableMonitoring", true);
150  fMinTransEigenVal = pset.get<float>("MinTransEigenVal", 0.09);
151  fMinEigenToProcess = pset.get<float>("MinEigenToProcess", 2.0);
152  fOutputHistograms = pset.get<bool>("OutputHistograms", false);
153 
154  fTimeToProcess = 0.;
155 
156  // If asked, define some histograms
157  if (fOutputHistograms) {
158  // Access ART's TFileService, which will handle creating and writing
159  // histograms and n-tuples for us.
161 
162  // Make a directory for these histograms
163  art::TFileDirectory dir = tfs->mkdir("MergeClusters");
164 
165  fFirstEigenValueHists.resize(3, nullptr);
166  fNextEigenValueHists.resize(3, nullptr);
167 
168  std::vector<float> maxValsVec = {20., 50., 250.};
169 
170  for (size_t idx : {0, 1, 2}) {
171  fFirstEigenValueHists[idx] =
172  dir.make<TH1F>(Form("FEigen1st%1zu", idx), "Eigen Val", 200, 0., maxValsVec[idx]);
173  fNextEigenValueHists[idx] =
174  dir.make<TH1F>(Form("FEigen2nd%1zu", idx), "Eigen Val", 200, 0., maxValsVec[idx]);
175  }
176 
177  fNumMergedClusters = dir.make<TH1F>("NumMergedClus", "Number Merged", 200, 0., 1000.);
178 
180  dir.make<TH1F>("1stTo2ndPosLen", "Distance between Clusters", 250, 0., 1000.);
181 
182  fRMaxFirstHist = dir.make<TH1F>("rMaxFirst", "Radius of First Cluster", 200, 0., 100.);
183  fCosMaxFirstHist = dir.make<TH1F>("CosMaxFirst", "Cos Angle First Cyl/Axis", 200, 0., 1.);
184  fCosFirstAxisHist = dir.make<TH1F>("CosFirstAxis", "Cos Angle First Next/Axis", 200, 0., 1.);
186  dir.make<TH1F>("1stTo2ndProjEigen", "Projected Distance First", 200, 0., 200.);
187 
188  fRMaxNextHist = dir.make<TH1F>("rMaxNext", "Radius of Next Cluster", 200, 0., 100.);
189  fCosMaxNextHist = dir.make<TH1F>("CosMaxNext", "Cos Angle Next Cyl/Axis", 200, 0., 1.);
190  fCosNextAxisHist = dir.make<TH1F>("CosNextAxis", "Cos Angle Next Next/Axis", 200, 0., 1.);
192  dir.make<TH1F>("2ndTo1stProjEigen", "Projected Distance Next", 200, 0., 200.);
193 
194  fGapBetweenClusHist = dir.make<TH1F>("ClusterGap", "Gap Between Clusters", 400, -200., 200.);
195  fGapRatToLenHist = dir.make<TH1F>("GapRatToLen", "Ratio Gap to Distance", 100, -8., 2.);
197  dir.make<TH1F>("ProjEndPointLen", "Projected End Point Len", 200, -100., 100.);
199  dir.make<TH1F>("ProjEndPointRat", "Projected End Point Ratio", 100, 0., 1.);
200 
201  fAxesDocaHist = dir.make<TH1F>("AxesDocaHist", "DOCA", 200, 0., 25.);
202  f1stDocaArcLRatHist = dir.make<TH1F>("ALenPOCA1Hist", "Arc Len to POCA 1", 400, -50., 50.);
203  f2ndDocaArcLRatHist = dir.make<TH1F>("ALenPOCA2Hist", "Arc Len to POCA 2", 400, -50., 50.);
204 
206  dir.make<TH1F>("1stTo2ndPosLenRat", "Ratio clus dist to eigen", 200., 0., 20.);
207  fGapRatHist = dir.make<TH1F>("GapRat", "Ratio Gap to Next Eigen", 400, -20., 20.);
208  }
209 
210  return;
211  }
212 
213  void ClusterMergeAlg::initializeHistograms(art::TFileDirectory&)
214  {
215  return;
216  }
217 
219  {
227  // Initial clustering is done, now trim the list and get output parameters
228  cet::cpu_timer theClockBuildClusters;
229 
230  // Start clocks if requested
231  if (fEnableMonitoring) theClockBuildClusters.start();
232 
233  // Resort by the largest first eigen value (so the "longest" cluster)
234  clusterParametersList.sort([](auto& left, auto& right) {
235  return left.getFullPCA().getEigenValues()[2] > right.getFullPCA().getEigenValues()[2];
236  });
237 
238  // The idea is to continually loop through all clusters until we get to the point where we are no longer doing any merging.
239  // If two clusters are merged then we need to recycle through again because it may be that the new cluster can match when
240  // it might not have in the past
241  size_t lastClusterListCount = clusterParametersList.size() + 1;
242  reco::ClusterParametersList::iterator lastFirstClusterItr = clusterParametersList.begin();
243 
244  int numMergedClusters(0);
245  int nOutsideLoops(0);
246 
247  while (clusterParametersList.size() != lastClusterListCount) {
248  // Update the last count
249  lastClusterListCount = clusterParametersList.size();
250 
251  // Keep track of the first cluster iterator each pass through
252  reco::ClusterParametersList::iterator firstClusterItr = lastFirstClusterItr++;
253 
254  // Loop through the clusters
255  while (firstClusterItr != clusterParametersList.end()) {
256  reco::ClusterParameters& firstClusterParams = *firstClusterItr;
257  reco::ClusterParametersList::iterator nextClusterItr = firstClusterItr;
258 
259  // Take a brief interlude to do some histogramming
260  if (fOutputHistograms) {
261  const reco::PrincipalComponents& firstPCA = firstClusterParams.getFullPCA();
262 
263  Eigen::Vector3f firstEigenVals(
264  std::min(1.5 * std::sqrt(std::max(firstPCA.getEigenValues()[0], fMinTransEigenVal)),
265  50.),
266  std::min(1.5 * std::sqrt(std::max(firstPCA.getEigenValues()[1], fMinTransEigenVal)),
267  100.),
268  1.5 * std::sqrt(firstPCA.getEigenValues()[2]));
269 
270  for (size_t idx = 0; idx < 3; idx++)
271  fFirstEigenValueHists[idx]->Fill(firstEigenVals[idx], 1.);
272  }
273 
274  // Once you get down to the smallest clusters if they haven't already been absorbed there is no need to check them
275  if (firstClusterParams.getFullPCA().getEigenValues()[2] < fMinEigenToProcess) break;
276 
277  while (++nextClusterItr != clusterParametersList.end()) {
278  reco::ClusterParameters& nextClusterParams = *nextClusterItr;
279 
280  // On any given loop through here it **might** be that the first cluster has been modified. So can't cache
281  // the parameters, need the curret ones
282  if (linearClusters(firstClusterParams, nextClusterParams)) {
283  if (mergeClusters(firstClusterParams, nextClusterParams)) {
284  // Now remove the "next" cluster
285  nextClusterItr = clusterParametersList.erase(nextClusterItr);
286 
287  // Our new cluster may be larger than those before it so we should try to move backwards to make sure to
288  // give now smaller clusters the chance to get merged as well
289  reco::ClusterParametersList::iterator biggestItr = firstClusterItr;
290 
291  // Step backwards until we hit the beginning of the list
292  while (biggestItr-- != clusterParametersList.begin()) {
293  // The list has already been sorted largest to smallest so if we find a cluster that is "larger" then
294  // we have hit the limit
295  if ((*biggestItr).getFullPCA().getEigenValues()[2] >
296  (*firstClusterItr).getFullPCA().getEigenValues()[2]) {
297  // Want to insert after the cluster with the larger primary axes.. but there is
298  // no point in doing anything if the new cluster is already in the right spot
299  if (++biggestItr != firstClusterItr)
300  clusterParametersList.splice(
301  biggestItr, clusterParametersList, firstClusterItr);
302 
303  break;
304  }
305  }
306 
307  // Restart loop?
308  nextClusterItr = firstClusterItr;
309 
310  numMergedClusters++;
311  }
312  }
313  }
314 
315  firstClusterItr++;
316  }
317 
318  nOutsideLoops++;
319  }
320 
321  std::cout << "==> # merged: " << numMergedClusters << ", in " << nOutsideLoops
322  << " outside loops " << std::endl;
323 
324  if (fOutputHistograms) fNumMergedClusters->Fill(numMergedClusters, 1.);
325 
326  if (fEnableMonitoring) {
327  theClockBuildClusters.stop();
328 
329  fTimeToProcess = theClockBuildClusters.accumulated_real_time();
330  }
331 
332  mf::LogDebug("Cluster3D") << ">>>>> Merge clusters done, found " << clusterParametersList.size()
333  << " clusters" << std::endl;
334 
335  return;
336  }
337 
339  reco::ClusterParameters& nextCluster) const
340  {
341  // Assume failure
342  bool consistent(false);
343 
344  // The goal here is to compare the two input PCA's and determine if they are effectively colinear and
345  // within reasonable range to consider merging them. Note that a key assumption is that the first input
346  // PCA is from the "bigger" cluster, the second is "smaller" and may have a less reliable PCA.
347 
348  // Dereference the PCA's
349  const reco::PrincipalComponents& firstPCA = firstCluster.getFullPCA();
350  const reco::PrincipalComponents& nextPCA = nextCluster.getFullPCA();
351 
352  // Recover the positions of the centers of the two clusters
353  const Eigen::Vector3f& firstCenter = firstPCA.getAvePosition();
354  const Eigen::Vector3f& nextCenter = nextPCA.getAvePosition();
355 
356  // And form a vector between the two centers
357  Eigen::Vector3f firstPosToNextPosVec = nextCenter - firstCenter;
358  Eigen::Vector3f firstPosToNextPosUnit = firstPosToNextPosVec.normalized();
359  float firstPosToNextPosLen = firstPosToNextPosVec.norm();
360 
361  // Now get the first PCA's primary axis and since we'll use them get all of them at once...
362  Eigen::Vector3f firstAxis0(firstPCA.getEigenVectors().row(0));
363  Eigen::Vector3f firstAxis1(firstPCA.getEigenVectors().row(1));
364  Eigen::Vector3f firstAxis2(firstPCA.getEigenVectors().row(2));
365 
366  // Will want the distance of closest approach of the next cluser's center to the primary, start by finding arc length
367  float arcLenToNextDoca = firstPosToNextPosVec.dot(firstAxis2);
368 
369  // Adopt the convention that the cluster axis is in same direction as vector from first to next centers
370  // And preserve the overall orientation of the PCA by flipping all if we flip the first one
371  if (arcLenToNextDoca < 0.) {
372  firstAxis0 = -firstAxis0;
373  firstAxis1 = -firstAxis1;
374  firstAxis2 = -firstAxis2;
375  arcLenToNextDoca = -arcLenToNextDoca;
376  }
377 
378  // Recover the eigen values of the first and second PCAs for selection cuts
379  Eigen::Vector3f firstEigenVals(
380  std::min(1.5 * sqrt(std::max(firstPCA.getEigenValues()[0], fMinTransEigenVal)), 20.),
381  std::min(1.5 * sqrt(std::max(firstPCA.getEigenValues()[1], fMinTransEigenVal)), 50.),
382  std::min(1.5 * sqrt(firstPCA.getEigenValues()[2]), 250.));
383 
384  // We treat the PCA as defining an elliptical tube and we use the projection of the vector between centers to
385  // get the radius to the edge of the tube, from which we can determine the projected length inside the tube
386  Eigen::Vector2f firstProj01Unit =
387  Eigen::Vector2f(firstPosToNextPosUnit.dot(firstAxis0), firstPosToNextPosUnit.dot(firstAxis1))
388  .normalized();
389  float firstEigen0Proj = firstEigenVals[0] * firstProj01Unit[0];
390  float firstEigen1Proj = firstEigenVals[1] * firstProj01Unit[1];
391  float rMaxFirst =
392  std::sqrt(firstEigen0Proj * firstEigen0Proj + firstEigen1Proj * firstEigen1Proj);
393  float cosMaxFirst =
394  firstEigenVals[2] / std::sqrt(firstEigenVals[2] * firstEigenVals[2] + rMaxFirst * rMaxFirst);
395  float cosFirstAxis = firstAxis2.dot(firstPosToNextPosUnit);
396 
397  // Now calculate a measure of the length inside the tube along the vector between the cluster centers
398  float firstEigenVal2 = firstEigenVals[2];
399  float firstToNextProjEigen = firstEigenVal2;
400 
401  // There are two cases to consider, the first is that the vector exits out the side of the tube
402  if (cosFirstAxis < cosMaxFirst) {
403  // In this case we need the sign of the angle between the vector and the cluster primary axis
404  float sinFirstAxis = std::sqrt(1. - cosFirstAxis * cosFirstAxis);
405 
406  // Here the length will be given by the radius, not the primary eigen value
407  firstToNextProjEigen = rMaxFirst / sinFirstAxis;
408  }
409  else
410  firstToNextProjEigen /= cosFirstAxis;
411 
412  // Get scale factor for selecting this pair
413  float firstPosToNextPosScaleFactor = 8.;
414 
415  // A brief interlude to fill a few histograms
416  if (fOutputHistograms) {
417  f1stTo2ndPosLenHist->Fill(firstPosToNextPosLen, 1.);
418  fRMaxFirstHist->Fill(rMaxFirst, 1.);
419  fCosMaxFirstHist->Fill(cosMaxFirst, 1.);
420  fCosFirstAxisHist->Fill(cosFirstAxis, 1.);
421  f1stTo2ndProjEigenHist->Fill(firstToNextProjEigen, 1.);
422  }
423 
424  // This makes first selection, it should eliminate most of the junk cases:
425  if (firstPosToNextPosLen < firstPosToNextPosScaleFactor * firstToNextProjEigen) {
426  // Recover the axes for the next PCA and make sure pointing convention is observed
427  Eigen::Vector3f nextAxis0(nextPCA.getEigenVectors().row(0));
428  Eigen::Vector3f nextAxis1(nextPCA.getEigenVectors().row(1));
429  Eigen::Vector3f nextAxis2(nextPCA.getEigenVectors().row(2));
430 
431  // Recover the cos of the angle between the primary axes...
432  float cosFirstNextAxis = firstAxis2.dot(nextAxis2);
433 
434  // And in this case we want to choose the sign of the next cluster's axes so that the
435  // primary axes "point" in the same direction
436  if (cosFirstNextAxis < 0.) {
437  nextAxis0 = -nextAxis0;
438  nextAxis1 = -nextAxis1;
439  nextAxis2 = -nextAxis2;
440  cosFirstNextAxis = -cosFirstNextAxis;
441  }
442 
443  // Get the eigen values again
444  Eigen::Vector3f nextEigenVals(
445  std::min(1.5 * sqrt(std::max(nextPCA.getEigenValues()[0], fMinTransEigenVal)), 20.),
446  std::min(1.5 * sqrt(std::max(nextPCA.getEigenValues()[1], fMinTransEigenVal)), 50.),
447  std::min(1.5 * sqrt(nextPCA.getEigenValues()[2]), 250.));
448 
449  // Repeat the calculation of the length of the vector through the cluster "tube"...
450  Eigen::Vector2f nextProj01Unit =
451  Eigen::Vector2f(firstPosToNextPosUnit.dot(nextAxis0), firstPosToNextPosUnit.dot(nextAxis1))
452  .normalized();
453  float nextEigen0Proj = nextEigenVals[0] * nextProj01Unit[0];
454  float nextEigen1Proj = nextEigenVals[1] * nextProj01Unit[1];
455  float rMaxNext = std::sqrt(nextEigen0Proj * nextEigen0Proj + nextEigen1Proj * nextEigen1Proj);
456  float cosMaxNext =
457  nextEigenVals[2] / std::sqrt(nextEigenVals[2] * nextEigenVals[2] + rMaxNext * rMaxNext);
458  float cosNextAxis = std::abs(nextAxis2.dot(firstPosToNextPosUnit));
459 
460  // Now calculate a measure of the length inside the cylider along the vector between the cluster centers
461  float nextToFirstProjEigen = nextEigenVals[2];
462 
463  // There are two cases to consider, the first is that the vector exits out the side of the cylinder
464  if (cosNextAxis < cosMaxNext) {
465  // In this case we need the sign of the angle between the vector and the cluster primary axis
466  float sinNextAxis = std::sqrt(1. - cosNextAxis * cosNextAxis);
467 
468  // Here the length will be given by the radius, not the primary eigen value
469  nextToFirstProjEigen = rMaxNext / sinNextAxis;
470  }
471  else
472  nextToFirstProjEigen /= cosNextAxis;
473 
474  // Allow a generous gap but significantly derate as angle to next cluster increases
475  //float nextToFirstScaleFactor = 6. * cosFirstNextAxis;
476  float nextToFirstScaleFactor = 8. * cosFirstNextAxis;
477 
478  // Form the "gap" along the axis between centers from the projections of the eigen values
479  // Note the gap can be negative
480  float gapFirstToNext = firstPosToNextPosLen - firstToNextProjEigen - nextToFirstProjEigen;
481 
482  // Look at the presumed nearest endpoints of the two clusters
483  Eigen::Vector3f firstEndPoint = firstCenter + firstEigenVals[2] * firstAxis2;
484  Eigen::Vector3f nextTailPoint = nextCenter - nextEigenVals[2] * nextAxis2;
485  Eigen::Vector3f endToTailVec = nextTailPoint - firstEndPoint;
486 
487  // Get projection along vector between centers
488  float endPointProjection = endToTailVec.dot(firstPosToNextPosUnit);
489  float endToTailLen = endToTailVec.norm();
490 
491  // Another brief interlude to fill some histograms
492  if (fOutputHistograms) {
493  for (size_t idx = 0; idx < 3; idx++)
494  fNextEigenValueHists[idx]->Fill(nextEigenVals[idx], 1.);
495 
496  fRMaxNextHist->Fill(rMaxNext, 1.);
497  fCosMaxNextHist->Fill(cosMaxNext, 1.);
498  fCosNextAxisHist->Fill(cosNextAxis, 1.);
499  f2ndTo1stProjEigenHist->Fill(nextToFirstProjEigen, 1.);
500  fGapBetweenClusHist->Fill(gapFirstToNext, 1.);
501  fProjEndPointLenHist->Fill(endPointProjection, 1.);
502  fProjEndPointRatHist->Fill(std::abs(endPointProjection) / endToTailLen, 1.);
503  }
504 
505  // We mirror here the first selection above but now operate on the distance between centers less
506  // the first cluster's projected eigen value
507  if (gapFirstToNext < nextToFirstScaleFactor * nextToFirstProjEigen ||
508  (std::abs(endPointProjection) < nextToFirstProjEigen &&
509  endToTailLen < nextEigenVals[2])) {
510  // Now check the distance of closest approach betweent the two vectors
511  // Closest approach calculaiton results vectors
512  Eigen::Vector3f firstPoca;
513  Eigen::Vector3f nextPoca;
514  Eigen::Vector3f firstToNextUnit;
515 
516  // Recover the doca of the two axes and their points of closest approach along each axis
517  float lineDoca = closestApproach(
518  firstCenter, firstAxis2, nextCenter, nextAxis2, firstPoca, nextPoca, firstToNextUnit);
519 
520  // Get the range through the first clusters "tube" for this doca vec
521  Eigen::Vector3f firstPOCAProjUnit = Eigen::Vector3f(firstToNextUnit.dot(firstAxis0),
522  firstToNextUnit.dot(firstAxis1),
523  firstToNextUnit.dot(firstAxis1))
524  .normalized();
525  float firstPOCAVecProjEigen =
526  std::sqrt(std::pow(firstEigenVals[0] * firstPOCAProjUnit[0], 2) +
527  std::pow(firstEigenVals[1] * firstPOCAProjUnit[1], 2) +
528  std::pow(firstEigenVals[2] * firstPOCAProjUnit[2], 2));
529 
530  // Similarly for the next vector
531  Eigen::Vector3f nextPOCAProjUnit = Eigen::Vector3f(firstToNextUnit.dot(firstAxis0),
532  firstToNextUnit.dot(firstAxis1),
533  firstToNextUnit.dot(firstAxis1))
534  .normalized();
535  float nextPOCAVecProjEigen = std::sqrt(std::pow(nextEigenVals[0] * nextPOCAProjUnit[0], 2) +
536  std::pow(nextEigenVals[1] * nextPOCAProjUnit[1], 2) +
537  std::pow(nextEigenVals[2] * nextPOCAProjUnit[2], 2));
538 
539  if (fOutputHistograms) {
540  // The below returned the signed arc lengths to their respective pocas
541  float arcLenToFirstPoca = (firstPoca - firstCenter).dot(firstAxis2);
542  float arcLenToNextPoca = (nextPoca - nextCenter).dot(nextAxis2);
543 
544  fAxesDocaHist->Fill(lineDoca, 1.);
545  f1stDocaArcLRatHist->Fill(arcLenToFirstPoca / firstEigenVals[2], 1.);
546  f2ndDocaArcLRatHist->Fill(arcLenToNextPoca / nextEigenVals[2], 1.);
547  }
548 
549  // Scaling factor to increase doca distance as distance grows
550  float rMaxScaleFactor = 1.2;
551 
552  if (lineDoca < rMaxScaleFactor * (firstPOCAVecProjEigen + nextPOCAVecProjEigen)) {
553  consistent = true;
554 
555  if (fOutputHistograms) {
556  f1stTo2ndPosLenRatHist->Fill(firstPosToNextPosLen / firstToNextProjEigen, 1.);
557  }
558  }
559  }
560  }
561 
562  return consistent;
563  }
564 
566  reco::ClusterParameters& nextClusterParams) const
567  {
568  bool merged(false);
569 
570  // Merge the next cluster into the first one
571  // Get the hits
572  reco::HitPairListPtr& hitPairListPtr = firstClusterParams.getHitPairListPtr();
573 
574  // We copy the hits from the old to new but note that we need to update the parameters for each 2D hit we add
575  for (const auto* hit : nextClusterParams.getHitPairListPtr()) {
576  hitPairListPtr.push_back(hit);
577 
578  for (const auto* hit2D : hit->getHits())
579  if (hit2D) firstClusterParams.UpdateParameters(hit2D);
580  }
581 
582  // Recalculate the PCA
583  fPCAAlg.PCAAnalysis_3D(firstClusterParams.getHitPairListPtr(), firstClusterParams.getFullPCA());
584 
585  // Must have a valid pca
586  if (firstClusterParams.getFullPCA().getSvdOK()) {
587  // Finish out the merging here
588  reco::Hit3DToEdgeMap& firstEdgeMap = firstClusterParams.getHit3DToEdgeMap();
589  reco::Hit3DToEdgeMap& nextEdgeMap = nextClusterParams.getHit3DToEdgeMap();
590 
591  for (const auto& pair : nextEdgeMap)
592  firstEdgeMap[pair.first] = pair.second;
593 
594  // Set the skeleton PCA to make sure it has some value
595  firstClusterParams.getSkeletonPCA() = firstClusterParams.getFullPCA();
596 
597  // Zap the cluster we merged into the new one...
598  nextClusterParams = reco::ClusterParameters();
599 
600  merged = true;
601  }
602 
603  return merged;
604  }
605 
606  float ClusterMergeAlg::closestApproach(const Eigen::Vector3f& P0,
607  const Eigen::Vector3f& u0,
608  const Eigen::Vector3f& P1,
609  const Eigen::Vector3f& u1,
610  Eigen::Vector3f& poca0,
611  Eigen::Vector3f& poca1,
612  Eigen::Vector3f& firstNextUnit) const
613  {
614  // Technique is to compute the arclength to each point of closest approach
615  Eigen::Vector3f w0 = P0 - P1;
616  float a(1.);
617  float b(u0.dot(u1));
618  float c(1.);
619  float d(u0.dot(w0));
620  float e(u1.dot(w0));
621  float den(a * c - b * b);
622 
623  // Make sure lines are not colinear
624  if (std::abs(den) > 10. * std::numeric_limits<float>::epsilon()) {
625  float arcLen0 = (b * e - c * d) / den;
626  float arcLen1 = (a * e - b * d) / den;
627 
628  poca0 = P0 + arcLen0 * u0;
629  poca1 = P1 + arcLen1 * u1;
630  }
631  // Otherwise, take the poca to be the distance to the line at the second point
632  else {
633  float arcLenToNextPoint = w0.dot(u0);
634 
635  poca0 = P0 + arcLenToNextPoint * u0;
636  poca1 = P1;
637  }
638 
639  firstNextUnit = poca1 - poca0;
640 
641  float docaDist = firstNextUnit.norm();
642 
643  firstNextUnit = firstNextUnit.normalized();
644 
645  return docaDist;
646  }
647 
649  const Eigen::Vector3f& refPoint,
650  const Eigen::Vector3f& refVector,
651  const reco::HitPairListPtr& hitList) const
652  {
653  const reco::ClusterHit3D* nearestHit3D(hitList.front());
654  float closest(std::numeric_limits<float>::max());
655 
656  for (const auto& hit3D : hitList) {
657  Eigen::Vector3f refToHitVec = hit3D->getPosition() - refPoint;
658  float arcLenToHit = refToHitVec.dot(refVector);
659 
660  if (arcLenToHit < closest) {
661  nearestHit3D = hit3D;
662  closest = arcLenToHit;
663  }
664  }
665 
666  return nearestHit3D;
667  }
668 
670  const Eigen::Vector3f& refPoint,
671  const Eigen::Vector3f& refVector,
672  const reco::HitPairListPtr& hitList) const
673  {
674  const reco::ClusterHit3D* nearestHit3D(hitList.front());
675  float furthest(-std::numeric_limits<float>::max());
676 
677  for (const auto& hit3D : hitList) {
678  Eigen::Vector3f refToHitVec = hit3D->getPosition() - refPoint;
679  float arcLenToHit = refToHitVec.dot(refVector);
680 
681  if (arcLenToHit > furthest) {
682  nearestHit3D = hit3D;
683  furthest = arcLenToHit;
684  }
685  }
686 
687  return nearestHit3D;
688  }
689 
691 } // namespace lar_cluster3d
intermediate_table::iterator iterator
void ModifyClusters(reco::ClusterParametersList &) const override
Scan an input collection of clusters and modify those according to the specific implementing algorith...
std::vector< TH1F * > fNextEigenValueHists
Next Cluster eigen value.
std::vector< TH1F * > fFirstEigenValueHists
First Cluster eigen value.
void configure(fhicl::ParameterSet const &pset) override
bool getSvdOK() const
Definition: Cluster3D.h:240
constexpr auto const & right(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:102
#define DEFINE_ART_CLASS_TOOL(tool)
Definition: ToolMacros.h:42
void PCAAnalysis_3D(const reco::HitPairListPtr &hitPairList, reco::PrincipalComponents &pca, bool skeletonOnly=false) const
float closestApproach(const Eigen::Vector3f &, const Eigen::Vector3f &, const Eigen::Vector3f &, const Eigen::Vector3f &, Eigen::Vector3f &, Eigen::Vector3f &, Eigen::Vector3f &) const
TH1F * fCosMaxFirstHist
Cos angle beteeen cylinder axis and edge.
Float_t den
Definition: plot.C:35
void initializeHistograms(art::TFileDirectory &) override
Interface for initializing histograms if they are desired Note that the idea is to put hisgtograms in...
TNtupleSim Fill(f1, f2, f3, f4)
reco::PrincipalComponents & getSkeletonPCA()
Definition: Cluster3D.h:465
float fTimeToProcess
Keep track of how long it took to run this algorithm.
constexpr auto abs(T v)
Returns the absolute value of the argument.
TH1F * f2ndDocaArcLRatHist
arc length to POCA for DOCA second cluster
bool fOutputHistograms
Take the time to create and fill some histograms for diagnostics.
reco::Hit3DToEdgeMap & getHit3DToEdgeMap()
Definition: Cluster3D.h:466
reco::HitPairListPtr & getHitPairListPtr()
Definition: Cluster3D.h:463
TH1F * fRMaxFirstHist
radius of "cylinder" first cluster
TH1F * fGapRatHist
Ratio of gap and next proj eigen.
IClusterModAlg interface class definiton.
TH1F * fCosFirstAxisHist
Cos angle between vector between centers and first axis.
TH1F * fProjEndPointRatHist
Ratio of projection to vector length.
float fMinEigenToProcess
Don&#39;t look anymore at clusters below this size.
const EigenValues & getEigenValues() const
Definition: Cluster3D.h:242
bool mergeClusters(reco::ClusterParameters &, reco::ClusterParameters &) const
float getTimeToExecute() const override
If monitoring, recover the time to execute a particular function.
reco::PrincipalComponents & getFullPCA()
Definition: Cluster3D.h:464
TH1F * fCosNextAxisHist
Cos angle between vector between centers and next axis.
const reco::ClusterHit3D * findFurthestHit3D(const Eigen::Vector3f &, const Eigen::Vector3f &, const reco::HitPairListPtr &) const
TH1F * f2ndTo1stProjEigenHist
arc length along vector between centers from next center to cylinder
parameter set interface
T get(std::string const &key) const
Definition: ParameterSet.h:314
TH1F * f1stTo2ndPosLenRatHist
Ratio of distance between centers and first proj eigen.
const Eigen::Vector3f & getAvePosition() const
Definition: Cluster3D.h:244
ClusterMergeAlg(const fhicl::ParameterSet &)
Constructor.
TH1F * fRMaxNextHist
radius of "cylinder" next cluster
Float_t d
Definition: plot.C:235
std::list< const reco::ClusterHit3D * > HitPairListPtr
Definition: Cluster3D.h:326
void UpdateParameters(const reco::ClusterHit2D *hit)
Definition: Cluster3D.h:440
bool fEnableMonitoring
Data members to follow.
TH1F * f1stTo2ndProjEigenHist
arc length along vector between centers from first center to cylinder
const reco::ClusterHit3D * findClosestHit3D(const Eigen::Vector3f &, const Eigen::Vector3f &, const reco::HitPairListPtr &) const
constexpr auto dot(Vector const &a, OtherVector const &b)
Return cross product of two vectors.
TH1F * fNumMergedClusters
How many clusters were merged?
Detector simulation of raw signals on wires.
TH1F * fProjEndPointLenHist
Projection of vector between the endpoints.
This header file defines the interface to a principal components analysis designed to be used within ...
bool linearClusters(reco::ClusterParameters &, reco::ClusterParameters &) const
float fMinTransEigenVal
Set a mininum allowed value for the transverse eigen values.
constexpr auto const & left(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:94
decltype(auto) get(T &&obj)
ADL-aware version of std::to_string.
Definition: StdUtils.h:120
TDirectory * dir
Definition: macro.C:5
MaybeLogger_< ELseverityLevel::ELsev_success, false > LogDebug
This provides an art tool interface definition for 3D Cluster algorithms.
TH1F * fAxesDocaHist
Closest distance between two primary axes.
TH1F * fGapRatToLenHist
Ratio of gap to distance between centers.
TH1F * fCosMaxNextHist
Cos angle beteeen cylinder axis and edge.
std::unordered_map< const reco::ClusterHit3D *, reco::EdgeList > Hit3DToEdgeMap
Definition: Cluster3D.h:339
Float_t e
Definition: plot.C:35
TH1F * fGapBetweenClusHist
Gap between clusters.
TH1F * f1stDocaArcLRatHist
arc length to POCA for DOCA first cluster
TH1F * f1stTo2ndPosLenHist
Distance between cluster centers.
std::list< ClusterParameters > ClusterParametersList
Definition: Cluster3D.h:393
const EigenVectors & getEigenVectors() const
Definition: Cluster3D.h:243