Python interface#

calzone.define(*, materials=None, meshes=None)#

Define materials and meshes.

The materials and meshes definitions can be provided directly as a Python dict object, or loaded from a file (in Gate DB, JSON, TOML or YAML format). For instance, the following defines materials from a TOML file.

>>> calzone.define(materials="materials.toml")

See the Materials and Meshes definition sections for further information.

Important

Materials and meshes are uniquely identified by their name. However, once loaded they cannot be unloaded, nor modified. If a different definition is provided for an existing (already loaded) material or mesh, then a ValueError is raised.

calzone.describe(*, material=None, mesh=None)#

Describe a material or a mesh.

A namespace object is returned containing the material or the mesh properties, or None if the material or the mesh is undefined. For example

>>> calzone.describe(material="G4_AIR").density
0.0012047899999999999

calzone.download(destination=None, *, exclude=None, force=False, include=None, verbose=False)#

Download Geant4 data.

Geant4 requires 2 GB of materials data in order to operate. These data are not distributed with Calzone, but are available for download from the Geant4 website. This method automates the process of downloading these data.

The destination argument specifies where the downloaded data should be stored. If None, the data are stored under Calzone’s user data (i.e. $HOME/.local/share/calzone/data).

Note

In order to use (already available) Geant4 data but located outside of Calzone’s user space, the $GEANT4_DATA_DIR must be set accordingly.

Tip

This method can also be invoked directly from a shell, for example as follows

calzone download --force

class calzone.Geant4Exception#

A Geant4 exception.

This class represents an exception issued by the Geant4 kernel. Note however that Geant4 does not raise C++ exceptions, but instead relies on G4VExceptionHandler. Therefore, Geant4 exceptions might be reported long after the scope that actually issued the error.

class calzone.Geometry(volume)#

A static Monte Carlo geometry.

This class wraps a collection of G4VPhysicalVolumes, constituting a Geant4 Monte Carlo geometry. The corresponding Volume objects are accessible as a sequence. For instance,

>>> for index, volume in enumerate(geometry):
...     assert volume == geometry[index]

Alternatively, a Monte Carlo Volume can be accessed by providing its absolute pathname inside the geometry. For instance,

>>> volume = geometry["Environment.Detector"]
__new__(definition)#

Create a new geometry instance from a definition. The geometry definition can be provided directly as a Python dict object, or loaded from a definition file (in JSON, TOML, or YAML format). For instance, the following creates a Monte Carlo geometry from a dict-definition encoded in a TOML file.

>>> geometry = calzone.Geometry("geometry.toml")

See also

The GeometryBuilder class (described hereafter) allows for customisation of the geometry before actually building it.

check(resolution=None)#

Check the geometry by looking for overlapping volumes.

An integer resolution can be provided, specifying the number of Monte Carlo trials when looking for overlaps. The default resolution is of 1000 trials per couple of volumes.

On failure, i.e. as soon as an overlap is found, a Geant4Exception is raised. Thus, only the first found overlap is reported, in case that the geometry comprises multiple overlaps.

Tip

This method can also be invoked directly from a shell, for example as follows

calzone check geometry.toml --resolution 10000
display(data=None, /)#

Display the geometry.

Note

This method requires the calzone-display extension module to be installed.

Launch an interactive display of the geometry. If tracking data is provided (as returned by the Simulation.run() method), this information will be superimposed on the geometry display.

Tip

This method can also be invoked directly from a shell, for example as follows

calzone display geometry.toml
export(format=None)#

Export the Geant4 geometry.

The format argument specifies the destination format. The supported values are listed in Table 23 below. The default format is "goupil".

Table 23 Export formats.#

Format

Destination

"goupil"

goupil.ExternalGeometry

"mulder"

mulder.LocalGeometry

Note

This methods requires the goupil and/or mulder modules to be installed.

find(stem)#

Find a geometry volume matching the given stem.

The stem argument might specify a volume name or the tail of an incomplete pathname.

Note

In the event of multiple potential matches, only the Volume with the lowest index value is returned.

Attributes

root#

The geometry root volume.


class calzone.GeometryBuilder(definition, /, *, algorithm=None)#

A Monte Carlo geometry builder.

This class manages a geometry definition. It provides high level operators for customising the Monte Carlo geometry before actually building it.

__new__(definition, /, *, algorithm=None)#

Create a new geometry builder from an initial definition, provided directly as a Python dict object, or loaded from a definition file (in JSON, or TOML format). For instance,

>>> builder = calzone.GeometryBuilder("geometry.toml")

Optionally, the meshes traversal algorithm can be specified (see the algorithm attribute).

