Skip to main content

peclet-dem (peclet.dem)

PyPI version Python License: MIT CI DOI

pip install peclet-dem          # CPU (OpenMP) wheels — or `pip install peclet` for the whole family
pip install peclet-dem-cu13     # the CUDA 13 build of the same module, in its OWN venv

Both ship the discrete-element (XPBD + Hertz–Mindlin) solver as peclet.dem; they provide the same import, so they are mutually exclusive in one environment — one venv per backend. Multi-GPU/MPI and AMD/HIP are source or container builds: see Install & run.

Performance-portable Discrete Element Method (DEM) particle simulation: an XPBD solver with SDF-based point-shell collision detection. Built on Kokkos + ArborX, so the same source runs on CUDA, HIP (AMD/LUMI), and OpenMP backends (selected at build time by the install prefix). Optional MPI for domain partitioning, with nanobind Python bindings (zero-copy, via scikit-build-core) for scripting and visualization.

The CUDA implementation was retired (2026-06): the Kokkos peclet.dem module was validated against it before the CUDA sources were removed. Restore point: git tag pre-cuda-retirement.

Features

  • Hybrid XPBD Solver: Two-pass velocity/position solver for stable high-density packing.
  • SDF Collision: Point-shell collision detection using Signed Distance Fields (supports analytic shapes like hollow cylinders).
  • Periodicity: Full periodic boundary conditions (Ghost Particles).
  • Python Bindings: Control simulation logic, data initialization, and export entirely from Python.
  • MPI Support: Optional Multi-GPU/Node support via domain decomposition.

Folder Structure

├── CMakeLists.txt              # Build configuration (find_package Kokkos + ArborX; version from pyproject.toml)
├── pyproject.toml              # scikit-build-core packaging (peclet-dem); THE version source
├── packaging/                  # peclet/dem/__init__.py, particle_builder.py, scene_particle.py, CUDA-wheel pyproject
├── src                         # Kokkos sources (header-only, namespace peclet::dem)
│   ├── dem_bindings.cpp          # nanobind module entry point (the `peclet.dem` module)
│   ├── sim.hpp                   # Simulation facade: the host-facing setters/getters, walls, steppers, MPI driver state
│   ├── shape_registry.hpp        # ShapeRegistry (Simulation's base): shapes, shells, inertias + the device upload
│   ├── step_solve.hpp            # Single-rank step drivers: demStep, computeOverlapsKokkos, contact-buffer sizing
│   ├── step_solve_mpi.hpp        # Distributed step drivers + MPI hook policies, gated PECLET_DEM_MPI
│   ├── dem_portable.hpp          # POD types + math + analytic SDFs shared by every kernel
│   ├── particles.hpp             # Particle SoA container (the Kokkos Views)
│   ├── shapes_portable.hpp       # Surface-shell point generators for the analytic shapes
│   ├── broadphase_arborx.hpp     # ArborX BVH broad-phase
│   ├── narrowphase.hpp           # Narrow-phase point-shell-vs-SDF collision + boundary planes
│   ├── contact_preprocessing.hpp # Contact -> manifold reduction
│   ├── solve_driver.hpp          # The shared XPBD contact-solve driver (velocity + position, coloured GS)
│   ├── solve_driver_force.hpp    # The force-based (explicit Hertz-Mindlin) step driver
│   ├── solver_velocity.hpp       # Manifold velocity solve (restitution impulse)
│   ├── solver_position.hpp       # XPBD position solve (overlap removal)
│   ├── solver_friction.hpp       # Coulomb friction cluster
│   ├── solver_fused.hpp          # Fused colour sweeps (one persistent kernel per sweep)
│   ├── solver_multilevel.hpp     # Multilevel (GraphMG-style) contact stabilization
│   ├── solver_hertz.hpp          # Soft-sphere Hertz-Mindlin force model
│   ├── sleeping.hpp              # Island sleeping / freezing for the statics path
│   ├── integration.hpp           # Time integration & prediction
│   ├── periodicity.hpp           # Periodic ghost generation
│   ├── output_sdf.hpp            # Packed-bed SDF grid reconstruction (get_sdf_grid)
│   ├── io.hpp                    # LAMMPS-dump + SDF-VTI export
│   └── mpi_halo.hpp              # Distributed particle halo (core), gated PECLET_DEM_MPI
├── tests                       # ctest suites, built by -DPECLET_DEM_BUILD_TESTS=ON: kokkos/ (kernels),
│                               #   arborx/ (broad-phase + pipeline), kokkos_mpi/ (distributed step, np=1,2,4),
│                               #   python/ (pytest) + python/mpi/ (mpirun-launched pytest files)
├── examples                    # demos: packing / collision / stacking / precession / thermostat, pack.py,
│                               #   shape + packing generators, the distributed driver + microbenchmark
├── docs                        # Reference docs: solver_details.md, mpi.md, multi_gpu_testing.md,
│                               #   visualization.md, Doxyfile; archive/ = dated campaign records
└── notebooks                   # packing_analysis.ipynb

