20.5. Checking Energy
Dan: What does STDERR mean, in front of the print statements?
Carol: That means that the information will be printed on the standard
error stream. By default, information will be printed on the standard
out stream. These are the two main output streams in Unix.
Dan: Why is it called error stream?
Carol: If you have a lot of output, you want to redirect that to a file.
But if something suddenly goes wrong, you would like to be warned about
that on the screen. You certainly don't want a warning message or error
message to be mixed in with all the other stuff in the output file; you
might never notice it there.
In our case, I would like to use this error channel to report on the
behavior of the energies. In fact, we want to determine the energy
errors, so it is somewhat appropriate to use the error stream, even
though the name suggests that it is normally used to report real errors.
But why not? We will use it here to report on small numerical errors.
Dan: So you report the values of the various energy contributions only
at the beginning and at the end of the run.
Carol: For now, that is good enough. At least it's a start. But let
me check to see whether all this works. We don't need the positions and
velocities for now, so I will redirect those to our waste basket
/dev/null
|gravity> ruby euler_energy_try1.rb > /dev/null
E_kin = 0.125, E_pot = -1; E_tot = -0.875
E_tot - E_init = 0, (E_tot - E_init) / E_init = -0
E_kin = 0.495, E_pot = -0.101; E_tot = 0.394
E_tot - E_init = 1.27, (E_tot - E_init) / E_init = -1.45
Dan: That does look pretty, I must say. But look, the energy is totally
different, at the beginning and at the end of the run.
Erica: As it should be: remember, this was our very first run, when we
used a time step that was too big to integrate an elliptic orbit! We made
a huge error at pericenter. In fact, we can now see that the energy changed
sign. We started with a bound orbit, with a total energy that was negative.
But at the end of the integration the energy has become positive!
That means that the particles can escape to infinity.
Carol: Why is that?
Erica: When the particles are very far away from each other, the potential
energy becomes negligible, and the energy is dominated by the kinetic energy.
Since kinetic energy cannot be negative, such a wide separation is impossible
if the total energy is negative. But for zero or positive total energy, there
is nothing that can prevent the two particles to fly away from each other
completely. And clearly, that is what happened here, as a result of numerical
errors.
Dan: Before drawing too many conclusions, we'd better check whether we
still are talking about the same orbit as we did before.
Carol: My pleasure. Here is what the old code gave:
|gravity> ruby euler_vector.rb > euler_vector.out
|gravity> head -1 euler_vector.out
1 0 0 0 0.5 0
|gravity> tail -1 euler_vector.out
7.6937453936572 -6.27772005661599 0.0 0.812206830641815 -0.574200201239989 0.0
And here is what the diagnostics produces, also at the very beginning and
end of the output file:
|gravity> ruby euler_energy_try1.rb > euler_energy_try1.out
E_kin = 0.125, E_pot = -1; E_tot = -0.875
E_tot - E_init = 0, (E_tot - E_init) / E_init = -0
E_kin = 0.495, E_pot = -0.101; E_tot = 0.394
E_tot - E_init = 1.27, (E_tot - E_init) / E_init = -1.45
|gravity> head -1 euler_energy_try1.out
1 0 0 0 0.5 0
|gravity> tail -1 euler_energy_try1.out
7.6937453936572 -6.27772005661599 0.0 0.812206830641815 -0.574200201239989 0.0
Dan: Good! Exactly the same.
20.6. Error Growth
Erica: We know that our first orbit integration produced large errors,
and we have quantified that by looking at the final energy error, at
the end of our orbit integration. But it would be a lot more instructive
to see how the energy error is growing in time.
Carol: Easily done: in file euler_energy_try2.rb, I will modify
our print_pos_vel method to include the total energy value as well,
calling it print_pos_vel_energy instead:
def print_pos_vel_energy(r,v,e0)
r.each{|x| print(x, " ")}
v.each{|x| print(x, " ")}
etot = energies(r,v).last
print (etot-e0)/e0
print "\n"
end
As you can see, I am printing the energy last, after the positions and
velocities. And of course, in the code I'm replacing the old name by
the new name in the two invocations, just before entering the loop and
at the end of the loop. Let's run it:
|gravity> ruby euler_energy_try2.rb > euler_energy_try2.out
E_kin = 0.125, E_pot = -1; E_tot = -0.875
E_tot - E_init = 0, (E_tot - E_init) / E_init = -0
E_kin = 0.495, E_pot = -0.101; E_tot = 0.394
E_tot - E_init = 1.27, (E_tot - E_init) / E_init = -1.45
|gravity> head -1 euler_energy_try2.out
1 0 0 0 0.5 0 -0.0
|gravity> tail -1 euler_energy_try2.out
7.6937453936572 -6.27772005661599 0.0 0.812206830641815 -0.574200201239989 0.0 -1.45027113997184
Erica: I don't like the way these numbers are rolling on and on.
We don't really need that much precision in the positions and velocities,
if we just want to make a pretty picture. Four or five digits should be
more than enough.
Carol: That's easy to fix. In file euler_energy_try3.rb
I will change the frequent output method as follows:
def print_pos_vel_energy(r,v,e0)
r.each{|x| printf("%.5g ", x)}
v.each{|x| printf("%.5g ", x)}
etot = energies(r,v).last
print (etot-e0)/e0
print "\n"
end
This should look better now:
|gravity> ruby euler_energy_try3.rb > euler_energy_try3.out
E_kin = 0.125, E_pot = -1; E_tot = -0.875
E_tot - E_init = 0, (E_tot - E_init) / E_init = -0
E_kin = 0.495, E_pot = -0.101; E_tot = 0.394
E_tot - E_init = 1.27, (E_tot - E_init) / E_init = -1.45
|gravity> head -1 euler_energy_try3.out
1 0 0 0 0.5 0 -0.0
|gravity> tail -1 euler_energy_try3.out
7.6937 -6.2777 0 0.81221 -0.5742 0 -1.45027113997184
20.7. Pericenter Troubles
Carol: Let's figure out what magic incantations gnuplot wants us to
give it, to plot the energy as a function of time. To start with, let
me remember how we have plotted the orbit. Ah yes, we have been using
the fact that gnuplot use the first two columns, if you don't specify
anything otherwise. Instead of relaying on that default choice, let's
plot the orbit again, this time giving gnuplot explicit instructions to
use the data from columns 1 and 2:
|gravity> gnuplot
gnuplot> set size ratio -1
gnuplot> plot "euler_energy_try3.out" using 1:2
gnuplot> quit

