Configuration

Molenspin exposes configuration at two levels: assembly-wide settings passed to ms.Assembly(), and per-simulation settings passed to .simulate().

Assembly options

asm = ms.Assembly(
    name="my-gearbox",          # str — label for exports and error messages
    gravity=9.81,               # float, m/s² — used only if dynamic mode enabled
    default_module=2.0,         # float, mm — tooth module for gears without explicit module
    backlash=0.0,               # float, radians — global backlash; override per gear pair
    frame_origin=(0.0, 0.0),    # tuple[float, float] — SVG coordinate origin
)

Simulation options

result = asm.simulate(
    duration=2.0,               # float, seconds
    dt=1e-3,                    # float, seconds — timestep
    integrator="rk4",           # "rk4" | "verlet" | "euler" — default rk4
    rtol=1e-6,                  # float — relative tolerance (adaptive integrators)
    atol=1e-9,                  # float — absolute tolerance
    warm_start=True,            # bool — use previous state as initial condition
    record_contacts=False,      # bool — log contact forces (slow, for diagnostics)
)

Integrator notes

IntegratorOrderUse when
rk44Default. Good accuracy/speed balance for smooth inputs.
verlet2 (symplectic)Long-duration runs where energy conservation matters.
euler1Quick prototyping only — accumulates error fast.

Gear options

gear = asm.add_gear(
    teeth=36,                   # int — tooth count, required
    driven_by=some_shaft,       # Shaft | None
    meshes_with=other_gear,     # Gear | None — at most one of driven_by/meshes_with
    module=None,                # float | None — overrides assembly default_module
    pressure_angle=20.0,        # float, degrees — involute pressure angle
    helix_angle=0.0,            # float, degrees — for helical gear approximation
    name="g1",                  # str
)

Bevel gear pairs

For right-angle drives (e.g. the windmill vertical-to-horizontal shaft hand-off), use ms.BevelPair:

bevel = ms.BevelPair(
    gear_a=asm.add_gear(teeth=24, driven_by=shaft_vertical),
    gear_b=asm.add_gear(teeth=24),
    shaft_angle=90.0,           # degrees between the two shaft axes
)

Belt and chain drives

belt = ms.BeltDrive(
    sprocket_driver=asm.add_gear(teeth=18, driven_by=shaft_in),
    sprocket_driven=asm.add_gear(teeth=54),
    slip=0.0,                   # fraction — 0.02 = 2% slip
)

SVG export options

result.export_svg(
    path="output.svg",
    fps=30,                     # int — frames per second
    duration=None,              # float | None — trim to this many seconds
    scale=1.0,                  # float — global scale factor
    show_pitch_circles=True,    # bool — draw pitch circle overlays
    show_contact_points=False,  # bool — mark instantaneous contact points
    tooth_style={               # dict — SVG CSS overrides for tooth fills/strokes
        "fill": "#1c2130",
        "stroke": "#e08a30",
        "stroke-width": "0.5",
    },
    background="#10131a",       # str — SVG background color
)

JSON export

result.export_json("timeseries.json", indent=2)
# Produces: { "dt": 0.001, "shafts": { "input": { "omega": [...], "theta": [...] }, ... } }
⚠️

record_contacts=True increases memory by roughly 6× and slows simulation by 30–40%. Only enable it when you actually need contact force data.

Logging

import logging
logging.basicConfig(level=logging.DEBUG)
import molenspin as ms
# The molenspin logger is named "molenspin"
# Set to WARNING in production — DEBUG prints every constraint iteration