def __init__(self, D_d=1000, D_s=2000, D_ds=500):
        """

        :param D_d: angular diameter to the deflector [MPC]
        :param D_s: angular diameter to the source [MPC]
        :param D_ds: angular diameter from the deflector to the source [MPC]
        """
        self._cosmo = Cosmo(D_d=D_d, D_s=D_s, D_ds=D_ds)
示例#2
0
    def __init__(self, D_d=1000, D_s=2000, D_ds=500, psf_type='GAUSSIAN', fwhm=0.7, moffat_beta=2.6):
        """

        :param D_d: angular diameter to the deflector [MPC]
        :param D_s: angular diameter to the source [MPC]
        :param D_ds: angular diameter from the deflector to the source [MPC]
        :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
        """
        self._cosmo = Cosmo(D_d=D_d, D_s=D_s, D_ds=D_ds)
        self._psf = PSF(psf_type=psf_type, fwhm=fwhm, moffat_beta=moffat_beta)
示例#3
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)
示例#4
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
示例#5
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)
示例#6
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)
    def __init__(self, D_d, D_s, D_ds, kwargs_aperture, kwargs_psf):
        """

        :param D_d: angular diameter to the deflector [MPC]
        :param D_s: angular diameter to the source [MPC]
        :param D_ds: angular diameter from the deflector to the source [MPC]
        :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
        """
        if D_ds <= 0 or D_s <= 0 or D_d <= 0:
            raise ValueError(
                'input angular diameter distances Dd: %s, Ds: %s, Dds: %s are not suppored for a lens model!'
                % (D_d, D_s, D_ds))
        self._cosmo = Cosmo(D_d=D_d, D_s=D_s, D_ds=D_ds)
        self._psf = psf_select(**kwargs_psf)
        self.aperture = aperture_select(**kwargs_aperture)
示例#8
0
    def __init__(self, profile_list, kwargs_cosmo={'D_d': 1000, 'D_s': 2000, 'D_ds': 500}, kwargs_numerics={}):
        """

        :param profile_list:
        """
        kwargs_options = {'lens_model_list': profile_list}
        self.model = LensModel(profile_list)
        self.cosmo = Cosmo(kwargs_cosmo)
        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.0001)
示例#9
0
    def __init__(self,
                 kwargs_cosmo,
                 interpol_grid_num=100,
                 log_integration=False,
                 max_integrate=100,
                 min_integrate=0.001):
        """

        :param kwargs_cosmo: keyword argument with angular diameter distances
        """

        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._cosmo = Cosmo(**kwargs_cosmo)
        self._spp = SPP()
        Anisotropy.__init__(self, anisotropy_type='OM')
