Exemplo n.º 1
0
    def delta_sigma(self, r, r_off=None):
        """Difference between the mean Sigma within a disk and its boundary.

        Parameters
        ----------
        r : float or array_like
            Projected radial distance from the halo center.
        r_off : float, optional
            Projected radial offset of the halo center. If provided, the
            computed Delta Sigma is the mean over all azimuthal angles, i.e.,
            averaged over the circle of radius r_off centered on the halo.

        Notes
        -----
        The calculation is much slower when r_off is given due to the
        numerical integration necessary.

        Value(s) are returned in units of solar mass per square parsec.

        """
        prefactor = conv(2 * self.rs * self.rho0, "solMass / pc2")
        r = np.atleast_1d(r)

        # Basic centered calculation (excluding the constant prefactor)
        def _delta_sigma(r):
            # Recast radius in units of rs
            x = (conv(r, self.rs.unit.to_string()) / self.rs).value
            return self._func2(x)

        # Off-centered Delta Sigma as a function of angle and offset radius
        def _delta_sigma_of_theta(theta, r, r_off):
            term1 = np.power(r, 2) + np.power(r_off, 2)
            term2 = 2 * r * r_off * np.cos(theta)
            return _delta_sigma(np.sqrt(term1 - term2))

        if r_off is None or r_off == 0:
            # Simple centered Delta Sigma
            result = _delta_sigma(r)
        else:
            # Average over all azimuthal angles
            r_off = conv(r_off, self.rs.unit.to_string())
            a, b = (0, 2 * np.pi)  # Integration limits
            integrals = [
                quad(_delta_sigma_of_theta, a, b / 2, args=(rr, r_off))[0]
                for rr in r
            ]
            result = 2 * np.array(integrals) / (b - a)

        # Clean up as necessary
        if len(result) == 1:
            result = result[0]

        return prefactor * result
Exemplo n.º 2
0
def lsq_fit(theta, gamma_t, err, z_halo, z_src, cosmology, model='nfw'):
    """Fit a halo profile to measured tangential shear data by least squares.

    Parameters
    ----------
    theta : array_like
        Angular distances (e.g. bin centers) from the halo/cluster center.
        Units of arcmin are assumed if not given as an astropy quantity.
    gamma_t : array_like
        Mean measured tangential shear at distances `theta`.
    err : array_like
        Error on `gamma_t`. This is typically the standard error of the mean
        in each `theta` bin.
    z_halo : float
        Redshift of the halo/cluster.
    z_src : float or array_like
        Effective redshift of source galaxies per `theta` bin. If only a
        single float is given, this value is used for all `theta` bins.
    cosmology : astropy.cosmology.core.Cosmology
        Assumed cosmological model in which the halo/cluster lives.
    model : {'nfw', 'bmo', 'einasto', 'sis'}, optional
        Halo model type. Currently only 'nfw' is supported, so it is default.
        See lenspack.halos.profiles for available options.

    Returns
    -------
    tuple of numpy arrays
        Best-fit parameters as ((c200, m200), cov), where cov is the 2x2
        covariance matrix.

    """
    # Check inputs
    assert len(theta) == len(gamma_t) == len(err), "Input lengths not equal."
    assert model in ('nfw', 'bmo', 'einasto', 'sis'), "Invalid model."

    # Convert angular theta to proper distance [Mpc]
    arcmin2mpc = conv(cosmology.kpc_proper_per_arcmin(z_halo), "Mpc / arcmin")
    r = conv(theta, 'arcmin') * arcmin2mpc

    def nfw_gamma_t(r, c200, m200):
        """Predicted shear profile of an NFW model."""
        halo = nfw_profile(z_halo, c200, m200=m200, cosmology=cosmology)
        # g_t = halo.gamma_t(r, z_src) / (1 - halo.kappa(r, z_src))
        return halo.gamma_t(r, z_src)

    def bmo_gamma_t(r, c200, m200):
        """Predicted shear profile of a BMO model."""
        # halo = bmo_profile(z_halo, c200, m200=m200, cosmology=cosmology)
        # return halo.gamma_t(r, z_src)
        pass

    # Add options here once the profiles are defined in lenspack.halo.profiles
    if model == 'nfw':
        model_gamma_t = nfw_gamma_t
    else:
        raise ValueError("Only the NFW model is currently supported.")

    # Fit the model
    p0 = (4, 5e14)  # Initial (c200, m200) values
    fit = curve_fit(model_gamma_t, xdata=r, ydata=gamma_t, sigma=err, p0=p0)

    return fit
