Source code for dddm.detectors.super_cdms

import typing as ty
from abc import ABC

from .experiment import Experiment
from .lindhard_factors import lindhard_quenching_factor_semi_conductors, _get_nr_resolution
import numpy as np
import dddm
from functools import partial

export, __all__ = dddm.exporter()


class _BaseSuperCdms(Experiment, ABC):
    """Base class of superCDMS to introduce shared properties"""
    location = "SNOLAB"

    # Parameters needed for eq. 3, 4 of https://arxiv.org/pdf/1610.00006.pdf
    # Since they are not directly used, they are not set as class attributes
    _energy_parameters = dict(

        si_hv={'Z': 14,
               'epsilon': 0.003,
               'e_delta_v': 0.1,
               'e_thr_phonon': 100e-3,
               'sigma_phonon': 5e-3,
               'sigma_ion': np.nan,  # Only phonons
               # https://arxiv.org/pdf/2001.06503.pdf Table III & IV
               'k': 0.161,
               'c0': 0.0091,
               'c1': 3.33e-05,
               'U': 0.15
               },
        si_izip={'Z': 14,
                 'epsilon': 0.003,
                 'e_delta_v': 0.008,
                 'e_thr_phonon': 175e-3,
                 'sigma_phonon': 25e-3,
                 'sigma_ion': 110e-3,
                 # https://arxiv.org/pdf/2001.06503.pdf Table III & IV
                 'k': 0.161,
                 'c0': 9.1e-3,
                 'c1': 3.33e-05,
                 'U': 0.15
                 },
        ge_hv={'Z': 32,
               'epsilon': 0.00382,
               'e_delta_v': 0.1,
               'e_thr_phonon': 100e-3,
               'sigma_phonon': 10e-3,
               'sigma_ion': np.nan,  # Only phonons
               # https://arxiv.org/pdf/2001.06503.pdf Table III & IV
               'k': 0.162,
               'c0': 9.1e-3,
               'c1': 0.62e-05,
               'U': 0.15
               },
        ge_izip={'Z': 32,
                 'epsilon': 0.00382,
                 'e_delta_v': 0.006,
                 'e_thr_phonon': 350e-3,
                 'sigma_phonon': 50e-3,
                 'sigma_ion': 100e-3,
                 # https://arxiv.org/pdf/2001.06503.pdf Table III & IV
                 'k': 0.162,
                 'c0': 9.1e-3,
                 'c1': 0.62e-05,
                 'U': 0.15
                 },
    )

    def get_energy_thr_ee_from_phonon_thr(self) -> ty.Union[float, int]:
        """get the energy threshold (ee) based on the energy_parameters"""
        assert 'migdal' in self.interaction_type
        this_conf = self._energy_parameters[self.detector_key]
        return energy_ee_from_energy_phonon(
            e_ph=this_conf['e_thr_phonon'],
            e_delta_v=this_conf['e_delta_v'],
            epsilon=this_conf['epsilon']
        )

    def get_energy_res_ee_from_phonon_res(self) -> ty.Union[float, int]:
        """get the energy resolution (ee) based on the energy_parameters"""
        assert 'migdal' in self.interaction_type
        this_conf = self._energy_parameters[self.detector_key]
        return energy_ee_from_energy_phonon(
            e_ph=this_conf['sigma_phonon'],
            e_delta_v=this_conf['e_delta_v'],
            epsilon=this_conf['epsilon']
        )

    def energy_nr_to_detectable_energy_function(self) -> ty.Callable:
        """
        Get phonon energy (hv) or ionization energy (izip) from nuclear recoil energy
        """
        assert 'migdal' not in self.interaction_type
        det_key = self.detector_key
        this_conf = self._energy_parameters[det_key]
        if 'izip' in det_key:
            return partial(energy_ionization_from_e_nr,
                           Z=this_conf['Z'],
                           k=this_conf['k'],
                           c0=this_conf['c0'],
                           c1=this_conf['c1'],
                           U=this_conf['U'],
                           )
        if 'hv' in det_key:
            return partial(energy_phonon_from_energy_nr,
                           Z=this_conf['Z'],
                           k=this_conf['k'],
                           e_delta_v=this_conf['e_delta_v'],
                           epsilon=this_conf['epsilon'],
                           c0=this_conf['c0'],
                           c1=this_conf['c1'],
                           U=this_conf['U'],
                           )
        raise ValueError(f'got {det_key}?!')

    @property
    def detector_key(self) -> str:
        material = self.target_material.lower()
        if 'hv' in self.detector_name.lower():
            return f'{material}_hv'
        assert 'izip' in self.detector_name.lower()
        return f'{material}_izip'


