Пример #1
0
    def test_draw_light_PJaffe(self):
        np.random.seed(41)
        lightProfile = LightProfile(profile_list=['PJAFFE'])
        kwargs_profile = [{'amp': 1., 'Rs': 0.5, 'Ra': 0.2}]
        r_list = lightProfile.draw_light_2d(kwargs_profile, n=100000)
        bins = np.linspace(0, 2, 10)
        hist, bins_hist = np.histogram(r_list, bins=bins, density=True)
        light2d = lightProfile.light_2d(R=(bins_hist[1:] + bins_hist[:-1]) /
                                        2.,
                                        kwargs_list=kwargs_profile)
        light2d *= (bins_hist[1:] + bins_hist[:-1]) / 2.
        light2d /= np.sum(light2d)
        hist /= np.sum(hist)
        print(light2d / hist)
        npt.assert_almost_equal(light2d[8] / hist[8], 1, decimal=1)

        lightProfile = LightProfile(profile_list=['PJAFFE'],
                                    min_interpolate=0.0001,
                                    max_interpolate=20.)
        kwargs_profile = [{'amp': 1., 'Rs': 0.04, 'Ra': 0.02}]
        r_list = lightProfile.draw_light_2d(kwargs_profile, n=100000)
        bins = np.linspace(0., 0.1, 10)
        hist, bins_hist = np.histogram(r_list, bins=bins, density=True)
        light2d = lightProfile.light_2d(R=(bins_hist[1:] + bins_hist[:-1]) /
                                        2.,
                                        kwargs_list=kwargs_profile)
        light2d *= (bins_hist[1:] + bins_hist[:-1]) / 2.
        light2d /= np.sum(light2d)
        hist /= np.sum(hist)
        print(light2d / hist)
        npt.assert_almost_equal(light2d[5] / hist[5], 1, decimal=1)
        assert hasattr(lightProfile, '_kwargs_light_circularized')
        lightProfile.delete_cache()
        if hasattr(lightProfile, '_kwargs_light_circularized'):
            assert False
Пример #2
0
    def __init__(self, mass_profile_list, light_profile_list, kwargs_aperture, kwargs_psf, anisotropy_model='isotropic',
                 kwargs_cosmo={'D_d': 1000, 'D_s': 2000, 'D_ds': 500},
                 sampling_number=1000, interpol_grid_num=500, log_integration=False, max_integrate=10, min_integrate=0.001):
        """

        :param mass_profile_list: list of lens (mass) model profiles
        :param light_profile_list: list of light model profiles of the lensing galaxy
        :param kwargs_aperture: keyword arguments describing the spectroscopic aperture, see Aperture() class
        :param anisotropy_model: type of stellar anisotropy model. See details in MamonLokasAnisotropy() class.
        :param kwargs_psf: keyword argument specifying the PSF of the observation
        :param kwargs_cosmo: keyword arguments that define the cosmology in terms of the angular diameter distances involved
        """
        self.massProfile = MassProfile(mass_profile_list, kwargs_cosmo, interpol_grid_num=interpol_grid_num,
                                         max_interpolate=max_integrate, min_interpolate=min_integrate)
        self.lightProfile = LightProfile(light_profile_list, interpol_grid_num=interpol_grid_num,
                                         max_interpolate=max_integrate, min_interpolate=min_integrate)
        self.aperture = aperture_select(**kwargs_aperture)
        self.anisotropy = MamonLokasAnisotropy(anisotropy_model)

        self.cosmo = Cosmo(**kwargs_cosmo)
        self._num_sampling = sampling_number
        self._interp_grid_num = interpol_grid_num
        self._log_int = log_integration
        self._max_integrate = max_integrate  # maximal integration (and interpolation) in units of arcsecs
        self._min_integrate = min_integrate  # min integration (and interpolation) in units of arcsecs
        self._psf = psf_select(**kwargs_psf)
Пример #3
0
    def __init__(self, mass_profile_list, light_profile_list, aperture_type='slit', anisotropy_model='isotropic',
                 psf_type='GAUSSIAN', fwhm=0.7, moffat_beta=2.6, kwargs_cosmo={'D_d': 1000, 'D_s': 2000, 'D_ds': 500},
                 sampling_number=1000, interpol_grid_num=500, log_integration=False, max_integrate=10, min_integrate=0.001):
        """

        :param mass_profile_list: list of lens (mass) model profiles
        :param light_profile_list: list of light model profiles of the lensing galaxy
        :param aperture_type: type of slit/shell aperture where the light is coming from. See details in Aperture() class.
        :param anisotropy_model: type of stellar anisotropy model. See details in MamonLokasAnisotropy() class.
        :param psf_type: string, point spread functino type, current support for 'GAUSSIAN' and 'MOFFAT'
        :param fwhm: full width at half maximum seeing condition
        :param moffat_beta: float, beta parameter of Moffat profile
        :param kwargs_cosmo: keyword arguments that define the cosmology in terms of the angular diameter distances involved
        """
        self.massProfile = MassProfile(mass_profile_list, kwargs_cosmo, interpol_grid_num=interpol_grid_num,
                                         max_interpolate=max_integrate, min_interpolate=min_integrate)
        self.lightProfile = LightProfile(light_profile_list, interpol_grid_num=interpol_grid_num,
                                         max_interpolate=max_integrate, min_interpolate=min_integrate)
        self.aperture = Aperture(aperture_type)
        self.anisotropy = MamonLokasAnisotropy(anisotropy_model)

        self.cosmo = Cosmo(**kwargs_cosmo)
        self._num_sampling = sampling_number
        self._interp_grid_num = interpol_grid_num
        self._log_int = log_integration
        self._max_integrate = max_integrate  # maximal integration (and interpolation) in units of arcsecs
        self._min_integrate = min_integrate  # min integration (and interpolation) in units of arcsecs
        self._psf = PSF(psf_type=psf_type, fwhm=fwhm, moffat_beta=moffat_beta)
Пример #4
0
    def test_draw_light_3d(self):
        lightProfile = LightProfile(profile_list=['HERNQUIST'],
                                    min_interpolate=0.0001,
                                    max_interpolate=100.)
        kwargs_profile = [{'amp': 1., 'Rs': 0.5}]
        r_list = lightProfile.draw_light_3d(kwargs_profile,
                                            n=100000,
                                            new_compute=False)
        print(r_list, 'r_list')
        # project it
        R, x, y = velocity_util.project2d_random(r_list)
        # test with draw light 2d profile routine

        bins = np.linspace(0.1, 1, 10)
        hist, bins_hist = np.histogram(r_list, bins=bins, density=True)
        bins_plot = (bins_hist[1:] + bins_hist[:-1]) / 2.
        light3d = lightProfile.light_3d(r=bins_plot,
                                        kwargs_list=kwargs_profile)
        light3d *= bins_plot**2
        light3d /= np.sum(light3d)
        hist /= np.sum(hist)
        #import matplotlib.pyplot as plt
        #plt.plot(bins_plot , light3d/light3d[5], label='3d reference')
        #plt.plot(bins_plot, hist / hist[5], label='hist')
        #plt.legend()
        #plt.show()
        print(light3d / hist)
        npt.assert_almost_equal(light3d[5] / hist[5], 1, decimal=1)
Пример #5
0
 def __init__(self,
              mass_profile_list,
              light_profile_list,
              aperture_type='slit',
              anisotropy_model='isotropic',
              fwhm=0.7,
              kwargs_numerics={},
              kwargs_cosmo={
                  'D_d': 1000,
                  'D_s': 2000,
                  'D_ds': 500
              }):
     self.massProfile = MassProfile(mass_profile_list,
                                    kwargs_cosmo,
                                    kwargs_numerics=kwargs_numerics)
     self.lightProfile = LightProfile(light_profile_list,
                                      kwargs_numerics=kwargs_numerics)
     self.aperture = Aperture(aperture_type)
     self.anisotropy = MamonLokasAnisotropy(anisotropy_model)
     self.FWHM = fwhm
     self.cosmo = Cosmo(kwargs_cosmo)
     #kwargs_numerics = {'sampling_number': 10000, 'interpol_grid_num': 5000, 'log_integration': False,
     #                   'max_integrate': 500}
     self._num_sampling = kwargs_numerics.get('sampling_number', 1000)
     self._interp_grid_num = kwargs_numerics.get('interpol_grid_num', 500)
     self._log_int = kwargs_numerics.get('log_integration', False)
     self._max_integrate = kwargs_numerics.get(
         'max_integrate',
         10)  # maximal integration (and interpolation) in units of arcsecs
     self._min_integrate = kwargs_numerics.get(
         'min_integrate',
         0.001)  # min integration (and interpolation) in units of arcsecs
Пример #6
0
    def test_realistic(self):
        """
        realistic test example
        :return:
        """
        light_profile_list = ['HERNQUIST_ELLIPSE', 'PJAFFE_ELLIPSE']
        phi, q = 0.74260706384506325, 0.46728323131925864
        e1, e2 = param_util.phi_q2_ellipticity(phi, q)

        phi2, q2 = -0.33379268413794494, 0.66582356813012267
        e12, e22 = param_util.phi_q2_ellipticity(phi2, q2)
        kwargs_light = [{
            'Rs': 0.10535462602138289,
            'e1': e1,
            'e2': e2,
            'center_x': -0.02678473951679429,
            'center_y': 0.88691126347462712,
            'amp': 3.7114695634960109
        }, {
            'Rs': 0.44955054610388684,
            'e1': e12,
            'e2': e22,
            'center_x': 0.019536801118136753,
            'center_y': 0.0218888643537157,
            'Ra': 0.0010000053334891974,
            'amp': 967.00280526319796
        }]
        lightProfile = LightProfile(light_profile_list)
        R = 0.01
        light2d = lightProfile.light_2d(R=R, kwargs_list=kwargs_light)
        out = integrate.quad(
            lambda x: lightProfile.light_3d(np.sqrt(R**2 + x**2), kwargs_light
                                            ), 0, 100)
        print(out, 'out')
        npt.assert_almost_equal(light2d / (out[0] * 2), 1., decimal=3)
