CrossGapsExtensionAlgorithm.cc
Go to the documentation of this file.
1 /**
2  * @file larpandoracontent/LArTwoDReco/LArClusterAssociation/CrossGapsExtensionAlgorithm.cc
3  *
4  * @brief Implementation of the cross gaps extension algorithm class.
5  *
6  * $Log: $
7  */
8 
9 #include "Pandora/AlgorithmHeaders.h"
10 
14 
16 
17 using namespace pandora;
18 
19 namespace lar_content
20 {
21 
22 CrossGapsExtensionAlgorithm::CrossGapsExtensionAlgorithm() :
23  m_minClusterLength(5.f),
24  m_minGapFraction(0.5f),
25  m_maxGapTolerance(2.f),
26  m_maxTransverseDisplacement(2.5f),
27  m_maxRelativeAngle(10.f)
28 {
29 }
30 
31 //------------------------------------------------------------------------------------------------------------------------------------------
32 
33 void CrossGapsExtensionAlgorithm::GetListOfCleanClusters(const ClusterList *const pClusterList, ClusterVector &clusterVector) const
34 {
35  // ATTN May want to opt-out completely if no gap information available
36  // if (PandoraContentApi::GetGeometry(*this)->GetDetectorGapList().empty())
37  // return;
38 
39  for (const Cluster *const pCluster : *pClusterList)
40  {
42  continue;
43 
44  clusterVector.push_back(pCluster);
45  }
46 
47  std::sort(clusterVector.begin(), clusterVector.end(), LArClusterHelper::SortByNHits);
48 }
49 
50 //------------------------------------------------------------------------------------------------------------------------------------------
51 
53 {
54  // Build lists of pointing clusters in proximity to gaps
55  LArPointingClusterList innerPointingClusterList, outerPointingClusterList;
56  this->BuildPointingClusterList(clusterVector, innerPointingClusterList, outerPointingClusterList);
57 
58  // Form associations between pairs of pointing clusters
59  for (const LArPointingCluster &pointingClusterInner : innerPointingClusterList)
60  {
61  const LArPointingCluster::Vertex &pointingVertexInner(pointingClusterInner.GetInnerVertex());
62  const float zInner(pointingVertexInner.GetPosition().GetZ());
63 
64  for (const LArPointingCluster &pointingClusterOuter : outerPointingClusterList)
65  {
66  const LArPointingCluster::Vertex &pointingVertexOuter(pointingClusterOuter.GetOuterVertex());
67  const float zOuter(pointingVertexOuter.GetPosition().GetZ());
68 
69  if (!this->IsAcrossGap(zOuter, zInner, LArClusterHelper::GetClusterHitType(pointingClusterInner.GetCluster())))
70  continue;
71 
72  if (!this->IsAssociated(pointingVertexInner, pointingVertexOuter))
73  continue;
74 
75  const Cluster *const pClusterInner(pointingClusterInner.GetCluster());
76  const Cluster *const pClusterOuter(pointingClusterOuter.GetCluster());
77 
78  const float lengthSquaredInner(LArClusterHelper::GetLengthSquared(pClusterInner));
79  const float lengthSquaredOuter(LArClusterHelper::GetLengthSquared(pClusterOuter));
80 
81  (void)clusterAssociationMatrix[pClusterInner].insert(ClusterAssociationMap::value_type(pClusterOuter,
83  (void)clusterAssociationMatrix[pClusterOuter].insert(ClusterAssociationMap::value_type(pClusterInner,
85  }
86  }
87 }
88 
89 //------------------------------------------------------------------------------------------------------------------------------------------
90 
92  LArPointingClusterList &innerPointingClusterList, LArPointingClusterList &outerPointingClusterList) const
93 {
94  // Convert each input cluster into a pointing cluster
95  LArPointingClusterList pointingClusterList;
96 
97  for (const Cluster *const pCluster : clusterVector)
98  {
99  try
100  {
101  pointingClusterList.push_back(LArPointingCluster(pCluster));
102  }
103  catch (StatusCodeException &)
104  {
105  }
106  }
107 
108  // Identify clusters adjacent to detector gaps
109  this->BuildPointingClusterList(true, pointingClusterList, innerPointingClusterList);
110  this->BuildPointingClusterList(false, pointingClusterList, outerPointingClusterList);
111 }
112 
113 //------------------------------------------------------------------------------------------------------------------------------------------
114 
116  const bool useInner, const LArPointingClusterList &inputPointingClusterList, LArPointingClusterList &outputPointingClusterList) const
117 {
118  for (const LArPointingCluster &pointingCluster : inputPointingClusterList)
119  {
120  const LArPointingCluster::Vertex &pointingVertex(useInner ? pointingCluster.GetInnerVertex() : pointingCluster.GetOuterVertex());
121  const HitType hitType(LArClusterHelper::GetClusterHitType(pointingCluster.GetCluster()));
122 
123  if (LArGeometryHelper::IsInGap(this->GetPandora(), pointingVertex.GetPosition(), hitType, m_maxGapTolerance))
124  outputPointingClusterList.push_back(pointingCluster);
125  }
126 }
127 
128 //------------------------------------------------------------------------------------------------------------------------------------------
129 
131 {
132  const float maxLongitudinalDisplacement((pointingVertex2.GetPosition() - pointingVertex1.GetPosition()).GetMagnitude());
133 
134  const bool isAssociated1(LArPointingClusterHelper::IsEmission(pointingVertex1.GetPosition(), pointingVertex2, -1.f,
135  maxLongitudinalDisplacement + 1.f, m_maxTransverseDisplacement, m_maxRelativeAngle));
136  const bool isAssociated2(LArPointingClusterHelper::IsEmission(pointingVertex2.GetPosition(), pointingVertex1, -1.f,
137  maxLongitudinalDisplacement + 1.f, m_maxTransverseDisplacement, m_maxRelativeAngle));
138 
139  return (isAssociated1 && isAssociated2);
140 }
141 
142 //------------------------------------------------------------------------------------------------------------------------------------------
143 
144 bool CrossGapsExtensionAlgorithm::IsAcrossGap(const float minZ, const float maxZ, const HitType hitType) const
145 {
146  if (maxZ - minZ < std::numeric_limits<float>::epsilon())
147  return false;
148 
149  const float gapDeltaZ(LArGeometryHelper::CalculateGapDeltaZ(this->GetPandora(), minZ, maxZ, hitType));
150 
151  if (gapDeltaZ / (maxZ - minZ) < m_minGapFraction)
152  return false;
153 
154  return true;
155 }
156 
157 //------------------------------------------------------------------------------------------------------------------------------------------
158 
159 void CrossGapsExtensionAlgorithm::FillClusterMergeMap(const ClusterAssociationMatrix &inputAssociationMatrix, ClusterMergeMap &clusterMergeMap) const
160 {
161  // Decide which associations will become merges
162  // To make the merge A <-> B, both A -> B and B -> A must be strong associations
163  // with the largest figures of merit of all the A -> X and B -> Y associations
164 
165  // First step: remove double-counting from the map of associations
166  // i.e. if the map has A <-> B, B <-> C, A <-> C, then remove A <-> C
167  ClusterAssociationMatrix clusterAssociationMatrix;
168 
169  ClusterVector sortedInputClusters;
170  for (const auto &mapEntry : inputAssociationMatrix)
171  sortedInputClusters.push_back(mapEntry.first);
172  std::sort(sortedInputClusters.begin(), sortedInputClusters.end(), LArClusterHelper::SortByNHits);
173 
174  for (const Cluster *const pCluster1 : sortedInputClusters)
175  {
176  const ClusterAssociationMap &associationMap1(inputAssociationMatrix.at(pCluster1));
177 
178  for (const Cluster *const pCluster2 : sortedInputClusters)
179  {
180  if (pCluster1 == pCluster2)
181  continue;
182 
183  const ClusterAssociationMap &associationMap2(inputAssociationMatrix.at(pCluster2));
184 
185  ClusterAssociationMap::const_iterator iter12 = associationMap1.find(pCluster2);
186  if (associationMap1.end() == iter12)
187  continue;
188 
189  ClusterAssociationMap::const_iterator iter21 = associationMap2.find(pCluster1);
190  if (associationMap2.end() == iter21)
191  continue;
192 
193  const ClusterAssociation &association12(iter12->second);
194  const ClusterAssociation &association21(iter21->second);
195 
196  bool isAssociated(true);
197 
198  ClusterVector sortedAssociationClusters;
199  for (const auto &mapEntry : associationMap1)
200  sortedAssociationClusters.push_back(mapEntry.first);
201  std::sort(sortedAssociationClusters.begin(), sortedAssociationClusters.end(), LArClusterHelper::SortByNHits);
202 
203  for (const Cluster *const pCluster3 : sortedAssociationClusters)
204  {
205  const ClusterAssociation &association13(associationMap1.at(pCluster3));
206 
207  ClusterAssociationMap::const_iterator iter23 = associationMap2.find(pCluster3);
208  if (associationMap2.end() == iter23)
209  continue;
210 
211  const ClusterAssociation &association23(iter23->second);
212 
213  if (association12.GetParent() == association13.GetParent() && association23.GetParent() == association21.GetParent() &&
214  association13.GetDaughter() != association23.GetDaughter())
215  {
216  isAssociated = false;
217  break;
218  }
219  }
220 
221  if (isAssociated)
222  {
223  (void)clusterAssociationMatrix[pCluster1].insert(ClusterAssociationMap::value_type(pCluster2, association12));
224  (void)clusterAssociationMatrix[pCluster2].insert(ClusterAssociationMap::value_type(pCluster1, association21));
225  }
226  }
227  }
228 
229  // Second step: find the best associations A -> X and B -> Y
230  ClusterAssociationMatrix intermediateAssociationMatrix;
231 
232  ClusterVector sortedClusters;
233  for (const auto &mapEntry : clusterAssociationMatrix)
234  sortedClusters.push_back(mapEntry.first);
235  std::sort(sortedClusters.begin(), sortedClusters.end(), LArClusterHelper::SortByNHits);
236 
237  for (const Cluster *const pParentCluster : sortedClusters)
238  {
239  const ClusterAssociationMap &clusterAssociationMap(clusterAssociationMatrix.at(pParentCluster));
240 
241  const Cluster *pBestClusterInner(nullptr);
243 
244  const Cluster *pBestClusterOuter(nullptr);
246 
247  ClusterVector sortedAssociationClusters;
248  for (const auto &mapEntry : clusterAssociationMap)
249  sortedAssociationClusters.push_back(mapEntry.first);
250  std::sort(sortedAssociationClusters.begin(), sortedAssociationClusters.end(), LArClusterHelper::SortByNHits);
251 
252  for (const Cluster *const pDaughterCluster : sortedAssociationClusters)
253  {
254  const ClusterAssociation &clusterAssociation(clusterAssociationMap.at(pDaughterCluster));
255 
256  // Inner associations
257  if (clusterAssociation.GetParent() == ClusterAssociation::INNER)
258  {
259  if (clusterAssociation.GetFigureOfMerit() > bestAssociationInner.GetFigureOfMerit())
260  {
261  bestAssociationInner = clusterAssociation;
262  pBestClusterInner = pDaughterCluster;
263  }
264  }
265 
266  // Outer associations
267  if (clusterAssociation.GetParent() == ClusterAssociation::OUTER)
268  {
269  if (clusterAssociation.GetFigureOfMerit() > bestAssociationOuter.GetFigureOfMerit())
270  {
271  bestAssociationOuter = clusterAssociation;
272  pBestClusterOuter = pDaughterCluster;
273  }
274  }
275  }
276 
277  if (pBestClusterInner)
278  (void)intermediateAssociationMatrix[pParentCluster].insert(ClusterAssociationMap::value_type(pBestClusterInner, bestAssociationInner));
279 
280  if (pBestClusterOuter)
281  (void)intermediateAssociationMatrix[pParentCluster].insert(ClusterAssociationMap::value_type(pBestClusterOuter, bestAssociationOuter));
282  }
283 
284  // Third step: make the merge if A -> X and B -> Y is in fact A -> B and B -> A
285  ClusterVector intermediateSortedClusters;
286  for (const auto &mapEntry : intermediateAssociationMatrix)
287  intermediateSortedClusters.push_back(mapEntry.first);
288  std::sort(intermediateSortedClusters.begin(), intermediateSortedClusters.end(), LArClusterHelper::SortByNHits);
289 
290  for (const Cluster *const pParentCluster : intermediateSortedClusters)
291  {
292  const ClusterAssociationMap &parentAssociationMap(intermediateAssociationMatrix.at(pParentCluster));
293 
294  ClusterVector sortedAssociationClusters;
295  for (const auto &mapEntry : parentAssociationMap)
296  sortedAssociationClusters.push_back(mapEntry.first);
297  std::sort(sortedAssociationClusters.begin(), sortedAssociationClusters.end(), LArClusterHelper::SortByNHits);
298 
299  for (const Cluster *const pDaughterCluster : sortedAssociationClusters)
300  {
301  const ClusterAssociation &parentToDaughterAssociation(parentAssociationMap.at(pDaughterCluster));
302 
303  ClusterAssociationMatrix::const_iterator iter5 = intermediateAssociationMatrix.find(pDaughterCluster);
304 
305  if (intermediateAssociationMatrix.end() == iter5)
306  continue;
307 
308  const ClusterAssociationMap &daughterAssociationMap(iter5->second);
309 
310  ClusterAssociationMap::const_iterator iter6 = daughterAssociationMap.find(pParentCluster);
311 
312  if (daughterAssociationMap.end() == iter6)
313  continue;
314 
315  const ClusterAssociation &daughterToParentAssociation(iter6->second);
316 
317  if (parentToDaughterAssociation.GetParent() == daughterToParentAssociation.GetDaughter() &&
318  parentToDaughterAssociation.GetDaughter() == daughterToParentAssociation.GetParent())
319  {
320  ClusterList &parentList(clusterMergeMap[pParentCluster]);
321 
322  if (parentList.end() == std::find(parentList.begin(), parentList.end(), pDaughterCluster))
323  parentList.push_back(pDaughterCluster);
324  }
325  }
326  }
327 }
328 
329 //------------------------------------------------------------------------------------------------------------------------------------------
330 
331 StatusCode CrossGapsExtensionAlgorithm::ReadSettings(const TiXmlHandle xmlHandle)
332 {
333  PANDORA_RETURN_RESULT_IF_AND_IF(
334  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MinClusterLength", m_minClusterLength));
335 
336  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MinGapFraction", m_minGapFraction));
337 
338  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxGapTolerance", m_maxGapTolerance));
339 
340  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
341  XmlHelper::ReadValue(xmlHandle, "MaxTransverseDisplacement", m_maxTransverseDisplacement));
342 
343  float maxCosRelativeAngle(std::cos(m_maxRelativeAngle * M_PI / 180.0));
344  PANDORA_RETURN_RESULT_IF_AND_IF(
345  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxCosRelativeAngle", maxCosRelativeAngle));
346  m_maxRelativeAngle = (180.0 / M_PI) * std::acos(maxCosRelativeAngle);
347 
348  return ClusterExtensionAlgorithm::ReadSettings(xmlHandle);
349 }
350 
351 } // 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.
static bool IsEmission(const pandora::CartesianVector &parentVertex, const LArPointingCluster::Vertex &daughterVertex, const float minLongitudinalDistance, const float maxLongitudinalDistance, const float maxTransverseDistance, const float angularAllowance)
Whether pointing vertex is emitted from a given position.
void BuildPointingClusterList(const pandora::ClusterVector &clusterVector, LArPointingClusterList &innerPointingClusterList, LArPointingClusterList &outerPointingClusterList) const
Build lists of pointing clusters that are adjacent to a detector gap.
enum cvn::HType HitType
std::vector< LArPointingCluster > LArPointingClusterList
static float CalculateGapDeltaZ(const pandora::Pandora &pandora, const float minZ, const float maxZ, const pandora::HitType hitType)
Calculate the total distance within a given 2D region that is composed of detector gaps...
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 FillClusterMergeMap(const ClusterAssociationMatrix &clusterAssociationMatrix, ClusterMergeMap &clusterMergeMap) const
Fill the cluster merge map.
intermediate_table::const_iterator const_iterator
LArPointingCluster class.
static pandora::HitType GetClusterHitType(const pandora::Cluster *const pCluster)
Get the hit type associated with a two dimensional cluster.
static bool IsInGap(const pandora::Pandora &pandora, const pandora::CartesianVector &testPoint2D, const pandora::HitType hitType, const float gapTolerance=0.f)
Whether a 2D test point lies in a registered gap with the associated hit type.
Header file for the geometry helper class.
Header file for the cluster helper class.
void FillClusterAssociationMatrix(const pandora::ClusterVector &clusterVector, ClusterAssociationMatrix &clusterAssociationMatrix) const
Fill the cluster association matrix.
pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
std::unordered_map< const pandora::Cluster *, ClusterAssociation > ClusterAssociationMap
std::unordered_map< const pandora::Cluster *, ClusterAssociationMap > ClusterAssociationMatrix
#define M_PI
Definition: includeROOT.h:54
bool IsAcrossGap(const float minZ, const float maxZ, const pandora::HitType hitType) const
Determine whether a start and end position sit either side of a gap.
bool IsAssociated(const LArPointingCluster::Vertex &pointingVertex1, const LArPointingCluster::Vertex &pointingVertex2) const
Use pointing information to determine whether two clusters are associated.
static float GetLengthSquared(const pandora::Cluster *const pCluster)
Get length squared of cluster.
std::unordered_map< const pandora::Cluster *, pandora::ClusterList > ClusterMergeMap
std::vector< art::Ptr< recob::Cluster > > ClusterVector
const pandora::CartesianVector & GetPosition() const
Get the vertex position.
virtual pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
Header file for the cross gaps extension algorithm class.