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, className(), "G4NativeSystemFactory::buildSolid for ", 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 {
136 "The boolean constructor of <", g4name, "> uses unsupported operator <",
137 solidOperations[1], ">. Use +, -, or *.");
138 }
139 return thisG4Volume->getSolid();
140 }
142 "The boolean constructor of <", g4name, "> must be: left operator right.");
143 return nullptr;
144 }
145
146 // Parse and validate parameters for the requested primitive.
147 std::vector<double> pars = checkAndReturnParameters(s);
148
149 std::string type = s->getType();
150
151 if (type == "G4Box") {
152 thisG4Volume->setSolid(new G4Box(g4name, // name
153 pars[0], // half-length in X
154 pars[1], // half-length in Y
155 pars[2] // half-length in Z
156 ), log);
157 return thisG4Volume->getSolid();
158 }
159 else if (type == "G4Tubs") {
160 thisG4Volume->setSolid(new G4Tubs(g4name, // name
161 pars[0], // Inner radius
162 pars[1], // Outer radius
163 pars[2], // half-length in z
164 pars[3], // Starting phi angle
165 pars[4] // Delta Phi angle
166 ), log);
167 return thisG4Volume->getSolid();
168 }
169 else if (type == "G4Sphere") {
170 thisG4Volume->setSolid(new G4Sphere(g4name, // name
171 pars[0], // Inner radius
172 pars[1], // Outer radius
173 pars[2], // Starting phi angle
174 pars[3], // Delta Phi angle
175 pars[4], // Starting delta angle
176 pars[5] // Delta delta angle
177 ), log);
178 return thisG4Volume->getSolid();
179 }
180 else if (type == "G4Torus") {
181 thisG4Volume->setSolid(new G4Torus(g4name, // name
182 pars[0], // Inside radius of the torus tube
183 pars[1], // Outside radius of the torus tube
184 pars[2], // Swept radius of the torus
185 pars[3], // Starting phi angle
186 pars[4] // Delta phi angle
187 ), log);
188 return thisG4Volume->getSolid();
189 }
190 else if (type == "G4CutTubs") {
191 thisG4Volume->setSolid(new G4CutTubs(g4name, // name
192 pars[0], // Inner radius
193 pars[1], // Outer radius
194 pars[2], // half-length in z
195 pars[3], // Starting phi angle
196 pars[4], // Delta Phi angle
197 G4ThreeVector(pars[5], pars[6], pars[7]), // Outside Normal at -z
198 G4ThreeVector(pars[8], pars[9], pars[10]) // Outside Normal at +z
199 ), log);
200 return thisG4Volume->getSolid();
201 }
202 else if (type == "G4Cons") {
203 thisG4Volume->setSolid(new G4Cons(g4name, // name
204 pars[0], // Inside radius at -pDz
205 pars[1], // Outside radius at -pDz
206 pars[2], // Inside radius at +pDz
207 pars[3], // Outside radius at +pDz
208 pars[4], // half-length in z
209 pars[5], // Starting phi angle
210 pars[6] // Delta Phi angle
211 ), log);
212 return thisG4Volume->getSolid();
213 }
214 else if (type == "G4Para") {
215 thisG4Volume->setSolid(new G4Para(g4name, // name
216 pars[0], // half-length in x
217 pars[1], // half-length in y
218 pars[2], // half-length in z
219 pars[3],
220 // 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
221 pars[4],
222 // Polar angle of the line joining the center of the faces at -dz and +dz in z
223 pars[5]
224 // Azimuthal angle of the line joining the center of the faces at -dz and +dz in z
225 ), log);
226 return thisG4Volume->getSolid();
227 }
228 else if (type == "G4Trd") {
229 thisG4Volume->setSolid(new G4Trd(g4name, // name
230 pars[0], // Half-length along x at the surface positioned at -dz
231 pars[1], // Half-length along x at the surface positioned at +dz
232 pars[2], // Half-length along y at the surface positioned at -dz
233 pars[3], // Half-length along y at the surface positioned at +dz
234 pars[4] // Half-length along z axis
235 ), log);
236 return thisG4Volume->getSolid();
237 }
238 else if (type == "G4Trap") {
239 // G4Trap supports multiple constructor layouts; parameter count decides which one is used.
240 if (pars.size() == 4) {
241 thisG4Volume->setSolid(new G4Trap(g4name, // name
242 pars[0], // Length along Z
243 pars[1], // Length along Y
244 pars[2], // Length along X wider side
245 pars[3] // Length along X at the narrower side (plTX<=pX)
246 ), log);
247 }
248 else if (pars.size() == 11) {
249 thisG4Volume->setSolid(new G4Trap(g4name, // name
250 pars[0], // Half Z length - distance from the origin to the bases
251 pars[1],
252 // Polar angle of the line joining the center of the bases at -/+pDz
253 pars[2], // Azimuthal angle of the same line
254 pars[3], // Half Y length of the base at -pDz
255 pars[4], // Half Y length of the base at +pDz
256 pars[5], // Half X length at smaller Y of the base at -pDz
257 pars[6], // Half X length at bigger Y of the base at -pDz
258 pars[7], // Half X length at smaller Y of the base at +pDz
259 pars[8], // Half X length at bigger y of the base at +pDz
260 pars[9], // Angle between Y-axis and center line at -pDz
261 pars[10] // Angle between Y-axis and center line at +pDz
262 ), log);
263 }
264 else if (pars.size() == 24) {
265 G4ThreeVector pt[8];
266 pt[0] = G4ThreeVector(pars[0], pars[1], pars[2]);
267 pt[1] = G4ThreeVector(pars[3], pars[4], pars[5]);
268 pt[2] = G4ThreeVector(pars[6], pars[7], pars[8]);
269 pt[3] = G4ThreeVector(pars[9], pars[10], pars[11]);
270 pt[4] = G4ThreeVector(pars[12], pars[13], pars[14]);
271 pt[5] = G4ThreeVector(pars[15], pars[16], pars[17]);
272 pt[6] = G4ThreeVector(pars[18], pars[19], pars[20]);
273 pt[7] = G4ThreeVector(pars[21], pars[22], pars[23]);
274
275 thisG4Volume->setSolid(new G4Trap(g4name, pt), log);
276 }
277 else {
279 "The constructor of <", g4name, "> must have 4, 11 or 24 parameters",
280 " see https://geant4-userdoc.web.cern.ch/UsersGuides/ForApplicationDeveloper/html/Detector/Geometry/geomSolids.html");
281 }
282 return thisG4Volume->getSolid();
283 }
284 else if (type == "G4Polycone") {
285 double phistart = pars[0];
286 double phitotal = pars[1];
287 int zplanes = static_cast<int>(pars[2]);
288
289 // Allocate arrays (data is copied by G4Polycone during construction).
290 auto zPlane = std::make_unique<double[]>(zplanes);
291 auto rInner = std::make_unique<double[]>(zplanes);
292 auto rOuter = std::make_unique<double[]>(zplanes);
293
294 for (int zpl = 0; zpl < zplanes; ++zpl) {
295 zPlane[zpl] = pars[3 + 0 * zplanes + zpl];
296 rInner[zpl] = pars[3 + 1 * zplanes + zpl];
297 rOuter[zpl] = pars[3 + 2 * zplanes + zpl];
298 }
299
300 thisG4Volume->setSolid(new G4Polycone(g4name, // name
301 phistart, // Initial Phi starting angle
302 phitotal, // Total Phi angle
303 zplanes, // Number of z planes
304 zPlane.get(),
305 rInner.get(),
306 rOuter.get()
307 ), log);
308 return thisG4Volume->getSolid();
309 }
310 else if (type == "G4Polyhedra") {
311 // GEMC2 "Pgon" parameter order: phiStart, phiTotal, numSides, numZPlanes,
312 // then rInner[], rOuter[], zPlane[].
313 double phistart = pars[0];
314 double phitotal = pars[1];
315 int numSides = static_cast<int>(pars[2]);
316 int zplanes = static_cast<int>(pars[3]);
317
318 if (numSides < 1 || static_cast<int>(pars.size()) != 4 + 3 * zplanes) {
320 "The constructor of <", g4name, "> must have numSides >= 1 and ",
321 4 + 3 * zplanes, " parameters (4 + 3 x numZPlanes), we got ", pars.size());
322 }
323
324 // Allocate arrays (data is copied by G4Polyhedra during construction).
325 auto zPlane = std::make_unique<double[]>(zplanes);
326 auto rInner = std::make_unique<double[]>(zplanes);
327 auto rOuter = std::make_unique<double[]>(zplanes);
328
329 for (int zpl = 0; zpl < zplanes; ++zpl) {
330 rInner[zpl] = pars[4 + 0 * zplanes + zpl];
331 rOuter[zpl] = pars[4 + 1 * zplanes + zpl];
332 zPlane[zpl] = pars[4 + 2 * zplanes + zpl];
333 }
334
335 thisG4Volume->setSolid(new G4Polyhedra(g4name, // name
336 phistart, // Initial Phi starting angle
337 phitotal, // Total Phi angle
338 numSides, // Number of sides
339 zplanes, // Number of z planes
340 zPlane.get(),
341 rInner.get(),
342 rOuter.get()
343 ), log);
344 return thisG4Volume->getSolid();
345 }
346 else if (type == "G4Paraboloid") {
347 thisG4Volume->setSolid(new G4Paraboloid(g4name, // name
348 pars[0], // half-length in z
349 pars[1], // radius at -dz
350 pars[2] // radius at +dz
351 ), log);
352 return thisG4Volume->getSolid();
353 }
354 else if (type == "G4EllipticalTube") {
355 thisG4Volume->setSolid(new G4EllipticalTube(g4name, // name
356 pars[0], // half length in x
357 pars[1], // half length in y
358 pars[2] // half length in z
359 ), log);
360 return thisG4Volume->getSolid();
361 }
362 else if (type == "G4Ellipsoid") {
363 thisG4Volume->setSolid(new G4Ellipsoid(g4name, // name
364 pars[0], // semi-axis in x
365 pars[1], // semi-axis in y
366 pars[2], // semi-axis in z
367 pars[3], // lower cut in z (0 = no cut)
368 pars[4] // upper cut in z (0 = no cut)
369 ), log);
370 return thisG4Volume->getSolid();
371 }
372 else {
374 "The constructor of <", g4name, "> uses an unknown solid type <", type,
375 ">. See Geant4 manual for supported primitives.");
376 }
377 return nullptr;
378}
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)