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(simParticleVector, inputGenEvents.back(), shadowGenEvents.back());
167  }
168  else{
169  outputG4Event = this->ISF_to_G4Event(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(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 ISF::ISFParticleVector& ispVector, HepMC::GenEvent *genEvent, HepMC::GenEvent *shadowGenEvent, bool useHepMC) const
470 {
471  const int eventID(1);
472  G4Event *g4evt = new G4Event(eventID);
473 
474  // retrieve world solid (volume)
475  const G4VSolid *worldSolid = G4TransportationManager::GetTransportationManager()->GetNavigatorForTracking()->GetWorldVolume()->GetLogicalVolume()->GetSolid();
476 
477  for ( ISF::ISFParticle *ispPtr: ispVector ) {
478  ISF::ISFParticle &isp = *ispPtr;
479  if ( !isInsideG4WorldVolume(isp, worldSolid) ) {
480  ATH_MSG_WARNING("Unable to convert ISFParticle to G4PrimaryParticle!");
481  ATH_MSG_WARNING(" ISFParticle: " << isp );
482  if (worldSolid) {
483  ATH_MSG_WARNING(" is outside Geant4 world volume: ");
484  worldSolid->DumpInfo();
485  G4cout << std::flush;
486  }
487  else {
488  ATH_MSG_WARNING(" is outside Geant4 world volume.");
489  }
490  continue;
491  }
492  this->addG4PrimaryVertex(g4evt,isp,useHepMC,shadowGenEvent);
493  }
494 
495  AtlasG4EventUserInfo *atlasG4EvtUserInfo=new AtlasG4EventUserInfo();
496  atlasG4EvtUserInfo->SetLastProcessedTrackID(0); // TODO Check if it is better to set this to -1 initially
497  atlasG4EvtUserInfo->SetLastProcessedStep(0); // TODO Check if it is better to set this to -1 initially
498  atlasG4EvtUserInfo->SetHepMCEvent(genEvent);
499  g4evt->SetUserInformation(atlasG4EvtUserInfo);
500 
501  return g4evt;
502 }
503 
504 //________________________________________________________________________
505 const G4ParticleDefinition* ISF::InputConverter::getG4ParticleDefinition(int pdgcode) const
506 {
508  if (pdgcode==998) {
509  return G4ChargedGeantino::Definition();
510  }
511  if (pdgcode==999) {
512  return G4Geantino::GeantinoDefinition();
513  }
515  G4ParticleTable *ptable = G4ParticleTable::GetParticleTable();
516  if (ptable) {
517  return ptable->FindParticle(pdgcode);
518  }
519  ATH_MSG_ERROR("getG4ParticleDefinition - Failed to retrieve G4ParticleTable!");
520  return nullptr;
521 }
522 
523 double SetProperTimeFromDetectorFrameDecayLength(G4PrimaryParticle& g4particle,const double GeneratorDecayLength)
524 {
525  //particle with velocity v travels distance l in time t=l/v
526  //proper time: c^2 tau^2 = c^2 t^2 - l^2 = l^2/c^2 * (1/beta^2 -1)
527  //beta^2 = p^2/E^2 with particle momentum p and energy E; E^2=m^2 + p^2
528  //tau^2 = l^2/c^2 * m^2/p^2
529  const double p2=std::pow(g4particle.GetTotalMomentum(),2); //magnitude of particle momentum squared
530  const double m2=std::pow(g4particle.GetMass(),2); //mass^2 of particle
531  const double l2=std::pow(GeneratorDecayLength,2); //distance^2 of particle decay length
532  const double tau2=l2*m2/p2/CLHEP::c_squared;
533  const double tau=std::sqrt(tau2);
534  g4particle.SetProperTime( tau );
535  return tau;
536 }
537 
538 //________________________________________________________________________
539 #ifdef HEPMC3
540 G4PrimaryParticle* ISF::InputConverter::getDaughterG4PrimaryParticle(const HepMC::GenParticlePtr& genpart, bool makeLinkToTruth) const{
541  ATH_MSG_VERBOSE("Creating G4PrimaryParticle from GenParticle.");
542 
543  const G4ParticleDefinition *particleDefinition = this->getG4ParticleDefinition(genpart->pdg_id());
544 
545  if (particleDefinition==nullptr) {
546  ATH_MSG_ERROR("ISF_to_G4Event particle conversion failed. ISF_Particle PDG code = " << genpart->pdg_id() <<
547  "\n This usually indicates a problem with the evgen step.\n" <<
548  "Please report this to the Generators group, mentioning the release and generator used for evgen and the PDG code above." );
549  return nullptr;
550  }
551 
552  // create new primaries and set them to the vertex
553  // G4double mass = particleDefinition->GetPDGMass();
554  auto &genpartMomentum = genpart->momentum();
555  G4double px = genpartMomentum.x();
556  G4double py = genpartMomentum.y();
557  G4double pz = genpartMomentum.z();
558 
559  std::unique_ptr<G4PrimaryParticle> g4particle = std::make_unique<G4PrimaryParticle>(particleDefinition,px,py,pz);
560 
561  if (genpart->end_vertex()) {
562  // Set the lifetime appropriately - this is slow but rigorous, and we
563  // don't want to end up with something like vertex time that we have
564  // to validate for every generator on earth...
565  const auto& prodVtx = genpart->production_vertex()->position();
566  const auto& endVtx = genpart->end_vertex()->position();
567  //const G4LorentzVector lv0 ( prodVtx.x(), prodVtx.y(), prodVtx.z(), prodVtx.t() );
568  //const G4LorentzVector lv1 ( endVtx.x(), endVtx.y(), endVtx.z(), endVtx.t() );
569  //Old calculation, not taken because vertex information is not sufficiently precise
570  //g4particle->SetProperTime( (lv1-lv0).mag()/Gaudi::Units::c_light );
571 
572  CLHEP::Hep3Vector dist3D(endVtx.x()-prodVtx.x(), endVtx.y()-prodVtx.y(), endVtx.z()-prodVtx.z());
573  double tau=SetProperTimeFromDetectorFrameDecayLength(*g4particle,dist3D.mag());
574 
575  if (msgLvl(MSG::VERBOSE)) {
576  double pmag2=g4particle->GetTotalMomentum(); //magnitude of particle momentum
577  pmag2*=pmag2; //magnitude of particle momentum squared
578  double e2=g4particle->GetTotalEnergy(); //energy of particle
579  e2*=e2; //energy of particle squared
580  double beta2=pmag2/e2; //beta^2=v^2/c^2 for particle
581  double tau2=dist3D.mag2()*(1/beta2-1)/Gaudi::Units::c_light/Gaudi::Units::c_light;
582  ATH_MSG_VERBOSE("lifetime tau(beta)="<<std::sqrt(tau2)<<" tau="<<tau);
583  }
584  if (m_quasiStableParticlesIncluded) {
585  ATH_MSG_VERBOSE( "Detected primary particle with end vertex." );
586  ATH_MSG_VERBOSE( "Will add the primary particle set on." );
587  ATH_MSG_VERBOSE( "Primary Particle: " << genpart );
588  ATH_MSG_VERBOSE( "Number of daughters: " << genpart->end_vertex()->particles_out().size()<<" at position "<<genpart->end_vertex() );
589  }
590  else {
591  ATH_MSG_WARNING( "Detected primary particle with end vertex." );
592  ATH_MSG_WARNING( "Will add the primary particle set on." );
593  ATH_MSG_WARNING( "Primary Particle: " << genpart );
594  ATH_MSG_WARNING( "Number of daughters : " << genpart->end_vertex()->particles_out().size()<<" at position "<<genpart->end_vertex() );
595  }
596  // Add all necessary daughter particles
597  for ( auto daughter: genpart->end_vertex()->particles_out() ) {
598  if (m_quasiStableParticlesIncluded) {
599  ATH_MSG_VERBOSE ( "Attempting to add daughter particle : " << daughter );
600  }
601  else {
602  ATH_MSG_WARNING ( "Attempting to add daughter particle: " << daughter );
603  }
604  G4PrimaryParticle *daughterG4Particle = this->getDaughterG4PrimaryParticle( daughter, makeLinkToTruth );
605  if (!daughterG4Particle) {
606  ATH_MSG_ERROR("Bailing out of loop over daughters of particle: "<<
607  " due to errors - will not return G4Particle.");
608  return nullptr;
609  }
610  g4particle->SetDaughter( daughterG4Particle );
611  }
612  }
613 
614  if (makeLinkToTruth) {
615  // Set the user information for this primary to point to the HepMcParticleLink...
616  std::unique_ptr<PrimaryParticleInformation> primaryPartInfo = std::make_unique<PrimaryParticleInformation>(genpart);
617  primaryPartInfo->SetRegenerationNr(0);
618  g4particle->SetUserInformation(primaryPartInfo.release());
619  ATH_MSG_VERBOSE("Making primary down the line with barcode " << primaryPartInfo->GetParticleUniqueID());
620  }
621 
622  return g4particle.release();
623 }
624 
625 //________________________________________________________________________
627  ATH_MSG_VERBOSE("Creating G4PrimaryParticle from GenParticle.");
628 
629  const G4ParticleDefinition *particleDefinition = this->getG4ParticleDefinition(genpart->pdg_id());
630 
631  if (particleDefinition==nullptr) {
632  ATH_MSG_ERROR("ISF_to_G4Event particle conversion failed. ISF_Particle PDG code = " << genpart->pdg_id() <<
633  "\n This usually indicates a problem with the evgen step.\n" <<
634  "Please report this to the Generators group, mentioning the release and generator used for evgen and the PDG code above." );
635  return nullptr;
636  }
637 
638  // create new primaries and set them to the vertex
639  // G4double mass = particleDefinition->GetPDGMass();
640  auto &genpartMomentum = genpart->momentum();
641  G4double px = genpartMomentum.x();
642  G4double py = genpartMomentum.y();
643  G4double pz = genpartMomentum.z();
644 
645  std::unique_ptr<G4PrimaryParticle> g4particle = std::make_unique<G4PrimaryParticle>(particleDefinition,px,py,pz);
646 
647  if (genpart->end_vertex()) {
648  // Set the lifetime appropriately - this is slow but rigorous, and we
649  // don't want to end up with something like vertex time that we have
650  // to validate for every generator on earth...
651  const auto& prodVtx = genpart->production_vertex()->position();
652  const auto& endVtx = genpart->end_vertex()->position();
653  //const G4LorentzVector lv0 ( prodVtx.x(), prodVtx.y(), prodVtx.z(), prodVtx.t() );
654  //const G4LorentzVector lv1 ( endVtx.x(), endVtx.y(), endVtx.z(), endVtx.t() );
655  //Old calculation, not taken because vertex information is not sufficiently precise
656  //g4particle->SetProperTime( (lv1-lv0).mag()/Gaudi::Units::c_light );
657 
658  CLHEP::Hep3Vector dist3D(endVtx.x()-prodVtx.x(), endVtx.y()-prodVtx.y(), endVtx.z()-prodVtx.z());
659  double tau=SetProperTimeFromDetectorFrameDecayLength(*g4particle,dist3D.mag());
660 
661  if (msgLvl(MSG::VERBOSE)) {
662  double pmag2=g4particle->GetTotalMomentum(); //magnitude of particle momentum
663  pmag2*=pmag2; //magnitude of particle momentum squared
664  double e2=g4particle->GetTotalEnergy(); //energy of particle
665  e2*=e2; //energy of particle squared
666  double beta2=pmag2/e2; //beta^2=v^2/c^2 for particle
667  double tau2=dist3D.mag2()*(1/beta2-1)/Gaudi::Units::c_light/Gaudi::Units::c_light;
668  ATH_MSG_VERBOSE("lifetime tau(beta)="<<std::sqrt(tau2)<<" tau="<<tau);
669  }
670  if (m_quasiStableParticlesIncluded) {
671  ATH_MSG_VERBOSE( "Detected primary particle with end vertex." );
672  ATH_MSG_VERBOSE( "Will add the primary particle set on." );
673  ATH_MSG_VERBOSE( "Primary Particle: " << genpart );
674  ATH_MSG_VERBOSE( "Number of daughters : " << genpart->end_vertex()->particles_out().size()<<" at position "<<genpart->end_vertex() );
675  }
676  else {
677  ATH_MSG_WARNING( "Detected primary particle with end vertex." );
678  ATH_MSG_WARNING( "Will add the primary particle set on." );
679  ATH_MSG_WARNING( "Primary Particle: " << genpart );
680  ATH_MSG_WARNING( "Number of daughters: " << genpart->end_vertex()->particles_out().size()<<" at position "<<genpart->end_vertex() );
681  }
682  // Add all necessary daughter particles
683  for ( auto daughter: genpart->end_vertex()->particles_out() ) {
684  if (m_quasiStableParticlesIncluded) {
685  ATH_MSG_VERBOSE ( "Attempting to add daughter particle: " << daughter );
686  }
687  else {
688  ATH_MSG_WARNING ( "Attempting to add daughter particle: " << daughter );
689  }
690  G4PrimaryParticle *daughterG4Particle = this->getDaughterG4PrimaryParticle( daughter );
691  if (!daughterG4Particle) {
692  ATH_MSG_ERROR("Bailing out of loop over daughters of particle due to errors - will not return G4Particle.");
693  return nullptr;
694  }
695  g4particle->SetDaughter( daughterG4Particle );
696  }
697  }
698 
699  return g4particle.release();
700 }
701 #else
702 G4PrimaryParticle* ISF::InputConverter::getDaughterG4PrimaryParticle(HepMC::GenParticle& genpart, bool makeLinkToTruth) const
703 {
704  ATH_MSG_VERBOSE("Creating G4PrimaryParticle from GenParticle.");
705 
706  const G4ParticleDefinition *particleDefinition = this->getG4ParticleDefinition(genpart.pdg_id());
707 
708  if (particleDefinition==nullptr) {
709  ATH_MSG_ERROR("ISF_to_G4Event particle conversion failed. ISF_Particle PDG code = " << genpart.pdg_id() <<
710  "\n This usually indicates a problem with the evgen step.\n" <<
711  "Please report this to the Generators group, mentioning the release and generator used for evgen and the PDG code above." );
712  return nullptr;
713  }
714 
715  // create new primaries and set them to the vertex
716  // G4double mass = particleDefinition->GetPDGMass();
717  auto &genpartMomentum = genpart.momentum();
718  G4double px = genpartMomentum.x();
719  G4double py = genpartMomentum.y();
720  G4double pz = genpartMomentum.z();
721 
722  std::unique_ptr<G4PrimaryParticle> g4particle = std::make_unique<G4PrimaryParticle>(particleDefinition,px,py,pz);
723 
724  if (genpart.end_vertex()) {
725  // Set the lifetime appropriately - this is slow but rigorous, and we
726  // don't want to end up with something like vertex time that we have
727  // to validate for every generator on earth...
728  const auto& prodVtx = genpart.production_vertex()->position();
729  const auto& endVtx = genpart.end_vertex()->position();
730  //const G4LorentzVector lv0 ( prodVtx.x(), prodVtx.y(), prodVtx.z(), prodVtx.t() );
731  //const G4LorentzVector lv1 ( endVtx.x(), endVtx.y(), endVtx.z(), endVtx.t() );
732  //Old calculation, not taken because vertex information is not sufficiently precise
733  //g4particle->SetProperTime( (lv1-lv0).mag()/Gaudi::Units::c_light );
734 
735  CLHEP::Hep3Vector dist3D(endVtx.x()-prodVtx.x(), endVtx.y()-prodVtx.y(), endVtx.z()-prodVtx.z());
736  double tau=SetProperTimeFromDetectorFrameDecayLength(*g4particle,dist3D.mag());
737 
738  if (msgLvl(MSG::VERBOSE)) {
739  double pmag2=g4particle->GetTotalMomentum(); //magnitude of particle momentum
740  pmag2*=pmag2; //magnitude of particle momentum squared
741  double e2=g4particle->GetTotalEnergy(); //energy of particle
742  e2*=e2; //energy of particle squared
743  double beta2=pmag2/e2; //beta^2=v^2/c^2 for particle
744  double tau2=dist3D.mag2()*(1/beta2-1)/Gaudi::Units::c_light/Gaudi::Units::c_light;
745  ATH_MSG_VERBOSE("lifetime tau(beta)="<<std::sqrt(tau2)<<" tau="<<tau);
746  }
747  if (m_quasiStableParticlesIncluded) {
748  ATH_MSG_VERBOSE( "Detected primary particle with end vertex." );
749  ATH_MSG_VERBOSE( "Will add the primary particle set on." );
750  ATH_MSG_VERBOSE( "Primary Particle: " << genpart );
751  ATH_MSG_VERBOSE( "Number of daughters: " << genpart.end_vertex()->particles_out_size()<<" at position "<<genpart.end_vertex());
752  }
753  else {
754  ATH_MSG_WARNING( "Detected primary particle with end vertex." );
755  ATH_MSG_WARNING( "Will add the primary particle set on." );
756  ATH_MSG_WARNING( "Primary Particle: " << genpart );
757  ATH_MSG_WARNING( "Number of daughters: " << genpart.end_vertex()->particles_out_size()<<" at position "<<genpart.end_vertex() );
758  }
759  // Add all necessary daughter particles
760  for ( auto daughterIter=genpart.end_vertex()->particles_out_const_begin();
761  daughterIter!=genpart.end_vertex()->particles_out_const_end(); ++daughterIter ) {
762  if (m_quasiStableParticlesIncluded) {
763  ATH_MSG_VERBOSE ( "Attempting to add daughter particle: " << **daughterIter );
764  }
765  else {
766  ATH_MSG_WARNING ( "Attempting to add daughter particle: " << **daughterIter );
767  }
768  G4PrimaryParticle *daughterG4Particle = this->getDaughterG4PrimaryParticle( **daughterIter, makeLinkToTruth );
769  if (!daughterG4Particle) {
770  ATH_MSG_ERROR("Bailing out of loop over daughters of particle due to errors - will not return G4Particle.");
771  return nullptr;
772  }
773  g4particle->SetDaughter( daughterG4Particle );
774  }
775  }
776 
777  if (makeLinkToTruth) {
778  // Set the user information for this primary to point to the HepMcParticleLink...
779  std::unique_ptr<PrimaryParticleInformation> primaryPartInfo = std::make_unique<PrimaryParticleInformation>(&genpart);
780  primaryPartInfo->SetRegenerationNr(0);
781  g4particle->SetUserInformation(primaryPartInfo.release());
782  ATH_MSG_VERBOSE("Making primary down the line with barcode " << primaryPartInfo->GetParticleUniqueID());
783  }
784 
785  return g4particle.release();
786 }
787 #endif
788 
789 //________________________________________________________________________
791  const HepMC::ConstGenParticlePtr& p2) const // TODO Helper method?
792 {
793  return (HepMC::barcode(p1) == HepMC::barcode(p2))
794  && (p1->status() == p2->status())
795  && (p1->pdg_id() == p2->pdg_id())
796  && ((p1->momentum().px()) == (p2->momentum().px()))
797  && ((p1->momentum().py()) == (p2->momentum().py()))
798  && ((p1->momentum().pz()) == (p2->momentum().pz()))
799  && (float(p1->momentum().m()) == float(p2->momentum().m()));
800 }
801 
802 //________________________________________________________________________
803 HepMC::GenParticlePtr ISF::InputConverter::findShadowParticle(const HepMC::ConstGenParticlePtr& genParticle, HepMC::GenEvent *shadowGenEvent) const // TODO Helper method?
804 {
805  if (!shadowGenEvent) {
806  ATH_MSG_FATAL ("Found status==2 GenParticle with no end vertex and shadow GenEvent is missing - something is wrong here!");
807  abort();
808  }
809 #ifdef HEPMC3
810  // TODO in the future switch to using an Attribute which stores the shadow GenParticlePtr directly.
811  const int shadowId = genParticle->attribute<HepMC3::IntAttribute>("ShadowParticleId")->value();
812  for (auto& shadowParticle : shadowGenEvent->particles()) {
813  if (shadowParticle->id() == shadowId && matchedGenParticles(genParticle, shadowParticle) ) { return shadowParticle; }
814  }
815  return std::make_shared<HepMC::GenParticle>();
816 #else
817  for (HepMC::GenEvent::particle_iterator pitr=shadowGenEvent->particles_begin(); pitr != shadowGenEvent->particles_end(); ++pitr) {
818  HepMC::GenParticlePtr shadowParticle = (*pitr);
819  if (matchedGenParticles(genParticle, shadowParticle) ) { return shadowParticle; }
820  }
821  return nullptr;
822 #endif
823 
824 }
825 
826 #ifdef HEPMC3
827 //________________________________________________________________________
828 void ISF::InputConverter::processPredefinedDecays(const HepMC::ConstGenParticlePtr& genpart, ISF::ISFParticle& isp, G4PrimaryParticle* g4particle) const
829 {
833  const auto& prodVtx = genpart->production_vertex()->position();
834  const auto& endVtx = genpart->end_vertex()->position();
835 
836  CLHEP::Hep3Vector dist3D(endVtx.x()-prodVtx.x(), endVtx.y()-prodVtx.y(), endVtx.z()-prodVtx.z());
837  double tau=SetProperTimeFromDetectorFrameDecayLength(*g4particle,dist3D.mag());
838 
839  if (msgLvl(MSG::VERBOSE)) {
840  double pmag2=g4particle->GetTotalMomentum(); //magnitude of particle momentum
841  pmag2*=pmag2; //magnitude of particle momentum squared
842  double e2=g4particle->GetTotalEnergy(); //energy of particle
843  e2*=e2; //energy^2 of particle
844  double beta2=pmag2/e2; //beta^2=v^2/c^2 for particle
845  double mass2=g4particle->GetMass(); //mass of particle
846  mass2*=mass2; //mass^2 of particle
847 
848  double tau2=dist3D.mag2()*(1/beta2-1)/Gaudi::Units::c_squared;
849 
850  const G4LorentzVector lv0( prodVtx.x(), prodVtx.y(), prodVtx.z(), prodVtx.t() );
851  const G4LorentzVector lv1( endVtx.x(), endVtx.y(), endVtx.z(), endVtx.t() );
852  //Old calculation, not taken because vertex information is not sufficiently precise
853  //g4particle->SetProperTime( (lv1-lv0).mag()/Gaudi::Units::c_light );
854  G4LorentzVector dist4D(lv1);
855  dist4D-=lv0;
856 
857  double dist4Dgamma=std::numeric_limits<double>::infinity();
858  if (dist4D.t()>0 && dist4D.mag2()>0) {
859  dist4Dgamma=dist4D.gamma();
860  } else {
861  ATH_MSG_VERBOSE( "dist4D t="<<dist4D.t()<<" mag2="<<dist4D.mag2());
862  }
863 
864  G4LorentzVector fourmom(g4particle->GetMomentum(),g4particle->GetTotalEnergy());
865  double fourmomgamma=std::numeric_limits<double>::infinity();
866  if (fourmom.t()>0 && fourmom.mag2()>0) {
867  fourmomgamma=fourmom.gamma();
868  } else {
869  ATH_MSG_VERBOSE( "fourmom t="<<fourmom.t()<<" mag2="<<fourmom.mag2());
870  }
871 
872  ATH_MSG_VERBOSE( "gammaVertex="<<dist4Dgamma<<" gammamom="<<fourmomgamma<<" gamma(beta)="<<1/std::sqrt(1-beta2)<<" lifetime tau(beta)="<<std::sqrt(tau2)<<" lifetime tau="<<tau);
873  }
874  if (m_quasiStableParticlesIncluded) {
875  ATH_MSG_VERBOSE( "Detected primary particle with end vertex." );
876  ATH_MSG_VERBOSE( "Will add the primary particle set on." );
877  ATH_MSG_VERBOSE( "ISF Particle: " << isp );
878  ATH_MSG_VERBOSE( "Primary Particle: " << genpart );
879  ATH_MSG_VERBOSE( "Number of daughters: " << genpart->end_vertex()->particles_out().size() << " at position "<< genpart->end_vertex() );
880  }
881  else {
882  ATH_MSG_WARNING( "Detected primary particle with end vertex. This should only be the case if" );
883  ATH_MSG_WARNING( "you are running with quasi-stable particle simulation enabled. This is not" );
884  ATH_MSG_WARNING( "yet validated - you'd better know what you're doing. Will add the primary" );
885  ATH_MSG_WARNING( "particle set on." );
886  ATH_MSG_WARNING( "ISF Particle: " << isp );
887  ATH_MSG_WARNING( "Primary Particle: " << genpart );
888  ATH_MSG_VERBOSE( "Number of daughters: " << genpart->end_vertex()->particles_out().size() );
889  }
890  // Add all necessary daughter particles
891  for ( auto daughter: *(genpart->end_vertex())) {
892  if (m_quasiStableParticlesIncluded) {
893  ATH_MSG_VERBOSE ( "Attempting to add daughter particle" << daughter );
894  }
895  else {
896  ATH_MSG_WARNING ( "Attempting to add daughter particle: " << daughter );
897  }
898  G4PrimaryParticle *daughterG4Particle = this->getDaughterG4PrimaryParticle( daughter );
899  if (!daughterG4Particle) {
900  ATH_MSG_FATAL("Bailing out of loop over daughters due to errors.");
901  }
902  g4particle->SetDaughter( daughterG4Particle );
903  }
904 }
905 #endif
906 
907 //________________________________________________________________________
908 void ISF::InputConverter::processPredefinedDecays(const HepMC::GenParticlePtr& genpart, ISF::ISFParticle& isp, G4PrimaryParticle* g4particle, bool makeLinkToTruth) const
909 {
913  const auto& prodVtx = genpart->production_vertex()->position();
914  const auto& endVtx = genpart->end_vertex()->position();
915 
916  CLHEP::Hep3Vector dist3D(endVtx.x()-prodVtx.x(), endVtx.y()-prodVtx.y(), endVtx.z()-prodVtx.z());
917  double tau=SetProperTimeFromDetectorFrameDecayLength(*g4particle,dist3D.mag());
918 
919  if (msgLvl(MSG::VERBOSE)) {
920  double pmag2=g4particle->GetTotalMomentum(); //magnitude of particle momentum
921  pmag2*=pmag2; //magnitude of particle momentum squared
922  double e2=g4particle->GetTotalEnergy(); //energy of particle
923  e2*=e2; //energy^2 of particle
924  double beta2=pmag2/e2; //beta^2=v^2/c^2 for particle
925  double mass2=g4particle->GetMass(); //mass of particle
926  mass2*=mass2; //mass^2 of particle
927 
928  double tau2=dist3D.mag2()*(1/beta2-1)/Gaudi::Units::c_squared;
929 
930  const G4LorentzVector lv0( prodVtx.x(), prodVtx.y(), prodVtx.z(), prodVtx.t() );
931  const G4LorentzVector lv1( endVtx.x(), endVtx.y(), endVtx.z(), endVtx.t() );
932  //Old calculation, not taken because vertex information is not sufficiently precise
933  //g4particle->SetProperTime( (lv1-lv0).mag()/Gaudi::Units::c_light );
934  G4LorentzVector dist4D(lv1);
935  dist4D-=lv0;
936 
937  double dist4Dgamma=std::numeric_limits<double>::infinity();
938  if (dist4D.t()>0 && dist4D.mag2()>0) {
939  dist4Dgamma=dist4D.gamma();
940  } else {
941  ATH_MSG_VERBOSE( "dist4D t="<<dist4D.t()<<" mag2="<<dist4D.mag2());
942  }
943 
944  G4LorentzVector fourmom(g4particle->GetMomentum(),g4particle->GetTotalEnergy());
945  double fourmomgamma=std::numeric_limits<double>::infinity();
946  if (fourmom.t()>0 && fourmom.mag2()>0) {
947  fourmomgamma=fourmom.gamma();
948  } else {
949  ATH_MSG_VERBOSE( "fourmom t="<<fourmom.t()<<" mag2="<<fourmom.mag2());
950  }
951 
952  ATH_MSG_VERBOSE( "gammaVertex="<<dist4Dgamma<<" gammamom="<<fourmomgamma<<" gamma(beta)="<<1/std::sqrt(1-beta2)<<" lifetime tau(beta)="<<std::sqrt(tau2)<<" lifetime tau="<<tau);
953  }
954  if (m_quasiStableParticlesIncluded) {
955  ATH_MSG_VERBOSE( "Detected primary particle with end vertex." );
956  ATH_MSG_VERBOSE( "Will add the primary particle set on." );
957  ATH_MSG_VERBOSE( "ISF Particle: " << isp );
958  ATH_MSG_VERBOSE( "Primary Particle: " << genpart );
959  ATH_MSG_VERBOSE( "Number of daughters: " << genpart->end_vertex()->particles_out_size() << " at position "<< genpart->end_vertex() );
960  }
961  else {
962  ATH_MSG_WARNING( "Detected primary particle with end vertex. This should only be the case if" );
963  ATH_MSG_WARNING( "you are running with quasi-stable particle simulation enabled. This is not" );
964  ATH_MSG_WARNING( "yet validated - you'd better know what you're doing. Will add the primary" );
965  ATH_MSG_WARNING( "particle set on." );
966  ATH_MSG_WARNING( "ISF Particle: " << isp );
967  ATH_MSG_WARNING( "Primary Particle: " << genpart );
968  ATH_MSG_WARNING( "Number of daughters: " << genpart->end_vertex()->particles_out_size() );
969  }
970  // Add all necessary daughter particles
971  for ( auto daughter: *(genpart->end_vertex())) {
972  if (m_quasiStableParticlesIncluded) {
973  ATH_MSG_VERBOSE ( "Attempting to add daughter particle: " << daughter );
974  }
975  else {
976  ATH_MSG_WARNING ( "Attempting to add daughter particle: " << daughter );
977  }
978 #ifdef HEPMC3
979  G4PrimaryParticle *daughterG4Particle = this->getDaughterG4PrimaryParticle( daughter, makeLinkToTruth );
980 #else
981  G4PrimaryParticle *daughterG4Particle = this->getDaughterG4PrimaryParticle( *daughter, makeLinkToTruth );
982 #endif
983  if (!daughterG4Particle) {
984  ATH_MSG_FATAL("Bailing out of loop over daughters due to errors.");
985  }
986  g4particle->SetDaughter( daughterG4Particle );
987  }
988 }
989 
990 G4PrimaryParticle* ISF::InputConverter::getG4PrimaryParticle(ISF::ISFParticle& isp, bool useHepMC, HepMC::GenEvent *shadowGenEvent) const
991 {
992  ATH_MSG_VERBOSE("Creating G4PrimaryParticle from ISFParticle.");
993 
994  auto* truthBinding = isp.getTruthBinding();
995  if (!truthBinding) {
996  G4ExceptionDescription description;
997  description << G4String("getG4PrimaryParticle: ") + "No ISF::TruthBinding associated with ISParticle (" << isp <<")";
998  G4Exception("iGeant4::TransportTool", "NoISFTruthBinding", FatalException, description);
999  return nullptr; //The G4Exception call above should abort the job, but Coverity does not seem to pick this up.
1000  }
1001  HepMC::GenParticlePtr currentGenPart = truthBinding->getCurrentGenParticle();
1002  HepMC::GenParticlePtr primaryGenpart = truthBinding->getPrimaryGenParticle();
1003 
1004  const G4ParticleDefinition *particleDefinition = this->getG4ParticleDefinition(isp.pdgCode());
1005 
1006  if (particleDefinition==nullptr) {
1007  ATH_MSG_ERROR("ISF_to_G4Event particle conversion failed. ISF_Particle PDG code = " << isp.pdgCode() <<
1008  "\n This usually indicates a problem with the evgen step.\n" <<
1009  "Please report this to the Generators group, mentioning the release and generator used for evgen and the PDG code above." );
1010  return nullptr;
1011  }
1012 
1013  // create new primaries and set them to the vertex
1014  // G4double mass = particleDefinition->GetPDGMass();
1015  G4double px(0.0);
1016  G4double py(0.0);
1017  G4double pz(0.0);
1018  if (useHepMC && currentGenPart) {
1019  auto &currentGenPartMomentum = currentGenPart->momentum();
1020  px = currentGenPartMomentum.x();
1021  py = currentGenPartMomentum.y();
1022  pz = currentGenPartMomentum.z();
1023  }
1024  else {
1025  auto &ispMomentum = isp.momentum();
1026  px = ispMomentum.x();
1027  py = ispMomentum.y();
1028  pz = ispMomentum.z();
1029  }
1030 
1031  std::unique_ptr<G4PrimaryParticle> g4particle = std::make_unique<G4PrimaryParticle>(particleDefinition,px,py,pz);
1032  // UserInformation
1033  std::unique_ptr<PrimaryParticleInformation> primaryPartInfo = std::make_unique<PrimaryParticleInformation>(primaryGenpart,&isp);
1034 
1038  const int regenerationNr = HepMC::StatusBased::generations(&isp);
1039  if (HepMC::BarcodeBased::generations(&isp) != regenerationNr) {
1040  ATH_MSG_WARNING ("StatusBased::generations() = " << regenerationNr << ", BarcodeBased::generations() = " << HepMC::BarcodeBased::generations(&isp) << ", isp: " << isp);
1041  }
1042  primaryPartInfo->SetRegenerationNr(regenerationNr);
1043 
1044  if ( currentGenPart ) {
1045  if (currentGenPart->end_vertex()) {
1046  // Old approach particle had an end vertex - predefined decays taken from the main GenEvent
1047  // No longer supported
1048  ATH_MSG_ERROR ( "getG4PrimaryParticle(): GenParticle has a valid end GenVertexPtr!" );
1049  ATH_MSG_ERROR ( "getG4PrimaryParticle(): currentGenPart: " << currentGenPart << ", barcode: " << HepMC::barcode(currentGenPart) );
1050  ATH_MSG_ERROR ( "getG4PrimaryParticle(): currentGenPart->end_vertex(): " << currentGenPart->end_vertex() << ", barcode: " << HepMC::barcode(currentGenPart->end_vertex()) );
1051  ATH_MSG_FATAL ( "getG4PrimaryParticle(): Passing GenParticles with a valid end GenVertexPtr as input is no longer supported." );
1052  abort();
1053  }
1054  else if (MC::isDecayed(currentGenPart) // Some assumptions about main GenEvent here
1055  && !currentGenPart->end_vertex()) {
1056  // New approach - predefined decays taken from shadow GenEvent
1057  // Find the matching particle in the shadowGenEvent
1058 #ifdef HEPMC3
1059  auto A_part = currentGenPart->attribute<HepMC::ShadowParticle>("ShadowParticle");
1060  HepMC::ConstGenParticlePtr shadowPart = (A_part) ? A_part->value() : findShadowParticle(currentGenPart, shadowGenEvent);
1061 #else
1062  HepMC::GenParticlePtr shadowPart = findShadowParticle(currentGenPart, shadowGenEvent);
1063 #endif
1064  if (!shadowPart) {
1065  ATH_MSG_FATAL ("Found a GenParticle with no matching GenParticle in the shadowGenEvent - something is wrong here!");
1066  abort();
1067  }
1068  if (!shadowPart->end_vertex()) {
1069  ATH_MSG_FATAL ("Found status==2 shadow GenParticle with no end vertex - something is wrong here!");
1070  abort();
1071  }
1072 #ifdef HEPMC3
1073  processPredefinedDecays(shadowPart, isp, g4particle.get());
1074 #else
1075  processPredefinedDecays(shadowPart, isp, g4particle.get(), false); // false to avoid truth-links to the shadow GenEvent
1076 #endif
1077  }
1078 
1079  double px,py,pz;
1080  const double pmass = g4particle->GetMass();
1081  CLHEP::Hep3Vector gpv = g4particle->GetMomentum();
1082  double g4px=g4particle->GetMomentum().x();
1083  double g4py=g4particle->GetMomentum().y();
1084  double g4pz=g4particle->GetMomentum().z();
1085  if (useHepMC) {
1086  //Code adapted from TruthHepMCEventConverter::TransformHepMCParticle
1087  px=g4px;
1088  py=g4py;
1089  pz=g4pz;
1090  } else {
1091  //Take mass from g4particle, put keep momentum as in currentGenPart
1092  px=currentGenPart->momentum().px();
1093  py=currentGenPart->momentum().py();
1094  pz=currentGenPart->momentum().pz();
1095  //Now a dirty hack to keep backward compatibility in the truth:
1096  //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
1097  //together with the mass of the g4particle after the 1st initialization of the g4particle from the genevent. This is done for a consistent mass
1098  //value in the truth record compared to the used g4 mass. Since g4particles don't store the 3-momentum directly, but rather a
1099  //unit direction vector, the mass and the kinetic energy, this reduces the numeric accuracy.
1100  //For backward compatibility, if all 3-momentum components agree to the g4particle momentum within 1 keV, we keep
1101  //this old method. This comparison is needed, since in ISF this code could be rerun after the ID or CALO simulation, where
1102  //real energy was lost in previous detectors and hence currentGenPart should NOT be changed to some g4particle values!
1103  //TODO: find a way to implement this in a backward compatible way in ISF::InputConverter::convertParticle(HepMC::GenParticlePtr genPartPtr)
1104  if (std::abs(px-g4px)<CLHEP::keV && std::abs(py-g4py)<CLHEP::keV && std::abs(pz-g4pz)<CLHEP::keV) {
1105  px=g4px;
1106  py=g4py;
1107  pz=g4pz;
1108  }
1109  }
1110  const double mag2=px*px + py*py + pz*pz;
1111  const double pe = std::sqrt(mag2 + pmass*pmass); // this does only change for boosts, etc.
1112 
1113  double originalEnergy=currentGenPart->momentum().e();
1114  if (originalEnergy>0.01) { //only test for >1 MeV in momentum
1115  if ((originalEnergy-pe)/originalEnergy>0.01) {
1116  double genpx=currentGenPart->momentum().px();
1117  double genpy=currentGenPart->momentum().py();
1118  double genpz=currentGenPart->momentum().pz();
1119  double genp=sqrt(genpx*genpx + genpy*genpy + genpz*genpz);
1120  ATH_MSG_WARNING("Truth change in energy for: " << currentGenPart<<" Morg="<<currentGenPart->momentum().m()<<" Mmod="<<pmass<<" Eorg="<<originalEnergy<<" Emod="<<pe<<" porg="<<genp<<" pmod="<<gpv.mag());
1121  }
1122  }
1123 
1124 #ifdef HEPMC3
1125  auto& currentGenPart_nc = currentGenPart;
1126 #else
1127  auto* currentGenPart_nc = currentGenPart;
1128 #endif
1129  currentGenPart_nc->set_momentum(HepMC::FourVector(px,py,pz,pe));
1130  } // Truth was detected
1131 
1132  ATH_MSG_VERBOSE("PrimaryParticleInformation:");
1133  ATH_MSG_VERBOSE(" GetParticleUniqueID = " << primaryPartInfo->GetParticleUniqueID());
1134  ATH_MSG_VERBOSE(" GetRegenerationNr = " << primaryPartInfo->GetRegenerationNr());
1135  ATH_MSG_VERBOSE(" GetHepMCParticle = " << primaryPartInfo->GetHepMCParticle());
1136  ATH_MSG_VERBOSE(" GetISFParticle = " << primaryPartInfo->GetISFParticle());
1137  g4particle->SetUserInformation(primaryPartInfo.release());
1138 
1139  return g4particle.release();
1140 }
1141 
1142 //________________________________________________________________________
1143 void ISF::InputConverter::addG4PrimaryVertex(G4Event* g4evt, ISF::ISFParticle& isp, bool useHepMC, HepMC::GenEvent *shadowGenEvent) const
1144 {
1145  /*
1146  see conversion from PrimaryParticleInformation to TrackInformation in
1147  http://acode-browser.usatlas.bnl.gov/lxr/source/atlas/Simulation/G4Atlas/G4AtlasAlg/src/AthenaStackingAction.cxx#0044
1148 
1149  need to check with
1150  http://acode-browser.usatlas.bnl.gov/lxr/source/atlas/Simulation/G4Atlas/G4AtlasAlg/src/TruthHepMCEventConverter.cxx#0151
1151 
1152  that we don't miss something
1153  */
1154 
1155  G4PrimaryParticle *g4particle = this->getG4PrimaryParticle( isp, useHepMC, shadowGenEvent );
1156  if (!g4particle) {
1157  ATH_MSG_ERROR("Failed to create G4PrimaryParticle for ISParticle (" << isp <<")");
1158  return;
1159  }// Already printed a warning
1160 
1161  // create a new vertex
1162  G4PrimaryVertex *g4vertex = new G4PrimaryVertex(isp.position().x(),
1163  isp.position().y(),
1164  isp.position().z(),
1165  isp.timeStamp());
1166  g4vertex->SetPrimary( g4particle );
1167  ATH_MSG_VERBOSE("Print G4PrimaryVertex: ");
1168  if (msgLevel(MSG::VERBOSE)) { g4vertex->Print(); }
1169  g4evt->AddPrimaryVertex( g4vertex );
1170  return;
1171 }
1172 
1173 //________________________________________________________________________
1174 bool ISF::InputConverter::isInsideG4WorldVolume(const ISF::ISFParticle& isp, const G4VSolid* worldSolid) const
1175 {
1176 
1177  const Amg::Vector3D &pos = isp.position();
1178  const G4ThreeVector g4Pos( pos.x(), pos.y(), pos.z() );
1179  EInside insideStatus = worldSolid->Inside( g4Pos );
1180 
1181  bool insideWorld = insideStatus != kOutside;
1182  return insideWorld;
1183 }
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
ISF::InputConverter::addG4PrimaryVertex
void addG4PrimaryVertex(G4Event *g4evt, ISF::ISFParticle &isp, bool useHepMC, HepMC::GenEvent *shadowGenEvent) const
Definition: InputConverter.cxx:1143
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
python.PerfMonSerializer.p
def p
Definition: PerfMonSerializer.py:743
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:505
FullCPAlgorithmsTest_eljob.flush
flush
Definition: FullCPAlgorithmsTest_eljob.py:168
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:235
HepMC::signal_process_id
int signal_process_id(const GenEvent &e)
Definition: GenEvent.h:513
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
conifer::pow
constexpr int pow(int x)
Definition: conifer.h:20
HepMC::GenParticlePtr
GenParticle * GenParticlePtr
Definition: GenParticle.h:37
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:122
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
dqt_zlumi_pandas.mass
mass
Definition: dqt_zlumi_pandas.py:170
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:204
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:908
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:803
python.SystemOfUnits.keV
int keV
Definition: SystemOfUnits.py:156
TruthBinding.h
ISFParticle.h
ISF::InputConverter::ISF_to_G4Event
G4Event * ISF_to_G4Event(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
skel.l2
l2
Definition: skel.GENtoEVGEN.py:426
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:990
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:1174
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:113
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:790
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:702
ISF::InputConverter::m_quasiStableParticlesIncluded
bool m_quasiStableParticlesIncluded
Definition: InputConverter.h:130
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:195
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:494
FakeBkgTools::maxParticles
constexpr uint8_t maxParticles()
Definition: FakeBkgInternals.h:93
ISF::InputConverter::convertHepMCToG4Event
virtual StatusCode convertHepMCToG4Event(McEventCollection &inputGenEvents, G4Event *&outputG4Event, McEventCollection &shadowGenEvents) const override final
Definition: InputConverter.cxx:155
python.PhysicalConstants.c_light
float c_light
Definition: PhysicalConstants.py:63
SetProperTimeFromDetectorFrameDecayLength
double SetProperTimeFromDetectorFrameDecayLength(G4PrimaryParticle &g4particle, const double GeneratorDecayLength)
Definition: InputConverter.cxx:523
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
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
DiTauMassTools::MaxHistStrategyV2::e
e
Definition: PhysicsAnalysis/TauID/DiTauMassTools/DiTauMassTools/HelperFunctions.h:26
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)
Definition: HepMCHelpers.h:29
PrimaryParticleInformation.h
IGenParticleFilter.h
python.DecayParser.children
children
Definition: DecayParser.py:32
declareProperty
#define declareProperty(n, p, h)
Definition: BaseFakeBkgTool.cxx:15
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:30
ISF::InputConverter::convertHepMCToG4EventLegacy
virtual StatusCode convertHepMCToG4EventLegacy(McEventCollection &inputGenEvents, G4Event *&outputG4Event) const override final
Definition: InputConverter.cxx:175
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