g4system
Loading...
Searching...
No Matches
buildSolid.cc
Go to the documentation of this file.
1
7// g4system
10
11// guts
12#include "gutilities.h"
13
14// geant4
15#include "G4Box.hh"
16#include "G4Sphere.hh"
17#include "G4Torus.hh"
18#include "G4Tubs.hh"
19#include "G4CutTubs.hh"
20#include "G4Cons.hh"
21#include "G4Para.hh"
22#include "G4Trap.hh"
23#include "G4Trd.hh"
24#include "G4Polycone.hh"
25#include "G4Polyhedra.hh"
26#include "G4Paraboloid.hh"
27#include "G4EllipticalTube.hh"
28#include "G4UnionSolid.hh"
29#include "G4SubtractionSolid.hh"
30#include "G4IntersectionSolid.hh"
31
32// Build a native Geant4 solid for the given volume definition.
33// Header documentation is authoritative; this implementation comment is intentionally brief.
35 std::unordered_map<std::string, G4Volume*>* g4s) {
36 std::string g4name = s->getG4Name();
37
38 log->info(2, className(), "G4NativeSystemFactory::buildSolid for ", g4name);
39
40 // Dependencies must be satisfied before constructing a solid (copy/boolean operands).
41 if (!checkSolidDependencies(s, g4s)) return nullptr;
42
43 // Locate or allocate the wrapper used to cache solid/logical/physical pointers.
44 auto thisG4Volume = getOrCreateG4Volume(g4name, g4s);
45
46 // Record the volume's own frame rotation and position: when this solid is the
47 // second operand of a boolean operation, they define the relative transform.
48 {
49 auto* rot = getRotation(s);
50 thisG4Volume->setSolidPlacement(*rot, getPosition(s));
51 delete rot;
52 }
53
54 // Solid exists, return it.
55 if (thisG4Volume->getSolid() != nullptr) return thisG4Volume->getSolid();
56
57 // If this is a copy, reuse the source solid if available.
58 std::string copyOf = s->getCopyOf();
59 if (copyOf != "" && copyOf != UNINITIALIZEDSTRINGQUANTITY) {
60 auto gsystem = s->getSystem();
61 auto volume_copy = gsystem + "/" + copyOf;
62 auto thisG4Volume = getOrCreateG4Volume(volume_copy, g4s);
63 if (thisG4Volume->getSolid() != nullptr) return thisG4Volume->getSolid();
64 }
65
66 // Boolean solids use already-built operand solids.
67 std::string solidsOpr = s->getSolidsOpr();
68 if (solidsOpr != "" && solidsOpr != UNINITIALIZEDSTRINGQUANTITY) {
69 std::vector<std::string> solidOperations = gutilities::getStringVectorFromString(solidsOpr);
70 if (solidOperations.size() == 3) {
71 auto resolveOperandName = [s, g4s](const std::string& operand) -> std::string {
72 if (getSolidFromMap(operand, g4s) != nullptr) return operand;
73 return s->getSystem() + "/" + operand;
74 };
75
76 auto leftName = resolveOperandName(solidOperations[0]);
77 auto rightName = resolveOperandName(solidOperations[2]);
78 auto left = getSolidFromMap(leftName, g4s);
79 auto right = getSolidFromMap(rightName, g4s);
80 if (left == nullptr || right == nullptr) return nullptr;
81
82 // GEMC2 `Operation:` convention (clas12Tags detector.cc): the first solid
83 // is taken at identity; the second is rotated by the inverse of its own
84 // frame rotation, then translated by its own position.
85 auto rightWrapper = getOrCreateG4Volume(rightName, g4s);
86 G4RotationMatrix rotate = rightWrapper->getSolidRotation();
87 G4ThreeVector translate = rightWrapper->getSolidTranslation();
88 G4RotationMatrix invRot = rotate.invert();
89 G4Transform3D transf1(invRot, G4ThreeVector(0, 0, 0));
90 G4Transform3D transf2(G4RotationMatrix(), translate);
91 G4Transform3D transform = transf2 * transf1;
92
93 if (solidOperations[1] == "+") {
94 thisG4Volume->setSolid(new G4UnionSolid(g4name, left, right, transform), log);
95 }
96 else if (solidOperations[1] == "-") {
97 thisG4Volume->setSolid(new G4SubtractionSolid(g4name, left, right, transform), log);
98 }
99 else if (solidOperations[1] == "*") {
100 thisG4Volume->setSolid(new G4IntersectionSolid(g4name, left, right, transform), log);
101 }
102 else {
104 "The boolean constructor of <", g4name, "> uses unsupported operator <",
105 solidOperations[1], ">. Use +, -, or *.");
106 }
107 return thisG4Volume->getSolid();
108 }
110 "The boolean constructor of <", g4name, "> must be: left operator right.");
111 return nullptr;
112 }
113
114 // Parse and validate parameters for the requested primitive.
115 std::vector<double> pars = checkAndReturnParameters(s);
116
117 std::string type = s->getType();
118
119 if (type == "G4Box") {
120 thisG4Volume->setSolid(new G4Box(g4name, // name
121 pars[0], // half-length in X
122 pars[1], // half-length in Y
123 pars[2] // half-length in Z
124 ), log);
125 return thisG4Volume->getSolid();
126 }
127 else if (type == "G4Tubs") {
128 thisG4Volume->setSolid(new G4Tubs(g4name, // name
129 pars[0], // Inner radius
130 pars[1], // Outer radius
131 pars[2], // half-length in z
132 pars[3], // Starting phi angle
133 pars[4] // Delta Phi angle
134 ), log);
135 return thisG4Volume->getSolid();
136 }
137 else if (type == "G4Sphere") {
138 thisG4Volume->setSolid(new G4Sphere(g4name, // name
139 pars[0], // Inner radius
140 pars[1], // Outer radius
141 pars[2], // Starting phi angle
142 pars[3], // Delta Phi angle
143 pars[4], // Starting delta angle
144 pars[5] // Delta delta angle
145 ), log);
146 return thisG4Volume->getSolid();
147 }
148 else if (type == "G4Torus") {
149 thisG4Volume->setSolid(new G4Torus(g4name, // name
150 pars[0], // Inside radius of the torus tube
151 pars[1], // Outside radius of the torus tube
152 pars[2], // Swept radius of the torus
153 pars[3], // Starting phi angle
154 pars[4] // Delta phi angle
155 ), log);
156 return thisG4Volume->getSolid();
157 }
158 else if (type == "G4CutTubs") {
159 thisG4Volume->setSolid(new G4CutTubs(g4name, // name
160 pars[0], // Inner radius
161 pars[1], // Outer radius
162 pars[2], // half-length in z
163 pars[3], // Starting phi angle
164 pars[4], // Delta Phi angle
165 G4ThreeVector(pars[5], pars[6], pars[7]), // Outside Normal at -z
166 G4ThreeVector(pars[8], pars[9], pars[10]) // Outside Normal at +z
167 ), log);
168 return thisG4Volume->getSolid();
169 }
170 else if (type == "G4Cons") {
171 thisG4Volume->setSolid(new G4Cons(g4name, // name
172 pars[0], // Inside radius at -pDz
173 pars[1], // Outside radius at -pDz
174 pars[2], // Inside radius at +pDz
175 pars[3], // Outside radius at +pDz
176 pars[4], // half-length in z
177 pars[5], // Starting phi angle
178 pars[6] // Delta Phi angle
179 ), log);
180 return thisG4Volume->getSolid();
181 }
182 else if (type == "G4Para") {
183 thisG4Volume->setSolid(new G4Para(g4name, // name
184 pars[0], // half-length in x
185 pars[1], // half-length in y
186 pars[2], // half-length in z
187 pars[3],
188 // Angle formed by the y axis and by the plane joining the center of the faces parallel to the z-x plane at -dy and +dy
189 pars[4],
190 // Polar angle of the line joining the center of the faces at -dz and +dz in z
191 pars[5]
192 // Azimuthal angle of the line joining the center of the faces at -dz and +dz in z
193 ), log);
194 return thisG4Volume->getSolid();
195 }
196 else if (type == "G4Trd") {
197 thisG4Volume->setSolid(new G4Trd(g4name, // name
198 pars[0], // Half-length along x at the surface positioned at -dz
199 pars[1], // Half-length along x at the surface positioned at +dz
200 pars[2], // Half-length along y at the surface positioned at -dz
201 pars[3], // Half-length along y at the surface positioned at +dz
202 pars[4] // Half-length along z axis
203 ), log);
204 return thisG4Volume->getSolid();
205 }
206 else if (type == "G4Trap") {
207 // G4Trap supports multiple constructor layouts; parameter count decides which one is used.
208 if (pars.size() == 4) {
209 thisG4Volume->setSolid(new G4Trap(g4name, // name
210 pars[0], // Length along Z
211 pars[1], // Length along Y
212 pars[2], // Length along X wider side
213 pars[3] // Length along X at the narrower side (plTX<=pX)
214 ), log);
215 }
216 else if (pars.size() == 11) {
217 thisG4Volume->setSolid(new G4Trap(g4name, // name
218 pars[0], // Half Z length - distance from the origin to the bases
219 pars[1],
220 // Polar angle of the line joining the center of the bases at -/+pDz
221 pars[2], // Azimuthal angle of the same line
222 pars[3], // Half Y length of the base at -pDz
223 pars[4], // Half Y length of the base at +pDz
224 pars[5], // Half X length at smaller Y of the base at -pDz
225 pars[6], // Half X length at bigger Y of the base at -pDz
226 pars[7], // Half X length at smaller Y of the base at +pDz
227 pars[8], // Half X length at bigger y of the base at +pDz
228 pars[9], // Angle between Y-axis and center line at -pDz
229 pars[10] // Angle between Y-axis and center line at +pDz
230 ), log);
231 }
232 else if (pars.size() == 24) {
233 G4ThreeVector pt[8];
234 pt[0] = G4ThreeVector(pars[0], pars[1], pars[2]);
235 pt[1] = G4ThreeVector(pars[3], pars[4], pars[5]);
236 pt[2] = G4ThreeVector(pars[6], pars[7], pars[8]);
237 pt[3] = G4ThreeVector(pars[9], pars[10], pars[11]);
238 pt[4] = G4ThreeVector(pars[12], pars[13], pars[14]);
239 pt[5] = G4ThreeVector(pars[15], pars[16], pars[17]);
240 pt[6] = G4ThreeVector(pars[18], pars[19], pars[20]);
241 pt[7] = G4ThreeVector(pars[21], pars[22], pars[23]);
242
243 thisG4Volume->setSolid(new G4Trap(g4name, pt), log);
244 }
245 else {
247 "The constructor of <", g4name, "> must have 4, 11 or 24 parameters",
248 " see https://geant4-userdoc.web.cern.ch/UsersGuides/ForApplicationDeveloper/html/Detector/Geometry/geomSolids.html");
249 }
250 return thisG4Volume->getSolid();
251 }
252 else if (type == "G4Polycone") {
253 double phistart = pars[0];
254 double phitotal = pars[1];
255 int zplanes = static_cast<int>(pars[2]);
256
257 // Allocate arrays (data is copied by G4Polycone during construction).
258 auto zPlane = std::make_unique<double[]>(zplanes);
259 auto rInner = std::make_unique<double[]>(zplanes);
260 auto rOuter = std::make_unique<double[]>(zplanes);
261
262 for (int zpl = 0; zpl < zplanes; ++zpl) {
263 zPlane[zpl] = pars[3 + 0 * zplanes + zpl];
264 rInner[zpl] = pars[3 + 1 * zplanes + zpl];
265 rOuter[zpl] = pars[3 + 2 * zplanes + zpl];
266 }
267
268 thisG4Volume->setSolid(new G4Polycone(g4name, // name
269 phistart, // Initial Phi starting angle
270 phitotal, // Total Phi angle
271 zplanes, // Number of z planes
272 zPlane.get(),
273 rInner.get(),
274 rOuter.get()
275 ), log);
276 return thisG4Volume->getSolid();
277 }
278 else if (type == "G4Polyhedra") {
279 // GEMC2 "Pgon" parameter order: phiStart, phiTotal, numSides, numZPlanes,
280 // then rInner[], rOuter[], zPlane[].
281 double phistart = pars[0];
282 double phitotal = pars[1];
283 int numSides = static_cast<int>(pars[2]);
284 int zplanes = static_cast<int>(pars[3]);
285
286 if (numSides < 1 || static_cast<int>(pars.size()) != 4 + 3 * zplanes) {
288 "The constructor of <", g4name, "> must have numSides >= 1 and ",
289 4 + 3 * zplanes, " parameters (4 + 3 x numZPlanes), we got ", pars.size());
290 }
291
292 // Allocate arrays (data is copied by G4Polyhedra during construction).
293 auto zPlane = std::make_unique<double[]>(zplanes);
294 auto rInner = std::make_unique<double[]>(zplanes);
295 auto rOuter = std::make_unique<double[]>(zplanes);
296
297 for (int zpl = 0; zpl < zplanes; ++zpl) {
298 rInner[zpl] = pars[4 + 0 * zplanes + zpl];
299 rOuter[zpl] = pars[4 + 1 * zplanes + zpl];
300 zPlane[zpl] = pars[4 + 2 * zplanes + zpl];
301 }
302
303 thisG4Volume->setSolid(new G4Polyhedra(g4name, // name
304 phistart, // Initial Phi starting angle
305 phitotal, // Total Phi angle
306 numSides, // Number of sides
307 zplanes, // Number of z planes
308 zPlane.get(),
309 rInner.get(),
310 rOuter.get()
311 ), log);
312 return thisG4Volume->getSolid();
313 }
314 else if (type == "G4Paraboloid") {
315 thisG4Volume->setSolid(new G4Paraboloid(g4name, // name
316 pars[0], // half-length in z
317 pars[1], // radius at -dz
318 pars[2] // radius at +dz
319 ), log);
320 return thisG4Volume->getSolid();
321 }
322 else if (type == "G4EllipticalTube") {
323 thisG4Volume->setSolid(new G4EllipticalTube(g4name, // name
324 pars[0], // half length in x
325 pars[1], // half length in y
326 pars[2] // half length in z
327 ), log);
328 return thisG4Volume->getSolid();
329 }
330 else {
332 "The constructor of <", g4name, "> uses an unknown solid type <", type,
333 ">. See Geant4 manual for supported primitives.");
334 }
335 return nullptr;
336}
std::string_view className() const override
Human-readable name used for logging.
std::vector< double > checkAndReturnParameters(const GVolume *s)
Validate the number of parameters for the given primitive and return them as numeric values.
G4VSolid * buildSolid(const GVolume *s, std::unordered_map< std::string, G4Volume * > *g4s) override
Create (or reuse) a native Geant4 solid based on the GVolume "type".
Definition buildSolid.cc:34
G4Volume * getOrCreateG4Volume(const std::string &volume_name, std::unordered_map< std::string, G4Volume * > *g4s)
Get or create a G4Volume wrapper entry in the map.
bool checkSolidDependencies(const GVolume *s, std::unordered_map< std::string, G4Volume * > *g4s)
Check whether all prerequisites to build a solid are satisfied.
static G4RotationMatrix * getRotation(const GVolume *s)
Parse rotation string and build a Geant4 rotation matrix.
static G4VSolid * getSolidFromMap(const std::string &volume_name, std::unordered_map< std::string, G4Volume * > *g4s)
Lookup solid in the g4s map.
static G4ThreeVector getPosition(const GVolume *s)
Parse position and optional shift strings to compute placement translation.
std::shared_ptr< GLogger > log
void info(int level, Args &&... args) const
void error(int exit_code, Args &&... args) const
std::string getCopyOf() const
std::string getG4Name() const
std::string getSystem() const
std::string getType() const
std::string getSolidsOpr() 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_G4PARAMETERSMISMATCH
Solid parameter count/format did not match expected constructors.
#define ERR_G4SOLIDTYPENOTFOUND
Requested solid type is not supported by the native factory.
#define UNINITIALIZEDSTRINGQUANTITY
vector< std::string > getStringVectorFromString(const std::string &input)