-
Notifications
You must be signed in to change notification settings - Fork 18
Tutorial: Traversing a merger tree
In Galacticus, merger trees are represented as a collection of interconnected "nodes" - each of which represents a dark matter halo and all of the content (stars, gas, etc.) contained within it. Nodes are connected to other nodes, having one or "child" nodes (those progenitors which exist at the prior timestep), a "parent" node (the node into which the current node will merge at the next timestep) and, possibly, a "sibling" node (the next "child" with the same "parent").
Often we need to traverse the tree, moving from one node to the next in a certain order to perform some calculation. This tutorial gives some examples of how to do this traversal.
A node in the tree is represented by an object of the treeNode
class.
Importantly, for tree traversal purposes, each node contains pointers to other nodes to define the tree structure. These pointers are:
-
firstChild
- a pointer to the first child node; -
parent
- a pointer to the parent node; -
sibling
- a pointer to the next child of the same parent. Not all all these pointers will actually point to anything - some nodes do not have a sibling for example. In those cases, the relevant pointer will benull
.
An example simple tree structure:
graph
1--firstChild-->2
2--parent-->1
3--parent-->1
2--sibling-->3
2--firstChild-->4
4--parent-->2
5--parent-->2
4--sibling-->5
3--firstChild-->6
6--parent-->3
7--parent-->3
6--sibling-->7
The treeNode
class also has some useful methods for tree traversal. For now we'll consider only one:
-
isPrimaryProgenitor()
- this returnstrue
if a node is the primary (most massive) progenitor of its parent - that is, doesnode
-->parent
-->firstChild
point back tonode
.
Suppose we are given a treeNode
object node
, and we want to visit each of its primary progenitors. To do this we create a worker treeNode
pointer that we can use to traverse the tree. Here's the example:
type(treeNode), pointer :: nodeWorker
! Begin by pointer our worker node to the node we have been given.
nodeWorker => node
! Begin a loop to visit each primary progenitor. We check that our worker node is associated, and exit the loop if it is not.
do while (associated(nodeWorker))
! Do whatever calculation we want here on the current primary progenitor node currently pointed to by `nodeWorker`.
.....
! Move to the next primary progenitor of the current worker node - this is found by just following the `firstChild` pointer.
nodeWorker => nodeWorker%firstChild
! Go back to the top of the loop.
end do
We begin by pointing our worker node at the original node
that we were given. We the enter a loop - in the loop condition we check if our node worker is associated()
- i.e. that it is not null
. This will allow us to exit the loop once no more primary progenitors are available. We next do whatever calculation we want to do on nodeWorker
, knowing that it points to a primary progenitor of the original node
. Then we simply move to the next primary progenitor by following the firstChild
pointer attached to nodeWorker
.
Suppose we are given a treeNode
object node
, and we want to visit each of its descendant halos along its branch, until that branch merges with another branch. To do this we create a worker treeNode
pointer that we can use to traverse the tree. Here's the example:
type(treeNode), pointer :: nodeWorker
! Begin by pointer our worker node to the node we have been given.
nodeWorker => node
! Begin a loop to visit each descendant. We check that our worker node is associated, and exit the loop if it is not.
do while (associated(nodeWorker))
! Do whatever calculation we want here on the current descendant node currently pointed to by `nodeWorker`.
.....
! Check if the current worker node is the primary progenitor of its parent.
if (nodeWorker%isPrimaryProgenitor()) then
! It is the primary progenitor, so simply move to the parent node.
nodeWorker => nodeWorker%parent
else
! It is not the primary progenitor, so this branch is about to merge into a larger branch. We don't want to follow this larger branch so we are finished. Nullify our worker node so that we will exit the loop.
nodeWorker => null()
end if
! Go back to the top of the loop.
end do
We begin by pointing our worker node at the original node
that we were given. We the enter a loop - in the loop condition we check if our node worker is associated()
- i.e. that it is not null
. This will allow us to exit the loop once no more descendants are available. We next do whatever calculation we want to do on nodeWorker
, knowing that it points to a descendant of the original node
. Then we check if the current worker node is the primary progenitor of its parent. If it is, we move to that parent by following the firstChild
pointer attached to nodeWorker
, otherwise we make nodeWorker
point to null
so that the loop will exit.
-
Tutorials
- Introduction to Galacticus parameter files
- Dark matter halo mass function
- Warm dark matter halo mass function
- Power spectra
- Warm dark matter power spectra
- Dark matter only merger trees
- Subsampling of merger tree branches
- Dark matter only subhalo evolution
- Solving the excursion set problem
- Reionization calculations
- Instantaneous & Non-instantaneous recycling
- Computing Broadband Stellar Luminosities
- Postprocessing of stellar spectra
- Using N-body Merger Trees
- Generating Mock Catalogs with Lightcones
- Constraining Galacticus parameters
- Generating galaxy merger trees
-
How Galacticus works
- Structure Formation Flowchart
- Merger Tree Building Flowchart
- How Galacticus Evolves Halos and Galaxies
- Galaxy Physics Flowchart
- CGM Cooling Physics Flowchart
- Star Formation Physics Flowchart
- Outflow Physics Flowchart
- Galactic Structure Flowchart
- CGM Physics Flowchart
- SMBH Physics Flowchart
- Subhalo Evolution Flowchart
-
Contributing
- Coding conventions
- Coding tutorials
-
Reference models
- Benchmarks and validation scores
- Validation plots and data