def test_profile_normalization(self): """ Test that the mass enclosed within r200 of the composite profile is correct and check that the ULDM core density is correct. """ profile_args = {'log10_m_uldm': -21, 'uldm_plaw': 1/3, 'scale_nfw':True} mass = 1e10 zl = 0.5 zs = 1.5 single_halo = SingleHalo(mass, 0.5, 0.5, 'ULDM', zl, zl, zs, None, True, profile_args, None) _, _, kwargs_lens, _ = single_halo.lensing_quantities(add_mass_sheet_correction=False) Rs_angle, _ = single_halo.halos[0].lens_cosmo.nfw_physical2angle(mass, single_halo.halos[0].c, zl) sigma_crit = single_halo.halos[0].lens_cosmo.sigmacrit r200 = single_halo.halos[0].c * Rs_angle cnfw_kwargs, uldm_kwargs = kwargs_lens M_nfw = CNFW().mass_3d_lens(r200, cnfw_kwargs['Rs'], cnfw_kwargs['alpha_Rs']*sigma_crit, cnfw_kwargs['r_core']) M_uldm = Uldm().mass_3d_lens(r200, uldm_kwargs['kappa_0']*sigma_crit, uldm_kwargs['theta_c']) npt.assert_almost_equal((M_uldm+M_nfw)/mass,1,decimal=2) # less than 1% error _,theta_c,kappa_0 = single_halo.halos[0].profile_args rho0 = Uldm().density_lens(0,uldm_kwargs['kappa_0'], uldm_kwargs['theta_c']) rhos = CNFW().density_lens(0,cnfw_kwargs['Rs'], cnfw_kwargs['alpha_Rs'], cnfw_kwargs['r_core']) rho_goal = Uldm().density_lens(0,kappa_0,theta_c) npt.assert_array_less(np.array([1-(rho0+rhos)/rho_goal]),np.array([0.03])) # less than 3% error
class TestUldm(object): """ tests the Gaussian methods """ def setup(self): self.model = Uldm() def test_function(self): r = np.linspace(start=0.01, stop=2, num=10) kappa_0 = 0.1 theta_c = 5 f_ = self.model.function(r, 0, kappa_0, theta_c) delta = 0.0001 f_d = self.model.function(r + delta, 0, kappa_0, theta_c) f_x_num = (f_d - f_) / delta f_x, _ = self.model.derivatives(r, 0, kappa_0, theta_c) npt.assert_almost_equal(f_x_num, f_x, decimal=3) # Try MSD limit theta_c_large = 10 f_reference = self.model.function(0, 0, kappa_0, theta_c_large) f_large = self.model.function(r, 0, kappa_0, theta_c_large) f_MSD = 0.5 * kappa_0 * r**2 npt.assert_almost_equal(f_large - f_reference, f_MSD, decimal=3) def test_derivatives(self): x = 0.5 y = 0.8 r = np.sqrt(x**2 + y**2) kappa_0, theta_c = 0.2, 9 # Trying MSD limit f_x, f_y = self.model.derivatives(x, y, kappa_0, theta_c) alpha_MSD = kappa_0 * r npt.assert_almost_equal(f_x, alpha_MSD * x / r, decimal=3) npt.assert_almost_equal(f_y, alpha_MSD * y / r, decimal=3) def test_hessian(self): x = np.linspace(start=0.01, stop=100, num=100) y = 0 r = np.sqrt(x**2 + y**2) kappa_0 = 0.12 theta_c = 6 f_xx, f_yy, f_xy = self.model.hessian(x, y, kappa_0, theta_c) kappa = 1. / 2 * (f_xx + f_yy) kappa_direct = self.model.kappa_r(r, kappa_0, theta_c) npt.assert_almost_equal(kappa, kappa_direct, decimal=5) def test_mass_3d(self): x = np.array([1, 3, 4]) y = np.array([2, 1, 1]) r = np.sqrt(x**2 + y**2) kappa_0 = 0.1 theta_c = 7 m3d = self.model.mass_3d(r, kappa_0, theta_c) m3d_lens = self.model.mass_3d_lens(r, kappa_0, theta_c) npt.assert_almost_equal(m3d, m3d_lens, decimal=8)
def _rescaled_cnfw_params(self, cnfw_params, uldm_params): """ :param cnfw_params: cored NFW halo lensing params :param uldm_params: ULDM halo lensing params :return: rescaled cored NFW params to fill up the remainder of the mass budget such that the composite profile has the inputted virial mass. """ r200 = self._c * cnfw_params['Rs'] rho0 = Uldm().density_lens(0, uldm_params['kappa_0'], uldm_params['theta_c']) rhos = CNFW().density_lens(0, cnfw_params['Rs'], cnfw_params['alpha_Rs'], cnfw_params['r_core']) args = (r200, self.mass, cnfw_params['Rs'], cnfw_params['alpha_Rs'], uldm_params['kappa_0'], uldm_params['theta_c'], rho0, rhos) initial_guess = np.array([0.9, 1.1]) bounds = ((0.5, 10), (0.5, 1.5)) method = 'Nelder-Mead' beta, q = minimize(self._function_to_minimize, initial_guess, args, method=method, bounds=bounds, tol=0.1)['x'] if beta < 0: raise ValueError( 'Negative CNFW core radius, tweak your parameters.') elif q < 0: raise ValueError( 'Negative ULDM profile mass, tweak your parameters.') else: pass cnfw_params['r_core'] /= beta uldm_params['kappa_0'] /= q M_nfw = CNFW().mass_3d_lens( r200, cnfw_params['Rs'], cnfw_params['alpha_Rs'] * self._lens_cosmo.sigmacrit, cnfw_params['r_core']) M_uldm = Uldm().mass_3d_lens( r200, uldm_params['kappa_0'] * self._lens_cosmo.sigmacrit, uldm_params['theta_c']) if (self._args['scale_nfw']): # When scale_nfw is True rescale alpha_Rs to improve mass accuracy scale = self.mass / (M_nfw + M_uldm) cnfw_params['alpha_Rs'] *= scale return [cnfw_params, uldm_params]
def __init__(self, profile_type='CORED_DENSITY'): if profile_type == 'CORED_DENSITY': self._profile = CoredDensity() elif profile_type == 'CORED_DENSITY_2': self._profile = CoredDensity2() elif profile_type == 'CORED_DENSITY_EXP': self._profile = CoredDensityExp() # Due to parameters name conventions/positioning, right now only the free soliton with # the default value of slope = 8 is supported elif profile_type == 'CORED_DENSITY_ULDM': self._profile = Uldm() else: raise ValueError('profile_type %s not supported for CoredDensityMST instance.' % profile_type) self._convergence = Convergence() super(CoredDensityMST, self).__init__()
def _constraint_mass(self, beta, q, r, m_target, rs, alpha_rs, kappa_0, theta_c): """ :param beta: CNFW core radius ('r_core') rescaling parameter :param q: ULDM core density ('kappa_0') rescaling parameter :param r: r200 of CNFW profile :param m_target: halo virial mass :param rs: CNFW scale radius :param alpha_rs: CNFW deflection angle at rs, in absence of core :param kappa_0: ULDM core density :param theta_c: ULDM core radius :return: Evaluated mass constraint equation for CNFW component profile """ r_core = beta * rs sigma_crit = self.lens_cosmo.sigmacrit args_nfw = (r, rs, alpha_rs * sigma_crit, r_core) args_uldm = (r, kappa_0 * sigma_crit, theta_c) m_nfw = CNFW().mass_3d_lens(*args_nfw) / m_target m_uldm = q * Uldm().mass_3d_lens(*args_uldm) / m_target penalty = np.absolute(m_nfw + m_uldm - 1) if np.isnan(penalty): return 1e+12 # penalize if not equal to zero return penalty
def setup(self): self.model = Uldm()
def _import_class(lens_type, custom_class, kwargs_interp, z_lens=None, z_source=None): """ :param lens_type: string, lens model type :param custom_class: custom class :param z_lens: lens redshift # currently only used in NFW_MC model as this is redshift dependent :param z_source: source redshift # currently only used in NFW_MC model as this is redshift dependent :param kwargs_interp: interpolation keyword arguments specifying the numerics. See description in the Interpolate() class. Only applicable for 'INTERPOL' and 'INTERPOL_SCALED' models. :return: class instance of the lens model type """ if lens_type == 'SHIFT': from lenstronomy.LensModel.Profiles.constant_shift import Shift return Shift() elif lens_type == 'NIE_POTENTIAL': from lenstronomy.LensModel.Profiles.nie_potential import NIE_POTENTIAL return NIE_POTENTIAL() elif lens_type == 'CONST_MAG': from lenstronomy.LensModel.Profiles.const_mag import ConstMag return ConstMag() elif lens_type == 'SHEAR': from lenstronomy.LensModel.Profiles.shear import Shear return Shear() elif lens_type == 'SHEAR_GAMMA_PSI': from lenstronomy.LensModel.Profiles.shear import ShearGammaPsi return ShearGammaPsi() elif lens_type == 'SHEAR_REDUCED': from lenstronomy.LensModel.Profiles.shear import ShearReduced return ShearReduced() elif lens_type == 'CONVERGENCE': from lenstronomy.LensModel.Profiles.convergence import Convergence return Convergence() elif lens_type == 'HESSIAN': from lenstronomy.LensModel.Profiles.hessian import Hessian return Hessian() elif lens_type == 'FLEXION': from lenstronomy.LensModel.Profiles.flexion import Flexion return Flexion() elif lens_type == 'FLEXIONFG': from lenstronomy.LensModel.Profiles.flexionfg import Flexionfg return Flexionfg() elif lens_type == 'POINT_MASS': from lenstronomy.LensModel.Profiles.point_mass import PointMass return PointMass() elif lens_type == 'SIS': from lenstronomy.LensModel.Profiles.sis import SIS return SIS() elif lens_type == 'SIS_TRUNCATED': from lenstronomy.LensModel.Profiles.sis_truncate import SIS_truncate return SIS_truncate() elif lens_type == 'SIE': from lenstronomy.LensModel.Profiles.sie import SIE return SIE() elif lens_type == 'SPP': from lenstronomy.LensModel.Profiles.spp import SPP return SPP() elif lens_type == 'NIE': from lenstronomy.LensModel.Profiles.nie import NIE return NIE() elif lens_type == 'NIE_SIMPLE': from lenstronomy.LensModel.Profiles.nie import NIEMajorAxis return NIEMajorAxis() elif lens_type == 'CHAMELEON': from lenstronomy.LensModel.Profiles.chameleon import Chameleon return Chameleon() elif lens_type == 'DOUBLE_CHAMELEON': from lenstronomy.LensModel.Profiles.chameleon import DoubleChameleon return DoubleChameleon() elif lens_type == 'TRIPLE_CHAMELEON': from lenstronomy.LensModel.Profiles.chameleon import TripleChameleon return TripleChameleon() elif lens_type == 'SPEP': from lenstronomy.LensModel.Profiles.spep import SPEP return SPEP() elif lens_type == 'PEMD': from lenstronomy.LensModel.Profiles.pemd import PEMD return PEMD() elif lens_type == 'SPEMD': from lenstronomy.LensModel.Profiles.spemd import SPEMD return SPEMD() elif lens_type == 'EPL': from lenstronomy.LensModel.Profiles.epl import EPL return EPL() elif lens_type == 'EPL_NUMBA': from lenstronomy.LensModel.Profiles.epl_numba import EPL_numba return EPL_numba() elif lens_type == 'SPL_CORE': from lenstronomy.LensModel.Profiles.splcore import SPLCORE return SPLCORE() elif lens_type == 'NFW': from lenstronomy.LensModel.Profiles.nfw import NFW return NFW() elif lens_type == 'NFW_ELLIPSE': from lenstronomy.LensModel.Profiles.nfw_ellipse import NFW_ELLIPSE return NFW_ELLIPSE() elif lens_type == 'NFW_ELLIPSE_GAUSS_DEC': from lenstronomy.LensModel.Profiles.gauss_decomposition import NFWEllipseGaussDec return NFWEllipseGaussDec() elif lens_type == 'NFW_ELLIPSE_CSE': from lenstronomy.LensModel.Profiles.nfw_ellipse_cse import NFW_ELLIPSE_CSE return NFW_ELLIPSE_CSE() elif lens_type == 'TNFW': from lenstronomy.LensModel.Profiles.tnfw import TNFW return TNFW() elif lens_type == 'TNFW_ELLIPSE': from lenstronomy.LensModel.Profiles.tnfw_ellipse import TNFW_ELLIPSE return TNFW_ELLIPSE() elif lens_type == 'CNFW': from lenstronomy.LensModel.Profiles.cnfw import CNFW return CNFW() elif lens_type == 'CNFW_ELLIPSE': from lenstronomy.LensModel.Profiles.cnfw_ellipse import CNFW_ELLIPSE return CNFW_ELLIPSE() elif lens_type == 'CTNFW_GAUSS_DEC': from lenstronomy.LensModel.Profiles.gauss_decomposition import CTNFWGaussDec return CTNFWGaussDec() elif lens_type == 'NFW_MC': from lenstronomy.LensModel.Profiles.nfw_mass_concentration import NFWMC return NFWMC(z_lens=z_lens, z_source=z_source) elif lens_type == 'SERSIC': from lenstronomy.LensModel.Profiles.sersic import Sersic return Sersic() elif lens_type == 'SERSIC_ELLIPSE_POTENTIAL': from lenstronomy.LensModel.Profiles.sersic_ellipse_potential import SersicEllipse return SersicEllipse() elif lens_type == 'SERSIC_ELLIPSE_KAPPA': from lenstronomy.LensModel.Profiles.sersic_ellipse_kappa import SersicEllipseKappa return SersicEllipseKappa() elif lens_type == 'SERSIC_ELLIPSE_GAUSS_DEC': from lenstronomy.LensModel.Profiles.gauss_decomposition import SersicEllipseGaussDec return SersicEllipseGaussDec() elif lens_type == 'PJAFFE': from lenstronomy.LensModel.Profiles.p_jaffe import PJaffe return PJaffe() elif lens_type == 'PJAFFE_ELLIPSE': from lenstronomy.LensModel.Profiles.p_jaffe_ellipse import PJaffe_Ellipse return PJaffe_Ellipse() elif lens_type == 'HERNQUIST': from lenstronomy.LensModel.Profiles.hernquist import Hernquist return Hernquist() elif lens_type == 'HERNQUIST_ELLIPSE': from lenstronomy.LensModel.Profiles.hernquist_ellipse import Hernquist_Ellipse return Hernquist_Ellipse() elif lens_type == 'HERNQUIST_ELLIPSE_CSE': from lenstronomy.LensModel.Profiles.hernquist_ellipse_cse import HernquistEllipseCSE return HernquistEllipseCSE() elif lens_type == 'GAUSSIAN': from lenstronomy.LensModel.Profiles.gaussian_potential import Gaussian return Gaussian() elif lens_type == 'GAUSSIAN_KAPPA': from lenstronomy.LensModel.Profiles.gaussian_kappa import GaussianKappa return GaussianKappa() elif lens_type == 'GAUSSIAN_ELLIPSE_KAPPA': from lenstronomy.LensModel.Profiles.gaussian_ellipse_kappa import GaussianEllipseKappa return GaussianEllipseKappa() elif lens_type == 'GAUSSIAN_ELLIPSE_POTENTIAL': from lenstronomy.LensModel.Profiles.gaussian_ellipse_potential import GaussianEllipsePotential return GaussianEllipsePotential() elif lens_type == 'MULTI_GAUSSIAN_KAPPA': from lenstronomy.LensModel.Profiles.multi_gaussian_kappa import MultiGaussianKappa return MultiGaussianKappa() elif lens_type == 'MULTI_GAUSSIAN_KAPPA_ELLIPSE': from lenstronomy.LensModel.Profiles.multi_gaussian_kappa import MultiGaussianKappaEllipse return MultiGaussianKappaEllipse() elif lens_type == 'INTERPOL': from lenstronomy.LensModel.Profiles.interpol import Interpol return Interpol(**kwargs_interp) elif lens_type == 'INTERPOL_SCALED': from lenstronomy.LensModel.Profiles.interpol import InterpolScaled return InterpolScaled(**kwargs_interp) elif lens_type == 'SHAPELETS_POLAR': from lenstronomy.LensModel.Profiles.shapelet_pot_polar import PolarShapelets return PolarShapelets() elif lens_type == 'SHAPELETS_CART': from lenstronomy.LensModel.Profiles.shapelet_pot_cartesian import CartShapelets return CartShapelets() elif lens_type == 'DIPOLE': from lenstronomy.LensModel.Profiles.dipole import Dipole return Dipole() elif lens_type == 'CURVED_ARC_CONST': from lenstronomy.LensModel.Profiles.curved_arc_const import CurvedArcConst return CurvedArcConst() elif lens_type == 'CURVED_ARC_CONST_MST': from lenstronomy.LensModel.Profiles.curved_arc_const import CurvedArcConstMST return CurvedArcConstMST() elif lens_type == 'CURVED_ARC_SPP': from lenstronomy.LensModel.Profiles.curved_arc_spp import CurvedArcSPP return CurvedArcSPP() elif lens_type == 'CURVED_ARC_SIS_MST': from lenstronomy.LensModel.Profiles.curved_arc_sis_mst import CurvedArcSISMST return CurvedArcSISMST() elif lens_type == 'CURVED_ARC_SPT': from lenstronomy.LensModel.Profiles.curved_arc_spt import CurvedArcSPT return CurvedArcSPT() elif lens_type == 'CURVED_ARC_TAN_DIFF': from lenstronomy.LensModel.Profiles.curved_arc_tan_diff import CurvedArcTanDiff return CurvedArcTanDiff() elif lens_type == 'ARC_PERT': from lenstronomy.LensModel.Profiles.arc_perturbations import ArcPerturbations return ArcPerturbations() elif lens_type == 'coreBURKERT': from lenstronomy.LensModel.Profiles.coreBurkert import CoreBurkert return CoreBurkert() elif lens_type == 'CORED_DENSITY': from lenstronomy.LensModel.Profiles.cored_density import CoredDensity return CoredDensity() elif lens_type == 'CORED_DENSITY_2': from lenstronomy.LensModel.Profiles.cored_density_2 import CoredDensity2 return CoredDensity2() elif lens_type == 'CORED_DENSITY_EXP': from lenstronomy.LensModel.Profiles.cored_density_exp import CoredDensityExp return CoredDensityExp() elif lens_type == 'CORED_DENSITY_MST': from lenstronomy.LensModel.Profiles.cored_density_mst import CoredDensityMST return CoredDensityMST(profile_type='CORED_DENSITY') elif lens_type == 'CORED_DENSITY_2_MST': from lenstronomy.LensModel.Profiles.cored_density_mst import CoredDensityMST return CoredDensityMST(profile_type='CORED_DENSITY_2') elif lens_type == 'CORED_DENSITY_EXP_MST': from lenstronomy.LensModel.Profiles.cored_density_mst import CoredDensityMST return CoredDensityMST(profile_type='CORED_DENSITY_EXP') elif lens_type == 'NumericalAlpha': from lenstronomy.LensModel.Profiles.numerical_deflections import NumericalAlpha return NumericalAlpha(custom_class) elif lens_type == 'MULTIPOLE': from lenstronomy.LensModel.Profiles.multipole import Multipole return Multipole() elif lens_type == 'CSE': from lenstronomy.LensModel.Profiles.cored_steep_ellipsoid import CSE return CSE() elif lens_type == 'ElliSLICE': from lenstronomy.LensModel.Profiles.elliptical_density_slice import ElliSLICE return ElliSLICE() elif lens_type == 'ULDM': from lenstronomy.LensModel.Profiles.uldm import Uldm return Uldm() elif lens_type == 'CORED_DENSITY_ULDM_MST': from lenstronomy.LensModel.Profiles.cored_density_mst import CoredDensityMST return CoredDensityMST(profile_type='CORED_DENSITY_ULDM') else: raise ValueError( '%s is not a valid lens model. Supported are: %s.' % (lens_type, _SUPPORTED_MODELS))
class CoredDensityMST(LensProfileBase): """ approximate mass-sheet transform of a density core. This routine takes the parameters of the density core and subtracts a mass-sheet that approximates the cored profile in it's center to counter-act (in approximation) this model. This allows for better sampling of the mass-sheet transformed quantities that do not have strong covariances. The subtraction of the mass-sheet is done such that the sampler returns the real central convergence of the original model (but be careful, the output of quantities like the Einstein angle of the main deflector are still the not-scaled one). Attention!!! The interpretation of the result is that the mass sheet as 'CONVERGENCE' that is present needs to be subtracted in post-processing. """ param_names = ['lambda_approx', 'r_core', 'center_x', 'center_y'] lower_limit_default = { 'lambda_approx': -1, 'r_core': 0, 'center_x': -100, 'center_y': -100 } upper_limit_default = { 'lambda_approx': 10, 'r_core': 100, 'center_x': 100, 'center_y': 100 } def __init__(self, profile_type='CORED_DENSITY'): if profile_type == 'CORED_DENSITY': self._profile = CoredDensity() elif profile_type == 'CORED_DENSITY_2': self._profile = CoredDensity2() elif profile_type == 'CORED_DENSITY_EXP': self._profile = CoredDensityExp() # Due to parameters name conventions/positioning, right now only the free soliton with # the default value of slope = 8 is supported elif profile_type == 'CORED_DENSITY_ULDM': self._profile = Uldm() else: raise ValueError( 'profile_type %s not supported for CoredDensityMST instance.' % profile_type) self._convergence = Convergence() super(CoredDensityMST, self).__init__() def function(self, x, y, lambda_approx, r_core, center_x=0, center_y=0): """ lensing potential of approximate mass-sheet correction :param x: x-coordinate :param y: y-coordinate :param lambda_approx: approximate mass sheet transform :param r_core: core radius of the cored density profile :param center_x: x-center of the profile :param center_y: y-center of the profile :return: lensing potential correction """ kappa_ext = (1 - lambda_approx) / lambda_approx f_cored_density = self._profile.function(x, y, kappa_ext, r_core, center_x, center_y) f_ms = self._convergence.function(x, y, kappa_ext, center_x, center_y) return f_cored_density - f_ms def derivatives(self, x, y, lambda_approx, r_core, center_x=0, center_y=0): """ deflection angles of approximate mass-sheet correction :param x: x-coordinate :param y: y-coordinate :param lambda_approx: approximate mass sheet transform :param r_core: core radius of the cored density profile :param center_x: x-center of the profile :param center_y: y-center of the profile :return: alpha_x, alpha_y """ kappa_ext = (1 - lambda_approx) / lambda_approx f_x_cd, f_y_cd = self._profile.derivatives(x, y, kappa_ext, r_core, center_x, center_y) f_x_ms, f_y_ms = self._convergence.derivatives(x, y, kappa_ext, center_x, center_y) return f_x_cd - f_x_ms, f_y_cd - f_y_ms def hessian(self, x, y, lambda_approx, r_core, center_x=0, center_y=0): """ Hessian terms of approximate mass-sheet correction :param x: x-coordinate :param y: y-coordinate :param lambda_approx: approximate mass sheet transform :param r_core: core radius of the cored density profile :param center_x: x-center of the profile :param center_y: y-center of the profile :return: df/dxx, df/dxy, df/dyx, df/dyy """ kappa_ext = (1 - lambda_approx) / lambda_approx f_xx_cd, f_xy_cd, f_yx_cd, f_yy_cd = self._profile.hessian( x, y, kappa_ext, r_core, center_x, center_y) f_xx_ms, f_xy_ms, f_yx_ms, f_yy_ms = self._convergence.hessian( x, y, kappa_ext, center_x, center_y) return f_xx_cd - f_xx_ms, f_xy_cd - f_xy_ms, f_yx_cd - f_yx_ms, f_yy_cd - f_yy_ms