October 14, 2025 · release · 4 min read

v0.2 — BeltDrive, bevel gears, and a better error model

Three months since the initial release. The main things I wanted to fix: the complete absence of non-spur-gear support, and an error system that printed "solver did not converge" with no context whatsoever. Both are addressed in 0.2.

BeltDrive

Chain and belt transmissions are now first-class citizens. The BeltDrive class takes a driver and driven sprocket (which are just gears with tooth counts interpreted as sprocket teeth) and an optional slip fraction:

belt = ms.BeltDrive(
    sprocket_driver=asm.add_gear(teeth=22, driven_by=motor_shaft),
    sprocket_driven=asm.add_gear(teeth=66),
    slip=0.015   # 1.5% creep
)

The slip model is the simplest possible thing — a constant fractional reduction on the driven sprocket velocity. It's not a tribology model; if you need Eytelwein creep equations you'll have to implement a custom Constraint. For most practical purposes the constant-slip approximation is fine.

Bevel gears

Right-angle shaft drives now work. The BevelPair constructor takes two gears and a shaft angle (default 90°). The kinematic model is exact for a 90° bevel pair; other angles use the standard pitch cone approximation which is accurate to within 0.5% for angles between 45° and 135°.

Better exceptions

The old solver raised a bare RuntimeError("solver did not converge"). The new exception hierarchy tells you what failed and why:

# Old
RuntimeError: solver did not converge

# New
molenspin.ConvergenceError: Newton solver failed at step 847 (t=0.847s)
  Residual norm: 3.42e-2 (tolerance: 1e-6)
  Largest residual: constraint 'PivotConstraint(wallower_shaft)' —
    try loosening atol or checking for a near-singular configuration

This took longer to implement than the bevel gear support. Getting useful error messages out of a compiled Rust extension requires some care around how you ferry diagnostic state across the FFI boundary — I ended up using a thread-local error buffer in Rust that Python reads back on exception.

Breaking changes

See the full changelog for the complete list.