Пример #7
0
    def __init__(self,
                 kwargs_model,
                 kwargs_cosmo,
                 interpol_grid_num=100,
                 log_integration=False,
                 max_integrate=100,
                 min_integrate=0.001):
        """

        :param interpol_grid_num:
        :param log_integration:
        :param max_integrate:
        :param min_integrate:
        """
        mass_profile_list = kwargs_model.get('mass_profile_list')
        light_profile_list = kwargs_model.get('light_profile_list')
        anisotropy_model = kwargs_model.get('anisotropy_model')
        self._interp_grid_num = interpol_grid_num
        self._log_int = log_integration
        self._max_integrate = max_integrate  # maximal integration (and interpolation) in units of arcsecs
        self._min_integrate = min_integrate  # min integration (and interpolation) in units of arcsecs
        self._max_interpolate = max_integrate  # we chose to set the interpolation range to the integration range
        self._min_interpolate = min_integrate  # we chose to set the interpolation range to the integration range
        self.lightProfile = LightProfile(light_profile_list,
                                         interpol_grid_num=interpol_grid_num,
                                         max_interpolate=max_integrate,
                                         min_interpolate=min_integrate)
        Anisotropy.__init__(self, anisotropy_type=anisotropy_model)
        self.cosmo = Cosmo(**kwargs_cosmo)
        self._mass_profile = SinglePlane(mass_profile_list)
Пример #8
0
 def test_light_3d(self):
     lightProfile = LightProfile(profile_list=['HERNQUIST'])
     r = np.logspace(-2, 2, 100)
     kwargs_profile = [{'amp': 1., 'Rs': 0.5}]
     light_3d = lightProfile.light_3d_interp(r, kwargs_profile)
     light_3d_exact = lightProfile.light_3d(r, kwargs_profile)
     for i in range(len(r)):
         npt.assert_almost_equal(light_3d[i]/light_3d_exact[i], 1, decimal=3)
Пример #9
0
 def test_realistic_1(self):
     """
     realistic test example
     :return:
     """
     light_profile_list = ['HERNQUIST_ELLIPSE']
     kwargs_light = [{'Rs': 0.10535462602138289, 'q': 0.46728323131925864, 'center_x': -0.02678473951679429, 'center_y': 0.88691126347462712, 'phi_G': 0.74260706384506325, 'sigma0': 3.7114695634960109}]
     lightProfile = LightProfile(light_profile_list)
     R = 0.01
     light2d = lightProfile.light_2d(R=R, kwargs_list=kwargs_light)
     out = integrate.quad(lambda x: lightProfile.light_3d(np.sqrt(R**2+x**2), kwargs_light), 0, 100)
     print(out, 'out')
     npt.assert_almost_equal(light2d/(out[0]*2), 1., decimal=3)
Пример #10
0
    def test_projected_light_integral_pjaffe(self):
        """

        :return:
        """
        light_profile_list = ['PJAFFE']
        kwargs_light = [{'Rs': .5, 'Ra': 0.01, 'sigma0': 1.}]  # effective half light radius (2d projected) in arcsec
        lightProfile = LightProfile(light_profile_list)
        R = 0.01
        light2d = lightProfile.light_2d(R=R, kwargs_list=kwargs_light)
        out = integrate.quad(lambda x: lightProfile.light_3d(np.sqrt(R**2+x**2), kwargs_light), 0, 100)
        print(out, 'out')
        npt.assert_almost_equal(light2d/(out[0]*2), 1., decimal=3)
Пример #11
0
    def test_projected_light_integral_hernquist_ellipse(self):
        """

        :return:
        """
        light_profile_list = ['HERNQUIST_ELLIPSE']
        r_eff = 1.
        kwargs_light = [{'Rs': r_eff, 'sigma0': 1., 'q': 0.8, 'phi_G': 1.}]  # effective half light radius (2d projected) in arcsec
        lightProfile = LightProfile(light_profile_list)
        R = 2
        light2d = lightProfile.light_2d(R=R, kwargs_list=kwargs_light)
        out = integrate.quad(lambda x: lightProfile.light_3d(np.sqrt(R**2+x**2), kwargs_light), 0, 10)
        npt.assert_almost_equal(light2d, out[0]*2, decimal=3)
Пример #12
0
    def test_projected_light_integral_hernquist(self):
        """

        :return:
        """
        light_profile_list = ['HERNQUIST']
        Rs = 1.
        kwargs_light = [{'Rs': Rs, 'amp': 1.}]  # effective half light radius (2d projected) in arcsec
        lightProfile = LightProfile(light_profile_list)
        R = 2
        light2d = lightProfile.light_2d(R=R, kwargs_list=kwargs_light)
        out = integrate.quad(lambda x: lightProfile.light_3d(np.sqrt(R**2+x**2), kwargs_light), 0, 100)
        npt.assert_almost_equal(light2d, out[0]*2, decimal=3)
Пример #13
0
    def test_draw_light_PJaffe(self):
        lightProfile = LightProfile(profile_list=['PJAFFE'])
        kwargs_profile = [{'amp': 1., 'Rs': 0.5, 'Ra': 0.2}]
        r_list = lightProfile.draw_light_2d(kwargs_profile, n=100000)
        bins = np.linspace(0, 2, 10)
        hist, bins_hist = np.histogram(r_list, bins=bins, normed=True)
        light2d = lightProfile.light_2d(R=(bins_hist[1:] + bins_hist[:-1]) /
                                        2.,
                                        kwargs_list=kwargs_profile)
        light2d *= (bins_hist[1:] + bins_hist[:-1]) / 2.
        light2d /= np.sum(light2d)
        hist /= np.sum(hist)
        print(light2d / hist)
        npt.assert_almost_equal(light2d[8] / hist[8], 1, decimal=1)

        lightProfile = LightProfile(profile_list=['PJAFFE'],
                                    min_interpolate=0.0001,
                                    max_interpolate=20.)
        kwargs_profile = [{'amp': 1., 'Rs': 0.04, 'Ra': 0.02}]
        r_list = lightProfile.draw_light_2d(kwargs_profile, n=100000)
        bins = np.linspace(0., 0.1, 10)
        hist, bins_hist = np.histogram(r_list, bins=bins, normed=True)
        light2d = lightProfile.light_2d(R=(bins_hist[1:] + bins_hist[:-1]) /
                                        2.,
                                        kwargs_list=kwargs_profile)
        light2d *= (bins_hist[1:] + bins_hist[:-1]) / 2.
        light2d /= np.sum(light2d)
        hist /= np.sum(hist)
        print(light2d / hist)
        npt.assert_almost_equal(light2d[5] / hist[5], 1, decimal=1)
Пример #14
0
 def test_draw_light(self):
     lightProfile = LightProfile(profile_list=['HERNQUIST'])
     kwargs_profile = [{'amp': 1., 'Rs': 0.8}]
     r_list = lightProfile.draw_light_2d(kwargs_profile, n=500000)
     bins = np.linspace(0., 1, 20)
     hist, bins_hist = np.histogram(r_list, bins=bins, normed=True)
     light2d = lightProfile.light_2d(R=(bins_hist[1:] + bins_hist[:-1])/2., kwargs_list=kwargs_profile)
     light2d *= (bins_hist[1:] + bins_hist[:-1]) / 2.
     light2d /= np.sum(light2d)
     hist /= np.sum(hist)
     print(light2d / hist)
     for i in range(len(hist)):
         print(bins_hist[i], i, light2d[i] / hist[i])
         npt.assert_almost_equal(light2d[i] / hist[i], 1, decimal=1)
Пример #15
0
    def test_projected_light_integral_hernquist_ellipse(self):
        """

        :return:
        """
        light_profile_list = ['HERNQUIST_ELLIPSE']
        Rs = 1.
        phi, q = 1, 0.8
        e1, e2 = param_util.phi_q2_ellipticity(phi, q)
        kwargs_light = [{'Rs': Rs, 'amp': 1.,'e1': e1, 'e2': e2}]  # effective half light radius (2d projected) in arcsec
        lightProfile = LightProfile(light_profile_list)
        R = 2
        light2d = lightProfile.light_2d(R=R, kwargs_list=kwargs_light)
        out = integrate.quad(lambda x: lightProfile.light_3d(np.sqrt(R**2+x**2), kwargs_light), 0, 10)
        npt.assert_almost_equal(light2d, out[0]*2, decimal=3)
