next up previous contents
Next: 7.3 A More Modular Up: 7. A General -Body Previous: 7.1 A Wish List

7.2 A Standard $N$-Body Snapshot Format

Let us take a look at the last code from Part 1, hermite2.C. There we had the initial conditions for the three stars on a figure-8 orbit included at the beginning of the code. Clearly, this is not a very flexible approach. It would be much better to write one $N$-body code which can start from an arbitrary $N$-body snapshot, for an arbitrary number of particles $N$.

Before we adapt our code, we first have to agree upon a unique format for a realization of an $N$-body system. If we stick with the same standard format, we can build a suite of programs that are compatible in that they all can read and write $N$-body snapshots in the same way. Some programs can set up initial conditions, others integrate the orbits, and yet others can analyze the resulting data.

We will start with the following simple format, which is good enough for our current purposes. Later we will introduce extensions when needed. On the first line we will print one number, an integer, $N$, the number of particles. On the second line we will print the time $t$. These two lines will be followed by $N$ lines, one for each particle. Each of those lines will contain seven numbers: the mass of the particle, followed by the three Cartesian components of the position vector, followed similarly by the three components of the velocity vector.

For example, to store the initial conditions for the figure-8 orbits in a file, we need to write the following five lines:

3
0
1 0.9700436 -0.24308753 0 0.466203685 0.43236573 0
1 -0.9700436 0.24308753 0 0.466203685 0.43236573 0
1 0 0 0 -0.93240737 -0.86473146 0

The simplest way to read an $N$-body snapshot is to obtain the data from the standard input stream cin. For example, when we put the above five lines in a file figure8.in, we can then run our new Hermite version as hermite3 < figure8.in.

Let us see how many changes we have to make to hermite2.C in order to make it compatible with our new standard data format. The first problem is that we can no longer use this stream to obtain the parameters for the integrator. In hermite2.C we allowed the user to provide those parameters in the following lines:

    cerr << "Please provide a value for the time step" << endl;
    cin >> dt;
    cerr << "and for the duration of the run" << endl;
    cin >> t_end;

An alternative is to write the parameters as command line arguments. C++ allows us to add the following optional arguments to main():

int main(int argc, char *argv[])

When we run the program, argc, the argument counter, will contain the number of arguments specified, while argv[], the vector of arguments, will contain the command line options. For example, if we want to run hermite3 with a time step of dt = 0.001 for a total duration of t_end = 10, we could communicate this to the program by invoking it as:

hermite3 0.001 10 < figure8.in

As soon as our program starts its execution, entering main(), the argument counter will be set to 3, since by convention the program name is the first argument. The argument vector will contain the following values:

argv[0] = "hermite3"
argv[1] = "0.001"
argv[2] = "10"

At this point, the parameters are available for the program, but they are still written in the form of character strings. Fortunately, there are convenient library function that will convert such a string into a number. They are called st atoi for a conversion of an ASCII string to an integer, and st atof for a conversion of an ASCII string to an floating point number. We can get access to those functions by adding the line #include <cstdlib> to the beginning of the file.

Below is the code listing. The changes from hermite2.C are straightforward: as soon as we have determined the values of the command line arguments, we know the value of $N$ and we can allocate space for the $N$ particles. Since $N$ is not known at compile time, we have to explicitly allocate space for the arrays, using the new operator. For two-dimensional arrays, this requires the length of the second array dimension to be known at compile time; fortunately that is no problem for us, since its value is known to be 3, the number of spatial dimensions in our world. Note the placement of the parentheses around * r, etc., necessary because the array brackets [] have a higher precendence as operators than the * operator. Omitting the parentheses, as in double *r[3], would be interpreted by the compiler as double *(r[3]), which is not what we want. By the way, strictly speaking we should deallocate the dynamically created arrays at the end of our main() program. However, since at that point the program exits and releases all its memory, we omit that here. In general it is good form to use a delete statement for every new statement used, in order to free the memory allocated. We will make this a habit starting in the next chapter.

The only other difference with hermite2.C is that we have to replace the value m for the mass which was shared for all particles by m[i] for the individual mass of particle i.



\begin{Code}[hermite3.C]
\small\verbatiminput{chap7/hermite3.C} \end{Code}


next up previous contents
Next: 7.3 A More Modular Up: 7. A General -Body Previous: 7.1 A Wish List
Jun Makino
平成18年10月10日