All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MCTruthEventAction.cc
Go to the documentation of this file.
2 
3 #include "nug4/G4Base/PrimaryParticleInformation.h"
4 
5 #include "nusimdata/SimulationBase/MCParticle.h"
6 
7 #include "canvas/Persistency/Common/Ptr.h"
8 
9 #include "messagefacility/MessageLogger/MessageLogger.h"
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
33  genie_specific(int const pdg)
34  {
35  constexpr int genieLoPdg = 2000000001;
36  constexpr int genieHiPdg = 2000000300;
37  return pdg >= genieLoPdg && pdg <= genieHiPdg;
38  }
39 }
40 
41 G4ParticleTable* larg4::MCTruthEventActionService::fParticleTable = nullptr;
42 
44  : PrimaryGeneratorActionBase(p.get<string>("name", "MCTruthEventActionService"))
45 {}
46 
48 {
49  // Print out a list of "unknown" PDG codes we saw in the input.
50  if (!fUnknownPDG.empty()) {
51  std::ostringstream badtxt;
52  for (auto const [pdg, n] : fUnknownPDG) {
53  badtxt << "\n Unknown PDG code = " << pdg << ", seen " << n << " times.";
54  if (genie_specific(pdg)) { badtxt << " (GENIE specific)"; }
55  }
56 
57  mf::LogWarning("MCTruthEventAction") << "The following unknown PDG codes were present in the "
58  << "simb::MCTruth input.\n"
59  << "They were not processed by Geant4." << badtxt.str();
60  }
61 
62  // Print out a list of PDG codes we saw in the input that weren't status=1
63  if (!fNon1StatusPDG.empty()) {
64  std::ostringstream non1txt;
65  for (auto const [pdg, n] : fNon1StatusPDG) {
66  non1txt << "\n PDG code = " << pdg << ", seen " << n << " times.";
67  if (genie_specific(pdg)) { non1txt << " (GENIE specific)"; }
68  }
69 
70  mf::LogDebug("MCTruthEventAction") << "The following PDG codes were present in the "
71  << "simb::MCTruth input with Status != 1.\n"
72  << "They were not processed by Geant4." << non1txt.str();
73  }
74 
75  // Print out a list of PDG codes that were processed
76  if (!fProcessedPDG.empty()) {
77  std::ostringstream goodtxt;
78  for (auto const [pdg, n] : fProcessedPDG) {
79  goodtxt << "\n PDG code = " << pdg << ", seen " << n << " times.";
80  }
81  mf::LogDebug("MCTruthEventAction") << "The following PDG codes were present in the "
82  << "simb::MCTruth input with Status = 1 "
83  << "and were processed by Geant4" << goodtxt.str();
84  }
85 }
86 
87 // Create a primary particle for an event!
88 // (Standard Art G4 simulation)
89 void
91 {
92  // For each MCTruth (probably only one, but you never know):
93  // index keeps track of which MCTruth object you are using
94  auto const& mclistHandles = *fMCLists;
95 
96  size_t mclSize = mclistHandles.size(); // -- should match the number of generators
97  mf::LogDebug("generatePrimaries") << "MCTruth Handles Size: " << mclSize;
98 
99  // -- Loop over MCTruth Handle List
100  size_t index = 0;
101  std::map<CLHEP::HepLorentzVector, G4PrimaryVertex*> vertexMap;
102  for (size_t mcl = 0; mcl < mclSize; ++mcl) {
103  mf::LogDebug("generatePrimaries")
104  << "MCTruth Handle Number: " << (mcl + 1) << " of " << mclSize;
105  art::Handle<std::vector<simb::MCTruth>> mclistHandle = mclistHandles[mcl];
106  // Loop over all MCTruth handle entries for a given generator,
107  // usually only one, but you never know
108  for (size_t i = 0; i < mclistHandle->size(); ++i) {
109  art::Ptr<simb::MCTruth> mclist(mclistHandle, i);
110 
111  mf::LogDebug("generatePrimaries")
112  << "art::Ptr number " << (i + 1) << " of " << mclistHandle->size() << ", Ptr: " << mclist;
113  int nPart = mclist->NParticles();
114  MF_LOG_INFO("generatePrimaries") << "Generating " << nPart << " particles";
115 
116  // -- Loop over all particles in MCTruth Object
117  for (int m = 0; m != nPart; ++m) {
118  simb::MCParticle const& particle = mclist->GetParticle(m);
119  G4int const pdgCode = particle.PdgCode();
120 
121  if (particle.StatusCode() != 1) {
122  MF_LOG_DEBUG("generatePrimaries")
123  << "Status code != 1, skipping particle number with MCTruth index = "
124  << "(" << mcl << "," << i << ")"
125  << " and particle index = " << m;
126  fNon1StatusPDG[pdgCode] += 1;
127  continue;
128  }
129 
130  if (((m + 1) % nPart) < 2) // -- only first and last will satisfy this
131  {
132  mf::LogDebug("generatePrimaries") << "Particle Number: " << (m + 1) << " of " << nPart;
133  mf::LogDebug("generatePrimaries") << "TrackID: " << particle.TrackId();
134  mf::LogDebug("generatePrimaries") << "index: " << index;
135  }
136 
137  G4double x = particle.Vx() * CLHEP::cm;
138  G4double y = particle.Vy() * CLHEP::cm;
139  G4double z = particle.Vz() * CLHEP::cm;
140  G4double t = particle.T() * CLHEP::ns;
141 
142  // Create a CLHEP four-vector from the particle's vertex.
143  CLHEP::HepLorentzVector fourpos(x, y, z, t);
144 
145  // Is this vertex already in our map?
146  G4PrimaryVertex* vertex = nullptr;
147  auto result = vertexMap.find(fourpos);
148  if (result == vertexMap.end()) {
149  // No, it's not, so create a new vertex and add it to the map.
150  vertex = new G4PrimaryVertex(x, y, z, t);
151  vertexMap[fourpos] = vertex;
152 
153  // Add the vertex to the G4Event.
154  anEvent->AddPrimaryVertex(vertex);
155  }
156  else {
157  // Yes, it is, so use the existing vertex.
158  vertex = result->second;
159  }
160 
161  // Get additional particle information.
162  TLorentzVector const& momentum = particle.Momentum(); // (px,py,pz,E)
163  TVector3 const& polarization = particle.Polarization();
164 
165  // Get the particle table if necessary. (Note: we're doing
166  // this "late" because I'm not sure at what point the G4
167  // particle table is initialized in the loading process.)
168  if (fParticleTable == nullptr) { fParticleTable = G4ParticleTable::GetParticleTable(); }
169 
170  // Get Geant4's definition of the particle.
171  G4ParticleDefinition* particleDefinition = nullptr;
172 
173  if (pdgCode == 0) { particleDefinition = fParticleTable->FindParticle("opticalphoton"); }
174  else {
175  particleDefinition = fParticleTable->FindParticle(pdgCode);
176  }
177 
178  if (pdgCode >= 2'000'000'000) { // If the particle is generator-specific
179  mf::LogDebug("ConvertPrimaryToGeant4")
180  << ": %%% Will skip particle with generator-specific PDG code = " << pdgCode;
181  fUnknownPDG[pdgCode] += 1;
182  continue;
183  }
184  if (pdgCode > 1'000'000'000) { // If the particle is a nucleus
185  mf::LogDebug("ConvertPrimaryToGeant4")
186  << ": %%% Nuclear PDG code = " << pdgCode << " (x,y,z,t)=(" << x << "," << y << "," << z
187  << "," << t << ")"
188  << " P=" << momentum.P() << ", E=" << momentum.E();
189  // If the particle table doesn't have a definition yet, ask
190  // the ion table for one. This will create a new ion
191  // definition as needed.
192  if (!particleDefinition) {
193  int const Z = (pdgCode % 10'000'000) / 10'000; // atomic number
194  int const A = (pdgCode % 10'000) / 10; // mass number
195  particleDefinition = fParticleTable->GetIonTable()->GetIon(Z, A, 0.);
196  }
197  }
198 
199  // What if the PDG code is unknown? This has been a known
200  // issue with GENIE.
201  if (particleDefinition == nullptr) {
202  mf::LogDebug("ConvertPrimaryToGeant4") << ": %%% Code not found = " << pdgCode;
203  fUnknownPDG[pdgCode] += 1;
204  continue;
205  }
206 
207  fProcessedPDG[pdgCode] += 1;
208 
209  // Create a Geant4 particle to add to the vertex.
210  auto* g4particle = new G4PrimaryParticle(particleDefinition,
211  momentum.Px() * CLHEP::GeV,
212  momentum.Py() * CLHEP::GeV,
213  momentum.Pz() * CLHEP::GeV);
214 
215  // Add more particle information the Geant4 particle.
216  g4particle->SetCharge(particleDefinition->GetPDGCharge());
217  g4particle->SetPolarization(polarization.x(), polarization.y(), polarization.z());
218 
219  // Add the particle to the vertex.
220  vertex->SetPrimary(g4particle);
221 
222  // Create a PrimaryParticleInformation object, and save the
223  // MCTruth pointer in it. This will allow the
224  // ParticleActionList class to access MCTruth information
225  // during Geant4's tracking.
226 
227  auto* primaryParticleInfo = new g4b::PrimaryParticleInformation;
228  primaryParticleInfo->SetMCTruth(mclist.get(), index, m);
229 
230  // Save the PrimaryParticleInformation in the
231  // G4PrimaryParticle for access during tracking.
232  g4particle->SetUserInformation(primaryParticleInfo);
233  } // -- for each particle in MCTruth
234  ++index;
235  } // -- for each MCTruth entry
236  } // -- for each MCTruth handle
237 } // -- generatePrimaries()
process_name vertex
Definition: cheaterreco.fcl:51
util::quantities::gigaelectronvolt GeV
process_name opflash particleana ie ie ie z
var pdg
Definition: selectors.fcl:14
static G4ParticleTable * fParticleTable
Geant4&#39;s table of particle definitions.
process_name opflash particleana ie x
pdgs p
Definition: selectors.fcl:22
tuple m
now if test mode generate materials, CRT shell, world, gdml header else just generate CRT shell for u...
process_name opflash particleana ie ie y
void generatePrimaries(G4Event *anEvent) override
MCTruthEventActionService(fhicl::ParameterSet const &)
float A
Definition: dedx.py:137