Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Geant4Simulation.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file Geant4Simulation.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2021 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
10 
32 
33 #include <cassert>
34 #include <cstddef>
35 #include <iostream>
36 #include <map>
37 #include <stdexcept>
38 #include <utility>
39 
40 #include <G4FieldManager.hh>
41 #include <G4RunManager.hh>
42 #include <G4TransportationManager.hh>
43 #include <G4UniformMagField.hh>
44 #include <G4UserEventAction.hh>
45 #include <G4UserLimits.hh>
46 #include <G4UserRunAction.hh>
47 #include <G4UserSteppingAction.hh>
48 #include <G4UserTrackingAction.hh>
49 #include <G4VUserDetectorConstruction.hh>
50 #include <G4VUserPhysicsList.hh>
51 #include <G4Version.hh>
52 #include <Randomize.hh>
53 
56  : IAlgorithm(std::move(name), level) {
57  if (cfg.inputParticles.empty()) {
58  throw std::invalid_argument("Missing input particle collection");
59  }
60  if (!cfg.detectorConstructionFactory) {
61  throw std::invalid_argument("Missing detector construction factory");
62  }
63  if (!cfg.randomNumbers) {
64  throw std::invalid_argument("Missing random numbers");
65  }
66 
67  m_logger = Acts::getDefaultLogger("Geant4", level);
68 
69  m_eventStore = std::make_shared<EventStore>();
70 
71  // tweak logging
72  // If we are in VERBOSE mode, set the verbose level in Geant4 to 2.
73  // 3 would be also possible, but that produces infinite amount of output.
75 }
76 
78 
80  // Set the detector construction
81  {
82  // Clear detector construction if it exists
83  if (runManager().GetUserDetectorConstruction() != nullptr) {
84  delete runManager().GetUserDetectorConstruction();
85  }
86  // G4RunManager will take care of deletion
87  m_detectorConstruction =
88  config().detectorConstructionFactory->factorize().release();
89  runManager().SetUserInitialization(m_detectorConstruction);
90  runManager().InitializeGeometry();
91  }
92 }
93 
95  return *m_geant4Instance->runManager.get();
96 }
97 
99  const {
100  return *m_eventStore;
101 }
102 
104  // Initialize the Geant4 run manager
105  runManager().Initialize();
106 
108 }
109 
111  const ActsExamples::AlgorithmContext& ctx) const {
112  // Ensure exclusive access to the Geant4 run manager
113  std::lock_guard<std::mutex> guard(m_geant4Instance->mutex);
114 
115  // Set the seed new per event, so that we get reproducible results
116  G4Random::setTheSeed(config().randomNumbers->generateSeed(ctx));
117 
118  // Get and reset event registry state
119  eventStore() = EventStore{};
120 
121  // Register the current event store to the registry
122  // this will allow access from the User*Actions
123  eventStore().store = &(ctx.eventStore);
124 
125  // Register the input particle read handle
126  eventStore().inputParticles = &m_inputParticles;
127 
128  ACTS_DEBUG("Sending Geant RunManager the BeamOn() command.");
129  {
130  Acts::FpeMonitor mon{0}; // disable all FPEs while we're in Geant4
131  // Start simulation. each track is simulated as a separate Geant4 event.
132  runManager().BeamOn(1);
133  }
134 
135  // Since these are std::set, this ensures that each particle is in both sets
136  throw_assert(
137  eventStore().particlesInitial.size() ==
138  eventStore().particlesFinal.size(),
139  "initial and final particle collections does not have the same size: "
140  << eventStore().particlesInitial.size() << " vs "
141  << eventStore().particlesFinal.size());
142 
143  // Print out warnings about possible particle collision if happened
144  if (eventStore().particleIdCollisionsInitial > 0 or
145  eventStore().particleIdCollisionsFinal > 0 or
146  eventStore().parentIdNotFound > 0) {
147  ACTS_WARNING(
148  "Particle ID collisions detected, don't trust the particle "
149  "identification!");
150  ACTS_WARNING(
151  "- initial states: " << eventStore().particleIdCollisionsInitial);
152  ACTS_WARNING("- final states: " << eventStore().particleIdCollisionsFinal);
153  ACTS_WARNING("- parent ID not found: " << eventStore().parentIdNotFound);
154  }
155 
156  if (eventStore().hits.empty()) {
157  ACTS_DEBUG("Step merging: No steps recorded");
158  } else {
159  ACTS_DEBUG("Step merging: mean hits per hit: "
160  << static_cast<double>(eventStore().numberGeantSteps) /
161  eventStore().hits.size());
162  ACTS_DEBUG(
163  "Step merging: max hits per hit: " << eventStore().maxStepsForHit);
164  }
165 
167 }
168 
169 std::shared_ptr<ActsExamples::Geant4Handle>
171  return m_geant4Instance;
172 }
173 
176  : Geant4SimulationBase(cfg, "Geant4Simulation", level), m_cfg(cfg) {
181  if (m_geant4Instance->physicsListName != m_cfg.physicsList) {
182  throw std::runtime_error("inconsistent physics list");
183  }
184 
186 
187  // Set the primarty generator
188  {
189  // Clear primary generation action if it exists
190  if (runManager().GetUserPrimaryGeneratorAction() != nullptr) {
191  delete runManager().GetUserPrimaryGeneratorAction();
192  }
194  prCfg.eventStore = m_eventStore;
195  // G4RunManager will take care of deletion
196  auto primaryGeneratorAction = new SimParticleTranslation(
197  prCfg, m_logger->cloneWithSuffix("SimParticleTranslation"));
198  // Set the primary generator action
199  runManager().SetUserAction(primaryGeneratorAction);
200  }
201 
202  // Particle action
203  {
204  // Clear tracking action if it exists
205  if (runManager().GetUserTrackingAction() != nullptr) {
206  delete runManager().GetUserTrackingAction();
207  }
208  ParticleTrackingAction::Config trackingCfg;
209  trackingCfg.eventStore = m_eventStore;
210  trackingCfg.keepParticlesWithoutHits = cfg.keepParticlesWithoutHits;
211  // G4RunManager will take care of deletion
212  auto trackingAction = new ParticleTrackingAction(
213  trackingCfg, m_logger->cloneWithSuffix("ParticleTracking"));
214  runManager().SetUserAction(trackingAction);
215  }
216 
217  // Stepping actions
218  {
219  // Clear stepping action if it exists
220  if (runManager().GetUserSteppingAction() != nullptr) {
221  delete runManager().GetUserSteppingAction();
222  }
223 
224  ParticleKillAction::Config particleKillCfg;
225  particleKillCfg.volume = cfg.killVolume;
226  particleKillCfg.maxTime = cfg.killAfterTime;
227  particleKillCfg.secondaries = cfg.killSecondaries;
228 
230  stepCfg.eventStore = m_eventStore;
231  stepCfg.charged = true;
232  stepCfg.neutral = false;
233  stepCfg.primary = true;
234  stepCfg.secondary = cfg.recordHitsOfSecondaries;
235 
236  SteppingActionList::Config steppingCfg;
237  steppingCfg.actions.push_back(std::make_unique<ParticleKillAction>(
238  particleKillCfg, m_logger->cloneWithSuffix("Killer")));
239  steppingCfg.actions.push_back(std::make_unique<SensitiveSteppingAction>(
240  stepCfg, m_logger->cloneWithSuffix("SensitiveStepping")));
241 
242  // G4RunManager will take care of deletion
243  auto steppingAction = new SteppingActionList(steppingCfg);
244  runManager().SetUserAction(steppingAction);
245  }
246 
247  // Get the g4World cache
248  G4VPhysicalVolume* g4World = m_detectorConstruction->Construct();
249 
250  // Please note:
251  // The following two blocks rely on the fact that the Acts
252  // detector constructions cache the world volume
253 
254  // Set the magnetic field
255  if (cfg.magneticField) {
256  ACTS_INFO("Setting ACTS configured field to Geant4.");
257 
258  MagneticFieldWrapper::Config g4FieldCfg;
259  g4FieldCfg.magneticField = cfg.magneticField;
260  m_magneticField = std::make_unique<MagneticFieldWrapper>(g4FieldCfg);
261 
262  // Set the field or the G4Field manager
263  m_fieldManager = std::make_unique<G4FieldManager>();
264  m_fieldManager->SetDetectorField(m_magneticField.get());
265  m_fieldManager->CreateChordFinder(m_magneticField.get());
266 
267  // Propagate down to all childrend
268  g4World->GetLogicalVolume()->SetFieldManager(m_fieldManager.get(), true);
269  }
270 
271  // An ACTS TrackingGeometry is provided, so simulation for sensitive
272  // detectors is turned on - they need to get matched first
273  if (cfg.trackingGeometry) {
274  ACTS_INFO(
275  "Remapping selected volumes from Geant4 to Acts::Surface::GeometryID");
276 
278  ssmCfg.trackingGeometry = cfg.trackingGeometry;
279  ssmCfg.volumeMappings = cfg.volumeMappings;
280  ssmCfg.materialMappings = cfg.materialMappings;
281 
282  SensitiveSurfaceMapper sensitiveSurfaceMapper(
283  ssmCfg, m_logger->cloneWithSuffix("SensitiveSurfaceMapper"));
284  int sCounter = 0;
285  sensitiveSurfaceMapper.remapSensitiveNames(
286  g4World, Acts::Transform3::Identity(), sCounter);
287 
288  ACTS_INFO("Remapping successful for " << sCounter << " selected volumes.");
289  }
290 
292  m_outputSimHits.initialize(cfg.outputSimHits);
293  m_outputParticlesInitial.initialize(cfg.outputParticlesInitial);
294  m_outputParticlesFinal.initialize(cfg.outputParticlesFinal);
295 }
296 
298 
300  const ActsExamples::AlgorithmContext& ctx) const {
302 
303  // Output handling: Simulation
304  m_outputParticlesInitial(
305  ctx, SimParticleContainer(eventStore().particlesInitial.begin(),
306  eventStore().particlesInitial.end()));
307  m_outputParticlesFinal(
308  ctx, SimParticleContainer(eventStore().particlesFinal.begin(),
309  eventStore().particlesFinal.end()));
310 
311 #if BOOST_VERSION < 107800
313  for (const auto& hit : eventStore().hits) {
314  container.insert(hit);
315  }
316  m_outputSimHits(ctx, std::move(container));
317 #else
318  m_outputSimHits(
319  ctx, SimHitContainer(eventStore().hits.begin(), eventStore().hits.end()));
320 #endif
321 
323 }
324 
327  : Geant4SimulationBase(cfg, "Geant4Simulation", level), m_cfg(cfg) {
328  auto physicsListName = "MaterialPhysicsList";
334  std::make_unique<MaterialPhysicsList>(
335  m_logger->cloneWithSuffix("MaterialPhysicsList")),
336  physicsListName);
337  if (m_geant4Instance->physicsListName != physicsListName) {
338  throw std::runtime_error("inconsistent physics list");
339  }
340 
342 
343  // Set the primarty generator
344  {
345  // Clear primary generation action if it exists
346  if (runManager().GetUserPrimaryGeneratorAction() != nullptr) {
347  delete runManager().GetUserPrimaryGeneratorAction();
348  }
349 
351  prCfg.eventStore = m_eventStore;
352  prCfg.forcedPdgCode = 0;
353  prCfg.forcedCharge = 0.;
354  prCfg.forcedMass = 0.;
355 
356  // G4RunManager will take care of deletion
357  auto primaryGeneratorAction = new SimParticleTranslation(
358  prCfg, m_logger->cloneWithSuffix("SimParticleTranslation"));
359  // Set the primary generator action
360  runManager().SetUserAction(primaryGeneratorAction);
361  }
362 
363  // Particle action
364  {
365  // Clear tracking action if it exists
366  if (runManager().GetUserTrackingAction() != nullptr) {
367  delete runManager().GetUserTrackingAction();
368  }
369  ParticleTrackingAction::Config trackingCfg;
370  trackingCfg.eventStore = m_eventStore;
371  trackingCfg.keepParticlesWithoutHits = true;
372  // G4RunManager will take care of deletion
373  auto trackingAction = new ParticleTrackingAction(
374  trackingCfg, m_logger->cloneWithSuffix("ParticleTracking"));
375  runManager().SetUserAction(trackingAction);
376  }
377 
378  // Stepping action
379  {
380  // Clear stepping action if it exists
381  if (runManager().GetUserSteppingAction() != nullptr) {
382  delete runManager().GetUserSteppingAction();
383  }
384  MaterialSteppingAction::Config steppingCfg;
385  steppingCfg.eventStore = m_eventStore;
387  // G4RunManager will take care of deletion
388  auto steppingAction = new MaterialSteppingAction(
389  steppingCfg, m_logger->cloneWithSuffix("MaterialSteppingAction"));
390  runManager().SetUserAction(steppingAction);
391  }
392 
393  runManager().Initialize();
394 
396  m_outputMaterialTracks.initialize(cfg.outputMaterialTracks);
397 }
398 
400 
402  const ActsExamples::AlgorithmContext& ctx) const {
404 
405  // Output handling: Material tracks
406  m_outputMaterialTracks(
407  ctx, decltype(eventStore().materialTracks)(eventStore().materialTracks));
408 
410 }