PyVista API



GEMC uses PyVista to visualize detector geometry directly from Python. Geometry can be viewed interactively, exported as a .vtksz file for web-based display, or rendered as a screenshot.

Import the API from pyvista_api:

from pyvista_api import GMesh, pvmeshes_from_gmeshes, set_yz_view_x_into_screen


The GMesh Class

GMesh is the core object for building visualizable volumes. Every instance requires a unique name and a PyVista mesh. All other parameters are optional:

Parameter Type Default Description
name str Unique volume identifier
mesh pv.PolyData PyVista mesh in local coordinates
mother str None Parent volume name; None → placed directly in the world
material str None Geant4 material name (e.g. "G4_AIR")
mfield str None Magnetic field name
color str "white" Color name or hex string (see Color Reference)
opacity float 1.0 Opacity from 0.0 (invisible) to 1.0 (fully opaque)
position (x, y, z) (0,0,0) Local translation relative to mother, in mm
rotation (rx, ry, rz) (0,0,0) Intrinsic ZYX Euler angles in degrees

Example:

import pyvista as pv
from pyvista_api import GMesh

sphere_gm = GMesh(
    name="sphere",
    mesh=pv.Sphere(radius=0.7, theta_resolution=32, phi_resolution=32),
    mother="box",
    material="G4_Pb",
    color="tomato",
    opacity=0.85,
    position=(0.0, 0.0, 1.2),
    rotation=(0.0, 0.0, 0.0),
)


Supported Primitive Shapes

GMesh meshes are automatically converted to Geant4 solids when published via GVolume.from_gmesh(). The mapping is determined by the mesh bounding geometry:

PyVista Constructor Detected As Geant4 Solid Notes
pv.Cube(x_length, y_length, z_length) 8-point box G4Box Half-lengths used
pv.Cylinder(radius, height, direction=(0,0,1)) Round in XY, tall in Z G4Tubs Full 360° tube
pv.Sphere(radius) Equal extents in all axes G4Sphere Full sphere

Sphere detection takes priority over cylinder since both share “round in XY” bounding-box geometry.


Display Styles

The display appearance of a GVolume (created via from_gmesh or directly) is controlled by three attributes:

Attribute Value Effect
gvolume.visible 1 (default) Volume is rendered normally
  0 Nearly invisible (opacity 0.05, wireframe only) — for container or world volumes
gvolume.style 1 (default) Solid surface rendering with smooth shading
  0 Wireframe — feature edges at 30° threshold; clean outlines on all solids including cylinders
  2 Cloud — semi-transparent surface with a sampled point cloud overlay
gvolume.opacity 0.01.0 Surface transparency; independent of visible

Style 1 — Solid (default)

volume.style = 1
volume.opacity = 1.0

Style 0 — Wireframe

Renders only the characteristic edges of the shape, extracted using extract_feature_edges at a 30° dihedral threshold. Triangulated shapes (cylinders, spheres) show clean geometric outlines rather than every triangle edge.

volume.style = 0

Style 2 — Cloud

A semi-transparent surface is shown together with an 8000-point cloud sampled from the surface.

volume.style = 2

visible = 0 — Container / Envelope

Setting visible = 0 makes the volume nearly transparent (opacity 0.05, wireframe only). Use this for container or world volumes that should not dominate the scene:

world.visible = 0   # NOT world.style = 0


Rotations

GMesh uses intrinsic ZYX Euler angles: the angles (rx, ry, rz) are applied sequentially in the order X → Y → Z about the local frame axes, giving the rotation matrix:

R = Rx(rx) · Ry(ry) · Rz(rz)

Parent-to-world transforms are accumulated recursively through the volume hierarchy, so a child volume’s world position is T_parent + R_parent × T_local.

Example — 35° rotation about Z:

GMesh(
    name="rotated_box",
    mesh=pv.Cube(x_length=1.2, y_length=0.4, z_length=1.2),
    mother="box",
    rotation=(0.0, 0.0, 35.0),   # (rx, ry, rz) in degrees
)


Standalone PyVista Rendering

pvmeshes_from_gmeshes resolves the full parent-child hierarchy (position + rotation) and returns (mesh, color, opacity) tuples ready for direct PyVista rendering, without a Geant4 backend:

import pyvista as pv
from pyvista_api import GMesh, pvmeshes_from_gmeshes, set_yz_view_x_into_screen

gmeshes = make_basic_shapes()
pvmeshes = pvmeshes_from_gmeshes(gmeshes)

p = pv.Plotter()
for mesh, color, opacity in pvmeshes:
    p.add_mesh(mesh, color=color, opacity=opacity, show_edges=True)

p.add_axes_at_origin(xlabel="X", ylabel="Y", zlabel="Z")
set_yz_view_x_into_screen(p, distance=10.0)
p.show()

