ATLAS Offline Software
Loading...
Searching...
No Matches
TruthSegToTruthPartAssocAlg.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
5
8#include "AthLinks/ElementLink.h"
9#include "Identifier/Identifier.h"
15#include "Acts/Surfaces/PlaneSurface.hpp"
16
17#include <unordered_set>
18
19
20using namespace Acts::UnitLiterals;
21using namespace MuonR4::SegmentFit;
22namespace {
23 using IdSet_t = std::unordered_set<Identifier>;
24 unsigned int countMatched(const std::unordered_set<const xAOD::MuonSimHit*>& simHits,
25 const IdSet_t& matchIds) {
26 return std::ranges::count_if(simHits, [&matchIds](const xAOD::MuonSimHit* hit) {
27 return matchIds.count(hit->identify());
28 });
29 }
30
31 Acts::Vector4 vertexPos(const xAOD::TruthParticle& truthPart) {
32 if (truthPart.hasProdVtx()){
33 const xAOD::TruthVertex* vtx = truthPart.prodVtx();
34 return ActsTrk::convertPosToActs(Amg::Vector3D{vtx->x(), vtx->y(), vtx->z()}, vtx->t());
35 }
36 return Acts::Vector4::Zero();
37 }
38
39 static const SG::ConstAccessor<float> acc_q{"charge"};
40 static const SG::ConstAccessor<float> acc_pt{"pt"};
41}
42namespace MuonR4{
44 ATH_CHECK(m_truthKey.initialize());
45 for (const std::string& hitIds : m_simHitIds) {
46 m_simHitKeys.emplace_back(m_truthKey, hitIds);
47 }
48 ATH_CHECK(m_simHitKeys.initialize());
49 ATH_CHECK(m_segLinkKey.initialize());
50 ATH_CHECK(m_segmentKey.initialize());
51 ATH_CHECK(m_truthLinkKey.initialize());
52 ATH_CHECK(m_idHelperSvc.retrieve());
53 ATH_CHECK(m_trackingGeometryTool.retrieve(EnableTool{m_includePileUpObjs}));
54 ATH_CHECK(m_extrapolationTool.retrieve(EnableTool{m_includePileUpObjs}));
55 ATH_CHECK(detStore()->retrieve(m_detMgr));
56 return StatusCode::SUCCESS;
57 }
58 StatusCode TruthSegToTruthPartAssocAlg::execute(const EventContext& ctx) const {
59
60 const xAOD::TruthParticleContainer* truthParticles{nullptr};
61 ATH_CHECK(SG::get(truthParticles, m_truthKey, ctx));
62
65 using SegLinkVec_t = std::vector<SegLink_t>;
67
69 std::vector<IdDecorHandle_t> idDecorHandles{};
71 idDecorHandles.emplace_back(hitKey, ctx);
72 }
74 using IdSet_t = std::unordered_set<Identifier>;
75 using TruthTuple_t = std::tuple<const xAOD::TruthParticle*, IdSet_t>;
76 std::vector<TruthTuple_t> truthPartWithIds{};
78 std::vector<const xAOD::TruthParticle*> bkgMuons{};
79 truthPartWithIds.reserve(truthParticles->size());
80 for (const xAOD::TruthParticle* truthMuon : *truthParticles){
81 segLinkDecor(*truthMuon).clear();
82 IdSet_t assocIds{};
83 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Truth muon "<<truthMuon->pt()<<", eta: "<<truthMuon->eta()
84 <<", phi: "<<(truthMuon->phi() / 1._degree)<<", id: "<< HepMC::uniqueID(truthMuon));
85 for (const IdDecorHandle_t& hitDecor : idDecorHandles) {
86 std::ranges::transform(hitDecor(*truthMuon), std::inserter(assocIds, assocIds.begin()),
87 [this](unsigned long long rawId){
88 const Identifier id{rawId};
89 ATH_MSG_VERBOSE(" --- associated hit id: "<<m_idHelperSvc->toString(id));
90 return id;
91 });
92 }
93 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Truth muon with pt: "<<(truthMuon->pt() / 1_GeV)
94 <<", eta: "<<truthMuon->eta()<<", phi: "<<truthMuon->phi()
95 <<", uniqueID: "<<HepMC::uniqueID(truthMuon)<<", associated hits: "<<assocIds.size());
96 if (assocIds.empty()) {
97 bkgMuons.push_back(truthMuon);
98 } else {
99 truthPartWithIds.emplace_back(std::make_tuple(truthMuon, std::move(assocIds)));
100 }
101 }
103 const xAOD::MuonSegmentContainer* segments{nullptr};
104 ATH_CHECK(SG::get(segments, m_segmentKey, ctx));
105
107 TruthPartDecor_t truthLinkDecor{m_truthLinkKey, ctx};
109 std::vector<const xAOD::MuonSegment*> bkgSegments{};
110
111 for (const xAOD::MuonSegment* segment : *segments){
112 std::unordered_set<const xAOD::MuonSimHit*> simHits = getMatchingSimHits(*segment);
113 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Reconstructed truth segment "
115 <<", chamberId: "<<Muon::MuonStationIndex::chName(segment->chamberIndex())
116 <<", phi: "<<segment->sector()<<", nPrecHits: "<<segment->nPrecisionHits()
117 <<", nDoF: "<<segment->numberDoF()<<" sim hits: "<<simHits.size());
118 if (msgLvl(MSG::VERBOSE)){
119 std::vector<const xAOD::MuonSimHit*> sortedHits{simHits.begin(), simHits.end()};
120 std::ranges::sort(sortedHits, [](const xAOD::MuonSimHit* a, const xAOD::MuonSimHit* b){
121 return a->identify() < b->identify();
122 });
123 for (const xAOD::MuonSimHit* hit: sortedHits) {
124 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Associated sim hit: "<<m_idHelperSvc->toString(hit->identify())
125 <<", locPos: "<<Amg::toString(xAOD::toEigen(hit->localPosition()))
126 <<", locDir: "<<Amg::toString(xAOD::toEigen(hit->localDirection()))
127 <<", "<<hit->genParticleLink());
128 }
129 }
131 if (!(*simHits.begin())->genParticleLink().isValid()) {
132 bkgSegments.push_back(segment);
133 truthLinkDecor(*segment) = TruthPartLink_t{};
134 continue;
135 }
136 /* now find the truth particle with all associated hits */
137 const auto best_itr = std::ranges::max_element(truthPartWithIds,
138 [&simHits](const TruthTuple_t& truthTupleA,
139 const TruthTuple_t& truthTupleB) {
140 return countMatched(simHits, std::get<1>(truthTupleA)) <
141 countMatched(simHits, std::get<1>(truthTupleB));
142 });
143 if (best_itr == truthPartWithIds.end()) {
144 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" - No truth particle matched the truth hits of the segment");
145 continue;
146 }
147 if (1.*countMatched(simHits, std::get<1>(*best_itr)) < 0.5* simHits.size()) {
148 if (msgLvl(MSG::VERBOSE)) {
149 for (const auto& [truthMuon, assocIds]: truthPartWithIds){
150 std::stringstream unMatchedStr{};
151 unsigned int counts{0};
152 for (const xAOD::MuonSimHit* hit: simHits) {
153 if (!assocIds.count(hit->identify())){
154 unMatchedStr<<" *** "<<m_idHelperSvc->toString(hit->identify())<<std::endl;
155 } else {
156 ++counts;
157 }
158 }
159 if (!counts) {
160 continue;
161 }
162 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Truth muon "<<truthMuon->pt()<<", eta: "<<truthMuon->eta()
163 <<", "<<truthMuon->phi()<<", barcode: "<<HepMC::uniqueID(truthMuon)<<", matched hits: "
164 <<counts<<", unmatched: "<<std::endl<<unMatchedStr.str());
165 }
166 }
167 continue;
168 }
169 const xAOD::TruthParticle* truthPart{std::get<0>(*best_itr)};
170 segLinkDecor(*truthPart).emplace_back(segments, segment->index());
171 truthLinkDecor(*segment) = TruthPartLink_t{truthParticles, truthPart->index()};
172
173 }
175 for (const xAOD::TruthParticle* truthMuon : *truthParticles){
176 const Amg::Vector3D dir = Amg::Vector3D{truthMuon->px(), truthMuon->py(), truthMuon->pz()}.normalized();
177 const xAOD::TruthVertex* vtx{truthMuon->prodVtx()};
178 const Amg::Vector3D pos = (vtx? Amg::Vector3D{vtx->x(), vtx->y(), vtx->z()} : Amg::Vector3D::Zero());
179 SegLinkVec_t& linkedSegs{segLinkDecor(*truthMuon)};
180 std::ranges::sort(linkedSegs,[&pos, &dir](const SegLink_t& linkA, const SegLink_t& linkB){
181 return dir.dot((*linkA)->position() - pos) <
182 dir.dot((*linkB)->position() - pos);
183 });
184 }
185 return StatusCode::SUCCESS;
186 }
188 const std::vector<const xAOD::TruthParticle*>& pileUpMuons,
189 const std::vector<const xAOD::MuonSegment*>& pileUpSegments,
190 TruthPartDecor_t& truthPartDecor,
191 TruthSegLinkDecor_t& truthSegDecor) const {
192
193 if (!m_includePileUpObjs) {
194 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Matching between pile-up segments & muons is disabled");
195 return;
196 }
197 if (pileUpMuons.empty() || pileUpSegments.empty()) {
198 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Segments ("<<pileUpSegments.size()<<") or muons ("
199 <<pileUpMuons.size()<<") are empty -> nothing to do");
200 return;
201 }
202 if (msgLvl(MSG::DEBUG)) {
203 std::stringstream sstr{};
204 for (const xAOD::TruthParticle* bkgMuon : pileUpMuons) {
205 sstr<<" *** pT: "<<(bkgMuon->pt() / 1_GeV)<<", eta: "<<bkgMuon->eta()
206 <<", phi: "<<(bkgMuon->phi() / 1_degree)<<", pdgId: "<<bkgMuon->pdgId()
207 <<", pos: "<<Amg::toString(vertexPos(*bkgMuon))
208 <<", uid: "<<bkgMuon->uid() <<std::endl;
209 }
210 sstr<<"\n To these segments: "<<std::endl;
211 unsigned int counter{0};
212 for (const xAOD::MuonSegment* bkgSeg : pileUpSegments) {
213 sstr<<" "<<(counter++)<<") "<<Amg::toString(bkgSeg->position())
214 <<", phi: "<<(bkgSeg->position().phi() / 1_degree)<<", eta: "<<(bkgSeg->position().eta())
215 <<", pT: "<< (acc_pt(*bkgSeg) / 1_GeV)
216 <<" chamberId: "<<Muon::MuonStationIndex::chName(bkgSeg->chamberIndex())
217 <<", sector: "<<bkgSeg->sector()<<", nPrecHits: "<<bkgSeg->nPrecisionHits()
218 <<", chi2: "<<(bkgSeg->chiSquared() / std::max(bkgSeg->numberDoF(), 1.f))
219 <<", nDoF: "<<bkgSeg->numberDoF()<<std::endl;
220 }
221 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - \nMatch the following muons "<<std::endl<<sstr.str());
222 }
223 std::vector<char> segmentMatched(pileUpSegments.size(), 0);
224
225 const ActsTrk::GeometryContext& gctx{m_trackingGeometryTool->getGeometryContext(ctx)};
226 const Acts::GeometryContext tgContext = gctx.context();
227
228 for (const xAOD::TruthParticle* bkgMuon : pileUpMuons) {
229 const Acts::Vector4 fourPos = vertexPos(*bkgMuon);
230 const Acts::Vector4 fourMom = ActsTrk::convertMomToActs(Amg::Vector3D{bkgMuon->px(), bkgMuon->py(), bkgMuon->pz()},
231 bkgMuon->m());
232 const Amg::Vector3D threeMom = fourMom.block<3,1>(0,0);
233 const Amg::Vector3D start = ActsTrk::convertPosFromActs(fourPos).first;
234 auto startSurf = Acts::Surface::makeShared<Acts::PlaneSurface>(Amg::getTranslate3D(start));
235
236
237 Acts::BoundMatrix initialCov{Acts::BoundMatrix::Identity()};
238
239 auto initialPars = Acts::BoundTrackParameters::create(tgContext, startSurf, fourPos, threeMom.unit(),
240 bkgMuon->charge() / threeMom.mag(),
241 initialCov, Acts::ParticleHypothesis::muon());
242 if(!initialPars.ok()) {
243 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" - Failed to create start parameters");
244 continue;
245 }
246 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Created new start parameters "<<Amg::toString(fourPos)<<", "
247 <<Amg::toString(threeMom / 1_GeV) <<", pT: "<<(threeMom.perp() / 1_GeV)
248 <<", eta: "<<threeMom.eta()<<", phi: "<<(threeMom.phi() / 1_degree)<<", q: "<<bkgMuon->charge());
249
250 for (std::size_t sIdx =0 ; sIdx < pileUpSegments.size(); ++sIdx) {
251 // Skip already matched segments
252 if (segmentMatched[sIdx]) {
253 continue;
254 }
255 const xAOD::MuonSegment* bkgSeg = pileUpSegments[sIdx];
256 if (acc_q(*bkgSeg) != bkgMuon->charge()) {
257 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Segment charge does not match");
258 continue;
259 }
260 const Amg::Vector3D segPos = bkgSeg->position();
261 const Amg::Vector3D segDir = bkgSeg->direction();
263 const double lDist = std::abs(Amg::lineDistance(segPos, segDir, start, threeMom.unit()));
264 const double dPhi = std::abs(segPos.deltaPhi(threeMom));
265 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Segment "<<sIdx<<", "<<Amg::toString(segPos)
266 <<", "<<Amg::toString(segDir) <<", lDist: "<<lDist<<", dPhi: "<<(dPhi / 1_degree));
267 if (dPhi > m_pileUpObjDPhiCut) {
268 continue;
269 }
270 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Start extrapolation");
271 const auto* sector = m_detMgr->getSectorEnvelope(bkgSeg->chamberIndex(), bkgSeg->sector(), bkgSeg->etaIndex());
272 auto propPars = m_extrapolationTool->propagate(ctx, *initialPars, sector->surface());
273 if (!propPars) {
274 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Extrapolation failed.");
275 continue;
276 }
278 const Amg::Vector2D dPosExtp = propPars->localPosition() - (sector->globalToLocalTrans(gctx) * segPos).segment<2>(0);
279 const double dThetaExtp = std::abs(segDir.theta() - propPars->theta());
280 const double dPhiExtp = std::abs(segDir.phi() - propPars->phi());
281 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Parameter difference: "<<Amg::toString(dPosExtp)
282 <<", dTheta:"<<(dThetaExtp / 1._degree)<<", dPhi: "<<(dPhiExtp / 1._degree));
283
284
285 if (std::abs(dPosExtp.x()) > m_pileUpObjExtpDxCut || std::abs(dPosExtp.y()) > m_pileUpObjExtpDyCut ||
286 dThetaExtp > m_pileUpObjExtpDthetaCut || dPhiExtp > m_pileUpObjExtpDphiCut) {
287 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Parameters differ too much. Cut values"
289 continue;
290 }
291 truthPartDecor(*bkgSeg) = TruthPartLink_t{truthSegDecor.cptr(), bkgMuon->index()};
292 truthSegDecor(*bkgMuon).emplace_back(truthPartDecor.cptr(), bkgSeg->index());
293 segmentMatched[sIdx] = true;
294 }
295 }
296 }
297}
Scalar eta() const
pseudorapidity method
Scalar phi() const
phi method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
ATLAS-specific HepMC functions.
static Double_t a
Handle class for reading a decoration on an object.
Handle class for adding a decoration to an object.
Acts::GeometryContext context() const
const ServiceHandle< StoreGateSvc > & detStore() const
bool msgLvl(const MSG::Level lvl) const
size_type size() const noexcept
Returns the number of elements in the collection.
Gaudi::Property< std::vector< std::string > > m_simHitIds
List of simHit id decorations to read from the truth particle.
Gaudi::Property< bool > m_includePileUpObjs
Property checking whether a pile-up muons shall be matched to pile-up segments.
const MuonGMR4::MuonDetectorManager * m_detMgr
Pointer to the muon detector manager.
SG::WriteDecorHandleKey< xAOD::MuonSegmentContainer > m_truthLinkKey
Key of the truthParticleLink decorated onto the segment.
SG::WriteDecorHandle< xAOD::TruthParticleContainer, TruthSegLinkVec_t > TruthSegLinkDecor_t
ElementLink< xAOD::TruthParticleContainer > TruthPartLink_t
PublicToolHandle< ActsTrk::ITrackingGeometryTool > m_trackingGeometryTool
Tracking geometry tool.
virtual StatusCode initialize() override final
Gaudi::Property< double > m_pileUpObjExtpDthetaCut
Cut on the delta theta between the bkg segment and the extrapolated parameters.
SG::ReadHandleKey< xAOD::TruthParticleContainer > m_truthKey
Key to the truth particle container to associate.
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
IdHelperSvc to decode the Identifiers.
SG::WriteDecorHandleKey< xAOD::TruthParticleContainer > m_segLinkKey
Declaration of the segmentLink to the truth particle.
Gaudi::Property< double > m_pileUpObjDPhiCut
Delta phi cut between particle momentum & segment position to start the extrapolation.
Gaudi::Property< double > m_pileUpObjExtpDyCut
Cut on the delta y0 between the bkg segment and the extrapolated parameters.
void matchPileupSegments(const EventContext &ctx, const std::vector< const xAOD::TruthParticle * > &pileUpMuons, const std::vector< const xAOD::MuonSegment * > &pileUpSegments, TruthPartDecor_t &truthPartDecor, TruthSegLinkDecor_t &truthSegDecor) const
Match truth muons without any HEPMC link with truth segments reconstructed in the MS.
virtual StatusCode execute(const EventContext &ctx) const override final
Gaudi::Property< double > m_pileUpObjExtpDphiCut
Cut on the delta phi between the bkg segment and the extrapolated parameters.
SG::ReadHandleKey< xAOD::MuonSegmentContainer > m_segmentKey
Key to the truth segment container to associate.
SG::ReadDecorHandleKeyArray< xAOD::TruthParticleContainer > m_simHitKeys
Declaration of the dependency on the simHit decorations.
ToolHandle< ActsTrk::IExtrapolationTool > m_extrapolationTool
Track extrapolation tool.
SG::WriteDecorHandle< xAOD::MuonSegmentContainer, TruthPartLink_t > TruthPartDecor_t
Gaudi::Property< double > m_pileUpObjExtpDxCut
Cut on the delta x0 between the bkg segment and the extrapolated parameters.
size_t index() const
Return the index of this element within its container.
Helper class to provide constant type-safe access to aux data.
Property holding a SG store/key/clid/attr name from which a ReadDecorHandle is made.
Handle class for reading a decoration on an object.
const_pointer_type cptr()
Dereference the pointer.
Handle class for adding a decoration to an object.
Amg::Vector3D direction() const
Returns the direction as Amg::Vector.
::Muon::MuonStationIndex::ChIndex chamberIndex() const
Returns the chamber index.
Amg::Vector3D position() const
Returns the position as Amg::Vector.
int etaIndex() const
Returns the eta index, which corresponds to stationEta in the offline identifiers (and the ).
ConstVectorMap< 3 > localDirection() const
Returns the local direction of the traversing particle.
Identifier identify() const
Returns the global ATLAS identifier of the SimHit.
ConstVectorMap< 3 > localPosition() const
Returns the local postion of the traversing particle.
const HepMcParticleLink & genParticleLink() const
Returns the link to the HepMC particle producing this hit.
bool hasProdVtx() const
Check for a production vertex on this particle.
const TruthVertex_v1 * prodVtx() const
The production vertex of this particle.
float z() const
Vertex longitudinal distance along the beam line form the origin.
float y() const
Vertex y displacement.
float t() const
Vertex time.
float x() const
Vertex x displacement.
std::pair< Amg::Vector3D, double > convertPosFromActs(const Acts::Vector4 &actsPos)
Converts an Acts 4-vector into a pair of an Athena spatial vector and the passed time.
Acts::Vector4 convertMomToActs(const Amg::Vector3D &threeMom, const double mass=0.)
Converts a three momentum vector from Athena together with the associated particle mass into an Acts ...
Acts::Vector4 convertPosToActs(const Amg::Vector3D &athenaPos, const double athenaTime=0.)
Converts a position vector & time from Athena units into Acts units.
Amg::Transform3D getTranslate3D(const double X, const double Y, const double Z)
: Returns a shift transformation along an arbitrary axis
std::string toString(const Translation3D &translation, int precision=4)
GeoPrimitvesToStringConverter.
double lineDistance(const AmgVector(N)&posA, const AmgVector(N)&dirA, const AmgVector(N)&posB, const AmgVector(N)&dirB)
: Calculates the shortest distance between two lines
Eigen::Matrix< double, 2, 1 > Vector2D
Eigen::Matrix< double, 3, 1 > Vector3D
int uniqueID(const T &p)
Parameters localSegmentPars(const xAOD::MuonSegment &seg)
Returns the localSegPars decoration from a xAODMuon::Segment.
std::string toString(const Parameters &pars)
Dumps the parameters into a string with labels in front of each number.
This header ties the generic definitions in this package.
std::unordered_set< const xAOD::MuonSimHit * > getMatchingSimHits(const xAOD::MuonSegment &segment)
: Returns all sim hits matched to a xAOD::MuonSegment
ElementLink< MuonR4::SegmentContainer > SegLink_t
Abrivation of the link to the reco segment container.
std::vector< SegLink_t > SegLinkVec_t
unsigned int countMatched(const simHitSet &truthHits, const simHitSet &recoHits)
const std::string & chName(ChIndex index)
convert ChIndex into a string
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
MuonSegmentContainer_v1 MuonSegmentContainer
Definition of the current "MuonSegment container version".
TruthVertex_v1 TruthVertex
Typedef to implementation.
Definition TruthVertex.h:15
MuonSimHit_v1 MuonSimHit
Defined the version of the MuonSimHit.
Definition MuonSimHit.h:12
TruthParticle_v1 TruthParticle
Typedef to implementation.
MuonSegment_v1 MuonSegment
Reference the current persistent version:
TruthParticleContainer_v1 TruthParticleContainer
Declare the latest version of the truth particle container.