Tracing Particles between Snapshots¶
The bridge
module has tools for connecting different
outputs which allows you to trace particles from one snapshot of the
simulation to another. Normally, matching up particle IDs between
different snapshots can be a pain, but bridge
does all
of this for you transparently.
In pynbody, a Bridge
is an object that links two snapshots
together. Once connected, a bridge object called on a specific subset
of particles in one snapshot will trace these particles back (or
forward) to the second snapshot. Constructing bridges is also very straight-forward
in most cases. The easiest way is to try it in action.
Since the pynbody test data does not include a simulation with two different outputs,
we will borrow the test_gadget
data from tangos to illustrate the point. Click
here to download.
Basic usage¶
Load the data at high and low redshift:
In [1]: import pynbody
In [2]: f1 = pynbody.load("tutorial_gadget/snapshot_018")
In [3]: f2 = pynbody.load("tutorial_gadget/snapshot_020")
Verify the redshifts:
In [4]: "f1 redshift=%.2f; f2 redshift=%.2f"%(f1.properties['z'], f2.properties['z'])
Out[4]: 'f1 redshift=0.48; f2 redshift=0.00'
Load the halo catalogue at low redshift:
In [5]: h2 = f2.halos()
Create the bridge object:
In [6]: b = f2.bridge(f1)
b
is now an Bridge
object that links
the two outputs f1
and f2
together. Note that you can either
bridge from f2
to f1
(as here) or from f1
to f2
and it
makes no difference at all to basic functionality: the bridge can be traversed
in either direction.
There are different
subclasses of bridge that implement the mapping back and forth in different ways,
and by default the bridge()
method will attempt to choose the best
possible option, by inspecting the file formats. However, if you
prefer, you can explicitly instantiate the bridge for yourself (see
below).
Passing a SubSnap
from
one of the two linked snapshots to b
will return a SubSnap
with the same particles in the other snapshot. So, if we want to see
where all the particles that are in halo 9 in the low-redshift
snapshot (f1
) came from at low redshift (f2
), we can simply do:
In [7]: progenitor_particles = b(h2[9])
progenitor_particles
now contains the particles which were in halos_1[9]
in the high redshift output. This will have been achieved by matching the
unique particle indexes, also known in pynbody as the iord
array. To verify,
In [8]: h2[9]['iord']
Out[8]: SimArray([1703383, 1637974, 1706077, ..., 2085209, 20825, 4440])
In [9]: progenitor_particles['iord']
Out[9]: SimArray([1703383, 1637974, 1706077, ..., 2085209, 20825, 4440])
In [10]: all(h2[9]['iord'] == progenitor_particles['iord'])
Out[10]: True
But of course the actual particle properties are different in the two cases, being taken from the two snapshots, e.g.
In [11]: progenitor_particles['x']
Out[11]:
SimArray([44.0088008 , 43.99995 , 44.06862841, ..., 46.11663313,
46.11094241, 46.1493806 ], 'Mpc a h**-1')
In [12]: h2[9]['x']
Out[12]:
SimArray([45.55277015, 45.56460556, 45.54647529, ..., 44.46931648,
44.53770257, 44.43297325], 'Mpc a h**-1')
Identifying halos between different outputs¶
You may wish to work out how a halo catalogue maps onto a halo catalogue for a different output. For this purpose a simple function, :func:`~pynbody.bridge.Bridge.match_catalog, is provided. Extending the example above, this would be called as follows:
In [13]: cat = b.match_catalog()
In [14]: cat
Out[14]:
array([-2, 17, 2, 3, 5, 7, 19, 9, 10, 12, 14, 13, -1, -1, 28, 15, -1,
27, 16, 20, 26, 22, 18, -1, -1, 21, -1, 23, 24, -1, -1])
The ith element of the returned array indicates that f1.halos()[cat[i]]
is
the major progenitor for f2.halos()[i]
. Negative
values indicate a halo that either does not exist (e.g. halo 0 in
SubFind catalogues) or that cannot be matched in the counterpart
catalogue.
If you want multiple matches per halo, e.g. to identify mergers, you
can usee fuzzy_match_catalog()
; see the reference documentation.
Which class to use?¶
There is a built-in-logic which selects the best possible subclass of
Bridge
when you call the method
bridge()
.
However, you can equally well choose the bridge and its options
for yourself.
For files where the particle ordering is static, so that the particle
with index i in the first snapshot also has index i in the second
snapshot, use the Bridge
class, as follows:
b = pynbody.bridge.Bridge(f1, f2)
For files which can spawn new particles, and therefore have a monotonically
increasing particle ordering array (e.g. “iord” in gasoline), use the
OrderBridge
class:
b = pynbody.bridge.OrderBridge(f1, f2)
Snapshot formats where the particle ordering can change require a more processor and memory intensive mapping algorithm to be used, which you can enable by asking for it explicitly:
b = pynbody.bridge.OrderBridge(f1, f2, monotonic=False)