phyz 0.4.0

Multi-physics differentiable simulation engine
Documentation
# phyz

The differentiable rigid-body core of phyz.

`phyz` re-exports the focused `phyz-*` crates that make up the rigid-body
stack, so `cargo add phyz` gets you all of them behind one dependency and
`phyz::collision` is literally `phyz_collision`. The one thing it adds is
`sim` — the time loop, which none of the focused crates own.

It does **not** cover the whole workspace — `phyz-gpu`, `phyz-particle`,
`phyz-lbm` and friends are separate crates you add explicitly.

## Features

`collision`, `contact` and `diff` are on by default; `math`, `model` and
`rigid` are always present.

```bash
cargo add phyz                                  # everything below
cargo add phyz --no-default-features            # math + model + rigid only
cargo add phyz --no-default-features -F diff    # ...plus gradients
```

## Modules

| Module | Contents |
| --- | --- |
| `math` | [`phyz-math`]https://docs.rs/phyz-math: `Vec3`, `Mat3`, `Quat`, `SpatialVec`, `SpatialTransform`, `SpatialInertia` |
| `model` | [`phyz-model`]https://docs.rs/phyz-model: `Model`, `ModelBuilder`, `Body`, `Joint`, `JointType`, `Actuator`, `State` |
| `rigid` | [`phyz-rigid`]https://docs.rs/phyz-rigid: `aba`, `rnea`, `crba`, `forward_kinematics`, `body_wrenches` |
| `collision` | [`phyz-collision`]https://docs.rs/phyz-collision: `gjk_distance`, `epa_penetration`, `ray_cast`, `sweep_and_prune`, `AABB` |
| `contact` | [`phyz-contact`]https://docs.rs/phyz-contact: `find_contacts`, `find_ground_contacts`, `contact_forces`, `ContactMaterial` |
| `diff` | [`phyz-diff`]https://docs.rs/phyz-diff: `adjoint_rollout_gradient`, plus the per-step Jacobians |
| `sim` | `Simulator`, `Solver`, `SemiImplicitEulerSolver`, `Rk4Solver` — this crate's only original code |

## Forward dynamics

```rust
use phyz::{Mat3, ModelBuilder, SpatialInertia, SpatialTransform, Vec3};
use phyz::rigid::aba;

let model = ModelBuilder::new()
    .gravity(Vec3::new(0.0, -9.81, 0.0))
    .dt(0.002)
    .add_revolute_body(
        "pendulum",
        -1,
        SpatialTransform::identity(),
        SpatialInertia::new(
            1.0,
            Vec3::new(0.0, -0.5, 0.0),
            Mat3::from_diagonal(&Vec3::new(0.083, 0.0, 0.083)),
        ),
    )
    .build();

let mut state = model.default_state();
state.q[0] = 0.3;

let qdd = aba(&model, &state);   // joint accelerations, O(n)
assert_eq!(qdd.len(), model.nv);
```

## Gradients

`diff::adjoint_rollout_gradient` runs one forward rollout and one backward
adjoint pass, returning:

* `d_inertia[i]` — `dJ/dπ` for body `i`'s ten spatial-inertia scalars
  `[m, cx, cy, cz, Ixx, Iyy, Izz, Ixy, Ixz, Iyz]`
* `d_vertices[i]` — `∂J/∂x` per collision-mesh vertex, when ground contact is
  configured

Both are **exact**: the per-step Jacobians come from dual numbers through a
scalar-generic ABA, not finite differences. See the repository README for a
worked example.

For per-step Jacobians with respect to `(q, v, ctrl)`, use
[`phyz-diff`](https://docs.rs/phyz-diff) instead.

## Naming

Each sub-crate is reachable under both names: `phyz::rigid::aba` and
`phyz::phyz_rigid::aba` are the same function, and both are
`phyz_rigid::aba`. Types cross crate boundaries freely because there is only
ever one of them.

## Part of phyz

[`phyz`](https://github.com/ecto/phyz) is an open-source differentiable
multi-physics simulation workspace in pure Rust. Each crate is independent —
adding this one does not pull in the rest.

Licensed under [MIT](https://github.com/ecto/phyz/blob/main/LICENSE).