9 #include "Pandora/AlgorithmHeaders.h" 23 CosmicRayTaggingTool::CosmicRayTaggingTool() :
25 m_angularUncertainty(5.
f),
26 m_positionalUncertainty(3.
f),
27 m_maxAssociationDist(3.
f * 18.
f),
33 m_maxNeutrinoCosTheta(0.2
f),
34 m_minCosmicCosTheta(0.6
f),
35 m_maxCosmicCurvature(0.04
f),
66 return STATUS_CODE_INVALID_PARAMETER;
69 return STATUS_CODE_SUCCESS;
76 if (this->GetPandora().GetSettings()->ShouldDisplayAlgorithmInfo())
77 std::cout <<
"----> Running Algorithm Tool: " << this->GetInstanceName() <<
", " << this->GetType() <<
std::endl;
80 const LArTPCMap &larTPCMap(this->GetPandora().
GetGeometry()->GetLArTPCMap());
81 const LArTPC *
const pFirstLArTPC(larTPCMap.begin()->second);
83 float parentMinX(pFirstLArTPC->GetCenterX() - 0.5f * pFirstLArTPC->GetWidthX());
84 float parentMaxX(pFirstLArTPC->GetCenterX() + 0.5f * pFirstLArTPC->GetWidthX());
85 float parentMinY(pFirstLArTPC->GetCenterY() - 0.5f * pFirstLArTPC->GetWidthY());
86 float parentMaxY(pFirstLArTPC->GetCenterY() + 0.5f * pFirstLArTPC->GetWidthY());
87 float parentMinZ(pFirstLArTPC->GetCenterZ() - 0.5f * pFirstLArTPC->GetWidthZ());
88 float parentMaxZ(pFirstLArTPC->GetCenterZ() + 0.5f * pFirstLArTPC->GetWidthZ());
90 for (
const LArTPCMap::value_type &mapEntry : larTPCMap)
92 const LArTPC *
const pLArTPC(mapEntry.second);
93 parentMinX =
std::min(parentMinX, pLArTPC->GetCenterX() - 0.5f * pLArTPC->GetWidthX());
94 parentMaxX =
std::max(parentMaxX, pLArTPC->GetCenterX() + 0.5f * pLArTPC->GetWidthX());
95 parentMinY =
std::min(parentMinY, pLArTPC->GetCenterY() - 0.5f * pLArTPC->GetWidthY());
96 parentMaxY =
std::max(parentMaxY, pLArTPC->GetCenterY() + 0.5f * pLArTPC->GetWidthY());
97 parentMinZ =
std::min(parentMinZ, pLArTPC->GetCenterZ() - 0.5f * pLArTPC->GetWidthZ());
98 parentMaxZ =
std::max(parentMaxZ, pLArTPC->GetCenterZ() + 0.5f * pLArTPC->GetWidthZ());
112 this->
SliceEvent(parentCosmicRayPfos, pfoAssociationMap, pfoToSliceIdMap);
115 this->
GetCRCandidates(parentCosmicRayPfos, pfoToSliceIdMap, candidates);
127 this->
GetNeutrinoSlices(candidates, pfoToInTimeMap, pfoToIsContainedMap, neutrinoSliceSet);
130 this->
TagCRMuons(candidates, pfoToInTimeMap, pfoToIsTopToBottomMap, neutrinoSliceSet, pfoToIsLikelyCRMuonMap);
132 for (
const ParticleFlowObject *
const pPfo : parentCosmicRayPfos)
134 if (!pfoToIsLikelyCRMuonMap.at(pPfo))
135 ambiguousPfos.push_back(pPfo);
143 pCluster3D =
nullptr;
144 ClusterList clusters3D;
148 if (clusters3D.empty() || (clusters3D.front()->GetNCaloHits() <
m_minimumHits))
151 pCluster3D = clusters3D.front();
161 const float layerPitch(pFirstLArTPC->GetWirePitchW());
165 for (
const ParticleFlowObject *
const pPfo : parentCosmicRayPfos)
167 const pandora::Cluster *pCluster(
nullptr);
171 (void)pfoToSlidingFitsMap.insert(PfoToSlidingFitsMap::value_type(pPfo,
175 for (
const ParticleFlowObject *
const pPfo1 : parentCosmicRayPfos)
178 if (pfoToSlidingFitsMap.end() == iter1)
183 for (
const ParticleFlowObject *
const pPfo2 : parentCosmicRayPfos)
189 if (pfoToSlidingFitsMap.end() == iter2)
207 PfoList &pfoList1(pfoAssociationMap[pPfo1]), &pfoList2(pfoAssociationMap[pPfo2]);
209 if (pfoList1.end() == std::find(pfoList1.begin(), pfoList1.end(), pPfo2))
210 pfoList1.push_back(pPfo2);
212 if (pfoList2.end() == std::find(pfoList2.begin(), pfoList2.end(), pPfo1))
213 pfoList2.push_back(pPfo1);
221 const CartesianVector &endPoint1,
const CartesianVector &endDir1,
const CartesianVector &endPoint2,
const CartesianVector &endDir2)
const 224 const CartesianVector
n(endDir1.GetUnitVector());
225 const CartesianVector
m(endDir2.GetUnitVector());
226 const CartesianVector
a(endPoint2 - endPoint1);
227 const float b(
n.GetDotProduct(
m));
230 if (std::fabs(
b - 1.
f) < std::numeric_limits<float>::epsilon())
234 const float lambda((
n -
m *
b).GetDotProduct(a) / (1.
f - b * b));
237 const float mu((
n * b -
m).GetDotProduct(a) / (1.
f - b * b));
244 if ((lambda < -maxVertexUncertainty) || (mu < -maxVertexUncertainty) || (lambda >
m_maxAssociationDist + maxVertexUncertainty) ||
251 const CartesianVector impactPosition((endPoint1 +
n * lambda + endPoint2 +
m * mu) * 0.5
f);
253 if ((impactPosition.GetX() <
m_face_Xa - maxVertexUncertainty) || (impactPosition.GetX() >
m_face_Xc + maxVertexUncertainty) ||
254 (impactPosition.GetY() <
m_face_Yb - maxVertexUncertainty) || (impactPosition.GetY() >
m_face_Yt + maxVertexUncertainty) ||
255 (impactPosition.GetZ() <
m_face_Zu - maxVertexUncertainty) || (impactPosition.GetZ() >
m_face_Zd + maxVertexUncertainty))
261 const float maxImpactDist(std::sin(deltaTheta) * (std::fabs(mu) + std::fabs(lambda)) +
m_positionalUncertainty);
262 const CartesianVector
d(a -
n * lambda +
m * mu);
264 return (d.GetMagnitude() < maxImpactDist);
273 for (
const ParticleFlowObject *
const pPfo : parentCosmicRayPfos)
275 bool isAlreadyInSlice(
false);
277 for (
const PfoList &slice : sliceList)
279 if (std::find(slice.begin(), slice.end(), pPfo) != slice.end())
281 isAlreadyInSlice =
true;
286 if (!isAlreadyInSlice)
288 sliceList.push_back(PfoList());
289 this->
FillSlice(pPfo, pfoAssociationMap, sliceList.back());
293 unsigned int sliceId(0);
294 for (
const PfoList &slice : sliceList)
296 for (
const ParticleFlowObject *
const pPfo : slice)
298 if (!pfoToSliceIdMap.insert(PfoToSliceIdMap::value_type(pPfo, sliceId)).second)
299 throw StatusCodeException(STATUS_CODE_ALREADY_PRESENT);
310 if (std::find(slice.begin(), slice.end(), pPfo) != slice.end())
313 slice.push_back(pPfo);
317 if (pfoAssociationMap.end() != iter)
319 for (
const ParticleFlowObject *
const pAssociatedPfo : iter->second)
320 this->
FillSlice(pAssociatedPfo, pfoAssociationMap, slice);
328 for (
const ParticleFlowObject *
const pPfo : parentCosmicRayPfos)
331 throw StatusCodeException(STATUS_CODE_INVALID_PARAMETER);
333 candidates.push_back(
CRCandidate(this->GetPandora(), pPfo, pfoToSliceIdMap.at(pPfo)));
341 const LArTPCMap &larTPCMap(this->GetPandora().
GetGeometry()->GetLArTPCMap());
348 if (candidate.m_canFit)
350 minX = ((candidate.m_endPoint1.GetX() < candidate.m_endPoint2.GetX()) ? candidate.m_endPoint1.GetX() : candidate.m_endPoint2.GetX());
351 maxX = ((candidate.m_endPoint1.GetX() > candidate.m_endPoint2.GetX()) ? candidate.m_endPoint1.GetX() : candidate.m_endPoint2.GetX());
356 for (
const Cluster *
const pCluster : candidate.m_pPfo->GetClusterList())
359 pCluster->GetClusterSpanX(clusterMinX, clusterMaxX);
375 catch (
const StatusCodeException &)
383 CaloHitList caloHitList;
388 bool isFirstHit(
true);
389 bool isInSingleVolume(
true);
392 for (
const CaloHit *
const pCaloHit : caloHitList)
394 const LArCaloHit *
const pLArCaloHit(dynamic_cast<const LArCaloHit *>(pCaloHit));
406 isInSingleVolume =
false;
413 if (isInSingleVolume && (larTPCMap.end() != tpcIter))
415 const float thisFaceXLow(tpcIter->second->GetCenterX() - 0.5f * tpcIter->second->GetWidthX());
416 const float thisFaceXHigh(tpcIter->second->GetCenterX() + 0.5f * tpcIter->second->GetWidthX());
423 if (!pfoToInTimeMap.insert(PfoToBoolMap::value_type(candidate.m_pPfo, isInTime)).second)
424 throw StatusCodeException(STATUS_CODE_ALREADY_PRESENT);
435 (candidate.m_endPoint1.GetY() > candidate.m_endPoint2.GetY()) ? candidate.m_endPoint1.GetY() : candidate.m_endPoint2.GetY());
437 (candidate.m_endPoint1.GetY() < candidate.m_endPoint2.GetY()) ? candidate.m_endPoint1.GetY() : candidate.m_endPoint2.GetY());
439 const float zAtUpperY(
440 (candidate.m_endPoint1.GetY() > candidate.m_endPoint2.GetY()) ? candidate.m_endPoint1.GetZ() : candidate.m_endPoint2.GetZ());
441 const float zAtLowerY(
442 (candidate.m_endPoint1.GetY() < candidate.m_endPoint2.GetY()) ? candidate.m_endPoint1.GetZ() : candidate.m_endPoint2.GetZ());
448 if (!pfoToIsContainedMap.insert(PfoToBoolMap::value_type(candidate.m_pPfo, isContained)).second)
449 throw StatusCodeException(STATUS_CODE_ALREADY_PRESENT);
460 (candidate.m_endPoint1.GetY() > candidate.m_endPoint2.GetY()) ? candidate.m_endPoint1.GetY() : candidate.m_endPoint2.GetY());
462 (candidate.m_endPoint1.GetY() < candidate.m_endPoint2.GetY()) ? candidate.m_endPoint1.GetY() : candidate.m_endPoint2.GetY());
466 if (!pfoToIsTopToBottomMap.insert(PfoToBoolMap::value_type(candidate.m_pPfo, isTopToBottom)).second)
467 throw StatusCodeException(STATUS_CODE_ALREADY_PRESENT);
480 if (sliceIdToIsInTimeMap.find(candidate.m_sliceId) == sliceIdToIsInTimeMap.end())
481 sliceIdToIsInTimeMap.insert(std::make_pair(candidate.m_sliceId,
true));
483 if (!pfoToInTimeMap.at(candidate.m_pPfo))
484 sliceIdToIsInTimeMap.at(candidate.m_sliceId) =
false;
489 if (neutrinoSliceSet.count(candidate.m_sliceId))
492 const bool likelyNeutrino(candidate.m_canFit && sliceIdToIsInTimeMap.at(candidate.m_sliceId) &&
496 (void)neutrinoSliceSet.insert(candidate.m_sliceId);
507 const bool likelyCRMuon(
508 !neutrinoSliceSet.count(candidate.m_sliceId) &&
509 (!pfoToInTimeMap.at(candidate.m_pPfo) ||
510 (candidate.m_canFit && (pfoToIsTopToBottomMap.at(candidate.m_pPfo) ||
513 if (!pfoToIsLikelyCRMuonMap.insert(PfoToBoolMap::value_type(candidate.m_pPfo, likelyCRMuon)).second)
514 throw StatusCodeException(STATUS_CODE_ALREADY_PRESENT);
531 ClusterList clusters3D;
534 if (!clusters3D.empty() && (clusters3D.front()->GetNCaloHits() > 15))
537 const LArTPC *
const pFirstLArTPC(pandora.GetGeometry()->GetLArTPCMap().begin()->second);
551 if (std::fabs(
m_length) > std::numeric_limits<float>::epsilon())
556 CartesianPointVector directionList;
560 if (STATUS_CODE_SUCCESS == slidingFitResult.
GetGlobalFitDirection(static_cast<float>(i) * layerPitch, direction))
561 directionList.push_back(direction);
564 CartesianVector meanDirection(0.
f, 0.
f, 0.
f);
565 for (
const CartesianVector &
direction : directionList)
568 if (!directionList.empty() > 0)
569 meanDirection *= 1.
f / static_cast<float>(directionList.size());
572 for (
const CartesianVector &
direction : directionList)
575 if (!directionList.empty() > 0)
576 m_curvature *= 1.
f / static_cast<float>(directionList.size());
583 PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
"CutMode",
m_cutMode));
586 PANDORA_RETURN_RESULT_IF_AND_IF(
587 STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
"AngularUncertainty",
m_angularUncertainty));
589 PANDORA_RETURN_RESULT_IF_AND_IF(
590 STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
"PositionalUncertainty",
m_positionalUncertainty));
592 PANDORA_RETURN_RESULT_IF_AND_IF(
593 STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
"MaxAssociationDist",
m_maxAssociationDist));
595 PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
"HitThreshold",
m_minimumHits));
597 PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
"InTimeMargin",
m_inTimeMargin));
599 PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
"InTimeMaxX0",
m_inTimeMaxX0));
601 PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
"MarginY",
m_marginY));
603 PANDORA_RETURN_RESULT_IF_AND_IF(STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
"MarginZ",
m_marginZ));
605 PANDORA_RETURN_RESULT_IF_AND_IF(
606 STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
"MaxNeutrinoCosTheta",
m_maxNeutrinoCosTheta));
608 PANDORA_RETURN_RESULT_IF_AND_IF(
609 STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
"MinCosmicCosTheta",
m_minCosmicCosTheta));
611 PANDORA_RETURN_RESULT_IF_AND_IF(
612 STATUS_CODE_SUCCESS, STATUS_CODE_NOT_FOUND, !=, XmlHelper::ReadValue(xmlHandle,
"MaxCosmicCurvature",
m_maxCosmicCurvature));
614 return STATUS_CODE_SUCCESS;
std::unordered_map< const pandora::ParticleFlowObject *, pandora::PfoList > PfoToPfoListMap
std::unordered_map< const pandora::ParticleFlowObject *, bool > PfoToBoolMap
void FindAmbiguousPfos(const pandora::PfoList &parentCosmicRayPfos, pandora::PfoList &ambiguousPfos, const MasterAlgorithm *const pAlgorithm)
Find the list of ambiguous pfos (could represent cosmic-ray muons or neutrinos)
float m_face_Yb
Bottom Y face.
pandora::StatusCode ReadSettings(const pandora::TiXmlHandle xmlHandle)
void TagCRMuons(const CRCandidateList &candidates, const PfoToBoolMap &pfoToInTimeMap, const PfoToBoolMap &pfoToIsTopToBottomMap, const UIntSet &neutrinoSliceSet, PfoToBoolMap &pfoToIsLikelyCRMuonMap) const
Tag Pfos which are likely to be a CR muon.
Header file for the pfo helper class.
std::unordered_map< const pandora::ParticleFlowObject *, unsigned int > PfoToSliceIdMap
float m_maxNeutrinoCosTheta
The maximum cos(theta) that a Pfo can have to be classified as a likely neutrino. ...
float m_face_Zu
Upstream Z face.
const pandora::CartesianVector & GetGlobalMinLayerDirection() const
Get global direction corresponding to the fit result in minimum fit layer.
Header file for the lar calo hit class.
void SliceEvent(const pandora::PfoList &parentCosmicRayPfos, const PfoToPfoListMap &pfoAssociationMap, PfoToSliceIdMap &pfoToSliceIdMap) const
Break the event up into slices of associated Pfos.
bool CheckAssociation(const pandora::CartesianVector &endPoint1, const pandora::CartesianVector &endDir1, const pandora::CartesianVector &endPoint2, const pandora::CartesianVector &endDir2) const
Check whethe two Pfo endpoints are associated by distance of closest approach.
float m_positionalUncertainty
The uncertainty in cm for the position of Pfo endpoint in 3D.
void CheckIfTopToBottom(const CRCandidateList &candidates, PfoToBoolMap &pfoToIsTopToBottomMap) const
Check if each candidate is "top to bottom".
int GetMaxLayer() const
Get the maximum occupied layer in the sliding fit.
static const pandora::Vertex * GetVertex(const pandora::ParticleFlowObject *const pPfo)
Get the pfo vertex.
double m_theta
Direction made with vertical.
bool m_canFit
If there are a sufficient number of 3D hits to perform a fitting.
Class to encapsulate the logic required determine if a Pfo should or shouldn't be tagged as a cosmic ...
void CheckIfContained(const CRCandidateList &candidates, PfoToBoolMap &pfoToIsContainedMap) const
Check if each candidate is "contained" (contained = no associations to Y or Z detector faces...
unsigned int GetLArTPCVolumeId() const
Get the lar tpc volume id.
float m_angularUncertainty
The uncertainty in degrees for the angle of a Pfo.
CRCandidate(const pandora::Pandora &pandora, const pandora::ParticleFlowObject *const pPfo, const unsigned int sliceId)
Constructor.
void FillSlice(const pandora::ParticleFlowObject *const pPfo, const PfoToPfoListMap &pfoAssociationMap, pandora::PfoList &slice) const
Fill a slice iteratively using Pfo associations.
std::string m_cutMode
Choose a set of cuts using a keyword - "cautious" = remove as few neutrinos as possible "nominal" = o...
std::list< CRCandidate > CRCandidateList
double m_length
Straight line length of the linear fit.
float m_inTimeMaxX0
The maximum pfo x0 (determined from shifted vertex) to allow pfo to still be considered in time...
float m_face_Yt
Top Y face.
Header file for the cluster helper class.
void CheckIfInTime(const CRCandidateList &candidates, PfoToBoolMap &pfoToInTimeMap) const
Check if each candidate is "in time".
pandora::CartesianVector m_endPoint2
Second fitted end point in 3D.
const pandora::CartesianVector & GetGlobalMaxLayerDirection() const
Get global direction corresponding to the fit result in maximum fit layer.
float m_inTimeMargin
The maximum distance outside of the physical detector volume that a Pfo may be to still be considered...
float m_face_Xa
Anode X face.
double m_curvature
Measure of the curvature of the track.
pandora::StatusCode GetGlobalFitDirection(const float rL, pandora::CartesianVector &direction) const
Get global fit direction for a given longitudinal coordinate.
std::set< unsigned int > UIntSet
void CalculateFitVariables(const ThreeDSlidingFitResult &slidingFitResult)
Calculate all variables which require a fit.
float GetLayerPitch() const
Get the layer pitch, units cm.
void GetPfoAssociations(const pandora::PfoList &parentCosmicRayPfos, PfoToPfoListMap &pfoAssociationMap) const
Get mapping between Pfos that are associated with it other by pointing.
static int max(int a, int b)
int GetMinLayer() const
Get the minimum occupied layer in the sliding fit.
static void GetThreeDClusterList(const pandora::ParticleFlowObject *const pPfo, pandora::ClusterList &clusterList)
Get the list of 3D clusters from an input pfo.
pandora::StatusCode Initialize()
const pandora::CartesianVector & GetGlobalMaxLayerPosition() const
Get global position corresponding to the fit result in maximum fit layer.
float m_minCosmicCosTheta
The minimum cos(theta) that a Pfo can have to be classified as a likely CR muon.
bool GetValid3DCluster(const pandora::ParticleFlowObject *const pPfo, const pandora::Cluster *&pCluster3D) const
Get the 3D calo hit cluster associated with a given Pfo, and check if it has sufficient hits...
std::vector< pandora::PfoList > SliceList
std::unordered_map< int, bool > IntBoolMap
float m_marginZ
The minimum distance from a detector Z-face for a Pfo to be associated.
float m_marginY
The minimum distance from a detector Y-face for a Pfo to be associated.
T min(sqlite3 *const db, std::string const &table_name, std::string const &column_name)
float m_maxCosmicCurvature
The maximum curvature that a Pfo can have to be classified as a likely CR muon.
static bool IsFinalState(const pandora::ParticleFlowObject *const pPfo)
Whether a pfo is a primary parent particle.
ThreeDSlidingFitResult class.
const TwoDSlidingFitResult & GetFirstFitResult() const
Get the first sliding fit result for this cluster.
float m_face_Xc
Cathode X face.
float m_maxAssociationDist
The maximum distance from endpoint to point of closest approach, typically a multiple of LAr radiatio...
decltype(auto) constexpr begin(T &&obj)
ADL-aware version of std::begin.
unsigned int m_minimumHits
The minimum number of hits for a Pfo to be considered.
float m_face_Zd
Downstream Z face.
std::unordered_map< const pandora::ParticleFlowObject *, SlidingFitPair > PfoToSlidingFitsMap
second_as<> second
Type of time stored in seconds, in double precision.
void GetCRCandidates(const pandora::PfoList &parentCosmicRayPfos, const PfoToSliceIdMap &pfoToSliceIdMap, CRCandidateList &candidates) const
Make a list of CRCandidates.
static void GetCaloHits(const pandora::PfoList &pfoList, const pandora::HitType &hitType, pandora::CaloHitList &caloHitList)
Get a list of calo hits of a particular hit type from a list of pfos.
void GetNeutrinoSlices(const CRCandidateList &candidates, const PfoToBoolMap &pfoToInTimeMap, const PfoToBoolMap &pfoToIsContainedMap, UIntSet &neutrinoSliceSet) const
Get the slice indices which contain a likely neutrino Pfo.
QTextStream & endl(QTextStream &s)
GeomAnalyzerI * GetGeometry(void)
const pandora::CartesianVector & GetGlobalMinLayerPosition() const
Get global position corresponding to the fit result in minimum fit layer.
pandora::CartesianVector m_endPoint1
First fitted end point in 3D.