Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Cluster-builder validation

San Diego State University

The build_cluster convenience layer is a thin, differentiable wrapper over the composable build_spatial_ic core: it resolves the mass spec, auto-pairs the scale-matched equilibrium velocity DF (matched_velocity_df), and applies optional tidal / rotation modifiers. Because the base case is proven bit-identical to the manual composition (exact == in tests/unit/builders/test_cluster_builders.py), every equilibrium guarantee is inherited from the already-validated profiles and DFs — this page verifies only the new surface: that each alias builds a near-virial IC, that the modifiers are physically correct, and that the differentiable knobs flow.

Figures: scripts/validate_cluster_builders.py (prints expected-vs-measured for every check and exits non-zero on any failure). End-to-end equilibrium across all five profiles: tests/integration/test_cluster_builders_integration.py. Gradient correctness: the differentiability audit (Differentiability gradient audit) registers eight measured AD-vs-FD cases for the builders plus a tidal straight-through teeth test.

What is verified

Each row is a PASS/FAIL check in scripts/validate_cluster_builders.py. The Tolerance is the bound the script enforces (sys.exit(1) on any failure); the Measured value is regenerated on every run.

Property

Tolerance (as enforced)

Measured

Anchor

Virial Q=T/VQ=T/|V|, all 5 aliases (N=5000N=5000)

Q0.5<0.03|Q-0.5|<0.03

0.5000 (Plummer/King/EFF/Michie/LIMEPY)

default Q=0.5Q=0.5 virial scale

Density recovery, sampled vs shell-weighted analytic

max rel <0.20<0.20 (bins 50\ge50 counts)

Plummer 0.072 / King 0.135 / EFF 0.128

inherited inverse-CDF sampling

Tidal cut: ghost mass beyond rtr_t

exact 0

0.000 (survivor fraction 0.71)

apply_tidal_truncation (hard cut)

Rotation Lz(ω)L_z(\omega) slope vs ΣmR2\Sigma m R^2

rel err <109<10^{-9}

4.7×10164.7\times10^{-16} (machine-exact)

solid-body overlay

Differential vϕ(R)v_\phi(R) overlay

max rel <0.15<0.15

0.057

peaked rotation curve

Anisotropy β(r)\beta(r) vs r2/(r2+ra2)r^2/(r^2+r_a^2)

max dev <0.05<0.05

0.0187

Osipkov-Merritt matched DF

Density recovery across the families

Sampled radial number-density (points) vs the shell-volume-weighted analytic density
(lines) for Plummer, King, and EFF built through the named aliases, with a residual
panel below. The convenience builder reuses each profile’s own inverse-CDF sampler, so the
shape is recovered to a few percent per well-populated bin — the residual at the steep
King edge is honest Poisson noise in the outermost bin, not a sampler bias.

Figure 1:Sampled radial number-density (points) vs the shell-volume-weighted analytic density (lines) for Plummer, King, and EFF built through the named aliases, with a residual panel below. The convenience builder reuses each profile’s own inverse-CDF sampler, so the shape is recovered to a few percent per well-populated bin — the residual at the steep King edge is honest Poisson noise in the outermost bin, not a sampler bias.

Tidal truncation (Plummer-only)

Radial mass profile of a Plummer build with vs without tidal_radius. The cut at
r_t is exact (the straight-through apply_tidal_truncation forward pass is a hard
Heaviside), and the truncated stars become zero-mass ghosts beyond r_t (fixed shape
N, so the build stays jit/grad-safe). Survivors keep velocities drawn for the
untruncated potential — the set is super-virial (audit S4); use revirialize=True, or a
native-r_t King/LIMEPY model, for a stationary truncated equilibrium.

Figure 2:Radial mass profile of a Plummer build with vs without tidal_radius. The cut at rtr_t is exact (the straight-through apply_tidal_truncation forward pass is a hard Heaviside), and the truncated stars become zero-mass ghosts beyond rtr_t (fixed shape NN, so the build stays jit/grad-safe). Survivors keep velocities drawn for the untruncated potential — the set is super-virial (audit S4); use revirialize=True, or a native-rtr_t King/LIMEPY model, for a stationary truncated equilibrium.

Rotation: solid-body and differential

Left: solid-body angular momentum L_z(\omega) is exactly linear with slope
\Sigma m R^2 (measured to machine precision, rel err 4.7\times10^{-16}, since the
overlay leaves positions unchanged). Right: a differential RotationSpec produces a
peaked v_\phi(R) curve recovered to <6\% in the inner region. Rotation injects net
L_z (audit S3, deliberately non-stationary).

Figure 3:Left: solid-body angular momentum Lz(ω)L_z(\omega) is exactly linear with slope ΣmR2\Sigma m R^2 (measured to machine precision, rel err 4.7×10164.7\times10^{-16}, since the overlay leaves positions unchanged). Right: a differential RotationSpec produces a peaked vϕ(R)v_\phi(R) curve recovered to <6%<6\% in the inner region. Rotation injects net LzL_z (audit S3, deliberately non-stationary).

Osipkov-Merritt anisotropy

Measured anisotropy \beta(r)=1-\sigma_t^2/(2\sigma_r^2) for a Plummer build with
anisotropy_radius set, against the analytic Osipkov-Merritt profile
\beta(r)=r^2/(r^2+r_a^2) (max deviation 0.019). anisotropy_radius is threaded into the
matched DF and is valid only for Plummer/EFF; King is isotropic and Michie/LIMEPY carry
their anisotropy intrinsically (set r_a on the profile).

Figure 4:Measured anisotropy β(r)=1σt2/(2σr2)\beta(r)=1-\sigma_t^2/(2\sigma_r^2) for a Plummer build with anisotropy_radius set, against the analytic Osipkov-Merritt profile β(r)=r2/(r2+ra2)\beta(r)=r^2/(r^2+r_a^2) (max deviation 0.019). anisotropy_radius is threaded into the matched DF and is valid only for Plummer/EFF; King is isotropic and Michie/LIMEPY carry their anisotropy intrinsically (set rar_a on the profile).

Differentiability

Every differentiable knob of the builder is registered in the release gradient-gate (Differentiability gradient audit) with a measured AD-vs-FD tolerance: build_cluster in r_h (machine-exact), anisotropy_radius, and rotation ω\omega; the per-family alias paths (King r_c, EFF γ\gamma, Michie/LIMEPY W0); and build_cluster_from_params in r_h through the ClusterParams θ-PyTree. The tidal_radius channel is a straight-through surrogate (live but not finite-difference-consistent by design) and is covered by a dedicated live-gradient teeth test rather than a false FD-consistent case.