Files
create/docs/src/solver/overview.md
forbes acc255972d
Some checks failed
Build and Test / build (pull_request) Failing after 7m52s
feat(assembly): fixed reference planes + solver docs
Assembly Origin Planes:
- AssemblyObject::setupObject() relabels origin planes to
  Top (XY), Front (XZ), Right (YZ) on assembly creation
- CommandCreateAssembly.py makes origin planes visible by default
- AssemblyUtils.cpp getObjFromRef() resolves LocalCoordinateSystem
  to child datum elements for joint references to origin planes
- TestAssemblyOriginPlanes.py: 9 integration tests covering
  structure, labels, grounding, reference resolution, solver,
  and save/load round-trip

Solver Documentation:
- docs/src/solver/: 7 new pages covering architecture overview,
  expression DAG, constraints, solving algorithms, diagnostics,
  assembly integration, and writing custom solvers
- docs/src/SUMMARY.md: added Kindred Solver section
2026-02-21 09:09:16 -06:00

6.7 KiB

Kindred Solver Overview

The Kindred solver is an expression-based Newton-Raphson constraint solver for the Assembly workbench. It is a pure-Python implementation that registers as a pluggable backend through the KCSolve framework, providing an alternative to the built-in OndselSolver (Lagrangian) backend.

Architecture

                            Assembly Module
                                  │
                      ┌───────────┴───────────┐
                      │    SolverRegistry      │
                      │  get("kindred")        │
                      └───────────┬───────────┘
                                  │
                      ┌───────────┴───────────┐
                      │    KindredSolver       │
                      │  (kcsolve.IKCSolver)   │
                      └───────────┬───────────┘
                                  │
              ┌───────────────────┼───────────────────┐
              │                   │                   │
     ┌────────┴────────┐  ┌──────┴──────┐  ┌────────┴────────┐
     │  _build_system  │  │  Solve      │  │  Diagnostics    │
     │  ────────────── │  │  ─────      │  │  ───────────    │
     │  ParamTable     │  │  pre-passes │  │  DOF counting   │
     │  RigidBody      │  │  Newton-R   │  │  overconstrained│
     │  Constraints    │  │  BFGS       │  │  per-entity DOF │
     │  Residuals      │  │  decompose  │  │  half-spaces    │
     └─────────────────┘  └─────────────┘  └─────────────────┘

Design principles

Symbolic differentiation. All constraint equations are built as immutable expression DAGs (Expr trees). The Jacobian is computed symbolically via expr.diff() rather than finite differences. This gives exact derivatives, avoids numerical step-size tuning, and allows pre-passes to simplify or eliminate trivial equations before the iterative solver runs.

Residual-based formulation. Each constraint produces a list of residual expressions that should evaluate to zero when satisfied. A Coincident constraint produces 3 residuals (dx, dy, dz), a Revolute produces 5 (3 position + 2 axis alignment), and so on. The solver minimizes the residual vector norm.

Unit quaternions for rotation. Orientation is parameterized as a unit quaternion (w, x, y, z) rather than Euler angles, avoiding gimbal lock. A quaternion normalization residual (qw^2 + qx^2 + qy^2 + qz^2 - 1 = 0) is added for each free body, and quaternions are re-projected onto the unit sphere after each Newton step.

Current placements as initial guess. The solver uses the parts' current positions as the initial guess, so it naturally converges to the nearest solution. Combined with half-space tracking, this produces physically intuitive results without branch-switching surprises.

Solve pipeline

When KindredSolver.solve(ctx) is called with a SolveContext:

  1. Build system (_build_system) -- Create a ParamTable with 7 parameters per part (tx, ty, tz, qw, qx, qy, qz). Grounded parts have all parameters fixed. Build constraint objects from the context, collect their residual expressions, and add quaternion normalization residuals for free bodies.

  2. Solution preferences -- Compute half-space trackers for branching constraints (Distance, Parallel, Angle, Perpendicular) and build a minimum-movement weight vector that penalizes quaternion changes more than translation changes.

  3. Pre-passes -- Run the substitution pass (replace fixed parameters with constants) and the single-equation pass (analytically solve residuals with only one free variable).

  4. Solve -- For assemblies with 8+ free bodies, decompose the constraint graph into biconnected components and solve each cluster independently. For smaller assemblies, solve the full system monolithically. In both cases, use Newton-Raphson first, falling back to L-BFGS-B if Newton doesn't converge.

  5. Post-process -- Count degrees of freedom via Jacobian SVD rank. On failure, run overconstrained detection to identify redundant or conflicting constraints. Extract solved placements from the parameter table.

Module map

Module Purpose
solver.py KindredSolver class: IKCSolver bridge, solve/diagnose/drag entry points
expr.py Immutable expression DAG with eval, diff, simplify
params.py Parameter table: named variables with fixed/free tracking
entities.py RigidBody: 7-DOF entity owning solver parameters
quat.py Quaternion rotation as polynomial Expr trees
geometry.py Marker axis extraction, vector ops (dot, cross, point-plane, point-line)
constraints.py 24 constraint classes producing residual expressions
newton.py Newton-Raphson with symbolic Jacobian, quaternion renormalization
bfgs.py L-BFGS-B fallback via scipy
prepass.py Substitution pass and single-equation analytical solve
decompose.py Biconnected component graph decomposition and cluster-by-cluster solving
dof.py DOF counting via Jacobian SVD rank
diagnostics.py Overconstrained detection, per-entity DOF classification
preference.py Half-space tracking and minimum-movement weighting

File locations

  • Solver addon: mods/solver/ (git submodule)
  • KCSolve C++ framework: src/Mod/Assembly/Solver/
  • Python bindings: src/Mod/Assembly/Solver/bindings/
  • Integration tests: src/Mod/Assembly/AssemblyTests/TestKindredSolverIntegration.py
  • Unit tests: mods/solver/tests/