diff options
Diffstat (limited to 'fg21sim')
| -rw-r--r-- | fg21sim/extragalactic/clusters/cosmology.py | 230 | 
1 files changed, 230 insertions, 0 deletions
| diff --git a/fg21sim/extragalactic/clusters/cosmology.py b/fg21sim/extragalactic/clusters/cosmology.py new file mode 100644 index 0000000..a66ff66 --- /dev/null +++ b/fg21sim/extragalactic/clusters/cosmology.py @@ -0,0 +1,230 @@ +# Copyright (c) 2016-2017 Weitian LI <liweitianux@live.com> +# MIT license + +""" +Cosmological models +""" + +import logging + +import numpy as np +from scipy import integrate +import astropy.units as au +from astropy.cosmology import LambdaCDM, z_at_value + + +logger = logging.getLogger(__name__) + + +class Cosmo: +    """ +    Cosmological model. + +    Attributes +    ---------- +    H0 : float +        Hubble parameter at present day (z=0) +    Om0 : float +        Density parameter of matter at present day +    Ode0 : float +        Density parameter of dark energy at present day +    model : str +        Type of the current cosmological model: +        * open : Om0 < 1, Ode0 = 0 +        * closed : Om0 > 1, Ode0 = 0 +        * EdS (Einstein-de Sitter) : Om0 = 1, Ode0 = 0 +        * flatLambdaCDM : Om0 + Ode0 = 1, Ode0 > 0 +    """ + +    def __init__(self, H0=71.0, Om0=0.27, Ode0=None, sigma8=None): +        Ode0 = 1.0 - Om0 if Ode0 is None else Ode0 +        if (Ode0 > 0) and abs(Om0 + Ode0 - 1) > 1e-5: +            raise ValueError("non-flat LambdaCDM model not supported!") +        self.H0 = H0  # [km/s/Mpc] +        self.Om0 = Om0 +        self.Ode0 = Ode0 +        self._sigma8 = sigma8 +        self.cosmo = LambdaCDM(H0=H0, Om0=Om0, Ode0=Ode0) +        logger.info("Cosmological model: {0}".format(self.model)) + +    @property +    def model(self): +        if self.Ode0 < 1e-5: +            if self.Om0 < 1: +                model = "open" +            elif self.Om0 == 1: +                model = "EdS" +            else: +                model = "closed" +        else: +            model = "flatLambdaCDM" +        return model + +    @property +    def h(self): +        """ +        Dimensionless/reduced Hubble parameter +        """ +        return self.H0 / 100.0 + +    @property +    def sigma8(self): +        """ +        Present-day rms density fluctuation on a scale of 8 h^-1 Mpc. + +        References +        ---------- +        [1] Randall, Sarazin & Ricker 2002, ApJ, 577, 579 +            http://adsabs.harvard.edu/abs/2002ApJ...577..579R +            Sec.2 +        """ +        if hasattr(self, "_sigma8"): +            return self._sigma8 +        # +        if self.model == "open": +            sigma8 = 0.827 +        elif self.model == "closed": +            raise NotImplementedError +        elif self.model == "EdS": +            sigma8 = 0.514 +        elif self.model == "flatLambdaCDM": +            sigma8 = 0.834 +        else: +            raise ValueError("unknown model: {0}".format(self.model)) +        return sigma8 + +    @property +    def M8(self): +        """ +        Mass contained in a sphere of radius of 8 h^-1 Mpc. +        Unit: [Msun] +        """ +        r = 8 * au.Mpc.to(au.cm) / self.h  # [cm] +        M8 = (4*np.pi/3) * r**3 * self.rho_crit(0) +        return (M8 * au.g.to(au.solMass)) + +    def age(self, z): +        """ +        Cosmic time at redshift z. + +        Parameters +        ---------- +        z : float +            Redshift + +        Returns +        ------- +        age : float +            Age of the universe (cosmic time) at the given redshift. +            Unit: [Gyr] +        """ +        return self.cosmo.age(z).value + +    def redshift(self, age): +        """ +        Invert the above age calculation, to return the redshift +        corresponding to the given cosmic time. + +        Parameters +        ---------- +        age : float +            Age of the universe (cosmic time), unit [Gyr] + +        Returns +        ------- +        z : float +            Redshift corresponding to the input age. +        """ +        return z_at_value(self.age, age) + +    def rho_crit(self, z): +        """ +        Critical density at redshift z. +        Unit: [g/cm^3] +        """ +        return self.cosmo.critical_density(0).value + +    def OmegaM(self, z): +        """ +        Density parameter of matter at redshift z. +        """ +        return self.Om0 * (1+z)**3 / self.cosmo.efunc(z)**2 + +    def overdensity_virial(self, z): +        """ +        Calculate the virial overdensity, which generally used to +        determine the virial radius of a cluster. + +        References +        ---------- +        [1] Cassano & Brunetti 2005, MNRAS, 357, 1313 +            http://adsabs.harvard.edu/abs/2005MNRAS.357.1313C +            Eqs.(10,A4) +        """ +        if self.model == "open": +            raise NotImplementedError +        elif self.model == "closed": +            raise NotImplementedError +        elif self.model == "EdS": +            Delta_c = 18 * np.pi**2 +        elif self.model == "flatLambdaCDM": +            omega_z = (1 / self.OmegaM(z)) - 1 +            Delta_c = 18*np.pi**2 * (1 + 0.4093 * omega_z**0.9052) +        else: +            raise ValueError("unknown model: {0}".format(self.model)) +        return Delta_c + +    def overdensity_crit(self, z): +        """ +        Critical (linear) overdensity for a region to collapse +        at a redshift z. + +        References +        ---------- +        [1] Randall, Sarazin & Ricker 2002, ApJ, 577, 579 +            http://adsabs.harvard.edu/abs/2002ApJ...577..579R +            Appendix.A, Eq.(A1) +        """ +        if self.model == "open": +            raise NotImplementedError +        elif self.model == "closed": +            raise NotImplementedError +        elif self.model == "EdS": +            coef = 3 * (12*np.pi) ** (2/3) / 20 +            delta_c = coef * (self.age(0) / self.age(z)) ** (2/3) +        elif self.model == "flatLambdaCDM": +            coef = 3 * (12*np.pi) ** (2/3) / 20 +            D0 = self.growth_factor(0) +            D_z = self.growth_factor(z) +            Om_z = self.OmegaM(z) +            delta_c = coef * (D0 / D_z) * (1 + 0.0123*np.log10(Om_z)) +        else: +            raise ValueError("unknown model: {0}".format(self.model)) +        return delta_c + +    def growth_factor(self, z): +        """ +        Growth factor at redshift z. + +        References +        ---------- +        [1] Randall, Sarazin & Ricker 2002, ApJ, 577, 579 +            http://adsabs.harvard.edu/abs/2002ApJ...577..579R +            Appendix.A, Eq.(A7) +        """ +        if self.model == "open": +            raise NotImplementedError +        elif self.model == "closed": +            raise NotImplementedError +        elif self.model == "EdS": +            raise NotImplementedError +        elif self.model == "flatLambdaCDM": +            x0 = (2 * self.Ode0 / self.Om0) ** (1/3) +            x = x0 / (1 + z) +            coef = np.sqrt(x**3 + 2) / (x**1.5) +            integral = integrate.quad(lambda y: y**1.5 * (y**3+2)**(-1.5), +                                      0, x)[0] +            D = coef * integral +        else: +            raise ValueError("unknown model: {0}".format(self.model)) +        return D | 
