1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
# Copyright (c) 2017 Weitian LI <liweitianux@live.com>
# MIT license
"""
Merger tree that represents the merging history of a cluster using
the binary tree data structure.
"""
import os
import pickle
import logging
from matplotlib.figure import Figure
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
logger = logging.getLogger(__name__)
class MergerTree:
"""
A binary tree that represents the cluster merging history.
Description
-----------
Merged (M0, z0, age0)
~~~~~~~~~~~~~~~~~~~~~
/ \
Main (M1, z1, age1) Sub (M2, z2, age2)
~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
* "Merged" is the merged cluster from "Main" and "Sub" at redshift
z1 (=z2) or cosmic time age1 (=age2).
M0 = M1 + M2, M1 > M2
* If "Sub" is missing, then this is an accretion event (not a merger).
Parameters
----------
data : dict
Data (e.g., mass, redshift, age) associated with this tree node.
main, sub : `~MergerTree`
Links to the main and sub (optional) clusters between which the
merger happens.
The ``sub`` cluster may be missing, which is regarded as an
accretion event rather than a merger.
"""
def __init__(self, data, main=None, sub=None):
self.data = data
self.main = main
self.sub = sub
def save_mtree(mtree, outfile, clobber=False):
"""
Pickle the merger tree data and save to file.
"""
if os.path.exists(outfile):
if clobber:
os.remove(outfile)
logger.warning("Removed existing file: {0}".format(outfile))
else:
raise OSError("Output file already exists: {0}".format(outfile))
pickle.dump(mtree, open(outfile, "wb"))
logger.info("Saved merger tree to file: {0}".format(outfile))
def read_mtree(infile):
mtree = pickle.load(open(infile, "wb"))
logger.info("Loaded merger tree from file: {0}".format(infile))
return mtree
def plot_mtree(mtree, outfile, figsize=(12, 8)):
"""
Plot the cluster merger tree.
TODO/XXX: This function needs significant speed optimization!
Parameters
----------
mtree : `~MergerTree`
The merger tree to be plotted
outfile : str
Output filename to save the plotted figure
figsize : tuple
The (width, height) of the plotting figure
"""
def _plot(tree, ax):
if tree is None:
return
if tree.main is None:
# Only plot a point for current tree node
x = [tree.data["age"]]
y = [tree.data["mass"]]
ax.plot(x, y, marker="o", markersize=1.5, color="black",
linestyle=None)
return
# Plot a point for current tree node
x = [tree.data["age"]]
y = [tree.data["mass"]]
ax.plot(x, y, marker="o", markersize=1.5, color="black",
linestyle=None)
# Plot a line from current tree node to its main node
x = [tree.data["age"], tree.main.data["age"]]
y = [tree.data["mass"], tree.main.data["mass"]]
ax.plot(x, y, color="blue")
if tree.sub:
# Plot a line between main and sub nodes
x = [tree.main.data["age"], tree.sub.data["age"]]
y = [tree.main.data["mass"], tree.sub.data["mass"]]
ax.plot(x, y, color="green", linewidth=1, alpha=0.8)
# Recursively plot the descendant nodes
_plot(tree.main, ax)
_plot(tree.sub, ax)
fig = Figure(figsize=figsize)
canvas = FigureCanvas(fig)
ax = fig.add_subplot(1, 1, 1)
ax.hold(True)
_plot(mtree, ax=ax)
ax.set_xlabel("Cosmic time [Gyr]")
ax.set_ylabel("Mass [Msun]")
ax.set_xlim((0, mtree.data["age"]))
ax.set_ylim((0, mtree.data["mass"]))
fig.tight_layout()
canvas.print_figure(outfile)
|