peclet-dem 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. peclet_dem-0.1.0/.github/workflows/ci.yml +73 -0
  2. peclet_dem-0.1.0/.github/workflows/docs.yml +51 -0
  3. peclet_dem-0.1.0/.github/workflows/quality.yml +26 -0
  4. peclet_dem-0.1.0/.github/workflows/release.yml +53 -0
  5. peclet_dem-0.1.0/.gitignore +40 -0
  6. peclet_dem-0.1.0/.vscode/launch.json +57 -0
  7. peclet_dem-0.1.0/CMakeLists.txt +65 -0
  8. peclet_dem-0.1.0/LICENSE +21 -0
  9. peclet_dem-0.1.0/PKG-INFO +133 -0
  10. peclet_dem-0.1.0/README.md +119 -0
  11. peclet_dem-0.1.0/activate_env.sh +18 -0
  12. peclet_dem-0.1.0/cmake/PecletDeps.cmake +123 -0
  13. peclet_dem-0.1.0/debug_two_particles.py +60 -0
  14. peclet_dem-0.1.0/debug_vtp.py +40 -0
  15. peclet_dem-0.1.0/diag_cube_stick_slip.py +62 -0
  16. peclet_dem-0.1.0/diag_incline_roll.py +53 -0
  17. peclet_dem-0.1.0/docs/Doxyfile +66 -0
  18. peclet_dem-0.1.0/docs/packing_investigation.md +351 -0
  19. peclet_dem-0.1.0/docs/solver_details.md +204 -0
  20. peclet_dem-0.1.0/docs/velocity_solver_algorithm.md +57 -0
  21. peclet_dem-0.1.0/docs/visualization.md +79 -0
  22. peclet_dem-0.1.0/generate_particles.py +79 -0
  23. peclet_dem-0.1.0/mpi/CMakeLists.txt +39 -0
  24. peclet_dem-0.1.0/mpi/README.md +119 -0
  25. peclet_dem-0.1.0/mpi/bench_step.py +54 -0
  26. peclet_dem-0.1.0/mpi/driver_distributed.py +112 -0
  27. peclet_dem-0.1.0/mpi/multi_gpu_testing.md +188 -0
  28. peclet_dem-0.1.0/mpi/test_dem_scheme_b.cpp +203 -0
  29. peclet_dem-0.1.0/mpi/test_dem_scheme_c.cpp +194 -0
  30. peclet_dem-0.1.0/mpi/test_dem_step.cpp +198 -0
  31. peclet_dem-0.1.0/mpi/test_device_halo.cu +132 -0
  32. peclet_dem-0.1.0/mpi/test_particle_migration.cpp +152 -0
  33. peclet_dem-0.1.0/mpi/validate_exact.py +102 -0
  34. peclet_dem-0.1.0/mpi/validate_periodic.py +236 -0
  35. peclet_dem-0.1.0/mpi/verify_distributed.py +184 -0
  36. peclet_dem-0.1.0/notebooks/packing_analysis.ipynb +383 -0
  37. peclet_dem-0.1.0/pack.py +219 -0
  38. peclet_dem-0.1.0/pack_meter.py +119 -0
  39. peclet_dem-0.1.0/packaging/dem_init.py +16 -0
  40. peclet_dem-0.1.0/phase0_sphere_repro.py +70 -0
  41. peclet_dem-0.1.0/phase1_protocol_spheres.py +96 -0
  42. peclet_dem-0.1.0/phase2_protocol_fixed.py +150 -0
  43. peclet_dem-0.1.0/pyproject.toml +63 -0
  44. peclet_dem-0.1.0/python/export_vtp.py +88 -0
  45. peclet_dem-0.1.0/python/run_packing.py +43 -0
  46. peclet_dem-0.1.0/python/shapes/gen_hollow_cylinder.py +70 -0
  47. peclet_dem-0.1.0/python/simulation_script.py +13 -0
  48. peclet_dem-0.1.0/python/visualizer.py +7 -0
  49. peclet_dem-0.1.0/requirements.txt +4 -0
  50. peclet_dem-0.1.0/src/broadphase_arborx.hpp +95 -0
  51. peclet_dem-0.1.0/src/contact_preprocessing.hpp +186 -0
  52. peclet_dem-0.1.0/src/dem_bindings.cpp +249 -0
  53. peclet_dem-0.1.0/src/dem_portable.hpp +100 -0
  54. peclet_dem-0.1.0/src/integration.hpp +237 -0
  55. peclet_dem-0.1.0/src/io.hpp +91 -0
  56. peclet_dem-0.1.0/src/mpi_halo.hpp +415 -0
  57. peclet_dem-0.1.0/src/narrowphase.hpp +197 -0
  58. peclet_dem-0.1.0/src/output_sdf.hpp +116 -0
  59. peclet_dem-0.1.0/src/particles.hpp +87 -0
  60. peclet_dem-0.1.0/src/periodicity.hpp +69 -0
  61. peclet_dem-0.1.0/src/shapes_portable.hpp +113 -0
  62. peclet_dem-0.1.0/src/sim.hpp +610 -0
  63. peclet_dem-0.1.0/src/solver_friction.hpp +175 -0
  64. peclet_dem-0.1.0/src/solver_position.hpp +129 -0
  65. peclet_dem-0.1.0/src/solver_velocity.hpp +139 -0
  66. peclet_dem-0.1.0/stacking_test.vtp +20 -0
  67. peclet_dem-0.1.0/test.vtp +24 -0
  68. peclet_dem-0.1.0/test_bounce.py +68 -0
  69. peclet_dem-0.1.0/test_bounce_gravity.py +53 -0
  70. peclet_dem-0.1.0/tests/arborx/CMakeLists.txt +30 -0
  71. peclet_dem-0.1.0/tests/arborx/test_broadphase_arborx.cpp +106 -0
  72. peclet_dem-0.1.0/tests/arborx/test_pipeline.cpp +139 -0
  73. peclet_dem-0.1.0/tests/convergence_test.py +132 -0
  74. peclet_dem-0.1.0/tests/debug_2_particles.py +53 -0
  75. peclet_dem-0.1.0/tests/debug_crash.py +43 -0
  76. peclet_dem-0.1.0/tests/debug_fundamental.py +129 -0
  77. peclet_dem-0.1.0/tests/generate_dense_packing.py +0 -0
  78. peclet_dem-0.1.0/tests/generate_packing_cylinders.py +90 -0
  79. peclet_dem-0.1.0/tests/generate_packing_sdf.py +83 -0
  80. peclet_dem-0.1.0/tests/generate_periodic_packing.py +95 -0
  81. peclet_dem-0.1.0/tests/inspect_vti.py +0 -0
  82. peclet_dem-0.1.0/tests/kokkos/CMakeLists.txt +56 -0
  83. peclet_dem-0.1.0/tests/kokkos/test_contact_preprocessing.cpp +136 -0
  84. peclet_dem-0.1.0/tests/kokkos/test_integration.cpp +138 -0
  85. peclet_dem-0.1.0/tests/kokkos/test_narrowphase.cpp +206 -0
  86. peclet_dem-0.1.0/tests/kokkos/test_periodicity.cpp +107 -0
  87. peclet_dem-0.1.0/tests/kokkos/test_solver_friction.cpp +138 -0
  88. peclet_dem-0.1.0/tests/kokkos/test_solver_position.cpp +146 -0
  89. peclet_dem-0.1.0/tests/kokkos/test_solver_velocity.cpp +155 -0
  90. peclet_dem-0.1.0/tests/kokkos/test_thermostat.cpp +69 -0
  91. peclet_dem-0.1.0/tests/kokkos_mpi/CMakeLists.txt +44 -0
  92. peclet_dem-0.1.0/tests/kokkos_mpi/test_demstep_mpi.cpp +233 -0
  93. peclet_dem-0.1.0/tests/kokkos_mpi/test_rebalance_mpi.cpp +225 -0
  94. peclet_dem-0.1.0/tests/study_sphere_packing.py +114 -0
  95. peclet_dem-0.1.0/tests/test_hollow_cylinder_overlap.py +139 -0
  96. peclet_dem-0.1.0/tests/test_hollow_cylinder_overlap_2.py +139 -0
  97. peclet_dem-0.1.0/tests/test_periodicity_corner.cpp +104 -0
  98. peclet_dem-0.1.0/tests/test_physics.py +163 -0
  99. peclet_dem-0.1.0/tests/verify_cylinders.py +81 -0
  100. peclet_dem-0.1.0/tests/verify_flexible.py +204 -0
  101. peclet_dem-0.1.0/tests/verify_restitution.py +132 -0
  102. peclet_dem-0.1.0/tests/verify_sdf_cylinder.py +62 -0
  103. peclet_dem-0.1.0/tests/verify_sdf_cylinder_highres.py +55 -0
  104. peclet_dem-0.1.0/tests/verify_sdf_sphere.py +43 -0
  105. peclet_dem-0.1.0/tests/verify_vti_periodic.py +68 -0
  106. peclet_dem-0.1.0/verify_collision_hollow_cylinders.py +249 -0
  107. peclet_dem-0.1.0/verify_collision_spheres.py +103 -0
  108. peclet_dem-0.1.0/verify_output.txt +56079 -0
  109. peclet_dem-0.1.0/verify_packing_hollow_cylinders.py +207 -0
  110. peclet_dem-0.1.0/verify_packing_hollow_cylinders_test.py +206 -0
  111. peclet_dem-0.1.0/verify_packing_spheres.py +101 -0
  112. peclet_dem-0.1.0/verify_precession.py +127 -0
  113. peclet_dem-0.1.0/verify_stacking.py +70 -0
  114. peclet_dem-0.1.0/verify_stacking_inelastic.py +163 -0
  115. peclet_dem-0.1.0/verify_stacking_nofric.py +61 -0
  116. peclet_dem-0.1.0/verify_thermostat.py +109 -0
