TransverseAssociationAlgorithm.cc
Go to the documentation of this file.
1 /**
2  * @file larpandoracontent/LArTwoDReco/LArClusterAssociation/TransverseAssociationAlgorithm.cc
3  *
4  * @brief Implementation of the transverse association algorithm class.
5  *
6  * $Log: $
7  */
8 
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 
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 
160  ClusterVector &transverseMediumVector, ClusterVector &longitudinalMediumVector, ClusterVector &longVector) const
161 {
162  for (ClusterVector::const_iterator iter = inputVector.begin(), iterEnd = inputVector.end(); iter != iterEnd; ++iter)
163  {
164  const Cluster *const pCluster = *iter;
165 
166  const float clusterLengthT(this->GetTransverseSpan(pCluster));
167  const float clusterLengthL(this->GetLongitudinalSpan(pCluster));
168  const float clusterLengthSquared(clusterLengthT * clusterLengthT + clusterLengthL * clusterLengthL);
169 
170  if (clusterLengthSquared < m_firstLengthCut * m_firstLengthCut)
171  {
172  shortVector.push_back(pCluster);
173  }
174  else if (clusterLengthSquared < m_secondLengthCut * m_secondLengthCut)
175  {
176  if (clusterLengthL < clusterLengthT * std::fabs(m_clusterTanAngle))
177  transverseMediumVector.push_back(pCluster);
178  else
179  longitudinalMediumVector.push_back(pCluster);
180  }
181  else
182  {
183  longVector.push_back(pCluster);
184  }
185  }
186 
187  std::sort(shortVector.begin(), shortVector.end(), LArClusterHelper::SortByNHits);
188  std::sort(transverseMediumVector.begin(), transverseMediumVector.end(), LArClusterHelper::SortByNHits);
189  std::sort(longitudinalMediumVector.begin(), longitudinalMediumVector.end(), LArClusterHelper::SortByNHits);
190  std::sort(longVector.begin(), longVector.end(), LArClusterHelper::SortByNHits);
191 }
192 
193 //------------------------------------------------------------------------------------------------------------------------------------------
194 
196  const ClusterVector &secondVector, ClusterAssociationMap &clusterAssociationMap) const
197 {
198  // To build a 'reduced' association map, form associations between clusters in the first cluster vector,
199  // but prevent these associations from hopping over any clusters in the second cluster vector.
200  // i.e. A->B from the first vector is forbidden if A->C->B exists with C from the second vector
201 
202  ClusterAssociationMap firstAssociationMap, firstAssociationMapSwapped;
203  ClusterAssociationMap secondAssociationMap, secondAssociationMapSwapped;
204 
205  this->FillAssociationMap(nearbyClusters, firstVector, firstVector, firstAssociationMap, firstAssociationMapSwapped);
206  this->FillAssociationMap(nearbyClusters, firstVector, secondVector, secondAssociationMap, secondAssociationMapSwapped);
207  this->FillReducedAssociationMap(firstAssociationMap, secondAssociationMap, secondAssociationMapSwapped, clusterAssociationMap);
208 }
209 
210 //------------------------------------------------------------------------------------------------------------------------------------------
211 
213  const ClusterVector &secondVector, ClusterAssociationMap &firstAssociationMap, ClusterAssociationMap &secondAssociationMap) const
214 {
215  for (ClusterVector::const_iterator iterI = firstVector.begin(), iterEndI = firstVector.end(); iterI != iterEndI; ++iterI)
216  {
217  const Cluster *const pClusterI = *iterI;
218 
219  for (ClusterVector::const_iterator iterJ = secondVector.begin(), iterEndJ = secondVector.end(); iterJ != iterEndJ; ++iterJ)
220  {
221  const Cluster *const pClusterJ = *iterJ;
222 
223  if (pClusterI == pClusterJ)
224  continue;
225 
226  if (this->IsAssociated(true, pClusterI, pClusterJ, nearbyClusters))
227  {
228  firstAssociationMap[pClusterI].m_forwardAssociations.insert(pClusterJ);
229  secondAssociationMap[pClusterJ].m_backwardAssociations.insert(pClusterI);
230  }
231 
232  if (this->IsAssociated(false, pClusterI, pClusterJ, nearbyClusters))
233  {
234  firstAssociationMap[pClusterI].m_backwardAssociations.insert(pClusterJ);
235  secondAssociationMap[pClusterJ].m_forwardAssociations.insert(pClusterI);
236  }
237  }
238  }
239 }
240 
241 //------------------------------------------------------------------------------------------------------------------------------------------
242 
244  const ClusterAssociationMap &inputAssociationMap, TransverseClusterList &transverseClusterList) const
245 {
246  for (ClusterVector::const_iterator iter = inputVector.begin(), iterEnd = inputVector.end(); iter != iterEnd; ++iter)
247  {
248  const Cluster *const pCluster = *iter;
249  ClusterVector associatedClusters;
250 
251  this->GetAssociatedClusters(nearbyClusters, pCluster, inputAssociationMap, associatedClusters);
252 
253  if (this->GetTransverseSpan(pCluster, associatedClusters) < m_transverseClusterMinLength)
254  continue;
255 
256  transverseClusterList.push_back(new LArTransverseCluster(pCluster, associatedClusters));
257  }
258 }
259 
260 //------------------------------------------------------------------------------------------------------------------------------------------
261 
263  const TransverseClusterList &transverseClusterList, const ClusterAssociationMap &transverseAssociationMap,
264  ClusterAssociationMap &clusterAssociationMap) const
265 {
266  for (TransverseClusterList::const_iterator iter1 = transverseClusterList.begin(), iterEnd1 = transverseClusterList.end(); iter1 != iterEnd1; ++iter1)
267  {
268  LArTransverseCluster *const pInnerTransverseCluster = *iter1;
269  const Cluster *const pInnerCluster(pInnerTransverseCluster->GetSeedCluster());
270 
271  ClusterAssociationMap::const_iterator iterInner = transverseAssociationMap.find(pInnerCluster);
272  if (transverseAssociationMap.end() == iterInner)
273  continue;
274 
275  for (TransverseClusterList::const_iterator iter2 = transverseClusterList.begin(), iterEnd2 = transverseClusterList.end();
276  iter2 != iterEnd2; ++iter2)
277  {
278  LArTransverseCluster *const pOuterTransverseCluster = *iter2;
279  const Cluster *const pOuterCluster(pOuterTransverseCluster->GetSeedCluster());
280 
281  ClusterAssociationMap::const_iterator iterOuter = transverseAssociationMap.find(pOuterCluster);
282  if (transverseAssociationMap.end() == iterOuter)
283  continue;
284 
285  if (pInnerCluster == pOuterCluster)
286  continue;
287 
288  if (iterInner->second.m_forwardAssociations.count(pOuterCluster) == 0 || iterOuter->second.m_backwardAssociations.count(pInnerCluster) == 0)
289  continue;
290 
291  if (!this->IsExtremalCluster(true, pInnerCluster, pOuterCluster) || !this->IsExtremalCluster(false, pOuterCluster, pInnerCluster))
292  continue;
293 
294  if (!this->IsTransverseAssociated(pInnerTransverseCluster, pOuterTransverseCluster, nearbyClusters))
295  continue;
296 
297  clusterAssociationMap[pInnerCluster].m_forwardAssociations.insert(pOuterCluster);
298  clusterAssociationMap[pOuterCluster].m_backwardAssociations.insert(pInnerCluster);
299  }
300  }
301 }
302 
303 //------------------------------------------------------------------------------------------------------------------------------------------
304 
305 void TransverseAssociationAlgorithm::GetAssociatedClusters(const ClusterToClustersMap &nearbyClusters, const Cluster *const pClusterI,
306  const ClusterAssociationMap &associationMap, ClusterVector &associatedVector) const
307 {
308  ClusterAssociationMap::const_iterator iterI = associationMap.find(pClusterI);
309  if (associationMap.end() == iterI)
310  return;
311 
312  for (ClusterSet::const_iterator iterJ = iterI->second.m_forwardAssociations.begin(), iterEndJ = iterI->second.m_forwardAssociations.end();
313  iterJ != iterEndJ; ++iterJ)
314  {
315  const Cluster *const pClusterJ = *iterJ;
316 
317  if (this->IsTransverseAssociated(pClusterI, pClusterJ, nearbyClusters))
318  associatedVector.push_back(pClusterJ);
319  }
320 
321  for (ClusterSet::const_iterator iterJ = iterI->second.m_backwardAssociations.begin(), iterEndJ = iterI->second.m_backwardAssociations.end();
322  iterJ != iterEndJ; ++iterJ)
323  {
324  const Cluster *const pClusterJ = *iterJ;
325 
326  if (this->IsTransverseAssociated(pClusterJ, pClusterI, nearbyClusters))
327  associatedVector.push_back(pClusterJ);
328  }
329 
330  std::sort(associatedVector.begin(), associatedVector.end(), LArClusterHelper::SortByNHits);
331 }
332 
333 //------------------------------------------------------------------------------------------------------------------------------------------
334 
335 bool TransverseAssociationAlgorithm::IsAssociated(const bool isForward, const Cluster *const pFirstCluster,
336  const Cluster *const pSecondCluster, const ClusterToClustersMap &nearbyClusters) const
337 {
338  if ((0 == nearbyClusters.at(pFirstCluster).count(pSecondCluster)) || (0 == nearbyClusters.at(pSecondCluster).count(pFirstCluster)))
339  return false;
340 
341  CartesianVector firstInner(0.f, 0.f, 0.f), firstOuter(0.f, 0.f, 0.f);
342  CartesianVector secondInner(0.f, 0.f, 0.f), secondOuter(0.f, 0.f, 0.f);
343  this->GetExtremalCoordinatesX(pFirstCluster, firstInner, firstOuter);
344  this->GetExtremalCoordinatesX(pSecondCluster, secondInner, secondOuter);
345 
346  const CartesianVector firstCoordinate(isForward ? firstOuter : firstInner);
347  const CartesianVector secondCoordinate(isForward ? secondOuter : secondInner);
348 
349  if ((firstCoordinate.GetZ() > std::max(secondInner.GetZ(), secondOuter.GetZ()) + m_maxLongitudinalOverlap) ||
350  (firstCoordinate.GetZ() < std::min(secondInner.GetZ(), secondOuter.GetZ()) - m_maxLongitudinalOverlap))
351  return false;
352 
353  if ((isForward && secondCoordinate.GetX() < firstCoordinate.GetX()) || (!isForward && secondCoordinate.GetX() > firstCoordinate.GetX()))
354  return false;
355 
356  const CartesianVector firstProjection(LArClusterHelper::GetClosestPosition(firstCoordinate, pSecondCluster));
357 
358  if ((isForward && firstProjection.GetX() < firstCoordinate.GetX() - m_maxTransverseOverlap) ||
359  (!isForward && firstProjection.GetX() > firstCoordinate.GetX() + m_maxTransverseOverlap))
360  return false;
361 
362  if ((isForward && firstProjection.GetX() > firstCoordinate.GetX() + m_clusterWindow) ||
363  (!isForward && firstProjection.GetX() < firstCoordinate.GetX() - m_clusterWindow))
364  return false;
365 
366  return true;
367 }
368 
369 //------------------------------------------------------------------------------------------------------------------------------------------
370 
372  const Cluster *const pInnerCluster, const Cluster *const pOuterCluster, const ClusterToClustersMap &nearbyClusters) const
373 {
374  if ((0 == nearbyClusters.at(pInnerCluster).count(pOuterCluster)) || (0 == nearbyClusters.at(pOuterCluster).count(pInnerCluster)))
375  return false;
376 
377  CartesianVector innerInner(0.f, 0.f, 0.f), innerOuter(0.f, 0.f, 0.f);
378  CartesianVector outerInner(0.f, 0.f, 0.f), outerOuter(0.f, 0.f, 0.f);
379  this->GetExtremalCoordinatesX(pInnerCluster, innerInner, innerOuter);
380  this->GetExtremalCoordinatesX(pOuterCluster, outerInner, outerOuter);
381 
382  const CartesianVector innerCentroid((innerInner + innerOuter) * 0.5);
383  const CartesianVector outerCentroid((outerInner + outerOuter) * 0.5);
384 
385  if ((std::fabs(innerCentroid.GetZ() - outerInner.GetZ()) > std::fabs(innerCentroid.GetX() - outerInner.GetX()) * std::fabs(m_clusterTanAngle)) &&
386  (std::fabs(innerCentroid.GetZ() - outerOuter.GetZ()) > std::fabs(innerCentroid.GetX() - outerOuter.GetX()) * std::fabs(m_clusterTanAngle)))
387  return false;
388 
389  if ((std::fabs(outerCentroid.GetZ() - innerInner.GetZ()) > std::fabs(outerCentroid.GetX() - innerInner.GetX()) * std::fabs(m_clusterTanAngle)) &&
390  (std::fabs(outerCentroid.GetZ() - innerOuter.GetZ()) > std::fabs(outerCentroid.GetX() - innerOuter.GetX()) * std::fabs(m_clusterTanAngle)))
391  return false;
392 
393  const CartesianVector innerProjection(LArClusterHelper::GetClosestPosition(outerInner, pInnerCluster));
394  const CartesianVector outerProjection(LArClusterHelper::GetClosestPosition(innerOuter, pOuterCluster));
395 
396  if (innerOuter.GetX() > innerProjection.GetX() + m_maxTransverseOverlap || outerInner.GetX() < outerProjection.GetX() - m_maxTransverseOverlap)
397  return false;
398 
399  return true;
400 }
401 
402 //------------------------------------------------------------------------------------------------------------------------------------------
403 
405  const LArTransverseCluster *const pOuterTransverseCluster, const ClusterToClustersMap &nearbyClusters) const
406 {
407  if (pInnerTransverseCluster->GetDirection().GetDotProduct(pOuterTransverseCluster->GetDirection()) < m_transverseClusterMinCosTheta)
408  return false;
409 
410  if (!this->IsTransverseAssociated(pInnerTransverseCluster, pOuterTransverseCluster->GetInnerVertex()))
411  return false;
412 
413  if (!this->IsTransverseAssociated(pOuterTransverseCluster, pInnerTransverseCluster->GetOuterVertex()))
414  return false;
415 
416  if (!this->IsTransverseAssociated(pInnerTransverseCluster->GetSeedCluster(), pOuterTransverseCluster->GetSeedCluster(), nearbyClusters))
417  return false;
418 
419  if (this->IsOverlapping(pInnerTransverseCluster->GetSeedCluster(), pOuterTransverseCluster->GetSeedCluster()))
420  return false;
421 
422  return true;
423 }
424 
425 //------------------------------------------------------------------------------------------------------------------------------------------
426 
427 bool TransverseAssociationAlgorithm::IsTransverseAssociated(const LArTransverseCluster *const pTransverseCluster, const CartesianVector &testVertex) const
428 {
429  const CartesianVector &innerVertex(pTransverseCluster->GetInnerVertex());
430  const CartesianVector &outerVertex(pTransverseCluster->GetOuterVertex());
431  const CartesianVector &direction(pTransverseCluster->GetDirection());
432 
433  if (direction.GetCrossProduct(testVertex - innerVertex).GetMagnitudeSquared() > m_transverseClusterMaxDisplacement * m_transverseClusterMaxDisplacement)
434  return false;
435 
436  if ((direction.GetDotProduct(testVertex - innerVertex) < -1.f * m_clusterWindow) ||
437  (direction.GetDotProduct(testVertex - outerVertex) > +1.f * m_clusterWindow))
438  return false;
439 
440  return true;
441 }
442 
443 //------------------------------------------------------------------------------------------------------------------------------------------
444 
445 bool TransverseAssociationAlgorithm::IsOverlapping(const Cluster *const pInnerCluster, const Cluster *const pOuterCluster) const
446 {
447  CartesianVector innerInner(0.f, 0.f, 0.f), innerOuter(0.f, 0.f, 0.f);
448  CartesianVector outerInner(0.f, 0.f, 0.f), outerOuter(0.f, 0.f, 0.f);
449  this->GetExtremalCoordinatesX(pInnerCluster, innerInner, innerOuter);
450  this->GetExtremalCoordinatesX(pOuterCluster, outerInner, outerOuter);
451 
452  const CartesianVector innerProjection(LArClusterHelper::GetClosestPosition(outerInner, pInnerCluster));
453  const CartesianVector outerProjection(LArClusterHelper::GetClosestPosition(innerOuter, pOuterCluster));
454 
455  const float innerOverlapSquared((innerProjection - innerOuter).GetMagnitudeSquared());
456  const float outerOverlapSquared((outerProjection - outerInner).GetMagnitudeSquared());
457 
458  return (std::max(innerOverlapSquared, outerOverlapSquared) > m_maxProjectedOverlap * m_maxProjectedOverlap);
459 }
460 
461 //------------------------------------------------------------------------------------------------------------------------------------------
462 
463 float TransverseAssociationAlgorithm::GetTransverseSpan(const Cluster *const pCluster) const
464 {
465  float minX(+std::numeric_limits<float>::max());
466  float maxX(-std::numeric_limits<float>::max());
467 
468  this->GetExtremalCoordinatesX(pCluster, minX, maxX);
469 
470  return (maxX - minX);
471 }
472 
473 //------------------------------------------------------------------------------------------------------------------------------------------
474 
475 float TransverseAssociationAlgorithm::GetLongitudinalSpan(const Cluster *const pCluster) const
476 {
477  float minZ(+std::numeric_limits<float>::max());
478  float maxZ(-std::numeric_limits<float>::max());
479 
480  this->GetExtremalCoordinatesZ(pCluster, minZ, maxZ);
481 
482  return (maxZ - minZ);
483 }
484 
485 //------------------------------------------------------------------------------------------------------------------------------------------
486 
487 float TransverseAssociationAlgorithm::GetTransverseSpan(const Cluster *const pCentralCluster, const ClusterVector &associatedClusters) const
488 {
489  float overallMinX(+std::numeric_limits<float>::max());
490  float overallMaxX(-std::numeric_limits<float>::max());
491 
492  this->GetExtremalCoordinatesX(pCentralCluster, overallMinX, overallMaxX);
493 
494  float localMinX(+std::numeric_limits<float>::max());
495  float localMaxX(-std::numeric_limits<float>::max());
496 
497  for (ClusterVector::const_iterator iter = associatedClusters.begin(), iterEnd = associatedClusters.end(); iter != iterEnd; ++iter)
498  {
499  const Cluster *const pAssociatedCluster = *iter;
500 
501  this->GetExtremalCoordinatesX(pAssociatedCluster, localMinX, localMaxX);
502 
503  if (localMinX < overallMinX)
504  overallMinX = localMinX;
505 
506  if (localMaxX > overallMaxX)
507  overallMaxX = localMaxX;
508  }
509 
510  return (overallMaxX - overallMinX);
511 }
512 
513 //------------------------------------------------------------------------------------------------------------------------------------------
514 
515 bool TransverseAssociationAlgorithm::IsExtremalCluster(const bool isForward, const Cluster *const pCurrentCluster, const Cluster *const pTestCluster) const
516 {
517  float currentMinX(0.f), currentMaxX(0.f);
518  this->GetExtremalCoordinatesX(pCurrentCluster, currentMinX, currentMaxX);
519 
520  float testMinX(0.f), testMaxX(0.f);
521  this->GetExtremalCoordinatesX(pTestCluster, testMinX, testMaxX);
522 
523  if (isForward)
524  {
525  if (std::fabs(testMaxX - currentMaxX) > std::numeric_limits<float>::epsilon())
526  return (testMaxX > currentMaxX);
527  }
528  else
529  {
530  if (std::fabs(testMinX - currentMaxX) > std::numeric_limits<float>::epsilon())
531  return (testMinX < currentMinX);
532  }
533 
534  return LArClusterHelper::SortByNHits(pTestCluster, pCurrentCluster);
535 }
536 
537 //------------------------------------------------------------------------------------------------------------------------------------------
538 
539 void TransverseAssociationAlgorithm::GetExtremalCoordinatesX(const Cluster *const pCluster, float &minX, float &maxX) const
540 {
541  return this->GetExtremalCoordinatesXZ(pCluster, true, minX, maxX);
542 }
543 
544 //------------------------------------------------------------------------------------------------------------------------------------------
545 
546 void TransverseAssociationAlgorithm::GetExtremalCoordinatesZ(const Cluster *const pCluster, float &minZ, float &maxZ) const
547 {
548  return this->GetExtremalCoordinatesXZ(pCluster, false, minZ, maxZ);
549 }
550 
551 //------------------------------------------------------------------------------------------------------------------------------------------
552 
553 void TransverseAssociationAlgorithm::GetExtremalCoordinatesXZ(const Cluster *const pCluster, const bool useX, float &minXZ, float &maxXZ) const
554 {
557 
558  const OrderedCaloHitList &orderedCaloHitList(pCluster->GetOrderedCaloHitList());
559 
560  for (OrderedCaloHitList::const_iterator iter = orderedCaloHitList.begin(), iterEnd = orderedCaloHitList.end(); iter != iterEnd; ++iter)
561  {
562  for (CaloHitList::const_iterator hitIter = iter->second->begin(), hitIterEnd = iter->second->end(); hitIter != hitIterEnd; ++hitIter)
563  {
564  const float caloHitXZ(useX ? (*hitIter)->GetPositionVector().GetX() : (*hitIter)->GetPositionVector().GetZ());
565 
566  if (caloHitXZ < minXZ)
567  minXZ = caloHitXZ;
568 
569  if (caloHitXZ > maxXZ)
570  maxXZ = caloHitXZ;
571  }
572  }
573 
574  if (maxXZ < minXZ)
575  throw pandora::StatusCodeException(STATUS_CODE_FAILURE);
576 }
577 
578 //------------------------------------------------------------------------------------------------------------------------------------------
579 
581  const Cluster *const pCluster, CartesianVector &innerCoordinate, CartesianVector &outerCoordinate) const
582 {
583  CartesianVector firstCoordinate(0.f, 0.f, 0.f), secondCoordinate(0.f, 0.f, 0.f);
584  LArClusterHelper::GetExtremalCoordinates(pCluster, firstCoordinate, secondCoordinate);
585 
586  innerCoordinate = (firstCoordinate.GetX() < secondCoordinate.GetX() ? firstCoordinate : secondCoordinate);
587  outerCoordinate = (firstCoordinate.GetX() > secondCoordinate.GetX() ? firstCoordinate : secondCoordinate);
588 }
589 
590 //------------------------------------------------------------------------------------------------------------------------------------------
591 
593  const ClusterAssociationMap &inputAssociationMap, ClusterAssociationMap &outputAssociationMap) const
594 {
595  return this->FillReducedAssociationMap(inputAssociationMap, inputAssociationMap, inputAssociationMap, outputAssociationMap);
596 }
597 
598 //------------------------------------------------------------------------------------------------------------------------------------------
599 
601  const ClusterAssociationMap &secondAssociationMap, const ClusterAssociationMap &secondAssociationMapSwapped,
602  ClusterAssociationMap &clusterAssociationMap) const
603 {
604  // Remove associations A->B from the first association map
605  // if A->C exists in the second map and C->B exists in the reversed second map
606 
607  // Method can also be accessed through FillReducedAssociationMap(input,output) method,
608  // which will remove association A->B from the input map if an association A->C and C->B
609  // already exists in the map.
610 
611  ClusterVector sortedClusters;
612  for (const auto &mapEntry : firstAssociationMap)
613  sortedClusters.push_back(mapEntry.first);
614  std::sort(sortedClusters.begin(), sortedClusters.end(), LArClusterHelper::SortByNHits);
615 
616  for (const Cluster *const pCluster : sortedClusters)
617  {
618  const ClusterAssociation &firstAssociation(firstAssociationMap.at(pCluster));
619 
620  ClusterVector sortedOuterClusters(firstAssociation.m_forwardAssociations.begin(), firstAssociation.m_forwardAssociations.end());
621  std::sort(sortedOuterClusters.begin(), sortedOuterClusters.end(), LArClusterHelper::SortByNHits);
622 
623  ClusterVector sortedInnerClusters(firstAssociation.m_backwardAssociations.begin(), firstAssociation.m_backwardAssociations.end());
624  std::sort(sortedInnerClusters.begin(), sortedInnerClusters.end(), LArClusterHelper::SortByNHits);
625 
626  ClusterAssociationMap::const_iterator iterSecond = secondAssociationMap.find(pCluster);
627  ClusterVector sortedMiddleClustersF, sortedMiddleClustersB;
628 
629  if (secondAssociationMap.end() != iterSecond)
630  {
631  sortedMiddleClustersF.insert(sortedMiddleClustersF.end(), iterSecond->second.m_forwardAssociations.begin(),
632  iterSecond->second.m_forwardAssociations.end());
633  sortedMiddleClustersB.insert(sortedMiddleClustersB.end(), iterSecond->second.m_backwardAssociations.begin(),
634  iterSecond->second.m_backwardAssociations.end());
635  std::sort(sortedMiddleClustersF.begin(), sortedMiddleClustersF.end(), LArClusterHelper::SortByNHits);
636  std::sort(sortedMiddleClustersB.begin(), sortedMiddleClustersB.end(), LArClusterHelper::SortByNHits);
637  }
638 
639  for (const Cluster *const pOuterCluster : sortedOuterClusters)
640  {
641  bool isNeighbouringCluster(true);
642 
643  for (const Cluster *const pMiddleCluster : sortedMiddleClustersF)
644  {
645  ClusterAssociationMap::const_iterator iterSecondCheck = secondAssociationMapSwapped.find(pMiddleCluster);
646  if (secondAssociationMapSwapped.end() == iterSecondCheck)
647  continue;
648 
649  if (iterSecondCheck->second.m_forwardAssociations.count(pOuterCluster) > 0)
650  {
651  isNeighbouringCluster = false;
652  break;
653  }
654  }
655 
656  if (isNeighbouringCluster)
657  clusterAssociationMap[pCluster].m_forwardAssociations.insert(pOuterCluster);
658  }
659 
660  for (const Cluster *const pInnerCluster : sortedInnerClusters)
661  {
662  bool isNeighbouringCluster(true);
663 
664  for (const Cluster *const pMiddleCluster : sortedMiddleClustersB)
665  {
666  ClusterAssociationMap::const_iterator iterSecondCheck = secondAssociationMapSwapped.find(pMiddleCluster);
667  if (secondAssociationMapSwapped.end() == iterSecondCheck)
668  continue;
669 
670  if (iterSecondCheck->second.m_backwardAssociations.count(pInnerCluster) > 0)
671  {
672  isNeighbouringCluster = false;
673  break;
674  }
675  }
676 
677  if (isNeighbouringCluster)
678  clusterAssociationMap[pCluster].m_backwardAssociations.insert(pInnerCluster);
679  }
680  }
681 }
682 
683 //------------------------------------------------------------------------------------------------------------------------------------------
684 
686  const ClusterAssociationMap &inputAssociationMap, ClusterAssociationMap &outputAssociationMap) const
687 {
688  // Generate a symmetrised association map, so that both A--Fwd-->B and B--Bwd-->A both exist.
689  // If A is associated to B through both a backward and forward association (very bad!),
690  // try to rationalise this through majority voting, otherwise remove the association.
691 
692  ClusterVector sortedClusters;
693  for (const auto &mapEntry : inputAssociationMap)
694  sortedClusters.push_back(mapEntry.first);
695  std::sort(sortedClusters.begin(), sortedClusters.end(), LArClusterHelper::SortByNHits);
696 
697  for (const Cluster *const pCluster : sortedClusters)
698  {
699  const ClusterAssociation &inputAssociation(inputAssociationMap.at(pCluster));
700 
701  ClusterVector sortedForwardClusters(inputAssociation.m_forwardAssociations.begin(), inputAssociation.m_forwardAssociations.end());
702  std::sort(sortedForwardClusters.begin(), sortedForwardClusters.end(), LArClusterHelper::SortByNHits);
703 
704  ClusterVector sortedBackwardClusters(inputAssociation.m_backwardAssociations.begin(), inputAssociation.m_backwardAssociations.end());
705  std::sort(sortedBackwardClusters.begin(), sortedBackwardClusters.end(), LArClusterHelper::SortByNHits);
706 
707  // Symmetrise forward associations
708  for (const Cluster *const pForwardCluster : sortedForwardClusters)
709  {
710  int nCounter(+1);
711 
712  if (inputAssociation.m_backwardAssociations.count(pForwardCluster))
713  --nCounter;
714 
715  ClusterAssociationMap::const_iterator iterCheck = inputAssociationMap.find(pForwardCluster);
716  if (inputAssociationMap.end() != iterCheck)
717  {
718  if (iterCheck->second.m_forwardAssociations.count(pCluster))
719  --nCounter;
720 
721  if (iterCheck->second.m_backwardAssociations.count(pCluster))
722  ++nCounter;
723  }
724 
725  if (nCounter > 0)
726  {
727  if (!(outputAssociationMap[pCluster].m_backwardAssociations.count(pForwardCluster) == 0 &&
728  outputAssociationMap[pForwardCluster].m_forwardAssociations.count(pCluster) == 0))
729  throw StatusCodeException(STATUS_CODE_FAILURE);
730 
731  outputAssociationMap[pCluster].m_forwardAssociations.insert(pForwardCluster);
732  outputAssociationMap[pForwardCluster].m_backwardAssociations.insert(pCluster);
733  }
734  }
735 
736  // Symmetrise backward associations
737  for (const Cluster *const pBackwardCluster : sortedBackwardClusters)
738  {
739  int nCounter(-1);
740 
741  if (inputAssociation.m_forwardAssociations.count(pBackwardCluster))
742  ++nCounter;
743 
744  ClusterAssociationMap::const_iterator iterCheck = inputAssociationMap.find(pBackwardCluster);
745  if (inputAssociationMap.end() != iterCheck)
746  {
747  if (iterCheck->second.m_backwardAssociations.count(pCluster))
748  ++nCounter;
749 
750  if (iterCheck->second.m_forwardAssociations.count(pCluster))
751  --nCounter;
752  }
753 
754  if (nCounter < 0)
755  {
756  if (!(outputAssociationMap[pCluster].m_forwardAssociations.count(pBackwardCluster) == 0 &&
757  outputAssociationMap[pBackwardCluster].m_backwardAssociations.count(pCluster) == 0))
758  throw StatusCodeException(STATUS_CODE_FAILURE);
759 
760  outputAssociationMap[pCluster].m_backwardAssociations.insert(pBackwardCluster);
761  outputAssociationMap[pBackwardCluster].m_forwardAssociations.insert(pCluster);
762  }
763  }
764  }
765 }
766 
767 //------------------------------------------------------------------------------------------------------------------------------------------
768 
770  const ClusterAssociationMap &inputAssociationMap, ClusterAssociationMap &outputAssociationMap) const
771 {
772  ClusterAssociationMap intermediateAssociationMap;
773  this->FillSymmetricAssociationMap(inputAssociationMap, intermediateAssociationMap);
774  this->FillReducedAssociationMap(intermediateAssociationMap, outputAssociationMap);
775 }
776 
777 //------------------------------------------------------------------------------------------------------------------------------------------
778 //------------------------------------------------------------------------------------------------------------------------------------------
779 
780 TransverseAssociationAlgorithm::LArTransverseCluster::LArTransverseCluster(const Cluster *const pSeedCluster, const ClusterVector &associatedClusters) :
781  m_pSeedCluster(pSeedCluster),
782  m_associatedClusters(associatedClusters),
783  m_innerVertex(0.f, 0.f, 0.f),
784  m_outerVertex(0.f, 0.f, 0.f),
785  m_direction(0.f, 0.f, 0.f)
786 {
787  double Swzz(0.), Swxx(0.), Swzx(0.), Swz(0.), Swx(0.), Sw(0.);
788  double minX(std::numeric_limits<double>::max());
789  double maxX(-std::numeric_limits<double>::max());
790 
791  ClusterList clusterList(1, pSeedCluster);
792  clusterList.insert(clusterList.end(), associatedClusters.begin(), associatedClusters.end());
793 
794  for (ClusterList::const_iterator iterI = clusterList.begin(), iterEndI = clusterList.end(); iterI != iterEndI; ++iterI)
795  {
796  for (OrderedCaloHitList::const_iterator iterJ = (*iterI)->GetOrderedCaloHitList().begin(),
797  iterEndJ = (*iterI)->GetOrderedCaloHitList().end();
798  iterJ != iterEndJ; ++iterJ)
799  {
800  for (CaloHitList::const_iterator iterK = iterJ->second->begin(), iterEndK = iterJ->second->end(); iterK != iterEndK; ++iterK)
801  {
802  const CaloHit *const pCaloHit = *iterK;
803 
804  if (pCaloHit->GetPositionVector().GetX() < minX)
805  minX = pCaloHit->GetPositionVector().GetX();
806 
807  if (pCaloHit->GetPositionVector().GetX() > maxX)
808  maxX = pCaloHit->GetPositionVector().GetX();
809 
810  Swzz += pCaloHit->GetPositionVector().GetZ() * pCaloHit->GetPositionVector().GetZ();
811  Swxx += pCaloHit->GetPositionVector().GetX() * pCaloHit->GetPositionVector().GetX();
812  Swzx += pCaloHit->GetPositionVector().GetZ() * pCaloHit->GetPositionVector().GetX();
813  Swz += pCaloHit->GetPositionVector().GetZ();
814  Swx += pCaloHit->GetPositionVector().GetX();
815  Sw += 1.;
816  }
817  }
818  }
819 
820  if (Sw > 0.f)
821  {
822  const double averageX(Swx / Sw);
823  const double averageZ(Swz / Sw);
824 
825  if (Sw * Swxx - Swx * Swx > 0.)
826  {
827  double m((Sw * Swzx - Swx * Swz) / (Sw * Swxx - Swx * Swx));
828  double px(1. / std::sqrt(1. + m * m));
829  double pz(m / std::sqrt(1. + m * m));
830 
831  m_innerVertex.SetValues(static_cast<float>(minX), 0.f, static_cast<float>(averageZ + m * (minX - averageX)));
832  m_outerVertex.SetValues(static_cast<float>(maxX), 0.f, static_cast<float>(averageZ + m * (maxX - averageX)));
833  m_direction.SetValues(static_cast<float>(px), 0.f, static_cast<float>(pz));
834  }
835  else
836  {
837  m_innerVertex.SetValues(static_cast<float>(averageX), 0.f, static_cast<float>(averageZ));
838  m_outerVertex.SetValues(static_cast<float>(averageX), 0.f, static_cast<float>(averageZ));
839  m_direction.SetValues(1.f, 0.f, 0.f);
840  }
841  }
842  else
843  {
844  throw StatusCodeException(STATUS_CODE_NOT_INITIALIZED);
845  }
846 }
847 
848 //------------------------------------------------------------------------------------------------------------------------------------------
849 //------------------------------------------------------------------------------------------------------------------------------------------
850 
851 StatusCode TransverseAssociationAlgorithm::ReadSettings(const TiXmlHandle xmlHandle)
852 {
853  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "FirstLengthCut", m_firstLengthCut));
854 
855  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "SecondLengthCut", m_secondLengthCut));
856 
857  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "ClusterWindow", m_clusterWindow));
858 
859  const StatusCode angleStatusCode(XmlHelper::ReadValue(xmlHandle, "clusterAngle", m_clusterAngle));
860 
861  if (STATUS_CODE_SUCCESS == angleStatusCode)
862  {
863  m_clusterCosAngle = std::cos(m_clusterAngle * M_PI / 180.f);
864  m_clusterTanAngle = std::tan(m_clusterAngle * M_PI / 180.f);
865  }
866  else if (STATUS_CODE_NOT_FOUND != angleStatusCode)
867  {
868  return angleStatusCode;
869  }
870 
871  PANDORA_RETURN_RESULT_IF_AND_IF(
872  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxTransverseOverlap", m_maxTransverseOverlap));
873 
874  PANDORA_RETURN_RESULT_IF_AND_IF(
875  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxProjectedOverlap", m_maxProjectedOverlap));
876 
877  PANDORA_RETURN_RESULT_IF_AND_IF(
878  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxLongitudinalOverlap", m_maxLongitudinalOverlap));
879 
880  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
881  XmlHelper::ReadValue(xmlHandle, "TransverseClusterMinCosTheta", m_transverseClusterMinCosTheta));
882 
883  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
884  XmlHelper::ReadValue(xmlHandle, "TransverseClusterMinLength", m_transverseClusterMinLength));
885 
886  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
887  XmlHelper::ReadValue(xmlHandle, "TransverseClusterMaxDisplacement", m_transverseClusterMaxDisplacement));
888 
890 }
891 
892 } // 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.
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.
std::unordered_map< const pandora::Cluster *, ClusterAssociation > ClusterAssociationMap
STL namespace.
intermediate_table::const_iterator const_iterator
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.
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.
std::vector< LArTransverseCluster * > TransverseClusterList
bool IsOverlapping(const pandora::Cluster *const pCluster1, const pandora::Cluster *const pCluster2) const
Determine whether two clusters are overlapping.
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 build(std::vector< KDTreeNodeInfoT< DATA, DIM >> &eltList, const KDTreeBoxT< DIM > &region)
Build the KD tree from the "eltList" in the space define by "region".
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.
static int max(int a, int b)
std::unordered_map< const pandora::Cluster *, pandora::ClusterSet > ClusterToClustersMap
std::unordered_map< const pandora::CaloHit *, const pandora::Cluster * > HitToClusterMap
Detector simulation of raw signals on wires.
#define M_PI
Definition: includeROOT.h:54
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.
T min(sqlite3 *const db, std::string const &table_name, std::string const &column_name)
Definition: statistics.h:55
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...
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
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.
bool IsAssociated(const bool isForward, const pandora::Cluster *const pCluster1, const pandora::Cluster *const pCluster2, const ClusterToClustersMap &nearbyClusters) const
Determine whether clusters are association.
std::vector< art::Ptr< recob::Cluster > > ClusterVector
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.
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 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...
void GetExtremalCoordinatesZ(const pandora::Cluster *const pCluster, float &minZ, float &maxZ) const
Get minimum and maximum Z coordinates for a given cluster.
QTextStream & endl(QTextStream &s)
LArTransverseCluster(const pandora::Cluster *const pSeedCluster, const pandora::ClusterVector &associatedClusters)
Constructor.