Prerequisites

  • Linux
  • CMake >= 3.24
  • Kokkos 5.x + ArborX (C++20) — provisioned by ../tools/bootstrap_deps.sh into ../extern/install/<backend> (nvidia-cuda / host-openmp / lumi-hip). A hard build dependency.
  • nanobind + scikit-build-core (found via the active Python interpreter; see pyproject.toml)
  • a backend compiler: nvcc (CUDA) on PATH, hipcc (ROCm), or just a host C++ compiler (OpenMP)
  • Python >= 3.10
  • MPI (optional, -DPECLET_DEM_MPI=ON) — OpenMPI or MPICH

Build Instructions

source ../.venv/bin/activate                      # the ONE suite venv (nanobind, numpy, ...)
export PATH=/usr/local/cuda-13.2/bin:$PATH        # if building the CUDA backend

# Canonical: build + install the module via scikit-build-core
CMAKE_PREFIX_PATH="$PWD/../extern/install/nvidia-cuda" pip install .

# Or a dev cmake build (nanobind is found via the active interpreter, no cmakedir needed):
cmake -B build -S . -DCMAKE_PREFIX_PATH="$PWD/../extern/install/nvidia-cuda"
cmake --build build -j$(nproc)

Swap the prefix to ../extern/install/host-openmp for the OpenMP backend. -DPECLET_DEM_MPI=ON links MPI and exposes the distributed step (init_mpi / enable_mpi_step / step_mpi), including dynamic load balancing — enable_mpi_step(..., rebalance_every=N) or an explicit rebalance() re-decomposes by particle count (weighted ORB) and migrates ownership so each rank keeps a near-equal share.

The compiled peclet.dem extension is placed in build/peclet/dem/; run scripts with build/ on PYTHONPATH (import peclet.dem).

Quick start

import numpy as np
from peclet import dem

sim = dem.Simulation(capacity=20000)             # owned + periodic ghost slots
sim.initialize_shape('sphere', 0.5)              # one shape; add_shape(...) appends more
sim.set_global_scale(0.01)                       # grain size -- set it BEFORE set_domain (both reset the skin)
sim.set_domain(extent=(0.2, 0.2, 0.4), origin=(0, 0, 0), periodic=(True, True, False))
sim.add_plane(point=(0, 0, 0), normal=(0, 0, 1)) # there is no implicit floor

xyz = np.random.rand(5000, 3) * [0.2, 0.2, 0.4]
sim.set_positions(xyz.astype(np.float32))        # (N, 3), or (N, 4) whose 4th column is the INVERSE mass
sim.set_gravity((0, 0, -9.81))                   # the default is ZERO
sim.set_material_params(restitution_normal=0.3, friction=0.4)   # friction defaults to ZERO
sim.set_solver_iterations(pos=10, vel=8)         # vel defaults to 0 = no restitution

sim.set_dt(1e-4)                                 # every stepper RAISES before this
sim.step(500)                                    # advance 500 substeps
sim.relax(50)                                    # dynamics-free overlap removal (no dt needed)

pos = sim.get_positions()                        # (N, 3) float32
print(sim.num_particles, sim.num_contacts, sim.compute_overlaps())

Counts and stored scalars are properties (num_particles, num_contacts, max_overlap, dt, gravity, stabilization, ...); computed scalars are methods (compute_overlaps()); array copies are get_*. Developer instruments, ablations and GPU execution policies live on sim.diagnostics. See CLAUDE.md for the call-order requirements and docs/solver_details.md for what the step does.

Running Simulations

examples/ holds the Python demos: verify_*.py (sphere / hollow-cylinder packing, collisions, stacking, precession, thermostat), the pack.py / pack_meter.py packing protocol + meter, generate_particles.py (Ovito shape mesh) and the packing generators, plus the distributed driver_distributed.py skeleton and bench_step.py (mpirun -np N python examples/bench_step.py, needs a built peclet.halo on PYTHONPATH). All run from the build tree:

export PYTHONPATH=$PYTHONPATH:$(pwd)/build        # import peclet.dem from the dev build
python examples/verify_packing_spheres.py

Tests

Configure with -DPECLET_DEM_BUILD_TESTS=ON (add -DPECLET_DEM_MPI=ON for the distributed suites) and run ctest: the kernel unit tests (tests/kokkos), the ArborX broad-phase + pipeline tests (tests/arborx), the distributed ctests (tests/kokkos_mpi, np=1,2,4) and the Python suite (tests/python, pytest: Hertz + non-spherical Hertz, cone friction, pair materials, coloured Gauss-Seidel, statics battery, restitution, SDF particles, rotating drum, ...; tests/python/mpi is launched through mpirun on top of peclet.halo). See CLAUDE.md for the exact recipe; CI (.github/workflows/ci.yml) runs all of it on the host OpenMP backend.

