LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
CrossedTrackSplittingAlgorithm.cc
Go to the documentation of this file.
1 
9 #include "Pandora/AlgorithmHeaders.h"
10 
13 
15 
17 
18 using namespace pandora;
19 
20 namespace lar_content
21 {
22 
23 CrossedTrackSplittingAlgorithm::CrossedTrackSplittingAlgorithm() :
24  m_maxClusterSeparation(2.f),
25  m_maxClusterSeparationSquared(m_maxClusterSeparation * m_maxClusterSeparation),
26  m_minCosRelativeAngle(0.966f),
27  m_searchRegion1D(2.f)
28 {
29 }
30 
31 //------------------------------------------------------------------------------------------------------------------------------------------
32 
34 {
35  // ATTN Don't need to update nearby cluster map after cluster merges because algorithm does not revisit processed clusters
36  HitToClusterMap hitToClusterMap;
37  CaloHitList allCaloHits;
38 
39  for (const Cluster *const pCluster : clusterVector)
40  {
41  CaloHitList daughterHits;
42  pCluster->GetOrderedCaloHitList().FillCaloHitList(daughterHits);
43  allCaloHits.insert(allCaloHits.end(), daughterHits.begin(), daughterHits.end());
44 
45  for (const CaloHit *const pCaloHit : daughterHits)
46  (void)hitToClusterMap.insert(HitToClusterMap::value_type(pCaloHit, pCluster));
47  }
48 
49  HitKDTree2D kdTree;
50  HitKDNode2DList hitKDNode2DList;
51 
52  KDTreeBox hitsBoundingRegion2D(fill_and_bound_2d_kd_tree(allCaloHits, hitKDNode2DList));
53  kdTree.build(hitKDNode2DList, hitsBoundingRegion2D);
54 
55  for (const Cluster *const pCluster : clusterVector)
56  {
57  CaloHitList daughterHits;
58  pCluster->GetOrderedCaloHitList().FillCaloHitList(daughterHits);
59 
60  for (const CaloHit *const pCaloHit : daughterHits)
61  {
63 
64  HitKDNode2DList found;
65  kdTree.search(searchRegionHits, found);
66 
67  for (const auto &hit : found)
68  (void)m_nearbyClusters[pCluster].insert(hitToClusterMap.at(hit.data));
69  }
70  }
71 
72  return STATUS_CODE_SUCCESS;
73 }
74 
75 //------------------------------------------------------------------------------------------------------------------------------------------
76 
78 {
79  m_nearbyClusters.clear();
80 
81  return STATUS_CODE_SUCCESS;
82 }
83 
84 //------------------------------------------------------------------------------------------------------------------------------------------
85 
87  const TwoDSlidingFitResult &slidingFitResult2, CartesianVector &splitPosition, CartesianVector &firstDirection, CartesianVector &secondDirection) const
88 {
89  // Use cached results from kd-tree to avoid expensive calculations
90  if (!m_nearbyClusters.count(slidingFitResult1.GetCluster()) || !m_nearbyClusters.count(slidingFitResult2.GetCluster()) ||
91  !m_nearbyClusters.at(slidingFitResult1.GetCluster()).count(slidingFitResult2.GetCluster()) ||
92  !m_nearbyClusters.at(slidingFitResult2.GetCluster()).count(slidingFitResult1.GetCluster()))
93  {
94  return STATUS_CODE_NOT_FOUND;
95  }
96 
97  // Identify crossed-track topology and find candidate intersection positions
98  const CartesianVector &minPosition1(slidingFitResult1.GetGlobalMinLayerPosition());
99  const CartesianVector &maxPosition1(slidingFitResult1.GetGlobalMaxLayerPosition());
100 
101  const CartesianVector &minPosition2(slidingFitResult2.GetGlobalMinLayerPosition());
102  const CartesianVector &maxPosition2(slidingFitResult2.GetGlobalMaxLayerPosition());
103 
104  if (LArClusterHelper::GetClosestDistance(minPosition1, slidingFitResult2.GetCluster()) < 2.f * m_maxClusterSeparation ||
105  LArClusterHelper::GetClosestDistance(maxPosition1, slidingFitResult2.GetCluster()) < 2.f * m_maxClusterSeparation ||
106  LArClusterHelper::GetClosestDistance(minPosition2, slidingFitResult1.GetCluster()) < 2.f * m_maxClusterSeparation ||
107  LArClusterHelper::GetClosestDistance(maxPosition2, slidingFitResult1.GetCluster()) < 2.f * m_maxClusterSeparation)
108  {
109  return STATUS_CODE_NOT_FOUND;
110  }
111 
112  if (LArClusterHelper::GetClosestDistance(slidingFitResult1.GetCluster(), slidingFitResult2.GetCluster()) > m_maxClusterSeparation)
113  return STATUS_CODE_NOT_FOUND;
114 
115  CartesianPointVector candidateVector;
116  this->FindCandidateSplitPositions(slidingFitResult1.GetCluster(), slidingFitResult2.GetCluster(), candidateVector);
117 
118  if (candidateVector.empty())
119  return STATUS_CODE_NOT_FOUND;
120 
121  // Loop over candidate positions and find best split position
122  bool foundSplit(false);
123  float closestSeparationSquared(std::numeric_limits<float>::max());
124 
125  const float halfWindowLength1(slidingFitResult1.GetLayerFitHalfWindowLength());
126  const float halfWindowLength2(slidingFitResult2.GetLayerFitHalfWindowLength());
127 
128  for (CartesianPointVector::const_iterator iter = candidateVector.begin(), iterEnd = candidateVector.end(); iter != iterEnd; ++iter)
129  {
130  const CartesianVector &candidatePosition(*iter);
131 
132  // Projections onto first cluster
133  float rL1(0.f), rT1(0.f);
134  CartesianVector R1(0.f, 0.f, 0.f);
135  CartesianVector F1(0.f, 0.f, 0.f);
136  CartesianVector B1(0.f, 0.f, 0.f);
137 
138  if (STATUS_CODE_SUCCESS != slidingFitResult1.GetGlobalFitProjection(candidatePosition, R1))
139  continue;
140 
141  slidingFitResult1.GetLocalPosition(R1, rL1, rT1);
142  if ((STATUS_CODE_SUCCESS != slidingFitResult1.GetGlobalFitPosition(rL1 + halfWindowLength1, F1)) ||
143  (STATUS_CODE_SUCCESS != slidingFitResult1.GetGlobalFitPosition(rL1 - halfWindowLength1, B1)))
144  {
145  continue;
146  }
147 
148  // Projections onto second cluster
149  float rL2(0.f), rT2(0.f);
150  CartesianVector R2(0.f, 0.f, 0.f);
151  CartesianVector F2(0.f, 0.f, 0.f);
152  CartesianVector B2(0.f, 0.f, 0.f);
153 
154  if (STATUS_CODE_SUCCESS != slidingFitResult2.GetGlobalFitProjection(candidatePosition, R2))
155  continue;
156 
157  slidingFitResult2.GetLocalPosition(R2, rL2, rT2);
158  if ((STATUS_CODE_SUCCESS != slidingFitResult2.GetGlobalFitPosition(rL2 + halfWindowLength2, F2)) ||
159  (STATUS_CODE_SUCCESS != slidingFitResult2.GetGlobalFitPosition(rL2 - halfWindowLength2, B2)))
160  {
161  continue;
162  }
163 
164  // Calculate average position
165  const CartesianVector C0((R1 + R2) * 0.5);
166 
167  // Calculate intersected position:
168  // ==============================
169  // First cluster gives set of points: B1->R1->F1
170  // Second cluster gives set of points: B2->R2->F2
171  //
172  // Try swapping B1 with B2 to see if this gives intersecting straight lines:
173  //
174  // F1 F2 a2 b1
175  // | | | |
176  // | | | |
177  // R1 R2 R1 R2
178  // | | | |
179  // | | | |
180  // B1 B2 a1 b2
181 
182  // First straight line is a1->R1->b1
183  // Second straight line is a2->R2->b2
184 
185  const CartesianVector a1(B1);
186  const CartesianVector a2(F1);
187 
188  for (unsigned int iForward = 0; iForward < 2; ++iForward)
189  {
190  const CartesianVector b1((0 == iForward) ? F2 : B2);
191  const CartesianVector b2((0 == iForward) ? B2 : F2);
192 
193  const CartesianVector s1((b1 - R2).GetUnitVector());
194  const CartesianVector t1((R1 - a1).GetUnitVector());
195  const CartesianVector s2((b2 - R2).GetUnitVector());
196  const CartesianVector t2((R1 - a2).GetUnitVector());
197 
198  if (s1.GetDotProduct(t1) < std::max(m_minCosRelativeAngle, -s1.GetDotProduct(s2)) ||
199  s2.GetDotProduct(t2) < std::max(m_minCosRelativeAngle, -t1.GetDotProduct(t2)))
200  continue;
201 
202  const CartesianVector p1((b1 - a1).GetUnitVector());
203  const CartesianVector p2((b2 - a2).GetUnitVector());
204 
205  float mu1(0.f), mu2(0.f);
206  CartesianVector C1(0.f, 0.f, 0.f);
207 
208  try
209  {
210  LArPointingClusterHelper::GetIntersection(a1, p1, a2, p2, C1, mu1, mu2);
211  }
212  catch (const StatusCodeException &)
213  {
214  continue;
215  }
216 
217  if (mu1 < 0.f || mu2 < 0.f || mu1 > (b1 - a1).GetMagnitude() || mu2 > (b2 - a2).GetMagnitude())
218  continue;
219 
220  const float thisSeparationSquared((C0 - C1).GetMagnitudeSquared());
221 
222  if (thisSeparationSquared < closestSeparationSquared)
223  {
224  closestSeparationSquared = thisSeparationSquared;
225  splitPosition = (C0 + C1) * 0.5;
226  firstDirection = t2 * -1.f;
227  secondDirection = t1;
228  foundSplit = true;
229  }
230  }
231  }
232 
233  if (!foundSplit)
234  return STATUS_CODE_NOT_FOUND;
235 
236  return STATUS_CODE_SUCCESS;
237 }
238 
239 //------------------------------------------------------------------------------------------------------------------------------------------
240 
242  const Cluster *const pCluster1, const Cluster *const pCluster2, CartesianPointVector &candidateVector) const
243 {
244  // ATTN The following is double-double counting
245  CaloHitList caloHitList1, caloHitList2;
246  pCluster1->GetOrderedCaloHitList().FillCaloHitList(caloHitList1);
247  pCluster2->GetOrderedCaloHitList().FillCaloHitList(caloHitList2);
248 
249  CaloHitVector caloHitVector1(caloHitList1.begin(), caloHitList1.end()), caloHitVector2(caloHitList2.begin(), caloHitList2.end());
250  std::sort(caloHitVector1.begin(), caloHitVector1.end(), LArClusterHelper::SortHitsByPosition);
251  std::sort(caloHitVector2.begin(), caloHitVector2.end(), LArClusterHelper::SortHitsByPosition);
252 
253  for (const CaloHit *const pCaloHit : caloHitVector1)
254  {
255  const CartesianVector position1(pCaloHit->GetPositionVector());
256  const CartesianVector position2(LArClusterHelper::GetClosestPosition(position1, pCluster2));
257 
258  if ((position1 - position2).GetMagnitudeSquared() < m_maxClusterSeparationSquared)
259  candidateVector.push_back((position1 + position2) * 0.5);
260  }
261 
262  for (const CaloHit *const pCaloHit : caloHitVector2)
263  {
264  const CartesianVector position2(pCaloHit->GetPositionVector());
265  const CartesianVector position1(LArClusterHelper::GetClosestPosition(position2, pCluster1));
266 
267  if ((position2 - position1).GetMagnitudeSquared() < m_maxClusterSeparationSquared)
268  candidateVector.push_back((position2 + position1) * 0.5);
269  }
270 }
271 
272 //------------------------------------------------------------------------------------------------------------------------------------------
273 
274 StatusCode CrossedTrackSplittingAlgorithm::ReadSettings(const TiXmlHandle xmlHandle)
275 {
276  PANDORA_RETURN_RESULT_IF_AND_IF(
277  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MaxClusterSeparation", m_maxClusterSeparation));
279 
280  PANDORA_RETURN_RESULT_IF_AND_IF(
281  STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "MinCosRelativeAngle", m_minCosRelativeAngle));
282 
283  PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle, "SearchRegion1D", m_searchRegion1D));
284 
286 }
287 
288 } // namespace lar_content
Header file for the kd tree linker algo template class.
pandora::StatusCode FindBestSplitPosition(const TwoDSlidingFitResult &slidingFit1, const TwoDSlidingFitResult &slidingFit2, pandora::CartesianVector &splitPosition, pandora::CartesianVector &direction1, pandora::CartesianVector &direction2) const
Find the best split position and direction for a pair of clusters.
TTree * t1
Definition: plottest35.C:26
static void GetIntersection(const LArPointingCluster::Vertex &firstVertex, const LArPointingCluster::Vertex &secondVertex, pandora::CartesianVector &intersectPosition, float &firstDisplacement, float &secondDisplacement)
Get intersection of two vertices.
Box structure used to define 2D field. It&#39;s used in KDTree building step to divide the detector space...
Header file for the crossed track splitting algorithm class.
float GetLayerFitHalfWindowLength() const
Get the layer fit half window length.
intermediate_table::const_iterator const_iterator
float m_minCosRelativeAngle
maximum relative angle between tracks after un-crossing
float m_maxClusterSeparationSquared
maximum separation of two clusters (squared)
TFile f
Definition: plotHisto.C:6
virtual pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
#define a2
std::unordered_map< const pandora::CaloHit *, const pandora::Cluster * > HitToClusterMap
static bool SortHitsByPosition(const pandora::CaloHit *const pLhs, const pandora::CaloHit *const pRhs)
Sort calo hits by their position (use Z, followed by X, followed by Y)
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.
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".
pandora::CartesianVector GetGlobalMinLayerPosition() const
Get global position corresponding to the fit result in minimum fit layer.
pandora::StatusCode TidyUpStep()
Tidy up any information cached in e.g. the preparation step.
void FindCandidateSplitPositions(const pandora::Cluster *const pCluster1, const pandora::Cluster *const pCluster2, pandora::CartesianPointVector &candidateVector) const
Find average positions of pairs of hits within a maximum separation.
Detector simulation of raw signals on wires.
TTree * t2
Definition: plottest35.C:36
const pandora::Cluster * GetCluster() const
Get the address of the cluster, if originally provided.
pandora::StatusCode PreparationStep(const pandora::ClusterVector &clusterVector)
Perform any preparatory actions, such as caching information for subsequent expensive calculations...
pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
float m_maxClusterSeparation
maximum separation of two clusters
pandora::StatusCode GetGlobalFitProjection(const pandora::CartesianVector &inputPosition, pandora::CartesianVector &projectedPosition) const
Get projected position on global fit for a given position vector.
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
ClusterToClustersMap m_nearbyClusters
The nearby clusters map.
std::vector< art::Ptr< recob::Cluster > > ClusterVector
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.
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 GetLocalPosition(const pandora::CartesianVector &position, float &rL, float &rT) const
Get local sliding fit coordinates for a given global position.
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...
float m_searchRegion1D
Search region, applied to each dimension, for look-up from kd-trees.
pandora::CartesianVector GetGlobalMaxLayerPosition() const
Get global position corresponding to the fit result in maximum fit layer.
#define a1
static float GetClosestDistance(const pandora::ClusterList &clusterList1, const pandora::ClusterList &clusterList2)
Get closest distance between clusters in a pair of cluster lists.