Creating Geometry

Precepts

  • Units may be specified but default to Geant4 ones (e.g. mm, rad).

  • Rotations are made using Tait-Bryan angles (rotation about reference x,y,z axes).

  • A Registry object should be used to hold all things related in a model and passed into the constructors of most objects.

  • GDML-like full lengths are used instead of typically half lengths

Units

  • The default units are mm and rad for length and angle.

  • Most constructors will take units as an optional key word argument (‘kwarg’).

  • The kwargs are typically lunit for length unit and aunit for angle unit.

  • Units are defined in pyg4ometry.gdml.Units.py.

The following units (as strings) are accepted:

Unit

Value

nm

1e-6

um

1e-3

mm

1

cm

10

m

1e3

km

1e6

deg

\(\pi/180\)

degree

\(\pi/180\)

rad

1

radian

1

mrad

1e-3

urad

1e-6

eV

1e-3

keV

1

MeV

1e+3

none

1

ns

1e-9

us

1e-6

ms

1e-3

s

1

Examples:

reg = pyg4ometry.geant4.Registry()
boxSolid = pyg4ometry.genat4.solid.Box("aBox", 10, 20, 30, reg)

This defines a box with the default units (none specified), so mm. We can specify them:

boxSolid = pyg4ometry.genat4.solid.Box("aBox", 10, 20, 30, reg, "cm")

Geant4 Python Scripting

Making use of pyg4ometry requires the following modules

import pyg4ometry

To make a simple geometry of a box located at the origin

 1# load pyg4ometry
 2import pyg4ometry
 3
 4# registry to store gdml data
 5reg  = pyg4ometry.geant4.Registry()
 6
 7# world solid and logical
 8ws   = pyg4ometry.geant4.solid.Box("ws",50,50,50,reg)
 9wl   = pyg4ometry.geant4.LogicalVolume(ws,"G4_Galactic","wl",reg)
10reg.setWorld(wl.name)
11
12# box placed at origin
13b1   = pyg4ometry.geant4.solid.Box("b1",10,10,10,reg)
14b1_l = pyg4ometry.geant4.LogicalVolume(b1,"G4_Fe","b1_l",reg)
15b1_p = pyg4ometry.geant4.PhysicalVolume([0,0,0],[0,0,0],b1_l,"b1_p",wl,reg)
16
17# visualise geometry
18v = pyg4ometry.visualisation.VtkViewer()
19v.addLogicalVolume(wl)
20v.addAxes(20)
21v.view()

Here is the vtk visualiser output of the above example

Simple python scripting example

GDML Defines

In GDML there are multiple define objects that can be used parameterise geometry, materials etc. These can be used as variables or definitions and mean that any equations used will be retained in GDML output. For example a GDML constant can be created in the following way

# registry to store gdml data
reg = pyg4ometry.geant4.Registry()

# constant called x
x = pyg4ometry.gdml.Constant("x",10,reg)

The normal set of mathematical operations in python can be performed and evaluated

y = 2*x + 10
y.eval()
>> 30

The constant x can of course be changed and y re-evaluated

x.setExpression(5)
y.eval()
>> 20

Note

Standard mathematical functions can be used with GDML defines (Constant, Variable, etc). So sin, cos, tan, exp and so on, but pyg4ometry functions have to be used

1x  = pyg4ometry.gdml.Constant("x",10,reg)
2cx = pyg4ometry.gdml.cos(x)

So the box example above can be rewritten using constants

 1# load pyg4ometry
 2import pyg4ometry
 3
 4# registry to store gdml data
 5reg  = pyg4ometry.geant4.Registry()
 6
 7bx = pyg4ometry.gdml.Constant("bx","10",reg,True)
 8by = pyg4ometry.gdml.Constant("by",2*bx,reg,True)
 9bz = pyg4ometry.gdml.Constant("bz",2*by,reg,True)
10
11# world solid and logical
12ws   = pyg4ometry.geant4.solid.Box("ws",50,50,50,reg)
13wl   = pyg4ometry.geant4.LogicalVolume(ws,"G4_Galactic","wl",reg)
14
15# box placed at origin
16b1   = pyg4ometry.geant4.solid.Box("b1",bx,by,bz,reg)
17b1_l = pyg4ometry.geant4.LogicalVolume(b1,"G4_Fe","b1_l",reg)
18b1_p = pyg4ometry.geant4.PhysicalVolume([0,0,0],[0,0,0],b1_l,"b1_p",wl,reg)
19
20# visualise geometry
21v = pyg4ometry.visualisation.VtkViewer()
22v.addLogicalVolume(wl)
23v.addAxes(20)
24v.view()

Note

All GDML defines (Constant, Variable, etc) can be used in the construction of other pyg4ometry classes interchangeably instead of floats or strings (where strings are either numbers or a GDML expression)

Warning

Avoid reassigning variables used as defines, this can have unexpected consequences so for example

