import numpy as np
from scipy.special import erf
from scipy.optimize import minimize, curve_fit
from scipy.stats import norm, expon
import matplotlib.pyplot as plt
from ..styles import use_cait_style, make_grid
# ------------------------------------------
# functions for binned least squares gaussian fit
# ------------------------------------------
[docs]def noise_trigger_template(x_max, d, sigma):
"""
A template for purely Gaussian noise.
:param x_max: The maxima of the empty noise baselines.
:type x_max: 1D array
:param d: The number of independent samples.
:type d: float
:param sigma: The baseline resolution.
:type sigma: float
:return: The template.
:rtype: 1D array
"""
P = (d / np.sqrt(2 * np.pi) / sigma)
P *= np.exp(-(x_max / np.sqrt(2) / sigma) ** 2)
P *= (1 / 2 + erf(x_max / np.sqrt(2) / sigma) / 2) ** (d - 1)
return P
def wrapper(x_max, d, sigma):
sigma_lower_bound = 0.0001
if sigma < sigma_lower_bound:
P = 1000 * np.abs(sigma_lower_bound - sigma) + 1000
else:
P = noise_trigger_template(x_max, d, sigma)
return P
[docs]def get_noise_parameters_binned(counts,
bins,
):
"""
Return the least squares fit parameters to the purely Gaussian noise model. You need to calculate a histogram of
the maxima of the empty baselines before already, e.g. with np.hist.
:param counts: The counts within the bins.
:type counts: 1D array
:param bins: The bin edges. This array is one number longer than the counts array.
:type bins: 1D array
:return: The fitted parameters (d, sigma).
:rtype: 2-tuple
"""
x_data = bins[:-1] + (bins[1] - bins[0]) / 2
ydata = counts
pars, _ = curve_fit(f=wrapper,
xdata=x_data,
ydata=ydata,
check_finite=True,
)
print('Fitted Noise Trigger Template Parameters: d {}, sigma {:.3} mV'.format(pars[0], pars[1]))
return pars
# ------------------------------------------
# utils functions for unbinned fit
# ------------------------------------------
def gauss_noise(x_max, d, sigma):
"""
A template for purely Gaussian noise. Opposite to noise_trigger_template, this function uses the scipy
implementation of the norm distribution.
:param x_max: The maxima of the empty noise baselines.
:type x_max: 1D array
:param d: The number of independent samples.
:type d: float
:param sigma: The baseline resolution
:type sigma: float
:return: The template.
:rtype: 1D array
"""
P = d * norm.pdf(x_max, loc=0, scale=sigma) * norm.cdf(x_max, loc=0, scale=sigma) ** (d - 1)
P[P < 10e-8] = 10e-8
return P
def pollution_exponential_noise(x_max, d, sigma, lamb):
"""
A template for purely Gaussian noise with a pollution that follows an exponential distribution.
:param x_max: The maxima of the empty noise baselines.
:type x_max: 1D array
:param d: The number of independent samples.
:type d: float
:param sigma: The baseline resolution
:type sigma: float
:param lamb: The rate parameter of the exponential distribution.
:type lamb: float
:return: The template.
:rtype: 1D array
"""
P = expon.pdf(x_max, scale=1 / lamb) * norm.cdf(x_max, loc=0, scale=sigma) ** (d - 1)
P += expon.cdf(x_max, scale=1 / lamb) * (d - 1) * norm.pdf(x_max, loc=0, scale=sigma) * norm.cdf(x_max, loc=0,
scale=sigma) ** (
d - 2)
P[P < 10e-10] = 10e-10
return P
def fraction_exponential_noise(x_max, d, sigma, lamb, w):
"""
A template for a Gaussian-exponential noise mixture.
:param x_max: The maxima of the empty noise baselines.
:type x_max: 1D array
:param d: The number of independent samples.
:type d: float
:param sigma: The baseline resolution.
:type sigma: float
:param lamb: The rate parameter of the exponential distribution.
:type lamb: float
:param w: The weight of the Gaussian fraction.
:type w: float
:return: The template.
:rtype: 1D array
"""
P = d * (w * norm.pdf(x_max, loc=0, scale=sigma) + (1 - w) * expon.pdf(x_max, scale=1 / lamb)) * \
(w * norm.cdf(x_max, loc=0, scale=sigma) + (1 - w) * expon.cdf(x_max, scale=1 / lamb)) ** (d - 1)
P[P < 10e-8] = 10e-8
return P
def pollution_gauss_noise(x_max, d, sigma, mu, sigma_2):
"""
A template for purely Gaussian noise with a pollution that follows another Gaussian distribution.
:param x_max: The maxima of the empty noise baselines.
:type x_max: 1D array
:param d: The number of independent samples.
:type d: float
:param sigma: The baseline resolution
:type sigma: float
:param mu: The mean of the Gaussian pollution.
:type mu: float
:param sigma_2: The standard deviation of the Gaussian pollution.
:type sigma_2: float
:return: The template.
:rtype: 1D array
"""
P = norm.pdf(x_max, loc=mu, scale=sigma_2) * norm.cdf(x_max, loc=0, scale=sigma) ** (d - 1)
P += (d - 1) * norm.cdf(x_max, loc=mu, scale=sigma_2) * norm.pdf(x_max, loc=0, scale=sigma) * norm.cdf(x_max, loc=0,
scale=sigma) ** (
d - 2)
P[P < 10e-8] = 10e-8
return P
def fraction_gauss_noise(x_max, d, sigma, mu, sigma_2, w):
"""
A template for a two component Gaussian noise mixture.
:param x_max: The maxima of the empty noise baselines.
:type x_max: 1D array
:param d: The number of independent samples.
:type d: float
:param sigma: The baseline resolution.
:type sigma: float
:param mu: The mean of the second component.
:type mu: float
:param sigma: The standard deviation of the second component.
:type sigma: float
:param w: The weight of the first Gaussian fraction.
:type w: float
:return: The template.
:rtype: 1D array
"""
P = d * (w * norm.pdf(x_max, loc=0, scale=sigma) + (1 - w) * norm.pdf(x_max, loc=mu, scale=sigma_2)) * \
(w * norm.cdf(x_max, loc=0, scale=sigma) + (1 - w) * norm.cdf(x_max, loc=mu, scale=sigma_2)) ** (d - 1)
P[P < 10e-8] = 10e-8
return P
# ------------------------------------------
# only gauss components
# ------------------------------------------
def pollution_exponential_noise_gauss_comp(x_max, d, sigma, lamb):
"""
A partial template for purely Gaussian noise with a pollution that follows an exponential distribution. Only the
Gaussian component is returned.
:param x_max: The maxima of the empty noise baselines.
:type x_max: 1D array
:param d: The number of independent samples.
:type d: float
:param sigma: The baseline resolution
:type sigma: float
:param lamb: The rate parameter of the exponential distribution.
:type lamb: float
:return: The template of the Gaussian component.
:rtype: 1D array
"""
P = expon.cdf(x_max, scale=1 / lamb) * (d - 1) * norm.pdf(x_max, loc=0, scale=sigma) * norm.cdf(x_max, loc=0,
scale=sigma) ** (
d - 2)
return P
def fraction_exponential_noise_gauss_comp(x_max, d, sigma, lamb, w):
"""
A partial template for a Gaussian-exponential noise mixture. Only the
Gaussian component is returned.
:param x_max: The maxima of the empty noise baselines.
:type x_max: 1D array
:param d: The number of independent samples.
:type d: float
:param sigma: The baseline resolution.
:type sigma: float
:param lamb: The rate parameter of the exponential distribution.
:type lamb: float
:param w: The weight of the Gaussian fraction.
:type w: float
:return: The template of the Gaussian component.
:rtype: 1D array
"""
P = d * (w * norm.pdf(x_max, loc=0, scale=sigma)) * \
(w * norm.cdf(x_max, loc=0, scale=sigma) + (1 - w) * expon.cdf(x_max, scale=1 / lamb)) ** (d - 1)
return P
def pollution_gauss_noise_gauss_comp(x_max, d, sigma, mu, sigma_2):
"""
A partial template for purely Gaussian noise with a pollution that follows another Gaussian distribution. Only the
Gaussian component is returned.
:param x_max: The maxima of the empty noise baselines.
:type x_max: 1D array
:param d: The number of independent samples.
:type d: float
:param sigma: The baseline resolution
:type sigma: float
:param mu: The mean of the Gaussian pollution.
:type mu: float
:param sigma_2: The standard deviation of the Gaussian pollution.
:type sigma_2: float
:return: The template of the Gaussian part.
:rtype: 1D array
"""
P = (d - 1) * norm.cdf(x_max, loc=mu, scale=sigma_2) * norm.pdf(x_max, loc=0, scale=sigma) * norm.cdf(x_max, loc=0,
scale=sigma) ** (
d - 2)
return P
def fraction_gauss_noise_gauss_comp(x_max, d, sigma, mu, sigma_2, w):
"""
A partial template for a two component Gaussian noise mixture. Only the
first Gaussian component is returned.
:param x_max: The maxima of the empty noise baselines.
:type x_max: 1D array
:param d: The number of independent samples.
:type d: float
:param sigma: The baseline resolution.
:type sigma: float
:param mu: The mean of the second component.
:type mu: float
:param sigma: The standard deviation of the second component.
:type sigma: float
:param w: The weight of the first Gaussian fraction.
:type w: float
:return: The template of the first Gaussian component.
:rtype: 1D array
"""
P = d * (w * norm.pdf(x_max, loc=0, scale=sigma)) * \
(w * norm.cdf(x_max, loc=0, scale=sigma) + (1 - w) * norm.cdf(x_max, loc=mu, scale=sigma_2)) ** (d - 1)
return P
# ------------------------------------------
# only pollution components
# ------------------------------------------
def pollution_exponential_noise_poll_comp(x_max, d, sigma, lamb):
"""
A partial template for purely Gaussian noise with a pollution that follows an exponential distribution. Only the
polluting component is returned.
:param x_max: The maxima of the empty noise baselines.
:type x_max: 1D array
:param d: The number of independent samples.
:type d: float
:param sigma: The baseline resolution
:type sigma: float
:param lamb: The rate parameter of the exponential distribution.
:type lamb: float
:return: The template of the pollution.
:rtype: 1D array
"""
P = expon.pdf(x_max, scale=1 / lamb) * norm.cdf(x_max, loc=0, scale=sigma) ** (d - 1)
return P
def fraction_exponential_noise_poll_comp(x_max, d, sigma, lamb, w):
"""
A partial template for a Gaussian-exponential noise mixture. Only the
exponential component is returned.
:param x_max: The maxima of the empty noise baselines.
:type x_max: 1D array
:param d: The number of independent samples.
:type d: float
:param sigma: The baseline resolution.
:type sigma: float
:param lamb: The rate parameter of the exponential distribution.
:type lamb: float
:param w: The weight of the Gaussian fraction.
:type w: float
:return: The template of the exponential component.
:rtype: 1D array
"""
P = d * ((1 - w) * expon.pdf(x_max, scale=1 / lamb)) * \
(w * norm.cdf(x_max, loc=0, scale=sigma) + (1 - w) * expon.cdf(x_max, scale=1 / lamb)) ** (d - 1)
return P
def pollution_gauss_noise_poll_comp(x_max, d, sigma, mu, sigma_2):
"""
A partial template for purely Gaussian noise with a pollution that follows another Gaussian distribution. Only the
polluting component is returned.
:param x_max: The maxima of the empty noise baselines.
:type x_max: 1D array
:param d: The number of independent samples.
:type d: float
:param sigma: The baseline resolution
:type sigma: float
:param mu: The mean of the Gaussian pollution.
:type mu: float
:param sigma_2: The standard deviation of the Gaussian pollution.
:type sigma_2: float
:return: The template of the pollution.
:rtype: 1D array
"""
P = norm.pdf(x_max, loc=mu, scale=sigma_2) * norm.cdf(x_max, loc=0, scale=sigma) ** (d - 1)
return P
def fraction_gauss_noise_poll_comp(x_max, d, sigma, mu, sigma_2, w):
"""
A partial template for a two component Gaussian noise mixture. Only the
second Gaussian component is returned.
:param x_max: The maxima of the empty noise baselines.
:type x_max: 1D array
:param d: The number of independent samples.
:type d: float
:param sigma: The baseline resolution.
:type sigma: float
:param mu: The mean of the second component.
:type mu: float
:param sigma: The standard deviation of the second component.
:type sigma: float
:param w: The weight of the first Gaussian fraction.
:type w: float
:return: The template of the second Gaussian component.
:rtype: 1D array
"""
P = d * ((1 - w) * norm.pdf(x_max, loc=mu, scale=sigma_2)) * \
(w * norm.cdf(x_max, loc=0, scale=sigma) + (1 - w) * norm.cdf(x_max, loc=mu, scale=sigma_2)) ** (d - 1)
return P
# ------------------------------------------
# negative likelihood functions for unbinned fit
# ------------------------------------------
def nll_gauss(pars, x):
d, sigma = pars
if sigma < 1e-5:
return 10e6 + 10e3 * np.abs(sigma - 1e-5)
elif d < 1:
return 10e6 + 10e3 * np.abs(d - 1)
else:
return -np.sum(np.log(gauss_noise(x, d, sigma)))
def nll_pollution_exponential(pars, x):
d, sigma, lamb = pars
if sigma < 1e-5:
return 10e6 + 10e3 * np.abs(sigma - 1e-5)
elif d < 1:
return 10e6 + 10e3 * np.abs(d - 1)
else:
return -np.sum(np.log(pollution_exponential_noise(x, d, sigma, lamb)))
def nll_fraction_exponential(pars, x):
d, sigma, lamb, w = pars
if sigma < 1e-5:
return 10e6 + 10e3 * np.abs(sigma - 1e-5)
elif d < 1:
return 10e6 + 10e3 * np.abs(d - 1)
elif w < 0 or w > 1:
return 10e6
else:
return -np.sum(np.log(fraction_exponential_noise(x, d, sigma, lamb, w)))
def nll_pollution_gauss(pars, x):
d, sigma, mu, sigma_2 = pars
if sigma < 1e-5:
return 10e6 + 10e3 * np.abs(sigma - 1e-5)
elif d < 1:
return 10e6 + 10e3 * np.abs(d - 1)
elif sigma_2 < 1e-5:
return 10e6 + 10e3 * np.abs(sigma_2 - 1e-5)
else:
return -np.sum(np.log(pollution_gauss_noise(x, d, sigma, mu, sigma_2)))
def nll_fraction_gauss(pars, x):
d, sigma, mu, sigma_2, w = pars
if sigma < 1e-5:
return 10e6 * 10e3 * np.abs(sigma - 1e-5)
elif d < 1:
return 10e6 + 10e3 * np.abs(d - 1)
elif sigma_2 < 1e-5:
return 10e6 * 10e3 * np.abs(sigma_2 - 1e-5)
elif w < 0 or w > 1:
return 10e6
else:
return -np.sum(np.log(fraction_gauss_noise(x, d, sigma, mu, sigma_2, w)))
# ------------------------------------------
# actual function for the fit
# ------------------------------------------
[docs]def get_noise_parameters_unbinned(events,
model='gauss',
sigma_x0=2,
):
"""
Find the maximum likelihood estimators of all noise trigger model parameters in an unbinned maximum likelihood fit.
:param events: The array of the unbinned noise baseline maxima.
:type events: 1D array
:param model: The model that is used to fit the noise maxima.
- 'gauss': Purely Gaussian noise model.
- 'pollution_exponential': Gaussian noise model with an exponentially distributed pollution.
- 'fraction_exponential': Gaussian-exponential mixture noise model.
- 'pollution_gauss': Gaussian noise model with an Gaussian distributed pollution.
- 'fraction_gauss': Gaussian mixture noise model.
:type model: string
:param sigma_x0: The start value for the baseline resolution.
:type sigma_x0: float
:return: The fitted parameters. These are different for each model and compatible with the functions gauss_noise,
pollution_exponential_noise, fraction_exponential_noise and pollution_gauss_noise.
:rtype: 1D array
"""
if model == 'gauss':
# d, sigma
minimizer = nll_gauss
x0 = np.array([400, sigma_x0])
elif model == 'pollution_exponential':
# d, sigma, lamb
minimizer = nll_pollution_exponential
x0 = np.array([400, sigma_x0, 2])
elif model == 'fraction_exponential':
# d, sigma, lamb, w
minimizer = nll_fraction_exponential
x0 = np.array([400, sigma_x0, 2, 0.9])
elif model == 'pollution_gauss':
# d, sigma, mu, sigma_2
minimizer = nll_pollution_gauss
x0 = np.array([400, sigma_x0, 0, 2])
elif model == 'fraction_gauss':
# d, sigma, mu, sigma_2, w
minimizer = nll_fraction_gauss
x0 = np.array([400, sigma_x0, 0, 2, 0.5])
else:
raise NotImplementedError('This model is not implemented!')
res = minimize(minimizer,
args=(events,),
x0=x0,
method='nelder-mead')
print('Independent samples d: ', res.x[0])
print('Baseline resolution sigma (mV): ', res.x[1])
if model == 'pollution_exponential':
print('Exponential pollution rate parameter lambda (1/mV): ', res.x[2])
if model == 'fraction_exponential':
print('Exponential fraction rate parameter lambda (1/mV): ', res.x[2])
print('Weight gaussian baseline parameter w: ', res.x[3])
if model == 'pollution_gauss':
print('Gaussian pollution mean parameter mu (mV): ', res.x[2])
print('Gaussian pollution standard deviation parameter sigma_2 (mV): ', res.x[3])
if model == 'fraction_gauss':
print('Gaussian fraction mean parameter mu (mV): ', res.x[2])
print('Gaussian fraction standard deviation parameter sigma_2 (mV): ', res.x[3])
print('Weight gaussian baseline parameter w: ', res.x[4])
return res.x
# ------------------------------------------
# plot
# ------------------------------------------
def plot_noise_trigger_model(bins_hist,
counts_hist,
x_grid,
trigger_window,
ph_distribution,
xran_hist,
noise_trigger_rate,
polluted_trigger_rate,
threshold,
polluted_ph_distribution,
nmbr_pollution_triggers,
model='gauss',
title=None,
yran=None,
allowed_noise_triggers=1,
xran=None,
ylog=False,
only_histogram=False,
save_path=None,
):
"""
Plot the noise trigger model.
:param bins_hist: The bins edges of the noise maxima histogram. This array is one number longer than the counts_hist
array.
:type bins_hist: 1D array
:param counts_hist: The counts of all histogram bins.
:type counts_hist: 1D array
:param x_grid: The grid to evaluate the noise trigger models on.
:type x_grid: 1D array
:param trigger_window: The exposure of the trigger window, in kg days.
:type trigger_window: float
:param ph_distribution: The evaluated noise template on x_grid, normalized to the exposure.
:type ph_distribution: 1D array
:param xran_hist: The range of the x axis on the histogram plot.
:type xran_hist: tuple of two floats
:param noise_trigger_rate: The number of noise triggers, depending on the threshold, given by the x_grid.
:type noise_trigger_rate: 1D array
:param polluted_trigger_rate: The number of pollution triggers, depending on the threshold, given by the x_grid.
:type polluted_trigger_rate: 1D array
:param threshold: The threshold in mV.
:type threshold: float
:param polluted_ph_distribution: The polluted component of the evaluated noise template on x_grid,
normalized to the exposure.
:type polluted_ph_distribution: 1D array
:param nmbr_pollution_triggers: The number of pollution triggers.
:type nmbr_pollution_triggers: float
:param model: Which model was fit to the noise.
- 'gauss': Model of purely Gaussian noise.
- 'pollution_exponential': Model of Gaussian noise with one exponentially distributed sample on each baseline.
- 'fraction_exponential': Mixture model of Gaussian and exponentially distributed noise.
- 'pollution_gauss': Model of Gaussian noise and one sample in each baseline that follows another, also Gaussian distribution.
- 'fraction_gauss': Mixture model of two Gaussian noise components.
:param title: A title for both plots.
:type title: string
:param yran: The range of the y axis on both plots.
:type yran: tuple of two floats
:param allowed_noise_triggers: The number of noise triggers that are allowed per kg day exposure.
:type allowed_noise_triggers: float
:param xran: The range of the x axis on the noise trigger estimation plot.
:type xran: tuple of two floats
:param ylog: If set, the y axis is plotted logarithmically on the histogram plot.
:type ylog: bool
:param only_histogram: Plot only the histogram, not the noise trigger model.
:type only_histogram: bool
:param save_path: A path to save the plots.
:type save_path: string
"""
# plot the counts
plt.close()
use_cait_style()
xdata = bins_hist[:-1] + (bins_hist[1] - bins_hist[0]) / 2
plt.hist(xdata, bins_hist, weights=counts_hist / trigger_window,
zorder=8, alpha=0.8, label='Counts')
if model == 'pollution_exponential' or model == 'pollution_gauss':
plt.plot(x_grid, ph_distribution, linewidth=2, zorder=12, color='C2', label='Gaussian Component')
plt.plot(x_grid, polluted_ph_distribution, linewidth=2, zorder=12, color='yellow', label='Pollution Component')
plt.plot(x_grid, ph_distribution + polluted_ph_distribution, linewidth=2, zorder=14, color='black',
label='Cumulative Model')
else:
plt.plot(x_grid, ph_distribution, linewidth=2, zorder=12, color='black', label='Fit')
make_grid()
if title is not None:
plt.title(title)
if xran_hist is not None:
plt.xlim(xran_hist)
if yran is not None:
plt.ylim(yran)
else:
plt.ylim(bottom=0.1)
if ylog:
plt.yscale('log')
plt.ylim(bottom=np.min(counts_hist[counts_hist > 0] / trigger_window) / 10)
plt.legend()
plt.xlabel('Pulse Height (mV)')
plt.ylabel('Counts (1 / kg days mV)')
if save_path is not None:
name = save_path + '_counts.pdf'
plt.savefig(name)
plt.show()
if not only_histogram:
# plot the threshold
plt.close()
use_cait_style()
plt.plot(x_grid, noise_trigger_rate, linewidth=2, zorder=16, color='black', label='Noise Triggers')
if model == 'pollution_exponential' or model == 'pollution_gauss':
plt.plot(x_grid, polluted_trigger_rate, linewidth=2, zorder=17, color='grey', linestyle='dotted',
label='Pollution Triggers')
plt.vlines(x=threshold, ymin=0.01, ymax=allowed_noise_triggers, color='tab:red',
linewidth=2, zorder=20, label='{} / kg days'.format(allowed_noise_triggers))
plt.hlines(y=allowed_noise_triggers, xmin=0, xmax=threshold, color='tab:red',
linewidth=2, zorder=20)
if model == 'pollution_exponential' or model == 'pollution_gauss':
plt.vlines(x=threshold, ymin=0.01, ymax=nmbr_pollution_triggers, linestyles='dotted', color='tab:red',
linewidth=2, zorder=21, label='{:.2f} / kg days'.format(nmbr_pollution_triggers))
plt.hlines(y=nmbr_pollution_triggers, xmin=0, xmax=threshold, linestyles='dotted', color='tab:red',
linewidth=2, zorder=21)
make_grid()
if yran is None:
plt.ylim(bottom=0.1)
else:
plt.ylim(yran)
if xran is not None:
plt.xlim(xran)
plt.yscale('log')
if title is not None:
plt.title(title)
plt.legend()
plt.xlabel('Threshold (mV)')
plt.ylabel('Noise Trigger Rate (1 / kg days)')
if save_path is not None:
name = save_path + '_ntr.pdf'
plt.savefig(name)
plt.show()
# ------------------------------------------
# calc the threshold
# ------------------------------------------
def calc_threshold(record_length, sample_length, detector_mass, interval_restriction, ul, ll, model,
pars, allowed_noise_triggers):
"""
Calculate the treshold for given noise baseline maxima, to obtain a defined number of noise triggers.
This method was described in "M. Mancuso et. al., A method to define the energy threshold depending on
noise level for rare event searches" (doi 10.1016/j.nima.2019.06.030).
:param record_length: The number of samples in a record window.
:type record_length: int
:param sample_length: The length of a sample in seconds.
:type sample_length: float
:param detector_mass: The mass of the detector in kg.
:type detector_mass: float
:param interval_restriction: A value between 0 and 1. Only this share of the trigger window is used in the maximum
search. This is typically 0.75 if filters are applied, to avoid border effects.
:type interval_restriction: float
:param ul: The upper limit of the interval that is used to search a threshold, in mV.
:type ul: float
:param ll: The lower limit of the interval that is used to search a threshold, in mV.
:type ll: float
:param model: Determine which model is fit to the noise.
- 'gauss': Model of purely Gaussian noise.
- 'pollution_exponential': Model of Gaussian noise with one exponentially distributed sample on each baseline.
- 'fraction_exponential': Mixture model of Gaussian and exponentially distributed noise.
- 'pollution_gauss': Model of Gaussian noise and one sample in each baseline that follows another, also Gaussian distribution.
- 'fraction_gauss': Mixture model of two Gaussian noise components.
:type model: string
:param pars: The fitted parameters for the model. They have to match the chosen model
:type pars: tuple
:param allowed_noise_triggers: The number of noise triggers that are allowed per kg day exposure.
:type allowed_noise_triggers: float
:return: 8-tuple
- The grid to evaluate the noise trigger models on.
- The exposure of the trigger window, in kg days.
- The evaluated noise template on x_grid, normalized to the exposure.
- The polluted component of the evaluated noise template on x_grid,
normalized to the exposure.
- The number of noise triggers, depending on the threshold, given by the x_grid.
- The number of pollution triggers, depending on the threshold, given by the x_grid.
- The threshold in mV.
- The number of pollution triggers.
:rtype: tuple
"""
if model == 'gauss':
d, sigma = pars
elif model == 'pollution_exponential':
d, sigma, lamb = pars
elif model == 'fraction_exponential':
d, sigma, lamb, w = pars
elif model == 'pollution_gauss':
d, sigma, mu, sigma_2 = pars
elif model == 'fraction_gauss':
d, sigma, mu, sigma_2, w = pars
else:
raise NotImplementedError('This model is not implemented!')
# calc the exposure in kg days
trigger_window = record_length * sample_length * detector_mass / 3600 / 24 * interval_restriction
# get the noise trigger rate
num = 3000
h = (ul - ll) / num
x_grid = np.linspace(start=ll, stop=ul, num=num)
if model == 'gauss':
ph_distribution = noise_trigger_template(x_max=x_grid, d=d, sigma=sigma)
elif model == 'fraction_exponential':
ph_distribution = fraction_exponential_noise(x_grid, *pars)
elif model == 'fraction_gauss':
ph_distribution = fraction_gauss_noise(x_grid, *pars)
else:
# get the correct template
if model == 'pollution_exponential':
template_noise = pollution_exponential_noise_gauss_comp
template_pollution = pollution_exponential_noise_poll_comp
elif model == 'pollution_gauss':
template_noise = pollution_gauss_noise_gauss_comp
template_pollution = pollution_gauss_noise_poll_comp
# get the noise trigger rate
ph_distribution = template_noise(x_grid, *pars)
# get the polluted noise trigger rate
polluted_ph_distribution = template_pollution(x_grid, *pars)
polluted_trigger_rate = np.array(
[h * np.sum(polluted_ph_distribution[i:]) for i in range(len(polluted_ph_distribution))])
polluted_ph_distribution /= trigger_window
polluted_trigger_rate /= trigger_window
# get the noise trigger rate
noise_trigger_rate = np.array([h * np.sum(ph_distribution[i:]) for i in range(len(ph_distribution))])
ph_distribution /= trigger_window
noise_trigger_rate /= trigger_window
# calc the threshold
try:
threshold = x_grid[noise_trigger_rate < allowed_noise_triggers][0]
if threshold == ul:
raise IndexError
except IndexError:
if model == 'pollution_exponential' or model == 'pollution_gauss':
retval = (x_grid, \
trigger_window, \
ph_distribution, \
polluted_ph_distribution, \
noise_trigger_rate, \
polluted_trigger_rate, \
None, \
None)
else:
retval = (x_grid, \
trigger_window, \
ph_distribution, \
None, \
noise_trigger_rate, \
None, \
None, \
None)
raise IndexError('The threshold for the wanted number of noise triggers is above the set interval. '
'Either increase the interval or allow for more noise triggers!', retval)
print('Threshold for {} Noise Trigger per kg day: {:.3f} mV'.format(allowed_noise_triggers, threshold))
if model == 'pollution_exponential' or model == 'pollution_gauss':
nmbr_pollution_triggers = polluted_trigger_rate[x_grid == threshold][0]
print('Pollution Triggers per kg day for this Threshold: {:.3f}'.format(nmbr_pollution_triggers))
if model == 'pollution_exponential' or model == 'pollution_gauss':
return x_grid, \
trigger_window, \
ph_distribution, \
polluted_ph_distribution, \
noise_trigger_rate, \
polluted_trigger_rate, \
threshold, \
nmbr_pollution_triggers
else:
return x_grid, \
trigger_window, \
ph_distribution, \
None, \
noise_trigger_rate, \
None, \
threshold, \
None