Skip to content

Solver Internal Data Migration Policy

Target convention for new or migrated device-oriented solver scratch under novaphy/include/dynamics/. This is not a description of every current solver: CPU and legacy paths still contain per-element std::vector and AoS caches, including Semi-Implicit, Featherstone, and MuJoCo-style internals. Their existence is not evidence that the migration is complete.

The design rationale is retained in the repository-only docs/superpowers/specs/2026-05-21-solver-internal-data-pipeline-design.md artifact, which is intentionally excluded from the public site.

Target rules for migrated scratch

Apply these rules when introducing GPU-capable scratch or explicitly migrating an existing cache:

  1. Container. Use DeviceArray<T> for per-element scratch that must follow the model device. CPU-only solver caches may remain std::vector<T> until their owning path is migrated.
  2. Layout. Prefer struct-of-arrays for kernel-facing scratch — one DeviceArray<scalar/Vec3f/Quatf/...> per field.
  3. Device. Migrated scratch should satisfy scratch.device() == model.device().
  4. Allocation. Allocate at the top of step() (or in a prepare_* / ensure_* helper called from step()), via DeviceArray<T>::zeros(N, model.device()) or DeviceArray<T>::empty_like(model.x). The constructor allocates only Settings and trivially-copyable scalar/counter members.
  5. Variable-size scratch. Capacity comes from Contacts::rigid_contact_max or a Model::*_count. Active count is contacts->rigid_count(). Solvers must assert(active_count <= capacity).
  6. Expected host-side exceptions. Build-time CPU-only assets (Mesh::vertices_, Mesh::indices_), tree-shaped ownership (std::unique_ptr<MultiBody>), and short-lived function-local temporaries may remain host containers. POD scalars and counters stay plain members (e.g. int active_contact_count_, float last_dt_).
  7. Private helper signatures. A helper on a migrated path should preserve DeviceArray<T>& / const DeviceArray<T>& through the kernel boundary.

Performance note

ensure_*_scratch_ helpers should reuse an existing allocation when its device and capacity already match the requested layout. This avoids per-step allocation overhead without changing the caller-visible solver contract.