NovaPhy¶
A C++20 and Python physics engine for embodied intelligence.
NovaPhy gives robotics, reinforcement-learning, and sim-to-real projects one
immutable Model, caller-owned runtime buffers, and a family of interchangeable
solver backends. The public stepping contract mirrors Newton:
Install NovaPhy Run the quick start Browse demos

Rendered from the current demo_viewer_api_basic.py scene with NovaPhy's
headless ViewerGL backend.
Built for simulation workflows¶
-
Robots and articulated systems
Native MuJoCo-style and Featherstone dynamics, URDF / MJCF / USD scene import, joint drives, sensors, actuators, and a composable high-level IK package.
-
Rigid and deformable contact
Sequential Impulse, XPBD, VBD / AVBD, and optional IPC paths share the same
Model/SimState/Control/Contactscontract. -
Fluid simulation
PBF with Akinci two-way rigid coupling, a CUDA-only SPH path, and optional CUDA sparse-block LBM with immersed-boundary coupling.
-
Multi-environment rollouts
Replicate one source scene into independent world slices and drive them through
MultiEnvRunner, including per-environment actions, resets, done flags, and episode lengths. -
One viewer lifecycle
Use interactive ModernGL, no-op headless, file recording, USD, Rerun, or Viser backends through
novaphy.viewer. Interactive rigid and VBD picking uses the same state/control loop. -
CPU and optional accelerators
CPU is the default development path. CUDA is available selectively for MuJoCo-style dynamics, Featherstone, VBD, collision, SPH, and LBM; IPC and Denglin DLAN have explicit build paths and runtime feature checks.
The complete simulation loop¶
This example creates a falling box, performs collision detection explicitly, and swaps two state buffers. It is the same ownership pattern used by checkpointed rollouts and parallel evaluation.
import numpy as np
import novaphy
builder = novaphy.ModelBuilder()
builder.add_ground_plane(y=0.0)
body = builder.add_body(
novaphy.Transform.from_translation(
np.array([0.0, 5.0, 0.0], dtype=np.float32)
)
)
builder.add_shape_box(
body,
xform=novaphy.Transform.identity(),
hx=0.5,
hy=0.5,
hz=0.5,
cfg=novaphy.ShapeConfig(density=1.0, mu=0.5),
)
model = builder.finalize()
solver = novaphy.solvers.SolverSemiImplicit(model)
state_in, state_out = model.state(), model.state()
control = model.control()
pipeline = novaphy.CollisionPipeline(model)
contacts = pipeline.contacts()
for _ in range(600):
state_in.clear_forces()
pipeline.collide(state_in, contacts)
solver.step(state_in, state_out, control, contacts, 1.0 / 120.0)
state_in, state_out = state_out, state_in
state_in and state_out may alias for in-place stepping. Use distinct
buffers when the previous state must remain available. Collision is also
explicit for most solvers: skipping pipeline.collide(...) means those
solvers receive no rigid contact constraints for that step. SolverIPC is
the exception because libuipc performs collision internally and ignores the
NovaPhy Contacts argument.
Architecture at a glance¶
flowchart LR
B[ModelBuilder<br/>mutable] --> M[Model<br/>immutable]
M --> S0[SimState in]
M --> S1[SimState out]
M --> C[Control]
M --> P[CollisionPipeline]
P --> K[Contacts]
M --> V[Solver]
S0 --> P
S0 --> V
S1 --> V
C --> V
K --> V
V --> O[step result]
The driver owns collision, profiling, viewer updates, and CUDA graph capture.
There is no World orchestrator hidden between the model and solver.
Solver capability matrix¶
| Solver | Best fit | Default path | Optional path | Availability |
|---|---|---|---|---|
SolverSemiImplicit |
Free rigid bodies and contact | CPU | — | Runnable |
SolverFeatherstone |
Articulated robots | CPU | CUDA | Runnable |
SolverXPBD |
Maximal-coordinate rigid constraints | CPU | — | Runnable |
SolverMuJoCo |
MuJoCo-style articulated dynamics and robot batches | CPU | CUDA | Runnable |
SolverVBD |
Rigid / soft VBD and AVBD scenes | CPU | CUDA or DLAN | Runnable |
SolverPBF |
Position Based Fluids and rigid coupling | CPU | — | Runnable |
SolverSPH |
Smoothed Particle Hydrodynamics | — | CUDA | Optional build |
SolverLBM |
Sparse-block Lattice Boltzmann fluids | — | CUDA | Optional build |
SolverIPC |
Penetration-free IPC contact | — | NVIDIA CUDA or CoreX | Optional build |
SolverMPM |
Material Point Method API parity | — | — | Scaffold; step() raises |
Gate optional backends with their documented capability surface. Most expose a
top-level feature check; SolverLBM is constructed first and reports
has_cuda_backend on the instance. See
Build from Source for exact flags and
Architecture for solver selection details.
Current-main showcase¶
| Workflow | What it demonstrates | Run it |
|---|---|---|
| MuJoCo-style robot grid | Native CPU dynamics, optional CUDA batching, contacts, drives | python python/demos/mujoco/demo_robot_basic.py --backend cpu --world-count 16 --headless 120 |
| Multi-environment foundation | Replication, independent world state, batched actions | python python/demos/demo_multi_env_physics.py --num-worlds 8 --steps 240 |
| Unified viewer | Shapes, meshes, lines, Gaussian splats, headless lifecycle | python python/demos/demo_viewer_api_basic.py --headless 120 --test |
| High-level IK | Objectives, LM / L-BFGS, analytic / finite-difference Jacobians | python python/demos/demo_ik_arm.py --headless --target-pos 0.5 0.0 0.4 |
| Moon gravity validation | Analytical drop-time comparison at lunar gravity | python python/demos/demo_moon_surface_drop_time.py --headless |
| PBF and SPH fluids | PBF on CPU; SPH demo requires the SPH CUDA build | python python/demos/demo_dam_break.py |
| Sparse-block LBM | ViewerGL volume rendering; ViewerNull lifecycle smoke without CUDA | python python/demos/demo_lbm_volume.py --viewer null --num-frames 2 |
The Demos page lists backend requirements, headless commands, and the rest of the current example tree.
Integration guarantees¶
- Units: SI units unless an API explicitly says otherwise.
- Precision: engine numerics are float32; use
np.float32at the Python boundary. - Ownership:
Modelis immutable afterfinalize(). Allocate independent state/control/contact buffers for independent rollouts. - Spatial layout: body, free-joint, and internal spatial vectors all use
[linear; angular]; wrench buffers use[force; torque]. - Optional features: query
has_ipc(),has_mujoco_cuda(),has_featherstone_cuda(),has_sph_cuda(),has_vbd_cuda(), andhas_vbd_dlan()at runtime. ForSolverLBM, construct the model-bound solver and inspectsolver.has_cuda_backend.
Where to go next¶
- Installation — choose a CPU, CUDA, IPC, or platform-specific build.
- Quick Start — build and inspect your first scene.
- Architecture — understand buffers, collision, and solver ownership.
- Python API — browse the public surface by topic.
- Migration from
world.step— update a pre-0.4 codebase.