Developer architecture ====================== Public API boundary ------------------- Public operations enter through ``DEMSolver`` and related setup objects. New simulation state normally follows this path: #. public API or cached initializer data; #. dynamic and kinematic worker storage; #. device-data pointer binding; #. host-to-device transfer; #. JIT substitutions or kernel arguments; #. 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: #. bind the method in ``src/DEM/python/bindings.cpp``; #. use explicit overload casts; #. provide parameter names and a practical docstring; #. document units, frame conventions, and object lifetime; #. add an installed-wheel test, not only an in-tree import test; #. update the Python guide when behavior differs from C++.