Skip to content

Quick Start

This walkthrough goes from a source checkout to a complete collision-and-step loop, then points to the right workflow for robots, fluids, multi-environment rollouts, and visualization.

1. Install a development build

NovaPhy is currently installed from an authorized source checkout; there is no public PyPI package. A CPU build is the shortest path and GPU backends are opt-in. See Contributing if you still need repository access.

conda env create -f environment.yml
conda activate novaphy

export CMAKE_TOOLCHAIN_FILE="/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake"
# Windows PowerShell:
# $env:CMAKE_TOOLCHAIN_FILE="C:/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake"

pip install -e .

For the interactive ModernGL viewer and recording backends:

pip install -e ".[viewer]"

See Installation for editable/wheel behavior and Build from Source for CUDA, IPC, CoreX, DLAN, and OpenHarmony options.

2. Verify the package

import novaphy

print("NovaPhy", novaphy.version())       # 0.4.0
print("IPC:", novaphy.has_ipc())
print("MuJoCo CUDA:", novaphy.has_mujoco_cuda())
print("Featherstone CUDA:", novaphy.has_featherstone_cuda())
print("SPH CUDA:", novaphy.has_sph_cuda())
print("VBD CUDA:", novaphy.has_vbd_cuda())
print("VBD DLAN:", novaphy.has_vbd_dlan())

Feature checks report what was compiled into the imported extension; they do not merely report whether a driver happens to be installed.

3. Build a scene

ModelBuilder is mutable. finalize() bakes an immutable Model that can be shared by several solvers or independent rollout buffers.

import numpy as np
import novaphy

builder = novaphy.ModelBuilder()
builder.add_ground_plane(y=0.0)

box = builder.add_body(
    novaphy.Transform.from_translation(
        np.array([0.0, 5.0, 0.0], dtype=np.float32)
    )
)
builder.add_shape_box(
    box,
    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()

NovaPhy uses SI units and float32 engine data. Supplying np.float32 arrays avoids unnecessary boundary conversions.

4. Allocate runtime objects

The caller owns state, control, and contact buffers. Allocate contacts from a pipeline so its capacity matches the model and selected broadphase.

solver = novaphy.solvers.SolverSemiImplicit(model)
state_in = model.state()
state_out = model.state()
control = model.control()

pipeline = novaphy.CollisionPipeline(model)
contacts = pipeline.contacts()

Solver classes live under novaphy.solvers; there are no top-level novaphy.SolverXXX aliases and no World orchestrator.

5. Collide, step, and swap

dt = 1.0 / 120.0

for step in range(600):
    # Clear forces that should not persist into the next step.
    state_in.clear_forces()

    # Broadphase + narrowphase populate caller-owned contact buffers.
    pipeline.collide(state_in, contacts)

    # Newton-aligned contract:
    # solver.step(state_in, state_out, control, contacts, dt)
    solver.step(state_in, state_out, control, contacts, dt)
    state_in, state_out = state_out, state_in

    if step % 120 == 0:
        position = state_in.transforms[box].position
        print(f"t={step * dt:.1f}s, y={position[1]:.3f}")

state_in and state_out may be the same object for in-place stepping. Two buffers are preferable when checkpointing, comparing rollouts, or preserving the input for an observation pipeline.

Collision is explicit for most solvers

Call pipeline.collide(state_in, contacts) before a rigid contact solve with Semi-Implicit, Featherstone, XPBD, MuJoCo, or VBD. If you omit it, those solvers advance without NovaPhy contact constraints for that step. Passing contacts=None is appropriate only when the workflow intentionally has no contacts. SolverIPC is the exception: libuipc performs collision internally and ignores this argument.

6. Run a current demo

All commands below are relative to the repository root.

python python/demos/demo_pyramid_ball.py \
  --solver semi_implicit --headless 120
python python/demos/demo_pyramid_ball.py \
  --solver featherstone --headless 120
python python/demos/xpbd/demo_xpbd_stack.py --headless 120
# Native MuJoCo-style CPU robot grid
python python/demos/mujoco/demo_robot_basic.py \
  --backend cpu --world-count 16 --headless 120

# High-level inverse kinematics
python python/demos/demo_ik_arm.py \
  --headless --target-pos 0.5 0.0 0.4

# URDF → simulation → USD export pipeline
python python/demos/demo_robot_sim_pipeline.py --headless
python python/demos/demo_dam_break.py --headless
python python/demos/demo_fluid_coupling.py --headless

# Requires NOVAPHY_WITH_SPH_CUDA=ON.
python python/demos/demo_sph_fluid.py --headless --steps 100

# Requires the VBD CUDA build
python python/demos/vbd/demo_vbd_soft.py \
  --scene cloth_hanging --backend cuda \
  --viewer null --num-frames 120
python python/demos/demo_multi_env_physics.py \
  --num-worlds 8 --steps 240

python python/demos/demo_viewer_api_basic.py \
  --headless 120 --test

See the Demos catalog for backend requirements and more scenes.

Choose the next API

Goal Start with Guide
Free rigid bodies SolverSemiImplicit or SolverXPBD Rigid bodies
Articulated robots SolverFeatherstone or SolverMuJoCo Articulated bodies, MuJoCo-style solver
Rigid / soft VBD SolverVBD VBD / AVBD
Penetration-free GPU contact SolverIPC after has_ipc() IPC
Position Based Fluids SolverPBF, optionally chained to a rigid solver Fluids
SPH fluids SolverSPH after has_sph_cuda() Fluids
Sparse-block LBM fluids SolverLBM after checking solver.has_cuda_backend Fluids
Batched independent worlds ModelBuilder.replicate + MultiEnvRunner Multi-environment
IK objectives and optimizers novaphy.ik.IKSolver Robotics and IK
Interactive or recorded output novaphy.viewer Viewer

SolverMPM is an API scaffold in 0.4.0; its step() method raises, and novaphy.is_scaffold(...) identifies it. SolverLBM is a real runtime with an optional CUDA numerical backend, not a scaffold.

Public Python surface

Area Entry points
Scene construction ModelBuilder, Model, Transform, ShapeConfig, JointType
Runtime buffers SimState, Control, Contacts, CollisionPipeline
Solvers novaphy.solvers.SolverSemiImplicit, SolverFeatherstone, SolverXPBD, SolverMuJoCo, SolverVBD, SolverPBF; CUDA-gated SolverSPH / SolverLBM; build-gated SolverIPC
Multi-environment ModelBuilder.replicate, MultiEnvLayout, MultiEnvRunner
Robotics and I/O UrdfParser, ModelBuilder.add_mjcf, OpenUsdImporter, SimulationExporter
IK and control novaphy.ik, novaphy.sensors, novaphy.actuators
Visualization novaphy.viewer.ViewerGL, ViewerNull, ViewerFile, ViewerUSD, ViewerRerun, ViewerViser
Runtime utilities PerformanceMonitor, Device, CudaGraphCapture, feature checks

The complete topic index is in the Python API Reference.

State and integration notes

  • Flat body/free-joint velocity buffers and C++ spatial motion vectors are ordered [linear; angular]; spatial force buffers are [force; torque].
  • Some std::vector bindings return copies. Use state setters such as set_linear_velocity() and set_angular_velocity() instead of assuming an array slice writes back to C++.
  • DeviceArray.numpy() always returns a copied host snapshot, on both CPU and CUDA. Buffered CPU arrays can expose a zero-copy view through np.asarray(device_array). Use the documented assignment/setter path when mutation must write back.
  • Guard optional GPU code with the corresponding has_*() function before allocating a GPU model or solver.

Next steps