From 6fa17cf200fb6edb2c64eb15db82a7b94dff7cfa Mon Sep 17 00:00:00 2001
From: Jack Christopher Hutchinson Rolph <jack.rolph@desy.de>
Date: Fri, 30 Sep 2022 11:16:06 +0200
Subject: [PATCH] Delete Model.py

---
 Model.py | 505 -------------------------------------------------------
 1 file changed, 505 deletions(-)
 delete mode 100644 Model.py

diff --git a/Model.py b/Model.py
deleted file mode 100644
index fdb8ba5..0000000
--- a/Model.py
+++ /dev/null
@@ -1,505 +0,0 @@
-from scipy.signal import fftconvolve as convolve
-from scipy.interpolate import interp1d
-from scipy.stats import poisson, norm, binom
-from AdditionalPDFs import gpoisson, borel
-from scipy.integrate import simps
-from numba import njit 
-import numpy as np
-
-#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-########################################################################################################
-## DETECTOR RESPONSE MODEL
-########################################################################################################
-#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-
-
-
-########################################################################################################
-## VARIABLES:
-########################################################################################################
-
-########################################################################################################
-## Q: bin centres of studied charge distribution in arb. units;
-## Q_0: pedestal position in arb. units
-## G: gain in arb. units
-## mu: mean number of Geiger discharges in n.p.e
-## lbda: GP-branching parameter (unitless) 
-## sigma_0: electonics noise/pedestal width in arb. units
-## sigma_1: gain spread width in arb. units;
-## tau_Ap: afterpulse time constant in nanoseconds;
-## p_Ap: afterpulse scaling parameter (unitless)
-## DCR: dark count rate in gigahertz;
-## tau: slow-component time constant of SiPM pulse in nanoseconds
-## tau_R: recovery time of SiPM in nanoseconds
-## t_gate: gate length in nanoseconds
-## t_0: pre-gate integration window in nanoseconds.
-## k_max: estimated maximum number of peaks to fit, based on percentile of Poisson distribution with estimated mean in PeakOTron. 
-## len_pad: number of bins to pad for convolution. Default value: 100 bins.
-##
-########################################################################################################
-
-
-
-
-
-########################################################################################################
-## FUNCTION DEFINITIONS:
-########################################################################################################
-
-
-
-########################################################################################################
-## epsilon() 
-########################################################################################################
-##
-## Returns machine epsilon for thresholding zeros
-##
-########################################################################################################
-
-
-@njit('float64()')
-def epsilon():
-    return np.finfo(np.float64).eps
-
-########################################################################################################
-## sigmak(k, sigma_0, sigma_1) 
-########################################################################################################
-##
-## Returns width of peak number k, sigma_k(k, sigma_0, sigma_1) = sqrt(sigma_0^2 + k*sigma__1^2)
-##
-########################################################################################################
-
-@njit
-def sigmak(k, sigma_0, sigma_1):
-    return np.sqrt(sigma_0**2 + k*sigma_1**2)
-
-########################################################################################################
-## dabsQdt(t, tau, t_gate)) 
-########################################################################################################
-##
-## Returns |dQ/dt|(t, tau, t_gate) see Chmill Et Al.
-##
-########################################################################################################
-
-@njit
-def dabsQdt(t, tau, t_gate): 
-    return 2*np.exp(-t_gate/(2*tau))*np.abs(np.sinh((t_gate/2 - t)/tau))/tau
-
-########################################################################################################
-## dp1dt(t, tau_R, tau_Ap, t_gate)
-########################################################################################################
-##
-## Returns dα/dt(t, tau_R, tau_Ap, t_gate) see Chmill Et Al.
-##
-########################################################################################################
-
-@njit
-def dp1dt(t, tau_R, tau_Ap, t_gate):
-    return ((tau_Ap + tau_R)/(tau_Ap*(tau_Ap - np.exp(-t_gate/tau_Ap)*(tau_Ap + tau_R*(1 - np.exp(-t_gate/tau_Ap))))))*(1 - np.exp(-t/tau_R))*np.exp(-t/tau_Ap)
-
-########################################################################################################
-## tQ( Q, tau, t_gate)
-########################################################################################################
-##
-## Returns time as a function of charge. 
-##
-########################################################################################################
-
-@njit
-def tQ( Q, tau, t_gate):
-    return t_gate/2 - tau*np.arccosh(((1-Q)*np.exp(t_gate/(2*tau)) + np.exp(-t_gate/(2*tau)))/2)
-
-########################################################################################################
-## PH_max(t_gate, tau)
-########################################################################################################
-##
-## Returns maximum pulse height of afterpulse
-##
-########################################################################################################
-
-@njit
-def PH_max(t_gate, tau): 
-    return (1-np.exp(-t_gate/(2*tau)))**2
-
-########################################################################################################
-## Alpha(tau_R, tau_Ap, t_gate, p_Ap)
-########################################################################################################
-##
-## Returns total afterpulse probability, α, occuring between 0 and t_gate.
-##
-########################################################################################################
-
-@njit
-def Alpha(tau_R, tau_Ap, t_gate, p_Ap):
-    return (p_Ap/(tau_Ap + tau_R))*(tau_Ap - np.exp(-t_gate/tau_Ap)*(tau_Ap + tau_R*(1 - np.exp(-t_gate/tau_R))))
-
-
-########################################################################################################
-## dAlpha(tau_R, tau_Ap, t_gate, p_Ap)
-########################################################################################################
-##
-## Returns error on total afterpulse probability, α, occuring between 0 and t_gate.
-##
-########################################################################################################
-
-
-@njit
-def dAlpha(tau_R, tau_Ap, t_gate, p_Ap, dtau_Ap, dp_Ap):
-    dalphadtauAp = p_Ap*((t_gate*(-tau_Ap - tau_R*(1 - np.exp(-t_gate/tau_R)))*np.exp(-t_gate/tau_Ap))/tau_Ap**2 + 1 - np.exp(-t_gate/tau_Ap))/(tau_Ap+tau_R) - p_Ap*(tau_Ap - (tau_Ap + tau_R*(1 - np.exp(-t_gate/tau_R)))*np.exp(-t_gate/tau_Ap))/(tau_Ap + tau_R)**2
-    
-    dalphadpAp = (tau_Ap - (tau_Ap + tau_R*(1 - np.exp(-t_gate/tau_R)))*np.exp(-t_gate/tau_Ap))/(tau_Ap + tau_R)
-    
-    dalpha =  np.sqrt((dalphadtauAp**2)*(dtau_Ap**2) + (dalphadpAp**2)*(dp_Ap**2))
-    
-    return dalpha
-
-
-########################################################################################################
-## DiracDelta(length, idx_0, dx)
-########################################################################################################
-##
-## Returns Dirac Delta Function at the bin, with probability density 1/dx.
-##
-########################################################################################################
-
-@njit
-def DiracDelta(length, idx_0, dx):
-    delta = np.zeros(length)
-    delta[idx_0] = 1/dx
-    return delta
-
-########################################################################################################
-## dp1dQ(Q, tau, tau_R, tau_Ap, t_gate) 
-########################################################################################################
-##
-## Returns afterpulse p.d.f as a function of n.p.e 
-## Note: as in Chmill et al. However, the two branches of solutions given by t and t_gate -t are summed together. This demonstrably yields a normalised probability distribution. 
-##
-########################################################################################################
-
-@njit
-def dp1dQ(Q, tau, tau_R, tau_Ap, t_gate):
-    _PH_max = PH_max(t_gate, tau)
-
-    tb0 = tQ(Q, tau, t_gate)
-    tb1 = t_gate - tb0
-    
-    N0 = dp1dt(tb0, tau_R, tau_Ap, t_gate)
-    N1 = dp1dt(tb1, tau_R, tau_Ap, t_gate)
-    
-    D0 = dabsQdt(tb0, tau, t_gate)
-    D1 = dabsQdt(tb1, tau, t_gate)
-    
-    D0 = np.where(D0<epsilon(), epsilon(), D0)
-    D1 = np.where(D1<epsilon(), epsilon(), D1)
-    
-    pdf0 = N0/D0
-    pdf1 = N1/D1
-    
-    pdf = pdf0 + pdf1
-    
-    pdf = np.where((Q>0) & (Q<_PH_max), pdf, 0)
-    
-    return pdf
-
-########################################################################################################
-## dpDCR1dQ(Q, tau, t_gate, t_0)
-########################################################################################################
-## Returns dark count p.d.f as a function of n.p.e 
-## Note: the distribution is normalised by factor (t_gate+t_0)/tau
-########################################################################################################
-
-@njit
-def dpDCR1dQ(Q, tau, t_gate, t_0):
-    
-    Q_min =  np.exp(-t_0/tau)*(1 - np.exp(-t_gate/tau))
-    Q_max =  (1 - np.exp(-t_gate/tau))
-    
-    Q_th = np.where(np.abs(Q)<epsilon(), epsilon(), Q)
-    
-    pdf_bfg = np.where((Q_th>Q_min) & (Q_th<Q_max), 1/(Q_th), 0)
-    pdf_dg = np.where((Q_th>0) & (Q_th<Q_max), 1/(1-Q_th), 0)
-    
-    pdf = (pdf_bfg + pdf_dg)*(tau/(t_gate + t_0))
-
-    return pdf
-
-########################################################################################################
-## I_dp1dQ(Q, tau, t_gate, t_0)
-########################################################################################################
-##
-## Returns dp1dQ, corrects for any nans/infs and ensures normalisation by applying Simpson's rule.
-########################################################################################################
-
-def I_dp1dQ(Q, dx, tau, tau_R, tau_Ap, t_gate):
-    
-    I = dp1dQ(Q, tau, tau_R, tau_Ap, t_gate)
-    I = np.nan_to_num(I, nan=0.0, posinf=0.0, neginf=0.0)
-    I = I/max(epsilon(), simps(I, dx=dx))
-
-    return I
-
-########################################################################################################
-## I_dpDCR1dQ(Q, dx, tau, t_gate, t_0)
-########################################################################################################
-##
-## Returns dpDCR1dQ, corrects for any nans/infs and ensures normalisation by applying Simpson's rule.
-########################################################################################################
-
-def I_dpDCR1dQ(Q, dx, tau, t_gate, t_0):
-    I = dpDCR1dQ(Q, tau, t_gate, t_0)
-    I = np.nan_to_num(I, nan=0.0, posinf=0.0, neginf=0.0)
-    I = I/max(epsilon(), simps(I, dx=dx))
-
-    return I
-
-########################################################################################################
-## Convolve(A, B, idx_0, length, dx, norm=True) 
-########################################################################################################
-##
-## Calculates convolution of A and B using scipy fftconvolve, selects the convolved range relative to the pedestal index due to convolution shift, 
-## and multiplies by dx to renormalise the distribution. Then, corrects for any nans/infs and ensures normalisation by applying Simpson's rule.
-##
-########################################################################################################
-
-def Convolve(A, B, idx_0, length, dx, norm=True):
-    A_o_B = convolve(A, B)[idx_0:length+idx_0]*dx
-    A_o_B = np.nan_to_num(A_o_B, nan=0.0, posinf=0.0, neginf=0.0)
-    if(norm):
-        A_o_B = A_o_B/max(epsilon(), simps(A_o_B, dx=dx))
-    return A_o_B
-
-
-########################################################################################################
-## DRM(Q, Q_0, G, mu, lbda, sigma_0, sigma_1, tau_Ap, p_Ap, DCR, tau, tau_R, t_gate, t_0, k_max, len_pad) 
-########################################################################################################
-##
-## Calculates full detector response model. 
-## Notes: 
-## - The model is fitted in n.p.e scale. k = (Q - Q_0)/G. 
-## - To rescale to the input parameter unit, the final pdf is scaled by a factor of 1/G -
-##   this is because dp/dQ = (dp/dk)*(dk/dQ),  dk/dQ = d/dQ (Q - Q_0)/G = 1/G
-##
-########################################################################################################
-
-def DRM(Q, Q_0, G, mu, lbda, sigma_0, sigma_1, tau_Ap, p_Ap, DCR, tau, tau_R, t_gate, t_0, k_max, len_pad):
-    
-    
-    ########################################################################################################
-    # Primary Geiger Model
-    #########################################################################################################
-    ##
-    ## 1. Correct negative t_0 and ensures len_pad is an integer.
-    ## 2. Calculate padded Q and indices of the original Q so that these can be selected at the end of the function. 
-    ## 3. Convert Q to  n.p.e units for the main fit.
-    ##
-    ########################################################################################################
-    
-    t_0 = abs(t_0)
-    len_pad = int(len_pad)
-    
-
-    
-    len_Q = len(Q)
-    len_Q_pad = len_Q + 2*len_pad
-    Q_idx = np.arange(0, len_Q)
-    Q_idx_pad =  np.arange(-len_pad, len_Q+len_pad)
-    
-    
-    f_lin = interp1d(Q, Q_idx, fill_value='extrapolate')
-    f_lin_inv = interp1d(Q_idx, Q, fill_value='extrapolate')
-    
-    Q_pad = f_lin_inv(Q_idx_pad)
-    k_pad = (Q_pad - Q_0)/G
-    dx = abs(f_lin_inv(1) - f_lin_inv(0))
-    
-    dx_k = dx/G
-    
-    idx_k_0_pad = np.argmin(abs(k_pad))
-
-
-    Q_idx_orig = np.where(np.in1d(Q_idx_pad,
-                                    np.intersect1d(
-                                    Q_idx_pad,
-                                    Q_idx
-                                )
-                        ), True, False)
-
-        
-    k_pg = np.arange(0, max(k_max, 3)).astype(int)
-    
- 
-    ########################################################################################################
-    # Primary Geiger Model
-    ########################################################################################################
-    ## 1. Calculate the probabilities:
-    ##  - Primary Geiger, GP(k;μ,λ) 
-    ##  - Afterpulse, Binom(k, i; alpha)
-    ##
-    ## 2. Calculate the p.d.fs:
-    ##  - dp0dQs(Q; sigma_k(k; sigma_0/G, sigma_1/G) - Gaus(Q; μ=k, σ = sigma_k)
-    ##  - Note: sigma_0 and sigma_1 are in bin units. To convert to n.p.e, divide by G. 
-    ##
-    ##  - dpndQs(k; tau, tau_R, tau_Ap, t_gate) - Afterpulse distributions. 
-    ##     dp2dQ = dp1dQ o dp1dQ, dp3dQ = dp1dQ o dp1dQ o dp1dQ ... etc, where o is convolution. 
-    ##
-    ## 3. Calculate primary Geiger p.d.f:
-    ##   - For zeroth order afterpulse, return Binom(0; k, α) x GP(k;μ,λ) x Gaus(Q; k; sigma_k)
-    ##   - For nth order afterpulse, return Binom(n, k, α) x GP(k;μ,λ) x (dpndQ o Gaus(Q; k; sigma_k))   (Afterpulse p.d.f convolved with noise peak)
-    ##   - The above statement is valid because of convolution distributivity rules: f o (g+h) = f o g + f o h
-    ##   - Sum across all k to k_max;
-    ## 
-    ## RESULT: pdf_pg - Primary Geiger Discharge p.d.f 
-    ########################################################################################################
-
-        
-    pdf_pg = np.zeros(len_Q_pad)
-    
-    Ppgs = np.expand_dims(gpoisson.pmf(k_pg, mu, lbda), -1)   
-    
-    Pns = np.zeros((int(k_max), int(k_max)))
-    
-    alpha = Alpha(tau_R, tau_Ap, t_gate, p_Ap)
-    
-    dp0dQs = np.vstack([norm.pdf(k_pad, loc=_k, scale=sigmak(_k, sigma_0/G, sigma_1/G)) for _k in k_pg])
-    
-    dpndQs = []
-        
-
-    for _k in k_pg:
-        if(_k==0):
-            _dpndQ = np.zeros(len(k_pad))
-            Pns[0,0] = binom.pmf(0, 0, alpha)
-        elif(_k==1):
-            _dpndQ = I_dp1dQ(k_pad, dx_k, tau, tau_R, tau_Ap, t_gate)
-            
-            
-            Pns[1,0:2] = np.array(binom.pmf(np.arange(0, 2).astype(int), _k, alpha))
-        else:
-            _dpndQ = Convolve(dpndQs[1], dpndQs[-1], idx_k_0_pad, len_Q_pad, dx_k, norm=True)
-            Pns[_k,0:_k+1] = np.array(binom.pmf(np.arange(0, _k+1).astype(int), _k, alpha))
-
-        dpndQs.append(_dpndQ)
-            
-    dpndQs = np.vstack(dpndQs)
-  
-    for _k in k_pg:
-
-        _dp0dQ = float(Pns[_k, 0])*dp0dQs[_k, :]
-        
-        if(_k>0):
-            
-            
-            _dpndQ_sum = np.sum(np.vstack([
-                                    float(Pns[_k, _j])*Convolve(dpndQs[_j, :],
-                                                             dp0dQs[_k, :],
-                                                             idx_k_0_pad,
-                                                             len_Q_pad,
-                                                             dx_k,
-                                                             norm=True   
-                                                            )
-                                    for _j in range(1, _k+1)
-                                ]),
-                               axis=0)
-
-        else:
-            _dpndQ_sum = np.zeros(len_Q_pad)
-            
-        _dp0dQ = np.nan_to_num(_dp0dQ, nan=0.0, posinf=0.0, neginf=0.0)
-        _dpndQ_sum = np.nan_to_num(_dpndQ_sum, nan=0.0, posinf=0.0, neginf=0.0)
-            
-  
-        pdf_pg += float(Ppgs[_k])*(_dp0dQ + _dpndQ_sum)
-        
-
-         
-    ########################################################################################################
-    # Dark Model
-    ########################################################################################################
-    ##
-    ## 1. Calculate the probabilities:
-    ##  - μDCR = DCR x (t_0 + t_gate)
-    ##  - Primary Geiger -  Poisson(k;μDCR) 
-    ##  - XTalk - Borel(k;λ) 
-    ##
-    ## 2. Calculate the p.d.fs:
-    ##  - Notation as in Chmill et al:
-    ##      - f_1 is 1st order dark distribution; f_2 = f_1 o f_1, f_3 = f_1 o f_1 o f_1 ... etc
-    ##      - h_1 is 1st order stretched XT distribution; h_1 = 1/2 x f_1(Q/2; pars), h_2 = 1/3 x f_1(Q/3; pars)
-    ##      - Convolutions of f_n and h_m shown as f_n_o_h_m. 
-    ##  
-    ## 3. Sum probabilities and pdfs as in Chmill et al. 
-    ##    - Note: The 0th term is incorrect in Chmill et al. The probability should be Poisson(0; μDCR); the distribution should be
-    ##      Dirac Delta distribution at pedestal 𝛿(Q - Q_0) (i.e. no discharge.)
-    ##
-    ## 4. Renormalise distribution
-    ##  - Since p.d.f only goes up to 4th order, the distribution is not always correctly normalised. 
-    ##    We correct for the normalisation by appling Simpson's rule. The effect of poor normalisation is only relevant 
-    ##    for λ > 0.4: 5% off at this value. 
-    ## 
-    ## RESULT: pdf_dark - Dark Count p.d.f
-    ########################################################################################################
-    
-
-    mu_DCR = DCR*(t_0 + t_gate)
-    
-    Pp0 = float(poisson.pmf(0, mu_DCR))
-    Pp1 = float(poisson.pmf(1, mu_DCR))
-    Pp2 = float(poisson.pmf(2, mu_DCR))
-    Pp3 = float(poisson.pmf(3, mu_DCR))
-    Pp4 = float(poisson.pmf(4, mu_DCR))
-    
-    Pb0 = float(borel.pmf(0, lbda))
-    Pb1 = float(borel.pmf(1, lbda))
-    Pb2 = float(borel.pmf(2, lbda))
-    Pb3 = float(borel.pmf(3, lbda))
-    
-    
-    f_0 = np.zeros(len_Q_pad)
-    f_0[idx_k_0_pad] = 1/dx_k
-    
-    f_1 = I_dpDCR1dQ(k_pad, dx_k, tau, t_gate, t_0)
-    
-    f_2 = Convolve(f_1, f_1, idx_k_0_pad, len_Q_pad, dx_k, norm=True)
-    f_3 = Convolve(f_1, f_2, idx_k_0_pad, len_Q_pad, dx_k, norm=True)
-    f_4 = Convolve(f_1, f_3, idx_k_0_pad, len_Q_pad, dx_k, norm=True)
-    
-    h_1 = I_dpDCR1dQ(k_pad/(1+1), dx_k, tau, t_gate, t_0)/(1+1)
-    h_2 = I_dpDCR1dQ(k_pad/(2+1), dx_k, tau, t_gate, t_0)/(2+1)
-    h_3 = I_dpDCR1dQ(k_pad/(3+1), dx_k, tau, t_gate, t_0)/(3+1)
-    
-
-    f_1_o_h_1 = Convolve(f_1, h_1, idx_k_0_pad, len_Q_pad, dx_k, norm=True)
-    f_1_o_h_2 = Convolve(f_1, h_2, idx_k_0_pad, len_Q_pad, dx_k, norm=True)
-    h_1_o_h_1 = Convolve(h_1, h_1, idx_k_0_pad, len_Q_pad, dx_k, norm=True)
-    f_2_o_h_1 = Convolve(f_2, h_1, idx_k_0_pad, len_Q_pad, dx_k, norm=True)
-    
-
-    
-    
-    dp0DCRdQ = Pp0*f_0
-    dp1DCRdQ = Pp1*Pb0*f_1 
-    dp2DCRdQ = Pp1*Pb1*h_1 + Pp2*(Pb0**2)*f_2
-    dp3DCRdQ = Pp1*Pb2*h_2 + 2*Pp2*Pb0*Pb1*f_1_o_h_1 + Pp3*(Pb0**3)*f_3
-    dp4DCRdQ = Pp1*Pb3*h_3 + 2*Pp2*Pb0*Pb2*f_1_o_h_2 + Pp2*(Pb1**2)*h_1_o_h_1 + 3*Pp3*(Pb0**2)*Pb1*f_2_o_h_1 + Pp4*(Pb0**4)*f_4
-    
-    pdf_dark = dp0DCRdQ + dp1DCRdQ + dp2DCRdQ + dp3DCRdQ + dp4DCRdQ
-    pdf_dark = pdf_dark/max(epsilon(), simps(pdf_dark, dx=dx_k))
-    
-
-    
-    ########################################################################################################
-    # Convolve Primary Geiger and Dark Models
-    ########################################################################################################
-    ##
-    ## 1. Convolve and normalise pdf_pg and pdf_dark
-    ## pdf_drm = pdf_pg o pdf_dark 
-    ## 
-    ## RESULT: pdf_drm, final detector response model p.d.f
-    ########################################################################################################
-    
-        
-    pdf_drm =  Convolve(pdf_pg, pdf_dark, idx_k_0_pad, len_Q_pad, dx_k, norm=True)
-    pdf_drm = pdf_drm[Q_idx_orig]/G
-        
-    return pdf_drm
-- 
GitLab