gdetector
Loading...
Searching...
No Matches
gdetectorConstruction.cc
Go to the documentation of this file.
1// gdetectorConstruction
3#include "gdetector_options.h"
4
5// gemc
10#include "gFluxDigitization.h"
13
14// geant4
15#include "G4SDManager.hh"
16#include "G4RunManager.hh"
17#include "G4Threading.hh"
18#include "G4UserLimits.hh"
19#include "G4VVisManager.hh"
20
21namespace {
22class GVisManagerGuard : public G4VVisManager {
23public:
24 static void set(G4VVisManager* visManager) { SetConcreteInstance(visManager); }
25
26 void Draw(const G4Circle&, const G4Transform3D&) override {}
27 void Draw(const G4Polyhedron&, const G4Transform3D&) override {}
28 void Draw(const G4Polyline&, const G4Transform3D&) override {}
29 void Draw(const G4Polymarker&, const G4Transform3D&) override {}
30 void Draw(const G4Square&, const G4Transform3D&) override {}
31 void Draw(const G4Text&, const G4Transform3D&) override {}
32 void Draw2D(const G4Circle&, const G4Transform3D&) override {}
33 void Draw2D(const G4Polyhedron&, const G4Transform3D&) override {}
34 void Draw2D(const G4Polyline&, const G4Transform3D&) override {}
35 void Draw2D(const G4Polymarker&, const G4Transform3D&) override {}
36 void Draw2D(const G4Square&, const G4Transform3D&) override {}
37 void Draw2D(const G4Text&, const G4Transform3D&) override {}
38 void Draw(const G4VTrajectory&) override {}
39 void Draw(const G4VHit&) override {}
40 void Draw(const G4VDigi&) override {}
41 void Draw(const G4LogicalVolume&, const G4VisAttributes&, const G4Transform3D&) override {}
42 void Draw(const G4VPhysicalVolume&, const G4VisAttributes&, const G4Transform3D&) override {}
43 void Draw(const G4VSolid&, const G4VisAttributes&, const G4Transform3D&) override {}
44 void BeginDraw(const G4Transform3D&) override {}
45 void EndDraw() override {}
46 void BeginDraw2D(const G4Transform3D&) override {}
47 void EndDraw2D() override {}
48 void GeometryHasChanged() override {}
49 void DispatchToModel(const G4VTrajectory&) override {}
50 G4bool FilterTrajectory(const G4VTrajectory&) override { return true; }
51 G4bool FilterHit(const G4VHit&) override { return true; }
52 G4bool FilterDigi(const G4VDigi&) override { return true; }
53};
54}
55
56G4ThreadLocal GMagneto *GDetectorConstruction::gmagneto = nullptr;
57G4ThreadLocal std::map<std::string, GSensitiveDetector*>* GDetectorConstruction::tlSDMap = nullptr;
58
59GDetectorConstruction::GDetectorConstruction(std::shared_ptr<GOptions> gopts)
60 : GBase(gopts, GDETECTOR_LOGGER),
61 G4VUserDetectorConstruction(), // Geant4 base class.
62 gopt(gopts) {
63 // Map is populated after SDs exist, in the SD/field construction path.
64 digitization_routines_map = std::make_shared<gdynamicdigitization::dRoutinesMap>();
65}
66
67// Builds (or rebuilds) the GEMC world and then the Geant4 world.
70
71 // Delete old geometry objects if they exist.
72 // These shared_ptr resets guarantee we won't keep references to stale world objects.
73 gworld.reset();
74 g4world.reset();
75
76 // - if no systems are provided, we just launched gemc: create from options
77 // - otherwise, it's a geometry re-load. use existing systems.
78 if (gsystems.empty()) {
79 log->debug(NORMAL, FUNCTION_NAME, "creating world from options");
80 gworld = std::make_shared<GWorld>(gopt);
81 } else {
82 log->debug(NORMAL, FUNCTION_NAME, "creating world from a gsystem vector of size ", gsystems.size());
83 gworld = std::make_shared<GWorld>(gopt, cloneSystemDescriptors(gsystems));
84 }
85
86 // Build Geant4 world (solids, logical and physical volumes) based on the GEMC world.
87 g4world = std::make_shared<G4World>(gworld.get(), gopt);
88
89 auto nsdetectors = gworld->getSensitiveDetectorsList().size();
90
91 // tally with number :
92 log->info(0, "Tally summary: \n - ", gworld->get_number_of_volumes() - 1, " volumes\n - ",
93 g4world->number_of_volumes(), " geant4 built volumes\n - ",
94 nsdetectors, " sensitive detectors\n");
95
96
97 // Return the physical volume for the ROOT world volume.
99}
100
101// Installs sensitive detectors and EM fields for the constructed geometry.
103 auto sdManager = G4SDManager::GetSDMpointer();
104
106
107 // Deactivate all SDs this thread registered in prior geometry loads.
108 // G4SDManager retains SDs indefinitely across reloads; an active stale SD has
109 // Initialize() called at the start of every event even when no volume uses it,
110 // producing orphan hit collections whose names are absent from the digitization map.
111 // We deactivate them here and reactivate only those needed for the current geometry.
112 if (!tlSDMap) {
113 tlSDMap = new std::map<std::string, GSensitiveDetector*>();
114 }
115 for (auto& [name, sd] : *tlSDMap) {
116 sd->Activate(false);
117 }
118
119 // Local cache of sensitive detectors keyed by digitization name.
120 // Multiple volumes can share the same digitization name and therefore reuse one SD instance.
121 std::unordered_map<std::string, GSensitiveDetector *> sensitiveDetectorsMap;
122
123 // Loop over all systems and their volumes.
124 for (const auto &[systemName, gsystemPtr]: *gworld->getSystemsMap()) {
125 for (const auto &[volumeName, gvolumePtr]: gsystemPtr->getGVolumesMap()) {
126 auto const &digitizationName = gvolumePtr->getDigitization();
127 auto const &g4name = gvolumePtr->getG4Name();
128 auto *g4volume = g4world->getG4Volume(g4name)->getLogical();
129
130 // Ensure the Geant4 logical volume exists.
131 // Some GEMC volumes can be "copy-of" another volume; in that case, reuse the
132 // referenced Geant4 logical volume rather than failing.
133 if (g4volume == nullptr) {
134 std::string copyOf = gvolumePtr->getCopyOf();
135 if (copyOf != "" && copyOf != UNINITIALIZEDSTRINGQUANTITY) {
136 auto gsystem = gvolumePtr->getSystem();
137 auto volume_copy = gsystem + "/" + copyOf;
138 auto copyG4Volume = g4world->getG4Volume(volume_copy)->getLogical();
139 if (copyG4Volume != nullptr) { g4volume = copyG4Volume; } else {
141 " Logical volume copy <" + volume_copy + "> not found.");
142 }
143 }
144 }
145 if (g4volume == nullptr) {
146 log->error(ERR_GVOLUMENOTFOUND, FUNCTION_NAME, " Logical volume <" + g4name + "> not found.");
147 }
148
149 // Skip volumes with no digitization.
150 if (digitizationName != "" && digitizationName != UNINITIALIZEDSTRINGQUANTITY) {
151 // Obtain (or create) the sensitive detector for this digitization name.
152 // We reuse an existing SD object already in G4SDManager rather than creating a
153 // new one because AddNewDetector() keeps the OLD object on duplicate names (DET1010).
154 // If we created a new SD, G4SDManager would call Initialize() on the stale object
155 // while SetSensitiveDetector() pointed the logical volume to the new one — leaving
156 // the new SD's gHitsCollection uninitialized when ProcessHits() is called.
157 if (sensitiveDetectorsMap.find(digitizationName) == sensitiveDetectorsMap.end()) {
158 // Reuse a previously registered SD for this name if one exists on this thread.
159 // AddNewDetector() silently keeps the OLD object on duplicate names (DET1010);
160 // reusing the same pointer avoids that and ensures G4SDManager's Initialize()
161 // fires on the same object that SetSensitiveDetector() wires to the volumes.
162 auto tlIt = tlSDMap->find(digitizationName);
163 if (tlIt != tlSDMap->end()) {
164 log->info(2, "Reusing existing sensitive detector <", digitizationName, "> for volume <", g4name, ">");
165 tlIt->second->resetTouchableMap();
166 tlIt->second->Activate(true);
167 sensitiveDetectorsMap[digitizationName] = tlIt->second;
168 } else {
169 log->info(2, "Creating new sensitive detector <", digitizationName, "> for volume <", g4name, ">");
170 auto* newSD = new GSensitiveDetector(digitizationName, gopt);
171 sdManager->AddNewDetector(newSD);
172 sensitiveDetectorsMap[digitizationName] = newSD;
173 (*tlSDMap)[digitizationName] = newSD;
174 }
175 } else {
176 log->info(2, "Sensitive detector <", digitizationName,
177 "> is already created and available for volume <", g4name, ">");
178 }
179
180 // Register the volume touchable with the sensitive detector.
181 // The touchable encodes identity and dimension metadata needed by digitization.
182 const auto &vdimensions = gvolumePtr->getDetectorDimensions();
183 const auto &identity = gvolumePtr->getGIdentity();
184 const auto &mass = g4volume->GetMass();
185 auto this_gtouchable = std::make_shared<
186 GTouchable>(gopt, digitizationName, identity, vdimensions, mass);
187 sensitiveDetectorsMap[digitizationName]->registerGVolumeTouchable(g4name, this_gtouchable);
188
189 // Attach the SD to the logical volume (no AddNewDetector call needed for reused SDs).
190 g4volume->SetSensitiveDetector(sensitiveDetectorsMap[digitizationName]);
191
192 // auto maxStep =
193
194 //g4volume->SetUserLimits(new G4UserLimits(0.1*mm, 0.1*mm));
195
196 log->info(2, "Logical Volume <" + g4name + "> has been successfully assigned to SD.",
197 sensitiveDetectorsMap[digitizationName]);
198 }
199
200 // Process electromagnetic fields.
201 // If a volume declares an EM field, ensure the field container exists and install
202 // a per-volume field manager configured by the named field map.
203 const auto &field_name = gvolumePtr->getEMField();
204 if (field_name != "" && field_name != UNINITIALIZEDSTRINGQUANTITY) {
205 if (gmagneto == nullptr) { gmagneto = new GMagneto(gopt); }
206 log->info(2, "Volume <", volumeName, "> has field: <", field_name,
207 ">. Looking into field map definitions.");
208 log->info(2, "Setting field manager for volume <", g4name, "> with field <", field_name, ">");
209 g4world->setFieldManagerForVolume(g4name, gmagneto->getFieldMgr(field_name).get(), true);
210 }
211 }
212 }
213
214 // Load digitization plugins only when geometry has changed and only on the master thread.
215 // digiplugins_need_reload is set true by reload_geometry() and prepare_geometry_for_run()
216 // so that routine BeamOn re-initializations (which also call ConstructSDandField() on the
217 // master) do not clear the shared map while worker threads may be concurrently reading it.
218 if (G4Threading::IsMasterThread() && digiplugins_need_reload) {
219 loadDigitizationPlugins();
220 digiplugins_need_reload = false;
221 }
222
223 // Bind each digitization routine to its corresponding sensitive detector.
224 const auto sdetectors = gworld->getSensitiveDetectorsList();
225 for (auto &sdname: sdetectors) {
226 auto digitization_routine = digitization_routines_map->at(sdname);
227 double maxStep = digitization_routine->readoutSpecs->getMaxStep();
228
229 sensitiveDetectorsMap[sdname]->assign_digi_routine(digitization_routine);
230 log->info(1, "Digitization routine <" + sdname + "> has been successfully assigned to SD.",
231 sensitiveDetectorsMap[sdname]);
232
233 // Loop over all systems and their volumes.
234 // and assign max step to the corresponding logical volume
235 for (const auto &[systemName, gsystemPtr]: *gworld->getSystemsMap()) {
236 for (const auto &[volumeName, gvolumePtr]: gsystemPtr->getGVolumesMap()) {
237 auto const &digitizationName = gvolumePtr->getDigitization();
238 if (digitizationName == sdname) {
239 auto const &g4name = gvolumePtr->getG4Name();
240 auto *g4volume = g4world->getG4Volume(g4name)->getLogical();
241
242 // g4volume->SetUserLimits(new G4UserLimits(maxStep, maxStep)); // this will also kill track cause
243 // the second argument is max track length
244 g4volume->SetUserLimits(new G4UserLimits(maxStep));
245
246 log->info(1, "Setting G4UserLimits for volume <", g4name, "> with maxStep <", maxStep, ">");
247 }
248 }
249 }
250 }
251}
252
253// Loads (or dynamically resolves) the digitization routine for each sensitive detector.
254void GDetectorConstruction::loadDigitizationPlugins() {
255 // Clear stale entries so a reloaded geometry always gets fresh routines.
256 // emplace() would silently skip existing keys, causing hits not to be recorded
257 // when the geometry is reloaded with the same SD names.
258 digitization_routines_map->clear();
259
260 // Resolve the variation each routine uses to load constants / translation tables.
261 // By default a routine follows the variation of the gsystem it belongs to; the
262 // digitization_variation option, when set, overrides that for every routine.
263 const std::string digiVariationOverride = gopt->getScalarString("digitization_variation");
264 const bool overrideVariation = (digiVariationOverride != UNINITIALIZEDSTRINGQUANTITY);
265
266 // Map each digitization (sensitive-detector) name to its system's variation.
267 std::map<std::string, std::string> systemVariationFor;
268 for (const auto& [systemName, gsystemPtr] : *gworld->getSystemsMap()) {
269 for (const auto& [volumeName, gvolumePtr] : gsystemPtr->getGVolumesMap()) {
270 const auto& digiName = gvolumePtr->getDigitization();
271 if (digiName != "" && digiName != UNINITIALIZEDSTRINGQUANTITY) {
272 systemVariationFor.emplace(digiName, gsystemPtr->getVariation());
273 }
274 }
275 }
276
277 const auto sdetectors = gworld->getSensitiveDetectorsList();
278
279 for (auto &sdname: sdetectors) {
280 if (sdname == FLUXNAME) {
281 log->info(1, "Loading flux digitization plugin for routine <" + sdname + ">");
282 digitization_routines_map->emplace(sdname, std::make_shared<GFluxDigitization>(gopt));
283 } else if (sdname == GPHOTON_DETECTORNAME) {
284 log->info(1, "Loading gPhotonDetector digitization plugin for routine <" + sdname + ">");
285 digitization_routines_map->emplace(sdname, std::make_shared<GPhotonDetectorDigitization>(gopt));
286 } else if (sdname == COUNTERNAME) {
287 log->info(1, "Loading particle counter digitization plugin for routine <" + sdname + ">");
288 digitization_routines_map->emplace(sdname, std::make_shared<GParticleCounterDigitization>(gopt));
289 } else if (sdname == DOSIMETERNAME) {
290 log->info(1, "Loading dosimeter digitization plugin for routine <" + sdname + ">");
291 digitization_routines_map->emplace(sdname, std::make_shared<GDosimeterDigitization>(gopt));
292 } else {
293 // if it's not in the map already, add it
294 log->info(0, "Loading new digitization plugin for routine <" + sdname + ">");
295 digitization_routines_map->emplace(sdname, gdynamicdigitization::load_dynamicRoutine(sdname, gopt));
296 }
297
298 // Ensure each routine uses the correct logger and is configured for readout.
299 digitization_routines_map->at(sdname)->set_loggers(gopt);
300
301 // Resolve and store the variation used when loading this routine's constants/TT:
302 // the digitization_variation option when set, otherwise the routine's gsystem variation.
303 std::string variation = "default";
304 if (const auto it = systemVariationFor.find(sdname); it != systemVariationFor.end()) {
305 variation = it->second;
306 }
307 if (overrideVariation) { variation = digiVariationOverride; }
308 digitization_routines_map->at(sdname)->setDigitizationVariation(variation);
309
310 if (digitization_routines_map->at(sdname)->defineReadoutSpecs()) {
311 log->info(1, "Digitization routine <" + sdname + "> has been successfully defined.");
312 } else { log->error(ERR_DEFINESPECFAIL, "defineReadoutSpecs failure for <" + sdname + ">"); }
313 }
314}
315
316
317SystemList GDetectorConstruction::cloneSystemDescriptors(const SystemList& systems) const {
318 SystemList descriptors;
319 descriptors.reserve(systems.size());
320
321 for (const auto& system: systems) {
322 if (system != nullptr) {
323 descriptors.emplace_back(system->descriptorClone(gopt));
324 }
325 }
326
327 return descriptors;
328}
329
330
332 // Geometry is changing: ensure loadDigitizationPlugins() runs on the next ConstructSDandField().
333 digiplugins_need_reload = true;
334
335 // it could be empty for tests
336 if (!sl.empty()) {
337 gsystems = cloneSystemDescriptors(sl);
338 }
339
340 // Reconstruct the master geometry immediately so GUI pages can inspect the
341 // new world without starting worker threads during a setup-tab reload.
342 auto rm = G4RunManager::GetRunManager();
343
344 if (rm) {
345 // Null out the vis manager while we clean and rebuild the geometry stores.
346 // G4*Store::Clean() would otherwise leave the ToolsSG scene graph with
347 // dangling references to deleted Geant4 objects, causing a crash on the
348 // next visualization flush.
349 auto* visManager = G4VVisManager::GetConcreteInstance();
350 GVisManagerGuard::set(nullptr);
351 rm->DefineWorldVolume(Construct());
352 GVisManagerGuard::set(visManager);
354 } else { log->error(1, "GDetectorConstruction::reload_geometry", "Geant4 Run manager not found."); }
355}
356
358 auto rm = G4RunManager::GetRunManager();
359
360 if (rm) {
361 auto* visManager = G4VVisManager::GetConcreteInstance();
362 GVisManagerGuard::set(nullptr);
363 // Geometry is being rebuilt: ensure loadDigitizationPlugins() runs inside Initialize().
364 digiplugins_need_reload = true;
365 rm->ReinitializeGeometry(true, true);
366 rm->GeometryHasBeenModified();
367 // Optical processes such as G4Cerenkov cache material-property tables by
368 // material index. A setup-tab reload can introduce a different variation's
369 // optical material, so force physics tables to rebuild after the new
370 // geometry/material set has been installed.
371 rm->PhysicsHasBeenModified();
372 rm->Initialize();
373 GVisManagerGuard::set(visManager);
374 } else { log->error(1, "GDetectorConstruction::prepare_geometry_for_run", "Geant4 Run manager not found."); }
375}
G4VPhysicalVolume * getPhysical() const noexcept
G4LogicalVolume * getLogical() const noexcept
std::size_t number_of_volumes() const noexcept
void setFieldManagerForVolume(const std::string &volumeName, G4FieldManager *fm, bool forceToAllDaughters)
const G4Volume * getG4Volume(const std::string &volumeName) const
std::shared_ptr< GLogger > log
GDetectorConstruction(std::shared_ptr< GOptions > gopts)
Constructs a detector builder configured by the provided options.
void reload_geometry(SystemList sl)
Reloads the geometry using a new list of GSystem objects.
G4VPhysicalVolume * Construct() override
Geant4 geometry construction hook.
void ConstructSDandField() override
Geant4 SD/field construction hook.
void prepare_geometry_for_run()
Reinitializes geometry through Geant4 before running events.
void debug(debug_type type, Args &&... args) const
void info(int level, Args &&... args) const
void error(int exit_code, Args &&... args) const
std::shared_ptr< G4FieldManager > getFieldMgr(std::string name)
std::string getScalarString(const std::string &tag) const
int get_number_of_volumes() const
std::vector< std::string > getSensitiveDetectorsList()
SystemMap * getSystemsMap() const
Defines the GDetectorConstruction class, the Geant4 detector-construction entry point for the gdetect...
Declares the gdetector module option aggregation entry point.
constexpr const char * GDETECTOR_LOGGER
Logger name used by the gdetector module.
#define FUNCTION_NAME
NORMAL
#define ERR_GVOLUMENOTFOUND
#define ROOTWORLDGVOLUMENAME
std::vector< SystemPtr > SystemList
#define UNINITIALIZEDSTRINGQUANTITY
std::shared_ptr< GDynamicDigitization > load_dynamicRoutine(const std::string &plugin_name, const std::shared_ptr< GOptions > &gopts)