Tet Null Space Plan
This page explains why the asset tet bodies of P01 collapse, what the two fixes do, what the probes and the trained arms show, and what we decide next. Teal marks the tet mesh and its energy. Gray marks the hex carrier grid. Orange marks contact and constraints. Green marks a kept state, red a defect. Every number traces to a file listed in the footer. Where a figure is a sketch and not data, the caption says so.
1. The problem in one picture
An asset tet body rides on a hex carrier grid. Each tet vertex sits at fixed trilinear coordinates inside one cell. Its position is the weighted sum of that cell's eight corners, x_v = J x. The corners are the only degrees of freedom. The tet energy reads J x and nothing else. When the grid has more corners than the mesh has vertices, there are corner motions that move no vertex. These motions form the null space of J. The energy cannot see them.
Only the inertia term touches the corners directly, and inertia resists only a change of velocity. Once a null-space velocity exists, nothing slows it down. Three sources feed it: the initial deformation velocity field, the contact forces of the voxel path, which act on the hex face samples and not on J x, and the network's per-query proposals, which no energy loss corrects in ker J. These three sources are the diagnosis report's, section "Summary".
2. Why assets and not lattice bodies
A lattice body puts a tet vertex on every grid corner. Its J is the identity. Nothing can move unseen. An asset mesh fills the cells with tets whose vertices sit inside the cells, so the grid has far more corners than the mesh has vertices.
Measured for the two bodies of the soft well: the plate 353 has 2268 cells, 8090 tets, 2847 vertices and 4675 hex corners. The compact body 145 has 1994 cells, 7730 tets, 2462 vertices and 4256 corners. The rank of J is 2846 of 2847 rows for the plate and 2462 of 2462 for the compact body. So dim ker J is 5487 of 14025 corner degrees of freedom for the plate and 5382 of 12768 for the compact body. Across the library the surplus of corners over vertices is 1700 to 1800 per body for the three failing meshes 353, 145 and 485. The lattice arm has no surplus.
3. What happens in a rollout
The diagnosis probe rolls the T1 epoch 20 checkpoint on the demo scene voxel_well_soft exactly as the renderer does: 8 queries per step, no noise, 450 steps. It records the carrier's lattice edge ratio and the tet edge ratio per body and per step. Both are the current edge length over the rest edge length, maximum over the body.
The order of events, all from the per-step record. The carrier drifts from step 0. After the 8 queries of step 0 the plate's longest hex edge is already 1.4 percent long and the compact body's 6 percent. No contact pair exists before step 22 in the per-step record; the report rounds this to the first 25 steps. The lattice edge ratio reaches 4.6 for the plate and 7.7 for the compact body by step 60. In free flight alone, with no plane and no walls, the drift rate is 0.16 to 0.19 edge-length units per step, which is 0.020 to 0.024 cells per query, about one step cap of 0.01625 cells per query. The first hex cell inverts at step 40 for the compact body and step 44 for the plate. The tet edge ratio stays at 1.00 to 1.08 until step 60. The scrambled hex surfaces produce spurious contacts and energy: the plate's kinetic energy is 13 J at step 275 and 637 J at step 300. The two hex surfaces meet around step 290. The first tet inverts at step 294, when the plate owns 174 body pairs. At step 449 the plate has 4063 inverted tets and the compact body 3874.
| step | plate carrier edge max | compact carrier edge max | plate tet edge max | compact tet edge max | plate inverted hex cells | compact inverted hex cells | plate inverted tets | compact inverted tets | plate body pairs | compact body pairs |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1.01 | 1.06 | 1.00 | 1.03 | 0 | 0 | 0 | 0 | 0 | 0 |
| 10 | 1.13 | 1.87 | 1.02 | 1.04 | 0 | 0 | 0 | 0 | 0 | 0 |
| 20 | 1.38 | 2.89 | 1.02 | 1.06 | 0 | 0 | 0 | 0 | 0 | 0 |
| 30 | 1.87 | 3.95 | 1.02 | 1.05 | 0 | 0 | 0 | 0 | 0 | 0 |
| 40 | 2.57 | 4.82 | 1.03 | 1.06 | 0 | 1 | 0 | 0 | 0 | 0 |
| 45 | 2.94 | 5.56 | 1.04 | 1.06 | 2 | 1 | 0 | 0 | 0 | 0 |
| 60 | 4.58 | 7.74 | 1.05 | 1.08 | 6 | 2 | 0 | 0 | 0 | 0 |
| 100 | 10.38 | 11.45 | 1.17 | 1.30 | 20 | 88 | 0 | 0 | 0 | 0 |
| 200 | 17.00 | 33.53 | 1.38 | 1.87 | 265 | 216 | 0 | 0 | 0 | 0 |
| 275 | 48.81 | 86.15 | 1.35 | 1.36 | 430 | 377 | 0 | 0 | 0 | 5 |
| 290 | 70.87 | 135.69 | 1.92 | 1.54 | 448 | 407 | 0 | 0 | 36 | 5 |
| 295 | 80.91 | 156.63 | 2.86 | 2.81 | 497 | 416 | 18 | 3 | 198 | 18 |
| 300 | 90.27 | 179.80 | 4.37 | 5.10 | 557 | 413 | 132 | 8 | 440 | 27 |
| 350 | 280.45 | 494.16 | 26.09 | 71.83 | 890 | 569 | 1610 | 1033 | 1672 | 1000 |
| 449 | 1548.17 | 1936.33 | 285.66 | 446.10 | 1306 | 1215 | 4063 | 3874 | 1982 | 888 |
The training runs show the same collapse. The cheap validation of one physical step cannot see a drift that needs advance to turn a displacement into a velocity, so it reported 8 of 8 survivors for T1. The full-horizon validation of 8 queries by 128 steps on 4 held-out scenes reports 0 of 4 survivors for T1, T2 and T2_hand at every epoch from epoch 4 on, all collapsed with hex penetration above 3 r and 592 to 1327 inverted hex cells. The training guard replaced 45 to 78 scenes per epoch from epoch 6 on. T0, the lattice arm, keeps 4 of 4 and replaces 0 to 3 scenes per epoch.
4. The controls that rule out other causes
Each control changes one thing on the same scene seed. The first inverted tet moves by a few steps at most. Only the lattice representation removes the failure. The table lists the step of the first inverted tet, the step of the first inverted hex cell, and the inverted tets at the end of 450 steps. Each cell reads plate / compact body.
| control | first inverted tet | first inverted hex cell | inverted tets at the end | reading |
|---|---|---|---|---|
| T1 baseline, 8 queries | 294 / 294 | 44 / 40 | 4063 / 3874 | the failure |
| T0 checkpoint, lattice bodies of the same seed, 7 bodies | never / never | never | 0 | max edge ratio 1.70; one body has 5 inverted tets at step 193 under contact, 0 at the end |
| kappa raised to 191.15 | 301 / 289 | 61 / 52 | 4104 / 3819 | contact stiffness changes nothing of substance |
| inversion factor 0 | 295 / 295 | 46 / 37 | 3354 / 3859 | the inversion rule is not the cause |
| inversion factor 30 | 260 / 286 | 45 / 36 | 3212 / 3876 | same |
| zero body velocity and zero drift | 261 / 250 | 41 / 40 | 4006 / 3919 | velocity is not the cause |
| at rest on the floor, no deformation field | 216 / 236 | 25 / 5 | 3966 / 3904 | fails earlier: contact on the hex surface drives the carrier too |
| 1 query per step instead of 8 | 348 / 124 | 80 / 29 | 156 / 1578 | slower damage, same drift |
| plate alone, free flight, no plane, no walls | 341 / - | 51 / - | 3160 / - | no contact at all, same drift and collapse |
| compact body alone, free flight | - / 305 | - / 40 | - / 3805 | same |
| same occupancies with lattice tets | never / never | never | 0 / 0 | max edge ratio 2.59 and 1.84; the problem is gone |
| Newton SolverVBD on the bare tet mesh, 10 iterations, dt 1/300 s, 450 substeps | 4 at substep 150 / never | - | 0 / 0 | plate: transient at the floor impact, final tet edge ratio max 1.12; the mesh and the material are fine |
Thinness and sliver content do not separate the failing bodies from the rest: 82 to 84 percent of the library has a median voxel thickness of 2 cells or less in every split, and all four inspected meshes have a minimum dihedral angle of 7 to 9 degrees. What the failing bodies share and the lattice bodies lack is the surplus of corners over vertices.
5. Every candidate, one picture each
Six ways to close the null space are on the table. Each panel uses the toy of Figure 1: three grey cells, teal tet vertices, dark corner dots. The purple arrows are the null-space corner motion of Figure 1, the same vector d with J d = 0. Orange marks the element that pins that motion. The chip gives the status and the measured number when one exists. Sections 6 to 8 give the details of the first three.
6. Fix 1: the carrier weight
Switch carrier_hex_weight = w. With w > 0 the potential of a tet batch is
Phi(x) = E_tet_elastic(J x) + E_tet_damping(J x) + w E_hex_elastic(x) + inertia + contact
E_hex_elastic is the hex Neo-Hookean elastic energy of the carrier cells: eight Gauss points per cell, the body's own lam and mu_scale, the inversion rule of stable_neo_hookean, no hex damping. In code it is physics.carrier_hex_elastic: scale * (hc.weights * stable_neo_hookean(F, lam)).sum(-1) per cell, with F the Gauss-point deformation gradient of the cell's eight corners, then w times the per-object sum. The term is zero at the rest lattice and depends on x itself, so a ker J displacement costs energy and gets a restoring force in every direction. The price is a bias: the body as a whole is stiffer by about w times the material's stiffness on the filled part of each cell, because a deformation of J x deforms the carrier too. On lattice tets the hex and tet energies agree on affine deformations, so the term would only stiffen the body uniformly; the lattice arms stay at w = 0 and are bitwise unchanged.
What the probe measures at step 120 of the soft well, plate and compact body: carrier edge max 12.18 and 13.63 at weight 0, 1.60 and 4.02 at 0.02, 1.28 and 3.37 at 0.05, 1.28 and 2.81 at 0.1, 1.30 and 2.22 at 0.2. The 60-step drift slope falls from 0.057 and 0.129 per step at weight 0 to at most 0.004 per step from 0.05 on. The maximal penetration falls from 1.28 and 2.24 r to 0.04 to 0.09 r. The tet edge max is 1.15 to 1.19 for the plate and 1.30 to 1.33 for the compact body at every weight, so the material is almost untouched at the probe. The network was trained without the term, so the probe is a stabilisation test, not an accuracy test.
Inference from these numbers, not a measurement: on the voxel surface the contact forces act on the hex face samples, so the carrier has to carry the contact, and a weight of 0.05 is not enough there. On the tet surface the carrier carries only the network's features and frames, and both weights suffice. The accuracy cost of the bias is not measured yet.
7. Fix 2: the minimal-norm reset
Switch carrier_reset. It adds no energy. At the end of every committed physical step, in Step.advance after the DAT truncation and before the velocity update, every asset body's corners are rebuilt. From the docstring of carrier_reset.py:
v = J x_committed the vertex positions the physics produced
x_rigid = R (x_rest - rc) + vc the rest corners under the rigid transform that best fits
the vertex motion: vertex-mass weighted Procrustes fit
x_new = x_rigid + J^T (J J^T)^-1 (v - J x_rigid) the minimum-norm corner correction that reproduces v
J x_new equals v to rounding. The material is unchanged. The carrier loses every accumulated component in ker J. V = X_new - X_prev sees the reset state. Pinned corners keep their positions and the least-squares runs on the free corners only. The system J_F J_F^T is fixed per mesh and pin set, assembled once, and solved per batch layout with one factorisation: dense Cholesky up to 1024 vertices per block, cuDSS above it, both in float64. Lattice bodies are skipped. The reset is not differentiable and never runs inside a query.
What the probe measures with the reset on, same scene, same checkpoint, weight 0. No tet inverts in 120 steps on either body. The compact body's tet edge max is 1.08, the best of all rows, against 1.30 at weight 0 and 1.32 at weight 0.05. Its elastic plus damping energy at step 120 is 0.39 J against 22.8 J at weight 0. But the plate's carrier edge ratio is still 10.7 at step 120 with 127 inverted hex cells, and the 60-step slopes fall only from 0.057 and 0.129 to 0.021 and 0.043 per step. The reason is in the per-corner dumps: the plate bends. Its vertices leave the rigid pose by 2.50 cells RMS and 8.90 cells max at step 119 at a tet edge ratio of 1.21. The minimum-norm reconstruction of that bend is not smooth.
| weight | reset | body | carrier edge max at 120 | inverted hex cells at 120 | inverted tets at 120 | tet edge max at 120 | max penetration, r |
|---|---|---|---|---|---|---|---|
| 0 | off | plate 353 | 12.18 | 32 | 0 | 1.15 | 1.28 |
| 0 | off | compact 145 | 13.63 | 117 | 0 | 1.30 | 2.24 |
| 0.05 | off | plate 353 | 1.28 | 0 | 0 | 1.19 | 0.04 |
| 0.05 | off | compact 145 | 3.37 | 0 | 0 | 1.32 | 0.06 |
| 0 | on | plate 353 | 10.71 | 127 | 0 | 1.21 | 0.38 |
| 0 | on | compact 145 | 3.52 | 0 | 0 | 1.08 | 1.57 |
Cost, measured with CUDA events: 2.94 ms per step on the two-body probe scene with 5309 vertices. On a D1 scene with 34135 cells, 22 bodies, 18 of them assets with 36002 vertices in total, the cuDSS reset costs 2.74 ms per step on average, median 2.62 ms, with 42 MB of device memory for the factor; the dense alternative costs 25.9 ms and 962 MB. One query of such a scene costs tens of milliseconds and the reset runs once per 8 queries.
Open problem of the reset path. The three cluster jobs with the reset, T1_p, D1_p and D1_pc05, failed twice each on 4 GPUs with an NCCL collective timeout at the first training update. One rank hangs. The copied logs of one attempt each show it: ranks 0, 2 and 3 report a watchdog timeout of the all-reduce with sequence number 101 after 10800000 ms, and rank 1 reports its last enqueued work as 100. progress.json of each job stops at epoch 1 with 1 completed update. The cause is not yet known. Two local smoke runs with the reset switch finished today without a hang: a two-rank run of 6000 cells completed 2 epochs and 31 updates, and a 32000-cell run completed 1 epoch and 6 updates. So the hang is not reproduced locally yet. Until the cause is found the reset has no training outcome.
8. Fix 3 in reserve: Option B of ADR 0003
ADR 0003 chose one deformation gradient per tet from the embedded vertices; the spec and the plan page call this Option A. The ADR kept one alternative as the fallback, in its words "the hex deformation gradient evaluated at tet quadrature points"; the spec calls it Option B. For linear tets the quadrature point is the tet centroid and its weight is the tet volume. The ADR says of it: it keeps the energy a function of the cell modes and cannot leave corners without weight. So there is no null space in practice. It is not Newton VBD's tet potential, so the lattice-tet fidelity gate would lose its exact reference. Spec section 12 records that Option B stays the fallback behind the two switches.
9. The plan
- Runs now. The weight arms are done: T1_c05 3 of 16, T1_c20 14 of 16, D1_c05 16 of 16, D1_c20 16 of 16, K1_c05 16 of 16. The reset arms T1_p, D1_p and D1_pc05 are blocked by the hang at the first update.
- Measure next. The beam error of the weight arms against Newton VBD on asset meshes, which is the number that prices the stiffness bias. Renders of the asset arms. The two-rank reproduction of the hang, then the reset arms again.
- Decision rule. Pick the fix whose asset arms match the lattice arms' survival, 16 of 16, and do not lose beam accuracy against VBD. A combination is allowed; D1_pc05 is that arm. If neither fix passes, build Option B.
- Question for Anka. Weight, reset or Option B? Which bias do you accept: a stiffness of about w times the material on the filled part of each cell, a per-step projection that distorts the carrier of a bent sheet, or a hex energy that is not VBD's tet potential?