Constructing solids and logical volumes

Header: <n4-shapes.hh>

Summary

Constructing a G4VSolid

auto ball = n4::sphere("ball").r(1.2*m).solid();
auto radius = 1.2*m;
auto ball   = new G4Sphere("ball", 0, radius, 0, CLHEP::twopi, 0, CLHEP::pi);

(In this specific example, n4::sphere notices that it would be more efficient to create a G4Orb instead of a G4Sphere and does that for you automatically.)

Constructing a G4LogicalVolume

Frequently, after having made a G4VSolid you immediately use it to make a G4LogicalVolume with the same name. nain4 allows you to do this in a single step:

auto copper = n4::material("G4_Cu");
auto ball   = n4::sphere("ball").r(1.2*m).volume(copper);
auto copper = G4NistManager::Instance() -> FindOrBuildMaterial("G4_Cu");
auto radius = 1.2*m;
auto ball_solid = new G4Sphere("ball", 0, radius, 0, CLHEP::twopi, 0, CLHEP::pi);
auto ball = new G4VLogicalVolume(ball_solid, copper, "ball");

If you need to do this as two separate steps

auto copper     = n4::material("G4_Cu");
auto ball_solid = n4::sphere("ball").r(1.2*m).solid();
auto ball       = n4::volume(ball_solid, copper);

Not all G4VSolids are supported by the nain4::shape interface, yet. In such cases n4::volume lets you combine the construction of a solid and corresponding logical volume in a single step. Here is how you could do it if n4::sphere did not exist:

auto copper = n4::material("G4_Cu");
auto radius = 1.2*m;
auto ball   = n4::volume<G4Sphere>("ball", copper, 0, radius, 0, CLHEP::twopi, 0, CLHEP::pi);

The arguments passed after the name and material, are forwarded to the specified G4VSolid's constructor.

Placing a volume

Should you want to place your G4LogicalVolume immediately, nain4 allows you to include this in a single step, too:

auto safe = ...
auto gold = n4::material("G4_Au");
n4::box("nugget").cube(2*cm).place(gold).in(safe).now();
auto safe_solid = ...
auto safe = ...
auto gold = G4NistManager::Instance() -> FindOrBuildMaterial("G4_Au");
auto nugget_solid = new G4Box("nugget", 2*cm/2, 2*cm/2, 2*cm/2);
auto nugget_logical = new G4VLogicalVolume(nugget_solid, gold, "nugget");
new G4PVPlacement(nullptr, {}, nugget_logical, "nugget", safe, false, 0);

However, frequently you need to keep a handle to the logical volume, in order to be able to place things into it later. In such cases you would break this into two separate steps:

auto safe   = ...
auto gold   = n4::material("G4_Au");
auto nugget = n4::box("nugget").cube(2*cm).volume(gold);
n4::place(nugget).in(safe).now();

nain4's placement utilities have a rich interface, which is described in Placement of physical volumes.

Summary

G4VSolid        * s = n4::SOLID("name", ...).solid();
G4VLogicalVolume* v = n4::SOLID("name", ...).volume(material);
G4PVPlacement   * p = n4::SOLID("name", ...).place (material).in(volume) ... .now();

Specifying Dimensions

The G4VSolids, and hence also the n4::shapes, are parameterized by combinations of four principal types of coordinate

  1. Cartesian lengths, x, y and z
  2. Radial length, r
  3. Azimuthal angle, φ
  4. Polar angle, θ

nain4 provides a consistent set of methods for setting these, in any n4::shape that uses them. These methods are described here, the n4::shapes are described in the next section. All the methods provided by nain4 have short, but explicit names. This removes the need for the user to remember the order of the arguments while highlighting their meaning.

Cartesian lengths

The principal methods for setting Cartesian lengths are

  • x
  • half_x

and their equivalents for y and z.

Explicit names