Пример #16
0
    def __init__(self,
                 kwargs_model,
                 kwargs_cosmo,
                 interpol_grid_num=1000,
                 log_integration=True,
                 max_integrate=1000,
                 min_integrate=0.0001,
                 max_light_draw=None,
                 lum_weight_int_method=True):
        """
        What we need:
        - max projected R to have ACCURATE I_R_sigma values
        - make sure everything outside cancels out (or is not rendered)

        :param interpol_grid_num: number of interpolation bins for integrand and interpolated functions
        :param log_integration: bool, if True, performs the numerical integral in log space distance (adviced)
         (only applies for lum_weight_int_method=True)
        :param max_integrate: maximum radius (in arc seconds) of the Jeans equation integral
         (assumes zero tracer particles outside this radius)
        :param max_light_draw: float; (optional) if set, draws up to this radius, else uses max_interpolate value
        :param lum_weight_int_method: bool, luminosity weighted dispersion integral to calculate LOS projected Jean's
         solution. ATTENTION: currently less accurate than 3d solution
        :param min_integrate:
        """
        mass_profile_list = kwargs_model.get('mass_profile_list')
        light_profile_list = kwargs_model.get('light_profile_list')
        anisotropy_model = kwargs_model.get('anisotropy_model')
        self._interp_grid_num = interpol_grid_num
        self._log_int = log_integration
        self._max_integrate = max_integrate  # maximal integration (and interpolation) in units of arcsecs
        self._min_integrate = min_integrate  # min integration (and interpolation) in units of arcsecs
        self._max_interpolate = max_integrate  # we chose to set the interpolation range to the integration range
        self._min_interpolate = min_integrate  # we chose to set the interpolation range to the integration range
        if max_light_draw is None:
            max_light_draw = max_integrate  # make sure the actual solution for the kinematics is only computed way inside the integral
        self.lightProfile = LightProfile(light_profile_list,
                                         interpol_grid_num=interpol_grid_num,
                                         max_interpolate=max_integrate,
                                         min_interpolate=min_integrate,
                                         max_draw=max_light_draw)
        Anisotropy.__init__(self, anisotropy_type=anisotropy_model)
        self.cosmo = Cosmo(**kwargs_cosmo)
        self._mass_profile = SinglePlane(mass_profile_list)
        self._lum_weight_int_method = lum_weight_int_method
Пример #17
0
    def __init__(self,
                 mass_profile_list,
                 light_profile_list,
                 aperture_type='slit',
                 anisotropy_model='isotropic',
                 fwhm=0.7,
                 kwargs_cosmo={
                     'D_d': 1000,
                     'D_s': 2000,
                     'D_ds': 500
                 },
                 sampling_number=1000,
                 interpol_grid_num=500,
                 log_integration=False,
                 max_integrate=10,
                 min_integrate=0.001):
        """

        :param mass_profile_list: list of lens (mass) model profiles
        :param light_profile_list: list of light model profiles of the lensing galaxy
        :param aperture_type: type of slit/shell aperture where the light is coming from. See details in Aperture() class.
        :param anisotropy_model: type of stellar anisotropy model. See details in MamonLokasAnisotropy() class.
        :param fwhm: full width at half maximum seeing condition
        :param kwargs_numerics: keyword arguments that control the numerical computation
        :param kwargs_cosmo: keyword arguments that define the cosmology in terms of the angular diameter distances involved
        """
        self.massProfile = MassProfile(mass_profile_list,
                                       kwargs_cosmo,
                                       interpol_grid_num=interpol_grid_num,
                                       max_interpolate=max_integrate,
                                       min_interpolate=min_integrate)
        self.lightProfile = LightProfile(light_profile_list,
                                         interpol_grid_num=interpol_grid_num,
                                         max_interpolate=max_integrate,
                                         min_interpolate=min_integrate)
        self.aperture = Aperture(aperture_type)
        self.anisotropy = MamonLokasAnisotropy(anisotropy_model)
        self._fwhm = fwhm
        self.cosmo = Cosmo(**kwargs_cosmo)
        self._num_sampling = sampling_number
        self._interp_grid_num = interpol_grid_num
        self._log_int = log_integration
        self._max_integrate = max_integrate  # maximal integration (and interpolation) in units of arcsecs
        self._min_integrate = min_integrate  # min integration (and interpolation) in units of arcsecs
Пример #18
0
 def test_del_cache(self):
     lightProfile = LightProfile(profile_list=['HERNQUIST'])
     lightProfile._light_cdf = 1
     lightProfile._light_cdf_log = 2
     lightProfile._f_light_3d = 3
     lightProfile.delete_cache()
     assert hasattr(lightProfile, '_light_cdf') is False
     assert hasattr(lightProfile, '_light_cdf_log') is False
     assert hasattr(lightProfile, '_f_light_3d') is False
Пример #19
0
    def test_light_2d_finite(self):
        interpol_grid_num = 5000
        max_interpolate = 10
        min_interpolate = 0.0001
        lightProfile = LightProfile(profile_list=['HERNQUIST'], interpol_grid_num=interpol_grid_num,
                                    max_interpolate=max_interpolate, min_interpolate=min_interpolate)
        kwargs_profile = [{'amp': 1., 'Rs': 1.}]

        # check whether projected light integral is the same as analytic expression
        R = 1.

        I_R = lightProfile.light_2d_finite(R, kwargs_profile)
        out = integrate.quad(lambda x: lightProfile.light_3d(np.sqrt(R ** 2 + x ** 2), kwargs_profile),
                             min_interpolate, np.sqrt(max_interpolate ** 2 - R ** 2))
        l_R_quad = out[0] * 2

        npt.assert_almost_equal(l_R_quad / I_R, 1, decimal=2)

        l_R = lightProfile.light_2d(R, kwargs_profile)
        npt.assert_almost_equal(l_R / I_R, 1, decimal=2)
Пример #20
0
    def test_draw_light_3d_power_law(self):
        lightProfile = LightProfile(profile_list=['POWER_LAW'], min_interpolate=0.0001, max_interpolate=1000.)
        kwargs_profile = [{'amp': 1., 'gamma': 2, 'e1': 0, 'e2': 0}]
        r_list = lightProfile.draw_light_3d(kwargs_profile, n=1000000, new_compute=False)
        print(r_list, 'r_list')
        # project it
        R, x, y = velocity_util.project2d_random(r_list)
        # test with draw light 2d profile routine

        # compare with 3d analytical solution vs histogram binned
        bins = np.linspace(0.1, 10, 10)
        hist, bins_hist = np.histogram(r_list, bins=bins, density=True)
        bins_plot = (bins_hist[1:] + bins_hist[:-1]) / 2.
        light3d = lightProfile.light_3d(r=bins_plot, kwargs_list=kwargs_profile)
        light3d *= bins_plot ** 2
        light3d /= np.sum(light3d)
        hist /= np.sum(hist)
        #import matplotlib.pyplot as plt
        #plt.plot(bins_plot , light3d/light3d[5], label='3d reference power-law')
        #plt.plot(bins_plot, hist / hist[5], label='hist')
        #plt.legend()
        #plt.show()
        print(light3d / hist)
        npt.assert_almost_equal(light3d / hist, 1, decimal=1)

        # compare with 2d analytical solution vs histogram binned
        #bins = np.linspace(0.1, 1, 10)
        hist, bins_hist = np.histogram(R, bins=bins, density=True)
        bins_plot = (bins_hist[1:] + bins_hist[:-1]) / 2.
        light2d = lightProfile.light_2d_finite(R=bins_plot, kwargs_list=kwargs_profile)
        light2d *= bins_plot ** 1
        light2d /= np.sum(light2d)
        hist /= np.sum(hist)
        #import matplotlib.pyplot as plt
        #plt.plot(bins_plot , light2d/light2d[5], label='2d reference power-law')
        #plt.plot(bins_plot, hist / hist[5], label='hist')
        #plt.legend()
        #plt.show()
        print(light2d / hist)
        npt.assert_almost_equal(light2d / hist, 1, decimal=1)
Пример #21
0
 def test_draw_light_2d_linear(self):
     np.random.seed(41)
     lightProfile = LightProfile(profile_list=['HERNQUIST'],
                                 interpol_grid_num=1000,
                                 max_interpolate=10,
                                 min_interpolate=0.01)
     kwargs_profile = [{'amp': 1., 'Rs': 0.8}]
     r_list = lightProfile.draw_light_2d_linear(kwargs_profile, n=100000)
     bins = np.linspace(0., 1, 20)
     hist, bins_hist = np.histogram(r_list, bins=bins, density=True)
     light2d = lightProfile.light_2d(R=(bins_hist[1:] + bins_hist[:-1]) /
                                     2.,
                                     kwargs_list=kwargs_profile)
     light2d_upper = lightProfile.light_2d(
         R=bins_hist[1:], kwargs_list=kwargs_profile) * bins_hist[1:]
     light2d_lower = lightProfile.light_2d(
         R=bins_hist[:-1], kwargs_list=kwargs_profile) * bins_hist[:-1]
     light2d *= (bins_hist[1:] + bins_hist[:-1]) / 2.
     print((light2d_upper - light2d_lower) /
           (light2d_upper + light2d_lower) * 2)
     light2d /= np.sum(light2d)
     hist /= np.sum(hist)
     print(light2d / hist)
     for i in range(2, len(hist)):
         print(bins_hist[i], i, light2d[i] / hist[i])
         npt.assert_almost_equal(light2d[i] / hist[i], 1, decimal=1)
