Developer architecture

Public API boundary

Public operations enter through DEMSolver and related setup objects. New simulation state normally follows this path:

  1. public API or cached initializer data;

  2. dynamic and kinematic worker storage;

  3. device-data pointer binding;

  4. host-to-device transfer;

  5. JIT substitutions or kernel arguments;

  6. runtime update behavior.

Missing one stage can produce failures only after initialization or only on a second GPU.

CUDA and JIT kernels

Kernel launch argument types must match exactly. Validate and explicitly narrow host values at the launch boundary. Treat block size as correctness-sensitive: large JIT kernels can exceed register or launch limits even when a smaller kernel accepts the same configuration.

Synchronization

The dT/kT streams, events, and handoff buffers implement the intended synchronization. Avoid adding device-wide synchronization to hot paths. When cross-device data is transferred, preserve the lifetime of the source storage until the destination operation has completed.

Adding Python bindings

When exposing a public API:

  1. bind the method in src/DEM/python/bindings.cpp;

  2. use explicit overload casts;

  3. provide parameter names and a practical docstring;

  4. document units, frame conventions, and object lifetime;

  5. add an installed-wheel test, not only an in-tree import test;

  6. update the Python guide when behavior differs from C++.