Unlike Geant4, nain4 favours the use of full lengths instead of half-lengths. Using half-lengths creates noise by either introducing /2 operations continuously or by defining variables with long names to indicate the subtle but important distinction between half- and full-lengths. For instance, in order to create a cube of side 1 m in pure G4:

auto box_half_length = 0.5*m;
G4Box* box1 = new G4Box("box", box_half_length,  box_half_length,  box_half_length);
// or
auto box_length = 1*m;
G4Box* box2 = new G4Box("box", box_length/2,  box_length/2,  box_length/2);

In contrast, in nain4 one can simply write

auto box = n4::box("box").cube(1*m);

Overriding

If you set a Cartesian length more than once in the same shape, the last setting overrides previous ones. For example:

.x(1*m).half_x(3*m)  // `x` set to 6 m
.x(1*m).     x(3*m)  // `x` set to 3 m

Convenient alternatives

n4::shapes which depend on more than one Cartesian length, typically provide extra methods for setting various combinations, for example n4::box offers extra methods cube, xyz, xy, xz and yz along with their half_ variants.

Radial length: r

Three methods are provided for specifying the two degrees of freedom in radial lengths:

  • r_inner
  • r_delta
  • r

with the constraint r = r_inner + r_delta. Thus valid combinations of these methods are

Methods used Implied value
r_innerr_deltar
r 0 r
r_inner r r - r_inner
r_deltar r - r_delta
r_innerr_delta 0 r_inner + r_delta

Providing too few or too many values results in a run-time error.

Some shapes, such as n4::cons (G4Cons), have multiple radii. In such cases the method names acquire a number, to distinguish between them r* -> r1*, r2*.

Azimuthal angle: φ

Three methods are provided for specifying the two degrees of freedom in azimuthal angles:

  • phi_start
  • phi_delta
  • phi_end

with the constraint phi_end = phi_start + phi_delta. Thus valid combinations of these methods are

Methods used Implied value
phi_startphi_deltaphi_end
0
phi_start 2π - start
phi_delta 0 δ
phi_end 0 end
phi_deltaphi_end end - δ
phi_start phi_end end - start
phi_startphi_delta start + δ

Providing too few or too many values results in a run-time error.

Polar angle: θ

Three methods are provided for specifying the two degrees of freedom in polar angles:

  • theta_start
  • theta_delta
  • theta_end

with the constraint theta_end = theta_start + theta_delta. Thus valid combinations of these methods are

Methods used Implied value
theta_starttheta_deltatheta_end
0 π π
theta_start π - start π
theta_delta 0 δ
theta_end 0 end
theta_deltatheta_end end - δ
theta_start theta_end end - start
theta_starttheta_delta start + δ

Providing too few or too many values results in a run-time error.

Common methods

All n4::SOLIDs share the following methods:

Available Shapes

n4::box

Constructs G4Box: cuboid with side lengths x, y and z. Within its frame of reference it is centred on the origin with sides parallel to the x/y/z axes. Displacements and rotations can be applied with .place(material).

Example

G4Box* box = n4::box("box").xz(10*cm).y(50*cm).solid();
auto cross_section_length = 10*cm, y_length = 50*cm;
G4Box* box = new G4Box("box", cross_section_length/2, y_length/2, cross_section_length/2);

Methods

Full-length methods

All these methods take full (as opposed to half-) lengths:

  • x(lx), y(ly), z(ly): set one dimension.
  • xy(l), xz(l), yz(l): set two dimensions to the same value.
  • xyz(lx, ly, lz), xyz(g4-three-vector): set three dimensions independently
  • cube(l): set all dimensions to the same value.

Note the, perhaps surprising, difference between .xyz() and the .xy()-.xz()-.yz() triumvirate: The latter assign a single value to multiple coordinates; the former accepts a separate value for each coordinate it sets.

Half-length methods

All the aforementioned full-length methods have alternatives which accept half-lengths: half_x(lx/2), half_cube(l/2), half_xy(lxy), etc.