1b1   = pyg4ometry.geant4.solid.Box("b1",bx,by,bz,reg)
2b1.pX = 20              # do not do this
3b1.pX.setExpression(20) # rather do this

Solids

The python geant4 solids match the Geant4 constructors as much possible (different constructor signatures are not supported in python). For example looking at the G4Box class

pyg4ometry.geant4.solid.Box(name, pX, pY, pZ, registry, lunit)
G4Box(const G4String& pName, G4double  pX, G4double  pY, G4double pZ)

A full list of solids can be found in Geant4 solids.

Warning

The parameters stick to the GDML convention of full lengths opposed to half lengths.

Materials

As with solids materials are defined in a similar way to Geant4 C++. Python does not have overloaded constructors, so unique signatures are needed, in contrast to Geant4.

To define a material from the Geant4 predefined (e.g. NIST) materials

1import pyg4ometry.geant4 as _g4
2wm = _g4.MaterialPredefined("G4_Galactic")
3bm = _g4.MaterialPredefined("G4_Fe")

To define a single element in terms of atomic number, atomic mass and density.

1import pyg4ometry.geant4 as _g4
2wm = _g4.MaterialSingleElement("galactic",1,1.008,1e-25,reg)   # low density hydrogen
3bm = _g4.MaterialSingleElement("iron",26,55.8452,7.874,reg)    # iron at near room temp

To define a compound two elements using the mass fraction

1import pyg4ometry.geant4 as _g4
2wm = _g4.MaterialCompound("air",1.290e-3,2,reg)
3ne = _g4.ElementSimple("nitrogen","N",7,14.01)
4oe = _g4.ElementSimple("oxygen","O",8,16.0)
5wm.add_element_massfraction(ne,0.7)
6wm.add_element_massfraction(oe,0.3)
7bm = _g4.MaterialSingleElement("iron",26,55.8452,7.874,reg)    # iron at near room temp

To define a compound using number of atoms

1import pyg4ometry.geant4 as _g4
2bm = _g4.MaterialCompound("plastic",1.38,3,reg)    # Generic PET C_10 H_8 O_4
3he = _g4.ElementSimple("hydrogen","H",1,1.008)
4ce = _g4.ElementSimple("carbon","C",6,12.0096)
5oe = _g4.ElementSimple("oxygen","O",8,16.0)
6bm.add_element_natoms(he,8)
7bm.add_element_natoms(ce,10)
8bm.add_element_natoms(oe,4)

Material as a mixture of materials

1import pyg4ometry.geant4 as _g4
2bm     = _g4.MaterialCompound("YellowBrass_C26800", 8.14, 2, reg)
3copper = _g4.MaterialPredefined("G4_Cu")
4zinc   = _g4.MaterialPredefined("G4_Zn")
5bm.add_material(copper, 0.67)
6bm.add_material(zinc, 0.33)

Example of elements formed by isotopes

1import pyg4ometry.geant4 as _g4
2u235 = _g4.Isotope("U235", 92, 235, 235.044)
3u238 = _g4.Isotope("U238", 92, 238, 238.051)
4uranium = _g4.ElementIsotopeMixture("uranium", "U", 2)
5uranium.add_isotope(u235, 0.00716)
6uranium.add_isotope(u238, 0.99284)
7bm = _g4.MaterialCompound("natural_uranium", 19.1, 1, reg)
8bm.add_element_massfraction(uranium, 1)

NIST Materials

Geant4 has many predefined materials according to the NIST database. Their name typically starts with G4_. These typically can be used with MaterialPredefined and we do not need to specify the full composition - Geant4 will find them at run time.

However, in the case of conversion to FLUKA, these are fully expanded according to their definition in Geant4 based on a cache in pyg4ometry of the material compositions generated using BDSIM from Geant4 (10.7.p01 as of writing). Should the user wish to use these, they can be accessed from the functions in the geant4 module.

1import pyg4ometry
2nistHydrogenElement = pyg4ometry.geant4.nist_element_2geant4Element('G4_H')

Note, an ‘element’ cannot be used as a ‘material’ in a logical volume. We must upgrade it to a material for that. The NIST elements contain the appropriate mixture of natural isotopes and can be used in MaterialCompound as demonstrated above.

Alternatively, we can access the NIST materials and materials of elements.

1import pyg4ometry
2nistHydrogenMaterial = pyg4ometry.geant4.nist_material_2geant4Material('G4_H')
3nistConcreteMaterial = pyg4ometry.geant4.nist_material_2geant4Material('G4_CONCRETE')

Detector Construction

This largely proceeds in exactly the same way as in G4 or GDML. Hierarchy of solids, booleans, logical, physical (replica, division, param) volumes.

  1. Create registry to hold everything

  2. Create solids

  3. Create logical volumes

  4. Place logical volumes (construct physical volumes)

  5. Visualise

  6. Check

  7. Export

Transformations & Physical Volumes

Transformations in 3D are essential for the easy placement of solids in a CSG tree or LV placement. There is not a specific transformation class like in Geant4. The matrices and vectors used for placements are here typically Numpy arrays or matrices.

