Esempio n. 1
0
    def __init__(self, z_lens, z_source, cosmo=None):
        """

        :param z_lens: redshift of lens
        :param z_source: redshift of source
        :param cosmo: astropy.cosmology instance
        """

        self.z_lens = z_lens
        self.z_source = z_source
        self.background = Background(cosmo=cosmo)
        self.nfw_param = NFWParam()
Esempio n. 2
0
    def test_against_colossus(self):
        """
        This test class asks to get the same parameters back as colossus: https://bdiemer.bitbucket.io/colossus/index.html
        """
        cosmo = FlatLambdaCDM(H0=70, Om0=0.285, Ob0=0.05)
        nfw_param = NFWParam(cosmo=cosmo)

        from colossus.cosmology import cosmology as cosmology_colossus
        from colossus.halo.profile_nfw import NFWProfile
        colossus_kwargs = {
            'H0': 70,
            'Om0': 0.285,
            'Ob0': 0.05,
            'ns': 0.96,
            'sigma8': 0.82,
            'persistence': ''
        }
        colossus = cosmology_colossus.setCosmology('custom', colossus_kwargs)

        m200 = 10**8
        c = 17.

        zvals = np.linspace(0.0, 2, 50)
        h = 0.7

        for z in zvals:
            nfw_colossus = NFWProfile(M=m200 * h, z=z, c=c, mdef='200c')
            rhos_colossus, rs_colossus = nfw_colossus.nativeParameters(
                m200 * h, c, z, mdef='200c')
            r200_colossus = rs_colossus * c

            # according to colossus documentation the density is in physical units[M h^2/kpc^3] and distance [kpc/h]
            rs_colossus *= h**-1
            rhos_colossus *= h**2

            r200_lenstronomy = nfw_param.r200_M(m200 * h,
                                                z) / h  # physical radius r200
            rs_lenstronomy = r200_lenstronomy / c
            rhos_lenstronomy = nfw_param.rho0_c(
                c, z) * h**2  # physical density in M_sun/Mpc**3

            # convert Mpc to kpc
            rhos_lenstronomy *= 1000**-3
            rs_lenstronomy *= 1000
            npt.assert_almost_equal(rs_lenstronomy / rs_colossus, 1, decimal=3)
            npt.assert_almost_equal(rhos_lenstronomy / rhos_colossus,
                                    1,
                                    decimal=3)
Esempio n. 3
0
class TestLensCosmo(object):
    """
    tests the UnitManager class routines
    """
    def setup(self):
        z_L = 0.8
        z_S = 3.0
        from astropy.cosmology import FlatLambdaCDM
        cosmo = FlatLambdaCDM(H0=70, Om0=0.3, Ob0=0.05)
        self.nfwParam = NFWParam()

    def test_rho0_c(self):
        c = 4
        rho0 = self.nfwParam.rho0_c(c)
        c_out = self.nfwParam.c_rho0(rho0)
        npt.assert_almost_equal(c_out, c, decimal=3)

    def test_profileMain(self):
        M = 10**(13.5)
        z = 0.5
        r200, rho0, c, Rs = self.nfwParam.profileMain(M, z)

        c_ = self.nfwParam.c_M_z(M, z)
        r200_ = self.nfwParam.r200_M(M)
        rho0_ = self.nfwParam.rho0_c(c)
        Rs_ = r200_ / c_
        npt.assert_almost_equal(c_, c, decimal=5)
        npt.assert_almost_equal(r200_, r200, decimal=5)
        npt.assert_almost_equal(rho0_, rho0, decimal=5)
        npt.assert_almost_equal(Rs_, Rs, decimal=5)
Esempio n. 4
0
 def setup(self):
     z_L = 0.8
     z_S = 3.0
     from astropy.cosmology import FlatLambdaCDM
     cosmo = FlatLambdaCDM(H0=70, Om0=0.3, Ob0=0.05)
     self.nfwParam = NFWParam()
