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