g4system
Loading...
Searching...
No Matches
buildSolid.cc
Go to the documentation of this file.
1
6
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 "G4Ellipsoid.hh"
29#include "G4Orb.hh"
30#include "G4GenericPolycone.hh"
31#include "G4EllipticalCone.hh"
32#include "G4Hype.hh"
33#include "G4Tet.hh"
34#include "G4TwistedBox.hh"
35#include "G4TwistedTrap.hh"
36#include "G4TwistedTrd.hh"
37#include "G4TwistedTubs.hh"
38#include "G4UnionSolid.hh"
39#include "G4SubtractionSolid.hh"
40#include "G4IntersectionSolid.hh"
41
42// Build a native Geant4 solid for the given volume definition.
43// Header documentation is authoritative; this implementation comment is intentionally brief.
45 std::unordered_map<std::string, G4Volume*>* g4s) {
46 std::string g4name = s->getG4Name();
47
48 log->info(2, "%1G4NativeSystemFactory::buildSolid for %2", className(), g4name);
49
50 // Dependencies must be satisfied before constructing a solid (copy/boolean operands).
51 if (!checkSolidDependencies(s, g4s)) return nullptr;
52
53 // Locate or allocate the wrapper used to cache solid/logical/physical pointers.
54 auto thisG4Volume = getOrCreateG4Volume(g4name, g4s);
55
56 // Record the volume's own frame rotation and position: when this solid is the
57 // second operand of a boolean operation, they define the relative transform.
58 {
59 auto* rot = getRotation(s);
60 thisG4Volume->setSolidPlacement(*rot, getPosition(s));
61 delete rot;
62 }
63
64 // Solid exists, return it.
65 if (thisG4Volume->getSolid() != nullptr) return thisG4Volume->getSolid();
66
67 // If this is a copy, reuse the source solid if available.
68 const auto& copyOf = s->getCopyOf();
69 if (copyOf) {
70 auto gsystem = s->getSystem();
71 auto volume_copy = gsystem + "/" + *copyOf;
72 auto thisG4Volume = getOrCreateG4Volume(volume_copy, g4s);
73 if (thisG4Volume->getSolid() != nullptr) return thisG4Volume->getSolid();
74 }
75
76 // Boolean solids use already-built operand solids.
77 const auto& solidsOpr = s->getSolidsOpr();
78 if (solidsOpr) {
79 std::vector<std::string> solidOperations = gutilities::getStringVectorFromString(*solidsOpr);
80
81 // GEMC2 `Operation:@` marks operands whose position/rotation are given in the common mother
82 // frame (clas12Tags detector.cc). pygemc encodes it as a leading "@" token. When absent, the
83 // default `Operation:` convention applies (first solid at identity).
84 bool absoluteCoordinates = false;
85 if (!solidOperations.empty() && solidOperations[0] == "@") {
86 absoluteCoordinates = true;
87 solidOperations.erase(solidOperations.begin());
88 }
89
90 if (solidOperations.size() == 3) {
91 auto resolveOperandName = [s, g4s](const std::string& operand) -> std::string {
92 if (getSolidFromMap(operand, g4s) != nullptr) return operand;
93 return s->getSystem() + "/" + operand;
94 };
95
96 auto leftName = resolveOperandName(solidOperations[0]);
97 auto rightName = resolveOperandName(solidOperations[2]);
98 auto left = getSolidFromMap(leftName, g4s);
99 auto right = getSolidFromMap(rightName, g4s);
100 if (left == nullptr || right == nullptr) return nullptr;
101
102 auto rightWrapper = getOrCreateG4Volume(rightName, g4s);
103
104 G4Transform3D transform;
105 if (absoluteCoordinates) {
106 // `Operation:@`: both operands are placed in absolute (common mother) coordinates, so
107 // the second solid's transform relative to the first accounts for the first solid's own
108 // rotation and position (clas12Tags detector.cc). Reduces to the default convention when
109 // the first solid is at identity.
110 auto leftWrapper = getOrCreateG4Volume(leftName, g4s);
111 G4RotationMatrix rot1 = leftWrapper->getSolidRotation();
112 G4RotationMatrix rot2 = rightWrapper->getSolidRotation();
113 G4ThreeVector pos1 = leftWrapper->getSolidTranslation();
114 G4ThreeVector pos2 = rightWrapper->getSolidTranslation();
115
116 G4RotationMatrix invRot1 = rot1.inverse();
117 G4RotationMatrix invNetRotation = (rot2 * invRot1).invert();
118 G4ThreeVector netTranslation = pos2 - pos1;
119 netTranslation *= rot1;
120 transform = G4Transform3D(invNetRotation, netTranslation);
121 }
122 else {
123 // GEMC2 `Operation:` convention (clas12Tags detector.cc): the first solid is taken at
124 // identity; the second is rotated by the inverse of its own frame rotation, then
125 // translated by its own position.
126 G4RotationMatrix rotate = rightWrapper->getSolidRotation();
127 G4ThreeVector translate = rightWrapper->getSolidTranslation();
128 G4RotationMatrix invRot = rotate.invert();
129 G4Transform3D transf1(invRot, G4ThreeVector(0, 0, 0));
130 G4Transform3D transf2(G4RotationMatrix(), translate);
131 transform = transf2 * transf1;
132 }
133
134 if (solidOperations[1] == "+") {
135 thisG4Volume->setSolid(new G4UnionSolid(g4name, left, right, transform), log);
136 }
137 else if (solidOperations[1] == "-") {
138 thisG4Volume->setSolid(new G4SubtractionSolid(g4name, left, right, transform), log);
139 }
140 else if (solidOperations[1] == "*") {
141 thisG4Volume->setSolid(new G4IntersectionSolid(g4name, left, right, transform), log);
142 }
143 else {
144 log->error(
146 "The boolean constructor of <%1> uses unsupported operator <%2>. Use +, -, or *.", g4name,
147 solidOperations[1]);
148 }
149 return thisG4Volume->getSolid();
150 }
151 log->error(
152 g4system::ERR_G4PARAMETERSMISMATCH, "The boolean constructor of <%1> must be: left operator right.",
153 g4name);
154 return nullptr;
155 }
156
157 // Parse and validate parameters for the requested primitive.
158 std::vector<double> pars = checkAndReturnParameters(s);
159
160 std::string type = s->getType();
161
162 if (type == "G4Box") {
163 thisG4Volume->setSolid(new G4Box(g4name, // name
164 pars[0], // half-length in X
165 pars[1], // half-length in Y
166 pars[2] // half-length in Z
167 ), log);
168 return thisG4Volume->getSolid();
169 }
170 else if (type == "G4Tubs") {
171 thisG4Volume->setSolid(new G4Tubs(g4name, // name
172 pars[0], // Inner radius
173 pars[1], // Outer radius
174 pars[2], // half-length in z
175 pars[3], // Starting phi angle
176 pars[4] // Delta Phi angle
177 ), log);
178 return thisG4Volume->getSolid();
179 }
180 else if (type == "G4Sphere") {
181 thisG4Volume->setSolid(new G4Sphere(g4name, // name
182 pars[0], // Inner radius
183 pars[1], // Outer radius
184 pars[2], // Starting phi angle
185 pars[3], // Delta Phi angle
186 pars[4], // Starting delta angle
187 pars[5] // Delta delta angle
188 ), log);
189 return thisG4Volume->getSolid();
190 }
191 else if (type == "G4Torus") {
192 thisG4Volume->setSolid(new G4Torus(g4name, // name
193 pars[0], // Inside radius of the torus tube
194 pars[1], // Outside radius of the torus tube
195 pars[2], // Swept radius of the torus
196 pars[3], // Starting phi angle
197 pars[4] // Delta phi angle
198 ), log);
199 return thisG4Volume->getSolid();
200 }
201 else if (type == "G4CutTubs") {
202 thisG4Volume->setSolid(new G4CutTubs(g4name, // name
203 pars[0], // Inner radius
204 pars[1], // Outer radius
205 pars[2], // half-length in z
206 pars[3], // Starting phi angle
207 pars[4], // Delta Phi angle
208 G4ThreeVector(pars[5], pars[6], pars[7]), // Outside Normal at -z
209 G4ThreeVector(pars[8], pars[9], pars[10]) // Outside Normal at +z
210 ), log);
211 return thisG4Volume->getSolid();
212 }
213 else if (type == "G4Cons") {
214 thisG4Volume->setSolid(new G4Cons(g4name, // name
215 pars[0], // Inside radius at -pDz
216 pars[1], // Outside radius at -pDz
217 pars[2], // Inside radius at +pDz
218 pars[3], // Outside radius at +pDz
219 pars[4], // half-length in z
220 pars[5], // Starting phi angle
221 pars[6] // Delta Phi angle
222 ), log);
223 return thisG4Volume->getSolid();
224 }
225 else if (type == "G4Para") {
226 thisG4Volume->setSolid(new G4Para(g4name, // name
227 pars[0], // half-length in x
228 pars[1], // half-length in y
229 pars[2], // half-length in z
230 pars[3],
231 // 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
232 pars[4],
233 // Polar angle of the line joining the center of the faces at -dz and +dz in z
234 pars[5]
235 // Azimuthal angle of the line joining the center of the faces at -dz and +dz in z
236 ), log);
237 return thisG4Volume->getSolid();
238 }
239 else if (type == "G4Trd") {
240 thisG4Volume->setSolid(new G4Trd(g4name, // name
241 pars[0], // Half-length along x at the surface positioned at -dz
242 pars[1], // Half-length along x at the surface positioned at +dz
243 pars[2], // Half-length along y at the surface positioned at -dz
244 pars[3], // Half-length along y at the surface positioned at +dz
245 pars[4] // Half-length along z axis
246 ), log);
247 return thisG4Volume->getSolid();
248 }
249 else if (type == "G4Trap") {
250 // G4Trap supports multiple constructor layouts; parameter count decides which one is used.
251 if (pars.size() == 4) {
252 thisG4Volume->setSolid(new G4Trap(g4name, // name
253 pars[0], // Length along Z
254 pars[1], // Length along Y
255 pars[2], // Length along X wider side
256 pars[3] // Length along X at the narrower side (plTX<=pX)
257 ), log);
258 }
259 else if (pars.size() == 11) {
260 thisG4Volume->setSolid(new G4Trap(g4name, // name
261 pars[0], // Half Z length - distance from the origin to the bases
262 pars[1],
263 // Polar angle of the line joining the center of the bases at -/+pDz
264 pars[2], // Azimuthal angle of the same line
265 pars[3], // Half Y length of the base at -pDz
266 pars[4], // Half Y length of the base at +pDz
267 pars[5], // Half X length at smaller Y of the base at -pDz
268 pars[6], // Half X length at bigger Y of the base at -pDz
269 pars[7], // Half X length at smaller Y of the base at +pDz
270 pars[8], // Half X length at bigger y of the base at +pDz
271 pars[9], // Angle between Y-axis and center line at -pDz
272 pars[10] // Angle between Y-axis and center line at +pDz
273 ), log);
274 }
275 else if (pars.size() == 24) {
276 G4ThreeVector pt[8];
277 pt[0] = G4ThreeVector(pars[0], pars[1], pars[2]);
278 pt[1] = G4ThreeVector(pars[3], pars[4], pars[5]);
279 pt[2] = G4ThreeVector(pars[6], pars[7], pars[8]);
280 pt[3] = G4ThreeVector(pars[9], pars[10], pars[11]);
281 pt[4] = G4ThreeVector(pars[12], pars[13], pars[14]);
282 pt[5] = G4ThreeVector(pars[15], pars[16], pars[17]);
283 pt[6] = G4ThreeVector(pars[18], pars[19], pars[20]);
284 pt[7] = G4ThreeVector(pars[21], pars[22], pars[23]);
285
286 thisG4Volume->setSolid(new G4Trap(g4name, pt), log);
287 }
288 else {
289 log->error(
291 "The constructor of <%1> must have 4, 11 or 24 parameters see "
292 "https://geant4-userdoc.web.cern.ch/UsersGuides/ForApplicationDeveloper/html/Detector/"
293 "Geometry/geomSolids.html", g4name);
294 }
295 return thisG4Volume->getSolid();
296 }
297 else if (type == "G4Polycone") {
298 double phistart = pars[0];
299 double phitotal = pars[1];
300 int zplanes = static_cast<int>(pars[2]);
301
302 // Allocate arrays (data is copied by G4Polycone during construction).
303 auto zPlane = std::make_unique<double[]>(zplanes);
304 auto rInner = std::make_unique<double[]>(zplanes);
305 auto rOuter = std::make_unique<double[]>(zplanes);
306
307 for (int zpl = 0; zpl < zplanes; ++zpl) {
308 zPlane[zpl] = pars[3 + 0 * zplanes + zpl];
309 rInner[zpl] = pars[3 + 1 * zplanes + zpl];
310 rOuter[zpl] = pars[3 + 2 * zplanes + zpl];
311 }
312
313 thisG4Volume->setSolid(new G4Polycone(g4name, // name
314 phistart, // Initial Phi starting angle
315 phitotal, // Total Phi angle
316 zplanes, // Number of z planes
317 zPlane.get(),
318 rInner.get(),
319 rOuter.get()
320 ), log);
321 return thisG4Volume->getSolid();
322 }
323 else if (type == "G4Polyhedra") {
324 // GEMC2 "Pgon" parameter order: phiStart, phiTotal, numSides, numZPlanes,
325 // then rInner[], rOuter[], zPlane[].
326 double phistart = pars[0];
327 double phitotal = pars[1];
328 int numSides = static_cast<int>(pars[2]);
329 int zplanes = static_cast<int>(pars[3]);
330
331 if (numSides < 1 || static_cast<int>(pars.size()) != 4 + 3 * zplanes) {
332 log->error(
334 "The constructor of <%1> must have numSides >= 1 and %2 parameters (4 + 3 x numZPlanes), we "
335 "got %3", g4name, 4 + 3 * zplanes, pars.size());
336 }
337
338 // Allocate arrays (data is copied by G4Polyhedra during construction).
339 auto zPlane = std::make_unique<double[]>(zplanes);
340 auto rInner = std::make_unique<double[]>(zplanes);
341 auto rOuter = std::make_unique<double[]>(zplanes);
342
343 for (int zpl = 0; zpl < zplanes; ++zpl) {
344 rInner[zpl] = pars[4 + 0 * zplanes + zpl];
345 rOuter[zpl] = pars[4 + 1 * zplanes + zpl];
346 zPlane[zpl] = pars[4 + 2 * zplanes + zpl];
347 }
348
349 thisG4Volume->setSolid(new G4Polyhedra(g4name, // name
350 phistart, // Initial Phi starting angle
351 phitotal, // Total Phi angle
352 numSides, // Number of sides
353 zplanes, // Number of z planes
354 zPlane.get(),
355 rInner.get(),
356 rOuter.get()
357 ), log);
358 return thisG4Volume->getSolid();
359 }
360 else if (type == "G4Paraboloid") {
361 thisG4Volume->setSolid(new G4Paraboloid(g4name, // name
362 pars[0], // half-length in z
363 pars[1], // radius at -dz
364 pars[2] // radius at +dz
365 ), log);
366 return thisG4Volume->getSolid();
367 }
368 else if (type == "G4EllipticalTube") {
369 thisG4Volume->setSolid(new G4EllipticalTube(g4name, // name
370 pars[0], // half length in x
371 pars[1], // half length in y
372 pars[2] // half length in z
373 ), log);
374 return thisG4Volume->getSolid();
375 }
376 else if (type == "G4Ellipsoid") {
377 thisG4Volume->setSolid(new G4Ellipsoid(g4name, // name
378 pars[0], // semi-axis in x
379 pars[1], // semi-axis in y
380 pars[2], // semi-axis in z
381 pars[3], // lower cut in z (0 = no cut)
382 pars[4] // upper cut in z (0 = no cut)
383 ), log);
384 return thisG4Volume->getSolid();
385 }
386 else if (type == "G4Orb") {
387 thisG4Volume->setSolid(new G4Orb(g4name, // name
388 pars[0] // outer radius
389 ), log);
390 return thisG4Volume->getSolid();
391 }
392 else if (type == "G4GenericPolycone") {
393 double phistart = pars[0];
394 double phitotal = pars[1];
395 int numRZ = static_cast<int>(pars[2]);
396
397 // The (r, z) corner arrays are copied by G4GenericPolycone during construction.
398 auto r = std::make_unique<double[]>(numRZ);
399 auto z = std::make_unique<double[]>(numRZ);
400 for (int i = 0; i < numRZ; ++i) {
401 r[i] = pars[3 + 0 * numRZ + i];
402 z[i] = pars[3 + 1 * numRZ + i];
403 }
404
405 thisG4Volume->setSolid(new G4GenericPolycone(g4name, // name
406 phistart, // Initial Phi starting angle
407 phitotal, // Total Phi angle
408 numRZ, // Number of corners in the r,z space
409 r.get(),
410 z.get()
411 ), log);
412 return thisG4Volume->getSolid();
413 }
414 else if (type == "G4EllipticalCone") {
415 thisG4Volume->setSolid(new G4EllipticalCone(g4name, // name
416 pars[0], // semi-axis in x (dimensionless)
417 pars[1], // semi-axis in y (dimensionless)
418 pars[2], // height z
419 pars[3] // upper cut in z
420 ), log);
421 return thisG4Volume->getSolid();
422 }
423 else if (type == "G4Hype") {
424 thisG4Volume->setSolid(new G4Hype(g4name, // name
425 pars[0], // inner radius
426 pars[1], // outer radius
427 pars[2], // inner stereo angle
428 pars[3], // outer stereo angle
429 pars[4] // half-length in z
430 ), log);
431 return thisG4Volume->getSolid();
432 }
433 else if (type == "G4Tet") {
434 // Four vertices (12 parameters); an optional 13th legacy flag value is ignored.
435 thisG4Volume->setSolid(new G4Tet(g4name, // name
436 G4ThreeVector(pars[0], pars[1], pars[2]), // anchor
437 G4ThreeVector(pars[3], pars[4], pars[5]), // p2
438 G4ThreeVector(pars[6], pars[7], pars[8]), // p3
439 G4ThreeVector(pars[9], pars[10], pars[11]) // p4
440 ), log);
441 return thisG4Volume->getSolid();
442 }
443 else if (type == "G4TwistedBox") {
444 thisG4Volume->setSolid(new G4TwistedBox(g4name, // name
445 pars[0], // twist angle
446 pars[1], // half-length in x
447 pars[2], // half-length in y
448 pars[3] // half-length in z
449 ), log);
450 return thisG4Volume->getSolid();
451 }
452 else if (type == "G4TwistedTrap") {
453 // Like G4Trap, the parameter count selects the constructor layout.
454 if (pars.size() == 5) {
455 thisG4Volume->setSolid(new G4TwistedTrap(g4name, // name
456 pars[0], // twist angle
457 pars[1], // half x length at -pDz,-pDy
458 pars[2], // half x length at -pDz,+pDy
459 pars[3], // half y length
460 pars[4] // half z length
461 ), log);
462 }
463 else if (pars.size() == 11) {
464 thisG4Volume->setSolid(new G4TwistedTrap(g4name, // name
465 pars[0], // twist angle
466 pars[1], // half z length
467 pars[2], // polar angle between end planes
468 pars[3], // azimuthal angle between end planes
469 pars[4], // half y length at -pDz
470 pars[5], // half x length at -pDz,-pDy
471 pars[6], // half x length at -pDz,+pDy
472 pars[7], // half y length at +pDz
473 pars[8], // half x length at +pDz,-pDy
474 pars[9], // half x length at +pDz,+pDy
475 pars[10] // tilt angle
476 ), log);
477 }
478 else {
479 log->error(
481 "The constructor of <%1> must have 5 or 11 parameters.", g4name);
482 }
483 return thisG4Volume->getSolid();
484 }
485 else if (type == "G4TwistedTrd") {
486 thisG4Volume->setSolid(new G4TwistedTrd(g4name, // name
487 pars[0], // half x length at -pDz
488 pars[1], // half x length at +pDz
489 pars[2], // half y length at -pDz
490 pars[3], // half y length at +pDz
491 pars[4], // half z length
492 pars[5] // twist angle
493 ), log);
494 return thisG4Volume->getSolid();
495 }
496 else if (type == "G4TwistedTubs") {
497 thisG4Volume->setSolid(new G4TwistedTubs(g4name, // name
498 pars[0], // twist angle
499 pars[1], // inner radius at endcap
500 pars[2], // outer radius at endcap
501 pars[3], // half-length in z
502 pars[4] // phi angle of the segment
503 ), log);
504 return thisG4Volume->getSolid();
505 }
506 else {
507 log->error(
509 "The constructor of <%1> uses an unknown solid type <%2>. See Geant4 manual for supported "
510 "primitives.", g4name, type);
511 }
512 return nullptr;
513}
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:44
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
const std::string & getG4Name() const
const std::optional< std::string > & getSolidsOpr() const
const std::optional< std::string > & getCopyOf() const
std::string getSystem() const
std::string getType() 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.
constexpr int ERR_G4SOLIDTYPENOTFOUND
Requested solid type is not supported by the native factory.
constexpr int ERR_G4PARAMETERSMISMATCH
Solid parameter count/format did not match expected constructors.
vector< std::string > getStringVectorFromString(const std::string &input)