Example #1
0
def vmin_migdal(w, erec, mw):
    """Return minimum WIMP velocity to make a Migdal signal with energy w,
    given elastic recoil energy erec and WIMP mass mw.
    """
    y = (wr.mn() * erec / (2 * wr.mu_nucleus(mw)**2))**0.5
    y += w / (2 * wr.mn() * erec)**0.5
    return np.maximum(0, y)
Example #2
0
def sigma_w_erec(w,
                 erec,
                 v,
                 mw,
                 sigma_nucleon,
                 interaction='SI',
                 m_med=float('inf')):
    """Differential WIMP-nucleus Bremsstrahlung cross section.
    From Kouvaris/Pradler [arxiv:1607.01789v2], eq. 8

    :param w: Bremsstrahlung photon energy
    :param mw: WIMP mass
    :param erec: recoil energy
    :param v: WIMP speed (earth/detector frame)
    :param sigma_nucleon: WIMP/nucleon cross-section
    :param interaction: string describing DM-nucleus interaction.
    Default is 'SI' (spin-independent)
    :param m_med: Mediator mass. If not given, assumed very heavy.

    TODO: check for wmax!    # What is this? Still relevant?
    """
    # X-ray form factor
    form_atomic = np.abs(f1(w / nu.keV) + 1j * f2(w / nu.keV))

    # Note mn -> mn c^2, Kouvaris/Pradtler and McCabe use natural units
    return (4 * nu.alphaFS / (3 * np.pi * w) * erec / (wr.mn() * nu.c0**2) *
            form_atomic**2 *
            wr.sigma_erec(erec, v, mw, sigma_nucleon, interaction, m_med))
Example #3
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 #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 erec_bound(sign, w, v, mw):
    """Bremsstrahlung scattering recoil energy kinematic limits
    From Kouvaris/Pradler [arxiv:1607.01789v2], eq. between 8 and 9,
    simplified by vmin (see above)

    :param sign: +1 to get upper limit, -1 to get lower limit
    :param w: Bremsstrahlung photon energy
    :param mw: WIMP mass
    :param v: WIMP speed (earth/detector frame)
    """
    return (wr.mu_nucleus(mw)**2 * v**2 / wr.mn() *
            (1 - vmin_w(w, mw)**2 / (2 * v**2) + sign *
             (1 - vmin_w(w, mw)**2 / v**2)**0.5))
Example #6
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))
Example #7
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 #8
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)