Example #1
0
def velocity_integral_without_time(halo_model=None):
    """Precomputes the inverse_speed_integral by fixing
    the upper bound to vmax=konst and varring the lower bound vmin
    only used i the case where there is no time dependence in vmax i.e. t=None"""

    halo_model = wr.StandardHaloModel() if halo_model is None else halo_model
    _v_mins = np.linspace(0.0001, 1, 1000) * wr.v_max(
        None, halo_model.v_esc)  # v_max carries units

    _ims = np.array([
        quad(lambda v: 1 / v * halo_model.velocity_dist(v, None), _v_min,
             wr.v_max(None, halo_model.v_esc))[0] for _v_min in _v_mins
    ])
    """
    _ims = np.zeros(len(_v_mins))
    integrand=np.zeros(len(_v_mins))
    for _v_min,index in zip(_v_mins,range(len(_v_mins))):
        f = lambda v: 1/v*halo_model.velocity_dist(v,None)  #velocity_dist returns units
        integrand[index]= f(_v_min)
        _ims[index] = quad(f, _v_min, wr.v_max(None, halo_model.v_esc))[0]"""

    # Store interpolator in unit-dependent numbers
    # have to pass unit carring velocity to the interpolator
    # and return carryies units of the integral [1/velo]

    inv_mean_speed = interp1d(_v_mins, _ims, fill_value=0, bounds_error=False)
    # If we don't have 0 < v_min < v_max, we want to return 0
    # so the integrand vanishes

    return inv_mean_speed
Example #2
0
def rate_bremsstrahlung(w,
                        mw,
                        sigma_nucleon,
                        interaction='SI',
                        m_med=float('inf'),
                        t=None,
                        **kwargs):
    """Differential rate per unit detector mass and recoil energy of
    Bremsstrahlung elastic WIMP-nucleus scattering.

    :param w: Bremsstrahlung photon energy
    :param mw: Mass of WIMP
    :param sigma_nucleon: WIMP/nucleon cross-section
    :param m_med: Mediator mass. If not given, assumed very heavy.
    :param t: A J2000.0 timestamp. If not given,
    a conservative velocity distribution is used.
    :param interaction: string describing DM-nucleus interaction.
    See sigma_erec for options
    :param progress_bar: if True, show a progress bar during evaluation
    (if w is an array)

    Further kwargs are passed to scipy.integrate.quad numeric integrator
    (e.g. error tolerance).
    """
    vmin = vmin_w(w, mw)

    if vmin >= wr.v_max(t):
        return 0

    def integrand(v):
        return (sigma_w(w, v, mw, sigma_nucleon, interaction, m_med) * v *
                wr.observed_speed_dist(v, t))

    return wr.rho_dm() / mw * (1 / wr.mn()) * quad(integrand, vmin,
                                                   wr.v_max(t), **kwargs)[0]
Example #3
0
def rate_elastic(erec,
                 mw,
                 sigma_nucleon,
                 interaction='SI',
                 m_med=float('inf'),
                 t=None,
                 material='Xe',
                 halo_model=None,
                 **kwargs):
    """Differential rate per unit detector mass and recoil energy of
    elastic WIMP scattering

    :param erec: recoil energy
    :param mw: WIMP mass
    :param sigma_nucleon: WIMP/nucleon cross-section
    :param interaction: string describing DM-nucleus interaction,
    see sigma_erec for options
    :param m_med: Mediator mass. If not given, assumed very heavy.
    :param t: A J2000.0 timestamp.
    If not given, conservative velocity distribution is used.
    :param halo_model: class (default to standard halo model)
    containing velocity distribution
    :param progress_bar: if True, show a progress bar during evaluation
    (if erec is an array)
    :param material: name of the detection material (default is 'Xe')

    Further kwargs are passed to scipy.integrate.quad numeric integrator
    (e.g. error tolerance).

    Analytic expressions are known for this rate, but they are not used here.
    """

    halo_model = wr.StandardHaloModel() if halo_model is None else halo_model
    v_min = vmin_elastic(erec, mw, material)

    if v_min >= wr.v_max(t, halo_model.v_esc):
        return 0

    def integrand(v):
        return (sigma_erec(
            erec, v, mw, sigma_nucleon, interaction, m_med, material=material)
                * v * halo_model.velocity_dist(v, t))

    return halo_model.rho_dm / mw * (1 / mn(material)) * quad(
        integrand, v_min, wr.v_max(t, halo_model.v_esc), **kwargs)[0]
