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:
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.
# 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
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::vectorbindings return copies. Use state setters such asset_linear_velocity()andset_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 throughnp.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¶
- Architecture — ownership, collision, and solver selection.
- Build from Source — compiler and backend matrix.
- Viewer — interactive, headless, and recorded output.
- Migration from
world.step— upgrade older NovaPhy code.