Loader#
Scene files are YAML or JSON documents consumed by
load_sap_scene(). They describe simulation timing,
contact capacity, solver kwargs, builder defaults, external assets, optional
inline bodies, articulated joints, initial state edits, and deterministic
post-build edits.
The current schema version is 1.
Load Order#
The loader applies a scene in this order:
Read
simulation.num_worldsorreplicate.num_worldsand optional replication spacing.Apply supported
builderdefaults.Import external
assets.Add inline
bodiesand their shapes.Add inline
jointsandarticulations.Apply
initial_joint_q.Apply
post_buildoperations.Replicate the authored world.
Add the optional world
groundplane.
This order matters. For example, post_build edits run before replication,
so a joint-target edit is copied into every replicated world. The ground plane
is added after replication and remains a shared world shape.
Top-Level Sections#
schema_versionInteger schema marker. The current loader supports
1.nameHuman-readable scene identifier.
simulationRuntime settings used by
benchmark.py. Common keys aredt,num_worlds,max_rigid_contact, andsolver.builderScene builder defaults. The current loader consumes
rigid_gap,default_body_armature, anddefaults.joint/defaults.shapefrom this block. Other builder attributes can be changed withpost_build: [{op: set_attr, ...}]when supported.groundOptional world plane. It is enabled by default unless set to
falseorenabled: false.assetsExternal
usd,urdf, ormjcfassets. Sources may be local paths, inline URDF XML strings, or git-backed sparse checkouts.bodies,joints, andarticulationsInline procedural bodies and generalized-coordinate structure.
initial_joint_qDirect initial generalized position edits by index or joint reference.
post_buildDeterministic edits after assets and inline objects are added.
replicateOptional world replication settings.
simulation.num_worldsis also accepted and is what the current example scenes use.
Minimal Inline Scene#
This is the smallest useful shape of a procedural scene: one free box, one ground plane, and one solver configuration.
schema_version: 1
name: single_box
simulation:
dt: 0.003
num_worlds: 1
max_rigid_contact: 64
solver:
contact_preset_variant: approx32
line_search_variant: monotone_decay
builder:
rigid_gap: 0.002
defaults:
shape:
density: 1000.0
ke: 1000000.0
tau: 0.01
mu: 0.5
ground:
enabled: true
label: DEFAULT_GROUND
bodies:
- id: box
mass: 1.0
transform:
p: [0.0, 0.0, 1.0]
q: identity
shapes:
- type: box
hx: 0.5
hy: 0.5
hz: 0.5
joints:
- id: box_free
type: free
parent: world
child: box
articulations:
- id: box_articulation
joints: [box_free]
Run it with:
uv run python benchmark.py --scene path/to/single_box.yaml --frames 10
Simulation Block#
benchmark.py reads:
simulation:
dt: 0.003
num_worlds: 1
max_rigid_contact: 48
solver:
contact_preset_variant: drake
line_search_variant: armijo_decay
dtDefault timestep. The command-line
--dtflag overrides it.num_worldsNumber of independent replicated worlds. The command-line
--num-worldsflag overrides it.max_rigid_contactPer-world solver contact cap. With replicated worlds,
benchmark.pyallocates the flat collision contact buffer asmax_rigid_contact * num_worlds.solverKeyword arguments passed to
SolverSAP. See Solver for supported solver options.
Builder Defaults#
rigid_gapDefault shape
gapused when a shape does not provide one. This is a contact candidate generation band, not a force parameter.default_body_armatureDefault rigid-body armature for bodies created without an explicit per-body armature. The value is added to the diagonal of
body_inertia; it is body-space rotational inertia regularization, not joint DOF armature. Values that are reasonable for robot links can dominate tiny free objects, so keep this near zero when small bodies should spin down from contact using their physical inertia.defaults.shapeBase material and collision metadata for subsequently created shapes.
defaults.jointBase drive, limit, armature, effort, velocity, and friction metadata for subsequently created joint DOFs. Joint armature is stored in
joint_armatureand is separate fromdefault_body_armature.
Numeric values may be numbers or simple expressions containing pi and
+, -, *, or /:
angle: pi * 0.5
Transforms#
Transforms appear on assets, bodies, shapes, and joint frames.
identityIdentity transform.
pTranslation vector
[x, y, z].qQuaternion as
identity,[x, y, z, w], or anaxis_anglemapping.
Example:
xform:
p: [0.0, 0.0, 0.62]
q:
axis_angle:
axis: [0.0, 0.0, 1.0]
angle: pi * 0.5
Asset Sources#
A local source is a path string or mapping with path:
assets:
- id: robot
type: usd
source: assets/robots/robot.usda
Git sources fetch sparse checkouts into the SAP asset cache:
assets:
- id: g1
type: usd
source:
git:
repo: https://github.com/newton-physics/newton-assets.git
rev: 261cd1f429619d8ef4f546bd788ab9dea906b5e1
sparse:
- unitree_g1/usd_structured
- unitree_g1/meshes
path: unitree_g1/usd_structured/g1_29dof_with_hand_rev_1_0.usda
Git-backed assets are cached under SAP_WARP_ASSET_CACHE when set, otherwise
under ~/.cache/sap_warp/assets. SAP_WARP_ASSET_OFFLINE=1 disables
fetching and requires the cache entry to already exist.
Imported Asset Options#
Common options accepted by imported assets include:
xformRoot transform with
pandqfields.cfg,shape, orphysicsShape/material overrides applied while importing the asset.
cfgmay contain any shape config field;shapeandphysicsare normalized for common material aliases such aske,tau,mu,margin, andgap.enable_self_collisionsWhether shapes from the same imported asset may collide with one another.
hide_collision_shapesandforce_show_collidersVisibility flags stored in shape flags. They do not disable collision.
skip_mesh_approximationFor USD import, keep mesh shapes as meshes when possible instead of applying mesh approximation.
ignore_pathsUSD path patterns to skip.
floatingURDF/MJCF option that creates a floating base when supported by that importer path.
load_visual_shapesandparse_visuals_as_collidersURDF/MJCF options controlling whether visual geometry is imported and whether it participates in collision.
The current imported-asset path assumes z-up assets. Unsupported asset features
are collected in SapLoadedScene.unsupported_features; with strict=True
they raise SapUnsupportedSceneFeature.
Inline Bodies and Shapes#
Inline body fields:
idStable identifier used by inline joints and post-build body references.
labelOptional runtime body label. Defaults to
id.transformInitial body pose.
massInitial body mass. Shape density can add mass and inertia unless inertia is locked by an imported asset path.
shape/physicsfields or direct material aliasesBody-level defaults for child shapes.
Inline shapes support box, sphere, capsule, cylinder, cone,
ellipsoid, and mesh:
Shape |
Size fields |
|---|---|
|
|
|
|
|
|
|
|
|
Mesh geometry fields accepted by the loader. |
Shape-level cfg overrides body and builder defaults:
bodies:
- id: payload
transform:
p: [0.0, 0.0, 1.0]
q: identity
shapes:
- type: box
hx: 0.2
hy: 0.2
hz: 0.2
cfg:
density: 500.0
mu: 0.8
gap: 0.004
Inline Joints and Articulations#
Inline joints support fixed, free, revolute, and prismatic.
parentandchildBody ids or
worldfor the parent.parent_xformandchild_xformJoint frames expressed in parent and child body frames.
axisRevolute or prismatic axis. Use
[x, y, z],x/y/z-style tokens accepted by the loader, or the integer axis tokens used internally.collision_filter_parentDefaults to
true. When enabled, directly connected parent and child shapes are filtered from collision.articulationsLists joint ids in each articulation. The solver requires at least one joint DOF; a scene with only fixed joints is not enough for
SolverSAP.
Example:
joints:
- id: hinge
type: revolute
parent: world
child: link
axis: [0.0, 0.0, 1.0]
limit_lower: -1.57
limit_upper: 1.57
articulations:
- id: hinge_articulation
joints: [hinge]
Initial Joint Positions#
initial_joint_q edits entries in the generalized position array before the
initial forward-kinematics pass:
initial_joint_q:
- joint: revolute_a_b
value: pi * 0.5
- joint: box_free
offset: 2
value: 1.25
Use joint or joint_label to select a joint, plus an optional offset
inside that joint’s q block. Use index to edit an absolute
joint_q index.
Shape Defaults#
Shape defaults and per-shape cfg blocks use the same fields. Common fields
are:
densityDensity used when the loader computes body mass and inertia from shapes.
keandtauContact stiffness and explicitly specified contact dissipation time scale.
tauis read as material data; it is not computed fromkeor a damping coefficient.muCoulomb friction coefficient.
marginandgapmarginoffsets the effective contact surface and enters the signed gap used by the SAP solve.gapis an anticipation band for generating potential contact candidates before penetration. A pair can be emitted when its margin-adjusted separation satisfies\[d_{ab} - (m_a + m_b) \le g_a + g_b.\]Contacts with small positive \(\phi_0\) are then passed to the solver, which decides the force/impulse through the SAP objective. Increasing
gapcan improve contact stability, but it can also increase contact count, so keepsimulation.max_rigid_contactsized accordingly.gapdefaults to the builderrigid_gap.collision_groupBroad-phase group mask. Group
0does not collide. Positive groups collide with matching positive groups and with negative groups according to the loader’s group-pair test.has_shape_collisionandhas_particle_collisionCollision participation flags.
is_hydroelasticMarks a shape for hydroelastic contact support, which is in development.
is_visibleandis_siteVisibility and site flags. Sites are non-colliding, zero-density shapes.
Useful aliases are normalized before application:
Canonical field |
Accepted aliases |
|---|---|
|
|
|
|
|
|
|
|
|
|
Joint Defaults#
Joint defaults and joint definitions can set:
axisLocal joint axis.
target_pos,target_vel,target_ke,target_kdJoint drive target and gains.
limit_lower,limit_upper,limit_ke,limit_kdJoint limit bounds and stiffness/damping.
armature,effort_limit,velocity_limit,frictionPer-DOF solver parameters.
armatureadds diagonal generalized inertia on the joint DOF throughjoint_armature; it does not modifybody_inertia. Usebuilder.default_body_armaturefor rigid-body inertia regularization.actuator_modeInteger actuator mode stored on the runtime joint DOF.
Post-Build Operations#
Supported post_build operations include:
add_shapeAdd another inline shape to a body or the world.
set_arrayEdit a supported builder array with a scalar
value, explicitvalues, or values copiedfrom_array.copy_arrayCopy entries between supported builder arrays.
scale_sphere_shapesMultiply every sphere radius by
factor.approximate_meshesReplace colliding mesh shapes with oriented bounding boxes when
method: bounding_boxis selected.set_attrSet a supported builder attribute, for example
gravity.set_joint_targetsSet target mode and gains by index range, exact joint label, label prefix, or substring match.
set_joint_qSet one generalized position entry.
copy_joint_q_to_joint_targetsSeed joint position targets from current
joint_q.set_joint_armatureAssign armature values over a selected range.
Supported array names for set_array and copy_array are:
shape_margin, shape_gap, shape_material_ke, shape_material_tau,
shape_material_mu, shape_material_restitution,
shape_material_mu_torsional, shape_material_mu_rolling,
shape_material_kh, shape_collision_group, shape_flags,
joint_q, joint_qd, joint_target_pos, joint_target_vel,
joint_target_ke, joint_target_kd, joint_target_mode, joint_armature
Selectors and Ranges#
Array operations accept:
indexSingle index. Negative indices are relative to the end.
range: allEvery entry.
range: head:NFirst
Nentries.range: tail:NLast
Nentries.range: from:NEntries from
Nto the end.range: [start, end]Half-open range.
end: nullmeans the end of the array.
Joint-selector operations accept:
jointorjoint_labelExact joint label, or a suffix match after
/when the imported asset prefixes labels.jointsorjoint_labelsList of joint references.
label_prefixEvery joint label that starts with the prefix.
matchEvery joint label containing the substring.
Examples:
post_build:
- op: set_array
array: joint_q
range: [0, 3]
values: [0.0, 0.0, 0.67]
- op: set_joint_targets
range: from:6
mode: position
ke: 500.0
kd: 10.0
- op: set_joint_targets
label_prefix: robot/left_leg
mode: position
ke: 1000.0
kd: 20.0
Replication#
Replicated worlds are independent copies of authored bodies, shapes, joints,
articulations, controls, and labels. The loader offsets each world by
replicate.spacing. With two non-zero spacing axes, worlds are arranged in a
centered grid; with one non-zero axis, they are arranged in a line.
simulation:
num_worlds: 16
replicate:
spacing: [2.0, 2.0, 0.0]
load_sap_scene(..., num_worlds=...) and the benchmark --num-worlds flag
override the scene value.
Existing Examples#
assets/yaml/unitree_g1_usd.yamlG1 USD scene, Drake preset, Armijo line search, high default world count for throughput, and mesh-to-box post-build approximation.
assets/yaml/unitree_h1_usd.yamlH1 USD scene, Drake preset, Armijo line search, and full-position joint targets.
assets/yaml/anymal_c_urdf.yamlANYmal C URDF scene with a floating base, sphere scaling, joint target setup, and per-DOF armature edits.
assets/yaml/anymal_d_usd.yamlANYmal D USD scene with explicit initial base
joint_qedits and target setup.assets/yaml/multi_joints.yamlSmall inline scene covering fixed, revolute, prismatic, and free joints.
Runtime Data#
SAP Warp separates authoring-time scene data from runtime arrays. Scene files describe assets, inline bodies, defaults, solver options, and post-build edits. The loader turns that description into compact Warp arrays consumed by the collision pipeline and solver. The runtime model, state, control, and contact data structures produced by the loader are based on Newton’s runtime code. SAP Warp adapts those structures so it can wrap Newton-owned Warp arrays directly where possible, preserve Newton-style ordering metadata at the public boundary, and keep imported USD/URDF/MJCF scenes close to Newton behavior.
Load Result#
load_sap_scene() returns
SapLoadedScene with:
collision_modelShape metadata for broad phase, narrow phase, and contact writing.
collision_stateInitial collision body poses.
sap_modelArticulated solver model.
sap_stateInitial generalized and body state.
sap_controlInitial joint forces, targets, and actuator arrays.
body_labelsandshape_labelsStable labels useful for scene edits, diagnostics, and debugging.
State and Control#
state() clones the model’s initial state into a
mutable SapState.
control() clones control arrays into
SapControl. The benchmark uses two states and swaps
them after every step:
state_0 = loaded.sap_state
state_1 = loaded.sap_model.state()
control = loaded.sap_control
solver.step(state_0, state_1, control, contacts, dt)
state_0, state_1 = state_1, state_0
clear_forces() clears external body forces.
clear() clears direct joint forces and target
arrays.
Ordering Conventions#
The public runtime state uses the model-facing order, while the SAP kernels use an angular-first, body-origin order for free and distance joints. The full reference-point shifts, formulas, and order flags are documented in Convention.
World Replication#
Scene files may replicate one authored world into many independent worlds with
simulation.num_worlds or a replicate block. Replication duplicates
bodies, shapes, joints, controls, and labels while preserving shared global
objects such as the ground plane when applicable.
The G1 benchmark scene uses many worlds by default for throughput measurement.
Override --num-worlds during smoke tests.
Asset Resolution#
Asset sources may be local paths or git-backed sparse checkouts. Git assets are
cached under SAP_WARP_ASSET_CACHE or ~/.cache/sap_warp/assets. Set
SAP_WARP_ASSET_OFFLINE=1 only after the needed assets exist in the cache.
Unsupported Features#
The loader records recognized-but-unsupported scene features in
unsupported_features on SapLoadedScene. With
strict=True, unsupported features raise
SapUnsupportedSceneFeature instead of silently
continuing.