The fastest slide

Which track between two points does a bead slide down fastest, from rest and without friction? This is the brachistochrone problem, posed by Johann Bernoulli. The straight ramp is the shortest, but the bead starts slowly on it, and a circular arc is faster. The fastest track is a cycloid, the curve that a point on a rolling circle traces. The cycloid is also the tautochrone, the curve that Huygens used for his pendulum clocks. Wherever on it the bead starts from rest, it takes the same time to the bottom.

The example slides a bead down three tracks from (0, 0) to \((\pi r, -2r)\), for a rolling circle of radius \(r = 1\,\text{m}\). The cycloid must take \(\pi \sqrt{r/g}\), less than the others, from any height on it, and its length must be \(4r\). Swept with a disc, it is a tube whose volume must be \(\pi \rho^2 L\).

Beads sliding down the ramp, the arc and the cycloid, shown at the same instants

[1]:
import math
import numpy as np
import netgen.occ as occ
import planex as px

Three tracks

The cycloid is \(x = r\,(\theta - \sin\theta)\), \(y = -r\,(1 - \cos\theta)\), from its cusp at \(\theta = 0\) to its bottom at \(\theta = \pi\). px.Curve draws it from this formula, with \(\theta\) in degrees, t*pi/180 where it stands alone. The arc is the circle through both ends that is level at the bottom, as the cycloid is, so its center lies straight above the bottom end, at the height \((\pi^2 - 4)\,r/4\) (Figure 1). The cycloid’s length is \(4r\), which the print compares:

[3]:
g = 9.81
r = 1.0


def cycloid(theta0):
    """the cycloid from theta0, in radians, to its bottom at pi"""
    return px.Curve((f"{r}*(t*pi/180 - sin(t))", f"-{r}*(1 - cos(t))"), (math.degrees(theta0), 180))


top, bottom = (0.0, 0.0), (math.pi * r, -2 * r)
tracks = {
    "ramp": px.Line(top, bottom),
    "arc": px.Arc(top, bottom, (math.pi * r, (math.pi**2 - 4) * r / 4)),
    "cycloid": cycloid(0),
}
print("lengths", {name: track.mass for name, track in tracks.items()}, "| 4r", 4 * r)
lengths {'ramp': 3.724191778237173, 'arc': 3.9314191519790076, 'cycloid': 3.999999999999859} | 4r 4.0
The ramp, the arc and the cycloid, highlighted
Figure 1: Three tracks between the same two points

Sliding down

The bead’s speed follows from its energy, \(v = \sqrt{2 g h}\) after a drop \(h\). slide follows a track’s points, Polylines at a tolerance of 10⁻⁷, and adds the time of every straight piece between them. On a straight piece the bead’s speed changes linearly in time, so it takes \(2L/(v_1 + v_2)\) for its length \(L\), exactly. The points follow the curve closely enough for the sum to be the curve’s time. slide also returns the times at the points, for the picture below. The ramp’s time is \(L \sqrt{2/(g h)}\), and the cycloid’s is \(\pi \sqrt{r/g}\), the shortest of the three:

[5]:
def slide(track):
    """the points along the track and the times the bead, starting there at rest, reaches them"""
    points = np.asarray(track.Polylines(1e-7)[0])[:, :2]
    speed = np.sqrt(np.maximum(2 * g * (points[0, 1] - points[:, 1]), 0))
    pieces = np.hypot(*np.diff(points, axis=0).T)
    return points, np.concatenate([[0], np.cumsum(2 * pieces / (speed[:-1] + speed[1:]))])


slides = {name: slide(track) for name, track in tracks.items()}
for name, (points, times) in slides.items():
    print(name, "|", times[-1], "s")
print("ramp", math.dist(top, bottom) * math.sqrt(2 / (g * 2 * r)), "s | cycloid pi sqrt(r/g)", math.pi * math.sqrt(r / g), "s")
ramp | 1.1890429254666885 s
arc | 1.0149483026825508 s
cycloid | 1.0030333599965362 s
ramp 1.1890429254666885 s | cycloid pi sqrt(r/g) 1.0030333403553238 s

The next figure shows the beads at the same instants, every 0.2 s. The cycloid’s bead drops steeply at first, gains speed early and is ahead all the way.

../_images/examples_fastest-slide_9_0.png

From any height

On the tautochrone, the bead reaches the bottom in the same time from any point where it starts from rest. From \(\theta = 0^\circ\), \(45^\circ\), \(90^\circ\) and \(135^\circ\), the times to the bottom are compared with \(\pi \sqrt{r/g}\):

[8]:
for degrees in (0, 45, 90, 135):
    points, times = slide(cycloid(math.radians(degrees)))
    print("from", degrees, "degrees |", times[-1], "s, pi sqrt(r/g)", math.pi * math.sqrt(r / g), "s")
from 0 degrees | 1.0030333599965362 s, pi sqrt(r/g) 1.0030333403553238 s
from 45 degrees | 1.0030340444734425 s, pi sqrt(r/g) 1.0030333403553238 s
from 90 degrees | 1.0030347685300822 s, pi sqrt(r/g) 1.0030333403553238 s
from 135 degrees | 1.0030386546497416 s, pi sqrt(r/g) 1.0030333403553238 s

A track in 3D

A tube of radius \(\rho = 5\,\text{cm}\) along the cycloid is a disc on the plane square to the curve at its start, swept along it with occ.Pipe (Figure 2). Near the cusp the cycloid bends more sharply than the tube is thick, so the tube starts at \(\theta = 0.3\), where the curve’s radius of curvature, \(4r \sin(\theta/2)\), is well above \(\rho\). The disc’s center follows the curve, so the volume is the disc’s area times the length, \(\pi \rho^2 \cdot 4r \cos(\theta/2)\):

[9]:
start = 0.3
spine = cycloid(start)
x0, y0 = r * (start - math.sin(start)), -r * (1 - math.cos(start))
dx, dy = 1 - math.cos(start), -math.sin(start)
square = px.Plane(origin=(x0, y0, 0), normal=(dx / math.hypot(dx, dy), dy / math.hypot(dx, dy), 0))
tube = occ.Pipe(spine.ToOCCWire(), px.QuickSketch(square).Circle(0.05).Face().ToOCCFace())
print("tube", tube.mass, "m³, exact", math.pi * 0.05**2 * 4 * r * math.cos(start / 2))
tube 0.031063219298310796 m³, exact 0.031063159545259313
The tube along the cycloid
Figure 2: A tube swept along the cycloid

See Lines and arcs for px.Arc and Splines for splines through points.