Running a simulation
Two files control a run. sph.init chooses the kind of calculation, and
sph.input sets everything else. Both must be present.
The minimum sph.init is three lines, for example:
&INITT
INAME='erg'
&END
INAME is a three-letter code naming the setup routine. Two of them cover
most work:
ergBuild a star from a stellar-evolution profile, a MESA model for instance, and relax it into an SPH star. This is where nearly every project starts, and it is the subject of Relaxing a star.
hypPut two already-relaxed bodies on a Keplerian orbit and let them meet: a collision or fly-by between two stars, or between a star and a compact object. See A collision.
The usual sequence is therefore two runs: an erg run per star to make it,
then one hyp run to collide them.
The other thirteen initialization scripts
Code |
Sets up |
|---|---|
|
a single polytrope, from |
|
a single polytrope with a compact core |
|
a corotating binary |
|
a binary plus a single star |
|
a binary plus a black hole |
|
a binary encountering a single star |
|
a star whose particle-mass scheme varies with position |
|
a rescaling of an existing model |
|
a triple system |
|
an encounter with a supermassive black hole |
|
a general-relativistic setup |
|
a restart from an existing dump |
|
particles laid out from an ASCII image |
sph.init gives the same list with the routine each one calls.
Each code selects one initialization script, and they live in files named
initialize_*.f in parallel_bleeding_edge/src: initialize_parent.f
for erg, initialize_hyperbolic.f for hyp, and so on.
Which script you choose also decides which other files the run needs: a
stellar-evolution profile for erg, a relaxed star or two for hyp. See
Input files for what those are and where they come from.
The important thing about these routines is how little they do. A setup routine
runs once, before the first step, and its only job is to decide where the
particles start: their positions, masses, velocities and internal energies. It
then hands that state to the integrator and takes no further part. Everything
after that is the same code regardless of which INAME you chose: the
hydrodynamics, the gravity, the timestep control, and what gets written to
disk.
Two consequences are worth keeping in mind. A setting that controls the
evolution means the same thing for every INAME, so advice about cn1 or
nnopt carries across unchanged. A setting that describes the starting
state is read only by the routine that needs it, so e0 does nothing in a
relaxation and equalmass does nothing in a collision, and neither will warn
you.
See sph.input for every setting and its default.
Choosing the number of ranks
mpirun -np 8 ./my_run_gpu_sph
Worth testing on your own machine, but sixteen ranks is often a sensible ceiling. Beyond that, communication costs eat into the gains, and leaving cores free lets you run several simulations at once.
ngravprocs sets how many ranks compute gravity. It cannot exceed the number
of GPUs present: ask for more and it is clamped to what you have. Leave it at
0 to have it detected automatically.
GPU or CPU
make builds the GPU version and make cpu the CPU one. The GPU takes
over only the gravity: neighbour finding and the SPH sums run on the CPU either
way. That is why the two are indistinguishable on a small model and diverge
sharply on a large one. Gravity is the part that grows fastest with the number
of particles, and it is the only part the card touches.
What was measured
Relaxing a one-solar-mass polytrope, INAME='erg' aside, with:
&input
n=N, nnopt=22, tf=1.0, dtout=1000.0, nrelax=1, treloff=0,
starmass=1.0, starradius=1.0,
&end
on four MPI ranks, on one machine: an RTX 5080 with the CPU build using all
four ranks for gravity. Both builds were checked to produce the same answer,
with identical W, U and total energy to every digit written, so the two
columns are the same work, not merely similar work.
\(N\) |
Iterations |
GPU, per step |
CPU, per step |
CPU/GPU |
CPU, whole run |
|---|---|---|---|---|---|
1 000 |
23 |
82 ms |
75 ms |
0.9 |
1.7 s |
4 000 |
30 |
68 ms |
69 ms |
1.0 |
2.1 s |
16 000 |
43 |
68 ms |
171 ms |
2.5 |
7.4 s |
64 000 |
64 |
129 ms |
1817 ms |
14 |
116 s |
128 000 |
80 |
225 ms |
7104 ms |
32 |
568 s |
The per-step columns are the comparison worth making. The iteration count is
not a constant of the experiment: every run above stops at the same tf, but
a bigger model takes shorter timesteps and so needs more of them, 23 steps at
\(N=1000\) against 80 at \(N=128\,000\). A table of total wall times
alone would mix that in with the cost of a step and show neither clearly.
What this means in practice
Below about \(N=4000\) it makes no difference. The GPU is fractionally slower, because there is not enough work to cover the cost of moving it to the card. By \(N=16\,000\) the CPU is taking two and a half times as long, and by \(N=128\,000\) it is thirty times.
So a relaxation of a few thousand particles is perfectly comfortable without a card, and the tutorials in this documentation are sized to run either way. A production model of \(10^5\) particles is not: the same job that occupies a GPU for a few minutes will occupy the CPU build for hours.
These figures are one machine and one kind of run. The protocol is written above so you can repeat it on yours rather than trusting the numbers, and it is worth doing before committing to a long job.
Warning
If you have built both versions, ./*_sph matches two files and the shell
hands you the alphabetically first, which is the CPU build. It runs, just
more slowly, and nothing says so. The give-away is the first lines of
output: the GPU build reports SPHgrav found 1 CUDA devices and
is running on ... with gpu 0, and the CPU build reports neither.
Restarting
A restartrad.sph file is written every few iterations, overwriting the
previous one. If one is present when a run starts, it is used automatically.
You can also restart from any snapshot by renaming it to restartrad.sph.
Note
Output files are numbered per stage, not per rank. A fresh run writes
log0.sph and energy0.sph, the first resume writes log1.sph and
energy1.sph, and so on. A resumed run does not append to
energy0.sph, which matters if you are parsing an energy trace across a
restart.