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