ConvexHullPathFinder_tool.cc
Go to the documentation of this file.
1 /**
2  * @file ConvexHullPathFinder_tool.cc
3  *
4  * @brief art Tool for comparing clusters and merging those that are consistent
5  *
6  */
7 
8 // Framework Includes
11 #include "art_root_io/TFileDirectory.h"
12 #include "cetlib/cpu_timer.h"
14 
17 
18 // LArSoft includes
21 
22 // Eigen
23 #include <Eigen/Core>
24 
25 // Root histograms
26 #include "TH1F.h"
27 
28 // std includes
29 #include <string>
30 #include <iostream>
31 #include <memory>
32 
33 //------------------------------------------------------------------------------------------------------------------------------------------
34 // implementation follows
35 
36 namespace lar_cluster3d {
37 
38 class ConvexHullPathFinder : virtual public IClusterModAlg
39 {
40 public:
41  /**
42  * @brief Constructor
43  *
44  * @param pset
45  */
47 
48  /**
49  * @brief Destructor
50  */
52 
53  void configure(fhicl::ParameterSet const &pset) override;
54 
55  /**
56  * @brief Interface for initializing histograms if they are desired
57  * Note that the idea is to put hisgtograms in a subfolder
58  *
59  * @param TFileDirectory - the folder to store the hists in
60  */
61  void initializeHistograms(art::TFileDirectory&) override;
62 
63  /**
64  * @brief Scan an input collection of clusters and modify those according
65  * to the specific implementing algorithm
66  *
67  * @param clusterParametersList A list of cluster objects (parameters from associated hits)
68  */
69  void ModifyClusters(reco::ClusterParametersList&) const override;
70 
71  /**
72  * @brief If monitoring, recover the time to execute a particular function
73  */
74  float getTimeToExecute() const override {return fTimeToProcess;}
75 
76 private:
77 
78  /**
79  * @brief Use PCA to try to find path in cluster
80  *
81  * @param clusterParameters The given cluster parameters object to try to split
82  * @param clusterParametersList The list of clusters
83  */
87  reco::ClusterParametersList& outputClusterList,
88  int level = 0) const;
89 
90  using HitOrderTuple = std::tuple<float,float,reco::ProjectedPoint>;
91  using HitOrderTupleList = std::list<HitOrderTuple>;
92 
93  bool makeCandidateCluster(Eigen::Vector3f&,
97  int) const;
98 
99  bool makeCandidateCluster(Eigen::Vector3f&,
102  int) const;
103 
104  bool completeCandidateCluster(Eigen::Vector3f&, reco::ClusterParameters&, int) const;
105 
111 
112  float closestApproach(const Eigen::Vector3f&,
113  const Eigen::Vector3f&,
114  const Eigen::Vector3f&,
115  const Eigen::Vector3f&,
116  Eigen::Vector3f&,
117  Eigen::Vector3f&) const;
118 
119  using MinMaxPoints = std::pair<reco::ProjectedPoint,reco::ProjectedPoint>;
120  using MinMaxPointPair = std::pair<MinMaxPoints,MinMaxPoints>;
121 
122  void buildConvexHull(reco::ClusterParameters& clusterParameters, int level = 0) const;
123 
125 
126  using KinkTuple = std::tuple<int, reco::ConvexHullKinkTuple, HitOrderTupleList, HitOrderTupleList>;
127  using KinkTupleVec = std::vector<KinkTuple>;
128 
129  void orderHitsAlongEdge(const reco::ProjectedPointList&, const reco::ProjectedPoint&, const Eigen::Vector2f&, HitOrderTupleList&) const;
130 
132 
133  void fillConvexHullHists(reco::ClusterParameters&, bool) const;
134 
135  /**
136  * @brief FHICL parameters
137  */
138  bool fEnableMonitoring; ///<
139  size_t fMinTinyClusterSize; ///< Minimum size for a "tiny" cluster
140  float fMinGapSize; ///< Minimum gap size to break at gaps
141  float fMinEigen0To1Ratio; ///< Minimum ratio of eigen 0 to 1 to continue breaking
142  float fConvexHullKinkAngle; ///< Angle to declare a kink in convex hull calc
143  float fConvexHullMinSep; ///< Min hit separation to conisder in convex hull
144  mutable float fTimeToProcess; ///<
145 
146  /**
147  * @brief Histogram definitions
148  */
150 
160 
173 
174  /**
175  * @brief Tools
176  */
177  std::unique_ptr<lar_cluster3d::IClusterAlg> fClusterAlg; ///< Algorithm to do 3D space point clustering
178  PrincipalComponentsAlg fPCAAlg; // For running Principal Components Analysis
179 };
180 
182  fPCAAlg(pset.get<fhicl::ParameterSet>("PrincipalComponentsAlg"))
183 {
184  this->configure(pset);
185 }
186 
187 //------------------------------------------------------------------------------------------------------------------------------------------
188 
190 {
191 }
192 
193 //------------------------------------------------------------------------------------------------------------------------------------------
194 
196 {
197  fEnableMonitoring = pset.get<bool> ("EnableMonitoring", true );
198  fMinTinyClusterSize = pset.get<size_t>("MinTinyClusterSize", 40 );
199  fMinGapSize = pset.get<float >("MinClusterGapSize", 2.0 );
200  fMinEigen0To1Ratio = pset.get<float >("MinEigen0To1Ratio", 10.0 );
201  fConvexHullKinkAngle = pset.get<float >("ConvexHullKinkAgle", 0.95);
202  fConvexHullMinSep = pset.get<float >("ConvexHullMinSep", 0.65);
203  fClusterAlg = art::make_tool<lar_cluster3d::IClusterAlg>(pset.get<fhicl::ParameterSet>("ClusterAlg"));
204 
205  fTimeToProcess = 0.;
206 
207  return;
208 }
209 
210 void ConvexHullPathFinder::initializeHistograms(art::TFileDirectory& histDir)
211 {
212  // It is assumed that the input TFileDirectory has been set up to group histograms into a common
213  // folder at the calling routine's level. Here we create one more level of indirection to keep
214  // histograms made by this tool separate.
215  fFillHistograms = true;
216 
217  std::string dirName = "ConvexHullPath";
218 
219  art::TFileDirectory dir = histDir.mkdir(dirName.c_str());
220 
221  // Divide into two sets of hists... those for the overall cluster and
222  // those for the subclusters
223  fTopNum3DHits = dir.make<TH1F>("TopNum3DHits", "Number 3D Hits", 200, 0., 200.);
224  fTopNumEdges = dir.make<TH1F>("TopNumEdges", "Number Edges", 200, 0., 200.);
225  fTopEigen21Ratio = dir.make<TH1F>("TopEigen21Rat", "Eigen 2/1 Ratio", 100, 0., 1.);
226  fTopEigen20Ratio = dir.make<TH1F>("TopEigen20Rat", "Eigen 2/0 Ratio", 100, 0., 1.);
227  fTopEigen10Ratio = dir.make<TH1F>("TopEigen10Rat", "Eigen 1/0 Ratio", 100, 0., 1.);
228  fTopPrimaryLength = dir.make<TH1F>("TopPrimaryLen", "Primary Length", 200, 0., 200.);
229  fTopExtremeSep = dir.make<TH1F>("TopExtremeSep", "Extreme Dist", 200, 0., 200.);
230  fTopConvexCosEdge = dir.make<TH1F>("TopConvexCos", "CH Edge Cos", 100, -1., 1.);
231  fTopConvexEdgeLen = dir.make<TH1F>("TopConvexEdge", "CH Edge Len", 200, 0., 50.);
232 
233  fSubNum3DHits = dir.make<TH1F>("SubNum3DHits", "Number 3D Hits", 200, 0., 200.);
234  fSubNumEdges = dir.make<TH1F>("SubNumEdges", "Number Edges", 200, 0., 200.);
235  fSubEigen21Ratio = dir.make<TH1F>("SubEigen21Rat", "Eigen 2/1 Ratio", 100, 0., 1.);
236  fSubEigen20Ratio = dir.make<TH1F>("SubEigen20Rat", "Eigen 2/0 Ratio", 100, 0., 1.);
237  fSubEigen10Ratio = dir.make<TH1F>("SubEigen10Rat", "Eigen 1/0 Ratio", 100, 0., 1.);
238  fSubPrimaryLength = dir.make<TH1F>("SubPrimaryLen", "Primary Length", 200, 0., 200.);
239  fSubCosToPrevPCA = dir.make<TH1F>("SubCosToPrev", "Cos(theta)", 101, 0., 1.01);
240  fSubCosExtToPCA = dir.make<TH1F>("SubCosExtPCA", "Cos(theta)", 102, -1.01, 1.01);
241  fSubConvexCosEdge = dir.make<TH1F>("SubConvexCos", "CH Edge Cos", 100, -1., 1.);
242  fSubConvexEdgeLen = dir.make<TH1F>("SubConvexEdge", "CH Edge Len", 200, 0., 50.);
243  fSubMaxDefect = dir.make<TH1F>("SubMaxDefect", "Max Defect", 100, 0., 50.);
244  fSubUsedDefect = dir.make<TH1F>("SubUsedDefect", "Used Defect", 100, 0., 50.);
245 
246  return;
247 }
248 
250 {
251  /**
252  * @brief Top level interface for algorithm to consider pairs of clusters from the input
253  * list and determine if they are consistent with each other and, therefore, should
254  * be merged. This is done by looking at the PCA for each cluster and looking at the
255  * projection of the primary axis along the vector connecting their centers.
256  */
257 
258  // Initial clustering is done, now trim the list and get output parameters
259  cet::cpu_timer theClockBuildClusters;
260 
261  // Start clocks if requested
262  if (fEnableMonitoring) theClockBuildClusters.start();
263 
264  // This is the loop over candidate 3D clusters
265  // Note that it might be that the list of candidate clusters is modified by splitting
266  // So we use the following construct to make sure we get all of them
267  reco::ClusterParametersList::iterator clusterParametersListItr = clusterParametersList.begin();
268 
269  while(clusterParametersListItr != clusterParametersList.end())
270  {
271  // Dereference to get the cluster paramters
272  reco::ClusterParameters& clusterParameters = *clusterParametersListItr;
273 
274  // It turns out that computing the convex hull surrounding the points in the 2D projection onto the
275  // plane of largest spread in the PCA is a good way to break up the cluster... and we do it here since
276  // we (currently) want this to be part of the standard output
277  buildConvexHull(clusterParameters);
278 
279  // Make sure our cluster has enough hits...
280  if (clusterParameters.getHitPairListPtr().size() > fMinTinyClusterSize)
281  {
282  // Get an interim cluster list
283  reco::ClusterParametersList reclusteredParameters;
284 
285  // Call the main workhorse algorithm for building the local version of candidate 3D clusters
286  //******** Remind me why we need to call this at this point when the same hits will be used? ********
287  //fClusterAlg->Cluster3DHits(clusterParameters.getHitPairListPtr(), reclusteredParameters);
288  reclusteredParameters.push_back(clusterParameters);
289 
290  // Only process non-empty results
291  if (!reclusteredParameters.empty())
292  {
293  // Loop over the reclustered set
294  for (auto& cluster : reclusteredParameters)
295  {
296  // It turns out that computing the convex hull surrounding the points in the 2D projection onto the
297  // plane of largest spread in the PCA is a good way to break up the cluster... and we do it here since
298  // we (currently) want this to be part of the standard output
300 
301  // Break our cluster into smaller elements...
302  subDivideCluster(cluster, cluster.getFullPCA(), cluster.daughterList().end(), cluster.daughterList(), 0);
303 
304  // Add the daughters to the cluster
305  clusterParameters.daughterList().insert(clusterParameters.daughterList().end(),cluster);
306 
307  // If filling histograms we do the main cluster here
308  if (fFillHistograms)
309  {
310  reco::PrincipalComponents& fullPCA = cluster.getFullPCA();
311  std::vector<double> eigenValVec = {3. * std::sqrt(fullPCA.getEigenValues()[0]),
312  3. * std::sqrt(fullPCA.getEigenValues()[1]),
313  3. * std::sqrt(fullPCA.getEigenValues()[2])};
314  double eigen2To1Ratio = eigenValVec[0] / eigenValVec[1];
315  double eigen1To0Ratio = eigenValVec[1] / eigenValVec[2];
316  double eigen2To0Ratio = eigenValVec[2] / eigenValVec[2];
317  int num3DHits = cluster.getHitPairListPtr().size();
318  int numEdges = cluster.getConvexHull().getConvexHullEdgeList().size();
319 
320  fTopNum3DHits->Fill(std::min(num3DHits,199), 1.);
321  fTopNumEdges->Fill(std::min(numEdges,199), 1.);
322  fTopEigen21Ratio->Fill(eigen2To1Ratio, 1.);
323  fTopEigen20Ratio->Fill(eigen2To0Ratio, 1.);
324  fTopEigen10Ratio->Fill(eigen1To0Ratio, 1.);
325  fTopPrimaryLength->Fill(std::min(eigenValVec[2],199.), 1.);
326 // fTopExtremeSep->Fill(std::min(edgeLen,199.), 1.);
327  fillConvexHullHists(clusterParameters, true);
328  }
329  }
330  }
331  }
332 
333  // Go to next cluster parameters object
334  clusterParametersListItr++;
335  }
336 
337  if (fEnableMonitoring)
338  {
339  theClockBuildClusters.stop();
340 
341  fTimeToProcess = theClockBuildClusters.accumulated_real_time();
342  }
343 
344  mf::LogDebug("Cluster3D") << ">>>>> Cluster Path finding done" << std::endl;
345 
346  return;
347 }
348 
350  reco::PrincipalComponents& lastPCA,
352  reco::ClusterParametersList& outputClusterList,
353  int level) const
354 {
355  // This is a recursive routine to divide an input cluster, according to the maximum defect point of
356  // the convex hull until we reach the point of no further improvement.
357  // The assumption is that the input cluster is fully formed already, this routine then simply
358  // divides, if successful division into two pieces it then stores the results
359 
360  // No point in doing anything if we don't have enough space points
361  if (clusterToBreak.getHitPairListPtr().size() > size_t(2 * fMinTinyClusterSize))
362  {
363  // set an indention string
364 // std::string pluses(level/2, '+');
365 // std::string indent(level/2, ' ');
366 //
367 // indent += pluses;
368 
369  // We want to find the convex hull vertices that lie furthest from the line to/from the extreme points
370  // To find these we:
371  // 1) recover the extreme points
372  // 2) form the vector between them
373  // 3) loop through the vertices and keep track of distance to this vector
374  // 4) Sort the resulting list by furthest points and select the one we want
375  reco::ConvexHull& convexHull = clusterToBreak.getConvexHull();
376 
377  reco::ProjectedPointList::const_iterator extremePointListItr = convexHull.getConvexHullExtremePoints().begin();
378 
379  const reco::ClusterHit3D* firstEdgeHit = std::get<2>(*extremePointListItr++);
380  const reco::ClusterHit3D* secondEdgeHit = std::get<2>(*extremePointListItr);
381  Eigen::Vector3f edgeVec(secondEdgeHit->getPosition()[0] - firstEdgeHit->getPosition()[0],
382  secondEdgeHit->getPosition()[1] - firstEdgeHit->getPosition()[1],
383  secondEdgeHit->getPosition()[2] - firstEdgeHit->getPosition()[2]);
384 // double edgeLen = edgeVec.norm();
385 
386  // normalize it
387  edgeVec.normalize();
388 
389  // Recover the PCA for the input cluster
390  reco::PrincipalComponents& fullPCA = clusterToBreak.getFullPCA();
391  Eigen::Vector3f fullPrimaryVec(fullPCA.getEigenVectors().row(2));
392 
393  // Calculate the doca to the PCA primary axis for each 3D hit
394  // Importantly, this also gives us the arclength along that axis to the hit
395 // fPCAAlg.PCAAnalysis_calc3DDocas(clusHitPairVector, fullPCA);
396 
397  // Sort the hits along the PCA
398 // clusHitPairVector.sort([](const auto& left, const auto& right){return left->getArclenToPoca() < right->getArclenToPoca();});
399 
400  // Get a temporary container to hol
401  reco::ClusterParametersList tempClusterParametersList;
402 
403  // Try breaking clusters by finding the "maximum defect" point.
404  // If this fails the fallback in the event of still large clusters is to split in half
405  // If starting with the top level cluster then we first try to break at the kinks
406  if (level == 0)
407  {
408  // if (!breakClusterByMaxDefect(clusterToBreak, clusHitPairVector, tempClusterParametersList, level))
409  if (!breakClusterByKinks(clusterToBreak, tempClusterParametersList, level))
410  {
411  // Look to see if we can break at a gap
412  if (!breakClusterAtBigGap(clusterToBreak, tempClusterParametersList, level))
413  {
414  // It might be that we have a large deviation in the convex hull...
415  if (!breakClusterByMaxDefect(clusterToBreak, tempClusterParametersList, level))
416  {
417  std::vector<double> eigenValVec = {3. * std::sqrt(fullPCA.getEigenValues()[0]),
418  3. * std::sqrt(fullPCA.getEigenValues()[1]),
419  3. * std::sqrt(fullPCA.getEigenValues()[2])};
420 
421  // Well, we don't want "flippers" so make sure the edge has some teeth to it
422  //if (edgeLen > 10.) breakClusterInHalf(clusterToBreak, clusHitPairVector, tempClusterParametersList, level);
423  if (eigenValVec[2] > fMinEigen0To1Ratio * eigenValVec[1]) breakClusterInHalf(clusterToBreak, tempClusterParametersList, level);
424  }
425  }
426  }
427 
428  }
429  // Otherwise, change the order
430  else
431  {
432  // if (!breakClusterByMaxDefect(clusterToBreak, clusHitPairVector, tempClusterParametersList, level))
433  if (!breakClusterAtBigGap(clusterToBreak, tempClusterParametersList, level))
434  {
435  // Look to see if we can break at a gap
436  if (!breakClusterByKinks(clusterToBreak, tempClusterParametersList, level))
437  {
438  // It might be that we have a large deviation in the convex hull...
439  if (!breakClusterByMaxDefect(clusterToBreak, tempClusterParametersList, level))
440  {
441  std::vector<double> eigenValVec = {3. * std::sqrt(fullPCA.getEigenValues()[0]),
442  3. * std::sqrt(fullPCA.getEigenValues()[1]),
443  3. * std::sqrt(fullPCA.getEigenValues()[2])};
444 
445  // Well, we don't want "flippers" so make sure the edge has some teeth to it
446  //if (edgeLen > 10.) breakClusterInHalf(clusterToBreak, clusHitPairVector, tempClusterParametersList, level);
447  if (eigenValVec[2] > fMinEigen0To1Ratio * eigenValVec[1]) breakClusterInHalf(clusterToBreak, tempClusterParametersList, level);
448  }
449  }
450  }
451 
452  }
453 
454  // Can only end with no candidate clusters or two so don't
455  for(auto& clusterParams : tempClusterParametersList)
456  {
457  size_t curOutputClusterListSize = outputClusterList.size();
458 
459  positionItr = subDivideCluster(clusterParams, fullPCA, positionItr, outputClusterList, level+4);
460 
461  // If the cluster we sent in was successfully broken then the position iterator will be shifted
462  // This means we don't want to restore the current cluster here
463  if (curOutputClusterListSize < outputClusterList.size()) continue;
464 
465  // The current cluster was not further subdivided so we store its info here
466  // I think this is where we fill out the rest of the parameters?
467  // Start by adding the 2D hits...
468  // See if we can avoid duplicates by temporarily transferring to a set
469  std::set<const reco::ClusterHit2D*> hitSet;
470 
471  // Loop through 3D hits to get a set of unique 2D hits
472  for(const auto& hit3D : clusterParams.getHitPairListPtr())
473  {
474  for(const auto& hit2D : hit3D->getHits())
475  {
476  if (hit2D) hitSet.insert(hit2D);
477  }
478  }
479 
480  // Now add these to the new cluster
481  for(const auto& hit2D : hitSet)
482  {
483  hit2D->setStatusBit(reco::ClusterHit2D::USED);
484  clusterParams.UpdateParameters(hit2D);
485  }
486 
487  positionItr = outputClusterList.insert(positionItr,clusterParams);
488 
489  // Are we filling histograms
490  if (fFillHistograms)
491  {
492  std::vector<double> eigenValVec = {3. * std::sqrt(fullPCA.getEigenValues()[0]),
493  3. * std::sqrt(fullPCA.getEigenValues()[1]),
494  3. * std::sqrt(fullPCA.getEigenValues()[2])};
495 
496  // Recover the new fullPCA
497  reco::PrincipalComponents& newFullPCA = clusterParams.getFullPCA();
498 
499  Eigen::Vector3f newPrimaryVec(fullPCA.getEigenVectors().row(2));
500  Eigen::Vector3f lastPrimaryVec(newFullPCA.getEigenVectors().row(2));
501 
502  int num3DHits = clusterParams.getHitPairListPtr().size();
503  int numEdges = clusterParams.getConvexHull().getConvexHullEdgeList().size();
504  float cosToLast = newPrimaryVec.dot(lastPrimaryVec);
505  double eigen2To1Ratio = eigenValVec[0] / eigenValVec[1];
506  double eigen1To0Ratio = eigenValVec[1] / eigenValVec[2];
507  double eigen2To0Ratio = eigenValVec[0] / eigenValVec[2];
508 
509  fSubNum3DHits->Fill(std::min(num3DHits,199), 1.);
510  fSubNumEdges->Fill(std::min(numEdges,199), 1.);
511  fSubEigen21Ratio->Fill(eigen2To1Ratio, 1.);
512  fSubEigen20Ratio->Fill(eigen2To0Ratio, 1.);
513  fSubEigen10Ratio->Fill(eigen1To0Ratio, 1.);
514  fSubCosToPrevPCA->Fill(cosToLast, 1.);
515  fSubPrimaryLength->Fill(std::min(eigenValVec[2],199.), 1.);
516  fSubCosExtToPCA->Fill(fullPrimaryVec.dot(edgeVec), 1.);
517  }
518 
519  // The above points to the element, want the next element
520  positionItr++;
521  }
522  }
523 
524  return positionItr;
525 }
526 
527 bool ConvexHullPathFinder::makeCandidateCluster(Eigen::Vector3f& primaryPCA,
528  reco::ClusterParameters& candCluster,
529  reco::HitPairListPtr::iterator firstHitItr,
531  int level) const
532 {
533  std::string indent(level/2, ' ');
534 
535  reco::HitPairListPtr& hitPairListPtr = candCluster.getHitPairListPtr();
536 
537  // size the container...
538  hitPairListPtr.resize(std::distance(firstHitItr,lastHitItr));
539 
540  // and copy the hits into the container
541  std::copy(firstHitItr,lastHitItr,hitPairListPtr.begin());
542 
543  // Will we want to store this cluster?
544  bool keepThisCluster(false);
545 
546  // Must have a valid pca
547  if (completeCandidateCluster(primaryPCA, candCluster, level))
548  {
549  // Recover the new fullPCA
550  reco::PrincipalComponents& newFullPCA = candCluster.getFullPCA();
551 
552  // Need to check if the PCA direction has been reversed
553  Eigen::Vector3f newPrimaryVec(newFullPCA.getEigenVectors().row(2));
554 
555  std::vector<double> eigenValVec = {3. * std::sqrt(newFullPCA.getEigenValues()[0]),
556  3. * std::sqrt(newFullPCA.getEigenValues()[1]),
557  3. * std::sqrt(newFullPCA.getEigenValues()[2])};
558  double cosNewToLast = std::abs(primaryPCA.dot(newPrimaryVec));
559  double eigen2To1Ratio = eigenValVec[0] / eigenValVec[1];
560  double eigen1To0Ratio = eigenValVec[1] / eigenValVec[2];
561 
562  // Create a rough cut intended to tell us when we've reached the land of diminishing returns
563 // if (candCluster.getBestEdgeList().size() > 4 && cosNewToLast > 0.25 && eigen2To1Ratio < 0.9 && eigen2To0Ratio > 0.001)
564  if (candCluster.getConvexHull().getConvexHullEdgeList().size() > 4 && cosNewToLast > 0.25 && eigen2To1Ratio > 0.01 && eigen2To1Ratio < 0.99 && eigen1To0Ratio < 0.5)
565  {
566  keepThisCluster = true;
567  }
568  }
569 
570  return keepThisCluster;
571 }
572 
573 bool ConvexHullPathFinder::makeCandidateCluster(Eigen::Vector3f& primaryPCA,
574  reco::ClusterParameters& candCluster,
575  HitOrderTupleList& orderedList,
576  int level) const
577 {
578  std::string indent(level/2, ' ');
579 
580  reco::HitPairListPtr& hitPairListPtr = candCluster.getHitPairListPtr();
581 
582  // Fill the list the old fashioned way...
583  for(const auto& tupleVal : orderedList) hitPairListPtr.emplace_back(std::get<2>(std::get<2>(tupleVal)));
584 
585  // Will we want to store this cluster?
586  bool keepThisCluster(false);
587 
588  // Must have a valid pca
589  if (completeCandidateCluster(primaryPCA, candCluster, level))
590  {
591  // Recover the new fullPCA
592  reco::PrincipalComponents& newFullPCA = candCluster.getFullPCA();
593 
594  // Need to check if the PCA direction has been reversed
595  Eigen::Vector3f newPrimaryVec(newFullPCA.getEigenVectors().row(2));
596 
597  std::vector<double> eigenValVec = {3. * std::sqrt(newFullPCA.getEigenValues()[0]),
598  3. * std::sqrt(newFullPCA.getEigenValues()[1]),
599  3. * std::sqrt(newFullPCA.getEigenValues()[2])};
600  double cosNewToLast = std::abs(primaryPCA.dot(newPrimaryVec));
601  double eigen2To1Ratio = eigenValVec[0] / eigenValVec[1];
602  double eigen1To0Ratio = eigenValVec[1] / eigenValVec[2];
603 
604  // Create a rough cut intended to tell us when we've reached the land of diminishing returns
605  // if (candCluster.getBestEdgeList().size() > 4 && cosNewToLast > 0.25 && eigen2To1Ratio < 0.9 && eigen2To0Ratio > 0.001)
606  if (candCluster.getBestEdgeList().size() > 4 && cosNewToLast > 0.25 && eigen2To1Ratio > 0.01 && eigen2To1Ratio < 0.99 && eigen1To0Ratio < 0.5)
607  {
608  keepThisCluster = true;
609  }
610  }
611 
612  return keepThisCluster;
613 }
614 
615 bool ConvexHullPathFinder::completeCandidateCluster(Eigen::Vector3f& primaryPCA, reco::ClusterParameters& candCluster, int level) const
616 {
617  // First stage of feature extraction runs here
618  fPCAAlg.PCAAnalysis_3D(candCluster.getHitPairListPtr(), candCluster.getFullPCA());
619 
620  // Recover the new fullPCA
621  reco::PrincipalComponents& newFullPCA = candCluster.getFullPCA();
622 
623  // Will we want to store this cluster?
624  bool keepThisCluster(false);
625 
626  // Must have a valid pca
627  if (newFullPCA.getSvdOK())
628  {
629  // Need to check if the PCA direction has been reversed
630  Eigen::Vector3f newPrimaryVec(newFullPCA.getEigenVectors().row(2));
631 
632  // If the PCA's are opposite the flip the axes
633  if (primaryPCA.dot(newPrimaryVec) < 0.)
634  {
635  for(size_t vecIdx = 0; vecIdx < 3; vecIdx++) newFullPCA.flipAxis(vecIdx);
636  }
637 
638  // Set the skeleton PCA to make sure it has some value
639  candCluster.getSkeletonPCA() = candCluster.getFullPCA();
640 
641  // Be sure to compute the oonvex hull surrounding the now broken cluster
642  buildConvexHull(candCluster, level+2);
643 
644  keepThisCluster = true;
645  }
646 
647  return keepThisCluster;
648 }
649 
651 {
652  // Set up container to keep track of edges
653  using HitKinkTuple = std::tuple<int, reco::HitPairListPtr::iterator>;
654  using HitKinkTupleVec = std::vector<HitKinkTuple>;
655 
656  // Recover our hits
657  reco::HitPairListPtr& hitList = clusterToBreak.getHitPairListPtr();
658 
659  // Set up container to keep track of edges
660  HitKinkTupleVec kinkTupleVec;
661 
662  reco::ConvexHull& convexHull = clusterToBreak.getConvexHull();
663  reco::ConvexHullKinkTupleList& kinkPointList = convexHull.getConvexHullKinkPoints();
664 
665  for(auto& kink : kinkPointList)
666  {
667  const reco::ClusterHit3D* hit3D = std::get<2>(std::get<0>(kink));
668 
669  reco::HitPairListPtr::iterator kinkItr = std::find(hitList.begin(),hitList.end(),hit3D);
670 
671  if (kinkItr == hitList.end()) continue;
672 
673  int numStartToKink = std::distance(hitList.begin(),kinkItr);
674  int numKinkToEnd = std::distance(kinkItr, hitList.end());
675  int minNumHits = std::min(numStartToKink,numKinkToEnd);
676 
677  if (minNumHits > int(fMinTinyClusterSize)) kinkTupleVec.emplace_back(minNumHits,kinkItr);
678  }
679 
680  // No work if the list is empty
681  if (!kinkTupleVec.empty())
682  {
683  std::sort(kinkTupleVec.begin(),kinkTupleVec.end(),[](const auto& left,const auto& right){return std::get<0>(left) > std::get<0>(right);});
684 
685  // Recover the kink point
686  reco::HitPairListPtr::iterator kinkItr = std::get<1>(kinkTupleVec.front());
687 
688  // Set up to split the input cluster
689  outputClusterList.push_back(reco::ClusterParameters());
690 
691  reco::ClusterParameters& clusterParams1 = outputClusterList.back();
692 
693  reco::PrincipalComponents& fullPCA(clusterToBreak.getFullPCA());
694  Eigen::Vector3f fullPrimaryVec(fullPCA.getEigenVectors().row(2));
695 
696  if (makeCandidateCluster(fullPrimaryVec, clusterParams1, hitList.begin(), kinkItr, level))
697  {
698  outputClusterList.push_back(reco::ClusterParameters());
699 
700  reco::ClusterParameters& clusterParams2 = outputClusterList.back();
701 
702  makeCandidateCluster(fullPrimaryVec, clusterParams2, kinkItr, hitList.end(), level);
703 
704  if (fFillHistograms)
705  {
706  fillConvexHullHists(clusterParams1, false);
707  fillConvexHullHists(clusterParams2, false);
708  }
709  }
710 
711  // If we did not make 2 clusters then be sure to clear the output list
712  if (outputClusterList.size() != 2) outputClusterList.clear();
713  }
714 
715  return !outputClusterList.empty();
716 }
717 
719 {
720  // Set up container to keep track of edges
721  KinkTupleVec kinkTupleVec;
722 
723  reco::ConvexHull& convexHull = clusterToBreak.getConvexHull();
724  reco::ProjectedPointList& pointList = convexHull.getProjectedPointList();
725  reco::ConvexHullKinkTupleList& kinkPointList = convexHull.getConvexHullKinkPoints();
726 
727  for(auto& kink : kinkPointList)
728  {
729  // Make an instance of the vec value to avoid copying if we can...
730  kinkTupleVec.push_back(KinkTuple());
731 
732  KinkTuple& kinkTuple = kinkTupleVec.back();
733 
734  std::get<1>(kinkTuple) = kink;
735 
736  // Recover vectors, want them pointing away from intersection point
737  Eigen::Vector2f firstEdge = -std::get<1>(kink);
738  HitOrderTupleList& firstList = std::get<2>(kinkTuple);
739  HitOrderTupleList& secondList = std::get<3>(kinkTuple);
740 
741  orderHitsAlongEdge(pointList, std::get<0>(kink), firstEdge, firstList);
742 
743  if (firstList.size() > fMinTinyClusterSize)
744  {
745  Eigen::Vector2f secondEdge = std::get<2>(kink);
746 
747  orderHitsAlongEdge(pointList, std::get<0>(kink), secondEdge, secondList);
748 
749  if (secondList.size() > fMinTinyClusterSize)
750  std::get<0>(kinkTuple) = std::min(firstList.size(),secondList.size());
751  }
752 
753  // Special handling...
754  if (firstList.size() + secondList.size() > pointList.size())
755  {
756  if (firstList.size() > secondList.size()) pruneHitOrderTupleLists(firstList,secondList);
757  else pruneHitOrderTupleLists(secondList,firstList);
758 
759  std::get<0>(kinkTuple) = std::min(firstList.size(),secondList.size());
760  }
761 
762  if (std::get<0>(kinkTuple) < int(fMinTinyClusterSize)) kinkTupleVec.pop_back();
763  }
764 
765  // No work if the list is empty
766  if (!kinkTupleVec.empty())
767  {
768  // If more than one then want the kink with the most elements both sizes
769  std::sort(kinkTupleVec.begin(),kinkTupleVec.end(),[](const auto& left,const auto& right){return std::get<0>(left) > std::get<0>(right);});
770 
771  // Recover the kink point
772  KinkTuple& kinkTuple = kinkTupleVec.front();
773 
774  // Set up to split the input cluster
775  outputClusterList.push_back(reco::ClusterParameters());
776 
777  reco::ClusterParameters& clusterParams1 = outputClusterList.back();
778 
779  reco::PrincipalComponents& fullPCA(clusterToBreak.getFullPCA());
780  Eigen::Vector3f fullPrimaryVec(fullPCA.getEigenVectors().row(2));
781 
782  if (makeCandidateCluster(fullPrimaryVec, clusterParams1, std::get<2>(kinkTuple), level))
783  {
784  outputClusterList.push_back(reco::ClusterParameters());
785 
786  reco::ClusterParameters& clusterParams2 = outputClusterList.back();
787 
788  makeCandidateCluster(fullPrimaryVec, clusterParams2, std::get<3>(kinkTuple), level);
789 
790  if (fFillHistograms)
791  {
792  fillConvexHullHists(clusterParams1, false);
793  fillConvexHullHists(clusterParams2, false);
794  }
795  }
796 
797  // If we did not make 2 clusters then be sure to clear the output list
798  if (outputClusterList.size() != 2) outputClusterList.clear();
799  }
800 
801  return !outputClusterList.empty();
802 }
803 
805  const reco::ProjectedPoint& point,
806  const Eigen::Vector2f& edge,
807  HitOrderTupleList& orderedList) const
808 {
809  // Use the input kink point as the start point of the edge
810  Eigen::Vector2f kinkPos(std::get<0>(point),std::get<1>(point));
811 
812  // Loop over the input hits
813  for (const auto& hit : hitList)
814  {
815  // Now we need to calculate the doca and poca...
816  // Start by getting this hits position
817  Eigen::Vector2f hitPos(std::get<0>(hit),std::get<1>(hit));
818 
819  // Form a TVector from this to the cluster average position
820  Eigen::Vector2f hitToKinkVec = hitPos - kinkPos;
821 
822  // With this we can get the arclength to the doca point
823  float arcLenToPoca = hitToKinkVec.dot(edge);
824 
825  // Require the hit to not be past the kink point
826  if (arcLenToPoca < 0.) continue;
827 
828  // Get the coordinates along the axis for this point
829  Eigen::Vector2f pocaPos = kinkPos + arcLenToPoca * edge;
830 
831  // Now get doca and poca
832  Eigen::Vector2f pocaPosToHitPos = hitPos - pocaPos;
833  float pocaToAxis = pocaPosToHitPos.norm();
834 
835  std::cout << "-- arcLenToPoca: " << arcLenToPoca << ", doca: " << pocaToAxis << std::endl;
836 
837  orderedList.emplace_back(arcLenToPoca,pocaToAxis,hit);
838  }
839 
840  // Sort the list in order of ascending distance from the kink point
841  orderedList.sort([](const auto& left,const auto& right){return std::get<0>(left) < std::get<0>(right);});
842 
843  return;
844 }
845 
847 {
848  // Assume the first list is the short one, so we loop through the elements of that list..
849  HitOrderTupleList::iterator shortItr = shortList.begin();
850 
851  while(shortItr != shortList.end())
852  {
853  // Recover the search key
854  const reco::ClusterHit3D* hit3D = std::get<2>(std::get<2>(*shortItr));
855 
856  // Ok, find the corresponding point in the other list...
857  HitOrderTupleList::iterator longItr = std::find_if(longList.begin(),longList.end(),[&hit3D](const auto& elem){return hit3D == std::get<2>(std::get<2>(elem));});
858 
859  if (longItr != longList.end())
860  {
861  if (std::get<1>(*longItr) < std::get<1>(*shortItr))
862  {
863  shortItr = shortList.erase(shortItr);
864  }
865  else
866  {
867  longItr = longList.erase(longItr);
868  shortItr++;
869  }
870  }
871  else shortItr++;
872  }
873 
874 
875  return;
876 }
877 
879 {
880  // Set up container to keep track of edges
881  using DistEdgeTuple = std::tuple<float, const reco::EdgeTuple*>;
882  using DistEdgeTupleVec = std::vector<DistEdgeTuple>;
883 
884  DistEdgeTupleVec distEdgeTupleVec;
885 
886  reco::ProjectedPointList::const_iterator extremePointListItr = clusterToBreak.getConvexHull().getConvexHullExtremePoints().begin();
887 
888  const reco::ClusterHit3D* firstEdgeHit = std::get<2>(*extremePointListItr++);
889  const reco::ClusterHit3D* secondEdgeHit = std::get<2>(*extremePointListItr);
890  Eigen::Vector3f edgeVec(secondEdgeHit->getPosition()[0] - firstEdgeHit->getPosition()[0],
891  secondEdgeHit->getPosition()[1] - firstEdgeHit->getPosition()[1],
892  secondEdgeHit->getPosition()[2] - firstEdgeHit->getPosition()[2]);
893  double edgeLen = edgeVec.norm();
894 
895  // normalize it
896  edgeVec.normalize();
897 
898  // Now loop through all the edges and search for the furthers point
899  for(const auto& edge : clusterToBreak.getBestEdgeList())
900  {
901  const reco::ClusterHit3D* nextEdgeHit = std::get<0>(edge); // recover the first point
902 
903  // Create vector to this point from the longest edge
904  Eigen::Vector3f hitToEdgeVec(nextEdgeHit->getPosition()[0] - firstEdgeHit->getPosition()[0],
905  nextEdgeHit->getPosition()[1] - firstEdgeHit->getPosition()[1],
906  nextEdgeHit->getPosition()[2] - firstEdgeHit->getPosition()[2]);
907 
908  // Get projection
909  float hitProjection = hitToEdgeVec.dot(edgeVec);
910 
911  // Require that the point is really "opposite" the longest edge
912  if (hitProjection > 0. && hitProjection < edgeLen)
913  {
914  Eigen::Vector3f distToHitVec = hitToEdgeVec - hitProjection * edgeVec;
915  float distToHit = distToHitVec.norm();
916 
917  distEdgeTupleVec.emplace_back(distToHit,&edge);
918  }
919  }
920 
921  std::sort(distEdgeTupleVec.begin(),distEdgeTupleVec.end(),[](const auto& left,const auto& right){return std::get<0>(left) > std::get<0>(right);});
922 
923  reco::PrincipalComponents& fullPCA(clusterToBreak.getFullPCA());
924  Eigen::Vector3f fullPrimaryVec(fullPCA.getEigenVectors().row(2));
925 
926  // Recover our hits
927  reco::HitPairListPtr& hitList = clusterToBreak.getHitPairListPtr();
928 
929  // Calculate the doca to the PCA primary axis for each 3D hit
930  // Importantly, this also gives us the arclength along that axis to the hit
931  fPCAAlg.PCAAnalysis_calc3DDocas(hitList, fullPCA);
932 
933  // Sort the hits along the PCA
934  hitList.sort([](const auto& left, const auto& right){return left->getArclenToPoca() < right->getArclenToPoca();});
935 
936  // Get a temporary container to hol
937  float usedDefectDist(0.);
938 
939  for(const auto& distEdgeTuple : distEdgeTupleVec)
940  {
941  const reco::EdgeTuple& edgeTuple = *std::get<1>(distEdgeTuple);
942  const reco::ClusterHit3D* edgeHit = std::get<0>(edgeTuple);
943 
944  usedDefectDist = std::get<0>(distEdgeTuple);
945 
946  // Now find the hit identified above as furthest away
947  reco::HitPairListPtr::iterator vertexItr = std::find(hitList.begin(),hitList.end(),edgeHit);
948 
949  // Make sure enough hits either side, otherwise we just keep the current cluster
950  if (vertexItr == hitList.end() || std::distance(hitList.begin(),vertexItr) < int(fMinTinyClusterSize) || std::distance(vertexItr,hitList.end()) < int(fMinTinyClusterSize)) continue;
951 
952  outputClusterList.push_back(reco::ClusterParameters());
953 
954  reco::ClusterParameters& clusterParams1 = outputClusterList.back();
955 
956  if (makeCandidateCluster(fullPrimaryVec, clusterParams1, hitList.begin(), vertexItr, level))
957  {
958  outputClusterList.push_back(reco::ClusterParameters());
959 
960  reco::ClusterParameters& clusterParams2 = outputClusterList.back();
961 
962  if (makeCandidateCluster(fullPrimaryVec, clusterParams2, vertexItr, hitList.end(), level))
963  {
964  if (fFillHistograms)
965  {
966  fSubMaxDefect->Fill(std::get<0>(distEdgeTupleVec.front()), 1.);
967  fSubUsedDefect->Fill(usedDefectDist, 1.);
968  }
969  break;
970  }
971  }
972 
973  // If here then we could not make two valid clusters and so we try again
974  outputClusterList.clear();
975  }
976 
977  return !outputClusterList.empty();
978 }
979 
981 {
982  reco::PrincipalComponents& fullPCA(clusterToBreak.getFullPCA());
983  Eigen::Vector3f fullPrimaryVec(fullPCA.getEigenVectors().row(2));
984 
985  // Recover our hits
986  reco::HitPairListPtr& hitList = clusterToBreak.getHitPairListPtr();
987 
988  // Calculate the doca to the PCA primary axis for each 3D hit
989  // Importantly, this also gives us the arclength along that axis to the hit
990  fPCAAlg.PCAAnalysis_calc3DDocas(hitList, fullPCA);
991 
992  // Sort the hits along the PCA
993  hitList.sort([](const auto& left, const auto& right){return left->getArclenToPoca() < right->getArclenToPoca();});
994 
995  reco::HitPairListPtr::iterator vertexItr = hitList.begin();
996 
997  std::advance(vertexItr, hitList.size()/2);
998 
999  outputClusterList.push_back(reco::ClusterParameters());
1000 
1001  reco::ClusterParameters& clusterParams1 = outputClusterList.back();
1002 
1003  if (makeCandidateCluster(fullPrimaryVec, clusterParams1, hitList.begin(), vertexItr, level))
1004  {
1005  outputClusterList.push_back(reco::ClusterParameters());
1006 
1007  reco::ClusterParameters& clusterParams2 = outputClusterList.back();
1008 
1009  makeCandidateCluster(fullPrimaryVec, clusterParams2, vertexItr, hitList.end(), level);
1010  }
1011 
1012  if (outputClusterList.size() != 2) outputClusterList.clear();
1013 
1014  return !outputClusterList.empty();
1015 }
1016 
1018 {
1019  // Idea here is to scan the input hit list (assumed ordered along the current PCA) and look for "large" gaps
1020  // Here a gap is determined when the hits were ordered by their distance along the primary PCA to their doca to it.
1021  reco::PrincipalComponents& fullPCA(clusterToBreak.getFullPCA());
1022 
1023  // Recover our hits
1024  reco::HitPairListPtr& hitList = clusterToBreak.getHitPairListPtr();
1025 
1026  // Calculate the doca to the PCA primary axis for each 3D hit
1027  // Importantly, this also gives us the arclength along that axis to the hit
1028  fPCAAlg.PCAAnalysis_calc3DDocas(hitList, fullPCA);
1029 
1030  // Sort the hits along the PCA
1031  hitList.sort([](const auto& left, const auto& right){return left->getArclenToPoca() < right->getArclenToPoca();});
1032 
1033  // Loop through the input hit list and keep track of first hit of largest gap
1034  reco::HitPairListPtr::iterator bigGapHitItr = hitList.begin();
1035  float biggestGap = 0.;
1036 
1037  reco::HitPairListPtr::iterator lastHitItr = hitList.begin();
1038 
1039  for(reco::HitPairListPtr::iterator hitItr = hitList.begin(); hitItr != hitList.end(); hitItr++)
1040  {
1041  float currentGap = std::abs((*hitItr)->getArclenToPoca() - (*lastHitItr)->getArclenToPoca());
1042 
1043  if (currentGap > biggestGap)
1044  {
1045  bigGapHitItr = hitItr; // Note that this is an iterator and will be the "end" going from begin, and "begin" for second half
1046  biggestGap = currentGap;
1047  }
1048 
1049  lastHitItr = hitItr;
1050  }
1051 
1052  // Require some minimum gap size...
1053  if (biggestGap > fMinGapSize)
1054  {
1055  outputClusterList.push_back(reco::ClusterParameters());
1056 
1057  reco::ClusterParameters& clusterParams1 = outputClusterList.back();
1058 
1059  reco::PrincipalComponents& fullPCA(clusterToBreak.getFullPCA());
1060  Eigen::Vector3f fullPrimaryVec(fullPCA.getEigenVectors().row(2));
1061 
1062  if (makeCandidateCluster(fullPrimaryVec, clusterParams1, hitList.begin(), bigGapHitItr, level))
1063  {
1064  outputClusterList.push_back(reco::ClusterParameters());
1065 
1066  reco::ClusterParameters& clusterParams2 = outputClusterList.back();
1067 
1068  makeCandidateCluster(fullPrimaryVec, clusterParams2, bigGapHitItr, hitList.end(), level);
1069  }
1070 
1071  if (outputClusterList.size() != 2) outputClusterList.clear();
1072  }
1073 
1074  return !outputClusterList.empty();
1075 }
1076 
1077 
1079 {
1080  // set an indention string
1081  std::string minuses(level/2, '-');
1082  std::string indent(level/2, ' ');
1083 
1084  indent += minuses;
1085 
1086  // The plan is to build the enclosing 2D polygon around the points in the PCA plane of most spread for this cluster
1087  // To do so we need to start by building a list of 2D projections onto the plane of most spread...
1088  reco::PrincipalComponents& pca = clusterParameters.getFullPCA();
1089 
1090  // Recover the parameters from the Principal Components Analysis that we need to project and accumulate
1091  const Eigen::Vector3f& pcaCenter = pca.getAvePosition();
1092  reco::ConvexHull& convexHull = clusterParameters.getConvexHull();
1093  reco::ProjectedPointList& pointList = convexHull.getProjectedPointList();
1094 
1095  // Loop through hits and do projection to plane
1096  for(const auto& hit3D : clusterParameters.getHitPairListPtr())
1097  {
1098  Eigen::Vector3f pcaToHitVec(hit3D->getPosition()[0] - pcaCenter(0),
1099  hit3D->getPosition()[1] - pcaCenter(1),
1100  hit3D->getPosition()[2] - pcaCenter(2));
1101  Eigen::Vector3f pcaToHit = pca.getEigenVectors() * pcaToHitVec;
1102 
1103  pointList.emplace_back(pcaToHit(1),pcaToHit(2),hit3D);
1104  }
1105 
1106  // Sort the point vec by increasing x, then increase y
1107  pointList.sort([](const auto& left, const auto& right){return (std::abs(std::get<0>(left) - std::get<0>(right)) > std::numeric_limits<float>::epsilon()) ? std::get<0>(left) < std::get<0>(right) : std::get<1>(left) < std::get<1>(right);});
1108 
1109  // containers for finding the "best" hull...
1110  std::vector<ConvexHull> convexHullVec;
1111  std::vector<reco::ProjectedPointList> rejectedListVec;
1112  bool increaseDepth(pointList.size() > 3);
1113  float lastArea(std::numeric_limits<float>::max());
1114 
1115  while(increaseDepth)
1116  {
1117  // Get another convexHull container
1118  convexHullVec.push_back(ConvexHull(pointList, fConvexHullKinkAngle, fConvexHullMinSep));
1119  rejectedListVec.push_back(reco::ProjectedPointList());
1120 
1121  const ConvexHull& convexHull = convexHullVec.back();
1122  reco::ProjectedPointList& rejectedList = rejectedListVec.back();
1123  const reco::ProjectedPointList& convexHullPoints = convexHull.getConvexHull();
1124 
1125  increaseDepth = false;
1126 
1127  if (convexHull.getConvexHullArea() > 0.)
1128  {
1129  if (convexHullVec.size() < 2 || convexHull.getConvexHullArea() < 0.8 * lastArea)
1130  {
1131  for(auto& point : convexHullPoints)
1132  {
1133  pointList.remove(point);
1134  rejectedList.emplace_back(point);
1135  }
1136  lastArea = convexHull.getConvexHullArea();
1137 // increaseDepth = true;
1138  }
1139  }
1140  }
1141 
1142  // do we have a valid convexHull?
1143  while(!convexHullVec.empty() && convexHullVec.back().getConvexHullArea() < 0.5)
1144  {
1145  convexHullVec.pop_back();
1146  rejectedListVec.pop_back();
1147  }
1148 
1149  // If we found the convex hull then build edges around the region
1150  if (!convexHullVec.empty())
1151  {
1152  size_t nRejectedTotal(0);
1153  reco::HitPairListPtr hitPairListPtr = clusterParameters.getHitPairListPtr();
1154 
1155  for(const auto& rejectedList : rejectedListVec)
1156  {
1157  nRejectedTotal += rejectedList.size();
1158 
1159  for(const auto& rejectedPoint : rejectedList)
1160  {
1161  if (convexHullVec.back().findNearestDistance(rejectedPoint) > 0.5)
1162  hitPairListPtr.remove(std::get<2>(rejectedPoint));
1163  }
1164  }
1165 
1166  // Now add "edges" to the cluster to describe the convex hull (for the display)
1167  reco::ProjectedPointList& convexHullPointList = convexHull.getConvexHullPointList();
1168  reco::Hit3DToEdgeMap& edgeMap = convexHull.getConvexHullEdgeMap();
1169  reco::EdgeList& edgeList = convexHull.getConvexHullEdgeList();
1170 
1171  reco::ProjectedPoint lastPoint = convexHullVec.back().getConvexHull().front();
1172 
1173  for(auto& curPoint : convexHullVec.back().getConvexHull())
1174  {
1175  if (curPoint == lastPoint) continue;
1176 
1177  const reco::ClusterHit3D* lastPoint3D = std::get<2>(lastPoint);
1178  const reco::ClusterHit3D* curPoint3D = std::get<2>(curPoint);
1179 
1180  float distBetweenPoints = (curPoint3D->getPosition()[0] - lastPoint3D->getPosition()[0]) * (curPoint3D->getPosition()[0] - lastPoint3D->getPosition()[0])
1181  + (curPoint3D->getPosition()[1] - lastPoint3D->getPosition()[1]) * (curPoint3D->getPosition()[1] - lastPoint3D->getPosition()[1])
1182  + (curPoint3D->getPosition()[2] - lastPoint3D->getPosition()[2]) * (curPoint3D->getPosition()[2] - lastPoint3D->getPosition()[2]);
1183 
1184  distBetweenPoints = std::sqrt(distBetweenPoints);
1185 
1186  reco::EdgeTuple edge(lastPoint3D,curPoint3D,distBetweenPoints);
1187 
1188  convexHullPointList.push_back(curPoint);
1189  edgeMap[lastPoint3D].push_back(edge);
1190  edgeMap[curPoint3D].push_back(edge);
1191  edgeList.emplace_back(edge);
1192 
1193  lastPoint = curPoint;
1194  }
1195 
1196  // Store the "extreme" points
1197  const ConvexHull::PointList& extremePoints = convexHullVec.back().getExtremePoints();
1198  reco::ProjectedPointList& extremePointList = convexHull.getConvexHullExtremePoints();
1199 
1200  for(const auto& point : extremePoints) extremePointList.push_back(point);
1201 
1202  // Store the "kink" points
1203  const reco::ConvexHullKinkTupleList& kinkPoints = convexHullVec.back().getKinkPoints();
1204  reco::ConvexHullKinkTupleList& kinkPointList = convexHull.getConvexHullKinkPoints();
1205 
1206  for(const auto& kink : kinkPoints) kinkPointList.push_back(kink);
1207  }
1208 
1209  return;
1210 }
1211 
1213 {
1214  reco::ProjectedPointList& convexHullPoints = clusterParameters.getConvexHull().getConvexHullPointList();
1215 
1216  if (convexHullPoints.size() > 2)
1217  {
1218  reco::ProjectedPointList::iterator pointItr = convexHullPoints.begin();
1219 
1220  // Advance to the second to last element
1221  std::advance(pointItr, convexHullPoints.size() - 2);
1222 
1223  reco::ProjectedPoint lastPoint = *pointItr++;
1224 
1225  // Reset pointer to the first element
1226  pointItr = convexHullPoints.begin();
1227 
1228  reco::ProjectedPoint curPoint = *pointItr++;
1229  Eigen::Vector2f lastEdge(std::get<0>(curPoint) - std::get<0>(lastPoint), std::get<1>(curPoint) - std::get<1>(lastPoint));
1230 
1231  lastEdge.normalize();
1232 
1233  while(pointItr != convexHullPoints.end())
1234  {
1235  reco::ProjectedPoint& nextPoint = *pointItr++;
1236 
1237  Eigen::Vector2f nextEdge(std::get<0>(nextPoint) - std::get<0>(curPoint), std::get<1>(nextPoint) - std::get<1>(curPoint));
1238  float nextEdgeLen = nextEdge.norm();
1239 
1240  nextEdge.normalize();
1241 
1242  float cosLastNextEdge = lastEdge.dot(nextEdge);
1243 
1244  if (top)
1245  {
1246  fTopConvexCosEdge->Fill(cosLastNextEdge, 1.);
1247  fTopConvexEdgeLen->Fill(std::min(nextEdgeLen,float(49.9)), 1.);
1248  }
1249  else
1250  {
1251  fSubConvexCosEdge->Fill(cosLastNextEdge, 1.);
1252  fSubConvexEdgeLen->Fill(std::min(nextEdgeLen,float(49.9)), 1.);
1253  }
1254 
1255  if (nextEdgeLen > fConvexHullMinSep) lastEdge = nextEdge;
1256 
1257  curPoint = nextPoint;
1258  }
1259  }
1260 
1261  return;
1262 }
1263 
1264 float ConvexHullPathFinder::closestApproach(const Eigen::Vector3f& P0,
1265  const Eigen::Vector3f& u0,
1266  const Eigen::Vector3f& P1,
1267  const Eigen::Vector3f& u1,
1268  Eigen::Vector3f& poca0,
1269  Eigen::Vector3f& poca1) const
1270 {
1271  // Technique is to compute the arclength to each point of closest approach
1272  Eigen::Vector3f w0 = P0 - P1;
1273  float a(1.);
1274  float b(u0.dot(u1));
1275  float c(1.);
1276  float d(u0.dot(w0));
1277  float e(u1.dot(w0));
1278  float den(a * c - b * b);
1279 
1280  float arcLen0 = (b * e - c * d) / den;
1281  float arcLen1 = (a * e - b * d) / den;
1282 
1283  poca0 = P0 + arcLen0 * u0;
1284  poca1 = P1 + arcLen1 * u1;
1285 
1286  return (poca0 - poca1).norm();
1287 }
1288 
1289 float ConvexHullPathFinder::findConvexHullEndPoints(const reco::EdgeList& convexHull, const reco::ClusterHit3D* first3D, const reco::ClusterHit3D* last3D) const
1290 {
1291  float largestDistance(0.);
1292 
1293  // Note that edges are vectors and that the convex hull edge list will be ordered
1294  // The idea is that the maximum distance from a given edge is to the edge just before the edge that "turns back" towards the current edge
1295  // meaning that the dot product of the two edges becomes negative.
1296  reco::EdgeList::const_iterator firstEdgeItr = convexHull.begin();
1297 
1298  while(firstEdgeItr != convexHull.end())
1299  {
1300  reco::EdgeList::const_iterator nextEdgeItr = firstEdgeItr;
1301 
1302 // Eigen::Vector2f firstEdgeVec(std::get<3>(*firstEdgeItr),std::get<);
1303 // Eigen::Vector2f lastPrimaryVec(lastPCA.getEigenVectors()[0][0],lastPCA.getEigenVectors()[0][1],lastPCA.getEigenVectors()[0][2]);
1304 // float cosToLast = newPrimaryVec.dot(lastPrimaryVec);
1305 
1306  while(++nextEdgeItr != convexHull.end())
1307  {
1308 
1309  }
1310  }
1311 
1312  return largestDistance;
1313 }
1314 
1316 } // namespace lar_cluster3d
size_t fMinTinyClusterSize
Minimum size for a "tiny" cluster.
float fConvexHullMinSep
Min hit separation to conisder in convex hull.
intermediate_table::iterator iterator
void PCAAnalysis_calc3DDocas(const reco::HitPairListPtr &hitPairVector, const reco::PrincipalComponents &pca) const
bool getSvdOK() const
Definition: Cluster3D.h:244
#define DEFINE_ART_CLASS_TOOL(tool)
Definition: ToolMacros.h:42
std::tuple< int, reco::ConvexHullKinkTuple, HitOrderTupleList, HitOrderTupleList > KinkTuple
void PCAAnalysis_3D(const reco::HitPairListPtr &hitPairList, reco::PrincipalComponents &pca, bool skeletonOnly=false) const
void cluster(In first, In last, Out result, Pred *pred)
Definition: NNClusters.h:41
float getTimeToExecute() const override
If monitoring, recover the time to execute a particular function.
void flipAxis(size_t axis)
Definition: Cluster3D.cxx:208
bool breakClusterByMaxDefect(reco::ClusterParameters &, reco::ClusterParametersList &, int level) const
std::string string
Definition: nybbler.cc:12
const Eigen::Vector3f getPosition() const
Definition: Cluster3D.h:158
std::tuple< float, float, reco::ProjectedPoint > HitOrderTuple
std::list< ProjectedPoint > ProjectedPointList
Definition: Cluster3D.h:353
reco::PrincipalComponents & getSkeletonPCA()
Definition: Cluster3D.h:478
std::list< Point > PointList
The list of the projected points.
Definition: ConvexHull.h:34
reco::ClusterParametersList::iterator subDivideCluster(reco::ClusterParameters &cluster, reco::PrincipalComponents &lastPCA, reco::ClusterParametersList::iterator positionItr, reco::ClusterParametersList &outputClusterList, int level=0) const
Use PCA to try to find path in cluster.
reco::EdgeList & getBestEdgeList()
Definition: Cluster3D.h:481
intermediate_table::const_iterator const_iterator
reco::HitPairListPtr & getHitPairListPtr()
Definition: Cluster3D.h:476
string dir
Cluster finding and building.
void configure(fhicl::ParameterSet const &pset) override
IClusterModAlg interface class definiton.
ClusterParametersList & daughterList()
Definition: Cluster3D.h:450
Implements a ConvexHull for use in clustering.
T abs(T value)
std::list< EdgeTuple > EdgeList
Definition: Cluster3D.h:345
const EigenValues & getEigenValues() const
Definition: Cluster3D.h:246
const double e
void fillConvexHullHists(reco::ClusterParameters &, bool) const
reco::PrincipalComponents & getFullPCA()
Definition: Cluster3D.h:477
reco::Hit3DToEdgeMap & getConvexHullEdgeMap()
Definition: Cluster3D.h:385
reco::ProjectedPointList & getConvexHullExtremePoints()
Definition: Cluster3D.h:387
const double a
reco::ConvexHullKinkTupleList & getConvexHullKinkPoints()
Definition: Cluster3D.h:388
Define a container for working with the convex hull.
Definition: Cluster3D.h:360
T get(std::string const &key) const
Definition: ParameterSet.h:271
const Eigen::Vector3f & getAvePosition() const
Definition: Cluster3D.h:248
void pruneHitOrderTupleLists(HitOrderTupleList &, HitOrderTupleList &) const
ConvexHullPathFinder(const fhicl::ParameterSet &)
Constructor.
std::list< ConvexHullKinkTuple > ConvexHullKinkTupleList
Definition: Cluster3D.h:355
std::tuple< const reco::ClusterHit3D *, const reco::ClusterHit3D *, double > EdgeTuple
Definition: Cluster3D.h:344
std::list< const reco::ClusterHit3D * > HitPairListPtr
Definition: Cluster3D.h:335
float getConvexHullArea() const
recover the area of the convex hull
Definition: ConvexHull.h:78
This provides an art tool interface definition for 3D Cluster algorithms.
double distance(double x1, double y1, double z1, double x2, double y2, double z2)
static int max(int a, int b)
const PointList & getConvexHull() const
recover the list of convex hull vertices
Definition: ConvexHull.h:58
Detector simulation of raw signals on wires.
ConvexHull class definiton.
Definition: ConvexHull.h:27
void buildConvexHull(reco::ClusterParameters &clusterParameters, int level=0) const
std::unique_ptr< lar_cluster3d::IClusterAlg > fClusterAlg
Tools.
bool breakClusterByKinksTrial(reco::ClusterParameters &, reco::ClusterParametersList &, int level) const
bool makeCandidateCluster(Eigen::Vector3f &, reco::ClusterParameters &, reco::HitPairListPtr::iterator, reco::HitPairListPtr::iterator, int) const
reco::ConvexHull & getConvexHull()
Definition: Cluster3D.h:482
void initializeHistograms(art::TFileDirectory &) override
Interface for initializing histograms if they are desired Note that the idea is to put hisgtograms in...
T min(sqlite3 *const db, std::string const &table_name, std::string const &column_name)
Definition: statistics.h:55
bool breakClusterByKinks(reco::ClusterParameters &, reco::ClusterParametersList &, int level) const
float fMinEigen0To1Ratio
Minimum ratio of eigen 0 to 1 to continue breaking.
float closestApproach(const Eigen::Vector3f &, const Eigen::Vector3f &, const Eigen::Vector3f &, const Eigen::Vector3f &, Eigen::Vector3f &, Eigen::Vector3f &) const
void orderHitsAlongEdge(const reco::ProjectedPointList &, const reco::ProjectedPoint &, const Eigen::Vector2f &, HitOrderTupleList &) const
float fConvexHullKinkAngle
Angle to declare a kink in convex hull calc.
MaybeLogger_< ELseverityLevel::ELsev_success, false > LogDebug
This provides an art tool interface definition for 3D Cluster algorithms.
bool breakClusterAtBigGap(reco::ClusterParameters &, reco::ClusterParametersList &, int level) const
T copy(T const &v)
static bool * b
Definition: config.cpp:1043
bool breakClusterInHalf(reco::ClusterParameters &, reco::ClusterParametersList &, int level) const
std::unordered_map< const reco::ClusterHit3D *, reco::EdgeList > Hit3DToEdgeMap
Definition: Cluster3D.h:347
double accumulated_real_time() const
Definition: cpu_timer.cc:66
auto const & get(AssnsNode< L, R, D > const &r)
Definition: AssnsNode.h:115
reco::ProjectedPointList & getConvexHullPointList()
Definition: Cluster3D.h:384
void ModifyClusters(reco::ClusterParametersList &) const override
Scan an input collection of clusters and modify those according to the specific implementing algorith...
reco::ProjectedPointList & getProjectedPointList()
Definition: Cluster3D.h:383
std::pair< MinMaxPoints, MinMaxPoints > MinMaxPointPair
reco::EdgeList & getConvexHullEdgeList()
Definition: Cluster3D.h:386
void start()
Definition: cpu_timer.cc:83
float findConvexHullEndPoints(const reco::EdgeList &, const reco::ClusterHit3D *, const reco::ClusterHit3D *) const
std::tuple< float, float, const reco::ClusterHit3D * > ProjectedPoint
Projected coordinates and pointer to hit.
Definition: Cluster3D.h:352
float fMinGapSize
Minimum gap size to break at gaps.
std::list< ClusterParameters > ClusterParametersList
Definition: Cluster3D.h:404
const EigenVectors & getEigenVectors() const
Definition: Cluster3D.h:247
std::pair< reco::ProjectedPoint, reco::ProjectedPoint > MinMaxPoints
QTextStream & endl(QTextStream &s)
bool completeCandidateCluster(Eigen::Vector3f &, reco::ClusterParameters &, int) const