LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
MCTruthEventAction.cc
Go to the documentation of this file.
2 
4 
6 
8 
10 
11 #include "fhiclcpp/ParameterSet.h"
12 
13 #include "Geant4/G4Event.hh"
14 #include "Geant4/G4IonTable.hh"
15 #include "Geant4/G4ParticleDefinition.hh"
16 #include "Geant4/G4ParticleTable.hh"
17 #include "Geant4/G4PrimaryParticle.hh"
18 #include "Geant4/G4PrimaryVertex.hh"
19 #include "Geant4/G4Types.hh"
20 
21 #include "CLHEP/Vector/LorentzVector.h"
22 
23 #include "TLorentzVector.h"
24 #include "TVector3.h"
25 
26 #include <sstream>
27 #include <string>
28 
29 using std::string;
30 
31 namespace {
32  constexpr bool genie_specific(int const pdg)
33  {
34  constexpr int genieLoPdg = 2000000001;
35  constexpr int genieHiPdg = 2000000300;
36  return pdg >= genieLoPdg && pdg <= genieHiPdg;
37  }
38 }
39 
40 G4ParticleTable* larg4::MCTruthEventActionService::fParticleTable = nullptr;
41 
43  : PrimaryGeneratorActionBase(p.get<string>("name", "MCTruthEventActionService"))
44 {}
45 
47 {
48  // Print out a list of "unknown" PDG codes we saw in the input.
49  if (!fUnknownPDG.empty()) {
50  std::ostringstream badtxt;
51  for (auto const [pdg, n] : fUnknownPDG) {
52  badtxt << "\n Unknown PDG code = " << pdg << ", seen " << n << " times.";
53  if (genie_specific(pdg)) { badtxt << " (GENIE specific)"; }
54  }
55 
56  mf::LogWarning("MCTruthEventAction") << "The following unknown PDG codes were present in the "
57  << "simb::MCTruth input.\n"
58  << "They were not processed by Geant4." << badtxt.str();
59  }
60 
61  // Print out a list of PDG codes we saw in the input that weren't status=1
62  if (!fNon1StatusPDG.empty()) {
63  std::ostringstream non1txt;
64  for (auto const [pdg, n] : fNon1StatusPDG) {
65  non1txt << "\n PDG code = " << pdg << ", seen " << n << " times.";
66  if (genie_specific(pdg)) { non1txt << " (GENIE specific)"; }
67  }
68 
69  mf::LogDebug("MCTruthEventAction") << "The following PDG codes were present in the "
70  << "simb::MCTruth input with Status != 1.\n"
71  << "They were not processed by Geant4." << non1txt.str();
72  }
73 
74  // Print out a list of PDG codes that were processed
75  if (!fProcessedPDG.empty()) {
76  std::ostringstream goodtxt;
77  for (auto const [pdg, n] : fProcessedPDG) {
78  goodtxt << "\n PDG code = " << pdg << ", seen " << n << " times.";
79  }
80  mf::LogDebug("MCTruthEventAction") << "The following PDG codes were present in the "
81  << "simb::MCTruth input with Status = 1 "
82  << "and were processed by Geant4" << goodtxt.str();
83  }
84 }
85 
86 // Create a primary particle for an event!
87 // (Standard Art G4 simulation)
89 {
90  // For each MCTruth (probably only one, but you never know):
91  // index keeps track of which MCTruth object you are using
92  auto const& mclistHandles = *fMCLists;
93 
94  size_t mclSize = mclistHandles.size(); // -- should match the number of generators
95  mf::LogDebug("generatePrimaries") << "MCTruth Handles Size: " << mclSize;
96 
97  // -- Loop over MCTruth Handle List
98  size_t index = 0;
99  std::map<CLHEP::HepLorentzVector, G4PrimaryVertex*> vertexMap;
100  for (size_t mcl = 0; mcl < mclSize; ++mcl) {
101  mf::LogDebug("generatePrimaries")
102  << "MCTruth Handle Number: " << (mcl + 1) << " of " << mclSize;
103  art::Handle<std::vector<simb::MCTruth>> mclistHandle = mclistHandles[mcl];
104  // Loop over all MCTruth handle entries for a given generator,
105  // usually only one, but you never know
106  for (size_t i = 0; i < mclistHandle->size(); ++i) {
107  art::Ptr<simb::MCTruth> mclist(mclistHandle, i);
108 
109  mf::LogDebug("generatePrimaries")
110  << "art::Ptr number " << (i + 1) << " of " << mclistHandle->size() << ", Ptr: " << mclist;
111  int nPart = mclist->NParticles();
112  MF_LOG_INFO("generatePrimaries") << "Generating " << nPart << " particles";
113 
114  // -- Loop over all particles in MCTruth Object
115  for (int m = 0; m != nPart; ++m) {
116  simb::MCParticle const& particle = mclist->GetParticle(m);
117  G4int const pdgCode = particle.PdgCode();
118 
119  if (particle.StatusCode() != 1) {
120  MF_LOG_DEBUG("generatePrimaries")
121  << "Status code != 1, skipping particle number with MCTruth index = "
122  << "(" << mcl << "," << i << ")"
123  << " and particle index = " << m;
124  fNon1StatusPDG[pdgCode] += 1;
125  continue;
126  }
127 
128  if (((m + 1) % nPart) < 2) // -- only first and last will satisfy this
129  {
130  mf::LogDebug("generatePrimaries") << "Particle Number: " << (m + 1) << " of " << nPart;
131  mf::LogDebug("generatePrimaries") << "TrackID: " << particle.TrackId();
132  mf::LogDebug("generatePrimaries") << "index: " << index;
133  }
134 
135  G4double x = particle.Vx() * CLHEP::cm;
136  G4double y = particle.Vy() * CLHEP::cm;
137  G4double z = particle.Vz() * CLHEP::cm;
138  G4double t = particle.T() * CLHEP::ns;
139 
140  // Create a CLHEP four-vector from the particle's vertex.
141  CLHEP::HepLorentzVector fourpos(x, y, z, t);
142 
143  // Is this vertex already in our map?
144  G4PrimaryVertex* vertex = nullptr;
145  auto result = vertexMap.find(fourpos);
146  if (result == vertexMap.end()) {
147  // No, it's not, so create a new vertex and add it to the map.
148  vertex = new G4PrimaryVertex(x, y, z, t);
149  vertexMap[fourpos] = vertex;
150 
151  // Add the vertex to the G4Event.
152  anEvent->AddPrimaryVertex(vertex);
153  }
154  else {
155  // Yes, it is, so use the existing vertex.
156  vertex = result->second;
157  }
158 
159  // Get additional particle information.
160  TLorentzVector const& momentum = particle.Momentum(); // (px,py,pz,E)
161  TVector3 const& polarization = particle.Polarization();
162 
163  // Get the particle table if necessary. (Note: we're doing
164  // this "late" because I'm not sure at what point the G4
165  // particle table is initialized in the loading process.)
166  if (fParticleTable == nullptr) { fParticleTable = G4ParticleTable::GetParticleTable(); }
167 
168  // Get Geant4's definition of the particle.
169  G4ParticleDefinition* particleDefinition = nullptr;
170 
171  if (pdgCode == 0) { particleDefinition = fParticleTable->FindParticle("opticalphoton"); }
172  else {
173  particleDefinition = fParticleTable->FindParticle(pdgCode);
174  }
175 
176  if (pdgCode >= 2'000'000'000) { // If the particle is generator-specific
177  mf::LogDebug("ConvertPrimaryToGeant4")
178  << ": %%% Will skip particle with generator-specific PDG code = " << pdgCode;
179  fUnknownPDG[pdgCode] += 1;
180  continue;
181  }
182  if (pdgCode > 1'000'000'000) { // If the particle is a nucleus
183  mf::LogDebug("ConvertPrimaryToGeant4")
184  << ": %%% Nuclear PDG code = " << pdgCode << " (x,y,z,t)=(" << x << "," << y << "," << z
185  << "," << t << ")"
186  << " P=" << momentum.P() << ", E=" << momentum.E();
187  // If the particle table doesn't have a definition yet, ask
188  // the ion table for one. This will create a new ion
189  // definition as needed.
190  if (!particleDefinition) {
191  int const Z = (pdgCode % 10'000'000) / 10'000; // atomic number
192  int const A = (pdgCode % 10'000) / 10; // mass number
193  particleDefinition = fParticleTable->GetIonTable()->GetIon(Z, A, 0.);
194  }
195  }
196 
197  // What if the PDG code is unknown? This has been a known
198  // issue with GENIE.
199  if (particleDefinition == nullptr) {
200  mf::LogDebug("ConvertPrimaryToGeant4") << ": %%% Code not found = " << pdgCode;
201  fUnknownPDG[pdgCode] += 1;
202  continue;
203  }
204 
205  fProcessedPDG[pdgCode] += 1;
206 
207  // Create a Geant4 particle to add to the vertex.
208  auto* g4particle = new G4PrimaryParticle(particleDefinition,
209  momentum.Px() * CLHEP::GeV,
210  momentum.Py() * CLHEP::GeV,
211  momentum.Pz() * CLHEP::GeV);
212 
213  // Add more particle information the Geant4 particle.
214  g4particle->SetCharge(particleDefinition->GetPDGCharge());
215  g4particle->SetPolarization(polarization.x(), polarization.y(), polarization.z());
216 
217  // Add the particle to the vertex.
218  vertex->SetPrimary(g4particle);
219 
220  // Create a PrimaryParticleInformation object, and save the
221  // MCTruth pointer in it. This will allow the
222  // ParticleActionList class to access MCTruth information
223  // during Geant4's tracking.
224 
225  auto* primaryParticleInfo = new g4b::PrimaryParticleInformation;
226  primaryParticleInfo->SetMCTruth(mclist.get(), index, m);
227 
228  // Save the PrimaryParticleInformation in the
229  // G4PrimaryParticle for access during tracking.
230  g4particle->SetUserInformation(primaryParticleInfo);
231  } // -- for each particle in MCTruth
232  ++index;
233  } // -- for each MCTruth entry
234  } // -- for each MCTruth handle
235 } // -- generatePrimaries()
Float_t x
Definition: compare.C:6
const TVector3 & Polarization() const
Definition: MCParticle.h:215
int PdgCode() const
Definition: MCParticle.h:213
static G4ParticleTable * fParticleTable
Geant4&#39;s table of particle definitions.
Float_t y
Definition: compare.C:6
Double_t z
Definition: plot.C:276
int StatusCode() const
Definition: MCParticle.h:212
int NParticles() const
Definition: MCTruth.h:75
Particle class.
int TrackId() const
Definition: MCParticle.h:211
Float_t Z
Definition: plot.C:37
std::vector< art::Handle< std::vector< simb::MCTruth > > > const * fMCLists
MCTruthCollection input lists.
void generatePrimaries(G4Event *anEvent) override
double T(const int i=0) const
Definition: MCParticle.h:225
std::map< G4int, G4int > fProcessedPDG
PDG codes processed.
#define MF_LOG_INFO(category)
std::map< G4int, G4int > fNon1StatusPDG
PDG codes skipped because not status 1.
const simb::MCParticle & GetParticle(int i) const
Definition: MCTruth.h:76
double Vx(const int i=0) const
Definition: MCParticle.h:222
decltype(auto) get(T &&obj)
ADL-aware version of std::to_string.
Definition: StdUtils.h:120
std::map< G4int, G4int > fUnknownPDG
map of unknown PDG codes to instances
MaybeLogger_< ELseverityLevel::ELsev_success, false > LogDebug
const TLorentzVector & Momentum(const int i=0) const
Definition: MCParticle.h:221
MaybeLogger_< ELseverityLevel::ELsev_warning, false > LogWarning
#define MF_LOG_DEBUG(id)
double Vz(const int i=0) const
Definition: MCParticle.h:224
Char_t n[5]
void SetMCTruth(const simb::MCTruth *m, size_t idx=0, GeneratedParticleIndex_t indexInTruth=simb::NoGeneratedParticleIndex)
MCTruthEventActionService(fhicl::ParameterSet const &)
T const * get() const
Definition: Ptr.h:138
double Vy(const int i=0) const
Definition: MCParticle.h:223
vertex reconstruction