Source code for lenstronomy.LensModel.Profiles.p_jaffe

import numpy as np
from lenstronomy.LensModel.Profiles.base_profile import LensProfileBase

__all__ = ["PJaffe"]


[docs] class PJaffe(LensProfileBase): """ class to compute the DUAL PSEUDO ISOTHERMAL MASS DISTRIBUTION based on Eliasdottir (2007) https://arxiv.org/pdf/0710.5636.pdf Appendix A Module name: 'PJAFFE'; An alternative name is dPIED (in the elliptical scenario) This profile is for the spherical case. For an elliptical version, use "PJAFFE_ELLIPSE" (ellipticitly in the potential) # TODO: add/revise name once ellipticity in the mass is available The 3D density distribution is .. math:: \\rho(r) = \\frac{\\rho_0}{(1+r^2/Ra^2)(1+r^2/Rs^2)} with :math:`Rs > Ra`. The projected density is .. math:: \\Sigma(R) = \\Sigma_0 \\frac{Ra Rs}{Rs-Ra}\\left(\\frac{1}{\\sqrt{Ra^2+R^2}} - \\frac{1}{\\sqrt{Rs^2+R^2}} \\right) with .. math:: \\Sigma_0 = \\pi \\rho_0 \\frac{Ra Rs}{Rs + Ra} In the lensing parameterization, .. math:: \\sigma_0 = \\frac{\\Sigma_0}{\\Sigma_{\\rm crit}} """ param_names = ["sigma0", "Ra", "Rs", "center_x", "center_y"] lower_limit_default = { "sigma0": 0, "Ra": 0, "Rs": 0, "center_x": -100, "center_y": -100, } upper_limit_default = { "sigma0": 10, "Ra": 100, "Rs": 100, "center_x": 100, "center_y": 100, }
[docs] def __init__(self): """""" LensProfileBase.__init__(self) self._s = 0.0001
[docs] def density(self, r, rho0, Ra, Rs): """Computes the density. :param r: radial distance from the center (in 3D) :param rho0: density normalization (see class documentation above) :param Ra: core radius :param Rs: transition radius from logarithmic slope -2 to -4 :return: density at r """ Ra, Rs = self._sort_ra_rs(Ra, Rs) rho = rho0 / ((1 + (r / Ra) ** 2) * (1 + (r / Rs) ** 2)) return rho
[docs] def density_2d(self, x, y, rho0, Ra, Rs, center_x=0, center_y=0): """Projected density. :param x: projected coordinate on the sky :param y: projected coordinate on the sky :param rho0: density normalization (see class documentation above) :param Ra: core radius :param Rs: transition radius from logarithmic slope -2 to -4 :param center_x: center of profile :param center_y: center of profile :return: projected density """ Ra, Rs = self._sort_ra_rs(Ra, Rs) x_ = x - center_x y_ = y - center_y r = np.sqrt(x_**2 + y_**2) sigma0 = self.rho2sigma(rho0, Ra, Rs) sigma = ( sigma0 * Ra * Rs / (Rs - Ra) * (1 / np.sqrt(Ra**2 + r**2) - 1 / np.sqrt(Rs**2 + r**2)) ) return sigma
[docs] def mass_3d(self, r, rho0, Ra, Rs): """Mass enclosed a 3d sphere or radius r. :param r: radial distance from the center (in 3D) :param rho0: density normalization (see class documentation above) :param Ra: core radius :param Rs: transition radius from logarithmic slope -2 to -4 :return: M(<r) """ m_3d = ( 4 * np.pi * rho0 * Ra**2 * Rs**2 / (Rs**2 - Ra**2) * (Rs * np.arctan(r / Rs) - Ra * np.arctan(r / Ra)) ) return m_3d
[docs] def mass_3d_lens(self, r, sigma0, Ra, Rs): """Mass enclosed a 3d sphere or radius r given a lens parameterization with angular units. :param r: radial distance from the center (in 3D) :param sigma0: density normalization (see class documentation above) :param Ra: core radius :param Rs: transition radius from logarithmic slope -2 to -4 :return: M(<r) in angular units (modulo critical mass density) """ rho0 = self.sigma2rho(sigma0, Ra, Rs) return self.mass_3d(r, rho0, Ra, Rs)
[docs] def mass_2d(self, r, rho0, Ra, Rs): """Mass enclosed projected 2d sphere of radius r. :param r: radial distance from the center in projection :param rho0: density normalization (see class documentation above) :param Ra: core radius :param Rs: transition radius from logarithmic slope -2 to -4 :return: Sigma(<r) """ Ra, Rs = self._sort_ra_rs(Ra, Rs) sigma0 = self.rho2sigma(rho0, Ra, Rs) m_2d = ( 2 * np.pi * sigma0 * Ra * Rs / (Rs - Ra) * (np.sqrt(Ra**2 + r**2) - Ra - np.sqrt(Rs**2 + r**2) + Rs) ) return m_2d
[docs] def mass_tot(self, rho0, Ra, Rs): """Total mass within the profile. :param rho0: density normalization (see class documentation above) :param Ra: core radius :param Rs: transition radius from logarithmic slope -2 to -4 :return: total mass """ Ra, Rs = self._sort_ra_rs(Ra, Rs) sigma0 = self.rho2sigma(rho0, Ra, Rs) m_tot = 2 * np.pi * sigma0 * Ra * Rs return m_tot
[docs] def grav_pot(self, r, rho0, Ra, Rs): """Gravitational potential (modulo 4 pi G and rho0 in appropriate units) :param r: radial distance from the center (in 3D) :param rho0: density normalization (see class documentation above) :param Ra: core radius :param Rs: transition radius from logarithmic slope -2 to -4 :return: gravitational potential (modulo 4 pi G and rho0 in appropriate units) """ Ra, Rs = self._sort_ra_rs(Ra, Rs) pot = ( 4 * np.pi * rho0 * Ra**2 * Rs**2 / (Rs**2 - Ra**2) * ( Rs / r * np.arctan(r / Rs) - Ra / r * np.arctan(r / Ra) + 1.0 / 2 * np.log((Rs**2 + r**2) / (Ra**2 + r**2)) ) ) return pot
[docs] def function(self, x, y, sigma0, Ra, Rs, center_x=0, center_y=0): """Lensing potential. :param x: projected coordinate on the sky :param y: projected coordinate on the sky :param sigma0: sigma0/sigma_crit (see class documentation above) :param Ra: core radius (see class documentation above) :param Rs: transition radius from logarithmic slope -2 to -4 (see class documentation above) :param center_x: center of profile :param center_y: center of profile :return: lensing potential """ Ra, Rs = self._sort_ra_rs(Ra, Rs) x_ = x - center_x y_ = y - center_y r = np.sqrt(x_**2 + y_**2) f_ = ( -2 * sigma0 * Ra * Rs / (Rs - Ra) * ( np.sqrt(Rs**2 + r**2) - np.sqrt(Ra**2 + r**2) + Ra * np.log(Ra + np.sqrt(Ra**2 + r**2)) - Rs * np.log(Rs + np.sqrt(Rs**2 + r**2)) ) ) return f_
[docs] def derivatives(self, x, y, sigma0, Ra, Rs, center_x=0, center_y=0): """Deflection angles. :param x: projected coordinate on the sky :param y: projected coordinate on the sky :param sigma0: sigma0/sigma_crit (see class documentation above) :param Ra: core radius (see class documentation above) :param Rs: transition radius from logarithmic slope -2 to -4 (see class documentation above) :param center_x: center of profile :param center_y: center of profile :return: f_x, f_y """ Ra, Rs = self._sort_ra_rs(Ra, Rs) x_ = x - center_x y_ = y - center_y r = np.sqrt(x_**2 + y_**2) if isinstance(r, int) or isinstance(r, float): r = max(self._s, r) else: r[r < self._s] = self._s alpha_r = 2 * sigma0 * Ra * Rs / (Rs - Ra) * self._f_A20(r / Ra, r / Rs) f_x = alpha_r * x_ / r f_y = alpha_r * y_ / r return f_x, f_y
[docs] def hessian(self, x, y, sigma0, Ra, Rs, center_x=0, center_y=0): """Hessian of lensing potential. :param x: projected coordinate on the sky :param y: projected coordinate on the sky :param sigma0: sigma0/sigma_crit (see class documentation above) :param Ra: core radius (see class documentation above) :param Rs: transition radius from logarithmic slope -2 to -4 (see class documentation above) :param center_x: center of profile :param center_y: center of profile :return: f_xx, f_xy, f_yx, f_yy """ Ra, Rs = self._sort_ra_rs(Ra, Rs) x_ = x - center_x y_ = y - center_y r = np.sqrt(x_**2 + y_**2) if isinstance(r, int) or isinstance(r, float): r = max(self._s, r) else: r[r < self._s] = self._s gamma = ( sigma0 * Ra * Rs / (Rs - Ra) * ( 2 * ( 1.0 / (Ra + np.sqrt(Ra**2 + r**2)) - 1.0 / (Rs + np.sqrt(Rs**2 + r**2)) ) - (1 / np.sqrt(Ra**2 + r**2) - 1 / np.sqrt(Rs**2 + r**2)) ) ) kappa = ( sigma0 * Ra * Rs / (Rs - Ra) * (1 / np.sqrt(Ra**2 + r**2) - 1 / np.sqrt(Rs**2 + r**2)) ) sin_2phi = -2 * x_ * y_ / r**2 cos_2phi = (y_**2 - x_**2) / r**2 gamma1 = cos_2phi * gamma gamma2 = sin_2phi * gamma f_xx = kappa + gamma1 f_yy = kappa - gamma1 f_xy = gamma2 return f_xx, f_xy, f_xy, f_yy
def _f_A20(self, r_a, r_s): """Equation A20 in Eliasdottir (2007) :param r_a: r/Ra :param r_s: r/Rs :return: f(R/a, R/s) """ return r_a / (1 + np.sqrt(1 + r_a**2)) - r_s / (1 + np.sqrt(1 + r_s**2))
[docs] def rho2sigma(self, rho0, Ra, Rs): """Converts 3d density into 2d projected density parameter, Equation A4 in Eliasdottir (2007) :param rho0: density normalization :param Ra: core radius (see class documentation above) :param Rs: transition radius from logarithmic slope -2 to -4 (see class documentation above) :return: projected density normalization """ return np.pi * rho0 * Ra * Rs / (Rs + Ra)
[docs] def sigma2rho(self, sigma0, Ra, Rs): """Inverse of rho2sigma() :param sigma0: projected density normalization :param Ra: core radius (see class documentation above) :param Rs: transition radius from logarithmic slope -2 to -4 (see class documentation above) :return: 3D density normalization """ return (Rs + Ra) / Ra / Rs / np.pi * sigma0
@staticmethod def _sort_ra_rs(Ra, Rs): """Sorts Ra and Rs to make sure Rs > Ra. :param Ra: :param Rs: :return: Ra, Rs in conventions used """ if Ra >= Rs: Ra, Rs = Rs, Ra if Ra < 0.0001: Ra = 0.0001 if Rs < Ra + 0.0001: Rs += 0.0001 return Ra, Rs