Skip to content

Quick Start

This guide gets external partners from a clean machine to a running NovaPhy demo as quickly as possible, then points to the rest of the documentation for deeper integration.

Prerequisites

  • Conda (Miniconda or Anaconda)
  • vcpkg installed
  • C++20 compiler
    • MSVC 2022 (Windows)
    • GCC 11+ (Linux)
    • Clang 14+ (Linux / macOS)
  • (Optional, for IPC) CUDA >= 12.4
  • (Optional, for VBD CUDA backend) CUDA toolkit + NVCC
  • (Optional, for SPH CUDA backend) CUDA toolkit + NVCC

Setup

# Create conda environment
conda env create -f environment.yml
conda activate novaphy

# Make your vcpkg toolchain visible to CMake
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"

# Install NovaPhy
pip install -e .

# (Optional) Install with IPC support
git submodule update --init --recursive
CMAKE_ARGS="--preset=ipc" pip install -e .

# (Optional) VBD with CUDA backend
CMAKE_ARGS="-DNOVAPHY_WITH_VBD_CUDA=ON -DCMAKE_CUDA_COMPILER=/path/to/nvcc" pip install -e .

# (Optional) Enable SPH CUDA backend
CMAKE_ARGS="-DNOVAPHY_WITH_SPH_CUDA=ON -DCMAKE_CUDA_COMPILER=/path/to/nvcc" pip install -e .

See Build from Source for full details, compiler compatibility tables, and troubleshooting.

Verify

import novaphy
print(novaphy.version())        # 0.4.0
print(novaphy.has_ipc())        # True if built with IPC
print(novaphy.has_sph_cuda())   # True if built with SPH CUDA backend

Run a Demo

Demos live under python/demos/ and use Polyscope for visualization (pip install polyscope).

# Rigid body demos (pick the solver backend with --solver)
python python/demos/demo_pyramid_ball.py --solver semi_implicit
python python/demos/demo_pyramid_ball.py --solver featherstone
python python/demos/demo_pyramid_ball.py --solver xpbd

# Articulated multibody demos
python python/demos/multibody_demo/demo_newtons_cradle_pendulum.py
python python/demos/multibody_demo/demo_rope.py

# Fluid demos
python python/demos/demo_dam_break.py
python python/demos/demo_ball_in_water.py
python python/demos/demo_sph_fluid.py

# IPC demo (requires IPC build)
python python/demos/demo_ipc_stack.py

# VBD / AVBD demos
python python/demos/demo_vbd_contacts_gui.py
python python/demos/demo_vbd_joint_gui.py
python python/demos/demo_vbd_spring_gui.py

The full demo list with screenshots and matching solver backends is in Demos.

What NovaPhy Looks Like in Code

NovaPhy uses Newton's solver-primary API: build a Model, construct a solver with that model, and call solver.step(state_in, state_out, control, contacts, dt) directly. There is no World wrapper.

import numpy as np
import novaphy

# 1. Build the scene
builder = novaphy.ModelBuilder()
builder.add_ground_plane(y=0.0)

half_extents = np.array([0.5, 0.5, 0.5], dtype=np.float32)
body = novaphy.RigidBody.from_box(1.0, half_extents)
idx = builder.add_body(
    body,
    novaphy.Transform.from_translation(np.array([0.0, 5.0, 0.0], dtype=np.float32)),
)
builder.add_shape(novaphy.CollisionShape.make_box(half_extents, idx))

# 2. Finalize and create a solver + runtime buffers
model    = builder.finalize()
solver   = novaphy.solvers.SolverSemiImplicit(model)
state    = model.state()
control  = model.control()
collision_pipeline = novaphy.CollisionPipeline(model)
contacts = collision_pipeline.contacts()

# 3. Step the simulation
for step in range(600):
    # Aliased state == in-place. Pass distinct buffers for checkpointed rollouts.
    collision_pipeline.collide(state, contacts)
    solver.step(state, state, control, contacts, 1.0 / 120.0)
    if step % 120 == 0:
        pos = state.transforms[idx].position
        print(f"t={step/120:.1f}s  y={pos[1]:.3f}")

Newton-aligned explicit collide + step chain

Newton separates broadphase / narrowphase from the constraint solve. Call collision_pipeline.collide before solver.step so the solver has contacts to consume:

for step in range(600):
    collision_pipeline.collide(state, contacts)                  # broadphase + narrowphase
    solver.step(state, state, control, contacts, 1.0 / 120.0)

collision_pipeline.collide(state, contacts) matches Newton's collision-on-model contract. If you skip the explicit call, rigid solvers run without rigid contact constraints for that step.

Public Python Surface

import novaphy is the primary entry point. The partner-facing public API is the set of names exported from novaphy.__all__, plus solver classes and configuration objects under novaphy.solvers.

