Notebooks · posts/euler-loses-the-planet
Forward Euler loses the planet
Ten orbits of a planet on an ellipse, 200 steps an orbit, stepped two ways at the same cost. Forward Euler gains energy until the planet is unbound. Leapfrog's energy error stays under one percent, and it keeps the angular momentum to rounding.
Look inside this program
- Atlas
The program’s map: every function and which calls which.
There’s no recorded run of this program yet.
Shift+Enter in a cell runs it and moves to the next. Running a cell runs every cell above it too, as one program.
The orbit, in units that keep the numbers small
A planet goes around a sun that sits still at the origin. The only force on it is the sun's gravity, which pulls toward the origin, so its acceleration is
where is the planet's position, is the gravitational constant and is the sun's mass. The program works in units where and the orbit's semi-major axis (half the long diameter of the ellipse) is 1. Kepler's third law then says one orbit takes time units.
The eccentricity ECC says how stretched the ellipse is: 0 is a circle, and at 0.6 the closest
approach to the sun (the perihelion) is and the farthest point is . The
program follows the planet for ten orbits with 200 time steps per orbit, and records how far the
energy has wandered at SAMPLES evenly spaced moments.
The number that should not change
With only gravity acting, the planet's energy per unit mass, kinetic plus potential,
has the same value at every moment of the true motion. That makes it a check on any numerical method: whatever change in the computer reports is error the method made.
The planet starts at perihelion, at , moving straight up. Its speed there is (from the vis-viva equation , with ). Before running: what is ? Any closed orbit has , so here. It is negative because the planet is bound: it lacks the energy to climb out of the sun's pull. If a method pushed above zero, the simulated planet would leave and never come back.
The printed start_energy is , as predicted. So a method has to add to the energy
to set the planet loose, and every bit it adds is error.
Forward Euler lets the start of the step stand in for the whole step
A computer cannot follow the motion continuously. It jumps ahead by a time step , again and again. Forward Euler is the simplest way to make each jump: take the velocity and the acceleration at the start of the step and pretend they stay fixed for the whole step,
The state lives in one array, s = [x, y, vx, vy], and a step overwrites it.
Before running one step: does it raise the energy or lower it? At perihelion the velocity points straight up and the pull points straight left, toward the sun. The position update moves the planet along a straight line, up and off the curve, which leaves it a little farther from the sun, so the potential energy rises. The velocity update adds a sideways kick at right angles to the velocity, which makes the velocity longer, so the kinetic energy rises too. Both parts go up.
The step is , and one Euler step from perihelion adds
to the energy (euler_one_step_gain), a tenth of the that would set the planet
loose. Perihelion is where the planet moves fastest and the pull is strongest, so it is where a
step's error is largest; there are 2000 steps to come.
Leapfrog kicks, drifts and kicks
Leapfrog does the same work per step, one evaluation of the force, in a different order. It gives the velocity half a step's worth of acceleration (a kick), then moves the position a full step with that updated velocity (a drift), then gives the velocity the other half kick using the force at the new position:
The second kick's force is the next step's first kick's force, so a program that keeps it pays for one force per step, the same as Euler. This one recomputes it, to keep each step on its own.
From the same starting point, one leapfrog step changes the energy by
(leap_one_step_change), 398 times less than Euler's step (step_ratio). That is not yet the
difference that matters. If each of the 2000 steps added that much, ten orbits would add
(leap_if_errors_added). What matters is whether the errors of many steps pile up or cancel.
Running ten orbits
The next function runs either method for ten orbits and records the energy error
, where is the starting energy, at SAMPLES evenly spaced times.
It records the base-10 logarithm, because the errors range over many powers of ten and a
logarithm puts them on one readable scale; the added inside keeps the logarithm finite
when the error is exactly zero. The planet's final state is left in FINAL for the checks further
down.
Forward Euler, ten orbits
Before running: 2000 steps, each adding a little energy. Does the total reach the that unbinds the planet?
It does. After ten orbits Euler has added (euler_final), so the planet's energy is
(euler_end_energy): positive, which means the simulated planet is no longer in orbit
at all. It is flying away. The real planet would go around forever.
Leapfrog, ten orbits, same step
Before running: if leapfrog's errors simply added up, the from the cell above would be the answer, half of what it takes to unbind the planet.
The largest energy error leapfrog makes in ten orbits is (leap_worst), which is
of the orbit's (leap_worst_share) and almost seventy times less than . Its errors do not add up.
The figure shows both runs on a logarithmic scale. Before looking: Euler's error is largest at perihelion, and the planet reaches perihelion once an orbit. Does Euler's error grow steadily, or in jumps?
In jumps. Euler's error is after the first orbit (euler_error_after_1_orbit), nearly all
of it gained in the few steps around the starting perihelion. That leaves the planet on a larger
orbit, one that takes of the original orbits to go around (euler_orbit_period), so the
next close pass to the sun comes near orbit 4.3 on the plot. That pass adds the rest, the total
goes past , and the planet leaves; there is no third pass.
Leapfrog's error sits near for most of each orbit and dips as the planet swings past the
sun. Its worst in the first orbit is (leap_first_orbit) and its worst in the tenth is
(leap_tenth_orbit): the same level, nine orbits later.
Why leapfrog's errors do not pile up
Leapfrog is symplectic. Each leapfrog step is the exact motion of a slightly different system, one whose energy function differs from the true by an amount that shrinks with . The steps hold that modified energy almost exactly constant, so the true can only wobble around it; it cannot drift away. Forward Euler has no such conserved quantity, so each step's error is free to add to the last.
A second conserved quantity makes the same point more directly. The angular momentum per unit mass, , is constant for any force that points at the origin. At the start it is , which equals for this orbit.
Before running: which method keeps at ? A leapfrog kick changes the velocity along , and , so a kick leaves alone. A drift changes the position along , and , so a drift leaves it alone too. Leapfrog should keep to rounding error. Euler moves the position and the velocity at the same time, from the old values; working out the product gives a change of per step, which is not zero.
Euler's angular momentum has grown by (euler_momentum_drift), about two fifths of its
starting value. Leapfrog's drift is at the level of rounding error,
(momentum_drift), and the test holds it under .
How the error shrinks with the step
A method is order p if its error shrinks like : halve the step and the error drops by a factor of . Leapfrog is second order, so half the step should give a quarter of the error. The cell below runs leapfrog again with 400 steps per orbit and measures from the two worst errors:
The measured order is (order). Twice the work per orbit buys four times the accuracy,
and the error stays bounded however long the run. Forward Euler is first order, and its error
keeps growing as the run gets longer.