All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MSTPathFinder_tool.cc
Go to the documentation of this file.
1 /**
2  * @file MSTPathFinder_tool.cc
3  *
4  * @brief art Tool for comparing clusters and merging those that are consistent
5  *
6  */
7 
8 // Framework Includes
9 #include "art/Utilities/ToolMacros.h"
10 #include "art/Utilities/make_tool.h"
11 #include "art_root_io/TFileService.h"
12 #include "cetlib/cpu_timer.h"
13 #include "cetlib/search_path.h"
14 #include "fhiclcpp/ParameterSet.h"
15 #include "messagefacility/MessageLogger/MessageLogger.h"
16 
19 
20 // LArSoft includes
29 
30 // Eigen
31 #include <Eigen/Dense>
32 
33 // Root histograms
34 #include "TH1F.h"
35 #include "TH2F.h"
36 #include "TProfile.h"
37 
38 // std includes
39 #include <functional>
40 #include <iostream>
41 #include <memory>
42 #include <string>
43 #include <numeric> // std::accumulate
44 
45 //------------------------------------------------------------------------------------------------------------------------------------------
46 // implementation follows
47 
48 namespace lar_cluster3d {
49 
50  class MSTPathFinder : virtual public IClusterModAlg {
51  public:
52  /**
53  * @brief Constructor
54  *
55  * @param pset
56  */
57  explicit MSTPathFinder(const fhicl::ParameterSet&);
58 
59  /**
60  * @brief Destructor
61  */
63 
64  void configure(fhicl::ParameterSet const& pset) override;
65 
66  /**
67  * @brief Interface for initializing histograms if they are desired
68  * Note that the idea is to put hisgtograms in a subfolder
69  *
70  * @param TFileDirectory - the folder to store the hists in
71  */
72  void initializeHistograms(art::TFileDirectory&) override;
73 
74  /**
75  * @brief Scan an input collection of clusters and modify those according
76  * to the specific implementing algorithm
77  *
78  * @param clusterParametersList A list of cluster objects (parameters from associated hits)
79  */
80  void ModifyClusters(reco::ClusterParametersList&) const override;
81 
82  /**
83  * @brief If monitoring, recover the time to execute a particular function
84  */
85  float
86  getTimeToExecute() const override
87  {
88  return std::accumulate(fTimeVector.begin(), fTimeVector.end(), 0.);
89  }
90 
91  private:
92  /**
93  * @brief enumerate the possible values for time checking if monitoring timing
94  */
95  enum TimeValues {
98  RUNDBSCAN = 2,
102  };
103 
104  /**
105  * @brief Driver for Prim's algorithm
106  */
110 
111  /**
112  * @brief Prune the obvious ambiguous hits
113  */
115 
116  /**
117  * @brief Algorithm to find the best path through the given cluster
118  */
120 
121  /**
122  * @brief a depth first search to find longest branches
123  */
125  const reco::Hit3DToEdgeMap&,
126  float&) const;
127 
128  /**
129  * @brief Alternative version of FindBestPathInCluster utilizing an A* algorithm
130  */
132 
133  /**
134  * @brief Algorithm to find shortest path between two 3D hits
135  */
136  void AStar(const reco::ClusterHit3D*,
137  const reco::ClusterHit3D*,
138  float alpha,
140  reco::ClusterParameters&) const;
141 
142  using BestNodeTuple = std::tuple<const reco::ClusterHit3D*, float, float>;
143  using BestNodeMap = std::unordered_map<const reco::ClusterHit3D*, BestNodeTuple>;
144 
146  BestNodeMap&,
148  reco::EdgeList&) const;
149 
150  float DistanceBetweenNodes(const reco::ClusterHit3D*, const reco::ClusterHit3D*) const;
151 
152  /**
153  * @brief Find the lowest cost path between two nodes using MST edges
154  */
155  void LeastCostPath(const reco::EdgeTuple&,
156  const reco::ClusterHit3D*,
158  float&) const;
159 
160  /**
161  * @brief Add ability to build the convex hull (these needs to be split out! )
162  */
163  using MinMaxPoints = std::pair<reco::ProjectedPoint, reco::ProjectedPoint>;
164  using MinMaxPointPair = std::pair<MinMaxPoints, MinMaxPoints>;
165 
166  void buildConvexHull(reco::ClusterParameters&, reco::HitPairListPtr&, int level = 0) const;
167 
169  const reco::ClusterHit3D*,
170  const reco::ClusterHit3D*) const;
171 
172  /**
173  * @brief Data members to follow
174  */
175  bool fEnableMonitoring; ///<
176  size_t fMinTinyClusterSize; ///< Minimum size for a "tiny" cluster
177  float fConvexHullKinkAngle; ///< Angle to declare a kink in convex hull calc
178  float fConvexHullMinSep; ///< Min hit separation to conisder in convex hull
179 
180  mutable std::vector<float> fTimeVector; ///<
181 
182  geo::Geometry const* fGeometry; //< pointer to the Geometry service
183 
184  PrincipalComponentsAlg fPCAAlg; // For running Principal Components Analysis
185  kdTree fkdTree; // For the kdTree
186 
187  std::unique_ptr<lar_cluster3d::IClusterParametersBuilder>
188  fClusterBuilder; ///< Common cluster builder tool
189  };
190 
191  MSTPathFinder::MSTPathFinder(fhicl::ParameterSet const& pset)
192  : fPCAAlg(pset.get<fhicl::ParameterSet>("PrincipalComponentsAlg"))
193  , fkdTree(pset.get<fhicl::ParameterSet>("kdTree"))
194  {
195  this->configure(pset);
196  }
197 
198  //------------------------------------------------------------------------------------------------------------------------------------------
199 
201 
202  //------------------------------------------------------------------------------------------------------------------------------------------
203 
204  void
205  MSTPathFinder::configure(fhicl::ParameterSet const& pset)
206  {
207  fEnableMonitoring = pset.get<bool>("EnableMonitoring", true);
208  fMinTinyClusterSize = pset.get<size_t>("MinTinyClusterSize", 40);
209  fConvexHullKinkAngle = pset.get<float>("ConvexHullKinkAgle", 0.95);
210  fConvexHullMinSep = pset.get<float>("ConvexHullMinSep", 0.65);
211 
212  art::ServiceHandle<geo::Geometry const> geometry;
213 
214  fGeometry = &*geometry;
215 
216  fTimeVector.resize(NUMTIMEVALUES, 0.);
217 
218  fClusterBuilder = art::make_tool<lar_cluster3d::IClusterParametersBuilder>(
219  pset.get<fhicl::ParameterSet>("ClusterParamsBuilder"));
220 
221  return;
222  }
223 
224  void
225  MSTPathFinder::initializeHistograms(art::TFileDirectory& histDir)
226  {
227  // It is assumed that the input TFileDirectory has been set up to group histograms into a common
228  // folder at the calling routine's level. Here we create one more level of indirection to keep
229  // histograms made by this tool separate.
230  // fFillHistograms = true;
231  //
232  // std::string dirName = "ConvexHullPath";
233  //
234  // art::TFileDirectory dir = histDir.mkdir(dirName.c_str());
235  //
236  // // Divide into two sets of hists... those for the overall cluster and
237  // // those for the subclusters
238  // fTopNum3DHits = dir.make<TH1F>("TopNum3DHits", "Number 3D Hits", 200, 0., 200.);
239 
240  return;
241  }
242 
243  void
245  {
246  /**
247  * @brief Top level interface tool for performing deghosting and primary path finding
248  * using a minimum spanning tree approach. This is a shell tool, it actually uses the
249  * Minimum Spanning Tree clusering tool...
250  */
251 
252  // Initial clustering is done, now trim the list and get output parameters
253  cet::cpu_timer theClockBuildClusters;
254 
255  // Start clocks if requested
256  if (fEnableMonitoring) theClockBuildClusters.start();
257 
258  // Ok, the idea here is to loop over the input clusters and the process one at a time and then use the MST algorithm
259  // to deghost and try to find the best path.
260  for (auto& clusterParams : clusterParametersList) {
261  // It turns out that computing the convex hull surrounding the points in the 2D projection onto the
262  // plane of largest spread in the PCA is a good way to break up the cluster... and we do it here since
263  // we (currently) want this to be part of the standard output
264  buildConvexHull(clusterParams, clusterParams.getHitPairListPtr());
265 
266  // Make sure our cluster has enough hits...
267  if (clusterParams.getHitPairListPtr().size() > fMinTinyClusterSize) {
268  // DBScan is driven of its "epsilon neighborhood". Computing adjacency within DBScan can be time
269  // consuming so the idea is the prebuild the adjaceny map and then run DBScan.
270  // The following call does this work
271  kdTree::KdTreeNodeList kdTreeNodeContainer;
272  kdTree::KdTreeNode topNode =
273  fkdTree.BuildKdTree(clusterParams.getHitPairListPtr(), kdTreeNodeContainer);
274 
276 
277  // We are making subclusters
278  reco::ClusterParametersList& daughterParametersList = clusterParams.daughterList();
279 
280  // Run DBScan to get candidate clusters
281  RunPrimsAlgorithm(clusterParams.getHitPairListPtr(), topNode, daughterParametersList);
282 
283  // Initial clustering is done, now trim the list and get output parameters
284  cet::cpu_timer theClockBuildClusters;
285 
286  // Start clocks if requested
287  if (fEnableMonitoring) theClockBuildClusters.start();
288 
289  fClusterBuilder->BuildClusterInfo(daughterParametersList);
290 
291  if (fEnableMonitoring) {
292  theClockBuildClusters.stop();
293 
294  fTimeVector[BUILDCLUSTERINFO] = theClockBuildClusters.accumulated_real_time();
295  }
296 
297  // Test run the path finding algorithm
298  for (auto& daughterParams : daughterParametersList)
299  FindBestPathInCluster(daughterParams, topNode);
300  }
301  }
302 
303  if (fEnableMonitoring) {
304  theClockBuildClusters.stop();
305 
306  fTimeVector[BUILDCLUSTERINFO] = theClockBuildClusters.accumulated_real_time();
307  }
308 
309  mf::LogDebug("MSTPathFinder") << ">>>>> Cluster Path finding done" << std::endl;
310 
311  return;
312  }
313 
314  //------------------------------------------------------------------------------------------------------------------------------------------
315  void
317  kdTree::KdTreeNode& topNode,
318  reco::ClusterParametersList& clusterParametersList) const
319  {
320  // If no hits then no work
321  if (hitPairList.empty()) return;
322 
323  // Now proceed with building the clusters
324  cet::cpu_timer theClockDBScan;
325 
326  // Start clocks if requested
327  if (fEnableMonitoring) theClockDBScan.start();
328 
329  // Initialization
330  size_t clusterIdx(0);
331 
332  // This will contain our list of edges
333  reco::EdgeList curEdgeList;
334 
335  // Get the first point
336  reco::HitPairListPtr::const_iterator freeHitItr = hitPairList.begin();
337  const reco::ClusterHit3D* lastAddedHit = *freeHitItr++;
338 
340 
341  // Make a cluster...
342  clusterParametersList.push_back(reco::ClusterParameters());
343 
344  // Get an iterator to the first cluster
345  reco::ClusterParameters* curCluster = &clusterParametersList.back();
346 
347  // We use pointers here because the objects they point to will change in the loop below
348  reco::Hit3DToEdgeMap* curEdgeMap = &curCluster->getHit3DToEdgeMap();
349  reco::HitPairListPtr* curClusterHitList = &curCluster->getHitPairListPtr();
350 
351  // Loop until all hits have been associated to a cluster
352  while (1) {
353  // and the 3D hit status bits
355 
356  // Purge the current list to get rid of edges which point to hits already in the cluster
357  for (reco::EdgeList::iterator curEdgeItr = curEdgeList.begin();
358  curEdgeItr != curEdgeList.end();) {
359  if (std::get<1>(*curEdgeItr)->getStatusBits() & reco::ClusterHit3D::CLUSTERATTACHED)
360  curEdgeItr = curEdgeList.erase(curEdgeItr);
361  else
362  curEdgeItr++;
363  }
364 
365  // Add the lastUsedHit to the current cluster
366  curClusterHitList->push_back(lastAddedHit);
367 
368  // Set up to find the list of nearest neighbors to the last used hit...
369  kdTree::CandPairList CandPairList;
370  float bestDistance(1.5); //std::numeric_limits<float>::max());
371 
372  // And find them... result will be an unordered list of neigbors
373  fkdTree.FindNearestNeighbors(lastAddedHit, topNode, CandPairList, bestDistance);
374 
375  // Copy edges to the current list (but only for hits not already in a cluster)
376  // for(auto& pair : CandPairList)
377  // if (!(pair.second->getStatusBits() & reco::ClusterHit3D::CLUSTERATTACHED)) curEdgeList.push_back(reco::EdgeTuple(lastAddedHit,pair.second,pair.first));
378  for (auto& pair : CandPairList) {
379  if (!(pair.second->getStatusBits() & reco::ClusterHit3D::CLUSTERATTACHED)) {
380  double edgeWeight =
381  pair.first * lastAddedHit->getHitChiSquare() * pair.second->getHitChiSquare();
382 
383  curEdgeList.push_back(reco::EdgeTuple(lastAddedHit, pair.second, edgeWeight));
384  }
385  }
386 
387  // If the edge list is empty then we have a complete cluster
388  if (curEdgeList.empty()) {
389  std::cout << "-----------------------------------------------------------------------------"
390  "------------"
391  << std::endl;
392  std::cout << "**> Cluster idx: " << clusterIdx++ << " has " << curClusterHitList->size()
393  << " hits" << std::endl;
394 
395  // Look for the next "free" hit
396  freeHitItr = std::find_if(freeHitItr, hitPairList.end(), [](const auto& hit) {
397  return !(hit->getStatusBits() & reco::ClusterHit3D::CLUSTERATTACHED);
398  });
399 
400  // If at end of input list we are done with all hits
401  if (freeHitItr == hitPairList.end()) break;
402 
403  std::cout << "##################################################################>"
404  "Processing another cluster"
405  << std::endl;
406 
407  // Otherwise, get a new cluster and set up
408  clusterParametersList.push_back(reco::ClusterParameters());
409 
410  curCluster = &clusterParametersList.back();
411 
412  curEdgeMap = &curCluster->getHit3DToEdgeMap();
413  curClusterHitList = &curCluster->getHitPairListPtr();
414  lastAddedHit = *freeHitItr++;
415  }
416  // Otherwise we are still processing the current cluster
417  else {
418  // Sort the list of edges by distance
419  curEdgeList.sort([](const auto& left, const auto& right) {
420  return std::get<2>(left) < std::get<2>(right);
421  });
422 
423  // Populate the map with the edges...
424  reco::EdgeTuple& curEdge = curEdgeList.front();
425 
426  (*curEdgeMap)[std::get<0>(curEdge)].push_back(curEdge);
427  (*curEdgeMap)[std::get<1>(curEdge)].push_back(
428  reco::EdgeTuple(std::get<1>(curEdge), std::get<0>(curEdge), std::get<2>(curEdge)));
429 
430  // Update the last hit to be added to the collection
431  lastAddedHit = std::get<1>(curEdge);
432  }
433  }
434 
435  if (fEnableMonitoring) {
436  theClockDBScan.stop();
437 
438  fTimeVector[RUNDBSCAN] = theClockDBScan.accumulated_real_time();
439  }
440 
441  return;
442  }
443 
444  void
446  {
447  reco::HitPairListPtr longestCluster;
448  float bestQuality(0.);
449  float aveNumEdges(0.);
450  size_t maxNumEdges(0);
451  size_t nIsolatedHits(0);
452 
453  // Now proceed with building the clusters
454  cet::cpu_timer theClockPathFinding;
455 
456  // Start clocks if requested
457  if (fEnableMonitoring) theClockPathFinding.start();
458 
459  reco::HitPairListPtr& hitPairList = curCluster.getHitPairListPtr();
460  reco::Hit3DToEdgeMap& curEdgeMap = curCluster.getHit3DToEdgeMap();
461  reco::EdgeList& bestEdgeList = curCluster.getBestEdgeList();
462 
463  // Do some spelunking...
464  for (const auto& hit : hitPairList) {
465  if (!curEdgeMap[hit].empty() && curEdgeMap[hit].size() == 1) {
466  float quality(0.);
467 
468  reco::HitPairListPtr tempList =
469  DepthFirstSearch(curEdgeMap[hit].front(), curEdgeMap, quality);
470 
471  tempList.push_front(std::get<0>(curEdgeMap[hit].front()));
472 
473  if (quality > bestQuality) {
474  longestCluster = tempList;
475  bestQuality = quality;
476  }
477 
478  nIsolatedHits++;
479  }
480 
481  aveNumEdges += float(curEdgeMap[hit].size());
482  maxNumEdges = std::max(maxNumEdges, curEdgeMap[hit].size());
483  }
484 
485  aveNumEdges /= float(hitPairList.size());
486  std::cout << "----> # isolated hits: " << nIsolatedHits
487  << ", longest branch: " << longestCluster.size()
488  << ", cluster size: " << hitPairList.size() << ", ave # edges: " << aveNumEdges
489  << ", max: " << maxNumEdges << std::endl;
490 
491  if (!longestCluster.empty()) {
492  hitPairList = longestCluster;
493  for (const auto& hit : hitPairList) {
494  for (const auto& edge : curEdgeMap[hit])
495  bestEdgeList.emplace_back(edge);
496  }
497 
498  std::cout << " ====> new cluster size: " << hitPairList.size() << std::endl;
499  }
500 
501  if (fEnableMonitoring) {
502  theClockPathFinding.stop();
503 
504  fTimeVector[PATHFINDING] += theClockPathFinding.accumulated_real_time();
505  }
506 
507  return;
508  }
509 
510  void
512  kdTree::KdTreeNode& topNode) const
513  {
514  // Set up for timing the function
515  cet::cpu_timer theClockPathFinding;
516 
517  // Start clocks if requested
518  if (fEnableMonitoring) theClockPathFinding.start();
519 
520  // Trial A* here
521  if (clusterParams.getHitPairListPtr().size() > 2) {
522  // Do a quick PCA to determine our parameter "alpha"
523  fPCAAlg.PCAAnalysis_3D(clusterParams.getHitPairListPtr(), clusterParams.getFullPCA());
524 
525  // Recover the new fullPCA
526  reco::PrincipalComponents& pca = clusterParams.getFullPCA();
527 
528  // The chances of a failure are remote, still we should check
529  if (pca.getSvdOK()) {
530  // Get references to what we need....
531  reco::HitPairListPtr& curCluster = clusterParams.getHitPairListPtr();
532  reco::Hit3DToEdgeMap& curEdgeMap = clusterParams.getHit3DToEdgeMap();
533  float pcaLen = 3.0 * sqrt(pca.getEigenValues()[2]);
534  float pcaWidth = 3.0 * sqrt(pca.getEigenValues()[1]);
535  float pcaHeight = 3.0 * sqrt(pca.getEigenValues()[0]);
536  const Eigen::Vector3f& pcaCenter = pca.getAvePosition();
537  float alpha = std::min(float(1.), std::max(float(0.001), pcaWidth / pcaLen));
538 
539  // Create a temporary container for the isolated points
540  reco::ProjectedPointList isolatedPointList;
541 
542  // Go through and find the isolated points, for those get the projection to the plane of maximum spread
543  for (const auto& hit3D : curCluster) {
544  // the definition of an isolated hit is that it only has one associated edge
545  if (!curEdgeMap[hit3D].empty() && curEdgeMap[hit3D].size() == 1) {
546  Eigen::Vector3f pcaToHitVec(hit3D->getPosition()[0] - pcaCenter(0),
547  hit3D->getPosition()[1] - pcaCenter(1),
548  hit3D->getPosition()[2] - pcaCenter(2));
549  Eigen::Vector3f pcaToHit = pca.getEigenVectors() * pcaToHitVec;
550 
551  // This sets x,y where x is the longer spread, y the shorter
552  isolatedPointList.emplace_back(pcaToHit(2), pcaToHit(1), hit3D);
553  }
554  }
555 
556  std::cout << "************* Finding best path with A* in cluster *****************"
557  << std::endl;
558  std::cout << "**> There are " << curCluster.size() << " hits, " << isolatedPointList.size()
559  << " isolated hits, the alpha parameter is " << alpha << std::endl;
560  std::cout << "**> PCA len: " << pcaLen << ", wid: " << pcaWidth << ", height: " << pcaHeight
561  << ", ratio: " << pcaHeight / pcaWidth << std::endl;
562 
563  // If no isolated points then nothing to do...
564  if (isolatedPointList.size() > 1) {
565  // Sort the point vec by increasing x, if same then by increasing y.
566  isolatedPointList.sort([](const auto& left, const auto& right) {
567  return (std::abs(std::get<0>(left) - std::get<0>(right)) >
568  std::numeric_limits<float>::epsilon()) ?
569  std::get<0>(left) < std::get<0>(right) :
570  std::get<1>(left) < std::get<1>(right);
571  });
572 
573  // Ok, get the two most distance points...
574  const reco::ClusterHit3D* startHit = std::get<2>(isolatedPointList.front());
575  const reco::ClusterHit3D* stopHit = std::get<2>(isolatedPointList.back());
576 
577  std::cout << "**> Sorted " << isolatedPointList.size()
578  << " hits, longest distance: " << DistanceBetweenNodes(startHit, stopHit)
579  << std::endl;
580 
581  // Call the AStar function to try to find the best path...
582  // AStar(startHit,stopHit,alpha,topNode,clusterParams);
583 
584  float cost(std::numeric_limits<float>::max());
585 
586  LeastCostPath(curEdgeMap[startHit].front(), stopHit, clusterParams, cost);
587 
588  clusterParams.getBestHitPairListPtr().push_front(startHit);
589 
590  std::cout << "**> Best path has " << clusterParams.getBestHitPairListPtr().size()
591  << " hits, " << clusterParams.getBestEdgeList().size() << " edges" << std::endl;
592  }
593 
594  // Recalculate the PCA based on the hits comprisig the path
595  fPCAAlg.PCAAnalysis_3D(clusterParams.getBestHitPairListPtr(), pca);
596 
597  // And now compute the convex hull
598  buildConvexHull(clusterParams, clusterParams.getBestHitPairListPtr());
599  }
600  else {
601  std::cout << "++++++>>> PCA failure! # hits: " << clusterParams.getHitPairListPtr().size()
602  << std::endl;
603  }
604  }
605 
606  if (fEnableMonitoring) {
607  theClockPathFinding.stop();
608 
609  fTimeVector[PATHFINDING] += theClockPathFinding.accumulated_real_time();
610  }
611 
612  return;
613  }
614 
615  void
617  const reco::ClusterHit3D* goalNode,
618  float alpha,
619  kdTree::KdTreeNode& topNode,
620  reco::ClusterParameters& clusterParams) const
621  {
622  // Recover the list of hits and edges
623  reco::HitPairListPtr& pathNodeList = clusterParams.getBestHitPairListPtr();
624  reco::EdgeList& bestEdgeList = clusterParams.getBestEdgeList();
625  reco::Hit3DToEdgeMap& curEdgeMap = clusterParams.getHit3DToEdgeMap();
626 
627  // Find the shortest path from start to goal using an A* algorithm
628  // Keep track of the nodes which have already been evaluated
629  reco::HitPairListPtr closedList;
630 
631  // Keep track of the nodes that have been "discovered" but yet to be evaluated
632  reco::HitPairListPtr openList = {startNode};
633 
634  // Create a map which, for each node, will tell us the node it can be most efficiencly reached from.
635  BestNodeMap bestNodeMap;
636 
637  bestNodeMap[startNode] =
638  BestNodeTuple(startNode, 0., DistanceBetweenNodes(startNode, goalNode));
639 
640  alpha = 1.; //std::max(0.5,alpha);
641 
642  while (!openList.empty()) {
643  // The list is not empty so by def we will return something
644  reco::HitPairListPtr::iterator currentNodeItr = openList.begin();
645 
646  // If the list contains more than one element then we need to find the one with the smallest total estimated cost to the end
647  if (openList.size() > 1)
648  currentNodeItr = std::min_element(
649  openList.begin(), openList.end(), [bestNodeMap](const auto& next, const auto& best) {
650  return std::get<2>(bestNodeMap.at(next)) < std::get<2>(bestNodeMap.at(best));
651  });
652 
653  // Easier to deal directly with the pointer to the node
654  const reco::ClusterHit3D* currentNode = *currentNodeItr;
655 
656  // Check to see if we have reached the goal and need to evaluate the path
657  if (currentNode == goalNode) {
658  // The path reconstruction will
659  ReconstructBestPath(goalNode, bestNodeMap, pathNodeList, bestEdgeList);
660 
661  break;
662  }
663 
664  // Otherwise need to keep evaluating
665  else {
666  openList.erase(currentNodeItr);
668 
669  // Get tuple values for the current node
670  const BestNodeTuple& currentNodeTuple = bestNodeMap.at(currentNode);
671  float currentNodeScore = std::get<1>(currentNodeTuple);
672 
673  // Recover the edges associated to the current point
674  const reco::EdgeList& curEdgeList = curEdgeMap[currentNode];
675 
676  for (const auto& curEdge : curEdgeList) {
677  const reco::ClusterHit3D* candHit3D = std::get<1>(curEdge);
678 
679  if (candHit3D->getStatusBits() & reco::ClusterHit3D::PATHCHECKED) continue;
680 
681  float tentative_gScore = currentNodeScore + std::get<2>(curEdge);
682 
683  // Have we seen the candidate node before?
684  BestNodeMap::iterator candNodeItr = bestNodeMap.find(candHit3D);
685 
686  if (candNodeItr == bestNodeMap.end()) { openList.push_back(candHit3D); }
687  else if (tentative_gScore > std::get<1>(candNodeItr->second))
688  continue;
689 
690  // Make a guess at score to get to target...
691  float guessToTarget = DistanceBetweenNodes(candHit3D, goalNode) / 0.3;
692 
693  bestNodeMap[candHit3D] =
694  BestNodeTuple(currentNode, tentative_gScore, tentative_gScore + guessToTarget);
695  }
696  }
697  }
698 
699  return;
700  }
701 
702  void
704  BestNodeMap& bestNodeMap,
705  reco::HitPairListPtr& pathNodeList,
706  reco::EdgeList& bestEdgeList) const
707  {
708  while (std::get<0>(bestNodeMap.at(goalNode)) != goalNode) {
709  const reco::ClusterHit3D* nextNode = std::get<0>(bestNodeMap[goalNode]);
710  reco::EdgeTuple bestEdge =
711  reco::EdgeTuple(goalNode, nextNode, DistanceBetweenNodes(goalNode, nextNode));
712 
713  pathNodeList.push_front(goalNode);
714  bestEdgeList.push_front(bestEdge);
715 
716  goalNode = nextNode;
717  }
718 
719  pathNodeList.push_front(goalNode);
720 
721  return;
722  }
723 
724  void
726  const reco::ClusterHit3D* goalNode,
727  reco::ClusterParameters& clusterParams,
728  float& showMeTheMoney) const
729  {
730  // Recover the mapping between hits and edges
731  reco::Hit3DToEdgeMap& curEdgeMap = clusterParams.getHit3DToEdgeMap();
732 
733  reco::Hit3DToEdgeMap::const_iterator edgeListItr = curEdgeMap.find(std::get<1>(curEdge));
734 
735  showMeTheMoney = std::numeric_limits<float>::max();
736 
737  if (edgeListItr != curEdgeMap.end() && !edgeListItr->second.empty()) {
738  reco::HitPairListPtr& bestNodeList = clusterParams.getBestHitPairListPtr();
739  reco::EdgeList& bestEdgeList = clusterParams.getBestEdgeList();
740 
741  for (const auto& edge : edgeListItr->second) {
742  // skip the self reference
743  if (std::get<1>(edge) == std::get<0>(curEdge)) continue;
744 
745  // Have we found the droid we are looking for?
746  if (std::get<1>(edge) == goalNode) {
747  bestNodeList.push_back(goalNode);
748  bestEdgeList.push_back(edge);
749  showMeTheMoney = std::get<2>(edge);
750  break;
751  }
752 
753  // Keep searching, it is out there somewhere...
754  float currentCost(0.);
755 
756  LeastCostPath(edge, goalNode, clusterParams, currentCost);
757 
758  if (currentCost < std::numeric_limits<float>::max()) {
759  showMeTheMoney = std::get<2>(edge) + currentCost;
760  break;
761  }
762  }
763  }
764 
765  if (showMeTheMoney < std::numeric_limits<float>::max()) {
766  clusterParams.getBestHitPairListPtr().push_front(std::get<1>(curEdge));
767  clusterParams.getBestEdgeList().push_front(curEdge);
768  }
769 
770  return;
771  }
772 
773  float
775  const reco::ClusterHit3D* node2) const
776  {
777  const Eigen::Vector3f& node1Pos = node1->getPosition();
778  const Eigen::Vector3f& node2Pos = node2->getPosition();
779  float deltaNode[] = {
780  node1Pos[0] - node2Pos[0], node1Pos[1] - node2Pos[1], node1Pos[2] - node2Pos[2]};
781 
782  // Standard euclidean distance
783  return std::sqrt(deltaNode[0] * deltaNode[0] + deltaNode[1] * deltaNode[1] +
784  deltaNode[2] * deltaNode[2]);
785 
786  // Manhatten distance
787  //return std::fabs(deltaNode[0]) + std::fabs(deltaNode[1]) + std::fabs(deltaNode[2]);
788  /*
789  // Chebyshev distance
790  // We look for maximum distance by wires...
791 
792  // Now go through the hits and compare view by view to look for delta wire and tigher constraint on delta t
793  int wireDeltas[] = {0,0,0};
794 
795  for(size_t idx = 0; idx < 3; idx++)
796  wireDeltas[idx] = std::abs(int(node1->getWireIDs()[idx].Wire) - int(node2->getWireIDs()[idx].Wire));
797 
798  // put wire deltas in order...
799  std::sort(wireDeltas, wireDeltas + 3);
800 
801  return std::sqrt(deltaNode[0]*deltaNode[0] + 0.09 * float(wireDeltas[2]*wireDeltas[2]));
802  */
803  }
804 
807  const reco::Hit3DToEdgeMap& hitToEdgeMap,
808  float& bestTreeQuality) const
809  {
810  reco::HitPairListPtr hitPairListPtr;
811  float bestQuality(0.);
812  float curEdgeWeight = std::max(0.3, std::get<2>(curEdge));
813  float curEdgeProj(1. / curEdgeWeight);
814 
815  reco::Hit3DToEdgeMap::const_iterator edgeListItr = hitToEdgeMap.find(std::get<1>(curEdge));
816 
817  if (edgeListItr != hitToEdgeMap.end()) {
818  // The input edge weight has quality factors applied, recalculate just the position difference
819  const Eigen::Vector3f& firstHitPos = std::get<0>(curEdge)->getPosition();
820  const Eigen::Vector3f& secondHitPos = std::get<1>(curEdge)->getPosition();
821  float curEdgeVec[] = {secondHitPos[0] - firstHitPos[0],
822  secondHitPos[1] - firstHitPos[1],
823  secondHitPos[2] - firstHitPos[2]};
824  float curEdgeMag = std::sqrt(curEdgeVec[0] * curEdgeVec[0] + curEdgeVec[1] * curEdgeVec[1] +
825  curEdgeVec[2] * curEdgeVec[2]);
826 
827  curEdgeMag = std::max(float(0.1), curEdgeMag);
828 
829  for (const auto& edge : edgeListItr->second) {
830  // skip the self reference
831  if (std::get<1>(edge) == std::get<0>(curEdge)) continue;
832 
833  float quality(0.);
834 
835  reco::HitPairListPtr tempList = DepthFirstSearch(edge, hitToEdgeMap, quality);
836 
837  if (quality > bestQuality) {
838  hitPairListPtr = tempList;
839  bestQuality = quality;
840  curEdgeProj = 1. / curEdgeMag;
841  }
842  }
843  }
844 
845  hitPairListPtr.push_front(std::get<1>(curEdge));
846 
847  bestTreeQuality += bestQuality + curEdgeProj;
848 
849  return hitPairListPtr;
850  }
851 
852  void
854  reco::Hit2DToClusterMap& hit2DToClusterMap) const
855  {
856 
857  // Recover the HitPairListPtr from the input clusterParams (which will be the
858  // only thing that has been provided)
859  reco::HitPairListPtr& hitPairVector = clusterParams.getHitPairListPtr();
860 
861  size_t nStartedWith(hitPairVector.size());
862  size_t nRejectedHits(0);
863 
864  reco::HitPairListPtr goodHits;
865 
866  // Loop through the hits and try to week out the clearly ambiguous ones
867  for (const auto& hit3D : hitPairVector) {
868  // Loop to try to remove ambiguous hits
869  size_t n2DHitsIn3DHit(0);
870  size_t nThisClusterOnly(0);
871  size_t nOtherCluster(0);
872 
873  // reco::ClusterParameters* otherCluster;
874  const std::set<const reco::ClusterHit3D*>* otherClusterHits = 0;
875 
876  for (const auto& hit2D : hit3D->getHits()) {
877  if (!hit2D) continue;
878 
879  n2DHitsIn3DHit++;
880 
881  if (hit2DToClusterMap[hit2D].size() < 2)
882  nThisClusterOnly = hit2DToClusterMap[hit2D][&clusterParams].size();
883  else {
884  for (const auto& clusterHitMap : hit2DToClusterMap[hit2D]) {
885  if (clusterHitMap.first == &clusterParams) continue;
886 
887  if (clusterHitMap.second.size() > nOtherCluster) {
888  nOtherCluster = clusterHitMap.second.size();
889  // otherCluster = clusterHitMap.first;
890  otherClusterHits = &clusterHitMap.second;
891  }
892  }
893  }
894  }
895 
896  if (n2DHitsIn3DHit < 3 && nThisClusterOnly > 1 && nOtherCluster > 0) {
897  bool skip3DHit(false);
898 
899  for (const auto& otherHit3D : *otherClusterHits) {
900  size_t nOther2DHits(0);
901 
902  for (const auto& otherHit2D : otherHit3D->getHits()) {
903  if (!otherHit2D) continue;
904 
905  nOther2DHits++;
906  }
907 
908  if (nOther2DHits > 2) {
909  skip3DHit = true;
910  nRejectedHits++;
911  break;
912  }
913  }
914 
915  if (skip3DHit) continue;
916  }
917 
918  goodHits.emplace_back(hit3D);
919  }
920 
921  std::cout << "###>> Input " << nStartedWith << " hits, rejected: " << nRejectedHits
922  << std::endl;
923 
924  hitPairVector.resize(goodHits.size());
925  std::copy(goodHits.begin(), goodHits.end(), hitPairVector.begin());
926 
927  return;
928  }
929 
930  void
932  reco::HitPairListPtr& hitPairListPtr,
933  int level) const
934  {
935  // set an indention string
936  std::string minuses(level / 2, '-');
937  std::string indent(level / 2, ' ');
938 
939  indent += minuses;
940 
941  // The plan is to build the enclosing 2D polygon around the points in the PCA plane of most spread for this cluster
942  // To do so we need to start by building a list of 2D projections onto the plane of most spread...
943  reco::PrincipalComponents& pca = clusterParameters.getFullPCA();
944 
945  // Recover the parameters from the Principal Components Analysis that we need to project and accumulate
946  const Eigen::Vector3f& pcaCenter = pca.getAvePosition();
947  reco::ConvexHull& convexHull = clusterParameters.getConvexHull();
948  reco::ProjectedPointList& pointList = convexHull.getProjectedPointList();
949 
950  // Loop through hits and do projection to plane
951  for (const auto& hit3D : hitPairListPtr) {
952  Eigen::Vector3f pcaToHitVec(hit3D->getPosition()[0] - pcaCenter(0),
953  hit3D->getPosition()[1] - pcaCenter(1),
954  hit3D->getPosition()[2] - pcaCenter(2));
955  Eigen::Vector3f pcaToHit = pca.getEigenVectors() * pcaToHitVec;
956 
957  pointList.emplace_back(pcaToHit(1), pcaToHit(2), hit3D);
958  }
959 
960  // Sort the point vec by increasing x, then increase y
961  pointList.sort([](const auto& left, const auto& right) {
962  return (std::abs(std::get<0>(left) - std::get<0>(right)) >
963  std::numeric_limits<float>::epsilon()) ?
964  std::get<0>(left) < std::get<0>(right) :
965  std::get<1>(left) < std::get<1>(right);
966  });
967 
968  // containers for finding the "best" hull...
969  std::vector<ConvexHull> convexHullVec;
970  std::vector<reco::ProjectedPointList> rejectedListVec;
971  bool increaseDepth(pointList.size() > 3);
972  float lastArea(std::numeric_limits<float>::max());
973 
974  while (increaseDepth) {
975  // Get another convexHull container
976  convexHullVec.push_back(ConvexHull(pointList, fConvexHullKinkAngle, fConvexHullMinSep));
977  rejectedListVec.push_back(reco::ProjectedPointList());
978 
979  const ConvexHull& convexHull = convexHullVec.back();
980  reco::ProjectedPointList& rejectedList = rejectedListVec.back();
981  const reco::ProjectedPointList& convexHullPoints = convexHull.getConvexHull();
982 
983  increaseDepth = false;
984 
985  if (convexHull.getConvexHullArea() > 0.) {
986  if (convexHullVec.size() < 2 || convexHull.getConvexHullArea() < 0.8 * lastArea) {
987  for (auto& point : convexHullPoints) {
988  pointList.remove(point);
989  rejectedList.emplace_back(point);
990  }
991  lastArea = convexHull.getConvexHullArea();
992  // increaseDepth = true;
993  }
994  }
995  }
996 
997  // do we have a valid convexHull?
998  while (!convexHullVec.empty() && convexHullVec.back().getConvexHullArea() < 0.5) {
999  convexHullVec.pop_back();
1000  rejectedListVec.pop_back();
1001  }
1002 
1003  // If we found the convex hull then build edges around the region
1004  if (!convexHullVec.empty()) {
1005  size_t nRejectedTotal(0);
1006  reco::HitPairListPtr locHitPairListPtr = hitPairListPtr;
1007 
1008  for (const auto& rejectedList : rejectedListVec) {
1009  nRejectedTotal += rejectedList.size();
1010 
1011  for (const auto& rejectedPoint : rejectedList) {
1012  if (convexHullVec.back().findNearestDistance(rejectedPoint) > 0.5)
1013  locHitPairListPtr.remove(std::get<2>(rejectedPoint));
1014  }
1015  }
1016 
1017  // Now add "edges" to the cluster to describe the convex hull (for the display)
1018  reco::ProjectedPointList& convexHullPointList = convexHull.getConvexHullPointList();
1019  reco::Hit3DToEdgeMap& edgeMap = convexHull.getConvexHullEdgeMap();
1020  reco::EdgeList& edgeList = convexHull.getConvexHullEdgeList();
1021 
1022  reco::ProjectedPoint lastPoint = convexHullVec.back().getConvexHull().front();
1023 
1024  for (auto& curPoint : convexHullVec.back().getConvexHull()) {
1025  if (curPoint == lastPoint) continue;
1026 
1027  const reco::ClusterHit3D* lastPoint3D = std::get<2>(lastPoint);
1028  const reco::ClusterHit3D* curPoint3D = std::get<2>(curPoint);
1029 
1030  float distBetweenPoints = (curPoint3D->getPosition()[0] - lastPoint3D->getPosition()[0]) *
1031  (curPoint3D->getPosition()[0] - lastPoint3D->getPosition()[0]) +
1032  (curPoint3D->getPosition()[1] - lastPoint3D->getPosition()[1]) *
1033  (curPoint3D->getPosition()[1] - lastPoint3D->getPosition()[1]) +
1034  (curPoint3D->getPosition()[2] - lastPoint3D->getPosition()[2]) *
1035  (curPoint3D->getPosition()[2] - lastPoint3D->getPosition()[2]);
1036 
1037  distBetweenPoints = std::sqrt(distBetweenPoints);
1038 
1039  reco::EdgeTuple edge(lastPoint3D, curPoint3D, distBetweenPoints);
1040 
1041  convexHullPointList.push_back(curPoint);
1042  edgeMap[lastPoint3D].push_back(edge);
1043  edgeMap[curPoint3D].push_back(edge);
1044  edgeList.emplace_back(edge);
1045 
1046  lastPoint = curPoint;
1047  }
1048 
1049  // Store the "extreme" points
1050  const ConvexHull::PointList& extremePoints = convexHullVec.back().getExtremePoints();
1051  reco::ProjectedPointList& extremePointList = convexHull.getConvexHullExtremePoints();
1052 
1053  for (const auto& point : extremePoints)
1054  extremePointList.push_back(point);
1055 
1056  // Store the "kink" points
1057  const reco::ConvexHullKinkTupleList& kinkPoints = convexHullVec.back().getKinkPoints();
1058  reco::ConvexHullKinkTupleList& kinkPointList = convexHull.getConvexHullKinkPoints();
1059 
1060  for (const auto& kink : kinkPoints)
1061  kinkPointList.push_back(kink);
1062  }
1063 
1064  return;
1065  }
1066 
1067  float
1069  const reco::ClusterHit3D* first3D,
1070  const reco::ClusterHit3D* last3D) const
1071  {
1072  float largestDistance(0.);
1073 
1074  // Note that edges are vectors and that the convex hull edge list will be ordered
1075  // 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
1076  // meaning that the dot product of the two edges becomes negative.
1077  reco::EdgeList::const_iterator firstEdgeItr = convexHull.begin();
1078 
1079  while (firstEdgeItr != convexHull.end()) {
1080  reco::EdgeList::const_iterator nextEdgeItr = firstEdgeItr;
1081 
1082  // Eigen::Vector2f firstEdgeVec(std::get<3>(*firstEdgeItr),std::get<);
1083  // Eigen::Vector2f lastPrimaryVec(lastPCA.getEigenVectors()[0][0],lastPCA.getEigenVectors()[0][1],lastPCA.getEigenVectors()[0][2]);
1084  // float cosToLast = newPrimaryVec.dot(lastPrimaryVec);
1085 
1086  while (++nextEdgeItr != convexHull.end()) {}
1087  }
1088 
1089  return largestDistance;
1090  }
1091 
1092  DEFINE_ART_CLASS_TOOL(MSTPathFinder)
1093 } // namespace lar_cluster3d
reco::HitPairListPtr & getBestHitPairListPtr()
Definition: Cluster3D.h:479
void RunPrimsAlgorithm(const reco::HitPairListPtr &, kdTree::KdTreeNode &, reco::ClusterParametersList &) const
Driver for Prim&#39;s algorithm.
bool getSvdOK() const
Definition: Cluster3D.h:243
void PCAAnalysis_3D(const reco::HitPairListPtr &hitPairList, reco::PrincipalComponents &pca, bool skeletonOnly=false) const
std::unordered_map< const reco::ClusterHit3D *, BestNodeTuple > BestNodeMap
std::tuple< const reco::ClusterHit3D *, float, float > BestNodeTuple
size_t fMinTinyClusterSize
Minimum size for a &quot;tiny&quot; cluster.
kdTree class definiton
Definition: kdTree.h:30
void FindBestPathInCluster(reco::ClusterParameters &) const
Algorithm to find the best path through the given cluster.
const geo::GeometryCore * geometry
const Eigen::Vector3f getPosition() const
Definition: Cluster3D.h:157
void initializeHistograms(art::TFileDirectory &) override
Interface for initializing histograms if they are desired Note that the idea is to put hisgtograms in...
std::list< ProjectedPoint > ProjectedPointList
Definition: Cluster3D.h:352
Declaration of signal hit object.
walls no right
Definition: selectors.fcl:105
reco::HitPairListPtr DepthFirstSearch(const reco::EdgeTuple &, const reco::Hit3DToEdgeMap &, float &) const
a depth first search to find longest branches
std::pair< MinMaxPoints, MinMaxPoints > MinMaxPointPair
std::list< Point > PointList
The list of the projected points.
Definition: ConvexHull.h:34
std::list< KdTreeNode > KdTreeNodeList
Definition: kdTree.h:67
Implements a kdTree for use in clustering.
size_t FindNearestNeighbors(const reco::ClusterHit3D *, const KdTreeNode &, CandPairList &, float &) const
Definition: kdTree.cxx:170
std::size_t size(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:561
reco::EdgeList & getBestEdgeList()
Definition: Cluster3D.h:480
reco::Hit3DToEdgeMap & getHit3DToEdgeMap()
Definition: Cluster3D.h:478
reco::HitPairListPtr & getHitPairListPtr()
Definition: Cluster3D.h:475
float fConvexHullMinSep
Min hit separation to conisder in convex hull.
MSTPathFinder(const fhicl::ParameterSet &)
Constructor.
process_name hit
Definition: cheaterreco.fcl:51
unsigned int getStatusBits() const
Definition: Cluster3D.h:156
TimeValues
enumerate the possible values for time checking if monitoring timing
IClusterModAlg interface class definiton.
void PruneAmbiguousHits(reco::ClusterParameters &, reco::Hit2DToClusterMap &) const
Prune the obvious ambiguous hits.
Implements a ConvexHull for use in clustering.
T abs(T value)
std::list< EdgeTuple > EdgeList
Definition: Cluster3D.h:344
walls no front
Definition: selectors.fcl:105
define a kd tree node
Definition: kdTree.h:118
const EigenValues & getEigenValues() const
Definition: Cluster3D.h:245
reco::PrincipalComponents & getFullPCA()
Definition: Cluster3D.h:476
reco::Hit3DToEdgeMap & getConvexHullEdgeMap()
Definition: Cluster3D.h:384
void LeastCostPath(const reco::EdgeTuple &, const reco::ClusterHit3D *, reco::ClusterParameters &, float &) const
Find the lowest cost path between two nodes using MST edges.
void ModifyClusters(reco::ClusterParametersList &) const override
Scan an input collection of clusters and modify those according to the specific implementing algorith...
reco::ProjectedPointList & getConvexHullExtremePoints()
Definition: Cluster3D.h:386
reco::ConvexHullKinkTupleList & getConvexHullKinkPoints()
Definition: Cluster3D.h:387
Define a container for working with the convex hull.
Definition: Cluster3D.h:359
std::unique_ptr< lar_cluster3d::IClusterParametersBuilder > fClusterBuilder
Common cluster builder tool.
const Eigen::Vector3f & getAvePosition() const
Definition: Cluster3D.h:247
Path checking algorithm has seen this hit.
Definition: Cluster3D.h:110
void ReconstructBestPath(const reco::ClusterHit3D *, BestNodeMap &, reco::HitPairListPtr &, reco::EdgeList &) const
std::list< ConvexHullKinkTuple > ConvexHullKinkTupleList
Definition: Cluster3D.h:354
std::tuple< const reco::ClusterHit3D *, const reco::ClusterHit3D *, double > EdgeTuple
Definition: Cluster3D.h:343
std::list< const reco::ClusterHit3D * > HitPairListPtr
Definition: Cluster3D.h:334
walls no left
Definition: selectors.fcl:105
float getConvexHullArea() const
recover the area of the convex hull
Definition: ConvexHull.h:78
The geometry of one entire detector, as served by art.
Definition: Geometry.h:181
const PointList & getConvexHull() const
recover the list of convex hull vertices
Definition: ConvexHull.h:58
PrincipalComponentsAlg fPCAAlg
float DistanceBetweenNodes(const reco::ClusterHit3D *, const reco::ClusterHit3D *) const
float fConvexHullKinkAngle
Angle to declare a kink in convex hull calc.
std::list< CandPair > CandPairList
Definition: kdTree.h:79
ConvexHull class definiton.
Definition: ConvexHull.h:27
This header file defines the interface to a principal components analysis designed to be used within ...
Encapsulate the geometry of a wire.
geo::Geometry const * fGeometry
float findConvexHullEndPoints(const reco::EdgeList &, const reco::ClusterHit3D *, const reco::ClusterHit3D *) const
reco::ConvexHull & getConvexHull()
Definition: Cluster3D.h:481
bool fEnableMonitoring
Data members to follow.
Encapsulate the construction of a single detector plane.
This provides an art tool interface definition for 3D Cluster algorithms.
std::vector< float > fTimeVector
void AStar(const reco::ClusterHit3D *, const reco::ClusterHit3D *, float alpha, kdTree::KdTreeNode &, reco::ClusterParameters &) const
Algorithm to find shortest path between two 3D hits.
T copy(T const &v)
std::pair< reco::ProjectedPoint, reco::ProjectedPoint > MinMaxPoints
Add ability to build the convex hull (these needs to be split out! )
std::unordered_map< const reco::ClusterHit2D *, ClusterToHitPairSetMap > Hit2DToClusterMap
Definition: Cluster3D.h:510
float getTimeToExecute() const override
If monitoring, recover the time to execute a particular function.
float getHitChiSquare() const
Definition: Cluster3D.h:165
std::unordered_map< const reco::ClusterHit3D *, reco::EdgeList > Hit3DToEdgeMap
Definition: Cluster3D.h:346
void configure(fhicl::ParameterSet const &pset) override
Interface for configuring the particular algorithm tool.
float getTimeToExecute() const
Definition: kdTree.h:95
reco::ProjectedPointList & getConvexHullPointList()
Definition: Cluster3D.h:383
void buildConvexHull(reco::ClusterParameters &, reco::HitPairListPtr &, int level=0) const
reco::ProjectedPointList & getProjectedPointList()
Definition: Cluster3D.h:382
KdTreeNode & BuildKdTree(Hit3DVec::iterator, Hit3DVec::iterator, KdTreeNodeList &, int depth=0) const
Given an input set of ClusterHit3D objects, build a kd tree structure.
Definition: kdTree.cxx:112
reco::EdgeList & getConvexHullEdgeList()
Definition: Cluster3D.h:385
bool empty(FixedBins< T, C > const &) noexcept
Definition: FixedBins.h:555
std::tuple< float, float, const reco::ClusterHit3D * > ProjectedPoint
Projected coordinates and pointer to hit.
Definition: Cluster3D.h:351
art framework interface to geometry description
BEGIN_PROLOG could also be cout
std::list< ClusterParameters > ClusterParametersList
Definition: Cluster3D.h:403
const EigenVectors & getEigenVectors() const
Definition: Cluster3D.h:246
attached to a cluster
Definition: Cluster3D.h:108
void setStatusBit(unsigned bits) const
Definition: Cluster3D.h:179