build()#

Build the Monte Carlo Geometry.

Upon successful completion, a Geometry instance is returned, for instance as

>>> geometry = builder.build()

Note that the returned geometry is immutable. That is, subsequent builder operations do not modify the returned geometry.

delete(pathname)#

Remove a volume from the geometry definition.

The volume to remove is identified by its absolute pathname. For instance, the following deletes the "Detector" volume nested inside the root "Environment" one.

>>> builder.delete("Environment.Detector")
<calzone.GeometryBuilder object at ...>
modify(pathname, material=None, position=None, role=None, rotation=None, shape=None, subtract=None)#

Modify the definition of a geometry volume.

The volume to modify is identified by its absolute pathname. The other arguments specify replacement values, if not None. See Table 4 for the meaning of arguments. For instance, the following changes the shape of the root "Environment" volume.

>>> builder.modify("Environment", shape={"box": 1.0})
<calzone.GeometryBuilder object at ...>
move(source, destination)#

Relocate a volume within the geometry definition.

Source and destination volumes are identified by their absolute pathnames. For instance, the following renames the "Detector" volume,

>>> builder.move("Environment.Terrain", "Environment.Ground")
<calzone.GeometryBuilder object at ...>

Note

the root volume cannot be moved, nor replaced, with this method.

place(definition, mother=None, position=None, rotation=None)#

(Re)place a definition of a geometry volume.

The volume definition can be provided directly as a Python dict object, or loaded from a definition file (in JSON, or TOML format). The mother argument specifies the location of the volume within the geometry hierarchy. Note that if a volume with the same name already exists at the given location, then it is replaced with the new definition. See the geometry description section for the meaning of the other arguments.

Attributes

algorithm#

Meshes traversal algorithm.

If not None, this attribute will override the traversal algorithm for all meshes within the geometry. The available options are "bvh" or "voxels". Refer to the Geometry section for further details.

Caution

Voxels are inefficient for large meshes (e.g. topographies), due to the large memory usage.


class calzone.Map(map)#

A structured topography map spanning a x-y grid.

This class manages a regular grid of topography elevation values, e.g. from a Digital Elevation Model (DEM). Data are exposed as a mutable numpy.ndarray, and can be exported as a 2D image (in Turtle / PNG [NBCM20] format), or as a 3D model (in STL format).

__new__(data)#

Create a new map instance from a grid of elevation values.

The data can be provided as a geotiff object, or loaded from an image file (in ASCII Grid, GeoTIFF, or PNG format). For instance, the following loads topography data from a GeoTIFF file,

>>> topography = calzone.Map("topography.tif")
dump(filename, **kwargs)#

Dump the map to a file.

The export format is specified by the file extension. It must be one of ".asc", ".png" or ".stl". When exporting as STL, optional kwargs can be provided in order to customise the 3D shape (see Table 11). For instance, the following exports the map as a PNG image (including topography metadata).

>>> topography.dump("topography.png")
static from_array(z, xlim, ylim, crs=None)#

Create a new topography map from a 2D-numpy ndarray.

The z argument must be a dim-2 numpy.ndarray (of shape [ny, nx]) containing the topography elevation values (in C order, see the z attribute below). The xlim and ylim arguments are length-2 sequences specifying the map limits along the x and y-axis (i.e. x0, x1, y0 and y1 attributes below). For instance,

>>> topography = calzone.Map.from_array(z, [x0, x1], [y0, y1])

Optionally, the map CRS can also be indicated.

Note

The coordinates of map nodes (0,0), or (ny,nx), are (x0,y0), or (x1,y1), respectively. Since image formats are conventionally rendered with node (0,0) located at upper left corner, it is frequent to have y0 > y1.

Attributes

crs#

Coordinates Reference System (CRS).

This property is purely informative (and optional), since Map objects do not allow for frame transforms.

nx#

Number of nodes along the x-axis (i.e. columns).

ny#

Number of nodes along the y-axis (i.e. rows).

x0#

Left x-coordinate (index-wise).

x1#

Right x-coordinate (index-wise).

y0#

Lower y-coordinate (index-wise).

y1#

Upper y-coordinate (index-wise).

z#

Topography elevation values at map nodes.

Elevation values are indexed in C-order. That is, z[i,j] corresponds to the elevation at grid node j along x-axis (columns), and i along y-axis (rows).


calzone.particles(shape, **kwargs)#

Create an array of Monte Carlo particles.

This function returns a structured numpy array with the given shape. Primary particles are initialised with default properties, if not overriden by specifying kwargs. For instance, the following creates an array of 100 primary particles (photons, by default) with a kinetic energy of 0.5 MeV, starting from the origin (by default), and going downwards.

