Cluster3D_module.cc
Go to the documentation of this file.
1 /**
2  * @file Cluster3D_module.cc
3  *
4  * Class: Cluster3D
5  * Module Type: Producer
6  *
7  * @brief Producer module to create 3D clusters from input recob::Hit objects
8  *
9  * This producer module will drive the 3D association of recob::Hit objects
10  * to form 3D clusters. This information will be output as:
11  * 1) a PFParticle to anchor all the other objects (as associations)
12  * 2) three recob::Cluster objects representing 2D hit clusterswith associations
13  * to the 2D hits comprising each of them
14  * 3) One or more recob::PCAxis objects representing the Principal Components
15  * Analysis output of the space points associated to the 3D objects
16  * 4) recob::SpacePoints representing the accepted 3D points for each PFParticle
17  * 5) recob::Seed objects and associated seed hits representing candidate straight
18  * line segments in the space point collection.
19  *
20  * The module has two main sections
21  * 1) Find the 3D clusters of 3D hits
22  * 2) Get the output objects for each of the 3D clusters
23  * See the code below for more detail on these steps
24  *
25  * Note that the general 3D cluster suite of algorithms make extensive use of a set of data objects
26  * which contain volatile data members. At the end of the routine these are used to make the output
27  * LArSoft data products described above. See LarData/RecoObjects/Cluster3D.h
28  *
29  * Configuration parameters:
30  * HitFinderModuleLabel: the producer module responsible for making the recob:Hits to use
31  * EnableMonitoring: if true then basic monitoring of the module performed
32  * ClusterAlg: Parameter block required by the 3D clustering algorithm
33  * PrincipalComponentsAlg: Parameter block required by the Principal Components Analysis Algorithm
34  * SkeletonAlg: Parameter block required by the 3D skeletonization algorithm
35  * SeedFinderAlg: Parameter block required by the Hough Seed Finder algorithm
36  * PCASeedFinderAlg: Parameter block required by the PCA Seed Finder algorithm
37  * ParrallelHitsAlg: Parameter block required by the parallel hits algorithm
38  *
39  * The current producer module does not try to analyze or break apart PFParticles
40  * so, for example, all tracks emanating from a common vertex will be associated
41  * to a single PFParticle
42  *
43  * @author usher@slac.stanford.edu
44  */
45 
46 // Framework Includes
51 #include "art_root_io/TFileService.h"
52 #include "cetlib/cpu_timer.h"
53 
54 // LArSoft includes
68 
83 
84 // ROOT includes
85 #include "TTree.h"
86 #include "TVector3.h"
87 
88 // std includes
89 #include <iostream>
90 #include <memory>
91 #include <string>
92 
93 //------------------------------------------------------------------------------------------------------------------------------------------
94 
95 namespace lar_cluster3d {
96  using Hit3DToSPPtrMap =
97  std::unordered_map<const reco::ClusterHit3D*, art::Ptr<recob::SpacePoint>>;
100 
101  //------------------------------------------------------------------------------------------------------------------------------------------
102  // Definition of the producer module here
103 
104  /**
105  * @brief Definition of the Cluster3D class
106  */
107  class Cluster3D : public art::EDProducer {
108  public:
109  explicit Cluster3D(fhicl::ParameterSet const& pset);
110 
111  private:
112  void beginJob() override;
113  void produce(art::Event& evt) override;
114 
116  public:
118  std::string& pathName,
119  std::string& vertexName,
120  std::string& extremeName)
121  : artPCAxisVector(new std::vector<recob::PCAxis>)
122  , artPFParticleVector(new std::vector<recob::PFParticle>)
123  , artClusterVector(new std::vector<recob::Cluster>)
128  , artSeedVector(new std::vector<recob::Seed>)
129  , artEdgeVector(new std::vector<recob::Edge>)
132  , artClusterAssociations(new art::Assns<recob::Cluster, recob::Hit>)
133  , artPFPartAxisAssociations(new art::Assns<recob::PFParticle, recob::PCAxis>)
134  , artPFPartClusAssociations(new art::Assns<recob::PFParticle, recob::Cluster>)
135  , artPFPartSPAssociations(new art::Assns<recob::SpacePoint, recob::PFParticle>)
136  , artPFPartPPAssociations(new art::Assns<recob::SpacePoint, recob::PFParticle>)
137  , artPFPartSeedAssociations(new art::Assns<recob::PFParticle, recob::Seed>)
138  , artPFPartEdgeAssociations(new art::Assns<recob::Edge, recob::PFParticle>)
139  , artPFPartPathEdgeAssociations(new art::Assns<recob::Edge, recob::PFParticle>)
140  , artSeedHitAssociations(new art::Assns<recob::Seed, recob::Hit>)
145  , fEvt(evt)
146  , fSPPtrMaker(evt)
147  , fSPPtrMakerPath(evt, pathName)
148  , fEdgePtrMaker(evt)
149  , fEdgePtrMakerPath(evt, pathName)
150  , fPathName(pathName)
151  , fVertexName(vertexName)
152  , fExtremeName(extremeName)
153  {}
154 
155  void
157  {
159  }
160 
161  void
162  makeSpacePointHitAssns(std::vector<recob::SpacePoint>& spacePointVector,
163  RecobHitVector& recobHits,
165  const std::string& path = "")
166  {
167  for (auto& hit : recobHits)
168  util::CreateAssn(fEvt, spacePointVector, hit, spHitAssns, path);
169  }
170 
171  void
173  {
178  artPCAxisVector->size() - 2,
179  artPCAxisVector->size());
180  }
181 
182  void
183  makePFPartSeedAssns(size_t numSeedsStart)
184  {
187  *artSeedVector,
189  numSeedsStart,
190  artSeedVector->size());
191  }
192 
193  void
194  makePFPartClusterAssns(size_t clusterStart)
195  {
200  clusterStart,
201  artClusterVector->size());
202  }
203 
204  void
206  std::vector<recob::SpacePoint>& spacePointVector,
208  size_t spacePointStart,
209  const std::string& instance = "")
210  {
211  for (size_t idx = spacePointStart; idx < spacePointVector.size(); idx++) {
213  util::CreateAssn(fEvt, *artPFParticleVector, spacePoint, pfPartSPAssociations);
214  }
215  }
216 
217  void
218  makePFPartEdgeAssns(std::vector<recob::Edge>& edgeVector,
219  art::Assns<recob::Edge, recob::PFParticle>& pfPartEdgeAssociations,
220  size_t edgeStart,
221  const std::string& instance = "")
222  {
223  for (size_t idx = edgeStart; idx < edgeVector.size(); idx++) {
225 
226  util::CreateAssn(fEvt, *artPFParticleVector, edge, pfPartEdgeAssociations);
227  }
228  }
229 
230  void
231  makeEdgeSpacePointAssns(std::vector<recob::Edge>& edgeVector,
232  RecobSpacePointVector& spacePointVector,
233  art::Assns<recob::SpacePoint, recob::Edge>& edgeSPAssociations,
234  const std::string& path = "")
235  {
236  for (auto& spacePoint : spacePointVector)
237  util::CreateAssn(fEvt, edgeVector, spacePoint, edgeSPAssociations, path);
238  }
239 
240  void
242  {
267  }
268 
271  {
272  if (empty(instance)) { return fSPPtrMaker(index); }
273  return fSPPtrMakerPath(index);
274  };
275 
277  makeEdgePtr(size_t index, const std::string& instance = "")
278  {
279  if (empty(instance)) { return fEdgePtrMaker(index); }
280  return fEdgePtrMakerPath(index);
281  };
282 
283  std::unique_ptr<std::vector<recob::PCAxis>> artPCAxisVector;
284  std::unique_ptr<std::vector<recob::PFParticle>> artPFParticleVector;
285  std::unique_ptr<std::vector<recob::Cluster>> artClusterVector;
286  std::unique_ptr<std::vector<recob::SpacePoint>> artSpacePointVector;
287  std::unique_ptr<std::vector<recob::SpacePoint>> artPathPointVector;
288  std::unique_ptr<std::vector<recob::SpacePoint>> artVertexPointVector;
289  std::unique_ptr<std::vector<recob::SpacePoint>> artExtremePointVector;
290  std::unique_ptr<std::vector<recob::Seed>> artSeedVector;
291  std::unique_ptr<std::vector<recob::Edge>> artEdgeVector;
292  std::unique_ptr<std::vector<recob::Edge>> artPathEdgeVector;
293  std::unique_ptr<std::vector<recob::Edge>> artVertexEdgeVector;
294 
295  std::unique_ptr<art::Assns<recob::Cluster, recob::Hit>> artClusterAssociations;
296  std::unique_ptr<art::Assns<recob::PFParticle, recob::PCAxis>> artPFPartAxisAssociations;
297  std::unique_ptr<art::Assns<recob::PFParticle, recob::Cluster>> artPFPartClusAssociations;
298  std::unique_ptr<art::Assns<recob::SpacePoint, recob::PFParticle>> artPFPartSPAssociations;
299  std::unique_ptr<art::Assns<recob::SpacePoint, recob::PFParticle>> artPFPartPPAssociations;
300  std::unique_ptr<art::Assns<recob::PFParticle, recob::Seed>> artPFPartSeedAssociations;
301  std::unique_ptr<art::Assns<recob::Edge, recob::PFParticle>> artPFPartEdgeAssociations;
302  std::unique_ptr<art::Assns<recob::Edge, recob::PFParticle>> artPFPartPathEdgeAssociations;
303  std::unique_ptr<art::Assns<recob::Seed, recob::Hit>> artSeedHitAssociations;
304  std::unique_ptr<art::Assns<recob::Hit, recob::SpacePoint>> artSPHitAssociations;
305  std::unique_ptr<art::Assns<recob::Hit, recob::SpacePoint>> artPPHitAssociations;
306  std::unique_ptr<art::Assns<recob::SpacePoint, recob::Edge>> artEdgeSPAssociations;
307  std::unique_ptr<art::Assns<recob::SpacePoint, recob::Edge>> artEdgePPAssociations;
308 
309  private:
318  };
319 
320  /**
321  * @brief Event Preparation
322  *
323  * @param evt the ART event
324  */
325  void PrepareEvent(const art::Event& evt);
326 
327  /**
328  * @brief Initialize the internal monitoring
329  */
330  void InitializeMonitoring();
331 
332  /**
333  * @brief An interface to the seed finding algorithm
334  *
335  * @param evt the ART event
336  * @param cluster structure of information representing a single cluster
337  * @param hitToPtrMap This maps our Cluster2D hits back to art Ptr's to reco Hits
338  * @param seedVec the output vector of candidate seeds
339  * @param seedHitAssns the associations between the seeds and the 2D hits making them
340  */
341  void findTrackSeeds(art::Event& evt,
343  IHit3DBuilder::RecobHitToPtrMap& hitToPtrMap,
344  std::vector<recob::Seed>& seedVec,
345  art::Assns<recob::Seed, recob::Hit>& seedHitAssns) const;
346 
347  /**
348  * @brief Attempt to split clusters by using a minimum spanning tree
349  *
350  * @param clusterParameters The given cluster parameters object to try to split
351  * @param clusterParametersList The list of clusters
352  */
353  void splitClustersWithMST(reco::ClusterParameters& clusterParameters,
354  reco::ClusterParametersList& clusterParametersList) const;
355 
356  /**
357  * @brief Attempt to split clusters using the output of the Hough Filter
358  *
359  * @param clusterParameters The given cluster parameters object to try to split
360  * @param clusterParametersList The list of clusters
361  */
362  void splitClustersWithHough(reco::ClusterParameters& clusterParameters,
363  reco::ClusterParametersList& clusterParametersList) const;
364 
365  /**
366  * @brief Special routine to handle creating and saving space points
367  *
368  * @param output the object containting the art output
369  * @param clusterParameters Cluster info to output (in internal format)
370  * @param pfParticleParent The parent ID reference for the output PFParticle
371  * @param hitToPtrMap This maps our Cluster2D hits back to art Ptr's to reco Hits
372  */
374  std::vector<recob::SpacePoint>& spacePointVec,
376  reco::HitPairListPtr& clusHitPairVector,
377  IHit3DBuilder::RecobHitToPtrMap& hitToPtrMap,
378  Hit3DToSPPtrMap& hit3DToSPPtrMap,
379  const std::string& path = "") const;
380 
381  /**
382  * @brief Special routine to handle creating and saving space points
383  *
384  * @param output the object containting the art output
385  * @param clusHitPairVector List of 3D hits to output as "extreme" space points
386  */
388  reco::ConvexHullKinkTupleList& clusHitPairVector) const;
389 
390  /**
391  * @brief Special routine to handle creating and saving space points & edges associated to voronoi diagrams
392  *
393  * @param output the object containting the art output
394  * @param vertexList list of vertices in the diagram
395  * @param HalfEdgeList list of half edges in the diagram
396  */
399  dcel2d::HalfEdgeList&) const;
400 
401  /**
402  * @brief Special routine to handle creating and saving space points & edges PCA points
403  *
404  * @param output the object containting the art output
405  * @param clusterParamsList List of clusters to get PCA's from
406  */
407  using IdxToPCAMap = std::map<size_t, const reco::PrincipalComponents*>;
408 
411  IdxToPCAMap&) const;
412 
413  /**
414  * @brief This will produce art output for daughters noting that it needs to be done recursively
415  *
416  * @param output the object containting the art output
417  * @param clusterParameters Cluster info to output (in internal format)
418  * @param pfParticleParent The parent ID reference for the output PFParticle
419  * @param daughterList List of PFParticle indices for stored daughters
420  * @param hitToPtrMap This maps our Cluster2D hits back to art Ptr's to reco Hits
421  */
424  reco::ClusterParameters& clusterParameters,
425  size_t pfParticleParent,
426  IdxToPCAMap& idxToPCAMap,
427  IHit3DBuilder::RecobHitToPtrMap& hitToPtrMap,
428  Hit3DToSPPtrMap& hit3DToSPPtrMap,
429  Hit3DToSPPtrMap& best3DToSPPtrMap) const;
430 
431  /**
432  * @brief Top level output routine, allows checking cluster status
433  *
434  * @param hitPairList List of all 3D Hits in internal Cluster3D format
435  * @param clusterParametersList Data structure containing the cluster information to output
436  * @param hitToPtrMap This maps our Cluster2D hits back to art Ptr's to reco Hits
437  */
440  reco::HitPairList& hitPairList,
441  reco::ClusterParametersList& clusterParametersList,
442  IHit3DBuilder::RecobHitToPtrMap& hitToPtrMap) const;
443 
444  /**
445  * @brief Produces the art output from all the work done in this producer module
446  *
447  * @param output the object containting the art output
448  * @param clusterParameters Cluster info to output (in internal format)
449  * @param pfParticleParent The parent ID reference for the output PFParticle
450  * @param hitToPtrMap This maps our Cluster2D hits back to art Ptr's to reco Hits
451  */
452  size_t ConvertToArtOutput(util::GeometryUtilities const& gser,
454  reco::ClusterParameters& clusterParameters,
455  size_t pfParticleParent,
456  IHit3DBuilder::RecobHitToPtrMap& hitToPtrMap,
457  Hit3DToSPPtrMap& hit3DToSPPtrMap,
458  Hit3DToSPPtrMap& best3DToSPPtrMap) const;
459 
460  /**
461  * @brief There are several places we will want to know if a candidate cluster is a
462  * "parallel hits" type of cluster so encapsulate that here.
463  *
464  * @param pca The Principal Components Analysis parameters for the cluster
465  */
466  bool
468  {
469  return fabs(pca.getEigenVectors().row(2)(0)) > m_parallelHitsCosAng &&
470  3. * sqrt(pca.getEigenValues()(1)) > m_parallelHitsTransWid;
471  }
472 
473  /**
474  * @brief Count number of end of line daughters
475  *
476  * @param clusterParams input cluster parameters to look at
477  */
478  size_t countUltimateDaughters(reco::ClusterParameters& clusterParameters) const;
479 
480  /**
481  * Algorithm parameters
482  */
483  bool m_onlyMakSpacePoints; ///< If true we don't do the full cluster 3D processing
484  bool m_enableMonitoring; ///< Turn on monitoring of this algorithm
485  float m_parallelHitsCosAng; ///< Cut for PCA 3rd axis angle to X axis
486  float m_parallelHitsTransWid; ///< Cut on transverse width of cluster (PCA 2nd eigenvalue)
487 
488  /**
489  * Tree variables for output
490  */
491  TTree* m_pRecoTree; ///<
492  int m_run; ///<
493  int m_event; ///<
494  int m_hits; ///< Keeps track of the number of hits seen
495  int m_hits3D; ///< Keeps track of the number of 3D hits made
496  float m_totalTime; ///< Keeps track of total execution time
497  float m_artHitsTime; ///< Keeps track of time to recover hits
498  float m_makeHitsTime; ///< Keeps track of time to build 3D hits
499  float m_buildNeighborhoodTime; ///< Keeps track of time to build epsilon neighborhood
500  float m_dbscanTime; ///< Keeps track of time to run DBScan
501  float m_clusterMergeTime; ///< Keeps track of the time to merge clusters
502  float m_pathFindingTime; ///< Keeps track of the path finding time
503  float m_finishTime; ///< Keeps track of time to run output module
504  std::string m_pathInstance; ///< Special instance for path points
505  std::string m_vertexInstance; ///< Special instance name for vertex points
506  std::string m_extremeInstance; ///< Instance name for the extreme points
507 
508  // Algorithms
509  std::unique_ptr<lar_cluster3d::IHit3DBuilder>
510  m_hit3DBuilderAlg; ///< Builds the 3D hits to operate on
511  std::unique_ptr<lar_cluster3d::IClusterAlg>
512  m_clusterAlg; ///< Algorithm to do 3D space point clustering
513  std::unique_ptr<lar_cluster3d::IClusterModAlg>
514  m_clusterMergeAlg; ///< Algorithm to do cluster merging
515  std::unique_ptr<lar_cluster3d::IClusterModAlg>
516  m_clusterPathAlg; ///< Algorithm to do cluster path finding
517  std::unique_ptr<lar_cluster3d::IClusterParametersBuilder>
518  m_clusterBuilder; ///< Common cluster builder tool
519  PrincipalComponentsAlg m_pcaAlg; ///< Principal Components algorithm
520  SkeletonAlg m_skeletonAlg; ///< Skeleton point finder
522  PCASeedFinderAlg m_pcaSeedFinderAlg; ///< Use PCA axis to find seeds
523  ParallelHitsSeedFinderAlg m_parallelHitsAlg; ///< Deal with parallel hits clusters
524  };
525 
527 
528 } // namespace lar_cluster3d
529 
530 //------------------------------------------------------------------------------------------------------------------------------------------
531 // implementation follows
532 
533 namespace lar_cluster3d {
534 
536  : EDProducer{pset}
537  , m_pcaAlg(pset.get<fhicl::ParameterSet>("PrincipalComponentsAlg"))
538  , m_skeletonAlg(pset.get<fhicl::ParameterSet>("SkeletonAlg"))
539  , m_seedFinderAlg(pset.get<fhicl::ParameterSet>("SeedFinderAlg"))
540  , m_pcaSeedFinderAlg(pset.get<fhicl::ParameterSet>("PCASeedFinderAlg"))
541  , m_parallelHitsAlg(pset.get<fhicl::ParameterSet>("ParallelHitsAlg"))
542  {
543  m_onlyMakSpacePoints = pset.get<bool>("MakeSpacePointsOnly", false);
544  m_enableMonitoring = pset.get<bool>("EnableMonitoring", false);
545  m_parallelHitsCosAng = pset.get<float>("ParallelHitsCosAng", 0.999);
546  m_parallelHitsTransWid = pset.get<float>("ParallelHitsTransWid", 25.0);
547  m_pathInstance = pset.get<std::string>("PathPointsName", "Path");
548  m_vertexInstance = pset.get<std::string>("VertexPointsName", "Vertex");
549  m_extremeInstance = pset.get<std::string>("ExtremePointsName", "Extreme");
550 
551  m_hit3DBuilderAlg = art::make_tool<lar_cluster3d::IHit3DBuilder>(
552  pset.get<fhicl::ParameterSet>("Hit3DBuilderAlg"));
553  m_clusterAlg =
554  art::make_tool<lar_cluster3d::IClusterAlg>(pset.get<fhicl::ParameterSet>("ClusterAlg"));
555  m_clusterMergeAlg = art::make_tool<lar_cluster3d::IClusterModAlg>(
556  pset.get<fhicl::ParameterSet>("ClusterMergeAlg"));
557  m_clusterPathAlg = art::make_tool<lar_cluster3d::IClusterModAlg>(
558  pset.get<fhicl::ParameterSet>("ClusterPathAlg"));
559  m_clusterBuilder = art::make_tool<lar_cluster3d::IClusterParametersBuilder>(
560  pset.get<fhicl::ParameterSet>("ClusterParamsBuilder"));
561 
562  // Handle special case of Space Point building outputting a new hit collection
563  m_hit3DBuilderAlg->produces(producesCollector());
564 
565  produces<std::vector<recob::PCAxis>>();
566  produces<std::vector<recob::PFParticle>>();
567  produces<std::vector<recob::Cluster>>();
568  produces<std::vector<recob::Seed>>();
569  produces<std::vector<recob::Edge>>();
570 
571  produces<art::Assns<recob::PFParticle, recob::PCAxis>>();
572  produces<art::Assns<recob::PFParticle, recob::Cluster>>();
573  produces<art::Assns<recob::PFParticle, recob::Seed>>();
574  produces<art::Assns<recob::Edge, recob::PFParticle>>();
575  produces<art::Assns<recob::Seed, recob::Hit>>();
576  produces<art::Assns<recob::Cluster, recob::Hit>>();
577 
578  produces<std::vector<recob::SpacePoint>>();
579  produces<art::Assns<recob::SpacePoint, recob::PFParticle>>();
580  produces<art::Assns<recob::Hit, recob::SpacePoint>>();
581  produces<art::Assns<recob::SpacePoint, recob::Edge>>();
582 
583  produces<std::vector<recob::SpacePoint>>(m_pathInstance);
584  produces<std::vector<recob::Edge>>(m_pathInstance);
585  produces<art::Assns<recob::SpacePoint, recob::PFParticle>>(m_pathInstance);
586  produces<art::Assns<recob::Edge, recob::PFParticle>>(m_pathInstance);
587  produces<art::Assns<recob::Hit, recob::SpacePoint>>(m_pathInstance);
588  produces<art::Assns<recob::SpacePoint, recob::Edge>>(m_pathInstance);
589 
590  produces<std::vector<recob::Edge>>(m_vertexInstance);
591  produces<std::vector<recob::SpacePoint>>(m_vertexInstance);
592 
593  produces<std::vector<recob::SpacePoint>>(m_extremeInstance);
594  }
595 
596  //------------------------------------------------------------------------------------------------------------------------------------------
597 
598  void
600  {
601  /**
602  * @brief beginJob will be tasked with initializing monitoring, in necessary, but also to init the
603  * geometry and detector services (and this probably needs to go in a "beginEvent" method?)
604  */
606  }
607 
608  //------------------------------------------------------------------------------------------------------------------------------------------
609 
610  void
612  {
613  mf::LogInfo("Cluster3D") << " *** Cluster3D::produce(...) [Run=" << evt.run()
614  << ", Event=" << evt.id().event() << "] Starting Now! *** "
615  << std::endl;
616 
617  // Set up for monitoring the timing... at some point this should be removed in favor of
618  // external profilers
619  cet::cpu_timer theClockTotal;
620  cet::cpu_timer theClockFinish;
621 
622  if (m_enableMonitoring) theClockTotal.start();
623 
624  // This really only does anything if we are monitoring since it clears our tree variables
625  this->PrepareEvent(evt);
626 
627  // Get instances of the primary data structures needed
628  reco::ClusterParametersList clusterParametersList;
629  IHit3DBuilder::RecobHitToPtrMap clusterHitToArtPtrMap;
630  std::unique_ptr<reco::HitPairList> hitPairList(
631  new reco::HitPairList); // Potentially lots of hits, use heap instead of stack
632 
633  // Call the algorithm that builds 3D hits and stores the hit collection
634  m_hit3DBuilderAlg->Hit3DBuilder(evt, *hitPairList, clusterHitToArtPtrMap);
635 
636  // Only do the rest if we are not in the mode of only building space points (requested by ML folks)
637  if (!m_onlyMakSpacePoints) {
638  // Call the main workhorse algorithm for building the local version of candidate 3D clusters
639  m_clusterAlg->Cluster3DHits(*hitPairList, clusterParametersList);
640 
641  // Try merging clusters
642  m_clusterMergeAlg->ModifyClusters(clusterParametersList);
643 
644  // Run the path finding
645  m_clusterPathAlg->ModifyClusters(clusterParametersList);
646  }
647 
648  if (m_enableMonitoring) theClockFinish.start();
649 
650  // Get the art ouput object
652 
653  // Call the module that does the end processing (of which there is quite a bit of work!)
654  // This goes here to insure that something is always written to the data store
655  auto const clockData = art::ServiceHandle<detinfo::DetectorClocksService const>()->DataFor(evt);
656  auto const detProp =
658  util::GeometryUtilities const gser{*lar::providerFrom<geo::Geometry>(), clockData, detProp};
659  ProduceArtClusters(gser, output, *hitPairList, clusterParametersList, clusterHitToArtPtrMap);
660 
661  // Output to art
662  output.outputObjects();
663 
664  // If monitoring then deal with the fallout
665  if (m_enableMonitoring) {
666  theClockFinish.stop();
667  theClockTotal.stop();
668 
669  m_run = evt.run();
670  m_event = evt.id().event();
671  m_totalTime = theClockTotal.accumulated_real_time();
675  m_dbscanTime = m_clusterAlg->getTimeToExecute(IClusterAlg::RUNDBSCAN) +
676  m_clusterAlg->getTimeToExecute(IClusterAlg::BUILDCLUSTERINFO);
677  m_clusterMergeTime = m_clusterMergeAlg->getTimeToExecute();
678  m_pathFindingTime = m_clusterPathAlg->getTimeToExecute();
679  m_finishTime = theClockFinish.accumulated_real_time();
680  m_hits = static_cast<int>(clusterHitToArtPtrMap.size());
681  m_hits3D = static_cast<int>(hitPairList->size());
682  m_pRecoTree->Fill();
683 
684  mf::LogDebug("Cluster3D") << "*** Cluster3D total time: " << m_totalTime
685  << ", art: " << m_artHitsTime << ", make: " << m_makeHitsTime
686  << ", build: " << m_buildNeighborhoodTime
687  << ", clustering: " << m_dbscanTime
688  << ", merge: " << m_clusterMergeTime
689  << ", path: " << m_pathFindingTime << ", finish: " << m_finishTime
690  << std::endl;
691  }
692 
693  // Will we ever get here? ;-)
694  return;
695  }
696 
697  //------------------------------------------------------------------------------------------------------------------------------------------
698 
699  void
701  {
703  m_pRecoTree = tfs->make<TTree>("monitoring", "LAr Reco");
704  m_pRecoTree->Branch("run", &m_run, "run/I");
705  m_pRecoTree->Branch("event", &m_event, "event/I");
706  m_pRecoTree->Branch("hits", &m_hits, "hits/I");
707  m_pRecoTree->Branch("hits3D", &m_hits3D, "hits3D/I");
708  m_pRecoTree->Branch("totalTime", &m_totalTime, "time/F");
709  m_pRecoTree->Branch("artHitsTime", &m_artHitsTime, "time/F");
710  m_pRecoTree->Branch("makeHitsTime", &m_makeHitsTime, "time/F");
711  m_pRecoTree->Branch("buildneigborhoodTime", &m_buildNeighborhoodTime, "time/F");
712  m_pRecoTree->Branch("dbscanTime", &m_dbscanTime, "time/F");
713  m_pRecoTree->Branch("clusterMergeTime", &m_clusterMergeTime, "time/F");
714  m_pRecoTree->Branch("pathfindingtime", &m_pathFindingTime, "time/F");
715  m_pRecoTree->Branch("finishTime", &m_finishTime, "time/F");
716 
717  m_clusterPathAlg->initializeHistograms(*tfs.get());
718  }
719 
720  //------------------------------------------------------------------------------------------------------------------------------------------
721 
722  void
724  {
725  m_run = evt.run();
726  m_event = evt.id().event();
727  m_hits = 0;
728  m_hits3D = 0;
729  m_totalTime = 0.f;
730  m_artHitsTime = 0.f;
731  m_makeHitsTime = 0.f;
733  m_dbscanTime = 0.f;
734  m_pathFindingTime = 0.f;
735  m_finishTime = 0.f;
736  }
737 
738  //------------------------------------------------------------------------------------------------------------------------------------------
739 
740  void
743  IHit3DBuilder::RecobHitToPtrMap& hitToPtrMap,
744  std::vector<recob::Seed>& seedVec,
745  art::Assns<recob::Seed, recob::Hit>& seedHitAssns) const
746  {
747  /**
748  * @brief This method provides an interface to various algorithms for finding candiate
749  * recob::Seed objects and, as well, their candidate related seed hits
750  */
751 
752  // Make sure we are using the right pca
753  reco::PrincipalComponents& fullPCA = cluster.getFullPCA();
754  reco::PrincipalComponents& skeletonPCA = cluster.getSkeletonPCA();
755  reco::HitPairListPtr& hitPairListPtr = cluster.getHitPairListPtr();
756  reco::HitPairListPtr skeletonListPtr;
757 
758  // We want to work with the "skeleton" hits so first step is to call the algorithm to
759  // recover only these hits from the entire input collection
760  m_skeletonAlg.GetSkeletonHits(hitPairListPtr, skeletonListPtr);
761 
762  // Skeleton hits are nice but we can do better if we then make a pass through to "average"
763  // the skeleton hits position in the Y-Z plane
764  m_skeletonAlg.AverageSkeletonPositions(skeletonListPtr);
765 
766  SeedHitPairListPairVec seedHitPairVec;
767 
768  // Some combination of the elements below will be used to determine which seed finding algorithm
769  // to pursue below
770  float eigenVal0 = 3. * sqrt(skeletonPCA.getEigenValues()[0]);
771  float eigenVal1 = 3. * sqrt(skeletonPCA.getEigenValues()[1]);
772  float eigenVal2 = 3. * sqrt(skeletonPCA.getEigenValues()[2]);
773  float transRMS = std::hypot(eigenVal0, eigenVal1);
774 
775  bool foundGoodSeed(false);
776 
777  // Choose a method for finding the seeds based on the PCA that was run...
778  // Currently we have an ad hoc if-else block which I hope will be improved soon!
779  if (aParallelHitsCluster(fullPCA)) {
780  // In this case we have a track moving relatively parallel to the wire plane with lots of
781  // ambiguous 3D hits. Your best bet here is to use the "parallel hits" algorithm to get the
782  // best axis and seeds
783  // This algorithm does not fail (foundGoodSeed will always return true)
784  foundGoodSeed = m_parallelHitsAlg.findTrackSeeds(hitPairListPtr, skeletonPCA, seedHitPairVec);
785  }
786  else if (eigenVal2 > 40. && transRMS < 5.) {
787  // If the input cluster is relatively "straight" then chances are it is a single straight track,
788  // probably a CR muon, and we can simply use the PCA to determine the seed
789  // This algorithm will check both "ends" of the input hits and if the angles become inconsistent
790  // then it will "fail"
791  foundGoodSeed =
792  m_pcaSeedFinderAlg.findTrackSeeds(skeletonListPtr, skeletonPCA, seedHitPairVec);
793  }
794 
795  // In the event the above two methods failed then we hit it with the real seed finder
796  if (!foundGoodSeed) {
797  // If here then we have a complicated 3D cluster and we'll use the hough transform algorithm to
798  // return a list of candidate seeds and seed hits
799  m_seedFinderAlg.findTrackSeeds(skeletonListPtr, skeletonPCA, seedHitPairVec);
800  }
801 
802  // Go through the returned lists and build out the art friendly seeds and hits
803  for (const auto& seedHitPair : seedHitPairVec) {
804  seedVec.push_back(seedHitPair.first);
805 
806  // We use a set here because our 3D hits can share 2D hits
807  // The set will make sure we get unique combinations of 2D hits
808  std::set<art::Ptr<recob::Hit>> seedHitSet;
809  for (const auto& hit3D : seedHitPair.second) {
810  for (const auto& hit2D : hit3D->getHits()) {
811  if (!hit2D) continue;
812 
813  const recob::Hit* recobHit = hit2D->getHit();
814  seedHitSet.insert(hitToPtrMap[recobHit]);
815  }
816  }
817 
818  RecobHitVector seedHitVec;
819  for (const auto& hit2D : seedHitSet)
820  seedHitVec.push_back(hit2D);
821 
822  util::CreateAssn(evt, seedVec, seedHitVec, seedHitAssns);
823  }
824  }
825 
827  bool
828  operator()(const std::pair<float, const reco::ClusterHit3D*>& left,
829  const std::pair<float, const reco::ClusterHit3D*>& right)
830  {
831  return left.first < right.first;
832  }
833  };
834 
835  void
837  reco::ClusterParametersList& clusterParametersList) const
838  {
839  // This is being left in place for future development. Essentially, it was an attempt to implement
840  // a Minimum Spanning Tree as a way to split a particular cluster topology, one where two straight
841  // tracks cross closely enought to appear as one cluster. As of Feb 2, 2015 I think the idea is still
842  // worth merit so am leaving this module in place for now.
843  //
844  // If this routine is called then we believe we have a cluster which needs splitting.
845  // The way we will do this is to use a Minimum Spanning Tree algorithm to associate all
846  // hits together by their distance apart. In theory, we should be able to split the cluster
847  // by finding the largest distance and splitting at that point.
848  //
849  // Typedef some data structures that we will use.
850  // Start with the adjacency map
851  typedef std::pair<float, const reco::ClusterHit3D*> DistanceHit3DPair;
852  typedef std::list<DistanceHit3DPair> DistanceHit3DPairList;
853  typedef std::map<const reco::ClusterHit3D*, DistanceHit3DPairList> Hit3DToDistanceMap;
854 
855  // Now typedef the lists we'll keep
856  typedef std::list<const reco::ClusterHit3D*> Hit3DList;
857  typedef std::pair<Hit3DList::iterator, Hit3DList::iterator> Hit3DEdgePair;
858  typedef std::pair<float, Hit3DEdgePair> DistanceEdgePair;
859  typedef std::list<DistanceEdgePair> DistanceEdgePairList;
860 
861  struct DistanceEdgePairOrder {
862  bool
863  operator()(const DistanceEdgePair& left, const DistanceEdgePair& right) const
864  {
865  return left.first > right.first;
866  }
867  };
868 
869  // Recover the hits we'll work on.
870  // Note that we use on the skeleton hits so will need to recover them
871  reco::HitPairListPtr& hitPairListPtr = clusterParameters.getHitPairListPtr();
872  reco::HitPairListPtr skeletonListPtr;
873 
874  // We want to work with the "skeleton" hits so first step is to call the algorithm to
875  // recover only these hits from the entire input collection
876  m_skeletonAlg.GetSkeletonHits(hitPairListPtr, skeletonListPtr);
877 
878  // Skeleton hits are nice but we can do better if we then make a pass through to "average"
879  // the skeleton hits position in the Y-Z plane
880  m_skeletonAlg.AverageSkeletonPositions(skeletonListPtr);
881 
882  // First task is to define and build the adjacency map
883  Hit3DToDistanceMap hit3DToDistanceMap;
884 
885  for (reco::HitPairListPtr::const_iterator hit3DOuterItr = skeletonListPtr.begin();
886  hit3DOuterItr != skeletonListPtr.end();) {
887  const reco::ClusterHit3D* hit3DOuter = *hit3DOuterItr++;
888  DistanceHit3DPairList& outerHitList = hit3DToDistanceMap[hit3DOuter];
889  TVector3 outerPos(
890  hit3DOuter->getPosition()[0], hit3DOuter->getPosition()[1], hit3DOuter->getPosition()[2]);
891 
892  for (reco::HitPairListPtr::const_iterator hit3DInnerItr = hit3DOuterItr;
893  hit3DInnerItr != skeletonListPtr.end();
894  hit3DInnerItr++) {
895  const reco::ClusterHit3D* hit3DInner = *hit3DInnerItr;
896  TVector3 innerPos(
897  hit3DInner->getPosition()[0], hit3DInner->getPosition()[1], hit3DInner->getPosition()[2]);
898  TVector3 deltaPos = innerPos - outerPos;
899  float hitDistance(float(deltaPos.Mag()));
900 
901  if (hitDistance > 20.) continue;
902 
903  hit3DToDistanceMap[hit3DInner].emplace_back(hitDistance, hit3DOuter);
904  outerHitList.emplace_back(hitDistance, hit3DInner);
905  }
906 
907  // Make sure our membership bit is clear
909  }
910 
911  // Make pass through again to order each of the lists
912  for (auto& mapPair : hit3DToDistanceMap) {
913  mapPair.second.sort(Hit3DDistanceOrder());
914  }
915 
916  // Get the containers for the MST to operate on/with
917  Hit3DList hit3DList;
918  DistanceEdgePairList distanceEdgePairList;
919 
920  // Initialize with first element
921  hit3DList.emplace_back(skeletonListPtr.front());
922  distanceEdgePairList.emplace_back(
923  DistanceEdgePair(0., Hit3DEdgePair(hit3DList.begin(), hit3DList.begin())));
924 
925  skeletonListPtr.front()->setStatusBit(reco::ClusterHit3D::SELECTEDBYMST);
926 
927  float largestDistance(0.);
928  float averageDistance(0.);
929 
930  // Now run the MST
931  // Basically, we loop until the MST list is the same size as the input list
932  while (hit3DList.size() < skeletonListPtr.size()) {
933  Hit3DList::iterator bestHit3DIter = hit3DList.begin();
934  float bestDist = 10000000.;
935 
936  // Loop through all hits currently in the list and look for closest hit not in the list
937  for (Hit3DList::iterator hit3DIter = hit3DList.begin(); hit3DIter != hit3DList.end();
938  hit3DIter++) {
939  const reco::ClusterHit3D* hit3D = *hit3DIter;
940 
941  // For the given 3D hit, find the closest to it that is not already in the list
942  DistanceHit3DPairList& nearestList = hit3DToDistanceMap[hit3D];
943 
944  while (!nearestList.empty()) {
945  const reco::ClusterHit3D* hit3DToCheck = nearestList.front().second;
946 
947  if (!hit3DToCheck->bitsAreSet(reco::ClusterHit3D::SELECTEDBYMST)) {
948  if (nearestList.front().first < bestDist) {
949  bestHit3DIter = hit3DIter;
950  bestDist = nearestList.front().first;
951  }
952  break;
953  }
954 
955  nearestList.pop_front();
956  }
957  }
958 
959  if (bestDist > largestDistance) largestDistance = bestDist;
960 
961  averageDistance += bestDist;
962 
963  // Now we add the best hit not in the list to our list, keep track of the distance
964  // to the object it was closest to
965  const reco::ClusterHit3D* bestHit3D = *bestHit3DIter; // "best" hit already in the list
966  const reco::ClusterHit3D* nextHit3D =
967  hit3DToDistanceMap[bestHit3D].front().second; // "next" hit we are adding to the list
968 
969  Hit3DList::iterator nextHit3DIter = hit3DList.insert(hit3DList.end(), nextHit3D);
970 
971  distanceEdgePairList.emplace_back(bestDist, Hit3DEdgePair(bestHit3DIter, nextHit3DIter));
972 
974  }
975 
976  averageDistance /= float(hit3DList.size());
977 
978  float thirdDist = 2. * sqrt(clusterParameters.getSkeletonPCA().getEigenValues()[2]);
979 
980  // Ok, find the largest distance in the iterator map
981  distanceEdgePairList.sort(DistanceEdgePairOrder());
982 
983  DistanceEdgePairList::iterator largestDistIter = distanceEdgePairList.begin();
984 
985  for (DistanceEdgePairList::iterator edgeIter = distanceEdgePairList.begin();
986  edgeIter != distanceEdgePairList.end();
987  edgeIter++) {
988  if (edgeIter->first < thirdDist) break;
989 
990  largestDistIter = edgeIter;
991  }
992 
993  reco::HitPairListPtr::iterator breakIter = largestDistIter->second.second;
994  reco::HitPairListPtr bestList;
995 
996  bestList.resize(std::distance(hit3DList.begin(), breakIter));
997 
998  std::copy(hit3DList.begin(), breakIter, bestList.begin());
999 
1000  // Remove from the grand hit list and see what happens...
1001  // The pieces below are incomplete and were really for testing only.
1002  hitPairListPtr.sort();
1003  bestList.sort();
1004 
1005  reco::HitPairListPtr::iterator newListEnd = std::set_difference(hitPairListPtr.begin(),
1006  hitPairListPtr.end(),
1007  bestList.begin(),
1008  bestList.end(),
1009  hitPairListPtr.begin());
1010 
1011  hitPairListPtr.erase(newListEnd, hitPairListPtr.end());
1012  }
1013 
1015  public:
1016  CopyIfInRange(float maxRange) : m_maxRange(maxRange) {}
1017 
1018  bool
1019  operator()(const reco::ClusterHit3D* hit3D) const
1020  {
1021  return hit3D->getDocaToAxis() < m_maxRange;
1022  }
1023 
1024  private:
1025  float m_maxRange;
1026  };
1027 
1028  void
1030  reco::ClusterParametersList& clusterParametersList) const
1031  {
1032  // @brief A method for splitted "crossed tracks" clusters into separate clusters
1033  //
1034  // If this routine is called then we believe we have a cluster which needs splitting.
1035  // The specific topology we are looking for is two long straight tracks which cross at some
1036  // point in close proximity so their hits were joined into a single 3D cluster. The method
1037  // to split this topology is to let the hough transform algorithm find the two leading candidates
1038  // and then to see if we use those to build two clusters instead of one.
1039 
1040  // Recover the hits we'll work on.
1041  // Note that we use on the skeleton hits so will need to recover them
1042  reco::HitPairListPtr& hitPairListPtr = clusterParameters.getHitPairListPtr();
1043  reco::HitPairListPtr skeletonListPtr;
1044 
1045  // We want to work with the "skeleton" hits so first step is to call the algorithm to
1046  // recover only these hits from the entire input collection
1047  m_skeletonAlg.GetSkeletonHits(hitPairListPtr, skeletonListPtr);
1048 
1049  // Skeleton hits are nice but we can do better if we then make a pass through to "average"
1050  // the skeleton hits position in the Y-Z plane
1051  m_skeletonAlg.AverageSkeletonPositions(skeletonListPtr);
1052 
1053  // Define the container for our lists of hits
1054  reco::HitPairListPtrList hitPairListPtrList;
1055 
1056  // Now feed this to the Hough Transform to find candidate straight lines
1058  skeletonListPtr, clusterParameters.getSkeletonPCA(), hitPairListPtrList);
1059 
1060  // We need at least two lists or else there is nothing to do
1061  if (hitPairListPtrList.size() < 2) return;
1062 
1063  // The game plan will be the following:
1064  // 1) Take the first list of hits and run the PCA on this to get an axis
1065  // - Then calculate the 3d doca for ALL hits in the cluster to this axis
1066  // - Move all hits within "3 sigam" of the axis to a new list
1067  // 2) run the PCA on the second list of hits to get that axis
1068  // - Then calculate the 3d doca for all hits in our first list
1069  // - Copy hits in the first list which are within 3 sigma of the new axis
1070  // back into our original cluster - these are shared hits
1071  reco::HitPairListPtrList::iterator hitPairListIter = hitPairListPtrList.begin();
1072  reco::HitPairListPtr& firstHitList = *hitPairListIter++;
1073  reco::PrincipalComponents firstHitListPCA;
1074 
1075  m_pcaAlg.PCAAnalysis_3D(firstHitList, firstHitListPCA);
1076 
1077  // Make sure we have a successful calculation.
1078  if (firstHitListPCA.getSvdOK()) {
1079  // The fill routines below will expect to see unused 2D hits so we need to clear the
1080  // status bits... and I am not sure of a better way...
1081  for (const auto& hit3D : hitPairListPtr) {
1082  for (const auto& hit2D : hit3D->getHits())
1083  if (hit2D) hit2D->clearStatusBits(0x1);
1084  }
1085 
1086  // Calculate the 3D doca's for the hits which were used to make this PCA
1087  m_pcaAlg.PCAAnalysis_calc3DDocas(firstHitList, firstHitListPCA);
1088 
1089  // Divine from the ether some maximum allowed range for transfering hits
1090  float allowedHitRange = 6. * firstHitListPCA.getAveHitDoca();
1091 
1092  // Now go through and calculate the 3D doca's for ALL the hits in the original cluster
1093  m_pcaAlg.PCAAnalysis_calc3DDocas(hitPairListPtr, firstHitListPCA);
1094 
1095  // Let's make a new cluster to hold the hits
1096  clusterParametersList.push_back(reco::ClusterParameters());
1097 
1098  // Can we get a reference to what we just created?
1099  reco::ClusterParameters& newClusterParams = clusterParametersList.back();
1100 
1101  reco::HitPairListPtr& newClusterHitList = newClusterParams.getHitPairListPtr();
1102 
1103  newClusterHitList.resize(hitPairListPtr.size());
1104 
1105  // Do the actual copy of the hits we want
1106  reco::HitPairListPtr::iterator newListEnd = std::copy_if(hitPairListPtr.begin(),
1107  hitPairListPtr.end(),
1108  newClusterHitList.begin(),
1109  CopyIfInRange(allowedHitRange));
1110 
1111  // Shrink to fit
1112  newClusterHitList.resize(std::distance(newClusterHitList.begin(), newListEnd));
1113 
1114  // And now remove these hits from the original cluster
1115  hitPairListPtr.remove_if(CopyIfInRange(allowedHitRange));
1116 
1117  // Get an empty hit to cluster map...
1118  reco::Hit2DToClusterMap hit2DToClusterMap;
1119 
1120  // Now "fill" the cluster parameters but turn off the hit rejection
1121  m_clusterBuilder->FillClusterParams(newClusterParams, hit2DToClusterMap, 0., 1.);
1122 
1123  // Set the skeleton pca to the value calculated above on input
1124  clusterParameters.getSkeletonPCA() = firstHitListPCA;
1125 
1126  // We are done with splitting out one track. Because the two tracks cross in
1127  // close proximity, this is the one case where we might consider sharing 3D hits
1128  // So let's make a little detour here to try to copy some of those hits back into
1129  // the main hit list
1130  reco::HitPairListPtr& secondHitList = *hitPairListIter;
1131  reco::PrincipalComponents secondHitListPCA;
1132 
1133  m_pcaAlg.PCAAnalysis_3D(secondHitList, secondHitListPCA);
1134 
1135  // Make sure we have a successful calculation.
1136  if (secondHitListPCA.getSvdOK()) {
1137  // Calculate the 3D doca's for the hits which were used to make this PCA
1138  m_pcaAlg.PCAAnalysis_calc3DDocas(secondHitList, secondHitListPCA);
1139 
1140  // Since this is the "other" cluster, we'll be a bit more generous in adding back hits
1141  float newAllowedHitRange = 6. * secondHitListPCA.getAveHitDoca();
1142 
1143  // Go through and calculate the 3D doca's for the hits in our new candidate cluster
1144  m_pcaAlg.PCAAnalysis_calc3DDocas(newClusterHitList, secondHitListPCA);
1145 
1146  // Create a temporary list to fill with the hits we might want to save
1147  reco::HitPairListPtr tempHitList(newClusterHitList.size());
1148 
1149  // Do the actual copy of the hits we want...
1150  reco::HitPairListPtr::iterator tempListEnd =
1151  std::copy_if(newClusterHitList.begin(),
1152  newClusterHitList.end(),
1153  tempHitList.begin(),
1154  CopyIfInRange(newAllowedHitRange));
1155 
1156  hitPairListPtr.insert(hitPairListPtr.end(), tempHitList.begin(), tempListEnd);
1157  }
1158 
1159  // Of course, now we need to modify the original cluster parameters
1160  reco::ClusterParameters originalParams(hitPairListPtr);
1161 
1162  // Now "fill" the cluster parameters but turn off the hit rejection
1163  m_clusterBuilder->FillClusterParams(originalParams, hit2DToClusterMap, 0., 1.);
1164 
1165  // Overwrite original cluster parameters with our new values
1166  clusterParameters.getClusterParams() = originalParams.getClusterParams();
1167  clusterParameters.getFullPCA() = originalParams.getFullPCA();
1168  clusterParameters.getSkeletonPCA() = secondHitListPCA;
1169  }
1170  }
1171 
1172  void
1175  reco::HitPairList& hitPairVector,
1176  reco::ClusterParametersList& clusterParametersList,
1177  IHit3DBuilder::RecobHitToPtrMap& hitToPtrMap) const
1178  {
1179  /**
1180  * @brief The workhorse to take the candidate 3D clusters and produce all of the necessary art output
1181  */
1182 
1183  mf::LogDebug("Cluster3D") << " *** Cluster3D::ProduceArtClusters() *** " << std::endl;
1184 
1185  // Make sure there is something to do here!
1186  if (!clusterParametersList.empty()) {
1187  // This is the loop over candidate 3D clusters
1188  // Note that it might be that the list of candidate clusters is modified by splitting
1189  // So we use the following construct to make sure we get all of them
1190  for (auto& clusterParameters : clusterParametersList) {
1191  // It should be straightforward at this point to transfer information from our vector of clusters
1192  // to the larsoft objects... of course we still have some work to do first, in particular to
1193  // find the candidate seeds and their seed hits
1194 
1195  // The chances of getting here and this condition not being true are probably zero... but check anyway
1196  if (!clusterParameters.getFullPCA().getSvdOK()) {
1197  mf::LogDebug("Cluster3D")
1198  << "--> no feature extraction done on this cluster!!" << std::endl;
1199  continue;
1200  }
1201 
1202  // Keep track of hit 3D to SP for when we do edges
1203  Hit3DToSPPtrMap hit3DToSPPtrMap;
1204  Hit3DToSPPtrMap best3DToSPPtrMap;
1205 
1206  // Do a special output of voronoi vertices here...
1207  dcel2d::VertexList& vertexList = clusterParameters.getVertexList();
1208  dcel2d::HalfEdgeList& halfEdgeList = clusterParameters.getHalfEdgeList();
1209 
1210  MakeAndSaveVertexPoints(output, vertexList, halfEdgeList);
1211 
1212  // Special case handling... if no daughters then call standard conversion routine to make sure space points
1213  // created, etc.
1214  if (clusterParameters.daughterList().empty()) {
1215  ConvertToArtOutput(gser,
1216  output,
1217  clusterParameters,
1219  hitToPtrMap,
1220  hit3DToSPPtrMap,
1221  best3DToSPPtrMap);
1222 
1223  // Get the extreme points
1224  MakeAndSaveKinkPoints(output,
1225  clusterParameters.getConvexHull().getConvexHullKinkPoints());
1226  }
1227  // Otherwise, the cluster has daughters so we handle specially
1228  else {
1229  // Set up to keep track of parent/daughters
1230  IdxToPCAMap idxToPCAMap;
1231  size_t numTotalDaughters = countUltimateDaughters(clusterParameters);
1232  size_t pfParticleIdx(output.artPFParticleVector->size() + numTotalDaughters);
1233 
1234  FindAndStoreDaughters(gser,
1235  output,
1236  clusterParameters,
1237  pfParticleIdx,
1238  idxToPCAMap,
1239  hitToPtrMap,
1240  hit3DToSPPtrMap,
1241  best3DToSPPtrMap);
1242 
1243  // Need to make a daughter vec from our map
1244  std::vector<size_t> daughterVec;
1245 
1246  for (auto& idxToPCA : idxToPCAMap)
1247  daughterVec.emplace_back(idxToPCA.first);
1248 
1249  // Now create/handle the parent PFParticle
1250  recob::PFParticle pfParticle(
1251  13, pfParticleIdx, recob::PFParticle::kPFParticlePrimary, daughterVec);
1252  output.artPFParticleVector->push_back(pfParticle);
1253 
1254  recob::PCAxis::EigenVectors eigenVecs;
1255  double eigenVals[] = {0., 0., 0.};
1256  double avePosition[] = {0., 0., 0.};
1257 
1258  eigenVecs.resize(3);
1259 
1260  reco::PrincipalComponents& skeletonPCA = clusterParameters.getSkeletonPCA();
1261 
1262  for (size_t outerIdx = 0; outerIdx < 3; outerIdx++) {
1263  avePosition[outerIdx] = skeletonPCA.getAvePosition()[outerIdx];
1264  eigenVals[outerIdx] = skeletonPCA.getEigenValues()[outerIdx];
1265 
1266  eigenVecs[outerIdx].resize(3);
1267 
1268  // Be careful here... eigen stores in column major order buy default
1269  for (size_t innerIdx = 0; innerIdx < 3; innerIdx++)
1270  eigenVecs[outerIdx][innerIdx] = skeletonPCA.getEigenVectors().row(outerIdx)[innerIdx];
1271  }
1272 
1273  recob::PCAxis skelPcAxis(skeletonPCA.getSvdOK(),
1274  skeletonPCA.getNumHitsUsed(),
1275  eigenVals,
1276  eigenVecs,
1277  avePosition,
1278  skeletonPCA.getAveHitDoca(),
1279  output.artPCAxisVector->size());
1280 
1281  output.artPCAxisVector->push_back(skelPcAxis);
1282 
1283  reco::PrincipalComponents& fullPCA = clusterParameters.getFullPCA();
1284 
1285  for (size_t outerIdx = 0; outerIdx < 3; outerIdx++) {
1286  avePosition[outerIdx] = fullPCA.getAvePosition()[outerIdx];
1287  eigenVals[outerIdx] = fullPCA.getEigenValues()[outerIdx];
1288 
1289  for (size_t innerIdx = 0; innerIdx < 3; innerIdx++)
1290  eigenVecs[outerIdx][innerIdx] = fullPCA.getEigenVectors().row(outerIdx)(innerIdx);
1291  }
1292 
1293  recob::PCAxis fullPcAxis(fullPCA.getSvdOK(),
1294  fullPCA.getNumHitsUsed(),
1295  eigenVals,
1296  eigenVecs,
1297  avePosition,
1298  fullPCA.getAveHitDoca(),
1299  output.artPCAxisVector->size());
1300 
1301  output.artPCAxisVector->push_back(fullPcAxis);
1302 
1303  // Create associations to the PFParticle
1304  output.makePFPartPCAAssns();
1305 
1306  // Make associations to all space points for this cluster
1307  MakeAndSaveSpacePoints(output,
1308  *output.artSpacePointVector.get(),
1309  *output.artSPHitAssociations.get(),
1310  clusterParameters.getHitPairListPtr(),
1311  hitToPtrMap,
1312  hit3DToSPPtrMap);
1313 
1314  // Get the extreme points
1315  MakeAndSaveKinkPoints(output,
1316  clusterParameters.getConvexHull().getConvexHullKinkPoints());
1317 
1318  // Build the edges now
1319  size_t edgeStart(output.artEdgeVector->size());
1320 
1321  for (const auto& edge : clusterParameters.getConvexHull().getConvexHullEdgeList()) {
1322  RecobSpacePointVector spacePointVec;
1323 
1324  try {
1325  spacePointVec.push_back(hit3DToSPPtrMap.at(std::get<0>(edge)));
1326  spacePointVec.push_back(hit3DToSPPtrMap.at(std::get<1>(edge)));
1327  }
1328  catch (...) {
1329  std::cout << "Caught exception in looking up space point ptr... " << std::get<0>(edge)
1330  << ", " << std::get<1>(edge) << std::endl;
1331  continue;
1332  }
1333 
1334  output.artEdgeVector->emplace_back(std::get<2>(edge),
1335  spacePointVec[0].key(),
1336  spacePointVec[1].key(),
1337  output.artEdgeVector->size());
1338 
1339  output.makeEdgeSpacePointAssns(
1340  *output.artEdgeVector.get(), spacePointVec, *output.artEdgeSPAssociations.get());
1341  }
1342 
1343  output.makePFPartEdgeAssns(
1344  *output.artEdgeVector.get(), *output.artPFPartEdgeAssociations.get(), edgeStart);
1345  }
1346  }
1347  }
1348 
1349  // Right now error matrix is uniform...
1350  int nFreePoints(0);
1351 
1352  // Run through the HitPairVector and add any unused hit pairs to the list
1353  for (auto& hitPair : hitPairVector) {
1354  if (hitPair.bitsAreSet(reco::ClusterHit3D::MADESPACEPOINT)) continue;
1355 
1356  double spacePointPos[] = {
1357  hitPair.getPosition()[0], hitPair.getPosition()[1], hitPair.getPosition()[2]};
1358  double spacePointErr[] = {1., 0., 0., 1., 0., 1.};
1359  double chisq = hitPair.getHitChiSquare();
1360 
1361  RecobHitVector recobHits;
1362 
1363  for (const auto hit : hitPair.getHits()) {
1364  if (!hit) {
1365  chisq = -1000.;
1366  continue;
1367  }
1368 
1369  art::Ptr<recob::Hit> hitPtr = hitToPtrMap[hit->getHit()];
1370  recobHits.push_back(hitPtr);
1371  }
1372 
1373  nFreePoints++;
1374 
1375  spacePointErr[1] = hitPair.getTotalCharge();
1376  spacePointErr[3] = hitPair.getChargeAsymmetry();
1377 
1378  output.artSpacePointVector->push_back(
1379  recob::SpacePoint(spacePointPos, spacePointErr, chisq, output.artSpacePointVector->size()));
1380 
1381  if (!recobHits.empty())
1382  output.makeSpacePointHitAssns(
1383  *output.artSpacePointVector.get(), recobHits, *output.artSPHitAssociations.get());
1384  }
1385 
1386  std::cout << "++++>>>> total num hits: " << hitPairVector.size()
1387  << ", num free: " << nFreePoints << std::endl;
1388  }
1389 
1390  size_t
1392  {
1393  size_t localCount(0);
1394 
1395  if (!clusterParameters.daughterList().empty()) {
1396  for (auto& clusterParams : clusterParameters.daughterList())
1397  localCount += countUltimateDaughters(clusterParams);
1398  }
1399  else
1400  localCount++;
1401 
1402  return localCount;
1403  }
1404 
1405  size_t
1408  reco::ClusterParameters& clusterParameters,
1409  size_t pfParticleParent,
1410  IdxToPCAMap& idxToPCAMap,
1411  IHit3DBuilder::RecobHitToPtrMap& hitToPtrMap,
1412  Hit3DToSPPtrMap& hit3DToSPPtrMap,
1413  Hit3DToSPPtrMap& best3DToSPPtrMap) const
1414  {
1415  // This is a recursive routine, we keep calling ourself as long as the daughter list is non empty
1416  if (!clusterParameters.daughterList().empty()) {
1417  for (auto& clusterParams : clusterParameters.daughterList())
1418  FindAndStoreDaughters(gser,
1419  output,
1420  clusterParams,
1421  pfParticleParent,
1422  idxToPCAMap,
1423  hitToPtrMap,
1424  hit3DToSPPtrMap,
1425  best3DToSPPtrMap);
1426  }
1427  // Otherwise we want to store the information
1428  else {
1429  size_t daughterIdx = ConvertToArtOutput(gser,
1430  output,
1431  clusterParameters,
1432  pfParticleParent,
1433  hitToPtrMap,
1434  hit3DToSPPtrMap,
1435  best3DToSPPtrMap);
1436 
1437  idxToPCAMap[daughterIdx] = &clusterParameters.getFullPCA();
1438  }
1439 
1440  return idxToPCAMap.size();
1441  }
1442 
1443  size_t
1446  reco::ClusterParameters& clusterParameters,
1447  size_t pfParticleParent,
1448  IHit3DBuilder::RecobHitToPtrMap& hitToPtrMap,
1449  Hit3DToSPPtrMap& hit3DToSPPtrMap,
1450  Hit3DToSPPtrMap& best3DToSPPtrMap) const
1451  {
1452  // prepare the algorithm to compute the cluster characteristics;
1453  // we use the "standard" one here, except that we override selected items
1454  // (so, thanks to metaprogramming, we finally have wrappers of wrappers);
1455  // configuration would happen here, but we are using the default
1456  // configuration for that algorithm
1457  using OverriddenClusterParamsAlg_t =
1459 
1461 
1462  // It should be straightforward at this point to transfer information from our vector of clusters
1463  // to the larsoft objects... of course we still have some work to do first, in particular to
1464  // find the candidate seeds and their seed hits
1465 
1466  // We keep track of 2 PCA axes, the first is the "full" PCA run over all the 3D hits in the
1467  // candidate cluster. The second will be that derived from just using the "skeleton" hits.
1468  // Make a copy of the full PCA to keep that, then get a reference for the skeleton PCA
1469  reco::PrincipalComponents& fullPCA = clusterParameters.getFullPCA();
1470  reco::PrincipalComponents& skeletonPCA = clusterParameters.getSkeletonPCA();
1471 
1472  size_t clusterStart = output.artClusterVector->size();
1473 
1474  // Start loop over views to build out the hit lists and the 2D cluster objects
1475  for (const auto& clusParametersPair : clusterParameters.getClusterParams()) {
1476  const reco::RecobClusterParameters& clusParams = clusParametersPair.second;
1477 
1478  // Protect against a missing view
1479  if (clusParams.m_view == geo::kUnknown) continue;
1480 
1481  // Get a vector of hit pointers to seed the cluster algorithm
1482  std::vector<const recob::Hit*> recobHitVec(clusParams.m_hitVector.size());
1483 
1484  std::transform(clusParams.m_hitVector.begin(),
1485  clusParams.m_hitVector.end(),
1486  recobHitVec.begin(),
1487  [](const auto& hit2D) { return hit2D->getHit(); });
1488 
1489  // Get the tdc/wire slope... from the unit vector...
1490  double startWire(clusParams.m_startWire);
1491  double endWire(clusParams.m_endWire);
1492  double startTime(clusParams.m_startTime);
1493  double endTime(clusParams.m_endTime);
1494 
1495  // feed the algorithm with all the cluster hits
1496  ClusterParamAlgo.ImportHits(gser, recobHitVec);
1497 
1498  // create the recob::Cluster directly in the vector
1499  cluster::ClusterCreator artCluster(gser,
1500  ClusterParamAlgo, // algo
1501  startWire, // start_wire
1502  0., // sigma_start_wire
1503  startTime, // start_tick
1504  clusParams.m_sigmaStartTime, // sigma_start_tick
1505  endWire, // end_wire
1506  0., // sigma_end_wire,
1507  endTime, // end_tick
1508  clusParams.m_sigmaEndTime, // sigma_end_tick
1509  output.artClusterVector->size(), // ID
1510  clusParams.m_view, // view
1511  clusParams.m_plane, // plane
1512  recob::Cluster::Sentry // sentry
1513  );
1514 
1515  output.artClusterVector->emplace_back(artCluster.move());
1516 
1517  // We love looping. In this case, our list of hits is comprised of "ClusterHits" and we need to get a RecobHitVector instead...
1518  RecobHitVector recobHits;
1519 
1520  for (const auto& hit2D : clusParams.m_hitVector) {
1521  if (hit2D == nullptr || hit2D->getHit() == nullptr) continue;
1522 
1523  art::Ptr<recob::Hit> hitPtr = hitToPtrMap[hit2D->getHit()];
1524  recobHits.push_back(hitPtr);
1525  }
1526 
1527  output.makeClusterHitAssns(recobHits);
1528  }
1529 
1530  // Last, let's try to get seeds for tracking..
1531  // Keep track of how many we have so far
1532  size_t numSeedsStart = output.artSeedVector->size();
1533 
1534  // Empty daughter vector for now
1535  std::vector<size_t> nullVector;
1536  size_t pfParticleIdx(output.artPFParticleVector->size());
1537 
1538  recob::PFParticle pfParticle(13, pfParticleIdx, pfParticleParent, nullVector);
1539  output.artPFParticleVector->push_back(pfParticle);
1540 
1541  // Assume that if we are a daughter particle then there is a set of "best" 3D points and the convex hull will have been calculated from them
1542  // If there is no best list then assume the convex hull is from all of the points...
1543  std::string instance("");
1544  reco::HitPairListPtr* hit3DListPtr = &clusterParameters.getHitPairListPtr();
1545  Hit3DToSPPtrMap* hit3DToPtrMap = &hit3DToSPPtrMap;
1546 
1547  auto spVector = output.artSpacePointVector.get();
1548  auto edgeVector = output.artEdgeVector.get();
1549  auto pfPartEdgeAssns = output.artPFPartEdgeAssociations.get();
1550  auto spEdgeAssns = output.artEdgeSPAssociations.get();
1551  auto spHitAssns = output.artSPHitAssociations.get();
1552  auto pfPartSPAssns = output.artPFPartSPAssociations.get();
1553 
1554  // Make associations to all space points for this cluster
1555  int spacePointStart = spVector->size();
1556 
1558  output, *spVector, *spHitAssns, *hit3DListPtr, hitToPtrMap, *hit3DToPtrMap);
1559 
1560  // And make sure to have associations to the PFParticle
1561  output.makePFPartSpacePointAssns(*spVector, *pfPartSPAssns, spacePointStart);
1562 
1563  // If they exist, make space points for the Path points
1564  if (!clusterParameters.getBestHitPairListPtr().empty()) {
1565  instance = m_pathInstance;
1566  hit3DListPtr = &clusterParameters.getBestHitPairListPtr();
1567  hit3DToPtrMap = &best3DToSPPtrMap;
1568 
1569  spVector = output.artPathPointVector.get();
1570  edgeVector = output.artPathEdgeVector.get();
1571  pfPartEdgeAssns = output.artPFPartPathEdgeAssociations.get();
1572  spEdgeAssns = output.artEdgePPAssociations.get();
1573  spHitAssns = output.artPPHitAssociations.get();
1574  pfPartSPAssns = output.artPFPartPPAssociations.get();
1575 
1576  spacePointStart = spVector->size();
1577 
1579  output, *spVector, *spHitAssns, *hit3DListPtr, hitToPtrMap, *hit3DToPtrMap, instance);
1580 
1581  // And make sure to have associations to the PFParticle
1582  output.makePFPartSpacePointAssns(*spVector, *pfPartSPAssns, spacePointStart, instance);
1583  }
1584 
1585  // Now handle the edges according to whether associated with regular or "best" space points
1586  if (!clusterParameters.getBestEdgeList().empty()) {
1587  size_t edgeStart = edgeVector->size();
1588 
1589  for (const auto& edge : clusterParameters.getBestEdgeList()) {
1590  RecobSpacePointVector spacePointVec;
1591 
1592  try {
1593  spacePointVec.push_back(hit3DToPtrMap->at(std::get<0>(edge)));
1594  spacePointVec.push_back(hit3DToPtrMap->at(std::get<1>(edge)));
1595  }
1596  catch (...) {
1597  std::cout << "Caught exception in looking up space point ptr... " << std::get<0>(edge)
1598  << ", " << std::get<1>(edge) << std::endl;
1599  continue;
1600  }
1601 
1602  edgeVector->emplace_back(
1603  std::get<2>(edge), spacePointVec[0].key(), spacePointVec[1].key(), edgeVector->size());
1604 
1605  output.makeEdgeSpacePointAssns(*edgeVector, spacePointVec, *spEdgeAssns, instance);
1606  }
1607 
1608  output.makePFPartEdgeAssns(*edgeVector, *pfPartEdgeAssns, edgeStart, instance);
1609  }
1610 
1611  // Look at making the PCAxis and associations - for both the skeleton (the first) and the full
1612  // First need some float to double conversion containers
1613  recob::PCAxis::EigenVectors eigenVecs;
1614  double eigenVals[] = {0., 0., 0.};
1615  double avePosition[] = {0., 0., 0.};
1616 
1617  eigenVecs.resize(3);
1618 
1619  for (size_t outerIdx = 0; outerIdx < 3; outerIdx++) {
1620  avePosition[outerIdx] = skeletonPCA.getAvePosition()[outerIdx];
1621  eigenVals[outerIdx] = skeletonPCA.getEigenValues()[outerIdx];
1622 
1623  eigenVecs[outerIdx].resize(3);
1624 
1625  for (size_t innerIdx = 0; innerIdx < 3; innerIdx++)
1626  eigenVecs[outerIdx][innerIdx] = skeletonPCA.getEigenVectors().row(outerIdx)(innerIdx);
1627  }
1628 
1629  recob::PCAxis skelPcAxis(skeletonPCA.getSvdOK(),
1630  skeletonPCA.getNumHitsUsed(),
1631  eigenVals,
1632  eigenVecs,
1633  avePosition,
1634  skeletonPCA.getAveHitDoca(),
1635  output.artPCAxisVector->size());
1636 
1637  output.artPCAxisVector->push_back(skelPcAxis);
1638 
1639  for (size_t outerIdx = 0; outerIdx < 3; outerIdx++) {
1640  avePosition[outerIdx] = fullPCA.getAvePosition()[outerIdx];
1641  eigenVals[outerIdx] = fullPCA.getEigenValues()[outerIdx];
1642 
1643  for (size_t innerIdx = 0; innerIdx < 3; innerIdx++)
1644  eigenVecs[outerIdx][innerIdx] = fullPCA.getEigenVectors().row(outerIdx)(innerIdx);
1645  }
1646 
1647  recob::PCAxis fullPcAxis(fullPCA.getSvdOK(),
1648  fullPCA.getNumHitsUsed(),
1649  eigenVals, //fullPCA.getEigenValues(),
1650  eigenVecs, //fullPCA.getEigenVectors(),
1651  avePosition, //fullPCA.getAvePosition(),
1652  fullPCA.getAveHitDoca(),
1653  output.artPCAxisVector->size());
1654 
1655  output.artPCAxisVector->push_back(fullPcAxis);
1656 
1657  // Create associations to the PFParticle
1658  output.makePFPartPCAAssns();
1659  output.makePFPartSeedAssns(numSeedsStart);
1660  output.makePFPartClusterAssns(clusterStart);
1661 
1662  return pfParticleIdx;
1663  }
1664 
1665  void
1667  std::vector<recob::SpacePoint>& spacePointVec,
1669  reco::HitPairListPtr& clusHitPairVector,
1670  IHit3DBuilder::RecobHitToPtrMap& hitToPtrMap,
1671  Hit3DToSPPtrMap& hit3DToSPPtrMap,
1672  const std::string& instance) const
1673  {
1674  // Reserve space...
1675  spacePointVec.reserve(spacePointVec.size() + clusHitPairVector.size());
1676 
1677  // Right now error matrix is uniform...
1678  double spError[] = {1., 0., 1., 0., 0., 1.};
1679 
1680  // Copy these hits to the vector to be stored with the event
1681  for (auto& hitPair : clusHitPairVector) {
1682  // Skip those space points that have already been created
1683  if (hit3DToSPPtrMap.find(hitPair) != hit3DToSPPtrMap.end()) continue;
1684 
1685  // Don't make space point if this hit was "rejected"
1686  if (hitPair->bitsAreSet(reco::ClusterHit3D::REJECTEDHIT)) continue;
1687 
1688  double chisq = hitPair->getHitChiSquare(); // secret handshake...
1689 
1690  // Mark this hit pair as in use
1691  hitPair->setStatusBit(reco::ClusterHit3D::MADESPACEPOINT);
1692 
1693  // Create and store the space point
1694  size_t spacePointID = spacePointVec.size();
1695  double spacePointPos[] = {
1696  hitPair->getPosition()[0], hitPair->getPosition()[1], hitPair->getPosition()[2]};
1697 
1698  spError[1] = hitPair->getTotalCharge();
1699  spError[3] = hitPair->getChargeAsymmetry();
1700 
1701  spacePointVec.emplace_back(spacePointPos, spError, chisq, spacePointID);
1702 
1703  // Update mapping
1704  hit3DToSPPtrMap[hitPair] = output.makeSpacePointPtr(spacePointID, instance);
1705 
1706  // space point hits associations
1707  RecobHitVector recobHits;
1708 
1709  for (const auto& hit : hitPair->getHits()) {
1710  if (!hit) continue;
1711 
1712  art::Ptr<recob::Hit> hitPtr = hitToPtrMap[hit->getHit()];
1713  recobHits.push_back(hitPtr);
1714  }
1715 
1716  if (!recobHits.empty())
1717  output.makeSpacePointHitAssns(spacePointVec, recobHits, spHitAssns, instance);
1718  }
1719 
1720  return;
1721  }
1722 
1723  void
1725  reco::ConvexHullKinkTupleList& kinkTupleVec) const
1726  {
1727  // Right now error matrix is uniform...
1728  double spError[] = {1., 0., 1., 0., 0., 1.};
1729 
1730  // Copy these hits to the vector to be stored with the event
1731  for (auto& kinkTuple : kinkTupleVec) {
1732  const reco::ClusterHit3D* hit = std::get<2>(std::get<0>(kinkTuple));
1733 
1734  double chisq = hit->getHitChiSquare(); // secret handshake...
1735 
1736  // Create and store the space point
1737  double spacePointPos[] = {
1738  hit->getPosition()[0], hit->getPosition()[1], hit->getPosition()[2]};
1739 
1740  spError[1] = hit->getTotalCharge();
1741  spError[3] = hit->getChargeAsymmetry();
1742 
1743  output.artExtremePointVector->push_back(
1744  recob::SpacePoint(spacePointPos, spError, chisq, output.artExtremePointVector->size()));
1745  }
1746 
1747  return;
1748  }
1749 
1750  void
1752  dcel2d::VertexList& vertexList,
1753  dcel2d::HalfEdgeList& halfEdgeList) const
1754  {
1755  // We actually do two things here:
1756  // 1) Create space points to represent the vertex locations of the voronoi diagram
1757  // 2) Create the edges that link the space points together
1758 
1759  // Set up the space point creation
1760  // Right now error matrix is uniform...
1761  double spError[] = {1., 0., 1., 0., 0., 1.};
1762  double chisq = 1.;
1763 
1764  // Keep track of the vertex to space point association
1765  std::map<const dcel2d::Vertex*, size_t> vertexToSpacePointMap;
1766 
1767  // Copy these hits to the vector to be stored with the event
1768  for (auto& vertex : vertexList) {
1769  const dcel2d::Coords& coords = vertex.getCoords();
1770 
1771  // Create and store the space point
1772  double spacePointPos[] = {coords[0], coords[1], coords[2]};
1773 
1774  vertexToSpacePointMap[&vertex] = output.artVertexPointVector->size();
1775 
1776  output.artVertexPointVector->emplace_back(
1777  spacePointPos, spError, chisq, output.artVertexPointVector->size());
1778  }
1779 
1780  // Try to avoid double counting
1781  std::set<const dcel2d::HalfEdge*> halfEdgeSet;
1782 
1783  // Build the edges now
1784  for (const auto& halfEdge : halfEdgeList) {
1785  // Recover twin
1786  const dcel2d::HalfEdge* twin = halfEdge.getTwinHalfEdge();
1787 
1788  // It can happen that we have no twin... and also check that we've not been here before
1789  if (twin && halfEdgeSet.find(twin) == halfEdgeSet.end()) {
1790  // Recover the vertices
1791  const dcel2d::Vertex* fromVertex = twin->getTargetVertex();
1792  const dcel2d::Vertex* toVertex = halfEdge.getTargetVertex();
1793 
1794  // It can happen for the open edges that there is no target vertex
1795  if (!toVertex || !fromVertex) continue;
1796 
1797  if (vertexToSpacePointMap.find(fromVertex) == vertexToSpacePointMap.end() ||
1798  vertexToSpacePointMap.find(toVertex) == vertexToSpacePointMap.end())
1799  continue;
1800 
1801  // Need the distance between vertices
1802  Eigen::Vector3f distVec = toVertex->getCoords() - fromVertex->getCoords();
1803 
1804  output.artVertexEdgeVector->emplace_back(distVec.norm(),
1805  vertexToSpacePointMap[fromVertex],
1806  vertexToSpacePointMap[toVertex],
1807  output.artEdgeVector->size());
1808 
1809  halfEdgeSet.insert(&halfEdge);
1810  }
1811  }
1812 
1813  return;
1814  }
1815 
1816  void
1818  const reco::PrincipalComponents& fullPCA,
1819  IdxToPCAMap& idxToPCAMap) const
1820  {
1821  // We actually do two things here:
1822  // 1) Create space points from the centroids of the PCA for each cluster
1823  // 2) Create the edges that link the space points together
1824 
1825  // The first task is to put the list of PCA's into some semblance of order... they may be
1826  // preordered by likely they are piecewise ordered so fix that here
1827 
1828  // We'll need the current PCA axis to determine doca and arclen
1829  Eigen::Vector3f avePosition(
1830  fullPCA.getAvePosition()[0], fullPCA.getAvePosition()[1], fullPCA.getAvePosition()[2]);
1831  Eigen::Vector3f axisDirVec(fullPCA.getEigenVectors().row(2));
1832 
1833  using DocaToPCAPair = std::pair<float, const reco::PrincipalComponents*>;
1834  using DocaToPCAVec = std::vector<DocaToPCAPair>;
1835 
1836  DocaToPCAVec docaToPCAVec;
1837 
1838  // Outer loop over views
1839  for (const auto& idxToPCA : idxToPCAMap) {
1840  const reco::PrincipalComponents* pca = idxToPCA.second;
1841 
1842  // Now we need to calculate the doca and poca...
1843  // Start by getting this hits position
1844  Eigen::Vector3f pcaPos(
1845  pca->getAvePosition()[0], pca->getAvePosition()[1], pca->getAvePosition()[2]);
1846 
1847  // Form a TVector from this to the cluster average position
1848  Eigen::Vector3f pcaToAveVec = pcaPos - avePosition;
1849 
1850  // With this we can get the arclength to the doca point
1851  float arclenToPoca = pcaToAveVec.dot(axisDirVec);
1852 
1853  docaToPCAVec.emplace_back(arclenToPoca, pca);
1854  }
1855 
1856  std::sort(docaToPCAVec.begin(), docaToPCAVec.end(), [](const auto& left, const auto& right) {
1857  return left.first < right.first;
1858  });
1859 
1860  // Set up the space point creation
1861  // Right now error matrix is uniform...
1862  double spError[] = {1., 0., 1., 0., 0., 1.};
1863  double chisq = 1.;
1864 
1865  const reco::PrincipalComponents* lastPCA(nullptr);
1866 
1867  // Set up to loop through the clusters
1868  for (const auto& docaToPCAPair : docaToPCAVec) {
1869  // Recover the PCA for this cluster
1870  const reco::PrincipalComponents* curPCA = docaToPCAPair.second;
1871 
1872  if (lastPCA) {
1873  double lastPointPos[] = {
1874  lastPCA->getAvePosition()[0], lastPCA->getAvePosition()[1], lastPCA->getAvePosition()[2]};
1875  size_t lastPointBin = output.artVertexPointVector->size();
1876  double curPointPos[] = {
1877  curPCA->getAvePosition()[0], curPCA->getAvePosition()[1], curPCA->getAvePosition()[2]};
1878  size_t curPointBin = lastPointBin + 1;
1879 
1880  output.artVertexPointVector->emplace_back(lastPointPos, spError, chisq, lastPointBin);
1881  output.artVertexPointVector->emplace_back(curPointPos, spError, chisq, curPointBin);
1882 
1883  Eigen::Vector3f distVec(curPointPos[0] - lastPointPos[0],
1884  curPointPos[1] - lastPointPos[1],
1885  curPointPos[2] - lastPointPos[2]);
1886 
1887  output.artVertexEdgeVector->emplace_back(
1888  distVec.norm(), lastPointBin, curPointBin, output.artEdgeVector->size());
1889  }
1890 
1891  lastPCA = curPCA;
1892  }
1893  }
1894 
1895 } // namespace lar_cluster3d
reco::HitPairListPtr & getBestHitPairListPtr()
Definition: Cluster3D.h:480
intermediate_table::iterator iterator
std::unique_ptr< std::vector< recob::Edge > > artPathEdgeVector
float m_buildNeighborhoodTime
Keeps track of time to build epsilon neighborhood.
std::unique_ptr< art::Assns< recob::SpacePoint, recob::PFParticle > > artPFPartSPAssociations
void PCAAnalysis_calc3DDocas(const reco::HitPairListPtr &hitPairVector, const reco::PrincipalComponents &pca) const
std::list< reco::ClusterHit3D > HitPairList
Definition: Cluster3D.h:339
std::unique_ptr< std::vector< recob::SpacePoint > > artVertexPointVector
std::unique_ptr< lar_cluster3d::IClusterModAlg > m_clusterPathAlg
Algorithm to do cluster path finding.
bool getSvdOK() const
Definition: Cluster3D.h:244
void makeEdgeSpacePointAssns(std::vector< recob::Edge > &edgeVector, RecobSpacePointVector &spacePointVector, art::Assns< recob::SpacePoint, recob::Edge > &edgeSPAssociations, const std::string &path="")
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.
std::unique_ptr< std::vector< recob::Seed > > artSeedVector
Reconstruction base classes.
T * get() const
Definition: ServiceHandle.h:63
std::string m_vertexInstance
Special instance name for vertex points.
static constexpr size_t kPFParticlePrimary
Define index to signify primary particle.
Definition: PFParticle.h:61
std::list< HalfEdge > HalfEdgeList
Definition: DCEL.h:184
void makePFPartSpacePointAssns(std::vector< recob::SpacePoint > &spacePointVector, art::Assns< recob::SpacePoint, recob::PFParticle > &pfPartSPAssociations, size_t spacePointStart, const std::string &instance="")
std::string string
Definition: nybbler.cc:12
const Eigen::Vector3f getPosition() const
Definition: Cluster3D.h:158
std::unique_ptr< art::Assns< recob::Seed, recob::Hit > > artSeedHitAssociations
MaybeLogger_< ELseverityLevel::ELsev_info, false > LogInfo
float m_parallelHitsTransWid
Cut on transverse width of cluster (PCA 2nd eigenvalue)
HalfEdge * getTwinHalfEdge() const
Definition: DCEL.h:163
Unknown view.
Definition: geo_types.h:136
Cluster3D class.
Definition: SkeletonAlg.h:31
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...
reco::PrincipalComponents & getSkeletonPCA()
Definition: Cluster3D.h:478
EDProducer(fhicl::ParameterSet const &pset)
Definition: EDProducer.h:20
void clearStatusBits(unsigned bits) const
Definition: Cluster3D.h:181
void findTrackSeeds(art::Event &evt, reco::ClusterParameters &cluster, IHit3DBuilder::RecobHitToPtrMap &hitToPtrMap, std::vector< recob::Seed > &seedVec, art::Assns< recob::Seed, recob::Hit > &seedHitAssns) const
An interface to the seed finding algorithm.
void makeClusterHitAssns(RecobHitVector &recobHits)
SkeletonAlg m_skeletonAlg
Skeleton point finder.
void makePFPartEdgeAssns(std::vector< recob::Edge > &edgeVector, art::Assns< recob::Edge, recob::PFParticle > &pfPartEdgeAssociations, size_t edgeStart, const std::string &instance="")
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::Edge > > artVertexEdgeVector
const std::string instance
int m_hits3D
Keeps track of the number of 3D hits made.
STL namespace.
float getTotalCharge() const
Definition: Cluster3D.h:162
Hit has been rejected for any reason.
Definition: Cluster3D.h:99
reco::EdgeList & getBestEdgeList()
Definition: Cluster3D.h:481
int getNumHitsUsed() const
Definition: Cluster3D.h:245
HoughSeedFinderAlg class.
boost::graph_traits< ModuleGraph >::edge_descriptor Edge
Definition: ModuleGraph.h:24
intermediate_table::const_iterator const_iterator
std::list< HitPairListPtr > HitPairListPtrList
Definition: Cluster3D.h:337
A utility class used in construction of 3D clusters.
Definition: Cluster3D.h:300
reco::HitPairListPtr & getHitPairListPtr()
Definition: Cluster3D.h:476
void ProduceArtClusters(util::GeometryUtilities const &gser, ArtOutputHandler &output, reco::HitPairList &hitPairList, reco::ClusterParametersList &clusterParametersList, IHit3DBuilder::RecobHitToPtrMap &hitToPtrMap) const
Top level output routine, allows checking cluster status.
bool m_onlyMakSpacePoints
If true we don&#39;t do the full cluster 3D processing.
void makePFPartSeedAssns(size_t numSeedsStart)
Cluster finding and building.
reco::PlaneToClusterParamsMap & getClusterParams()
Definition: Cluster3D.h:474
HoughSeedFinderAlg m_seedFinderAlg
Seed finder.
std::unique_ptr< lar_cluster3d::IClusterAlg > m_clusterAlg
Algorithm to do 3D space point clustering.
std::unique_ptr< art::Assns< recob::PFParticle, recob::Seed > > artPFPartSeedAssociations
float m_makeHitsTime
Keeps track of time to build 3D hits.
std::unique_ptr< std::vector< recob::PFParticle > > artPFParticleVector
void makeSpacePointHitAssns(std::vector< recob::SpacePoint > &spacePointVector, RecobHitVector &recobHits, art::Assns< recob::Hit, recob::SpacePoint > &spHitAssns, const std::string &path="")
void GetSkeletonHits(const reco::HitPairListPtr &inputHitList, reco::HitPairListPtr &skeletonHitList) const
Return the skeleton hits from the input list.
art::Ptr< recob::Edge > makeEdgePtr(size_t index, const std::string &instance="")
float getDocaToAxis() const
Definition: Cluster3D.h:169
art framework interface to geometry description
std::enable_if_t< std::is_arithmetic_v< T >, T > hypot(T x, T y)
Definition: hypot.h:60
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:112
void ImportHits(util::GeometryUtilities const &gser, Iter begin, Iter end)
Calls SetHits() with the hits in the sequence.
ClusterParametersList & daughterList()
Definition: Cluster3D.h:450
std::unique_ptr< art::Assns< recob::Hit, recob::SpacePoint > > artPPHitAssociations
Overrides another ClusterParamsAlgBase class with selected constants.
const EigenValues & getEigenValues() const
Definition: Cluster3D.h:246
art::PtrMaker< recob::Edge > fEdgePtrMaker
std::string m_extremeInstance
Instance name for the extreme points.
#define DEFINE_ART_MODULE(klass)
Definition: ModuleMacros.h:67
reco::PrincipalComponents & getFullPCA()
Definition: Cluster3D.h:477
size_t countUltimateDaughters(reco::ClusterParameters &clusterParameters) const
Count number of end of line daughters.
float getChargeAsymmetry() const
Definition: Cluster3D.h:168
art::PtrMaker< recob::SpacePoint > fSPPtrMakerPath
void push_back(Ptr< U > const &p)
Definition: PtrVector.h:435
void splitClustersWithMST(reco::ClusterParameters &clusterParameters, reco::ClusterParametersList &clusterParametersList) const
Attempt to split clusters by using a minimum spanning tree.
def key(type, name=None)
Definition: graph.py:13
std::unique_ptr< std::vector< recob::SpacePoint > > artPathPointVector
void InitializeMonitoring()
Initialize the internal monitoring.
def move(depos, offset)
Definition: depos.py:107
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.
std::unique_ptr< std::vector< recob::SpacePoint > > artSpacePointVector
float m_clusterMergeTime
Keeps track of the time to merge clusters.
const Eigen::Vector3f & getAvePosition() const
Definition: Cluster3D.h:248
std::unique_ptr< art::Assns< recob::PFParticle, recob::PCAxis > > artPFPartAxisAssociations
std::unique_ptr< art::Assns< recob::Cluster, recob::Hit > > artClusterAssociations
PCASeedFinderAlg m_pcaSeedFinderAlg
Use PCA axis to find seeds.
ArtOutputHandler(art::Event &evt, std::string &pathName, std::string &vertexName, std::string &extremeName)
std::list< ConvexHullKinkTuple > ConvexHullKinkTupleList
Definition: Cluster3D.h:355
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.
ProductID put(std::unique_ptr< PROD > &&edp, std::string const &instance={})
Definition: DataViewImpl.h:686
std::unique_ptr< std::vector< recob::Edge > > artEdgeVector
void MakeAndSavePCAPoints(ArtOutputHandler &, const reco::PrincipalComponents &, IdxToPCAMap &) const
std::list< const reco::ClusterHit3D * > HitPairListPtr
Definition: Cluster3D.h:335
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.
std::unique_ptr< art::Assns< recob::Hit, recob::SpacePoint > > artSPHitAssociations
This provides an art tool interface definition for 3D Cluster algorithms.
double distance(double x1, double y1, double z1, double x2, double y2, double z2)
RunNumber_t run() const
Definition: DataViewImpl.cc:71
std::unique_ptr< art::Assns< recob::SpacePoint, recob::Edge > > artEdgePPAssociations
int m_hits
Keeps track of the number of hits seen.
void produce(art::Event &evt) override
size_t FindAndStoreDaughters(util::GeometryUtilities const &gser, ArtOutputHandler &output, reco::ClusterParameters &clusterParameters, size_t pfParticleParent, IdxToPCAMap &idxToPCAMap, IHit3DBuilder::RecobHitToPtrMap &hitToPtrMap, Hit3DToSPPtrMap &hit3DToSPPtrMap, Hit3DToSPPtrMap &best3DToSPPtrMap) const
This will produce art output for daughters noting that it needs to be done recursively.
bool empty() const
Definition: PtrVector.h:330
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)
void MakeAndSaveSpacePoints(ArtOutputHandler &output, std::vector< recob::SpacePoint > &spacePointVec, art::Assns< recob::Hit, recob::SpacePoint > &spHitAssns, reco::HitPairListPtr &clusHitPairVector, IHit3DBuilder::RecobHitToPtrMap &hitToPtrMap, Hit3DToSPPtrMap &hit3DToSPPtrMap, const std::string &path="") const
Special routine to handle creating and saving space points.
This provides an art tool interface definition for tools which construct 3D hits used in 3D clusterin...
std::unique_ptr< art::Assns< recob::SpacePoint, recob::PFParticle > > artPFPartPPAssociations
std::unique_ptr< art::Assns< recob::PFParticle, recob::Cluster > > artPFPartClusAssociations
ProducesCollector & producesCollector() noexcept
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...
bool operator()(const reco::ClusterHit3D *hit3D) const
art::Ptr< recob::SpacePoint > makeSpacePointPtr(size_t index, const std::string &instance="")
std::unique_ptr< lar_cluster3d::IClusterParametersBuilder > m_clusterBuilder
Common cluster builder tool.
Cluster3D(fhicl::ParameterSet const &pset)
art::PtrMaker< recob::SpacePoint > fSPPtrMaker
Declaration of signal hit object.
std::unique_ptr< std::vector< recob::SpacePoint > > artExtremePointVector
Hierarchical representation of particle flow.
Definition: PFParticle.h:44
bool bitsAreSet(const unsigned int &bitsToCheck) const
Definition: Cluster3D.h:177
const float getAveHitDoca() const
Definition: Cluster3D.h:249
void makePFPartClusterAssns(size_t clusterStart)
Hit has been made into Space Point.
Definition: Cluster3D.h:103
MaybeLogger_< ELseverityLevel::ELsev_success, false > LogDebug
This provides an art tool interface definition for 3D Cluster algorithms.
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< std::vector< recob::Cluster > > artClusterVector
std::unique_ptr< lar_cluster3d::IClusterModAlg > m_clusterMergeAlg
Algorithm to do cluster merging.
size_t ConvertToArtOutput(util::GeometryUtilities const &gser, ArtOutputHandler &output, reco::ClusterParameters &clusterParameters, size_t pfParticleParent, IHit3DBuilder::RecobHitToPtrMap &hitToPtrMap, Hit3DToSPPtrMap &hit3DToSPPtrMap, Hit3DToSPPtrMap &best3DToSPPtrMap) const
Produces the art output from all the work done in this producer module.
std::unique_ptr< lar_cluster3d::IHit3DBuilder > m_hit3DBuilderAlg
Builds the 3D hits to operate on.
T copy(T const &v)
PrincipalComponentsAlg m_pcaAlg
Principal Components algorithm.
EventNumber_t event() const
Definition: EventID.h:116
std::unordered_map< const reco::ClusterHit2D *, ClusterToHitPairSetMap > Hit2DToClusterMap
Definition: Cluster3D.h:511
art::PtrMaker< recob::Edge > fEdgePtrMakerPath
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.
std::vector< SeedHitPairListPair > SeedHitPairListPairVec
ClusterHit2DVec m_hitVector
Definition: Cluster3D.h:327
2D representation of charge deposited in the TDC/wire plane
Definition: Hit.h:48
Algorithm collection class computing cluster parameters.
float getHitChiSquare() const
Definition: Cluster3D.h:166
Definition of the Cluster3D class.
float m_artHitsTime
Keeps track of time to recover hits.
Interface to class computing cluster parameters.
double accumulated_real_time() const
Definition: cpu_timer.cc:66
std::unique_ptr< std::vector< recob::PCAxis > > artPCAxisVector
TCEvent evt
Definition: DataStructs.cxx:7
Vertex * getTargetVertex() const
Definition: DCEL.h:161
Eigen::Vector3f Coords
Definition: DCEL.h:46
std::unique_ptr< art::Assns< recob::SpacePoint, recob::Edge > > artEdgeSPAssociations
std::unordered_map< const reco::ClusterHit3D *, art::Ptr< recob::SpacePoint >> Hit3DToSPPtrMap
void MakeAndSaveKinkPoints(ArtOutputHandler &output, reco::ConvexHullKinkTupleList &clusHitPairVector) const
Special routine to handle creating and saving space points.
PCASeedFinderAlg class.
std::vector< std::vector< double > > EigenVectors
Definition: PCAxis.h:29
void start()
Definition: cpu_timer.cc:83
std::unordered_map< const recob::Hit *, art::Ptr< recob::Hit >> RecobHitToPtrMap
Defines a structure mapping art representation to internal.
Definition: IHit3DBuilder.h:56
void PrepareEvent(const art::Event &evt)
Event Preparation.
const Coords & getCoords() const
Definition: DCEL.h:69
std::list< Vertex > VertexList
Definition: DCEL.h:182
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.
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.cc:34
std::string m_pathInstance
Special instance for path points.
float m_totalTime
Keeps track of total execution time.
std::list< ClusterParameters > ClusterParametersList
Definition: Cluster3D.h:404
decltype(auto) constexpr empty(T &&obj)
ADL-aware version of std::empty.
Definition: StdUtils.h:97
const EigenVectors & getEigenVectors() const
Definition: Cluster3D.h:247
QTextStream & endl(QTextStream &s)
std::unique_ptr< art::Assns< recob::Edge, recob::PFParticle > > artPFPartEdgeAssociations
std::unique_ptr< art::Assns< recob::Edge, recob::PFParticle > > artPFPartPathEdgeAssociations
void setStatusBit(unsigned bits) const
Definition: Cluster3D.h:180
std::map< size_t, const reco::PrincipalComponents * > IdxToPCAMap
Special routine to handle creating and saving space points & edges PCA points.
vertex reconstruction
bool m_enableMonitoring
Turn on monitoring of this algorithm.