class TestSIE(object): """ tests the Gaussian methods """ def setup(self): from lenstronomy.LensModel.Profiles.sie import SIE from lenstronomy.LensModel.Profiles.spemd import SPEMD self.sie = SIE() self.spemd = SPEMD() def test_function(self): x = np.array([1]) y = np.array([2]) theta_E = 1. q = 0.9 phi_G = 1. values = self.sie.function(x, y, theta_E, q, phi_G) gamma = 2 values_spemd = self.spemd.function(x, y, theta_E, gamma, q, phi_G) assert values == values_spemd def test_derivatives(self): x = np.array([1]) y = np.array([2]) theta_E = 1. q = 0.9 phi_G = 1. values = self.sie.derivatives(x, y, theta_E, q, phi_G) gamma = 2 values_spemd = self.spemd.derivatives(x, y, theta_E, gamma, q, phi_G) assert values == values_spemd def test_hessian(self): x = np.array([1]) y = np.array([2]) theta_E = 1. q = 0.9 phi_G = 1. values = self.sie.hessian(x, y, theta_E, q, phi_G) gamma = 2 values_spemd = self.spemd.hessian(x, y, theta_E, gamma, q, phi_G) assert values[0] == values_spemd[0]
class CurvedArcTanDiff(LensProfileBase): """ Curved arc model with an additional non-zero tangential stretch differential in tangential direction component Observables are: - curvature radius (basically bending relative to the center of the profile) - radial stretch (plus sign) thickness of arc with parity (more generalized than the power-law slope) - tangential stretch (plus sign). Infinity means at critical curve - direction of curvature - position of arc Requirements: - Should work with other perturbative models without breaking its meaning (say when adding additional shear terms) - Must best reflect the observables in lensing - minimal covariances between the parameters, intuitive parameterization. """ param_names = [ 'tangential_stretch', 'radial_stretch', 'curvature', 'dtan_dtan', 'direction', 'center_x', 'center_y' ] lower_limit_default = { 'tangential_stretch': -100, 'radial_stretch': -5, 'curvature': 0.000001, 'dtan_dtan': -10, 'direction': -np.pi, 'center_x': -100, 'center_y': -100 } upper_limit_default = { 'tangential_stretch': 100, 'radial_stretch': 5, 'curvature': 100, 'dtan_dtan': 10, 'direction': np.pi, 'center_x': 100, 'center_y': 100 } def __init__(self): self._sie = SIE(NIE=True) self._mst = Convergence() super(CurvedArcTanDiff, self).__init__() @staticmethod def stretch2sie_mst(tangential_stretch, radial_stretch, curvature, dtan_dtan, direction, center_x, center_y): """ :param tangential_stretch: float, stretch of intrinsic source in tangential direction :param radial_stretch: float, stretch of intrinsic source in radial direction :param curvature: 1/curvature radius :param dtan_dtan: d(tangential_stretch) / d(tangential direction) / tangential stretch :param direction: float, angle in radian :param center_x: center of source in image plane :param center_y: center of source in image plane :return: parameters in terms of a spherical SIS + MST resulting in the same observables """ center_x_sis, center_y_sis = center_deflector(curvature, direction, center_x, center_y) r_curvature = 1. / curvature lambda_mst = 1. / radial_stretch kappa_ext = 1 - lambda_mst theta_E = r_curvature * (1. - radial_stretch / tangential_stretch) # analytic relation (see Birrer 2021) dlambda_tan_dr = tangential_stretch / r_curvature * ( 1 - tangential_stretch / radial_stretch) # translate tangential eigenvalue gradient in lens ellipticity dtan_dtan_ = dtan_dtan * tangential_stretch epsilon = np.abs(dtan_dtan_ / dlambda_tan_dr) # bound epsilon by (-1, 1) epsilon = np.minimum(epsilon, 0.99999) q = np.sqrt((1 - epsilon) / (1 + epsilon)) if dtan_dtan_ < 0: phi = direction - np.pi / 4 else: phi = direction + np.pi / 4 e1_sie, e2_sie = param_util.phi_q2_ellipticity(phi, q) # ellipticity adopted Einstein radius to match local tangential and radial stretch factor = np.sqrt(1 + q**2) / np.sqrt(2 * q) theta_E_sie = theta_E * factor return theta_E_sie, e1_sie, e2_sie, kappa_ext, center_x_sis, center_y_sis def function(self, x, y, tangential_stretch, radial_stretch, curvature, dtan_dtan, direction, center_x, center_y): """ ATTENTION: there may not be a global lensing potential! :param x: :param y: :param tangential_stretch: float, stretch of intrinsic source in tangential direction :param radial_stretch: float, stretch of intrinsic source in radial direction :param curvature: 1/curvature radius :param direction: float, angle in radian :param dtan_dtan: d(tangential_stretch) / d(tangential direction) / tangential stretch :param center_x: center of source in image plane :param center_y: center of source in image plane :return: """ lambda_mst = 1. / radial_stretch theta_E_sie, e1_sie, e2_sie, kappa_ext, center_x_sis, center_y_sis = self.stretch2sie_mst( tangential_stretch, radial_stretch, curvature, dtan_dtan, direction, center_x, center_y) f_sis = self._sie.function( x, y, theta_E_sie, e1_sie, e2_sie, center_x_sis, center_y_sis ) # - self._sis.function(center_x, center_y, theta_E, center_x_sis, center_y_sis) alpha_x, alpha_y = self._sie.derivatives(center_x, center_y, theta_E_sie, e1_sie, e2_sie, center_x_sis, center_y_sis) f_sis_0 = alpha_x * (x - center_x) + alpha_y * (y - center_y) f_mst = self._mst.function(x, y, kappa_ext, ra_0=center_x, dec_0=center_y) return lambda_mst * (f_sis - f_sis_0) + f_mst def derivatives(self, x, y, tangential_stretch, radial_stretch, curvature, dtan_dtan, direction, center_x, center_y): """ :param x: :param y: :param tangential_stretch: float, stretch of intrinsic source in tangential direction :param radial_stretch: float, stretch of intrinsic source in radial direction :param curvature: 1/curvature radius :param direction: float, angle in radian :param dtan_dtan: d(tangential_stretch) / d(tangential direction) / tangential stretch :param center_x: center of source in image plane :param center_y: center of source in image plane :return: """ lambda_mst = 1. / radial_stretch theta_E_sie, e1_sie, e2_sie, kappa_ext, center_x_sis, center_y_sis = self.stretch2sie_mst( tangential_stretch, radial_stretch, curvature, dtan_dtan, direction, center_x, center_y) f_x_sis, f_y_sis = self._sie.derivatives(x, y, theta_E_sie, e1_sie, e2_sie, center_x_sis, center_y_sis) f_x0, f_y0 = self._sie.derivatives(center_x, center_y, theta_E_sie, e1_sie, e2_sie, center_x_sis, center_y_sis) f_x_mst, f_y_mst = self._mst.derivatives(x, y, kappa_ext, ra_0=center_x, dec_0=center_y) f_x = lambda_mst * (f_x_sis - f_x0) + f_x_mst f_y = lambda_mst * (f_y_sis - f_y0) + f_y_mst return f_x, f_y def hessian(self, x, y, tangential_stretch, radial_stretch, curvature, dtan_dtan, direction, center_x, center_y): """ :param x: :param y: :param tangential_stretch: float, stretch of intrinsic source in tangential direction :param radial_stretch: float, stretch of intrinsic source in radial direction :param curvature: 1/curvature radius :param direction: float, angle in radian :param dtan_dtan: d(tangential_stretch) / d(tangential direction) / tangential stretch :param center_x: center of source in image plane :param center_y: center of source in image plane :return: """ lambda_mst = 1. / radial_stretch theta_E_sie, e1_sie, e2_sie, kappa_ext, center_x_sis, center_y_sis = self.stretch2sie_mst( tangential_stretch, radial_stretch, curvature, dtan_dtan, direction, center_x, center_y) f_xx_sis, f_xy_sis, f_yx_sis, f_yy_sis = self._sie.hessian( x, y, theta_E_sie, e1_sie, e2_sie, center_x_sis, center_y_sis) f_xx_mst, f_xy_mst, f_yx_mst, f_yy_mst = self._mst.hessian( x, y, kappa_ext, ra_0=center_x, dec_0=center_y) return lambda_mst * f_xx_sis + f_xx_mst, lambda_mst * f_xy_sis + f_xy_mst, lambda_mst * f_yx_sis + f_yx_mst, lambda_mst * f_yy_sis + f_yy_mst
class TestSIE(object): """ tests the Gaussian methods """ def setup(self): from lenstronomy.LensModel.Profiles.sie import SIE from lenstronomy.LensModel.Profiles.spemd import SPEMD from lenstronomy.LensModel.Profiles.nie import NIE self.sie = SIE(NIE=False) self.sie_nie = SIE(NIE=True) self.spemd = SPEMD() self.nie = NIE() def test_function(self): x = np.array([1]) y = np.array([2]) theta_E = 1. q = 0.9 phi_G = 1. e1, e2 = param_util.phi_q2_ellipticity(phi_G, q) values = self.sie.function(x, y, theta_E, e1, e2) gamma = 2 values_spemd = self.spemd.function(x, y, theta_E, gamma, e1, e2) assert values == values_spemd values = self.sie_nie.function(x, y, theta_E, e1, e2) s_scale = 0.0000001 values_spemd = self.nie.function(x, y, theta_E, e1, e2, s_scale) npt.assert_almost_equal(values, values_spemd, decimal=6) def test_derivatives(self): x = np.array([1]) y = np.array([2]) theta_E = 1. q = 0.9 phi_G = 1. e1, e2 = param_util.phi_q2_ellipticity(phi_G, q) values = self.sie.derivatives(x, y, theta_E, e1, e2) gamma = 2 values_spemd = self.spemd.derivatives(x, y, theta_E, gamma, e1, e2) assert values == values_spemd values = self.sie_nie.derivatives(x, y, theta_E, e1, e2) s_scale = 0.0000001 values_spemd = self.nie.derivatives(x, y, theta_E, e1, e2, s_scale) npt.assert_almost_equal(values, values_spemd, decimal=6) def test_hessian(self): x = np.array([1]) y = np.array([2]) theta_E = 1. q = 0.9 phi_G = 1. e1, e2 = param_util.phi_q2_ellipticity(phi_G, q) values = self.sie.hessian(x, y, theta_E, e1, e2) gamma = 2 values_spemd = self.spemd.hessian(x, y, theta_E, gamma, e1, e2) assert values[0] == values_spemd[0] values = self.sie_nie.hessian(x, y, theta_E, e1, e2) s_scale = 0.0000001 values_spemd = self.nie.hessian(x, y, theta_E, e1, e2, s_scale) npt.assert_almost_equal(values, values_spemd, decimal=5)