Overriding

If any value is specified more than once, the last setting overrides any earlier ones. Thus, the following three lines are equivalent.

.cube(1*m          ).z(2*m)
.xyz (1*m, 1*m, 1*m).z(2*m)
.xy  (1*m          ).z(2*m)

While the first two work, the last one states the intent most clearly.

See the sections about setting Cartesian lengths for more details.

n4::sphere

Constructs G4Sphere or G4Orb, depending on values provided.

  • G4Sphere: section of a spherical shell, between specified azimuthal (φ) and polar (θ) angles. Within its frame of reference, φ is measured counterclockwise WRT the x-axis when viewed from positive z; θ is measured WRT positive z. Displacements and rotations can be applied with .place(material).
  • G4Orb: special case of G4Sphere.

Examples

A solid sphere

G4Orb* ball = n4::sphere("ball").r(1*m).solid();
G4Orb* ball = new G4Orb("ball", 1*m);

thus nain4 helps you avoid the common mistake of creating an equivalent (but less efficient) G4Sphere instead

G4Sphere* ball = new G4Sphere("ball", 0, 1*m, 0, CLHEP::twopi, 0, CLHEP::pi);

A hollow sphere

G4Sphere* hollow = n4::sphere("hollow").r(2*m).r_delta(10*cm).solid();
auto outer = 2*m, thickness = 10*cm;
G4Sphere* hollow = new G4Sphere("hollow", outer - thickness, outer, 0, CLHEP::twopi, 0, CLHEP::pi);

A spherical wedge

G4Sphere* wedge = n4::sphere("wedge").r(1*m).phi_start(20*deg).phi_end(30*deg).solid();
auto start_phi = 20*deg, end_phi = 30*deg;
G4Sphere* wedge = new G4Sphere("wedge", 0, 1*m, start_phi, end_phi - start_phi, 0, CLHEP::pi);

Methods

  • r_inner(l)
  • r_delta(l)
  • r(l)
  • phi_start(a)
  • phi_delta(a)
  • phi_end(a)
  • theta_start(a)
  • theta_delta(a)
  • theta_end(a)

See the sections about setting radial lengths, azimuthal angles and polar angles for more details.

n4::tubs

Constructs G4Tubs: tube or tube segment. Within its frame of reference, it is parallel to and centred on the z-axis; φ is measured counterclockwise WRT the x-axis when viewed from positive z. Displacements and rotations can be applied with .place(material).

Examples

A solid cylinder

G4Tubs* cylinder = n4::tubs("cylinder").r(1*m).z(2*m).solid();
auto radius   = 1*m;
auto z_length = 2*m;
G4Tubs* cylinder = new G4Tubs("cylinder", 0, radius, z_length/2, 0, CLHEP::twopi);

A tube

G4Tubs* tube = n4::tubs("tube").r(1*m).r_delta(10*cm).z(2*m).solid();
auto z_length = 2*m, outer_radius = 1*m, thickness = 10*cm;
G4Tubs* tube = new G4Tubs("tube", outer_radius - thickness, 1*m, z_length/2, 0, CLHEP::twopi);

A cylindrical wedge

G4Tubs* wedge = n4::tubs("wedge").r(1*m).z(2*m).phi_start(20*deg).phi_end(30*deg).solid();
auto z_length = 2*m, radius = 1*m, start_phi = 20*deg, end_phi = 30*deg;
G4Tubs* wedge = new G4Tubs("wedge", 0, radius, z_length/2, start_phi, end_phi - start_phi);

Methods

  • z()
  • half_z()
  • r_inner(l)
  • r_delta(l)
  • r(l)
  • phi_start(a)
  • phi_delta(a)
  • phi_end(a)

See the sections about setting Cartesian lengths, radial lengths and azimuthal angles for more details.

n4::trd