示例#10
0
class Jeans_solver(object):
    """
    class to solve radial Jeans equation for different configuration
    """
    def __init__(self, kwargs_cosmo, mass_profile, light_profile, anisotropy_type):
        self.cosmo = Cosmo(kwargs_cosmo)
        self._mass_profile = mass_profile
        self._light_profile = light_profile
        self._anisotropy_type = anisotropy_type

    def power_law_anisotropy(self, r, kwargs_profile, kwargs_anisotropy, kwargs_light):
        """
        equation (19) in Suyu+ 2010
        :param r:
        :return:
        """
        # first term
        theta_E = kwargs_profile['theta_E']
        gamma = kwargs_profile['gamma']
        r_ani = kwargs_anisotropy['r_ani']
        a = 0.551 * kwargs_light['r_eff']
        rho0_r0_gamma = self._rho0_r0_gamma(theta_E, gamma)
        prefac1 = 4*np.pi * const.G * a**(-gamma) * rho0_r0_gamma / (3-gamma)
        prefac2 = r * (r + a)**3/(r**2 + r_ani**2)
        hyp1 = vel_util.hyp_2F1(a=2+gamma, b=gamma, c=3+gamma, z=1./(1+r/a))
        hyp2 = vel_util.hyp_2F1(a=3, b=gamma, c=1+gamma, z=-a/r)
        fac = r_ani**2/a**2 * hyp1 / ((2+gamma) * (r/a + 1)**(2+gamma)) + hyp2 / (gamma*(r/a)**gamma)
        sigma2_dim_less = prefac1 * prefac2 * fac
        return sigma2_dim_less * (self.cosmo.arcsec2phys_lens(1.) * const.Mpc / 1000)**2

    def _rho0_r0_gamma(self, theta_E, gamma):
        # equation (14) in Suyu+ 2010
        return -1 * math.gamma(gamma/2.)/(np.sqrt(np.pi)*math.gamma((gamma-3)/2.)) * theta_E**gamma/self.cosmo.arcsec2phys_lens(theta_E) * self.cosmo.epsilon_crit * const.M_sun/const.Mpc**3  # units kg/m^3

    def sigma_r2(self, r, kwargs_profile, kwargs_anisotropy, kwargs_light):
        """
        solves radial Jeans equation
        """
        if self._mass_profile == 'power_law':
            if self._anisotropy_type == 'r_ani':
                if self._light_profile == 'Hernquist':
                    sigma_r = self.power_law_anisotropy(r, kwargs_profile, kwargs_anisotropy, kwargs_light)
                else:
                    raise ValueError(' light profile %s not supported for Jeans solver' % self._light_profile)
            else:
                raise ValueError('anisotropy type %s not implemented in Jeans equation modelling' % self._anisotropy_type)
        else:
            raise ValueError('mass profile type %s not implemented in Jeans solver' % self._mass_profile)
        return sigma_r
示例#11
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
示例#12
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
示例#13
0
    def __init__(self,
                 profile_list,
                 kwargs_cosmo={
                     'D_d': 1000,
                     'D_s': 2000,
                     'D_ds': 500
                 },
                 interpol_grid_num=1000,
                 max_interpolate=100,
                 min_interpolate=0.001):
        """

        :param profile_list:
        """
        self.model = SinglePlane(profile_list)
        self.cosmo = Cosmo(**kwargs_cosmo)
        self._interp_grid_num = interpol_grid_num
        self._max_interpolate = max_interpolate
        self._min_interpolate = min_interpolate
