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