CrossGapsAssociationAlgorithm.cc
Go to the documentation of this file.
1 /**
2  * @file larpandoracontent/LArTwoDReco/LArClusterAssociation/CrossGapsAssociationAlgorithm.cc
3  *
4  * @brief Implementation of the cross gaps association algorithm class.
5  *
6  * $Log: $
7  */
8 
9 #include "Pandora/AlgorithmHeaders.h"
10 
13 
15 
16 using namespace pandora;
17 
18 namespace lar_content
19 {
20 
21 CrossGapsAssociationAlgorithm::CrossGapsAssociationAlgorithm() :
22  m_minClusterHits(10),
23  m_minClusterLayers(6),
24  m_slidingFitWindow(20),
25  m_maxSamplingPoints(1000),
26  m_sampleStepSize(0.5f),
27  m_maxUnmatchedSampleRun(8),
28  m_maxOnClusterDistance(1.5f),
29  m_minMatchedSamplingPoints(10),
30  m_minMatchedSamplingFraction(0.5f),
31  m_gapTolerance(0.f)
32 {
33 }
34 
35 //------------------------------------------------------------------------------------------------------------------------------------------
36 
37 void CrossGapsAssociationAlgorithm::GetListOfCleanClusters(const ClusterList *const pClusterList, ClusterVector &clusterVector) const
38 {
39  // ATTN May want to opt-out completely if no gap information available
40  // if (PandoraContentApi::GetGeometry(*this)->GetDetectorGapList().empty())
41  // return;
42 
43  for (const Cluster *const pCluster : *pClusterList)
44  {
45  if (pCluster->GetNCaloHits() < m_minClusterHits)
46  continue;
47 
48  if (1 + pCluster->GetOuterPseudoLayer() - pCluster->GetInnerPseudoLayer() < m_minClusterLayers)
49  continue;
50 
51  clusterVector.push_back(pCluster);
52  }
53 
54  std::sort(clusterVector.begin(), clusterVector.end(), LArClusterHelper::SortByInnerLayer);
55 }
56 
57 //------------------------------------------------------------------------------------------------------------------------------------------
58 
60 {
61  TwoDSlidingFitResultMap slidingFitResultMap;
62  const float slidingFitPitch(LArGeometryHelper::GetWireZPitch(this->GetPandora()));
63 
64  for (const Cluster *const pCluster : clusterVector)
65  {
66  try
67  {
68  (void)slidingFitResultMap.insert(
69  TwoDSlidingFitResultMap::value_type(pCluster, TwoDSlidingFitResult(pCluster, m_slidingFitWindow, slidingFitPitch)));
70  }
71  catch (StatusCodeException &)
72  {
73  }
74  }
75 
76  // ATTN This method assumes that clusters have been sorted by layer
77  for (ClusterVector::const_iterator iterI = clusterVector.begin(), iterIEnd = clusterVector.end(); iterI != iterIEnd; ++iterI)
78  {
79  const Cluster *const pInnerCluster = *iterI;
80  TwoDSlidingFitResultMap::const_iterator fitIterI = slidingFitResultMap.find(pInnerCluster);
81 
82  if (slidingFitResultMap.end() == fitIterI)
83  continue;
84 
85  for (ClusterVector::const_iterator iterJ = iterI, iterJEnd = clusterVector.end(); iterJ != iterJEnd; ++iterJ)
86  {
87  const Cluster *const pOuterCluster = *iterJ;
88 
89  if (pInnerCluster == pOuterCluster)
90  continue;
91 
92  TwoDSlidingFitResultMap::const_iterator fitIterJ = slidingFitResultMap.find(pOuterCluster);
93 
94  if (slidingFitResultMap.end() == fitIterJ)
95  continue;
96 
97  if (!this->AreClustersAssociated(fitIterI->second, fitIterJ->second))
98  continue;
99 
100  clusterAssociationMap[pInnerCluster].m_forwardAssociations.insert(pOuterCluster);
101  clusterAssociationMap[pOuterCluster].m_backwardAssociations.insert(pInnerCluster);
102  }
103  }
104 }
105 
106 //------------------------------------------------------------------------------------------------------------------------------------------
107 
108 bool CrossGapsAssociationAlgorithm::IsExtremalCluster(const bool isForward, const Cluster *const pCurrentCluster, const Cluster *const pTestCluster) const
109 {
110  const unsigned int currentLayer(isForward ? pCurrentCluster->GetOuterPseudoLayer() : pCurrentCluster->GetInnerPseudoLayer());
111  const unsigned int testLayer(isForward ? pTestCluster->GetOuterPseudoLayer() : pTestCluster->GetInnerPseudoLayer());
112 
113  if (isForward && ((testLayer > currentLayer) || ((testLayer == currentLayer) && LArClusterHelper::SortByNHits(pTestCluster, pCurrentCluster))))
114  return true;
115 
116  if (!isForward && ((testLayer < currentLayer) || ((testLayer == currentLayer) && LArClusterHelper::SortByNHits(pTestCluster, pCurrentCluster))))
117  return true;
118 
119  return false;
120 }
121 
122 //------------------------------------------------------------------------------------------------------------------------------------------
123 
125 {
126  if (outerFitResult.GetCluster()->GetInnerPseudoLayer() < innerFitResult.GetCluster()->GetInnerPseudoLayer())
127  throw pandora::StatusCodeException(STATUS_CODE_NOT_ALLOWED);
128 
129  if (outerFitResult.GetCluster()->GetInnerPseudoLayer() < innerFitResult.GetCluster()->GetOuterPseudoLayer())
130  return false;
131 
132  return (this->IsAssociated(innerFitResult.GetGlobalMaxLayerPosition(), innerFitResult.GetGlobalMaxLayerDirection(), outerFitResult) &&
133  this->IsAssociated(outerFitResult.GetGlobalMinLayerPosition(), outerFitResult.GetGlobalMinLayerDirection() * -1.f, innerFitResult));
134 }
135 
136 //------------------------------------------------------------------------------------------------------------------------------------------
137 
139  const CartesianVector &startPosition, const CartesianVector &startDirection, const TwoDSlidingFitResult &targetFitResult) const
140 {
141  const HitType hitType(LArClusterHelper::GetClusterHitType(targetFitResult.GetCluster()));
142  unsigned int nSamplingPoints(0), nGapSamplingPoints(0), nMatchedSamplingPoints(0), nUnmatchedSampleRun(0);
143 
144  for (unsigned int iSample = 0; iSample < m_maxSamplingPoints; ++iSample)
145  {
146  ++nSamplingPoints;
147  const CartesianVector samplingPoint(startPosition + startDirection * static_cast<float>(iSample) * m_sampleStepSize);
148 
149  if (LArGeometryHelper::IsInGap(this->GetPandora(), samplingPoint, hitType, m_gapTolerance))
150  {
151  ++nGapSamplingPoints;
152  nUnmatchedSampleRun = 0; // ATTN Choose to also reset run when entering gap region
153  continue;
154  }
155 
156  if (this->IsNearCluster(samplingPoint, targetFitResult))
157  {
158  ++nMatchedSamplingPoints;
159  nUnmatchedSampleRun = 0;
160  }
161  else if (++nUnmatchedSampleRun > m_maxUnmatchedSampleRun)
162  {
163  break;
164  }
165  }
166 
167  const float expectation((targetFitResult.GetGlobalMaxLayerPosition() - targetFitResult.GetGlobalMinLayerPosition()).GetMagnitude() / m_sampleStepSize);
168  const float matchedSamplingFraction(expectation > 0.f ? static_cast<float>(nMatchedSamplingPoints) / expectation : 0.f);
169 
170  if ((nMatchedSamplingPoints > m_minMatchedSamplingPoints) || (matchedSamplingFraction > m_minMatchedSamplingFraction))
171  return true;
172 
173  return false;
174 }
175 
176 //------------------------------------------------------------------------------------------------------------------------------------------
177 
178 bool CrossGapsAssociationAlgorithm::IsNearCluster(const CartesianVector &samplingPoint, const TwoDSlidingFitResult &targetFitResult) const
179 {
181  targetFitResult.GetLocalPosition(samplingPoint, rL, rT);
182 
183  CartesianVector fitPosition(0.f, 0.f, 0.f);
184 
185  if (STATUS_CODE_SUCCESS == targetFitResult.GetGlobalFitPosition(rL, fitPosition))
186  {
187  if ((fitPosition - samplingPoint).GetMagnitudeSquared() < m_maxOnClusterDistance * m_maxOnClusterDistance)
188  return true;
189  }
190 
191  CartesianVector fitPositionAtX(0.f, 0.f, 0.f);
192 
193  if (STATUS_CODE_SUCCESS == targetFitResult.GetGlobalFitPositionAtX(samplingPoint.GetX(), fitPositionAtX))
194  {
195  if ((fitPositionAtX - samplingPoint).GetMagnitudeSquared() < m_maxOnClusterDistance * m_maxOnClusterDistance)
196  return true;
197  }
198 
199  return false;
200 }
201 
202 //------------------------------------------------------------------------------------------------------------------------------------------
203 
204 StatusCode CrossGapsAssociationAlgorithm::ReadSettings(const TiXmlHandle xmlHandle)
205 {
206  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MinClusterHits", m_minClusterHits));
207 
208  PANDORA_RETURN_RESULT_IF_AND_IF(
209  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MinClusterLayers", m_minClusterLayers));
210 
211  PANDORA_RETURN_RESULT_IF_AND_IF(
212  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "SlidingFitWindow", m_slidingFitWindow));
213 
214  PANDORA_RETURN_RESULT_IF_AND_IF(
215  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxSamplingPoints", m_maxSamplingPoints));
216 
217  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "SampleStepSize", m_sampleStepSize));
218 
219  if (m_sampleStepSize < std::numeric_limits<float>::epsilon())
220  {
221  std::cout << "CrossGapsAssociationAlgorithm: Invalid value for SampleStepSize " << m_sampleStepSize << std::endl;
222  throw StatusCodeException(STATUS_CODE_INVALID_PARAMETER);
223  }
224 
225  PANDORA_RETURN_RESULT_IF_AND_IF(
226  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxUnmatchedSampleRun", m_maxUnmatchedSampleRun));
227 
228  PANDORA_RETURN_RESULT_IF_AND_IF(
229  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxOnClusterDistance", m_maxOnClusterDistance));
230 
231  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
232  XmlHelper::ReadValue(xmlHandle, "MinMatchedSamplingPoints", m_minMatchedSamplingPoints));
233 
234  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
235  XmlHelper::ReadValue(xmlHandle, "MinMatchedSamplingFraction", m_minMatchedSamplingFraction));
236 
237  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "GapTolerance", m_gapTolerance));
238 
240 }
241 
242 } // namespace lar_content
float m_minMatchedSamplingFraction
Minimum ratio between matched sampling points and expectation to declare association.
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...
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.
enum cvn::HType HitType
void PopulateClusterAssociationMap(const pandora::ClusterVector &clusterVector, ClusterAssociationMap &clusterAssociationMap) const
Populate the cluster association map.
virtual pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
Header file for the cross gaps association algorithm class.
std::unordered_map< const pandora::Cluster *, ClusterAssociation > ClusterAssociationMap
unsigned int m_minClusterLayers
The minimum allowed number of layers for a clean cluster.
intermediate_table::const_iterator const_iterator
static pandora::HitType GetClusterHitType(const pandora::Cluster *const pCluster)
Get the hit type associated with a two dimensional cluster.
pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
pandora::CartesianVector GetGlobalMinLayerDirection() const
Get global direction corresponding to the fit result in minimum fit layer.
static float GetWireZPitch(const pandora::Pandora &pandora, const float maxWirePitchDiscrepancy=0.01)
Return the wire pitch.
unsigned int m_minClusterHits
The minimum allowed number of hits in a clean cluster.
unsigned int m_maxSamplingPoints
The maximum number of extension sampling points considered per association check. ...
bool IsAssociated(const pandora::CartesianVector &startPosition, const pandora::CartesianVector &startDirection, const TwoDSlidingFitResult &targetFitResult) const
Sample points along the extrapolation from a starting position to a target fit result to declare clus...
unsigned int m_slidingFitWindow
The layer window for the sliding linear fits.
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.
float m_gapTolerance
The tolerance to use when querying whether a sampling point is in a gap, units cm.
Header file for the cluster helper class.
pandora::StatusCode GetGlobalFitPosition(const float rL, pandora::CartesianVector &position) const
Get global fit position for a given longitudinal coordinate.
unsigned int m_maxUnmatchedSampleRun
The maximum run of unmatched (and non-gap) samples to consider before stopping.
pandora::CartesianVector GetGlobalMinLayerPosition() const
Get global position corresponding to the fit result in minimum fit layer.
static int max(int a, int b)
std::unordered_map< const pandora::Cluster *, TwoDSlidingFitResult > TwoDSlidingFitResultMap
float m_maxOnClusterDistance
The maximum distance between a sampling point and sliding fit to target cluster.
const pandora::Cluster * GetCluster() const
Get the address of the cluster, if originally provided.
pandora::StatusCode GetGlobalFitPositionAtX(const float x, pandora::CartesianVector &position) const
Get global fit position for a given input x coordinate.
unsigned int m_minMatchedSamplingPoints
Minimum number of matched sampling points to declare association.
pandora::CartesianVector GetGlobalMaxLayerDirection() const
Get global direction corresponding to the fit result in maximum fit layer.
bool AreClustersAssociated(const TwoDSlidingFitResult &innerFitResult, const TwoDSlidingFitResult &outerFitResult) const
Determine whether two clusters are associated.
std::vector< art::Ptr< recob::Cluster > > ClusterVector
void GetLocalPosition(const pandora::CartesianVector &position, float &rL, float &rT) const
Get local sliding fit coordinates for a given global position.
bool IsNearCluster(const pandora::CartesianVector &samplingPoint, const TwoDSlidingFitResult &targetFitResult) const
Whether a sampling point lies near a target 2d sliding fit result.
pandora::CartesianVector GetGlobalMaxLayerPosition() const
Get global position corresponding to the fit result in maximum fit layer.
bool IsExtremalCluster(const bool isForward, const pandora::Cluster *const pCurrentCluster, const pandora::Cluster *const pTestCluster) const
Determine which of two clusters is extremal.
float m_sampleStepSize
The sampling step size used in association checks, units cm.
QTextStream & endl(QTextStream &s)
static bool SortByInnerLayer(const pandora::Cluster *const pLhs, const pandora::Cluster *const pRhs)
Sort clusters by inner layer, then position, then pulse-height.