Esempio n. 5
0
class LensCosmo(object):
    """
    class to manage the physical units and distances present in a single plane lens with fixed input cosmology
    """
    def __init__(self, z_lens, z_source, cosmo=None):
        """

        :param z_lens: redshift of lens
        :param z_source: redshift of source
        :param cosmo: astropy.cosmology instance
        """

        self.z_lens = z_lens
        self.z_source = z_source
        self.background = Background(cosmo=cosmo)
        self.nfw_param = NFWParam()

    def a_z(self, z):
        """
        convert redshift into scale factor
        :param z: redshift
        :return: scale factor
        """
        return 1. / (1. + z)

    @property
    def h(self):
        return self.background.cosmo.H(0).value / 100.

    @property
    def D_d(self):
        """

        :return: angular diameter distance to the deflector [Mpc]
        """
        return self.background.D_xy(0, self.z_lens)

    @property
    def D_s(self):
        """

        :return: angular diameter distance to the source [Mpc]
        """
        return self.background.D_xy(0, self.z_source)

    @property
    def D_ds(self):
        """

        :return: angular diameter distance from deflector to source [Mpc]
        """
        return self.background.D_xy(self.z_lens, self.z_source)

    @property
    def D_dt(self):
        """

        :return: time delay distance [Mpc]
        """
        return (1 + self.z_lens) * self.D_d * self.D_s / self.D_ds

    @property
    def epsilon_crit(self):
        """
        returns the critical projected mass density in units of M_sun/Mpc^2 (physical units)
        :return: critical projected mass density
        """
        if not hasattr(self, '_Epsilon_Crit'):
            const_SI = const.c**2 / (4 * np.pi * const.G
                                     )  #c^2/(4*pi*G) in units of [kg/m]
            conversion = const.Mpc / const.M_sun  # converts [kg/m] to [M_sun/Mpc]
            factor = const_SI * conversion  #c^2/(4*pi*G) in units of [M_sun/Mpc]
            self._Epsilon_Crit = self.D_s / (
                self.D_d * self.D_ds) * factor  #[M_sun/Mpc^2]
        return self._Epsilon_Crit

    def phys2arcsec_lens(self, phys):
        """
        convert physical Mpc into arc seconds
        :param phys: physical distance [Mpc]
        :return: angular diameter [arcsec]
        """
        return phys / self.D_d / const.arcsec

    def arcsec2phys_lens(self, arcsec):
        """
        convert angular to physical quantities for lens plane
        :param arcsec: angular size at lens plane [arcsec]
        :return: physical size at lens plane [Mpc]
        """
        return arcsec * const.arcsec * self.D_d

    def arcsec2phys_source(self, arcsec):
        """
        convert angular to physical quantities for source plane
        :param arcsec: angular size at source plane [arcsec]
        :return: physical size at source plane [Mpc]
        """
        return arcsec * const.arcsec * self.D_s

    def kappa2proj_mass(self, kappa):
        """
        convert convergence to projected mass M_sun/Mpc^2
        :param kappa: lensing convergence
        :return: projected mass [M_sun/Mpc^2]
        """
        return kappa * self.epsilon_crit

    def mass_in_theta_E(self, theta_E):
        """
        mass within Einstein radius (area * epsilon crit) [M_sun]
        :param theta_E: Einstein radius [arcsec]
        :return: mass within Einstein radius [M_sun]
        """
        mass = self.arcsec2phys_lens(theta_E)**2 * np.pi * self.epsilon_crit
        return mass

    def mass_in_coin(self, theta_E):
        """

        :param theta_E: Einstein radius [arcsec]
        :return: mass in coin calculated in mean density of the universe
        """
        chi_L = self.background.T_xy(0, self.z_lens)
        chi_S = self.background.T_xy(0, self.z_source)
        return 1. / 3 * np.pi * (
            chi_L * theta_E * const.arcsec
        )**2 * chi_S * self.background.rho_crit  #[M_sun/Mpc**3]

    def time_delay_units(self, fermat_pot, kappa_ext=0):
        """

        :param fermat_pot: in units of arcsec^2 (e.g. Fermat potential)
        :param kappa_ext: unit-less
        :return: time delay in days
        """
        D_dt = self.D_dt / (1. - kappa_ext) * const.Mpc  # eqn 7 in Suyu et al.
        return D_dt / const.c * fermat_pot / const.day_s * const.arcsec**2  # * self.arcsec2phys_lens(1.)**2

    def time_delay2fermat_pot(self, dt):
        """

        :param dt: time delay in units of days
        :return: Fermat potential in units arcsec**2 for a given cosmology
        """
        D_dt = self.D_dt * const.Mpc
        return dt * const.c * const.day_s / D_dt / const.arcsec**2

    def nfw_angle2physical(self, Rs_angle, alpha_Rs):
        """
        converts the angular parameters into the physical ones for an NFW profile

        :param alpha_Rs: observed bending angle at the scale radius in units of arcsec
        :param Rs: scale radius in units of arcsec
        :return: M200, r200, Rs_physical, c
        """
        Rs = Rs_angle * const.arcsec * self.D_d
        theta_scaled = alpha_Rs * self.epsilon_crit * self.D_d * const.arcsec
        rho0 = theta_scaled / (4 * Rs**2 * (1 + np.log(1. / 2.)))
        rho0_com = rho0 / self.h**2 * self.a_z(self.z_lens)**3
        c = self.nfw_param.c_rho0(rho0_com)
        r200 = c * Rs
        M200 = self.nfw_param.M_r200(
            r200 * self.h / self.a_z(self.z_lens)) / self.h
        return rho0, Rs, c, r200, M200

    def nfw_physical2angle(self, M, c):
        """
        converts the physical mass and concentration parameter of an NFW profile into the lensing quantities

        :param M: mass enclosed 200 rho_crit in units of M_sun
        :param c: NFW concentration parameter (r200/r_s)
        :return: alpha_Rs (observed bending angle at the scale radius, Rs_angle (angle at scale radius) (in units of arcsec)
        """
        rho0, Rs, r200 = self.nfwParam_physical(M, c)
        Rs_angle = Rs / self.D_d / const.arcsec  # Rs in arcsec
        alpha_Rs = rho0 * (4 * Rs**2 * (1 + np.log(1. / 2.)))
        return Rs_angle, alpha_Rs / self.epsilon_crit / self.D_d / const.arcsec

    def nfwParam_physical(self, M, c):
        """
        returns the NFW parameters in physical units
        :param M: physical mass in M_sun
        :param c: concentration
        :return:
        """
        r200 = self.nfw_param.r200_M(M * self.h) / self.h * self.a_z(
            self.z_lens)  # physical radius r200
        rho0 = self.nfw_param.rho0_c(c) * self.h**2 / self.a_z(
            self.z_lens)**3  # physical density in M_sun/Mpc**3
        Rs = r200 / c
        return rho0, Rs, r200

    def sis_theta_E2sigma_v(self, theta_E):
        """
        converts the lensing Einstein radius into a physical velocity dispersion
        :param theta_E: Einstein radius (in arcsec)
        :return: velocity dispersion in units (km/s)
        """
        v_sigma_c2 = theta_E * const.arcsec / (4 *
                                               np.pi) * self.D_s / self.D_ds
        return np.sqrt(v_sigma_c2) * const.c / 1000

    def sis_sigma_v2theta_E(self, v_sigma):
        """
        converts the velocity dispersion into an Einstein radius for a SIS profile
        :param v_sigma: velocity dispersion (km/s)
        :return: theta_E (arcsec)
        """
        theta_E = 4 * np.pi * (
            v_sigma * 1000. / const.c)**2 * self.D_ds / self.D_s / const.arcsec
        return theta_E
