g4system
Loading...
Searching...
No Matches
g4objectsFactory.cc
Go to the documentation of this file.
1
11#include "g4objectsFactory.h"
12
13#include "gutilities.h"
14#include "gsystemConventions.h"
15#include "g4systemConventions.h"
16
17// Geant4
18#include "G4LogicalVolumeStore.hh"
19#include "G4Material.hh"
20#include "G4NistManager.hh"
21#include "G4PVPlacement.hh"
22
23// c++
24#include <algorithm>
25#include <cctype>
26#include <iostream>
27#include <string_view>
28#include <vector>
29
30namespace {
31 std::vector<std::string> tokenizeRotation(std::string rotation) {
32 std::replace(rotation.begin(), rotation.end(), ',', ' ');
34 }
35
36 std::string lowercase(std::string value) {
37 std::transform(value.begin(), value.end(), value.begin(),
38 [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
39 return value;
40 }
41
42 void requireRotationTokenCount(const std::vector<std::string> &tokens,
43 size_t expected,
44 const GVolume *s) {
45 if (tokens.size() == expected) { return; }
46 std::cerr << " >> ERROR: rotation <" << s->getRot() << "> for " << s->getName()
47 << " has wrong number of entries. Exiting." << std::endl;
48 exit(1);
49 }
50
51 void applyOrderedRotation(G4RotationMatrix *rot,
52 const std::string &order,
53 const std::vector<double> &angles,
54 const GVolume *s) {
55 if (order == "xzy") {
56 rot->rotateX(angles[0]);
57 rot->rotateZ(angles[1]);
58 rot->rotateY(angles[2]);
59 }
60 else if (order == "yxz") {
61 rot->rotateY(angles[0]);
62 rot->rotateX(angles[1]);
63 rot->rotateZ(angles[2]);
64 }
65 else if (order == "yzx") {
66 rot->rotateY(angles[0]);
67 rot->rotateZ(angles[1]);
68 rot->rotateX(angles[2]);
69 }
70 else if (order == "zxy") {
71 rot->rotateZ(angles[0]);
72 rot->rotateX(angles[1]);
73 rot->rotateY(angles[2]);
74 }
75 else if (order == "zyx") {
76 rot->rotateZ(angles[0]);
77 rot->rotateY(angles[1]);
78 rot->rotateX(angles[2]);
79 }
80 else {
81 std::cerr << " >> ERROR: Ordered rotation <" << order << "> for " << s->getName()
82 << " is wrong, it's none of the following: xzy, yxz, yzx, zxy or zyx. Exiting."
83 << std::endl;
84 exit(1);
85 }
86 }
87}
88
89// Configure factory behavior (overlap checking + backup material).
91 const std::string &backup_mat) {
92 checkOverlaps = check_overlaps;
93 backupMaterial = backup_mat;
94}
95
96// Convenience: retrieve the solid pointer for a named volume from the wrapper map.
97G4VSolid *G4ObjectsFactory::getSolidFromMap(const std::string &vname,
98 std::unordered_map<std::string, G4Volume *> *g4s) {
99 auto it = g4s->find(vname);
100 return (it != g4s->end()) ? it->second->getSolid() : nullptr;
101}
102
103// Convenience: retrieve the logical pointer for a named volume from the wrapper map.
104G4LogicalVolume *G4ObjectsFactory::getLogicalFromMap(const std::string &volume_name,
105 std::unordered_map<std::string, G4Volume *> *g4s) {
106 auto it = g4s->find(volume_name);
107 return (it != g4s->end()) ? it->second->getLogical() : nullptr;
108}
109
110// Convenience: retrieve the physical pointer for a named volume from the wrapper map.
111G4VPhysicalVolume *G4ObjectsFactory::getPhysicalFromMap(const std::string &vname,
112 std::unordered_map<std::string, G4Volume *> *g4s) {
113 auto it = g4s->find(vname);
114 return (it != g4s->end()) ? it->second->getPhysical() : nullptr;
115}
116
118 // Convert GEMC color + opacity into a Geant4 color. The helper returns a \c G4Colour.
119 std::string_view color = s->getColor();
120 double opacity = s->getOpacity();
121
122 const auto g4color = gutilities::makeG4Colour(color, opacity);
123
124 log->info(2, className(), " createVisualAttributes for color ", color,
125 " resulted in RGB = (", g4color.GetRed(), ", ", g4color.GetGreen(), ", ", g4color.GetBlue(),
126 ", opacity: ", opacity, ")");
127
128 G4VisAttributes attr(g4color);
129
130 // Visibility and style flags are stored on the volume definition.
131 s->isVisible() ? attr.SetVisibility(true) : attr.SetVisibility(false);
132
133 switch (s->getStyle()) {
134 case 0:
135 attr.SetForceWireframe(true);
136 break;
137 case 1:
138 attr.SetForceSolid(true);
139 break;
140 case 2:
141 attr.SetForceCloud(true);
142 break;
143 default:
144 break;
145 }
146
147
148 return attr;
149}
150
151G4RotationMatrix *G4ObjectsFactory::getRotation(const GVolume *s) {
152 auto rot = new G4RotationMatrix();
153
154 const auto tokens = tokenizeRotation(s->getRot());
155 if (tokens.empty()) { return rot; }
156
157 const auto rotationType = tokens[0];
158 if (rotationType != "ordered:" && rotationType != "doubleRotation:") {
159 requireRotationTokenCount(tokens, 3, s);
160 const auto pars = gutilities::getG4NumbersFromStringVector(tokens);
161 rot->rotateX(pars[0]);
162 rot->rotateY(pars[1]);
163 rot->rotateZ(pars[2]);
164 }
165 else if (rotationType == "doubleRotation:") {
166 requireRotationTokenCount(tokens, 7, s);
168 {tokens[1], tokens[2], tokens[3], tokens[4], tokens[5], tokens[6]});
169 rot->rotateX(pars[0]);
170 rot->rotateY(pars[1]);
171 rot->rotateZ(pars[2]);
172 rot->rotateX(pars[3]);
173 rot->rotateY(pars[4]);
174 rot->rotateZ(pars[5]);
175 }
176 else if (rotationType == "ordered:") {
177 requireRotationTokenCount(tokens, 5, s);
178 const auto order = lowercase(tokens[1]);
179 const auto pars = gutilities::getG4NumbersFromStringVector({tokens[2], tokens[3], tokens[4]});
180 applyOrderedRotation(rot, order, pars, s);
181 }
182 else {
183 std::cerr << " >> ERROR: rotation type <" << rotationType << "> unknown. Exiting."
184 << std::endl;
185 exit(1);
186 }
187 return rot;
188}
189
190G4ThreeVector G4ObjectsFactory::getPosition(const GVolume *s) {
191 // Base placement position.
192 G4ThreeVector pos(0., 0., 0.);
193 const auto vec = gutilities::getG4NumbersFromString(s->getPos());
194 if (vec.size() == 3) pos.set(vec[0], vec[1], vec[2]);
195
196 // Optional shift modifier (applied after parsing the base position).
197 if (s->getShift() != GSYSTEMNOMODIFIER) {
198 const auto shift = gutilities::getG4NumbersFromString(s->getShift());
199 if (shift.size() == 3) pos += G4ThreeVector(shift[0], shift[1], shift[2]);
200 }
201 return pos;
202}
203
204G4LogicalVolume *G4ObjectsFactory::buildLogical(const GVolume *s,
205 std::unordered_map<std::string, G4Volume *> *g4s) {
206 const std::string g4name = s->getG4Name();
207 auto thisG4Volume = getOrCreateG4Volume(g4name, g4s);
208
209 // Logical exists, return it.
210 if (thisG4Volume->getLogical()) return thisG4Volume->getLogical();
211
212 // If this volume is a "copy of" another, reuse the logical volume if it already exists.
213 std::string copyOf = s->getCopyOf();
214 if (copyOf != "" && copyOf != UNINITIALIZEDSTRINGQUANTITY) {
215 auto gsystem = s->getSystem();
216 auto volume_copy = gsystem + "/" + copyOf;
217 auto copyG4Volume = getOrCreateG4Volume(volume_copy, g4s);
218 if (copyG4Volume->getLogical() != nullptr) {
219 return copyG4Volume->getLogical();
220 }
221 }
222
223 // Material lookup:
224 // - first try requested material
225 // - if missing and a backup material was configured, fall back to it
226 auto *nist = G4NistManager::Instance();
227 auto *mat = nist->FindOrBuildMaterial(s->getMaterial());
228 if (!mat && !backupMaterial.empty()) {
229 mat = nist->FindOrBuildMaterial(backupMaterial);
230 log->warning("Material <", s->getMaterial(), "> not found. Using backup material <", backupMaterial, ">.");
231 }
232
233 if (!mat) {
235 "Material <", s->getMaterial(), "> not found.");
236 }
237
238 // The solid must exist: a logical volume built on a null solid would be placed
239 // and crash the navigation voxelization later.
240 if (thisG4Volume->getSolid() == nullptr) { return nullptr; }
241
242 // Create the logical volume with the already-created solid.
243 auto *logical = new G4LogicalVolume(thisG4Volume->getSolid(),
244 mat, g4name);
245
246 // Apply visualization attributes (color/opacity/visibility/style).
247 logical->SetVisAttributes(createVisualAttributes(s));
248 thisG4Volume->setLogical(logical, log);
249 return logical;
250}
251
252G4VPhysicalVolume *G4ObjectsFactory::buildPhysical(const GVolume *s,
253 std::unordered_map<std::string, G4Volume *> *g4s) {
254 // Nonexistent volumes are ignored by design.
255 if (!s->getExistence()) return nullptr;
256
257 // Mother/logical prerequisites must exist; otherwise caller will retry later.
258 if (!checkPhysicalDependencies(s, g4s)) return nullptr;
259
260 const std::string g4name = s->getG4Name();
261 auto thisG4Volume = getOrCreateG4Volume(g4name, g4s);
262 auto logicalVolume = thisG4Volume->getLogical();
263
264 // If this is a copy, reuse the source logical volume when available.
265 std::string copyOf = s->getCopyOf();
266 if (copyOf != "" && copyOf != UNINITIALIZEDSTRINGQUANTITY) {
267 auto gsystem = s->getSystem();
268 auto volume_copy = gsystem + "/" + copyOf;
269 auto copyG4Volume = getOrCreateG4Volume(volume_copy, g4s);
270 if (copyG4Volume->getLogical() != nullptr) {
271 logicalVolume = copyG4Volume->getLogical();
272 }
273 }
274
275 // Create the placement only once; subsequent calls return the cached physical volume.
276 if (!thisG4Volume->getPhysical()) {
277 auto *rotation = getRotation(s);
278 G4ThreeVector translation = getPosition(s);
279 const auto placementType = s->getG4PlacementType();
280
281 if (placementType == "active") {
282 G4RotationMatrix rotation_instance = *rotation;
283 delete rotation;
284 thisG4Volume->setPhysical(new G4PVPlacement(G4Transform3D(rotation_instance, translation),
285 logicalVolume,
286 g4name,
288 false,
289 s->getPCopyNo(),
290 checkOverlaps > 0),
291 log);
292 }
293 else if (placementType == "passive") {
294 thisG4Volume->setPhysical(new G4PVPlacement(rotation,
295 translation,
296 logicalVolume,
297 g4name,
299 false,
300 s->getPCopyNo(),
301 checkOverlaps > 0),
302 log);
303 }
304 else {
306 "GVolume <", s->getName(), "> has unsupported g4placement_type <",
307 placementType, ">. Use 'active' or 'passive'.");
308 }
309 }
310 return thisG4Volume->getPhysical();
311}
312
314 std::unordered_map<std::string, G4Volume *> *g4s) {
315 const auto name = s->getG4Name();
316
317 // Component volumes are boolean-operation building blocks: they contribute their
318 // solid to `solidsOpr` operations but get no material, logical, or placement.
320 auto *solid = buildSolid(s, g4s);
321 log->info(2, className(), " result for component ", name, ": solid: ", solid != nullptr);
322 return solid != nullptr;
323 }
324
325 // Build steps can fail due to missing dependencies; each returns nullptr in that case.
326 // The logical volume must not be created before the solid is available (a boolean
327 // volume can precede its operands in the build order): a logical constructed with a
328 // null solid would be placed and crash the navigation voxelization later.
329 auto sbuild = buildSolid(s, g4s);
330 if (sbuild == nullptr) {
331 log->info(2, className(), " result for ", name, ": solid not ready, deferring");
332 return false;
333 }
334 auto lbuild = buildLogical(s, g4s);
335 auto pbuild = buildPhysical(s, g4s);
336
337 const bool okSolid = (sbuild != nullptr);
338 const bool okLogical = (lbuild != nullptr);
339 const bool okPhysical = (pbuild != nullptr);
340 const bool okAll = okSolid && okLogical && okPhysical;
341
342 log->info(2, className(), " result for ",
343 name,
344 ": solid: ", okSolid,
345 " logical: ", okLogical,
346 " physical: ", okPhysical);
347
348 return okAll;
349}
G4Volume * getOrCreateG4Volume(const std::string &volume_name, std::unordered_map< std::string, G4Volume * > *g4s)
Get or create a G4Volume wrapper entry in the map.
virtual G4LogicalVolume * buildLogical(const GVolume *s, std::unordered_map< std::string, G4Volume * > *g4s)
Build or retrieve the G4LogicalVolume for a volume.
virtual G4VPhysicalVolume * buildPhysical(const GVolume *s, std::unordered_map< std::string, G4Volume * > *g4s)
Build or retrieve the G4VPhysicalVolume for a volume.
static G4VPhysicalVolume * getPhysicalFromMap(const std::string &volume_name, std::unordered_map< std::string, G4Volume * > *g4s)
Lookup physical volume in the g4s map.
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.
static G4RotationMatrix * getRotation(const GVolume *s)
Parse rotation string and build a Geant4 rotation matrix.
virtual std::string_view className() const =0
Short, human-readable factory name for logging.
virtual G4VSolid * buildSolid(const GVolume *s, std::unordered_map< std::string, G4Volume * > *g4s)=0
Build the G4VSolid for a volume.
bool checkPhysicalDependencies(const GVolume *s, std::unordered_map< std::string, G4Volume * > *g4s)
Verify prerequisites to build a physical placement.
static G4VSolid * getSolidFromMap(const std::string &volume_name, std::unordered_map< std::string, G4Volume * > *g4s)
Lookup solid in the g4s map.
static G4LogicalVolume * getLogicalFromMap(const std::string &volume_name, std::unordered_map< std::string, G4Volume * > *g4s)
Lookup logical volume in the g4s map.
void initialize_context(int checkOverlaps, const std::string &backupMaterial)
Configure overlap checking and backup material behavior for this factory.
static G4ThreeVector getPosition(const GVolume *s)
Parse position and optional shift strings to compute placement translation.
G4VisAttributes createVisualAttributes(const GVolume *s)
Build visualization attributes from the volume definition.
std::string backupMaterial
Backup material name used if the requested material is absent.
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 getRot() const
std::string getCopyOf() const
bool getExistence() const
std::string getPos() const
int getPCopyNo() const
std::string getShift() const
std::string getG4PlacementType() const
std::string getName() const
std::string getMaterial() const
std::string getG4Name() const
std::string getSystem() const
int getStyle() const
bool isVisible() const
std::string_view getColor() const
double getOpacity() const
std::string getG4MotherName() const
Abstract factory that converts a GEMC DB GVolume into Geant4 objects.
Conventions, labels, and error codes used by the g4system geometry/material layer.
#define ERR_G4MATERIALNOTFOUND
Material lookup failed and no fallback was available.
#define ERR_G4PLACEMENTTYPE
#define GSYSTEMCOMPONENTMATERIAL
#define GSYSTEMNOMODIFIER
#define UNINITIALIZEDSTRINGQUANTITY
vector< double > getG4NumbersFromString(const string &vstring, bool warnIfNotUnit=false)
G4Colour makeG4Colour(std::string_view code, double opacity)
vector< double > getG4NumbersFromStringVector(const vector< string > &vstring, bool warnIfNotUnit=false)
vector< std::string > getStringVectorFromString(const std::string &input)