def test_draw_cdm(self): n = 1 mtheory = integrate_power_law_analytic(self.norm, 10**self.log_mlow, 10**self.log_mhigh, n, self.plaw_index) m = self.func_cdm.draw() npt.assert_almost_equal(np.sum(m) / mtheory, 1, 2)
def test_integrate_mass_function(self): def _analytic_integral_bound(x, a, b, c, n): term1 = x**(a + n + 1) * ((c + x) / c)**(-b) * ((c + x) / x)**b term2 = hyp2f1(-b, a - b + n + 1, a - b + n + 2, -x / c) / (a - b + n + 1) return term1 * term2 norm = 1. m_low, m_high = 10**6, 10**10 log_mc = 0. a_wdm, b_wdm, c_wdm = 1., 1., -1.3 plaw_index = -1.8 for n in [0, 1]: integral = integrate_power_law_quad(norm, m_low, m_high, log_mc, n, plaw_index, a_wdm, b_wdm, c_wdm) integral_analytic = integrate_power_law_analytic( norm, m_low, m_high, n, plaw_index) analytic_integral = norm * (m_high**(1 + plaw_index + n) - m_low**( 1 + plaw_index + n)) / (n + 1 + plaw_index) npt.assert_almost_equal(integral / analytic_integral, 1, 4) npt.assert_almost_equal(integral_analytic / analytic_integral, 1, 5) log_mc = 8. a_wdm, b_wdm, c_wdm = 1., 1, -1.3 for n in [0, 1]: integral = integrate_power_law_quad(norm, m_low, m_high, log_mc, n, plaw_index, a_wdm, b_wdm, c_wdm) analytic_integral = _analytic_integral_bound(m_high, plaw_index, c_wdm, 10 ** log_mc, n) - \ _analytic_integral_bound(m_low, plaw_index, c_wdm, 10 ** log_mc, n) npt.assert_almost_equal(integral / analytic_integral, 1, 4) integral_analytic = integrate_power_law_analytic( norm, m_low, m_high, 0, -1) npt.assert_almost_equal(integral_analytic, norm * np.log(m_high / m_low))
def test_convergence_correction(self): idx = 20 z = self.lens_plane_redshifts[idx] dz = self.delta_zs[idx] log_mass_sheet_min = 8. # factor of two because the kappa sheets are added at every other lens plane norm, slope = self.rendering_class._normalization_slope(z, 2 * dz) mtheory = integrate_power_law_analytic( norm, 10**self.kwargs_cdm['log_mass_sheet_min'], 10**self.kwargs_cdm['log_mass_sheet_max'], 1., slope) mtheory_2 = integrate_power_law_analytic( norm, 10**log_mass_sheet_min, 10**self.kwargs_cdm['log_mass_sheet_max'], 1., slope) area = self.geometry.angle_to_physical_area( 0.5 * self.kwargs_cdm['cone_opening_angle'], z) kappa_theory = mtheory / self.lens_cosmo.sigma_crit_mass(z, area) kappa_theory_2 = mtheory_2 / self.lens_cosmo.sigma_crit_mass(z, area) kwargs_out, profile_names_out, redshifts = self.rendering_class.convergence_sheet_correction( ) kwargs_out_2, profile_names_out_2, redshifts_2 = self.rendering_class.\ convergence_sheet_correction({'log_mass_sheet_min': log_mass_sheet_min}) idx = np.where(redshifts == z)[0][0] kw = kwargs_out[idx] kw2 = kwargs_out_2[idx] name = profile_names_out[idx] npt.assert_equal(True, name == 'CONVERGENCE') kappa_generated = -kw['kappa'] kappa_generated_2 = -kw2['kappa'] npt.assert_almost_equal(kappa_theory, kappa_generated) npt.assert_almost_equal(kappa_theory_2, kappa_generated_2) npt.assert_equal(True, kw['kappa'] < 0.)
def __init__(self, log_mlow, log_mhigh, power_law_index, draw_poisson, normalization, log_mc, a_wdm, b_wdm, c_wdm): if a_wdm is None: assert b_wdm is None, 'If one of a_wdm, b_wdm, or c_wdm is not specified (None), all parameters must be None' assert c_wdm is None, 'If one of a_wdm, b_wdm, or c_wdm is not specified (None), all parameters must be None' else: assert b_wdm is not None, 'Must specify values for all three of a_wdm, b_wdm, c_wdm' assert c_wdm is not None, 'Must specify values for all three of a_wdm, b_wdm, c_wdm' if b_wdm is None: assert a_wdm is None, 'If one of a_wdm, b_wdm, or c_wdm is not specified (None), all parameters must be None' assert c_wdm is None, 'If one of a_wdm, b_wdm, or c_wdm is not specified (None), all parameters must be None' else: assert a_wdm is not None, 'Must specify values for all three of a_wdm, b_wdm, c_wdm' assert c_wdm is not None, 'Must specify values for all three of a_wdm, b_wdm, c_wdm' if c_wdm is None: assert a_wdm is None, 'If one of a_wdm, b_wdm, or c_wdm is not specified (None), all parameters must be None' assert b_wdm is None, 'If one of a_wdm, b_wdm, or c_wdm is not specified (None), all parameters must be None' else: assert a_wdm is not None, 'Must specify values for all three of a_wdm, b_wdm, c_wdm' assert b_wdm is not None, 'Must specify values for all three of a_wdm, b_wdm, c_wdm' if normalization < 0: raise Exception('normalization cannot be < 0.') if c_wdm is not None and c_wdm > 0: raise ValueError('c_wdm should be a negative number (otherwise mass function gets steeper (unphysical)') if a_wdm is not None and a_wdm < 0: raise ValueError('a_wdm should be a positive number for suppression factor: ' '( 1 + (a_wdm * m/m_c)^b_wdm)^c_wdm') if np.any([a_wdm is None, b_wdm is None, c_wdm is None]): assert log_mc is None, 'If log_mc is specified, must also specify kwargs for a_wdm, b_wdm, c_wdm.' \ '(See documentation in pyHalo/Rendering/MassFunctions/Powerlaw/broken_powerlaw' self._log_mc = log_mc self._a_wdm = a_wdm self._b_wdm = b_wdm self._c_wdm = c_wdm self.draw_poisson = draw_poisson self._index = power_law_index self._mL = 10 ** log_mlow self._mH = 10 ** log_mhigh self._nhalos_mean_unbroken = integrate_power_law_analytic(normalization, 10 ** log_mlow, 10 ** log_mhigh, 0, power_law_index)
def test_mass_rendered_line_of_sight(self): m_theory = 0 m_rendered = 0 for z, dz in zip(self.lens_plane_redshifts, self.delta_zs): m_rendered += self.realization_cdm_field.mass_at_z_exact(z) slope = self.halo_mass_function.plaw_index_z( z) + self.kwargs_cdm['delta_power_law_index'] norm = self.kwargs_cdm['LOS_normalization'] * \ self.halo_mass_function.norm_at_z_density(z, slope, self.kwargs_cdm['m_pivot']) * \ self.geometry.volume_element_comoving(z, dz) m_theory += integrate_power_law_analytic( norm, 10**self.kwargs_cdm['log_mlow'], 10**self.kwargs_cdm['log_mhigh'], 1, slope) ratio = m_theory / m_rendered npt.assert_array_less(1 - ratio, 0.05) m_theory = 0 m_rendered = 0 for z, dz in zip(self.lens_plane_redshifts, self.delta_zs): m_rendered += self.realization_wdm_field.mass_at_z_exact(z) slope = self.halo_mass_function.plaw_index_z( z) + self.kwargs_wdm['delta_power_law_index'] norm = self.kwargs_cdm['LOS_normalization'] * \ self.halo_mass_function.norm_at_z_density(z, slope, self.kwargs_wdm['m_pivot']) * \ self.geometry.volume_element_comoving(z, dz) m_theory += integrate_power_law_quad( norm, 10**self.kwargs_wdm['log_mlow'], 10**self.kwargs_wdm['log_mhigh'], self.kwargs_wdm['log_mc'], 1, slope, self.kwargs_wdm['a_wdm'], self.kwargs_wdm['b_wdm'], self.kwargs_wdm['c_wdm']) ratio = m_theory / m_rendered npt.assert_array_less(1 - ratio, 0.05)
def test_mass_rendered_subhalos(self): plaw_index = self.kwargs_cdm['power_law_index'] + \ self.kwargs_cdm['delta_power_law_index'] * self.kwargs_cdm['delta_power_law_index_coupling'] norm = normalization_sigmasub(self.kwargs_cdm['sigma_sub'], self.kwargs_cdm['host_m200'], self.lens_cosmo.z_lens, self.geometry.kpc_per_arcsec_zlens, self.kwargs_cdm['cone_opening_angle'], plaw_index, self.kwargs_cdm['m_pivot']) mtheory = integrate_power_law_analytic( norm, 10**self.kwargs_cdm['log_mlow'], 10**self.kwargs_cdm['log_mhigh'], 1., plaw_index) m_subs = 0. for halo in self.realization_cdm.halos: if halo.is_subhalo: m_subs += halo.mass ratio = mtheory / m_subs npt.assert_array_less(1 - ratio, 0.05) plaw_index = self.kwargs_wdm['power_law_index'] + \ self.kwargs_wdm['delta_power_law_index'] * self.kwargs_wdm['delta_power_law_index_coupling'] norm = normalization_sigmasub(self.kwargs_wdm['sigma_sub'], self.kwargs_wdm['host_m200'], self.lens_cosmo.z_lens, self.geometry.kpc_per_arcsec_zlens, self.kwargs_cdm['cone_opening_angle'], plaw_index, self.kwargs_wdm['m_pivot']) mtheory = integrate_power_law_quad( norm, 10**self.kwargs_wdm['log_mlow'], 10**self.kwargs_wdm['log_mhigh'], self.kwargs_wdm['log_mc'], 1., plaw_index, self.kwargs_wdm['a_wdm'], self.kwargs_wdm['b_wdm'], self.kwargs_wdm['c_wdm']) m_subs = 0. for halo in self.realization_wdm_subhalos.halos: if halo.is_subhalo: m_subs += halo.mass ratio = mtheory / m_subs npt.assert_array_less(1 - ratio, 0.1)
def test_convergence_correction(self): z = self.lens_cosmo.z_lens norm, slope = self.rendering_class_uniform._norm_slope() # factor of two because the kappa sheets are added at every other lens plane mtheory = integrate_power_law_analytic( norm, 10**self.kwargs_cdm['log_mass_sheet_min'], 10**self.kwargs_cdm['log_mass_sheet_max'], 1., slope) area = self.geometry.angle_to_physical_area( 0.5 * self.kwargs_cdm['cone_opening_angle'], z) kappa_theory = mtheory / self.lens_cosmo.sigma_crit_mass(z, area) kwargs_out, profile_names_out, redshifts = self.rendering_class_uniform.convergence_sheet_correction( ) kw = kwargs_out[0] name = profile_names_out[0] npt.assert_equal(True, name == 'CONVERGENCE') kappa_generated = -kw['kappa'] npt.assert_array_less(abs(kappa_theory / kappa_generated - 1), 0.05)
def convergence_sheet_correction(self, kwargs_mass_sheets=None): norm, slope = self._norm_slope() kw_mass_sheets = self._convergence_sheet_kwargs if kwargs_mass_sheets is not None: kwargs_mass_sheets.update(kw_mass_sheets) log_mass_sheet_correction_min, log_mass_sheet_correction_max = \ kw_mass_sheets['log_mass_sheet_min'], kw_mass_sheets['log_mass_sheet_max'] log_mass_sheet_correction_min, log_mass_sheet_correction_max = self._redshift_dependent_mass_range( self._zlens, log_mass_sheet_correction_min, log_mass_sheet_correction_max) kappa_scale = kw_mass_sheets['subhalo_mass_sheet_scale'] m_low, m_high = 10**log_mass_sheet_correction_min, 10**log_mass_sheet_correction_max delta_power_law_index = self._rendering_kwargs[ 'delta_power_law_index_coupling'] * self._rendering_kwargs[ 'delta_power_law_index'] power_law_index = self._rendering_kwargs[ 'power_law_index'] + delta_power_law_index if self._rendering_kwargs['log_mc'] is not None: mass_in_subhalos = integrate_power_law_quad( norm, m_low, m_high, self._rendering_kwargs['log_mc'], 1, power_law_index, self._rendering_kwargs['a_wdm'], self._rendering_kwargs['b_wdm'], self._rendering_kwargs['c_wdm']) else: mass_in_subhalos = integrate_power_law_analytic( norm, m_low, m_high, 1, power_law_index) if kw_mass_sheets[ 'subhalo_convergence_correction_profile'] == 'UNIFORM': area = self.geometry.angle_to_physical_area( 0.5 * self.geometry.cone_opening_angle, self._zlens) kappa = mass_in_subhalos / self.lens_cosmo.sigma_crit_mass( self._zlens, area) negative_kappa = -1 * kappa_scale * kappa kwargs_out = [{'kappa': negative_kappa}] profile_name_out = ['CONVERGENCE'] redshifts_out = [self._zlens] elif kw_mass_sheets['subhalo_convergence_correction_profile'] == 'NFW': if 'host_m200' not in self._rendering_kwargs.keys(): raise Exception( 'must specify host halo mass when using NFW convergence sheet correction for subhalos' ) Rs_host_kpc = self.spatial_distribution_model._rs_kpc rs_mpc = Rs_host_kpc / 1000 r_tidal_host_kpc = self.spatial_distribution_model.xtidal * Rs_host_kpc Rs_angle = Rs_host_kpc / self.geometry.kpc_per_arcsec_zlens r_core_angle = r_tidal_host_kpc / self.geometry.kpc_per_arcsec_zlens x = self.geometry.cone_opening_angle / Rs_angle / 2 eps_crit = self.lens_cosmo.get_sigma_crit_lensing( self._zlens, self._z_source) D_d = self.lens_cosmo.cosmo.D_A_z(self._zlens) denom = 4 * np.pi * rs_mpc**3 * (np.log(x / 2) + self._nfw_F(x)) rho0 = mass_in_subhalos / denom # solar mass per Mpc^3 theta_Rs = rho0 * (4 * rs_mpc**2 * (1 + np.log(1. / 2.))) theta_Rs *= 1 / (D_d * eps_crit * self.lens_cosmo.cosmo.arcsec) kwargs_out = [{ 'alpha_Rs': -kappa_scale * theta_Rs, 'Rs': Rs_angle, 'center_x': 0., 'center_y': 0., 'r_core': r_core_angle }] profile_name_out = ['CNFW'] redshifts_out = [self._zlens] else: raise Exception( 'subhalo convergence correction profile ' + str(kw_mass_sheets['subhalo_convergence_correction_profile']) + ' not recognized.') return kwargs_out, profile_name_out, redshifts_out