Output & Visualization

The simulation supports two primary output formats:

1. LAMMPS + STL (Ovito)

For particle visualization (especially non-spherical shapes), we use the LAMMPS dump format combined with an STL mesh.

  1. Generate Output: The simulation writes dump.custom.* files.
  2. Generate Shape: Run python examples/generate_particles.py to create particle_shape.stl.
  3. Visualize:
    • Open Ovito.
    • Load the dump.custom.* sequence.
    • Add a Particle Types modifier.
    • Set the shape visualization to Mesh/User-defined and load particle_shape.stl.
    • Ovito will automatically scale the mesh by the particle radius.

See docs/visualization.md for a detailed guide.

2. VTI (ParaView)

For visualizing fields (like the Signed Distance Field or occupancy grids), the simulation exports VTI files (.vti).

  1. Generate Output: sim.export_sdf("bed.vti", resolution=(128, 128, 128)) (the resolution is an (rx, ry, rz) triple; sim.get_sdf_grid((rx, ry, rz)) returns the same field as an array indexed [x, y, z]).
  2. Visualize:
    • Open ParaView.
    • Load the .vti file.
    • Use "Volume" representation or "Slice" filter to inspect the field.

Status

The single-GPU engine is complete and validated: it reaches stable high-density (random close) packing, and energy is conserved to ~0.3% (see docs/archive/packing_investigation.md). Friction is stabilized for spheres; body-body tangential friction is a known follow-up (currently weaker than ideal). Active work is at-scale multi-GPU/MPI tuning.

Release files for peclet-dem-cu13 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distributions (wheels)

Table of built distributions (wheels) for peclet-dem-cu13 1.1.0
File
peclet_dem_cu13-1.1.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.14 CPython 3.14 Linux glibc 2.28+ x86-64, Linux glibc 2.27+ x86-64 Details
peclet_dem_cu13-1.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.13 CPython 3.13 Linux glibc 2.28+ x86-64, Linux glibc 2.27+ x86-64 Details
peclet_dem_cu13-1.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.12 CPython 3.12 Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 Details
peclet_dem_cu13-1.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.11 CPython 3.11 Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 Details
peclet_dem_cu13-1.1.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.10 CPython 3.10 Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 Details

Total release size: 37.6 MB

Release files / peclet_dem_cu13-1.1.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL peclet_dem_cu13-1.1.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 7.5 MB
Tags CPython 3.14 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
00cc00182a8f38f1718fe66130c9ba07d22d295797bda66a05ad562de298e90e
BLAKE2b-256 checksum
How to use checksums
3a06d5a12429d4df99190149bfeed26b1f70df337aea5a6d68e24708f40d05b7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release files / peclet_dem_cu13-1.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL peclet_dem_cu13-1.1.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 7.5 MB
Tags CPython 3.13 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
1120ddda7df5bdca49641a5ab7a5c6307c98f59efe512ca12706fb8b1c92d898
BLAKE2b-256 checksum
How to use checksums
1f922ec5438d0e586d8ca2fc30c998133dd7a144b398890e42373c94847bf4e5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release files / peclet_dem_cu13-1.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL peclet_dem_cu13-1.1.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 7.5 MB
Tags CPython 3.12 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
922f409d7171ba5210ea660e67d0b1619ef834da22bf035d1a37cfa8a031550b
BLAKE2b-256 checksum
How to use checksums
a3de08be1732b26854daa2e6c5776db42a7ef3c47c3c80c55eb7a409c2bdeb15
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release files / peclet_dem_cu13-1.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL peclet_dem_cu13-1.1.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 7.5 MB
Tags CPython 3.11 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
062be90e22fa4884c0458947012b55cd47b6be8f47bbcf27973635786ec3c4a5
BLAKE2b-256 checksum
How to use checksums
5d60a82dcb4a0196ad6e784e602f7a7e4efa13d10f340ddc46e89b1feeab1d14
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release files / peclet_dem_cu13-1.1.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL peclet_dem_cu13-1.1.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 7.5 MB
Tags CPython 3.10 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
4f8afd429f70aa3ea908c40cc5be49bbefd2df9880059c070d1b05a7d8f01fa9
BLAKE2b-256 checksum
How to use checksums
afbe7116bd15f9730840c41dc7b0f0e80ddb1c37e3c4b75a6a411512602b06c5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 27, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.1.0 This release

5 release files

1.0.2

5 release files

1.0.1

4 release files

1.0.0

4 release files

0.5.1

4 release files

0.5.0

4 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page