Constructs G4Trd: frustrum (a.k.a. truncated pyramid) with two parallel rectangular faces and four trapezoidal faces. The rectangular faces are perpendicular to the z axis and their sides are parallel to the x and y axes. The bottom face (placed at negative z) has side lengths x1, y1, and the top face (placed at positive z) has side lengths x2, y2. Both faces are separated by a distance z. Within its frame of reference it is centred on the origin. Displacements and rotations can be applied with .place(material).

Example

G4Trd* trd = n4::trd("trd").xy1(10*cm).xy2(5*cm).z(50*cm).solid();
auto cross_section_bottom = 10*cm, cross_section_top = 5*cm, z_length = 50*cm;
G4Trd* trd = new G4Trd("trd", cross_section_bottom/2, cross_section_top/2, cross_section_bottom/2, cross_section_top/2, z_length/2);

Methods

Full-length methods

All these methods take full (as opposed to half-) lengths:

  • x1(lx), x2(ly), y1(lx), y2(ly): set one dimension of one rectangular face. 1 Refers to the bottom face, while 2 refers to the top face.
  • xy1(l), xy2(l): set both dimensions of one face. 1 Refers to the bottom face, while 2 refers to the top face.
  • z(l): set the distance between the two parallel faces.
Half-length methods

All the aforementioned full-length methods have alternatives which accept half-lengths: half_x1(lx/2), half_xy2(lxy/2), half_z(lz/2), etc.

See the sections about setting Cartesian lengths for more details.

n4::cons

Constructs G4Cons: truncated cone or cone section. Within its frame of reference, it is parallel to and centred on the z-axis; φ is measured counterclockwise WRT the x-axis when viewed from positive z. Displacements and rotations can be applied with .place(material).

Examples

A solid truncated cone

G4Tubs* solid_cone = n4::tubs("solid_cone").r1(1*m).r2(2*m).z(3*m).solid();
auto radius_1 = 1*m, radius_2 = 2*m, z_length = 3*m;
G4Cons* solid_cone = new G4Cons("solid_cone", 0, radius_1, 0, radius_2, z_length/2, 0, CLHEP::twopi);

A hollow cone

G4Cone* hollow_cone = n4::cone("hollow_cone").r1(1*m).r2(2*cm).r_delta(10*cm).z(3*m).solid();
auto radius_1 = 1*m, radius_2 = 2*m, thickness = 10*cm, z_length = 3*m;
G4Cons* hollow_cone = new G4Cons("hollow_cone", radius_1 - thickness, radius_1, radius_2 - thickness, radius_2, z_length/2, 0, CLHEP::twopi);

A conical wedge

G4Cons* wedge = n4::cons("wedge").r1(1*m).r2(2*m).z(3*m).phi_start(20*deg).phi_end(30*deg).solid();
auto radius_1 = 1*m, radius_2 = 2*m, z_length = 3*m, start_phi = 20*deg, end_phi = 30*deg;
G4Cons* wedge = new G4Cons("wedge", 0, radius_1, 0, radius_2, z_length/2, start_phi, end_phi - start_phi);

Methods

  • z()
  • half_z()
  • r1_inner(l), r1_delta(l), r1(l): Sets the radial limits of the bottom (negative z) side
  • r2_inner(l), r2_delta(l), r2(l): Sets the radial limits of the top (positive z) side
  • r_delta: Sets both r1_delta and r2_delta
  • phi_start(a)
  • phi_delta(a)
  • phi_end(a)

See the sections about setting Cartesian lengths, radial lengths and azimuthal angles for more details.

Obviously missing shapes

n4::torus

n4::trap

n4::para

n4::polycone

n4::elliptical_tube

n4::ellipse

n4::ellipsoid

n4::elliptical_cone

n4::tet (...rahedron)

n4::hype (...rbolic cone)

n4::twisted_*

n4::breps Boundary Represented Solids

n4::tesselated_solids

TODO: document envelope_of