LArSoft  v07_13_02
Liquid Argon Software toolkit - http://larsoft.org/
TransverseAssociationAlgorithm.cc
Go to the documentation of this file.
1 
9 #include "Pandora/AlgorithmHeaders.h"
10 
12 
14 
16 
17 using namespace pandora;
18 
19 namespace lar_content
20 {
21 
22 TransverseAssociationAlgorithm::TransverseAssociationAlgorithm() :
23  m_firstLengthCut(1.5f),
24  m_secondLengthCut(7.5f),
25  m_clusterWindow(3.f),
26  m_clusterAngle(45.f),
27  m_clusterCosAngle(std::cos(m_clusterAngle * M_PI / 180.f)),
28  m_clusterTanAngle(std::tan(m_clusterAngle * M_PI / 180.f)),
29  m_maxTransverseOverlap(0.5f),
30  m_maxProjectedOverlap(1.f),
31  m_maxLongitudinalOverlap(1.5f),
32  m_transverseClusterMinCosTheta(0.866f),
33  m_transverseClusterMinLength(0.5f),
34  m_transverseClusterMaxDisplacement(1.5f),
35  m_searchRegionX(3.5f),
36  m_searchRegionZ(2.f)
37 {
38 }
39 
40 //------------------------------------------------------------------------------------------------------------------------------------------
41 
42 void TransverseAssociationAlgorithm::GetListOfCleanClusters(const ClusterList *const pClusterList, ClusterVector &clusterVector) const
43 {
44  clusterVector.clear();
45  clusterVector.insert(clusterVector.begin(), pClusterList->begin(), pClusterList->end());
46  std::sort(clusterVector.begin(), clusterVector.end(), LArClusterHelper::SortByNHits);
47 }
48 
49 //------------------------------------------------------------------------------------------------------------------------------------------
50 
52 {
53  TransverseClusterList transverseClusterList;
54 
55  try
56  {
57  ClusterToClustersMap nearbyClusters;
58  this->GetNearbyClusterMap(allClusters, nearbyClusters);
59 
60  if (nearbyClusters.empty())
61  return;
62 
63  // Step 1: Sort the input clusters into sub-samples
64  // (a) shortClusters: below first length cut
65  // (b) mediumClusters: between first and second length cuts (separated into transverse and longitudinal)
66  // (c) longClusters: above second length cut
67  ClusterVector shortClusters, transverseMediumClusters, longitudinalMediumClusters, longClusters;
68  this->SortInputClusters(allClusters, shortClusters, transverseMediumClusters, longitudinalMediumClusters, longClusters);
69 
70  ClusterVector transverseClusters(shortClusters.begin(), shortClusters.end());
71  transverseClusters.insert(transverseClusters.end(), transverseMediumClusters.begin(), transverseMediumClusters.end());
72 
73  ClusterVector establishedClusters(transverseMediumClusters.begin(), transverseMediumClusters.end());
74  establishedClusters.insert(establishedClusters.end(), longitudinalMediumClusters.begin(), longitudinalMediumClusters.end());
75  establishedClusters.insert(establishedClusters.end(), longClusters.begin(), longClusters.end());
76 
77  // Step 2: Form loose transverse associations between short clusters,
78  // without hopping over any established clusters
79  ClusterAssociationMap firstAssociationMap;
80  this->FillReducedAssociationMap(nearbyClusters, shortClusters, establishedClusters, firstAssociationMap);
81 
82  // Step 3: Form transverse cluster objects. Basically, try to assign a direction to each
83  // of the clusters in the 'transverseClusters' list. For the short clusters in
84  // this list, the direction is obtained from a straight line fit to its associated
85  // clusters as selected in the previous step.
86  this->FillTransverseClusterList(nearbyClusters, transverseClusters, firstAssociationMap, transverseClusterList);
87 
88  // Step 4: Form loose transverse associations between transverse clusters
89  // (First, associate medium clusters, without hopping over long clusters
90  // Next, associate all transverse clusters, without hopping over any clusters)
91  ClusterAssociationMap secondAssociationMap;
92  this->FillReducedAssociationMap(nearbyClusters, transverseMediumClusters, longClusters, secondAssociationMap);
93  this->FillReducedAssociationMap(nearbyClusters, transverseClusters, allClusters, secondAssociationMap);
94 
95  // Step 5: Form associations between transverse cluster objects
96  // (These transverse associations must already exist as loose associations
97  // between transverse clusters as identified in the previous step).
98  ClusterAssociationMap transverseAssociationMap;
99  this->FillTransverseAssociationMap(nearbyClusters, transverseClusterList, secondAssociationMap, transverseAssociationMap);
100 
101  // Step 6: Finalise the forward/backward transverse associations by symmetrising the
102  // transverse association map and removing any double-counting
103  this->FinalizeClusterAssociationMap(transverseAssociationMap, clusterAssociationMap);
104  }
105  catch (StatusCodeException &statusCodeException)
106  {
107  std::cout << "TransverseAssociationAlgorithm: exception " << statusCodeException.ToString() << std::endl;
108  }
109 
110  for (TransverseClusterList::const_iterator iter = transverseClusterList.begin(), iterEnd = transverseClusterList.end(); iter != iterEnd; ++iter)
111  {
112  delete *iter;
113  }
114 }
115 
116 //------------------------------------------------------------------------------------------------------------------------------------------
117 
119 {
120  HitToClusterMap hitToClusterMap;
121  CaloHitList allCaloHits;
122 
123  for (const Cluster *const pCluster : allClusters)
124  {
125  CaloHitList daughterHits;
126  pCluster->GetOrderedCaloHitList().FillCaloHitList(daughterHits);
127  allCaloHits.insert(allCaloHits.end(), daughterHits.begin(), daughterHits.end());
128 
129  for (const CaloHit *const pCaloHit : daughterHits)
130  (void) hitToClusterMap.insert(HitToClusterMap::value_type(pCaloHit, pCluster));
131  }
132 
133  HitKDTree2D kdTree;
134  HitKDNode2DList hitKDNode2DList;
135 
136  KDTreeBox hitsBoundingRegion2D(fill_and_bound_2d_kd_tree(allCaloHits, hitKDNode2DList));
137  kdTree.build(hitKDNode2DList, hitsBoundingRegion2D);
138 
139  for (const Cluster *const pCluster : allClusters)
140  {
141  CaloHitList daughterHits;
142  pCluster->GetOrderedCaloHitList().FillCaloHitList(daughterHits);
143 
144  for (const CaloHit *const pCaloHit : daughterHits)
145  {
147 
148  HitKDNode2DList found;
149  kdTree.search(searchRegionHits, found);
150 
151  for (const auto &hit : found)
152  (void) nearbyClusters[pCluster].insert(hitToClusterMap.at(hit.data));
153  }
154  }
155 }
156 
157 //------------------------------------------------------------------------------------------------------------------------------------------
158 
159 void TransverseAssociationAlgorithm::SortInputClusters(const ClusterVector &inputVector, ClusterVector &shortVector, ClusterVector &transverseMediumVector, ClusterVector &longitudinalMediumVector, ClusterVector &longVector) const
160 {
161  for (ClusterVector::const_iterator iter = inputVector.begin(), iterEnd = inputVector.end(); iter != iterEnd; ++iter)
162  {
163  const Cluster *const pCluster = *iter;
164 
165  const float clusterLengthT(this->GetTransverseSpan(pCluster));
166  const float clusterLengthL(this->GetLongitudinalSpan(pCluster));
167  const float clusterLengthSquared(clusterLengthT * clusterLengthT + clusterLengthL * clusterLengthL);
168 
169  if (clusterLengthSquared < m_firstLengthCut * m_firstLengthCut)
170  {
171  shortVector.push_back(pCluster);
172  }
173  else if (clusterLengthSquared < m_secondLengthCut * m_secondLengthCut)
174  {
175  if (clusterLengthL < clusterLengthT * std::fabs(m_clusterTanAngle))
176  transverseMediumVector.push_back(pCluster);
177  else
178  longitudinalMediumVector.push_back(pCluster);
179  }
180  else
181  {
182  longVector.push_back(pCluster);
183  }
184  }
185 
186  std::sort(shortVector.begin(), shortVector.end(), LArClusterHelper::SortByNHits);
187  std::sort(transverseMediumVector.begin(), transverseMediumVector.end(), LArClusterHelper::SortByNHits);
188  std::sort(longitudinalMediumVector.begin(), longitudinalMediumVector.end(), LArClusterHelper::SortByNHits);
189  std::sort(longVector.begin(), longVector.end(), LArClusterHelper::SortByNHits);
190 }
191 
192 //------------------------------------------------------------------------------------------------------------------------------------------
193 
195  const ClusterVector &secondVector, ClusterAssociationMap &clusterAssociationMap) const
196 {
197  // To build a 'reduced' association map, form associations between clusters in the first cluster vector,
198  // but prevent these associations from hopping over any clusters in the second cluster vector.
199  // i.e. A->B from the first vector is forbidden if A->C->B exists with C from the second vector
200 
201  ClusterAssociationMap firstAssociationMap, firstAssociationMapSwapped;
202  ClusterAssociationMap secondAssociationMap, secondAssociationMapSwapped;
203 
204  this->FillAssociationMap(nearbyClusters, firstVector, firstVector, firstAssociationMap, firstAssociationMapSwapped);
205  this->FillAssociationMap(nearbyClusters, firstVector, secondVector, secondAssociationMap, secondAssociationMapSwapped);
206  this->FillReducedAssociationMap(firstAssociationMap, secondAssociationMap, secondAssociationMapSwapped, clusterAssociationMap);
207 }
208 
209 //------------------------------------------------------------------------------------------------------------------------------------------
210 
212  const ClusterVector &secondVector, ClusterAssociationMap &firstAssociationMap, ClusterAssociationMap &secondAssociationMap) const
213 {
214  for (ClusterVector::const_iterator iterI = firstVector.begin(), iterEndI = firstVector.end(); iterI != iterEndI; ++iterI)
215  {
216  const Cluster *const pClusterI = *iterI;
217 
218  for (ClusterVector::const_iterator iterJ = secondVector.begin(), iterEndJ = secondVector.end(); iterJ != iterEndJ; ++iterJ)
219  {
220  const Cluster *const pClusterJ = *iterJ;
221 
222  if (pClusterI == pClusterJ)
223  continue;
224 
225  if (this->IsAssociated(true, pClusterI, pClusterJ, nearbyClusters))
226  {
227  firstAssociationMap[pClusterI].m_forwardAssociations.insert(pClusterJ);
228  secondAssociationMap[pClusterJ].m_backwardAssociations.insert(pClusterI);
229  }
230 
231  if (this->IsAssociated(false, pClusterI, pClusterJ, nearbyClusters))
232  {
233  firstAssociationMap[pClusterI].m_backwardAssociations.insert(pClusterJ);
234  secondAssociationMap[pClusterJ].m_forwardAssociations.insert(pClusterI);
235  }
236  }
237  }
238 }
239 
240 //------------------------------------------------------------------------------------------------------------------------------------------
241 
243  const ClusterAssociationMap &inputAssociationMap, TransverseClusterList &transverseClusterList) const
244 {
245  for (ClusterVector::const_iterator iter = inputVector.begin(), iterEnd = inputVector.end(); iter != iterEnd; ++iter)
246  {
247  const Cluster *const pCluster = *iter;
248  ClusterVector associatedClusters;
249 
250  this->GetAssociatedClusters(nearbyClusters, pCluster, inputAssociationMap, associatedClusters);
251 
252  if (this->GetTransverseSpan(pCluster, associatedClusters) < m_transverseClusterMinLength)
253  continue;
254 
255  transverseClusterList.push_back(new LArTransverseCluster(pCluster, associatedClusters));
256  }
257 }
258 
259 //------------------------------------------------------------------------------------------------------------------------------------------
260 
262  const ClusterAssociationMap &transverseAssociationMap, ClusterAssociationMap &clusterAssociationMap) const
263 {
264  for (TransverseClusterList::const_iterator iter1 = transverseClusterList.begin(), iterEnd1 = transverseClusterList.end(); iter1 != iterEnd1; ++iter1)
265  {
266  LArTransverseCluster *const pInnerTransverseCluster = *iter1;
267  const Cluster *const pInnerCluster(pInnerTransverseCluster->GetSeedCluster());
268 
269  ClusterAssociationMap::const_iterator iterInner = transverseAssociationMap.find(pInnerCluster);
270  if (transverseAssociationMap.end() == iterInner)
271  continue;
272 
273  for (TransverseClusterList::const_iterator iter2 = transverseClusterList.begin(), iterEnd2 = transverseClusterList.end(); iter2 != iterEnd2; ++iter2)
274  {
275  LArTransverseCluster *const pOuterTransverseCluster = *iter2;
276  const Cluster *const pOuterCluster(pOuterTransverseCluster->GetSeedCluster());
277 
278  ClusterAssociationMap::const_iterator iterOuter = transverseAssociationMap.find(pOuterCluster);
279  if(transverseAssociationMap.end() == iterOuter)
280  continue;
281 
282  if (pInnerCluster == pOuterCluster)
283  continue;
284 
285  if (iterInner->second.m_forwardAssociations.count(pOuterCluster) == 0 ||
286  iterOuter->second.m_backwardAssociations.count(pInnerCluster) == 0)
287  continue;
288 
289  if (!this->IsExtremalCluster(true, pInnerCluster, pOuterCluster) ||
290  !this->IsExtremalCluster(false, pOuterCluster, pInnerCluster))
291  continue;
292 
293  if (!this->IsTransverseAssociated(pInnerTransverseCluster, pOuterTransverseCluster, nearbyClusters))
294  continue;
295 
296  clusterAssociationMap[pInnerCluster].m_forwardAssociations.insert(pOuterCluster);
297  clusterAssociationMap[pOuterCluster].m_backwardAssociations.insert(pInnerCluster);
298  }
299  }
300 }
301 
302 //------------------------------------------------------------------------------------------------------------------------------------------
303 
304 void TransverseAssociationAlgorithm::GetAssociatedClusters(const ClusterToClustersMap &nearbyClusters, const Cluster *const pClusterI,
305  const ClusterAssociationMap &associationMap, ClusterVector &associatedVector) const
306 {
307  ClusterAssociationMap::const_iterator iterI = associationMap.find(pClusterI);
308  if (associationMap.end() == iterI)
309  return;
310 
311  for (ClusterSet::const_iterator iterJ = iterI->second.m_forwardAssociations.begin(), iterEndJ = iterI->second.m_forwardAssociations.end(); iterJ != iterEndJ; ++iterJ)
312  {
313  const Cluster *const pClusterJ = *iterJ;
314 
315  if (this->IsTransverseAssociated(pClusterI, pClusterJ, nearbyClusters))
316  associatedVector.push_back(pClusterJ);
317  }
318 
319  for (ClusterSet::const_iterator iterJ = iterI->second.m_backwardAssociations.begin(), iterEndJ = iterI->second.m_backwardAssociations.end(); iterJ != iterEndJ; ++iterJ)
320  {
321  const Cluster *const pClusterJ = *iterJ;
322 
323  if (this->IsTransverseAssociated(pClusterJ, pClusterI, nearbyClusters))
324  associatedVector.push_back(pClusterJ);
325  }
326 
327  std::sort(associatedVector.begin(), associatedVector.end(), LArClusterHelper::SortByNHits);
328 }
329 
330 //------------------------------------------------------------------------------------------------------------------------------------------
331 
332 bool TransverseAssociationAlgorithm::IsAssociated(const bool isForward, const Cluster *const pFirstCluster, const Cluster *const pSecondCluster,
333  const ClusterToClustersMap &nearbyClusters) const
334 {
335  if ((0 == nearbyClusters.at(pFirstCluster).count(pSecondCluster)) || (0 == nearbyClusters.at(pSecondCluster).count(pFirstCluster)))
336  return false;
337 
338  CartesianVector firstInner(0.f,0.f,0.f), firstOuter(0.f,0.f,0.f);
339  CartesianVector secondInner(0.f,0.f,0.f), secondOuter(0.f,0.f,0.f);
340  this->GetExtremalCoordinatesX(pFirstCluster, firstInner, firstOuter);
341  this->GetExtremalCoordinatesX(pSecondCluster, secondInner, secondOuter);
342 
343  const CartesianVector firstCoordinate(isForward ? firstOuter : firstInner);
344  const CartesianVector secondCoordinate(isForward ? secondOuter : secondInner);
345 
346  if ((firstCoordinate.GetZ() > std::max(secondInner.GetZ(),secondOuter.GetZ()) + m_maxLongitudinalOverlap) ||
347  (firstCoordinate.GetZ() < std::min(secondInner.GetZ(),secondOuter.GetZ()) - m_maxLongitudinalOverlap))
348  return false;
349 
350  if ((isForward && secondCoordinate.GetX() < firstCoordinate.GetX()) ||
351  (!isForward && secondCoordinate.GetX() > firstCoordinate.GetX()))
352  return false;
353 
354  const CartesianVector firstProjection(LArClusterHelper::GetClosestPosition(firstCoordinate, pSecondCluster));
355 
356  if((isForward && firstProjection.GetX() < firstCoordinate.GetX() - m_maxTransverseOverlap) ||
357  (!isForward && firstProjection.GetX() > firstCoordinate.GetX() + m_maxTransverseOverlap))
358  return false;
359 
360  if((isForward && firstProjection.GetX() > firstCoordinate.GetX() + m_clusterWindow) ||
361  (!isForward && firstProjection.GetX() < firstCoordinate.GetX() - m_clusterWindow))
362  return false;
363 
364  return true;
365 }
366 
367 //------------------------------------------------------------------------------------------------------------------------------------------
368 
369 bool TransverseAssociationAlgorithm::IsTransverseAssociated(const Cluster *const pInnerCluster, const Cluster *const pOuterCluster,
370  const ClusterToClustersMap &nearbyClusters) const
371 {
372  if ((0 == nearbyClusters.at(pInnerCluster).count(pOuterCluster)) || (0 == nearbyClusters.at(pOuterCluster).count(pInnerCluster)))
373  return false;
374 
375  CartesianVector innerInner(0.f,0.f,0.f), innerOuter(0.f,0.f,0.f);
376  CartesianVector outerInner(0.f,0.f,0.f), outerOuter(0.f,0.f,0.f);
377  this->GetExtremalCoordinatesX(pInnerCluster, innerInner, innerOuter);
378  this->GetExtremalCoordinatesX(pOuterCluster, outerInner, outerOuter);
379 
380  const CartesianVector innerCentroid((innerInner + innerOuter) * 0.5);
381  const CartesianVector outerCentroid((outerInner + outerOuter) * 0.5);
382 
383  if ((std::fabs(innerCentroid.GetZ() - outerInner.GetZ()) > std::fabs(innerCentroid.GetX() - outerInner.GetX()) * std::fabs(m_clusterTanAngle)) &&
384  (std::fabs(innerCentroid.GetZ() - outerOuter.GetZ()) > std::fabs(innerCentroid.GetX() - outerOuter.GetX()) * std::fabs(m_clusterTanAngle)))
385  return false;
386 
387  if ((std::fabs(outerCentroid.GetZ() - innerInner.GetZ()) > std::fabs(outerCentroid.GetX() - innerInner.GetX()) * std::fabs(m_clusterTanAngle)) &&
388  (std::fabs(outerCentroid.GetZ() - innerOuter.GetZ()) > std::fabs(outerCentroid.GetX() - innerOuter.GetX()) * std::fabs(m_clusterTanAngle)))
389  return false;
390 
391  const CartesianVector innerProjection(LArClusterHelper::GetClosestPosition(outerInner, pInnerCluster));
392  const CartesianVector outerProjection(LArClusterHelper::GetClosestPosition(innerOuter, pOuterCluster));
393 
394  if (innerOuter.GetX() > innerProjection.GetX() + m_maxTransverseOverlap ||
395  outerInner.GetX() < outerProjection.GetX() - m_maxTransverseOverlap)
396  return false;
397 
398  return true;
399 }
400 
401 //------------------------------------------------------------------------------------------------------------------------------------------
402 
404  const LArTransverseCluster *const pOuterTransverseCluster, const ClusterToClustersMap &nearbyClusters) const
405 {
406  if (pInnerTransverseCluster->GetDirection().GetDotProduct(pOuterTransverseCluster->GetDirection()) < m_transverseClusterMinCosTheta)
407  return false;
408 
409  if (!this->IsTransverseAssociated(pInnerTransverseCluster, pOuterTransverseCluster->GetInnerVertex()))
410  return false;
411 
412  if (!this->IsTransverseAssociated(pOuterTransverseCluster, pInnerTransverseCluster->GetOuterVertex()))
413  return false;
414 
415  if (!this->IsTransverseAssociated(pInnerTransverseCluster->GetSeedCluster(), pOuterTransverseCluster->GetSeedCluster(), nearbyClusters))
416  return false;
417 
418  if (this->IsOverlapping(pInnerTransverseCluster->GetSeedCluster(), pOuterTransverseCluster->GetSeedCluster()))
419  return false;
420 
421  return true;
422 }
423 
424 //------------------------------------------------------------------------------------------------------------------------------------------
425 
426 bool TransverseAssociationAlgorithm::IsTransverseAssociated(const LArTransverseCluster *const pTransverseCluster, const CartesianVector &testVertex) const
427 {
428  const CartesianVector &innerVertex(pTransverseCluster->GetInnerVertex());
429  const CartesianVector &outerVertex(pTransverseCluster->GetOuterVertex());
430  const CartesianVector &direction(pTransverseCluster->GetDirection());
431 
432  if (direction.GetCrossProduct(testVertex - innerVertex).GetMagnitudeSquared() > m_transverseClusterMaxDisplacement * m_transverseClusterMaxDisplacement)
433  return false;
434 
435  if ((direction.GetDotProduct(testVertex - innerVertex) < -1.f * m_clusterWindow) || (direction.GetDotProduct(testVertex - outerVertex) > +1.f * m_clusterWindow))
436  return false;
437 
438  return true;
439 }
440 
441 //------------------------------------------------------------------------------------------------------------------------------------------
442 
443 bool TransverseAssociationAlgorithm::IsOverlapping(const Cluster *const pInnerCluster, const Cluster *const pOuterCluster) const
444 {
445  CartesianVector innerInner(0.f,0.f,0.f), innerOuter(0.f,0.f,0.f);
446  CartesianVector outerInner(0.f,0.f,0.f), outerOuter(0.f,0.f,0.f);
447  this->GetExtremalCoordinatesX(pInnerCluster, innerInner, innerOuter);
448  this->GetExtremalCoordinatesX(pOuterCluster, outerInner, outerOuter);
449 
450  const CartesianVector innerProjection(LArClusterHelper::GetClosestPosition(outerInner, pInnerCluster));
451  const CartesianVector outerProjection(LArClusterHelper::GetClosestPosition(innerOuter, pOuterCluster));
452 
453  const float innerOverlapSquared((innerProjection - innerOuter).GetMagnitudeSquared());
454  const float outerOverlapSquared((outerProjection - outerInner).GetMagnitudeSquared());
455 
456  return (std::max(innerOverlapSquared, outerOverlapSquared) > m_maxProjectedOverlap * m_maxProjectedOverlap);
457 }
458 
459 //------------------------------------------------------------------------------------------------------------------------------------------
460 
461 float TransverseAssociationAlgorithm::GetTransverseSpan(const Cluster *const pCluster) const
462 {
463  float minX(+std::numeric_limits<float>::max());
464  float maxX(-std::numeric_limits<float>::max());
465 
466  this->GetExtremalCoordinatesX(pCluster, minX, maxX);
467 
468  return (maxX - minX);
469 }
470 
471 //------------------------------------------------------------------------------------------------------------------------------------------
472 
473 float TransverseAssociationAlgorithm::GetLongitudinalSpan(const Cluster *const pCluster) const
474 {
475  float minZ(+std::numeric_limits<float>::max());
476  float maxZ(-std::numeric_limits<float>::max());
477 
478  this->GetExtremalCoordinatesZ(pCluster, minZ, maxZ);
479 
480  return (maxZ - minZ);
481 }
482 
483 //------------------------------------------------------------------------------------------------------------------------------------------
484 
485 float TransverseAssociationAlgorithm::GetTransverseSpan(const Cluster *const pCentralCluster, const ClusterVector &associatedClusters) const
486 {
487  float overallMinX(+std::numeric_limits<float>::max());
488  float overallMaxX(-std::numeric_limits<float>::max());
489 
490  this->GetExtremalCoordinatesX(pCentralCluster, overallMinX, overallMaxX);
491 
492  float localMinX(+std::numeric_limits<float>::max());
493  float localMaxX(-std::numeric_limits<float>::max());
494 
495  for (ClusterVector::const_iterator iter = associatedClusters.begin(), iterEnd = associatedClusters.end(); iter != iterEnd; ++iter)
496  {
497  const Cluster *const pAssociatedCluster = *iter;
498 
499  this->GetExtremalCoordinatesX(pAssociatedCluster, localMinX, localMaxX);
500 
501  if (localMinX < overallMinX)
502  overallMinX = localMinX;
503 
504  if (localMaxX > overallMaxX)
505  overallMaxX = localMaxX;
506  }
507 
508  return (overallMaxX - overallMinX);
509 }
510 
511 //------------------------------------------------------------------------------------------------------------------------------------------
512 
513 bool TransverseAssociationAlgorithm::IsExtremalCluster(const bool isForward, const Cluster *const pCurrentCluster, const Cluster *const pTestCluster) const
514 {
515  float currentMinX(0.f), currentMaxX(0.f);
516  this->GetExtremalCoordinatesX(pCurrentCluster, currentMinX, currentMaxX);
517 
518  float testMinX(0.f), testMaxX(0.f);
519  this->GetExtremalCoordinatesX(pTestCluster, testMinX, testMaxX);
520 
521  if (isForward)
522  {
523  if (std::fabs(testMaxX - currentMaxX) > std::numeric_limits<float>::epsilon())
524  return (testMaxX > currentMaxX);
525  }
526  else
527  {
528  if (std::fabs(testMinX - currentMaxX) > std::numeric_limits<float>::epsilon())
529  return (testMinX < currentMinX);
530  }
531 
532  return LArClusterHelper::SortByNHits(pTestCluster, pCurrentCluster);
533 }
534 
535 //------------------------------------------------------------------------------------------------------------------------------------------
536 
537 void TransverseAssociationAlgorithm::GetExtremalCoordinatesX(const Cluster *const pCluster, float &minX, float &maxX) const
538 {
539  return this->GetExtremalCoordinatesXZ(pCluster, true, minX, maxX);
540 }
541 
542 //------------------------------------------------------------------------------------------------------------------------------------------
543 
544 void TransverseAssociationAlgorithm::GetExtremalCoordinatesZ(const Cluster *const pCluster, float &minZ, float &maxZ) const
545 {
546  return this->GetExtremalCoordinatesXZ(pCluster, false, minZ, maxZ);
547 }
548 
549 //------------------------------------------------------------------------------------------------------------------------------------------
550 
551 void TransverseAssociationAlgorithm::GetExtremalCoordinatesXZ(const Cluster *const pCluster, const bool useX, float &minXZ, float &maxXZ) const
552 {
555 
556  const OrderedCaloHitList &orderedCaloHitList(pCluster->GetOrderedCaloHitList());
557 
558  for (OrderedCaloHitList::const_iterator iter = orderedCaloHitList.begin(), iterEnd = orderedCaloHitList.end(); iter != iterEnd; ++iter)
559  {
560  for (CaloHitList::const_iterator hitIter = iter->second->begin(), hitIterEnd = iter->second->end(); hitIter != hitIterEnd; ++hitIter)
561  {
562  const float caloHitXZ(useX ? (*hitIter)->GetPositionVector().GetX() : (*hitIter)->GetPositionVector().GetZ());
563 
564  if (caloHitXZ < minXZ)
565  minXZ = caloHitXZ;
566 
567  if (caloHitXZ > maxXZ)
568  maxXZ = caloHitXZ;
569  }
570  }
571 
572  if (maxXZ < minXZ)
573  throw pandora::StatusCodeException(STATUS_CODE_FAILURE);
574 }
575 
576 //------------------------------------------------------------------------------------------------------------------------------------------
577 
578 void TransverseAssociationAlgorithm::GetExtremalCoordinatesX(const Cluster *const pCluster, CartesianVector &innerCoordinate, CartesianVector &outerCoordinate) const
579 {
580  CartesianVector firstCoordinate(0.f,0.f,0.f), secondCoordinate(0.f,0.f,0.f);
581  LArClusterHelper::GetExtremalCoordinates(pCluster, firstCoordinate, secondCoordinate);
582 
583  innerCoordinate = (firstCoordinate.GetX() < secondCoordinate.GetX() ? firstCoordinate : secondCoordinate);
584  outerCoordinate = (firstCoordinate.GetX() > secondCoordinate.GetX() ? firstCoordinate : secondCoordinate);
585 }
586 
587 //------------------------------------------------------------------------------------------------------------------------------------------
588 
590 {
591  return this->FillReducedAssociationMap(inputAssociationMap, inputAssociationMap, inputAssociationMap, outputAssociationMap);
592 }
593 
594 //------------------------------------------------------------------------------------------------------------------------------------------
595 
597  const ClusterAssociationMap &secondAssociationMapSwapped, ClusterAssociationMap &clusterAssociationMap) const
598 {
599  // Remove associations A->B from the first association map
600  // if A->C exists in the second map and C->B exists in the reversed second map
601 
602  // Method can also be accessed through FillReducedAssociationMap(input,output) method,
603  // which will remove association A->B from the input map if an association A->C and C->B
604  // already exists in the map.
605 
606  ClusterVector sortedClusters;
607  for (const auto &mapEntry : firstAssociationMap) sortedClusters.push_back(mapEntry.first);
608  std::sort(sortedClusters.begin(), sortedClusters.end(), LArClusterHelper::SortByNHits);
609 
610  for (const Cluster *const pCluster : sortedClusters)
611  {
612  const ClusterAssociation &firstAssociation(firstAssociationMap.at(pCluster));
613 
614  ClusterVector sortedOuterClusters(firstAssociation.m_forwardAssociations.begin(), firstAssociation.m_forwardAssociations.end());
615  std::sort(sortedOuterClusters.begin(), sortedOuterClusters.end(), LArClusterHelper::SortByNHits);
616 
617  ClusterVector sortedInnerClusters(firstAssociation.m_backwardAssociations.begin(), firstAssociation.m_backwardAssociations.end());
618  std::sort(sortedInnerClusters.begin(), sortedInnerClusters.end(), LArClusterHelper::SortByNHits);
619 
620  ClusterAssociationMap::const_iterator iterSecond = secondAssociationMap.find(pCluster);
621  ClusterVector sortedMiddleClustersF, sortedMiddleClustersB;
622 
623  if (secondAssociationMap.end() != iterSecond)
624  {
625  sortedMiddleClustersF.insert(sortedMiddleClustersF.end(), iterSecond->second.m_forwardAssociations.begin(), iterSecond->second.m_forwardAssociations.end());
626  sortedMiddleClustersB.insert(sortedMiddleClustersB.end(), iterSecond->second.m_backwardAssociations.begin(), iterSecond->second.m_backwardAssociations.end());
627  std::sort(sortedMiddleClustersF.begin(), sortedMiddleClustersF.end(), LArClusterHelper::SortByNHits);
628  std::sort(sortedMiddleClustersB.begin(), sortedMiddleClustersB.end(), LArClusterHelper::SortByNHits);
629  }
630 
631  for (const Cluster *const pOuterCluster : sortedOuterClusters)
632  {
633  bool isNeighbouringCluster(true);
634 
635  for (const Cluster *const pMiddleCluster : sortedMiddleClustersF)
636  {
637  ClusterAssociationMap::const_iterator iterSecondCheck = secondAssociationMapSwapped.find(pMiddleCluster);
638  if (secondAssociationMapSwapped.end() == iterSecondCheck)
639  continue;
640 
641  if (iterSecondCheck->second.m_forwardAssociations.count(pOuterCluster) > 0)
642  {
643  isNeighbouringCluster = false;
644  break;
645  }
646  }
647 
648  if (isNeighbouringCluster)
649  clusterAssociationMap[pCluster].m_forwardAssociations.insert(pOuterCluster);
650  }
651 
652  for (const Cluster *const pInnerCluster : sortedInnerClusters)
653  {
654  bool isNeighbouringCluster(true);
655 
656  for (const Cluster *const pMiddleCluster : sortedMiddleClustersB)
657  {
658  ClusterAssociationMap::const_iterator iterSecondCheck = secondAssociationMapSwapped.find(pMiddleCluster);
659  if (secondAssociationMapSwapped.end() == iterSecondCheck)
660  continue;
661 
662  if (iterSecondCheck->second.m_backwardAssociations.count(pInnerCluster) > 0)
663  {
664  isNeighbouringCluster = false;
665  break;
666  }
667  }
668 
669  if (isNeighbouringCluster)
670  clusterAssociationMap[pCluster].m_backwardAssociations.insert(pInnerCluster);
671  }
672  }
673 }
674 
675 //------------------------------------------------------------------------------------------------------------------------------------------
676 
678 {
679  // Generate a symmetrised association map, so that both A--Fwd-->B and B--Bwd-->A both exist.
680  // If A is associated to B through both a backward and forward association (very bad!),
681  // try to rationalise this through majority voting, otherwise remove the association.
682 
683  ClusterVector sortedClusters;
684  for (const auto &mapEntry : inputAssociationMap) sortedClusters.push_back(mapEntry.first);
685  std::sort(sortedClusters.begin(), sortedClusters.end(), LArClusterHelper::SortByNHits);
686 
687  for (const Cluster *const pCluster : sortedClusters)
688  {
689  const ClusterAssociation &inputAssociation(inputAssociationMap.at(pCluster));
690 
691  ClusterVector sortedForwardClusters(inputAssociation.m_forwardAssociations.begin(), inputAssociation.m_forwardAssociations.end());
692  std::sort(sortedForwardClusters.begin(), sortedForwardClusters.end(), LArClusterHelper::SortByNHits);
693 
694  ClusterVector sortedBackwardClusters(inputAssociation.m_backwardAssociations.begin(), inputAssociation.m_backwardAssociations.end());
695  std::sort(sortedBackwardClusters.begin(), sortedBackwardClusters.end(), LArClusterHelper::SortByNHits);
696 
697  // Symmetrise forward associations
698  for (const Cluster *const pForwardCluster : sortedForwardClusters)
699  {
700  int nCounter(+1);
701 
702  if (inputAssociation.m_backwardAssociations.count(pForwardCluster))
703  --nCounter;
704 
705  ClusterAssociationMap::const_iterator iterCheck = inputAssociationMap.find(pForwardCluster);
706  if (inputAssociationMap.end() != iterCheck)
707  {
708  if (iterCheck->second.m_forwardAssociations.count(pCluster))
709  --nCounter;
710 
711  if (iterCheck->second.m_backwardAssociations.count(pCluster))
712  ++nCounter;
713  }
714 
715  if (nCounter > 0)
716  {
717  if(!(outputAssociationMap[pCluster].m_backwardAssociations.count(pForwardCluster) == 0 &&
718  outputAssociationMap[pForwardCluster].m_forwardAssociations.count(pCluster) == 0))
719  throw StatusCodeException(STATUS_CODE_FAILURE);
720 
721  outputAssociationMap[pCluster].m_forwardAssociations.insert(pForwardCluster);
722  outputAssociationMap[pForwardCluster].m_backwardAssociations.insert(pCluster);
723  }
724  }
725 
726  // Symmetrise backward associations
727  for (const Cluster *const pBackwardCluster : sortedBackwardClusters)
728  {
729  int nCounter(-1);
730 
731  if (inputAssociation.m_forwardAssociations.count(pBackwardCluster))
732  ++nCounter;
733 
734  ClusterAssociationMap::const_iterator iterCheck = inputAssociationMap.find(pBackwardCluster);
735  if (inputAssociationMap.end() != iterCheck)
736  {
737  if (iterCheck->second.m_backwardAssociations.count(pCluster))
738  ++nCounter;
739 
740  if (iterCheck->second.m_forwardAssociations.count(pCluster))
741  --nCounter;
742  }
743 
744  if (nCounter < 0)
745  {
746  if(!(outputAssociationMap[pCluster].m_forwardAssociations.count(pBackwardCluster) == 0 &&
747  outputAssociationMap[pBackwardCluster].m_backwardAssociations.count(pCluster) == 0))
748  throw StatusCodeException(STATUS_CODE_FAILURE);
749 
750  outputAssociationMap[pCluster].m_backwardAssociations.insert(pBackwardCluster);
751  outputAssociationMap[pBackwardCluster].m_forwardAssociations.insert(pCluster);
752  }
753  }
754  }
755 }
756 
757 //------------------------------------------------------------------------------------------------------------------------------------------
758 
760 {
761  ClusterAssociationMap intermediateAssociationMap;
762  this->FillSymmetricAssociationMap(inputAssociationMap, intermediateAssociationMap);
763  this->FillReducedAssociationMap(intermediateAssociationMap, outputAssociationMap);
764 }
765 
766 //------------------------------------------------------------------------------------------------------------------------------------------
767 //------------------------------------------------------------------------------------------------------------------------------------------
768 
769 TransverseAssociationAlgorithm::LArTransverseCluster::LArTransverseCluster(const Cluster *const pSeedCluster, const ClusterVector &associatedClusters) :
770  m_pSeedCluster(pSeedCluster),
771  m_associatedClusters(associatedClusters),
772  m_innerVertex(0.f, 0.f, 0.f),
773  m_outerVertex(0.f, 0.f, 0.f),
774  m_direction(0.f, 0.f, 0.f)
775 {
776  double Swzz(0.), Swxx(0.), Swzx(0.), Swz(0.), Swx(0.), Sw(0.);
777  double minX(std::numeric_limits<double>::max());
778  double maxX(-std::numeric_limits<double>::max());
779 
780  ClusterList clusterList(1, pSeedCluster);
781  clusterList.insert(clusterList.end(), associatedClusters.begin(), associatedClusters.end());
782 
783  for (ClusterList::const_iterator iterI = clusterList.begin(), iterEndI = clusterList.end(); iterI != iterEndI; ++iterI)
784  {
785  for (OrderedCaloHitList::const_iterator iterJ = (*iterI)->GetOrderedCaloHitList().begin(), iterEndJ = (*iterI)->GetOrderedCaloHitList().end(); iterJ != iterEndJ; ++iterJ)
786  {
787  for (CaloHitList::const_iterator iterK = iterJ->second->begin(), iterEndK = iterJ->second->end(); iterK != iterEndK; ++iterK)
788  {
789  const CaloHit *const pCaloHit = *iterK;
790 
791  if (pCaloHit->GetPositionVector().GetX() < minX)
792  minX = pCaloHit->GetPositionVector().GetX();
793 
794  if (pCaloHit->GetPositionVector().GetX() > maxX)
795  maxX = pCaloHit->GetPositionVector().GetX();
796 
797  Swzz += pCaloHit->GetPositionVector().GetZ() * pCaloHit->GetPositionVector().GetZ();
798  Swxx += pCaloHit->GetPositionVector().GetX() * pCaloHit->GetPositionVector().GetX();
799  Swzx += pCaloHit->GetPositionVector().GetZ() * pCaloHit->GetPositionVector().GetX();
800  Swz += pCaloHit->GetPositionVector().GetZ();
801  Swx += pCaloHit->GetPositionVector().GetX();
802  Sw += 1.;
803  }
804  }
805  }
806 
807  if (Sw > 0.f)
808  {
809  const double averageX(Swx / Sw);
810  const double averageZ(Swz / Sw);
811 
812  if (Sw * Swxx - Swx * Swx > 0.)
813  {
814  double m((Sw * Swzx - Swx * Swz) / (Sw * Swxx - Swx * Swx));
815  double px(1. / std::sqrt(1. + m * m));
816  double pz(m / std::sqrt(1. + m * m));
817 
818  m_innerVertex.SetValues(static_cast<float>(minX), 0.f, static_cast<float>(averageZ + m * (minX - averageX)));
819  m_outerVertex.SetValues(static_cast<float>(maxX), 0.f, static_cast<float>(averageZ + m * (maxX - averageX)));
820  m_direction.SetValues(static_cast<float>(px), 0.f, static_cast<float>(pz));
821  }
822  else
823  {
824  m_innerVertex.SetValues(static_cast<float>(averageX), 0.f, static_cast<float>(averageZ));
825  m_outerVertex.SetValues(static_cast<float>(averageX), 0.f, static_cast<float>(averageZ));
826  m_direction.SetValues(1.f, 0.f, 0.f);
827  }
828  }
829  else
830  {
831  throw StatusCodeException(STATUS_CODE_NOT_INITIALIZED);
832  }
833 }
834 
835 //------------------------------------------------------------------------------------------------------------------------------------------
836 //------------------------------------------------------------------------------------------------------------------------------------------
837 
838 StatusCode TransverseAssociationAlgorithm::ReadSettings(const TiXmlHandle xmlHandle)
839 {
840  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
841  "FirstLengthCut", m_firstLengthCut));
842 
843  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
844  "SecondLengthCut", m_secondLengthCut));
845 
846  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
847  "ClusterWindow", m_clusterWindow));
848 
849  const StatusCode angleStatusCode(XmlHelper::ReadValue(xmlHandle, "clusterAngle", m_clusterAngle));
850 
851  if (STATUS_CODE_SUCCESS == angleStatusCode)
852  {
853  m_clusterCosAngle = std::cos(m_clusterAngle * M_PI / 180.f);
854  m_clusterTanAngle = std::tan(m_clusterAngle * M_PI / 180.f);
855  }
856  else if (STATUS_CODE_NOT_FOUND != angleStatusCode)
857  {
858  return angleStatusCode;
859  }
860 
861  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
862  "MaxTransverseOverlap", m_maxTransverseOverlap));
863 
864  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
865  "MaxProjectedOverlap", m_maxProjectedOverlap));
866 
867  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
868  "MaxLongitudinalOverlap", m_maxLongitudinalOverlap));
869 
870  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
871  "TransverseClusterMinCosTheta", m_transverseClusterMinCosTheta));
872 
873  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
874  "TransverseClusterMinLength", m_transverseClusterMinLength));
875 
876  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
877  "TransverseClusterMaxDisplacement", m_transverseClusterMaxDisplacement));
878 
880 }
881 
882 } // namespace lar_content
static bool SortByNHits(const pandora::Cluster *const pLhs, const pandora::Cluster *const pRhs)
Sort clusters by number of hits, then layer span, then inner layer, then position, then pulse-height.
void PopulateClusterAssociationMap(const pandora::ClusterVector &clusterVector, ClusterAssociationMap &clusterAssociationMap) const
Populate the cluster association map.
Header file for the kd tree linker algo template class.
const pandora::CartesianVector & GetInnerVertex() const
Get the inner vertex position.
float GetTransverseSpan(const pandora::Cluster *const pCluster) const
Calculate the overall span in X for a clusters.
Class that implements the KDTree partition of 2D space and a closest point search algorithm...
void FillTransverseAssociationMap(const ClusterToClustersMap &nearbyClusters, const TransverseClusterList &transverseClusterList, const ClusterAssociationMap &transverseAssociationMap, ClusterAssociationMap &clusterAssociationMap) const
Form associations between transverse cluster objects.
Box structure used to define 2D field. It&#39;s used in KDTree building step to divide the detector space...
virtual pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
void GetExtremalCoordinatesXZ(const pandora::Cluster *const pCluster, const bool useX, float &minXZ, float &maxXZ) const
Get minimum and maximum X or Z coordinates for a given cluster.
STL namespace.
bool IsExtremalCluster(const bool isForward, const pandora::Cluster *const pCurrentCluster, const pandora::Cluster *const pTestCluster) const
Determine which of two clusters is extremal.
void FillSymmetricAssociationMap(const ClusterAssociationMap &inputAssociationMap, ClusterAssociationMap &outputAssociationMap) const
Symmetrise an association map.
std::vector< LArTransverseCluster * > TransverseClusterList
bool IsTransverseAssociated(const pandora::Cluster *const pCluster1, const pandora::Cluster *const pCluster2, const ClusterToClustersMap &nearbyClusters) const
Determine whether two clusters are within the same cluster window.
void FillAssociationMap(const ClusterToClustersMap &nearbyClusters, const pandora::ClusterVector &firstVector, const pandora::ClusterVector &secondVector, ClusterAssociationMap &firstAssociationMap, ClusterAssociationMap &secondAssociationMap) const
Form associations between two input lists of cluster.
TFile f
Definition: plotHisto.C:6
bool IsOverlapping(const pandora::Cluster *const pCluster1, const pandora::Cluster *const pCluster2) const
Determine whether two clusters are overlapping.
Int_t max
Definition: plot.C:27
void search(const KDTreeBoxT< DIM > &searchBox, std::vector< KDTreeNodeInfoT< DATA, DIM > > &resRecHitList)
Search in the KDTree for all points that would be contained in the given searchbox The founded points...
intermediate_table::const_iterator const_iterator
void GetExtremalCoordinatesX(const pandora::Cluster *const pCluster, float &minX, float &maxX) const
Get minimum and maximum X coordinates for a given cluster.
Header file for the cluster helper class.
float m_searchRegionX
Search region, applied to x dimension, for look-up from kd-trees.
void SortInputClusters(const pandora::ClusterVector &inputClusters, pandora::ClusterVector &shortClusters, pandora::ClusterVector &transverseMediumClusters, pandora::ClusterVector &longitudinalMediumClusters, pandora::ClusterVector &longClusters) const
Separate input clusters by length.
void FillTransverseClusterList(const ClusterToClustersMap &nearbyClusters, const pandora::ClusterVector &inputClusters, const ClusterAssociationMap &inputAssociationMap, TransverseClusterList &transverseClusterList) const
Create transverse cluster objects, these are protoclusters with a direction and inner/outer vertices...
const pandora::CartesianVector & GetOuterVertex() const
Get the outer vertex position.
std::unordered_map< const pandora::Cluster *, pandora::ClusterSet > ClusterToClustersMap
Detector simulation of raw signals on wires.
std::vector< art::Ptr< recob::Cluster > > ClusterVector
static void GetExtremalCoordinates(const pandora::ClusterList &clusterList, pandora::CartesianVector &innerCoordinate, pandora::CartesianVector &outerCoordinate)
Get positions of the two most distant calo hits in a list of cluster (ordered by Z) ...
void FinalizeClusterAssociationMap(const ClusterAssociationMap &inputAssociationMap, ClusterAssociationMap &outputAssociationMap) const
Symmetrise and then remove double-counting from an association map.
Int_t min
Definition: plot.C:26
pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
void GetListOfCleanClusters(const pandora::ClusterList *const pClusterList, pandora::ClusterVector &clusterVector) const
Populate cluster vector with subset of cluster list, containing clusters judged to be clean...
void GetNearbyClusterMap(const pandora::ClusterVector &allClusters, ClusterToClustersMap &nearbyClusters) const
Use a kd-tree to obtain details of all nearby cluster combinations.
const pandora::CartesianVector & GetDirection() const
Get the direction.
KDTreeBox fill_and_bound_2d_kd_tree(const MANAGED_CONTAINER< const T * > &points, std::vector< KDTreeNodeInfoT< const T *, 2 > > &nodes)
fill_and_bound_2d_kd_tree
std::unordered_map< const pandora::CaloHit *, const pandora::Cluster * > HitToClusterMap
bool IsAssociated(const bool isForward, const pandora::Cluster *const pCluster1, const pandora::Cluster *const pCluster2, const ClusterToClustersMap &nearbyClusters) const
Determine whether clusters are association.
void FillReducedAssociationMap(const ClusterToClustersMap &nearbyClusters, const pandora::ClusterVector &firstVector, const pandora::ClusterVector &secondVector, ClusterAssociationMap &clusterAssociationMap) const
Form a reduced set of associations between two input lists of clusters.
float GetLongitudinalSpan(const pandora::Cluster *const pCluster) const
Calculate the overall span in Z for a clusters.
Header file for the transverse association algorithm class.
std::unordered_map< const pandora::Cluster *, ClusterAssociation > ClusterAssociationMap
void GetAssociatedClusters(const ClusterToClustersMap &nearbyClusters, const pandora::Cluster *const pCluster, const ClusterAssociationMap &inputAssociationMap, pandora::ClusterVector &associatedClusters) const
Find the clusters that are transversely associated with a target cluster.
static pandora::CartesianVector GetClosestPosition(const pandora::CartesianVector &position, const pandora::ClusterList &clusterList)
Get closest position in a list of clusters to a specified input position vector.
float m_searchRegionZ
Search region, applied to u/v/w dimension, for look-up from kd-trees.
KDTreeBox build_2d_kd_search_region(const pandora::CaloHit *const point, const float x_span, const float z_span)
build_2d_kd_search_region
void GetExtremalCoordinatesZ(const pandora::Cluster *const pCluster, float &minZ, float &maxZ) const
Get minimum and maximum Z coordinates for a given cluster.
void build(std::vector< KDTreeNodeInfoT< DATA, DIM > > &eltList, const KDTreeBoxT< DIM > &region)
Build the KD tree from the "eltList" in the space define by "region".
LArTransverseCluster(const pandora::Cluster *const pSeedCluster, const pandora::ClusterVector &associatedClusters)
Constructor.