class AnalyticKinematics(object):
    """
    class to compute eqn 20 in Suyu+2010 with a Monte-Carlo from rendering from the
    light profile distribution and displacing them with a Gaussian seeing convolution

    This class assumes spherical symmetry in light and mass distribution and
        - a Hernquist light profile (parameterised by the half-light radius)
        - a power-law mass profile (parameterized by the Einstein radius and logarithmic slop)

    The analytic equations for the kinematics in this approximation are presented e.g. in Suyu et al. 2010 and
    the spectral rendering approach to compute the seeing convolved slit measurement is presented in Birrer et al. 2016.
    The stellar anisotropy is parameterised based on Osipkov 1979; Merritt 1985.

    Units
    -----
    all units are meant to be in angular arc seconds. The physical units are fold in through the angular diameter
    distances

    """
    def __init__(self, D_d, D_s, D_ds, kwargs_aperture, kwargs_psf):
        """

        :param D_d: angular diameter to the deflector [MPC]
        :param D_s: angular diameter to the source [MPC]
        :param D_ds: angular diameter from the deflector to the source [MPC]
        :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
        """
        if D_ds <= 0 or D_s <= 0 or D_d <= 0:
            raise ValueError(
                'input angular diameter distances Dd: %s, Ds: %s, Dds: %s are not suppored for a lens model!'
                % (D_d, D_s, D_ds))
        self._cosmo = Cosmo(D_d=D_d, D_s=D_s, D_ds=D_ds)
        self._psf = psf_select(**kwargs_psf)
        self.aperture = aperture_select(**kwargs_aperture)

    def vel_disp(self, gamma, theta_E, r_eff, r_ani, rendering_number=1000):
        """
        computes the averaged LOS velocity dispersion in the slit (convolved)

        :param gamma: power-law slope of the mass profile (isothermal = 2)
        :param theta_E: Einstein radius of the lens (in arcseconds)
        :param r_eff: half light radius of the Hernquist profile (or as an approximation of any other profile to be described as a Hernquist profile
        :param r_ani: anisotropy radius
        :param kwargs_aperture: keyword arguments describing the aperture of the collected spectral
        :param rendering_number: number of spectral renderings drawn from the light distribution that go through the
            slit of the observations

        :return: LOS integrated velocity dispersion in units [km/s]
        """
        sigma_s2_sum = 0
        rho0_r0_gamma = self._rho0_r0_gamma(theta_E, gamma)
        for i in range(0, rendering_number):
            sigma_s2_draw = self.vel_disp_one(gamma, rho0_r0_gamma, r_eff,
                                              r_ani)
            sigma_s2_sum += sigma_s2_draw
        sigma_s2_average = sigma_s2_sum / rendering_number
        return np.sqrt(sigma_s2_average)

    def _rho0_r0_gamma(self, theta_E, gamma):
        # equation (14) in Suyu+ 2010
        return -1 * math.gamma(gamma/2) / (np.sqrt(np.pi)*math.gamma((gamma-3)/2.)) * theta_E ** gamma / \
               self._cosmo.arcsec2phys_lens(theta_E) * self._cosmo.epsilon_crit * const.M_sun / const.Mpc ** 3

    def vel_disp_one(self, gamma, rho0_r0_gamma, r_eff, r_ani):
        """
        computes one realisation of the velocity dispersion realized in the slit

        :param gamma: power-law slope of the mass profile (isothermal = 2)
        :param rho0_r0_gamma: combination of Einstein radius and power-law slope as equation (14) in Suyu+ 2010
        :param r_eff: half light radius of the Hernquist profile (or as an approximation of any other profile to be described as a Hernquist profile
        :param r_ani: anisotropy radius
        :param kwargs_aperture: keyword arguments describing the aperture of the collected spectral
        :param FWHM: full width at half maximum of the seeing conditions, described as a Gaussian
        :return: projected velocity dispersion of a single drawn position in the potential [km/s]
        """
        a = 0.551 * r_eff
        while True:
            r = self.P_r(a)  # draw r
            R, x, y = self.R_r(r)  # draw projected R
            x_, y_ = self._psf.displace_psf(x, y)
            bool = self.aperture.aperture_select(x_, y_)
            if bool is True:
                break
        sigma_s2 = self.sigma_s2(r, R, r_ani, a, gamma, rho0_r0_gamma)
        return np.array(sigma_s2, dtype=float)

    def P_r(self, a):
        """

        :param a: 0.551*r_eff
        :return: realisation of radius of Hernquist luminosity weighting in 3d
        """
        P = np.random.uniform()  # draws uniform between [0,1)
        r = a * np.sqrt(P) * (np.sqrt(P) + 1) / (
            1 - P)  # solves analytically to r from P(r)
        return r

    def R_r(self, r):
        """
        draws a random projection from radius r in 2d and 1d
        :param r: 3d radius
        :return: R, x, y
        """
        phi = np.random.uniform(0, 2 * np.pi)
        theta = np.random.uniform(0, np.pi)
        x = r * np.sin(theta) * np.cos(phi)
        y = r * np.sin(theta) * np.sin(phi)
        R = np.sqrt(x**2 + y**2)
        return R, x, y

    def sigma_s2(self, r, R, r_ani, a, gamma, rho0_r0_gamma):
        """
        projected velocity dispersion
        :param r:
        :param R:
        :param r_ani:
        :param a:
        :param gamma:
        :param phi_E:
        :return:
        """
        beta = self._beta_ani(r, r_ani)
        return (1 - beta * R**2 / r**2) * self.sigma_r2(
            r, a, gamma, rho0_r0_gamma, r_ani)

    def sigma_r2(self, r, a, gamma, rho0_r0_gamma, r_ani):
        """
        equation (19) in Suyu+ 2010
        """
        # first term
        prefac1 = 4 * np.pi * const.G * a**(-gamma) * rho0_r0_gamma / (3 -
                                                                       gamma)
        prefac2 = r * (r + a)**3 / (r**2 + r_ani**2)
        hyp1 = vel_util.hyp_2F1(a=2 + gamma,
                                b=gamma,
                                c=3 + gamma,
                                z=1. / (1 + r / a))
        hyp2 = vel_util.hyp_2F1(a=3, b=gamma, c=1 + gamma, z=-a / r)
        fac = r_ani**2 / a**2 * hyp1 / (
            (2 + gamma) * (r / a + 1)**(2 + gamma)) + hyp2 / (gamma *
                                                              (r / a)**gamma)
        return prefac1 * prefac2 * fac * (self._cosmo.arcsec2phys_lens(1.) *
                                          const.Mpc / 1000)**2

    def _beta_ani(self, r, r_ani):
        """
        anisotropy parameter beta
        :param r: radius
        :param r_ani: anisotropy radius
        :return: beta(r) in the OM parameterization
        """
        return r**2 / (r_ani**2 + r**2)
