Skip to content

Architecture

Core pipeline

NovaPhy follows a Newton-aligned solver-primary pipeline:

graph LR
    A[ModelBuilder<br/>mutable] --> B[Model<br/>immutable]
    B --> C[State / Control / Contacts<br/>caller-owned buffers]
    B --> D[Solver<br/>bound to Model]
    C --> E[solver.step<br/>state_in, state_out, control, contacts, dt]
    D --> E
    E --> C
  1. ModelBuilder — mutable scene description. Add bodies, shapes, joints, articulations, fluid blocks.
  2. Model — immutable, baked data. Created by builder.finalize(). The same Model can be shared across independent solvers and rollout states.
  3. State / Control / Contacts — caller-owned runtime buffers allocated from the model via model.state(), model.control(), and CollisionPipeline.contacts().
  4. Solver — bound to a Model at construction time and stepped through solver.step(state_in, state_out, control, contacts, dt).

Canonical Python shape:

import novaphy

model = builder.finalize()
solver = novaphy.solvers.SolverSemiImplicit(
    model, novaphy.solvers.SolverSemiImplicit.Config()
)  # or SolverXPBD / SolverFeatherstone / SolverVBD / SolverPBF / SolverSPH / SolverIPC
state    = model.state()
control  = model.control()
collision_pipeline = novaphy.CollisionPipeline(model)
contacts = collision_pipeline.contacts()

collision_pipeline.collide(state, contacts)
solver.step(state, state, control, contacts, dt)

state_in and state_out may alias for in-place stepping. Pass two distinct state buffers when checkpointing, swapping rollout buffers, or running parallel evaluations from a shared Model.

Solvers

Solver Algorithm Header
SolverSemiImplicit Semi-implicit Euler + PGS free bodies dynamics/semi_implicit/solver_semi_implicit.h
SolverFeatherstone Articulated FK → RNEA → CRBA → Cholesky dynamics/featherstone/solver_featherstone.h
SolverXPBD XPBD maximal-coordinate dynamics/xpbd/solver_xpbd.h
SolverPBF Position-Based Fluids + Akinci coupling fluid/pbf_solver.h
SolverSPH SPH (CPU default, optional CUDA) fluid/solver_sph.h
SolverIPC IPC via external libuipc submodule (NVIDIA/CoreX GPU paths) dynamics/ipc/solver_ipc.h
SolverVBD VBD / AVBD primal-dual (CPU SolverBase path) dynamics/vbd/solver_vbd.h

Every SolverBase solver implements:

  • step(state_in, state_out, control, contacts, dt) — Newton-aligned forward dynamics.
  • notify_model_changed(SolverNotifyFlags) — cache invalidation.
  • joint_support()JointSupportMatrix capability table.
  • backend_info()SolverBackendInfo (device, fixed-dt, etc.).

Collision is explicit and lives on CollisionPipeline or Model.collide, not on the public Python solver binding.

SolverVBD follows the same solver-primary API while using VBD-specific model attributes and color groups generated by ModelBuilder.color().

See Solver Internal Data Pipeline for the per-solver scratch / DeviceArray SoA convention.

Free bodies (SolverSemiImplicit)

graph LR
    A[Broadphase<br/>SAP] --> B[Narrowphase]
    B --> C[Sequential Impulse<br/>PGS]
    C --> D[Integrate]

Articulated bodies (SolverFeatherstone)

graph LR
    A[FK] --> B[RNEA<br/>bias forces]
    B --> C[CRBA<br/>mass matrix]
    C --> D[Cholesky<br/>solve]
    D --> E[Integrate]

PBF fluids (SolverPBF)

graph LR
    A[Predict] --> B[Neighbor search<br/>spatial hash]
    B --> C[Density constraint<br/>iterative]
    C --> D[XSPH + Vorticity]
    D --> E[Update]

Collision system

Broadphase

CollisionPipeline exposes three public modes:

  • Explicit — uses the model's precomputed shape contact pairs.
  • SAP — sweep-and-prune AABB sorting.
  • NXN / all-pairs — brute-force AABB checks for small scenes and tests.

Narrowphase

Dispatches to specialized collision-pair algorithms:

  • Sphere-sphere, sphere-plane, sphere-box.
  • Box-plane, box-box (analytic contacts).
  • contact core for general convex shapes.
  • Cylinder collision pairs.

Contact convention

  • Normal direction: body_a → body_b (positive impulse separates).
  • Plane shapes use body_index = -1 (world-owned, always static).

Profiling

Optional. Wrap a step in the thread-local context manager (mirrors Newton's event_scope / EventTracer):

monitor = novaphy.PerformanceMonitor()
monitor.enabled = True
monitor.trace_enabled = True

for _ in range(steps):
    with monitor.scoped():
        solver.step(state, state, control, contacts, dt)

for stat in monitor.phase_stats():
    print(stat.name, stat.avg_ms)
monitor.write_trace_json("trace.json")

Outside the with block every C++ phase scope is a zero-cost no-op, so solvers pay nothing when profiling is not in use.

Data types

NovaPhy uses float32 exclusively — never double.

Type Definition
Scalar float
Vec3f Eigen::Vector3f
Mat3f Eigen::Matrix3f
Quatf Eigen::Quaternionf
VecXf Eigen::VectorXf
MatXf Eigen::MatrixXf

Spatial algebra helpers use the Featherstone convention: [angular; linear] for 6D vectors. The flat rigid-body state views use state.body_qd == [linear; angular] and state.body_f == [force; torque].

File organization

NovaPhy/
├── novaphy/include/        # Public C++ headers
│   ├── math/               # Vec3f, Mat3f, Quatf, spatial algebra
│   ├── core/               # Body, Shape, Joint, Model, ModelBuilder, Contacts
│   ├── collision/          # SAP, BVH, narrowphase, contact core
│   ├── dynamics/           # SolverBase, integrator
│   │   ├── semi_implicit/  # SolverSemiImplicit + free-body PGS
│   │   ├── featherstone/   # SolverFeatherstone, FK / RNEA / CRBA
│   │   ├── xpbd/           # SolverXPBD
│   │   ├── vbd/            # SolverVBD (CPU SolverBase path)
│   │   └── ipc/            # SolverIPC over libuipc (CUDA)
│   ├── fluid/              # SolverPBF, SolverSPH, SPH kernels, Akinci boundary
│   ├── io/                 # URDF, MJCF, OpenUSD, scene builder
│   └── sim/                # SimState, PerformanceMonitor
├── novaphy/src/            # C++ implementations (mirrors novaphy/include/)
├── python/
│   ├── novaphy/            # Python package (sensors, actuators, solvers, viz)
│   └── bindings/           # pybind11 binding files
│   └── tests/              # pytest files
├── novaphy/tests/          # C++ unit tests
├── cmake/cmake_project/    # CMake integration sample projects
└── python/demos/           # Polyscope demo scripts