Пример #22
0
    def test_draw_light_3d_hernquist(self):
        lightProfile = LightProfile(profile_list=['HERNQUIST'], min_interpolate=0.0001, max_interpolate=1000.)
        kwargs_profile = [{'amp': 1., 'Rs': 0.5}]
        r_list = lightProfile.draw_light_3d(kwargs_profile, n=1000000, new_compute=False)
        print(r_list, 'r_list')
        # project it

        # test with draw light 2d profile routine
        # compare with 3d analytical solution vs histogram binned
        bins = np.linspace(0.0, 10, 20)
        hist, bins_hist = np.histogram(r_list, bins=bins, density=True)
        bins_plot = (bins_hist[1:] + bins_hist[:-1]) / 2.
        light3d = lightProfile.light_3d(r=bins_plot, kwargs_list=kwargs_profile)
        light3d *= bins_plot ** 2
        light3d /= np.sum(light3d)
        hist /= np.sum(hist)
        #import matplotlib.pyplot as plt
        #plt.plot(bins_plot , light3d/light3d[5], label='3d reference Hernquist')
        #plt.plot(bins_plot, hist / hist[5], label='hist')
        #plt.legend()
        #plt.show()
        print(light3d / hist)
        #npt.assert_almost_equal(light3d / hist, 1, decimal=1)

        # compare with 2d analytical solution vs histogram binned
        #bins = np.linspace(0.1, 1, 10)
        R, x, y = velocity_util.project2d_random(np.array(r_list))
        hist_2d, bins_hist = np.histogram(R, bins=bins, density=True)
        hist_2d /= np.sum(hist_2d)
        bins_plot = (bins_hist[1:] + bins_hist[:-1]) / 2.
        light2d = lightProfile.light_2d(R=bins_plot, kwargs_list=kwargs_profile)
        light2d *= bins_plot ** 1
        light2d /= np.sum(light2d)

        light2d_finite = lightProfile.light_2d_finite(R=bins_plot, kwargs_list=kwargs_profile)
        light2d_finite *= bins_plot ** 1
        light2d_finite /= np.sum(light2d_finite)
        hist /= np.sum(hist)
        #import matplotlib.pyplot as plt
        #plt.plot(bins_plot, light2d/light2d[5], '--', label='2d reference Hernquist')
        #plt.plot(bins_plot, light2d_finite / light2d_finite[5], '-.', label='2d reference Hernquist finite')
        #plt.plot(bins_plot, hist_2d / hist_2d[5], label='hist')
        #plt.legend()
        #plt.show()
        print(light2d / hist_2d)

        #plt.plot(R, r_list, '.', label='test')
        #plt.legend()
        #plt.xlim([0, 0.2])
        #plt.ylim([0, 0.2])
        #plt.show()

        npt.assert_almost_equal(light2d / hist_2d, 1, decimal=1)
Пример #23
0
 def test_draw_light_2d_linear(self):
     lightProfile = LightProfile(profile_list=['HERNQUIST'])
     kwargs_profile = [{'sigma0': 1., 'Rs': 0.8}]
     r_list = lightProfile.draw_light_2d_linear(kwargs_profile, n=100000)
     bins = np.linspace(0., 1, 20)
     hist, bins_hist = np.histogram(r_list, bins=bins, normed=True)
     light2d = lightProfile.light_2d(R=(bins_hist[1:] + bins_hist[:-1])/2., kwargs_list=kwargs_profile)
     light2d_upper = lightProfile.light_2d(R=bins_hist[1:], kwargs_list=kwargs_profile) * bins_hist[1:]
     light2d_lower = lightProfile.light_2d(R=bins_hist[:-1], kwargs_list=kwargs_profile) * bins_hist[:-1]
     light2d *= (bins_hist[1:] + bins_hist[:-1]) / 2.
     print((light2d_upper - light2d_lower)/(light2d_upper + light2d_lower) * 2)
     light2d /= np.sum(light2d)
     hist /= np.sum(hist)
     print(light2d / hist)
     for i in range(2, len(hist)):
         print(bins_hist[i], i, light2d[i] / hist[i])
         npt.assert_almost_equal(light2d[i] / hist[i], 1, decimal=1)