Figure 43: Trajectory of our very first forward Euler integration attempt.
So this gives figure 43, and indeed, it looks just
like before, when we produced figure 18.
Now to make Carol happy, we will plot the values of the total energy,
which reside in column 7.
Dan: But wait, that is different. First we were plotting y as a function
of x. Now you are going to plot the energy E as a function of what?
Of time, I guess.
Carol: Yes, that would be the most obvious choice. And because we are
using constant time steps, that boils down to plotting E as a function
of output number, if we number the output lines successively. And indeed,
gnuplot does have a way to use the output line number as the thing to plot
along the horizontal axis: if you specify the value 0 as a column number,
the output line number will be used.
Dan: Ah, that makes sense, and that is easy to remember. If you have
an output line that reads, say, in the first three columns:
8 20 3
4 5 6
9 2 1
...
then it is as if gnuplot itself adds the line numbers to the left:
1 8 20 3
2 4 5 6
3 9 2 1
...
and now the column numbering starts at 0 instead of at 1.
Carol: Yes, come to think of it, that must be the reason they
introduced that notation. Well, let me try:
|gravity> gnuplot
gnuplot> set size ratio -1
gnuplot> plot "euler_energy_try3.out" using 0:7
gnuplot> quit

Figure 44: Error growth for our very first forward Euler integration attempt.
Erica: Beautiful! Just as we expected, the main error is generated
when the two stars pass close to each other, at pericenter. But I
had not expected the error to be so sensitive to the distance between
the stars. The error is generated almost instantaneously!
Carol: Why would that be?
Erica: When the two stars come closer, the orbit becomes more curved,
and in addition, the speed becomes larger. So for both reasons, there
is more of a change in the orbit during a constant interval in time.
It would be great if we could use a smaller time step during pericenter
passage, and I'm sure we'll get to that point, later on. But for now,
as long as we are using constant time steps, a higher speed means that
each step will cover a larger distance in space. So we are in a
situation that we are actually taking longer steps in space exactly
there where the orbit is curved most.
Dan: Not a good thing to do.
Erica: I agree, but it's the simplest thing to do. We can later try
to be more clever.