Module pmt_analysis.analysis.dark_counts
Expand source code
import numpy as np
import warnings
class DarkCountRate:
"""Class for dark count rate estimations through search for random dark counts above threshold in waveforms.
Attributes:
amplitudes: Array with maximum data pulse amplitudes per waveform.
threshold: Amplitude threshold to consider pulse as dark count. Usually set to amplitude values
corresponding to pulses of 0.25 PE or 0.5 PE area, deduced from an approximately linear
readout-specific dependence between amplitude and area.
samples_per_wf: Samples per waveform used for amplitude determination.
adc_f: ADC sampling frequency in samples per second.
"""
def __init__(self, amplitudes: np.ndarray, threshold: float,
samples_per_wf: int, adc_f: float = 500e6):
"""Init of the DarkCountRate class.
Args:
amplitudes: Array with maximum data pulse amplitudes per waveform.
threshold: Amplitude threshold to consider pulse as dark count. Usually set to amplitude values
corresponding to pulses of 0.25 PE or 0.5 PE area, deduced from an approximately linear
readout-specific dependence between amplitude and area.
samples_per_wf: Samples per waveform used for amplitude determination.
adc_f: ADC sampling frequency in samples per second as provided by same attribute of
`pmt_analysis.utils.input.ADCRawData`. Default: 500e6
(ADC sampling frequency of 500 MS/s for CAEN v1730d).
"""
if threshold < 0:
threshold = - threshold
warnings.warn('Amplitudes are defined to be positive, the passed threshold value is hence '
'converted to a positive float.')
self.amplitudes = amplitudes
self.threshold = threshold
self.samples_per_wf = samples_per_wf
self.adc_f = adc_f
@staticmethod
def correction_poisson(fraction_above_thr: float) -> float:
"""Poisson-correction for dark count rates where the probability of more than one dark count per
wave function is not negligible.
As `fraction_above_thr` represents an estimator for the probability of at least one dark count per waveform,
the mean probability of a dark count in a waveform can be calculated assuming Poisson statistics.
Using the discrete probability distribution
.. math::
m = P(k > 0, \lambda) = 1 - P(k = 0, \lambda) = 1 - \lambda^0/0! \cdot e^{-\lambda} = 1 - e^{-\lambda}
one obtains the corrected dark count probability as
.. math::
\lambda = -ln(1-m).
Args:
fraction_above_thr: Fraction of waveforms with amplitude above threshold, hence assumed to
contain at least one dark count.
Returns:
dc_prob_per_wf: Dark count probability per waveform.
"""
if (fraction_above_thr >= 1) or (fraction_above_thr < 0):
raise ValueError('Argument fraction_above_thr must be between 0 and 1.')
dc_prob_per_wf = -np.log(1 - fraction_above_thr)
return dc_prob_per_wf
def compute(self) -> tuple:
"""Compute dark count rate and corresponding uncertainty in units of dark counts / second.
Returns:
dc_rate: Dark count rate in units of dark counts / second.
dc_rate_unc: Dark count rate uncertainty in units of dark counts / second.
"""
# Fraction of waveforms with amplitude above threshold
fraction_above_thr = float(np.mean(self.amplitudes > self.threshold))
# Assume Poisson uncertainty sqrt(n) on counts above threshold
fraction_above_thr_unc = np.sqrt(fraction_above_thr/self.amplitudes.shape[0])
# Poisson-correction for non-negligible multiple dark count probability per waveform
dc_prob_per_wf = self.correction_poisson(fraction_above_thr)
# Error propagation with d(-ln(1-x))/dx = 1/(1-x)
dc_prob_per_wf_unc = fraction_above_thr_unc/(1-fraction_above_thr)
# Temporal width per waveform from number of samples and sampling frequency
wf_time_width = self.samples_per_wf / self.adc_f
# Dark count rate and uncertainty
dc_rate = dc_prob_per_wf / wf_time_width
dc_rate_unc = dc_prob_per_wf_unc / wf_time_width
return dc_rate, dc_rate_unc
Classes
class DarkCountRate (amplitudes: numpy.ndarray, threshold: float, samples_per_wf: int, adc_f: float = 500000000.0)
-
Class for dark count rate estimations through search for random dark counts above threshold in waveforms.
Attributes
amplitudes
- Array with maximum data pulse amplitudes per waveform.
threshold
- Amplitude threshold to consider pulse as dark count. Usually set to amplitude values corresponding to pulses of 0.25 PE or 0.5 PE area, deduced from an approximately linear readout-specific dependence between amplitude and area.
samples_per_wf
- Samples per waveform used for amplitude determination.
adc_f
- ADC sampling frequency in samples per second.
Init of the DarkCountRate class.
Args
amplitudes
- Array with maximum data pulse amplitudes per waveform.
threshold
- Amplitude threshold to consider pulse as dark count. Usually set to amplitude values corresponding to pulses of 0.25 PE or 0.5 PE area, deduced from an approximately linear readout-specific dependence between amplitude and area.
samples_per_wf
- Samples per waveform used for amplitude determination.
adc_f
- ADC sampling frequency in samples per second as provided by same attribute of
ADCRawData
. Default: 500e6 (ADC sampling frequency of 500 MS/s for CAEN v1730d).
Expand source code
class DarkCountRate: """Class for dark count rate estimations through search for random dark counts above threshold in waveforms. Attributes: amplitudes: Array with maximum data pulse amplitudes per waveform. threshold: Amplitude threshold to consider pulse as dark count. Usually set to amplitude values corresponding to pulses of 0.25 PE or 0.5 PE area, deduced from an approximately linear readout-specific dependence between amplitude and area. samples_per_wf: Samples per waveform used for amplitude determination. adc_f: ADC sampling frequency in samples per second. """ def __init__(self, amplitudes: np.ndarray, threshold: float, samples_per_wf: int, adc_f: float = 500e6): """Init of the DarkCountRate class. Args: amplitudes: Array with maximum data pulse amplitudes per waveform. threshold: Amplitude threshold to consider pulse as dark count. Usually set to amplitude values corresponding to pulses of 0.25 PE or 0.5 PE area, deduced from an approximately linear readout-specific dependence between amplitude and area. samples_per_wf: Samples per waveform used for amplitude determination. adc_f: ADC sampling frequency in samples per second as provided by same attribute of `pmt_analysis.utils.input.ADCRawData`. Default: 500e6 (ADC sampling frequency of 500 MS/s for CAEN v1730d). """ if threshold < 0: threshold = - threshold warnings.warn('Amplitudes are defined to be positive, the passed threshold value is hence ' 'converted to a positive float.') self.amplitudes = amplitudes self.threshold = threshold self.samples_per_wf = samples_per_wf self.adc_f = adc_f @staticmethod def correction_poisson(fraction_above_thr: float) -> float: """Poisson-correction for dark count rates where the probability of more than one dark count per wave function is not negligible. As `fraction_above_thr` represents an estimator for the probability of at least one dark count per waveform, the mean probability of a dark count in a waveform can be calculated assuming Poisson statistics. Using the discrete probability distribution .. math:: m = P(k > 0, \lambda) = 1 - P(k = 0, \lambda) = 1 - \lambda^0/0! \cdot e^{-\lambda} = 1 - e^{-\lambda} one obtains the corrected dark count probability as .. math:: \lambda = -ln(1-m). Args: fraction_above_thr: Fraction of waveforms with amplitude above threshold, hence assumed to contain at least one dark count. Returns: dc_prob_per_wf: Dark count probability per waveform. """ if (fraction_above_thr >= 1) or (fraction_above_thr < 0): raise ValueError('Argument fraction_above_thr must be between 0 and 1.') dc_prob_per_wf = -np.log(1 - fraction_above_thr) return dc_prob_per_wf def compute(self) -> tuple: """Compute dark count rate and corresponding uncertainty in units of dark counts / second. Returns: dc_rate: Dark count rate in units of dark counts / second. dc_rate_unc: Dark count rate uncertainty in units of dark counts / second. """ # Fraction of waveforms with amplitude above threshold fraction_above_thr = float(np.mean(self.amplitudes > self.threshold)) # Assume Poisson uncertainty sqrt(n) on counts above threshold fraction_above_thr_unc = np.sqrt(fraction_above_thr/self.amplitudes.shape[0]) # Poisson-correction for non-negligible multiple dark count probability per waveform dc_prob_per_wf = self.correction_poisson(fraction_above_thr) # Error propagation with d(-ln(1-x))/dx = 1/(1-x) dc_prob_per_wf_unc = fraction_above_thr_unc/(1-fraction_above_thr) # Temporal width per waveform from number of samples and sampling frequency wf_time_width = self.samples_per_wf / self.adc_f # Dark count rate and uncertainty dc_rate = dc_prob_per_wf / wf_time_width dc_rate_unc = dc_prob_per_wf_unc / wf_time_width return dc_rate, dc_rate_unc
Static methods
def correction_poisson(fraction_above_thr: float) ‑> float
-
Poisson-correction for dark count rates where the probability of more than one dark count per wave function is not negligible.
As
fraction_above_thr
represents an estimator for the probability of at least one dark count per waveform, the mean probability of a dark count in a waveform can be calculated assuming Poisson statistics. Using the discrete probability distribution[ m = P(k > 0, \lambda) = 1 - P(k = 0, \lambda) = 1 - \lambda^0/0! \cdot e^{-\lambda} = 1 - e^{-\lambda} ] one obtains the corrected dark count probability as
[ \lambda = -ln(1-m). ]
Args
fraction_above_thr
- Fraction of waveforms with amplitude above threshold, hence assumed to contain at least one dark count.
Returns
dc_prob_per_wf
- Dark count probability per waveform.
Expand source code
@staticmethod def correction_poisson(fraction_above_thr: float) -> float: """Poisson-correction for dark count rates where the probability of more than one dark count per wave function is not negligible. As `fraction_above_thr` represents an estimator for the probability of at least one dark count per waveform, the mean probability of a dark count in a waveform can be calculated assuming Poisson statistics. Using the discrete probability distribution .. math:: m = P(k > 0, \lambda) = 1 - P(k = 0, \lambda) = 1 - \lambda^0/0! \cdot e^{-\lambda} = 1 - e^{-\lambda} one obtains the corrected dark count probability as .. math:: \lambda = -ln(1-m). Args: fraction_above_thr: Fraction of waveforms with amplitude above threshold, hence assumed to contain at least one dark count. Returns: dc_prob_per_wf: Dark count probability per waveform. """ if (fraction_above_thr >= 1) or (fraction_above_thr < 0): raise ValueError('Argument fraction_above_thr must be between 0 and 1.') dc_prob_per_wf = -np.log(1 - fraction_above_thr) return dc_prob_per_wf
Methods
def compute(self) ‑> tuple
-
Compute dark count rate and corresponding uncertainty in units of dark counts / second.
Returns
dc_rate
- Dark count rate in units of dark counts / second.
dc_rate_unc
- Dark count rate uncertainty in units of dark counts / second.
Expand source code
def compute(self) -> tuple: """Compute dark count rate and corresponding uncertainty in units of dark counts / second. Returns: dc_rate: Dark count rate in units of dark counts / second. dc_rate_unc: Dark count rate uncertainty in units of dark counts / second. """ # Fraction of waveforms with amplitude above threshold fraction_above_thr = float(np.mean(self.amplitudes > self.threshold)) # Assume Poisson uncertainty sqrt(n) on counts above threshold fraction_above_thr_unc = np.sqrt(fraction_above_thr/self.amplitudes.shape[0]) # Poisson-correction for non-negligible multiple dark count probability per waveform dc_prob_per_wf = self.correction_poisson(fraction_above_thr) # Error propagation with d(-ln(1-x))/dx = 1/(1-x) dc_prob_per_wf_unc = fraction_above_thr_unc/(1-fraction_above_thr) # Temporal width per waveform from number of samples and sampling frequency wf_time_width = self.samples_per_wf / self.adc_f # Dark count rate and uncertainty dc_rate = dc_prob_per_wf / wf_time_width dc_rate_unc = dc_prob_per_wf_unc / wf_time_width return dc_rate, dc_rate_unc