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 G4VSolid
s 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 G4VSolid
s, and hence also the n4::shape
s, are parameterized by combinations of four principal types of coordinate
- Cartesian lengths,
x
,y
andz
- Radial length,
r
- Azimuthal angle,
φ
- 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::shape
s 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_inner | r_delta | r | |||
r | 0 | r | |||
r_inner | r | r - r_inner | |||
r_delta | r | r - r_delta | |||
r_inner | r_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_start | phi_delta | phi_end | |||
0 | 2π | 2π | |||
phi_start | 2π - start | 2π | |||
phi_delta | 0 | δ | |||
phi_end | 0 | end | |||
phi_delta | phi_end | end - δ | |||
phi_start | phi_end | end - start | |||
phi_start | phi_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_start | theta_delta | theta_end | |||
0 | π | π | |||
theta_start | π - start | π | |||
theta_delta | 0 | δ | |||
theta_end | 0 | end | |||
theta_delta | theta_end | end - δ | |||
theta_start | theta_end | end - start | |||
theta_start | theta_delta | start + δ |
Providing too few or too many values results in a run-time error.
Common methods
All n4::SOLID
s share the following methods:
- Builder methods
- Boolean solid methods:
add()
/join()
sub()
/subtract()
inter()
/intersect()
- Optional logical volume settings:
sensitive(sensitive-detector)
- TODO maybe field manager, user limits, optimize
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 independentlycube(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 ofG4Sphere
.
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) sider2_inner(l)
,r2_delta(l)
,r2(l)
: Sets the radial limits of the top (positive z) sider_delta
: Sets bothr1_delta
andr2_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