From b010b4c79d87f9364b2581fc0701e1dcfefd91fa Mon Sep 17 00:00:00 2001
From: Aaron LI <aly@aaronly.me>
Date: Wed, 19 Jul 2017 14:37:56 +0800
Subject: clusters/formation.py: Add method "_trace_main()"

Also update "simulate_megertree()" default to trace only the main cluster.

Signed-off-by: Aaron LI <aly@aaronly.me>
---
 fg21sim/extragalactic/clusters/formation.py | 87 +++++++++++++++++++++++++----
 1 file changed, 77 insertions(+), 10 deletions(-)

diff --git a/fg21sim/extragalactic/clusters/formation.py b/fg21sim/extragalactic/clusters/formation.py
index 6dbf58e..59c8cbe 100644
--- a/fg21sim/extragalactic/clusters/formation.py
+++ b/fg21sim/extragalactic/clusters/formation.py
@@ -169,23 +169,92 @@ class ClusterFormation:
         dS = self.cdf_K_inv(r, dw)
         return dS
 
-    def simulate_mergertree(self):
+    def simulate_mergertree(self, main_only=True):
         """
         Simulate the merger tree of this cluster by tracing its formation
         using the PS formalism.
 
+        Parameters
+        ----------
+        main_only : bool, optional
+            Whether to only trace the forming history of the main
+            halo/cluster.
+            (default: True)
+
         References: Ref.[1],Sec.(3.1)
         """
         logger.info("Simulating cluster formation: " +
-                    "M0={:.3g}[Msun] from z={:.2f} ...".format(
-                        self.M0, self.z0))
-        self.mtree = self._trace_formation(self.M0,
-                                           dMc=self.merger_mass_min,
-                                           _z=self.z0)
+                    "M0={:.3g}[Msun] from z={:.2f} to z={zmax} ...".format(
+                        self.M0, self.z0, zmax=self.zmax))
+        self.main_only = main_only
+        if main_only:
+            logger.info("Only trace the formation of the *main* cluster ...")
+            self.mtree = self._trace_main()
+        else:
+            logger.info("Trace formations of both main and sub cluster ...")
+            self.mtree = self._trace_formation(self.M0, _z=self.z0)
         logger.info("Simulated cluster formation with merger tree")
         return self.mtree
 
-    def _trace_formation(self, M, dMc, _z=None):
+    def _trace_main(self):
+        """
+        Iteratively trace the merger and accretion events of the
+        main cluster/halo.
+        """
+        # Initial properties
+        zc = self.z0
+        Mc = self.M0
+        mtree = MergerTree(data={"mass": Mc,
+                                 "z": zc,
+                                 "age": self.cosmo.age(zc)})
+
+        while True:
+            # Whether to stop the trace
+            if self.zmax is not None and zc > self.zmax:
+                break
+            if Mc <= self.merger_mass_min:
+                break
+
+            # Trace the formation by simulate a merger/accretion event
+            # Notation: progenitor (*1) -> current (*2)
+
+            # Current properties
+            w2 = self.f_delta_c(z=zc)
+            S2 = self.f_sigma(Mc) ** 2
+            dw = 0.5 * self.f_dw_max(Mc)
+            dS = self.gen_dS(dw)
+            # Progenitor properties
+            z1 = self.calc_z(w2 + dw)
+            age1 = self.cosmo.age(z1)
+            S1 = S2 + dS
+            M1 = self.calc_mass(S1)
+            dM = Mc - M1
+
+            M_min = min(M1, dM)
+            if M_min <= self.merger_mass_min:
+                # Accretion
+                M_main = Mc - M_min
+                # NOTE: no sub node
+            else:
+                # Merger event
+                M_main = max(M1, dM)
+                M_sub = M_min
+                mtree.sub = MergerTree(data={"mass": M_sub,
+                                             "z": z1,
+                                             "age": age1})
+
+            # Update main cluster
+            mtree.main = MergerTree(data={"mass": M_main,
+                                          "z": z1,
+                                          "age": age1})
+
+            # Update for next iteration
+            Mc = M_main
+            zc = z1
+            mtree = mtree.main
+
+        return mtree
+
     def _trace_formation(self, M, _z=None, zmax=None):
         """
         Recursively trace the cluster formation and thus simulate its
@@ -194,12 +263,10 @@ class ClusterFormation:
         z = 0.0 if _z is None else _z
         node_data = {"mass": M, "z": z, "age": self.cosmo.age(z)}
 
+        # Whether to stop the trace
         if self.zmax is not None and z > self.zmax:
-            # Stop the trace
             return MergerTree(data=node_data)
-
         if M <= self.merger_mass_min:
-            # Stop the trace
             return MergerTree(data=node_data)
 
         # Trace the formation by simulate a merger/accretion event
-- 
cgit v1.2.2