LArSoft  v07_13_02
Liquid Argon Software toolkit - http://larsoft.org/
ClusterParamsBuilder.cxx
Go to the documentation of this file.
1 
8 // Framework Includes
9 #include "cetlib/search_path.h"
10 
12 
13 // LArSoft includes
18 
19 // std includes
20 #include <string>
21 #include <functional>
22 #include <iostream>
23 #include <memory>
24 
25 //------------------------------------------------------------------------------------------------------------------------------------------
26 // implementation follows
27 
28 namespace lar_cluster3d {
29 
31  m_pcaAlg(pset.get<fhicl::ParameterSet>("PrincipalComponentsAlg"))
32 {
33  this->configure(pset);
34 }
35 
36 //------------------------------------------------------------------------------------------------------------------------------------------
37 
39 {
40 }
41 
42 //------------------------------------------------------------------------------------------------------------------------------------------
43 
45 {
46  m_clusterMinHits = pset.get<size_t>("ClusterMinHits", 3 );
47  m_clusterMinUniqueFraction = pset.get<double>("ClusterMinUniqueFraction", 0.5 );
48  m_clusterMaxLostFraction = pset.get<double>("ClusterMaxLostFraction", 0.5 );
49 
50  return;
51 }
52 
54 {
64  // This is a remote possibility but why not check?
65  if (!clusterParametersList.empty())
66  {
67  // We want to order our clusters on by largest (most number hits) to smallest. So, we'll loop through the clusters,
68  // weeding out the unwanted ones and keep track of things in a set of "good" clusters which we'll order
69  // by cluster size.
70  clusterParametersList.sort();
71 
72  // The smallest clusters are now at the end, drop those off the back that are less than the mininum necessary
73  while(!clusterParametersList.empty() && clusterParametersList.back().getHitPairListPtr().size() < m_clusterMinHits) clusterParametersList.pop_back();
74 
75  // The next step is to build out a mapping of all 2D hits to clusters
76  // Keep track of where the hits get distributed...
77  reco::Hit2DToClusterMap hit2DToClusterMap;
78 
79  reco::ClusterParametersList::iterator clusterItr = clusterParametersList.begin();
80 
81  for(auto& clusterParams : clusterParametersList)
82  {
83  for(const auto& hit3D : clusterParams.getHitPairListPtr())
84  {
85  for(const auto& hit2D : hit3D->getHits())
86  {
87  if (!hit2D) continue;
88 
89  hit2DToClusterMap[hit2D][&clusterParams].insert(hit3D);
90  }
91  }
92  }
93 
94  // Ok, spin through again to remove ambiguous hits
95  // for(auto& clusterParams : clusterParametersList) PruneAmbiguousHits(clusterParams,hit2DToClusterMap);
96 
97  // What remains is an order set of clusters, largest first
98  // Now go through and obtain cluster parameters
99  clusterItr = clusterParametersList.begin();
100 
101  while(clusterItr != clusterParametersList.end())
102  {
103  // Dereference for ease...
104  reco::ClusterParameters& clusterParams = *clusterItr;
105 
106  // Do the actual work of filling the parameters
107  FillClusterParams(clusterParams, hit2DToClusterMap, m_clusterMinUniqueFraction, m_clusterMaxLostFraction);
108 
109  // If this cluster is rejected then the parameters will be empty
110  if (clusterParams.getClusterParams().empty() || !clusterParams.getFullPCA().getSvdOK())
111  {
112  clusterItr = clusterParametersList.erase(clusterItr);
113  }
114  else clusterItr++;
115  }
116  }
117 
118  return;
119 }
120 
122  reco::Hit2DToClusterMap& hit2DToClusterMap,
123  double minUniqueFrac,
124  double maxLostFrac) const
125 {
130  // Recover the HitPairListPtr from the input clusterParams (which will be the
131  // only thing that has been provided)
132  reco::HitPairListPtr& hitPairVector = clusterParams.getHitPairListPtr();
133 
134  // To be sure, we should clear the other data members
135  clusterParams.getClusterParams().clear();
136  clusterParams.getFullPCA() = reco::PrincipalComponents();
137 
138  // A test of the emergency broadcast system...
139  // FindBestPathInCluster(clusterParams);
140  // CheckHitSorting(clusterParams);
141 
142  // See if we can avoid duplicates by temporarily transferring to a set
143  std::set<const reco::ClusterHit2D*> hitSet;
144 
145  // Ultimately we want to keep track of the number of unique 2D hits in this cluster
146  // So use a vector (by plane) of sets of hits
147  std::vector<size_t> planeHit2DVec;
148  std::vector<size_t> planeUniqueHit2DVec;
149 
150  planeHit2DVec.resize(3);
151  planeUniqueHit2DVec.resize(3);
152 
153  // Map from 2D hits to associated 3D hits
154  reco::Hit2DToHit3DListMap& hit2DToHit3DListMap = clusterParams.getHit2DToHit3DListMap();
155 
156  // The map from 2D to 3D hits will contain unique entries for 2D hits so we can do some quick accounting here
157  for(const auto& hitMapPair : hit2DToHit3DListMap)
158  {
159  size_t plane = hitMapPair.first->getHit().WireID().Plane;
160 
161  planeHit2DVec[plane] += hitMapPair.second.size();
162  if (!(hitMapPair.first->getStatusBits() & reco::ClusterHit2D::USED)) planeUniqueHit2DVec[plane] += hitMapPair.second.size();
163  }
164 
165  // Get totals
166  int numTotalHits(0);
167  int numUniqueHits(0);
168 
169  // Also consider the number of hits shared on a given view...
170  std::vector<float> uniqueHitFracVec(3,0.);
171  int nPlanesWithHits(0);
172  int nPlanesWithUniqueHits(0);
173  size_t minPlane(0);
174  size_t minPlaneCnt = planeUniqueHit2DVec.at(0);
175 
176  // Loop through the planes
177  for(int idx = 0; idx < 3; idx++)
178  {
179  // numerology
180  numTotalHits += planeHit2DVec.at(idx);
181  numUniqueHits += planeUniqueHit2DVec.at(idx);
182 
183  if (planeHit2DVec.at(idx) > 0) nPlanesWithHits++;
184  if (planeUniqueHit2DVec.at(idx) > 0) nPlanesWithUniqueHits++;
185 
186  // Compute the fraction of unique hits in this plane
187  uniqueHitFracVec[idx] = float(planeUniqueHit2DVec.at(idx)) / std::max(float(planeHit2DVec.at(idx)),float(1.));
188 
189  // Finding the plane with the fewest hits
190  if (planeHit2DVec.at(idx) < minPlaneCnt)
191  {
192  minPlaneCnt = planeHit2DVec.at(idx);
193  minPlane = idx;
194  }
195  }
196 
197  // If we have something left then at this point we make one more check
198  // This check is intended to weed out clusters made from isolated groups of ambiguous hits which
199  // really belong to a larger cluster
200  if (numUniqueHits > 0.25 * numTotalHits && nPlanesWithHits > 1 && nPlanesWithUniqueHits > 1)
201  {
202  // Sorts lowest to highest
203  std::sort(uniqueHitFracVec.begin(),uniqueHitFracVec.end());
204 
205  float acceptRatio = 0.;
206 
207  if(uniqueHitFracVec[0] * uniqueHitFracVec[1] > 0.25) acceptRatio = 1.;
208 
209  float uniqueFraction = uniqueHitFracVec[0] * uniqueHitFracVec[1] * uniqueHitFracVec[2];
210 
211  // Arbitrary rejection criteria... need to understand
212  if (uniqueFraction > 0.6 && acceptRatio > 0.)
213  {
214  // Create a list to hold 3D hits which are already in use (criteria below)
215  reco::HitPairListPtr usedHitPairList;
216 
217 // std::cout << "--------> Starting 3D hit removal, # 2D hits/plane: " << planeHit2DVec[0] << "/" << planeHit2DVec[1] << "/" << planeHit2DVec[2] << std::endl;
218 
219  // Have survived laugh test, do final processing...
220  // In this first loop go through all the 2D hits and identify the 3D hits that are candidates for deletion
221  for(auto& pair : hit2DToHit3DListMap)
222  {
223  // Check to see if this can happen
224  if (pair.second.empty())
225  {
226  std::cout << "<<<<<< no matching 3D hits for reco hit in final hit processing >>>>>>" << std::endl;
227  continue;
228  }
229 
230  // Which plane for this hit?
231  size_t hitPlane = pair.first->getHit().WireID().Plane;
232 
233  // Only reject hits on the planes not the fewest 2D hits and really only do this if more than a couple
234  if (hitPlane != minPlane && pair.second.size() > 2)
235  {
236  // If this hit is associated to a number of 3D hits then do some arbitration
237  // Start by sorting the 3D hits by "significance"
238  // --> Really should do this by the significance of adding the hit we are looking at?
239  //pair.second.sort([hitPlane](const auto& left, const auto& right){return left->getHitDelTSigVec()[hitPlane] < right->getHitDelTSigVec()[hitPlane];});
240  pair.second.sort([](const auto& left, const auto& right){return left->getHitChiSquare() < right->getHitChiSquare();});
241 
243  //std::cout << "~~~~> Checking hit removal, # matches: " << pair.second.size() << ", first params: " << pair.second.front()->getHitChiSquare() << ", last params: "<< pair.second.back()->getHitChiSquare();
244 
245  // From sorted list, determine a rejection value to eliminate bad hits
246  //float cutDeltaTSig = std::min(2.0,std::max(0.5, double((pair.second.front()->getHitDelTSigVec()[hitPlane]))));
247  float cutDeltaTSig = std::min(2.0,std::max(0.5, double(pair.second.front()->getHitChiSquare())));
248 
249  //std::cout << ", cutDeltaTSig: " << cutDeltaTSig;
250 
251  cutDeltaTSig = 10.;
252 
253  // And here go through the process of eliminating it
254  //reco::HitPairListPtr::iterator firstBadHitItr = std::find_if(pair.second.begin(),pair.second.end(),[hitPlane,cutDeltaTSig](const auto& hitPtr){return hitPtr->getHitDelTSigVec()[hitPlane] > cutDeltaTSig;});
255  reco::HitPairListPtr::iterator firstBadHitItr = std::find_if(pair.second.begin(),pair.second.end(),[cutDeltaTSig](const auto& hitPtr){return hitPtr->getHitChiSquare() > cutDeltaTSig;});
256 
257  // We need to worry about cutting too many hits... use this loop to try to expand the range in a reasonable fashion
258 // while(std::distance(pair.second.begin(),firstBadHitItr) < int(pair.second.size()/3) && cutDeltaTSig < 0.5)
259 // {
260 // float candDeltaTSig = (*firstBadHitItr)->getHitDelTSigVec()[hitPlane];
261 //
262 // if (candDeltaTSig > 2. * cutDeltaTSig) break;
263 //
264 // firstBadHitItr++;
265 // cutDeltaTSig = candDeltaTSig;
266 // }
267 
268  reco::HitPairListPtr rejectCandList;
269 
270  std::copy(firstBadHitItr,pair.second.end(),std::back_inserter(rejectCandList));
271 
272  //std::cout << ", bad hits: " << rejectCandList.size() << std::endl;
273 
274  // Remove the 3D hits from all the lists
275  for(const auto& hit3D : rejectCandList)
276  {
277  bool rejectThisHit(true);
278  std::vector<std::pair<reco::HitPairListPtr&,reco::HitPairListPtr::iterator>> deleteVec;
279 
280  for(const auto& hit2D : hit3D->getHits())
281  {
282  // Watch for null hit (dead channels)
283  if (!hit2D) continue;
284 
285  reco::HitPairListPtr& removeHitList = hit2DToHit3DListMap.at(hit2D);
286 
287  // Don't allow all the 3D hits associated to this 2D hit to be rejected?
288  if (removeHitList.size() < 2)
289  {
290  //std::cout << " ---> remove list too small, size: " << removeHitList.size() << " for hit: " << hit2D << ", pair.first: " << pair.first << std::endl;
291  rejectThisHit = false;
292  break;
293  }
294 
295  reco::HitPairListPtr::iterator removeItr = std::find(removeHitList.begin(),removeHitList.end(),hit3D);
296 
297  if (removeItr != removeHitList.end()) deleteVec.emplace_back(removeHitList,removeItr);
298  //else std::cout << "======>> Did not find 3D hit to remove from list for 2D hit! <<+++++++++" << std::endl;
299  }
300 
301  if (rejectThisHit)
302  {
303  for(auto& rejectPair : deleteVec) rejectPair.first.erase(rejectPair.second);
304 
305  usedHitPairList.push_back(hit3D);
306  }
307  }
308  }
309 
310  hitSet.insert(pair.first);
311  }
312 
313  // Now we go through the list of candidates and delete those which are unworthy of being processed...
314  if (!usedHitPairList.empty())
315  {
316  // Loop through the hits watching out for double counting
317  const reco::ClusterHit3D* lastHit3D = 0;
318 
319  for(const auto& hit3D : usedHitPairList)
320  {
321  if (hit3D == lastHit3D) continue;
322 
323  reco::HitPairListPtr::iterator hit3DItr = std::find(hitPairVector.begin(),hitPairVector.end(),hit3D);
324 
325  if (hit3DItr != hitPairVector.end())
326  {
327  // Mark the hit
328  hit3D->setStatusBit(reco::ClusterHit3D::REJECTEDHIT);
329 
330  // Remove from the cluster's hit container
331  hitPairVector.erase(hit3DItr);
332 
333  // If the clustering algorithm includes edges then need to get rid of those as well
334  if (!clusterParams.getHit3DToEdgeMap().empty())
335  {
336  reco::Hit3DToEdgeMap& edgeMap = clusterParams.getHit3DToEdgeMap();
337 
338  edgeMap.erase(edgeMap.find(hit3D));
339  }
340  }
341 
342  lastHit3D = hit3D;
343  }
344 // removeUsedHitsFromMap(clusterParams, usedHitPairList, hit2DToClusterMap);
345  }
346 
347  // First stage of feature extraction runs here
348  m_pcaAlg.PCAAnalysis_3D(hitPairVector, clusterParams.getFullPCA());
349 
350  // Must have a valid pca
351  if (clusterParams.getFullPCA().getSvdOK())
352  {
353  // Set the skeleton PCA to make sure it has some value
354  clusterParams.getSkeletonPCA() = clusterParams.getFullPCA();
355 
356  // Add the "good" hits to our cluster parameters
357  for(const auto& hit2D : hitSet)
358  {
359  hit2D->setStatusBit(reco::ClusterHit2D::USED);
360  clusterParams.UpdateParameters(hit2D);
361  }
362  }
363  }
364  }
365 
366  return;
367 }
368 
370  reco::HitPairListPtr& usedHitPairList,
371  reco::Hit2DToClusterMap& hit2DToClusterMap) const
372 {
373  // Clean up our hit to cluster map
374  for(const auto& hit3D : usedHitPairList)
375  {
376  for(const auto& hit2D : hit3D->getHits())
377  {
378  if (!hit2D) continue;
379 
380  reco::Hit2DToClusterMap::iterator hitToClusMapItr = hit2DToClusterMap.find(hit2D);
381 
382  // I am pretty sure this can't happen but let's check anyway...
383  if (hitToClusMapItr == hit2DToClusterMap.end())
384  {
385  std::cout << "*********** COULD NOT FIND ENTRY FOR 2D HIT! **************" << std::endl;
386  break;
387  }
388 
389  // Ok, the same hit can be shared in the same cluster so must be careful here
390  // First loop over clusters looking for match
391  reco::ClusterToHitPairSetMap::iterator clusToHit3DMapItr = hitToClusMapItr->second.find(&clusterParams);
392 
393  // This also can't happen
394  if (clusToHit3DMapItr == hitToClusMapItr->second.end())
395  {
396  std::cout << "************ DUCK! THE SKY HAS FALLEN!! *********" << std::endl;
397  break;
398  }
399 
400  // If this hit is shared by more than one 3D hit then pick the right one
401  if (clusToHit3DMapItr->second.size() > 1)
402  {
403  reco::HitPairSetPtr::iterator hit3DItr = clusToHit3DMapItr->second.find(hit3D);
404 
405  clusToHit3DMapItr->second.erase(hit3DItr);
406  }
407  else
408  hitToClusMapItr->second.erase(clusToHit3DMapItr);
409 
410  }
411  }
412 
413  return;
414 }
415 
416 } // namespace lar_cluster3d
bool getSvdOK() const
Definition: Cluster3D.h:226
ClusterParamsBuilder(fhicl::ParameterSet const &pset)
Constructor.
constexpr auto const & right(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:112
void PCAAnalysis_3D(const reco::HitPairListPtr &hitPairList, reco::PrincipalComponents &pca, bool skeletonOnly=false) const
This algorithm will create and then cluster 3D hits using DBScan.
intermediate_table::iterator iterator
Declaration of signal hit object.
reco::PrincipalComponents & getSkeletonPCA()
Definition: Cluster3D.h:455
reco::Hit2DToHit3DListMap & getHit2DToHit3DListMap()
Definition: Cluster3D.h:452
Hit has been rejected for any reason.
Definition: Cluster3D.h:92
void BuildClusterInfo(reco::ClusterParametersList &clusterParametersList) const
Given the results of running DBScan, format the clusters so that they can be easily transferred back ...
reco::Hit3DToEdgeMap & getHit3DToEdgeMap()
Definition: Cluster3D.h:456
reco::HitPairListPtr & getHitPairListPtr()
Definition: Cluster3D.h:453
reco::PlaneToClusterParamsMap & getClusterParams()
Definition: Cluster3D.h:451
void removeUsedHitsFromMap(reco::ClusterParameters &, reco::HitPairListPtr &, reco::Hit2DToClusterMap &) const
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) ...
Int_t max
Definition: plot.C:27
size_t m_clusterMinHits
Data members to follow.
reco::PrincipalComponents & getFullPCA()
Definition: Cluster3D.h:454
parameter set interface
T get(std::string const &key) const
Definition: ParameterSet.h:231
std::unordered_map< const reco::ClusterHit2D *, reco::HitPairListPtr > Hit2DToHit3DListMap
Definition: Cluster3D.h:329
std::list< const reco::ClusterHit3D * > HitPairListPtr
Definition: Cluster3D.h:317
void UpdateParameters(const reco::ClusterHit2D *hit)
Definition: Cluster3D.h:429
Encapsulate the geometry of a wire.
constexpr auto const & left(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:104
Utility object to perform functions of association.
Encapsulate the construction of a single detector plane.
Int_t min
Definition: plot.C:26
std::unordered_map< const reco::ClusterHit2D *, ClusterToHitPairSetMap > Hit2DToClusterMap
Definition: Cluster3D.h:488
std::unordered_map< const reco::ClusterHit3D *, reco::EdgeList > Hit3DToEdgeMap
Definition: Cluster3D.h:328
void configure(const fhicl::ParameterSet &)
std::list< ClusterParameters > ClusterParametersList
Definition: Cluster3D.h:381