ATLAS Offline Software
Loading...
Searching...
No Matches
TruthCollectionMaker.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// TruthCollectionMaker.cxx
7// Author: James Catmore (James.Catmore@cern.ch)
8// Removes all truth particles/vertices which do not pass a user-defined cut
9
10// Base class
12// EDM includes
16// Handles
18// To look up which generator is being used
23// STL Includes
24#include <vector>
25#include <string>
26#include <numeric>
27
28// Athena initialize and finalize
30{
31 ATH_MSG_VERBOSE("initialize() ...");
32 // Input truth particles
33 ATH_CHECK( m_particlesKey.initialize() );
34 ATH_MSG_INFO("Using " << m_particlesKey.key() << " as the input truth container key");
35
36 // Output (new) truth particles
37 ATH_CHECK(m_outputParticlesKey.initialize());
38 ATH_MSG_INFO("New truth particles container key: " << m_outputParticlesKey.key() );
39
40 if (m_partString.empty()) {
41 ATH_MSG_FATAL("No selection string provided");
42 return StatusCode::FAILURE;
43 } else {ATH_MSG_INFO("Truth particle selection string: " << m_partString );}
44
45 // Set up the text-parsing machinery for thinning the truth directly according to user cuts
46 if (!m_partString.empty()) {
47 ATH_CHECK( initializeParser(m_partString) );
48 }
49
50 // Initialise read handle keys
51 ATH_CHECK(m_originReadDecorKey.initialize());
52 ATH_CHECK(m_typeReadDecorKey.initialize());
53 ATH_CHECK(m_outcomeReadDecorKey.initialize());
55
56 // Initialise decorator handle keys
57 ATH_CHECK(m_linkDecoratorKey.initialize());
58 ATH_CHECK(m_originDecoratorKey.initialize());
59 ATH_CHECK(m_typeDecoratorKey.initialize());
60 ATH_CHECK(m_outcomeDecoratorKey.initialize());
65
66 return StatusCode::SUCCESS;
67}
68
70{
71 ATH_MSG_VERBOSE("finalize() ...");
72 //ATH_MSG_INFO("Processed "<< m_ntotvtx <<" truth vertices, "<< m_npassvtx << " were retained ");
73 ATH_MSG_INFO("Processed "<< m_ntotpart <<" truth particles, "<< m_npasspart << " were retained ");
74 ATH_CHECK( finalizeParser() );
75 return StatusCode::SUCCESS;
76}
77
78// Selection and collection creation
79StatusCode DerivationFramework::TruthCollectionMaker::addBranches(const EventContext& ctx) const
80{
81 // Event context for AthenaMT
82
83 // Set up for some metadata handling
84 // TODO: this isn't MT compliant. This information should go into the config level and avoid meta store
85 static const bool is_sherpa = [this]() {
86 bool is_sherpa = false;
87 if (m_metaStore->contains<xAOD::TruthMetaDataContainer>("TruthMetaData")){
88 // Note that I'd like to get this out of metadata in general, but it seems that the
89 // metadata isn't fully available in initialize, and since this is a const function
90 // I can only do the retrieve every event, rather than lazy-initializing, since this
91 // metadata ought not change during a run
92 const xAOD::TruthMetaDataContainer* truthMetaData(nullptr);
93 // Shamelessly stolen from the file meta data tool
94 if (m_metaStore->retrieve(truthMetaData).isSuccess() && !truthMetaData->empty()){
95 // Let's just be super sure...
96 const std::string gens = truthMetaData->at(0)->generators();
97 is_sherpa = (gens.find("sherpa")==std::string::npos &&
98 gens.find("Sherpa")==std::string::npos &&
99 gens.find("SHERPA")==std::string::npos) ? false : true;
100 } // Seems to be the only sure way...
101 else {
102 ATH_MSG_WARNING("Found xAODTruthMetaDataContainer empty! Configuring to be NOT Sherpa.");
103 }
104 ATH_MSG_INFO("From metadata configured: Sherpa? " << is_sherpa);
105 } else {
106 ATH_MSG_WARNING("Could not find metadata container in storegate; assuming NOT Sherpa");
107 }
108 return is_sherpa;
109 }();
110
111 // Retrieve truth collections
113 if (!truthParticles.isValid()) {
114 ATH_MSG_ERROR("Couldn't retrieve TruthParticle collection with name " << m_particlesKey);
115 return StatusCode::FAILURE;
116 }
117
118 // Create the new particle containers and WriteHandles
120 ATH_CHECK(newParticlesWriteHandle.record(std::make_unique<xAOD::TruthParticleContainer>(),
121 std::make_unique<xAOD::TruthParticleAuxContainer>()));
122 ATH_MSG_DEBUG( "Recorded new TruthParticleContainer with key: " << (m_outputParticlesKey.key()));
123
124 // Set up a mask with the same entries as the full collections
125 unsigned int nParticles = truthParticles->size();
126 m_ntotpart += nParticles;
127
128 // Set up decor readers
133
134 // Set up decorators
143
144 // Execute the text parsers and update the mask
145 if (!m_partString.empty()) {
146 std::vector<int> entries = m_parser->evaluateAsVector();
147 unsigned int nEntries = entries.size();
148 // check the sizes are compatible
149 if (nParticles != nEntries ) {
150 ATH_MSG_ERROR("Sizes incompatible! Are you sure your selection string used TruthParticles?");
151 return StatusCode::FAILURE;
152 } else {
153 // add relevant particles to new collection
154
155 //---------------
156 //This is some code to *add* new particles. Probably a good idea to break this off as a sub-function, but I'll let James C decide where that should go.
157 //---------------
158
159 //Let's check if we want to build W/Z bosons
160 bool SherpaW = false;
161 bool SherpaZ = false;
162 if (m_partString.value().find("24") != std::string::npos && m_do_sherpa && is_sherpa) {
163 SherpaW = true;
164 }
165 if (m_partString.value().find("23") != std::string::npos && m_do_sherpa && is_sherpa) {
166 SherpaZ = true;
167 }
168 if ((SherpaW or SherpaZ) && is_sherpa){
169 if (std::accumulate(entries.begin(),entries.end(),0) > 0){ //We actually have some W and Z bosons in there.
170 SherpaW = false;
171 SherpaZ = false;
172 }
173 }
174
175 if ((SherpaW || SherpaZ) && is_sherpa){
176 // Currently only handles un-ambiguous cases
177 std::vector<const xAOD::TruthParticle*> status_nonPhysical;
178 for (unsigned int i=0; i<nParticles; ++i) {
179 // Nullptr check
180 if (!truthParticles->at(i)) continue;
181 // Only collect leptons
182 if (!MC::isSMLepton(truthParticles->at(i))) continue;
183 // Gather by status
184 if (!MC::isPhysical(truthParticles->at(i))) status_nonPhysical.push_back( truthParticles->at(i) );
185 } // Done with loop over truth particles
186 // Boson cases that we can actually deal with -- generically up to VVV
187 if ((status_nonPhysical.size()==2 || status_nonPhysical.size()==4 || status_nonPhysical.size()==6) && (SherpaZ || SherpaW)){
188 // Basic boson pairing...
189 int gens[3] = {0,0,0};
190 for (size_t i=0;i<status_nonPhysical.size();++i){
191 if (status_nonPhysical[i]->absPdgId()<13) gens[0]++;
192 else if (status_nonPhysical[i]->absPdgId()<15) gens[1]++;
193 else gens[2]++;
194 } // Loop over status_nonPhysical particles
195 // Should only have even numbers per generation. Any number greater than 2 or ==1 and we're dead
196 if (gens[0]>2 || gens[0]==1 || gens[1]>2 || gens[1]==1 || gens[2]>2 || gens[2]==1){
197 // In agreeing with Sherpa authors, these are Q-M ambiguous states. Do not let users be evil.
198 ATH_MSG_VERBOSE("Too many leptons of one generation. Cannot make bosons. Give up");
199 return StatusCode::SUCCESS;
200 }
201 std::vector<const xAOD::TruthParticle*> boson;
202 for (size_t i=0;i<status_nonPhysical.size();++i){
203 if (status_nonPhysical[i]->absPdgId()<13) boson.push_back(status_nonPhysical[i]);
204 else if (gens[0]==0 && status_nonPhysical[i]->absPdgId()<15) boson.push_back(status_nonPhysical[i]);
205 else if (gens[0]==0 && gens[1]==0) boson.push_back(status_nonPhysical[i]);
206 if (boson.size()==2){
207 // Make a boson! Just have to figure out _which_ boson!
208 int pdg_id=0;
209 // Easy case: Z boson
210 if (boson[0]->pdgId()==-boson[1]->pdgId()) pdg_id=23;
211 else if (std::abs(boson[0]->pdgId()+boson[1]->pdgId())!=1){
212 // No idea what you were
213 ATH_MSG_WARNING("Do not know how to interpret as a boson: " << boson[0]->pdgId() << " " << boson[1]->pdgId());
214 } else {
215 // W boson
216 pdg_id=24*(boson[0]->pdgId()+boson[1]->pdgId());
217 }
218 if ( (SherpaW && MC::isW(pdg_id)) ||
219 (SherpaZ && MC::isZ(pdg_id)) ){
220 // Make a Z or a W
221 xAOD::TruthParticle* xTruthParticle = new xAOD::TruthParticle();
222 newParticlesWriteHandle->push_back( xTruthParticle );
224 if (boson[0]->hasProdVtx()) {
225 if ((boson[0]->prodVtx()->nIncomingParticles() > 0) && (boson[0]->prodVtx()->incomingParticle(0)!=nullptr)) {
226 motherIDDecorator(*xTruthParticle) = boson[0]->prodVtx()->incomingParticle(0)->pdgId();
227 } else {motherIDDecorator(*xTruthParticle) = 0;}
228 } else {motherIDDecorator(*xTruthParticle) = 0;}
229 if (boson[0]->hasDecayVtx()) {
230 if ((boson[0]->decayVtx()->nOutgoingParticles() > 0) && (boson[0]->decayVtx()->outgoingParticle(0)!=nullptr)) {
231 daughterIDDecorator(*xTruthParticle) = boson[0]->decayVtx()->outgoingParticle(0)->pdgId();
232 } else {daughterIDDecorator(*xTruthParticle) = 0;}
233 } else {daughterIDDecorator(*xTruthParticle) = 0;}
234 }
235 // Set with what makes sense here
236 xTruthParticle->setPdgId(pdg_id);
237 // Set dummy values
238 xTruthParticle->setUid(HepMC::INVALID_PARTICLE_ID);
239 xTruthParticle->setStatus(3);
240 // Use the sum of the momenta
241 xAOD::IParticle::FourMom_t new_mom = boson[0]->p4()+boson[1]->p4();
242 xTruthParticle->setM(new_mom.M());
243 xTruthParticle->setPx(new_mom.Px());
244 xTruthParticle->setPy(new_mom.Py());
245 xTruthParticle->setPz(new_mom.Pz());
246 xTruthParticle->setE(new_mom.E());
247 }
248 // Now clear the vectors
249 boson.clear();
250 // And move to the next generation
251 if (gens[0]==0 && gens[1]==0) gens[2]=0;
252 else if (gens[0]==0) gens[1]=0;
253 else gens[0]=0;
254 } // Done making a boson
255 } // Done looping over particles
256 }
257 if (status_nonPhysical.size()==1 || status_nonPhysical.size()==3 || status_nonPhysical.size()==5 || status_nonPhysical.size()>6){
258 ATH_MSG_WARNING(status_nonPhysical.size() << " leptons found in the Sherpa event record. Not sure how to deal with this.");
259 }
260 return StatusCode::SUCCESS;
261 }
262 for (unsigned int i=0; i<nParticles; ++i) {
263 ElementLink<xAOD::TruthParticleContainer> eltp(*truthParticles,i);
264 if (entries[i]==1) {
265 //In TRUTH3, we want to remove all particles but the first and last in a decay chain. This is off in TRUTH1. The first and last particles in the decay chain are decorated as such.
266
267 const xAOD::TruthParticle* theParticle = (*truthParticles)[i];
268 if (m_do_compress){
269 bool same_as_mother = false;
270 bool same_as_daughter = false;
271 if (theParticle->hasProdVtx()){
272 if ((theParticle->prodVtx()->nIncomingParticles() > 0) && (theParticle->prodVtx()->incomingParticle(0)!=nullptr)) {
273 if (theParticle->prodVtx()->incomingParticle(0)->pdgId() == theParticle->pdgId()){
274 same_as_mother = true;
275 }
276 }
277 }
278 if (theParticle->hasDecayVtx()){
279 if ((theParticle->decayVtx()->nOutgoingParticles() > 0) && (theParticle->decayVtx()->outgoingParticle(0)!=nullptr)) {
280 if (theParticle->decayVtx()->outgoingParticle(0)->pdgId() == theParticle->pdgId()){
281 same_as_daughter = true;
282 }
283 }
284 }
285 if (same_as_mother && same_as_daughter){
286 entries[i]=0;
287 continue;
288 }
289 }
290 xAOD::TruthParticle* xTruthParticle = new xAOD::TruthParticle();
291 newParticlesWriteHandle->push_back( xTruthParticle );
293 if (theParticle->hasProdVtx()) {
294 if ((theParticle->prodVtx()->nIncomingParticles() > 0) && (theParticle->prodVtx()->incomingParticle(0)!=nullptr)) {
295 motherIDDecorator(*xTruthParticle) = theParticle->prodVtx()->incomingParticle(0)->pdgId();
296 } else {motherIDDecorator(*xTruthParticle) = 0;}
297 } else {motherIDDecorator(*xTruthParticle) = 0;}
298 if (theParticle->hasDecayVtx()) {
299 if ((theParticle->decayVtx()->nOutgoingParticles() > 0) && (theParticle->decayVtx()->outgoingParticle(0)!=nullptr)) {
300 daughterIDDecorator(*xTruthParticle) = theParticle->decayVtx()->outgoingParticle(0)->pdgId();
301 } else {daughterIDDecorator(*xTruthParticle) = 0;}
302 } else {daughterIDDecorator(*xTruthParticle) = 0;}
303 }
304 // Fill with numerical content
305 *xTruthParticle=*theParticle;
306 // Copy over the decorations if they are available
307 typeDecorator(*xTruthParticle) =
308 typeReadDecor(*theParticle);
309
310 originDecorator(*xTruthParticle) =
311 originReadDecor(*theParticle);
312
313 outcomeDecorator(*xTruthParticle) =
314 outcomeReadDecor(*theParticle);
315
316 classificationDecorator(*xTruthParticle) =
317 classificationReadDecor(*theParticle);
318
319 if (m_outputParticlesKey.key()=="TruthHFHadrons"){ // FIXME Cannot find any other reference to this container name in Athena...
320 static const SG::ConstAccessor<int> TopHadronOriginFlagAcc("TopHadronOriginFlag"); // FIXME This should be a ReadDecorHandle
321 hadronOriginDecorator(*xTruthParticle) =
322 TopHadronOriginFlagAcc.withDefault (*theParticle, 0); // FIXME avoid using withDefault as it masks configuration/scheduling issues?
323 }
324
325 if(m_keep_navigation_info) linkDecorator(*xTruthParticle) = eltp;
326 }
327 }
328 }
329 // Count the mask
330 for (unsigned int i=0; i<nParticles; ++i) if (entries[i]) ++m_npasspart;
331 }
332
333 return StatusCode::SUCCESS;
334}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_ERROR(x)
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
Helper class to provide constant type-safe access to aux data.
ATLAS-specific HepMC functions.
Handle class for adding a decoration to an object.
const T * at(size_type n) const
Access an element, as an rvalue.
bool empty() const noexcept
Returns true if the collection is empty.
ServiceHandle< StoreGateSvc > m_metaStore
Handle on the metadata store for init.
SG::ReadHandleKey< xAOD::TruthParticleContainer > m_particlesKey
virtual StatusCode finalize() override final
SG::ReadDecorHandleKey< xAOD::TruthParticleContainer > m_originReadDecorKey
virtual StatusCode initialize() override final
SG::WriteDecorHandleKey< xAOD::TruthParticleContainer > m_typeDecoratorKey
virtual StatusCode addBranches(const EventContext &ctx) const override final
SG::ReadDecorHandleKey< xAOD::TruthParticleContainer > m_outcomeReadDecorKey
SG::WriteDecorHandleKey< xAOD::TruthParticleContainer > m_linkDecoratorKey
SG::ReadDecorHandleKey< xAOD::TruthParticleContainer > m_classificationReadDecorKey
Gaudi::Property< std::string > m_partString
SG::WriteDecorHandleKey< xAOD::TruthParticleContainer > m_hadronOriginDecoratorKey
SG::WriteDecorHandleKey< xAOD::TruthParticleContainer > m_originDecoratorKey
SG::WriteDecorHandleKey< xAOD::TruthParticleContainer > m_daughterIDDecoratorKey
SG::WriteDecorHandleKey< xAOD::TruthParticleContainer > m_outcomeDecoratorKey
SG::WriteHandleKey< xAOD::TruthParticleContainer > m_outputParticlesKey
SG::WriteDecorHandleKey< xAOD::TruthParticleContainer > m_classificationDecoratorKey
SG::ReadDecorHandleKey< xAOD::TruthParticleContainer > m_typeReadDecorKey
SG::WriteDecorHandleKey< xAOD::TruthParticleContainer > m_motherIDDecoratorKey
Helper class to provide constant type-safe access to aux data.
const_reference_type withDefault(const ELT &e, const T &deflt) const
Fetch the variable for one element, as a const reference, with a default.
Handle class for reading a decoration on an object.
virtual bool isValid() override final
Can the handle be successfully dereferenced?
Handle class for adding a decoration to an object.
StatusCode record(std::unique_ptr< T > data)
Record a const object to the store.
TLorentzVector FourMom_t
Definition of the 4-momentum type.
void setPy(float value)
Set the y component of the particle's momentum.
void setUid(int value)
Set unique ID.
void setM(float value)
Also store the mass.
void setE(float value)
Set the energy of the particle.
int pdgId() const
PDG ID code.
const TruthVertex_v1 * decayVtx() const
The decay vertex of this particle.
void setPz(float value)
Set the z component of the particle's momentum.
bool hasProdVtx() const
Check for a production vertex on this particle.
bool hasDecayVtx() const
Check for a decay vertex on this particle.
const TruthVertex_v1 * prodVtx() const
The production vertex of this particle.
void setStatus(int value)
Set status code.
void setPdgId(int pid)
Set PDG ID code.
void setPx(float value)
Set the x component of the particle's momentum.
const TruthParticle_v1 * outgoingParticle(size_t index) const
Get one of the outgoing particles.
const TruthParticle_v1 * incomingParticle(size_t index) const
Get one of the incoming particles.
size_t nOutgoingParticles() const
Get the number of outgoing particles.
size_t nIncomingParticles() const
Get the number of incoming particles.
double entries
Definition listroot.cxx:49
constexpr int INVALID_PARTICLE_ID
bool isZ(const T &p)
bool isSMLepton(const T &p)
APID: the fourth generation leptons are not standard model leptons.
bool isW(const T &p)
bool isPhysical(const T &p)
Identify if the particle is physical, i.e. is stable or decayed.
TruthMetaDataContainer_v1 TruthMetaDataContainer
Declare the latest version of the truth vertex container.
TruthParticle_v1 TruthParticle
Typedef to implementation.