@@ -0,0 +1,73 @@
1
+ name: CI
2
+
3
+ # Build the Kokkos (OpenMP/Serial) backend + ArborX from source, then build the dem Python module and
4
+ # smoke-import it. The GPU (CUDA/HIP) backends and the multi-rank MPI step (DEM_MPI, needs the
5
+ # core sibling) are NOT exercisable on free GitHub runners (no GPU); this CI covers the
6
+ # portable host build, which catches the bulk of regressions. The single-rank module depends only on
7
+ # Kokkos + ArborX + pybind11 (the distributed step is gated behind DEM_MPI).
8
+
9
+ on:
10
+ push:
11
+ branches: [main, "**"]
12
+ pull_request:
13
+ workflow_dispatch:
14
+
15
+ env:
16
+ KOKKOS_TAG: "5.1.1" # keep in lockstep with ../tools/bootstrap_deps.sh
17
+ ARBORX_TAG: "v2.1"
18
+
19
+ jobs:
20
+ build-test:
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+
25
+ - name: Install toolchain
26
+ run: |
27
+ sudo apt-get update
28
+ sudo apt-get install -y cmake ninja-build libomp-dev python3-dev python3-pip
29
+ python3 -m pip install --upgrade pip pybind11 numpy
30
+
31
+ - name: Cache Kokkos + ArborX install
32
+ id: deps-cache
33
+ uses: actions/cache@v4
34
+ with:
35
+ path: ${{ github.workspace }}/deps-install
36
+ key: kokkos-${{ env.KOKKOS_TAG }}-arborx-${{ env.ARBORX_TAG }}-openmp-${{ runner.os }}
37
+
38
+ - name: Build Kokkos (OpenMP + Serial) + ArborX
39
+ if: steps.deps-cache.outputs.cache-hit != 'true'
40
+ run: |
41
+ PREFIX="$GITHUB_WORKSPACE/deps-install"
42
+ git clone --depth 1 --branch "$KOKKOS_TAG" https://github.com/kokkos/kokkos.git /tmp/kokkos
43
+ cmake -S /tmp/kokkos -B /tmp/kokkos/build -G Ninja \
44
+ -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=20 \
45
+ -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
46
+ -DKokkos_ENABLE_OPENMP=ON -DKokkos_ENABLE_SERIAL=ON \
47
+ -DCMAKE_INSTALL_PREFIX="$PREFIX"
48
+ cmake --build /tmp/kokkos/build -j && cmake --install /tmp/kokkos/build
49
+ # ArborX is header-only; it just needs to find the Kokkos we built.
50
+ git clone --depth 1 --branch "$ARBORX_TAG" https://github.com/arborx/ArborX.git /tmp/arborx
51
+ cmake -S /tmp/arborx -B /tmp/arborx/build -G Ninja \
52
+ -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=20 \
53
+ -DCMAKE_PREFIX_PATH="$PREFIX" -DCMAKE_INSTALL_PREFIX="$PREFIX"
54
+ cmake --install /tmp/arborx/build
55
+
56
+ - name: Build dem module
57
+ run: |
58
+ cmake -S . -B build \
59
+ -DCMAKE_PREFIX_PATH="$GITHUB_WORKSPACE/deps-install;$(python3 -m pybind11 --cmakedir)"
60
+ cmake --build build -j
61
+
62
+ - name: Import smoke test
63
+ run: PYTHONPATH=$PWD/build python3 -c "import dem; print('dem import OK')"
64
+
65
+ - name: Build + run Kokkos kernel unit tests (tests/kokkos)
66
+ run: |
67
+ # Kokkos-only unit suite (no ArborX / MPI): contact preprocessing, narrow-phase, the
68
+ # velocity/position/friction XPBD solves, integration, periodicity and the thermostat,
69
+ # each validated in isolation against a serial reference.
70
+ cmake -S tests/kokkos -B build_kokkos \
71
+ -DCMAKE_PREFIX_PATH="$GITHUB_WORKSPACE/deps-install"
72
+ cmake --build build_kokkos -j
73
+ ctest --test-dir build_kokkos --output-on-failure
@@ -0,0 +1,51 @@
1
+ name: Documentation
2
+
3
+ # Build the Doxygen API docs (Kokkos + ArborX C++ in src/ & mpi/) and publish them to GitHub Pages.
4
+ # Requires Pages to be enabled for the repository with "Source: GitHub Actions" (Settings -> Pages).
5
+ # Runs on every push to main; can also be triggered manually. Doxygen only parses the sources (no
6
+ # Kokkos/ArborX toolchain needed), so this runs on a plain ubuntu runner.
7
+
8
+ on:
9
+ push:
10
+ branches: [main]
11
+ paths:
12
+ - 'src/**'
13
+ - 'mpi/**'
14
+ - 'docs/**'
15
+ - 'README.md'
16
+ - '.github/workflows/docs.yml'
17
+ workflow_dispatch:
18
+
19
+ permissions:
20
+ contents: read
21
+ pages: write
22
+ id-token: write
23
+
24
+ concurrency:
25
+ group: pages
26
+ cancel-in-progress: true
27
+
28
+ jobs:
29
+ build:
30
+ runs-on: ubuntu-latest
31
+ steps:
32
+ - uses: actions/checkout@v4
33
+ - name: Install Doxygen + Graphviz
34
+ run: sudo apt-get update && sudo apt-get install -y doxygen graphviz
35
+ - name: Build documentation
36
+ run: doxygen docs/Doxyfile
37
+ - name: Upload Pages artifact
38
+ uses: actions/upload-pages-artifact@v3
39
+ with:
40
+ path: docs/html
41
+
42
+ deploy:
43
+ needs: build
44
+ runs-on: ubuntu-latest
45
+ environment:
46
+ name: github-pages
47
+ url: ${{ steps.deployment.outputs.page_url }}
48
+ steps:
49
+ - name: Deploy to GitHub Pages
50
+ id: deployment
51
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,26 @@
1
+ name: Quality
2
+
3
+ # Lightweight, non-breaking code-quality gates. The Python job hard-fails on *critical* errors only
4
+ # (syntax errors, undefined names, bad string formatting / comparisons — the classic stop-the-build
5
+ # set), so it catches real bugs without drowning research scripts in style nits.
6
+
7
+ on:
8
+ push:
9
+ branches: [main, "**"]
10
+ pull_request:
11
+ workflow_dispatch:
12
+
13
+ jobs:
14
+ python-lint:
15
+ name: ruff (critical errors)
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.12"
22
+ - run: pip install ruff
23
+ - name: Ruff critical-error check
24
+ run: |
25
+ ruff check . --select E9,F63,F7,F82 \
26
+ --exclude ".venv,venv,build,build_*,__pycache__,_deps,extern,legacy,notebooks"
@@ -0,0 +1,53 @@
1
+ name: Release
2
+
3
+ # Build the sdist + self-contained OpenMP CPU wheels and publish to PyPI (peclet-dem) on a version tag.
4
+ # Uses PyPI Trusted Publishing (OIDC) — configure the publisher on PyPI first; no API token secret needed.
5
+ # The wheels FetchContent-build Kokkos (OpenMP+Serial) + the core headers inside each build
6
+ # (PECLET_VENDOR_DEPS=ON, from pyproject's [tool.cibuildwheel]); GPU/HIP + MPI are the source (sdist) path.
7
+
8
+ on:
9
+ push:
10
+ tags: ["v*"]
11
+ workflow_dispatch:
12
+
13
+ jobs:
14
+ sdist:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: actions/setup-python@v5
19
+ with:
20
+ python-version: "3.12"
21
+ - run: pip install build
22
+ - run: python -m build --sdist
23
+ - uses: actions/upload-artifact@v4
24
+ with:
25
+ name: sdist
26
+ path: dist/*.tar.gz
27
+
28
+ wheels:
29
+ name: wheels (manylinux_2_28 x86_64)
30
+ runs-on: ubuntu-latest
31
+ steps:
32
+ - uses: actions/checkout@v4
33
+ # cibuildwheel reads [tool.cibuildwheel] from pyproject.toml (PECLET_VENDOR_DEPS=ON): each wheel
34
+ # compiles Kokkos as a subproject, so builds are slow but self-contained.
35
+ - uses: pypa/cibuildwheel@v2.21
36
+ - uses: actions/upload-artifact@v4
37
+ with:
38
+ name: wheels-linux
39
+ path: wheelhouse/*.whl
40
+
41
+ publish:
42
+ needs: [sdist, wheels]
43
+ runs-on: ubuntu-latest
44
+ if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
45
+ environment: pypi
46
+ permissions:
47
+ id-token: write # required for trusted publishing
48
+ steps:
49
+ - uses: actions/download-artifact@v4
50
+ with:
51
+ path: dist
52
+ merge-multiple: true
53
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,40 @@
1
+ # Build directories + logs
2
+ build/
3
+ build_*/
4
+ build_log*.txt
5
+ *.log
6
+ cmake-build-debug/
7
+ cmake-build-release/
8
+ bin/
9
+ lib/
10
+ *.so
11
+ *.a
12
+ *.o
13
+
14
+ # Python
15
+ __pycache__/
16
+ *.pyc
17
+ *.pyo
18
+ *.pyd
19
+ .Python
20
+ env/
21
+ venv/
22
+ .env
23
+ .venv
24
+
25
+ # IDEs
26
+ # .vscode/
27
+ .idea/
28
+ *.swp
29
+ *~
30
+ .DS_Store
31
+
32
+ # Generated artifacts (if any not in build)
33
+ *.egg-info/
34
+ dist/
35
+ .cache/
36
+ output/
37
+
38
+ # Generated Doxygen output (built in CI, published to Pages)
39
+ docs/html/
40
+ docs/latex/
@@ -0,0 +1,57 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "name": "(gdb) Launch Python Script (C++ Only)",
6
+ "type": "cppdbg",
7
+ "request": "launch",
8
+ "program": "${workspaceFolder}/.venv/bin/python",
9
+ "args": [
10
+ "${workspaceFolder}/verify_packing_hollow_cylinders_test.py"
11
+ ],
12
+ "stopAtEntry": false,
13
+ "cwd": "${workspaceFolder}",
14
+ "environment": [
15
+ {
16
+ "name": "PYTHONPATH",
17
+ "value": "${workspaceFolder}/build:${env:PYTHONPATH}"
18
+ }
19
+ ],
20
+ "externalConsole": false,
21
+ "MIMode": "gdb",
22
+ "setupCommands": [
23
+ {
24
+ "description": "Enable pretty-printing for gdb",
25
+ "text": "-enable-pretty-printing",
26
+ "ignoreFailures": true
27
+ }
28
+ ]
29
+ },
30
+ {
31
+ "name": "Python: Current File",
32
+ "type": "python",
33
+ "request": "launch",
34
+ "program": "${file}",
35
+ "console": "integratedTerminal",
36
+ "justMyCode": false,
37
+ "env": {
38
+ "PYTHONPATH": "${workspaceFolder}/build:${env:PYTHONPATH}"
39
+ }
40
+ },
41
+ {
42
+ "name": "(gdb) Attach to Python Process",
43
+ "type": "cppdbg",
44
+ "request": "attach",
45
+ "program": "${workspaceFolder}/.venv/bin/python",
46
+ "processId": "${command:pickProcess}",
47
+ "MIMode": "gdb",
48
+ "setupCommands": [
49
+ {
50
+ "description": "Enable pretty-printing for gdb",
51
+ "text": "-enable-pretty-printing",
52
+ "ignoreFailures": true
53
+ }
54
+ ]
55
+ }
56
+ ]
57
+ }
@@ -0,0 +1,65 @@
1
+ cmake_minimum_required(VERSION 3.24)
2
+ project(dem LANGUAGES CXX)
3
+
4
+ # Canonical packing-gpu build: the Kokkos + ArborX `dem` XPBD DEM module (CUDA / HIP / OpenMP backends,
5
+ # selected by the install prefix). Kokkos, ArborX and pybind11 are found via find_package against the
6
+ # bootstrapped install prefix (../extern/install/<backend>, built once by ../tools/bootstrap_deps.sh -- a
7
+ # HARD build dependency). With nvcc, put it on PATH (export PATH=/usr/local/cuda-13.2/bin:$PATH).
8
+ #
9
+ # Build (single-rank Python module); nanobind is found via the active interpreter (SuiteNanobind):
10
+ # cmake -S . -B build -DCMAKE_PREFIX_PATH="$PWD/../extern/install/nvidia-cuda"
11
+ # cmake --build build -j -> build/dem.*.so
12
+ #
13
+ # Multi-rank: -DDEM_MPI=ON links MPI + exposes init_mpi/enable_mpi_step/step_mpi in the module; the
14
+ # distributed step is also covered by the tests/kokkos_mpi ctests (run under mpirun).
15
+
16
+ set(CMAKE_CXX_STANDARD 20)
17
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
18
+ if(NOT CMAKE_BUILD_TYPE)
19
+ set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
20
+ endif()
21
+
22
+ option(PECLET_DEM_MPI "Build the dem module with the distributed (MPI) step exposed" OFF)
23
+
24
+ # Dependencies via the vendored PecletDeps helper: installed Kokkos+ArborX prefix + sibling checkout for
25
+ # the dev/suite build, or FetchContent-built Kokkos+ArborX + fetched core headers for a
26
+ # self-contained sdist/wheel (cibuildwheel). See cmake/PecletDeps.cmake.
27
+ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
28
+ include(PecletDeps)
29
+ peclet_require_kokkos()
30
+ peclet_require_arborx()
31
+ peclet_require_nanobind() # nanobind + core's View<->ndarray zero-copy bridge
32
+ peclet_sibling_include(peclet-core "${PECLET_TPX_TAG}" "../core" TPX_INCLUDE)
33
+
34
+ # NB_STATIC: bundle nanobind's runtime into the module; the Kokkos device path is routed by the
35
+ # launch compiler regardless. NOMINSIZE: nanobind's default -Os is rejected by nvcc ("'s': expected a
36
+ # number") since Kokkos device sources compile as CXX through the launch compiler.
37
+ nanobind_add_module(dem NB_STATIC NOMINSIZE src/dem_bindings.cpp)
38
+ # -> peclet.dem._dem (NB_MODULE(_dem)), re-exported by peclet/dem/__init__.py. The __init__ is kept as a
39
+ # plain file under packaging/ (OUTSIDE any importable peclet/ dir, so an incomplete source package can
40
+ # never shadow the installed one) and staged into <build>/peclet/dem so `PYTHONPATH=<build> python …`
41
+ # (`import peclet.dem`) works in the dev loop too.
42
+ set_target_properties(dem PROPERTIES OUTPUT_NAME _dem
43
+ LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/peclet/dem")
44
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/packaging/dem_init.py
45
+ ${CMAKE_CURRENT_BINARY_DIR}/peclet/dem/__init__.py COPYONLY)
46
+ target_include_directories(dem PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ${TPX_INCLUDE})
47
+ target_link_libraries(dem PRIVATE ArborX::ArborX Kokkos::kokkos)
48
+
49
+ if(PECLET_DEM_MPI)
50
+ find_package(MPI REQUIRED COMPONENTS CXX)
51
+ target_compile_definitions(dem PRIVATE PECLET_DEM_MPI=1)
52
+ target_link_libraries(dem PRIVATE MPI::MPI_CXX)
53
+ message(STATUS "dem: distributed step ENABLED (MPI)")
54
+ endif()
55
+
56
+ # --- pip / scikit-build-core install rule -------------------------------------------------------
57
+ # `pip install .` (driven by pyproject.toml) builds against a Kokkos+ArborX prefix on
58
+ # CMAKE_PREFIX_PATH and installs the extension as a top-level `dem` module. The rule is inert for a
59
+ # plain `cmake --build` (no `cmake --install`), so the developer workflow is unchanged. SKBUILD is set
60
+ # by scikit-build-core; SKBUILD_PROJECT_NAME-style guards keep it from interfering elsewhere.
61
+ if(DEFINED SKBUILD)
62
+ install(TARGETS dem LIBRARY DESTINATION peclet/dem COMPONENT python)
63
+ install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/packaging/dem_init.py
64
+ DESTINATION peclet/dem RENAME __init__.py COMPONENT python)
65
+ endif()
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Frank Peters
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,133 @@
1
+ Metadata-Version: 2.4
2
+ Name: peclet-dem
3
+ Version: 0.1.0
4
+ Summary: peclet.dem — performance-portable XPBD Discrete Element Method (Kokkos + ArborX)
5
+ Author-Email: Frank Peters <e.a.j.f.peters@gmail.com>
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Project-URL: Homepage, https://github.com/computational-chemical-engineering/peclet
9
+ Project-URL: Documentation, https://github.com/computational-chemical-engineering/peclet
10
+ Project-URL: Source, https://github.com/computational-chemical-engineering/peclet-dem
11
+ Requires-Python: >=3.10
12
+ Requires-Dist: numpy>=1.20
13
+ Description-Content-Type: text/markdown
14
+
15
+ # peclet-dem (`peclet.dem`)
16
+
17
+ 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.
18
+
19
+ > 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`.
20
+
21
+ ## Features
22
+
23
+ - **Hybrid XPBD Solver**: Two-pass velocity/position solver for stable high-density packing.
24
+ - **SDF Collision**: Point-shell collision detection using Signed Distance Fields (supports analytic shapes like hollow cylinders).
25
+ - **Periodicity**: Full periodic boundary conditions (Ghost Particles).
26
+ - **Python Bindings**: Control simulation logic, data initialization, and export entirely from Python.
27
+ - **MPI Support**: Optional Multi-GPU/Node support via domain decomposition.
28
+
29
+ ## Folder Structure
30
+
31
+ ```text
32
+ ├── CMakeLists.txt # Build configuration (find_package Kokkos + ArborX)
33
+ ├── src # Kokkos sources (header-only, namespace peclet::dem)
34
+ │ ├── dem_bindings.cpp # nanobind module entry point (the `peclet.dem` module)
35
+ │ ├── sim.hpp # Simulation facade + the demStep XPBD substep
36
+ │ ├── integration.hpp # Time integration & prediction
37
+ │ ├── broadphase_arborx.hpp # ArborX BVH broad-phase
38
+ │ ├── narrowphase.hpp # Narrow-phase point-shell-vs-SDF collision
39
+ │ ├── solver_velocity.hpp # Velocity solver kernels
40
+ │ ├── solver_position.hpp # Position solver kernels (XPBD overlap removal)
41
+ │ ├── solver_friction.hpp # Coulomb friction cluster
42
+ │ ├── output_sdf.hpp # SDF/VTI grid generation (Eikonal)
43
+ │ ├── shapes_portable.hpp # Analytic shapes (sphere / hollow cylinder / box)
44
+ │ ├── io.hpp # LAMMPS-dump + SDF-VTI export
45
+ │ └── mpi_halo.hpp # Distributed particle halo (core), gated PECLET_DEM_MPI
46
+ ├── tests # C++ unit tests: kokkos/ (kernels), arborx/, kokkos_mpi/
47
+ ├── docs # Documentation
48
+ └── *.py # Python verification/example scripts (verify_*.py)
49
+ ```
50
+
51
+ ## Prerequisites
52
+
53
+ - **Linux**
54
+ - **CMake** >= 3.24
55
+ - **Kokkos 5.x + ArborX** (C++20) — provisioned by `../tools/bootstrap_deps.sh` into
56
+ `../extern/install/<backend>` (`nvidia-cuda` / `host-openmp` / `lumi-hip`). A **hard build dependency**.
57
+ - **nanobind** + **scikit-build-core** (found via the active Python interpreter; see `pyproject.toml`)
58
+ - a backend compiler: **nvcc** (CUDA) on `PATH`, **hipcc** (ROCm), or just a host C++ compiler (OpenMP)
59
+ - **Python** >= 3.10
60
+ - **MPI** (optional, `-DDEM_MPI=ON`) — OpenMPI or MPICH
61
+
62
+ ## Build Instructions
63
+
64
+ ```bash
65
+ python -m venv .venv && source .venv/bin/activate && pip install nanobind numpy
66
+ export PATH=/usr/local/cuda-13.2/bin:$PATH # if building the CUDA backend
67
+
68
+ # Canonical: build + install the module via scikit-build-core
69
+ CMAKE_PREFIX_PATH="$PWD/../extern/install/nvidia-cuda" pip install .
70
+
71
+ # Or a dev cmake build (nanobind is found via the active interpreter, no cmakedir needed):
72
+ cmake -B build -S . -DCMAKE_PREFIX_PATH="$PWD/../extern/install/nvidia-cuda"
73
+ cmake --build build -j$(nproc)
74
+ ```
75
+ *Swap the prefix to `../extern/install/host-openmp` for the OpenMP backend. `-DDEM_MPI=ON` links MPI
76
+ and exposes the distributed step (`init_mpi` / `enable_mpi_step` / `step_mpi`), including dynamic load
77
+ balancing — `enable_mpi_step(..., rebalance_every=N)` or an explicit `rebalance()` re-decomposes by
78
+ particle count (weighted ORB) and migrates ownership so each rank keeps a near-equal share.*
79
+
80
+ The compiled `peclet.dem` extension is placed in `build/peclet/dem/`; run scripts with `build/` on `PYTHONPATH` (`import peclet.dem`).
81
+
82
+ ## Running Simulations
83
+
84
+ Example scripts are provided in the root directory:
85
+
86
+ ```bash
87
+ # Add build artifact to python path if needed (or symlink it)
88
+ export PYTHONPATH=$PYTHONPATH:$(pwd)/build
89
+
90
+ # Run a verification script
91
+ python verify_packing_hollow_cylinders.py
92
+ ```
93
+
94
+ ## Output & Visualization
95
+
96
+ The simulation supports two primary output formats:
97
+
98
+ ### 1. LAMMPS + STL (Ovito)
99
+
100
+ For particle visualization (especially non-spherical shapes), we use the LAMMPS dump format combined with an STL mesh.
101
+
102
+ 1. **Generate Output**: The simulation writes `dump.custom.*` files.
103
+ 2. **Generate Shape**: Run `python generate_particles.py` to create `particle_shape.stl`.
104
+ 3. **Visualize**:
105
+ - Open **Ovito**.
106
+ - Load the `dump.custom.*` sequence.
107
+ - Add a **Particle Types** modifier.
108
+ - Set the shape visualization to **Mesh/User-defined** and load `particle_shape.stl`.
109
+ - Ovito will automatically scale the mesh by the particle radius.
110
+
111
+ *See `docs/visualization.md` for a detailed guide.*
112
+
113
+ ### 2. VTI (ParaView)
114
+
115
+ For visualizing fields (like the Signed Distance Field or occupancy grids), the simulation exports VTI files (`.vti`).
116
+
117
+ 1. **Generate Output**: Use `Simulation.export_sdf("filename.vti", resolution=...)`.
118
+ 2. **Visualize**:
119
+ - Open **ParaView**.
120
+ - Load the `.vti` file.
121
+ - Use "Volume" representation or "Slice" filter to inspect the field.
122
+
123
+ ## Status
124
+
125
+ The single-GPU engine is complete and validated: it reaches stable high-density (random close)
126
+ packing, and energy is conserved to ~0.3% (see `docs/packing_investigation.md`). Friction is
127
+ stabilized for spheres; body-body tangential friction is a known follow-up (currently weaker than
128
+ ideal). Active work is at-scale multi-GPU/MPI tuning.
129
+
130
+ > [!NOTE]
131
+ > The distributed (MPI) step is validated against the single-rank result (`tests/kokkos_mpi`,
132
+ > np=1,2,4 on OpenMP + CUDA) and supports dynamic load rebalancing; remaining MPI work is at-scale
133
+ > multi-GPU tuning.
@@ -0,0 +1,119 @@
1
+ # peclet-dem (`peclet.dem`)
2
+
3
+ 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.
4
+
5
+ > 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`.
6
+
7
+ ## Features
8
+
9
+ - **Hybrid XPBD Solver**: Two-pass velocity/position solver for stable high-density packing.
10
+ - **SDF Collision**: Point-shell collision detection using Signed Distance Fields (supports analytic shapes like hollow cylinders).
11
+ - **Periodicity**: Full periodic boundary conditions (Ghost Particles).
12
+ - **Python Bindings**: Control simulation logic, data initialization, and export entirely from Python.
13
+ - **MPI Support**: Optional Multi-GPU/Node support via domain decomposition.
14
+
15
+ ## Folder Structure
16
+
17
+ ```text
18
+ ├── CMakeLists.txt # Build configuration (find_package Kokkos + ArborX)
19
+ ├── src # Kokkos sources (header-only, namespace peclet::dem)
20
+ │ ├── dem_bindings.cpp # nanobind module entry point (the `peclet.dem` module)
21
+ │ ├── sim.hpp # Simulation facade + the demStep XPBD substep
22
+ │ ├── integration.hpp # Time integration & prediction
23
+ │ ├── broadphase_arborx.hpp # ArborX BVH broad-phase
24
+ │ ├── narrowphase.hpp # Narrow-phase point-shell-vs-SDF collision
25
+ │ ├── solver_velocity.hpp # Velocity solver kernels
26
+ │ ├── solver_position.hpp # Position solver kernels (XPBD overlap removal)
27
+ │ ├── solver_friction.hpp # Coulomb friction cluster
28
+ │ ├── output_sdf.hpp # SDF/VTI grid generation (Eikonal)
29
+ │ ├── shapes_portable.hpp # Analytic shapes (sphere / hollow cylinder / box)
30
+ │ ├── io.hpp # LAMMPS-dump + SDF-VTI export
31
+ │ └── mpi_halo.hpp # Distributed particle halo (core), gated PECLET_DEM_MPI
32
+ ├── tests # C++ unit tests: kokkos/ (kernels), arborx/, kokkos_mpi/
33
+ ├── docs # Documentation
34
+ └── *.py # Python verification/example scripts (verify_*.py)
35
+ ```
36
+
37
+ ## Prerequisites
38
+
39
+ - **Linux**
40
+ - **CMake** >= 3.24
41
+ - **Kokkos 5.x + ArborX** (C++20) — provisioned by `../tools/bootstrap_deps.sh` into
42
+ `../extern/install/<backend>` (`nvidia-cuda` / `host-openmp` / `lumi-hip`). A **hard build dependency**.
43
+ - **nanobind** + **scikit-build-core** (found via the active Python interpreter; see `pyproject.toml`)
44
+ - a backend compiler: **nvcc** (CUDA) on `PATH`, **hipcc** (ROCm), or just a host C++ compiler (OpenMP)
45
+ - **Python** >= 3.10
46
+ - **MPI** (optional, `-DDEM_MPI=ON`) — OpenMPI or MPICH
47
+
48
+ ## Build Instructions
49
+
50
+ ```bash
51
+ python -m venv .venv && source .venv/bin/activate && pip install nanobind numpy
52
+ export PATH=/usr/local/cuda-13.2/bin:$PATH # if building the CUDA backend
53
+
54
+ # Canonical: build + install the module via scikit-build-core
55
+ CMAKE_PREFIX_PATH="$PWD/../extern/install/nvidia-cuda" pip install .
56
+
57
+ # Or a dev cmake build (nanobind is found via the active interpreter, no cmakedir needed):
58
+ cmake -B build -S . -DCMAKE_PREFIX_PATH="$PWD/../extern/install/nvidia-cuda"
59
+ cmake --build build -j$(nproc)
60
+ ```
61
+ *Swap the prefix to `../extern/install/host-openmp` for the OpenMP backend. `-DDEM_MPI=ON` links MPI
62
+ and exposes the distributed step (`init_mpi` / `enable_mpi_step` / `step_mpi`), including dynamic load
63
+ balancing — `enable_mpi_step(..., rebalance_every=N)` or an explicit `rebalance()` re-decomposes by
64
+ particle count (weighted ORB) and migrates ownership so each rank keeps a near-equal share.*
65
+
66
+ The compiled `peclet.dem` extension is placed in `build/peclet/dem/`; run scripts with `build/` on `PYTHONPATH` (`import peclet.dem`).
67
+
68
+ ## Running Simulations
69
+
70
+ Example scripts are provided in the root directory:
71
+
72
+ ```bash
73
+ # Add build artifact to python path if needed (or symlink it)
74
+ export PYTHONPATH=$PYTHONPATH:$(pwd)/build
75
+
76
+ # Run a verification script
77
+ python verify_packing_hollow_cylinders.py
78
+ ```
79
+
80
+ ## Output & Visualization
81
+
82
+ The simulation supports two primary output formats:
83
+
84
+ ### 1. LAMMPS + STL (Ovito)
85
+
86
+ For particle visualization (especially non-spherical shapes), we use the LAMMPS dump format combined with an STL mesh.
87
+
88
+ 1. **Generate Output**: The simulation writes `dump.custom.*` files.
89
+ 2. **Generate Shape**: Run `python generate_particles.py` to create `particle_shape.stl`.
90
+ 3. **Visualize**:
91
+ - Open **Ovito**.
92
+ - Load the `dump.custom.*` sequence.
93
+ - Add a **Particle Types** modifier.
94
+ - Set the shape visualization to **Mesh/User-defined** and load `particle_shape.stl`.
95
+ - Ovito will automatically scale the mesh by the particle radius.
96
+
97
+ *See `docs/visualization.md` for a detailed guide.*
98
+
99
+ ### 2. VTI (ParaView)
100
+
101
+ For visualizing fields (like the Signed Distance Field or occupancy grids), the simulation exports VTI files (`.vti`).
102
+
103
+ 1. **Generate Output**: Use `Simulation.export_sdf("filename.vti", resolution=...)`.
104
+ 2. **Visualize**:
105
+ - Open **ParaView**.
106
+ - Load the `.vti` file.
107
+ - Use "Volume" representation or "Slice" filter to inspect the field.
108
+
109
+ ## Status
110
+
111
+ The single-GPU engine is complete and validated: it reaches stable high-density (random close)
112
+ packing, and energy is conserved to ~0.3% (see `docs/packing_investigation.md`). Friction is
113
+ stabilized for spheres; body-body tangential friction is a known follow-up (currently weaker than
114
+ ideal). Active work is at-scale multi-GPU/MPI tuning.
115
+
116
+ > [!NOTE]
117
+ > The distributed (MPI) step is validated against the single-rank result (`tests/kokkos_mpi`,
118
+ > np=1,2,4 on OpenMP + CUDA) and supports dynamic load rebalancing; remaining MPI work is at-scale
119
+ > multi-GPU tuning.
@@ -0,0 +1,18 @@
1
+ #!/bin/bash
2
+ # Source this script to set up the development environment
3
+ # Usage: source activate_env.sh
4
+
5
+ # Get the absolute path of the script directory
6
+ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
7
+ BUILD_DIR="$SCRIPT_DIR/build"
8
+
9
+ if [ ! -d "$BUILD_DIR" ]; then
10
+ echo "Warning: Build directory '$BUILD_DIR' does not exist yet."
11
+ echo "Make sure to run: mkdir build && cd build && cmake .. && make"
12
+ fi
13
+
14
+ # Add build directory to PYTHONPATH
15
+ export PYTHONPATH="$BUILD_DIR:$PYTHONPATH"
16
+
17
+ echo "Environment configured."
18
+ echo "PYTHONPATH includes: $BUILD_DIR"