LDMX Software
SimulatorBase.cxx
1#include "SimCore/SimulatorBase.h"
2
3#include <G4CascadeParameters.hh>
4#include <G4GeometryManager.hh>
5
6#include "SimCore/DetectorConstruction.h"
9#include "SimCore/G4User/UserEventInformation.h"
11#include "SimCore/Geo/Parser.h"
12#include "SimCore/SDs/SensitiveDetector.h"
13
14namespace simcore {
15
16const std::vector<std::string> SimulatorBase::INVALID_COMMANDS = {
17 "/run/initialize", // hard coded at the right time
18 "/run/beamOn", // passed commands should only be sim setup
19 "/random/setSeeds", // handled by own config parameter (if passed)
20 "ldmx", // all ldmx messengers have been removed
21 "/persistency/gdml/read" // detector description is read after passed a
22 // path to the detector description (required)
23};
24SimulatorBase::SimulatorBase(const std::string& name,
25 framework::Process& process)
26 : framework::Producer(name, process), conditions_intf_(this) {
27 ui_manager_ = G4UImanager::GetUIpointer();
28}
29void SimulatorBase::prepEvent(framework::Event& event) {
31 PrimaryGenerator::Factory::get().apply(
32 [&event](auto gen) { gen->prepEvent(event); });
33}
34void SimulatorBase::updateEventHeader(ldmx::EventHeader& eventHeader) const {
35 auto event_info = static_cast<UserEventInformation*>(
36 run_manager_->GetCurrentEvent()->GetUserInformation());
37
38 eventHeader.setWeight(event_info->getWeight());
39 eventHeader.setFloatParameter("total_photonuclear_energy",
40 event_info->getPNEnergy());
41 eventHeader.setFloatParameter("total_electronuclear_energy",
42 event_info->getENEnergy());
43 eventHeader.setFloatParameter("db_material_z",
44 event_info->getDarkBremMaterialZ());
45 eventHeader.setFloatParameter("aprime_conversion_material_z",
46 event_info->getAPrimeConversionMaterialZ());
47 eventHeader.setIntParameter("pn_target_z", event_info->getPNTargetZ());
48 eventHeader.setIntParameter("pn_target_a", event_info->getPNTargetA());
49 eventHeader.setIntParameter("pn_resample_count",
50 event_info->getPNResampleCount());
51}
52void SimulatorBase::onProcessEnd() {
53 run_manager_->TerminateEventLoop();
54 run_manager_->RunTermination();
55 // Delete Run Manager
56 // From Geant4 Basic Example B01:
57 // Job termination
58 // Free the store: user actions, physics list and detector descriptions
59 // are owned and deleted by the run manager, so they should not be
60 // deleted in the main() program
61 // This needs to happen here because otherwise, Geant4 objects are deleted
62 // twice:
63 // 1. When the histogram file is closed (all ROOT objects created during
64 // processing are put there because ROOT)
65 // 2. When Simulator is deleted because run_manager_ is a unique_ptr
66 run_manager_.reset(nullptr);
67
68 // Delete the G4UIsession
69 // I don't think this needs to happen here, but since we are cleaning up
70 // loose ends...
71 session_handle_.reset(nullptr);
72};
73void SimulatorBase::onProcessStart() {
74 // initialize run
75 run_manager_->Initialize();
76
77 for (const std::string& cmd : post_init_commands_) {
78 int g4_ret = ui_manager_->ApplyCommand(cmd);
79 if (g4_ret > 0) {
80 EXCEPTION_RAISE("PostInitCmd",
81 "Post Initialization command '" + cmd +
82 "' returned a failue status from Geant4: " +
83 std::to_string(g4_ret));
84 }
85 }
86
87 // Instantiate the scoring worlds including any parallel worlds.
88 run_manager_->ConstructScoringWorlds();
89
90 // Initialize the current run
91 run_manager_->RunInitialization();
92
93 // Initialize the event processing
94 run_manager_->InitializeEventLoop(1);
95
96 return;
97}
98void SimulatorBase::verifyParameters() const {
99 // in past versions of SimCore, the run number for the simulation was
100 // passed directly to the simulator class rather than pulled from central
101 // framework. This is here to prevent the user from accidentally using the
102 // old style.
103 if (parameters_.exists("runNumber")) {
104 EXCEPTION_RAISE("InvalidParam",
105 "Remove old-style of setting the simulation run number "
106 "(sim.runNumber)."
107 " Replace with using the Process object (p.run).");
108 }
109 // Looks for sub-strings matching the ones listed as an invalid command.
110 // These invalid commands are mostly commands where control has been handed
111 // over to Simulator.
112 for (const auto& invalid_command : INVALID_COMMANDS) {
113 for (const auto& cmd : pre_init_commands_) {
114 if (cmd.find(invalid_command) != std::string::npos) {
115 EXCEPTION_RAISE("PreInitCmd", "Pre Initialization command '" + cmd +
116 "' is not allowed because another "
117 "part of Simulator handles it.");
118 }
119 }
120 for (const auto& cmd : post_init_commands_) {
121 if (cmd.find(invalid_command) != std::string::npos) {
122 EXCEPTION_RAISE("PostInitCmd", "Post Initialization command '" + cmd +
123 "' is not allowed because another "
124 "part of Simulator handles it.");
125 }
126 }
127 }
128}
129
130void SimulatorBase::configure(framework::config::Parameters& parameters) {
131 // parameters used to configure the simulation
132 parameters_ = parameters;
133
134 pre_init_commands_ =
135 parameters_.get<std::vector<std::string>>("pre_init_commands", {});
136
137 // Get the extra simulation configuring commands
138 post_init_commands_ =
139 parameters_.get<std::vector<std::string>>("post_init_commands", {});
140
141 verifyParameters();
142 if (run_manager_) {
143 // TODO: This won't work, need to think of a better solution
144 EXCEPTION_RAISE(
145 "MultipleSimulators",
146 "A simulator or resimulator producer has already been created. Only "
147 "one of them can be present in a given run. To run the resimulator, "
148 "use a an existing eventFile as input.");
149 }
150 // Set up logging before creating the run manager so that output from the
151 // creation of the runManager goes to the appropriate place.
152 createLogging();
153 run_manager_ = std::make_unique<RunManager>(parameters_, conditions_intf_);
154 // Instantiate the class so cascade parameters can be set.
155 // TODO: Are we actually using this?
156 G4CascadeParameters::Instance();
157
158 buildGeometry();
159 for (const std::string& cmd : pre_init_commands_) {
160 int g4_ret = ui_manager_->ApplyCommand(cmd);
161 if (g4_ret > 0) {
162 EXCEPTION_RAISE("PreInitCmd",
163 "Pre Initialization command '" + cmd +
164 "' returned a failure status from Geant4: " +
165 std::to_string(g4_ret));
166 }
167 }
168}
169void SimulatorBase::createLogging() {
170 auto logging_prefix = parameters_.get<std::string>("logging_prefix");
171 session_handle_ = std::make_unique<LoggedSession>(logging_prefix);
172
173 if (session_handle_ != nullptr)
174 ui_manager_->SetCoutDestination(session_handle_.get());
175}
176
177void SimulatorBase::saveTracks(framework::Event& event) {
178 TrackMap& tracks{g4user::TrackingAction::get()->getTrackMap()};
179 tracks.traceAncestry();
180 event.add("SimParticles", tracks.getParticleMap());
181}
182void SimulatorBase::saveSDHits(framework::Event& event) {
183 // Copy hit objects from SD hit collections into the output event.
184 SensitiveDetector::Factory::get().apply([&event](auto sd) {
185 sd->saveHits(event);
186 sd->onFinishedEvent();
187 });
188}
189
190void SimulatorBase::savePhotonuclearInteractions(framework::Event& event) {
191 // Save photonuclear interactions if the PhotonuclearTracker is active
192 auto pn_tracker = PhotonuclearTracker::get();
193 if (pn_tracker) {
194 auto pn_interactions = pn_tracker->getInteractions();
195 if (!pn_interactions.empty()) {
196 event.add("PhotonuclearInteractions", pn_interactions);
197 }
198 }
199}
200
201void SimulatorBase::buildGeometry() {
202 // Instantiate the GDML parser and corresponding messenger owned and
203 // managed by DetectorConstruction
204 auto parser{simcore::geo::Parser::Factory::get().make("gdml", parameters_,
205 conditions_intf_)};
206 if (not parser) {
207 EXCEPTION_RAISE(
208 "UnableToCreate",
209 "Unable to find a parser registered under the name 'gdml'.");
210 }
211 auto parser_ptr{parser.value()};
212
213 // Set the DetectorConstruction instance used to build the detector
214 // from the GDML description.
215 run_manager_->SetUserInitialization(
216 new DetectorConstruction(parser_ptr, parameters_, conditions_intf_));
217
218 // Parse the detector geometry and validate if specified.
219 auto detector_path{parameters_.get<std::string>("detector")};
220 ldmx_log(trace) << "Reading in geometry from '" << detector_path << "'";
221 G4GeometryManager::GetInstance()->OpenGeometry();
222 parser_ptr->read();
223 run_manager_->DefineWorldVolume(parser_ptr->getWorldVolume());
224}
225} // namespace simcore
UserAction for tracking detailed photonuclear interaction information.
Header file for PrimaryGenerator.
Class which implements the user tracking action.
Implements an event buffer system for storing event data.
Definition Event.h:40
Class which represents the process under execution.
Definition Process.h:34
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:26
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:75
Provides header information an event such as event number and timestamp.
Definition EventHeader.h:44
void setIntParameter(const std::string &name, int value)
Set an int parameter value.
void setWeight(double weight)
Set the event weight.
void setFloatParameter(const std::string &name, float value)
Set a float parameter value.
static const std::vector< std::string > INVALID_COMMANDS
Commands not allowed to be passed from python config file This is because Simulator already runs them...
Encapsulates user defined information associated with a Geant4 event.
All classes in the ldmx-sw project use this namespace.
Dynamically loadable photonuclear models either from SimCore or external libraries implementing this ...