示例#15
0
 def __init__(self, kwargs_cosmo, mass_profile, light_profile, anisotropy_type):
     self.cosmo = Cosmo(kwargs_cosmo)
     self._mass_profile = mass_profile
     self._light_profile = light_profile
     self._anisotropy_type = anisotropy_type
class AnalyticKinematics(object):
    """
    class to compute eqn 20 in Suyu+2010 with a Monte-Carlo from rendering from the
    light profile distribution and displacing them with a Gaussian seeing convolution

    This class assumes spherical symmetry in light and mass distribution and
        - a Hernquist light profile (parameterised by the half-light radius)
        - a power-law mass profile (parameterized by the Einstein radius and logarithmic slop)

    The analytic equations for the kinematics in this approximation are presented e.g. in Suyu et al. 2010 and
    the spectral rendering approach to compute the seeing convolved slit measurement is presented in Birrer et al. 2016.
    The stellar anisotropy is parameterised based on Osipkov 1979; Merritt 1985.

    Units
    -----
    all units are meant to be in angular arc seconds. The physical units are fold in through the angular diameter
    distances

    """
    def __init__(self, kwargs_cosmo={'D_d': 1000, 'D_s': 2000, 'D_ds': 500}):
        """

        :param kwargs_cosmo: keyword arguments of the angular diameter distances (in Mpc)
        """
        self._cosmo = Cosmo(kwargs_cosmo)

    def vel_disp(self,
                 gamma,
                 theta_E,
                 r_eff,
                 r_ani,
                 R_slit,
                 dR_slit,
                 FWHM,
                 rendering_number=1000):
        """
        computes the averaged LOS velocity dispersion in the slit (convolved)

        :param gamma: power-law slope of the mass profile (isothermal = 2)
        :param theta_E: Einstein radius of the lens (in arcseconds)
        :param r_eff: half light radius of the Hernquist profile (or as an approximation of any other profile to be described as a Hernquist profile
        :param r_ani: anisotropy radius
        :param R_slit: length of the slit/box
        :param dR_slit: width of the slit/box
        :param FWHM: full width at half maximum of the seeing conditions, described as a Gaussian
        :param rendering_number: number of spectral renderings drawn from the light distribution that go through the
            slit of the observations

        :return: LOS integrated velocity dispersion in units [km/s]
        """
        sigma_s2_sum = 0
        rho0_r0_gamma = self._rho0_r0_gamma(theta_E, gamma)
        for i in range(0, rendering_number):
            sigma_s2_draw = self.vel_disp_one(gamma, rho0_r0_gamma, r_eff,
                                              r_ani, R_slit, dR_slit, FWHM)
            sigma_s2_sum += sigma_s2_draw
        sigma_s2_average = sigma_s2_sum / rendering_number
        return np.sqrt(sigma_s2_average)

    def _rho0_r0_gamma(self, theta_E, gamma):
        # equation (14) in Suyu+ 2010
        return -1 * math.gamma(gamma/2) / (np.sqrt(np.pi)*math.gamma((gamma-3)/2.)) * theta_E ** gamma / \
               self._cosmo.arcsec2phys_lens(theta_E) * self._cosmo.epsilon_crit * const.M_sun / const.Mpc ** 3

    def vel_disp_one(self, gamma, rho0_r0_gamma, r_eff, r_ani, R_slit, dR_slit,
                     FWHM):
        """
        computes one realisation of the velocity dispersion realized in the slit

        :param gamma: power-law slope of the mass profile (isothermal = 2)
        :param rho0_r0_gamma: combination of Einstein radius and power-law slope as equation (14) in Suyu+ 2010
        :param r_eff: half light radius of the Hernquist profile (or as an approximation of any other profile to be described as a Hernquist profile
        :param r_ani: anisotropy radius
        :param R_slit: length of the slit/box
        :param dR_slit: width of the slit/box
        :param FWHM: full width at half maximum of the seeing conditions, described as a Gaussian
        :return: projected velocity dispersion of a single drawn position in the potential [km/s]
        """
        a = 0.551 * r_eff
        while True:
            r = self.P_r(a)  # draw r
            R, x, y = self.R_r(r)  # draw projected R
            x_, y_ = self.displace_PSF(x, y, FWHM)  # displace via PSF
            bool = self.check_in_slit(x_, y_, R_slit, dR_slit)
            if bool is True:
                break
        sigma_s2 = self.sigma_s2(r, R, r_ani, a, gamma, rho0_r0_gamma)
        return sigma_s2

    def P_r(self, a):
        """

        :param a: 0.551*r_eff
        :return: realisation of radius of Hernquist luminosity weighting in 3d
        """
        P = np.random.uniform()  # draws uniform between [0,1)
        r = a * np.sqrt(P) * (np.sqrt(P) + 1) / (
            1 - P)  # solves analytically to r from P(r)
        return r

    def R_r(self, r):
        """
        draws a random projection from radius r in 2d and 1d
        :param r: 3d radius
        :return: R, x, y
        """
        phi = np.random.uniform(0, 2 * np.pi)
        theta = np.random.uniform(0, np.pi)
        x = r * np.sin(theta) * np.cos(phi)
        y = r * np.sin(theta) * np.sin(phi)
        R = np.sqrt(x**2 + y**2)
        return R, x, y

    def displace_PSF(self, x, y, FWHM):
        """

        :param x: x-coord (arc sec)
        :param y: y-coord (arc sec)
        :param FWHM: psf size (arc sec)
        :return: x', y' random displaced according to psf
        """
        sigma = FWHM / (2 * np.sqrt(2 * np.log(2)))
        sigma_one_direction = sigma
        x_ = x + np.random.normal() * sigma_one_direction
        y_ = y + np.random.normal() * sigma_one_direction
        return x_, y_

    def check_in_slit(self, x, y, R_slit, dR_slit):
        """

        check whether a ray in position (x,y) is captured in the slit with Radius R_slit and width dR_slit

        :param x:
        :param y:
        :param R_slit:
        :param dR_slit:
        :return:
        """
        if abs(x) < R_slit / 2. and abs(y) < dR_slit / 2.:
            return True
        else:
            return False

    def sigma_s2(self, r, R, r_ani, a, gamma, rho0_r0_gamma):
        """
        projected velocity dispersion
        :param r:
        :param R:
        :param r_ani:
        :param a:
        :param gamma:
        :param phi_E:
        :return:
        """
        beta = self._beta_ani(r, r_ani)
        return (1 - beta * R**2 / r**2) * self.sigma_r2(
            r, a, gamma, rho0_r0_gamma, r_ani)

    def sigma_r2(self, r, a, gamma, rho0_r0_gamma, r_ani):
        """
        equation (19) in Suyu+ 2010
        """
        # first term
        prefac1 = 4 * np.pi * const.G * a**(-gamma) * rho0_r0_gamma / (3 -
                                                                       gamma)
        prefac2 = r * (r + a)**3 / (r**2 + r_ani**2)
        hyp1 = vel_util.hyp_2F1(a=2 + gamma,
                                b=gamma,
                                c=3 + gamma,
                                z=1. / (1 + r / a))
        hyp2 = vel_util.hyp_2F1(a=3, b=gamma, c=1 + gamma, z=-a / r)
        fac = r_ani**2 / a**2 * hyp1 / (
            (2 + gamma) * (r / a + 1)**(2 + gamma)) + hyp2 / (gamma *
                                                              (r / a)**gamma)
        return prefac1 * prefac2 * fac * (self._cosmo.arcsec2phys_lens(1.) *
                                          const.Mpc / 1000)**2

    def _beta_ani(self, r, r_ani):
        """
        anisotropy parameter beta
        :param r:
        :param r_ani:
        :return:
        """
        return r**2 / (r_ani**2 + r**2)
    def __init__(self, kwargs_cosmo={'D_d': 1000, 'D_s': 2000, 'D_ds': 500}):
        """

        :param kwargs_cosmo: keyword arguments of the angular diameter distances (in Mpc)
        """
        self._cosmo = Cosmo(kwargs_cosmo)
