Previous | ToC | Up | Next |
Alice: Hi, Bob! To judge from the look on your face, I would bet you found
a good recipe for a sixth-order Runge Kutta method.
Bob: As a matter of fact, yes, I did. But before I show it to you,
let me know, did you find out the history behind the mysterious formula
in Abramowitz and Stegun, that we have been sweating over so much?
Alice: Yes, indeed. It was presented in 1925 by a Finnish mathematician,
in a Finnish publication. Here is the full reference: Nystrom, E.J.,
1925, Acta Soc. Sci. Fenn., 50, No.13, 1-55. I dug it up in the library.
Bob: I'm impressed! I hope it was not written in Finnish?
Alice: No, it was written in German.
Bob: That wouldn't have helped me.
Alice: Fortunately, I had some German in high school, and I remembered
just enough to figure out the gist of the story.
Bob: But how did you find this article?
Alice: I first went to some text books on numerical methods. After some
searching, I learned that our mystery formula is an example of what
are called Runge-Kutta-Nystrom algorithms. It was Nystrom who made
the extension from normal Runge-Kutta methods to special treatments
for second-order differential equations. And there is a whole lot
more to the history. In recent years, quite a number of interesting
books have appeared that have a bearing on the general topic. I found
it hard to put them down.
Bob: Now that you got bitten by the bug, perhaps you'll be deriving new
methods for us, from scratch?
Alice: It's tempting to do so, just to see how it's done. Like having a
peak into the kitchen, rather than ordering from a menu. But not now.
Bob: No, let me show you first what I found on my menu, a Japanese menu
it turned out, which featured a sixth-order Runge-Kutta method prepared by
an astronomer working at the National Observatory in Tokyo, Haruo Yoshida.
Alice: Let me guess how long the code will be. The rk2 method was
five lines long, and the rk4 method was eight lines long. I seem to
remember that the complexity of Runge-Kutta methods increases rapidly with
order, so the new code must at least be twice as long, more likely a bit more.
Okay, I'll bet on twenty lines.
Bob: How much are you willing to bet?
Alice: I'm not much of a gambler. Let's say you'll get my admiration if
you did it in less than twenty lines.
Bob: How much admiration will I get if it would be less than ten lines?
Alice: I don't know, since I wouldn't believe you.
Bob: It that case you must be ready for a really wild move. How much are
you willing to bet against my having written a sixth-order Runge-Kutta code
in five lines?
Alice: Hahaha! I will bet a good laugh. You can't be serious.
Bob: I am.
Alice: Huh? You're not going to tell me that your sixth-order Runge-Kutta
is as long as the second-order Runge-Kutta?
Bob: Seeing is believing. Here it is, in the file yo6body.rb
Alice: Seven leapfrog calls? And that does it?
Bob: Yes, that does it, or so is the claim. It was invented by Yoshida,
a Japanese astronomer, around 1990, that's why I called it yo6
instead of rk6. I haven't gotten the time to
test it out yet. I just finished it when you came in.
Alice: I'm not sure whether to call this incredible, shocking, or what.
But before I look for the right adjective, let me first see that it does
what you claim it does.
Bob: I'm curious too. Let us take our good old Kepler orbit for a spin:
ten time units, as before. We will start with the fourth-order Runge-Kutta
method:
Alice: Significant, all right: almost machine accuracy. Can you give a
larger time step? Make it ten times as large! That may be way too large,
but we can always make it smaller again.
Bob: My pleasure!
Bob: Going around a rather eccentric Kepler orbits a few times, with only
a hundred steps in total? Well, we can always try.
Alice: All good things come to an end. We finally found the limit of your
new code, but only barely: while the error is of order unity, the system
hasn't really exploded numerically yet.
Bob: Let's change time step values a bit more carefully. How about
something in between 0.1 and 0.01. I'll take 0.04:
Bob: Here we go:
Bob: I'll halve the time step again, to 0.01:
Bob: This is almost spooky. The integrator has no right to converge
that fast.
6. A Sixth-Order Integrator
6.1. A Multiple leapfrog
6.2. Great!
|gravity> ruby integrator_driver2g.rb < euler.in
dt = 0.001
dt_dia = 10
dt_out = 10
dt_end = 10
method = rk4
at time t = 0, after 0 steps :
E_kin = 0.125 , E_pot = -1 , E_tot = -0.875
E_tot - E_init = 0
(E_tot - E_init) / E_init =-0
at time t = 10, after 10000 steps :
E_kin = 0.554 , E_pot = -1.43 , E_tot = -0.875
E_tot - E_init = -2.46e-09
(E_tot - E_init) / E_init =2.81e-09
1.0000000000000000e+00
5.9961758437074986e-01 -3.6063455639926667e-01
1.0308068733946525e+00 2.1389536225475009e-01
for which we found this reasonable result, with a relative accuracy in
the conservation of the total energy of a few in a billion.
Presumably the sixth-order method should do significantly better, for
the same time step value:
|gravity> ruby integrator_driver3a.rb < euler.in
dt = 0.001
dt_dia = 10
dt_out = 10
dt_end = 10
method = yo6
at time t = 0, after 0 steps :
E_kin = 0.125 , E_pot = -1 , E_tot = -0.875
E_tot - E_init = 0
(E_tot - E_init) / E_init =-0
at time t = 10, after 10000 steps :
E_kin = 0.554 , E_pot = -1.43 , E_tot = -0.875
E_tot - E_init = 1.72e-14
(E_tot - E_init) / E_init =-1.97e-14
1.0000000000000000e+00
5.9961755487188750e-01 -3.6063458346955279e-01
1.0308069102782800e+00 2.1389530415211538e-01
6.3. Too Good?
|gravity> ruby integrator_driver3b.rb < euler.in
dt = 0.01
dt_dia = 10
dt_out = 10
dt_end = 10
method = yo6
at time t = 0, after 0 steps :
E_kin = 0.125 , E_pot = -1 , E_tot = -0.875
E_tot - E_init = 0
(E_tot - E_init) / E_init =-0
at time t = 10, after 1000 steps :
E_kin = 0.554 , E_pot = -1.43 , E_tot = -0.875
E_tot - E_init = 1.23e-14
(E_tot - E_init) / E_init =-1.41e-14
1.0000000000000000e+00
5.9960497793690160e-01 -3.6065834429401844e-01
1.0308122043933747e+00 2.1385575804694398e-01
Alice: No! This is too good to be true. Still essentially machine
accuracy, for only a thousand steps, while rk2 with ten-thousand steps
did far worse, by a whopping factor of a hundred thousand. Well, let's
really push it: can you make the time step yet again ten times larger?
|gravity> ruby integrator_driver3c.rb < euler.in
dt = 0.1
dt_dia = 10
dt_out = 10
dt_end = 10
method = yo6
at time t = 0, after 0 steps :
E_kin = 0.125 , E_pot = -1 , E_tot = -0.875
E_tot - E_init = 0
(E_tot - E_init) / E_init =-0
at time t = 10, after 100 steps :
E_kin = 0.125 , E_pot = -0.605 , E_tot = -0.481
E_tot - E_init = 0.394
(E_tot - E_init) / E_init =-0.451
1.0000000000000000e+00
1.0619008919577413e+00 -1.2659613458920589e+00
-2.3237432724190762e-02 4.9855659376135231e-01
6.4. Spooky
|gravity> ruby integrator_driver3d.rb < euler.in
dt = 0.04
dt_dia = 10
dt_out = 10
dt_end = 10
method = yo6
at time t = 0, after 0 steps :
E_kin = 0.125 , E_pot = -1 , E_tot = -0.875
E_tot - E_init = 0
(E_tot - E_init) / E_init =-0
at time t = 10, after 250 steps :
E_kin = 0.513 , E_pot = -1.39 , E_tot = -0.879
E_tot - E_init = -0.00351
(E_tot - E_init) / E_init =0.00402
1.0000000000000000e+00
5.8605062658932228e-01 -4.1556477918009915e-01
1.0033546518756853e+00 1.4169619805243588e-01
Alice: Ah, good, less than a percent error in relative total energy
conservations. We're getting somewhere in our testing. Let's halve
the time step. If your yo6 method is really sixth-order,
halving the time step should make the result 64 times more accurate.
|gravity> ruby integrator_driver3e.rb < euler.in
dt = 0.02
dt_dia = 10
dt_out = 10
dt_end = 10
method = yo6
at time t = 0, after 0 steps :
E_kin = 0.125 , E_pot = -1 , E_tot = -0.875
E_tot - E_init = 0
(E_tot - E_init) / E_init =-0
at time t = 10, after 500 steps :
E_kin = 0.554 , E_pot = -1.43 , E_tot = -0.875
E_tot - E_init = -1.49e-07
(E_tot - E_init) / E_init =1.7e-07
1.0000000000000000e+00
5.9887919973409587e-01 -3.6203156818146032e-01
1.0311098923820705e+00 2.1157132982705190e-01
Alice: Hmm, that's much much more improvement than a factor 64.
I guess the run with a time step of 0.04 was not yet in the linear regime.
|gravity> ruby integrator_driver3b.rb < euler.in
dt = 0.01
dt_dia = 10
dt_out = 10
dt_end = 10
method = yo6
at time t = 0, after 0 steps :
E_kin = 0.125 , E_pot = -1 , E_tot = -0.875
E_tot - E_init = 0
(E_tot - E_init) / E_init =-0
at time t = 10, after 1000 steps :
E_kin = 0.554 , E_pot = -1.43 , E_tot = -0.875
E_tot - E_init = 1.23e-14
(E_tot - E_init) / E_init =-1.41e-14
1.0000000000000000e+00
5.9960497793690160e-01 -3.6065834429401844e-01
1.0308122043933747e+00 2.1385575804694398e-01
Alice: Again an improvement of far far more than a factor of 64. What
is going on?
Previous | ToC | Up | Next |