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