[docs]@export class SuperCdmsHvGeNr(_BaseSuperCdms): detector_name = 'SuperCDMS_HV_Ge_NR' target_material = 'Ge' interaction_type = 'SI' __version__ = '0.0.0' exposure_tonne_year = 44 * 1.e-3 # Tonne year energy_threshold_kev = 40. / 1e3 # table VIII, Enr cut_efficiency = 0.85 # p. 11, right column detection_efficiency = 0.85 # p. 11, left column NOTE: ER type!
[docs] def resolution(self, energies_in_kev): """Flat resolution""" phonon_energy_from_nr = self.energy_nr_to_detectable_energy_function() phonon_resolution = self._energy_parameters[self.detector_key]['sigma_phonon'] return _get_nr_resolution(energies_in_kev, phonon_energy_from_nr, phonon_resolution)
[docs] def background_function(self, energies_in_kev): """Flat bg rate""" bg_rate_nr = 27 # counts/kg/keV/year conv_units = 1.0e3 # Tonne return self._flat_background(len(energies_in_kev), bg_rate_nr * conv_units)
[docs]@export class SuperCdmsHvSiNr(_BaseSuperCdms): detector_name = 'SuperCDMS_HV_Si_NR' target_material = 'Si' interaction_type = 'SI' __version__ = '0.0.0' exposure_tonne_year = 9.6 * 1.e-3 # Tonne year energy_threshold_kev = 78. / 1e3 # table VIII, Enr cut_efficiency = 0.85 # p. 11, right column detection_efficiency = 0.85 # p. 11, left column NOTE: ER type!
[docs] def resolution(self, energies_in_kev): """Flat resolution""" phonon_energy_from_nr = self.energy_nr_to_detectable_energy_function() phonon_resolution = self._energy_parameters[self.detector_key]['sigma_phonon'] return _get_nr_resolution(energies_in_kev, phonon_energy_from_nr, phonon_resolution)
[docs] def background_function(self, energies_in_kev): """Flat bg rate""" bg_rate_nr = 300 # counts/kg/keV/year conv_units = 1.0e3 # Tonne return self._flat_background(len(energies_in_kev), bg_rate_nr * conv_units)
[docs]@export class SuperCdmsIzipGeNr(_BaseSuperCdms): detector_name = 'SuperCDMS_iZIP_Ge_NR' target_material = 'Ge' interaction_type = 'SI' __version__ = '0.0.0' exposure_tonne_year = 56 * 1.e-3 # Tonne year energy_threshold_kev = 272. / 1e3 # table VIII, Enr cut_efficiency = 0.75 # p. 11, right column detection_efficiency = 0.85 # p. 11, left column
[docs] def resolution(self, energies_in_kev): """Flat resolution""" ionization_energy_from_nr = self.energy_nr_to_detectable_energy_function() ionization_resolution = self._energy_parameters[self.detector_key]['sigma_ion'] return _get_nr_resolution(energies_in_kev, ionization_energy_from_nr, ionization_resolution)
[docs] def background_function(self, energies_in_kev): """Flat bg rate""" bg_rate_nr = 3300e-6 # counts/kg/keV/year conv_units = 1.0e3 # Tonne return self._flat_background(len(energies_in_kev), bg_rate_nr * conv_units)
[docs]@export class SuperCdmsIzipSiNr(_BaseSuperCdms): detector_name = 'SuperCDMS_iZIP_Si_NR' target_material = 'Si' interaction_type = 'SI' __version__ = '0.0.0' exposure_tonne_year = 4.8 * 1.e-3 # Tonne year energy_threshold_kev = 166. / 1e3 # table VIII, Enr cut_efficiency = 0.75 # p. 11, right column detection_efficiency = 0.85 # p. 11, left column
[docs] def resolution(self, energies_in_kev): """Flat resolution""" ionization_energy_from_nr = self.energy_nr_to_detectable_energy_function() ionization_resolution = self._energy_parameters[self.detector_key]['sigma_ion'] return _get_nr_resolution(energies_in_kev, ionization_energy_from_nr, ionization_resolution)
[docs] def background_function(self, energies_in_kev): """Flat bg rate""" bg_rate_nr = 2900e-6 # counts/kg/keV/year conv_units = 1.0e3 # Tonne return self._flat_background(len(energies_in_kev), bg_rate_nr * conv_units)
[docs]@export class SuperCdmsHvGeMigdal(_BaseSuperCdms): detector_name = 'SuperCDMS_HV_Ge_Migdal' target_material = 'Ge' interaction_type = 'migdal_SI' __version__ = '0.0.0' exposure_tonne_year = 44 * 1.e-3 # Tonne year cut_efficiency = 0.85 # p. 11, right column detection_efficiency = 0.5 # p. 11, left column NOTE: migdal is ER type! @property def energy_threshold_kev(self): return self.get_energy_thr_ee_from_phonon_thr()
[docs] def resolution(self, energies_in_kev): """Flat resolution""" e_res_ee = self.get_energy_res_ee_from_phonon_res() return self._flat_resolution(len(energies_in_kev), e_res_ee)
[docs] def background_function(self, energies_in_kev): """Flat bg rate""" bg_rate_nr = 27 # counts/kg/keV/year conv_units = 1.0e3 # Tonne return self._flat_background(len(energies_in_kev), bg_rate_nr * conv_units)
[docs]@export class SuperCdmsHvSiMigdal(_BaseSuperCdms): detector_name = 'SuperCDMS_HV_Si_Migdal' target_material = 'Si' interaction_type = 'migdal_SI' __version__ = '0.0.0' exposure_tonne_year = 9.6 * 1.e-3 # Tonne year cut_efficiency = 0.85 # p. 11, right column detection_efficiency = 0.675 # p. 11, left column NOTE: migdal is ER type! @property def energy_threshold_kev(self): return self.get_energy_thr_ee_from_phonon_thr()
[docs] def resolution(self, energies_in_kev): """Flat resolution""" e_res_ee = self.get_energy_res_ee_from_phonon_res() return self._flat_resolution(len(energies_in_kev), e_res_ee)
[docs] def background_function(self, energies_in_kev): """Flat bg rate""" bg_rate_nr = 300 # counts/kg/keV/year conv_units = 1.0e3 # Tonne return self._flat_background(len(energies_in_kev), bg_rate_nr * conv_units)
[docs]@export class SuperCdmsIzipGeMigdal(_BaseSuperCdms): detector_name = 'SuperCDMS_iZIP_Ge_Migdal' target_material = 'Ge' interaction_type = 'migdal_SI' __version__ = '0.0.0' exposure_tonne_year = 56 * 1.e-3 # Tonne year cut_efficiency = 0.75 # p. 11, right column detection_efficiency = 0.5 # p. 11, left column NOTE: migdal is ER type! @property def energy_threshold_kev(self): return self.get_energy_thr_ee_from_phonon_thr()
[docs] def resolution(self, energies_in_kev): """Flat resolution""" e_res_ee = self.get_energy_res_ee_from_phonon_res() return self._flat_resolution(len(energies_in_kev), e_res_ee)
[docs] def background_function(self, energies_in_kev): """Flat bg rate""" bg_rate_nr = 22 # counts/kg/keV/year conv_units = 1.0e3 # Tonne return self._flat_background(len(energies_in_kev), bg_rate_nr * conv_units)
[docs]@export class SuperCdmsIzipSiMigdal(_BaseSuperCdms): detector_name = 'SuperCDMS_iZIP_Si_Migdal' target_material = 'Si' interaction_type = 'migdal_SI' __version__ = '0.0.0' exposure_tonne_year = 4.8 * 1.e-3 # Tonne year cut_efficiency = 0.75 # p. 11, right column detection_efficiency = 0.675 # p. 11, left column NOTE: migdal is ER type! @property def energy_threshold_kev(self): return self.get_energy_thr_ee_from_phonon_thr()
[docs] def resolution(self, energies_in_kev): """Flat resolution""" e_res_ee = self.get_energy_res_ee_from_phonon_res() return self._flat_resolution(len(energies_in_kev), e_res_ee)
[docs] def background_function(self, energies_in_kev): """Flat bg rate""" bg_rate_nr = 370 # counts/kg/keV/year conv_units = 1.0e3 # Tonne return self._flat_background(len(energies_in_kev), bg_rate_nr * conv_units)
def energy_ee_from_energy_phonon(e_ph, e_delta_v, epsilon): """Eq. 4 in https://arxiv.org/abs/1610.00006 rewritten to ee (`y`=1) and `eta`=1""" return e_ph / (1 + e_delta_v / epsilon) def energy_phonon_from_energy_nr(e_r_nr, Z, k, e_delta_v, epsilon, c0, c1, U): y = lindhard_quenching_factor_semi_conductors(e_r_nr, atomic_number_z=Z, k=k, c0=c0, c1=c1, U=U) if not isinstance(y, np.ndarray): raise ValueError return e_r_nr * (1 + y * (e_delta_v / epsilon)) def energy_ionization_from_e_nr(e_r_nr, Z, k, c0, c1, U): y = lindhard_quenching_factor_semi_conductors(e_r_nr, atomic_number_z=Z, k=k, c0=c0, c1=c1, U=U) if not isinstance(y, np.ndarray): raise ValueError return e_r_nr * y