Пример #24
0
class NumericKinematics(Anisotropy):
    def __init__(self,
                 kwargs_model,
                 kwargs_cosmo,
                 interpol_grid_num=1000,
                 log_integration=True,
                 max_integrate=1000,
                 min_integrate=0.0001,
                 lum_weight_int_method=False):
        """
        What we need:
        - max projected R to have ACCURATE I_R_sigma values
        - make sure everything outside cancels out (or is not rendered)

        :param interpol_grid_num: number of interpolation bins for integrand and interpolated functions
        :param log_integration: bool, if True, performs the numerical integral in log space distance (adviced)
        :param max_integrate: maximum radius (in arc seconds) of the Jeans equation integral
         (assumes zero tracer particles outside this radius)
        :param lum_weight_int_method: bool, luminosity weighted dispersion integral to calculate LOS projected Jean's
         solution. ATTENTION: currently less accurate than 3d solution
        :param min_integrate:
        """
        mass_profile_list = kwargs_model.get('mass_profile_list')
        light_profile_list = kwargs_model.get('light_profile_list')
        anisotropy_model = kwargs_model.get('anisotropy_model')
        self._interp_grid_num = interpol_grid_num
        self._log_int = log_integration
        self._max_integrate = max_integrate  # maximal integration (and interpolation) in units of arcsecs
        self._min_integrate = min_integrate  # min integration (and interpolation) in units of arcsecs
        self._max_interpolate = max_integrate  # we chose to set the interpolation range to the integration range
        self._min_interpolate = min_integrate  # we chose to set the interpolation range to the integration range
        self.lightProfile = LightProfile(light_profile_list,
                                         interpol_grid_num=interpol_grid_num,
                                         max_interpolate=max_integrate,
                                         min_interpolate=min_integrate)
        Anisotropy.__init__(self, anisotropy_type=anisotropy_model)
        self.cosmo = Cosmo(**kwargs_cosmo)
        self._mass_profile = SinglePlane(mass_profile_list)
        self._lum_weight_int_method = lum_weight_int_method

    def sigma_s2(self, r, R, kwargs_mass, kwargs_light, kwargs_anisotropy):
        """
        returns unweighted los velocity dispersion for a specified projected radius

        :param r: 3d radius (not needed for this calculation)
        :param R: 2d projected radius (in angular units of arcsec)
        :param kwargs_mass: mass model parameters (following lenstronomy lens model conventions)
        :param kwargs_light: deflector light parameters (following lenstronomy light model conventions)
        :param kwargs_anisotropy: anisotropy parameters, may vary according to anisotropy type chosen.
            We refer to the Anisotropy() class for details on the parameters.
        :return: line-of-sight projected velocity dispersion at projected radius R
        """
        if self._lum_weight_int_method is True:
            return self.sigma_s2_project_int(r, R, kwargs_mass, kwargs_light,
                                             kwargs_anisotropy)
        else:
            return self.sigma_s2_full(r, R, kwargs_mass, kwargs_light,
                                      kwargs_anisotropy)

    def sigma_s2_project_int(self, r, R, kwargs_mass, kwargs_light,
                             kwargs_anisotropy):
        """
        returns unweighted los velocity dispersion for a specified projected radius

        :param r: 3d radius (not needed for this calculation)
        :param R: 2d projected radius (in angular units of arcsec)
        :param kwargs_mass: mass model parameters (following lenstronomy lens model conventions)
        :param kwargs_light: deflector light parameters (following lenstronomy light model conventions)
        :param kwargs_anisotropy: anisotropy parameters, may vary according to anisotropy type chosen.
            We refer to the Anisotropy() class for details on the parameters.
        :return: line-of-sight projected velocity dispersion at projected radius R
        """
        # TODO: this is potentially inaccurate as the light-only integral is analytically to infinity while the
        # nominator is numerically to a finite distance, so luminosity weighting might be off
        # this could lead to an under-prediction of the velocity dispersion
        I_R_sigma2 = self._I_R_sigma2_interp(R, kwargs_mass, kwargs_light,
                                             kwargs_anisotropy)
        I_R = self.lightProfile.light_2d(R, kwargs_light)
        return np.nan_to_num(I_R_sigma2 / I_R)

    def sigma_s2_full(self, r, R, kwargs_mass, kwargs_light,
                      kwargs_anisotropy):
        """
        returns unweighted los velocity dispersion for a specified projected radius

        :param r: 3d radius (not needed for this calculation)
        :param R: 2d projected radius (in angular units of arcsec)
        :param kwargs_mass: mass model parameters (following lenstronomy lens model conventions)
        :param kwargs_light: deflector light parameters (following lenstronomy light model conventions)
        :param kwargs_anisotropy: anisotropy parameters, may vary according to anisotropy type chosen.
            We refer to the Anisotropy() class for details on the parameters.
        :return: line-of-sight projected velocity dispersion at projected radius R from 3d radius r
        """
        beta = self.beta_r(r, **kwargs_anisotropy)
        return (1 - beta * R**2 / r**2) * self.sigma_r2(
            r, kwargs_mass, kwargs_light, kwargs_anisotropy)

    def sigma_r2(self, r, kwargs_mass, kwargs_light, kwargs_anisotropy):
        """
        computes numerically the solution of the Jeans equation for a specific 3d radius
        E.g. Equation (A1) of Mamon & Lokas https://arxiv.org/pdf/astro-ph/0405491.pdf
        l(r) \sigma_r(r) ^ 2 =  1/f(r) \int_r^{\infty} f(s) l(s) G M(s) / s^2 ds
        where l(r) is the 3d light profile
        M(s) is the enclosed 3d mass
        f is the solution to
        d ln(f)/ d ln(r) = 2 beta(r)

        :param r: 3d radius
        :param kwargs_mass: mass model parameters (following lenstronomy lens model conventions)
        :param kwargs_light: deflector light parameters (following lenstronomy light model conventions)
        :param kwargs_anisotropy: anisotropy parameters, may vary according to anisotropy type chosen.
            We refer to the Anisotropy() class for details on the parameters.
        :return: sigma_r**2
        """
        l_r = self.lightProfile.light_3d_interp(r, kwargs_light)
        f_r = self.anisotropy_solution(r, **kwargs_anisotropy)
        return 1 / f_r / l_r * self._jeans_solution_integral(
            r, kwargs_mass, kwargs_light, kwargs_anisotropy) * const.G / (
                const.arcsec * self.cosmo.dd * const.Mpc)

    def mass_3d(self, r, kwargs):
        """
        mass enclosed a 3d radius

        :param r: in arc seconds
        :param kwargs: lens model parameters in arc seconds
        :return: mass enclosed physical radius in kg
        """
        mass_dimless = self._mass_profile.mass_3d(r, kwargs)
        mass_dim = mass_dimless * const.arcsec ** 2 * self.cosmo.dd * self.cosmo.ds / self.cosmo.dds * const.Mpc * \
                   const.c ** 2 / (4 * np.pi * const.G)
        return mass_dim

    def grav_potential(self, r, kwargs_mass):
        """
        Gravitational potential in SI units

        :param r: radius (arc seconds)
        :param kwargs_mass:
        :return: gravitational potential
        """
        mass_dim = self.mass_3d(r, kwargs_mass)
        grav_pot = -const.G * mass_dim / (r * const.arcsec * self.cosmo.dd *
                                          const.Mpc)
        return grav_pot

    def draw_light(self, kwargs_light):
        """

        :param kwargs_light: keyword argument (list) of the light model
        :return: 3d radius (if possible), 2d projected radius, x-projected coordinate, y-projected coordinate
        """
        r = self.lightProfile.draw_light_3d(kwargs_light, n=1)[0]
        R, x, y = util.project2d_random(r)

        # this code is a remnant of the 2d-only rendering
        # (can be used when accurate luminosity-weighted integrated velocity dispersion predictions are made)
        # R = self.lightProfile.draw_light_2d(kwargs_light, n=1)[0]
        # x, y = util.draw_xy(R)
        # r = None
        return r, R, x, y

    def delete_cache(self):
        """
        delete interpolation function for a specific mass and light profile as well as for a specific anisotropy model

        :return:
        """
        if hasattr(self, '_log_mass_3d'):
            del self._log_mass_3d
        if hasattr(self, '_interp_jeans_integral'):
            del self._interp_jeans_integral
        if hasattr(self, '_interp_I_R_sigma2'):
            del self._interp_I_R_sigma2
        self.lightProfile.delete_cache()
        self.delete_anisotropy_cache()

    def _I_R_sigma2(self, R, kwargs_mass, kwargs_light, kwargs_anisotropy):
        """
        equation A15 in Mamon&Lokas 2005 as a logarithmic numerical integral (if option is chosen)

        :param R: 2d projected radius (in angular units)
        :param kwargs_mass: mass model parameters (following lenstronomy lens model conventions)
        :param kwargs_light: deflector light parameters (following lenstronomy light model conventions)
        :param kwargs_anisotropy: anisotropy parameters, may vary according to anisotropy type chosen.
            We refer to the Anisotropy() class for details on the parameters.
        :return: integral of A15 in Mamon&Lokas 2005
        """
        R = max(R, self._min_integrate)
        max_integrate = self._max_integrate  # make sure the integration of the Jeans equation is performed further out than the interpolation
        if self._log_int is True:
            min_log = np.log10(R + 0.001)
            max_log = np.log10(max_integrate)
            r_array = np.logspace(min_log, max_log, self._interp_grid_num)
            dlog_r = (np.log10(r_array[2]) - np.log10(r_array[1])) * np.log(10)
            IR_sigma2_dr = self._integrand_A15(
                r_array, R, kwargs_mass, kwargs_light,
                kwargs_anisotropy) * dlog_r * r_array
        else:
            r_array = np.linspace(R + 0.001, max_integrate,
                                  self._interp_grid_num)
            dr = r_array[2] - r_array[1]
            IR_sigma2_dr = self._integrand_A15(
                r_array, R, kwargs_mass, kwargs_light, kwargs_anisotropy) * dr
        IR_sigma2 = np.sum(
            IR_sigma2_dr)  # integral from angle to physical scales
        return IR_sigma2 * 2 * const.G / (const.arcsec * self.cosmo.dd *
                                          const.Mpc)

    def _I_R_sigma2_interp(self, R, kwargs_mass, kwargs_light,
                           kwargs_anisotropy):
        """
        equation A15 in Mamon&Lokas 2005 as interpolation in log space

        :param R: projected radius
        :param kwargs_mass: mass profile keyword arguments
        :param kwargs_light: light model keyword arguments
        :param kwargs_anisotropy: stellar anisotropy keyword arguments
        :return:
        """
        if not hasattr(self, '_interp_I_R_sigma2'):
            min_log = np.log10(self._min_integrate)
            max_log = np.log10(self._max_integrate)
            R_array = np.logspace(min_log, max_log, self._interp_grid_num)
            I_R_sigma2_array = []
            for R_i in R_array:
                I_R_sigma2_array.append(
                    self._I_R_sigma2(R_i, kwargs_mass, kwargs_light,
                                     kwargs_anisotropy))
            self._interp_I_R_sigma2 = interp1d(np.log(R_array),
                                               np.array(I_R_sigma2_array),
                                               fill_value="extrapolate")
        return self._interp_I_R_sigma2(np.log(R))

    def _integrand_A15(self, r, R, kwargs_mass, kwargs_light,
                       kwargs_anisotropy):
        """
        integrand of A15 (in log space) in Mamon&Lokas 2005

        :param r: 3d radius in arc seconds
        :param R: 2d projected radius
        :param kwargs_mass: mass model parameters (following lenstronomy lens model conventions)
        :param kwargs_light: deflector light parameters (following lenstronomy light model conventions)
        :param kwargs_anisotropy: anisotropy parameters, may vary according to anisotropy type chosen.
            We refer to the Anisotropy() class for details on the parameters.
        :return:
        """
        k_r = self.K(r, R, **kwargs_anisotropy)
        l_r = self.lightProfile.light_3d_interp(r, kwargs_light)
        m_r = self._mass_3d_interp(r, kwargs_mass)
        out = k_r * l_r * m_r / r
        return out

    def _jeans_solution_integral(self, r, kwargs_mass, kwargs_light,
                                 kwargs_anisotropy):
        """
        interpolated solution of the integral \int_r^{\infty} f(s) l(s) G M(s) / s^2 ds

        :param r: 3d radius
        :param kwargs_mass: mass profile keyword arguments
        :param kwargs_light: light profile keyword arguments
        :param kwargs_anisotropy: anisotropy keyword arguments
        :return: interpolated solution of the Jeans integral
         (copped values at large radius as they become numerically inaccurate)
        """
        if not hasattr(self, '_interp_jeans_integral'):
            min_log = np.log10(self._min_integrate)
            max_log = np.log10(
                self._max_integrate
            )  # we extend the integral but ignore these outer solutions in the interpolation
            r_array = np.logspace(min_log, max_log, self._interp_grid_num)
            dlog_r = (np.log10(r_array[2]) - np.log10(r_array[1])) * np.log(10)
            integrand_jeans = self._integrand_jeans_solution(
                r_array, kwargs_mass, kwargs_light,
                kwargs_anisotropy) * dlog_r * r_array
            #flip array from inf to finite
            integral_jeans_r = np.cumsum(np.flip(integrand_jeans))
            #flip array back
            integral_jeans_r = np.flip(integral_jeans_r)
            #call 1d interpolation function
            self._interp_jeans_integral = interp1d(
                np.log(r_array[r_array <= self._max_integrate]),
                integral_jeans_r[r_array <= self._max_integrate],
                fill_value="extrapolate")
        return self._interp_jeans_integral(np.log(r))

    def _integrand_jeans_solution(self, r, kwargs_mass, kwargs_light,
                                  kwargs_anisotropy):
        """
        integrand of A1 (in log space) in Mamon&Lokas 2005 to calculate the Jeans equation numerically
        f(s) l(s) M(s) / s^2

        :param r:
        :param kwargs_mass:
        :param kwargs_light:
        :param kwargs_anisotropy:
        :return:
        """
        f_r = self.anisotropy_solution(r, **kwargs_anisotropy)
        l_r = self.lightProfile.light_3d_interp(r, kwargs_light)
        m_r = self._mass_3d_interp(r, kwargs_mass)
        out = f_r * l_r * m_r / r**2
        return out

    def _mass_3d_interp(self, r, kwargs, new_compute=False):
        """

        :param r: in arc seconds
        :param kwargs: lens model parameters in arc seconds
        :param new_compute: bool, if True, recomputes the interpolation
        :return: mass enclosed physical radius in kg
        """
        if not hasattr(self, '_log_mass_3d') or new_compute is True:
            r_array = np.logspace(np.log10(self._min_interpolate),
                                  np.log10(self._max_interpolate),
                                  self._interp_grid_num)
            mass_3d_array = self.mass_3d(r_array, kwargs)
            mass_3d_array[mass_3d_array < 10.**(-10)] = 10.**(-10)
            #mass_dim_array = mass_3d_array * const.arcsec ** 2 * self.cosmo.dd * self.cosmo.ds \
            #                 / self.cosmo.dds * const.Mpc * const.c ** 2 / (4 * np.pi * const.G)
            self._log_mass_3d = interp1d(np.log(r_array),
                                         np.log(mass_3d_array / r_array),
                                         fill_value="extrapolate")
        return np.exp(self._log_mass_3d(np.log(r))) * r
