8.2. Boolean Solids
Table of Contents
Union
Boolean solids in Geant4 let you build complex detector shapes by combining simple solids through set–like operations on their volumes. The union operation creates a new solid that occupies the volume of both input solids together. This is very useful when your detector module is built as a fixed combination of standard parts, for example a crystal glued to a light guide or a mechanical support that is permanently attached.
In Geant4, unions are implemented by the class G4UnionSolid. A G4UnionSolid is not used directly as a logical or physical volume. It is a G4VSolid subclass, so you typically use it when defining the shape that you will then wrap inside a logical volume.
A simple construction pattern looks like this in C++:
auto solidA = new G4Box("SolidA", 5*cm, 5*cm, 5*cm);
auto solidB = new G4Tubs("SolidB", 0.*cm, 2*cm, 5*cm, 0.*deg, 360.*deg);
G4ThreeVector offsetB(0., 0., 5*cm); // position B relative to A
auto unionAB = new G4UnionSolid("UnionAB", solidA, solidB, nullptr, offsetB);
The new unionAB solid contains every point that is inside solidA, or inside solidB, or inside both. In set notation, if $V_A$ and $V_B$ are the volumes of the two solids, the union volume is $V_A \cup V_B$.
You can also use a rotation for the second solid by passing a rotation matrix instead of nullptr. The translation and rotation always define the placement of the second solid relative to the first solid. The first solid is always taken as the reference, with no additional transform applied in the Boolean operation itself.
You can nest unions. For example, you can create two separate unions and then again unite the results into an even more complex solid. In this case, each intermediate Boolean solid behaves like any other solid in Geant4:
auto unionAB = new G4UnionSolid("UnionAB", solidA, solidB, nullptr, offsetB);
auto unionABC = new G4UnionSolid("UnionABC", unionAB, solidC, nullptr, offsetC);Once you have a union solid, you can create a logical volume and then place it somewhere in your detector hierarchy:
auto logicUnion = new G4LogicalVolume(unionAB, someMaterial, "LogicUnion");The material you assign to the logical volume is used for the entire union, even though it comes from two or more original solids. If you need different materials in different parts, you cannot use a single union solid. Instead, you must keep those parts as separate logical volumes and place them individually.
Computation inside Boolean solids costs more CPU time than for simple primitives. Each tracking step inside a Boolean solid involves more complex geometry queries. For a small number of such solids, this is usually not a problem, but filling a large detector array with very complex Boolean shapes can slow the simulation down significantly.
In a G4UnionSolid, the resulting volume is the complete set of points that belong to either input solid:
$$V_{\text{union}} = V_A \cup V_B.$$
All parts of the union share a single material defined by the logical volume that uses the Boolean solid. You cannot assign different materials to different parts of the same Boolean solid.
Subtraction
Subtraction lets you remove part of one solid using another solid as a cutter. This is extremely useful for features like holes, cavities, grooves, and slots. For example, a cylindrical hole through a box or a recess inside a crystal can be created with a subtraction operation rather than with separate overlapping volumes.
Geant4 provides the G4SubtractionSolid class for this purpose. The first solid is often called the mother or the base solid. The second solid is the tool or the subtracted solid. A typical example is:
auto box = new G4Box("Box", 5*cm, 5*cm, 5*cm);
auto hole = new G4Tubs("Hole", 0.*cm, 1*cm, 6*cm, 0.*deg, 360.*deg);
G4ThreeVector holePos(0., 0., 0.);
auto boxWithHole = new G4SubtractionSolid("BoxWithHole", box, hole, nullptr, holePos);In this case, the resulting solid is the box with a cylindrical hole removed. Mathematically, if $V_{\text{box}}$ is the box volume and $V_{\text{hole}}$ is the cylindrical volume, then
$$V_{\text{subtraction}} = V_{\text{box}} \setminus V_{\text{hole}}.$$
You must carefully choose the position and rotation of the second solid relative to the first, because that determines which region gets removed. If the second solid does not overlap the first at all, the subtraction has no effect. If the second solid covers the first entirely, you can remove the entire base solid, which usually does not make sense for a detector.
A more explicit form with rotation looks like this:
auto base = new G4Box("Base", 5*cm, 5*cm, 5*cm);
auto groove = new G4Box("Groove", 2*cm, 1*cm, 6*cm);
auto rot = new G4RotationMatrix();
rot->rotateY(45*deg);
G4ThreeVector groovePos(0., 0., 0.);
auto baseWithGroove = new G4SubtractionSolid("BaseWithGroove", base, groove, rot, groovePos);The rotation matrix and translation are again applied to the second solid. You can subtract several shapes step by step, which is common when defining complex holders or housings:
auto solid1 = new G4SubtractionSolid("Step1", base, cutter1, nullptr, pos1);
auto solid2 = new G4SubtractionSolid("Step2", solid1, cutter2, nullptr, pos2);As with unions, the resulting subtraction solid can then be wrapped in a logical volume and placed in the geometry. The material of that logical volume is used everywhere in the remaining volume. The removed parts are truly empty for that logical volume. If another volume later occupies the space that has been removed, that other volume defines the material there.
Boolean subtractions are conceptually attractive because they mirror how parts are machined in the real world, but they can also create very thin or complex surfaces. These may occasionally lead to numerical problems in navigation, especially if the subtracted and base solids share coincident or nearly coincident surfaces.
To reduce problems, slightly extend the cutting solid so that it fully crosses the base solid rather than stopping exactly at its surface, and avoid creating extremely thin leftover regions if they are not physically important.
In a G4SubtractionSolid, the resulting volume is all points that are inside the base solid but not inside the cutting solid:
$$V_{\text{subtraction}} = V_{\text{base}} \setminus V_{\text{cut}}.$$
Ensure that the cutting solid overlaps the base solid by a comfortable margin to avoid coincident surfaces and potential geometry navigation issues.
Intersection
Intersection creates a new solid that occupies only the region common to both input solids. This is useful for shapes that can be described as the overlap of two simpler solids, for example a lens limited by a cylinder, a crystal segment defined as the overlap of a wedge and a cylinder, or a detector volume that is restricted to a particular region.
Geant4 uses the G4IntersectionSolid class to represent such operations. The typical construction looks like this:
auto cylinder = new G4Tubs("Cylinder", 0.*cm, 5*cm, 5*cm, 0.*deg, 360.*deg);
auto box = new G4Box("Box", 3*cm, 3*cm, 10*cm);
G4ThreeVector boxPos(0., 0., 0.);
auto overlap = new G4IntersectionSolid("Overlap", cylinder, box, nullptr, boxPos);
The result, overlap, contains only the points that are inside both the cylinder and the box at the same time. If $V_1$ and $V_2$ are the individual volumes, the intersection is
$$V_{\text{intersection}} = V_1 \cap V_2.$$
You again control the relative placement of the second solid with a rotation and translation. The first solid is kept fixed in its own coordinate system, and the second solid is rotated and translated to intersect it in the desired region. Moving or rotating one of the solids relative to the other is what changes the resulting intersected shape:
auto rot = new G4RotationMatrix();
rot->rotateX(30*deg);
G4ThreeVector shifted(0., 0., 2*cm);
auto tiltedOverlap = new G4IntersectionSolid("TiltedOverlap", cylinder, box, rot, shifted);This approach is especially powerful when a detector segment is geometrically defined as the region of one shape that lies inside another. Instead of trying to compute a custom shape analytically, you can compose it by intersection. Intersection can also be combined with unions and subtractions, because every Boolean solid can itself be used as an operand in another Boolean operation.
For example, you might start with a union, then cut a part away, then intersect the result with a limiting volume:
auto unionAB = new G4UnionSolid("UnionAB", solidA, solidB, nullptr, posB);
auto sub = new G4SubtractionSolid("Sub", unionAB, cutSolid, nullptr, cutPos);
auto finalShape = new G4IntersectionSolid("FinalShape", sub, limitSolid, nullptr, limitPos);
As usual, once you have finalShape, you create a logical volume and place it in the world or in some mother volume. The material applies to the entire intersection region.
Boolean intersections, like other Boolean operations, can make navigation harder if they create very narrow spikes or sliver regions. Whenever possible, keep the intersection geometry reasonably simple and avoid exact coincidences of surfaces. Diagnostics such as the geometry overlap check can help verify that the final shapes behave as expected.
A G4IntersectionSolid keeps only the common volume of its two input solids:
$$V_{\text{intersection}} = V_1 \cap V_2.$$
The final intersected solid must have a nonzero volume. If you choose positions or rotations so that the two solids do not overlap at all, the intersection is empty and cannot be used as a valid detector volume.
Views: 8
KAHIBARO