>>> particles = calzone.particles(100, pid="e-", energy=0.5, direction=(0, 0, -1))

The data structure (numpy.dtype) of a primary particle is the following (the corresponding physical units are also indicated).

Table 24 Primaries array structure.#

Field

Format

Units

"pid"

"i4"

"energy"

"f8"

MeV

"position"

"(f8, 3)"

cm

"direction"

"(f8, 3)"

Table 25 Common particles PIDs.#

Name

PID

Description

"e-", "e+"

11, -11

An electron (positron).

"mu-", "mu+"

13, -13

A (anti)muon.

"tau-", "tau+"

15, -15

A (anti)tau.

"gamma"

22

A photon.

"p"

2212

A proton.

"n"

2112

A neutron.


class calzone.ParticlesGenerator(*, geometry=None, random=None, weight=None)#

This class provides a utility for the generation of Monte Carlo particles from configurable distributions. This tool is typically used to seed the Monte Carlo simulation with an initial set of particles.

Once initialised, the generator can be further configured using the methods detailed below (i.e. following a builder pattern). The generate() method then triggers the actual sampling of Monte Carlo particles. As an example, the following generates N particles entering a specific volume, with a power-law energy distribution (between 10 keV and 10 MeV).

>>> particles = simulation.particles()      \
...     .on(volume, direction="ingoing")    \
...     .powerlaw(1E-02, 1E+01)             \
...     .pid("gamma")                       \
...     .generate(N)
__new__(*, geometry=None, random=None, weight=True)#

Create a new particles generator.

