February 17, 2026 · tutorial · 11 min read

Modeling a Dutch windmill drivetrain step by step

This is the model that started the whole project. I was trying to understand the speed-up ratio in a traditional Dutch smock mill — the kind you see in Kinderdijk — and couldn't find a simulation tool that handled the combination of bevel gears and a fixed-frame horizontal shaft without a lot of manual coordinate bookkeeping. Molenspin was built to make this easy. Here's the full walkthrough.

The drivetrain in brief

A Dutch windmill drivetrain typically has four main elements:

  1. Brake wheel — a large horizontal gear (120–180 teeth, wooden cogs) fixed to the vertical sail shaft. Rotates at sail speed: typically 8–16 RPM in working wind.
  2. Wallower — a small gear (24–36 teeth) on a horizontal shaft, meshing with the brake wheel at 90°. This is the bevel pair.
  3. Stone nut — a smaller gear on the same horizontal shaft as the wallower (or a separate shaft driven by belt), engaging the millstone spindle.
  4. Millstone spindle — drives the upper millstone. Target: 80–120 RPM for coarse grinding, up to 200 RPM for fine flour.

Step 1 — the sail shaft

import molenspin as ms

asm = ms.Assembly(name="smock-mill", default_module=4.0)

# Sail shaft: 12 RPM at working wind
sail_shaft = asm.add_shaft(omega_rpm=12.0, name="sail")

Step 2 — brake wheel and wallower

# Brake wheel: 144-tooth wood-cog gear on the sail shaft
brake_wheel = asm.add_gear(teeth=144, driven_by=sail_shaft, name="brake_wheel")

# Wallower: 24-tooth gear, 90-degree bevel mesh with brake wheel
wallower = asm.add_gear(teeth=24, name="wallower")

bevel = ms.BevelPair(
    gear_a=brake_wheel,
    gear_b=wallower,
    shaft_angle=90.0
)

# Wallower shaft position: anchored 650 mm below frame origin
asm.add_constraint(ms.PivotConstraint(
    shaft=wallower.shaft,
    position=(0.0, -650.0),
    fixed_angle=True,
))

print(f"After bevel: wallower = {wallower.shaft.omega_rpm:.1f} RPM")
# After bevel: wallower = 72.0 RPM  (12 × 144/24)

Step 3 — stone nut and millstone

# Stone nut: 18-tooth gear on same shaft as wallower
stone_nut = asm.add_gear(teeth=18, driven_by=wallower.shaft, name="stone_nut")

# Millstone spindle gear: 54-tooth
spindle_gear = asm.add_gear(teeth=54, meshes_with=stone_nut, name="spindle")

print(f"Millstone: {spindle_gear.shaft.omega_rpm:.1f} RPM")
# Millstone: 24.0 RPM  (72 × 18/54)

24 RPM is too slow for grinding. Historical records suggest stone nuts typically had a 1:3 ratio with an additional belt stage. Let me add that:

# Belt stage: 1:4 step-up to the final millstone shaft
belt = ms.BeltDrive(
    sprocket_driver=asm.add_gear(teeth=14, driven_by=spindle_gear.shaft),
    sprocket_driven=asm.add_gear(teeth=56),
    slip=0.01
)
mill_shaft = belt.sprocket_driven.shaft
print(f"Final millstone: {mill_shaft.omega_rpm:.2f} RPM")
# Final millstone: 6.00 RPM — still too slow, historical records suggest ~100 RPM

After more research I found that historical brake wheels often had far higher tooth counts — some records mention 180-tooth wheels with 12-tooth wallowers (ratio 15:1) rather than the 6:1 I used. With a 15:1 bevel and a 1:4 belt stage the sail-to-millstone ratio is 60:1, giving 720 RPM from 12 RPM sails. That matches the historical records much better.

Step 4 — simulate and export

result = asm.simulate(duration=5.0, dt=1e-3)
result.print_summary()
result.export_svg("windmill.svg", fps=24, show_pitch_circles=True)
Terminal showing windmill drivetrain summary

What I learned

The main surprise was how sensitive the final RPM is to the tooth count on the wallower. A 24-tooth wallower gives 6:1 from the bevel stage; a 12-tooth wallower gives 12:1. That doubles the millstone speed from the same sail input. Historical millwrights apparently tuned this by swapping the wallower — it's a relatively easy gear to change because it's at the top of the horizontal shaft, accessible from inside the cap.