Gothic arch (mujoco.org)
Many contacts
A mortarless pointed arch of seventeen loose stones stands on friction alone.
steel preset (the default), recordedon an NVIDIA RTX PRO 6000 Blackwell.Run it
cd examples# the default preset (steel), then the othersuv run shelf play mjorg_gothic_arch# change solver settings liveuv run shelf tune mjorg_gothic_archSource: examples/shelf/02_many_contacts/mjorg_gothic_arch/ · ported from google-deepmind/mujoco model/arch/gothic.xml (MuJoCo 4.0 tour on mujoco.org)
Presets
| Preset | Solver | Status | Checked |
|---|---|---|---|
steeldefault | steel | works | 2026-11-10, newton f6fc82a8b, NVIDIA RTX PRO 6000 Blackwell Server Edition |
Solvers with no row are untested on this example. The full settings are inpresets.toml.
Code
# SPDX-FileCopyrightText: Copyright (c) 2026 Skild AI and the Steel contributors# SPDX-License-Identifier: Apache-2.0
############################################################################ Example mujoco.org gothic arch## A dry-stone pointed arch: sixteen voussoirs and a keystone between two fixed springers,# standing on friction alone with no mortar.## Replicates the "Dry-Stone Gothic Arch" scene of the MuJoCo 4.0 tour on mujoco.org# from its MJCF (scene.xml, flattened from google-deepmind/mujoco model/arch/gothic.xml).# See README.md for the gaps.## Command: python -m shelf run mjorg_gothic_arch############################################################################
from pathlib import Path
import numpy as np
import newtonimport newton.examplesfrom common.steel_scenes import SceneExample
# How far the keystone may sink and any stone may move before the arch counts as fallen [m]; a few# stones can slip several mm in the first seconds before the arch locks.KEYSTONE_DROP_MAX = 0.01STONE_SHIFT_MAX = 0.025# How far any stone may move over the last 5 s of the check once the arch has settled [m].SETTLED_SHIFT_MAX = 0.005SETTLED_FRAME = 300
class Example(SceneExample): file = __file__ # MuJoCo's free camera for the scene: azimuth 140, elevation -20 around the statistic centre camera = {"eye": (2.63, -2.21, 2.26), "target": (0.0, 0.0, 1.01)}
def build(self): builder = newton.ModelBuilder() self.preset.apply_model(builder) builder.add_mjcf(str(Path(__file__).with_name("scene.xml"))) self.keystone = builder.body_label.index("Gothic arch/worldbody/keystone") self.start = np.array([list(builder.body_q[b])[:3] for b in range(builder.body_count)]) self.frame = 0 return builder, {}
def test_post_step(self): self.frame += 1 if self.frame == SETTLED_FRAME: self.settled = self.state_0.body_q.numpy()[:, :3].copy()
def check(self): """The arch stands: the keystone holds its height, no stone slides out, and the settled arch stays put.""" self.solver.check_constraint_capacity() # dropped contact rows invalidate the run q = self.state_0.body_q.numpy() drop = self.start[self.keystone, 2] - q[self.keystone, 2] shift = np.linalg.norm(q[:, :3] - self.start, axis=1).max() creep = np.linalg.norm(q[:, :3] - self.settled, axis=1).max() assert drop < KEYSTONE_DROP_MAX, f"the keystone sank {drop * 1000:.1f} mm" assert shift < STONE_SHIFT_MAX, f"a stone moved {shift * 1000:.1f} mm" assert creep < SETTLED_SHIFT_MAX, f"a stone moved {creep * 1000:.1f} mm after the arch settled"
if __name__ == "__main__": parser = Example.create_parser() viewer, args = newton.examples.init(parser) newton.examples.run(Example(viewer, args), args)example = "mjorg_gothic_arch"source = "google-deepmind/mujoco model/arch/gothic.xml (MuJoCo 4.0 tour on mujoco.org)"license = "Apache-2.0"default = "steel"
[check]# Arguments `python -m shelf check` passes in test mode.args = ["--num-frames", "600"]
[gallery]short = "A mortarless pointed arch of seventeen loose stones stands on friction alone."title = "Gothic arch (mujoco.org)"frame = 60
[xpbd]status = "untested"
[vbd]status = "untested"
[mujoco]status = "untested"
[featherstone]status = "untested"
[semi_implicit]status = "untested"
[feather_pgs]status = "untested"
[steel]status = "works"# 32 substeps x 20 iterations. No Steel setting holds this friction-held arch still: over 40 s runs every setting# creeps, the converged ones (32 x 48-96, 48 x 32, 64 x 48) included: the keystone sinks 0.6-1.2 mm and stones move# 2-3 mm per 20 s (median of 2-4 runs), and one 32 x 96 run slipped nine stones ~10 cm before locking again. 32 x 20# creeps at that rate (8 runs: keystone median 1.0 mm, range 0.3-2.1 mm, stones median 2.4 mm, range 1.3-5.0 mm per# 20 s; keystone 0.3-3.9 mm below its start at 40 s) and does not buzz (last-second stone motion 0.01-0.03 mm per# frame, median). 12 iterations creep faster (5 runs: keystone median 1.6 mm, stones 4.6 mm per 20 s) and buzz# (0.06 mm per frame: 2-4 stones read as jittering); 16 iterations reach the band in 5 of 6 runs. 16 substeps can fail late (one 16 x 96 run collapsed at# 20-30 s; 16 x 48 sinks 14 mm in 20 s); warm start, velocity iterations and serial Nesterov all creep more (5-40 mm# per 20 s), Nesterov below 32 substeps collapses. A 2 cm gap keeps distant stone pairs out of the rows; at most# 520 rows are used (1,024 reserved). Peak penetration 0.3 mm. 51.0 ms/frame on the RTX PRO 6000 (12 iterations:# 40.5 ms). MuJoCo 3.17 on the same file: keystone 1 mm, no stone > 2 mm in 10 s (README).sim = { fps = 60, substeps = 32 }model = { rigid_gap = 0.02 }solver = { solve_mode = "colored", iterations = 20, articulated_contact_response = "propagation", free_body_constraint_max = 1024, propagation_constraint_max = 1024 }checked = "2026-11-10, newton f6fc82a8b, NVIDIA RTX PRO 6000 Blackwell Server Edition"
[kamino]status = "untested"
[implicit_mpm]status = "untested"
[style3d]status = "untested"README
Steel replica of “Dry-Stone Gothic Arch” from the MuJoCo 4.0 tour on mujoco.org. Sixteen voussoirs and a keystone sit between two fixed springers with no mortar and stand on friction alone.
- Source:
google-deepmind/mujocomodel/arch/gothic.xml, Apache-2.0 (text inLICENSE).scene.xmlis that file compiled with MuJoCo 3.17 and re-emitted flat (itsreplicateblocks expanded, the 4.0 discrete integrator replaced byimplicitfast). - Check: after 10 s the keystone has sunk less than 10 mm, no stone has moved more than 25 mm, and no stone has moved more than 5 mm over the last 5 s. MuJoCo 3.17 on the same file sinks the keystone 1 mm and moves no stone more than 2 mm. The total bounds are loose on purpose: the stones stick and slip in the first seconds (below), and bounds of 5 mm and 10 mm failed about one run in eight although the arch then held.
Known gaps:
- Steel needs 0.5 ms steps (32 substeps at 60 fps) where MuJoCo takes 8 ms with an elliptic cone,
impratio10 and two noslip iterations; Steel has no noslip pass. - The stones stick and slip: in some runs the lowest voussoirs slip outward by up to about 20 mm in the first seconds, then the arch locks. Over 12 runs on an RTX PRO 6000 the keystone sank 0.6-5.7 mm in 10 s and at most 4.6 mm in 20 s.
- The arch never comes fully to rest in Steel: over 40 s every setting tried (up to 32 x 96 and 64 x 48 substeps x iterations) creeps, the keystone sinking about 1 mm and the stones moving 2-3 mm per 20 s; the preset’s 32 x 20 creeps at that rate.
- Rendering uses the shelf viewer, not MuJoCo’s Filament renderer.
Steel docs and tutorials are licensed under Apache-2.0. Third-party notices. "Skild", "Skild AI" and the Skild logo are trademarks of Skild AI and are not licensed under Apache-2.0.