eventDispenser
Loading...
Searching...
No Matches
eventDispenser.cc
Go to the documentation of this file.
1// Implements EventDispenser: run-weight parsing, event distribution, and per-run dispatch through Geant4.
2//
3// Doxygen documentation for the public API is maintained in eventDispenser.h.
4// This implementation file keeps only short, non-Doxygen summaries and inline clarifying comments.
5
8#include "eventDispenser.h"
10
11// c++
12#include <fstream>
13#include <random>
14#include <utility>
15
16// geant4
17#include "G4GeometryManager.hh"
18#include "G4UImanager.hh"
19#include "G4RunManager.hh"
20
21using namespace std;
22
23namespace {
24void closeOpenGeometryBeforeBeamOn(const std::shared_ptr<GLogger>& log) {
25 auto* geometryManager = G4GeometryManager::GetInstanceIfExist();
26 if (!geometryManager || geometryManager->IsGeometryClosed()) { return; }
27
28 log->info(1, "Geometry is open before BeamOn; closing it before event processing.");
29 geometryManager->CloseGeometry();
30}
31}
32
33// Constructor summary:
34// - Reads configuration (number of events, run number, optional run-weight file).
35// - Builds runWeights/runEvents/listOfRuns when weights are provided.
36// - Otherwise, falls back to single-run mode.
38 const std::shared_ptr<GOptions>& gopt,
39 const std::shared_ptr<const gdynamicdigitization::dRoutinesMap>& gdynamicDigitizationMap,
40 std::shared_ptr<GAnalysisAccumulator> analyzer)
41 : GBase(gopt, EVENTDISPENSER_LOGGER), gDigitizationMap(gdynamicDigitizationMap),
42 analysisAccumulator(std::move(analyzer)) {
43 // Retrieve configuration parameters from GOptions.
44 string filename = gopt->getScalarString("run_weights");
45 userRunno = gopt->getScalarInt("run");
46 neventsToProcess = gopt->getScalarInt("n");
47
48 // Detect offscreen mode once at construction so processEvents() needs no vis headers.
49 // g4view is only defined when g4display options are included (e.g. in the full gemc app).
50 if (gopt->doesOptionExist("g4view")) {
51 auto driverNode = gopt->getOptionMapInNode("g4view", "driver");
52 if (!driverNode.IsNull() && driverNode.IsDefined()) {
53 offscreen_screenshots = (driverNode.as<std::string>() == "TOOLSSG_OFFSCREEN");
54 }
55 }
56
57 // If there are no events to process, keep the object in an initialized-but-idle state.
58 if (neventsToProcess == 0) return;
59
60 // If no file is provided, use the user-specified run number (single-run mode).
61 if (filename == UNINITIALIZEDSTRINGQUANTITY && neventsToProcess > 0) {
62 runEvents[userRunno] = neventsToProcess;
63 return;
64 }
65 else {
66 // Multi-run mode: a filename was specified; attempt to open the run weights input file.
67 ifstream in(filename.c_str());
68 if (!in) {
69 // Keep behavior unchanged: log error and continue with an empty distribution.
71 "Error: can't open run weights input file >", filename, "<. Check your spelling. Exiting.");
72 }
73 else {
74 log->info(1, "Loading run weights from ", filename);
75
76 // Read "run weight" pairs, one per line.
77 // The order of insertion into listOfRuns reflects the file order and may be used by clients.
78 int run;
79 double weight;
80 while (in >> run >> weight) {
81 listOfRuns.push_back(run);
82 runWeights[run] = weight;
83 runEvents[run] = 0; // initialize per-run counters before distribution
84 }
85
86 // Distribute the total number of events among runs according to their weights.
87 distributeEvents(neventsToProcess);
88 }
89 in.close();
90
91 // Log summary information: overall distribution table.
92 log->info(0, "EventDispenser initialized with ", neventsToProcess, " events distributed among ",
93 runWeights.size(), " runs:");
94 log->info(0, " run\t weight\t n. events");
95 for (const auto& weight : runWeights) {
96 log->info(0, " ", weight.first, "\t ", weight.second, "\t ", runEvents[weight.first]);
97 }
98 }
99}
100
101
102// setNumberOfEvents summary:
103// - Clears any existing distribution and assigns all events to the user-selected run number.
104void EventDispenser::setNumberOfEvents(int nevents_to_process) {
105 runEvents.clear();
106 runEvents[userRunno] = nevents_to_process;
107}
108
110 currentRunno = -1;
111}
112
113
114// distributeEvents summary:
115// - Performs stochastic sampling to convert runWeights into integer runEvents counts.
116void EventDispenser::distributeEvents(int nevents_to_process) {
117 // Set up a random number generator drawing from U[0, 1].
118 random_device randomDevice;
119 mt19937 generator(randomDevice());
120 uniform_real_distribution<> randomDistribution(0, 1);
121
122 // Weights in the run-weights file are relative, not required to sum to 1. Compute the total
123 // once and scale each draw by it, so non-normalized weights distribute events correctly.
124 double totalWeight = 0;
125 for (const auto& weight : runWeights) { totalWeight += weight.second; }
126 if (totalWeight <= 0) {
128 "Run weights sum to ", totalWeight, " (must be > 0). Check your run weights file.");
129 return;
130 }
131
132 // For each event, select a run by comparing a random draw to the cumulative weight intervals.
133 for (int i = 0; i < nevents_to_process; i++) {
134 double randomNumber = randomDistribution(generator) * totalWeight;
135
136 double cumulativeWeight = 0;
137 for (const auto& weight : runWeights) {
138 cumulativeWeight += weight.second;
139 if (randomNumber <= cumulativeWeight) {
140 runEvents[weight.first]++;
141 break;
142 }
143 }
144 }
145}
146
147
148// getTotalNumberOfEvents summary:
149// - Sums all per-run event counts from runEvents.
151 int totalEvents = 0;
152 for (auto rEvents : runEvents) { totalEvents += rEvents.second; }
153 return totalEvents;
154}
155
156
157// processEvents summary:
158// - Iterates the run allocation.
159// - For each run, loads run-dependent constants/TT via digitization routines (if run changed).
160// - Dispatches the events to Geant4 via \c /run/beamOn.
162 // Get the Geant4 UI manager pointer used to apply macro commands.
163 G4UImanager* g4uim = G4UImanager::GetUIpointer();
164
165 // Iterate over each run in the run events map.
166 for (auto& run : runEvents) {
167 int runNumber = run.first;
168 int nevents = run.second;
169
170 // Load constants and translation tables if the run number has changed.
171 if (runNumber != currentRunno) {
172 // Iterate the (plugin name -> digitization routine) map.
173 // digiRoutine is a std::shared_ptr<GDynamicDigitization>.
174 for (const auto& [plugin, digiRoutine] : *gDigitizationMap) {
175 // The variation is resolved per routine at geometry load (gsystem variation,
176 // or the digitization_variation option override when set).
177 const std::string& variation = digiRoutine->getDigitizationVariation();
178
179 log->debug(NORMAL, FUNCTION_NAME, "Calling ", plugin, " loadConstants for run ", runNumber,
180 " with variation ", variation);
181 if (digiRoutine->loadConstants(runNumber, variation) == false) {
183 "Failed to load constants for ", plugin, " for run ", runNumber, " with variation ",
184 variation);
185 }
186
187 log->debug(NORMAL, FUNCTION_NAME, "Calling ", plugin, " loadTT for run ", runNumber);
188 if (digiRoutine->loadTT(runNumber, variation) == false) {
189 log->error(ERR_LOADTTFAIL,
190 "Failed to load translation table for ", plugin, " for run ", runNumber,
191 " with variation ", variation);
192 }
193 }
194 currentRunno = runNumber;
195 }
196
197 log->info(1, "Starting run ", runNumber, " with ", nevents, " events.");
198 if (analysisAccumulator != nullptr) { analysisAccumulator->setCurrentRunNumber(runNumber); }
199 // Tag the next G4Run with this run number. Guarded because standalone/unit-test
200 // contexts (e.g. the event_dispenser example) may run without a G4RunManager.
201 if (G4RunManager* g4rm = G4RunManager::GetRunManager()) { g4rm->SetRunIDCounter(runNumber); }
202
203 // Dispatch all events for this run in a single call.
204 // The command string is a standard Geant4 UI command: \c /run/beamOn <N>.
205 log->info(1, "Processing ", nevents, " events in one go");
206 closeOpenGeometryBeforeBeamOn(log);
207 // Record the moment the first BeamOn is issued so a timing summary can be produced later.
208 if (!beamOnTime.has_value()) { beamOnTime = std::chrono::steady_clock::now(); }
209 g4uim->ApplyCommand("/run/beamOn " + to_string(nevents));
210 // Take the screenshot after BeamOn returns. At this point G4VisManager::EndOfRun()
211 // has already joined the vis subthread (ARM64 offset 0xa35f8: bl thread::join), so
212 // DrawEvent calls are finished — no concurrent scene-graph writes. The transient store
213 // is still intact: the vis subthread's exit cleanup only runs after running=0 is set
214 // inside G4VisManager::EndOfRun(), which completes inside BeamOn before it returns.
215 if (offscreen_screenshots) {
216 g4uim->ApplyCommand("/vis/tsg/offscreen/set/size 3000 2000");
217 g4uim->ApplyCommand("/vis/tsg/offscreen/set/file gemc_run_" + to_string(runNumber) + ".png");
218 g4uim->ApplyCommand("/vis/viewer/rebuild");
219 }
220
221 log->info(1, "Run ", runNumber, " done with ", nevents, " events");
222 }
223
224 return 1;
225}
void resetRunContext()
Force per-run digitization setup to run again on the next event batch.
int processEvents()
Processes all runs by initializing digitization routines and dispatching events.
EventDispenser(const std::shared_ptr< GOptions > &gopt, const std::shared_ptr< const gdynamicdigitization::dRoutinesMap > &gdynamicDigitizationMap, std::shared_ptr< GAnalysisAccumulator > analysisAccumulator=nullptr)
Constructs an EventDispenser and prepares the run event distribution.
void setNumberOfEvents(int nevts)
Sets the total number of events to process in single-run mode.
int getTotalNumberOfEvents() const
Computes the total number of events across all runs.
GBase(const std::shared_ptr< GOptions > &gopt, std::string logger_name="")
std::shared_ptr< GLogger > log
Event Dispenser module error-code conventions.
#define ERR_EVENTDISTRIBUTIONFILENOTFOUND
Run-weight file could not be opened/read.
Declares the EventDispenser class.
Public declaration of the Event Dispenser module command-line / configuration options.
constexpr const char * EVENTDISPENSER_LOGGER
Logger name used by this module when creating a GLogger through the base infrastructure.
constexpr int ERR_LOADCONSTANTFAIL
constexpr int ERR_LOADTTFAIL
run
#define FUNCTION_NAME
NORMAL
#define UNINITIALIZEDSTRINGQUANTITY
constexpr const char * to_string(randomModel m) noexcept