LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
CrossGapsAssociationAlgorithm.cc
Go to the documentation of this file.
1 
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 
63  for (const Cluster *const pCluster : clusterVector)
64  {
65  try
66  {
67  const float slidingFitPitch(LArGeometryHelper::GetWirePitch(this->GetPandora(), LArClusterHelper::GetClusterHitType(pCluster)));
68  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  const float ratio{LArGeometryHelper::GetWirePitchRatio(this->GetPandora(), hitType)};
143  const float sampleStepSizeAdjusted{ratio * m_sampleStepSize};
144  unsigned int nSamplingPoints(0), nGapSamplingPoints(0), nMatchedSamplingPoints(0), nUnmatchedSampleRun(0);
145 
146  for (unsigned int iSample = 0; iSample < m_maxSamplingPoints; ++iSample)
147  {
148  ++nSamplingPoints;
149  const CartesianVector samplingPoint(startPosition + startDirection * static_cast<float>(iSample) * sampleStepSizeAdjusted);
150 
151  if (LArGeometryHelper::IsInGap(this->GetPandora(), samplingPoint, hitType, m_gapTolerance))
152  {
153  ++nGapSamplingPoints;
154  nUnmatchedSampleRun = 0; // ATTN Choose to also reset run when entering gap region
155  continue;
156  }
157 
158  if (this->IsNearCluster(samplingPoint, targetFitResult))
159  {
160  ++nMatchedSamplingPoints;
161  nUnmatchedSampleRun = 0;
162  }
163  else if (++nUnmatchedSampleRun > m_maxUnmatchedSampleRun)
164  {
165  break;
166  }
167  }
168 
169  const float expectation(
170  (targetFitResult.GetGlobalMaxLayerPosition() - targetFitResult.GetGlobalMinLayerPosition()).GetMagnitude() / sampleStepSizeAdjusted);
171  const float matchedSamplingFraction(expectation > 0.f ? static_cast<float>(nMatchedSamplingPoints) / expectation : 0.f);
172 
173  if ((nMatchedSamplingPoints > m_minMatchedSamplingPoints) || (matchedSamplingFraction > m_minMatchedSamplingFraction))
174  return true;
175 
176  return false;
177 }
178 
179 //------------------------------------------------------------------------------------------------------------------------------------------
180 
181 bool CrossGapsAssociationAlgorithm::IsNearCluster(const CartesianVector &samplingPoint, const TwoDSlidingFitResult &targetFitResult) const
182 {
183  const HitType hitType(LArClusterHelper::GetClusterHitType(targetFitResult.GetCluster()));
184  const float ratio{LArGeometryHelper::GetWirePitchRatio(this->GetPandora(), hitType)};
185  const float maxOnClusterDistanceAdjusted{ratio * m_maxOnClusterDistance};
186 
187  float rL(std::numeric_limits<float>::max()), rT(std::numeric_limits<float>::max());
188  targetFitResult.GetLocalPosition(samplingPoint, rL, rT);
189 
190  CartesianVector fitPosition(0.f, 0.f, 0.f);
191 
192  if (STATUS_CODE_SUCCESS == targetFitResult.GetGlobalFitPosition(rL, fitPosition))
193  {
194  if ((fitPosition - samplingPoint).GetMagnitudeSquared() < maxOnClusterDistanceAdjusted * maxOnClusterDistanceAdjusted)
195  return true;
196  }
197 
198  CartesianVector fitPositionAtX(0.f, 0.f, 0.f);
199 
200  if (STATUS_CODE_SUCCESS == targetFitResult.GetGlobalFitPositionAtX(samplingPoint.GetX(), fitPositionAtX))
201  {
202  if ((fitPositionAtX - samplingPoint).GetMagnitudeSquared() < maxOnClusterDistanceAdjusted * maxOnClusterDistanceAdjusted)
203  return true;
204  }
205 
206  return false;
207 }
208 
209 //------------------------------------------------------------------------------------------------------------------------------------------
210 
211 StatusCode CrossGapsAssociationAlgorithm::ReadSettings(const TiXmlHandle xmlHandle)
212 {
213  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MinClusterHits", m_minClusterHits));
214 
215  PANDORA_RETURN_RESULT_IF_AND_IF(
216  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MinClusterLayers", m_minClusterLayers));
217 
218  PANDORA_RETURN_RESULT_IF_AND_IF(
219  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "SlidingFitWindow", m_slidingFitWindow));
220 
221  PANDORA_RETURN_RESULT_IF_AND_IF(
222  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxSamplingPoints", m_maxSamplingPoints));
223 
224  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "SampleStepSize", m_sampleStepSize));
225 
226  if (m_sampleStepSize < std::numeric_limits<float>::epsilon())
227  {
228  std::cout << "CrossGapsAssociationAlgorithm: Invalid value for SampleStepSize " << m_sampleStepSize << std::endl;
229  throw StatusCodeException(STATUS_CODE_INVALID_PARAMETER);
230  }
231 
232  PANDORA_RETURN_RESULT_IF_AND_IF(
233  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxUnmatchedSampleRun", m_maxUnmatchedSampleRun));
234 
235  PANDORA_RETURN_RESULT_IF_AND_IF(
236  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxOnClusterDistance", m_maxOnClusterDistance));
237 
238  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
239  XmlHelper::ReadValue(xmlHandle, "MinMatchedSamplingPoints", m_minMatchedSamplingPoints));
240 
241  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=,
242  XmlHelper::ReadValue(xmlHandle, "MinMatchedSamplingFraction", m_minMatchedSamplingFraction));
243 
244  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "GapTolerance", m_gapTolerance));
245 
247 }
248 
249 } // 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.
static float GetWirePitchRatio(const pandora::Pandora &pandora, const pandora::HitType view)
Return the ratio of the wire pitch of the specified view to the minimum wire pitch for the detector...
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.
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.
TFile f
Definition: plotHisto.C:6
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 float GetWirePitch(const pandora::Pandora &pandora, const pandora::HitType view, const float maxWirePitchDiscrepancy=0.01)
Return the wire pitch.
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.
HitType
Definition: HitType.h:12
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.
static bool SortByInnerLayer(const pandora::Cluster *const pLhs, const pandora::Cluster *const pRhs)
Sort clusters by inner layer, then position, then pulse-height.