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