Esempio n. 6
0
    def setup(self):

        cosmo = FlatLambdaCDM(H0=70, Om0=0.3, Ob0=0.05)
        self.nfwParam = NFWParam(cosmo=cosmo)
        self.z = 0.5  # needed fixed redshift for the inversion function
Esempio n. 7
0
class TestLensCosmo(object):
    """
    tests the UnitManager class routines
    """
    def setup(self):

        cosmo = FlatLambdaCDM(H0=70, Om0=0.3, Ob0=0.05)
        self.nfwParam = NFWParam(cosmo=cosmo)
        self.z = 0.5  # needed fixed redshift for the inversion function

    def test_rho0_c(self):
        c = 4

        rho0 = self.nfwParam.rho0_c(c, z=self.z)
        c_out = self.nfwParam.c_rho0(rho0, z=self.z)
        npt.assert_almost_equal(c_out, c, decimal=3)

    def test_rhoc_z(self):
        z = 0
        rho0_z = self.nfwParam.rhoc_z(z=z)
        npt.assert_almost_equal(self.nfwParam.rhoc * (1 + z)**3, rho0_z)

    def test_M200(self):
        M200 = self.nfwParam.M200(rs=1, rho0=1, c=1)
        npt.assert_almost_equal(M200, 2.4271590540348216, decimal=5)

    def test_profileMain(self):
        M = 10**13.5
        z = 0.5
        r200, rho0, c, Rs = self.nfwParam.nfw_Mz(M, z)

        c_ = self.nfwParam.c_M_z(M, z)
        r200_ = self.nfwParam.r200_M(M, z)
        rho0_ = self.nfwParam.rho0_c(c, z)
        Rs_ = r200_ / c_
        npt.assert_almost_equal(c_, c, decimal=5)
        npt.assert_almost_equal(r200_, r200, decimal=5)
        npt.assert_almost_equal(rho0_, rho0, decimal=5)
        npt.assert_almost_equal(Rs_, Rs, decimal=5)

    def test_against_colossus(self):
        """
        This test class asks to get the same parameters back as colossus: https://bdiemer.bitbucket.io/colossus/index.html
        """
        cosmo = FlatLambdaCDM(H0=70, Om0=0.285, Ob0=0.05)
        nfw_param = NFWParam(cosmo=cosmo)

        from colossus.cosmology import cosmology as cosmology_colossus
        from colossus.halo.profile_nfw import NFWProfile
        colossus_kwargs = {
            'H0': 70,
            'Om0': 0.285,
            'Ob0': 0.05,
            'ns': 0.96,
            'sigma8': 0.82
        }
        colossus = cosmology_colossus.setCosmology('custom', colossus_kwargs)

        m200 = 10**8
        c = 17.

        zvals = np.linspace(0.0, 2, 50)
        h = 0.7

        for z in zvals:
            nfw_colossus = NFWProfile(m200 * h, z, mdef='200c')
            rhos_colossus, rs_colossus = nfw_colossus.fundamentalParameters(
                m200 * h, c, z, mdef='200c')
            r200_colossus = rs_colossus * c

            # according to colossus documentation the density is in physical units[M h^2/kpc^3] and distance [kpc/h]
            rs_colossus *= h**-1
            rhos_colossus *= h**2

            r200_lenstronomy = nfw_param.r200_M(m200 * h,
                                                z) / h  # physical radius r200
            rs_lenstronomy = r200_lenstronomy / c
            rhos_lenstronomy = nfw_param.rho0_c(
                c, z) * h**2  # physical density in M_sun/Mpc**3

            # convert Mpc to kpc
            rhos_lenstronomy *= 1000**-3
            rs_lenstronomy *= 1000
            npt.assert_almost_equal(rs_lenstronomy / rs_colossus, 1, decimal=3)
            npt.assert_almost_equal(rhos_lenstronomy / rhos_colossus,
                                    1,
                                    decimal=3)
