A corotating scan
A scan walks a synchronized binary inward through a series of separations, holding it in a corotating frame the whole way. If the walk is slow enough that the stars stay in equilibrium, every snapshot is an equilibrium configuration at its own separation, and the run traces a quasi-equilibrium sequence rather than an orbit.
That is useful when you want the structure of a binary as a function of separation. Where a star first fills its Roche lobe, and how its shape responds as the companion closes in, both become readable without waiting for an orbit to decay, and without letting the answer depend on how the system got there.
It is also an efficient way to build initial conditions. One scan produces a whole family of relaxed, synchronized binaries at known separations, and any of them can start a dynamical run. See Using a snapshot as initial conditions.
A scan is not itself a dynamical calculation. Nothing orbits, because the code places the two centres of mass where the schedule says they should be, every step.
What a scan run does
Setting nrelax=2 puts the calculation in a frame corotating with the binary
and adds the centrifugal acceleration. The angular frequency is not something
you supply. Each step getomega2 solves for the \(\omega\) that makes the
net force on the two centres of mass vanish:
omega2 = -sum m (x ax + y ay) / sum m (x^2 + y^2)
so the frame always rotates at the rate the current configuration requires. For an extended star this is not the point-mass Keplerian value, and it should not be, because the star’s own moment of inertia enters the denominator. On a giant with a close companion the difference reaches tens of per cent.
Then cmadj repositions both centres of mass onto the scheduled separation
along the x axis and zeroes their centre-of-mass velocities. The binary is
driven, not orbiting.
Note
nrelax=3 is the same frame with the Coriolis term added. Use it when the
gas is meant to move in the rotating frame. For a scan, where the point is to
keep everything as close to static as possible, nrelax=2 is what you want.
Setting one up
Start from a relaxed star, as produced by Relaxing a star. Bring its last snapshot in under the name the code expects:
cp ../relaxation/out1400.sph sph.start1u
sph.init selects the corotating setup:
&INITT
INAME='2cr'
&END
The second body can be either another relaxed star, copied to sph.start2u,
or a point mass. The absence of sph.start2u is what selects the point
mass: when the code cannot find that file it builds a single particle of mass
mbh, in units of munit, with smoothing length hco. There is no flag
to set.
A point mass is the right choice when the companion is small compared with the
smoothing lengths of the gas it will move through, since a resolved model of it
would buy nothing. Check that before assuming it, by comparing the companion’s
radius against the primary’s hp in the region the companion will reach.
Note
hco is fixed for the whole run. Particles with \(u = 0\) skip the
smoothing-length update in advance.f90, so unlike every other particle the
point mass keeps the length it was given. Choose it deliberately, because it
is the softening of the companion’s gravity for the rest of the calculation,
and it is inherited by any dynamical run started from these snapshots. Left
unset, it defaults to the smoothing length of the primary’s last particle,
which is a surface particle and typically far larger than you want.
The schedule
Two numbers set the geometry, sep0 and sepfinal, and one sets the timing,
tscanon. Before tscanon the separation is held at sep0, which gives
the star time to settle into the tidally distorted, synchronously rotating shape
the corotating frame demands. It was relaxed in isolation, so it does not arrive
in that shape. Several oscillation periods is a reasonable hold.
The scan itself is exponential in separation, not linear:
so the separation changes by a constant fraction per unit time. That is
usually what you want, since the tidal field scales with \(R/a\). A linear
form is present in advance.f90 but commented out. Do not be misled by it
when reading the source.
Important
tscanoff is not a setting. The code uses
so the scan ends when the run ends or when the drag switches off, whichever
comes first. Since treloff is also the moment the binary is released into
a dynamical calculation, a single run cannot both finish a scan and then sit
at the final separation. Choose one:
To trace the sequence and stop, set
treloffbeyondtf. The scan then spanstscanontotf, and the run never goes dynamical.To trace the sequence and then release it, set
treloffto the end of the scan andtfbeyond it.
For a settling phase at the final separation instead, see Resuming freezes the separation.
Note
For a corotating run that is not meant to scan at all, set sepfinal equal
to sep0. Leaving sepfinal at its default stops the run during setup,
which is the intended guard, and disabling the scan with a negative
tscanon alone is not enough: the separation would still jump to
sepfinal once t passed tscanoff.
Choosing the rate
The scan has to be slow compared with the star’s fundamental oscillation period, not merely compared with the run. A convenient way to state the rate is the fractional change per period, which the exponential form makes constant at \(1 - e^{-kP}\) for a period \(P\).
A few per cent per oscillation period is a reasonable starting point, and the cost of being wrong is one-sided. Too fast leaves the star chasing an equilibrium it never reaches, and the sequence is not quasi-static in any useful sense. Too slow only costs computer time. If in doubt, run a short fast scan first to confirm the mechanics, then commit.
The symptom of scanning too fast is the star ringing at the oscillation period with an amplitude that grows as the separation closes, rather than settling into a smoothly changing shape. Compare snapshots at the same separation from two scans at different rates. If they differ, the faster one was too fast.
Choosing the drag
The drag damps the oscillations that the changing tidal field excites, and that is what keeps the sequence quasi-static. A scan with the drag disabled will ring. A timescale near the star’s oscillation period is a sensible default, as it is for a single-star relaxation.
Setting trelax=0 asks for that timescale to come from the model. For a
corotating binary it is settled in initialize_corotating.f rather than in
relax.f, using star 1 alone, and it tries two things in order.
The value the relaxation used, if sph.start1u still carries one. The
drag timescale travels in the snapshot header, so a star dumped while its
relaxation was still under drag brings its own answer with it.
Otherwise star 1’s own radius, meaning the distance from that star’s centre
to its farthest particle, together with that star’s mass, giving
\(t_\mathrm{dyn} = \sqrt{R^3/M}\) and
\(t_\mathrm{relax} = 4\,t_\mathrm{dyn}\), the same rule a single-star
relaxation uses. The radius is measured before either star is moved out to
sep0, so it is the stellar radius and carries no trace of the separation.
Running the corotating example at sep0=8 and at sep0=800 returns the same
radius and the same trelax to every digit.
Note
The second branch is the usual one. A relaxation followed past treloff
switches its own drag off and records trelax=1.d30 in every snapshot from
then on, so the last dump of a properly released model carries a sentinel
rather than a timescale. Since the last dump is what you normally hand to
startfile1, expect the derivation rather than the inherited value, and
read log0.sph to see which one was used.
On a resumed run neither applies, because the setup routine does not run again. There the value settled the first time is read back from the dump header.
Important
trelax=0 does not set treloff here, though it does for a
single-star relaxation. That is deliberate. For a corotating binary
treloff also fixes tscanoff, so deriving it would let the star
silently redefine how long the scan lasts. Set treloff yourself.
Reading the output
energy0.sphThe last column is the current separation, so the file doubles as a record of the schedule actually followed. Check it against what you asked for before trusting anything else.
log0.sphRecords
omega2each step, andbinary separation ... at time=while the scan is running. Watchingomega2settle during the hold is the cleanest way to tell whether the star has finished adjusting to the corotating frame.out*.sphThe snapshots. With the schedule above, a snapshot’s separation follows from its time, so any snapshot can be placed on the sequence without opening it.
A scan stops itself when a particle strays past about 2.5 times either centre of
mass, reporting that it has overflowed an outer lagrangian point. One
particle is enough to end the run. A giant’s outermost particles sit far outside
the radius that holds most of its mass, so this fires well before the star as a
whole reaches its lobe. The scan described below ended this way at a separation
of 646, with the radius enclosing 99 per cent of the mass at 0.98 of the Roche
lobe, under one per cent of the mass outside the lobe, and the single outermost
particle at 3.9 times it. Read it as the limit on how far a scan can be pushed,
not as a measurement of contact.
Using a snapshot as initial conditions
Every out*.sph the scan writes is a relaxed, synchronized binary at a known
separation, and each one can start its own dynamical calculation. A single scan
therefore replaces a whole set of separate binary relaxations, one per
separation, which is usually the cheapest way to get a series of encounters that
differ only in how close the pair began.
Work in a fresh directory, so that the scan’s own output is not overwritten:
mkdir ../plunge_from_0525
cp out0525.sph ../plunge_from_0525/restartrad.sph
and in the new sph.input ask for a dynamical calculation:
nrelax=0,
treloff=0,
trelax=1.d30,
Important
treloff must be zero or negative, and this is the part that is easy to get
wrong, because the scan’s own sph.input carries a large one. Copied over
unchanged it breaks the release without stopping the run. With nrelax=0
the boost into the inertial frame never fires, and the code integrates
corotating-frame velocities as though they were inertial. With nrelax=3
the calculation never leaves the driven state, so cmadj keeps forcing the
separation and the outer Lagrangian abort stays armed.
Note also that trelax is the e-folding time of the drag, so switching the
drag off means making it huge rather than small. A small trelax damps the
star to a standstill in a few steps.
No sph.init is needed, because a run that finds restartrad.sph reads it
instead of building anything. Leaving it out is worth doing deliberately, so
that a missing restartrad.sph stops the run rather than quietly starting a
fresh scan.
Two lines in log0.sph look alarming and are not. did you mean to have
nrelax>=2? is printed whenever the dump was written by a corotating run and
the new one is not, which is exactly the case here, and the stop beneath it
is commented out. sep0 & sepfinal set to some tiny number follows from the
same cause: with nrelax below 2 every particle is counted into star 1, so
getcoms finds no second centre of mass and the separation it computes is
meaningless. Neither value is ever used, because cmadj only runs when
nrelax is 2 or more. Both lines are absent when releasing with
nrelax=3. The snapshot carries omega2 in its header, so
the code has everything it needs to turn the corotating configuration into an
orbiting one. It resets the clock and the snapshot counter, restores the
artificial viscosity coefficients, and gives every particle the orbital velocity
its position implies. log0.sph reports this as assuming a dynamical
calculation is starting, followed by main: going dynamic.
Important
The released orbit inherits the omega the corotating run had reached, not
the one the separation implies. Take a snapshot before the configuration has
settled and the pair starts out rotating too slowly to hold itself apart, so
the orbit is eccentric and the separation falls from the first step. Started
from a snapshot only 0.6 drag times into a relaxation, a test pair at a
separation of 8 came out turning at 6.50 degrees per unit time against the
7.30 a circular orbit wants, and closed to 7.31 within six time units.
Check that omega2 in log0.sph has flattened before picking a snapshot
to build on. During a scan it drifts as the separation changes, so what you
are looking for is a smooth trend rather than the transient of the first few
drag times.
Note
Confirm the separation of the snapshot you picked before building anything on
it, either from the last column of energy0.sph at that time or from the
schedule. Naming the new directory after the snapshot, as above, saves
working it out twice.
Important
tf has a floor you did not set. On a restart init.f runs
using the dump’s t, before t is reset to zero, and
\(t_{f,\mathrm{old}}\) is the tf the scan itself ran with. So any
tf below the dump’s own time is silently replaced by the scan’s. A
snapshot taken at \(t = 2.5\times10^{5}\) out of a scan that ran to
\(2.75\times10^{5}\) cannot be given a shorter run than that, whatever
tf says. Check the Reset final time to TF= line in log0.sph.
Which frame to release into is a choice, and the two are not the same physical
state. nrelax=0 overwrites every velocity with the rigid-rotation value its
position implies and zeroes the vertical component, so whatever residual motion
the corotating run still had is discarded. nrelax=3 keeps the velocities as
they stand and integrates in the rotating frame, but freezes omega2 at the
dump’s value, because getomega2 is guarded by gonedynamic. That frame is
corotating only while the separation stays near where the snapshot was taken.
The residual is small in a settled, detached snapshot and large in one taken during mass transfer, so the choice matters most exactly where the two runs would be most interesting to compare.
Resuming freezes the separation
Important
A scanning run cannot be resumed and carry on scanning. On restart,
init.f sees nrelaxold >= 2 and sets
freezing the separation wherever the run had reached. It says so in
log0.sph, but the run continues rather than stopping, so an interrupted
scan becomes a fixed-separation relaxation without ever failing.
Plan a scan to complete in one uninterrupted block, and check the separation
column of energy0.sph against the schedule before believing a resumed run.
This is deliberate, and it is the intended way to add a settling phase. Let the
scan finish, then restart it, and it relaxes at the separation it ended on. To
release that into a dynamical calculation, set treloff on the resumed run.
A worked example
A 5.29 \(M_\odot\) AGB giant, relaxed from a MESA profile with 99697 particles, scanned against a 0.4 \(M_\odot\) point mass from 1400 to 600 \(R_\odot\). The giant’s fundamental oscillation period is \(P = 1.1\times10^{4}\) in code units, which sets the whole schedule:
&input
tf=275000,
dtout=500,
tscanon=55000,
sep0=1400,
sepfinal=600,
nrelax=2,
trelax=10000,
treloff=1.d30,
startfile1='sph.start1u',
mbh=0.4,
hco=6.9,
nnopt=75,
nav=3, alpha=1, beta=2, ngr=3,
neos=1, gam=1.560d0,
runit=6.957d10,
munit=1.9884098706980504d33,
&end
That is a hold of 5 periods and a scan of 20, giving
\(k = 3.85\times10^{-6}\) and a separation falling by 4.1 per cent per
oscillation period. treloff sits above tf, so the scan runs to the end
of the run and the binary is never released.
Everything else in the file is carried over unchanged from the relaxation that
produced sph.start1u. That matters more than it looks. The star was relaxed
into equilibrium under a particular equation of state and neighbour number, and
changing either would leave it settling out of a shape it no longer fits.
log0.sph confirms the setup on startup:
init: new run, iname=2cr
corotating: sep0= 1400.0000000000000
particle 1 is a corepoint of mass 0.91193786057154913
mass1= 5.2938290052005037
n1= 99697
black_hole_mass 0.40000000000000002
mass2= 0.40000000000000002
Read the two masses and n1 every time. They are the cheapest confirmation
that the star you meant to scan is the one that was loaded, and that the point
mass came out at the mass you asked for.
Where to go next
A scan gives the sequence. It does not tell you whether the system would actually pass through it. A binary whose primary carries more spin angular momentum than a third of the orbital angular momentum has no stable synchronous state, so a real system would run away inward rather than move quasi-statically along the sequence. The sequence is still the right object to compute, since it is what the dynamical run should be compared against, but it is worth knowing which part of it corresponds to a configuration a binary could occupy.
To continue into the plunge, take a snapshot as described above, and see A collision for what a dynamical two-body run writes.