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 "G4UnionSolid.hh"
30#include "G4SubtractionSolid.hh"
31#include "G4IntersectionSolid.hh"
32
33// Build a native Geant4 solid for the given volume definition.
34// Header documentation is authoritative; this implementation comment is intentionally brief.
36 std::unordered_map<std::string, G4Volume*>* g4s) {
37 std::string g4name = s->getG4Name();
38
39 log->info(2, "%1G4NativeSystemFactory::buildSolid for %2", className(), g4name);
40
41 // Dependencies must be satisfied before constructing a solid (copy/boolean operands).
42 if (!checkSolidDependencies(s, g4s)) return nullptr;
43
44 // Locate or allocate the wrapper used to cache solid/logical/physical pointers.
45 auto thisG4Volume = getOrCreateG4Volume(g4name, g4s);
46
47 // Record the volume's own frame rotation and position: when this solid is the
48 // second operand of a boolean operation, they define the relative transform.
49 {
50 auto* rot = getRotation(s);
51 thisG4Volume->setSolidPlacement(*rot, getPosition(s));
52 delete rot;
53 }
54
55 // Solid exists, return it.
56 if (thisG4Volume->getSolid() != nullptr) return thisG4Volume->getSolid();
57
58 // If this is a copy, reuse the source solid if available.
59 const auto& copyOf = s->getCopyOf();
60 if (copyOf) {
61 auto gsystem = s->getSystem();
62 auto volume_copy = gsystem + "/" + *copyOf;
63 auto thisG4Volume = getOrCreateG4Volume(volume_copy, g4s);
64 if (thisG4Volume->getSolid() != nullptr) return thisG4Volume->getSolid();
65 }
66
67 // Boolean solids use already-built operand solids.
68 const auto& solidsOpr = s->getSolidsOpr();
69 if (solidsOpr) {
70 std::vector<std::string> solidOperations = gutilities::getStringVectorFromString(*solidsOpr);
71
72 // GEMC2 `Operation:@` marks operands whose position/rotation are given in the common mother
73 // frame (clas12Tags detector.cc). pygemc encodes it as a leading "@" token. When absent, the
74 // default `Operation:` convention applies (first solid at identity).
75 bool absoluteCoordinates = false;
76 if (!solidOperations.empty() && solidOperations[0] == "@") {
77 absoluteCoordinates = true;
78 solidOperations.erase(solidOperations.begin());
79 }
80
81 if (solidOperations.size() == 3) {
82 auto resolveOperandName = [s, g4s](const std::string& operand) -> std::string {
83 if (getSolidFromMap(operand, g4s) != nullptr) return operand;
84 return s->getSystem() + "/" + operand;
85 };
86
87 auto leftName = resolveOperandName(solidOperations[0]);
88 auto rightName = resolveOperandName(solidOperations[2]);
89 auto left = getSolidFromMap(leftName, g4s);
90 auto right = getSolidFromMap(rightName, g4s);
91 if (left == nullptr || right == nullptr) return nullptr;
92
93 auto rightWrapper = getOrCreateG4Volume(rightName, g4s);
94
95 G4Transform3D transform;
96 if (absoluteCoordinates) {
97 // `Operation:@`: both operands are placed in absolute (common mother) coordinates, so
98 // the second solid's transform relative to the first accounts for the first solid's own
99 // rotation and position (clas12Tags detector.cc). Reduces to the default convention when
100 // the first solid is at identity.
101 auto leftWrapper = getOrCreateG4Volume(leftName, g4s);
102 G4RotationMatrix rot1 = leftWrapper->getSolidRotation();
103 G4RotationMatrix rot2 = rightWrapper->getSolidRotation();
104 G4ThreeVector pos1 = leftWrapper->getSolidTranslation();
105 G4ThreeVector pos2 = rightWrapper->getSolidTranslation();
106
107 G4RotationMatrix invRot1 = rot1.inverse();
108 G4RotationMatrix invNetRotation = (rot2 * invRot1).invert();
109 G4ThreeVector netTranslation = pos2 - pos1;
110 netTranslation *= rot1;
111 transform = G4Transform3D(invNetRotation, netTranslation);
112 }
113 else {
114 // GEMC2 `Operation:` convention (clas12Tags detector.cc): the first solid is taken at
115 // identity; the second is rotated by the inverse of its own frame rotation, then
116 // translated by its own position.
117 G4RotationMatrix rotate = rightWrapper->getSolidRotation();
118 G4ThreeVector translate = rightWrapper->getSolidTranslation();
119 G4RotationMatrix invRot = rotate.invert();
120 G4Transform3D transf1(invRot, G4ThreeVector(0, 0, 0));
121 G4Transform3D transf2(G4RotationMatrix(), translate);
122 transform = transf2 * transf1;
123 }
124
125 if (solidOperations[1] == "+") {
126 thisG4Volume->setSolid(new G4UnionSolid(g4name, left, right, transform), log);
127 }
128 else if (solidOperations[1] == "-") {
129 thisG4Volume->setSolid(new G4SubtractionSolid(g4name, left, right, transform), log);
130 }
131 else if (solidOperations[1] == "*") {
132 thisG4Volume->setSolid(new G4IntersectionSolid(g4name, left, right, transform), log);
133 }
134 else {
135 log->error(
137 "The boolean constructor of <%1> uses unsupported operator <%2>. Use +, -, or *.", g4name,
138 solidOperations[1]);
139 }
140 return thisG4Volume->getSolid();
141 }
142 log->error(
143 g4system::ERR_G4PARAMETERSMISMATCH, "The boolean constructor of <%1> must be: left operator right.",
144 g4name);
145 return nullptr;
146 }
147
148 // Parse and validate parameters for the requested primitive.
149 std::vector<double> pars = checkAndReturnParameters(s);
150
151 std::string type = s->getType();
152
153 if (type == "G4Box") {
154 thisG4Volume->setSolid(new G4Box(g4name, // name
155 pars[0], // half-length in X
156 pars[1], // half-length in Y
157 pars[2] // half-length in Z
158 ), log);
159 return thisG4Volume->getSolid();
160 }
161 else if (type == "G4Tubs") {
162 thisG4Volume->setSolid(new G4Tubs(g4name, // name
163 pars[0], // Inner radius
164 pars[1], // Outer radius
165 pars[2], // half-length in z
166 pars[3], // Starting phi angle
167 pars[4] // Delta Phi angle
168 ), log);
169 return thisG4Volume->getSolid();
170 }
171 else if (type == "G4Sphere") {
172 thisG4Volume->setSolid(new G4Sphere(g4name, // name
173 pars[0], // Inner radius
174 pars[1], // Outer radius
175 pars[2], // Starting phi angle
176 pars[3], // Delta Phi angle
177 pars[4], // Starting delta angle
178 pars[5] // Delta delta angle
179 ), log);
180 return thisG4Volume->getSolid();
181 }
182 else if (type == "G4Torus") {
183 thisG4Volume->setSolid(new G4Torus(g4name, // name
184 pars[0], // Inside radius of the torus tube
185 pars[1], // Outside radius of the torus tube
186 pars[2], // Swept radius of the torus
187 pars[3], // Starting phi angle
188 pars[4] // Delta phi angle
189 ), log);
190 return thisG4Volume->getSolid();
191 }
192 else if (type == "G4CutTubs") {
193 thisG4Volume->setSolid(new G4CutTubs(g4name, // name
194 pars[0], // Inner radius
195 pars[1], // Outer radius
196 pars[2], // half-length in z
197 pars[3], // Starting phi angle
198 pars[4], // Delta Phi angle
199 G4ThreeVector(pars[5], pars[6], pars[7]), // Outside Normal at -z
200 G4ThreeVector(pars[8], pars[9], pars[10]) // Outside Normal at +z
201 ), log);
202 return thisG4Volume->getSolid();
203 }
204 else if (type == "G4Cons") {
205 thisG4Volume->setSolid(new G4Cons(g4name, // name
206 pars[0], // Inside radius at -pDz
207 pars[1], // Outside radius at -pDz
208 pars[2], // Inside radius at +pDz
209 pars[3], // Outside radius at +pDz
210 pars[4], // half-length in z
211 pars[5], // Starting phi angle
212 pars[6] // Delta Phi angle
213 ), log);
214 return thisG4Volume->getSolid();
215 }
216 else if (type == "G4Para") {
217 thisG4Volume->setSolid(new G4Para(g4name, // name
218 pars[0], // half-length in x
219 pars[1], // half-length in y
220 pars[2], // half-length in z
221 pars[3],
222 // 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
223 pars[4],
224 // Polar angle of the line joining the center of the faces at -dz and +dz in z
225 pars[5]
226 // Azimuthal angle of the line joining the center of the faces at -dz and +dz in z
227 ), log);
228 return thisG4Volume->getSolid();
229 }
230 else if (type == "G4Trd") {
231 thisG4Volume->setSolid(new G4Trd(g4name, // name
232 pars[0], // Half-length along x at the surface positioned at -dz
233 pars[1], // Half-length along x at the surface positioned at +dz
234 pars[2], // Half-length along y at the surface positioned at -dz
235 pars[3], // Half-length along y at the surface positioned at +dz
236 pars[4] // Half-length along z axis
237 ), log);
238 return thisG4Volume->getSolid();
239 }
240 else if (type == "G4Trap") {
241 // G4Trap supports multiple constructor layouts; parameter count decides which one is used.
242 if (pars.size() == 4) {
243 thisG4Volume->setSolid(new G4Trap(g4name, // name
244 pars[0], // Length along Z
245 pars[1], // Length along Y
246 pars[2], // Length along X wider side
247 pars[3] // Length along X at the narrower side (plTX<=pX)
248 ), log);
249 }
250 else if (pars.size() == 11) {
251 thisG4Volume->setSolid(new G4Trap(g4name, // name
252 pars[0], // Half Z length - distance from the origin to the bases
253 pars[1],
254 // Polar angle of the line joining the center of the bases at -/+pDz
255 pars[2], // Azimuthal angle of the same line
256 pars[3], // Half Y length of the base at -pDz
257 pars[4], // Half Y length of the base at +pDz
258 pars[5], // Half X length at smaller Y of the base at -pDz
259 pars[6], // Half X length at bigger Y of the base at -pDz
260 pars[7], // Half X length at smaller Y of the base at +pDz
261 pars[8], // Half X length at bigger y of the base at +pDz
262 pars[9], // Angle between Y-axis and center line at -pDz
263 pars[10] // Angle between Y-axis and center line at +pDz
264 ), log);
265 }
266 else if (pars.size() == 24) {
267 G4ThreeVector pt[8];
268 pt[0] = G4ThreeVector(pars[0], pars[1], pars[2]);
269 pt[1] = G4ThreeVector(pars[3], pars[4], pars[5]);
270 pt[2] = G4ThreeVector(pars[6], pars[7], pars[8]);
271 pt[3] = G4ThreeVector(pars[9], pars[10], pars[11]);
272 pt[4] = G4ThreeVector(pars[12], pars[13], pars[14]);
273 pt[5] = G4ThreeVector(pars[15], pars[16], pars[17]);
274 pt[6] = G4ThreeVector(pars[18], pars[19], pars[20]);
275 pt[7] = G4ThreeVector(pars[21], pars[22], pars[23]);
276
277 thisG4Volume->setSolid(new G4Trap(g4name, pt), log);
278 }
279 else {
280 log->error(
282 "The constructor of <%1> must have 4, 11 or 24 parameters see "
283 "https://geant4-userdoc.web.cern.ch/UsersGuides/ForApplicationDeveloper/html/Detector/"
284 "Geometry/geomSolids.html", g4name);
285 }
286 return thisG4Volume->getSolid();
287 }
288 else if (type == "G4Polycone") {
289 double phistart = pars[0];
290 double phitotal = pars[1];
291 int zplanes = static_cast<int>(pars[2]);
292
293 // Allocate arrays (data is copied by G4Polycone during construction).
294 auto zPlane = std::make_unique<double[]>(zplanes);
295 auto rInner = std::make_unique<double[]>(zplanes);
296 auto rOuter = std::make_unique<double[]>(zplanes);
297
298 for (int zpl = 0; zpl < zplanes; ++zpl) {
299 zPlane[zpl] = pars[3 + 0 * zplanes + zpl];
300 rInner[zpl] = pars[3 + 1 * zplanes + zpl];
301 rOuter[zpl] = pars[3 + 2 * zplanes + zpl];
302 }
303
304 thisG4Volume->setSolid(new G4Polycone(g4name, // name
305 phistart, // Initial Phi starting angle
306 phitotal, // Total Phi angle
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 == "G4Polyhedra") {
315 // GEMC2 "Pgon" parameter order: phiStart, phiTotal, numSides, numZPlanes,
316 // then rInner[], rOuter[], zPlane[].
317 double phistart = pars[0];
318 double phitotal = pars[1];
319 int numSides = static_cast<int>(pars[2]);
320 int zplanes = static_cast<int>(pars[3]);
321
322 if (numSides < 1 || static_cast<int>(pars.size()) != 4 + 3 * zplanes) {
323 log->error(
325 "The constructor of <%1> must have numSides >= 1 and %2 parameters (4 + 3 x numZPlanes), we "
326 "got %3", g4name, 4 + 3 * zplanes, pars.size());
327 }
328
329 // Allocate arrays (data is copied by G4Polyhedra during construction).
330 auto zPlane = std::make_unique<double[]>(zplanes);
331 auto rInner = std::make_unique<double[]>(zplanes);
332 auto rOuter = std::make_unique<double[]>(zplanes);
333
334 for (int zpl = 0; zpl < zplanes; ++zpl) {
335 rInner[zpl] = pars[4 + 0 * zplanes + zpl];
336 rOuter[zpl] = pars[4 + 1 * zplanes + zpl];
337 zPlane[zpl] = pars[4 + 2 * zplanes + zpl];
338 }
339
340 thisG4Volume->setSolid(new G4Polyhedra(g4name, // name
341 phistart, // Initial Phi starting angle
342 phitotal, // Total Phi angle
343 numSides, // Number of sides
344 zplanes, // Number of z planes
345 zPlane.get(),
346 rInner.get(),
347 rOuter.get()
348 ), log);
349 return thisG4Volume->getSolid();
350 }
351 else if (type == "G4Paraboloid") {
352 thisG4Volume->setSolid(new G4Paraboloid(g4name, // name
353 pars[0], // half-length in z
354 pars[1], // radius at -dz
355 pars[2] // radius at +dz
356 ), log);
357 return thisG4Volume->getSolid();
358 }
359 else if (type == "G4EllipticalTube") {
360 thisG4Volume->setSolid(new G4EllipticalTube(g4name, // name
361 pars[0], // half length in x
362 pars[1], // half length in y
363 pars[2] // half length in z
364 ), log);
365 return thisG4Volume->getSolid();
366 }
367 else if (type == "G4Ellipsoid") {
368 thisG4Volume->setSolid(new G4Ellipsoid(g4name, // name
369 pars[0], // semi-axis in x
370 pars[1], // semi-axis in y
371 pars[2], // semi-axis in z
372 pars[3], // lower cut in z (0 = no cut)
373 pars[4] // upper cut in z (0 = no cut)
374 ), log);
375 return thisG4Volume->getSolid();
376 }
377 else {
378 log->error(
380 "The constructor of <%1> uses an unknown solid type <%2>. See Geant4 manual for supported "
381 "primitives.", g4name, type);
382 }
383 return nullptr;
384}
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:35
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)