g4system
Loading...
Searching...
No Matches
g4world.cc
Go to the documentation of this file.
1// g4world.cc : implementation of the Geant4 world builder and material initialization.
12// gemc
13#include "g4world.h"
14#include "gfactory.h"
15
16// g4system
17#include "g4system_options.h"
19#include "gsystemConventions.h"
22
23// geant4
24#include "G4MaterialTable.hh"
25#include "G4Material.hh"
26#include "G4Element.hh"
27#include "G4Isotope.hh"
28
29// c++
30#include <vector>
31
32G4World::G4World(const GWorld* gworld, const std::shared_ptr<GOptions>& gopts)
33 : GBase(gopts, G4SYSTEM_LOGGER) {
34 auto gsystemMap = gworld->getSystemsMap();
35
36 // Phase 1: create and initialize a Geant4 object factory for each system.
37 // The factory provides solid/logical/physical creation for volumes in that system.
38 createG4SystemFactory(gopts,
39 gsystemMap,
40 gopts->getScalarString("useBackupMaterial"),
41 gopts->getScalarInt("check_overlaps")
42 );
43
44 // Phase 2: build all materials across systems, resolving dependencies iteratively.
45 buildMaterials(gsystemMap);
46
47 // Phase 3: ensure common isotopes/elements/materials exist (used by typical configurations).
48 buildDefaultMaterialsElementsAndIsotopes();
49
50 // Phase 4: build volumes. Some volumes depend on mothers that may not exist yet,
51 // so we iterate until the remaining list becomes empty or the dependency resolution stalls.
52 std::vector<GVolume*> thisIterationRemainingVolumes;
53 unsigned long allRemainingVolumes = 0;
54
55 do {
56 thisIterationRemainingVolumes.clear();
57
58 // Loop over all systems and attempt to build all volumes in each system.
59 for (auto& [systemName, gsystem] : *gsystemMap) {
60 std::string g4Factory = g4FactoryNameFromSystemFactory(gsystem->getFactoryName());
61 auto objectsFactory = get_factory(g4Factory);
62
63 for (auto& [volumeName, gvolumePtr] : gsystem->getGVolumesMap()) {
64 auto* gvolume = gvolumePtr.get();
65
66 // Try to build; if dependencies are missing, remember it for the next iteration.
67 if (!build_g4volume(gvolume, objectsFactory)) {
68 // Only track volumes that are meant to exist; nonexistent volumes are skipped quietly.
69 if (gvolume->getExistence()) {
70 log->warning(" >> adding volumeName <", volumeName, "> to the list of remaining volumes");
71 thisIterationRemainingVolumes.push_back(gvolume);
72 }
73 }
74 }
75
76 // Diagnostic listing of the volumes that could not be built due to missing mothers.
77 if (!thisIterationRemainingVolumes.empty()) {
78 log->info(2, "G4World: ", systemName, " : ",
79 thisIterationRemainingVolumes.size(),
80 " remaining motherless g4volumes to be built:");
81 for (auto* gvolumeLeft : thisIterationRemainingVolumes) {
82 log->info(2, "G4World: ", gvolumeLeft->getName(),
83 " with mother <", gvolumeLeft->getG4MotherName(), "> ");
84 }
85 }
86 }
87
88 // Dependency-stall detection:
89 // If the number of remaining volumes does not decrease across iterations, dependencies are not solvable.
90 if (allRemainingVolumes != 0 && !thisIterationRemainingVolumes.empty()) {
91 if (allRemainingVolumes >= thisIterationRemainingVolumes.size()) {
92 for (auto* gvolumeLeft : thisIterationRemainingVolumes) {
93 log->warning(" >> ", gvolumeLeft->getName(),
94 " with mother <", gvolumeLeft->getG4MotherName(), "> not built");
95 }
97 "dependencies are not being resolved: their number should diminish. "
98 "Above are the outstanding gvolumes");
99 }
100 }
101 else { allRemainingVolumes = thisIterationRemainingVolumes.size(); }
102 }
103 while (!thisIterationRemainingVolumes.empty());
104
105 // Optional diagnostic output: list known materials from the Geant4 NIST manager.
106 if (gopts->getSwitch("showPredefinedMaterials")) { G4NistManager::Instance()->ListMaterials("all"); }
107
108 // Optional diagnostic output: print materials used in the simulation.
109 if (gopts->getSwitch("printSystemsMaterials")) {
110 auto matTable = (G4MaterialTable*)G4Material::GetMaterialTable();
111 for (auto thisMat : *matTable) {
112 log->info(0, 2, "G4World: GEMC Material: <", thisMat->GetName(), ">, density: ",
113 thisMat->GetDensity() / (CLHEP::g / CLHEP::cm3), "g/cm3");
114
115 // Print each component; negative/zero values mean "fractional mass", positive means "number of atoms".
116 for (auto& [material, component] : thisMat->GetMatComponents()) {
117 if (component > 0.0) { log->info(0, "element", material->GetName(), "number of atoms: ", component); }
118 else { log->info(0, "element", material->GetName(), "fractional mass: ", component); }
119 }
120 }
121 }
122}
123
124/*──────────────────────── look-ups ──────────────────────────*/
125
126const G4Volume* G4World::getG4Volume(const std::string& volumeName) const {
127 auto it = g4volumesMap.find(volumeName);
128 return (it != g4volumesMap.end()) ? it->second : nullptr;
129}
130
131void G4World::setFieldManagerForVolume(const std::string& volumeName,
132 G4FieldManager* fm,
133 bool forceToAllDaughters) {
134 auto it = g4volumesMap.find(volumeName);
135 if (it != g4volumesMap.end()) it->second->setFieldManager(fm, forceToAllDaughters);
136}
137
138// ---- g4FactoryNameFromSystemFactory -----------------------------------------------------------
139std::string G4World::g4FactoryNameFromSystemFactory(const std::string& factory) const {
140 // Map GEMC system factory labels to g4system object factory labels.
141 if (factory == GSYSTEMASCIIFACTORYLABEL ||
142 factory == GSYSTEMSQLITETFACTORYLABEL ||
143 factory == GSYSTEMMYSQLTFACTORYLABEL) { return G4SYSTEMNATFACTORY; }
144 else if (factory == GSYSTEMCADTFACTORYLABEL) { return G4SYSTEMCADFACTORY; }
145 else {
147 "gsystemFactory factory <", factory, "> is not mapped to any G4SystemFactory");
148 }
149}
150
151bool G4World::createG4Material(const std::shared_ptr<GMaterial>& gmaterial) {
152 auto NISTman = G4NistManager::Instance(); // material G4 Manager
153 auto materialName = gmaterial->getName();
154
155 // Only build the material if it is not already available in Geant4.
156 auto g4material = NISTman->FindMaterial(materialName);
157 if (g4material != nullptr) {
158 log->info(2, "Material <", materialName, "> already exists in G4NistManager");
159 return true;
160 }
161
162 auto components = gmaterial->getComponents();
163 auto amounts = gmaterial->getAmounts();
164 bool isChemical = gmaterial->isChemicalFormula();
165
166 // Scan material components:
167 // return false if any component does not exist yet (caller will retry later).
168 for (auto& componentName : components) {
169 if (isChemical) {
170 if (NISTman->FindOrBuildElement(componentName) == nullptr) {
171 log->info(2, "Element <", componentName, ">, needed by ", materialName, ", not found yet");
172 return false;
173 }
174 else { log->info(2, "Element <", componentName, "> needed by ", materialName, " now found"); }
175 }
176 else {
177 if (NISTman->FindOrBuildMaterial(componentName) == nullptr) {
178 log->info(2, "Material <", componentName, ">, needed by ", materialName, ", not found yet");
179 return false;
180 }
181 else { log->info(2, "Material <", componentName, "> needed by ", materialName, " now found"); }
182 }
183 }
184
185 // Build the composed material from its components.
186 auto density = gmaterial->getDensity();
187 g4materialsMap[materialName] = new G4Material(materialName, density * CLHEP::g / CLHEP::cm3,
188 static_cast<G4int>(components.size()));
189
190 if (isChemical) {
191 log->info(2, "Building material <", materialName, "> with components:");
192 for (size_t i = 0; i < components.size(); i++) {
193 log->info(2, "element <", components[i], "> with amount: ", amounts[i]);
194 }
195
196 for (size_t i = 0; i < components.size(); i++) {
197 auto element = NISTman->FindOrBuildElement(components[i]);
198 g4materialsMap[materialName]->AddElement(element, static_cast<G4int>(amounts[i]));
199 }
200 }
201 else {
202 log->info(2, "Building material <", materialName, "> with components:");
203 for (size_t i = 0; i < components.size(); i++) {
204 log->info(2, "material <", components[i], "> with fractional mass: ", amounts[i]);
205 }
206
207 for (size_t i = 0; i < components.size(); i++) {
208 auto material = NISTman->FindOrBuildMaterial(components[i]);
209 g4materialsMap[materialName]->AddMaterial(material, amounts[i]);
210 }
211 }
212
213 return true;
214}
215
216void G4World::buildDefaultMaterialsElementsAndIsotopes() {
217 // Create a small set of commonly-used isotopes/elements/materials if they are missing.
218 // These are defined using Geant4 primitives and then registered in the local map for reference.
219 int Z, N;
220 double a, d, T;
221
222 // ---- Hydrogen
223
224 // Hydrogen gas material definition (Hydrogen element + state/gas parameters).
225 if (G4NistManager::Instance()->FindMaterial(HGAS_MATERIAL) == nullptr) {
226 Z = 1;
227 a = 1.01 * CLHEP::g / CLHEP::mole;
228 d = 0.00275 * CLHEP::g / CLHEP::cm3;
229 T = 50.0 * CLHEP::kelvin;
230 auto Hydrogen = new G4Element(HYDROGEN_ELEMENT, HYDROGEN_ELEMENT, Z, a);
231 g4materialsMap[HGAS_MATERIAL] = new G4Material(HGAS_MATERIAL,
232 d,
233 1,
234 kStateGas,
235 T);
236 g4materialsMap[HGAS_MATERIAL]->AddElement(Hydrogen, 1);
237 }
238 log->info(2, "G4World: Hydrogen gas material <", HGAS_MATERIAL, "> created with density <", d, ">");
239
240 // ---- Deuterium
241
242 // Deuteron isotope and Deuterium element definition.
243 if (G4NistManager::Instance()->FindOrBuildElement(DEUTERIUM_ELEMENT) == nullptr) {
244 Z = 1;
245 N = 2;
246 a = 2.0141018 * CLHEP::g / CLHEP::mole;
247 auto Deuteron = new G4Isotope(DEUTERON_ISOTOPE, Z, N, a);
248
249 // Deuterium element: isotope composition is explicitly set to the Deuteron isotope.
250 Deuterium = new G4Element(DEUTERIUM_ELEMENT, DEUTERIUM_ELEMENT, 1);
251 Deuterium->AddIsotope(Deuteron, 1);
252 }
253 log->info(2, "G4World: Deuterium element <", DEUTERIUM_ELEMENT, "> created with density <", d, ">");
254
255 // Deuterium gas material.
256 if (G4NistManager::Instance()->FindMaterial(DEUTERIUMGAS_MATERIAL) == nullptr) {
257 d = 0.000452 * CLHEP::g / CLHEP::cm3;
258 T = 294.25 * CLHEP::kelvin;
259 g4materialsMap[DEUTERIUMGAS_MATERIAL] = new G4Material(DEUTERIUMGAS_MATERIAL,
260 d,
261 1,
262 kStateGas,
263 T);
264 g4materialsMap[DEUTERIUMGAS_MATERIAL]->AddElement(Deuterium, 1);
265 }
266 log->info(2, "G4World: Deuterium gas material <", DEUTERIUMGAS_MATERIAL, "> created with density <", d, ">");
267
268 // Liquid Deuterium material.
269 if (G4NistManager::Instance()->FindMaterial(LD2_MATERIAL) == nullptr) {
270 d = 0.169 * CLHEP::g / CLHEP::cm3;
271 T = 22.0 * CLHEP::kelvin;
272 g4materialsMap[LD2_MATERIAL] = new G4Material(LD2_MATERIAL,
273 d,
274 1,
275 kStateLiquid,
276 T);
277 g4materialsMap[LD2_MATERIAL]->AddElement(Deuterium, 2);
278 }
279 log->info(2, "G4World: Liquid Deuterium material <", LD2_MATERIAL, "> created with density <", d, ">");
280
281 // Ammonia (ND3) material definition.
282 if (G4NistManager::Instance()->FindMaterial(ND3_MATERIAL) == nullptr) {
283 Z = 7;
284 a = 14.01 * CLHEP::g / CLHEP::mole;
285 d = 1.007 * CLHEP::g / CLHEP::cm3;
286 T = 1.0 * CLHEP::kelvin;
287 auto Nitrogen = new G4Element(NITRO_ELEMENT, NITRO_ELEMENT, Z, a);
288 g4materialsMap[ND3_MATERIAL] = new G4Material(ND3_MATERIAL,
289 d,
290 2,
291 kStateLiquid,
292 T);
293 g4materialsMap[ND3_MATERIAL]->AddElement(Nitrogen, 1);
294 g4materialsMap[ND3_MATERIAL]->AddElement(Deuterium, 3);
295 }
296 log->info(2, "G4World: Ammonia material <", ND3_MATERIAL, "> created with density <", d, ">");
297
298 // ---- Helium 3
299
300 // Helion isotope and Helium3 element definition.
301 if (G4NistManager::Instance()->FindOrBuildElement(HELIUM3_ELEMENT) == nullptr) {
302 Z = 2;
303 N = 3;
304 a = 3.0160293 * CLHEP::g / CLHEP::mole;
305 auto Helion = new G4Isotope(HELION_ISOTOPE, Z, N, a);
306
307 // Helium-3 element: isotope composition is explicitly set to the Helion isotope.
308 Helium3 = new G4Element(HELIUM3_ELEMENT, HELIUM3_ELEMENT, 1);
309 Helium3->AddIsotope(Helion, 1);
310 }
311 log->info(2, "G4World: Helium 3 element <", HELIUM3_ELEMENT, "> created with density <", d, ">");
312
313 // Helium-3 gas material definition.
314 if (G4NistManager::Instance()->FindMaterial(HELIUM3GAS_MATERIAL) == nullptr) {
315 // Density at 21.1°C (70°F): 0.1650 kg/m3.
316 d = 0.1650 * CLHEP::mg / CLHEP::cm3;
317 T = 294.25 * CLHEP::kelvin;
318 g4materialsMap[HELIUM3GAS_MATERIAL] = new G4Material(HELIUM3GAS_MATERIAL,
319 d,
320 1,
321 kStateGas,
322 T);
323 g4materialsMap[HELIUM3GAS_MATERIAL]->AddElement(Helium3, 1);
324 }
325 log->info(2, "G4World: Helium 3 gas material <", HELIUM3GAS_MATERIAL, "> created with density <", d, ">");
326
327 // ---- Tritium
328
329 // Triton isotope and Tritium element definition.
330 if (G4NistManager::Instance()->FindOrBuildElement(TRITIUM_ELEMENT) == nullptr) {
331 Z = 1;
332 N = 3;
333 a = 3.0160492 * CLHEP::g / CLHEP::mole;
334 auto Triton = new G4Isotope(TRITON_ISOTOPE, Z, N, a);
335
336 Tritium = new G4Element(TRITIUM_ELEMENT, TRITIUM_ELEMENT, 1);
337 Tritium->AddIsotope(Triton, 1);
338 }
339 log->info(2, "G4World: Tritium element <", TRITIUM_ELEMENT, "> created with density <", d, ">");
340
341 // Tritium gas material definition.
342 if (G4NistManager::Instance()->FindMaterial(TRITIUMGAS_MATERIAL) == nullptr) {
343 d = 0.0034 * CLHEP::g / CLHEP::cm3;
344 T = 40.0 * CLHEP::kelvin;
345 g4materialsMap[TRITIUMGAS_MATERIAL] = new G4Material(TRITIUMGAS_MATERIAL,
346 d,
347 1,
348 kStateGas, T);
349 g4materialsMap[TRITIUMGAS_MATERIAL]->AddElement(Tritium, 1);
350 }
351 log->info(2, "G4World: Tritium gas material <", TRITIUMGAS_MATERIAL, "> created with density <", d, ">");
352}
353
354void G4World::createG4SystemFactory(const std::shared_ptr<GOptions>& gopts,
355 SystemMap* gsystemsMap,
356 const std::string& backup_material,
357 int check_overlaps) {
358 // Instantiate a manager used to register and create factories.
359 GManager manager(gopts);
360
361 // Creating the native factory no matter what (it is the default for ASCII/SQLite/MySQL systems).
362 log->info(2, "G4World: registering default factory <", G4SYSTEMNATFACTORY, ">");
363 manager.RegisterObjectFactory<G4NativeSystemFactory>(G4SYSTEMNATFACTORY, gopts);
364
365 // Register factories based on the system factory label, then create/initialize them lazily.
366 for (auto& [gsystemName, gsystem] : *gsystemsMap) {
367 std::string factory = gsystem->getFactoryName();
368 std::string g4Factory = g4FactoryNameFromSystemFactory(factory);
369
370 log->info(2, "G4World: creating factory <", g4Factory, "> to for system <", gsystemName, ">");
371
372 // Register needed factory types:
373 // this will always be false for the default native label because it was registered above.
374 if (factory == GSYSTEMASCIIFACTORYLABEL || factory == GSYSTEMSQLITETFACTORYLABEL ||
375 factory == GSYSTEMMYSQLTFACTORYLABEL) {
376 if (g4systemFactory.find(g4Factory) == g4systemFactory.end()) {
377 manager.RegisterObjectFactory<G4NativeSystemFactory>(g4Factory, gopts);
378 }
379 }
380 else if (factory == GSYSTEMCADTFACTORYLABEL) {
381 if (g4systemFactory.find(GSYSTEMCADTFACTORYLABEL) == g4systemFactory.end()) {
382 manager.RegisterObjectFactory<G4CadSystemFactory>(g4Factory, gopts);
383 }
384 }
385
386 // Create and initialize the concrete factory instance once per label.
387 if (g4systemFactory.find(g4Factory) == g4systemFactory.end()) {
388 g4systemFactory[g4Factory] = manager.CreateObject<G4ObjectsFactory>(g4Factory);
389 g4systemFactory[g4Factory]->initialize_context(check_overlaps, backup_material);
390 }
391 }
392}
393
394void G4World::buildMaterials(SystemMap* system_map) {
395 // Build materials across all systems. Some materials may depend on other materials/elements,
396 // so we iterate until all dependencies are resolved or the resolution stalls.
397 std::vector<GMaterial*> thisIterationRemainingMaterials;
398 unsigned long allRemainingMaterials = 0;
399 do {
400 thisIterationRemainingMaterials.clear();
401
402 for (const auto& [systemName, system] : *system_map) {
403 // Loop over the material map in each system and attempt to build each material.
404 for (const auto& [gmaterialName, gmaterialPtr] : system->getGMaterialMap()) {
405 if (createG4Material(gmaterialPtr) == false) {
406 thisIterationRemainingMaterials.push_back(gmaterialPtr.get());
407 }
408 }
409 }
410
411 // Dependency-stall detection for material building.
412 if (allRemainingMaterials != 0 && !thisIterationRemainingMaterials.empty()) {
413 if (allRemainingMaterials >= thisIterationRemainingMaterials.size()) {
414 for (auto& gmaterialLeft : thisIterationRemainingMaterials) { log->warning(gmaterialLeft->getName()); }
416 "Dependencies are not being resolved: their number should diminish. Above are the Outstanding gmaterials");
417 }
418 }
419 else { allRemainingMaterials = thisIterationRemainingMaterials.size(); }
420 }
421 while (!thisIterationRemainingMaterials.empty());
422}
423
424bool G4World::build_g4volume(const GVolume* s, G4ObjectsFactory* objectsFactory) {
425 log->info(2, "G4World: using factory <", objectsFactory->className(),
426 "> to build g4volume <", s->getG4Name(), ">");
427
428 return objectsFactory->build_g4volume(s, &g4volumesMap);
429}
Factory that converts CAD files (PLY / STL) into Geant4 tessellated solids via CADMesh.
Builds a tessellated solid from CAD files using CADMesh.
Implements solid creation for Geant4 CSG primitives and validates constructor parameter counts.
Base class orchestrating the conversion of a GVolume into a Geant4 representation.
bool build_g4volume(const GVolume *s, std::unordered_map< std::string, G4Volume * > *g4s)
Build (or retrieve) solid, logical, and physical volumes for a given GVolume.
virtual std::string_view className() const =0
Short, human-readable factory name for logging.
Convenience container holding a Geant4 solid, logical, and physical volume.
Definition g4volume.h:50
G4World(const GWorld *gworld, const std::shared_ptr< GOptions > &gopts)
Construct and build the Geant4 world from a GEMC world.
Definition g4world.cc:32
void setFieldManagerForVolume(const std::string &volumeName, G4FieldManager *fm, bool forceToAllDaughters)
Attach a G4FieldManager to the logical volume of a named volume.
Definition g4world.cc:131
G4ObjectsFactory * get_factory(const std::string &factoryName)
Retrieve a registered factory by name.
Definition g4world.h:112
const G4Volume * getG4Volume(const std::string &volumeName) const
Return the G4Volume wrapper for a volume name.
Definition g4world.cc:126
std::shared_ptr< GLogger > log
void warning(Args &&... args) const
void info(int level, Args &&... args) const
void error(int exit_code, Args &&... args) const
std::string getG4Name() const
SystemMap * getSystemsMap() const
Factory that builds Geant4 native primitive solids (G4Box, G4Cons, G4Trap, ...) from GEMC GVolume rec...
Conventions, labels, and error codes used by the g4system geometry/material layer.
#define ERR_G4SYSTEMFACTORYNOTFOUND
A required Geant4 system factory was not found/mapped.
#define HELION_ISOTOPE
#define ERR_G4DEPENDENCIESNOTSOLVED
Geometry/material dependencies could not be resolved.
#define DEUTERIUM_ELEMENT
#define G4SYSTEMNATFACTORY
#define LD2_MATERIAL
#define DEUTERIUMGAS_MATERIAL
#define NITRO_ELEMENT
#define TRITON_ISOTOPE
#define TRITIUM_ELEMENT
#define HGAS_MATERIAL
#define HYDROGEN_ELEMENT
#define ND3_MATERIAL
#define HELIUM3GAS_MATERIAL
#define HELIUM3_ELEMENT
#define DEUTERON_ISOTOPE
#define TRITIUMGAS_MATERIAL
#define G4SYSTEMCADFACTORY
Option definitions for the g4system module (geometry/material construction layer).
constexpr const char * G4SYSTEM_LOGGER
Logger name used by the module-level builder (e.g. G4World).
High-level builder that turns a GEMC world description into Geant4 geometry.
std::map< std::string, SystemPtr > SystemMap