def test_radial_profile(self): Rs = 1. kwargs_light = [{'Rs': Rs, 'amp': 1., 'center_x': 0, 'center_y': 0}] kwargs_options = {'light_model_list': ['HERNQUIST']} lightModel = LightModel(**kwargs_options) profile = LightProfileAnalysis(light_model=lightModel) r_list = np.linspace(start=0.01, stop=10, num=10) I_r = profile.radial_light_profile(r_list, kwargs_light, center_x=None, center_y=None, model_bool_list=None) I_r_true = lightModel.surface_brightness(r_list, 0, kwargs_light) npt.assert_almost_equal(I_r, I_r_true, decimal=5) # test off-center Rs = 1. kwargs_light = [{'Rs': Rs, 'amp': 1., 'center_x': 1., 'center_y': 0}] kwargs_options = {'light_model_list': ['HERNQUIST']} lightModel = LightModel(**kwargs_options) profile = LightProfileAnalysis(light_model=lightModel) r_list = np.linspace(start=0.01, stop=10, num=10) I_r = profile.radial_light_profile(r_list, kwargs_light, center_x=None, center_y=None, model_bool_list=None) I_r_true = lightModel.surface_brightness(r_list + 1, 0, kwargs_light) npt.assert_almost_equal(I_r, I_r_true, decimal=5)
def test_merge_low_high_res(self): subpixel_x, subpixel_y = self._adaptive_grid._high_res_coordinates x, y = self._adaptive_grid._x_low_res, self._adaptive_grid._x_low_res model = LightModel(light_model_list=['GAUSSIAN']) kwargs_light = [{ 'center_x': 0, 'center_y': 0, 'sigma_x': 1, 'sigma_y': 1, 'amp': 1 }] subgrid_values = model.surface_brightness(subpixel_x, subpixel_y, kwargs_light) image1d = model.surface_brightness(x, y, kwargs_light) image_added = self._adaptive_grid._merge_low_high_res( image1d, subgrid_values) added_array = util.image2array(image_added) supersampled_values = self._adaptive_grid._average_subgrid( subgrid_values) assert added_array[util.image2array( self._supersampling_indexes)] == supersampled_values image_high_res = self._adaptive_grid._high_res_image(subgrid_values) assert len(image_high_res) == self.nx * self._supersampling_factor
class TestLightModel(object): """ tests the source model routines """ def setup(self): self.light_model_list = ['GAUSSIAN', 'MULTI_GAUSSIAN', 'SERSIC', 'SERSIC_ELLIPSE', 'DOUBLE_SERSIC', 'CORE_SERSIC', 'DOUBLE_CORE_SERSIC', 'BULDGE_DISK', 'SHAPELETS', 'HERNQUIST', 'HERNQUIST_ELLIPSE', 'PJAFFE', 'PJAFFE_ELLIPSE', 'UNIFORM', 'NONE' ] self.kwargs = [ {'amp': 1., 'sigma_x': 1, 'sigma_y': 1., 'center_x': 0, 'center_y': 0}, # 'GAUSSIAN' {'amp': [1., 2], 'sigma': [1, 3], 'center_x': 0, 'center_y': 0}, # 'MULTI_GAUSSIAN' {'I0_sersic': 1, 'R_sersic': 0.5, 'n_sersic': 1, 'center_x': 0, 'center_y': 0}, # 'SERSIC' {'I0_sersic': 1, 'R_sersic': 0.5, 'n_sersic': 1, 'q': 0.8, 'phi_G': 0.5, 'center_x': 0, 'center_y': 0}, # 'SERSIC_ELLIPSE' {'I0_sersic': 1, 'R_sersic': 0.5, 'n_sersic': 1, 'q': 0.8, 'phi_G': 0.5, 'center_x': 0, 'center_y': 0, 'I0_2': 1, 'R_2': 0.05, 'n_2': 2, 'phi_G_2': 0, 'q_2': 1}, # 'DOUBLE_SERSIC' {'I0_sersic': 1, 'R_sersic': 0.5, 'Re': 0.1, 'gamma': 2., 'n_sersic': 1, 'q': 0.8, 'phi_G': 0.5, 'center_x': 0, 'center_y': 0}, # 'CORE_SERSIC' {'I0_sersic': 1, 'R_sersic': 0.5, 'Re': 0.1, 'gamma': 2., 'n_sersic': 1, 'q': 0.8, 'phi_G': 0.5, 'center_x': 0, 'center_y': 0, 'I0_2': 1, 'R_2': 0.05, 'n_2': 2, 'phi_G_2': 0, 'q_2': 1}, # 'DOUBLE_CORE_SERSIC' {'I0_b': 1, 'R_b': 0.1, 'phi_G_b': 0, 'q_b': 1, 'I0_d': 2, 'R_d': 1, 'phi_G_d': 0.5, 'q_d': 0.7, 'center_x': 0, 'center_y': 0}, # BULDGE_DISK {'amp': [1, 1, 1], 'beta': 0.5, 'n_max': 1, 'center_x': 0, 'center_y': 0}, # 'SHAPELETS' {'sigma0': 1, 'Rs': 0.5, 'center_x': 0, 'center_y': 0}, # 'HERNQUIST' {'sigma0': 1, 'Rs': 0.5, 'center_x': 0, 'center_y': 0, 'q': 0.8, 'phi_G': 0}, # 'HERNQUIST_ELLIPSE' {'sigma0': 1, 'Ra': 1, 'Rs': 0.5, 'center_x': 0, 'center_y': 0}, # 'PJAFFE' {'sigma0': 1, 'Ra': 1, 'Rs': 0.5, 'center_x': 0, 'center_y': 0, 'q': 0.8, 'phi_G': 0}, # 'PJAFFE_ELLIPSE' {'mean': 1}, # 'UNIFORM' {}]# 'NONE' self.LightModel = LightModel(light_model_list=self.light_model_list) def test_init(self): model_list = ['CORE_SERSIC', 'DOUBLE_CORE_SERSIC', 'BULDGE_DISK', 'SHAPELETS', 'UNIFORM'] lightModel = LightModel(light_model_list=model_list) assert len(lightModel.profile_type_list) == len(model_list) def test_surface_brightness(self): output = self.LightModel.surface_brightness(x=1, y=1, kwargs_list=self.kwargs) npt.assert_almost_equal(output, 2.544428612985992, decimal=6) def test_surface_brightness_array(self): output = self.LightModel.surface_brightness(x=[1], y=[1], kwargs_list=self.kwargs) npt.assert_almost_equal(output[0], 2.544428612985992, decimal=6) def test_functions_split(self): output = self.LightModel.functions_split(x=1., y=1., kwargs_list=self.kwargs) assert output[0][0] == 0.058549831524319168 def test_re_normalize_flux(self): kwargs_out = self.LightModel.re_normalize_flux(kwargs_list=self.kwargs, norm_factor=2) assert kwargs_out[0]['amp'] == 2 * self.kwargs[0]['amp']
def test_multi_gaussian_decomposition_ellipse(self): Rs = 1. kwargs_light = [{'Rs': Rs, 'amp': 1., 'center_x': 0, 'center_y': 0}] kwargs_options = {'light_model_list': ['HERNQUIST']} lightModel = LightModel(**kwargs_options) profile = LightProfileAnalysis(light_model=lightModel) kwargs_mge = profile.multi_gaussian_decomposition_ellipse( kwargs_light, grid_spacing=0.01, grid_num=100, model_bool_list=None, n_comp=20, center_x=None, center_y=None) mge = MultiGaussianEllipse() r_array = np.logspace(start=-2, stop=0.5, num=10) flux = mge.function(r_array, 0, **kwargs_mge) flux_true = lightModel.surface_brightness(r_array, 0, kwargs_light) npt.assert_almost_equal(flux / flux_true, 1, decimal=2) # elliptic Rs = 1. kwargs_light = [{ 'Rs': Rs, 'amp': 1., 'e1': 0.1, 'e2': 0, 'center_x': 0, 'center_y': 0 }] kwargs_options = {'light_model_list': ['HERNQUIST_ELLIPSE']} lightModel = LightModel(**kwargs_options) profile = LightProfileAnalysis(light_model=lightModel) kwargs_mge = profile.multi_gaussian_decomposition_ellipse( kwargs_light, grid_spacing=0.1, grid_num=400, model_bool_list=None, n_comp=20, center_x=None, center_y=None) print(kwargs_mge['e1']) mge = MultiGaussianEllipse() r_array = np.logspace(start=-2, stop=0.5, num=10) flux = mge.function(r_array, 0, **kwargs_mge) flux_true = lightModel.surface_brightness(r_array, 0, kwargs_light) npt.assert_almost_equal(flux / flux_true, 1, decimal=1)
def test_derivatives(self): kwargs_arc = { 'tangential_stretch': 3, #'radial_stretch': 1., 'curvature': 0.8, 'direction': 0, 'center_x': 0, 'center_y': 0 } kwargs_arc_sis = { 'tangential_stretch': 3, 'radial_stretch': 1., 'curvature': 0.8, 'direction': 0, 'center_x': 0, 'center_y': 0 } x, y = util.make_grid(numPix=100, deltapix=0.01) f_x_sis, f_y_sis = self.arc_sis.derivatives(x, y, **kwargs_arc_sis) beta_x_sis = x - f_x_sis beta_y_sis = y - f_y_sis f_x_const, f_y_const = self.arc_const.derivatives(x, y, **kwargs_arc) beta_x_const = x - f_x_const beta_y_const = y - f_y_const from lenstronomy.LightModel.light_model import LightModel gauss = LightModel(['GAUSSIAN']) kwargs_source = [{ 'amp': 1, 'sigma': 0.05, 'center_x': 0, 'center_y': 0 }] flux_sis = gauss.surface_brightness(beta_x_sis, beta_y_sis, kwargs_source) flux_const = gauss.surface_brightness(beta_x_const, beta_y_const, kwargs_source) npt.assert_almost_equal((flux_const - flux_sis) / np.max(flux_const), 0, decimal=2) # check for stability outside the defined bounds of curvature f_x_const, f_y_const = self.arc_const.derivatives(x=0, y=1000, **kwargs_arc) npt.assert_almost_equal(f_x_const, 0) npt.assert_almost_equal(f_y_const, 0)
def setup(self): lightModel = LightModel(light_model_list=['GAUSSIAN']) self.delta_pix = 1 self.num_pix = 10 self.num_pix_kernel = 7 x, y = util.make_grid(numPix=self.num_pix_kernel, deltapix=self.delta_pix) kwargs_kernel = [{'amp': 1, 'sigma': 3, 'center_x': 0, 'center_y': 0}] kernel = lightModel.surface_brightness(x, y, kwargs_kernel) self.kernel = util.array2image(kernel) self.kernel /= np.sum(self.kernel) x, y = util.make_grid(numPix=self.num_pix, deltapix=self.delta_pix) kwargs = [{'amp': 1, 'sigma': 2, 'center_x': 0, 'center_y': 0}] flux = lightModel.surface_brightness(x, y, kwargs) self.model = util.array2image(flux)
def source_flux_rh(self, kwargs_result, deltaPix_s, numPix): """ A function to calculate flux, half light radius of the given modeling result. :param kwargs_results: modeling result :param deltaPix: pixel scale in the source plane :param numPix: pixel numbers in the source plane :return: flux (cts/s) and R_e (") in the source plane """ imageModel = class_creator.create_im_sim( self.multi_band_list, multi_band_type='single-band', kwargs_model=self.kwargs_model, bands_compute=[True], band_index=0) kwargs_source = kwargs_result['kwargs_source'] _, _, _, _ = imageModel.image_linear_solve(inv_bool=True, **kwargs_result) x_center = kwargs_source[0]['center_x'] y_center = kwargs_source[0]['center_y'] x_grid_source, y_grid_source = util.make_grid(numPix=numPix, deltapix=deltaPix_s) x_grid_source += x_center y_grid_source += y_center source_light_model = self.kwargs_model['source_light_model_list'] lightModel = LightModel(light_model_list=source_light_model) flux = lightModel.surface_brightness(x_grid_source, y_grid_source, kwargs_source) * deltaPix_s**2 rh = half_light_radius(flux, x_grid_source, y_grid_source, x_center, y_center) return flux, rh
def test_flux_array2image_low_high(self): x, y = self._adaptive_grid.coordinates_evaluate model = LightModel(light_model_list=['GAUSSIAN']) kwargs_light = [{'center_x': 0, 'center_y': 0, 'sigma': 1, 'amp': 1}] flux_values = model.surface_brightness(x, y, kwargs_light) image_low_res, image_high_res = self._adaptive_grid.flux_array2image_low_high(flux_values) assert len(image_high_res) == self.nx * self._supersampling_factor
def test_average_subgrid(self): subpixel_x, subpixel_y = self._adaptive_grid._high_res_coordinates model = LightModel(light_model_list=['GAUSSIAN']) kwargs_light = [{'center_x': 0, 'center_y': 0, 'sigma': 1, 'amp': 1}] subgrid_values = model.surface_brightness(subpixel_x, subpixel_y, kwargs_light) supersampled_values = self._adaptive_grid._average_subgrid(subgrid_values) assert len(supersampled_values) == 1
def test_delete_interpol_caches(self): x, y = util.make_grid(numPix=20, deltapix=1.) gauss = Gaussian() flux = gauss.function(x, y, amp=1., center_x=0., center_y=0., sigma=1.) image = util.array2image(flux) light_model_list = ['INTERPOL', 'INTERPOL'] kwargs_list = [{ 'image': image, 'scale': 1, 'phi_G': 0, 'center_x': 0, 'center_y': 0 }, { 'image': image, 'scale': 1, 'phi_G': 0, 'center_x': 0, 'center_y': 0 }] lightModel = LightModel(light_model_list=light_model_list) output = lightModel.surface_brightness(x, y, kwargs_list) for func in lightModel.func_list: assert hasattr(func, '_image_interp') lightModel.delete_interpol_caches() for func in lightModel.func_list: assert not hasattr(func, '_image_interp')
def test_sersic_vs_hernquist_kinematics(self): """ attention: this test only works for Sersic indices > \approx 2! Lower n_sersic will result in different predictions with the Hernquist assumptions replacing the correct Light model! :return: """ # anisotropy profile anisotropy_type = 'OsipkovMerritt' r_ani = 2. kwargs_anisotropy = {'r_ani': r_ani} # anisotropy radius [arcsec] # aperture as slit aperture_type = 'slit' length = 3.8 width = 0.9 kwargs_aperture = {'length': length, 'width': width, 'center_ra': 0, 'center_dec': 0, 'angle': 0} psf_fwhm = 0.7 # Gaussian FWHM psf kwargs_cosmo = {'D_d': 1000, 'D_s': 1500, 'D_ds': 800} # light profile light_profile_list = ['SERSIC'] r_sersic = .3 n_sersic = 2.8 kwargs_light = [{'amp': 1., 'R_sersic': r_sersic, 'n_sersic': n_sersic}] # effective half light radius (2d projected) in arcsec # mass profile mass_profile_list = ['SPP'] theta_E = 1.2 gamma = 2. kwargs_profile = [{'theta_E': theta_E, 'gamma': gamma}] # Einstein radius (arcsec) and power-law slope # Hernquist fit to Sersic profile lens_analysis = LensAnalysis({'lens_light_model_list': ['SERSIC'], 'lens_model_list': []}) r_eff = lens_analysis.half_light_radius_lens(kwargs_light, deltaPix=0.1, numPix=100) print(r_eff) light_profile_list_hernquist = ['HERNQUIST'] kwargs_light_hernquist = [{'Rs': r_eff*0.551, 'amp': 1.}] # mge of light profile lightModel = LightModel(light_profile_list) r_array = np.logspace(-3, 2, 100) * r_eff * 2 print(r_sersic/r_eff, 'r_sersic/r_eff') flux_r = lightModel.surface_brightness(r_array, 0, kwargs_light) amps, sigmas, norm = mge.mge_1d(r_array, flux_r, N=20) light_profile_list_mge = ['MULTI_GAUSSIAN'] kwargs_light_mge = [{'amp': amps, 'sigma': sigmas}] print(amps, sigmas, 'amp', 'sigma') galkin = Galkin(mass_profile_list, light_profile_list_hernquist, aperture_type=aperture_type, anisotropy_model=anisotropy_type, fwhm=psf_fwhm, kwargs_cosmo=kwargs_cosmo) sigma_v = galkin.vel_disp(kwargs_profile, kwargs_light_hernquist, kwargs_anisotropy, kwargs_aperture) galkin = Galkin(mass_profile_list, light_profile_list_mge, aperture_type=aperture_type, anisotropy_model=anisotropy_type, fwhm=psf_fwhm, kwargs_cosmo=kwargs_cosmo) sigma_v2 = galkin.vel_disp(kwargs_profile, kwargs_light_mge, kwargs_anisotropy, kwargs_aperture) print(sigma_v, sigma_v2, 'sigma_v Galkin, sigma_v MGEn') print((sigma_v/sigma_v2)**2) npt.assert_almost_equal((sigma_v-sigma_v2)/sigma_v2, 0, decimal=1)
def setup(self): lightModel = LightModel(light_model_list=['GAUSSIAN']) self.delta_pix = 1 x, y = util.make_grid(10, deltapix=self.delta_pix) kwargs = [{'amp': 1, 'sigma': 2, 'center_x': 0, 'center_y': 0}] flux = lightModel.surface_brightness(x, y, kwargs) self.model = util.array2image(flux)
def test_spt_mapping(self): e1, e2 = 0.1, -0.2 kwargs_arc_sis_mst = { 'tangential_stretch': 3, 'radial_stretch': 1.2, 'curvature': 0.8, 'direction': 0, 'center_x': 0, 'center_y': 0 } # inverse reduced shear transform as SPT kwargs_arc_spt = copy.deepcopy(kwargs_arc_sis_mst) kwargs_arc_spt['gamma1'] = -e1 kwargs_arc_spt['gamma2'] = -e2 x, y = util.make_grid(numPix=100, deltapix=0.01) f_x_sis, f_y_sis = self._curve_regular.derivatives( x, y, **kwargs_arc_sis_mst) beta_x_sis = x - f_x_sis beta_y_sis = y - f_y_sis f_x_spt, f_y_spt = self._curve_spt.derivatives(x, y, **kwargs_arc_spt) beta_x_spt = x - f_x_spt beta_y_spt = y - f_y_spt from lenstronomy.LightModel.light_model import LightModel gauss = LightModel(['GAUSSIAN_ELLIPSE']) kwargs_source = [{ 'amp': 1, 'sigma': 0.05, 'center_x': 0, 'center_y': 0, 'e1': 0, 'e2': 0 }] kwargs_source_spt = copy.deepcopy(kwargs_source) kwargs_source_spt[0]['e1'] = e1 kwargs_source_spt[0]['e2'] = e2 flux_sis = gauss.surface_brightness(beta_x_sis, beta_y_sis, kwargs_source) flux_spt = gauss.surface_brightness(beta_x_spt, beta_y_spt, kwargs_source_spt) npt.assert_almost_equal(flux_sis, flux_spt)
def setup(self): self.supersampling_factor = 3 lightModel = LightModel(light_model_list=['GAUSSIAN']) self.delta_pix = 1. x, y = util.make_grid(20, deltapix=self.delta_pix) x_sub, y_sub = util.make_grid(20*self.supersampling_factor, deltapix=self.delta_pix/self.supersampling_factor) kwargs = [{'amp': 1, 'sigma': 2, 'center_x': 0, 'center_y': 0}] flux = lightModel.surface_brightness(x, y, kwargs) self.model = util.array2image(flux) flux_sub = lightModel.surface_brightness(x_sub, y_sub, kwargs) self.model_sub = util.array2image(flux_sub) x, y = util.make_grid(5, deltapix=self.delta_pix) kwargs_kernel = [{'amp': 1, 'sigma': 1, 'center_x': 0, 'center_y': 0}] kernel = lightModel.surface_brightness(x, y, kwargs_kernel) self.kernel = util.array2image(kernel) / np.sum(kernel) x_sub, y_sub = util.make_grid(5*self.supersampling_factor, deltapix=self.delta_pix/self.supersampling_factor) kernel_sub = lightModel.surface_brightness(x_sub, y_sub, kwargs_kernel) self.kernel_sub = util.array2image(kernel_sub) / np.sum(kernel_sub)
def test_mge_light_and_mass(self): # anisotropy profile anisotropy_type = 'OsipkovMerritt' r_ani = 2. kwargs_anisotropy = {'r_ani': r_ani} # anisotropy radius [arcsec] # aperture as slit aperture_type = 'slit' length = 3.8 width = 0.9 kwargs_aperture = {'length': length, 'width': width, 'center_ra': 0, 'center_dec': 0, 'angle': 0} psf_fwhm = 0.7 # Gaussian FWHM psf kwargs_cosmo = {'D_d': 1000, 'D_s': 1500, 'D_ds': 800} # light profile light_profile_list = ['HERNQUIST'] r_eff = 1.8 kwargs_light = [{'Rs': r_eff, 'amp': 1.}] # effective half light radius (2d projected) in arcsec # mass profile mass_profile_list = ['SPP'] theta_E = 1.2 gamma = 2. kwargs_profile = [{'theta_E': theta_E, 'gamma': gamma}] # Einstein radius (arcsec) and power-law slope # mge of light profile lightModel = LightModel(light_profile_list) r_array = np.logspace(-2, 2, 200) * r_eff * 2 flux_r = lightModel.surface_brightness(r_array, 0, kwargs_light) amps, sigmas, norm = mge.mge_1d(r_array, flux_r, N=20) light_profile_list_mge = ['MULTI_GAUSSIAN'] kwargs_light_mge = [{'amp': amps, 'sigma': sigmas}] # mge of lens profile lensModel = LensModel(mass_profile_list) r_array = np.logspace(-2, 2, 200) kappa_r = lensModel.kappa(r_array, 0, kwargs_profile) amps, sigmas, norm = mge.mge_1d(r_array, kappa_r, N=20) mass_profile_list_mge = ['MULTI_GAUSSIAN_KAPPA'] kwargs_profile_mge = [{'amp': amps, 'sigma': sigmas}] galkin = Galkin(mass_profile_list, light_profile_list, aperture_type=aperture_type, anisotropy_model=anisotropy_type, fwhm=psf_fwhm, kwargs_cosmo=kwargs_cosmo) sigma_v = galkin.vel_disp(kwargs_profile, kwargs_light, kwargs_anisotropy, kwargs_aperture) galkin = Galkin(mass_profile_list_mge, light_profile_list_mge, aperture_type=aperture_type, anisotropy_model=anisotropy_type, fwhm=psf_fwhm, kwargs_cosmo=kwargs_cosmo) sigma_v2 = galkin.vel_disp(kwargs_profile_mge, kwargs_light_mge, kwargs_anisotropy, kwargs_aperture) print(sigma_v, sigma_v2, 'sigma_v Galkin, sigma_v MGEn') print((sigma_v/sigma_v2)**2) npt.assert_almost_equal((sigma_v-sigma_v2)/sigma_v2, 0, decimal=2)
def setup(self): lightModel = LightModel(light_model_list=['GAUSSIAN']) self.supersampling_factor = 3 self.delta_pix = 1 self.num_pix = 10 self.num_pix_kernel = 7 x, y = util.make_grid(numPix=self.num_pix_kernel, deltapix=self.delta_pix) kwargs_kernel = [{'amp': 1, 'sigma': 3, 'center_x': 0, 'center_y': 0}] kernel = lightModel.surface_brightness(x, y, kwargs_kernel) self.kernel = util.array2image(kernel) self.kernel /= np.sum(self.kernel) x_sub, y_sub = util.make_grid(numPix=self.num_pix_kernel, deltapix=self.delta_pix, subgrid_res=self.supersampling_factor) kernel_super = lightModel.surface_brightness(x_sub, y_sub, kwargs_kernel) self.kernel_super = util.array2image(kernel_super) self.kernel_super /= np.sum(self.kernel_super) x_sub, y_sub = util.make_grid(numPix=self.num_pix, deltapix=self.delta_pix, subgrid_res=self.supersampling_factor) kwargs = [{'amp': 1, 'sigma': 2, 'center_x': 0, 'center_y': 0}] flux = lightModel.surface_brightness(x_sub, y_sub, kwargs) self.model_super = util.array2image(flux) self.model = image_util.re_size(self.model_super, factor=self.supersampling_factor)
def test_multi_gaussian_decomposition(self): Rs = 1. kwargs_light = [{'Rs': Rs, 'amp': 1., 'center_x': 0, 'center_y': 0}] kwargs_options = {'light_model_list': ['HERNQUIST']} lightModel = LightModel(**kwargs_options) profile = LightProfileAnalysis(light_model=lightModel) amplitudes, sigmas, center_x, center_y = profile.multi_gaussian_decomposition(kwargs_light, grid_spacing=0.01, grid_num=100, model_bool_list=None, n_comp=20, center_x=None, center_y=None) mge = MultiGaussian() r_array = np.logspace(start=-2, stop=0.5, num=10) print(r_array, 'test r_array') flux = mge.function(r_array, 0, amp=amplitudes, sigma=sigmas, center_x=center_x, center_y=center_y) flux_true = lightModel.surface_brightness(r_array, 0, kwargs_light) npt.assert_almost_equal(flux / flux_true, 1, decimal=2) # test off-center Rs = 1. offset = 1. kwargs_light = [{'Rs': Rs, 'amp': 1., 'center_x': offset, 'center_y': 0}] kwargs_options = {'light_model_list': ['HERNQUIST']} lightModel = LightModel(**kwargs_options) profile = LightProfileAnalysis(light_model=lightModel) amplitudes, sigmas, center_x, center_y = profile.multi_gaussian_decomposition(kwargs_light, grid_spacing=0.01, grid_num=100, model_bool_list=None, n_comp=20, center_x=None, center_y=None) assert center_x == offset assert center_y == 0 mge = MultiGaussian() r_array = np.logspace(start=-2, stop=0.5, num=10) print(r_array, 'test r_array') flux = mge.function(r_array, 0, amp=amplitudes, sigma=sigmas, center_x=center_x, center_y=center_y) flux_true = lightModel.surface_brightness(r_array, 0, kwargs_light) npt.assert_almost_equal(flux / flux_true, 1, decimal=2) """
def source_plane(self, kwargs_options, kwargs_source, numPix, deltaPix): """ source plane simulation :param kwargs_options: :param kwargs_source: :param numPix: :param deltaPix: :return: """ x, y = util.make_grid(numPix, deltaPix) sourceModel = LightModel(kwargs_options.get('source_light_model_list', ['NONE'])) image1d = sourceModel.surface_brightness(x, y, kwargs_source) image2d = util.array2image(image1d) return image2d
class DifferentialExtinction(object): """ class to compute an extinction (for a specific band/wavelength). This class uses the functionality available in the LightModel module to describe an optical depth tau_ext to compute the extinction on the sky/image. """ def __init__(self, optical_depth_model=None, tau0_index=0): """ :param optical_depth_model: list of strings naming the profiles (same convention as LightModel module) describing the optical depth of the extinction """ if optical_depth_model is None: optical_depth_model = [] self._profile = LightModel(light_model_list=optical_depth_model) if len(optical_depth_model) == 0: self._compute_bool = False else: self._compute_bool = True self._tau0_index = tau0_index @property def compute_bool(self): """ :return: True when a differential extinction is set, False otherwise """ return self._compute_bool def extinction(self, x, y, kwargs_extinction=None, kwargs_special=None): """ :param x: coordinate in image plane of flux intensity :param y: coordinate in image plane of flux intensity :param kwargs_extinction: keyword argument list matching the extinction profile :param kwargs_special: keyword arguments hosting special parameters, here required 'tau0_list' :return: extinction corrected flux """ if self._compute_bool is False or kwargs_extinction is None: return 1 tau = self._profile.surface_brightness(x, y, kwargs_list=kwargs_extinction) tau0_list = kwargs_special.get('tau0_list', None) if tau0_list is not None: tau0 = tau0_list[self._tau0_index] else: tau0 = 1 return np.exp(-tau0 * tau)
def test_light2mass_conversion(self): numPix = 100 deltaPix = 0.05 lightModel = LightModel(light_model_list=['SERSIC_ELLIPSE', 'SERSIC']) kwargs_lens_light = [{ 'R_sersic': 0.5, 'n_sersic': 4, 'amp': 2, 'e1': 0, 'e2': 0.05 }, { 'R_sersic': 1.5, 'n_sersic': 1, 'amp': 2 }] kwargs_interpol = light2mass.light2mass_interpol( lens_light_model_list=['SERSIC_ELLIPSE', 'SERSIC'], kwargs_lens_light=kwargs_lens_light, numPix=numPix, deltaPix=deltaPix, subgrid_res=1) from lenstronomy.LensModel.lens_model import LensModel lensModel = LensModel(lens_model_list=['INTERPOL_SCALED']) kwargs_lens = [kwargs_interpol] import lenstronomy.Util.util as util x_grid, y_grid = util.make_grid(numPix, deltapix=deltaPix) kappa = lensModel.kappa(x_grid, y_grid, kwargs=kwargs_lens) kappa = util.array2image(kappa) kappa /= np.mean(kappa) flux = lightModel.surface_brightness(x_grid, y_grid, kwargs_lens_light) flux = util.array2image(flux) flux /= np.mean(flux) # import matplotlib.pyplot as plt # plt.matshow(flux-kappa) # plt.colorbar() # plt.show() delta_kappa = (kappa - flux) / flux max_delta = np.max(np.abs(delta_kappa)) assert max_delta < 1 # assert max_diff < 0.01 npt.assert_almost_equal(flux[0, 0], kappa[0, 0], decimal=2)
def kinematic_profiles(self, kwargs_lens, kwargs_lens_light, r_eff, MGE_light=False, MGE_mass=False, lens_model_kinematics_bool=None, light_model_kinematics_bool=None, Hernquist_approx=False): """ translates the lenstronomy lens and mass profiles into a (sub) set of profiles that are compatible with the GalKin module to compute the kinematics thereof. :param kwargs_lens: lens model parameters :param kwargs_lens_light: lens light parameters :param r_eff: a rough estimate of the half light radius of the lens light in case of computing the MGE of the light profile :param MGE_light: bool, if true performs the MGE for the light distribution :param MGE_mass: bool, if true performs the MGE for the mass distribution :param lens_model_kinematics_bool: bool list of length of the lens model. Only takes a subset of all the models as part of the kinematics computation (can be used to ignore substructure, shear etc that do not describe the main deflector potential :param light_model_kinematics_bool: bool list of length of the light model. Only takes a subset of all the models as part of the kinematics computation (can be used to ignore light components that do not describe the main deflector :param Hernquist_approx: bool, if True, uses a Hernquist light profile matched to the half light radius of the deflector light profile to compute the kinematics :return: mass_profile_list, kwargs_profile, light_profile_list, kwargs_light """ mass_profile_list = [] kwargs_profile = [] if lens_model_kinematics_bool is None: lens_model_kinematics_bool = [True] * len(kwargs_lens) for i, lens_model in enumerate(self.kwargs_options['lens_model_list']): if lens_model_kinematics_bool[i] is True: mass_profile_list.append(lens_model) if lens_model in ['INTERPOL', 'INTERPOL_SCLAED']: center_x, center_y = self._lensModelExt.lens_center( kwargs_lens, k=i) kwargs_lens_i = copy.deepcopy(kwargs_lens[i]) kwargs_lens_i['grid_interp_x'] -= center_x kwargs_lens_i['grid_interp_y'] -= center_y else: kwargs_lens_i = { k: v for k, v in kwargs_lens[i].items() if not k in ['center_x', 'center_y'] } kwargs_profile.append(kwargs_lens_i) if MGE_mass is True: lensModel = LensModel(lens_model_list=mass_profile_list) massModel = LensModelExtensions(lensModel) theta_E = massModel.effective_einstein_radius(kwargs_profile) r_array = np.logspace(-4, 2, 200) * theta_E mass_r = lensModel.kappa(r_array, np.zeros_like(r_array), kwargs_profile) amps, sigmas, norm = mge.mge_1d(r_array, mass_r, N=20) mass_profile_list = ['MULTI_GAUSSIAN_KAPPA'] kwargs_profile = [{'amp': amps, 'sigma': sigmas}] light_profile_list = [] kwargs_light = [] if light_model_kinematics_bool is None: light_model_kinematics_bool = [True] * len(kwargs_lens_light) for i, light_model in enumerate( self.kwargs_options['lens_light_model_list']): if light_model_kinematics_bool[i]: light_profile_list.append(light_model) kwargs_lens_light_i = { k: v for k, v in kwargs_lens_light[i].items() if not k in ['center_x', 'center_y'] } if 'e1' in kwargs_lens_light_i: kwargs_lens_light_i['e1'] = 0 kwargs_lens_light_i['e2'] = 0 kwargs_light.append(kwargs_lens_light_i) if Hernquist_approx is True: light_profile_list = ['HERNQUIST'] kwargs_light = [{'Rs': r_eff, 'amp': 1.}] else: if MGE_light is True: lightModel = LightModel(light_profile_list) r_array = np.logspace(-3, 2, 200) * r_eff * 2 flux_r = lightModel.surface_brightness(r_array, 0, kwargs_light) amps, sigmas, norm = mge.mge_1d(r_array, flux_r, N=20) light_profile_list = ['MULTI_GAUSSIAN'] kwargs_light = [{'amp': amps, 'sigma': sigmas}] return mass_profile_list, kwargs_profile, light_profile_list, kwargs_light
class TestLightModel(object): """ tests the source model routines """ def setup(self): self.light_model_list = [ 'GAUSSIAN', 'MULTI_GAUSSIAN', 'SERSIC', 'SERSIC_ELLIPSE', 'CORE_SERSIC', 'SHAPELETS', 'HERNQUIST', 'HERNQUIST_ELLIPSE', 'PJAFFE', 'PJAFFE_ELLIPSE', 'UNIFORM', 'POWER_LAW', 'NIE', 'INTERPOL', 'SHAPELETS_POLAR_EXP', 'ELLIPSOID' ] phi_G, q = 0.5, 0.8 e1, e2 = param_util.phi_q2_ellipticity(phi_G, q) self.kwargs = [ { 'amp': 1., 'sigma': 1., 'center_x': 0, 'center_y': 0 }, # 'GAUSSIAN' { 'amp': [1., 2], 'sigma': [1, 3], 'center_x': 0, 'center_y': 0 }, # 'MULTI_GAUSSIAN' { 'amp': 1, 'R_sersic': 0.5, 'n_sersic': 1, 'center_x': 0, 'center_y': 0 }, # 'SERSIC' { 'amp': 1, 'R_sersic': 0.5, 'n_sersic': 1, 'e1': e1, 'e2': e2, 'center_x': 0, 'center_y': 0 }, # 'SERSIC_ELLIPSE' { 'amp': 1, 'R_sersic': 0.5, 'Rb': 0.1, 'gamma': 2., 'n_sersic': 1, 'e1': e1, 'e2': e2, 'center_x': 0, 'center_y': 0 }, # 'CORE_SERSIC' { 'amp': [1, 1, 1], 'beta': 0.5, 'n_max': 1, 'center_x': 0, 'center_y': 0 }, # 'SHAPELETS' { 'amp': 1, 'Rs': 0.5, 'center_x': 0, 'center_y': 0 }, # 'HERNQUIST' { 'amp': 1, 'Rs': 0.5, 'center_x': 0, 'center_y': 0, 'e1': e1, 'e2': e2 }, # 'HERNQUIST_ELLIPSE' { 'amp': 1, 'Ra': 1, 'Rs': 0.5, 'center_x': 0, 'center_y': 0 }, # 'PJAFFE' { 'amp': 1, 'Ra': 1, 'Rs': 0.5, 'center_x': 0, 'center_y': 0, 'e1': e1, 'e2': e2 }, # 'PJAFFE_ELLIPSE' { 'amp': 1 }, # 'UNIFORM' { 'amp': 1., 'gamma': 2., 'e1': e1, 'e2': e2, 'center_x': 0, 'center_y': 0 }, # 'POWER_LAW' { 'amp': .001, 'e1': 0, 'e2': 1., 'center_x': 0, 'center_y': 0, 's_scale': 1. }, # 'NIE' { 'image': np.zeros((20, 5)), 'scale': 1, 'phi_G': 0, 'center_x': 0, 'center_y': 0 }, { 'amp': [1], 'n_max': 0, 'beta': 1, 'center_x': 0, 'center_y': 0 }, { 'amp': 1, 'radius': 1., 'e1': 0, 'e2': 0.1, 'center_x': 0, 'center_y': 0 } # 'ELLIPSOID' ] self.LightModel = LightModel(light_model_list=self.light_model_list) def test_init(self): model_list = [ 'CORE_SERSIC', 'SHAPELETS', 'SHAPELETS_POLAR', 'SHAPELETS_POLAR_EXP', 'UNIFORM', 'CHAMELEON', 'DOUBLE_CHAMELEON', 'TRIPLE_CHAMELEON' ] lightModel = LightModel(light_model_list=model_list) assert len(lightModel.profile_type_list) == len(model_list) def test_surface_brightness(self): output = self.LightModel.surface_brightness(x=1., y=1., kwargs_list=self.kwargs) npt.assert_almost_equal(output, 2.5886852663397137, decimal=6) def test_surface_brightness_array(self): output = self.LightModel.surface_brightness(x=[1], y=[1], kwargs_list=self.kwargs) npt.assert_almost_equal(output[0], 2.5886852663397137, decimal=6) def test_functions_split(self): output = self.LightModel.functions_split(x=1., y=1., kwargs_list=self.kwargs) npt.assert_almost_equal(output[0][0], 0.058549831524319168, decimal=6) def test_param_name_list(self): param_name_list = self.LightModel.param_name_list assert len(self.light_model_list) == len(param_name_list) def test_num_param_linear(self): num = self.LightModel.num_param_linear(self.kwargs, list_return=False) assert num == 19 num_list = self.LightModel.num_param_linear(self.kwargs, list_return=True) assert num_list[0] == 1 def test_update_linear(self): response, n = self.LightModel.functions_split(1, 1, self.kwargs) param = np.ones(n) * 2 kwargs_out, i = self.LightModel.update_linear(param, i=0, kwargs_list=self.kwargs) assert i == n assert kwargs_out[0]['amp'] == 2 def test_total_flux(self): light_model_list = [ 'SERSIC', 'SERSIC_ELLIPSE', 'INTERPOL', 'GAUSSIAN', 'GAUSSIAN_ELLIPSE', 'MULTI_GAUSSIAN', 'MULTI_GAUSSIAN_ELLIPSE' ] kwargs_list = [ { 'amp': 1, 'R_sersic': 0.5, 'n_sersic': 1, 'center_x': 0, 'center_y': 0 }, # 'SERSIC' { 'amp': 1, 'R_sersic': 0.5, 'n_sersic': 1, 'e1': 0.1, 'e2': 0, 'center_x': 0, 'center_y': 0 }, # 'SERSIC_ELLIPSE' { 'image': np.ones((20, 5)), 'scale': 1, 'phi_G': 0, 'center_x': 0, 'center_y': 0 }, # 'INTERPOL' { 'amp': 2, 'sigma': 2, 'center_x': 0, 'center_y': 0 }, # 'GAUSSIAN' { 'amp': 2, 'sigma': 2, 'e1': 0.1, 'e2': 0, 'center_x': 0, 'center_y': 0 }, # 'GAUSSIAN_ELLIPSE' { 'amp': [1, 1], 'sigma': [2, 1], 'center_x': 0, 'center_y': 0 }, # 'MULTI_GAUSSIAN' { 'amp': [1, 1], 'sigma': [2, 1], 'e1': 0.1, 'e2': 0, 'center_x': 0, 'center_y': 0 } # 'MULTI_GAUSSIAN_ELLIPSE' ] lightModel = LightModel(light_model_list=light_model_list) total_flux_list = lightModel.total_flux(kwargs_list) assert total_flux_list[2] == 100 assert total_flux_list[3] == 2 assert total_flux_list[4] == 2 assert total_flux_list[5] == 2 assert total_flux_list[6] == 2 total_flux_list = lightModel.total_flux(kwargs_list, norm=True) assert total_flux_list[2] == 100 assert total_flux_list[3] == 1 assert total_flux_list[4] == 1 assert total_flux_list[5] == 2 assert total_flux_list[6] == 2 def test_delete_interpol_caches(self): x, y = util.make_grid(numPix=20, deltapix=1.) gauss = Gaussian() flux = gauss.function(x, y, amp=1., center_x=0., center_y=0., sigma=1.) image = util.array2image(flux) light_model_list = ['INTERPOL', 'INTERPOL'] kwargs_list = [{ 'image': image, 'scale': 1, 'phi_G': 0, 'center_x': 0, 'center_y': 0 }, { 'image': image, 'scale': 1, 'phi_G': 0, 'center_x': 0, 'center_y': 0 }] lightModel = LightModel(light_model_list=light_model_list) output = lightModel.surface_brightness(x, y, kwargs_list) for func in lightModel.func_list: assert hasattr(func, '_image_interp') lightModel.delete_interpol_caches() for func in lightModel.func_list: assert not hasattr(func, '_image_interp') def test_check_positive_flux_profile(self): ligthModel = LightModel(light_model_list=['GAUSSIAN']) kwargs_list = [{'amp': 0, 'sigma': 1}] bool = ligthModel.check_positive_flux_profile(kwargs_list) assert bool kwargs_list = [{'amp': -1, 'sigma': 1}] bool = ligthModel.check_positive_flux_profile(kwargs_list) assert not bool
class LightProfile(object): """ class to deal with the light distribution """ def __init__(self, profile_list=['HERNQUIST'], kwargs_numerics={}): """ :param profile_list: """ self.light_model = LightModel(light_model_list=profile_list, smoothing=0.000001) self._interp_grid_num = kwargs_numerics.get('interpol_grid_num', 1000) self._max_interpolate = kwargs_numerics.get('max_integrate', 100) self._min_interpolate = kwargs_numerics.get('min_integrate', 0.001) def light_3d(self, r, kwargs_list): """ :param kwargs_list: :return: """ light_3d = self.light_model.light_3d(r, kwargs_list) return light_3d def light_3d_interp(self, r, kwargs_list, new_compute=False): """ :param kwargs_list: :return: """ if not hasattr(self, '_f_light_3d') or new_compute is True: r_array = np.logspace(np.log10(self._min_interpolate), np.log10(self._max_interpolate), self._interp_grid_num) light_3d_array = self.light_model.light_3d(r_array, kwargs_list) light_3d_array[light_3d_array < 10**(-100)] = 10**(-100) f = interp1d(np.log(r_array), np.log(light_3d_array), fill_value="extrapolate") self._f_light_3d = f return np.exp(self._f_light_3d(np.log(r))) def light_2d(self, R, kwargs_list): """ :param R: :param kwargs_list: :return: """ kwargs_list_copy = copy.deepcopy(kwargs_list) kwargs_list_new = [] for kwargs in kwargs_list_copy: if 'e1' in kwargs: kwargs['e1'] = 0 if 'e2' in kwargs: kwargs['e2'] = 0 kwargs_list_new.append({ k: v for k, v in kwargs.items() if not k in ['center_x', 'center_y'] }) return self.light_model.surface_brightness(R, 0, kwargs_list_new) def draw_light_2d_linear(self, kwargs_list, n=1, new_compute=False, r_eff=1.): """ constructs the CDF and draws from it random realizations of projected radii R :param kwargs_list: :return: """ if not hasattr(self, '_light_cdf') or new_compute is True: r_array = np.linspace(self._min_interpolate, self._max_interpolate, self._interp_grid_num) cum_sum = np.zeros_like(r_array) sum = 0 for i, r in enumerate(r_array): if i == 0: cum_sum[i] = 0 else: sum += self.light_2d(r, kwargs_list) * r cum_sum[i] = copy.deepcopy(sum) cum_sum_norm = cum_sum / cum_sum[-1] f = interp1d(cum_sum_norm, r_array) self._light_cdf = f cdf_draw = np.random.uniform(0., 1, n) r_draw = self._light_cdf(cdf_draw) return r_draw def draw_light_2d(self, kwargs_list, n=1, new_compute=False): """ constructs the CDF and draws from it random realizations of projected radii R :param kwargs_list: :return: """ if not hasattr(self, '_light_cdf_log') or new_compute is True: r_array = np.logspace(np.log10(self._min_interpolate), np.log10(self._max_interpolate), self._interp_grid_num) cum_sum = np.zeros_like(r_array) sum = 0 for i, r in enumerate(r_array): if i == 0: cum_sum[i] = 0 else: sum += self.light_2d(r, kwargs_list) * r * r cum_sum[i] = copy.deepcopy(sum) cum_sum_norm = cum_sum / cum_sum[-1] f = interp1d(cum_sum_norm, np.log(r_array)) self._light_cdf_log = f cdf_draw = np.random.uniform(0., 1, n) r_log_draw = self._light_cdf_log(cdf_draw) return np.exp(r_log_draw)
class LightProfile(object): """ class to deal with the light distribution for GalKin In particular, this class allows for: - (faster) interpolated calculation for a given profile (for a range that the Jeans equation is computed) - drawing 3d and 2d distributions from a given (spherical) profile (within bounds where the Jeans equation is expected to be accurate) - 2d projected profiles within the 3d integration range (truncated) """ def __init__(self, profile_list, interpol_grid_num=2000, max_interpolate=1000, min_interpolate=0.001, max_draw=None): """ :param profile_list: list of light profiles for LightModel module (must support light_3d() functionalities) :param interpol_grid_num: int; number of interpolation steps (logarithmically between min and max value) :param max_interpolate: float; maximum interpolation of 3d light profile :param min_interpolate: float; minimum interpolate (and also drawing of light profile) :param max_draw: float; (optional) if set, draws up to this radius, else uses max_interpolate value """ self.light_model = LightModel(light_model_list=profile_list) self._interp_grid_num = interpol_grid_num self._max_interpolate = max_interpolate self._min_interpolate = min_interpolate if max_draw is None: max_draw = max_interpolate self._max_draw = max_draw def light_3d(self, r, kwargs_list): """ three-dimensional light profile :param r: 3d radius :param kwargs_list: list of keyword arguments of light profiles (see LightModule) :return: flux per 3d volume at radius r """ light_3d = self.light_model.light_3d(r, kwargs_list) return light_3d def light_3d_interp(self, r, kwargs_list, new_compute=False): """ interpolated three-dimensional light profile within bounds [min_interpolate, max_interpolate] in logarithmic units with interpol_grid_num numbers of interpolation steps :param r: 3d radius :param kwargs_list: list of keyword arguments of light profiles (see LightModule) :param new_compute: boolean, if True, re-computes the interpolation (becomes valid with updated kwargs_list argument) :return: flux per 3d volume at radius r """ if not hasattr(self, '_f_light_3d') or new_compute is True: r_array = np.logspace(np.log10(self._min_interpolate), np.log10(self._max_interpolate), self._interp_grid_num) light_3d_array = self.light_model.light_3d(r_array, kwargs_list) light_3d_array[light_3d_array < 10 ** (-1000)] = 10 ** (-1000) f = interp1d(np.log(r_array), np.log(light_3d_array), fill_value=(np.log(light_3d_array[0]), -1000), bounds_error=False) # "extrapolate" self._f_light_3d = f return np.exp(self._f_light_3d(np.log(r))) def light_2d(self, R, kwargs_list): """ projected light profile (integrated to infinity in the projected axis) :param R: projected 2d radius :param kwargs_list: list of keyword arguments of light profiles (see LightModule) :return: projected surface brightness """ kwargs_light_circularized = self._circularize_kwargs(kwargs_list) return self.light_model.surface_brightness(R, 0, kwargs_light_circularized) def _circularize_kwargs(self, kwargs_list): """ :param kwargs_list: list of keyword arguments of light profiles (see LightModule) :return: circularized arguments """ # TODO make sure averaging is done azimuthally if not hasattr(self, '_kwargs_light_circularized'): kwargs_list_copy = copy.deepcopy(kwargs_list) kwargs_list_new = [] for kwargs in kwargs_list_copy: if 'e1' in kwargs: kwargs['e1'] = 0 if 'e2' in kwargs: kwargs['e2'] = 0 kwargs_list_new.append({k: v for k, v in kwargs.items() if not k in ['center_x', 'center_y']}) self._kwargs_light_circularized = kwargs_list_new return self._kwargs_light_circularized def _light_2d_finite_single(self, R, kwargs_list): """ projected light profile (integrated to FINITE 3d boundaries from the max_interpolate) for a single float number of R :param R: projected 2d radius (between min_interpolate and max_interpolate) :param kwargs_list: list of keyword arguments of light profiles (see LightModule) :return: projected surface brightness """ # here we perform a logarithmic integral stop = np.log10(np.maximum(np.sqrt(self._max_interpolate**2 - R**2), self._min_interpolate + 0.00001)) x = np.logspace(start=np.log10(self._min_interpolate), stop=stop, num=self._interp_grid_num) r_array = np.sqrt(x**2 + R**2) flux_r = self.light_3d(r_array, kwargs_list) dlog_r = (np.log10(x[2]) - np.log10(x[1])) * np.log(10) flux_r *= dlog_r * x # linear integral #x = np.linspace(start=self._min_interpolate, stop=np.sqrt(self._max_interpolate ** 2 - R ** 2), # num=self._interp_grid_num) #r_array = np.sqrt(x ** 2 + R ** 2) #dr = x[1] - x[0] #flux_r = self.light_3d(r_array + dr / 2, kwargs_circ) #dr = x[1] - x[0] #flux_r *= dr flux_R = np.sum(flux_r) # perform finite integral #out = integrate.quad(lambda x: self.light_3d(np.sqrt(R ** 2 + x ** 2), kwargs_circ), self._min_interpolate, # np.sqrt(self._max_interpolate**2 - R**2)) #print(out_1, out, 'test') #flux_R = out[0] return flux_R * 2 # integral in both directions def light_2d_finite(self, R, kwargs_list): """ projected light profile (integrated to FINITE 3d boundaries from the max_interpolate) :param R: projected 2d radius (between min_interpolate and max_interpolate :param kwargs_list: list of keyword arguments of light profiles (see LightModule) :return: projected surface brightness """ kwargs_circ = self._circularize_kwargs(kwargs_list) n = len(np.atleast_1d(R)) if n <= 1: return self._light_2d_finite_single(R, kwargs_circ) else: light_2d = np.zeros(n) for i, R_i in enumerate(R): light_2d[i] = self._light_2d_finite_single(R_i, kwargs_circ) return light_2d def draw_light_2d_linear(self, kwargs_list, n=1, new_compute=False): """ constructs the CDF and draws from it random realizations of projected radii R The interpolation of the CDF is done in linear projected radius space :param kwargs_list: list of keyword arguments of light profiles (see LightModule) :param n: int; number of draws :param new_compute: boolean, if True, re-computes the interpolation (becomes valid with updated kwargs_list argument) :return: draw of projected radius for the given light profile distribution """ if not hasattr(self, '_light_cdf') or new_compute is True: r_array = np.linspace(self._min_interpolate, self._max_draw, self._interp_grid_num) cum_sum = np.zeros_like(r_array) sum = 0 for i, r in enumerate(r_array): if i == 0: cum_sum[i] = 0 else: sum += self.light_2d(r, kwargs_list) * r cum_sum[i] = copy.deepcopy(sum) cum_sum_norm = cum_sum/cum_sum[-1] f = interp1d(cum_sum_norm, r_array) self._light_cdf = f cdf_draw = np.random.uniform(0., 1, n) r_draw = self._light_cdf(cdf_draw) return r_draw def draw_light_2d(self, kwargs_list, n=1, new_compute=False): """ constructs the CDF and draws from it random realizations of projected radii R CDF is constructed in logarithmic projected radius spacing :param kwargs_list: light model keyword argument list :param n: int, number of draws per functino call :param new_compute: re-computes the interpolated CDF :return: realization of projected radius following the distribution of the light model """ if not hasattr(self, '_light_cdf_log') or new_compute is True: r_array = np.logspace(np.log10(self._min_interpolate), np.log10(self._max_draw), self._interp_grid_num) cum_sum = np.zeros_like(r_array) sum = 0 for i, r in enumerate(r_array): if i == 0: cum_sum[i] = 0 else: sum += self.light_2d(r, kwargs_list) * r * r cum_sum[i] = copy.deepcopy(sum) cum_sum_norm = cum_sum/cum_sum[-1] f = interp1d(cum_sum_norm, np.log(r_array)) self._light_cdf_log = f cdf_draw = np.random.uniform(0., 1, n) r_log_draw = self._light_cdf_log(cdf_draw) return np.exp(r_log_draw) def draw_light_3d(self, kwargs_list, n=1, new_compute=False): """ constructs the CDF and draws from it random realizations of 3D radii r :param kwargs_list: light model keyword argument list :param n: int, number of draws per function call :param new_compute: re-computes the interpolated CDF :return: realization of projected radius following the distribution of the light model """ if not hasattr(self, '_light_3d_cdf_log') or new_compute is True: r_array = np.logspace(np.log10(self._min_interpolate), np.log10(self._max_draw), self._interp_grid_num) dlog_r = np.log10(r_array[1]) - np.log10(r_array[0]) r_array_int = np.logspace(np.log10(self._min_interpolate) + dlog_r / 2, np.log10(self._max_draw) + dlog_r / 2, self._interp_grid_num) cum_sum = np.zeros_like(r_array) sum = 0 for i, r in enumerate(r_array_int[:-1]): #if i == 0: # cum_sum[i] = 0 #else: sum += self.light_3d(r, kwargs_list) * r**2 * (r_array[i+1] - r_array[i])# * r cum_sum[i+1] = copy.deepcopy(sum) cum_sum_norm = cum_sum/cum_sum[-1] f = interp1d(cum_sum_norm, np.log(r_array)) self._light_3d_cdf_log = f cdf_draw = np.random.uniform(0., 1, n) r_log_draw = self._light_3d_cdf_log(cdf_draw) return np.exp(r_log_draw) def delete_cache(self): """ deletes cached interpolation function of the CDF for a specific light profile :return: None """ if hasattr(self, '_light_cdf_log'): del self._light_cdf_log if hasattr(self, '_light_cdf'): del self._light_cdf if hasattr(self, '_f_light_3d'): del self._f_light_3d if hasattr(self, '_kwargs_light_circularized'): del self._kwargs_light_circularized
def velocity_disperson_numerical(self, kwargs_lens, kwargs_lens_light, kwargs_anisotropy, kwargs_aperture, psf_fwhm, aperture_type, anisotropy_model, r_eff=1., kwargs_numerics={}, MGE_light=False, MGE_mass=False): """ :param kwargs_lens: :param kwargs_lens_light: :param kwargs_anisotropy: :param kwargs_aperature: :return: """ kwargs_cosmo = { 'D_d': self.lensCosmo.D_d, 'D_s': self.lensCosmo.D_s, 'D_ds': self.lensCosmo.D_ds } mass_profile_list = [] kwargs_profile = [] lens_model_internal_bool = self.kwargs_options.get( 'lens_model_deflector_bool', [True] * len(kwargs_lens)) for i, lens_model in enumerate(self.kwargs_options['lens_model_list']): if lens_model_internal_bool[i]: mass_profile_list.append(lens_model) kwargs_lens_i = { k: v for k, v in kwargs_lens[i].items() if not k in ['center_x', 'center_y'] } kwargs_profile.append(kwargs_lens_i) if MGE_mass is True: massModel = LensModelExtensions(lens_model_list=mass_profile_list) theta_E = massModel.effective_einstein_radius(kwargs_lens) r_array = np.logspace(-4, 2, 200) * theta_E mass_r = massModel.kappa(r_array, 0, kwargs_profile) amps, sigmas, norm = mge.mge_1d(r_array, mass_r, N=20) mass_profile_list = ['MULTI_GAUSSIAN_KAPPA'] kwargs_profile = [{'amp': amps, 'sigma': sigmas}] light_profile_list = [] kwargs_light = [] lens_light_model_internal_bool = self.kwargs_options.get( 'light_model_deflector_bool', [True] * len(kwargs_lens_light)) for i, light_model in enumerate( self.kwargs_options['lens_light_model_list']): if lens_light_model_internal_bool[i]: light_profile_list.append(light_model) kwargs_Lens_light_i = { k: v for k, v in kwargs_lens_light[i].items() if not k in ['center_x', 'center_y'] } if 'q' in kwargs_Lens_light_i: kwargs_Lens_light_i['q'] = 1 kwargs_light.append(kwargs_Lens_light_i) if MGE_light is True: lightModel = LightModel(light_profile_list) r_array = np.logspace(-3, 2, 200) * r_eff * 2 flux_r = lightModel.surface_brightness(r_array, 0, kwargs_light) amps, sigmas, norm = mge.mge_1d(r_array, flux_r, N=20) light_profile_list = ['MULTI_GAUSSIAN'] kwargs_light = [{'amp': amps, 'sigma': sigmas}] galkin = Galkin(mass_profile_list, light_profile_list, aperture_type=aperture_type, anisotropy_model=anisotropy_model, fwhm=psf_fwhm, kwargs_cosmo=kwargs_cosmo, kwargs_numerics=kwargs_numerics) sigma_v = galkin.vel_disp(kwargs_profile, kwargs_light, kwargs_anisotropy, kwargs_aperture, r_eff=r_eff) return sigma_v
class TestLightModel(object): """ tests the source model routines """ def setup(self): self.light_model_list = [ 'GAUSSIAN', 'MULTI_GAUSSIAN', 'SERSIC', 'SERSIC_ELLIPSE', 'CORE_SERSIC', 'SHAPELETS', 'HERNQUIST', 'HERNQUIST_ELLIPSE', 'PJAFFE', 'PJAFFE_ELLIPSE', 'UNIFORM', 'POWER_LAW', 'NIE', 'INTERPOL', 'SHAPELETS_POLAR_EXP' ] phi_G, q = 0.5, 0.8 e1, e2 = param_util.phi_q2_ellipticity(phi_G, q) self.kwargs = [ { 'amp': 1., 'sigma_x': 1, 'sigma_y': 1., 'center_x': 0, 'center_y': 0 }, # 'GAUSSIAN' { 'amp': [1., 2], 'sigma': [1, 3], 'center_x': 0, 'center_y': 0 }, # 'MULTI_GAUSSIAN' { 'amp': 1, 'R_sersic': 0.5, 'n_sersic': 1, 'center_x': 0, 'center_y': 0 }, # 'SERSIC' { 'amp': 1, 'R_sersic': 0.5, 'n_sersic': 1, 'e1': e1, 'e2': e2, 'center_x': 0, 'center_y': 0 }, # 'SERSIC_ELLIPSE' { 'amp': 1, 'R_sersic': 0.5, 'Re': 0.1, 'gamma': 2., 'n_sersic': 1, 'e1': e1, 'e2': e2, 'center_x': 0, 'center_y': 0 }, # 'CORE_SERSIC' { 'amp': [1, 1, 1], 'beta': 0.5, 'n_max': 1, 'center_x': 0, 'center_y': 0 }, # 'SHAPELETS' { 'amp': 1, 'Rs': 0.5, 'center_x': 0, 'center_y': 0 }, # 'HERNQUIST' { 'amp': 1, 'Rs': 0.5, 'center_x': 0, 'center_y': 0, 'e1': e1, 'e2': e2 }, # 'HERNQUIST_ELLIPSE' { 'amp': 1, 'Ra': 1, 'Rs': 0.5, 'center_x': 0, 'center_y': 0 }, # 'PJAFFE' { 'amp': 1, 'Ra': 1, 'Rs': 0.5, 'center_x': 0, 'center_y': 0, 'e1': e1, 'e2': e2 }, # 'PJAFFE_ELLIPSE' { 'amp': 1 }, # 'UNIFORM' { 'amp': 1., 'gamma': 2., 'e1': e1, 'e2': e2, 'center_x': 0, 'center_y': 0 }, # 'POWER_LAW' { 'amp': .001, 'e1': 0, 'e2': 1., 'center_x': 0, 'center_y': 0, 's_scale': 1. }, # 'NIE' { 'image': np.zeros((10, 10)), 'scale': 1, 'phi_G': 0, 'center_x': 0, 'center_y': 0 }, { 'amp': [1], 'n_max': 0, 'beta': 1, 'center_x': 0, 'center_y': 0 } ] self.LightModel = LightModel(light_model_list=self.light_model_list) def test_init(self): model_list = [ 'CORE_SERSIC', 'SHAPELETS', 'SHAPELETS_POLAR', 'SHAPELETS_POLAR_EXP', 'UNIFORM', 'CHAMELEON', 'DOUBLE_CHAMELEON', 'TRIPLE_CHAMELEON' ] lightModel = LightModel(light_model_list=model_list) assert len(lightModel.profile_type_list) == len(model_list) def test_surface_brightness(self): output = self.LightModel.surface_brightness(x=1, y=1, kwargs_list=self.kwargs) npt.assert_almost_equal(output, 3.7065728131855824, decimal=6) def test_surface_brightness_array(self): output = self.LightModel.surface_brightness(x=[1], y=[1], kwargs_list=self.kwargs) npt.assert_almost_equal(output[0], 3.7065728131855824, decimal=6) def test_functions_split(self): output = self.LightModel.functions_split(x=1., y=1., kwargs_list=self.kwargs) npt.assert_almost_equal(output[0][0], 0.058549831524319168, decimal=6) def test_re_normalize_flux(self): kwargs_out = self.LightModel.re_normalize_flux(kwargs_list=self.kwargs, norm_factor=2) assert kwargs_out[0]['amp'] == 2 * self.kwargs[0]['amp'] def test_param_name_list(self): param_name_list = self.LightModel.param_name_list() assert len(self.light_model_list) == len(param_name_list) def test_num_param_linear(self): num = self.LightModel.num_param_linear(self.kwargs, list_return=False) assert num == 18 num_list = self.LightModel.num_param_linear(self.kwargs, list_return=True) assert num_list[0] == 1 def test_update_linear(self): response, n = self.LightModel.functions_split(1, 1, self.kwargs) param = np.ones(n) * 2 kwargs_out, i = self.LightModel.update_linear(param, i=0, kwargs_list=self.kwargs) assert i == n assert kwargs_out[0]['amp'] == 2 def test_total_flux(self): light_model_list = [ 'SERSIC', 'SERSIC_ELLIPSE', 'INTERPOL', 'GAUSSIAN', 'GAUSSIAN_ELLIPSE', 'MULTI_GAUSSIAN', 'MULTI_GAUSSIAN_ELLIPSE' ] kwargs_list = [ { 'amp': 1, 'R_sersic': 0.5, 'n_sersic': 1, 'center_x': 0, 'center_y': 0 }, # 'SERSIC' { 'amp': 1, 'R_sersic': 0.5, 'n_sersic': 1, 'e1': 0.1, 'e2': 0, 'center_x': 0, 'center_y': 0 }, # 'SERSIC_ELLIPSE' { 'image': np.ones((10, 10)), 'scale': 1, 'phi_G': 0, 'center_x': 0, 'center_y': 0 }, # 'INTERPOL' { 'amp': 2, 'sigma_x': 2, 'sigma_y': 1, 'center_x': 0, 'center_y': 0 }, # 'GAUSSIAN' { 'amp': 2, 'sigma': 2, 'e1': 0.1, 'e2': 0, 'center_x': 0, 'center_y': 0 }, # 'GAUSSIAN_ELLIPSE' { 'amp': [1, 1], 'sigma': [2, 1], 'center_x': 0, 'center_y': 0 }, # 'MULTI_GAUSSIAN' { 'amp': [1, 1], 'sigma': [2, 1], 'e1': 0.1, 'e2': 0, 'center_x': 0, 'center_y': 0 } # 'MULTI_GAUSSIAN_ELLIPSE' ] lightModel = LightModel(light_model_list=light_model_list) total_flux_list = lightModel.total_flux(kwargs_list) assert total_flux_list[2] == 100 assert total_flux_list[3] == 2 assert total_flux_list[4] == 2 assert total_flux_list[5] == 2 assert total_flux_list[6] == 2 total_flux_list = lightModel.total_flux(kwargs_list, norm=True) assert total_flux_list[2] == 100 assert total_flux_list[3] == 1 assert total_flux_list[4] == 1 assert total_flux_list[5] == 2 assert total_flux_list[6] == 2
class LensAnalysis(object): """ class to compute flux ratio anomalies, inherited from standard MakeImage """ def __init__(self, kwargs_model): self.LensLightModel = LightModel( kwargs_model.get('lens_light_model_list', [])) self.SourceModel = LightModel( kwargs_model.get('source_light_model_list', [])) self.LensModel = LensModel( lens_model_list=kwargs_model.get('lens_model_list', []), z_source=kwargs_model.get('z_source', None), redshift_list=kwargs_model.get('redshift_list', None), multi_plane=kwargs_model.get('multi_plane', False)) self._lensModelExtensions = LensModelExtensions(self.LensModel) self.PointSource = PointSource(point_source_type_list=kwargs_model.get( 'point_source_model_list', [])) self.kwargs_model = kwargs_model self.NumLensModel = NumericLens( lens_model_list=kwargs_model.get('lens_model_list', [])) def fermat_potential(self, kwargs_lens, kwargs_ps): ra_pos, dec_pos = self.PointSource.image_position( kwargs_ps, kwargs_lens) ra_pos = ra_pos[0] dec_pos = dec_pos[0] ra_source, dec_source = self.LensModel.ray_shooting( ra_pos, dec_pos, kwargs_lens) ra_source = np.mean(ra_source) dec_source = np.mean(dec_source) fermat_pot = self.LensModel.fermat_potential(ra_pos, dec_pos, ra_source, dec_source, kwargs_lens) return fermat_pot def ellipticity_lens_light(self, kwargs_lens_light, center_x=0, center_y=0, model_bool_list=None, deltaPix=None, numPix=None): """ make sure that the window covers all the light, otherwise the moments may give to low answers. :param kwargs_lens_light: :param center_x: :param center_y: :param model_bool_list: :param deltaPix: :param numPix: :return: """ if model_bool_list is None: model_bool_list = [True] * len(kwargs_lens_light) if numPix is None: numPix = 100 if deltaPix is None: deltaPix = 0.05 x_grid, y_grid = util.make_grid(numPix=numPix, deltapix=deltaPix) x_grid += center_x y_grid += center_y I_xy = self._lens_light_internal(x_grid, y_grid, kwargs_lens_light, model_bool_list=model_bool_list) e1, e2 = analysis_util.ellipticities(I_xy, x_grid, y_grid) return e1, e2 def half_light_radius_lens(self, kwargs_lens_light, center_x=0, center_y=0, model_bool_list=None, deltaPix=None, numPix=None): """ computes numerically the half-light-radius of the deflector light and the total photon flux :param kwargs_lens_light: :return: """ if model_bool_list is None: model_bool_list = [True] * len(kwargs_lens_light) if numPix is None: numPix = 1000 if deltaPix is None: deltaPix = 0.05 x_grid, y_grid = util.make_grid(numPix=numPix, deltapix=deltaPix) x_grid += center_x y_grid += center_y lens_light = self._lens_light_internal(x_grid, y_grid, kwargs_lens_light, model_bool_list=model_bool_list) R_h = analysis_util.half_light_radius(lens_light, x_grid, y_grid, center_x, center_y) return R_h def half_light_radius_source(self, kwargs_source, center_x=0, center_y=0, deltaPix=None, numPix=None): """ computes numerically the half-light-radius of the deflector light and the total photon flux :param kwargs_source: :return: """ if numPix is None: numPix = 1000 if deltaPix is None: deltaPix = 0.005 x_grid, y_grid = util.make_grid(numPix=numPix, deltapix=deltaPix) x_grid += center_x y_grid += center_y source_light = self.SourceModel.surface_brightness( x_grid, y_grid, kwargs_source) R_h = analysis_util.half_light_radius(source_light, x_grid, y_grid, center_x=center_x, center_y=center_y) return R_h def _lens_light_internal(self, x_grid, y_grid, kwargs_lens_light, model_bool_list=None): """ evaluates only part of the light profiles :param x_grid: :param y_grid: :param kwargs_lens_light: :return: """ if model_bool_list is None: model_bool_list = [True] * len(kwargs_lens_light) lens_light = np.zeros_like(x_grid) for i, bool in enumerate(model_bool_list): if bool is True: lens_light_i = self.LensLightModel.surface_brightness( x_grid, y_grid, kwargs_lens_light, k=i) lens_light += lens_light_i return lens_light def multi_gaussian_lens_light(self, kwargs_lens_light, model_bool_list=None, e1=0, e2=0, n_comp=20, deltaPix=None, numPix=None): """ multi-gaussian decomposition of the lens light profile (in 1-dimension) :param kwargs_lens_light: :param n_comp: :return: """ if 'center_x' in kwargs_lens_light[0]: center_x = kwargs_lens_light[0]['center_x'] center_y = kwargs_lens_light[0]['center_y'] else: center_x, center_y = 0, 0 r_h = self.half_light_radius_lens(kwargs_lens_light, center_x=center_x, center_y=center_y, model_bool_list=model_bool_list, deltaPix=deltaPix, numPix=numPix) r_array = np.logspace(-3, 2, 200) * r_h * 2 x_coords, y_coords = param_util.transform_e1e2(r_array, np.zeros_like(r_array), e1=-e1, e2=-e2) x_coords += center_x y_coords += center_y #r_array = np.logspace(-2, 1, 50) * r_h flux_r = self._lens_light_internal(x_coords, y_coords, kwargs_lens_light, model_bool_list=model_bool_list) amplitudes, sigmas, norm = mge.mge_1d(r_array, flux_r, N=n_comp) return amplitudes, sigmas, center_x, center_y def multi_gaussian_lens(self, kwargs_lens, model_bool_list=None, e1=0, e2=0, n_comp=20): """ multi-gaussian lens model in convergence space :param kwargs_lens: :param n_comp: :return: """ if 'center_x' in kwargs_lens[0]: center_x = kwargs_lens[0]['center_x'] center_y = kwargs_lens[0]['center_y'] else: raise ValueError('no keyword center_x defined!') theta_E = self._lensModelExtensions.effective_einstein_radius( kwargs_lens) r_array = np.logspace(-4, 2, 200) * theta_E x_coords, y_coords = param_util.transform_e1e2(r_array, np.zeros_like(r_array), e1=-e1, e2=-e2) x_coords += center_x y_coords += center_y #r_array = np.logspace(-2, 1, 50) * theta_E if model_bool_list is None: model_bool_list = [True] * len(kwargs_lens) kappa_s = np.zeros_like(r_array) for i in range(len(kwargs_lens)): if model_bool_list[i] is True: kappa_s += self.LensModel.kappa(x_coords, y_coords, kwargs_lens, k=i) amplitudes, sigmas, norm = mge.mge_1d(r_array, kappa_s, N=n_comp) return amplitudes, sigmas, center_x, center_y def flux_components(self, kwargs_light, n_grid=400, delta_grid=0.01, deltaPix=0.05, type="lens"): """ computes the total flux in each component of the model :param kwargs_light: :param n_grid: :param delta_grid: :return: """ flux_list = [] R_h_list = [] x_grid, y_grid = util.make_grid(numPix=n_grid, deltapix=delta_grid) kwargs_copy = copy.deepcopy(kwargs_light) for k, kwargs in enumerate(kwargs_light): if 'center_x' in kwargs_copy[k]: kwargs_copy[k]['center_x'] = 0 kwargs_copy[k]['center_y'] = 0 if type == 'lens': light = self.LensLightModel.surface_brightness(x_grid, y_grid, kwargs_copy, k=k) elif type == 'source': light = self.SourceModel.surface_brightness(x_grid, y_grid, kwargs_copy, k=k) else: raise ValueError("type %s not supported!" % type) flux = np.sum(light) * delta_grid**2 / deltaPix**2 R_h = analysis_util.half_light_radius(light, x_grid, y_grid) flux_list.append(flux) R_h_list.append(R_h) return flux_list, R_h_list def error_map_source(self, kwargs_source, x_grid, y_grid, cov_param): """ variance of the linear source reconstruction in the source plane coordinates, computed by the diagonal elements of the covariance matrix of the source reconstruction as a sum of the errors of the basis set. :param kwargs_source: keyword arguments of source model :param x_grid: x-axis of positions to compute error map :param y_grid: y-axis of positions to compute error map :param cov_param: covariance matrix of liner inversion parameters :return: diagonal covariance errors at the positions (x_grid, y_grid) """ error_map = np.zeros_like(x_grid) basis_functions, n_source = self.SourceModel.functions_split( x_grid, y_grid, kwargs_source) basis_functions = np.array(basis_functions) if cov_param is not None: for i in range(len(error_map)): error_map[i] = basis_functions[:, i].T.dot( cov_param[:n_source, :n_source]).dot(basis_functions[:, i]) return error_map def light2mass_mge(self, kwargs_lens_light, model_bool_list=None, elliptical=False, numPix=100, deltaPix=0.05): # estimate center if 'center_x' in kwargs_lens_light[0]: center_x, center_y = kwargs_lens_light[0][ 'center_x'], kwargs_lens_light[0]['center_y'] else: center_x, center_y = 0, 0 # estimate half-light radius r_h = self.half_light_radius_lens(kwargs_lens_light, center_x=center_x, center_y=center_y, model_bool_list=model_bool_list, numPix=numPix, deltaPix=deltaPix) # estimate ellipticity at half-light radius if elliptical is True: e1, e2 = self.ellipticity_lens_light( kwargs_lens_light, center_x=center_x, center_y=center_y, model_bool_list=model_bool_list, deltaPix=deltaPix * 2, numPix=numPix) else: e1, e2 = 0, 0 # MGE around major axis amplitudes, sigmas, center_x, center_y = self.multi_gaussian_lens_light( kwargs_lens_light, model_bool_list=model_bool_list, e1=e1, e2=e2, n_comp=20) kwargs_mge = { 'amp': amplitudes, 'sigma': sigmas, 'center_x': center_x, 'center_y': center_y } if elliptical: kwargs_mge['e1'] = e1 kwargs_mge['e2'] = e2 # rotate axes and add ellipticity to model kwargs return kwargs_mge @staticmethod def light2mass_interpol(lens_light_model_list, kwargs_lens_light, numPix=100, deltaPix=0.05, subgrid_res=5, center_x=0, center_y=0): """ takes a lens light model and turns it numerically in a lens model (with all lensmodel quantities computed on a grid). Then provides an interpolated grid for the quantities. :param kwargs_lens_light: lens light keyword argument list :param numPix: number of pixels per axis for the return interpolation :param deltaPix: interpolation/pixel size :param center_x: center of the grid :param center_y: center of the grid :param subgrid: subgrid for the numerical integrals :return: """ # make sugrid x_grid_sub, y_grid_sub = util.make_grid(numPix=numPix * 5, deltapix=deltaPix, subgrid_res=subgrid_res) import lenstronomy.Util.mask as mask_util mask = mask_util.mask_sphere(x_grid_sub, y_grid_sub, center_x, center_y, r=1) x_grid, y_grid = util.make_grid(numPix=numPix, deltapix=deltaPix) # compute light on the subgrid lightModel = LightModel(light_model_list=lens_light_model_list) flux = lightModel.surface_brightness(x_grid_sub, y_grid_sub, kwargs_lens_light) flux_norm = np.sum(flux[mask == 1]) / np.sum(mask) flux /= flux_norm from lenstronomy.LensModel.numerical_profile_integrals import ConvergenceIntegrals integral = ConvergenceIntegrals() # compute lensing quantities with subgrid convergence_sub = flux f_x_sub, f_y_sub = integral.deflection_from_kappa(convergence_sub, x_grid_sub, y_grid_sub, deltaPix=deltaPix / float(subgrid_res)) f_sub = integral.potential_from_kappa(convergence_sub, x_grid_sub, y_grid_sub, deltaPix=deltaPix / float(subgrid_res)) # interpolation function on lensing quantities x_axes_sub, y_axes_sub = util.get_axes(x_grid_sub, y_grid_sub) from lenstronomy.LensModel.Profiles.interpol import Interpol_func interp_func = Interpol_func() interp_func.do_interp(x_axes_sub, y_axes_sub, f_sub, f_x_sub, f_y_sub) # compute lensing quantities on sparser grid x_axes, y_axes = util.get_axes(x_grid, y_grid) f_ = interp_func.function(x_grid, y_grid) f_x, f_y = interp_func.derivatives(x_grid, y_grid) # numerical differentials for second order differentials from lenstronomy.LensModel.numeric_lens_differentials import NumericLens lens_differential = NumericLens(lens_model_list=['INTERPOL']) kwargs = [{ 'grid_interp_x': x_axes_sub, 'grid_interp_y': y_axes_sub, 'f_': f_sub, 'f_x': f_x_sub, 'f_y': f_y_sub }] f_xx, f_xy, f_yx, f_yy = lens_differential.hessian( x_grid, y_grid, kwargs) kwargs_interpol = { 'grid_interp_x': x_axes, 'grid_interp_y': y_axes, 'f_': util.array2image(f_), 'f_x': util.array2image(f_x), 'f_y': util.array2image(f_y), 'f_xx': util.array2image(f_xx), 'f_xy': util.array2image(f_xy), 'f_yy': util.array2image(f_yy) } return kwargs_interpol def mass_fraction_within_radius(self, kwargs_lens, center_x, center_y, theta_E, numPix=100): """ computes the mean convergence of all the different lens model components within a spherical aperture :param kwargs_lens: lens model keyword argument list :param center_x: center of the aperture :param center_y: center of the aperture :param theta_E: radius of aperture :return: list of average convergences for all the model components """ x_grid, y_grid = util.make_grid(numPix=numPix, deltapix=2. * theta_E / numPix) x_grid += center_x y_grid += center_y mask = mask_util.mask_sphere(x_grid, y_grid, center_x, center_y, theta_E) kappa_list = [] for i in range(len(kwargs_lens)): kappa = self.LensModel.kappa(x_grid, y_grid, kwargs_lens, k=i) kappa_mean = np.sum(kappa * mask) / np.sum(mask) kappa_list.append(kappa_mean) return kappa_list
def test_magnification_finite_adaptive(self): lens_model_list = ['EPL', 'SHEAR'] z_source = 1.5 kwargs_lens = [{ 'theta_E': 1., 'gamma': 2., 'e1': 0.02, 'e2': -0.09, 'center_x': 0, 'center_y': 0 }, { 'gamma1': 0.01, 'gamma2': 0.03 }] lensmodel = LensModel(lens_model_list) extension = LensModelExtensions(lensmodel) solver = LensEquationSolver(lensmodel) source_x, source_y = 0.07, 0.03 x_image, y_image = solver.findBrightImage(source_x, source_y, kwargs_lens) source_fwhm_parsec = 40. pc_per_arcsec = 1000 / self.cosmo.arcsec_per_kpc_proper(z_source).value source_sigma = source_fwhm_parsec / pc_per_arcsec / 2.355 grid_size = auto_raytracing_grid_size(source_fwhm_parsec) grid_resolution = auto_raytracing_grid_resolution(source_fwhm_parsec) # make this even higher resolution to show convergence grid_number = int(2 * grid_size / grid_resolution) window_size = 2 * grid_size mag_square_grid = extension.magnification_finite( x_image, y_image, kwargs_lens, source_sigma=source_sigma, grid_number=grid_number, window_size=window_size) flux_ratios_square_grid = mag_square_grid / max(mag_square_grid) mag_adaptive_grid = extension.magnification_finite_adaptive( x_image, y_image, source_x, source_y, kwargs_lens, source_fwhm_parsec, z_source, cosmo=self.cosmo) flux_ratios_adaptive_grid = mag_adaptive_grid / max(mag_adaptive_grid) mag_adaptive_grid_fixed_aperture_size = extension.magnification_finite_adaptive( x_image, y_image, source_x, source_y, kwargs_lens, source_fwhm_parsec, z_source, cosmo=self.cosmo, fixed_aperture_size=True, grid_radius_arcsec=0.2) flux_ratios_fixed_aperture_size = mag_adaptive_grid_fixed_aperture_size / max( mag_adaptive_grid_fixed_aperture_size) mag_adaptive_grid_2 = extension.magnification_finite_adaptive( x_image, y_image, source_x, source_y, kwargs_lens, source_fwhm_parsec, z_source, cosmo=self.cosmo, axis_ratio=0) flux_ratios_adaptive_grid_2 = mag_adaptive_grid_2 / max( mag_adaptive_grid_2) # tests the default cosmology _ = extension.magnification_finite_adaptive(x_image, y_image, source_x, source_y, kwargs_lens, source_fwhm_parsec, z_source, cosmo=None, tol=0.0001) # test smallest eigenvalue _ = extension.magnification_finite_adaptive( x_image, y_image, source_x, source_y, kwargs_lens, source_fwhm_parsec, z_source, cosmo=None, tol=0.0001, use_largest_eigenvalue=False) # tests the r_max > sqrt(2) * grid_radius stop criterion _ = extension.magnification_finite_adaptive(x_image, y_image, source_x, source_y, kwargs_lens, source_fwhm_parsec, z_source, cosmo=None, tol=0.0001, step_size=1000) mag_point_source = abs( lensmodel.magnification(x_image, y_image, kwargs_lens)) quarter_precent_precision = [0.0025] * 4 npt.assert_array_less( flux_ratios_square_grid / flux_ratios_adaptive_grid - 1, quarter_precent_precision) npt.assert_array_less( flux_ratios_fixed_aperture_size / flux_ratios_adaptive_grid - 1, quarter_precent_precision) npt.assert_array_less( flux_ratios_square_grid / flux_ratios_adaptive_grid_2 - 1, quarter_precent_precision) half_percent_precision = [0.005] * 4 npt.assert_array_less(mag_square_grid / mag_adaptive_grid - 1, half_percent_precision) npt.assert_array_less(mag_square_grid / mag_adaptive_grid_2 - 1, half_percent_precision) npt.assert_array_less(mag_adaptive_grid / mag_point_source - 1, half_percent_precision) flux_array = np.array([0., 0.]) grid_x = np.array([0., source_sigma]) grid_y = np.array([0., 0.]) grid_r = np.hypot(grid_x, grid_y) # test that the double gaussian profile has 2x the flux when dx, dy = 0 magnification_double_gaussian = extension.magnification_finite_adaptive( x_image, y_image, source_x, source_y, kwargs_lens, source_fwhm_parsec, z_source, cosmo=self.cosmo, source_light_model='DOUBLE_GAUSSIAN', dx=0., dy=0., amp_scale=1., size_scale=1.) npt.assert_almost_equal(magnification_double_gaussian, 2 * mag_adaptive_grid) grid_radius = 0.3 npix = 400 _x = _y = np.linspace(-grid_radius, grid_radius, npix) resolution = 2 * grid_radius / npix xx, yy = np.meshgrid(_x, _y) for i in range(0, 4): beta_x, beta_y = lensmodel.ray_shooting(x_image[i] + xx.ravel(), y_image[i] + yy.ravel(), kwargs_lens) source_light_model = LightModel(['GAUSSIAN'] * 2) amp_scale, dx, dy, size_scale = 0.2, 0.005, -0.005, 1. kwargs_source = [{ 'amp': 1., 'center_x': source_x, 'center_y': source_y, 'sigma': source_sigma }, { 'amp': amp_scale, 'center_x': source_x + dx, 'center_y': source_y + dy, 'sigma': source_sigma * size_scale }] sb = source_light_model.surface_brightness(beta_x, beta_y, kwargs_source) magnification_double_gaussian_reference = np.sum( sb) * resolution**2 magnification_double_gaussian = extension.magnification_finite_adaptive( [x_image[i]], [y_image[i]], source_x, source_y, kwargs_lens, source_fwhm_parsec, z_source, cosmo=self.cosmo, source_light_model='DOUBLE_GAUSSIAN', dx=dx, dy=dy, amp_scale=amp_scale, size_scale=size_scale, grid_resolution=resolution, grid_radius_arcsec=grid_radius, axis_ratio=1.) npt.assert_almost_equal( magnification_double_gaussian / magnification_double_gaussian_reference, 1., 3) source_model = LightModel(['GAUSSIAN']) kwargs_source = [{ 'amp': 1., 'center_x': source_x, 'center_y': source_y, 'sigma': source_sigma }] r_min = 0. r_max = source_sigma * 0.9 flux_array = extension._magnification_adaptive_iteration( flux_array, x_image[0], y_image[0], grid_x, grid_y, grid_r, r_min, r_max, lensmodel, kwargs_lens, source_model, kwargs_source) bx, by = lensmodel.ray_shooting(x_image[0], y_image[0], kwargs_lens) sb_true = source_model.surface_brightness(bx, by, kwargs_source) npt.assert_equal(True, flux_array[0] == sb_true) npt.assert_equal(True, flux_array[1] == 0.) r_min = source_sigma * 0.9 r_max = 2 * source_sigma flux_array = extension._magnification_adaptive_iteration( flux_array, x_image[0], y_image[0], grid_x, grid_y, grid_r, r_min, r_max, lensmodel, kwargs_lens, source_model, kwargs_source) bx, by = lensmodel.ray_shooting(x_image[0] + source_sigma, y_image[0], kwargs_lens) sb_true = source_model.surface_brightness(bx, by, kwargs_source) npt.assert_equal(True, flux_array[1] == sb_true)
def light2mass_interpol(lens_light_model_list, kwargs_lens_light, numPix=100, deltaPix=0.05, subgrid_res=5, center_x=0, center_y=0): """ takes a lens light model and turns it numerically in a lens model (with all lensmodel quantities computed on a grid). Then provides an interpolated grid for the quantities. :param kwargs_lens_light: lens light keyword argument list :param numPix: number of pixels per axis for the return interpolation :param deltaPix: interpolation/pixel size :param center_x: center of the grid :param center_y: center of the grid :param subgrid_res: subgrid for the numerical integrals :return: """ # make super-sampled grid x_grid_sub, y_grid_sub = util.make_grid(numPix=numPix * 5, deltapix=deltaPix, subgrid_res=subgrid_res) import lenstronomy.Util.mask_util as mask_util mask = mask_util.mask_sphere(x_grid_sub, y_grid_sub, center_x, center_y, r=1) x_grid, y_grid = util.make_grid(numPix=numPix, deltapix=deltaPix) # compute light on the subgrid lightModel = LightModel(light_model_list=lens_light_model_list) flux = lightModel.surface_brightness(x_grid_sub, y_grid_sub, kwargs_lens_light) flux_norm = np.sum(flux[mask == 1]) / np.sum(mask) flux /= flux_norm from lenstronomy.LensModel import convergence_integrals as integral # compute lensing quantities with subgrid convergence_sub = util.array2image(flux) f_x_sub, f_y_sub = integral.deflection_from_kappa_grid( convergence_sub, grid_spacing=deltaPix / float(subgrid_res)) f_sub = integral.potential_from_kappa_grid(convergence_sub, grid_spacing=deltaPix / float(subgrid_res)) # interpolation function on lensing quantities x_axes_sub, y_axes_sub = util.get_axes(x_grid_sub, y_grid_sub) from lenstronomy.LensModel.Profiles.interpol import Interpol interp_func = Interpol() interp_func.do_interp(x_axes_sub, y_axes_sub, f_sub, f_x_sub, f_y_sub) # compute lensing quantities on sparser grid x_axes, y_axes = util.get_axes(x_grid, y_grid) f_ = interp_func.function(x_grid, y_grid) f_x, f_y = interp_func.derivatives(x_grid, y_grid) # numerical differentials for second order differentials from lenstronomy.LensModel.lens_model import LensModel lens_model = LensModel(lens_model_list=['INTERPOL']) kwargs = [{ 'grid_interp_x': x_axes_sub, 'grid_interp_y': y_axes_sub, 'f_': f_sub, 'f_x': f_x_sub, 'f_y': f_y_sub }] f_xx, f_xy, f_yx, f_yy = lens_model.hessian(x_grid, y_grid, kwargs, diff=0.00001) kwargs_interpol = { 'grid_interp_x': x_axes, 'grid_interp_y': y_axes, 'f_': util.array2image(f_), 'f_x': util.array2image(f_x), 'f_y': util.array2image(f_y), 'f_xx': util.array2image(f_xx), 'f_xy': util.array2image(f_xy), 'f_yy': util.array2image(f_yy) } return kwargs_interpol
class TestNumerics(object): def setup(self): # we define a model consisting of a singe Sersric profile from lenstronomy.LightModel.light_model import LightModel light_model_list = ['SERSIC_ELLIPSE'] self.lightModel = LightModel(light_model_list=light_model_list) self.kwargs_light = [ {'amp': 100, 'R_sersic': 0.5, 'n_sersic': 3, 'e1': 0, 'e2': 0, 'center_x': 0.02, 'center_y': 0}] # we define a pixel grid and a higher resolution super sampling factor self._supersampling_factor = 5 numPix = 61 # cutout pixel size deltaPix = 0.05 # pixel size in arcsec (area per pixel = deltaPix**2) x, y, ra_at_xy_0, dec_at_xy_0, x_at_radec_0, y_at_radec_0, Mpix2coord, Mcoord2pix = util.make_grid_with_coordtransform( numPix=numPix, deltapix=deltaPix, subgrid_res=1, left_lower=False, inverse=False) flux = self.lightModel.surface_brightness(x, y, kwargs_list=self.kwargs_light) flux = util.array2image(flux) flux_max = np.max(flux) conv_pixels_partial = np.zeros((numPix, numPix), dtype=bool) conv_pixels_partial[flux >= flux_max / 20] = True self._conv_pixels_partial = conv_pixels_partial # high resolution ray-tracing and high resolution convolution, the full calculation self.kwargs_numerics_true = {'supersampling_factor': self._supersampling_factor, # super sampling factor of (partial) high resolution ray-tracing 'compute_mode': 'regular', # 'regular' or 'adaptive' 'supersampling_convolution': True, # bool, if True, performs the supersampled convolution (either on regular or adaptive grid) 'supersampling_kernel_size': None, # size of the higher resolution kernel region (can be smaller than the original kernel). None leads to use the full size 'flux_evaluate_indexes': None, # bool mask, if None, it will evaluate all (sub) pixels 'supersampled_indexes': None, # bool mask of pixels to be computed in supersampled grid (only for adaptive mode) 'compute_indexes': None, # bool mask of pixels to be computed the PSF response (flux being added to). Only used for adaptive mode and can be set =likelihood mask. 'point_source_supersampling_factor': 1, # int, supersampling factor when rendering a point source (not used in this script) } # high resolution convolution on a smaller PSF with low resolution convolution on the edges of the PSF and high resolution ray tracing self.kwargs_numerics_high_res_narrow = {'supersampling_factor': self._supersampling_factor, 'compute_mode': 'regular', 'supersampling_convolution': True, 'supersampling_kernel_size': 5, } # low resolution convolution based on high resolution ray-tracing grid self.kwargs_numerics_low_conv_high_grid = {'supersampling_factor': self._supersampling_factor, 'compute_mode': 'regular', 'supersampling_convolution': False, # does not matter for supersampling_factor=1 'supersampling_kernel_size': None, # does not matter for supersampling_factor=1 } # low resolution convolution with a subset of pixels with high resolution ray-tracing self.kwargs_numerics_low_conv_high_adaptive = {'supersampling_factor': self._supersampling_factor, 'compute_mode': 'adaptive', 'supersampling_convolution': False, # does not matter for supersampling_factor=1 'supersampling_kernel_size': None, # does not matter for supersampling_factor=1 'supersampled_indexes': self._conv_pixels_partial, 'convolution_kernel_size': 9, } # low resolution convolution with a subset of pixels with high resolution ray-tracing and high resoluton convolution on smaller kernel size self.kwargs_numerics_high_adaptive = {'supersampling_factor': self._supersampling_factor, 'compute_mode': 'adaptive', 'supersampling_convolution': True, # does not matter for supersampling_factor=1 'supersampling_kernel_size': 5, # does not matter for supersampling_factor=1 'supersampled_indexes': self._conv_pixels_partial, 'convolution_kernel_size': 9, } # low resolution convolution and low resolution ray tracing, the simplest calculation self.kwargs_numerics_low_res = {'supersampling_factor': 1, 'compute_mode': 'regular', 'supersampling_convolution': False, # does not matter for supersampling_factor=1 'supersampling_kernel_size': None, # does not matter for supersampling_factor=1 'convolution_kernel_size': 9, } flux_evaluate_indexes = np.zeros((numPix, numPix), dtype=bool) flux_evaluate_indexes[flux >= flux_max / 1000] = True # low resolution convolution on subframe self.kwargs_numerics_partial = {'supersampling_factor': 1, 'compute_mode': 'regular', 'supersampling_convolution': False, # does not matter for supersampling_factor=1 'supersampling_kernel_size': None, # does not matter for supersampling_factor=1 'flux_evaluate_indexes': flux_evaluate_indexes, 'convolution_kernel_size': 9 } # import PSF file kernel_super = kernel_util.kernel_gaussian(kernel_numPix=11 * self._supersampling_factor, deltaPix=deltaPix / self._supersampling_factor, fwhm=0.1) kernel_size = 9 kernel_super = kernel_util.cut_psf(psf_data=kernel_super, psf_size=kernel_size * self._supersampling_factor) # make instance of the PixelGrid class from lenstronomy.Data.pixel_grid import PixelGrid kwargs_grid = {'nx': numPix, 'ny': numPix, 'transform_pix2angle': Mpix2coord, 'ra_at_xy_0': ra_at_xy_0, 'dec_at_xy_0': dec_at_xy_0} self.pixel_grid = PixelGrid(**kwargs_grid) # make instance of the PSF class from lenstronomy.Data.psf import PSF kwargs_psf = {'psf_type': 'PIXEL', 'kernel_point_source': kernel_super, 'point_source_supersampling_factor': self._supersampling_factor} self.psf_class = PSF(**kwargs_psf) # without convolution image_model_true = ImageModel(self.pixel_grid, self.psf_class, lens_light_model_class=self.lightModel, kwargs_numerics=self.kwargs_numerics_true) self.image_true = image_model_true.image(kwargs_lens_light=self.kwargs_light) def test_full(self): image_model_true = ImageModel(self.pixel_grid, self.psf_class, lens_light_model_class=self.lightModel, kwargs_numerics=self.kwargs_numerics_true) image_unconvolved = image_model_true.image(kwargs_lens_light=self.kwargs_light, unconvolved=True) npt.assert_almost_equal(np.sum(self.image_true) / np.sum(image_unconvolved), 1, decimal=2) def test_high_res_narrow(self): image_model = ImageModel(self.pixel_grid, self.psf_class, lens_light_model_class=self.lightModel, kwargs_numerics=self.kwargs_numerics_high_res_narrow) image_conv = image_model.image(kwargs_lens_light=self.kwargs_light, unconvolved=False) npt.assert_almost_equal((self.image_true - image_conv) / self.image_true, 0, decimal=2) def test_low_conv_high_grid(self): image_model = ImageModel(self.pixel_grid, self.psf_class, lens_light_model_class=self.lightModel, kwargs_numerics=self.kwargs_numerics_low_conv_high_grid) image_conv = image_model.image(kwargs_lens_light=self.kwargs_light, unconvolved=False) npt.assert_almost_equal((self.image_true - image_conv) / self.image_true, 0, decimal=1) def test_low_conv_high_adaptive(self): image_model = ImageModel(self.pixel_grid, self.psf_class, lens_light_model_class=self.lightModel, kwargs_numerics=self.kwargs_numerics_low_conv_high_adaptive) image_conv = image_model.image(kwargs_lens_light=self.kwargs_light, unconvolved=False) npt.assert_almost_equal((self.image_true - image_conv) / self.image_true, 0, decimal=1) def test_high_adaptive(self): image_model = ImageModel(self.pixel_grid, self.psf_class, lens_light_model_class=self.lightModel, kwargs_numerics=self.kwargs_numerics_high_adaptive) image_conv = image_model.image(kwargs_lens_light=self.kwargs_light, unconvolved=False) npt.assert_almost_equal((self.image_true - image_conv) / self.image_true, 0, decimal=1) def test_low_res(self): image_model = ImageModel(self.pixel_grid, self.psf_class, lens_light_model_class=self.lightModel, kwargs_numerics=self.kwargs_numerics_low_res) image_conv = image_model.image(kwargs_lens_light=self.kwargs_light, unconvolved=False) npt.assert_almost_equal((self.image_true - image_conv) / self.image_true, 0, decimal=1) def test_sub_frame(self): image_model = ImageModel(self.pixel_grid, self.psf_class, lens_light_model_class=self.lightModel, kwargs_numerics=self.kwargs_numerics_partial) image_conv = image_model.image(kwargs_lens_light=self.kwargs_light, unconvolved=False) delta = (self.image_true - image_conv) / self.image_true npt.assert_almost_equal(delta[self._conv_pixels_partial], 0, decimal=1)