class SersicEllipseKappa(LensProfileBase): """ this class contains the function and the derivatives of an elliptical sersic profile with the ellipticity introduced in the convergence (not the potential). This requires the use of numerical integrals (Keeton 2004) """ param_names = [ 'k_eff', 'R_sersic', 'n_sersic', 'e1', 'e2', 'center_x', 'center_y' ] lower_limit_default = { 'k_eff': 0, 'R_sersic': 0, 'n_sersic': 0.5, 'e1': -0.5, 'e2': -0.5, 'center_x': -100, 'center_y': -100 } upper_limit_default = { 'k_eff': 10, 'R_sersic': 100, 'n_sersic': 8, 'e1': 0.5, 'e2': 0.5, 'center_x': 100, 'center_y': 100 } def __init__(self): self._sersic = Sersic() super(SersicEllipseKappa, self).__init__() def function(self, x, y, n_sersic, R_sersic, k_eff, e1, e2, center_x=0, center_y=0): raise Exception('not yet implemented') # phi_G, q = param_util.ellipticity2phi_q(e1, e2) # # if isinstance(x, float) and isinstance(y, float): # # x_, y_ = self._coord_rotate(x, y, phi_G, center_x, center_y) # integral = quad(self._integrand_I, 0, 1, args=(x_, y_, q, n_sersic, R_sersic, k_eff, center_x, center_y))[0] # # else: # # assert isinstance(x, np.ndarray) or isinstance(x, list) # assert isinstance(y, np.ndarray) or isinstance(y, list) # x = np.array(x) # y = np.array(y) # shape0 = x.shape # assert shape0 == y.shape # # if isinstance(phi_G, float) or isinstance(phi_G, int): # phiG = np.ones_like(x) * float(phi_G) # q = np.ones_like(x) * float(q) # integral = [] # for i, (x_i, y_i, phi_i, q_i) in \ # enumerate(zip(x.ravel(), y.ravel(), phiG.ravel(), q.ravel())): # # integral.append(quad(self._integrand_I, 0, 1, args=(x_, y_, q, n_sersic, # R_sersic, k_eff, center_x, center_y))[0]) # # # return 0.5 * q * integral def derivatives(self, x, y, n_sersic, R_sersic, k_eff, e1, e2, center_x=0, center_y=0): phi_G, gam = param_util.shear_cartesian2polar(e1, e2) q = max(1 - gam, 0.00001) x, y = self._coord_rotate(x, y, phi_G, center_x, center_y) if isinstance(x, float) and isinstance(y, float): alpha_x, alpha_y = self._compute_derivative_atcoord( x, y, n_sersic, R_sersic, k_eff, phi_G, q, center_x=center_x, center_y=center_y) else: assert isinstance(x, np.ndarray) or isinstance(x, list) assert isinstance(y, np.ndarray) or isinstance(y, list) x = np.array(x) y = np.array(y) shape0 = x.shape assert shape0 == y.shape alpha_x, alpha_y = np.empty_like(x).ravel(), np.empty_like( y).ravel() if isinstance(phi_G, float) or isinstance(phi_G, int): phiG = np.ones_like(alpha_x) * float(phi_G) q = np.ones_like(alpha_x) * float(q) for i, (x_i, y_i, phi_i, q_i) in \ enumerate(zip(x.ravel(), y.ravel(), phiG.ravel(), q.ravel())): fxi, fyi = self._compute_derivative_atcoord(x_i, y_i, n_sersic, R_sersic, k_eff, phi_i, q_i, center_x=center_x, center_y=center_y) alpha_x[i], alpha_y[i] = fxi, fyi alpha_x = alpha_x.reshape(shape0) alpha_y = alpha_y.reshape(shape0) alpha_x, alpha_y = self._coord_rotate(alpha_x, alpha_y, -phi_G, 0, 0) return alpha_x, alpha_y def hessian(self, x, y, n_sersic, R_sersic, k_eff, e1, e2, center_x=0, center_y=0): """ returns Hessian matrix of function d^2f/dx^2, d^2/dxdy, d^2/dydx, d^f/dy^2 """ alpha_ra, alpha_dec = self.derivatives(x, y, n_sersic, R_sersic, k_eff, e1, e2, center_x, center_y) diff = 0.000001 alpha_ra_dx, alpha_dec_dx = self.derivatives(x + diff, y, n_sersic, R_sersic, k_eff, e1, e2, center_x, center_y) alpha_ra_dy, alpha_dec_dy = self.derivatives(x, y + diff, n_sersic, R_sersic, k_eff, e1, e2, center_x, center_y) f_xx = (alpha_ra_dx - alpha_ra) / diff f_xy = (alpha_ra_dy - alpha_ra) / diff f_yx = (alpha_dec_dx - alpha_dec) / diff f_yy = (alpha_dec_dy - alpha_dec) / diff return f_xx, f_xy, f_yx, f_yy def projected_mass(self, x, y, q, n_sersic, R_sersic, k_eff, u=1, power=1): b_n = self._sersic.b_n(n_sersic) elliptical_coord = self._elliptical_coord_u(x, y, u, q)**power elliptical_coord *= R_sersic**-power exponent = -b_n * (elliptical_coord**(1. / n_sersic) - 1) return k_eff * np.exp(exponent) def _integrand_J(self, u, x, y, n_sersic, q, R_sersic, k_eff, n_integral): kappa = self.projected_mass(x, y, q, n_sersic, R_sersic, k_eff, u=u, power=1) power = -(n_integral + 0.5) return kappa * (1 - (1 - q**2) * u)**power def _integrand_I(self, u, x, y, q, n_sersic, R_sersic, keff, centerx, centery): ellip_coord = self._elliptical_coord_u(x, y, u, q) def_angle_circular = self._sersic.alpha_abs(ellip_coord, 0, n_sersic, R_sersic, keff, centerx, centery) return ellip_coord * def_angle_circular * ( 1 - (1 - q**2) * u)**-0.5 * u**-1 def _compute_derivative_atcoord(self, x, y, n_sersic, R_sersic, k_eff, phi_G, q, center_x=0, center_y=0): alpha_x = x * q * quad(self._integrand_J, 0, 1, args=(x, y, n_sersic, q, R_sersic, k_eff, 0))[0] alpha_y = y * q * quad(self._integrand_J, 0, 1, args=(x, y, n_sersic, q, R_sersic, k_eff, 1))[0] return alpha_x, alpha_y @staticmethod def _elliptical_coord_u(x, y, u, q): fac = 1 - (1 - q**2) * u return (u * (x**2 + y**2 * fac**-1))**0.5 @staticmethod def _coord_rotate(x, y, phi_G, center_x, center_y): x_shift = x - center_x y_shift = y - center_y cos_phi = np.cos(phi_G) sin_phi = np.sin(phi_G) x_ = cos_phi * x_shift + sin_phi * y_shift y_ = -sin_phi * x_shift + cos_phi * y_shift return x_, y_
class TestSersic(object): """ tests the Gaussian methods """ def setup(self): self.sersic_2 = SersicEllipseKappa() self.sersic = Sersic() self.sersic_light = Sersic_light() def test_function(self): x = 1 y = 2 n_sersic = 2. R_sersic = 1. k_eff = 0.2 values = self.sersic.function(x, y, n_sersic, R_sersic, k_eff) npt.assert_almost_equal(values, 1.0272982586319199, decimal=10) x = np.array([0]) y = np.array([0]) values = self.sersic.function(x, y, n_sersic, R_sersic, k_eff) npt.assert_almost_equal(values[0], 0., decimal=9) x = np.array([2, 3, 4]) y = np.array([1, 1, 1]) values = self.sersic.function(x, y, n_sersic, R_sersic, k_eff) npt.assert_almost_equal(values[0], 1.0272982586319199, decimal=10) npt.assert_almost_equal(values[1], 1.3318743892966658, decimal=10) npt.assert_almost_equal(values[2], 1.584299393114988, decimal=10) def test_derivatives(self): x = np.array([1]) y = np.array([2]) n_sersic = 2. R_sersic = 1. k_eff = 0.2 f_x, f_y = self.sersic.derivatives(x, y, n_sersic, R_sersic, k_eff) f_x2, f_y2 = self.sersic_2.derivatives(x, y, n_sersic, R_sersic, k_eff, 0, 0.00000001) assert f_x[0] == 0.16556078301997193 assert f_y[0] == 0.33112156603994386 npt.assert_almost_equal(f_x2[0], f_x[0]) npt.assert_almost_equal(f_y2[0], f_y[0]) x = np.array([0]) y = np.array([0]) f_x, f_y = self.sersic.derivatives(x, y, n_sersic, R_sersic, k_eff) f_x2, f_y2 = self.sersic_2.derivatives(x, y, n_sersic, R_sersic, k_eff, 0, 0.00000001) assert f_x[0] == 0 assert f_y[0] == 0 npt.assert_almost_equal(f_x2[0], f_x[0]) npt.assert_almost_equal(f_y2[0], f_y[0]) x = np.array([1, 3, 4]) y = np.array([2, 1, 1]) values = self.sersic.derivatives(x, y, n_sersic, R_sersic, k_eff) values2 = self.sersic_2.derivatives(x, y, n_sersic, R_sersic, k_eff, 0, 0.00000001) assert values[0][0] == 0.16556078301997193 assert values[1][0] == 0.33112156603994386 assert values[0][1] == 0.2772992378623737 assert values[1][1] == 0.092433079287457892 npt.assert_almost_equal(values2[0][0], values[0][0]) npt.assert_almost_equal(values2[1][0], values[1][0]) npt.assert_almost_equal(values2[0][1], values[0][1]) npt.assert_almost_equal(values2[1][1], values[1][1]) values2 = self.sersic_2.derivatives(0.3, -0.2, n_sersic, R_sersic, k_eff, 0, 0.00000001) values = self.sersic.derivatives(0.3, -0.2, n_sersic, R_sersic, k_eff, 0, 0.00000001) npt.assert_almost_equal(values2[0], values[0]) npt.assert_almost_equal(values2[1], values[1]) def test_differentails(self): x_, y_ = 1., 1 n_sersic = 2. R_sersic = 1. k_eff = 0.2 r = np.sqrt(x_**2 + y_**2) d_alpha_dr = self.sersic.d_alpha_dr(x_, y_, n_sersic, R_sersic, k_eff) alpha = self.sersic.alpha_abs(x_, y_, n_sersic, R_sersic, k_eff) f_xx_ = d_alpha_dr * calc_util.d_r_dx( x_, y_) * x_ / r + alpha * calc_util.d_x_diffr_dx(x_, y_) f_yy_ = d_alpha_dr * calc_util.d_r_dy( x_, y_) * y_ / r + alpha * calc_util.d_y_diffr_dy(x_, y_) f_xy_ = d_alpha_dr * calc_util.d_r_dy( x_, y_) * x_ / r + alpha * calc_util.d_x_diffr_dy(x_, y_) f_xx = (d_alpha_dr / r - alpha / r**2) * y_**2 / r + alpha / r f_yy = (d_alpha_dr / r - alpha / r**2) * x_**2 / r + alpha / r f_xy = (d_alpha_dr / r - alpha / r**2) * x_ * y_ / r npt.assert_almost_equal(f_xx, f_xx_, decimal=10) npt.assert_almost_equal(f_yy, f_yy_, decimal=10) npt.assert_almost_equal(f_xy, f_xy_, decimal=10) def test_hessian(self): x = np.array([1]) y = np.array([2]) n_sersic = 2. R_sersic = 1. k_eff = 0.2 f_xx, f_xy, f_yx, f_yy = self.sersic.hessian(x, y, n_sersic, R_sersic, k_eff) assert f_xx[0] == 0.1123170666045793 npt.assert_almost_equal(f_yy[0], -0.047414082641598576, decimal=10) npt.assert_almost_equal(f_xy[0], -0.10648743283078525, decimal=10) npt.assert_almost_equal(f_xy, f_yx, decimal=5) x = np.array([1, 3, 4]) y = np.array([2, 1, 1]) values = self.sersic.hessian(x, y, n_sersic, R_sersic, k_eff) assert values[0][0] == 0.1123170666045793 npt.assert_almost_equal(values[3][0], -0.047414082641598576, decimal=10) npt.assert_almost_equal(values[1][0], -0.10648743283078525, decimal=10) npt.assert_almost_equal(values[0][1], -0.053273787681591328, decimal=10) npt.assert_almost_equal(values[3][1], 0.076243427402007985, decimal=10) npt.assert_almost_equal(values[1][1], -0.048568955656349749, decimal=10) f_xx2, f_xy2, f_yx2, f_yy2 = self.sersic_2.hessian( x, y, n_sersic, R_sersic, k_eff, 0.0000001, 0) npt.assert_almost_equal(f_xx2, values[0]) npt.assert_almost_equal(f_yy2, values[3], decimal=6) npt.assert_almost_equal(f_xy2, values[1], decimal=6) npt.assert_almost_equal(f_yx2, values[2], decimal=6) def test_alpha_abs(self): x = 1. dr = 0.0000001 n_sersic = 2.5 R_sersic = .5 k_eff = 0.2 alpha_abs = self.sersic.alpha_abs(x, 0, n_sersic, R_sersic, k_eff) f_dr = self.sersic.function(x + dr, 0, n_sersic, R_sersic, k_eff) f_ = self.sersic.function(x, 0, n_sersic, R_sersic, k_eff) alpha_abs_num = -(f_dr - f_) / dr npt.assert_almost_equal(alpha_abs_num, alpha_abs, decimal=3) def test_dalpha_dr(self): x = 1. dr = 0.0000001 n_sersic = 1. R_sersic = .5 k_eff = 0.2 d_alpha_dr = self.sersic.d_alpha_dr(x, 0, n_sersic, R_sersic, k_eff) alpha_dr = self.sersic.alpha_abs(x + dr, 0, n_sersic, R_sersic, k_eff) alpha = self.sersic.alpha_abs(x, 0, n_sersic, R_sersic, k_eff) d_alpha_dr_num = (alpha_dr - alpha) / dr npt.assert_almost_equal(d_alpha_dr, d_alpha_dr_num, decimal=3) def test_mag_sym(self): """ :return: """ r = 2. angle1 = 0. angle2 = 1.5 x1 = r * np.cos(angle1) y1 = r * np.sin(angle1) x2 = r * np.cos(angle2) y2 = r * np.sin(angle2) n_sersic = 4.5 R_sersic = 2.5 k_eff = 0.8 f_xx1, f_xy1, f_yx1, f_yy1 = self.sersic.hessian( x1, y1, n_sersic, R_sersic, k_eff) f_xx2, f_xy2, f_yx2, f_yy2 = self.sersic.hessian( x2, y2, n_sersic, R_sersic, k_eff) kappa_1 = (f_xx1 + f_yy1) / 2 kappa_2 = (f_xx2 + f_yy2) / 2 npt.assert_almost_equal(kappa_1, kappa_2, decimal=10) A_1 = (1 - f_xx1) * (1 - f_yy1) - f_xy1 * f_yx1 A_2 = (1 - f_xx2) * (1 - f_yy2) - f_xy2 * f_yx2 npt.assert_almost_equal(A_1, A_2, decimal=10) def test_convergernce(self): """ test the convergence and compares it with the original Sersic profile :return: """ x = np.array([0, 0, 0, 0, 0]) y = np.array([0.5, 1, 1.5, 2, 2.5]) n_sersic = 4.5 R_sersic = 2.5 k_eff = 0.2 f_xx, f_xy, f_yx, f_yy = self.sersic.hessian(x, y, n_sersic, R_sersic, k_eff) kappa = (f_xx + f_yy) / 2. assert kappa[0] > 0 flux = self.sersic_light.function(x, y, amp=1., R_sersic=R_sersic, n_sersic=n_sersic) flux /= flux[0] kappa /= kappa[0] npt.assert_almost_equal(flux[1], kappa[1], decimal=5) xvalues = np.linspace(0.5, 3., 100) e1, e2 = 0.4, 0. q = ellipticity2phi_q(e1, e2)[1] kappa_ellipse = self.sersic_2.projected_mass(xvalues, 0, q, n_sersic, R_sersic, k_eff) fxx, _, _, fyy = self.sersic_2.hessian(xvalues, 0, n_sersic, R_sersic, k_eff, e1, e2) npt.assert_almost_equal(kappa_ellipse, 0.5 * (fxx + fyy), decimal=5) def test_sersic_util(self): n = 1. Re = 2. k, bn = self.sersic.k_bn(n, Re) Re_new = self.sersic.k_Re(n, k) assert Re == Re_new