def Msph(r, z, dr=0., ref='200c', unit='Msun', r_unit='kpc'): """ Calculates the mass of a cluster with radius r, assuming it is spherical. Supported units are Msun (for M) and {Mpc,kpc} for r. """ contrast = int(ref[:-1]) # [rho] = Msun·Mpc⁻³ rho = contrast * cosmology.density(z, ref=ref[-1], unit='astro') # m = 4*pi/3 * rho*r**3 # --> r**3 = m / (4*pi/3) / rho if r_unit == 'kpc': r = r / 1000 m = rho * 4 * numpy.pi / 3 * r**3 # [M] = Msun if dr != 0.: dm = rho * 4 * numpy.pi * r**2 # [dM] = Msun # later: include alternative units if unit == 'Msun': if dr != 0: return m, dm else: return m
def rsph(m, z, dm=0., ref='200c', unit='Mpc'): """ Returns r, the radius that contains a density *contrast* times the critical density, assuming a spherical distribution, given a mass m, m = (4·pi/3)·r³·(contrast·rho_c) If an error on the mass is provided, also returns the error in r. *Input: m: the mass of the cluster, in solar masses. Can be an uncertainties.ufloat as well, in which case *r* will be returned with the same type. z: the redshift of the cluster dm: the uncertainty on the mass, in solar masses contrast: the density contrast with respect to the critical (or average) density of the Universe at redshift z. *Returns: r: the spherical radius, in kpc dr: the uncertainty on r, in kpc -- if dm is given """ contrast = int(ref[:-1]) # [rho] = Msun·Mpc⁻³ rho = contrast * cosmology.density(z, ref=ref[-1], unit='astro') # m = 4*pi/3 * rho*r**3 # --> r**3 = m / (4*pi/3) / rho r = (m / (4 * numpy.pi / 3) / rho) ** (1. / 3.) # [r] = Mpc if dm != 0. and type(m) != uncertainties.Variable: dr = 1 / (4 * numpy.pi) * 1 / (rho * r ** 2) * dm # [dr] = Mpc if unit == 'kpc': return 1e3 * r, 1e3 * dr if unit == 'Mpc': return r, dr if unit == 'kpc': return 1e3 * r if unit == 'Mpc': return r
def nfw(m, z, dm=0, ref_in='200c', ref_out='500c', c=1., err=1e-6, scaling='duffy08', full_output=False): """ Convert the mass of a cluster from one overdensity radius to another assuming an NFW profile. Parameters ---------- m : float mass, in units of solar mass z : float redshift dm : float (optional) mass uncertainty ref_in : {'2500c', '500c', '200c', '180c', '100c', '500a', '200a', '100a'} (default '200c') overdensity at which the input mass is measured. The last letter indicates whether the overdensity is with respect to the critical ('c') or average ('a') density of the Universe at redshift z. ref_out : {'2500c', '500c', '200c', '180c', '100c', '500a', '200a', '100a'} (default '500c') overdensity at which the output mass is measured. c : float (default 1) either a fixed concentration or a correction factor to the Duffy et al. relation (useful, e.g., for estimating uncertainties due to the c-M relation). See parameter duffy. err : float (default 1e-6) allowed difference for convergence scaling : {'duffy08', 'dutton14'} (optional) If given, use the corresponding concentration relation with a correction factor *c*. If False, the concentration is fixed to the value of *c*. Only possible if ref_in is either '200c' or '200a'. full_output : bool (default False) If True, also return the concentration used. Returns ------- m_out : float The mass at the output overdensity. If dm>0 then an uncertainty on this mass is also returned (m_out is then a tuple of length 2). c : float (optional) concentration. This is returned if full_output is set to True. """ #if ref_in != '200c': #return read_nfw(m, z, dm, ref_in=ref_in, ref_out=ref_out) if ref_in not in ('200a', '200c'): duffy = False if not numpy.iterable(m): m = numpy.array([m]) if not numpy.iterable(dm): dm = numpy.array([dm]) # iteratively calculate output mass def _mass(c, mx, scale, err): mx_out = 0 mass = mx while abs(mx_out/mass - 1) > err: mx_out = mass r_out = (3 * mx_out / (4 * numpy.pi * rho_out)) ** (1./3.) x = r_out / scale mass = mx * (numpy.log(1 + x) - x / (1 + x)) / \ (numpy.log(1 + c) - c / (1 + c)) return mass # density contrasts rho_in = int(ref_in[:-1]) * cosmology.density(z, ref=ref_in[-1]) rho_out = int(ref_out[:-1]) * cosmology.density(z, ref=ref_out[-1]) # radii r_in = conversions.rsph(m, z, ref=ref_in, unit='Mpc') if scaling in ('duffy08', 'dutton14'): c = c * scalings.cM(m, z, ref=ref_in, scaling=scaling) scale = r_in / c # mass and uncertainty (if defined) m_out = numpy.array([_mass(ci, mi, rs, err) for ci, mi, rs in izip(c, m, scale)]) if numpy.any(dm > 0): dm_hi = numpy.array([_mass(ci, mi+dmi, rs, err) for ci, mi, dmi, rs in izip(c, m, dm, scale)]) dm_lo = numpy.array([_mass(ci, mi-dmi, rs, err) for ci, mi, dmi, rs in izip(c, m, dm, scale)]) m_out = (m_out, (dm_hi+dm_lo)/2) if full_output: return m_out, c return m_out