示例#18
0
 def test_raise(self):
     with self.assertRaises(ValueError):
         Cosmo(d_d=-1, d_s=1, d_ds=1)
示例#19
0
class AnalyticKinematics(Anisotropy):
    """
    class to compute eqn 20 in Suyu+2010 with a Monte-Carlo from rendering from the
    light profile distribution and displacing them with a Gaussian seeing convolution.

    This class assumes spherical symmetry in light and mass distribution and
        - a Hernquist light profile (parameterised by the half-light radius)
        - a power-law mass profile (parameterized by the Einstein radius and logarithmic slop)

    The analytic equations for the kinematics in this approximation are presented e.g. in Suyu et al. 2010 and
    the spectral rendering approach to compute the seeing convolved slit measurement is presented in Birrer et al. 2016.
    The stellar anisotropy is parameterised based on Osipkov 1979; Merritt 1985.

    All units are meant to be in angular arc seconds. The physical units are fold in through the angular diameter
    distances

    """
    def __init__(self,
                 kwargs_cosmo,
                 interpol_grid_num=100,
                 log_integration=False,
                 max_integrate=100,
                 min_integrate=0.001):
        """

        :param kwargs_cosmo: keyword argument with angular diameter distances
        """

        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._cosmo = Cosmo(**kwargs_cosmo)
        self._spp = SPP()
        Anisotropy.__init__(self, anisotropy_type='OM')

    def _rho0_r0_gamma(self, theta_E, gamma):
        # equation (14) in Suyu+ 2010
        return -1 * math.gamma(gamma/2) / (np.sqrt(np.pi)*math.gamma((gamma-3)/2.)) * theta_E ** gamma / \
               self._cosmo.arcsec2phys_lens(theta_E) * self._cosmo.epsilon_crit * const.M_sun / const.Mpc ** 3

    @staticmethod
    def draw_light(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
        """
        if 'a' not in kwargs_light:
            kwargs_light['a'] = 0.551 * kwargs_light['r_eff']
        a = kwargs_light['a']
        r = vel_util.draw_hernquist(a)
        R, x, y = vel_util.project2d_random(r)
        return r, R, x, y

    def _sigma_s2(self, r, R, r_ani, a, gamma, rho0_r0_gamma):
        """
        projected velocity dispersion
        :param r: 3d radius of the light tracer particle
        :param R: 2d projected radius of the light tracer particle
        :param r_ani: anisotropy radius
        :param a: scale of the Hernquist light profile
        :param gamma: power-law slope of the mass profile
        :param rho0_r0_gamma: combination of Einstein radius and power-law slope as equation (14) in Suyu+ 2010
        :return: projected velocity dispersion
        """
        beta = self.beta_r(r, **{'r_ani': r_ani})
        return (1 - beta * R**2 / r**2) * self._sigma_r2_interp(
            r, a, gamma, rho0_r0_gamma, r_ani)

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

        :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
        """
        a, gamma, rho0_r0_gamma, r_ani = self._read_out_params(
            kwargs_mass, kwargs_light, kwargs_anisotropy)
        return self._sigma_s2(r, R, r_ani, a, gamma, rho0_r0_gamma), 1

    def sigma_r2(self, r, kwargs_mass, kwargs_light, kwargs_anisotropy):
        """
        equation (19) in Suyu+ 2010

        :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: velocity dispersion in [m/s]
        """
        a, gamma, rho0_r0_gamma, r_ani = self._read_out_params(
            kwargs_mass, kwargs_light, kwargs_anisotropy)
        return self._sigma_r2(r, a, gamma, rho0_r0_gamma, r_ani)

    def _read_out_params(self, kwargs_mass, kwargs_light, kwargs_anisotropy):
        """
        reads the relevant parameters out of the keyword arguments and transforms them to the conventions used in this
        class

        :param kwargs_mass: mass profile keyword arguments
        :param kwargs_light: light profile keyword arguments
        :param kwargs_anisotropy: anisotropy keyword arguments
        :return: a (Rs of Hernquist profile), gamma, rho0_r0_gamma, r_ani
        """
        if 'a' not in kwargs_light:
            kwargs_light['a'] = 0.551 * kwargs_light['r_eff']
        if 'rho0_r0_gamma' not in kwargs_mass:
            kwargs_mass['rho0_r0_gamma'] = self._rho0_r0_gamma(
                kwargs_mass['theta_E'], kwargs_mass['gamma'])
        a = kwargs_light['a']
        gamma = kwargs_mass['gamma']
        rho0_r0_gamma = kwargs_mass['rho0_r0_gamma']
        r_ani = kwargs_anisotropy['r_ani']
        return a, gamma, rho0_r0_gamma, r_ani

    def _sigma_r2(self, r, a, gamma, rho0_r0_gamma, r_ani):
        """
        equation (19) in Suyu+ 2010
        """
        # first term
        prefac1 = 4 * np.pi * const.G * a**(-gamma) * rho0_r0_gamma / (3 -
                                                                       gamma)
        prefac2 = r * (r + a)**3 / (r**2 + r_ani**2)
        # TODO check whether interpolation functions can speed this up
        hyp1 = vel_util.hyp_2F1(a=2 + gamma,
                                b=gamma,
                                c=3 + gamma,
                                z=1. / (1 + r / a))
        hyp2 = vel_util.hyp_2F1(a=3, b=gamma, c=1 + gamma, z=-a / r)
        fac = r_ani**2 / a**2 * hyp1 / (
            (2 + gamma) * (r / a + 1)**(2 + gamma)) + hyp2 / (gamma *
                                                              (r / a)**gamma)
        return prefac1 * prefac2 * fac * (const.arcsec * self._cosmo.dd *
                                          const.Mpc)**2

    def _sigma_r2_interp(self, r, a, gamma, rho0_r0_gamma, r_ani):
        """

        :param r:
        :param a:
        :param gamma:
        :param rho0_r0_gamma:
        :param r_ani:
        :return:
        """
        if not hasattr(self, '_interp_sigma_r2'):
            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._sigma_r2(r_i, a, gamma, rho0_r0_gamma, r_ani))
            self._interp_sigma_r2 = interp1d(np.log(r_array),
                                             np.array(I_R_sigma2_array),
                                             fill_value="extrapolate")
        return self._interp_sigma_r2(np.log(r))

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

        :param r: radius (arc seconds)
        :param kwargs_mass:
        :return: gravitational potential
        """
        theta_E = kwargs_mass['theta_E']
        gamma = kwargs_mass['gamma']
        mass_dimless = self._spp.mass_3d_lens(r, theta_E, gamma)
        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)
        grav_pot = -const.G * mass_dim / (r * const.arcsec * self._cosmo.dd *
                                          const.Mpc)
        return grav_pot

    def delete_cache(self):
        """
        deletes temporary cache tight to a specific model

        :return:
        """
        if hasattr(self, '_interp_sigma_r2'):
            del self._interp_sigma_r2