Пример #25
0
class Galkin(object):
    """
    Major class to compute velocity dispersion measurements given light and mass models

    The class supports any mass and light distribution (and superposition thereof) that has a 3d correspondance in their
    2d lens model distribution. For models that do not have this correspondance, you may want to apply a
    Multi-Gaussian Expansion (MGE) on their models and use the MGE to be de-projected to 3d.

    The computation follows Mamon&Lokas 2005 and performs the spectral rendering of the seeing convolved apperture with
    the method introduced by Birrer et al. 2016.

    The class supports various types of anisotropy models (see Anisotropy class) and aperture types (see Aperture class).
    Solving the Jeans Equation requires a numerical integral over the 3d light and mass profile (see Mamon&Lokas 2005).
    This class (as well as the dedicated LightModel and MassModel classes) perform those integral numerically with an
    interpolated grid.

    The seeing convolved integral over the aperture is computed by rendering spectra (light weighted LOS kinematics)
    from the light distribution.

    The cosmology assumed to compute the physical mass and distances are set via the kwargs_cosmo keyword arguments.
        D_d: Angular diameter distance to the deflector (in Mpc)
        D_s: Angular diameter distance to the source (in Mpc)
        D_ds: Angular diameter distance from the deflector to the source (in Mpc)

    The numerical options can be chosen through the kwargs_numerics keywords
        sampling_number: number of spectral rendering to compute the light weighted integrated LOS dispersion within
        the aperture. This keyword should be chosen high enough to result in converged results within the tolerance.

        interpol_grid_num: number of interpolation points in the light and mass profile (radially). This number should
        be chosen high enough to accurately describe the true light profile underneath.
        log_integration: bool, if True, performs the interpolation and numerical integration in log-scale.

        max_integrate: maximum 3d radius to where the numerical integration of the Jeans Equation solver is made.
        This value should be large enough to contain most of the light and to lead to a converged result.
        min_integrate: minimal integration value. This value should be very close to zero but some mass and light
        profiles are diverging and a numerically stabel value should be chosen.

    These numerical options should be chosen to allow for a converged result (within your tolerance) but not too
    conservative to impact too much the computational cost. Reasonable values might depend on the specific problem.

    """
    def __init__(self,
                 mass_profile_list,
                 light_profile_list,
                 aperture_type='slit',
                 anisotropy_model='isotropic',
                 fwhm=0.7,
                 kwargs_cosmo={
                     'D_d': 1000,
                     'D_s': 2000,
                     'D_ds': 500
                 },
                 sampling_number=1000,
                 interpol_grid_num=500,
                 log_integration=False,
                 max_integrate=10,
                 min_integrate=0.001):
        """

        :param mass_profile_list: list of lens (mass) model profiles
        :param light_profile_list: list of light model profiles of the lensing galaxy
        :param aperture_type: type of slit/shell aperture where the light is coming from. See details in Aperture() class.
        :param anisotropy_model: type of stellar anisotropy model. See details in MamonLokasAnisotropy() class.
        :param fwhm: full width at half maximum seeing condition
        :param kwargs_numerics: keyword arguments that control the numerical computation
        :param kwargs_cosmo: keyword arguments that define the cosmology in terms of the angular diameter distances involved
        """
        self.massProfile = MassProfile(mass_profile_list,
                                       kwargs_cosmo,
                                       interpol_grid_num=interpol_grid_num,
                                       max_interpolate=max_integrate,
                                       min_interpolate=min_integrate)
        self.lightProfile = LightProfile(light_profile_list,
                                         interpol_grid_num=interpol_grid_num,
                                         max_interpolate=max_integrate,
                                         min_interpolate=min_integrate)
        self.aperture = Aperture(aperture_type)
        self.anisotropy = MamonLokasAnisotropy(anisotropy_model)
        self._fwhm = fwhm
        self.cosmo = Cosmo(**kwargs_cosmo)
        self._num_sampling = sampling_number
        self._interp_grid_num = interpol_grid_num
        self._log_int = log_integration
        self._max_integrate = max_integrate  # maximal integration (and interpolation) in units of arcsecs
        self._min_integrate = min_integrate  # min integration (and interpolation) in units of arcsecs

    def vel_disp(self, kwargs_mass, kwargs_light, kwargs_anisotropy,
                 kwargs_apertur):
        """
        computes the averaged LOS velocity dispersion in the slit (convolved)

        :param kwargs_mass: mass model parameters (following lenstronomy lens model conventions)
        :param kwargs_light: deflector light parameters (following lenstronomy light model conventions)
        :param kwargs_anisotropy: anisotropy parameters, may vary according to anisotropy type chosen.
            We refer to the Anisotropy() class for details on the parameters.
        :param kwargs_apertur: Aperture parameters, may vary depending on aperture type chosen.
            We refer to the Aperture() class for details on the parameters.
        :return: integrated LOS velocity dispersion in units [km/s]
        """
        sigma2_R_sum = 0
        for i in range(0, self._num_sampling):
            sigma2_R = self.draw_one_sigma2(kwargs_mass, kwargs_light,
                                            kwargs_anisotropy, kwargs_apertur)
            sigma2_R_sum += sigma2_R
        sigma_s2_average = sigma2_R_sum / self._num_sampling
        # apply unit conversion from arc seconds and deflections to physical velocity disperison in (km/s)
        sigma_s2_average *= 2 * const.G  # correcting for integral prefactor
        return np.sqrt(sigma_s2_average /
                       (const.arcsec**2 * self.cosmo.D_d**2 *
                        const.Mpc)) / 1000.  # in units of km/s

    def draw_one_sigma2(self, kwargs_mass, kwargs_light, kwargs_anisotropy,
                        kwargs_aperture):
        """

        :param kwargs_mass: mass model parameters (following lenstronomy lens model conventions)
        :param kwargs_light: deflector light parameters (following lenstronomy light model conventions)
        :param kwargs_anisotropy: anisotropy parameters, may vary according to anisotropy type chosen.
            We refer to the Anisotropy() class for details on the parameters.
        :param kwargs_apertur: Aperture parameters, may vary depending on aperture type chosen.
            We refer to the Aperture() class for details on the parameters.
        :return: integrated LOS velocity dispersion in angular units for a single draw of the light distribution that
         falls in the aperture after displacing with the seeing
        """
        while True:
            R = self.lightProfile.draw_light_2d(kwargs_light)  # draw r
            x, y = util.draw_xy(R)  # draw projected R
            x_, y_ = util.displace_PSF(x, y, self._fwhm)  # displace via PSF
            bool = self.aperture.aperture_select(x_, y_, kwargs_aperture)
            if bool is True:
                break
        sigma2_R = self.sigma2_R(R, kwargs_mass, kwargs_light,
                                 kwargs_anisotropy)
        return sigma2_R

    def sigma2_R(self, R, kwargs_mass, kwargs_light, kwargs_anisotropy):
        """
        returns unweighted los velocity dispersion for a specified projected radius

        :param R: 2d projected radius (in angular units)
        :param kwargs_mass: mass model parameters (following lenstronomy lens model conventions)
        :param kwargs_light: deflector light parameters (following lenstronomy light model conventions)
        :param kwargs_anisotropy: anisotropy parameters, may vary according to anisotropy type chosen.
            We refer to the Anisotropy() class for details on the parameters.
        :return:
        """
        I_R_sigma2 = self.I_R_simga2(R, kwargs_mass, kwargs_light,
                                     kwargs_anisotropy)
        I_R = self.lightProfile.light_2d(R, kwargs_light)
        return I_R_sigma2 / I_R

    def I_R_simga2(self, R, kwargs_mass, kwargs_light, kwargs_anisotropy):
        """
        equation A15 in Mamon&Lokas 2005 as a logarithmic numerical integral (if option is chosen)
        modulo pre-factor 2*G

        :param R: 2d projected radius (in angular units)
        :param kwargs_mass: mass model parameters (following lenstronomy lens model conventions)
        :param kwargs_light: deflector light parameters (following lenstronomy light model conventions)
        :param kwargs_anisotropy: anisotropy parameters, may vary according to anisotropy type chosen.
            We refer to the Anisotropy() class for details on the parameters.
        :return: integral of A15 in Mamon&Lokas 2005
        """
        R = max(R, self._min_integrate)
        if self._log_int is True:
            min_log = np.log10(R + 0.001)
            max_log = np.log10(self._max_integrate)
            r_array = np.logspace(min_log, max_log, self._interp_grid_num)
            dlog_r = (np.log10(r_array[2]) - np.log10(r_array[1])) * np.log(10)
            IR_sigma2_dr = self._integrand_A15(
                r_array, R, kwargs_mass, kwargs_light,
                kwargs_anisotropy) * dlog_r * r_array
        else:
            r_array = np.linspace(R + 0.001, self._max_integrate,
                                  self._interp_grid_num)
            dr = r_array[2] - r_array[1]
            IR_sigma2_dr = self._integrand_A15(
                r_array, R, kwargs_mass, kwargs_light, kwargs_anisotropy) * dr
        IR_sigma2 = np.sum(
            IR_sigma2_dr
        ) * const.arcsec * self.cosmo.D_d  # integral from angle to physical scales
        return IR_sigma2

    def _integrand_A15(self, r, R, kwargs_mass, kwargs_light,
                       kwargs_anisotropy):
        """
        integrand of A15 (in log space) in Mamon&Lokas 2005

        :param r: 3d radius
        :param R: 2d projected radius
        :param kwargs_mass: mass model parameters (following lenstronomy lens model conventions)
        :param kwargs_light: deflector light parameters (following lenstronomy light model conventions)
        :param kwargs_anisotropy: anisotropy parameters, may vary according to anisotropy type chosen.
            We refer to the Anisotropy() class for details on the parameters.
        :return:
        """
        k_r = self.anisotropy.K(r, R, kwargs_anisotropy)
        l_r = self.lightProfile.light_3d_interp(r, kwargs_light)
        m_r = self.massProfile.mass_3d_interp(r, kwargs_mass)
        out = k_r * l_r * m_r / r
        return out