Example #4
0
def rate_dme(erec, n, l, mw, sigma_dme, t=None, **kwargs):
    """Return differential rate of dark matter electron scattering vs energy
    (i.e. dr/dE, not dr/dlogE)
    :param erec: Electronic recoil energy
    :param n: Principal quantum numbers of the shell that is hit
    :param l: Angular momentum quantum number of the shell that is hit
    :param mw: DM mass
    :param sigma_dme: DM-free electron scattering cross-section at fixed
    momentum transfer q=0
    :param t: A J2000.0 timestamp.
    If not given, a conservative velocity distribution is used.
    """
    shell = shell_str(n, l)
    eb = binding_es_for_dme(n, l)

    # No bounds are given for the q integral
    # but the form factors are only specified in a limited range of q
    qmax = (np.exp(shell_data[shell]['lnqs'].max()) *
            (nu.me * nu.c0 * nu.alphaFS))

    if t is None:
        # Use precomputed inverse mean speed,
        # so we only have to do a single integral
        def diff_xsec(q):
            vmin = v_min_dme(eb, erec, q, mw)
            result = q * dme_ionization_ff(shell, erec, q)
            # Note the interpolator is in kms, not unit-carrying numbers
            # see above
            result *= inverse_mean_speed_kms(vmin / (nu.km / nu.s))
            result /= (nu.km / nu.s)
            return result

        r = quad(diff_xsec, 0, qmax)[0]

    else:
        # Have to do double integral
        # Note dblquad expects the function to be f(y, x), not f(x, y)...
        def diff_xsec(v, q):
            result = q * dme_ionization_ff(shell, erec, q)
            result *= 1 / v * wr.observed_speed_dist(v, t)
            return result

        r = dblquad(diff_xsec, 0, qmax, lambda q: v_min_dme(eb, erec, q, mw),
                    lambda _: wr.v_max(t), **kwargs)[0]

    mu_e = mw * nu.me / (mw + nu.me)

    return (
        # Convert cross-section to rate, as usual
        wr.rho_dm() / mw * (1 / wr.mn())
        # d/lnE -> d/E
        * 1 / erec
        # Prefactors in cross-section
        * sigma_dme / (8 * mu_e**2) * r)
Example #5
0
def rate_elastic(erec,
                 mw,
                 sigma_nucleon,
                 interaction='SI',
                 m_med=float('inf'),
                 t=None,
                 **kwargs):
    """Differential rate per unit detector mass and recoil energy of
    elastic WIMP scattering

    :param erec: recoil energy
    :param mw: WIMP mass
    :param sigma_nucleon: WIMP/nucleon cross-section
    :param interaction: string describing DM-nucleus interaction,
    see sigma_erec for options
    :param m_med: Mediator mass. If not given, assumed very heavy.
    :param t: A J2000.0 timestamp.
    If not given, conservative velocity distribution is used.
    :param progress_bar: if True, show a progress bar during evaluation
    (if erec is an array)

    Further kwargs are passed to scipy.integrate.quad numeric integrator
    (e.g. error tolerance).

    Analytic expressions are known for this rate, but they are not used here.
    """
    v_min = vmin_elastic(erec, mw)

    if v_min >= wr.v_max(t):
        return 0

    def integrand(v):
        return (sigma_erec(erec, v, mw, sigma_nucleon, interaction, m_med) *
                v * wr.observed_speed_dist(v, t))

    return wr.rho_dm() / mw * (1 / mn()) * quad(integrand, v_min, wr.v_max(t),
                                                **kwargs)[0]
Example #6
0
def rate_migdal(w,
                mw,
                sigma_nucleon,
                interaction='SI',
                m_med=float('inf'),
                include_approx_nr=False,
                t=None,
                **kwargs):
    """Differential rate per unit detector mass and deposited ER energy of
    Migdal effect WIMP-nucleus scattering

    :param w: ER energy deposited in detector through Migdal effect
    :param mw: Mass of WIMP
    :param sigma_nucleon: WIMP/nucleon cross-section
    :param interaction: string describing DM-nucleus interaction.
    See sigma_erec for options
    :param m_med: Mediator mass. If not given, assumed very heavy.
    :param include_approx_nr: If True, instead return differential rate
        per *detected* energy, including the contribution of
        the simultaneous NR signal approximately, assuming q_{NR} = 0.15.
        This is how https://arxiv.org/abs/1707.07258
        presented the Migdal spectra.
    :param t: A J2000.0 timestamp.
    If not given, conservative velocity distribution is used.
    :param progress_bar: if True, show a progress bar during evaluation
    (if w is an array)

    Further kwargs are passed to scipy.integrate.quad numeric integrator
    (e.g. error tolerance).
    """
    include_approx_nr = 1 if include_approx_nr else 0

    result = 0
    for state, binding_e in binding_es_for_migdal.items():
        binding_e *= nu.eV
        # Only consider n=3 and n=4
        # n=5 is the valence band so unreliable in in liquid
        # n=1,2 contribute very little
        if state[0] not in ['3', '4']:
            continue

        # Lookup for differential probability (units of ev^-1)
        p = interp1d(df_migdal['E'].values * nu.eV,
                     df_migdal[state].values / nu.eV,
                     bounds_error=False,
                     fill_value=0)

        def diff_rate(v, erec):
            # Observed energy = energy of emitted electron
            #                 + binding energy of state
            eelec = w - binding_e - include_approx_nr * erec * 0.15
            if eelec < 0:
                return 0

            return (
                # Usual elastic differential rate,
                # common constants follow at end
                wr.sigma_erec(
                    erec, v, mw, sigma_nucleon, interaction, m_med=m_med) * v *
                wr.observed_speed_dist(v, t)

                # Migdal effect |Z|^2
                # TODO: ?? what is explicit (eV/c)**2 doing here?
                * (nu.me * (2 * erec / wr.mn())**0.5 /
                   (nu.eV / nu.c0))**2 / (2 * np.pi) * p(eelec))

        # Note dblquad expects the function to be f(y, x), not f(x, y)...
        r = dblquad(
            diff_rate, 0, wr.e_max(mw, wr.v_max(t)), lambda erec: vmin_migdal(
                w - include_approx_nr * erec * 0.15, erec, mw),
            lambda _: wr.v_max(t), **kwargs)[0]

        result += r

    return wr.rho_dm() / mw * (1 / wr.mn()) * result