Exemplo n.º 3
0
 def mass_enclosed(self, r):
     """Mass (3D) inside a sphere of a given radius."""
     r = conv(r, "Mpc")
     x = r / self.rs
     prefactor = 4 * np.pi * np.power(self.rs, 3) * self.rho0
     return conv(prefactor * (np.log(1 + x) - x / (1 + x)), "g")
Exemplo n.º 4
0
 def rho(self, r):
     """Density at a given distance from the halo center."""
     r = conv(r, "Mpc")
     return self.rho0 / (r / self.rs) / np.power(1 + r / self.rs, 2)
Exemplo n.º 5
0
 def _delta_sigma(r):
     # Recast radius in units of rs
     x = (conv(r, self.rs.unit.to_string()) / self.rs).value
     return self._func2(x)
Exemplo n.º 6
0
    def __init__(self, z, c200, m200=None, r200=None, cosmology='default'):
        """Navarro, Frenk, & White (1997) radial halo density profile.

        rho(r; rho0, rs) = rho0 / [(r / rs) * (1 + r / rs)^2],

        where rho0 can be written as the product delta_c * rho_crit. The more
        common (and useful) parameterization is (c200, m200) instead of
        (rho0, rs).

        Parameters
        ----------
        z : float
            Halo redshift. [dimensionless]
        c200 : float
            Halo concentration. [dimensionless]
        m200 : float, optional
            Spherical mass contained within a radius `r200`. [solar mass]
        r200 : float, optional
            Characteristic halo radius. [Mpc]
        cosmology : {'default', instance of `astropy.cosmology`}
            Cosmological model in which to calculate distances. The default
            is a `FlatLambdaCDM` object with parameters H0=70, Om0=0.3,
            Ob0=0.044, and Tcmb0=2.725. Alternatively, a custom cosmology can
            be provided as an instance of `astropy.cosmology`.

        Notes
        -----
        (1) Either `m200` or `r200` must be given to fully define the profile,
            and `m200` takes precedence over `r200` if both are given.
        (2) The reference background density implicitly used in the definition
            of `c200`, `m200`, and `r200` is the critical density at the
            redshift of the halo.

        """
        self.z = z
        self.c200 = c200

        # Background cosmological model
        if cosmology == 'default':
            self.cosmo = astropy.cosmology.FlatLambdaCDM(H0=70,
                                                         Om0=0.3,
                                                         Ob0=0.044,
                                                         Tcmb0=2.725)
        else:
            if not isinstance(cosmology, astropy.cosmology.core.Cosmology):
                raise TypeError("Invalid cosmology.")
            self.cosmo = cosmology

        # Reference background density
        rho_crit = self.cosmo.critical_density(z)

        # Either m200 or r200 must be provided
        assert (m200 is not None) or (r200 is not None)

        if m200 is not None:
            # Compute r200
            m200 = conv(m200, "solMass")
            r200 = np.power(3. * m200 / (800 * np.pi * rho_crit), 1. / 3)
            r200 = conv(r200, "Mpc")
        else:
            # Compute m200
            r200 = conv(r200, "Mpc")
            m200 = 800. * np.pi * rho_crit * np.power(r200, 3) / 3
            m200 = conv(m200, "solMass")
        self.m200 = m200
        self.r200 = r200

        # Standard NFW parameters
        fc = np.log(1. + self.c200) - self.c200 / (1. + self.c200)
        self.rs = self.r200 / self.c200
        self.delta_c = (200. / 3) * np.power(self.c200, 3) / fc
        self.rho0 = self.delta_c * rho_crit