Skip to content

novaphy.solvers.SolverBase

Newton-aligned base class for every NovaPhy SolverBase subclass. Defines the canonical 5-argument forward-dynamics contract plus notification and introspection hooks.

class SolverBase:
    def __init__(self, model: Model): ...
    def step(self, state_in: SimState, state_out: SimState,
             control: Control | None, contacts: Contacts | None,
             dt: float) -> None: ...
    def notify_model_changed(self, flags: SolverNotifyFlags) -> None: ...
    def joint_support(self) -> JointSupportMatrix: ...
    def backend_info(self) -> SolverBackendInfo: ...

Construction

The model argument is mandatory and positional. There is no settings= kwarg — solver-specific configuration objects (PBFConfig, IPCConfig, …) are passed where they apply. Some concrete rigid solvers also expose a mutable solver.settings; it is not a SolverBase property.

solver = novaphy.solvers.SolverSemiImplicit(model)

Step Contract

solver.step(state_in, state_out, control, contacts, dt)
Argument Description
state_in Input SimState. May alias state_out for in-place stepping.
state_out Output SimState. After return, holds the integrated state.
control Control (or None to use the defaults baked into Model).
contacts Contacts aggregate produced by CollisionPipeline.collide or Model.collide. For most solvers, passing None runs without contact constraints. SolverIPC ignores this argument and performs collision internally.
dt Time step in seconds. Some backends (e.g. SolverIPC) require a fixed dt matching their config.

Collision detection is explicit for most solvers. Populate contacts before stepping when the solver should enforce rigid / soft-point contacts:

pipeline = novaphy.CollisionPipeline(model, broad_phase="sap")
contacts = pipeline.contacts()

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

SolverIPC follows the same five-argument signature, but libuipc owns its collision handling and the adapter ignores the contacts value.

Aliasing state_in and state_out lets the same buffer be advanced in-place. Pass distinct buffers when the input must be preserved:

state_a = model.state()
state_b = model.state()
pipeline = novaphy.CollisionPipeline(model)
contacts = pipeline.contacts()

for _ in range(steps):
    state_a.clear_forces()
    pipeline.collide(state_a, contacts)
    solver.step(state_a, state_b, control, contacts, dt)
    state_a, state_b = state_b, state_a

Other Methods

Method Purpose
notify_model_changed(flags) Invalidate cached data after mutating Model properties (gravity, drives, filters). Pass an SolverNotifyFlags bitmask.
joint_support() Returns a JointSupportMatrix describing which joint types and joint properties this solver actually enforces.
backend_info() Returns a SolverBackendInfo describing device, fixed-dt requirement, and state ownership.

Properties

Property Description
model The constructor-bound Model. Read-only Newton-aligned attribute.

Multi-instance / Multi-Env Sharding

model.state() returns a fresh buffer per call, but concrete solvers may own warm-start history, factorization scratch, or backend caches. Use an independent (SimState, SolverBase, CollisionPipeline) tuple for each independent stream unless that solver explicitly documents batched worlds:

solver_per_env = [novaphy.solvers.SolverSemiImplicit(model) for _ in range(n_envs)]
state_per_env  = [model.state() for _ in range(n_envs)]
ctrl_per_env   = [model.control() for _ in range(n_envs)]
pipe_per_env   = [novaphy.CollisionPipeline(model, broad_phase="sap") for _ in range(n_envs)]
cont_per_env   = [pipe.contacts() for pipe in pipe_per_env]

for solver, s, c, pipe, k in zip(solver_per_env, state_per_env,
                                  ctrl_per_env, pipe_per_env, cont_per_env):
    s.clear_forces()
    pipe.collide(s, k)
    solver.step(s, s, c, k, dt)

Subclasses

See novaphy.solvers for the full list of solver classes. NovaPhy currently ships:

SolverSemiImplicit, SolverFeatherstone, SolverXPBD, SolverMuJoCo, SolverVBD, SolverPBF, CUDA-gated SolverSPH and SolverLBM, build-gated SolverIPC, plus the SolverMPM scaffold.

See Also