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"
17
18#include <unordered_set>
19
20
21using namespace Acts::UnitLiterals;
22using namespace MuonR4::SegmentFit;
23namespace {
24 using IdSet_t = std::unordered_set<Identifier>;
25 unsigned int countMatched(const std::unordered_set<const xAOD::MuonSimHit*>& simHits,
26 const IdSet_t& matchIds) {
27 return std::ranges::count_if(simHits, [&matchIds](const xAOD::MuonSimHit* hit) {
28 return matchIds.count(hit->identify());
29 });
30 }
31
32 Acts::Vector4 vertexPos(const xAOD::TruthParticle& truthPart) {
33 if (truthPart.hasProdVtx()){
34 const xAOD::TruthVertex* vtx = truthPart.prodVtx();
35 return ActsTrk::convertPosToActs(Amg::Vector3D{vtx->x(), vtx->y(), vtx->z()}, vtx->t());
36 }
37 return Acts::Vector4::Zero();
38 }
39
40 static const SG::ConstAccessor<float> acc_q{"charge"};
41 static const SG::ConstAccessor<float> acc_pt{"pt"};
42}
43namespace MuonR4{
45 ATH_CHECK(m_truthKey.initialize());
46 for (const std::string& hitIds : m_simHitIds) {
47 m_simHitKeys.emplace_back(m_truthKey, hitIds);
48 }
49 ATH_CHECK(m_simHitKeys.initialize());
50 ATH_CHECK(m_segLinkKey.initialize());
51 ATH_CHECK(m_segmentKey.initialize());
52 ATH_CHECK(m_truthLinkKey.initialize());
53 ATH_CHECK(m_idHelperSvc.retrieve());
54 ATH_CHECK(m_ctxProvider.initialize());
55 ATH_CHECK(m_extrapolationTool.retrieve(EnableTool{m_includePileUpObjs}));
56 ATH_CHECK(detStore()->retrieve(m_detMgr));
57 return StatusCode::SUCCESS;
58 }
59 StatusCode TruthSegToTruthPartAssocAlg::execute(const EventContext& ctx) const {
60
61 const xAOD::TruthParticleContainer* truthParticles{nullptr};
62 ATH_CHECK(SG::get(truthParticles, m_truthKey, ctx));
63
66 using SegLinkVec_t = std::vector<SegLink_t>;
68
70 std::vector<IdDecorHandle_t> idDecorHandles{};
72 idDecorHandles.emplace_back(hitKey, ctx);
73 }
75 using IdSet_t = std::unordered_set<Identifier>;
76 using TruthTuple_t = std::tuple<const xAOD::TruthParticle*, IdSet_t>;
77 std::vector<TruthTuple_t> truthPartWithIds{};
79 std::vector<const xAOD::TruthParticle*> bkgMuons{};
80 truthPartWithIds.reserve(truthParticles->size());
81 for (const xAOD::TruthParticle* truthMuon : *truthParticles){
82 segLinkDecor(*truthMuon).clear();
83 IdSet_t assocIds{};
84 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Truth muon "<<truthMuon->pt()<<", eta: "<<truthMuon->eta()
85 <<", phi: "<<(truthMuon->phi() / 1._degree)<<", id: "<< HepMC::uniqueID(truthMuon));
86 for (const IdDecorHandle_t& hitDecor : idDecorHandles) {
87 std::ranges::transform(hitDecor(*truthMuon), std::inserter(assocIds, assocIds.begin()),
88 [this](unsigned long long rawId){
89 const Identifier id{rawId};
90 ATH_MSG_VERBOSE(" --- associated hit id: "<<m_idHelperSvc->toString(id));
91 return id;
92 });
93 }
94 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Truth muon with pt: "<<(truthMuon->pt() / 1_GeV)
95 <<", eta: "<<truthMuon->eta()<<", phi: "<<truthMuon->phi()
96 <<", uniqueID: "<<HepMC::uniqueID(truthMuon)<<", associated hits: "<<assocIds.size());
97 if (assocIds.empty()) {
98 bkgMuons.push_back(truthMuon);
99 } else {
100 truthPartWithIds.emplace_back(std::make_tuple(truthMuon, std::move(assocIds)));
101 }
102 }
104 const xAOD::MuonSegmentContainer* segments{nullptr};
105 ATH_CHECK(SG::get(segments, m_segmentKey, ctx));
106
108 TruthPartDecor_t truthLinkDecor{m_truthLinkKey, ctx};
110 std::vector<const xAOD::MuonSegment*> bkgSegments{};
111
112 for (const xAOD::MuonSegment* segment : *segments){
113 std::unordered_set<const xAOD::MuonSimHit*> simHits = getMatchingSimHits(*segment);
114 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Reconstructed truth segment "
116 <<", chamberId: "<<Muon::MuonStationIndex::chName(segment->chamberIndex())
117 <<", phi: "<<segment->sector()<<", nPrecHits: "<<segment->nPrecisionHits()
118 <<", nDoF: "<<segment->numberDoF()<<" sim hits: "<<simHits.size());
119 if (msgLvl(MSG::VERBOSE)){
120 std::vector<const xAOD::MuonSimHit*> sortedHits{simHits.begin(), simHits.end()};
121 std::ranges::sort(sortedHits, [](const xAOD::MuonSimHit* a, const xAOD::MuonSimHit* b){
122 return a->identify() < b->identify();
123 });
124 for (const xAOD::MuonSimHit* hit: sortedHits) {
125 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Associated sim hit: "<<m_idHelperSvc->toString(hit->identify())
126 <<", locPos: "<<Amg::toString(xAOD::toEigen(hit->localPosition()))
127 <<", locDir: "<<Amg::toString(xAOD::toEigen(hit->localDirection()))
128 <<", "<<hit->genParticleLink());
129 }
130 }
132 if (!(*simHits.begin())->genParticleLink().isValid()) {
133 bkgSegments.push_back(segment);
134 truthLinkDecor(*segment) = TruthPartLink_t{};
135 continue;
136 }
137 /* now find the truth particle with all associated hits */
138 const auto best_itr = std::ranges::max_element(truthPartWithIds,
139 [&simHits](const TruthTuple_t& truthTupleA,
140 const TruthTuple_t& truthTupleB) {
141 return countMatched(simHits, std::get<1>(truthTupleA)) <
142 countMatched(simHits, std::get<1>(truthTupleB));
143 });
144 if (best_itr == truthPartWithIds.end()) {
145 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" - No truth particle matched the truth hits of the segment");
146 continue;
147 }
148 if (1.*countMatched(simHits, std::get<1>(*best_itr)) < 0.5* simHits.size()) {
149 if (msgLvl(MSG::VERBOSE)) {
150 for (const auto& [truthMuon, assocIds]: truthPartWithIds){
151 std::stringstream unMatchedStr{};
152 unsigned int counts{0};
153 for (const xAOD::MuonSimHit* hit: simHits) {
154 if (!assocIds.count(hit->identify())){
155 unMatchedStr<<" *** "<<m_idHelperSvc->toString(hit->identify())<<std::endl;
156 } else {
157 ++counts;
158 }
159 }
160 if (!counts) {
161 continue;
162 }
163 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Truth muon "<<truthMuon->pt()<<", eta: "<<truthMuon->eta()
164 <<", "<<truthMuon->phi()<<", barcode: "<<HepMC::uniqueID(truthMuon)<<", matched hits: "
165 <<counts<<", unmatched: "<<std::endl<<unMatchedStr.str());
166 }
167 }
168 continue;
169 }
170 const xAOD::TruthParticle* truthPart{std::get<0>(*best_itr)};
171 segLinkDecor(*truthPart).emplace_back(segments, segment->index());
172 truthLinkDecor(*segment) = TruthPartLink_t{truthParticles, truthPart->index()};
173
174 }
175 matchPileupSegments(ctx, bkgMuons, bkgSegments, truthLinkDecor, segLinkDecor);
177 for (const xAOD::TruthParticle* truthMuon : *truthParticles){
178 const Amg::Vector3D dir = Amg::Vector3D{truthMuon->px(), truthMuon->py(), truthMuon->pz()}.normalized();
179 const xAOD::TruthVertex* vtx{truthMuon->prodVtx()};
180 const Amg::Vector3D pos = (vtx? Amg::Vector3D{vtx->x(), vtx->y(), vtx->z()} : Amg::Vector3D::Zero());
181 SegLinkVec_t& linkedSegs{segLinkDecor(*truthMuon)};
182 std::ranges::sort(linkedSegs,[&pos, &dir](const SegLink_t& linkA, const SegLink_t& linkB){
183 return dir.dot((*linkA)->position() - pos) <
184 dir.dot((*linkB)->position() - pos);
185 });
186 }
187 return StatusCode::SUCCESS;
188 }
190 const std::vector<const xAOD::TruthParticle*>& pileUpMuons,
191 const std::vector<const xAOD::MuonSegment*>& pileUpSegments,
192 TruthPartDecor_t& truthPartDecor,
193 TruthSegLinkDecor_t& truthSegDecor) const {
194
195 if (!m_includePileUpObjs) {
196 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Matching between pile-up segments & muons is disabled");
197 return;
198 }
199 if (pileUpMuons.empty() || pileUpSegments.empty()) {
200 ATH_MSG_DEBUG(__func__<<"() "<<__LINE__<<" - Segments ("<<pileUpSegments.size()<<") or muons ("
201 <<pileUpMuons.size()<<") are empty -> nothing to do");
202 return;
203 }
204 if (msgLvl(MSG::DEBUG)) {
205 std::stringstream sstr{};
206 for (const xAOD::TruthParticle* bkgMuon : pileUpMuons) {
207 sstr<<" *** pT: "<<(bkgMuon->pt() / 1_GeV)<<", eta: "<<bkgMuon->eta()
208 <<", phi: "<<(bkgMuon->phi() / 1_degree)<<", pdgId: "<<bkgMuon->pdgId()
209 <<", pos: "<<Amg::toString(vertexPos(*bkgMuon))
210 <<", uid: "<<bkgMuon->uid() <<std::endl;
211 }
212 sstr<<"\n To these segments: "<<std::endl;
213 unsigned int counter{0};
214 for (const xAOD::MuonSegment* bkgSeg : pileUpSegments) {
215 sstr<<" "<<(counter++)<<") "<<Amg::toString(bkgSeg->position())
216 <<", phi: "<<(bkgSeg->position().phi() / 1_degree)<<", eta: "<<(bkgSeg->position().eta())
217 <<", pT: "<< (acc_pt(*bkgSeg) / 1_GeV)
218 <<" chamberId: "<<Muon::MuonStationIndex::chName(bkgSeg->chamberIndex())
219 <<", sector: "<<bkgSeg->sector()<<", nPrecHits: "<<bkgSeg->nPrecisionHits()
220 <<", chi2: "<<(bkgSeg->chiSquared() / std::max(bkgSeg->numberDoF(), 1.f))
221 <<", nDoF: "<<bkgSeg->numberDoF()<<std::endl;
222 }
223 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - \nMatch the following muons "<<std::endl<<sstr.str());
224 }
225 std::vector<char> segmentMatched(pileUpSegments.size(), 0);
226
227 const Acts::GeometryContext tgContext = m_ctxProvider.getGeometryContext(ctx);
228
229 for (const xAOD::TruthParticle* bkgMuon : pileUpMuons) {
230 const Acts::Vector4 fourPos = vertexPos(*bkgMuon);
231 const Acts::Vector4 fourMom = ActsTrk::convertMomToActs(Amg::Vector3D{bkgMuon->px(), bkgMuon->py(), bkgMuon->pz()},
232 bkgMuon->m());
233 const Amg::Vector3D threeMom = fourMom.block<3,1>(0,0);
234 const Amg::Vector3D start = ActsTrk::convertPosFromActs(fourPos).first;
235 auto startSurf = Acts::Surface::makeShared<Acts::PlaneSurface>(Amg::getTranslate3D(start));
236
237
238 Acts::BoundMatrix initialCov{Acts::BoundMatrix::Identity()};
239
240 auto initialPars = Acts::BoundTrackParameters::create(tgContext, startSurf, fourPos, threeMom.unit(),
241 bkgMuon->charge() / threeMom.mag(),
242 initialCov, Acts::ParticleHypothesis::muon());
243 if(!initialPars.ok()) {
244 ATH_MSG_WARNING(__func__<<"() "<<__LINE__<<" - Failed to create start parameters");
245 continue;
246 }
247 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Created new start parameters "<<Amg::toString(fourPos)<<", "
248 <<Amg::toString(threeMom) <<", pT: "<<(threeMom.perp() / 1_GeV)
249 <<" GeV, eta: "<<threeMom.eta()<<", phi: "<<(threeMom.phi() / 1_degree)<<", q: "<<bkgMuon->charge());
250
251 for (std::size_t sIdx =0 ; sIdx < pileUpSegments.size(); ++sIdx) {
252 // Skip already matched segments
253 if (segmentMatched[sIdx]) {
254 continue;
255 }
256 const xAOD::MuonSegment* bkgSeg = pileUpSegments[sIdx];
257 if (acc_q(*bkgSeg) != bkgMuon->charge()) {
258 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Segment charge does not match");
259 continue;
260 }
261 const Amg::Vector3D segPos = bkgSeg->position();
262 const Amg::Vector3D segDir = bkgSeg->direction();
264 const double lDist = std::abs(Amg::lineDistance(segPos, segDir, start, threeMom.unit()));
265 const double dPhi = std::abs(segPos.deltaPhi(threeMom));
266 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Segment "<<sIdx<<", "<<Amg::toString(segPos)
267 <<", "<<Amg::toString(segDir) <<", lDist: "<<lDist<<", dPhi: "<<(dPhi / 1_degree));
268 if (dPhi > m_pileUpObjDPhiCut) {
269 continue;
270 }
271 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Start extrapolation");
272 const auto* sector = m_detMgr->getSectorEnvelope(bkgSeg->chamberIndex(), bkgSeg->sector(), bkgSeg->etaIndex());
273 auto propPars = m_extrapolationTool->propagate(ctx, *initialPars, sector->surface());
274 if (!propPars.ok()) {
275 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Extrapolation failed.");
276 continue;
277 }
279 const Amg::Vector2D dPosExtp = propPars->localPosition() - (sector->globalToLocalTransform(tgContext) * segPos).segment<2>(0);
280 const double dThetaExtp = std::abs(segDir.theta() - propPars->theta());
281 const double dPhiExtp = std::abs(segDir.phi() - propPars->phi());
282 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Parameter difference: "<<Amg::toString(dPosExtp)
283 <<", dTheta:"<<(dThetaExtp / 1._degree)<<", dPhi: "<<(dPhiExtp / 1._degree));
284
285
286 if (std::abs(dPosExtp.x()) > m_pileUpObjExtpDxCut || std::abs(dPosExtp.y()) > m_pileUpObjExtpDyCut ||
287 dThetaExtp > m_pileUpObjExtpDthetaCut || dPhiExtp > m_pileUpObjExtpDphiCut) {
288 ATH_MSG_VERBOSE(__func__<<"() "<<__LINE__<<" - Parameters differ too much. Cut values"
290 continue;
291 }
292 truthPartDecor(*bkgSeg) = TruthPartLink_t{truthSegDecor.container(), bkgMuon->index()};
293 truthSegDecor(*bkgMuon).emplace_back(truthPartDecor.container(), bkgSeg->index());
294 segmentMatched[sIdx] = true;
295 }
296 }
297 }
298}
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.
bool hit(const Container &ids, int pdgId)
static Double_t a
Handle class for reading a decoration on an object.
Handle class for adding a decoration to an object.
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.
xAOD::ContainerDecorator< xAOD::TruthParticleContainer, TruthSegLinkVec_t > TruthSegLinkDecor_t
SG::WriteDecorHandleKey< xAOD::MuonSegmentContainer > m_truthLinkKey
Key of the truthParticleLink decorated onto the segment.
ActsTrk::ContextUtility m_ctxProvider
Context provider for geometry, magnetic field and calibration contexts.
ElementLink< xAOD::TruthParticleContainer > TruthPartLink_t
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.
xAOD::ContainerDecorator< xAOD::MuonSegmentContainer, TruthPartLink_t > TruthPartDecor_t
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.
Gaudi::Property< double > m_pileUpObjExtpDxCut
Cut on the delta x0 between the bkg segment and the extrapolated parameters.
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.
Auxiliary class to instantiate WriteDecorHandles.The handles can be created in an empty state.
const Cont_t * container() const
Returns the underlying container.
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 ).
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
std::vector< SegLink_t > SegLinkVec_t
ElementLink< xAOD::MuonSegmentContainer > SegLink_t
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.