The weight argument specifies whether generation weights should be computed (i.e. the inverse of the generation likelihoods (\(\omega = 1 / \text{pdf}(\text{S})\), for a Monte Carlo state \(\text{S}\)) or not. Note that this can be overridden by individual distributions (using the weight flag of other methods).

direction(value, /)#

Fix the Monte Carlo particles direction.

The direction is specified using Cartesian coordinates in the frame of the geometry root volume. For instance, the following sets the particles direction as upgoing along the (Oz) axis.

>>> generator.direction([0, 0, 1])
<calzone.ParticlesGenerator object at ...>
energy(value, /)#

Fix the Monte Carlo particles kinetic energy.

For instance, the following sets the kinetic energy of Monte Carlo particles to 1 MeV.

>>> generator.energy(1)
<calzone.ParticlesGenerator object at ...>
generate(shape=None, /)#

Generate Monte Carlo particles according to the current settings.

The shape argument defines the number of particles requested (as a ndarray shape).

inside(*, include_daughters=False, weight=None)#

Set particles positions to be distributed inside a volume.

By default, the daughters volumes are excluded when generating the particles positions. Set the include_daughters flag to True if this is not the desired behaviour.

on(volume, /, direction=None, *, weight=None)#

Set particles positions to be distributed on a volume surface.

The optional direction argument is required to be one of "ingoing" or "outgoing", if a value is provided. In this case, in addition to the position, the particle direction is also generated with respect to the surface normal, employing a cosine distribution over the half solid angle.

pid(value, /)#

Fix the Monte Carlo particles type.

Monte Carlo particles are indentified by their Particle ID (PID), which follows the Particle Data Group (PDG) numbering scheme. Alternatively, a name can be provided for some common particles (see Table 25).

position(value, /)#

Fix the Monte Carlo particles position.

The position is specified using Cartesian coordinates in the frame of the geometry root volume. For instance, the following sets the particles position 1 m above the origin.

>>> generator.position([0, 0, 1E+02])
<calzone.ParticlesGenerator object at ...>
powerlaw(energy_min, energy_max, /, *, exponent=None, weight=None)#

Set particles kinetic energy to follow a power-law.

The energy_min and energy_max arguments define the support of the power-law, as an interval.

The default setting is a \(1 / E\) power-lawx, corresponding to exponent=-1. Note that setting the exponent value to zero results in a uniform distribution being used.

solid_angle(theta=None, phi=None, *, weight=None)#

Set particles direction to be distributed over a solid-angle.

The default settings is to consider the entire solid angle. The optional theta and phi arguments may be used to restrict the solid angle by specifying an interval of acceptable angular values, in deg.

spectrum(data, /, *, weight=None)#

Set particles kinetic energy to be distributed according to spectral lines.

The data argument specifies the spectral lines as a sequence of (energy, intensity) tuples. For instance, the following defines a spectrum with two spectral lines (at 0.5 and 1 MeV) of equal intensities.

>>> generator.spectrum([
...     (0.5, 1), # First line, at 0.5 MeV.
...     (1.0, 1), # Second line, at 1.0 MeV.
... ])
<calzone.ParticlesGenerator object at ...>

class calzone.Physics(em_model=None, *, default_cut=None, had_model=None)#

Geant4 physics settings.

__new__(em_model=None, *, default_cut=None, had_model=None)#

Create a new set of Geant4 physics settings.

See the default_cut, em_model and had_model attributes below for the meaning of the optional arguments. If an argument is left None, then its default value is used. For instance, the following creates settings with standard electromagnetric physics (the default), and no hadronic physics.

>>> physics = calzone.Physics()

Attributes

default_cut#

Physics default cut, in cm.

em_model#

Electromagnetic physics list.

Must be one of "dna", "livermore", "option1", "option2", "option3", "option4", "penelope" or "standard" (default). See the Geant4 documentation for the meaning of these options.

Setting em_model to None disables the simulation of electromagnetic interactions.

Note

When em_model is not None, then G4EmExtraPhysics is automatically enabled, in addition to the selected model of electromagnetic interactions.

had_model#

Hadronic physics list.

Must be one of "FTFP_BERT", "FTFP_BERT_HP", "QGSP_BERT", "QGSP_BERT_HP", "QGSP_BIC" or "QGSP_BIC_HP". See the Geant4 documentation for the meaning of these options.

Setting had_model to None, which is the default, disables the simulation of hadronic interactions.


class calzone.Random(seed=None, *, index=None)#

A Pseudo-Random Numbers Generator (PRNG).

This class exposes a stream of pseudo-random numbers as a cyclic sequence of float. The stream is determined by the seed attribute, while the index attribute indicates its current state.

Note

A Permuted Congruential Generator (PCG) is used (namely Mcg128Xsl64), which has excellent performances for Monte Carlo applications.

__new__(seed=None, *, index=None)#

Create a new pseudo-random stream.

If seed is None, then a random value is picked using the system entropy. Otherwise, the specified seed value is used. For instance,

>>> prng = calzone.Random(123456789)
uniform01(shape=None)#

Generate pseudo-random number(s) uniformly distributed over (0,1).

If shape is None, then a single number is returned. Otherwise, a numpy.ndarray is returned, with the given shape. For instance, the following returns the next 100 pseudo-random numbers from the stream.

>>> rns = prng.uniform01(100)

Attributes

index#

Prng stream index.

This property can be modified, resulting in consuming or rewinding the pseudo-random stream. For instance, the following resets the stream.

>>> prng.index = 0
seed#

Prng initial seed.

The property fully determines (and identifies) the pseudo-random stream. Note that modifying the seed also resets the stream to index 0.


class calzone.Simulation(geometry=None, physics=None, random=None, sample_deposits=None, sample_particles=None, secondaries=None, tracking=None)#

Interface to a Geant4 simulation.

This class provides an interface for running a Geant4 simulation. The simulation is configured through a set of attributes described hereafter, or using the class constructor, below.

__new__(geometry=None, **kwargs)#

Create a new interface to a Geant4 simulation.

The optional keyword arguments initialise the corresponding simulation attributes, described below. For instance, the following creates a new simulation interface with tracking enabled.

>>> Simulation = calzone.Simulation("geometry.toml", tracking=True)
particles(weight=None)#

Create a Monte Carlo particles generator.

The returned ParticlesGenerator object is configured according to the simulation settings. Refer to the constructor of this object for further information.

run(particles, /, *, random_indices=None)#

Run a Geant4 Monte Carlo simulation.

The provided primary particles are transported through the Monte Carlo geometry, which must have been set first. The returned object depends on the simulation sample_deposits, sample_particles and tracking attributes. For example, if both deposits sampling and tracking are enabled, then a namespace object is returned, containing the sampled energy deposits, as well as the recorded tracks and vertices (as numpy.ndarray, each).

Optionally, an array of random_indices (of the same size as particles) can be provided to set the random engine state of the simulation for each event. This is typically used to replay previously simulated Monte Carlo events (e.g. with additional tracking data).

Attributes

geometry#

The Monte Carlo Geometry.

This property is a Geometry instance. However, by default, no geometry is attached to the simulation.

When setting this property, a path to a geometry file, or a dict geometry description, can be provided in lieu of a Geometry object. For instance,

>>> simulation.geometry = "geometry.toml"
physics#

Monte Carlo Physics settings.

This property is a Physics instance. By default, only ("standard") electromagnetic interactions are enabled.

When setting this property, the electromagnetic and/or hadronic physics models may be specified in lieu of a Physics object. For instance,

>>> simulation.physics = "penelope-FTFP_BERT"
random#

Monte Carlo pseudo-random stream.

This property is a Random instance. By default, the pseudo-random stream is seeded using the system entropy.

sample_deposits#

Sampling mode for energy deposits.

Must be one of "brief" (default) or "detailed". If set to None, then energy deposits sampling is disabled for all volumes.

In "brief" mode, only the total energy deposits per active volume is recorded. On the contrary, in "detailed" mode the full detail of energy deposition is reported.

sample_particles#

Flag controlling the sampling of particles at volume boundaries.

Must be a bool, or None. If False, then particles sampling is disabled at all volumes boundaries. By default, particles sampling is enabled.

secondaries#

Flag controlling the production of secondary particles.

Must be a bool, or None. By default, secondary particles are enabled.

Tip

The deactivation of secondary particles can significantly speed up the Monte Carlo simulation, by orders of magnitude depending on the application. However, care must be exercised as it may be crucial to account for these secondary particles as part of the detector response.

tracking#

Flag controlling the recording of Monte Carlo tracks.

Must be a bool, or None. By default, Monte Carlo tracks recording is disabled.


class calzone.Volume#

A volume of a Monte Carlo geometry.

This class provides an interface for inspecting a G4VPhysicalVolume of an instanciated Monte Carlo geometry. Note that the geometry is static, i.e. it cannot be modified once it has been built, except for volume’s role.

Volume objects are linked to a Geometry, and cannot be instanciated directly. Instead, they are indexed from a geometry, e.g. as

>>> volume = geometry["Environment.Detector"]
aabb(frame=None)#

Return the volume’s Axis-Aligned Bounding-Box (AABB).

The frame argument specifies the reference volume (by its absolute pathname) of the axis-aligned bounding-box. If frame is None, then the bounding box is computed in the root frame of the simulation. For instance, the following computes the volume AABB in its own frame (thus, the AABB of the underlying G4VSolid, actually).

>>> aabb = volume.aabb(volume.path)
display(data=None, /)#

Display the volume.

Note

This method requires the calzone-display extension module to be installed.

Launch an interactive display of the volume. If tracking data is provided (as returned by the Simulation.run() method), this information will be superimposed on the geometry display.

local_coordinates(points, /)#

Compute point(s) local coordinates.

The provided points must be a 3d numpy.ndarray. For instance,

>>> end = volume.local_coordinates(deposits.line["end"])

returns the local coordinates of line deposits end-points.

origin(frame=None)#

Return the coordinates of the volume origin.

As for the aabb() method, the frame argument specifies the reference volume. Note that depending on the underlying G4VSolid, the origin might or might not be at the volume centre.

side(elements, /, *, include_daughters=None)#

Return the side of elements w.r.t. this volume.

The element argument can be a structured numpy array containing the "position" key (e.g. Monte Carlo particles), or directy a numpy.ndarray of cartesian coordinates.

By default, points located inside daughter volumes are considered to be outside of the mother volume, when checking the side. Set include_daughters to True if this is not the desired behaviour.

volume(include_daughters=None)#

Return the cubic volume of this volume.

By default, daughter volumes are excluded from the mother volume computation. Set include_daughters to True if this is not the desired behaviour.

Attributes

daughters#

Daughter volume(s), if any (i.e. included insides).

Daughter’s absolute pathnames are returned, as a tuple of str objects. Note that only direct descendants are reported (e.g., not grand-daughters).

index#

The volume index within the geometry.

The volume index corresponds to its order when traversing the geometry. For instance,

>>> for index, volume in enumerate(geometry):
...     assert volume.index == index

Tip

The geometry tree is traversed using a depth-first / post-order algorithm. Consequently, daughter volumes have a lower index than their mother, with the root volume having the largest index value.

material#

The volume constitutive material.

This is the name of the underlying G4Material, as registered to Geant4.

mother#

The mother of this volume, if any (i.e. directly containing this volume).

name#

The volume name.

Caution

The volume name is not guaranteed to be unique within a given geometry (see the Geometry section).

path#

The volume absolute pathname.

Tip

The volume pathname is guaranteed to be unique within a given geometry (see the Geometry section).

role#

The volume role(s), if any.

See the the Roles section for a list of potential volume roles.

Tip

Unlike other volume properties, roles can be modified after the Monte Carlo geometry has been built.

solid#

The volume shape (according to Geant4).

This is the Geant4 type (as a str) of the underlying G4VSolid.

surface_area#

The volume surface area, in cm2.