Using the Particle Module in Python

The cee_envision.pt module exposes the Particle Component to Python via SWIG bindings. The API mirrors the C++ cee::pt namespace exactly — all classes, methods and constants are available under the same names.

Opening a Model

from cee_envision import pt

model = pt.ParticleModel()

err = pt.Error()
if not model.openFile("data/particles.ptfx", err):
    print("Error:", err.errorCode())

openFile() is a Python-only convenience wrapper that calls the C++ open(filePath) overload. To use a pre-constructed reader instead, call openWithReader(reader, err). Python ownership of the reader is transferred to C++ and the reader must not be used afterwards.

Adding the Model to a View and Rendering

from cee_envision import vis, osmesa, core

context_group = osmesa.OSMesaComponent.createOpenGLContextGroupForOSMesa()
viewer = osmesa.ViewerOSMesa(context_group)

view = vis.View()
viewer.setView(view)
viewer.resize(1024, 768)

view.addModel(model)
model.updateVisualization()

box = view.boundingBox()
if box.isValid():
    view.camera().fitView(box, core.Vec3d(0, 0, 1), core.Vec3d(0, 1, 0))

See Particle Model for the full C++ model API.

Scalar Mapping

fields = model.scalarFieldNames()   # returns a list of strings
if fields:
    model.setActiveScalarField(fields[0])

    mapper = vis.ScalarMapperContinuous()
    scalar_range = model.scalarRangeAsTuple()   # Python-only: returns (min, max) or None
    if scalar_range:
        mapper.setRange(scalar_range[0], scalar_range[1])
    mapper.setColors(vis.ColorTableFactory.BLUE_RED)
    model.setScalarMapper(mapper)

scalarRangeAsTuple() is a Python-only helper. The underlying C++ method scalarRange() takes output pointer arguments and is not directly callable from Python.

Note

ParticleModel automatically adds a color legend overlay to the view when a scalar mapper is set. There is no need to add one manually via view.overlay().

Frame Animation

frame_count = model.frameCount()

# Pre-load frames into the cache for smooth playback
model.preloadFrames(0, frame_count)

for i in range(frame_count):
    model.setCurrentFrameIndex(i)
    model.updateVisualization()
    # render frame ...

See Frame Iteration and Scalars and Frame Cache for details.

Low-level Data Access

Use a reader directly to inspect raw frame data without rendering:

reader = pt.createReader("data/particles.ptfx")   # auto-detects format

err = pt.Error()
if not reader.open("data/particles.ptfx", err):
    print("Error:", err.errorCode())

header = reader.header()
print("Frames:", header.frameCount)
print("Fields:", list(header.scalarFieldNames))

frame = reader.getFrameDataAsPtr(0)   # Python-only: returns FrameData* (or None)
if frame:
    print("Particles:", frame.particleCount)
    print("Positions:", frame.positions[:6])  # first two XYZ triples

getFrameDataAsPtr() is a Python-only wrapper around the C++ getFrameData() which returns a unique_ptr that cannot be expressed directly in Python. The returned object is owned by Python and will be freed when it goes out of scope.

See Particle Dataset Readers for the full reader API.

Writing PTFX Files

writer = pt.PtfxDatasetWriter()
err = pt.Error()

scalar_infos = [pt.PtfxScalarFieldInfo()]
scalar_infos[0].name = "Temperature"
scalar_infos[0].minValue = 273.0
scalar_infos[0].maxValue = 1500.0

writer.open("output.ptfx", max_particles, frame_count,
            bbox_min, bbox_max, scalar_infos, err)

for frame_idx in range(frame_count):
    writer.writeFrame(ids, positions, scalars, err)

writer.close(err)

See Writing PTFX Files for the full writer API.

Example Scripts

Four ready-to-run scripts are provided in Examples/ParticleExamples/:

  • render_particles.py — renders a single mid-animation frame to a PNG.
  • animate_particles.py — renders all frames to PNGs and optionally assembles an MP4 (requires pip install opencv-python).
  • particles_with_decimation.py — renders the same frame at 10 %, 50 % and 100 % density.
  • read_and_write_particles.py — reads frame data and writes a subset to a new .ptfx file.

All scripts default to the demo datasets in Examples/DemoFiles/particles/ but accept a custom file path as a command-line argument:

python render_particles.py path/to/my/particles.vtp