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