ATLAS Offline Software
InputConverter.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
3 */
4 
5 // class header include
6 #include "InputConverter.h"
7 
8 #include <memory>
9 
10 // framework
11 #include "GaudiKernel/IPartPropSvc.h"
12 #include "GaudiKernel/PhysicalConstants.h"
13 
14 // ISF_HepMC include
15 #include "ISF_Event/TruthBinding.h"
17 // ISF_Event include
18 #include "ISF_Event/ISFParticle.h"
22 // MCTruth includes
25 // McEventCollection
27 // Geant4 includes
28 #include "G4PrimaryParticle.hh"
29 #include "G4Event.hh"
30 #include "G4Geantino.hh"
31 #include "G4ChargedGeantino.hh"
32 #include "G4ParticleTable.hh"
33 #include "G4LorentzVector.hh"
34 #include "G4TransportationManager.hh"
35 // HepMC includes
36 #include "AtlasHepMC/GenParticle.h"
37 #include "AtlasHepMC/GenEvent.h"
38 #include "AtlasHepMC/GenVertex.h"
39 // CLHEP includes
40 #include "CLHEP/Geometry/Point3D.h"
41 #include "CLHEP/Geometry/Vector3D.h"
42 #include "CLHEP/Units/SystemOfUnits.h"
43 // HepPDT
44 #include "HepPDT/ParticleID.hh"
45 #include "HepPDT/DecayData.hh"
46 #include "HepPDT/ParticleDataTable.hh"
48 
50 ISF::InputConverter::InputConverter(const std::string& name, ISvcLocator* svc)
51  : base_class(name, svc)
52  , m_particlePropSvc("PartPropSvc",name)
53  , m_particleDataTable(nullptr)
54  , m_useGeneratedParticleMass(false)
55  , m_genParticleFilters(this)
56  , m_quasiStableParticlesIncluded(false)
57 {
58  // particle mass from particle data table?
59  declareProperty("UseGeneratedParticleMass",
61  "Use particle mass assigned to GenParticle.");
62  // particle filters
63  declareProperty("GenParticleFilters",
65  "Tools for filtering out GenParticles.");
66  // the particle property service
67  declareProperty("ParticlePropertyService",
69  "ParticlePropertyService to retrieve the PDT.");
70  declareProperty("QuasiStableParticlesIncluded", m_quasiStableParticlesIncluded);
71 }
72 
73 
76 {
77 }
78 
79 
80 // Athena algtool's Hooks
83 {
84  ATH_MSG_VERBOSE("initialize() begin");
85 
86  // setup PDT if requested (to get particle masses later on)
87  if (!m_useGeneratedParticleMass) {
88  ATH_CHECK(m_particlePropSvc.retrieve());
89  m_particleDataTable = m_particlePropSvc->PDT();
90  if (!m_particleDataTable) {
91  ATH_MSG_FATAL( "Could not get ParticleDataTable from " << m_particlePropSvc << ". Abort" );
92  return StatusCode::FAILURE;
93  }
94  }
95 
96  if (!m_genParticleFilters.empty()) {
97  ATH_CHECK(m_genParticleFilters.retrieve());
98  }
99 
100  ATH_MSG_VERBOSE("initialize() successful");
101  return StatusCode::SUCCESS;
102 }
103 
104 
108 {
109  ATH_MSG_DEBUG("Finalizing ...");
110  return StatusCode::SUCCESS;
111 }
112 
113 
118  ISF::ISFParticleContainer& simParticles) const
119 {
120  for ( auto eventPtr : inputGenEvents ) {
121  // skip empty events
122  if (eventPtr == nullptr) { continue; }
123 
124  ATH_MSG_DEBUG("Starting conversion of GenEvent with"
125  " signal_process_id=" << HepMC::signal_process_id(eventPtr) <<
126  " and event_number=" << eventPtr->event_number() );
127 
128  // new collection containing all gen particles that passed filters
129  bool legacyOrdering = true; // FIXME: this is only to keep the same order of particles
130  // as the prior 'StackFiller' implementation
131  // TODO: remove this functionality from the
132  // getSelectedParticles function once
133  // happy with the results
134  const auto passedGenParticles = getSelectedParticles(*eventPtr, legacyOrdering);
135 
136  for ( auto& genPartPtr : passedGenParticles ) {
137  ATH_MSG_VERBOSE("Picking up following GenParticle for conversion to ISFParticle: " << genPartPtr);
138  auto simParticlePtr = convertParticle(genPartPtr);
139  if (!simParticlePtr) {
140  ATH_MSG_ERROR("Error while trying to convert input generator particles. Aborting.");
141  return StatusCode::FAILURE;
142  }
143  // add to collection that will be returned
144  simParticles.push_back(simParticlePtr);
145 
146  } // loop over passed gen particles
147 
148  } // loop over gen events
149 
150  ATH_MSG_DEBUG( "Created initial simulation particle collection with size " << simParticles.size() );
151 
152  return StatusCode::SUCCESS;
153 }
154 
156  G4Event*& outputG4Event, McEventCollection& shadowGenEvents) const
157 {
158  ISF::ISFParticleContainer simParticleList{}; // particles for ISF simulation
159  ATH_CHECK(this->convert(inputGenEvents, simParticleList));
160  //Convert from ISFParticleContainer to ConstISFParticleVector
161  ISF::ISFParticleVector simParticleVector{
162  std::make_move_iterator(std::begin(simParticleList)),
163  std::make_move_iterator(std::end(simParticleList))
164  };
165  if (!shadowGenEvents.empty()) {
166  outputG4Event = this->ISF_to_G4Event(ctx, simParticleVector, inputGenEvents.back(), shadowGenEvents.back());
167  }
168  else{
169  outputG4Event = this->ISF_to_G4Event(ctx, simParticleVector, inputGenEvents.back(), nullptr);
170  }
171  return StatusCode::SUCCESS;
172 }
173 
174 
176  G4Event*& outputG4Event) const
177 {
178  ISF::ISFParticleContainer simParticleList{}; // particles for ISF simulation
179  ATH_CHECK(this->convert(inputGenEvents, simParticleList));
180  //Convert from ISFParticleContainer to ConstISFParticleVector
181  ISF::ISFParticleVector simParticleVector{
182  std::make_move_iterator(std::begin(simParticleList)),
183  std::make_move_iterator(std::end(simParticleList))
184  };
185  outputG4Event = this->ISF_to_G4Event(ctx, simParticleVector, inputGenEvents.back(), nullptr);
186  return StatusCode::SUCCESS;
187 }
188 
189 
191 #ifdef HEPMC3
192 std::vector<HepMC::GenParticlePtr>
193 ISF::InputConverter::getSelectedParticles(HepMC::GenEvent& evnt, bool legacyOrdering) const {
194  auto allGenPartBegin = evnt.particles().begin();
195  auto allGenPartEnd = evnt.particles().end();
196 
197  // reserve destination container with maximum size, i.e. number of particles in input event
198  std::vector<HepMC::GenParticlePtr> passedGenParticles{};
199  size_t maxParticles = std::distance(allGenPartBegin, allGenPartEnd);
200  passedGenParticles.reserve(maxParticles);
201 
202  if (m_useShadowEvent) {
203  if (legacyOrdering) {
204  // FIXME: remove this block and the 'legacyOrdering' flag
205  // once we don't need the legacy order any longer
206  for (auto vtx: evnt.vertices() ) {
207  std::copy_if (vtx->particles_out().begin(),
208  vtx->particles_out().end(),
209  std::back_inserter(passedGenParticles),
210  [](HepMC::GenParticlePtr p){return p->attribute<HepMC3::IntAttribute>("ShadowParticleId");});
211  }
212  }
213  else {
214  std::copy_if (allGenPartBegin,
215  allGenPartEnd,
216  std::back_inserter(passedGenParticles),
217  [](HepMC::GenParticlePtr p){return p->attribute<HepMC3::IntAttribute>("ShadowParticleId");});
218  }
219  }
220  else {
221  if (legacyOrdering) {
222  // FIXME: remove this block and the 'legacyOrdering' flag
223  // once we don't need the legacy order any longer
224  for (auto vtx: evnt.vertices() ) {
225  std::copy_if (vtx->particles_out().begin(),
226  vtx->particles_out().end(),
227  std::back_inserter(passedGenParticles),
228  [this](HepMC::GenParticlePtr p){return this->passesFilters(std::const_pointer_cast<const HepMC3::GenParticle>(p));});
229  }
230  }
231  else {
232  std::copy_if (allGenPartBegin,
233  allGenPartEnd,
234  std::back_inserter(passedGenParticles),
235  [this](HepMC::GenParticlePtr p){return this->passesFilters(std::const_pointer_cast<const HepMC3::GenParticle>(p));});
236  }
237  }
238 
239  passedGenParticles.shrink_to_fit();
240 
241  return passedGenParticles;
242 }
243 #else
244 std::vector<HepMC::GenParticlePtr>
245 ISF::InputConverter::getSelectedParticles(HepMC::GenEvent& evnt, bool legacyOrdering) const {
246  auto allGenPartBegin = evnt.particles_begin();
247  auto allGenPartEnd = evnt.particles_end();
248 
249  // reserve destination container with maximum size, i.e. number of particles in input event
250  std::vector<HepMC::GenParticlePtr> passedGenParticles{};
251  size_t maxParticles = std::distance(allGenPartBegin, allGenPartEnd);
252  passedGenParticles.reserve(maxParticles);
253 
254  if (legacyOrdering) {
255  // FIXME: remove this block and the 'legacyOrdering' flag
256  // once we don't need the legacy order any longer
257  auto vtxIt = evnt.vertices_begin();
258  auto vtxItEnd = evnt.vertices_end();
259  for ( ; vtxIt != vtxItEnd; ++vtxIt ) {
260  const auto vtxPtr = *vtxIt;
261  std::copy_if (vtxPtr->particles_begin(HepMC::children),
262  vtxPtr->particles_end(HepMC::children),
263  std::back_inserter(passedGenParticles),
264  [this](HepMC::GenParticlePtr p){return this->passesFilters(*p);});
265  }
266  }
267  else {
268  std::copy_if (allGenPartBegin,
269  allGenPartEnd,
270  std::back_inserter(passedGenParticles),
271  [this](HepMC::GenParticlePtr p){return this->passesFilters(*p);});
272  }
273 
274  passedGenParticles.shrink_to_fit();
275 
276  return passedGenParticles;
277 }
278 #endif
279 
280 
284  if (!genPartPtr) { return nullptr; }
285 
286  auto pVertex = genPartPtr->production_vertex();
287  if (!pVertex) {
288  ATH_MSG_ERROR("Unable to convert following generator particle due to missing production vertex for: " << genPartPtr);
289  return nullptr;
290  }
291  auto parentEvent = genPartPtr->parent_event();
292  if (!parentEvent) {
293  ATH_MSG_ERROR("Cannot convert a GenParticle without a parent GenEvent into an ISFParticle!!!");
294  return nullptr;
295  }
296 
297  const Amg::Vector3D pos(pVertex->position().x(), pVertex->position().y(), pVertex->position().z());
298  const auto& pMomentum(genPartPtr->momentum());
299  const Amg::Vector3D mom(pMomentum.px(), pMomentum.py(), pMomentum.pz());
300 #ifdef HEPMC3
301  const double pMass = this->getParticleMass(genPartPtr);
302 #else
303  const double pMass = this->getParticleMass(*genPartPtr);
304 #endif
305  double e=pMomentum.e();
306  if (e>1) { //only test for >1 MeV in momentum
307  double px=pMomentum.px();
308  double py=pMomentum.py();
309  double pz=pMomentum.pz();
310  double teste=std::sqrt(px*px + py*py + pz*pz + pMass*pMass);
311  if (std::abs(e-teste)>0.01*e) {
312  ATH_MSG_WARNING("Difference in energy for: " << genPartPtr<<" Morg="<<pMomentum.m()<<" Mmod="<<pMass<<" Eorg="<<e<<" Emod="<<teste);
313  }
314  if (MC::isDecayed(genPartPtr) && pVertex && genPartPtr->end_vertex()) { //check for possible changes of gamma for quasi stable particles
315  const auto& prodVtx = genPartPtr->production_vertex()->position();
316  const auto& endVtx = genPartPtr->end_vertex()->position();
317  CLHEP::Hep3Vector dist3D(endVtx.x()-prodVtx.x(), endVtx.y()-prodVtx.y(), endVtx.z()-prodVtx.z());
318 
319  if (dist3D.mag()>1*Gaudi::Units::mm) {
320  CLHEP::HepLorentzVector mom( pMomentum.x(), pMomentum.y(), pMomentum.z(), pMomentum.t() );
321  double gamma_org=mom.gamma();
322  mom.setE(teste);
323  double gamma_new=mom.gamma();
324 
325  if (std::abs(gamma_new-gamma_org)/(gamma_new+gamma_org)>0.001) {
326  ATH_MSG_WARNING("Difference in boost gamma for Quasi stable particle "<<genPartPtr);
327  ATH_MSG_WARNING(" gamma(m="<<mom.m()<<")="<<gamma_org<<" gamma(m="<<pMass<<")="<<gamma_new);
328  } else {
329  ATH_MSG_VERBOSE("Quasi stable particle "<<genPartPtr);
330  ATH_MSG_VERBOSE(" gamma(m="<<mom.m()<<")="<<gamma_org<<" gamma(m="<<pMass<<")="<<gamma_new);
331  }
332  }
333  }
334  }
335 
336  const int pPdgId = genPartPtr->pdg_id();
337  const double charge = HepPDT::ParticleID(pPdgId).charge();
338  const double pTime = pVertex->position().t() / Gaudi::Units::c_light;
341  const auto pBarcode = HepMC::barcode(genPartPtr);
342  const auto particleID = HepMC::uniqueID(genPartPtr);
343  auto tBinding = std::make_unique<ISF::TruthBinding>(genPartPtr);
344 
345  auto hmpl = std::make_unique<HepMcParticleLink>(particleID, parentEvent->event_number(), HepMcParticleLink::IS_EVENTNUM, HepMcParticleLink::IS_ID);
346 
347  auto sParticle = std::make_unique<ISF::ISFParticle>( std::move(pos),
348  std::move(mom),
349  pMass,
350  charge,
351  pPdgId,
352  genPartPtr->status(),
353  pTime,
354  origin,
355  particleID,
356  pBarcode,
357  tBinding.release(),
358  hmpl.release() );
359  return sParticle.release();
360 }
361 
362 
364 #ifdef HEPMC3
365 double
367  // default value: generated particle mass
368  double mass = part->generated_mass();
369  ATH_MSG_VERBOSE("part->generated_mass, mass="<<mass);
370 
371  // 1. use PDT mass?
372  if ( !m_useGeneratedParticleMass ) {
373  const int absPDG = std::abs(part->pdg_id());
374  HepPDT::ParticleData const *pData = (m_particleDataTable)
375  ? m_particleDataTable->particle(absPDG)
376  : nullptr;
377  if (pData) {
378  mass = pData->mass();
379  ATH_MSG_VERBOSE("using pData mass, mass="<<mass);
380  }
381  else {
382  ATH_MSG_WARNING( "Unable to find mass of particle with PDG ID '" << absPDG << "' in ParticleDataTable. Will set mass to generated_mass: " << mass);
383  }
384  }
385  return mass;
386 }
387 #else
388 double
390 {
391  // default value: generated particle mass
392  double mass = part.generated_mass();
393  ATH_MSG_VERBOSE("part.generated_mass, mass="<<mass);
394 
395  // 1. use PDT mass?
396  if ( !m_useGeneratedParticleMass ) {
397  const int absPDG = std::abs(part.pdg_id());
398  HepPDT::ParticleData const *pData = (m_particleDataTable)
399  ? m_particleDataTable->particle(absPDG)
400  : nullptr;
401  if (pData) {
402  mass = pData->mass();
403  ATH_MSG_VERBOSE("using pData mass, mass="<<mass);
404  }
405  else {
406  ATH_MSG_WARNING( "Unable to find mass of particle with PDG ID '" << absPDG << "' in ParticleDataTable. Will set mass to generated_mass: " << mass);
407  }
408  }
409  return mass;
410 }
411 #endif
412 
413 
415 #ifdef HEPMC3
416 bool
418 {
419  // TODO: implement this as a std::find_if with a lambda function
420  for ( const auto& filter : m_genParticleFilters ) {
421  // determine if the particle passes current filter
422  bool passFilter = filter->pass(part);
423  ATH_MSG_VERBOSE("GenParticleFilter '" << filter.typeAndName() << "' returned: "
424  << (passFilter ? "true, will keep particle."
425  : "false, will remove particle."));
426  const auto& momentum = part->momentum();
427  ATH_MSG_VERBOSE("Particle: ("
428  <<momentum.px()<<", "
429  <<momentum.py()<<", "
430  <<momentum.pz()<<"), pdgCode: "
431  <<part->pdg_id() );
432 
433  if (!passFilter) {
434  return false;
435  }
436  }
437 
438  return true;
439 }
440 #else
441 bool
443 {
444  // TODO: implement this as a std::find_if with a lambda function
445  for ( const auto& filter : m_genParticleFilters ) {
446  // determine if the particle passes current filter
447  bool passFilter = filter->pass(part);
448  ATH_MSG_VERBOSE("GenParticleFilter '" << filter.typeAndName() << "' returned: "
449  << (passFilter ? "true, will keep particle."
450  : "false, will remove particle."));
451  const auto& momentum = part.momentum();
452  ATH_MSG_VERBOSE("Particle: ("
453  <<momentum.px()<<", "
454  <<momentum.py()<<", "
455  <<momentum.pz()<<"), pdgCode: "
456  <<part.pdg_id() );
457 
458  if (!passFilter) {
459  return false;
460  }
461  }
462 
463  return true;
464 }
465 #endif
466 
467 
468 //________________________________________________________________________
469 G4Event* ISF::InputConverter::ISF_to_G4Event(const EventContext& ctx, const ISF::ISFParticleVector& ispVector, HepMC::GenEvent *genEvent, HepMC::GenEvent *shadowGenEvent, bool useHepMC) const
470 {
471  G4Event *g4evt = new G4Event(ctx.eventID().event_number());
472 
473  // retrieve world solid (volume)
474  const G4VSolid *worldSolid = G4TransportationManager::GetTransportationManager()->GetNavigatorForTracking()->GetWorldVolume()->GetLogicalVolume()->GetSolid();
475 
476  for ( ISF::ISFParticle *ispPtr: ispVector ) {
477  ISF::ISFParticle &isp = *ispPtr;
478  if ( !isInsideG4WorldVolume(isp, worldSolid) ) {
479  ATH_MSG_WARNING("Unable to convert ISFParticle to G4PrimaryParticle!");
480  ATH_MSG_WARNING(" ISFParticle: " << isp );
481  if (worldSolid) {
482  ATH_MSG_WARNING(" is outside Geant4 world volume: ");
483  worldSolid->DumpInfo();
484  G4cout << std::flush;
485  }
486  else {
487  ATH_MSG_WARNING(" is outside Geant4 world volume.");
488  }
489  continue;
490  }
491  this->addG4PrimaryVertex(g4evt,isp,useHepMC,shadowGenEvent);
492  }
493 
494  AtlasG4EventUserInfo *atlasG4EvtUserInfo=new AtlasG4EventUserInfo();
495  atlasG4EvtUserInfo->SetLastProcessedTrackID(0); // TODO Check if it is better to set this to -1 initially
496  atlasG4EvtUserInfo->SetLastProcessedStep(0); // TODO Check if it is better to set this to -1 initially
497  atlasG4EvtUserInfo->SetHepMCEvent(genEvent);
498  g4evt->SetUserInformation(atlasG4EvtUserInfo);
499 
500  return g4evt;
501 }
502 
503 //________________________________________________________________________
504 const G4ParticleDefinition* ISF::InputConverter::getG4ParticleDefinition(int pdgcode) const
505 {
507  if (pdgcode==998) {
508  return G4ChargedGeantino::Definition();
509  }
510  if (pdgcode==999) {
511  return G4Geantino::GeantinoDefinition();
512  }
514  G4ParticleTable *ptable = G4ParticleTable::GetParticleTable();
515  if (ptable) {
516  return ptable->FindParticle(pdgcode);
517  }
518  ATH_MSG_ERROR("getG4ParticleDefinition - Failed to retrieve G4ParticleTable!");
519  return nullptr;
520 }
521 
522 double SetProperTimeFromDetectorFrameDecayLength(G4PrimaryParticle& g4particle,const double GeneratorDecayLength)
523 {
524  //particle with velocity v travels distance l in time t=l/v
525  //proper time: c^2 tau^2 = c^2 t^2 - l^2 = l^2/c^2 * (1/beta^2 -1)
526  //beta^2 = p^2/E^2 with particle momentum p and energy E; E^2=m^2 + p^2
527  //tau^2 = l^2/c^2 * m^2/p^2
528  const double p2=std::pow(g4particle.GetTotalMomentum(),2); //magnitude of particle momentum squared
529  const double m2=std::pow(g4particle.GetMass(),2); //mass^2 of particle
530  const double l2=std::pow(GeneratorDecayLength,2); //distance^2 of particle decay length
531  const double tau2=l2*m2/p2/CLHEP::c_squared;
532  const double tau=std::sqrt(tau2);
533  g4particle.SetProperTime( tau );
534  return tau;
535 }
536 
537 //________________________________________________________________________
538 #ifdef HEPMC3
539 G4PrimaryParticle* ISF::InputConverter::getDaughterG4PrimaryParticle(const HepMC::GenParticlePtr& genpart, bool makeLinkToTruth) const{
540  ATH_MSG_VERBOSE("Creating G4PrimaryParticle from GenParticle.");
541 
542  const G4ParticleDefinition *particleDefinition = this->getG4ParticleDefinition(genpart->pdg_id());
543 
544  if (particleDefinition==nullptr) {
545  ATH_MSG_ERROR("ISF_to_G4Event particle conversion failed. ISF_Particle PDG code = " << genpart->pdg_id() <<
546  "\n This usually indicates a problem with the evgen step.\n" <<
547  "Please report this to the Generators group, mentioning the release and generator used for evgen and the PDG code above." );
548  return nullptr;
549  }
550 
551  // create new primaries and set them to the vertex
552  // G4double mass = particleDefinition->GetPDGMass();
553  auto &genpartMomentum = genpart->momentum();
554  G4double px = genpartMomentum.x();
555  G4double py = genpartMomentum.y();
556  G4double pz = genpartMomentum.z();
557 
558  std::unique_ptr<G4PrimaryParticle> g4particle = std::make_unique<G4PrimaryParticle>(particleDefinition,px,py,pz);
559 
560  if (genpart->end_vertex()) {
561  // Set the lifetime appropriately - this is slow but rigorous, and we
562  // don't want to end up with something like vertex time that we have
563  // to validate for every generator on earth...
564  const auto& prodVtx = genpart->production_vertex()->position();
565  const auto& endVtx = genpart->end_vertex()->position();
566  //const G4LorentzVector lv0 ( prodVtx.x(), prodVtx.y(), prodVtx.z(), prodVtx.t() );
567  //const G4LorentzVector lv1 ( endVtx.x(), endVtx.y(), endVtx.z(), endVtx.t() );
568  //Old calculation, not taken because vertex information is not sufficiently precise
569  //g4particle->SetProperTime( (lv1-lv0).mag()/Gaudi::Units::c_light );
570 
571  CLHEP::Hep3Vector dist3D(endVtx.x()-prodVtx.x(), endVtx.y()-prodVtx.y(), endVtx.z()-prodVtx.z());
572  double tau=SetProperTimeFromDetectorFrameDecayLength(*g4particle,dist3D.mag());
573 
574  if (msgLvl(MSG::VERBOSE)) {
575  double pmag2=g4particle->GetTotalMomentum(); //magnitude of particle momentum
576  pmag2*=pmag2; //magnitude of particle momentum squared
577  double e2=g4particle->GetTotalEnergy(); //energy of particle
578  e2*=e2; //energy of particle squared
579  double beta2=pmag2/e2; //beta^2=v^2/c^2 for particle
580  double tau2=dist3D.mag2()*(1/beta2-1)/Gaudi::Units::c_light/Gaudi::Units::c_light;
581  ATH_MSG_VERBOSE("lifetime tau(beta)="<<std::sqrt(tau2)<<" tau="<<tau);
582  }
583  if (m_quasiStableParticlesIncluded) {
584  ATH_MSG_VERBOSE( "Detected primary particle with end vertex." );
585  ATH_MSG_VERBOSE( "Will add the primary particle set on." );
586  ATH_MSG_VERBOSE( "Primary Particle: " << genpart );
587  ATH_MSG_VERBOSE( "Number of daughters: " << genpart->end_vertex()->particles_out().size()<<" at position "<<genpart->end_vertex() );
588  }
589  else {
590  ATH_MSG_WARNING( "Detected primary particle with end vertex." );
591  ATH_MSG_WARNING( "Will add the primary particle set on." );
592  ATH_MSG_WARNING( "Primary Particle: " << genpart );
593  ATH_MSG_WARNING( "Number of daughters : " << genpart->end_vertex()->particles_out().size()<<" at position "<<genpart->end_vertex() );
594  }
595  // Add all necessary daughter particles
596  for ( auto daughter: genpart->end_vertex()->particles_out() ) {
597  if (m_quasiStableParticlesIncluded) {
598  ATH_MSG_VERBOSE ( "Attempting to add daughter particle : " << daughter );
599  }
600  else {
601  ATH_MSG_WARNING ( "Attempting to add daughter particle: " << daughter );
602  }
603  G4PrimaryParticle *daughterG4Particle = this->getDaughterG4PrimaryParticle( daughter, makeLinkToTruth );
604  if (!daughterG4Particle) {
605  ATH_MSG_ERROR("Bailing out of loop over daughters of particle: "<<
606  " due to errors - will not return G4Particle.");
607  return nullptr;
608  }
609  g4particle->SetDaughter( daughterG4Particle );
610  }
611  }
612 
613  if (makeLinkToTruth) {
614  // Set the user information for this primary to point to the HepMcParticleLink...
615  std::unique_ptr<PrimaryParticleInformation> primaryPartInfo = std::make_unique<PrimaryParticleInformation>(genpart);
616  primaryPartInfo->SetRegenerationNr(0);
617  ATH_MSG_VERBOSE("Making primary down the line with barcode " << primaryPartInfo->GetParticleUniqueID());
618  g4particle->SetUserInformation(primaryPartInfo.release());
619  }
620 
621  return g4particle.release();
622 }
623 
624 //________________________________________________________________________
626  ATH_MSG_VERBOSE("Creating G4PrimaryParticle from GenParticle.");
627 
628  const G4ParticleDefinition *particleDefinition = this->getG4ParticleDefinition(genpart->pdg_id());
629 
630  if (particleDefinition==nullptr) {
631  ATH_MSG_ERROR("ISF_to_G4Event particle conversion failed. ISF_Particle PDG code = " << genpart->pdg_id() <<
632  "\n This usually indicates a problem with the evgen step.\n" <<
633  "Please report this to the Generators group, mentioning the release and generator used for evgen and the PDG code above." );
634  return nullptr;
635  }
636 
637  // create new primaries and set them to the vertex
638  // G4double mass = particleDefinition->GetPDGMass();
639  auto &genpartMomentum = genpart->momentum();
640  G4double px = genpartMomentum.x();
641  G4double py = genpartMomentum.y();
642  G4double pz = genpartMomentum.z();
643 
644  std::unique_ptr<G4PrimaryParticle> g4particle = std::make_unique<G4PrimaryParticle>(particleDefinition,px,py,pz);
645 
646  if (genpart->end_vertex()) {
647  // Set the lifetime appropriately - this is slow but rigorous, and we
648  // don't want to end up with something like vertex time that we have
649  // to validate for every generator on earth...
650  const auto& prodVtx = genpart->production_vertex()->position();
651  const auto& endVtx = genpart->end_vertex()->position();
652  //const G4LorentzVector lv0 ( prodVtx.x(), prodVtx.y(), prodVtx.z(), prodVtx.t() );
653  //const G4LorentzVector lv1 ( endVtx.x(), endVtx.y(), endVtx.z(), endVtx.t() );
654  //Old calculation, not taken because vertex information is not sufficiently precise
655  //g4particle->SetProperTime( (lv1-lv0).mag()/Gaudi::Units::c_light );
656 
657  CLHEP::Hep3Vector dist3D(endVtx.x()-prodVtx.x(), endVtx.y()-prodVtx.y(), endVtx.z()-prodVtx.z());
658  double tau=SetProperTimeFromDetectorFrameDecayLength(*g4particle,dist3D.mag());
659 
660  if (msgLvl(MSG::VERBOSE)) {
661  double pmag2=g4particle->GetTotalMomentum(); //magnitude of particle momentum
662  pmag2*=pmag2; //magnitude of particle momentum squared
663  double e2=g4particle->GetTotalEnergy(); //energy of particle
664  e2*=e2; //energy of particle squared
665  double beta2=pmag2/e2; //beta^2=v^2/c^2 for particle
666  double tau2=dist3D.mag2()*(1/beta2-1)/Gaudi::Units::c_light/Gaudi::Units::c_light;
667  ATH_MSG_VERBOSE("lifetime tau(beta)="<<std::sqrt(tau2)<<" tau="<<tau);
668  }
669  if (m_quasiStableParticlesIncluded) {
670  ATH_MSG_VERBOSE( "Detected primary particle with end vertex." );
671  ATH_MSG_VERBOSE( "Will add the primary particle set on." );
672  ATH_MSG_VERBOSE( "Primary Particle: " << genpart );
673  ATH_MSG_VERBOSE( "Number of daughters : " << genpart->end_vertex()->particles_out().size()<<" at position "<<genpart->end_vertex() );
674  }
675  else {
676  ATH_MSG_WARNING( "Detected primary particle with end vertex." );
677  ATH_MSG_WARNING( "Will add the primary particle set on." );
678  ATH_MSG_WARNING( "Primary Particle: " << genpart );
679  ATH_MSG_WARNING( "Number of daughters: " << genpart->end_vertex()->particles_out().size()<<" at position "<<genpart->end_vertex() );
680  }
681  // Add all necessary daughter particles
682  for ( auto daughter: genpart->end_vertex()->particles_out() ) {
683  if (m_quasiStableParticlesIncluded) {
684  ATH_MSG_VERBOSE ( "Attempting to add daughter particle: " << daughter );
685  }
686  else {
687  ATH_MSG_WARNING ( "Attempting to add daughter particle: " << daughter );
688  }
689  G4PrimaryParticle *daughterG4Particle = this->getDaughterG4PrimaryParticle( daughter );
690  if (!daughterG4Particle) {
691  ATH_MSG_ERROR("Bailing out of loop over daughters of particle due to errors - will not return G4Particle.");
692  return nullptr;
693  }
694  g4particle->SetDaughter( daughterG4Particle );
695  }
696  }
697 
698  return g4particle.release();
699 }
700 #else
701 G4PrimaryParticle* ISF::InputConverter::getDaughterG4PrimaryParticle(HepMC::GenParticle& genpart, bool makeLinkToTruth) const
702 {
703  ATH_MSG_VERBOSE("Creating G4PrimaryParticle from GenParticle.");
704 
705  const G4ParticleDefinition *particleDefinition = this->getG4ParticleDefinition(genpart.pdg_id());
706 
707  if (particleDefinition==nullptr) {
708  ATH_MSG_ERROR("ISF_to_G4Event particle conversion failed. ISF_Particle PDG code = " << genpart.pdg_id() <<
709  "\n This usually indicates a problem with the evgen step.\n" <<
710  "Please report this to the Generators group, mentioning the release and generator used for evgen and the PDG code above." );
711  return nullptr;
712  }
713 
714  // create new primaries and set them to the vertex
715  // G4double mass = particleDefinition->GetPDGMass();
716  auto &genpartMomentum = genpart.momentum();
717  G4double px = genpartMomentum.x();
718  G4double py = genpartMomentum.y();
719  G4double pz = genpartMomentum.z();
720 
721  std::unique_ptr<G4PrimaryParticle> g4particle = std::make_unique<G4PrimaryParticle>(particleDefinition,px,py,pz);
722 
723  if (genpart.end_vertex()) {
724  // Set the lifetime appropriately - this is slow but rigorous, and we
725  // don't want to end up with something like vertex time that we have
726  // to validate for every generator on earth...
727  const auto& prodVtx = genpart.production_vertex()->position();
728  const auto& endVtx = genpart.end_vertex()->position();
729  //const G4LorentzVector lv0 ( prodVtx.x(), prodVtx.y(), prodVtx.z(), prodVtx.t() );
730  //const G4LorentzVector lv1 ( endVtx.x(), endVtx.y(), endVtx.z(), endVtx.t() );
731  //Old calculation, not taken because vertex information is not sufficiently precise
732  //g4particle->SetProperTime( (lv1-lv0).mag()/Gaudi::Units::c_light );
733 
734  CLHEP::Hep3Vector dist3D(endVtx.x()-prodVtx.x(), endVtx.y()-prodVtx.y(), endVtx.z()-prodVtx.z());
735  double tau=SetProperTimeFromDetectorFrameDecayLength(*g4particle,dist3D.mag());
736 
737  if (msgLvl(MSG::VERBOSE)) {
738  double pmag2=g4particle->GetTotalMomentum(); //magnitude of particle momentum
739  pmag2*=pmag2; //magnitude of particle momentum squared
740  double e2=g4particle->GetTotalEnergy(); //energy of particle
741  e2*=e2; //energy of particle squared
742  double beta2=pmag2/e2; //beta^2=v^2/c^2 for particle
743  double tau2=dist3D.mag2()*(1/beta2-1)/Gaudi::Units::c_light/Gaudi::Units::c_light;
744  ATH_MSG_VERBOSE("lifetime tau(beta)="<<std::sqrt(tau2)<<" tau="<<tau);
745  }
746  if (m_quasiStableParticlesIncluded) {
747  ATH_MSG_VERBOSE( "Detected primary particle with end vertex." );
748  ATH_MSG_VERBOSE( "Will add the primary particle set on." );
749  ATH_MSG_VERBOSE( "Primary Particle: " << genpart );
750  ATH_MSG_VERBOSE( "Number of daughters: " << genpart.end_vertex()->particles_out_size()<<" at position "<<genpart.end_vertex());
751  }
752  else {
753  ATH_MSG_WARNING( "Detected primary particle with end vertex." );
754  ATH_MSG_WARNING( "Will add the primary particle set on." );
755  ATH_MSG_WARNING( "Primary Particle: " << genpart );
756  ATH_MSG_WARNING( "Number of daughters: " << genpart.end_vertex()->particles_out_size()<<" at position "<<genpart.end_vertex() );
757  }
758  // Add all necessary daughter particles
759  for ( auto daughterIter=genpart.end_vertex()->particles_out_const_begin();
760  daughterIter!=genpart.end_vertex()->particles_out_const_end(); ++daughterIter ) {
761  if (m_quasiStableParticlesIncluded) {
762  ATH_MSG_VERBOSE ( "Attempting to add daughter particle: " << **daughterIter );
763  }
764  else {
765  ATH_MSG_WARNING ( "Attempting to add daughter particle: " << **daughterIter );
766  }
767  G4PrimaryParticle *daughterG4Particle = this->getDaughterG4PrimaryParticle( **daughterIter, makeLinkToTruth );
768  if (!daughterG4Particle) {
769  ATH_MSG_ERROR("Bailing out of loop over daughters of particle due to errors - will not return G4Particle.");
770  return nullptr;
771  }
772  g4particle->SetDaughter( daughterG4Particle );
773  }
774  }
775 
776  if (makeLinkToTruth) {
777  // Set the user information for this primary to point to the HepMcParticleLink...
778  std::unique_ptr<PrimaryParticleInformation> primaryPartInfo = std::make_unique<PrimaryParticleInformation>(&genpart);
779  primaryPartInfo->SetRegenerationNr(0);
780  g4particle->SetUserInformation(primaryPartInfo.release());
781  ATH_MSG_VERBOSE("Making primary down the line with barcode " << primaryPartInfo->GetParticleUniqueID());
782  }
783 
784  return g4particle.release();
785 }
786 #endif
787 
788 //________________________________________________________________________
790  const HepMC::ConstGenParticlePtr& p2) const // TODO Helper method?
791 {
792  return (HepMC::barcode(p1) == HepMC::barcode(p2))
793  && (p1->status() == p2->status())
794  && (p1->pdg_id() == p2->pdg_id())
795  && ((p1->momentum().px()) == (p2->momentum().px()))
796  && ((p1->momentum().py()) == (p2->momentum().py()))
797  && ((p1->momentum().pz()) == (p2->momentum().pz()))
798  && (float(p1->momentum().m()) == float(p2->momentum().m()));
799 }
800 
801 //________________________________________________________________________
802 HepMC::GenParticlePtr ISF::InputConverter::findShadowParticle(const HepMC::ConstGenParticlePtr& genParticle, HepMC::GenEvent *shadowGenEvent) const // TODO Helper method?
803 {
804  if (!shadowGenEvent) {
805  ATH_MSG_FATAL ("Found status==2 GenParticle with no end vertex and shadow GenEvent is missing - something is wrong here!");
806  abort();
807  }
808 #ifdef HEPMC3
809  // TODO in the future switch to using an Attribute which stores the shadow GenParticlePtr directly.
810  const int shadowId = genParticle->attribute<HepMC3::IntAttribute>("ShadowParticleId")->value();
811  for (auto& shadowParticle : shadowGenEvent->particles()) {
812  if (shadowParticle->id() == shadowId && matchedGenParticles(genParticle, shadowParticle) ) { return shadowParticle; }
813  }
814  return std::make_shared<HepMC::GenParticle>();
815 #else
816  for (HepMC::GenEvent::particle_iterator pitr=shadowGenEvent->particles_begin(); pitr != shadowGenEvent->particles_end(); ++pitr) {
817  HepMC::GenParticlePtr shadowParticle = (*pitr);
818  if (matchedGenParticles(genParticle, shadowParticle) ) { return shadowParticle; }
819  }
820  return nullptr;
821 #endif
822 
823 }
824 
825 #ifdef HEPMC3
826 //________________________________________________________________________
827 void ISF::InputConverter::processPredefinedDecays(const HepMC::ConstGenParticlePtr& genpart, ISF::ISFParticle& isp, G4PrimaryParticle* g4particle) const
828 {
832  const auto& prodVtx = genpart->production_vertex()->position();
833  const auto& endVtx = genpart->end_vertex()->position();
834 
835  CLHEP::Hep3Vector dist3D(endVtx.x()-prodVtx.x(), endVtx.y()-prodVtx.y(), endVtx.z()-prodVtx.z());
836  double tau=SetProperTimeFromDetectorFrameDecayLength(*g4particle,dist3D.mag());
837 
838  if (msgLvl(MSG::VERBOSE)) {
839  double pmag2=g4particle->GetTotalMomentum(); //magnitude of particle momentum
840  pmag2*=pmag2; //magnitude of particle momentum squared
841  double e2=g4particle->GetTotalEnergy(); //energy of particle
842  e2*=e2; //energy^2 of particle
843  double beta2=pmag2/e2; //beta^2=v^2/c^2 for particle
844  double mass2=g4particle->GetMass(); //mass of particle
845  mass2*=mass2; //mass^2 of particle
846 
847  double tau2=dist3D.mag2()*(1/beta2-1)/Gaudi::Units::c_squared;
848 
849  const G4LorentzVector lv0( prodVtx.x(), prodVtx.y(), prodVtx.z(), prodVtx.t() );
850  const G4LorentzVector lv1( endVtx.x(), endVtx.y(), endVtx.z(), endVtx.t() );
851  //Old calculation, not taken because vertex information is not sufficiently precise
852  //g4particle->SetProperTime( (lv1-lv0).mag()/Gaudi::Units::c_light );
853  G4LorentzVector dist4D(lv1);
854  dist4D-=lv0;
855 
856  double dist4Dgamma=std::numeric_limits<double>::infinity();
857  if (dist4D.t()>0 && dist4D.mag2()>0) {
858  dist4Dgamma=dist4D.gamma();
859  } else {
860  ATH_MSG_VERBOSE( "dist4D t="<<dist4D.t()<<" mag2="<<dist4D.mag2());
861  }
862 
863  G4LorentzVector fourmom(g4particle->GetMomentum(),g4particle->GetTotalEnergy());
864  double fourmomgamma=std::numeric_limits<double>::infinity();
865  if (fourmom.t()>0 && fourmom.mag2()>0) {
866  fourmomgamma=fourmom.gamma();
867  } else {
868  ATH_MSG_VERBOSE( "fourmom t="<<fourmom.t()<<" mag2="<<fourmom.mag2());
869  }
870 
871  ATH_MSG_VERBOSE( "gammaVertex="<<dist4Dgamma<<" gammamom="<<fourmomgamma<<" gamma(beta)="<<1/std::sqrt(1-beta2)<<" lifetime tau(beta)="<<std::sqrt(tau2)<<" lifetime tau="<<tau);
872  }
873  if (m_quasiStableParticlesIncluded) {
874  ATH_MSG_VERBOSE( "Detected primary particle with end vertex." );
875  ATH_MSG_VERBOSE( "Will add the primary particle set on." );
876  ATH_MSG_VERBOSE( "ISF Particle: " << isp );
877  ATH_MSG_VERBOSE( "Primary Particle: " << genpart );
878  ATH_MSG_VERBOSE( "Number of daughters: " << genpart->end_vertex()->particles_out().size() << " at position "<< genpart->end_vertex() );
879  }
880  else {
881  ATH_MSG_WARNING( "Detected primary particle with end vertex. This should only be the case if" );
882  ATH_MSG_WARNING( "you are running with quasi-stable particle simulation enabled. This is not" );
883  ATH_MSG_WARNING( "yet validated - you'd better know what you're doing. Will add the primary" );
884  ATH_MSG_WARNING( "particle set on." );
885  ATH_MSG_WARNING( "ISF Particle: " << isp );
886  ATH_MSG_WARNING( "Primary Particle: " << genpart );
887  ATH_MSG_VERBOSE( "Number of daughters: " << genpart->end_vertex()->particles_out().size() );
888  }
889  // Add all necessary daughter particles
890  for ( auto daughter: *(genpart->end_vertex())) {
891  if (m_quasiStableParticlesIncluded) {
892  ATH_MSG_VERBOSE ( "Attempting to add daughter particle" << daughter );
893  }
894  else {
895  ATH_MSG_WARNING ( "Attempting to add daughter particle: " << daughter );
896  }
897  G4PrimaryParticle *daughterG4Particle = this->getDaughterG4PrimaryParticle( daughter );
898  if (!daughterG4Particle) {
899  ATH_MSG_FATAL("Bailing out of loop over daughters due to errors.");
900  }
901  g4particle->SetDaughter( daughterG4Particle );
902  }
903 }
904 #endif
905 
906 //________________________________________________________________________
907 void ISF::InputConverter::processPredefinedDecays(const HepMC::GenParticlePtr& genpart, ISF::ISFParticle& isp, G4PrimaryParticle* g4particle, bool makeLinkToTruth) const
908 {
912  const auto& prodVtx = genpart->production_vertex()->position();
913  const auto& endVtx = genpart->end_vertex()->position();
914 
915  CLHEP::Hep3Vector dist3D(endVtx.x()-prodVtx.x(), endVtx.y()-prodVtx.y(), endVtx.z()-prodVtx.z());
916  double tau=SetProperTimeFromDetectorFrameDecayLength(*g4particle,dist3D.mag());
917 
918  if (msgLvl(MSG::VERBOSE)) {
919  double pmag2=g4particle->GetTotalMomentum(); //magnitude of particle momentum
920  pmag2*=pmag2; //magnitude of particle momentum squared
921  double e2=g4particle->GetTotalEnergy(); //energy of particle
922  e2*=e2; //energy^2 of particle
923  double beta2=pmag2/e2; //beta^2=v^2/c^2 for particle
924  double mass2=g4particle->GetMass(); //mass of particle
925  mass2*=mass2; //mass^2 of particle
926 
927  double tau2=dist3D.mag2()*(1/beta2-1)/Gaudi::Units::c_squared;
928 
929  const G4LorentzVector lv0( prodVtx.x(), prodVtx.y(), prodVtx.z(), prodVtx.t() );
930  const G4LorentzVector lv1( endVtx.x(), endVtx.y(), endVtx.z(), endVtx.t() );
931  //Old calculation, not taken because vertex information is not sufficiently precise
932  //g4particle->SetProperTime( (lv1-lv0).mag()/Gaudi::Units::c_light );
933  G4LorentzVector dist4D(lv1);
934  dist4D-=lv0;
935 
936  double dist4Dgamma=std::numeric_limits<double>::infinity();
937  if (dist4D.t()>0 && dist4D.mag2()>0) {
938  dist4Dgamma=dist4D.gamma();
939  } else {
940  ATH_MSG_VERBOSE( "dist4D t="<<dist4D.t()<<" mag2="<<dist4D.mag2());
941  }
942 
943  G4LorentzVector fourmom(g4particle->GetMomentum(),g4particle->GetTotalEnergy());
944  double fourmomgamma=std::numeric_limits<double>::infinity();
945  if (fourmom.t()>0 && fourmom.mag2()>0) {
946  fourmomgamma=fourmom.gamma();
947  } else {
948  ATH_MSG_VERBOSE( "fourmom t="<<fourmom.t()<<" mag2="<<fourmom.mag2());
949  }
950 
951  ATH_MSG_VERBOSE( "gammaVertex="<<dist4Dgamma<<" gammamom="<<fourmomgamma<<" gamma(beta)="<<1/std::sqrt(1-beta2)<<" lifetime tau(beta)="<<std::sqrt(tau2)<<" lifetime tau="<<tau);
952  }
953  if (m_quasiStableParticlesIncluded) {
954  ATH_MSG_VERBOSE( "Detected primary particle with end vertex." );
955  ATH_MSG_VERBOSE( "Will add the primary particle set on." );
956  ATH_MSG_VERBOSE( "ISF Particle: " << isp );
957  ATH_MSG_VERBOSE( "Primary Particle: " << genpart );
958  ATH_MSG_VERBOSE( "Number of daughters: " << genpart->end_vertex()->particles_out_size() << " at position "<< genpart->end_vertex() );
959  }
960  else {
961  ATH_MSG_WARNING( "Detected primary particle with end vertex. This should only be the case if" );
962  ATH_MSG_WARNING( "you are running with quasi-stable particle simulation enabled. This is not" );
963  ATH_MSG_WARNING( "yet validated - you'd better know what you're doing. Will add the primary" );
964  ATH_MSG_WARNING( "particle set on." );
965  ATH_MSG_WARNING( "ISF Particle: " << isp );
966  ATH_MSG_WARNING( "Primary Particle: " << genpart );
967  ATH_MSG_WARNING( "Number of daughters: " << genpart->end_vertex()->particles_out_size() );
968  }
969  // Add all necessary daughter particles
970  for ( auto daughter: *(genpart->end_vertex())) {
971  if (m_quasiStableParticlesIncluded) {
972  ATH_MSG_VERBOSE ( "Attempting to add daughter particle: " << daughter );
973  }
974  else {
975  ATH_MSG_WARNING ( "Attempting to add daughter particle: " << daughter );
976  }
977 #ifdef HEPMC3
978  G4PrimaryParticle *daughterG4Particle = this->getDaughterG4PrimaryParticle( daughter, makeLinkToTruth );
979 #else
980  G4PrimaryParticle *daughterG4Particle = this->getDaughterG4PrimaryParticle( *daughter, makeLinkToTruth );
981 #endif
982  if (!daughterG4Particle) {
983  ATH_MSG_FATAL("Bailing out of loop over daughters due to errors.");
984  }
985  g4particle->SetDaughter( daughterG4Particle );
986  }
987 }
988 
989 G4PrimaryParticle* ISF::InputConverter::getG4PrimaryParticle(ISF::ISFParticle& isp, bool useHepMC, HepMC::GenEvent *shadowGenEvent) const
990 {
991  ATH_MSG_VERBOSE("Creating G4PrimaryParticle from ISFParticle.");
992 
993  auto* truthBinding = isp.getTruthBinding();
994  if (!truthBinding) {
995  G4ExceptionDescription description;
996  description << G4String("getG4PrimaryParticle: ") + "No ISF::TruthBinding associated with ISParticle (" << isp <<")";
997  G4Exception("iGeant4::TransportTool", "NoISFTruthBinding", FatalException, description);
998  return nullptr; //The G4Exception call above should abort the job, but Coverity does not seem to pick this up.
999  }
1000  HepMC::GenParticlePtr currentGenPart = truthBinding->getCurrentGenParticle();
1001  HepMC::GenParticlePtr primaryGenpart = truthBinding->getPrimaryGenParticle();
1002 
1003  const G4ParticleDefinition *particleDefinition = this->getG4ParticleDefinition(isp.pdgCode());
1004 
1005  if (particleDefinition==nullptr) {
1006  ATH_MSG_ERROR("ISF_to_G4Event particle conversion failed. ISF_Particle PDG code = " << isp.pdgCode() <<
1007  "\n This usually indicates a problem with the evgen step.\n" <<
1008  "Please report this to the Generators group, mentioning the release and generator used for evgen and the PDG code above." );
1009  return nullptr;
1010  }
1011 
1012  // create new primaries and set them to the vertex
1013  // G4double mass = particleDefinition->GetPDGMass();
1014  G4double px(0.0);
1015  G4double py(0.0);
1016  G4double pz(0.0);
1017  if (useHepMC && currentGenPart) {
1018  auto &currentGenPartMomentum = currentGenPart->momentum();
1019  px = currentGenPartMomentum.x();
1020  py = currentGenPartMomentum.y();
1021  pz = currentGenPartMomentum.z();
1022  }
1023  else {
1024  auto &ispMomentum = isp.momentum();
1025  px = ispMomentum.x();
1026  py = ispMomentum.y();
1027  pz = ispMomentum.z();
1028  }
1029 
1030  std::unique_ptr<G4PrimaryParticle> g4particle = std::make_unique<G4PrimaryParticle>(particleDefinition,px,py,pz);
1031  // UserInformation
1032  std::unique_ptr<PrimaryParticleInformation> primaryPartInfo = std::make_unique<PrimaryParticleInformation>(primaryGenpart,&isp);
1033 
1037  const int regenerationNr = HepMC::StatusBased::generations(&isp);
1038  if (HepMC::BarcodeBased::generations(&isp) != regenerationNr) {
1039  ATH_MSG_WARNING ("StatusBased::generations() = " << regenerationNr << ", BarcodeBased::generations() = " << HepMC::BarcodeBased::generations(&isp) << ", isp: " << isp);
1040  }
1041  primaryPartInfo->SetRegenerationNr(regenerationNr);
1042 
1043  if ( currentGenPart ) {
1044  if (currentGenPart->end_vertex()) {
1045  // Old approach particle had an end vertex - predefined decays taken from the main GenEvent
1046  // No longer supported
1047  ATH_MSG_ERROR ( "getG4PrimaryParticle(): GenParticle has a valid end GenVertexPtr!" );
1048  ATH_MSG_ERROR ( "getG4PrimaryParticle(): currentGenPart: " << currentGenPart << ", barcode: " << HepMC::barcode(currentGenPart) );
1049  ATH_MSG_ERROR ( "getG4PrimaryParticle(): currentGenPart->end_vertex(): " << currentGenPart->end_vertex() << ", barcode: " << HepMC::barcode(currentGenPart->end_vertex()) );
1050  ATH_MSG_FATAL ( "getG4PrimaryParticle(): Passing GenParticles with a valid end GenVertexPtr as input is no longer supported." );
1051  abort();
1052  }
1053  else if (MC::isDecayed(currentGenPart) // Some assumptions about main GenEvent here
1054  && !currentGenPart->end_vertex()) {
1055  // New approach - predefined decays taken from shadow GenEvent
1056  // Find the matching particle in the shadowGenEvent
1057 #ifdef HEPMC3
1058  auto A_part = currentGenPart->attribute<HepMC::ShadowParticle>("ShadowParticle");
1059  HepMC::ConstGenParticlePtr shadowPart = (A_part) ? A_part->value() : findShadowParticle(currentGenPart, shadowGenEvent);
1060 #else
1061  HepMC::GenParticlePtr shadowPart = findShadowParticle(currentGenPart, shadowGenEvent);
1062 #endif
1063  if (!shadowPart) {
1064  ATH_MSG_FATAL ("Found a GenParticle with no matching GenParticle in the shadowGenEvent - something is wrong here!");
1065  abort();
1066  }
1067  if (!shadowPart->end_vertex()) {
1068  ATH_MSG_FATAL ("Found status==2 shadow GenParticle with no end vertex - something is wrong here!");
1069  abort();
1070  }
1071 #ifdef HEPMC3
1072  processPredefinedDecays(shadowPart, isp, g4particle.get());
1073 #else
1074  processPredefinedDecays(shadowPart, isp, g4particle.get(), false); // false to avoid truth-links to the shadow GenEvent
1075 #endif
1076  }
1077 
1078  double px,py,pz;
1079  const double pmass = g4particle->GetMass();
1080  CLHEP::Hep3Vector gpv = g4particle->GetMomentum();
1081  double g4px=g4particle->GetMomentum().x();
1082  double g4py=g4particle->GetMomentum().y();
1083  double g4pz=g4particle->GetMomentum().z();
1084  if (useHepMC) {
1085  //Code adapted from TruthHepMCEventConverter::TransformHepMCParticle
1086  px=g4px;
1087  py=g4py;
1088  pz=g4pz;
1089  } else {
1090  //Take mass from g4particle, put keep momentum as in currentGenPart
1091  px=currentGenPart->momentum().px();
1092  py=currentGenPart->momentum().py();
1093  pz=currentGenPart->momentum().pz();
1094  //Now a dirty hack to keep backward compatibility in the truth:
1095  //When running AtlasG4 or FullG4 between 21.0.41 and 21.0.111, the currentGenPart 3-momentum and mass was reset to the values from the g4particle
1096  //together with the mass of the g4particle after the 1st initialization of the g4particle from the genevent. This is done for a consistent mass
1097  //value in the truth record compared to the used g4 mass. Since g4particles don't store the 3-momentum directly, but rather a
1098  //unit direction vector, the mass and the kinetic energy, this reduces the numeric accuracy.
1099  //For backward compatibility, if all 3-momentum components agree to the g4particle momentum within 1 keV, we keep
1100  //this old method. This comparison is needed, since in ISF this code could be rerun after the ID or CALO simulation, where
1101  //real energy was lost in previous detectors and hence currentGenPart should NOT be changed to some g4particle values!
1102  //TODO: find a way to implement this in a backward compatible way in ISF::InputConverter::convertParticle(HepMC::GenParticlePtr genPartPtr)
1103  if (std::abs(px-g4px)<CLHEP::keV && std::abs(py-g4py)<CLHEP::keV && std::abs(pz-g4pz)<CLHEP::keV) {
1104  px=g4px;
1105  py=g4py;
1106  pz=g4pz;
1107  }
1108  }
1109  const double mag2=px*px + py*py + pz*pz;
1110  const double pe = std::sqrt(mag2 + pmass*pmass); // this does only change for boosts, etc.
1111 
1112  double originalEnergy=currentGenPart->momentum().e();
1113  if (originalEnergy>0.01) { //only test for >1 MeV in momentum
1114  if ((originalEnergy-pe)/originalEnergy>0.01) {
1115  double genpx=currentGenPart->momentum().px();
1116  double genpy=currentGenPart->momentum().py();
1117  double genpz=currentGenPart->momentum().pz();
1118  double genp=sqrt(genpx*genpx + genpy*genpy + genpz*genpz);
1119  ATH_MSG_WARNING("Truth change in energy for: " << currentGenPart<<" Morg="<<currentGenPart->momentum().m()<<" Mmod="<<pmass<<" Eorg="<<originalEnergy<<" Emod="<<pe<<" porg="<<genp<<" pmod="<<gpv.mag());
1120  }
1121  }
1122 
1123 #ifdef HEPMC3
1124  auto& currentGenPart_nc = currentGenPart;
1125 #else
1126  auto* currentGenPart_nc = currentGenPart;
1127 #endif
1128  currentGenPart_nc->set_momentum(HepMC::FourVector(px,py,pz,pe));
1129  } // Truth was detected
1130 
1131  ATH_MSG_VERBOSE("PrimaryParticleInformation:");
1132  ATH_MSG_VERBOSE(" GetParticleUniqueID = " << primaryPartInfo->GetParticleUniqueID());
1133  ATH_MSG_VERBOSE(" GetRegenerationNr = " << primaryPartInfo->GetRegenerationNr());
1134  ATH_MSG_VERBOSE(" GetHepMCParticle = " << primaryPartInfo->GetHepMCParticle());
1135  ATH_MSG_VERBOSE(" GetISFParticle = " << primaryPartInfo->GetISFParticle());
1136  g4particle->SetUserInformation(primaryPartInfo.release());
1137 
1138  return g4particle.release();
1139 }
1140 
1141 //________________________________________________________________________
1142 void ISF::InputConverter::addG4PrimaryVertex(G4Event* g4evt, ISF::ISFParticle& isp, bool useHepMC, HepMC::GenEvent *shadowGenEvent) const
1143 {
1144  /*
1145  see conversion from PrimaryParticleInformation to TrackInformation in
1146  http://acode-browser.usatlas.bnl.gov/lxr/source/atlas/Simulation/G4Atlas/G4AtlasAlg/src/AthenaStackingAction.cxx#0044
1147 
1148  need to check with
1149  http://acode-browser.usatlas.bnl.gov/lxr/source/atlas/Simulation/G4Atlas/G4AtlasAlg/src/TruthHepMCEventConverter.cxx#0151
1150 
1151  that we don't miss something
1152  */
1153 
1154  G4PrimaryParticle *g4particle = this->getG4PrimaryParticle( isp, useHepMC, shadowGenEvent );
1155  if (!g4particle) {
1156  ATH_MSG_ERROR("Failed to create G4PrimaryParticle for ISParticle (" << isp <<")");
1157  return;
1158  }// Already printed a warning
1159 
1160  // create a new vertex
1161  G4PrimaryVertex *g4vertex = new G4PrimaryVertex(isp.position().x(),
1162  isp.position().y(),
1163  isp.position().z(),
1164  isp.timeStamp());
1165  g4vertex->SetPrimary( g4particle );
1166  ATH_MSG_VERBOSE("Print G4PrimaryVertex: ");
1167  if (msgLevel(MSG::VERBOSE)) { g4vertex->Print(); }
1168  g4evt->AddPrimaryVertex( g4vertex );
1169  return;
1170 }
1171 
1172 //________________________________________________________________________
1173 bool ISF::InputConverter::isInsideG4WorldVolume(const ISF::ISFParticle& isp, const G4VSolid* worldSolid) const
1174 {
1175 
1176  const Amg::Vector3D &pos = isp.position();
1177  const G4ThreeVector g4Pos( pos.x(), pos.y(), pos.z() );
1178  EInside insideStatus = worldSolid->Inside( g4Pos );
1179 
1180  bool insideWorld = insideStatus != kOutside;
1181  return insideWorld;
1182 }
ISF::ISFParticleContainer
std::list< ISF::ISFParticle * > ISFParticleContainer
generic ISFParticle container (not necessarily a std::list!)
Definition: ISFParticleContainer.h:23
LArG4FSStartPointFilter.part
part
Definition: LArG4FSStartPointFilter.py:21
AllowedVariables::e
e
Definition: AsgElectronSelectorTool.cxx:37
ISF::InputConverter::addG4PrimaryVertex
void addG4PrimaryVertex(G4Event *g4evt, ISF::ISFParticle &isp, bool useHepMC, HepMC::GenEvent *shadowGenEvent) const
Definition: InputConverter.cxx:1142
ISF::DetRegionSvcIDPair
std::pair< AtlasDetDescr::AtlasRegion, ISF::SimSvcID > DetRegionSvcIDPair
the datatype to be used to store each individual particle hop
Definition: ISFParticle.h:30
AtlasG4EventUserInfo::SetLastProcessedTrackID
void SetLastProcessedTrackID(int trackID)
record the value of G4Track::GetTrackID() for the current G4Step.
Definition: AtlasG4EventUserInfo.h:83
AtlasG4EventUserInfo
This class is attached to G4Event objects as UserInformation. It holds a pointer to the HepMC::GenEve...
Definition: AtlasG4EventUserInfo.h:21
ATH_MSG_FATAL
#define ATH_MSG_FATAL(x)
Definition: AthMsgStreamMacros.h:34
GenEvent.h
test_pyathena.px
px
Definition: test_pyathena.py:18
python.SystemOfUnits.m2
int m2
Definition: SystemOfUnits.py:92
AtlasG4EventUserInfo::SetLastProcessedStep
void SetLastProcessedStep(int stepNumber)
record value of the G4Track::GetCurrentStepNumber() for the current G4Step.
Definition: AtlasG4EventUserInfo.h:100
ISF::InputConverter::getG4ParticleDefinition
const G4ParticleDefinition * getG4ParticleDefinition(int pdgcode) const
Definition: InputConverter.cxx:504
FullCPAlgorithmsTest_eljob.flush
flush
Definition: FullCPAlgorithmsTest_eljob.py:182
Base_Fragment.mass
mass
Definition: Sherpa_i/share/common/Base_Fragment.py:59
HepMC::StatusBased::generations
int generations(const T &p)
Method to return how many interactions a particle has undergone during simulation based on the status...
Definition: MagicNumbers.h:271
HepMC::signal_process_id
int signal_process_id(const GenEvent &e)
Definition: GenEvent.h:635
ISF::InputConverter::ISF_to_G4Event
G4Event * ISF_to_G4Event(const EventContext &ctx, const std::vector< ISF::ISFParticle * > &isp, HepMC::GenEvent *genEvent, HepMC::GenEvent *shadowGenEvent=nullptr, bool useHepMC=false) const override final
Converts vector of ISF::ISFParticles to G4Event.
Definition: InputConverter.cxx:469
AtlasG4EventUserInfo::SetHepMCEvent
void SetHepMCEvent(HepMC::GenEvent *)
set m_theEvent, the pointer to the HepMC::GenEvent used to create the G4Event.
Definition: AtlasG4EventUserInfo.cxx:13
GenVertex.h
HepMC::GenParticlePtr
GenParticle * GenParticlePtr
Definition: GenParticle.h:37
TRTCalib_cfilter.p1
p1
Definition: TRTCalib_cfilter.py:130
PlotCalibFromCool.begin
begin
Definition: PlotCalibFromCool.py:94
ISF::ISFParticle
Definition: ISFParticle.h:42
ISF::ISFParticle::pdgCode
int pdgCode() const
PDG value.
AtlasDetDescr::fUndefinedAtlasRegion
@ fUndefinedAtlasRegion
Definition: AtlasRegion.h:29
athena.value
value
Definition: athena.py:124
PrimaryParticleInformation::GetRegenerationNr
int GetRegenerationNr()
return the number of times the particle represented by the G4PrimaryParticle has undergone a non-dest...
Definition: PrimaryParticleInformation.h:55
covarianceTool.passFilter
bool passFilter
Definition: covarianceTool.py:604
ATH_MSG_VERBOSE
#define ATH_MSG_VERBOSE(x)
Definition: AthMsgStreamMacros.h:28
ISF::InputConverter::m_particlePropSvc
ServiceHandle< IPartPropSvc > m_particlePropSvc
ParticlePropertyService and ParticleDataTable.
Definition: InputConverter.h:123
Analysis::pMass
constexpr double pMass
Definition: JpsiPlus2Tracks.cxx:33
LArG4AODNtuplePlotter.pe
pe
Definition: LArG4AODNtuplePlotter.py:116
GenParticle.h
ISF::InputConverter::convertParticle
ISF::ISFParticle * convertParticle(const HepMC::GenParticlePtr &genPartPtr) const
convert GenParticle to ISFParticle
Definition: InputConverter.cxx:283
HepMC::BarcodeBased::generations
int generations(const T &p)
Method to return how many interactions a particle has undergone during simulation (only to be used in...
Definition: MagicNumbers.h:212
mergePhysValFiles.end
end
Definition: DataQuality/DataQualityUtils/scripts/mergePhysValFiles.py:93
ISF::InputConverter::processPredefinedDecays
void processPredefinedDecays(const HepMC::GenParticlePtr &genpart, ISF::ISFParticle &isp, G4PrimaryParticle *g4particle, bool makeLinkToTruth=true) const
Definition: InputConverter.cxx:907
ISF::ISFParticle::position
const Amg::Vector3D & position() const
The current position of the ISFParticle.
ISF::InputConverter::InputConverter
InputConverter(const std::string &name, ISvcLocator *svc)
Constructor.
Definition: InputConverter.cxx:50
covarianceTool.filter
filter
Definition: covarianceTool.py:514
ISFParticleContainer.h
ISF::InputConverter::findShadowParticle
HepMC::GenParticlePtr findShadowParticle(const HepMC::ConstGenParticlePtr &genParticle, HepMC::GenEvent *shadowGenEvent) const
Definition: InputConverter.cxx:802
python.SystemOfUnits.keV
int keV
Definition: SystemOfUnits.py:156
TruthBinding.h
TRTCalib_cfilter.p2
p2
Definition: TRTCalib_cfilter.py:131
ISFParticle.h
skel.l2
l2
Definition: skel.GENtoEVGEN.py:399
python.utils.AtlRunQueryDQUtils.p
p
Definition: AtlRunQueryDQUtils.py:210
ParticleGun_EoverP_Config.mom
mom
Definition: ParticleGun_EoverP_Config.py:63
ISF::InputConverter::initialize
virtual StatusCode initialize() override final
Athena algtool Hooks.
Definition: InputConverter.cxx:82
ATH_MSG_ERROR
#define ATH_MSG_ERROR(x)
Definition: AthMsgStreamMacros.h:33
ISF::InputConverter::getG4PrimaryParticle
G4PrimaryParticle * getG4PrimaryParticle(ISF::ISFParticle &isp, bool useHepMC, HepMC::GenEvent *shadowGenEvent) const
Definition: InputConverter.cxx:989
ParticleGun_EoverP_Config.momentum
momentum
Definition: ParticleGun_EoverP_Config.py:63
McEventCollection.h
InputConverter.h
PrimaryParticleInformation::GetISFParticle
const ISF::ISFParticle * GetISFParticle() const
return a pointer to the ISFParticle used to create the G4PrimaryParticle
Definition: PrimaryParticleInformation.h:66
ISF::InputConverter::isInsideG4WorldVolume
bool isInsideG4WorldVolume(const ISF::ISFParticle &isp, const G4VSolid *worldSolid) const
Tests whether the given ISFParticle is within the Geant4 world volume.
Definition: InputConverter.cxx:1173
EL::StatusCode
::StatusCode StatusCode
StatusCode definition for legacy code.
Definition: PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/StatusCode.h:22
ATH_MSG_DEBUG
#define ATH_MSG_DEBUG(x)
Definition: AthMsgStreamMacros.h:29
HepMC::barcode
int barcode(const T *p)
Definition: Barcode.h:16
ISF::ISFParticleVector
std::vector< ISF::ISFParticle * > ISFParticleVector
ISFParticle vector.
Definition: ISFParticleContainer.h:26
HepMC::uniqueID
int uniqueID(const T &p)
Definition: MagicNumbers.h:116
Amg::pz
@ pz
Definition: GeoPrimitives.h:40
ISF::InputConverter::m_useGeneratedParticleMass
bool m_useGeneratedParticleMass
use GenParticle::generated_mass() in simulation
Definition: InputConverter.h:126
ISF::InputConverter::matchedGenParticles
bool matchedGenParticles(const HepMC::ConstGenParticlePtr &p1, const HepMC::ConstGenParticlePtr &p2) const
Definition: InputConverter.cxx:789
jobOptions.ParticleID
ParticleID
Definition: jobOptions.decayer.py:85
AnalysisUtils::copy_if
Out copy_if(In first, const In &last, Out res, const Pred &p)
Definition: IFilterUtils.h:30
ATH_CHECK
#define ATH_CHECK
Definition: AthCheckMacros.h:40
ISF::InputConverter::convert
virtual StatusCode convert(McEventCollection &inputGenEvents, ISF::ISFParticleContainer &simParticles) const override final
Convert selected particles from the given McEventCollection into ISFParticles and push them into the ...
Definition: InputConverter.cxx:117
Handler::svc
AthROOTErrorHandlerSvc * svc
Definition: AthROOTErrorHandlerSvc.cxx:10
McEventCollection
This defines the McEventCollection, which is really just an ObjectVector of McEvent objects.
Definition: McEventCollection.h:33
DataVector::back
const T * back() const
Access the last element in the collection as an rvalue.
HepMC::ConstGenParticlePtr
const GenParticle * ConstGenParticlePtr
Definition: GenParticle.h:38
ParticleUserInformation.h
Amg::py
@ py
Definition: GeoPrimitives.h:39
ISF::ISFParticle::timeStamp
double timeStamp() const
Timestamp of the ISFParticle.
ISF::InputConverter::getDaughterG4PrimaryParticle
G4PrimaryParticle * getDaughterG4PrimaryParticle(HepMC::GenParticle &gp, bool makeLinkToTruth=true) const
Definition: InputConverter.cxx:701
ISF::InputConverter::m_quasiStableParticlesIncluded
bool m_quasiStableParticlesIncluded
Definition: InputConverter.h:130
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:228
ISF::ISFParticle::momentum
const Amg::Vector3D & momentum() const
The current momentum vector of the ISFParticle.
python.PhysicalConstants.c_squared
float c_squared
Definition: PhysicalConstants.py:64
charge
double charge(const T &p)
Definition: AtlasPID.h:756
FakeBkgTools::maxParticles
constexpr uint8_t maxParticles()
Definition: FakeBkgInternals.h:93
python.PhysicalConstants.c_light
float c_light
Definition: PhysicalConstants.py:63
SetProperTimeFromDetectorFrameDecayLength
double SetProperTimeFromDetectorFrameDecayLength(G4PrimaryParticle &g4particle, const double GeneratorDecayLength)
Definition: InputConverter.cxx:522
PrimaryParticleInformation::SetRegenerationNr
void SetRegenerationNr(int i)
update the number of times the particle represented by the G4PrimaryParticle has undergone a non-dest...
Definition: PrimaryParticleInformation.h:61
Amg::Vector3D
Eigen::Matrix< double, 3, 1 > Vector3D
Definition: GeoPrimitives.h:47
TMVAToMVAUtils::convert
std::unique_ptr< MVAUtils::BDT > convert(TMVA::MethodBDT *bdt, bool isRegression=true, bool useYesNoLeaf=false)
Definition: TMVAToMVAUtils.h:114
python.LumiBlobConversion.pos
pos
Definition: LumiBlobConversion.py:18
ISF::InputConverter::convertHepMCToG4Event
virtual StatusCode convertHepMCToG4Event(const EventContext &ctx, McEventCollection &inputGenEvents, G4Event *&outputG4Event, McEventCollection &shadowGenEvents) const override final
Definition: InputConverter.cxx:155
PrimaryParticleInformation::GetHepMCParticle
HepMC::ConstGenParticlePtr GetHepMCParticle() const
return a pointer to the GenParticle used to create the G4PrimaryParticle
Definition: PrimaryParticleInformation.h:47
python.SystemOfUnits.mm
int mm
Definition: SystemOfUnits.py:83
skel.genpart
tuple genpart
Check that the actual generators, tune, and main PDF are consistent with the JO name.
Definition: skel.ABtoEVGEN.py:262
AtlasG4EventUserInfo.h
egammaEnergyPositionAllSamples::e2
double e2(const xAOD::CaloCluster &cluster)
return the uncorrected cluster energy in 2nd sampling
ATH_MSG_WARNING
#define ATH_MSG_WARNING(x)
Definition: AthMsgStreamMacros.h:32
MC::isDecayed
bool isDecayed(const T &p)
Identify if the particle decayed.
Definition: HepMCHelpers.h:42
PrimaryParticleInformation.h
IGenParticleFilter.h
python.DecayParser.children
children
Definition: DecayParser.py:32
ISF::InputConverter::getSelectedParticles
std::vector< HepMC::GenParticlePtr > getSelectedParticles(HepMC::GenEvent &evnt, bool legacyOrdering=false) const
get all generator particles which pass filters
Definition: InputConverter.cxx:245
python.Constants.VERBOSE
int VERBOSE
Definition: Control/AthenaCommon/python/Constants.py:14
ISF::fEventGeneratorSimID
@ fEventGeneratorSimID
Definition: SimSvcID.h:34
mag2
Scalar mag2() const
mag2 method - forward to squaredNorm()
Definition: AmgMatrixBasePlugin.h:31
ISF::InputConverter::convertHepMCToG4EventLegacy
virtual StatusCode convertHepMCToG4EventLegacy(const EventContext &ctx, McEventCollection &inputGenEvents, G4Event *&outputG4Event) const override final
Definition: InputConverter.cxx:175
pow
constexpr int pow(int base, int exp) noexcept
Definition: ap_fixedTest.cxx:15
Amg::distance
float distance(const Amg::Vector3D &p1, const Amg::Vector3D &p2)
calculates the distance between two point in 3D space
Definition: GeoPrimitivesHelpers.h:54
ISF::InputConverter::passesFilters
bool passesFilters(const HepMC::GenParticle &p) const
check if the given particle passes all filters
Definition: InputConverter.cxx:442
ISF::InputConverter::~InputConverter
virtual ~InputConverter()
Destructor.
Definition: InputConverter.cxx:75
ISF::InputConverter::getParticleMass
double getParticleMass(const HepMC::GenParticle &p) const
get right GenParticle mass
Definition: InputConverter.cxx:389
readCCLHist.float
float
Definition: readCCLHist.py:83
DataVector::empty
bool empty() const noexcept
Returns true if the collection is empty.
HepMCHelpers.h
ISF::InputConverter::m_genParticleFilters
ToolHandleArray< IGenParticleFilter > m_genParticleFilters
HepMC::GenParticle filters.
Definition: InputConverter.h:128
ISF::ISFParticle::getTruthBinding
const TruthBinding * getTruthBinding() const
pointer to the simulation truth - optional, can be 0
GenParticle
@ GenParticle
Definition: TruthClasses.h:30
SelectedParticlesUtil::getSelectedParticles
void getSelectedParticles(const ParticleLinks< Container > *links, SelectedParticles &selparts)
Definition: SelectedParticlesUtil.h:66
description
std::string description
glabal timer - how long have I taken so far?
Definition: hcg.cxx:88
PrimaryParticleInformation::GetParticleUniqueID
int GetParticleUniqueID() const
Definition: PrimaryParticleInformation.cxx:28
ISF::InputConverter::finalize
virtual StatusCode finalize() override final
Athena algtool Hook.
Definition: InputConverter.cxx:107