Geant4 has two possible constructors for a physical volume. These provide active and passive transformations. In pyg4ometry, only one is provided.

  • The transform in a physical volume first translates the placed logical volume with respect to the mother logical, then rotates it.

The physical volume class is documented here: Geant4 module, but an example is shown here.

 1import pyg4ometry
 2r = pyg4ometry.geant4.Registry()
 3vacuum = _g4.MaterialPredefined("G4_Galactic")
 4water = _g4.MaterialPredefined("G4_WATER")
 5worldSolid = pyg4ometry.geant4.solid.Box("world_solid", 100, 100, 100, reg)
 6boxSolid = pyg4ometry.geant4.solid.Box("box_solid", 10, 20, 40, reg)
 7worldLV = pyg4ometry.geant4.LogicalVolume(worldSolid, vacuum, "world_lv", reg)
 8boxLV = pyg4ometry.geant4.LogicalVolume(boxSolid, water, "box_lv", reg)
 9
10pyg4ometry.geant4.PhysicalVolume([0,0,0],
11                                 [0,0,0],
12                                 boxLV,
13                                 "box_pv",
14                                 worldLV,
15                                 reg)

This creates a box of water inside a box of vacuum. The box of water is 10 x 20 x 50 mm long (note mm are the default length units), and it is placed with no offset and no rotation (i.e. at the centre) of the world volume. Alternatively:

1import numpy as np
2pyg4ometry.geant4.PhysicalVolume([0,np.pi/3.0,0],
3                                 [0,0,0],
4                                 boxLV,
5                                 "box_pv",
6                                 worldLV,
7                                 reg)

In this case, the box is placed with no offset but with a rotation of \(\pi/3\) radians about the y axis of the world box.

Note

The rotations are Tait-Bryan angles, which are rotations about the reference x,y,z axes. i.e. if there is a rotation about both x and y, these are independent and it is not a compound frame that is rotated. These are commonly thought of like an aircraft and called pitch, yaw and tilt.

There are utility functions for translation between different transformations in pyg4ometry.transformation. See Transformation.

Optical Surfaces

Optical surfaces can be created in a similar way as in Geant4 C++. A pyg4ometry.geant4.solid.OpticalSurface instance holds all the needed properties of the surface (including extra properties, e.g. for optical processes). This is then assigned to the surface between either

1opa = _g4.solid.OpticalSurface("AirSurface", finish="polished", model="glisur", surf_type="dielectric_dielectric", value="1", registry=reg)
2opw = _g4.solid.OpticalSurface("WaterSurface", finish="ground", model="unified", surf_type="dielectric_dielectric", value="0", registry=reg)
3
4_g4.SkinSurface("AirSurface", air_lv, opa, reg)
5_g4.BorderSurface("WaterSurface", water_phys, world_phys, opw, reg)

Properties of Materials and Optical Surfaces

Materials and optical surfaces support adding properties that can be used by Geant4 to influence processes, e.g. for scintillation, refraction or other optical processes.

In the GDML, a matrix is used to hold the value(s) of the property.

  • addProperty(name, matrix) - Add a property based on an existing pyg4ometry.gdml.Matrix object.

  • addVecProperty(name, e, v, eunit='eV', vunit='') - Add a property based on a energy vector and a value vector.

  • addConstProperty(name, value, vunit='')- Add a property that has only one constant value.

Units can be specified by setting the parameters eunit for the energy vector and vunit for the values. The given vectors are expected to be homogeneous in their units.

Note

Optical properties can only use units (or combinations of units) that are also defined in pyg4ometry. If needed, additional units can be added: pyg4ometry.gdml.Units.units['ps'] = 1e-12.

1scint = _g4.Material(...)
2scint.addConstProperty('SCINTILLATIONTIMECONSTANT1', 2.5, vunit='ns')
3scint.addConstProperty('SCINTILLATIONYIELD', 8000, vunit='/MeV')
4scint.addVecProperty('RINDEX', [1, 10], [1.3, 1.05])

FLUKA Geometry Creation

In a very similar way to geant4 geometry authoring it is possible to use pyg4ometry to create fluka output. To create a simple region consisting of a single body

 1import pyg4ometry.convert as convert
 2import pyg4ometry.visualisation as vi
 3from pyg4ometry.fluka import RPP, Region, Zone, FlukaRegistry
 4
 5freg = FlukaRegistry()
 6
 7rpp = RPP("RPP_BODY", 0, 10, 0, 10, 0, 10, flukaregistry=freg)
 8z = Zone()
 9z.addIntersection(rpp)
10region = Region("RPP_REG", material="COPPER")
11region.addZone(z)
12freg.addRegion(region)
13
14greg = convert.fluka2Geant4(freg)
15greg.getWorldVolume().clipSolid()
16
17v = vi.VtkViewer()
18v.addAxes(length=20)
19v.addLogicalVolume(greg.getWorldVolume())
20v.view()