Пример #26
0
class Galkin(object):
    """
    major class to compute velocity dispersion measurements given light and mass models
    """
    def __init__(self,
                 mass_profile_list,
                 light_profile_list,
                 aperture_type='slit',
                 anisotropy_model='isotropic',
                 fwhm=0.7,
                 kwargs_numerics={},
                 kwargs_cosmo={
                     'D_d': 1000,
                     'D_s': 2000,
                     'D_ds': 500
                 }):
        self.massProfile = MassProfile(mass_profile_list,
                                       kwargs_cosmo,
                                       kwargs_numerics=kwargs_numerics)
        self.lightProfile = LightProfile(light_profile_list,
                                         kwargs_numerics=kwargs_numerics)
        self.aperture = Aperture(aperture_type)
        self.anisotropy = MamonLokasAnisotropy(anisotropy_model)
        self.FWHM = fwhm
        self.cosmo = Cosmo(kwargs_cosmo)
        #kwargs_numerics = {'sampling_number': 10000, 'interpol_grid_num': 5000, 'log_integration': False,
        #                   'max_integrate': 500}
        self._num_sampling = kwargs_numerics.get('sampling_number', 1000)
        self._interp_grid_num = kwargs_numerics.get('interpol_grid_num', 500)
        self._log_int = kwargs_numerics.get('log_integration', False)
        self._max_integrate = kwargs_numerics.get(
            'max_integrate',
            10)  # maximal integration (and interpolation) in units of arcsecs
        self._min_integrate = kwargs_numerics.get(
            'min_integrate',
            0.001)  # min integration (and interpolation) in units of arcsecs

    def vel_disp(self,
                 kwargs_mass,
                 kwargs_light,
                 kwargs_anisotropy,
                 kwargs_apertur,
                 r_eff=1.):
        """
        computes the averaged LOS velocity dispersion in the slit (convolved)
        :param gamma:
        :param phi_E:
        :param r_eff:
        :param r_ani:
        :param R_slit:
        :param FWHM:
        :return:
        """
        sigma2_R_sum = 0
        for i in range(0, self._num_sampling):
            sigma2_R = self.draw_one_sigma2(kwargs_mass,
                                            kwargs_light,
                                            kwargs_anisotropy,
                                            kwargs_apertur,
                                            r_eff=r_eff)
            sigma2_R_sum += sigma2_R
        sigma_s2_average = sigma2_R_sum / self._num_sampling
        # apply unit conversion from arc seconds and deflections to physical velocity disperison in (km/s)
        sigma_s2_average *= 2 * const.G  # correcting for integral prefactor
        return np.sqrt(sigma_s2_average /
                       (const.arcsec**2 * self.cosmo.D_d**2 *
                        const.Mpc)) / 1000.  # in units of km/s

    def draw_one_sigma2(self,
                        kwargs_mass,
                        kwargs_light,
                        kwargs_anisotropy,
                        kwargs_aperture,
                        r_eff=1.):
        """

        :param kwargs_mass:
        :param kwargs_light:
        :param kwargs_anisotropy:
        :param kwargs_aperture:
        :return:
        """
        while True:
            R = self.lightProfile.draw_light_2d(kwargs_light,
                                                r_eff=r_eff)  # draw r
            x, y = util.draw_xy(R)  # draw projected R
            x_, y_ = util.displace_PSF(x, y, self.FWHM)  # displace via PSF
            bool = self.aperture.aperture_select(x_, y_, kwargs_aperture)
            if bool is True:
                break
        sigma2_R = self.sigma2_R(R, kwargs_mass, kwargs_light,
                                 kwargs_anisotropy)
        return sigma2_R

    def sigma2_R(self, R, kwargs_mass, kwargs_light, kwargs_anisotropy):
        """
        returns unweighted los velocity dispersion
        :param R:
        :param kwargs_mass:
        :param kwargs_light:
        :param kwargs_anisotropy:
        :return:
        """
        I_R_sigma2 = self.I_R_simga2(R, kwargs_mass, kwargs_light,
                                     kwargs_anisotropy)
        I_R = self.lightProfile.light_2d(R, kwargs_light)
        #I_R = self.lightProfile._integrand_light(R, kwargs_light)
        return I_R_sigma2 / I_R

    def I_R_simga2(self, R, kwargs_mass, kwargs_light, kwargs_anisotropy):
        """
        equation A15 in Mamon&Lokas 2005 as a logarithmic numerical integral
        modulo pre-factor 2*G
        :param R:
        :param kwargs_mass:
        :param kwargs_light:
        :param kwargs_anisotropy:
        :return:
        """
        R = max(R, self._min_integrate)
        if self._log_int is True:
            min_log = np.log10(R + 0.001)
            max_log = np.log10(self._max_integrate)
            r_array = np.logspace(min_log, max_log, self._interp_grid_num)
            dlog_r = (np.log10(r_array[2]) - np.log10(r_array[1])) * np.log(10)
            IR_sigma2_dr = self._integrand_A15(
                r_array, R, kwargs_mass, kwargs_light,
                kwargs_anisotropy) * dlog_r * r_array
        else:
            r_array = np.linspace(R + 0.001, self._max_integrate,
                                  self._interp_grid_num)
            dr = r_array[2] - r_array[1]
            IR_sigma2_dr = self._integrand_A15(
                r_array, R, kwargs_mass, kwargs_light, kwargs_anisotropy) * dr
        IR_sigma2 = np.sum(IR_sigma2_dr)
        return IR_sigma2

    def _integrand_A15(self, r, R, kwargs_mass, kwargs_light,
                       kwargs_anisotropy):
        """
        integrand of A15 (in log space)
        :param r:
        :param kwargs_mass:
        :param kwargs_light:
        :param kwargs_anisotropy:
        :return:
        """
        k_r = self.anisotropy.K(r, R, kwargs_anisotropy)
        l_r = self.lightProfile.light_3d_interp(r, kwargs_light)
        m_r = self.massProfile.mass_3d_interp(r, kwargs_mass)
        out = k_r * l_r * m_r / r
        return out
