Migrating from 0.6 to 0.7#

Version 0.7.0 changes how a compiled robot is identified, how a consumer artifact is built and reloaded, how collision margins are specified, and what the plant’s second derivative means on a manifold. Python users who call load_robot and the batched handle methods need only rebuild their caches. Consumers who compile the generated header into their own kernels should walk the steps below in order. Each step names the failure a user meets without it.

1. Pin and keep the old environment#

Install 0.7.0 into a new environment or a new cache directory (cache_dir= or GRID_RBD_CACHE_DIR) and keep the previous deployment until the steps below pass. Do not purge a shared cache: entries are content-keyed, so old and new builds coexist, and a purge removes artifacts another process may still be using.

2. Rebuild prepared artifacts once#

Consumer artifact schema 2 stores the model descriptor beside the header and binds the three together. An artifact built by 0.6 has no descriptor, so load_artifact refuses it rather than guessing. Rebuild with build_artifact once; do not copy a descriptor file next to an old binary, because the association check compares hashes and will reject the pair.

from grid_rbd import describe_model, build_artifact, load_artifact

model = describe_model("robot.urdf")
art = build_artifact("myapp", "robot", "robot.urdf",
                     sources=["kernel.cu"],
                     algorithm_list=["end_effector_pose"],
                     expected_descriptor_id=model["descriptor_id"])
# later, without a compiler or the URDF:
art = load_artifact("myapp", "robot",
                    expected_descriptor_id=model["descriptor_id"])

expected_descriptor_id is optional, but an application that inspected the model earlier should pass it so a changed URDF cannot silently pair with a stale binary. The descriptor identity covers coordinates, names, limits and placements; it does not cover inertias, so two physically different robots can share one identity. Build reuse is decided by the artifact content key, not by the descriptor.

3. Declare Python extensions#

A consumer that compiles a pybind11 module through build_artifact now passes python_extension=True. The build then records the interpreter ABI in the artifact identity, exports only the module entry point and keeps the module’s static state private, so two revisions or two robots loaded in one process do not share symbols. Without the flag a build whose object exports a PyInit_* symbol is refused before publication, so a pybind module cannot be recorded as a plain native library. There is no stable-ABI promise: a module built for CPython 3.12 is reused by CPython 3.12 only.

Loading a second revision of a module under the same name in one process is not supported by the interpreter; switch revisions in a fresh process.

4. Build lookup and prepared execution are different paths#

At build and lookup time GRiD runs the compiler’s dependency pass (nvcc -MM) over every compilation unit, so an edit to any header the unit actually includes, including a transitive or shadowing one, invalidates the artifact. Manual headers= lists remain available as a supplement. This means a warm lookup needs the compiler and the sources. A prepared application that calls load_artifact needs neither; it checks the saved descriptor, header hash and binary hash and loads. Containers that ship without a toolchain must use the prepared path. System headers excluded by the compiler’s dependency output are an environment assumption: the toolkit and host compiler identities are recorded, their contents are not.

5. Collision margins#

The checkers take a CollisionMargins<T> with environment_margin and self_margin where 0.6 took one scalar. A scalar still compiles and applies the same value to both checks, so an unchanged call keeps its 0.6 behaviour. An application that wants clearance against the environment but none between its own links passes the two values explicitly:

auto margins = grid_collision::make_collision_margins<float>(0.02f, std::nullopt, 0.0f);

The host factory rejects non-finite or negative values. See Collision code generation.

6. Dimensions#

The descriptor distinguishes nq (stored configuration entries), nv (tangent entries), nj (effective joints) and nb (effective bodies). On a floating base or a spherical joint nq exceeds nv; the difference does not identify a free-flyer, and neither count is an actuator count. Stored state has nq+nv entries and tangent state 2*nv. A solver’s control width, its linear feature rows (MX, MU on the cost menu) and its actuation map are the solver’s own definitions. See Model coordinates and named frames.

7. Defects, duals and the second derivative#

The native difference D(a, b) is reference-first, and its two Jacobians reduce to -I and +I only on scalar coordinates. A shooting solver that linearises a nonzero floating or spherical defect must compose the step gradient with the difference Jacobians at the actual anchors and transport its multipliers accordingly; a sign change is not a substitute. plant_step_hessian is the derivative of the moving step gradient, not a fixed-chart Hessian and not the curvature of a defect. Scalar fixed-base models are unaffected. The maps, the row normalisation and the covector relation are specified in Integration, derivatives and model primitives.

8. Workspaces inside your kernel#

grid_plant now exposes checked bind views for the plant gradient, the plant Hessian and the cost menu, sized by the same layout the kernels use. A view establishes capacity and alignment only; the enclosing kernel’s static plus dynamic shared memory must still be admitted against the device limit by the caller. The Hessian view covers fixed-base nq == nv models with Euler and semi-implicit Euler; other configurations keep using the tiered kernels. See Composing generated workspaces.

9. Validate#

Run your own regression on the rebuilt artifacts, compare a handful of outputs against the 0.6 deployment, and exercise the prepared path in a process without a compiler before switching production. The example bindings/examples/consumer_artifact_migration.py builds a native library and a declared extension, reuses them, and loads them again from a fresh process that cannot see the sources or the toolchain.