ATLAS Offline Software
Loading...
Searching...
No Matches
RecoToTruthAssociationAlg.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
6
11#include "TrkTrack/Track.h"
18using namespace xAOD::P4Helpers;
19namespace {
20 constexpr unsigned int dummy_unsigned = 999;
21 constexpr int com_bit = (1<<xAOD::Muon::Author::Commissioning);
22 void increment_unsigned(unsigned& val) {
23 if (val == dummy_unsigned)
24 val = 1;
25 else
26 ++val;
27 }
29 const SG::ConstAccessor<int> acc_origin("truthOrigin");
30 const SG::ConstAccessor<int> acc_type("truthType");
31 const SG::ConstAccessor<unsigned int> acc_classification("truthClassification");
32 const SG::ConstAccessor<TruthLink_t> acc_link("truthParticleLink");
33 //
34 const SG::Decorator<int> dec_origin("truthOrigin");
35 const SG::Decorator<unsigned int> dec_classification("truthClassification");
36 const SG::Decorator<int> dec_type ("truthType");
37 const SG::Decorator<TruthLink_t> dec_link("truthParticleLink");
38
39 static const SG::ConstAccessor<std::vector<unsigned long long>> truthMdtHitsAcc("truthMdtHits");
40 static const SG::ConstAccessor<std::vector<unsigned long long>> truthCscHitsAcc("truthCscHits");
41 static const SG::ConstAccessor<std::vector<unsigned long long>> truthRpcHitsAcc("truthRpcHits");
42 static const SG::ConstAccessor<std::vector<unsigned long long>> truthTgcHitsAcc("truthTgcHits");
43
44} // namespace
45
46namespace Muon {
47using namespace MuonStationIndex;
48// Initialize method:
50 ATH_CHECK(m_idHelperSvc.retrieve());
51 ATH_CHECK(m_truthMuKey.initialize());
52 ATH_CHECK(m_recoMuKey.initialize());
53
54 if (m_recoLink.empty()){
56 } else {
58 }
59
60 ATH_CHECK(m_muonTruthRecoLink.initialize(!m_recoLink.empty()));
68 for (const std::string& trk_coll : m_assocTrkContainers.value()){
69 m_inputDecorKey.emplace_back(trk_coll + ".truthParticleLink");
70 }
71
72 m_inputDecorKey.emplace_back(m_truthMuKey, SG::AuxTypeRegistry::instance().getName(acc_origin.auxid()));
73 m_inputDecorKey.emplace_back(m_truthMuKey, SG::AuxTypeRegistry::instance().getName(acc_type.auxid()));
74 if (m_idHelperSvc->hasMDT()) {
75 m_inputDecorKey.emplace_back(m_truthMuKey, SG::AuxTypeRegistry::instance().getName(truthMdtHitsAcc.auxid()));
76 }
77 if (m_idHelperSvc->hasRPC()) {
78 m_inputDecorKey.emplace_back(m_truthMuKey, SG::AuxTypeRegistry::instance().getName(truthRpcHitsAcc.auxid()));
79 }
80 if (m_idHelperSvc->hasTGC()) {
81 m_inputDecorKey.emplace_back(m_truthMuKey, SG::AuxTypeRegistry::instance().getName(truthTgcHitsAcc.auxid()));
82 }
83 if (m_idHelperSvc->hasCSC()) {
84 m_inputDecorKey.emplace_back(m_truthMuKey, SG::AuxTypeRegistry::instance().getName(truthCscHitsAcc.auxid()));
85 }
86
87 ATH_CHECK(m_inputDecorKey.initialize());
88 return StatusCode::SUCCESS;
89}
90
91// Execute method:
92StatusCode RecoToTruthAssociationAlg::execute(const EventContext& ctx) const {
93 const xAOD::TruthParticleContainer* muonTruthContainer{nullptr};
94 ATH_CHECK(SG::get(muonTruthContainer, m_truthMuKey , ctx));
95 std::unique_ptr<SG::WriteDecorHandle<xAOD::TruthParticleContainer, ElementLink<xAOD::MuonContainer>>> muonTruthParticleRecoLink{};
96 if (!m_muonTruthRecoLink.empty()) {
97 muonTruthParticleRecoLink = std::make_unique<SG::WriteDecorHandle<xAOD::TruthParticleContainer, ElementLink<xAOD::MuonContainer>>>(m_muonTruthRecoLink, ctx);
98 }
99
101 ctx);
102 if (!muonTruthParticleLink.isValid()) {
103 ATH_MSG_WARNING("muon particle container not valid");
104 return StatusCode::FAILURE;
105 }
110 ctx);
113
114 // add link to reco muons and viceversa
115 bool saw_staco = false;
116 bool decor_staco = false;
117
118 // loop over muons
119 for (const xAOD::Muon* muon : *muonTruthParticleLink) {
120 // use primary track particle to get the truth link (except for the case of STACO, where we must use the ID track particle, as the
121 // combined is not truth-matched)
122 ATH_MSG_DEBUG("muon with pT " << muon->pt() << " MeV, eta: " << muon->eta() << ", phi " << muon->phi() << " and author "
123 << muon->author());
124 const xAOD::TrackParticle* tp = nullptr;
125 if (m_associateWithInDetTP || muon->author() == xAOD::Muon::STACO || muon->author() == xAOD::Muon::MuGirl) {
126 tp = muon->trackParticle(xAOD::Muon::InnerDetectorTrackParticle);
127 } else {
128 tp = muon->primaryTrackParticle();
129 }
130
131 bool foundTruth{false}, setOrigin{false};
132 if (tp) {
133 // Associate reco with truth muon. Loop over reconstructed muons, get track particle for each one.
134 // Each track particle should carry a link to the corresponding truth particle. Then compare this truth particle link with the
135 // given truth muon particle
136 if (acc_origin.isAvailable(*tp) && acc_origin(*tp) != 0) {
137 muonTruthParticleOrigin(*muon) = acc_origin(*tp);
138 muonTruthParticleType(*muon) = acc_type(*tp);
139 muonTruthParticleClassification(*muon) = acc_classification(*tp);
140 setOrigin = true;
141 }
142
144 if (acc_link.isAvailable(*tp)) {
145 truthLink = acc_link(*tp);
146 } else {
147 ATH_MSG_DEBUG("Could not find any truth link associated with track having pt:"<<tp->pt()<<" MeV, eta: "<<tp->eta()<<", phi: "<<tp->phi()<<", charge: "<<tp->charge()<<". d0:"<<tp->d0()<<", z0: "<<tp->z0());
148 }
149
150 if (truthLink.isValid()) {
151 ATH_MSG_VERBOSE(" Got valid truth link for muon author " << muon->author() << " uniqueID " << HepMC::uniqueID(*truthLink));
152 // loop over truth particles
153 for (const xAOD::TruthParticle* truthParticle : *muonTruthContainer) {
154 if (!MC::isStable(truthParticle)) continue;
155 ATH_MSG_DEBUG("Got truth muon with uniqueID " << HepMC::uniqueID(truthParticle) << " pt " << truthParticle->pt());
156 if ( !HepMC::is_sim_descendant(*truthLink, truthParticle)) {
157 ATH_MSG_VERBOSE("UniqueID truth link: " << HepMC::uniqueID(*truthLink)
158 << " is not decendant of " << HepMC::uniqueID(truthParticle));
159 continue;
160 }
161 ATH_MSG_VERBOSE("Truth muon uniqueID matches -> creating link with truth particle " << HepMC::uniqueID(*truthLink));
162 foundTruth = true;
164 ElementLink<xAOD::TruthParticleContainer> muonTruthLink{*muonTruthContainer,
165 truthParticle->index(),
166 ctx};
167 muonTruthLink.toPersistent();
168 muonTruthParticleLink(*muon) = muonTruthLink;
169 if (!setOrigin) {
170 muonTruthParticleOrigin(*muon) = acc_origin(*tp);
171 muonTruthParticleType(*muon) = acc_type(*tp);
172 muonTruthParticleClassification(*muon) = acc_classification(*tp);
173 setOrigin = true;
174 }
176 if (muonTruthParticleRecoLink && muonTruthParticleRecoLink->operator()(*truthParticle).isValid()){
177 const xAOD::Muon* decor_muon = *muonTruthParticleRecoLink->operator()(*truthParticle);
178 ATH_MSG_VERBOSE("Truth particle is already decorated with reco muon "<<decor_muon->pt()*1.e-3
179 <<" eta: "<<decor_muon->eta()<<" phi: "<<decor_muon->phi()<<" charge: "<<
180 decor_muon->charge()<<" author: "<<decor_muon->author()<<" all authors: "<<
181 decor_muon->allAuthors());
182
183 // Check first if the exiting muon has a better author
184 if (MuonCombined::authorRank(decor_muon->author()) < MuonCombined::authorRank(muon->author())){
185 ATH_MSG_DEBUG("Author of the decorated muon is better than the one of the new candidate");
186 continue;
187 }
189 const int com_score = (muon->allAuthors() & com_bit) - (decor_muon->allAuthors() &com_bit);
190 if (com_score > 0){
191 ATH_MSG_DEBUG("Found two muons reconstructed by an equivalent author. But this one is from the commissioning chain");
192 continue;
193 }
195 if (deltaR2(muon,truthParticle) >= deltaR2(muon, decor_muon)) continue;
196 }
197
198
199 ElementLink<xAOD::MuonContainer> muonLink{muon, *muonTruthParticleLink, ctx};
200
202 std::vector<unsigned int> nprecHitsPerChamberLayer(toInt(ChIndex::ChIndexMax), dummy_unsigned);
203 std::vector<unsigned int> nphiHitsPerChamberLayer(toInt(PhiIndex::PhiIndexMax), dummy_unsigned);
204 std::vector<unsigned int> ntrigEtaHitsPerChamberLayer(toInt(PhiIndex::PhiIndexMax), dummy_unsigned);
205
206 constexpr int author_sel = (1<<xAOD::Muon::MuidCo) | (1<<xAOD::Muon::MuidSA) | (1<<xAOD::Muon::MuGirl);
207 count_chamber_layers(muon->allAuthors() & author_sel
208 ? truthParticle
209 : nullptr,
210 tp->track(), nprecHitsPerChamberLayer, nphiHitsPerChamberLayer, ntrigEtaHitsPerChamberLayer);
212 muonTruthParticleNPrecMatched(*muon) = nprecHitsPerChamberLayer;
213 muonTruthParticleNPhiMatched(*muon) = nphiHitsPerChamberLayer;
214 muonTruthParticleNTrigEtaMatched(*muon) = ntrigEtaHitsPerChamberLayer;
215
216 if (muonTruthParticleRecoLink) (*muonTruthParticleRecoLink)(*truthParticle) = muonLink;
217 break;
218 }
219 } else {
220 ATH_MSG_DEBUG("Invalid truth link");
221 }
222 } else {
223 ATH_MSG_WARNING("Could not find the appropiate track particle for muon with pT: " << muon->pt() * 1.e-3 << " GeV, eta: "
224 << muon->eta() << ", phi: " << muon->phi()
225 << " author: " << muon->author());
226 }
227
228 if (!setOrigin) {
229 muonTruthParticleOrigin(*muon) = 0;
230 muonTruthParticleType(*muon) = 0;
231 muonTruthParticleClassification(*muon) = 0;
232 }
233 if (!foundTruth) {
234 muonTruthParticleLink(*muon) = ElementLink<xAOD::TruthParticleContainer>();
235 // add these empty vectors
236 muonTruthParticleNPrecMatched(*muon) = std::vector<unsigned int>{};
237 muonTruthParticleNPhiMatched(*muon) = std::vector<unsigned int>{};
238 muonTruthParticleNTrigEtaMatched(*muon) = std::vector<unsigned int>{};
239 }
242 if (muon->author() == xAOD::Muon::STACO) {
243 const xAOD::TrackParticle* cmb_trk = muon->trackParticle(xAOD::Muon::CombinedTrackParticle);
244 if (!cmb_trk){
245 ATH_MSG_WARNING("Even a STACO muon should have a combined track");
246 continue;
247 } else {
248 if (!saw_staco) {
249 saw_staco = true;
250 decor_staco = !dec_origin.isAvailable (*cmb_trk);
251 }
252 if (decor_staco) {
253 dec_origin(*cmb_trk) = acc_origin(*muon);
254 dec_classification(*cmb_trk) = acc_classification(*muon);
255 dec_type(*cmb_trk) = acc_type(*muon);
256 dec_link(*cmb_trk) = acc_link(*muon);
257 }
258 }
259 }
260 }
262 if (muonTruthParticleRecoLink && !muonTruthParticleRecoLink->isAvailable()) {
263 for (const xAOD::TruthParticle* truthParticle : **muonTruthParticleRecoLink) {
264 ATH_MSG_DEBUG("no reco muon link set, add an empty one");
265 (*muonTruthParticleRecoLink)(*truthParticle) = ElementLink<xAOD::MuonContainer>();
266 }
267 }
268
269 return StatusCode::SUCCESS;
270}
271
273 std::vector<unsigned int>& nprecHitsPerChamberLayer,
274 std::vector<unsigned int>& nphiHitsPerChamberLayer,
275 std::vector<unsigned int>& ntrigEtaHitsPerChamberLayer) const {
276
277 if (!truthParticle || !truthMdtHitsAcc.isAvailable(*truthParticle)) {
278 ATH_MSG_DEBUG("muon has no truth hits vector in the truth association alg");
279 nprecHitsPerChamberLayer.clear();
280 nphiHitsPerChamberLayer.clear();
281 ntrigEtaHitsPerChamberLayer.clear();
282 return;
283 }
284 const std::vector<unsigned long long>& mdtTruth = truthMdtHitsAcc(*truthParticle);
285 std::vector<unsigned long long> cscTruth;
286
287 if (m_idHelperSvc->hasCSC()) cscTruth = truthCscHitsAcc(*truthParticle);
288 const std::vector<unsigned long long>& rpcTruth = truthRpcHitsAcc(*truthParticle);
289 const std::vector<unsigned long long>& tgcTruth = truthTgcHitsAcc(*truthParticle);
290
291 for (const Trk::TrackStateOnSurface* tsit : *ptrk->trackStateOnSurfaces()) {
292 if (!tsit || !tsit->trackParameters() || !tsit->measurementOnTrack()) continue;
293 const Trk::MeasurementBase* meas = tsit->measurementOnTrack();
294 Identifier id;
295 const Trk::RIO_OnTrack* rot = dynamic_cast<const Trk::RIO_OnTrack*>(meas);
296 if (rot)
297 id = rot->identify();
298 else {
299 const Muon::CompetingMuonClustersOnTrack* crot = dynamic_cast<const Muon::CompetingMuonClustersOnTrack*>(meas);
300 if (crot && !crot->containedROTs().empty() && crot->containedROTs().front()) id = crot->containedROTs().front()->identify();
301 }
302 if (!m_idHelperSvc->isMuon(id)) continue;
303
304 bool measPhi = m_idHelperSvc->measuresPhi(id);
305 bool isTgc = m_idHelperSvc->isTgc(id);
306 ChIndex chIndex = !isTgc ? m_idHelperSvc->chamberIndex(id) : ChIndex::ChUnknown;
307 if (m_idHelperSvc->isMdt(id)) {
308 for (unsigned int i = 0; i < mdtTruth.size(); ++i) {
309 if (id == mdtTruth[i]) {
310 if (chIndex != ChIndex::ChUnknown) {
311 increment_unsigned(nprecHitsPerChamberLayer.at(toInt(chIndex)));
312 }
313 break;
314 }
315 }
316 } else if (m_idHelperSvc->hasCSC() && m_idHelperSvc->isCsc(id)) {
317 for (unsigned int i = 0; i < cscTruth.size(); ++i) {
318 if (id != cscTruth[i]) continue;
319 if (measPhi) {
320 PhiIndex index = m_idHelperSvc->phiIndex(id);
321 if (index != PhiIndex::PhiUnknown) {
322 increment_unsigned(nphiHitsPerChamberLayer.at(toInt(index)));
323 }
324 } else {
325 if (chIndex != ChIndex::ChUnknown) {
326 increment_unsigned(nprecHitsPerChamberLayer.at(toInt(chIndex)));
327 }
328 }
329 break;
330 }
331 } else if (m_idHelperSvc->isRpc(id)) {
332 for (unsigned int i = 0; i < rpcTruth.size(); ++i) {
333 if (id != rpcTruth[i]) { continue; }
334 PhiIndex index = m_idHelperSvc->phiIndex(id);
335 if (index != PhiIndex::PhiUnknown) {
336 if (measPhi) {
337 increment_unsigned(nphiHitsPerChamberLayer.at(toInt(index)));
338 } else {
339 increment_unsigned(ntrigEtaHitsPerChamberLayer.at(toInt(index)));
340 }
341 }
342 break;
343 }
344 } else if (m_idHelperSvc->isTgc(id)) {
345 for (unsigned int i = 0; i < tgcTruth.size(); ++i) {
346 if (id != tgcTruth[i]) { continue; }
347 PhiIndex index = m_idHelperSvc->phiIndex(id);
348 if (index != PhiIndex::PhiUnknown) {
349 if (measPhi) {
350 increment_unsigned(nphiHitsPerChamberLayer.at(toInt(index)));
351 } else {
352 increment_unsigned(ntrigEtaHitsPerChamberLayer.at(toInt(index)));
353 }
354 }
355 break;
356 }
357 }
358 } // end loop over TSOS
359 ATH_MSG_DEBUG("finished loop over TSOS");
360
361 // now, have to check if there are non-zero truth hits in indices without reco hits
362 clear_dummys(mdtTruth, nprecHitsPerChamberLayer);
363 clear_dummys(cscTruth, nprecHitsPerChamberLayer);
364
365 clear_dummys(cscTruth, nphiHitsPerChamberLayer);
366 clear_dummys(rpcTruth, nphiHitsPerChamberLayer);
367 clear_dummys(tgcTruth, nphiHitsPerChamberLayer);
368
369 clear_dummys(rpcTruth, ntrigEtaHitsPerChamberLayer);
370 clear_dummys(tgcTruth, ntrigEtaHitsPerChamberLayer);
371}
372void RecoToTruthAssociationAlg::clear_dummys(const std::vector<unsigned long long>& identifiers, std::vector<unsigned int>& vec) const {
375 if (identifiers.empty()) { return; }
376 for (unsigned int i = 0; i < vec.size(); ++i) {
377 if (vec[i] != dummy_unsigned) continue;
378 for (unsigned j = 0; j < identifiers.size(); ++j) {
379 const Identifier id{identifiers[j]};
380 if (m_idHelperSvc->measuresPhi(id)) {
381 const auto phiIdx = static_cast<PhiIndex>(i);
382 if (m_idHelperSvc->phiIndex(id) == phiIdx) {
383 vec[i] = 0;
384 break;
385 }
386 } else {
387 const auto chIdx = static_cast<ChIndex>(i);
388 if (m_idHelperSvc->chamberIndex(id) == chIdx) {
389 vec[i] = 0;
390 break;
391 }
392 }
393 }
394 }
395}
396}
#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)
std::vector< size_t > vec
Helper class to provide constant type-safe access to aux data.
ATLAS-specific HepMC functions.
Handle class for adding a decoration to an object.
ElementLink< xAOD::TruthParticleContainer > TruthLink_t
Class for competing MuonClusters, it extends the Trk::CompetingRIOsOnTrack base class.
const std::vector< const MuonClusterOnTrack * > & containedROTs() const
returns the vector of SCT_ClusterOnTrack objects .
SG::ReadHandleKey< xAOD::MuonContainer > m_recoMuKey
SG::ReadDecorHandleKeyArray< xAOD::IParticleContainer > m_inputDecorKey
SG::WriteDecorHandleKey< xAOD::MuonContainer > m_muonTruthParticleOrigin
SG::WriteDecorHandleKey< xAOD::MuonContainer > m_muonTruthParticleNPhiMatched
Gaudi::Property< bool > m_associateWithInDetTP
SG::WriteDecorHandleKey< xAOD::TruthParticleContainer > m_muonTruthRecoLink
Decorations for the filtered muon truth particles.
Gaudi::Property< std::vector< std::string > > m_assocTrkContainers
SG::WriteDecorHandleKey< xAOD::MuonContainer > m_muonTruthParticleClassification
SG::WriteDecorHandleKey< xAOD::MuonContainer > m_muonTruthParticleType
SG::WriteDecorHandleKey< xAOD::MuonContainer > m_muonTruthParticleLink
Decorations for the reconstructed muon particles.
SG::WriteDecorHandleKey< xAOD::MuonContainer > m_muonTruthParticleNTrigEtaMatched
void count_chamber_layers(const xAOD::IParticle *truth_particle, const Trk::Track *ptrk, std::vector< unsigned int > &nprecHitsPerChamberLayer, std::vector< unsigned int > &nphiHitsPerChamberLayer, std::vector< unsigned int > &ntrigEtaHitsPerChamberLayer) const
void clear_dummys(const std::vector< unsigned long long > &identifiers, std::vector< unsigned int > &vec) const
ServiceHandle< Muon::IMuonIdHelperSvc > m_idHelperSvc
Gaudi::Property< std::string > m_recoLink
SG::ReadHandleKey< xAOD::TruthParticleContainer > m_truthMuKey
Key to the filtered muon truth particles.
StatusCode execute(const EventContext &ctx) const override
SG::WriteDecorHandleKey< xAOD::MuonContainer > m_muonTruthParticleNPrecMatched
static AuxTypeRegistry & instance()
Return the singleton registry instance.
Helper class to provide constant type-safe access to aux data.
Helper class to provide type-safe access to aux data.
Definition Decorator.h:59
Handle class for adding a decoration to an object.
This class is the pure abstract base class for all fittable tracking measurements.
Class to handle RIO On Tracks ROT) for InDet and Muons, it inherits from the common MeasurementBase.
Definition RIO_OnTrack.h:70
Identifier identify() const
return the identifier -extends MeasurementBase
represents the track state (measurement, material, fit parameters and quality) at a surface.
const Trk::TrackStates * trackStateOnSurfaces() const
return a pointer to a const DataVector of const TrackStateOnSurfaces.
Class providing the definition of the 4-vector interface.
XAOD_AUXDATA_DEPRECATED bool isAvailable(const std::string &name, const std::string &clsname="") const
Check if a user property is available for reading or not.
virtual double eta() const
The pseudorapidity ( ) of the particle.
virtual double phi() const
The azimuthal angle ( ) of the particle.
virtual double pt() const
The transverse momentum ( ) of the particle.
float charge() const
uint16_t allAuthors() const
Get all the authors of this Muon.
Author author() const
int uniqueID(const T &p)
bool is_sim_descendant(const T1 &p1, const T2 &p2)
Method to check if the first particle is a descendant of the second in the simulation,...
bool isStable(const T &p)
Identify if the particle is stable, i.e. has not decayed.
int authorRank(const xAOD::Muon::Author &a)
Definition TagBase.h:23
ChIndex chIndex(const std::string &index)
convert ChIndex name string to enum
PhiIndex
enum to classify the different phi layers in the muon spectrometer
constexpr int toInt(const EnumType enumVal)
ChIndex
enum to classify the different chamber layers in the muon spectrometer
NRpcCablingAlg reads raw condition data and writes derived condition data to the condition store.
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
Definition index.py:1
double deltaR2(double rapidity1, double phi1, double rapidity2, double phi2)
from bare rapidity,phi
TrackParticle_v1 TrackParticle
Reference the current persistent version:
TruthParticle_v1 TruthParticle
Typedef to implementation.
Muon_v1 Muon
Reference the current persistent version:
TruthParticleContainer_v1 TruthParticleContainer
Declare the latest version of the truth particle container.