5.2. Admittance
Alice: We'd better start at the gate: at the very bottom of our code,
we can see how we make our entrance:
clop = parse_command_line(options_text)
World.admit($stdin, clop).evolve(clop)
The method World.admit can be found at the end of our World
class definition. Unlike most methods we have written so far, this
particular function is not an instance method, but a class method,
as indicated by the fact that the Class name is added here in front of it.
Bob: And we need this feature, otherwise we couldn't read in the data
for a World object. Just like the general class method new, the class
method admit can operate by itself, without having to be invoked from
an existing World object.
Alice: The first thing this method does is to invoke our acs_read function,
which returns an object called object, that can either be a World object
or a Worldsnapshot object.
In the first case, we are dealing with a complete output dump from a
previous run. Any invocation of world1.rb gives us the choice
to produce a snapshot or a complete World dump, as we will see in a
moment. In the case of a dump, the class of the object is World,
and since we're looking at a class method of class World, self
here is the same as World. In that case, the if statement evaluates
to true, and we invoke the method object.setup_from_world,
in other words, an instance method of class World, with the name
setup_from_world.
Now I'm puzzled. That last function sets up all kind of things, it seems,
performing a variety of initializations. But after that, World.admit
just returns object. What happens with that object, of type World?
Bob: Ah, the compactness of Ruby notation! On the same line where we
invoke World.admit, at the very end of the code, we then invoke
the member function evolve of the object that is returned.
Alice: Oh, yes, of course. Okay, what happens if we start with a snapshot
output? For example, we could start with a Plummer model input file. Or
we could take the output from a previous run, if we had decided to do
an output of a WorldSnapshot object. Back to World.admit.
Bob: Wait a minute. If we start with a Plummer model, the class of the
object that has been read in will be NBody, not WorldSnapshot. Wouldn't
the elsif statement in World.admit fail?
Alice: No, it won't. That's the magic that we've built into
acsio.rb, remember? In the first line, acs_read
tries to recognize either a World object or a WorldSnapshot object.
Now an NBody object certainly has nothing to do with the World class.
But We have defined the WorldSnapshot class as a subclass of NBody.
When acs_read sees this, it opens the gate for the Nbody object,
and it then reads it in as a WorldSnapshot object.
Bob: Ah yes, how clever we were, when we wrote that! Almost too clever,
I'm afraid. But it does all seem to work. Okay, so even in the case
of a completely fresh Plummer model input file, the elsif statement
will evaluate as true. Good. In that case we create a new World
object w, which we return so that it can be evolved.
Alice: Yes, but before doing so, there is quite a bit of work done in
the method setup_from_snapshot, more than in the World case.
Bob: The reason is that a World object is already all set to go, since
it came from a previous integration with the same code world1.rb.
If we start from a general NBody object, we'll have to do more book keeping.
For example, we have to compute the initial energy. You can see the call
to startup_and_report_energy, a method of the WorldEra class,
in the middle of setup_from_snapshot. In contrast, if we start
from a World object, we can safely assume that the initial energy value
has already been stored there.
5.3. Output Choice
Alice: I get the picture now; it all comes back. And corresponding to
the two ways of reading input, there are also two ways of writing output.
In the case of input, we did not need any command line option, since
we would recognize from the input offered what type of file we were
dealing with. But for doing output, a decision has to be made. This
we do with the option, well, . . . I've forgotten which option. Let's
ask Ruby to list, first, all the options, and then to explain what the
output switch really does:
|gravity> kali world1.rb -h
Individual Time Step, Individual Integration Scheme Code
-c --step_size_control : Determines the time step size [default: 0.01]
-e --era_length : Duration of an era [default: 0.01]
-m --max_timestep_param: Maximum time step (units dt_era) [default: 1]
-d --diagnostics_interval: Diagnostics output interval [default: 1]
-o --output_interval : Snapshot output interval [default: 1]
-t --time_period : Duration of the integration [default: 10]
-i --init_out : Output the initial snapshot
-r --world_output : World output format, instead of snapshot
-a --shared_timesteps : All particles share the same time step
--verbosity : Screen Output Verbosity Level [default: 1]
--acs_verbosity : ACS Output Verbosity Level [default: 1]
--precision : Floating point precision [default: 16]
--indentation : Incremental indentation [default: 2]
-h --help : Help facility
---help : Program description (the header part of --help)
|gravity> kali world1.rb --help -r
-r --world_output : World output format, instead of snapshot
If this flag is set to true, each output will take the form of a
full world dump, instead of a snapshot (the default). Reading in
such an world again will allow a fully accurate restart of the
integration, since no information is lost in the process of writing
out and reading in, in terms of world format.
Ah, yes, the -r option, or more descriptively, the
--world_output.
Bob: The longer option is easier to recognize, while the shorter option
is easier to remember to write.
Alice: I'm not sure about that last part. Anyway, we can use either one,
and the help facility can always remind us of both. And in the code,
the world_output_flag determines the behavior of the methods
output and init_output in the World class.
Bob: With the -r flag switched on, the output is really simple:
output does nothing else but invoke our standard output method
acs_write, writing out self, the current state of the
World object itself.
Without that flag, that is, in the default case, we first have to take
a snapshot, using @era.take_snapshot, and then we can write out
that snapshot in a similar way, with a call to acs_write.
It's time that we have a look at WorldEra, to see all that happens
there, behind the scenes. The only part that we can see here is we
create a new era in the first line of setup_from_snapshot,
and then ask World#evolve to invoke WorldEra#evolve,
through the call to @era.evolve in the second line of the
evolve method here.
Alice: Perhaps we should first look at WorldSnapshot, before we descend
through the lineage of WorldEra, WorldLine, and WorldPoint.