Example #7
0
def rate_dme(erec,
             n,
             l,
             mw,
             sigma_dme,
             inv_mean_speed=None,
             halo_model=None,
             f_dm='1',
             t=None,
             **kwargs):
    """Return differential rate of dark matter electron scattering vs energy
    (i.e. dr/dE, not dr/dlogE)
    :param erec: Electronic recoil energy
    :param n: Principal quantum numbers of the shell that is hit
    :param l: Angular momentum quantum number of the shell that is hit
    :param mw: DM mass
    :param sigma_dme: DM-free electron scattering cross-section at fixed
    momentum transfer q=0
    :param f_dm: One of the following:
     '1':     |F_DM|^2 = 1, contact interaction / heavy mediator (default)
     '1_q':   |F_DM|^2 = (\alpha m_e c / q), dipole moment
     '1_q2': |F_DM|^2 = (\alpha m_e c / q)^2, ultralight mediator
    :param t: A J2000.0 timestamp.
    :param halo_model, Halo to be used if not given, the standard is used.
    :param inv_mean_speed: function to compute inverse_mean_speed integral for a given vmin
    """
    halo_model = wr.StandardHaloModel() if halo_model is None else halo_model
    inv_mean_speed = inv_mean_speed

    shell = shell_str(n, l)
    eb = binding_es_for_dme(n, l)

    f_dm = {
        '1': lambda q: 1,
        '1_q': lambda q: nu.alphaFS * nu.me * nu.c0 / q,
        '1_q2': lambda q: (nu.alphaFS * nu.me * nu.c0 / q)**2
    }[f_dm]

    # No bounds are given for the q integral
    # but the form factors are only specified in a limited range of q
    qmax = (np.exp(shell_data[shell]['lnqs'].max()) *
            (nu.me * nu.c0 * nu.alphaFS))

    if t is None and inv_mean_speed is not None:
        # Use precomputed inverse mean speed so we only have to do a single integral

        def diff_xsec(q):
            vmin = v_min_dme(eb, erec, q, mw)
            result = q * dme_ionization_ff(
                shell, erec, q) * f_dm(q)**2 * inv_mean_speed(vmin)
            # Note the interpolator return carryies units
            return result

        r = quad(diff_xsec, 0, qmax)[0]

    else:
        # Have to do double integral
        # Note dblquad expects the function to be f(y, x), not f(x, y)...
        def diff_xsec(v, q):
            result = q * dme_ionization_ff(shell, erec, q) * f_dm(q)**2
            result *= 1 / v * halo_model.velocity_dist(v, t)
            return result

        r = dblquad(diff_xsec, 0, qmax, lambda q: v_min_dme(eb, erec, q, mw),
                    lambda _: wr.v_max(t, halo_model.v_esc), **kwargs)[0]

    mu_e = mw * nu.me / (mw + nu.me)

    return (halo_model.rho_dm / mw * (1 / wr.mn())
            # d/lnE -> d/E
            * 1 / erec
            # Prefactors in cross-section
            * sigma_dme / (8 * mu_e**2) * r)
Example #8
0
    }[shell_str(n, l)] * nu.eV


@export
def v_min_dme(eb, erec, q, mw):
    """Minimal DM velocity for DM-electron scattering
    :param eb: binding energy of shell
    :param erec: electronic recoil energy energy
    :param q: momentum transfer
    :param mw: DM mass
    """
    return (erec + eb) / q + q / (2 * mw)


# Precompute velocity integrals for t=None
_v_mins = np.linspace(0, 1, 1000) * wr.v_max()
_ims = np.array([
    quad(lambda v: 1 / v * wr.observed_speed_dist(v), _v_min, wr.v_max())[0]
    for _v_min in _v_mins
])

# Store interpolator in km/s rather than unit-dependent numbers
# so we don't have to recalculate them when nu.reset_units() is called
inverse_mean_speed_kms = interp1d(
    _v_mins / (nu.km / nu.s),
    _ims * (nu.km / nu.s),
    # If we don't have 0 < v_min < v_max, we want to return 0
    # so the integrand vanishes
    fill_value=0,
    bounds_error=False)