utilities
Loading...
Searching...
No Matches
gemcUtilities.cc
Go to the documentation of this file.
1#include "gemcUtilities.h"
2#include "gemcConventions.h"
3
4// Implementation notes:
5// - Doxygen documentation is kept authoritative in gemcUtilities.h.
6// - This file only provides brief, non-Doxygen comments to clarify intent and flow.
7
8// geant4 headers
9#include "G4Threading.hh"
10#include "G4UImanager.hh"
11#include "G4UnitsTable.hh"
12
13// gemc
14#include "glogger.h"
15#include "gtouchable.h"
16#include "gutilities.h"
17#include "g4SceneProperties.h"
18#include "g4display_options.h"
19
20namespace gemc {
21 // return the number of cores from options.
22 // if 0 is given, returns max number of available cores
23 int get_nthreads(const std::shared_ptr<GOptions>& gopts, const std::shared_ptr<GLogger>& log) {
24 int useThreads = gopts->getScalarInt("nthreads");
25
26 // Geant4 provides a platform-specific core count helper.
27 int ncores = G4Threading::G4GetNumberOfCores();
28
29 // Clamp user request:
30 // - 0 means "use all available cores"
31 // - values larger than available cores are clamped
32 if (useThreads == 0 || useThreads > ncores) useThreads = ncores;
33
34 log->info(0, "Using ", useThreads, " threads out of ", ncores, " available cores.");
35
36 return useThreads;
37 }
38
39 std::vector<std::string> verbosity_commands([[maybe_unused]] const std::shared_ptr<GOptions>& gopts,
40 [[maybe_unused]] const std::shared_ptr<GLogger>& log) {
41 std::vector<std::string> cmds;
42
43 // --- Always-quiet commands ---
44 // These commands reduce Geant4 output noise for typical production runs.
45 cmds.emplace_back("/control/verbose 0");
46 cmds.emplace_back("/hit/verbose 0");
47
48 cmds.emplace_back("/process/verbose 0");
49 cmds.emplace_back("/process/setVerbose 0 all");
50 cmds.emplace_back("/process/had/verbose 0");
51 cmds.emplace_back("/process/had/deex/verbose 0");
52 cmds.emplace_back("/process/had/cascade 0");
53 cmds.emplace_back("/process/em/verbose 0");
54 cmds.emplace_back("/process/eLoss/verbose 0");
55
56 cmds.emplace_back("/tracking/verbose 0");
57 cmds.emplace_back("/geometry/navigator/verbose 0");
58
59 cmds.emplace_back("/event/verbose 0");
60 cmds.emplace_back("/event/stack/verbose 0");
61
62 cmds.emplace_back("/cuts/verbose 0");
63
64 cmds.emplace_back("/run/particle/verbose 0");
65 cmds.emplace_back("/run/verbose 0");
66
67 cmds.emplace_back("/material/verbose 0");
68
69 cmds.emplace_back("/vis/verbose 0");
70 cmds.emplace_back("/particle/verbose 0");
71
72 // cmds.emplace_back("/control/cout/ignoreInitializationCout 1");
73 // cmds.emplace_back("/control/cout/useBuffer 1"); // keep MT output tidy?
74
75 return cmds;
76 }
77
78 std::vector<std::string> initial_commands(const std::shared_ptr<GOptions>& gopts,
79 [[maybe_unused]] const std::shared_ptr<GLogger>& log,
80 bool configure_visualization) {
81 // check_overlaps is typically provided by the Geant4 system options set.
82 auto check_overlaps = gopts->getScalarInt("check_overlaps"); // notice: from g4system options
83 auto gui = gopts->getSwitch("gui");
84 auto g4view = g4display::getG4View(gopts);
85
86 std::vector<std::string> cmds;
87
88 // Batch mode: optionally schedule geometry overlap checks before initialization.
89 // Geant4 overlap checks use the current "/geometry/test/..." configuration.
90 if (check_overlaps == 2) {
91 log->info(0, "Running /geometry/test/run with 50 points.");
92 cmds.emplace_back("/geometry/test/resolution 50");
93 cmds.emplace_back("/geometry/test/run");
94 }
95 else if (check_overlaps >= 100) {
96 log->info(0, "Running /geometry/test/run with ", check_overlaps, " points.");
97 cmds.emplace_back("/geometry/test/resolution " + std::to_string(check_overlaps));
98 cmds.emplace_back("/geometry/test/run");
99 }
100
101 // In GUI mode without startup geometry, defer initialization until the setup
102 // tab has selected and loaded a real system. Initializing the synthetic ROOT
103 // world here leaves stale world/vis state behind after setup-tab reloads.
104 if (gui && !configure_visualization) { return cmds; }
105
106 // A re-initialize is required when:
107 // - physics changes
108 // - geometry changes
109 cmds.emplace_back("/run/initialize");
110
111 if (!configure_visualization) return cmds;
112
113
114 // If there is no GUI, or no need for batch screenshot, initialization commands are enough.
115 if (!gui && g4view.driver != "TOOLSSG_OFFSCREEN") return cmds;
116
117 // do not draw volumes in batch screenshot
118 if (g4view.driver != "TOOLSSG_OFFSCREEN") {
119 auto g4camera = g4display::getG4Camera(gopts);
120 auto g4light = g4display::getG4Light(gopts);
121 const double toDegrees = 180.0 / M_PI;
122 double thetaValue = gutilities::getG4Number(g4camera.theta) * toDegrees;
123 double phiValue = gutilities::getG4Number(g4camera.phi) * toDegrees;
124 double lightThetaValue = gutilities::getG4Number(g4light.theta) * toDegrees;
125 double lightPhiValue = gutilities::getG4Number(g4light.phi) * toDegrees;
126 if (lightThetaValue == 0.0 && lightPhiValue == 0.0) {
127 lightThetaValue = thetaValue;
128 lightPhiValue = phiValue;
129 }
130
131 cmds.emplace_back("/vis/drawVolume");
132 // Disable auto refresh and quieten vis messages whilst scene and trajectories are established.
133 cmds.emplace_back("/vis/viewer/set/autoRefresh false");
134 cmds.emplace_back("/vis/viewer/set/viewpointThetaPhi " + std::to_string(thetaValue) + " " + std::to_string(phiValue));
135 cmds.emplace_back("/vis/viewer/set/lightsThetaPhi " + std::to_string(lightThetaValue) + " " + std::to_string(lightPhiValue));
136 }
137
138 // GUI / batch screenshot mode: set up a minimal visualization scene with trajectories and hits.
139 cmds.emplace_back("/vis/scene/add/trajectories smooth");
140 cmds.emplace_back("/vis/modeling/trajectories/create/drawByCharge");
141 cmds.emplace_back("/vis/modeling/trajectories/drawByCharge-0/default/setDrawStepPts true");
142 cmds.emplace_back("/vis/modeling/trajectories/drawByCharge-0/default/setStepPtsSize 2");
143 // Draw optical photons in cyan so they are distinct from other neutral particles.
144 cmds.emplace_back("/vis/modeling/trajectories/create/drawByParticleID");
145 cmds.emplace_back("/vis/modeling/trajectories/drawByParticleID-0/set opticalphoton cyan");
146
147 cmds.emplace_back("/vis/scene/add/hits");
148 cmds.emplace_back("/vis/scene/endOfEventAction accumulate 10000");
149 cmds.push_back("/vis/viewer/set/background " + g4view.background);
150 cmds.push_back("/vis/viewer/set/numberOfCloudPoints " + std::to_string(g4view.cloudPoints));
151 G4SceneProperties g4SceneProperties(gopts);
152 const auto decorations = g4display::getG4Decorations(gopts);
153 for (const auto& command : g4SceneProperties.addSceneDecorations(gopts)) { cmds.emplace_back(command); }
154 for (const auto& command : g4SceneProperties.addSceneTexts(gopts)) { cmds.emplace_back(command); }
155 if (decorations.eventID) { cmds.emplace_back("/vis/scene/add/eventID"); }
156
157 // do not draw volumes in batch screenshot
158 if (g4view.driver != "TOOLSSG_OFFSCREEN") {
159 // Re-enable refresh and flush once configuration is complete.
160 cmds.emplace_back("/vis/viewer/set/autoRefresh true");
161 cmds.emplace_back("/vis/viewer/flush");
162 }
163
164
165 // cmds.emplace_back("/tracking/verbose 2");
166 return cmds;
167 }
168
169 // initialize G4MTRunManager
170 void run_manager_commands([[maybe_unused]] const std::shared_ptr<GOptions>& gopts,
171 const std::shared_ptr<GLogger>& log, const std::vector<std::string>& commands) {
172 auto* g4uim = G4UImanager::GetUIpointer();
173
174 // Apply commands sequentially so the UI manager sees the same order as a macro file.
175 for (const auto& cmd : commands) {
176 log->info(2, "Executing UIManager command: ", cmd);
177 g4uim->ApplyCommand(cmd);
178 }
179 }
180
182 new G4UnitDefinition("milligray", "milliGy", "Dose", gemc_units::milligray);
183 new G4UnitDefinition("microgray", "microGy", "Dose", gemc_units::microgray);
184 new G4UnitDefinition("nanogray", "nanoGy", "Dose", gemc_units::nanogray);
185 new G4UnitDefinition("picogray", "picoGy", "Dose", gemc_units::picogray);
186 }
187
188
189#include <unistd.h> // needed for get_pid
190
191 void start_random_engine(const std::shared_ptr<GOptions>& gopts, const std::shared_ptr<GLogger>& log) {
192 auto randomEngineName = gopts->getScalarString("randomEngine");
193 auto seed = gopts->getScalarInt("seed");
194
195 // If the user did not set a seed, derive one using several fast-changing sources.
196 // This helps reduce accidental seed reuse across runs.
197 if (seed == SEEDNOTSET) {
198 auto timed = time(NULL);
199 auto clockd = clock();
200 auto getpidi = getpid();
201 seed = (G4int)( timed - clockd - getpidi );
202 log->info(1, "Using random seed ", seed);
203 } else {
204 log->info(1, "User defined seed ", seed);
205 }
206
207
208 // The names come from the CLHEP library, can be found with
209 // grep ": public HepRandomEngine" *.h $CLHEP_BASE_DIR/include/CLHEP/Random/* | awk -Fclass '{print $2}' | awk -F: '{print $1}'
210 //
211 // Select the engine implementation based on the configured string.
212 if (randomEngineName == "DRand48Engine")
213 G4Random::setTheEngine(new CLHEP::DRand48Engine(seed));
214 else if (randomEngineName == "DualRand")
215 G4Random::setTheEngine(new CLHEP::DualRand(seed));
216 else if (randomEngineName == "Hurd160Engine")
217 G4Random::setTheEngine(new CLHEP::Hurd160Engine(seed));
218 else if (randomEngineName == "HepJamesRandom")
219 G4Random::setTheEngine(new CLHEP::HepJamesRandom(seed));
220 else if (randomEngineName == "MTwistEngine")
221 G4Random::setTheEngine(new CLHEP::MTwistEngine(seed));
222 else if (randomEngineName == "MixMaxRng")
223 G4Random::setTheEngine(new CLHEP::MixMaxRng(seed));
224 else if (randomEngineName == "RandEngine")
225 G4Random::setTheEngine(new CLHEP::RandEngine(seed));
226 else if (randomEngineName == "RanecuEngine")
227 G4Random::setTheEngine(new CLHEP::RanecuEngine(seed));
228 else if (randomEngineName == "Ranlux64Engine")
229 G4Random::setTheEngine(new CLHEP::Ranlux64Engine(seed));
230 else if (randomEngineName == "RanluxEngine")
231 G4Random::setTheEngine(new CLHEP::RanluxEngine(seed));
232 else if (randomEngineName == "RanshiEngine")
233 G4Random::setTheEngine(new CLHEP::RanshiEngine(seed));
234 else if (randomEngineName == "Hurd288Engine")
235 G4Random::setTheEngine(new CLHEP::Hurd288Engine(seed));
236 else if (randomEngineName == "TripleRand")
237 G4Random::setTheEngine(new CLHEP::TripleRand(seed));
238 else { log->error(EC__RANDOMENGINENOTFOUND, "Random engine >", randomEngineName, "< not found. Exiting."); }
239
240 // Apply the seed after selecting the engine so the engine instance is active.
241 log->info(0, "Starting random engine ", randomEngineName, " with seed ", seed);
242 G4Random::setTheSeed(seed);
243 }
244}
std::vector< std::string > addSceneTexts(const std::shared_ptr< GOptions > &gopts)
std::vector< std::string > addSceneDecorations(const std::shared_ptr< GOptions > &gopts)
Conventional constants used by GEMC utility helpers.
Utility helpers for runtime setup, command preparation, and random-engine initialization.
#define SEEDNOTSET
Sentinel value used to mark that the random seed has not been explicitly set.
#define EC__RANDOMENGINENOTFOUND
Error code used when the configured random engine name is not recognized.
void define_new_gemc_units()
std::vector< std::string > initial_commands(const std::shared_ptr< GOptions > &gopts, const std::shared_ptr< GLogger > &log, bool configure_visualization)
Build a list of Geant4 UI commands needed at startup.
void start_random_engine(const std::shared_ptr< GOptions > &gopts, const std::shared_ptr< GLogger > &log)
Select and start the random engine, then seed it.
std::vector< std::string > verbosity_commands(const std::shared_ptr< GOptions > &gopts, const std::shared_ptr< GLogger > &log)
Build a list of Geant4 UI commands that reduce verbosity across subsystems.
void run_manager_commands(const std::shared_ptr< GOptions > &gopts, const std::shared_ptr< GLogger > &log, const std::vector< std::string > &commands)
Execute a sequence of Geant4 UI commands through the UI manager.
int get_nthreads(const std::shared_ptr< GOptions > &gopts, const std::shared_ptr< GLogger > &log)
Determine the number of worker threads to use for the run.
G4Light getG4Light(const std::shared_ptr< GOptions > &gopts)
G4View getG4View(const std::shared_ptr< GOptions > &gopts)
G4Camera getG4Camera(const std::shared_ptr< GOptions > &gopts)
G4Decorations getG4Decorations(const std::shared_ptr< GOptions > &gopts)
constexpr G4double milligray
constexpr G4double nanogray
constexpr G4double microgray
constexpr G4double picogray
double getG4Number(const string &v, bool warnIfNotUnit=false)