Area Public entry points
Version and feature checks novaphy.version(), novaphy.has_ipc(), novaphy.has_sph_cuda()
Scene construction ModelBuilder, Model, RigidBody, CollisionShape, ShapeConfig, ShapeType, ShapeFlags, Transform, SpatialTransform, Mesh, AABB, Site
Collision CollisionPipeline, CollisionFilterPair, BroadPhaseMode, BroadPhasePair, SweepAndPrune
Runtime buffers SimState, Control, Contacts
Solvers novaphy.solvers.SolverSemiImplicit, SolverFeatherstone, SolverXPBD, SolverPBF, SolverSPH, SolverIPC, SolverMPM, SolverLBM, plus settings/config objects
Articulated bodies Joint, JointType, JointDofConfig, Axis, axis_to_vec3, forward_kinematics, inverse_dynamics, mass_matrix_crba, forward_dynamics, forward_link_velocities, eval_fk
Fluids FluidBlockDef, generate_fluid_block, SPHKernels, SpatialHashGrid, ParticleState, SPHState, BoundaryParticle, sample_model_boundaries, apply_particle_coupling_contacts, apply_particle_contact_reactions
Robot and scene I/O UrdfParser, UrdfImportOptions, OpenUsdImporter, SceneBuilderEngine, SimulationExporter, FeatureCompletenessChecker, ModelBuilder.add_mjcf, ModelBuilder.add_mjcf_string
Observations and control helpers novaphy.sensors, novaphy.actuators
Math, profiling, and utilities skew, spatial_*, deg2rad, rad2deg, compute_inertia_mesh, batch_transform_vertices, Device, PerformanceMonitor, is_scaffold, scaffold_reason

The full per-symbol reference lives in Python API Reference.

Integration Notes

When embedding NovaPhy into a partner application:

  • NovaPhy uses SI units (meters, kilograms, seconds) unless an API states otherwise.
  • Numeric data is float32-first. Prefer np.float32 for positions, velocities, forces, and geometry data crossing the boundary.
  • Model is immutable scene topology and can be shared across rollouts. Each environment instance allocates its own SimState, Control, and collision-sized Contacts via a CollisionPipeline bound to the same Model.
  • Guard optional CUDA features with feature checks before constructing CUDA-backed solvers:

    if novaphy.has_ipc() and novaphy.solvers.SolverIPC is not None:
        solver = novaphy.solvers.SolverIPC(model, novaphy.solvers.IPCConfig())
    
  • novaphy.VBDWorld and, in IPC-enabled builds, novaphy.IPCWorld are legacy backend containers. New integration code should prefer the solver-primary APIs (novaphy.solvers.SolverIPC for IPC; a SolverVBD wrapper is in flight for VBD).

Fluid Simulation

Run a Position Based Fluids dam break with the Newton-aligned solver chain:

import numpy as np
import novaphy

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

fluid_block = novaphy.FluidBlockDef()
fluid_block.lower = np.array([0.0, 0.0, 0.0])
fluid_block.upper = np.array([1.0, 1.0, 1.0])
fluid_block.particle_spacing = 0.05

positions = novaphy.generate_fluid_block(fluid_block)
mass = fluid_block.rest_density * fluid_block.particle_spacing ** 3
radius = fluid_block.particle_spacing * 0.5
builder.add_particles(
    positions,
    [fluid_block.initial_velocity] * len(positions),
    [mass] * len(positions),
    [radius] * len(positions),
)
model = builder.finalize()

pbf_config = novaphy.solvers.PBFConfig()
pbf_config.kernel_radius = 4.0 * 0.05
pbf_config.solver_iterations = 4

# SolverPBF (fluid) chained with SolverSemiImplicit (rigid) gives full
# Akinci two-way coupling through the shared Contacts aggregate.
pbf_solver   = novaphy.solvers.SolverPBF(model, pbf_config)
rigid_solver = novaphy.solvers.SolverSemiImplicit(model)

state    = model.state()
pbf_solver.initialize_state(state)
control  = model.control()
collision_pipeline = novaphy.CollisionPipeline(model)
contacts = collision_pipeline.contacts()

dt = 1.0 / 120.0
for _ in range(500):
    collision_pipeline.collide(state, contacts)
    pbf_solver.step(state, state, None, contacts, dt)
    collision_pipeline.collide(state, contacts)
    rigid_solver.step(state, state, control, contacts, dt)

Visualization

NovaPhy uses Polyscope for 3D visualization. Most demos include built-in visualization:

# Install polyscope
pip install polyscope

# Run a visual demo
python python/demos/demo_pyramid_ball.py
python python/demos/demo_dam_break.py
python python/demos/vbd/demo_vbd_rigid.py --scene pyramid --headless 120

Next Steps