Esempio n. 8
0
class LensCosmo(object):
    """
    class to manage the physical units and distances present in a single plane lens with fixed input cosmology
    """
    def __init__(self, z_lens, z_source, cosmo=None):
        """

        :param z_lens: redshift of lens
        :param z_source: redshift of source
        :param cosmo: astropy.cosmology instance
        """

        self.z_lens = z_lens
        self.z_source = z_source
        self.background = Background(cosmo=cosmo)
        self.nfw_param = NFWParam(cosmo=cosmo)

    def a_z(self, z):
        """
        convert redshift into scale factor
        :param z: redshift
        :return: scale factor
        """
        return 1. / (1. + z)

    @property
    def h(self):
        return self.background.cosmo.H(0).value / 100.

    @property
    def dd(self):
        """

        :return: angular diameter distance to the deflector [Mpc]
        """
        return self.background.d_xy(0, self.z_lens)

    @property
    def ds(self):
        """

        :return: angular diameter distance to the source [Mpc]
        """
        return self.background.d_xy(0, self.z_source)

    @property
    def dds(self):
        """

        :return: angular diameter distance from deflector to source [Mpc]
        """
        return self.background.d_xy(self.z_lens, self.z_source)

    @property
    def ddt(self):
        """

        :return: time delay distance [Mpc]
        """
        return (1 + self.z_lens) * self.dd * self.ds / self.dds

    @property
    def sigma_crit(self):
        """
        returns the critical projected lensing mass density in units of M_sun/Mpc^2
        :return: critical projected lensing mass density
        """
        if not hasattr(self, '_sigma_crit_mpc'):
            const_SI = const.c**2 / (4 * np.pi * const.G
                                     )  # c^2/(4*pi*G) in units of [kg/m]
            conversion = const.Mpc / const.M_sun  # converts [kg/m] to [M_sun/Mpc]
            factor = const_SI * conversion  # c^2/(4*pi*G) in units of [M_sun/Mpc]
            self._sigma_crit_mpc = self.ds / (
                self.dd * self.dds) * factor  # [M_sun/Mpc^2]
        return self._sigma_crit_mpc

    @property
    def sigma_crit_angle(self):
        """
        returns the critical surface density in units of M_sun/arcsec^2 (in physical solar mass units)
        when provided a physical mass per physical Mpc^2
        :return: critical projected mass density
        """
        if not hasattr(self, '_sigma_crit_arcsec'):
            const_SI = const.c**2 / (4 * np.pi * const.G
                                     )  # c^2/(4*pi*G) in units of [kg/m]
            conversion = const.Mpc / const.M_sun  # converts [kg/m] to [M_sun/Mpc]
            factor = const_SI * conversion  # c^2/(4*pi*G) in units of [M_sun/Mpc]
            self._sigma_crit_arcsec = self.ds / (
                self.dd * self.dds) * factor * (
                    self.dd * const.arcsec)**2  # [M_sun/arcsec^2]
        return self._sigma_crit_arcsec

    def phys2arcsec_lens(self, phys):
        """
        convert physical Mpc into arc seconds
        :param phys: physical distance [Mpc]
        :return: angular diameter [arcsec]
        """
        return phys / self.dd / const.arcsec

    def arcsec2phys_lens(self, arcsec):
        """
        convert angular to physical quantities for lens plane
        :param arcsec: angular size at lens plane [arcsec]
        :return: physical size at lens plane [Mpc]
        """
        return arcsec * const.arcsec * self.dd

    def arcsec2phys_source(self, arcsec):
        """
        convert angular to physical quantities for source plane
        :param arcsec: angular size at source plane [arcsec]
        :return: physical size at source plane [Mpc]
        """
        return arcsec * const.arcsec * self.ds

    def kappa2proj_mass(self, kappa):
        """
        convert convergence to projected mass M_sun/Mpc^2
        :param kappa: lensing convergence
        :return: projected mass [M_sun/Mpc^2]
        """
        return kappa * self.sigma_crit

    def mass_in_theta_E(self, theta_E):
        """
        mass within Einstein radius (area * epsilon crit) [M_sun]
        :param theta_E: Einstein radius [arcsec]
        :return: mass within Einstein radius [M_sun]
        """
        mass = self.arcsec2phys_lens(theta_E)**2 * np.pi * self.sigma_crit
        return mass

    def mass_in_coin(self, theta_E):
        """

        :param theta_E: Einstein radius [arcsec]
        :return: mass in coin calculated in mean density of the universe
        """
        chi_L = self.background.T_xy(0, self.z_lens)
        chi_S = self.background.T_xy(0, self.z_source)
        return 1. / 3 * np.pi * (
            chi_L * theta_E * const.arcsec
        )**2 * chi_S * self.background.rho_crit  #[M_sun/Mpc**3]

    def time_delay_units(self, fermat_pot, kappa_ext=0):
        """

        :param fermat_pot: in units of arcsec^2 (e.g. Fermat potential)
        :param kappa_ext: unit-less external shear not accounted for in the Fermat potential
        :return: time delay in days
        """
        D_dt = self.ddt * (1. - kappa_ext) * const.Mpc  # eqn 7 in Suyu et al.
        return D_dt / const.c * fermat_pot / const.day_s * const.arcsec**2  # * self.arcsec2phys_lens(1.)**2

    def time_delay2fermat_pot(self, dt):
        """

        :param dt: time delay in units of days
        :return: Fermat potential in units arcsec**2 for a given cosmology
        """
        D_dt = self.ddt * const.Mpc
        return dt * const.c * const.day_s / D_dt / const.arcsec**2

    def nfw_angle2physical(self, Rs_angle, alpha_Rs):
        """
        converts the angular parameters into the physical ones for an NFW profile

        :param alpha_Rs: observed bending angle at the scale radius in units of arcsec
        :param Rs: scale radius in units of arcsec
        :return: rho0, Rs, c, r200, M200
        """
        Rs = Rs_angle * const.arcsec * self.dd
        theta_scaled = alpha_Rs * self.sigma_crit * self.dd * const.arcsec
        rho0 = theta_scaled / (4 * Rs**2 * (1 + np.log(1. / 2.)))
        rho0_com = rho0 / self.h**2
        c = self.nfw_param.c_rho0(rho0_com, self.z_lens)
        r200 = c * Rs
        M200 = self.nfw_param.M_r200(r200 * self.h, self.z_lens) / self.h
        return rho0, Rs, c, r200, M200

    def nfw_physical2angle(self, M, c):
        """
        converts the physical mass and concentration parameter of an NFW profile into the lensing quantities

        :param M: mass enclosed 200 rho_crit in units of M_sun (physical units, meaning no little h)
        :param c: NFW concentration parameter (r200/r_s)
        :return: Rs_angle (angle at scale radius) (in units of arcsec), alpha_Rs (observed bending angle at the scale radius
        """
        rho0, Rs, r200 = self.nfwParam_physical(M, c)
        Rs_angle = Rs / self.dd / const.arcsec  # Rs in arcsec
        alpha_Rs = rho0 * (4 * Rs**2 * (1 + np.log(1. / 2.)))
        return Rs_angle, alpha_Rs / self.sigma_crit / self.dd / const.arcsec

    def nfwParam_physical(self, M, c):
        """
        returns the NFW parameters in physical units

        :param M: physical mass in M_sun
        :param c: concentration
        :return: rho0, Rs, r200
        """
        r200 = self.nfw_param.r200_M(
            M * self.h, self.z_lens) / self.h  # physical radius r200
        rho0 = self.nfw_param.rho0_c(
            c, self.z_lens) * self.h**2  # physical density in M_sun/Mpc**3
        Rs = r200 / c
        return rho0, Rs, r200

    def nfw_M_theta_vir(self, M):
        """
        returns virial radius in angular units of arc seconds on the sky

        :param M: physical mass in M_sun
        :return: angle (in arc seconds) of the virial radius
        """
        r200 = self.nfw_param.r200_M(
            M * self.h, self.z_lens) / self.h  # physical radius r200
        theta_r200 = r200 / self.dd / const.arcsec
        return theta_r200

    def sis_theta_E2sigma_v(self, theta_E):
        """
        converts the lensing Einstein radius into a physical velocity dispersion
        :param theta_E: Einstein radius (in arcsec)
        :return: velocity dispersion in units (km/s)
        """
        v_sigma_c2 = theta_E * const.arcsec / (4 * np.pi) * self.ds / self.dds
        return np.sqrt(v_sigma_c2) * const.c / 1000

    def sis_sigma_v2theta_E(self, v_sigma):
        """
        converts the velocity dispersion into an Einstein radius for a SIS profile
        :param v_sigma: velocity dispersion (km/s)
        :return: theta_E (arcsec)
        """
        theta_E = 4 * np.pi * (v_sigma * 1000. /
                               const.c)**2 * self.dds / self.ds / const.arcsec
        return theta_E

    def uldm_angular2phys(self, kappa_0, theta_c):
        """
        converts the anguar parameters entering the LensModel Uldm() (Ultra Light
        Dark Matter) class in physical masses, i.e. the total soliton mass and the
        mass of the particle
        :param kappa_0: central convergence of profile
        :param theta_c: core radius (in arcseconds)
        :return: m_eV_log10, M_sol_log10, the log10 of the masses, m in eV and M in M_sun
        """
        D_Lens = self.dd * 10**6  # in parsec
        Sigma_c = self.sigma_crit * 10**(-12)  # in M_sun / parsec^2
        r_c = theta_c * const.arcsec * D_Lens
        rho0 = 2048 * np.sqrt(0.091) * kappa_0 * Sigma_c / (429 * np.pi * r_c)
        m_log10 = -22 + 0.5 * np.log10(190 / rho0 * (r_c / 100)**(-4))
        M_log10 = 9 + np.log10(160 * 1.4 / r_c) - 2 * (m_log10 + 22)
        return m_log10, M_log10

    def uldm_mphys2angular(self, m_log10, M_log10):
        """
        converts physical ULDM mass in the ones, in angular units, that enter
        the LensModel Uldm() class
        :param m_log10: exponent of ULDM mass in eV
        :param M_log10: exponent of soliton mass in M_sun
        :return: kappa_0, theta_c, the central convergence and core radius (in arcseconds)
        """
        D_Lens = self.dd * 10**6  # in parsec
        Sigma_c = self.sigma_crit * 10**(-12)  # in M_sun/parsec^2
        m22 = 10**(m_log10 + 22)
        M9 = 10**(M_log10 - 9)
        r_c = 160 * 1.4 * m22**(-2) * M9**(-1)  # core radius in parsec
        rho0 = 190 * m22**(-2) * (r_c / 100)**(
            -4)  # central density in M_sun/parsec^3
        kappa_0 = 429 * np.pi * rho0 * r_c / (2048 * np.sqrt(0.091) * Sigma_c)
        theta_c = r_c / D_Lens / const.arcsec
        return kappa_0, theta_c