9 #include "Pandora/AlgorithmHeaders.h" 22 CosmicRaySplittingAlgorithm::CosmicRaySplittingAlgorithm() :
23 m_clusterMinLength(10.
f),
24 m_halfWindowLayers(30),
26 m_maxCosSplittingAngle(0.9925
f),
27 m_minCosMergingAngle(0.94
f),
28 m_maxTransverseDisplacement(5.
f),
29 m_maxLongitudinalDisplacement(30.
f),
30 m_maxLongitudinalDisplacementSquared(m_maxLongitudinalDisplacement * m_maxLongitudinalDisplacement)
38 const ClusterList *pClusterList = NULL;
39 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::GetCurrentList(*
this, pClusterList));
50 ClusterSet splitClusters;
54 if (splitClusters.count(*bIter) > 0)
59 if (slidingFitResultMap.end() == bFitIter)
65 CartesianVector splitPosition(0.
f,0.
f,0.
f);
66 CartesianVector splitDirection1(0.
f,0.
f,0.
f);
67 CartesianVector splitDirection2(0.
f,0.
f,0.
f);
69 if (STATUS_CODE_SUCCESS != this->
FindBestSplitPosition(branchSlidingFitResult, splitPosition, splitDirection1, splitDirection2))
81 if (splitClusters.count(*rIter) > 0)
86 if (slidingFitResultMap.end() == rFitIter)
97 if (STATUS_CODE_SUCCESS != this->
ConfirmSplitPosition(branchSlidingFitResult, replacementSlidingFitResult,
98 splitPosition, splitDirection1, splitDirection2, lengthSquared1, lengthSquared2))
101 if (lengthSquared1 < bestLengthSquared1)
103 bestLengthSquared1 = lengthSquared1;
104 bestReplacementIter1 = rFitIter;
107 if (lengthSquared2 < bestLengthSquared2)
109 bestLengthSquared2 = lengthSquared2;
110 bestReplacementIter2 = rFitIter;
114 const Cluster *pReplacementCluster1 = NULL;
115 const Cluster *pReplacementCluster2 = NULL;
117 if (slidingFitResultMap.end() != bestReplacementIter1)
118 pReplacementCluster1 = bestReplacementIter1->first;
120 if (slidingFitResultMap.end() != bestReplacementIter2)
121 pReplacementCluster2 = bestReplacementIter2->first;
123 if (NULL == pReplacementCluster1 && NULL == pReplacementCluster2)
126 const Cluster *
const pBranchCluster = branchSlidingFitResult.
GetCluster();
129 if (pReplacementCluster1 && pReplacementCluster2)
131 if (!(this->
IdentifyCrossedTracks(pBranchCluster, pReplacementCluster1, pReplacementCluster2, splitPosition)))
134 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, this->
PerformDoubleSplit(pBranchCluster, pReplacementCluster1,
135 pReplacementCluster2, splitPosition, splitDirection1, splitDirection2));
138 else if (pReplacementCluster1)
140 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, this->
PerformSingleSplit(pBranchCluster, pReplacementCluster1,
141 splitPosition, splitDirection1, splitDirection2));
143 else if (pReplacementCluster2)
145 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, this->
PerformSingleSplit(pBranchCluster, pReplacementCluster2,
146 splitPosition, splitDirection2, splitDirection1));
150 if (pReplacementCluster1)
151 splitClusters.insert(pReplacementCluster1);
153 if (pReplacementCluster2)
154 splitClusters.insert(pReplacementCluster2);
156 splitClusters.insert(pBranchCluster);
159 return STATUS_CODE_SUCCESS;
168 const Cluster *
const pCluster = *iter;
173 clusterVector.push_back(pCluster);
188 if (slidingFitResultMap.end() == slidingFitResultMap.find(*iter))
194 if (!slidingFitResultMap.insert(TwoDSlidingFitResultMap::value_type(*iter, slidingFitResult)).second)
195 throw StatusCodeException(STATUS_CODE_FAILURE);
197 catch (StatusCodeException &statusCodeException)
199 if (STATUS_CODE_FAILURE == statusCodeException.GetStatusCode())
200 throw statusCodeException;
209 CartesianVector &splitDirection1, CartesianVector &splitDirection2)
const 213 bool foundSplit(
false);
219 float minL(0.
f), maxL(0.
f), minT(0.
f), maxT(0.
f);
223 const unsigned int nSamplingPoints =
static_cast<unsigned int>((maxL - minL)/
m_samplingPitch);
225 for (
unsigned int n = 0;
n < nSamplingPoints; ++
n)
227 const float alpha((0.5
f + static_cast<float>(
n)) / static_cast<float>(nSamplingPoints));
228 const float rL(minL + (maxL - minL) * alpha);
230 CartesianVector centralPosition(0.
f,0.
f,0.
f), forwardDirection(0.
f,0.
f,0.
f), backwardDirection(0.
f,0.
f,0.
f);
232 if ((STATUS_CODE_SUCCESS != branchSlidingFitResult.
GetGlobalFitPosition(rL, centralPosition)) ||
233 (STATUS_CODE_SUCCESS != branchSlidingFitResult.
GetGlobalFitDirection(rL + halfWindowLength, forwardDirection)) ||
234 (STATUS_CODE_SUCCESS != branchSlidingFitResult.
GetGlobalFitDirection(rL - halfWindowLength, backwardDirection)))
239 const float cosTheta(forwardDirection.GetDotProduct(backwardDirection));
241 if (cosTheta < splitCosTheta)
243 CartesianVector forwardPosition(0.
f,0.
f,0.
f);
244 CartesianVector backwardPosition(0.
f,0.
f,0.
f);
246 if ((STATUS_CODE_SUCCESS != branchSlidingFitResult.
GetGlobalFitPosition(rL + halfWindowLength, forwardPosition)) ||
247 (STATUS_CODE_SUCCESS != branchSlidingFitResult.
GetGlobalFitPosition(rL - halfWindowLength, backwardPosition)))
252 CartesianVector forwardVectorOutwards(forwardPosition - centralPosition);
253 CartesianVector backwardVectorOutwards(backwardPosition - centralPosition);
255 splitPosition = centralPosition;
256 splitDirection1 = (forwardDirection.GetDotProduct(forwardVectorOutwards) > 0.f) ? forwardDirection : forwardDirection * -1.
f;
257 splitDirection2 = (backwardDirection.GetDotProduct(backwardVectorOutwards) > 0.f) ? backwardDirection : backwardDirection * -1.
f;
258 splitCosTheta = cosTheta;
264 return STATUS_CODE_NOT_FOUND;
266 return STATUS_CODE_SUCCESS;
272 const TwoDSlidingFitResult &replacementSlidingFitResult,
const CartesianVector &splitPosition,
const CartesianVector &splitDirection1,
273 const CartesianVector &splitDirection2,
float &bestLengthSquared1,
float &bestLengthSquared2)
const 279 bool foundMatch(
false);
288 const CartesianVector branchDirection1(splitDirection1 * -1.
f);
289 const CartesianVector branchDirection2(splitDirection2 * -1.
f);
291 const float cosSplittingAngle(-splitDirection1.GetDotProduct(splitDirection2));
292 const float branchLengthSquared((branchVertex2 - branchVertex1).GetMagnitudeSquared());
293 const float replacementLengthSquared((maxPosition - minPosition).GetMagnitudeSquared());
296 for (
unsigned int iFwd = 0; iFwd < 2; ++iFwd)
298 const CartesianVector pAxis((0 == iFwd) ? (maxPosition - minPosition) : (minPosition - maxPosition));
299 const CartesianVector vtxPosition((0 == iFwd) ? minPosition : maxPosition);
300 const CartesianVector endPosition((0 == iFwd) ? maxPosition : minPosition);
301 const CartesianVector vtxDirection((0 == iFwd) ? (pAxis.GetDotProduct(minDirection) > 0 ? minDirection : minDirection * -1.f) :
302 (pAxis.GetDotProduct(maxDirection) > 0 ? maxDirection : maxDirection * -1.f));
308 const float lengthSquared((vtxPosition - splitPosition).GetMagnitudeSquared());
309 const float lengthSquared1((vtxPosition - branchVertex1).GetMagnitudeSquared());
310 const float lengthSquared2((vtxPosition - branchVertex2).GetMagnitudeSquared());
312 if (vtxDisplacement > endDisplacement)
315 if (
std::min(lengthSquared,
std::min(lengthSquared1,lengthSquared2)) >
std::min(branchLengthSquared, replacementLengthSquared))
319 float impactL(0.
f), impactT(0.
f);
323 impactL < -1.f || impactT >
std::max(1.5
f, 0.577
f * (1.
f + impactL)))
329 if (lengthSquared < bestLengthSquared1)
331 bestLengthSquared1 = lengthSquared;
339 if (lengthSquared < bestLengthSquared2)
341 bestLengthSquared2 = lengthSquared;
348 return STATUS_CODE_NOT_FOUND;
350 return STATUS_CODE_SUCCESS;
356 const CartesianVector &splitPosition,
const CartesianVector &forwardDirection,
const CartesianVector &backwardDirection)
const 358 CaloHitList caloHitListToMove;
359 this->
GetCaloHitListToMove(pBranchCluster, pReplacementCluster, splitPosition, forwardDirection, backwardDirection, caloHitListToMove);
361 CaloHitList caloHitListToKeep;
364 if (caloHitListToKeep.empty())
365 return PandoraContentApi::MergeAndDeleteClusters(*
this, pReplacementCluster, pBranchCluster);
367 return this->
SplitCluster(pBranchCluster, pReplacementCluster, caloHitListToMove);
373 const CartesianVector &splitPosition,
const CartesianVector &splitDirection1,
const CartesianVector &splitDirection2)
const 375 CaloHitList caloHitListToMove1, caloHitListToMove2;
376 this->
GetCaloHitListsToMove(pBranchCluster, splitPosition, splitDirection1, splitDirection2, caloHitListToMove1, caloHitListToMove2);
378 CaloHitList caloHitListToKeep1;
381 if (caloHitListToKeep1.empty())
382 return STATUS_CODE_FAILURE;
384 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, this->
SplitCluster(pBranchCluster, pReplacementCluster1, caloHitListToMove1));
386 CaloHitList caloHitListToKeep2;
389 if (caloHitListToKeep2.empty())
390 return PandoraContentApi::MergeAndDeleteClusters(*
this, pReplacementCluster2, pBranchCluster);
392 return this->
SplitCluster(pBranchCluster, pReplacementCluster2, caloHitListToMove2);
398 const CartesianVector &splitPosition,
const CartesianVector &forwardDirection,
const CartesianVector &backwardDirection,
399 CaloHitList &caloHitListToMove)
const 403 const CartesianVector vtxDirection((forwardPosition - vtxPosition).GetUnitVector());
405 CaloHitList caloHitListToSort, caloHitListToCheck;
406 pBranchCluster->GetOrderedCaloHitList().FillCaloHitList(caloHitListToSort);
410 const CaloHit *
const pCaloHit = *iter;
412 if (forwardDirection.GetDotProduct(pCaloHit->GetPositionVector() - forwardPosition) > 0.
f)
414 caloHitListToMove.push_back(pCaloHit);
416 else if(forwardDirection.GetDotProduct(pCaloHit->GetPositionVector() - vtxPosition) > -1.25
f)
418 caloHitListToCheck.push_back(pCaloHit);
426 const CaloHit *
const pCaloHit = *iter;
428 if (vtxDirection.GetCrossProduct(pCaloHit->GetPositionVector() - forwardPosition).GetMagnitude() >
429 backwardDirection.GetCrossProduct(pCaloHit->GetPositionVector() - forwardPosition).GetMagnitude())
432 const float lengthSquared((pCaloHit->GetPositionVector() - vtxPosition).GetMagnitudeSquared());
434 if (lengthSquared < closestLengthSquared)
435 closestLengthSquared = lengthSquared;
440 const CaloHit *
const pCaloHit = *iter;
442 if ((pCaloHit->GetPositionVector() - vtxPosition).GetMagnitudeSquared() >= closestLengthSquared)
443 caloHitListToMove.push_back(pCaloHit);
450 const CartesianVector &splitDirection1,
const CartesianVector &splitDirection2, CaloHitList &caloHitListToMove1, CaloHitList &caloHitListToMove2)
const 452 CaloHitList caloHitListToSort;
453 pBranchCluster->GetOrderedCaloHitList().FillCaloHitList(caloHitListToSort);
457 const CaloHit *
const pCaloHit = *iter;
459 const float displacement1(splitDirection1.GetDotProduct(pCaloHit->GetPositionVector() - splitPosition));
460 const float displacement2(splitDirection2.GetDotProduct(pCaloHit->GetPositionVector() - splitPosition));
462 if (displacement1 > displacement2)
464 caloHitListToMove1.push_back(pCaloHit);
468 caloHitListToMove2.push_back(pCaloHit);
475 const Cluster *
const pReplacementCluster2,
const pandora::CartesianVector &splitPosition)
const 477 CartesianVector branchVertex1(0.
f,0.
f,0.
f), branchVertex2(0.
f,0.
f,0.
f);
483 if ((replacementVertex2 - replacementVertex1).GetMagnitudeSquared() > (branchVertex2 - branchVertex1).GetMagnitudeSquared())
492 CaloHitList &caloHitListToKeep)
const 494 if (caloHitListToMove.empty())
495 return STATUS_CODE_FAILURE;
497 CaloHitList caloHitList;
498 pBranchCluster->GetOrderedCaloHitList().FillCaloHitList(caloHitList);
502 const CaloHit *
const pCaloHit = *iter;
504 if (caloHitListToMove.end() == std::find(caloHitListToMove.begin(), caloHitListToMove.end(), pCaloHit))
505 caloHitListToKeep.push_back(pCaloHit);
508 return STATUS_CODE_SUCCESS;
515 if (caloHitListToMove.empty())
516 return STATUS_CODE_FAILURE;
520 const CaloHit *
const pCaloHit = *iter;
521 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::RemoveFromCluster(*
this, pBranchCluster, pCaloHit));
522 PANDORA_RETURN_RESULT_IF(STATUS_CODE_SUCCESS, !=, PandoraContentApi::AddToCluster(*
this, pReplacementCluster, pCaloHit));
525 return STATUS_CODE_SUCCESS;
532 PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
535 PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
538 PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
542 return STATUS_CODE_INVALID_PARAMETER;
544 PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
547 PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
550 PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
553 PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
557 return STATUS_CODE_SUCCESS;
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 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 GetCaloHitListsToMove(const pandora::Cluster *const pBranchCluster, const pandora::CartesianVector &splitPosition, const pandora::CartesianVector &splitDirection1, const pandora::CartesianVector &splitDirection2, pandora::CaloHitList &caloHitList1, pandora::CaloHitList &caloHitList2) const
Get lists of calo hits to move in order to split a branch cluster into two segments for case of two r...
bool IdentifyCrossedTracks(const pandora::Cluster *const pBranchCluster, const pandora::Cluster *const pReplacementCluster1, const pandora::Cluster *const pReplacementCluster2, const pandora::CartesianVector &splitPosition) const
Identify crossed tracks formed from the branch cluster and its replacement cluster.
Header file for the cosmic ray splitting algorithm class.
float m_maxLongitudinalDisplacement
maximum longitudinal displacement of associated clusters
void GetCaloHitListToMove(const pandora::Cluster *const pBranchCluster, const pandora::Cluster *const pReplacementCluster, const pandora::CartesianVector &splitPosition, const pandora::CartesianVector &forwardDirection, const pandora::CartesianVector &backwardDirection, pandora::CaloHitList &caloHitList) const
Get list of calo hits to move in order to split a branch cluster into two segments for case of one re...
static void GetImpactParameters(const LArPointingCluster::Vertex &pointingVertex, const LArPointingCluster::Vertex &targetVertex, float &longitudinal, float &transverse)
Calculate impact parameters between a pair of pointing vertices.
float GetLayerFitHalfWindowLength() const
Get the layer fit half window length.
pandora::StatusCode Run()
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.
std::unordered_map< const pandora::Cluster *, TwoDSlidingFitResult > TwoDSlidingFitResultMap
float m_clusterMinLength
minimum length of clusters for this algorithm
pandora::StatusCode SplitCluster(const pandora::Cluster *const pBranchCluster, const pandora::Cluster *const pReplacementCluster, const pandora::CaloHitList &caloHitListToMove) const
Split the branch cluster and add hits to the replacement cluster.
Header file for the geometry helper class.
pandora::StatusCode PerformSingleSplit(const pandora::Cluster *const pBranchCluster, const pandora::Cluster *const pReplacementCluster, const pandora::CartesianVector &splitPosition, const pandora::CartesianVector &forwardDirection, const pandora::CartesianVector &backwardDirection) const
Split a branch cluster for case of one replacement cluster.
float m_minCosMergingAngle
largest relative angle allowed when merging clusters
Header file for the cluster helper class.
pandora::StatusCode GetCaloHitListToKeep(const pandora::Cluster *const pBranchCluster, const pandora::CaloHitList &caloHitListToMove, pandora::CaloHitList &caloHitListToKeep) const
Split the branch cluster and add hits to the replacement cluster.
pandora::StatusCode GetGlobalFitPosition(const float rL, pandora::CartesianVector &position) const
Get global fit position for a given longitudinal coordinate.
float m_maxCosSplittingAngle
smallest scatter angle allowed when splitting cluster
float m_maxLongitudinalDisplacementSquared
longitudinal displacement squared
pandora::StatusCode PerformDoubleSplit(const pandora::Cluster *const pBranchCluster, const pandora::Cluster *const pReplacementCluster1, const pandora::Cluster *const pReplacementCluster2, const pandora::CartesianVector &splitPosition, const pandora::CartesianVector &splitDirection1, const pandora::CartesianVector &splitDirection2) const
Split a branch cluster for case of two replacement clusters.
pandora::CartesianVector GetGlobalMinLayerPosition() const
Get global position corresponding to the fit result in minimum fit layer.
std::vector< art::Ptr< recob::Cluster > > ClusterVector
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 BuildSlidingFitResultMap(const pandora::ClusterVector &clusterVector, TwoDSlidingFitResultMap &slidingFitResultMap) const
Build the map of sliding fit results.
float m_maxTransverseDisplacement
maximum transverse displacement of associated clusters
const pandora::Cluster * GetCluster() const
Get the address of the cluster, if originally provided.
pandora::StatusCode GetGlobalFitDirection(const float rL, pandora::CartesianVector &direction) const
Get global fit direction for a given longitudinal coordinate.
pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
pandora::StatusCode ConfirmSplitPosition(const TwoDSlidingFitResult &branchSlidingFitResult, const TwoDSlidingFitResult &replacementSlidingFitResult, const pandora::CartesianVector &splitPosition, const pandora::CartesianVector &splitDirection1, const pandora::CartesianVector &splitDirection2, float &lengthSquared1, float &lengthSquared2) const
Find a second replacement cluster that aligns with the scatter of the first branch cluster...
pandora::CartesianVector GetGlobalMaxLayerDirection() const
Get global direction corresponding to the fit result in maximum fit layer.
static float GetLengthSquared(const pandora::Cluster *const pCluster)
Get length squared of cluster.
pandora::StatusCode FindBestSplitPosition(const TwoDSlidingFitResult &slidingFitResult, pandora::CartesianVector &splitPosition, pandora::CartesianVector &splitDirection1, pandora::CartesianVector &splitDirection2) const
Find the position of greatest scatter along a sliding linear fit.
unsigned int m_halfWindowLayers
number of layers to use for half-window of sliding fit
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.
void GetLocalPosition(const pandora::CartesianVector &position, float &rL, float &rT) const
Get local sliding fit coordinates for a given global position.
pandora::CartesianVector GetGlobalMaxLayerPosition() const
Get global position corresponding to the fit result in maximum fit layer.
TwoDSlidingFitResult class.
float m_samplingPitch
sampling pitch for walking along sliding linear fit
static float GetClosestDistance(const pandora::ClusterList &clusterList1, const pandora::ClusterList &clusterList2)
Get closest distance between clusters in a pair of cluster lists.