Пример #27
0
class NumericKinematics(Anisotropy):
    def __init__(self,
                 kwargs_model,
                 kwargs_cosmo,
                 interpol_grid_num=100,
                 log_integration=False,
                 max_integrate=100,
                 min_integrate=0.001):
        """

        :param interpol_grid_num:
        :param log_integration:
        :param max_integrate:
        :param min_integrate:
        """
        mass_profile_list = kwargs_model.get('mass_profile_list')
        light_profile_list = kwargs_model.get('light_profile_list')
        anisotropy_model = kwargs_model.get('anisotropy_model')
        self._interp_grid_num = interpol_grid_num
        self._log_int = log_integration
        self._max_integrate = max_integrate  # maximal integration (and interpolation) in units of arcsecs
        self._min_integrate = min_integrate  # min integration (and interpolation) in units of arcsecs
        self._max_interpolate = max_integrate  # we chose to set the interpolation range to the integration range
        self._min_interpolate = min_integrate  # we chose to set the interpolation range to the integration range
        self.lightProfile = LightProfile(light_profile_list,
                                         interpol_grid_num=interpol_grid_num,
                                         max_interpolate=max_integrate,
                                         min_interpolate=min_integrate)
        Anisotropy.__init__(self, anisotropy_type=anisotropy_model)
        self.cosmo = Cosmo(**kwargs_cosmo)
        self._mass_profile = SinglePlane(mass_profile_list)

    def sigma_s2(self, r, R, kwargs_mass, kwargs_light, kwargs_anisotropy):
        """
        returns unweighted los velocity dispersion for a specified projected radius

        :param r: 3d radius (not needed for this calculation)
        :param R: 2d projected radius (in angular units of arcsec)
        :param kwargs_mass: mass model parameters (following lenstronomy lens model conventions)
        :param kwargs_light: deflector light parameters (following lenstronomy light model conventions)
        :param kwargs_anisotropy: anisotropy parameters, may vary according to anisotropy type chosen.
            We refer to the Anisotropy() class for details on the parameters.
        :return: line-of-sight projected velocity dispersion at projected radius R
        """
        I_R_sigma2 = self._I_R_sigma2_interp(R, kwargs_mass, kwargs_light,
                                             kwargs_anisotropy)
        I_R = self.lightProfile.light_2d(R, kwargs_light)
        return np.nan_to_num(I_R_sigma2 / I_R)

    def sigma_r2(self, r, kwargs_mass, kwargs_light, kwargs_anisotropy):
        """
        computes numerically the solution of the Jeans equation for a specific 3d radius
        E.g. Equation (A1) of Mamon & Lokas https://arxiv.org/pdf/astro-ph/0405491.pdf
        l(r) \sigma_r(r) ^ 2 =  1/f(r) \int_r^{\infty} f(s) l(s) G M(s) / s^2 ds
        where l(r) is the 3d light profile
        M(s) is the enclosed 3d mass
        f is the solution to
        d ln(f)/ d ln(r) = 2 beta(r)

        :param r: 3d radius
        :param kwargs_mass: mass model parameters (following lenstronomy lens model conventions)
        :param kwargs_light: deflector light parameters (following lenstronomy light model conventions)
        :param kwargs_anisotropy: anisotropy parameters, may vary according to anisotropy type chosen.
            We refer to the Anisotropy() class for details on the parameters.
        :return: sigma_r**2
        """
        l_r = self.lightProfile.light_3d_interp(r, kwargs_light)
        f_r = self.anisotropy_solution(r, **kwargs_anisotropy)
        return 1 / f_r / l_r * self._jeans_solution_integral(
            r, kwargs_mass, kwargs_light, kwargs_anisotropy) * const.G / (
                const.arcsec * self.cosmo.dd * const.Mpc)

    def mass_3d(self, r, kwargs):
        """
        mass enclosed a 3d radius

        :param r: in arc seconds
        :param kwargs: lens model parameters in arc seconds
        :return: mass enclosed physical radius in kg
        """
        mass_dimless = self._mass_profile.mass_3d(r, kwargs)
        mass_dim = mass_dimless * const.arcsec ** 2 * self.cosmo.dd * self.cosmo.ds / self.cosmo.dds * const.Mpc * \
                   const.c ** 2 / (4 * np.pi * const.G)
        return mass_dim

    def grav_potential(self, r, kwargs_mass):
        """
        Gravitational potential in SI units

        :param r: radius (arc seconds)
        :param kwargs_mass:
        :return: gravitational potential
        """
        mass_dim = self.mass_3d(r, kwargs_mass)
        grav_pot = -const.G * mass_dim / (r * const.arcsec * self.cosmo.dd *
                                          const.Mpc)
        return grav_pot

    def draw_light(self, kwargs_light):
        """

        :param kwargs_light: keyword argument (list) of the light model
        :return: 3d radius (if possible), 2d projected radius, x-projected coordinate, y-projected coordinate
        """
        R = self.lightProfile.draw_light_2d(kwargs_light, n=1)[0]
        x, y = util.draw_xy(R)
        r = None
        return r, R, x, y

    def delete_cache(self):
        """
        delete interpolation function for a specific mass and light profile as well as for a specific anisotropy model

        :return:
        """
        if hasattr(self, '_log_mass_3d'):
            del self._log_mass_3d
        if hasattr(self, '_interp_jeans_integral'):
            del self._interp_jeans_integral
        if hasattr(self, '_interp_I_R_sigma2'):
            del self._interp_I_R_sigma2
        self.lightProfile.delete_cache()
        self.delete_anisotropy_cache()

    def _I_R_sigma2(self, R, kwargs_mass, kwargs_light, kwargs_anisotropy):
        """
        equation A15 in Mamon&Lokas 2005 as a logarithmic numerical integral (if option is chosen)

        :param R: 2d projected radius (in angular units)
        :param kwargs_mass: mass model parameters (following lenstronomy lens model conventions)
        :param kwargs_light: deflector light parameters (following lenstronomy light model conventions)
        :param kwargs_anisotropy: anisotropy parameters, may vary according to anisotropy type chosen.
            We refer to the Anisotropy() class for details on the parameters.
        :return: integral of A15 in Mamon&Lokas 2005
        """
        R = max(R, self._min_integrate)
        if self._log_int is True:
            min_log = np.log10(R + 0.001)
            max_log = np.log10(self._max_integrate)
            r_array = np.logspace(min_log, max_log, self._interp_grid_num)
            dlog_r = (np.log10(r_array[2]) - np.log10(r_array[1])) * np.log(10)
            IR_sigma2_dr = self._integrand_A15(
                r_array, R, kwargs_mass, kwargs_light,
                kwargs_anisotropy) * dlog_r * r_array
        else:
            r_array = np.linspace(R + 0.001, self._max_integrate,
                                  self._interp_grid_num)
            dr = r_array[2] - r_array[1]
            IR_sigma2_dr = self._integrand_A15(
                r_array, R, kwargs_mass, kwargs_light, kwargs_anisotropy) * dr
        IR_sigma2 = np.sum(
            IR_sigma2_dr)  # integral from angle to physical scales
        return IR_sigma2 * 2 * const.G / (const.arcsec * self.cosmo.dd *
                                          const.Mpc)

    def _I_R_sigma2_interp(self, R, kwargs_mass, kwargs_light,
                           kwargs_anisotropy):
        """
        quation A15 in Mamon&Lokas 2005 as interpolation in log space

        :param R: projected radius
        :param kwargs_mass: mass profile keyword arguments
        :param kwargs_light: light model keyword arguments
        :param kwargs_anisotropy: stellar anisotropy keyword arguments
        :return:
        """
        if not hasattr(self, '_interp_I_R_sigma2'):
            min_log = np.log10(self._min_integrate)
            max_log = np.log10(self._max_integrate)
            R_array = np.logspace(min_log, max_log, self._interp_grid_num)
            I_R_sigma2_array = []
            for R_i in R_array:
                I_R_sigma2_array.append(
                    self._I_R_sigma2(R_i, kwargs_mass, kwargs_light,
                                     kwargs_anisotropy))
            self._interp_I_R_sigma2 = interp1d(np.log(R_array),
                                               np.array(I_R_sigma2_array),
                                               fill_value="extrapolate")
        return self._interp_I_R_sigma2(np.log(R))

    def _integrand_A15(self, r, R, kwargs_mass, kwargs_light,
                       kwargs_anisotropy):
        """
        integrand of A15 (in log space) in Mamon&Lokas 2005

        :param r: 3d radius in arc seconds
        :param R: 2d projected radius
        :param kwargs_mass: mass model parameters (following lenstronomy lens model conventions)
        :param kwargs_light: deflector light parameters (following lenstronomy light model conventions)
        :param kwargs_anisotropy: anisotropy parameters, may vary according to anisotropy type chosen.
            We refer to the Anisotropy() class for details on the parameters.
        :return:
        """
        k_r = self.K(r, R, **kwargs_anisotropy)
        l_r = self.lightProfile.light_3d_interp(r, kwargs_light)
        m_r = self._mass_3d_interp(r, kwargs_mass)
        out = k_r * l_r * m_r / r
        return out

    def _jeans_solution_integral(self, r, kwargs_mass, kwargs_light,
                                 kwargs_anisotropy):
        """
        interpolated solution of the integral \int_r^{\infty} f(s) l(s) G M(s) / s^2 ds

        :param r:
        :param kwargs_mass:
        :param kwargs_light:
        :param kwargs_anisotropy:
        :return:
        """
        if not hasattr(self, '_interp_jeans_integral'):
            min_log = np.log10(self._min_integrate)
            max_log = np.log10(self._max_integrate)
            r_array = np.logspace(min_log, max_log, self._interp_grid_num)
            dlog_r = (np.log10(r_array[2]) - np.log10(r_array[1])) * np.log(10)
            integrand_jeans = self._integrand_jeans_solution(
                r_array, kwargs_mass, kwargs_light,
                kwargs_anisotropy) * dlog_r * r_array
            #flip array from inf to finite
            integral_jeans_r = np.cumsum(np.flip(integrand_jeans))
            #flip array back
            integral_jeans_r = np.flip(integral_jeans_r)
            #call 1d interpolation function
            self._interp_jeans_integral = interp1d(np.log(r_array),
                                                   integral_jeans_r,
                                                   fill_value="extrapolate")
        return self._interp_jeans_integral(np.log(r))

    def _integrand_jeans_solution(self, r, kwargs_mass, kwargs_light,
                                  kwargs_anisotropy):
        """
        integrand of A1 (in log space) in Mamon&Lokas 2005 to calculate the Jeans equation numerically
        f(s) l(s) M(s) / s^2

        :param r:
        :param kwargs_mass:
        :param kwargs_light:
        :param kwargs_anisotropy:
        :return:
        """
        f_r = self.anisotropy_solution(r, **kwargs_anisotropy)
        l_r = self.lightProfile.light_3d_interp(r, kwargs_light)
        m_r = self._mass_3d_interp(r, kwargs_mass)
        out = f_r * l_r * m_r / r**2
        return out

    def _mass_3d_interp(self, r, kwargs, new_compute=False):
        """

        :param r: in arc seconds
        :param kwargs: lens model parameters in arc seconds
        :param new_compute: bool, if True, recomputes the interpolation
        :return: mass enclosed physical radius in kg
        """
        if not hasattr(self, '_log_mass_3d') or new_compute is True:
            r_array = np.logspace(np.log10(self._min_interpolate),
                                  np.log10(self._max_interpolate),
                                  self._interp_grid_num)
            mass_3d_array = self.mass_3d(r_array, kwargs)
            mass_3d_array[mass_3d_array < 10.**(-10)] = 10.**(-10)
            #mass_dim_array = mass_3d_array * const.arcsec ** 2 * self.cosmo.dd * self.cosmo.ds \
            #                 / self.cosmo.dds * const.Mpc * const.c ** 2 / (4 * np.pi * const.G)
            self._log_mass_3d = interp1d(np.log(r_array),
                                         np.log(mass_3d_array / r_array),
                                         fill_value="extrapolate")
        return np.exp(self._log_mass_3d(np.log(r))) * r