LArSoft  v07_13_02
Liquid Argon Software toolkit - http://larsoft.org/
Cluster3D_module.cc
Go to the documentation of this file.
1 
46 // Framework Includes
51 #include "cetlib/search_path.h"
52 #include "cetlib/cpu_timer.h"
53 
54 // LArSoft includes
70 
85 
86 // ROOT includes
87 #include "TTree.h"
88 
89 // std includes
90 #include <string>
91 #include <functional>
92 #include <iostream>
93 #include <memory>
94 
95 //------------------------------------------------------------------------------------------------------------------------------------------
96 
97 namespace lar_cluster3d
98 {
99 using RecobHitToPtrMap = std::map<const recob::Hit*, art::Ptr<recob::Hit>>;
100 using Hit3DToSPPtrMap = std::map<const reco::ClusterHit3D*, size_t>;
101 using RecobHitVector = std::vector<art::Ptr<recob::Hit>>;
102 
103 //------------------------------------------------------------------------------------------------------------------------------------------
104 // Definition of the producer module here
105 
110 {
111 public:
117  Cluster3D(fhicl::ParameterSet const &pset);
118 
122  virtual ~Cluster3D();
123 
127  void beginJob();
128  void endJob();
129  void produce(art::Event &evt);
130  void reconfigure(fhicl::ParameterSet const &pset);
131 
132 private:
133 
135  {
136  public:
137  ArtOutputHandler(const art::EDProducer& owner, art::Event& evt, std::string& instanceName, std::string& extremeName) :
138  artPCAxisVector( new std::vector<recob::PCAxis> ),
139  artPFParticleVector( new std::vector<recob::PFParticle> ),
140  artClusterVector( new std::vector<recob::Cluster> ),
141  artSpacePointVector( new std::vector<recob::SpacePoint> ),
142  artVertexPointVector( new std::vector<recob::SpacePoint> ),
143  artExtremePointVector( new std::vector<recob::SpacePoint> ),
144  artSeedVector( new std::vector<recob::Seed> ),
145  artEdgeVector( new std::vector<recob::Edge> ),
146  artVertexEdgeVector( new std::vector<recob::Edge> ),
147  artClusterAssociations( new art::Assns<recob::Cluster, recob::Hit> ),
148  artPFPartAxisAssociations( new art::Assns<recob::PFParticle, recob::PCAxis> ),
149  artPFPartClusAssociations( new art::Assns<recob::PFParticle, recob::Cluster> ),
150  artPFPartSPAssociations( new art::Assns<recob::PFParticle, recob::SpacePoint> ),
151  artPFPartSeedAssociations( new art::Assns<recob::PFParticle, recob::Seed> ),
152  artPFPartEdgeAssociations( new art::Assns<recob::PFParticle, recob::Edge> ),
153  artSeedHitAssociations( new art::Assns<recob::Seed, recob::Hit> ),
154  artSPHitAssociations( new art::Assns<recob::SpacePoint, recob::Hit> ),
155  artEdgeSPAssociations( new art::Assns<recob::Edge, recob::SpacePoint> ),
156  fOwner(owner),
157  fEvt(evt),
158  fInstanceName(instanceName),
159  fExtremeName(extremeName)
160  {}
161 
163  {
165  }
166 
168  {
170  }
171 
173  {
175  }
176 
177  void makePFPartSeedAssns(size_t numSeedsStart)
178  {
180  }
181 
182  void makePFPartClusterAssns(size_t clusterStart)
183  {
185  }
186 
187  void makePFPartSpacePointAssns(size_t spacePointStart)
188  {
190  }
191 
192  void makePFPartEdgeAssns(size_t edgeStart)
193  {
195  }
196 
198  {
199  fEvt.put(std::move(artPCAxisVector));
200  fEvt.put(std::move(artPFParticleVector));
201  fEvt.put(std::move(artClusterVector));
202  fEvt.put(std::move(artSpacePointVector));
205  fEvt.put(std::move(artSeedVector));
206  fEvt.put(std::move(artEdgeVector));
208  fEvt.put(std::move(artPFPartAxisAssociations));
209  fEvt.put(std::move(artPFPartClusAssociations));
210  fEvt.put(std::move(artClusterAssociations));
211  fEvt.put(std::move(artPFPartSPAssociations));
212  fEvt.put(std::move(artPFPartSeedAssociations));
213  fEvt.put(std::move(artPFPartEdgeAssociations));
214  fEvt.put(std::move(artSeedHitAssociations));
215  fEvt.put(std::move(artSPHitAssociations));
216  fEvt.put(std::move(artEdgeSPAssociations));
217  }
218 
219  std::unique_ptr< std::vector<recob::PCAxis>> artPCAxisVector;
220  std::unique_ptr< std::vector<recob::PFParticle>> artPFParticleVector;
221  std::unique_ptr< std::vector<recob::Cluster>> artClusterVector;
222  std::unique_ptr< std::vector<recob::SpacePoint>> artSpacePointVector;
223  std::unique_ptr< std::vector<recob::SpacePoint>> artVertexPointVector;
224  std::unique_ptr< std::vector<recob::SpacePoint>> artExtremePointVector;
225  std::unique_ptr< std::vector<recob::Seed>> artSeedVector;
226  std::unique_ptr< std::vector<recob::Edge>> artEdgeVector;
227  std::unique_ptr< std::vector<recob::Edge>> artVertexEdgeVector;
228 
229  std::unique_ptr< art::Assns<recob::Cluster, recob::Hit>> artClusterAssociations;
230  std::unique_ptr< art::Assns<recob::PFParticle, recob::PCAxis>> artPFPartAxisAssociations;
231  std::unique_ptr< art::Assns<recob::PFParticle, recob::Cluster>> artPFPartClusAssociations;
232  std::unique_ptr< art::Assns<recob::PFParticle, recob::SpacePoint>> artPFPartSPAssociations;
233  std::unique_ptr< art::Assns<recob::PFParticle, recob::Seed>> artPFPartSeedAssociations;
234  std::unique_ptr< art::Assns<recob::PFParticle, recob::Edge>> artPFPartEdgeAssociations;
235  std::unique_ptr< art::Assns<recob::Seed, recob::Hit>> artSeedHitAssociations;
236  std::unique_ptr< art::Assns<recob::SpacePoint, recob::Hit>> artSPHitAssociations;
237  std::unique_ptr< art::Assns<recob::Edge, recob::SpacePoint>> artEdgeSPAssociations;
238  private:
241  std::string& fInstanceName;
242  std::string& fExtremeName;
243  };
244 
250  void PrepareEvent(const art::Event &evt);
251 
255  void InitializeMonitoring();
256 
266  void findTrackSeeds(art::Event& evt,
268  RecobHitToPtrMap& hitToPtrMap,
269  std::vector<recob::Seed>& seedVec,
270  art::Assns<recob::Seed,recob::Hit>& seedHitAssns) const;
271 
278  void splitClustersWithMST(reco::ClusterParameters& clusterParameters,
279  reco::ClusterParametersList& clusterParametersList) const;
280 
287  void splitClustersWithHough(reco::ClusterParameters& clusterParameters,
288  reco::ClusterParametersList& clusterParametersList) const;
289 
298  size_t ConvertToArtOutput(ArtOutputHandler& output,
299  reco::ClusterParameters& clusterParameters,
300  size_t pfParticleParent,
301  RecobHitToPtrMap& hitToPtrMap,
302  Hit3DToSPPtrMap& hit3DToSPPtrMap) const;
303 
313  reco::HitPairListPtr& clusHitPairVector,
314  RecobHitToPtrMap& hitToPtrMap,
315  Hit3DToSPPtrMap& hit3DToSPPtrMap,
316  int spacePointStart) const;
317 
324  void MakeAndSaveKinkPoints(ArtOutputHandler& output, reco::ConvexHullKinkTupleList& clusHitPairVector) const;
325 
335  dcel2d::HalfEdgeList&) const;
336 
343  using IdxToPCAMap = std::map<size_t,const reco::PrincipalComponents*>;
344 
347  IdxToPCAMap&) const;
348 
359  reco::ClusterParameters& clusterParameters,
360  size_t pfParticleParent,
361  IdxToPCAMap& idxToPCAMap,
362  RecobHitToPtrMap& hitToPtrMap,
363  Hit3DToSPPtrMap& hit3DToSPPtrMap) const;
364 
373  reco::HitPairList& hitPairList,
374  reco::ClusterParametersList& clusterParametersList,
375  RecobHitToPtrMap& hitToPtrMap) const;
376 
384  {
385  return fabs(pca.getEigenVectors()[2][0]) > m_parallelHitsCosAng && 3. * sqrt(pca.getEigenValues()[1]) > m_parallelHitsTransWid;
386  }
387 
393  size_t countUltimateDaughters(reco::ClusterParameters& clusterParameters) const;
394 
401 
405  TTree* m_pRecoTree;
406  int m_run;
407  int m_event;
408  int m_hits;
409  float m_totalTime;
413  float m_dbscanTime;
416  float m_finishTime;
417  std::string m_spacePointInstance;
418  std::string m_extremeInstance;
419 
425 
426  // Algorithms
427  std::unique_ptr<lar_cluster3d::IHit3DBuilder> m_hit3DBuilderAlg;
428  std::unique_ptr<lar_cluster3d::IClusterAlg> m_clusterAlg;
429  std::unique_ptr<lar_cluster3d::IClusterModAlg> m_clusterMergeAlg;
430  std::unique_ptr<lar_cluster3d::IClusterModAlg> m_clusterPathAlg;
437 };
438 
440 
441 } // namespace lar_cluster3d
442 
443 //------------------------------------------------------------------------------------------------------------------------------------------
444 // implementation follows
445 
446 namespace lar_cluster3d {
447 
449  m_clusterBuilder(pset.get<fhicl::ParameterSet>("ClusterParamsBuilder")),
450  m_pcaAlg(pset.get<fhicl::ParameterSet>("PrincipalComponentsAlg")),
451  m_skeletonAlg(pset.get<fhicl::ParameterSet>("SkeletonAlg")),
452  m_seedFinderAlg(pset.get<fhicl::ParameterSet>("SeedFinderAlg")),
453  m_pcaSeedFinderAlg(pset.get<fhicl::ParameterSet>("PCASeedFinderAlg")),
454  m_parallelHitsAlg(pset.get<fhicl::ParameterSet>("ParallelHitsAlg"))
455 {
456  this->reconfigure(pset);
457 
458  m_spacePointInstance = "Voronoi";
459  m_extremeInstance = "Extreme";
460 
461  produces< std::vector<recob::PCAxis>>();
462  produces< std::vector<recob::PFParticle>>();
463  produces< std::vector<recob::Cluster>>();
464  produces< std::vector<recob::SpacePoint>>();
465  produces< std::vector<recob::SpacePoint>>(m_spacePointInstance);
466  produces< std::vector<recob::SpacePoint>>(m_extremeInstance);
467  produces< std::vector<recob::Seed>>();
468  produces< std::vector<recob::Edge>>();
469  produces< std::vector<recob::Edge>>(m_spacePointInstance);
470  produces< art::Assns<recob::PFParticle, recob::PCAxis>>();
471  produces< art::Assns<recob::PFParticle, recob::Cluster>>();
472  produces< art::Assns<recob::PFParticle, recob::SpacePoint>>();
473  produces< art::Assns<recob::PFParticle, recob::Seed>>();
474  produces< art::Assns<recob::PFParticle, recob::Edge>>();
475  produces< art::Assns<recob::Seed, recob::Hit>>();
476  produces< art::Assns<recob::Cluster, recob::Hit>>();
477  produces< art::Assns<recob::SpacePoint, recob::Hit>>();
478  produces< art::Assns<recob::Edge, recob::SpacePoint>>();
479 }
480 
481 //------------------------------------------------------------------------------------------------------------------------------------------
482 
484 {
485 }
486 
487 //------------------------------------------------------------------------------------------------------------------------------------------
488 
490 {
491  m_enableMonitoring = pset.get<bool> ("EnableMonitoring", false);
492  m_parallelHitsCosAng = pset.get<float>("ParallelHitsCosAng", 0.999);
493  m_parallelHitsTransWid = pset.get<float>("ParallelHitsTransWid", 25.0);
494 
495  m_hit3DBuilderAlg = art::make_tool<lar_cluster3d::IHit3DBuilder>(pset.get<fhicl::ParameterSet>("Hit3DBuilderAlg"));
496  m_clusterAlg = art::make_tool<lar_cluster3d::IClusterAlg>(pset.get<fhicl::ParameterSet>("ClusterAlg"));
497  m_clusterMergeAlg = art::make_tool<lar_cluster3d::IClusterModAlg>(pset.get<fhicl::ParameterSet>("ClusterMergeAlg"));
498  m_clusterPathAlg = art::make_tool<lar_cluster3d::IClusterModAlg>(pset.get<fhicl::ParameterSet>("ClusterPathAlg"));
499 
500  m_pcaAlg.reconfigure(pset.get<fhicl::ParameterSet>("PrincipalComponentsAlg"));
501  m_skeletonAlg.reconfigure(pset.get<fhicl::ParameterSet>("SkeletonAlg"));
502  m_seedFinderAlg.reconfigure(pset.get<fhicl::ParameterSet>("SeedFinderAlg"));
503  m_pcaSeedFinderAlg.reconfigure(pset.get<fhicl::ParameterSet>("PCASeedFinderAlg"));
504  m_parallelHitsAlg.reconfigure(pset.get<fhicl::ParameterSet>("ParallelHitsAlg"));
505 }
506 
507 //------------------------------------------------------------------------------------------------------------------------------------------
508 
510 {
515  if (m_enableMonitoring)
516  this->InitializeMonitoring();
517 
519 
520  m_geometry = &*geometry;
521  m_detector = lar::providerFrom<detinfo::DetectorPropertiesService>();
522 }
523 
524 //------------------------------------------------------------------------------------------------------------------------------------------
525 
527 {
528 }
529 
530 //------------------------------------------------------------------------------------------------------------------------------------------
531 
533 {
537  mf::LogInfo("Cluster3D") << " *** Cluster3D::produce(...) [Run=" << evt.run() << ", Event=" << evt.id().event() << "] Starting Now! *** " << std::endl;
538 
539  // Set up for monitoring the timing... at some point this should be removed in favor of
540  // external profilers
541  cet::cpu_timer theClockTotal;
542  cet::cpu_timer theClockFinish;
543 
544  if (m_enableMonitoring) theClockTotal.start();
545 
546  // This really only does anything if we are monitoring since it clears our tree variables
547  this->PrepareEvent(evt);
548 
549  // Get instances of the primary data structures needed
550  reco::ClusterParametersList clusterParametersList;
551  IHit3DBuilder::RecobHitToPtrMap clusterHitToArtPtrMap;
552  std::unique_ptr< reco::HitPairList > hitPairList(new reco::HitPairList); // Potentially lots of hits, use heap instead of stack
553 
554  // Call the algorithm that builds 3D hits
555  m_hit3DBuilderAlg->Hit3DBuilder(evt, *hitPairList, clusterHitToArtPtrMap);
556 
557  std::cout << "++> Produced: " << hitPairList->size() << " hits" << std::endl;
558 
559  // Call the main workhorse algorithm for building the local version of candidate 3D clusters
560  m_clusterAlg->Cluster3DHits(*hitPairList, clusterParametersList);
561 
562  std::cout << "++> Produced: " << clusterParametersList.size() << " clusters" << std::endl;
563 
564  // Try merging clusters
565  m_clusterMergeAlg->ModifyClusters(clusterParametersList);
566 
567  // Run the path finding
568  m_clusterPathAlg->ModifyClusters(clusterParametersList);
569 
570  if(m_enableMonitoring) theClockFinish.start();
571 
572  // Get the art ouput object
574 
575  std::cout << "++> Outputting clusters" << std::endl;
576 
577  // Call the module that does the end processing (of which there is quite a bit of work!)
578  // This goes here to insure that something is always written to the data store
579  ProduceArtClusters(output, *hitPairList, clusterParametersList, clusterHitToArtPtrMap);
580 
581  // Output to art
582  output.outputObjects();
583 
584  if (m_enableMonitoring) theClockFinish.stop();
585 
586  // If monitoring then deal with the fallout
587  if (m_enableMonitoring)
588  {
589  theClockTotal.stop();
590 
591  m_run = evt.run();
592  m_event = evt.id().event();
593  m_totalTime = theClockTotal.accumulated_real_time();
597  m_dbscanTime = m_clusterAlg->getTimeToExecute(IClusterAlg::RUNDBSCAN) +
598  m_clusterAlg->getTimeToExecute(IClusterAlg::BUILDCLUSTERINFO);
599  m_clusterMergeTime = m_clusterMergeAlg->getTimeToExecute();
600  m_pathFindingTime = m_clusterPathAlg->getTimeToExecute();
601  m_finishTime = theClockFinish.accumulated_real_time();
602  m_hits = static_cast<int>(clusterHitToArtPtrMap.size());
603  m_pRecoTree->Fill();
604 
605  mf::LogDebug("Cluster3D") << "*** Cluster3D total time: " << m_totalTime << ", art: " << m_artHitsTime << ", make: " << m_makeHitsTime
606  << ", build: " << m_buildNeighborhoodTime << ", clustering: " << m_dbscanTime << ", merge: " << m_clusterMergeTime << ", path: " << m_pathFindingTime << ", finish: " << m_finishTime << std::endl;
607  }
608 
609  // Will we ever get here? ;-)
610  return;
611 }
612 
613 //------------------------------------------------------------------------------------------------------------------------------------------
614 
616 {
618  m_pRecoTree = tfs->make<TTree>("monitoring", "LAr Reco");
619  m_pRecoTree->Branch("run", &m_run, "run/I");
620  m_pRecoTree->Branch("event", &m_event, "event/I");
621  m_pRecoTree->Branch("hits", &m_hits, "hits/I");
622  m_pRecoTree->Branch("totalTime", &m_totalTime, "time/F");
623  m_pRecoTree->Branch("artHitsTime", &m_artHitsTime, "time/F");
624  m_pRecoTree->Branch("makeHitsTime", &m_makeHitsTime, "time/F");
625  m_pRecoTree->Branch("buildneigborhoodTime", &m_buildNeighborhoodTime, "time/F");
626  m_pRecoTree->Branch("dbscanTime", &m_dbscanTime, "time/F");
627  m_pRecoTree->Branch("clusterMergeTime", &m_clusterMergeTime, "time/F");
628  m_pRecoTree->Branch("pathfindingtime", &m_pathFindingTime, "time/F");
629  m_pRecoTree->Branch("finishTime", &m_finishTime, "time/F");
630 
631  m_clusterPathAlg->initializeHistograms(*tfs.get());
632 
633  return;
634 }
635 
636 //------------------------------------------------------------------------------------------------------------------------------------------
637 
639 {
640  m_run = evt.run();
641  m_event = evt.id().event();
642  m_hits = 0;
643  m_totalTime = 0.f;
644  m_artHitsTime = 0.f;
645  m_makeHitsTime = 0.f;
647  m_dbscanTime = 0.f;
648  m_pathFindingTime = 0.f;
649  m_finishTime = 0.f;
650 }
651 
652 //------------------------------------------------------------------------------------------------------------------------------------------
653 
656  RecobHitToPtrMap& hitToPtrMap,
657  std::vector<recob::Seed>& seedVec,
658  art::Assns<recob::Seed,recob::Hit>& seedHitAssns) const
659 {
665  // Make sure we are using the right pca
666  reco::PrincipalComponents& fullPCA = cluster.getFullPCA();
667  reco::PrincipalComponents& skeletonPCA = cluster.getSkeletonPCA();
668  reco::HitPairListPtr& hitPairListPtr = cluster.getHitPairListPtr();
669  reco::HitPairListPtr skeletonListPtr;
670 
671  // We want to work with the "skeleton" hits so first step is to call the algorithm to
672  // recover only these hits from the entire input collection
673  m_skeletonAlg.GetSkeletonHits(hitPairListPtr, skeletonListPtr);
674 
675  // Skeleton hits are nice but we can do better if we then make a pass through to "average"
676  // the skeleton hits position in the Y-Z plane
677  m_skeletonAlg.AverageSkeletonPositions(skeletonListPtr);
678 
679  SeedHitPairListPairVec seedHitPairVec;
680 
681  // Some combination of the elements below will be used to determine which seed finding algorithm
682  // to pursue below
683  float eigenVal0 = 3. * sqrt(skeletonPCA.getEigenValues()[0]);
684  float eigenVal1 = 3. * sqrt(skeletonPCA.getEigenValues()[1]);
685  float eigenVal2 = 3. * sqrt(skeletonPCA.getEigenValues()[2]);
686  float transRMS = sqrt(std::pow(eigenVal1,2) + std::pow(eigenVal2,2));
687 
688  bool foundGoodSeed(false);
689 
690  // Choose a method for finding the seeds based on the PCA that was run...
691  // Currently we have an ad hoc if-else block which I hope will be improved soon!
692  if (aParallelHitsCluster(fullPCA))
693  {
694  // In this case we have a track moving relatively parallel to the wire plane with lots of
695  // ambiguous 3D hits. Your best bet here is to use the "parallel hits" algorithm to get the
696  // best axis and seeds
697  // This algorithm does not fail (foundGoodSeed will always return true)
698  foundGoodSeed = m_parallelHitsAlg.findTrackSeeds(hitPairListPtr, skeletonPCA, seedHitPairVec);
699  }
700  else if (eigenVal0 > 40. && transRMS < 5.)
701  {
702  // If the input cluster is relatively "straight" then chances are it is a single straight track,
703  // probably a CR muon, and we can simply use the PCA to determine the seed
704  // This algorithm will check both "ends" of the input hits and if the angles become inconsistent
705  // then it will "fail"
706  foundGoodSeed = m_pcaSeedFinderAlg.findTrackSeeds(skeletonListPtr, skeletonPCA, seedHitPairVec);
707  }
708 
709  // In the event the above two methods failed then we hit it with the real seed finder
710  if (!foundGoodSeed)
711  {
712  // If here then we have a complicated 3D cluster and we'll use the hough transform algorithm to
713  // return a list of candidate seeds and seed hits
714  m_seedFinderAlg.findTrackSeeds(skeletonListPtr, skeletonPCA, seedHitPairVec);
715  }
716 
717  // Go through the returned lists and build out the art friendly seeds and hits
718  for(const auto& seedHitPair : seedHitPairVec)
719  {
720  seedVec.push_back(seedHitPair.first);
721 
722  // We use a set here because our 3D hits can share 2D hits
723  // The set will make sure we get unique combinations of 2D hits
724  std::set<art::Ptr<recob::Hit> > seedHitSet;
725 
726  for(const auto& hit3D : seedHitPair.second)
727  {
728  for(const auto& hit2D : hit3D->getHits())
729  {
730  if (!hit2D) continue;
731 
732  const recob::Hit* recobHit = &hit2D->getHit();
733 
734  seedHitSet.insert(hitToPtrMap[recobHit]);
735  }
736  }
737 
738  RecobHitVector seedHitVec;
739 
740  for(const auto& hit2D : seedHitSet) seedHitVec.push_back(hit2D);
741 
742  util::CreateAssn(*this, evt, seedVec, seedHitVec, seedHitAssns);
743  }
744 
745  return;
746 }
747 
749 {
750  bool operator()(const std::pair<float, const reco::ClusterHit3D*>& left, const std::pair<float, const reco::ClusterHit3D*>& right)
751  {
752  return left.first < right.first;
753  }
754 };
755 
756 void Cluster3D::splitClustersWithMST(reco::ClusterParameters& clusterParameters, reco::ClusterParametersList& clusterParametersList) const
757 {
758  // This is being left in place for future development. Essentially, it was an attempt to implement
759  // a Minimum Spanning Tree as a way to split a particular cluster topology, one where two straight
760  // tracks cross closely enought to appear as one cluster. As of Feb 2, 2015 I think the idea is still
761  // worth merit so am leaving this module in place for now.
762  //
763  // If this routine is called then we believe we have a cluster which needs splitting.
764  // The way we will do this is to use a Minimum Spanning Tree algorithm to associate all
765  // hits together by their distance apart. In theory, we should be able to split the cluster
766  // by finding the largest distance and splitting at that point.
767  //
768  // Typedef some data structures that we will use.
769  // Start with the adjacency map
770  typedef std::pair<float, const reco::ClusterHit3D*> DistanceHit3DPair;
771  typedef std::list<DistanceHit3DPair > DistanceHit3DPairList;
772  typedef std::map<const reco::ClusterHit3D*, DistanceHit3DPairList > Hit3DToDistanceMap;
773 
774  // Now typedef the lists we'll keep
775  typedef std::list<const reco::ClusterHit3D*> Hit3DList;
776  typedef std::pair<Hit3DList::iterator, Hit3DList::iterator> Hit3DEdgePair;
777  typedef std::pair<float, Hit3DEdgePair > DistanceEdgePair;
778  typedef std::list<DistanceEdgePair > DistanceEdgePairList;
779 
780  struct DistanceEdgePairOrder
781  {
782  bool operator()(const DistanceEdgePair& left, const DistanceEdgePair& right) const
783  {
784  return left.first > right.first;
785  }
786  };
787 
788  // Recover the hits we'll work on.
789  // Note that we use on the skeleton hits so will need to recover them
790  reco::HitPairListPtr& hitPairListPtr = clusterParameters.getHitPairListPtr();
791  reco::HitPairListPtr skeletonListPtr;
792 
793  // We want to work with the "skeleton" hits so first step is to call the algorithm to
794  // recover only these hits from the entire input collection
795  m_skeletonAlg.GetSkeletonHits(hitPairListPtr, skeletonListPtr);
796 
797  // Skeleton hits are nice but we can do better if we then make a pass through to "average"
798  // the skeleton hits position in the Y-Z plane
799  m_skeletonAlg.AverageSkeletonPositions(skeletonListPtr);
800 
801  // First task is to define and build the adjacency map
802  Hit3DToDistanceMap hit3DToDistanceMap;
803 
804  for(reco::HitPairListPtr::const_iterator hit3DOuterItr = skeletonListPtr.begin(); hit3DOuterItr != skeletonListPtr.end(); )
805  {
806  const reco::ClusterHit3D* hit3DOuter = *hit3DOuterItr++;
807  DistanceHit3DPairList& outerHitList = hit3DToDistanceMap[hit3DOuter];
808  TVector3 outerPos(hit3DOuter->getPosition()[0], hit3DOuter->getPosition()[1], hit3DOuter->getPosition()[2]);
809 
810  for(reco::HitPairListPtr::const_iterator hit3DInnerItr = hit3DOuterItr; hit3DInnerItr != skeletonListPtr.end(); hit3DInnerItr++)
811  {
812  const reco::ClusterHit3D* hit3DInner = *hit3DInnerItr;
813  TVector3 innerPos(hit3DInner->getPosition()[0], hit3DInner->getPosition()[1], hit3DInner->getPosition()[2]);
814  TVector3 deltaPos = innerPos - outerPos;
815  float hitDistance(float(deltaPos.Mag()));
816 
817  if (hitDistance > 20.) continue;
818 
819  hit3DToDistanceMap[hit3DInner].emplace_back(DistanceHit3DPair(hitDistance,hit3DOuter));
820  outerHitList.emplace_back(DistanceHit3DPair(hitDistance,hit3DInner));
821  }
822 
823  // Make sure our membership bit is clear
825  }
826 
827  // Make pass through again to order each of the lists
828  for(auto& mapPair : hit3DToDistanceMap)
829  {
830  mapPair.second.sort(Hit3DDistanceOrder());
831  }
832 
833  // Get the containers for the MST to operate on/with
834  Hit3DList hit3DList;
835  DistanceEdgePairList distanceEdgePairList;
836 
837  // Initialize with first element
838  hit3DList.emplace_back(skeletonListPtr.front());
839  distanceEdgePairList.emplace_back(DistanceEdgePair(0.,Hit3DEdgePair(hit3DList.begin(),hit3DList.begin())));
840 
841  skeletonListPtr.front()->setStatusBit(reco::ClusterHit3D::SELECTEDBYMST);
842 
843  float largestDistance(0.);
844  float averageDistance(0.);
845 
846  // Now run the MST
847  // Basically, we loop until the MST list is the same size as the input list
848  while(hit3DList.size() < skeletonListPtr.size())
849  {
850  Hit3DList::iterator bestHit3DIter = hit3DList.begin();
851  float bestDist = 10000000.;
852 
853  // Loop through all hits currently in the list and look for closest hit not in the list
854  for(Hit3DList::iterator hit3DIter = hit3DList.begin(); hit3DIter != hit3DList.end(); hit3DIter++)
855  {
856  const reco::ClusterHit3D* hit3D = *hit3DIter;
857 
858  // For the given 3D hit, find the closest to it that is not already in the list
859  DistanceHit3DPairList& nearestList = hit3DToDistanceMap[hit3D];
860 
861  while(!nearestList.empty())
862  {
863  const reco::ClusterHit3D* hit3DToCheck = nearestList.front().second;
864 
865  if (!hit3DToCheck->bitsAreSet(reco::ClusterHit3D::SELECTEDBYMST))
866  {
867  if (nearestList.front().first < bestDist)
868  {
869  bestHit3DIter = hit3DIter;
870  bestDist = nearestList.front().first;
871  }
872 
873  break;
874  }
875  else nearestList.pop_front();
876  }
877  }
878 
879  if (bestDist > largestDistance) largestDistance = bestDist;
880 
881  averageDistance += bestDist;
882 
883  // Now we add the best hit not in the list to our list, keep track of the distance
884  // to the object it was closest to
885  const reco::ClusterHit3D* bestHit3D = *bestHit3DIter; // "best" hit already in the list
886  const reco::ClusterHit3D* nextHit3D = hit3DToDistanceMap[bestHit3D].front().second; // "next" hit we are adding to the list
887 
888  Hit3DList::iterator nextHit3DIter = hit3DList.insert(hit3DList.end(),nextHit3D);
889 
890  distanceEdgePairList.emplace_back(DistanceEdgePair(bestDist,Hit3DEdgePair(bestHit3DIter,nextHit3DIter)));
891 
893  }
894 
895  averageDistance /= float(hit3DList.size());
896 
897  float thirdDist = 2.*sqrt(clusterParameters.getSkeletonPCA().getEigenValues()[2]);
898 
899  // Ok, find the largest distance in the iterator map
900  distanceEdgePairList.sort(DistanceEdgePairOrder());
901 
902  DistanceEdgePairList::iterator largestDistIter = distanceEdgePairList.begin();
903 
904  for(DistanceEdgePairList::iterator edgeIter = distanceEdgePairList.begin(); edgeIter != distanceEdgePairList.end(); edgeIter++)
905  {
906  if (edgeIter->first < thirdDist) break;
907 
908  largestDistIter = edgeIter;
909  }
910 
911  reco::HitPairListPtr::iterator breakIter = largestDistIter->second.second;
912  reco::HitPairListPtr bestList;
913 
914  bestList.resize(std::distance(hit3DList.begin(), breakIter));
915 
916  std::copy(hit3DList.begin(), breakIter, bestList.begin());
917 
918  // Remove from the grand hit list and see what happens...
919  // The pieces below are incomplete and were really for testing only.
920  hitPairListPtr.sort();
921  bestList.sort();
922 
923  reco::HitPairListPtr::iterator newListEnd =
924  std::set_difference(hitPairListPtr.begin(), hitPairListPtr.end(),
925  bestList.begin(), bestList.end(),
926  hitPairListPtr.begin() );
927 
928  hitPairListPtr.erase(newListEnd, hitPairListPtr.end());
929 
930  return;
931 }
932 
934 {
935 public:
936  CopyIfInRange(float maxRange) : m_maxRange(maxRange) {}
937 
938  bool operator()(const reco::ClusterHit3D* hit3D)
939  {
940  return hit3D->getDocaToAxis() < m_maxRange;
941  }
942 private:
943  float m_maxRange;
944 };
945 
947  reco::ClusterParametersList& clusterParametersList) const
948 {
949  // @brief A method for splitted "crossed tracks" clusters into separate clusters
950  //
951  // If this routine is called then we believe we have a cluster which needs splitting.
952  // The specific topology we are looking for is two long straight tracks which cross at some
953  // point in close proximity so their hits were joined into a single 3D cluster. The method
954  // to split this topology is to let the hough transform algorithm find the two leading candidates
955  // and then to see if we use those to build two clusters instead of one.
956 
957  // Recover the hits we'll work on.
958  // Note that we use on the skeleton hits so will need to recover them
959  reco::HitPairListPtr& hitPairListPtr = clusterParameters.getHitPairListPtr();
960  reco::HitPairListPtr skeletonListPtr;
961 
962  // We want to work with the "skeleton" hits so first step is to call the algorithm to
963  // recover only these hits from the entire input collection
964  m_skeletonAlg.GetSkeletonHits(hitPairListPtr, skeletonListPtr);
965 
966  // Skeleton hits are nice but we can do better if we then make a pass through to "average"
967  // the skeleton hits position in the Y-Z plane
968  m_skeletonAlg.AverageSkeletonPositions(skeletonListPtr);
969 
970  // Define the container for our lists of hits
971  reco::HitPairListPtrList hitPairListPtrList;
972 
973  // Now feed this to the Hough Transform to find candidate straight lines
974  m_seedFinderAlg.findTrackHits(skeletonListPtr, clusterParameters.getSkeletonPCA(), hitPairListPtrList);
975 
976  // We need at least two lists or else there is nothing to do
977  if (hitPairListPtrList.size() < 2) return;
978 
979  // The game plan will be the following:
980  // 1) Take the first list of hits and run the PCA on this to get an axis
981  // - Then calculate the 3d doca for ALL hits in the cluster to this axis
982  // - Move all hits within "3 sigam" of the axis to a new list
983  // 2) run the PCA on the second list of hits to get that axis
984  // - Then calculate the 3d doca for all hits in our first list
985  // - Copy hits in the first list which are within 3 sigma of the new axis
986  // back into our original cluster - these are shared hits
987  reco::HitPairListPtrList::iterator hitPairListIter = hitPairListPtrList.begin();
988  reco::HitPairListPtr& firstHitList = *hitPairListIter++;
989  reco::PrincipalComponents firstHitListPCA;
990 
991  m_pcaAlg.PCAAnalysis_3D(firstHitList, firstHitListPCA);
992 
993  // Make sure we have a successful calculation.
994  if (firstHitListPCA.getSvdOK())
995  {
996  // The fill routines below will expect to see unused 2D hits so we need to clear the
997  // status bits... and I am not sure of a better way...
998  for(const auto& hit3D : hitPairListPtr)
999  {
1000  for(const auto& hit2D : hit3D->getHits())
1001  if (hit2D) hit2D->clearStatusBits(0x1);
1002  }
1003 
1004  // Calculate the 3D doca's for the hits which were used to make this PCA
1005  m_pcaAlg.PCAAnalysis_calc3DDocas(firstHitList, firstHitListPCA);
1006 
1007  // Divine from the ether some maximum allowed range for transfering hits
1008  float allowedHitRange = 6. * firstHitListPCA.getAveHitDoca();
1009 
1010  // Now go through and calculate the 3D doca's for ALL the hits in the original cluster
1011  m_pcaAlg.PCAAnalysis_calc3DDocas(hitPairListPtr, firstHitListPCA);
1012 
1013  // Let's make a new cluster to hold the hits
1014  clusterParametersList.push_back(reco::ClusterParameters());
1015 
1016  // Can we get a reference to what we just created?
1017  reco::ClusterParameters& newClusterParams = clusterParametersList.back();
1018 
1019  reco::HitPairListPtr& newClusterHitList = newClusterParams.getHitPairListPtr();
1020 
1021  newClusterHitList.resize(hitPairListPtr.size());
1022 
1023  // Do the actual copy of the hits we want
1024  reco::HitPairListPtr::iterator newListEnd =
1025  std::copy_if(hitPairListPtr.begin(), hitPairListPtr.end(), newClusterHitList.begin(), CopyIfInRange(allowedHitRange));
1026 
1027  // Shrink to fit
1028  newClusterHitList.resize(std::distance(newClusterHitList.begin(), newListEnd));
1029 
1030  // And now remove these hits from the original cluster
1031  hitPairListPtr.remove_if(CopyIfInRange(allowedHitRange));
1032 
1033  // Get an empty hit to cluster map...
1034  reco::Hit2DToClusterMap hit2DToClusterMap;
1035 
1036  // Now "fill" the cluster parameters but turn off the hit rejection
1037  m_clusterBuilder.FillClusterParams(newClusterParams, hit2DToClusterMap, 0., 1.);
1038 
1039  // Set the skeleton pca to the value calculated above on input
1040  clusterParameters.getSkeletonPCA() = firstHitListPCA;
1041 
1042  // We are done with splitting out one track. Because the two tracks cross in
1043  // close proximity, this is the one case where we might consider sharing 3D hits
1044  // So let's make a little detour here to try to copy some of those hits back into
1045  // the main hit list
1046  reco::HitPairListPtr& secondHitList = *hitPairListIter;
1047  reco::PrincipalComponents secondHitListPCA;
1048 
1049  m_pcaAlg.PCAAnalysis_3D(secondHitList, secondHitListPCA);
1050 
1051  // Make sure we have a successful calculation.
1052  if (secondHitListPCA.getSvdOK())
1053  {
1054  // Calculate the 3D doca's for the hits which were used to make this PCA
1055  m_pcaAlg.PCAAnalysis_calc3DDocas(secondHitList, secondHitListPCA);
1056 
1057  // Since this is the "other" cluster, we'll be a bit more generous in adding back hits
1058  float newAllowedHitRange = 6. * secondHitListPCA.getAveHitDoca();
1059 
1060  // Go through and calculate the 3D doca's for the hits in our new candidate cluster
1061  m_pcaAlg.PCAAnalysis_calc3DDocas(newClusterHitList, secondHitListPCA);
1062 
1063  // Create a temporary list to fill with the hits we might want to save
1064  reco::HitPairListPtr tempHitList(newClusterHitList.size());
1065 
1066  // Do the actual copy of the hits we want...
1067  reco::HitPairListPtr::iterator tempListEnd =
1068  std::copy_if(newClusterHitList.begin(), newClusterHitList.end(), tempHitList.begin(), CopyIfInRange(newAllowedHitRange));
1069 
1070  hitPairListPtr.insert(hitPairListPtr.end(), tempHitList.begin(), tempListEnd);
1071  }
1072 
1073  // Of course, now we need to modify the original cluster parameters
1074  reco::ClusterParameters originalParams(hitPairListPtr);
1075 
1076  // Now "fill" the cluster parameters but turn off the hit rejection
1077  m_clusterBuilder.FillClusterParams(originalParams, hit2DToClusterMap, 0., 1.);
1078 
1079  // Overwrite original cluster parameters with our new values
1080  clusterParameters.getClusterParams() = originalParams.getClusterParams();
1081  clusterParameters.getFullPCA() = originalParams.getFullPCA();
1082  clusterParameters.getSkeletonPCA() = secondHitListPCA;
1083  }
1084 
1085  return;
1086 }
1087 
1089  reco::HitPairList& hitPairVector,
1090  reco::ClusterParametersList& clusterParametersList,
1091  RecobHitToPtrMap& hitToPtrMap) const
1092 {
1097  mf::LogDebug("Cluster3D") << " *** Cluster3D::ProduceArtClusters() *** " << std::endl;
1098 
1099  // Make sure there is something to do here!
1100  if (!clusterParametersList.empty())
1101  {
1102  // This is the loop over candidate 3D clusters
1103  // Note that it might be that the list of candidate clusters is modified by splitting
1104  // So we use the following construct to make sure we get all of them
1105  for(auto& clusterParameters : clusterParametersList)
1106  {
1107  // It should be straightforward at this point to transfer information from our vector of clusters
1108  // to the larsoft objects... of course we still have some work to do first, in particular to
1109  // find the candidate seeds and their seed hits
1110 
1111  // The chances of getting here and this condition not being true are probably zero... but check anyway
1112  if (!clusterParameters.getFullPCA().getSvdOK())
1113  {
1114  mf::LogDebug("Cluster3D") << "--> no feature extraction done on this cluster!!" << std::endl;
1115  continue;
1116  }
1117 
1118  // Keep track of hit 3D to SP for when we do edges
1119  Hit3DToSPPtrMap hit3DToSPPtrMap;
1120 
1121  // Keep track of current start for space points
1122  int spacePointStart(output.artSpacePointVector->size());
1123 
1124  // Do a special output of voronoi vertices here...
1125  dcel2d::VertexList& vertexList = clusterParameters.getVertexList();
1126  dcel2d::HalfEdgeList& halfEdgeList = clusterParameters.getHalfEdgeList();
1127 
1128  std::cout << "Preparing to save the vertex point list, size: " << vertexList.size() << ", half edges: " << halfEdgeList.size() << std::endl;
1129 
1130  MakeAndSaveVertexPoints(output, vertexList, halfEdgeList);
1131 
1132  // Special case handling... if no daughters then call standard conversion routine to make sure space points
1133  // created, etc.
1134  if (clusterParameters.daughterList().empty())
1135  {
1136  ConvertToArtOutput(output, clusterParameters, recob::PFParticle::kPFParticlePrimary, hitToPtrMap, hit3DToSPPtrMap);
1137 
1138  // Get the extreme points
1139  MakeAndSaveKinkPoints(output, clusterParameters.getConvexHull().getConvexHullKinkPoints()); //getConvexExtremePoints());
1140  }
1141  // Otherwise, the cluster has daughters so we handle specially
1142  else
1143  {
1144  // Set up to keep track of parent/daughters
1145  IdxToPCAMap idxToPCAMap;
1146  size_t numTotalDaughters = countUltimateDaughters(clusterParameters);
1147  size_t pfParticleIdx(output.artPFParticleVector->size() + numTotalDaughters);
1148 
1149  FindAndStoreDaughters(output, clusterParameters, pfParticleIdx, idxToPCAMap, hitToPtrMap, hit3DToSPPtrMap);
1150 
1151  // Now make the piecewise curve
1152 // MakeAndSavePCAPoints(output, clusterParameters.getFullPCA(), idxToPCAMap);
1153 
1154  // Need to make a daughter vec from our map
1155  std::vector<size_t> daughterVec;
1156 
1157  for(auto& idxToPCA : idxToPCAMap) daughterVec.emplace_back(idxToPCA.first);
1158 
1159  // Now create/handle the parent PFParticle
1160  recob::PFParticle pfParticle(13, pfParticleIdx, recob::PFParticle::kPFParticlePrimary, daughterVec);
1161  output.artPFParticleVector->push_back(pfParticle);
1162 
1163  recob::PCAxis::EigenVectors eigenVecs;
1164  double eigenVals[] = {0.,0.,0.};
1165  double avePosition[] = {0.,0.,0.};
1166 
1167  eigenVecs.resize(3);
1168 
1169  reco::PrincipalComponents& skeletonPCA = clusterParameters.getSkeletonPCA();
1170 
1171  for(size_t outerIdx = 0; outerIdx < 3; outerIdx++)
1172  {
1173  avePosition[outerIdx] = skeletonPCA.getAvePosition()[outerIdx];
1174  eigenVals[outerIdx] = skeletonPCA.getEigenValues()[outerIdx];
1175 
1176  eigenVecs[outerIdx].resize(3);
1177 
1178  for(size_t innerIdx = 0; innerIdx < 3; innerIdx++) eigenVecs[outerIdx][innerIdx] = skeletonPCA.getEigenVectors()[outerIdx][innerIdx];
1179  }
1180 
1181 
1182  recob::PCAxis skelPcAxis(skeletonPCA.getSvdOK(),
1183  skeletonPCA.getNumHitsUsed(),
1184  eigenVals, //skeletonPCA.getEigenValues(),
1185  eigenVecs, //skeletonPCA.getEigenVectors(),
1186  avePosition, //skeletonPCA.getAvePosition(),
1187  skeletonPCA.getAveHitDoca(),
1188  output.artPCAxisVector->size());
1189 
1190  output.artPCAxisVector->push_back(skelPcAxis);
1191 
1192  reco::PrincipalComponents& fullPCA = clusterParameters.getFullPCA();
1193 
1194  for(size_t outerIdx = 0; outerIdx < 3; outerIdx++)
1195  {
1196  avePosition[outerIdx] = fullPCA.getAvePosition()[outerIdx];
1197  eigenVals[outerIdx] = fullPCA.getEigenValues()[outerIdx];
1198 
1199  for(size_t innerIdx = 0; innerIdx < 3; innerIdx++) eigenVecs[outerIdx][innerIdx] = fullPCA.getEigenVectors()[outerIdx][innerIdx];
1200  }
1201 
1202  recob::PCAxis fullPcAxis(fullPCA.getSvdOK(),
1203  fullPCA.getNumHitsUsed(),
1204  eigenVals, //fullPCA.getEigenValues(),
1205  eigenVecs, //fullPCA.getEigenVectors(),
1206  avePosition, //fullPCA.getAvePosition(),
1207  fullPCA.getAveHitDoca(),
1208  output.artPCAxisVector->size());
1209 
1210  output.artPCAxisVector->push_back(fullPcAxis);
1211 
1212  // Create associations to the PFParticle
1213  output.makePFPartPCAAssns();
1214 
1215  // Make associations to all space points for this cluster
1216  MakeAndSaveSpacePoints(output, clusterParameters.getHitPairListPtr(), hitToPtrMap, hit3DToSPPtrMap, spacePointStart);
1217 
1218  // Get the extreme points
1219  MakeAndSaveKinkPoints(output, clusterParameters.getConvexHull().getConvexHullKinkPoints()); //getConvexExtremePoints());
1220 
1221  // Build the edges now
1222  size_t edgeStart(output.artEdgeVector->size());
1223 
1224  for(const auto& edge : clusterParameters.getConvexHull().getConvexHullEdgeList())
1225  {
1226  Hit3DToSPPtrMap::iterator hit0Itr = hit3DToSPPtrMap.find(std::get<0>(edge));
1227  Hit3DToSPPtrMap::iterator hit1Itr = hit3DToSPPtrMap.find(std::get<1>(edge));
1228 
1229  bool hit0Found = hit0Itr != hit3DToSPPtrMap.end();
1230  bool hit1Found = hit1Itr != hit3DToSPPtrMap.end();
1231 
1232  if (!hit0Found || !hit1Found) std::cout << "<<<<< Did not find matching space point " << hit0Found << ", " << hit1Found << " >>>>>>" << std::endl;
1233 
1234  output.artEdgeVector->push_back(recob::Edge(std::get<2>(edge), hit3DToSPPtrMap[std::get<0>(edge)], hit3DToSPPtrMap[std::get<1>(edge)], output.artEdgeVector->size()));
1235  }
1236 
1237  output.makePFPartEdgeAssns(edgeStart);
1238  }
1239  }
1240  }
1241 
1242  // Right now error matrix is uniform...
1243  int nFreePoints(0);
1244 
1245  // Run through the HitPairVector and add any unused hit pairs to the list
1246  for(auto& hitPair : hitPairVector)
1247  {
1248  if (hitPair->bitsAreSet(reco::ClusterHit3D::MADESPACEPOINT)) continue;
1249 
1250  double spacePointPos[] = {hitPair->getPosition()[0],hitPair->getPosition()[1],hitPair->getPosition()[2]};
1251  double spacePointErr[] = {1., 0., 0., 1., 0., 1.};
1252  double chisq(-100.);
1253 
1254  RecobHitVector recobHits;
1255 
1256  for(const auto hit : hitPair->getHits())
1257  {
1258  if (!hit)
1259  {
1260  chisq = -1000.;
1261  continue;
1262  }
1263 
1264  art::Ptr<recob::Hit> hitPtr = hitToPtrMap[&hit->getHit()];
1265  recobHits.push_back(hitPtr);
1266  }
1267 
1268  nFreePoints++;
1269 
1270  output.artSpacePointVector->push_back(recob::SpacePoint(spacePointPos, spacePointErr, chisq, output.artSpacePointVector->size()));
1271 
1272  if (!recobHits.empty()) output.makeSpacePointHitAssns(recobHits);
1273  }
1274 
1275  std::cout << "++++>>>> total num hits: " << hitPairVector.size() << ", num free: " << nFreePoints << std::endl;
1276 
1277  return;
1278 }
1279 
1281 {
1282  size_t localCount(0);
1283 
1284  if (!clusterParameters.daughterList().empty())
1285  {
1286  for(auto& clusterParams : clusterParameters.daughterList())
1287  localCount += countUltimateDaughters(clusterParams);
1288  }
1289  else localCount++;
1290 
1291  return localCount;
1292 }
1293 
1295  reco::ClusterParameters& clusterParameters,
1296  size_t pfParticleParent,
1297  IdxToPCAMap& idxToPCAMap,
1298  RecobHitToPtrMap& hitToPtrMap,
1299  Hit3DToSPPtrMap& hit3DToSPPtrMap) const
1300 {
1301  // This is a recursive routine, we keep calling ourself as long as the daughter list is non empty
1302  if (!clusterParameters.daughterList().empty())
1303  {
1304  for(auto& clusterParams : clusterParameters.daughterList())
1305  FindAndStoreDaughters(output, clusterParams, pfParticleParent, idxToPCAMap, hitToPtrMap, hit3DToSPPtrMap);
1306  }
1307  // Otherwise we want to store the information
1308  else
1309  {
1310  size_t daughterIdx = ConvertToArtOutput(output, clusterParameters, pfParticleParent, hitToPtrMap, hit3DToSPPtrMap);
1311 
1312  idxToPCAMap[daughterIdx] = &clusterParameters.getFullPCA();
1313  }
1314 
1315  return idxToPCAMap.size();
1316 }
1317 
1319  reco::ClusterParameters& clusterParameters,
1320  size_t pfParticleParent,
1321  RecobHitToPtrMap& hitToPtrMap,
1322  Hit3DToSPPtrMap& hit3DToSPPtrMap) const
1323 {
1324 
1325  // prepare the algorithm to compute the cluster characteristics;
1326  // we use the "standard" one here, except that we override selected items
1327  // (so, thanks to metaprogramming, we finally have wrappers of wrappers);
1328  // configuration would happen here, but we are using the default
1329  // configuration for that algorithm
1331 
1333 
1334  // It should be straightforward at this point to transfer information from our vector of clusters
1335  // to the larsoft objects... of course we still have some work to do first, in particular to
1336  // find the candidate seeds and their seed hits
1337 
1338  // We keep track of 2 PCA axes, the first is the "full" PCA run over all the 3D hits in the
1339  // candidate cluster. The second will be that derived from just using the "skeleton" hits.
1340  // Make a copy of the full PCA to keep that, then get a reference for the skeleton PCA
1341  reco::PrincipalComponents& fullPCA = clusterParameters.getFullPCA();
1342  reco::PrincipalComponents& skeletonPCA = clusterParameters.getSkeletonPCA();
1343 
1344  // As tracks become more parallel to the wire plane the number of "ambiguous" 3D hits can increase
1345  // rapidly. Now that we have more information we can go back through these hits and do a better job
1346  // selecting "the right ones". Here we call the "medial skeleton" algorithm which uses a modification
1347  // of a standard medial skeleton procedure to get the 3D hits we want
1348  // But note that even this is hopeless in the worst case and, in fact, it can be a time waster
1349  // So bypass when you recognize that condition
1350  /*
1351  if (!aParallelHitsCluster(fullPCA))
1352  {
1353  int nSkeletonPoints = m_skeletonAlg.FindMedialSkeleton(clusterParameters.getHitPairListPtr());
1354 
1355  // If enough skeleton points then rerun pca with only those
1356  if (nSkeletonPoints > 10)
1357  {
1358  // Now rerun the principal components axis on just those points
1359  m_pcaAlg.PCAAnalysis_3D(clusterParameters.getHitPairListPtr(), skeletonPCA, true);
1360 
1361  // If there was a failure (can that happen?) then restore the full PCA
1362  if (!skeletonPCA.getSvdOK()) skeletonPCA = fullPCA;
1363  }
1364 
1365  // Here we can try to handle a specific case. It can happen that two tracks (think CR muons here) pass so
1366  // close together at some point to get merged into one cluster. Now that we have skeletonized the hits and
1367  // have run the PCA on the skeleton points we can try to divide these two tracks. The signature will be that
1368  // their are a large number of total hits, that the PCA will have a large spread in two dimensions. The
1369  // spread in the third dimension will be an indicator of the actual separation between the two tracks
1370  // which we might try to exploit in the actual algorithm.
1371  // hardwire for now to see what is going on...
1372  if (skeletonPCA.getNumHitsUsed() > 1000 && skeletonPCA.getEigenValues()[1] > 100. && fabs(skeletonPCA.getEigenVectors()[2][0]) < m_parallelHitsCosAng)
1373  {
1374  mf::LogDebug("Cluster3D") << "--> Detected crossed axes!! Total # hits: " << fullPCA.getNumHitsUsed() <<
1375  "\n Skeleton PCA # hits: " << skeletonPCA.getNumHitsUsed() << ", eigenValues: " <<
1376  skeletonPCA.getEigenValues()[0] << ", " <<skeletonPCA.getEigenValues()[1] << ", " <<skeletonPCA.getEigenValues()[2] << std::endl;
1377 
1378  splitClustersWithHough(clusterParameters, clusterParametersList);
1379  }
1380  }
1381  */
1382  size_t clusterStart = output.artClusterVector->size();
1383 
1384  // Start loop over views to build out the hit lists and the 2D cluster objects
1385  for(reco::PlaneToClusterParamsMap::const_iterator planeItr = clusterParameters.getClusterParams().begin(); planeItr != clusterParameters.getClusterParams().end(); planeItr++)
1386  {
1387  const reco::RecobClusterParameters& clusParams = planeItr->second;
1388 
1389  // Protect against a missing view
1390  if (clusParams.m_view == geo::kUnknown) continue;
1391 
1392  // We love looping. In this case, our list of hits is comprised of "ClusterHits" and we need to get a RecobHitVector instead...
1393  RecobHitVector recobHits;
1394 
1395  for(reco::ClusterHit2DVec::const_iterator hitItr = clusParams.m_hitVector.begin(); hitItr != clusParams.m_hitVector.end(); hitItr++)
1396  {
1397  art::Ptr<recob::Hit> hitPtr = hitToPtrMap[&(*hitItr)->getHit()];
1398  recobHits.push_back(hitPtr);
1399  }
1400 
1401  // And sorting! Sorting is good for the mind, soul and body
1402  // ooopsss... don't do this else event display will look funky
1403  // std::sort(recobHits.begin(), recobHits.end());
1404 
1405  // Get the tdc/wire slope... from the unit vector...
1406  double startWire(clusParams.m_startWire);
1407  double endWire(clusParams.m_endWire);
1408  double startTime(clusParams.m_startTime);
1409  double endTime(clusParams.m_endTime);
1410 
1411  // plane ID is not a part of clusParams... get the one from the first hit
1412  geo::PlaneID plane; // invalid by default
1413  if (!recobHits.empty())
1414  plane = recobHits.front()->WireID().planeID();
1415 
1416  // feed the algorithm with all the cluster hits
1417  ClusterParamAlgo.ImportHits(recobHits);
1418 
1419  // create the recob::Cluster directly in the vector
1420  cluster::ClusterCreator artCluster(
1421  ClusterParamAlgo, // algo
1422  startWire, // start_wire
1423  0., // sigma_start_wire
1424  startTime, // start_tick
1425  clusParams.m_sigmaStartTime, // sigma_start_tick
1426  endWire, // end_wire
1427  0., // sigma_end_wire,
1428  endTime, // end_tick
1429  clusParams.m_sigmaEndTime, // sigma_end_tick
1430  output.artClusterVector->size(), // ID
1431  clusParams.m_view, // view
1432  plane, // plane
1433  recob::Cluster::Sentry // sentry
1434  );
1435 
1436  output.artClusterVector->emplace_back(artCluster.move());
1437 
1438  output.makeClusterHitAssns(recobHits);
1439  }
1440 
1441  // Last, let's try to get seeds for tracking..
1442  // Keep track of how many we have so far
1443  size_t numSeedsStart = output.artSeedVector->size();
1444 
1445  // Call the magical algorith to do the dirty work
1446  // findTrackSeeds(evt, clusterParameters, hitToPtrMap, *artSeedVector, *artSeedHitAssociations);
1447 
1448  // Deal with converting the Hit Pairs to art
1449  // Recover the hit pairs and start looping! Love to loop!
1450  reco::HitPairListPtr& clusHitPairVector = clusterParameters.getHitPairListPtr();
1451  // reco::HitPairListPtr& clusHitPairVector = clusterParameters.getBestHitPairListPtr();
1452 
1453  // Keep track of current start for space points
1454  int spacePointStart(output.artSpacePointVector->size());
1455 
1456  // Copy these hits to the vector to be stored with the event
1457  for (auto& hitPair : clusHitPairVector)
1458  {
1459  // Don't make space point if this hit was "rejected"
1460  if (hitPair->bitsAreSet(reco::ClusterHit3D::REJECTEDHIT)) continue;
1461 
1462  double chisq = hitPair->getHitChiSquare(); // secret handshake...
1463 
1464 // if ( hitPair->bitsAreSet(reco::ClusterHit3D::SKELETONHIT) && !hitPair->bitsAreSet(reco::ClusterHit3D::EDGEHIT)) chisq = -1.; // pure skeleton point
1465 // else if (!hitPair->bitsAreSet(reco::ClusterHit3D::SKELETONHIT) && hitPair->bitsAreSet(reco::ClusterHit3D::EDGEHIT)) chisq = -2.; // pure edge point
1466 // else if ( hitPair->bitsAreSet(reco::ClusterHit3D::SKELETONHIT) && hitPair->bitsAreSet(reco::ClusterHit3D::EDGEHIT)) chisq = -3.; // skeleton and edge point
1467 //
1468 // if (hitPair->bitsAreSet(reco::ClusterHit3D::SEEDHIT) ) chisq = -4.; // Seed point
1469 //
1470 // if ((hitPair->getStatusBits() & 0x7) != 0x7) chisq = -10.;
1471 
1472  // Mark this hit pair as in use
1473  hitPair->setStatusBit(reco::ClusterHit3D::MADESPACEPOINT);
1474 
1475  // Create and store the space point
1476  size_t spacePointID = output.artSpacePointVector->size();
1477  double spacePointPos[] = {hitPair->getPosition()[0],hitPair->getPosition()[1],hitPair->getPosition()[2]};
1478  double spacePointErr[] = {m_detector->GetXTicksCoefficient() * hitPair->getSigmaPeakTime(), 0., 0., 0.15, 0., 0.15};
1479  output.artSpacePointVector->push_back(recob::SpacePoint(spacePointPos, spacePointErr, chisq, output.artSpacePointVector->size()));
1480 
1481  // Update mapping
1482  hit3DToSPPtrMap[hitPair] = spacePointID;
1483 
1484  // space point hits associations
1485  RecobHitVector recobHits;
1486 
1487  for(const auto& hit : hitPair->getHits())
1488  {
1489  if (!hit) continue;
1490  art::Ptr<recob::Hit> hitPtr = hitToPtrMap[&hit->getHit()];
1491  recobHits.push_back(hitPtr);
1492  }
1493 
1494  if (!recobHits.empty()) output.makeSpacePointHitAssns(recobHits);
1495  }
1496 
1497  // Build the edges now
1498  size_t edgeStart(output.artEdgeVector->size());
1499 
1500  for(const auto& edge : clusterParameters.getConvexHull().getConvexHullEdgeList())
1501  output.artEdgeVector->emplace_back(std::get<2>(edge), hit3DToSPPtrMap[std::get<0>(edge)], hit3DToSPPtrMap[std::get<1>(edge)], output.artEdgeVector->size());
1502 
1503  // Empty daughter vector for now
1504  std::vector<size_t> nullVector;
1505  size_t pfParticleIdx(output.artPFParticleVector->size());
1506 
1507  recob::PFParticle pfParticle(13, pfParticleIdx, pfParticleParent, nullVector);
1508  output.artPFParticleVector->push_back(pfParticle);
1509 
1510  // Look at making the PCAxis and associations - for both the skeleton (the first) and the full
1511  // First need some float to double conversion containers
1512  recob::PCAxis::EigenVectors eigenVecs;
1513  double eigenVals[] = {0.,0.,0.};
1514  double avePosition[] = {0.,0.,0.};
1515 
1516  eigenVecs.resize(3);
1517 
1518  for(size_t outerIdx = 0; outerIdx < 3; outerIdx++)
1519  {
1520  avePosition[outerIdx] = skeletonPCA.getAvePosition()[outerIdx];
1521  eigenVals[outerIdx] = skeletonPCA.getEigenValues()[outerIdx];
1522 
1523  eigenVecs[outerIdx].resize(3);
1524 
1525  for(size_t innerIdx = 0; innerIdx < 3; innerIdx++) eigenVecs[outerIdx][innerIdx] = skeletonPCA.getEigenVectors()[outerIdx][innerIdx];
1526  }
1527 
1528 
1529  recob::PCAxis skelPcAxis(skeletonPCA.getSvdOK(),
1530  skeletonPCA.getNumHitsUsed(),
1531  eigenVals, //skeletonPCA.getEigenValues(),
1532  eigenVecs, //skeletonPCA.getEigenVectors(),
1533  avePosition, //skeletonPCA.getAvePosition(),
1534  skeletonPCA.getAveHitDoca(),
1535  output.artPCAxisVector->size());
1536 
1537  output.artPCAxisVector->push_back(skelPcAxis);
1538 
1539  for(size_t outerIdx = 0; outerIdx < 3; outerIdx++)
1540  {
1541  avePosition[outerIdx] = fullPCA.getAvePosition()[outerIdx];
1542  eigenVals[outerIdx] = fullPCA.getEigenValues()[outerIdx];
1543 
1544  for(size_t innerIdx = 0; innerIdx < 3; innerIdx++) eigenVecs[outerIdx][innerIdx] = fullPCA.getEigenVectors()[outerIdx][innerIdx];
1545  }
1546 
1547  recob::PCAxis fullPcAxis(fullPCA.getSvdOK(),
1548  fullPCA.getNumHitsUsed(),
1549  eigenVals, //fullPCA.getEigenValues(),
1550  eigenVecs, //fullPCA.getEigenVectors(),
1551  avePosition, //fullPCA.getAvePosition(),
1552  fullPCA.getAveHitDoca(),
1553  output.artPCAxisVector->size());
1554 
1555  output.artPCAxisVector->push_back(fullPcAxis);
1556 
1557  // Create associations to the PFParticle
1558  output.makePFPartPCAAssns();
1559  output.makePFPartSeedAssns(numSeedsStart);
1560  output.makePFPartClusterAssns(clusterStart);
1561  output.makePFPartSpacePointAssns(spacePointStart);
1562  output.makePFPartEdgeAssns(edgeStart);
1563 
1564  return pfParticleIdx;
1565 }
1566 
1568  reco::HitPairListPtr& clusHitPairVector,
1569  RecobHitToPtrMap& hitToPtrMap,
1570  Hit3DToSPPtrMap& hit3DToSPPtrMap,
1571  int spacePointStart) const
1572 {
1573  // Right now error matrix is uniform...
1574  double spError[] = {1., 0., 1., 0., 0., 1.};
1575 
1576  // Copy these hits to the vector to be stored with the event
1577  for (auto& hitPair : clusHitPairVector)
1578  {
1579  // Skip those space points that have already been created
1580  if (hit3DToSPPtrMap.find(hitPair) != hit3DToSPPtrMap.end()) continue;
1581 
1582  // Don't make space point if this hit was "rejected"
1583  if (hitPair->bitsAreSet(reco::ClusterHit3D::REJECTEDHIT)) continue;
1584 
1585  double chisq = hitPair->getHitChiSquare(); // secret handshake...
1586 
1587 // if ( hitPair->bitsAreSet(reco::ClusterHit3D::SKELETONHIT) && !hitPair->bitsAreSet(reco::ClusterHit3D::EDGEHIT)) chisq = -1.; // pure skeleton point
1588 // else if (!hitPair->bitsAreSet(reco::ClusterHit3D::SKELETONHIT) && hitPair->bitsAreSet(reco::ClusterHit3D::EDGEHIT)) chisq = -2.; // pure edge point
1589 // else if ( hitPair->bitsAreSet(reco::ClusterHit3D::SKELETONHIT) && hitPair->bitsAreSet(reco::ClusterHit3D::EDGEHIT)) chisq = -3.; // skeleton and edge point
1590 
1591 // if (hitPair->bitsAreSet(reco::ClusterHit3D::SEEDHIT) ) chisq = -4.; // Seed point
1592 
1593 // if ((hitPair->getStatusBits() & 0x7) != 0x7) chisq = -10.;
1594 
1595  // Mark this hit pair as in use
1596  hitPair->setStatusBit(reco::ClusterHit3D::MADESPACEPOINT);
1597 
1598  // Create and store the space point
1599  size_t spacePointID = output.artSpacePointVector->size();
1600  double spacePointPos[] = {hitPair->getPosition()[0],hitPair->getPosition()[1],hitPair->getPosition()[2]};
1601  output.artSpacePointVector->push_back(recob::SpacePoint(spacePointPos, spError, chisq, output.artSpacePointVector->size()));
1602 
1603  // Update mapping
1604  hit3DToSPPtrMap[hitPair] = spacePointID;
1605 
1606  // space point hits associations
1607  RecobHitVector recobHits;
1608 
1609  for(const auto& hit : hitPair->getHits())
1610  {
1611  if (!hit) continue;
1612  art::Ptr<recob::Hit> hitPtr = hitToPtrMap[&hit->getHit()];
1613  recobHits.push_back(hitPtr);
1614  }
1615 
1616  if (!recobHits.empty()) output.makeSpacePointHitAssns(recobHits);
1617  }
1618 
1619  output.makePFPartSpacePointAssns(spacePointStart);
1620 
1621  return;
1622 }
1623 
1625 {
1626  // Right now error matrix is uniform...
1627  double spError[] = {1., 0., 1., 0., 0., 1.};
1628 
1629  // Copy these hits to the vector to be stored with the event
1630  for (auto& kinkTuple : kinkTupleVec)
1631  {
1632  const reco::ClusterHit3D* hit = std::get<2>(std::get<0>(kinkTuple));
1633 
1634  double chisq = hit->getHitChiSquare(); // secret handshake...
1635 
1636  // Create and store the space point
1637  double spacePointPos[] = {hit->getPosition()[0],hit->getPosition()[1],hit->getPosition()[2]};
1638 
1639  output.artExtremePointVector->push_back(recob::SpacePoint(spacePointPos, spError, chisq, output.artExtremePointVector->size()));
1640  }
1641 
1642  return;
1643 }
1644 
1646  dcel2d::VertexList& vertexList,
1647  dcel2d::HalfEdgeList& halfEdgeList) const
1648 {
1649  // We actually do two things here:
1650  // 1) Create space points to represent the vertex locations of the voronoi diagram
1651  // 2) Create the edges that link the space points together
1652 
1653  // Set up the space point creation
1654  // Right now error matrix is uniform...
1655  double spError[] = {1., 0., 1., 0., 0., 1.};
1656  double chisq = 1.;
1657 
1658  // Keep track of the vertex to space point association
1659  std::map<const dcel2d::Vertex*,size_t> vertexToSpacePointMap;
1660 
1661  // Copy these hits to the vector to be stored with the event
1662  for (auto& vertex : vertexList)
1663  {
1664  const dcel2d::Coords& coords = vertex.getCoords();
1665 
1666  // Create and store the space point
1667  double spacePointPos[] = {coords[0], coords[1], coords[2]};
1668 
1669  vertexToSpacePointMap[&vertex] = output.artVertexPointVector->size();
1670 
1671  output.artVertexPointVector->emplace_back(spacePointPos, spError, chisq, output.artVertexPointVector->size());
1672  }
1673 
1674  // Try to avoid double counting
1675  std::set<const dcel2d::HalfEdge*> halfEdgeSet;
1676 
1677  // Build the edges now
1678  for(const auto& halfEdge : halfEdgeList)
1679  {
1680  // Recover twin
1681  const dcel2d::HalfEdge* twin = halfEdge.getTwinHalfEdge();
1682 
1683  // It can happen that we have no twin... and also check that we've not been here before
1684  if (twin && halfEdgeSet.find(twin) == halfEdgeSet.end())
1685  {
1686  // Recover the vertices
1687  const dcel2d::Vertex* fromVertex = twin->getTargetVertex();
1688  const dcel2d::Vertex* toVertex = halfEdge.getTargetVertex();
1689 
1690  // It can happen for the open edges that there is no target vertex
1691  if (!toVertex || !fromVertex) continue;
1692 
1693  if (vertexToSpacePointMap.find(fromVertex) == vertexToSpacePointMap.end() ||
1694  vertexToSpacePointMap.find(toVertex) == vertexToSpacePointMap.end()) continue;
1695 
1696  // Need the distance between vertices
1697  Eigen::Vector3f distVec = toVertex->getCoords() - fromVertex->getCoords();
1698 
1699  output.artVertexEdgeVector->emplace_back(distVec.norm(), vertexToSpacePointMap.at(fromVertex), vertexToSpacePointMap.at(toVertex), output.artEdgeVector->size());
1700 
1701  halfEdgeSet.insert(&halfEdge);
1702  }
1703  }
1704 
1705  return;
1706 }
1707 
1709  const reco::PrincipalComponents& fullPCA,
1710  IdxToPCAMap& idxToPCAMap) const
1711 {
1712  // We actually do two things here:
1713  // 1) Create space points from the centroids of the PCA for each cluster
1714  // 2) Create the edges that link the space points together
1715 
1716  // The first task is to put the list of PCA's into some semblance of order... they may be
1717  // preordered by likely they are piecewise ordered so fix that here
1718 
1719  // We'll need the current PCA axis to determine doca and arclen
1720  Eigen::Vector3f avePosition(fullPCA.getAvePosition()[0], fullPCA.getAvePosition()[1], fullPCA.getAvePosition()[2]);
1721  Eigen::Vector3f axisDirVec(fullPCA.getEigenVectors()[0][0], fullPCA.getEigenVectors()[0][1], fullPCA.getEigenVectors()[0][2]);
1722 
1723  using DocaToPCAPair = std::pair<float, const reco::PrincipalComponents*>;
1724  using DocaToPCAVec = std::vector<DocaToPCAPair>;
1725 
1726  DocaToPCAVec docaToPCAVec;
1727 
1728  // Outer loop over views
1729  for (const auto& idxToPCA : idxToPCAMap)
1730  {
1731  const reco::PrincipalComponents* pca = idxToPCA.second;
1732 
1733  // Now we need to calculate the doca and poca...
1734  // Start by getting this hits position
1735  Eigen::Vector3f pcaPos(pca->getAvePosition()[0],pca->getAvePosition()[1],pca->getAvePosition()[2]);
1736 
1737  // Form a TVector from this to the cluster average position
1738  Eigen::Vector3f pcaToAveVec = pcaPos - avePosition;
1739 
1740  // With this we can get the arclength to the doca point
1741  float arclenToPoca = pcaToAveVec.dot(axisDirVec);
1742 
1743  docaToPCAVec.emplace_back(DocaToPCAPair(arclenToPoca,pca));
1744  }
1745 
1746  std::sort(docaToPCAVec.begin(),docaToPCAVec.end(),[](const auto& left, const auto& right){return left.first < right.first;});
1747 
1748  // Set up the space point creation
1749  // Right now error matrix is uniform...
1750  double spError[] = {1., 0., 1., 0., 0., 1.};
1751  double chisq = 1.;
1752 
1753  const reco::PrincipalComponents* lastPCA(NULL);
1754 
1755  // Set up to loop through the clusters
1756  for(const auto& docaToPCAPair : docaToPCAVec)
1757  {
1758  // Recover the PCA for this cluster
1759  const reco::PrincipalComponents* curPCA = docaToPCAPair.second;
1760 
1761  if(lastPCA)
1762  {
1763  double lastPointPos[] = {lastPCA->getAvePosition()[0],lastPCA->getAvePosition()[1],lastPCA->getAvePosition()[2]};
1764  size_t lastPointBin = output.artVertexPointVector->size();
1765  double curPointPos[] = {curPCA->getAvePosition()[0],curPCA->getAvePosition()[1],curPCA->getAvePosition()[2]};
1766  size_t curPointBin = lastPointBin + 1;
1767 
1768  output.artVertexPointVector->emplace_back(lastPointPos, spError, chisq, lastPointBin);
1769  output.artVertexPointVector->emplace_back(curPointPos, spError, chisq, curPointBin);
1770 
1771  Eigen::Vector3f distVec(curPointPos[0]-lastPointPos[0],curPointPos[1]-lastPointPos[1],curPointPos[2]-lastPointPos[2]);
1772 
1773  output.artVertexEdgeVector->emplace_back(distVec.norm(), lastPointBin, curPointBin, output.artEdgeVector->size());
1774  }
1775 
1776  lastPCA = curPCA;
1777  }
1778 
1779  return;
1780 }
1781 
1782 } // namespace lar_cluster3d
std::vector< SeedHitPairListPair > SeedHitPairListPairVec
float m_buildNeighborhoodTime
Keeps track of time to build epsilon neighborhood.
std::unique_ptr< std::vector< recob::Edge > > artEdgeVector
bool operator()(const reco::ClusterHit3D *hit3D)
void PCAAnalysis_calc3DDocas(const reco::HitPairListPtr &hitPairVector, const reco::PrincipalComponents &pca) const
std::unique_ptr< lar_cluster3d::IClusterModAlg > m_clusterPathAlg
Algorithm to do cluster path finding.
bool getSvdOK() const
Definition: Cluster3D.h:226
constexpr auto const & right(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:112
size_t FindAndStoreDaughters(ArtOutputHandler &output, reco::ClusterParameters &clusterParameters, size_t pfParticleParent, IdxToPCAMap &idxToPCAMap, RecobHitToPtrMap &hitToPtrMap, Hit3DToSPPtrMap &hit3DToSPPtrMap) const
This will produce art output for daughters noting that it needs to be done recursively.
An object to define a "edge" which is used to connect space points in a triangulation algorithm...
void PCAAnalysis_3D(const reco::HitPairListPtr &hitPairList, reco::PrincipalComponents &pca, bool skeletonOnly=false) const
Class managing the creation of a new recob::Cluster object.
Reconstruction base classes.
This algorithm will create and then cluster 3D hits using DBScan.
std::unique_ptr< art::Assns< recob::Seed, recob::Hit > > artSeedHitAssociations
T * get() const
Definition: ServiceHandle.h:71
static constexpr size_t kPFParticlePrimary
Define index to signify primary particle.
Definition: PFParticle.h:61
intermediate_table::iterator iterator
std::list< HalfEdge > HalfEdgeList
Definition: DCEL.h:180
virtual void reconfigure(fhicl::ParameterSet const &pset)
a handler for the case where the algorithm control parameters are to be reset
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
float m_parallelHitsTransWid
Cut on transverse width of cluster (PCA 2nd eigenvalue)
HalfEdge * getTwinHalfEdge() const
Definition: DCEL.h:159
Unknown view.
Definition: geo_types.h:83
Float_t x1[n_points_granero]
Definition: compare.C:5
void reconfigure(fhicl::ParameterSet const &pset)
a handler for the case where the algorithm control parameters are to be reset
Cluster3D class.
Definition: SkeletonAlg.h:33
float m_parallelHitsCosAng
Cut for PCA 3rd axis angle to X axis.
bool aParallelHitsCluster(const reco::PrincipalComponents &pca) const
There are several places we will want to know if a candidate cluster is a "parallel hits" type of clu...
Declaration of signal hit object.
std::unique_ptr< art::Assns< recob::PFParticle, recob::Seed > > artPFPartSeedAssociations
reco::PrincipalComponents & getSkeletonPCA()
Definition: Cluster3D.h:455
void clearStatusBits(unsigned bits) const
Definition: Cluster3D.h:166
void makeClusterHitAssns(RecobHitVector &recobHits)
virtual void reconfigure(fhicl::ParameterSet const &pset)
a handler for the case where the algorithm control parameters are to be reset
The data type to uniquely identify a Plane.
Definition: geo_types.h:250
SkeletonAlg m_skeletonAlg
Skeleton point finder.
virtual bool findTrackSeeds(reco::HitPairListPtr &hitPairListPtr, reco::PrincipalComponents &inputPCA, SeedHitPairListPairVec &seedHitMap) const
Given the list of hits this will search for candidate Seed objects and return them.
std::unique_ptr< std::vector< recob::PFParticle > > artPFParticleVector
STL namespace.
std::unique_ptr< art::Assns< recob::PFParticle, recob::SpacePoint > > artPFPartSPAssociations
Hit has been rejected for any reason.
Definition: Cluster3D.h:92
std::unique_ptr< art::Assns< recob::Edge, recob::SpacePoint > > artEdgeSPAssociations
const float * getAvePosition() const
Definition: Cluster3D.h:230
int getNumHitsUsed() const
Definition: Cluster3D.h:227
HoughSeedFinderAlg class.
std::string m_spacePointInstance
Special instance name for vertex points.
std::list< HitPairListPtr > HitPairListPtrList
Definition: Cluster3D.h:319
A utility class used in construction of 3D clusters.
Definition: Cluster3D.h:282
reco::HitPairListPtr & getHitPairListPtr()
Definition: Cluster3D.h:453
void makePFPartSeedAssns(size_t numSeedsStart)
Cluster finding and building.
std::unique_ptr< art::Assns< recob::PFParticle, recob::Cluster > > artPFPartClusAssociations
reco::PlaneToClusterParamsMap & getClusterParams()
Definition: Cluster3D.h:451
HoughSeedFinderAlg m_seedFinderAlg
Seed finder.
std::unique_ptr< lar_cluster3d::IClusterAlg > m_clusterAlg
Algorithm to do 3D space point clustering.
float m_makeHitsTime
Keeps track of time to build 3D hits.
void GetSkeletonHits(const reco::HitPairListPtr &inputHitList, reco::HitPairListPtr &skeletonHitList) const
Return the skeleton hits from the input list.
This is an algorithm for finding recob::Seed objects in 3D clusters.
float getDocaToAxis() const
Definition: Cluster3D.h:156
static const SentryArgument_t Sentry
An instance of the sentry object.
Definition: Cluster.h:182
float m_dbscanTime
Keeps track of time to run DBScan.
Hit has been used in Cluster Splitting MST.
Definition: Cluster3D.h:105
void reconfigure(fhicl::ParameterSet const &pset)
a handler for the case where the algorithm control parameters are to be reset
Definition: SkeletonAlg.cxx:43
std::list< std::unique_ptr< reco::ClusterHit3D >> HitPairList
Definition: Cluster3D.h:321
void produce(art::Event &evt)
ProductID put(std::unique_ptr< PROD > &&product)
Definition: Event.h:102
ClusterParametersList & daughterList()
Definition: Cluster3D.h:427
auto vector(Vector const &v)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:265
std::map< const recob::Hit *, art::Ptr< recob::Hit >> RecobHitToPtrMap
void FillClusterParams(reco::ClusterParameters &, reco::Hit2DToClusterMap &, double minUniqueFrac=0., double maxLostFrac=1.) const
Fill the cluster parameters (expose to outside world for case of splitting/merging clusters) ...
std::unique_ptr< art::Assns< recob::SpacePoint, recob::Hit > > artSPHitAssociations
virtual void reconfigure(fhicl::ParameterSet const &pset)
a handler for the case where the algorithm control parameters are to be reset
Overrides another ClusterParamsAlgBase class with selected constants.
This is an algorithm for finding recob::Seed objects in 3D clusters.
intermediate_table::const_iterator const_iterator
std::string m_extremeInstance
Instance name for the extreme points.
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:42
reco::PrincipalComponents & getFullPCA()
Definition: Cluster3D.h:454
size_t countUltimateDaughters(reco::ClusterParameters &clusterParameters) const
Count number of end of line daughters.
std::unique_ptr< std::vector< recob::SpacePoint > > artExtremePointVector
This is an algorithm for finding recob::Seed objects in 3D clusters.
void splitClustersWithMST(reco::ClusterParameters &clusterParameters, reco::ClusterParametersList &clusterParametersList) const
Attempt to split clusters by using a minimum spanning tree.
ClusterParamsBuilder class definiton.
void beginJob()
declare the standard art functions that we&#39;ll implement in this producer module
virtual double GetXTicksCoefficient(int t, int c) const =0
std::unique_ptr< std::vector< recob::PCAxis > > artPCAxisVector
void InitializeMonitoring()
Initialize the internal monitoring.
parameter set interface
const detinfo::DetectorProperties * m_detector
Pointer to the detector properties.
virtual ~Cluster3D()
Destructor.
float m_finishTime
Keeps track of time to run output module.
Helper functions to create a cluster.
Wrapper for ClusterParamsAlgBase objects to accept diverse input.
T get(std::string const &key) const
Definition: ParameterSet.h:231
float m_clusterMergeTime
Keeps track of the time to merge clusters.
PCASeedFinderAlg m_pcaSeedFinderAlg
Use PCA axis to find seeds.
std::list< ConvexHullKinkTuple > ConvexHullKinkTupleList
Definition: Cluster3D.h:336
void AverageSkeletonPositions(reco::HitPairListPtr &skeletonHitList) const
Modifies the position of input skeleton hits by averaging along the "best" wire direction.
Wrapper for ClusterParamsAlgBase objects to accept arbitrary input.
ParallelHitsSeedFinderAlg m_parallelHitsAlg
Deal with parallel hits clusters.
std::unique_ptr< std::vector< recob::Edge > > artVertexEdgeVector
void MakeAndSavePCAPoints(ArtOutputHandler &, const reco::PrincipalComponents &, IdxToPCAMap &) const
std::list< const reco::ClusterHit3D * > HitPairListPtr
Definition: Cluster3D.h:317
bool CreateAssn(PRODUCER const &prod, art::Event &evt, std::vector< T > const &a, art::Ptr< U > const &b, art::Assns< U, T > &assn, std::string a_instance, size_t indx=UINT_MAX)
Creates a single one-to-one association.
This provides an art tool interface definition for 3D Cluster algorithms.
The geometry of one entire detector, as served by art.
Definition: Geometry.h:110
int m_hits
Keeps track of the number of hits seen.
std::map< const recob::Hit *, art::Ptr< recob::Hit >> RecobHitToPtrMap
Defines a structure mapping art representation to internal.
Definition: IHit3DBuilder.h:44
Declaration of cluster object.
size_t ConvertToArtOutput(ArtOutputHandler &output, reco::ClusterParameters &clusterParameters, size_t pfParticleParent, RecobHitToPtrMap &hitToPtrMap, Hit3DToSPPtrMap &hit3DToSPPtrMap) const
Produces the art output from all the work done in this producer module.
const float * getPosition() const
Definition: Cluster3D.h:147
geo::Geometry * m_geometry
pointer to the Geometry service
Detector simulation of raw signals on wires.
bool operator()(const std::pair< float, const reco::ClusterHit3D * > &left, const std::pair< float, const reco::ClusterHit3D * > &right)
This provides an art tool interface definition for tools which construct 3D hits used in 3D clusterin...
This header file defines the interface to a principal components analysis designed to be used within ...
Encapsulate the geometry of a wire.
float m_pathFindingTime
Keeps track of the path finding time.
virtual bool findTrackHits(reco::HitPairListPtr &hitPairListPtr, reco::PrincipalComponents &inputPCA, reco::HitPairListPtrList &hitPairListPtrList) const
Given the list of hits this will return the sets of hits which belong on the same line...
std::unique_ptr< std::vector< recob::SpacePoint > > artVertexPointVector
ClusterParamsBuilder m_clusterBuilder
Common cluster builder tool.
Cluster3D(fhicl::ParameterSet const &pset)
Constructor.
std::unique_ptr< std::vector< recob::SpacePoint > > artSpacePointVector
constexpr auto const & left(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:104
reco::ConvexHull & getConvexHull()
Definition: Cluster3D.h:459
ArtOutputHandler(const art::EDProducer &owner, art::Event &evt, std::string &instanceName, std::string &extremeName)
std::map< size_t, const reco::PrincipalComponents * > IdxToPCAMap
Special routine to handle creating and saving space points & edges PCA points.
T * make(ARGS...args) const
Hierarchical representation of particle flow.
Definition: PFParticle.h:44
Utility object to perform functions of association.
bool bitsAreSet(const unsigned int &bitsToCheck) const
Definition: Cluster3D.h:162
const float * getEigenValues() const
Definition: Cluster3D.h:228
Encapsulate the construction of a single detector plane.
const float getAveHitDoca() const
Definition: Cluster3D.h:231
void makePFPartSpacePointAssns(size_t spacePointStart)
void makePFPartClusterAssns(size_t clusterStart)
Header file to define the interface to the SkeletonAlg.
void ImportHits(Iter begin, Iter end)
Calls SetHits() with the hits in the sequence.
Hit has been made into Space Point.
Definition: Cluster3D.h:96
MaybeLogger_< ELseverityLevel::ELsev_success, false > LogDebug
This provides an art tool interface definition for 3D Cluster algorithms.
void reconfigure(fhicl::ParameterSet const &pset)
void MakeAndSaveVertexPoints(ArtOutputHandler &, dcel2d::VertexList &, dcel2d::HalfEdgeList &) const
Special routine to handle creating and saving space points & edges associated to voronoi diagrams...
std::unique_ptr< lar_cluster3d::IClusterModAlg > m_clusterMergeAlg
Algorithm to do cluster merging.
std::unique_ptr< lar_cluster3d::IHit3DBuilder > m_hit3DBuilderAlg
Builds the 3D hits to operate on.
PrincipalComponentsAlg m_pcaAlg
Principal Components algorithm.
HLT enums.
EventNumber_t event() const
Definition: EventID.h:117
std::unordered_map< const reco::ClusterHit2D *, ClusterToHitPairSetMap > Hit2DToClusterMap
Definition: Cluster3D.h:488
virtual bool findTrackSeeds(reco::HitPairListPtr &hitPairListPtr, reco::PrincipalComponents &inputPCA, SeedHitPairListPairVec &seedHitPairVec) const
Given the list of hits this will search for candidate Seed objects and return them.
ClusterHit2DVec m_hitVector
Definition: Cluster3D.h:309
2D representation of charge deposited in the TDC/wire plane
Definition: Hit.h:49
Algorithm collection class computing cluster parameters.
float getHitChiSquare() const
Definition: Cluster3D.h:155
Definition of the Cluster3D class.
float m_artHitsTime
Keeps track of time to recover hits.
Interface to class computing cluster parameters.
std::unique_ptr< art::Assns< recob::PFParticle, recob::PCAxis > > artPFPartAxisAssociations
TCEvent evt
Definition: DataStructs.cxx:5
void MakeAndSaveSpacePoints(ArtOutputHandler &output, reco::HitPairListPtr &clusHitPairVector, RecobHitToPtrMap &hitToPtrMap, Hit3DToSPPtrMap &hit3DToSPPtrMap, int spacePointStart) const
Special routine to handle creating and saving space points.
Vertex * getTargetVertex() const
Definition: DCEL.h:157
Eigen::Vector3f Coords
Definition: DCEL.h:36
void findTrackSeeds(art::Event &evt, reco::ClusterParameters &cluster, RecobHitToPtrMap &hitToPtrMap, std::vector< recob::Seed > &seedVec, art::Assns< recob::Seed, recob::Hit > &seedHitAssns) const
An interface to the seed finding algorithm.
void makeSpacePointHitAssns(RecobHitVector &recobHits)
RunNumber_t run() const
Definition: Event.h:77
void MakeAndSaveKinkPoints(ArtOutputHandler &output, reco::ConvexHullKinkTupleList &clusHitPairVector) const
Special routine to handle creating and saving space points.
PCASeedFinderAlg class.
reco::EdgeList & getConvexHullEdgeList()
Definition: Cluster3D.h:364
std::vector< std::vector< double > > EigenVectors
Definition: PCAxis.h:29
std::unique_ptr< art::Assns< recob::Cluster, recob::Hit > > artClusterAssociations
void PrepareEvent(const art::Event &evt)
Event Preparation.
std::unique_ptr< art::Assns< recob::PFParticle, recob::Edge > > artPFPartEdgeAssociations
const Coords & getCoords() const
Definition: DCEL.h:61
std::list< Vertex > VertexList
Definition: DCEL.h:178
virtual bool findTrackSeeds(reco::HitPairListPtr &hitPairListPtr, reco::PrincipalComponents &inputPCA, SeedHitPairListPairVec &seedHitMap) const
Given the list of hits this will search for candidate Seed objects and return them.
std::unique_ptr< std::vector< recob::Seed > > artSeedVector
void splitClustersWithHough(reco::ClusterParameters &clusterParameters, reco::ClusterParametersList &clusterParametersList) const
Attempt to split clusters using the output of the Hough Filter.
EventID id() const
Definition: Event.h:56
art framework interface to geometry description
float m_totalTime
Keeps track of total execution time.
std::list< ClusterParameters > ClusterParametersList
Definition: Cluster3D.h:381
const EigenVectors & getEigenVectors() const
Definition: Cluster3D.h:229
std::unique_ptr< std::vector< recob::Cluster > > artClusterVector
Edge is an object containing the results of a Principal Components Analysis of a group of space point...
Definition: Edge.h:61
std::vector< art::Ptr< recob::Hit >> RecobHitVector
void ProduceArtClusters(ArtOutputHandler &output, reco::HitPairList &hitPairList, reco::ClusterParametersList &clusterParametersList, RecobHitToPtrMap &hitToPtrMap) const
Top level output routine, allows checking cluster status.
void setStatusBit(unsigned bits) const
Definition: Cluster3D.h:165
std::map< const reco::ClusterHit3D *, size_t > Hit3DToSPPtrMap
vertex reconstruction
bool m_enableMonitoring
Turn on monitoring of this algorithm.