set_yz_view_x_into_screen(p, distance) positions the camera so that +Z points right on screen and +X points into the screen.


CLI Flags

These flags are accepted by any Python geometry script that uses autogeometry:

Flag Argument Description
-pv Open an interactive PyVista window after building geometry
-pvb Open a background (non-blocking) PyVista window
-pvbg <color> Set the PyVista background color as a name, hex color, or r g b triple
-pvbgt <color> Set the optional top color for a PyVista background gradient; use none for a flat background
-pvvtk <name> Export geometry to <name>.vtksz for web viewing
-pvz <zoom> Camera zoom factor for the exported VTK scene (e.g. 0.07)
--pyvista-variation <name> Upcoming in the next release: render only this variation
--pyvista-fast Upcoming in the next release: batch large scenes into fewer actors
--no-pyvista-fast Upcoming in the next release: force detailed actor-per-volume rendering
--pyvista-fast-threshold <n> Upcoming in the next release: auto-batch above this volume count
./detector.py -pv                        # interactive window
./detector.py -pvvtk detector -pvz 0.02  # export vtksz
./detector.py -pvvtk detector -pvbg "0.92 0.92 0.98" -pvbgt none
./detector.py -pv --pyvista-variation default
./detector.py -pv --pyvista-fast

Upcoming in the next release: variation selection

For geometry scripts that publish more than one variation, PyVista renders only one variation. If no variation is specified, the first variation that publishes a PyVista-rendered volume is used. To choose a specific variation, pass --pyvista-variation:

./ec.py -pv --pyvista-variation rga_spring2018
./ec.py -pvvtk ec_rga --pyvista-variation rga_spring2018

The normal database or ASCII output is unchanged: a script may still publish every variation. The PyVista selection affects only the interactive window, background plotter, and .vtksz export.

When PyVista is enabled, the configuration summary prints the selected PyVista variation explicitly:

▪︎ PyVista Variation: rga_spring2018

The variation/run summary is also printed as a table. Each row records the run number that was active when that variation was published:

▪︎ Variation / Run:
    Variation                     Run
    ------------------------ --------
    default                        11
    ddvcs                    10000001

Upcoming in the next release: fast PyVista rendering

Large detector systems can contain thousands of volumes. Rendering one VTK actor per volume is useful for detailed debugging, but it is slow for systems such as EC. PyVista now defers actor creation until display or export time and automatically batches large scenes into fewer actors.

By default, batching is enabled when the selected PyVista variation contains more than 1000 rendered volumes. Use --pyvista-fast to force batching, or --no-pyvista-fast to keep the detailed actor-per-volume mode:

./ec.py -pv --pyvista-variation default --pyvista-fast
./ec.py -pv --pyvista-variation default --no-pyvista-fast
./ec.py -pv --pyvista-fast-threshold 2500

Fast mode preserves the visual geometry, colors, opacity, and styles by grouping volumes with matching render properties. The tradeoff is that the batched actor represents a group of volumes instead of a single named volume, so detailed per-volume inspection should use --no-pyvista-fast.

To generate an offscreen screenshot via the Geant4 renderer:

./detector.py                   # build geometry (creates gemc.db)
gemc detector.yaml -n=10 \
  -g4view="[{driver: TOOLSSG_OFFSCREEN, segsPerCircle: 200}]" \
  -g4camera="[{phi: -10*deg, theta: 250*deg}]"
# output: gemc_run_0.png


Publishing to Geant4

To use a GMesh in a full GEMC simulation, convert it to a GVolume and publish it:

from gconfiguration import autogeometry
from gvolume import GVolume

cfg = autogeometry("myexperiment", "mydetector")

for gm in make_shapes():
    gv = GVolume.from_gmesh(gm)
    gv.publish(cfg)

GVolume.from_gmesh automatically:

python API

Pass -h for additional command line options:

options:
  -h, --help            show this help message and exit
  -f, --factory FACTORY
						ascii, sqlite
  -v, --variation VARIATION
                        Set variation name
  -r, --run RUN         Set run number
  -sql, --dbhost DBHOST
                        SQLite filename or MYSQL host
  -pv, --pyvista        Show geometry using pyvista (needs pyvista)
  -pvb, --pvb, --pyvista-background
                        Use PyVista BackgroundPlotter (needs pyqt6 pyvistaqt)
  -pvw, --width WIDTH   Set plotter width
  -pvh, --height HEIGHT
						Set plotter height
  -pvx, --x X           Set plotter x position
  -pvy, --y Y           Set plotter y position
  -axes, --add_axes_at_zero

If you have pyvista (see also install pyvista), you can use the -pv and -pvb options to display the setup without having to run GEMC