Skip to content

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:

solver.step(state_in, state_out, control, contacts, dt)

Install NovaPhy Run the quick start Browse demos

NovaPhy ViewerGL rendering the current viewer API showcase: cloth, mesh, capsules, primitives, lines, and Gaussian splats.

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.

    Robotics and IK →

  • Rigid and deformable contact

    Sequential Impulse, XPBD, VBD / AVBD, and optional IPC paths share the same Model / SimState / Control / Contacts contract.

    Choose a solver →

  • Fluid simulation

    PBF with Akinci two-way rigid coupling, a CUDA-only SPH path, and optional CUDA sparse-block LBM with immersed-boundary coupling.

    Fluid guide →

  • 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.

    Multi-environment guide →

  • 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.

    Viewer guide →

  • 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.

    Build matrix →

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.float32 at the Python boundary.
  • Ownership: Model is immutable after finalize(). 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(), and has_vbd_dlan() at runtime. For SolverLBM, construct the model-bound solver and inspect solver.has_cuda_backend.

Where to go next