This page documents GVolume helper methods that place multiple copies of a volume in
structured patterns. These methods handle position, rotation, and naming automatically,
so that building arrays and rings requires only a few lines of Python.
distribute_on_circle
copies = gvolume.distribute_on_circle(
n, radius,
phistart=0, phispan=360,
align=False, axis='z',
lunit='mm', aunit='deg'
)
Replicates gvolume at n equal angular steps along a circle of the given radius.
Returns a list[GVolume], one copy per step, each named <name>_i.
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
n |
int | — | Number of copies |
radius |
float | — | Circle radius in lunit |
phistart |
float | 0 |
Starting angle in aunit |
phispan |
float | 360 |
Angular span in aunit (step = phispan / n) |
align |
bool | False |
If True, rotate each copy around axis by its φᵢ angle so the volume stays radially aligned |
axis |
str | 'z' |
Symmetry axis: 'x', 'y', or 'z' |
lunit |
str | 'mm' |
Length unit for radius |
aunit |
str | 'deg' |
Angle unit for phistart, phispan, and alignment rotations |
Positions
Each copy i is placed at φᵢ = phistart + i × phispan / n:
axis |
position |
|---|---|
'z' |
(radius·cos φᵢ, radius·sin φᵢ, 0) |
'x' |
(0, radius·cos φᵢ, radius·sin φᵢ) |
'y' |
(radius·cos φᵢ, 0, radius·sin φᵢ) |
Alignment rotation
With align=True the template’s existing rotation is preserved as the first half of a
doubleRotation: entry, and the angular step φᵢ is appended as the second half.
This is stored in GEMC’s doubleRotation: rx1, ry1, rz1, rx2, ry2, rz2 format so that
Geant4 and PyVista both apply the rotations in the correct order.
For axis='z' and a template rotation of (0, 0, -90)°:
doubleRotation: 0*deg, 0*deg, -90*deg, 0*deg, 0*deg, φᵢ*deg
Geant4 applies this as Rz(−90°) then Rz(φᵢ), giving a combined rotation of Rz(φᵢ − 90°), which maps the template’s wide face radially outward at angle φᵢ.
Example — scintillator barrel
The scintillator barrel example uses
distribute_on_circle to tile 48 trapezoidal paddles into a closed barrel ring:
import math
from pygemc import autogeometry, GVolume
cfg = autogeometry("examples", "scintillator_barrel")
n = 48
radius = 400.0 # mm
pZ = 500.0 # mm — half-length along beam axis
pY = 20.0 # mm — radial half-thickness
# Chord formula: adjacent outer faces meet exactly
half_step = math.radians(180.0 / n)
pX = (radius + pY) * math.sin(half_step) # outer half-width
pLTX = (radius - pY) * math.sin(half_step) # inner half-width
paddle = GVolume("paddle")
paddle.mother = "root"
paddle.make_general_trapezoid(pZ, 0, 0, pY, pLTX, pX, 0, pY, pLTX, pX, 0)
paddle.material = "G4_PLASTIC_SC_VINYLTOLUENE"
paddle.color = "cornflowerblue"
paddle.set_rotation(0, 0, -90) # wide face (local +Y) → radially outward
paddle.digitization = "flux"
for i, v in enumerate(paddle.distribute_on_circle(n, radius, align=True, axis='z')):
v.set_identifier("paddle", i)
v.publish(cfg)
The chord formula ensures that, for any n, the outer faces of adjacent paddles are
contiguous: pX = (radius + pY) · sin(π/n) and pLTX = (radius − pY) · sin(π/n).
Interactive viewer (full 48-paddle barrel):
More structure helpers
Additional placement helpers will be documented here as they are added to GVolume.