There are six command line options, Unix style, from which we can choose.
All essential options have default values, so it is perfectly possible to
run our code without specifying any of them. For example, if we start
with an -body snapshot in an input file data.in, we can run the
code to produce a stream of snapshot data in the output file data.out,
by typing:
|gravity> nbody_sh1 < data.in > data.out
This will have the exact same effect as if we had specified the default values for the four main options, namely the time step control parameter (0.03), the interval between diagnostics output (1 time unit), the interval between output of snapshots (1 time unit), and the duration of the integration (10 time units):
|gravity> nbody_sh1 -d 0.03 -e 1 -o 1 -t 10 < data.in > data.out
If we like to have three times smaller time steps, twice as many diagnostics outputs and with additional information, snapshot output intervals of 5 time units but starting at t = 0, and a total run time of 30 time units, we have to give the following command:
|gravity> nbody_sh1 -d 0.01 -e 0.5 -x -o 5 -i -t 30 < data.in > data.out
The order of the arguments is unimportant, but each option that
expects a value (the -d, -e, -o, -t options) should be
immediately followed by its corresponding value. By the way, the
value 0.03 as the default for the scale of the time step parameter
is somewhat arbitrary. In practice, a value of 0.1 is often
found to be too large, while 0.01 is often overkill. For example,
when we start from the initial conditions for three stars on a figure
8 orbit, running nbody_sh1 with all default values in place, we
wind up at time t = 10 with a relative energy error of order .
Of course, the optimal choice of values depend strongly on the particular application, and the default values are only a hint, in a blind attempt to come up with at least somewhat reasonable starting values. It is up to the user to make sure that these values are appropriate in a given situation, and if not, to supply a better value after some experimentation.
The help option can be invoked by typing:
|gravity> nbody_sh1 -h
This will not result in program execution, only in the printing of a short message that lays out the various command line option choices. A similar message will appear when we attempt to supply an non-existent option, for example:
|gravity> nbody_sh1 -q nbody_sh1: invalid option -- q usage: nbody_sh1 [-h (for help)] [-d step_size_control_parameter] [-e diagnostics_interval] [-o output_interval] [-t total_duration] [-i (start output at t = 0)] [-x (extra debugging diagnostics)] |gravity>
All this behavior can be inspected in the function read_options():
Note that the six variables corresponding to the command line arguments are all passed by reference, so that the results are available to the calling program main().
The function getopt() is a standard C library function that can be used equally well in C++ programs. Its third argument is a string which lists all command line options. Each option can only consist of a single letter. Those letters that should be followed by a value to be read in are indicated by a colon immediately following the letter. The string "hd:e:o:t:ix" tells us that options h, i and x do not expect additional values, while options d, e, o and t are to be followed with an argument, all of which are of type real in our particle case. All option arguments are by default passed as ASCII strings, so we need the function atof() to convert the ASCII information into the proper floating point value, as we already saw in the previous chapter.
Notice that each case in the body of the switch statement is ended by either a return statement or a break statement. The latter is necessary, since the default behavior of switch is to `fall through' from one case to the next, something that is clearly not desirable here. After we jump out of the switch statement through a break command, we encounter the last statement, ``return true;'' which tells the calling program that all is well, and that execution can continue.