TDCRPy 2.11.0__py3-none-any.whl → 2.13.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of TDCRPy might be problematic. Click here for more details.

tdcrpy/TDCRPy.py CHANGED
@@ -289,6 +289,7 @@ def TDCRPy(L, Rad, pmf_1, N, kB, V, mode="eff", Display=False, barp=False, Smode
289
289
  efficiency0_S, efficiency0_D, efficiency0_T, efficiency0_AB, efficiency0_BC, efficiency0_AC, efficiency0_D2 = tl.detectProbabilitiesMC(L, e_quenching, e_quenching2, t1, evenement, extDT, measTime)
290
290
  else:
291
291
  efficiency0_S, efficiency0_D, efficiency0_T, efficiency0_AB, efficiency0_BC, efficiency0_AC, efficiency0_D2 = tl.detectProbabilities(L, e_quenching, e_quenching2, t1, evenement, extDT, measTime)
292
+
292
293
  efficiency_S.append(efficiency0_S)
293
294
  efficiency_T.append(efficiency0_T)
294
295
  efficiency_D.append(efficiency0_D)
tdcrpy/TDCR_model_lib.py CHANGED
@@ -11,7 +11,6 @@ Bureau International des Poids et Mesures
11
11
  """
12
12
  ======= Import Python Module =======
13
13
  """
14
-
15
14
  import importlib.resources
16
15
  from importlib.resources import files
17
16
  import pkg_resources
@@ -24,6 +23,7 @@ import scipy.interpolate as interp
24
23
  import matplotlib.pyplot as plt
25
24
  from tqdm import tqdm
26
25
  import tempfile
26
+ import math
27
27
 
28
28
  """
29
29
  ======= Import ressource data =======
@@ -32,6 +32,16 @@ import tempfile
32
32
  # import advanced configuration data
33
33
  config = configparser.ConfigParser()
34
34
 
35
+ def readEffQ0():
36
+ global config, file_conf
37
+ config = configparser.ConfigParser()
38
+ with importlib.resources.as_file(files('tdcrpy').joinpath('config.toml')) as data_path:
39
+ file_conf = data_path
40
+ config.read(file_conf)
41
+
42
+ effQuantic0 = config["Inputs"].get("effQuantum")
43
+ return effQuantic0
44
+
35
45
  def readParameters(disp=False):
36
46
  global config, file_conf
37
47
  config = configparser.ConfigParser()
@@ -60,13 +70,15 @@ def readParameters(disp=False):
60
70
  diam_micelle = config["Inputs"].getfloat("diam_micelle")
61
71
  fAq = config["Inputs"].getfloat("fAq")
62
72
  micCorr = config["Inputs"].getboolean("micCorr")
63
- alphaDir = config["Inputs"].getfloat("alphaDir")
64
- effQuantic = config["Inputs"].get("effQuantum")
65
- effQuantic = effQuantic.split(',')
73
+ # alphaDir = config["Inputs"].getfloat("alphaDir")
74
+ effQuantic0 = config["Inputs"].get("effQuantum")
75
+ effQuantic = effQuantic0.split(',')
66
76
  for i, iS in enumerate(effQuantic):
67
77
  iS=iS.replace(" ","")
68
78
  if iS != 'None': effQuantic[i]=float(iS)
69
79
  optionModel = config["Inputs"].get("optionModel")
80
+ diffP = config["Inputs"].getfloat("diffP")
81
+ PMTspace = config["Inputs"].getfloat("PMTspace")
70
82
 
71
83
  if disp:
72
84
  print(f"number of integration bins for electrons = {nE_electron}")
@@ -83,16 +95,18 @@ def readParameters(disp=False):
83
95
  print(f"activation of the micelle correction = {micCorr}")
84
96
  print(f"diameter of micelle = {diam_micelle} nm")
85
97
  print(f"acqueous fraction = {fAq}")
86
- print(f"alpha parameter of the hidden Dirichlet process = {alphaDir}")
98
+ # print(f"alpha parameter of the hidden Dirichlet process = {alphaDir}")
87
99
  print(f"quantum efficiency of the photocathodes = {effQuantic}")
88
100
  print(f"Monte Carlo model of the optics = {optionModel}")
101
+ print(f"fraction of diffused scintillation photons = {diffP*100:.1f} %")
102
+ print(f"relative distance from vials border to PMT entrance = {PMTspace*100:.1f} %")
89
103
  print(f"coincidence resolving time = {tau} ns")
90
104
  print(f"extended dead time = {extDT} µs")
91
105
  print(f"measurement time = {measTime} min")
92
106
 
93
- return nE_electron, nE_alpha, RHO, Z, A, depthSpline, Einterp_a, Einterp_e, diam_micelle, fAq, tau, extDT, measTime, micCorr, alphaDir, effQuantic, optionModel, pH,pC,pN,pO,pP,pCl
107
+ return nE_electron, nE_alpha, RHO, Z, A, depthSpline, Einterp_a, Einterp_e, diam_micelle, fAq, tau, extDT, measTime, micCorr, effQuantic, optionModel, diffP, PMTspace, pH,pC,pN,pO,pP,pCl
94
108
 
95
- nE_electron, nE_alpha, RHO, Z, A, depthSpline, Einterp_a, Einterp_e, diam_micelle, fAq, tau, extDT, measTime, micCorr, alphaDir, effQuantic, optionModel, pH,pC,pN,pO,pP,pCl = readParameters()
109
+ nE_electron, nE_alpha, RHO, Z, A, depthSpline, Einterp_a, Einterp_e, diam_micelle, fAq, tau, extDT, measTime, micCorr, effQuantic, optionModel, diffP, PMTspace, pH,pC,pN,pO,pP,pCl = readParameters()
96
110
 
97
111
  p_atom = np.array([pH,pC,pN,pO,pP,pCl]) # atom abondance in the scintillator
98
112
  p_atom /= sum(p_atom)
@@ -106,7 +120,7 @@ def readConfigAsstr():
106
120
  def writeConfifAsstr(data):
107
121
  path2config = str(config.read(file_conf)[0])
108
122
  with open(path2config, 'w') as file:
109
- file.write(data)
123
+ file.write(data)
110
124
 
111
125
  def modifynE_electron(x):
112
126
  data0 = readConfigAsstr()
@@ -203,24 +217,37 @@ def modifyMicCorr(x):
203
217
  data1 = data0.replace(f"micCorr = {x0}",f"micCorr= {x}")
204
218
  writeConfifAsstr(data1)
205
219
 
206
- def modifyAlphaDir(x):
207
- data0 = readConfigAsstr()
208
- x0 = readParameters()[14]
209
- data1 = data0.replace(f"alphaDir = {x0}",f"alphaDir = {x}")
210
- writeConfifAsstr(data1)
220
+ # def modifyAlphaDir(x):
221
+ # data0 = readConfigAsstr()
222
+ # x0 = readParameters()[14]
223
+ # data1 = data0.replace(f"alphaDir = {x0}",f"alphaDir = {x}")
224
+ # writeConfifAsstr(data1)
211
225
 
212
226
  def modifyEffQ(x):
213
227
  data0 = readConfigAsstr()
214
- x0 = readParameters()[15]
228
+ # x0 = readParameters()[14]
229
+ x0 = readEffQ0()
215
230
  data1 = data0.replace(f"effQuantum = {x0}",f"effQuantum = {x}")
216
231
  writeConfifAsstr(data1)
217
232
 
218
233
  def modifyOptModel(x):
219
234
  data0 = readConfigAsstr()
220
- x0 = readParameters()[16]
235
+ x0 = readParameters()[15]
221
236
  data1 = data0.replace(f"optionModel = {x0}",f"optionModel = {x}")
222
237
  writeConfifAsstr(data1)
223
238
 
239
+ def modifyDiffP(x):
240
+ data0 = readConfigAsstr()
241
+ x0 = readParameters()[16]
242
+ data1 = data0.replace(f"diffP = {x0:.1f}",f"diffP = {x:.1f}")
243
+ writeConfifAsstr(data1)
244
+
245
+ def modifyPMTspace(x):
246
+ data0 = readConfigAsstr()
247
+ x0 = readParameters()[17]
248
+ data1 = data0.replace(f"PMTspace = {x0:.1f}",f"PMTspace = {x:.1f}")
249
+ writeConfifAsstr(data1)
250
+
224
251
  def read_temp_files(copy=False, path="C:"):
225
252
 
226
253
  temp_dir = tempfile.gettempdir()
@@ -2879,7 +2906,7 @@ def buildBetaSpectra(rad, V, N, prt=False):
2879
2906
  else: file.write(f"{b}\t{p2[i]}\n")
2880
2907
  print("file written in local")
2881
2908
 
2882
- def detectProbabilities(L, e_quenching, e_quenching2, t1, evenement, extDT, measTime):
2909
+ def detectProbabilities(L, e_quenching, e_quenching2, t1, evenement, extDT, measTime, effQuantic = effQuantic):
2883
2910
  """
2884
2911
  Calculate detection probabilities for LS counting systems - see Broda, R., Cassette, P., Kossert, K., 2007. Radionuclide metrology using liquid scintillation counting. Metrologia 44. https://doi.org/10.1088/0026-1394/44/4/S06
2885
2912
 
@@ -2920,17 +2947,20 @@ def detectProbabilities(L, e_quenching, e_quenching2, t1, evenement, extDT, meas
2920
2947
  """
2921
2948
  if isinstance(L, (tuple, list)):
2922
2949
  symm = False
2950
+ mu = effQuantic
2923
2951
  else:
2924
2952
  symm = True
2953
+ mu = np.mean(effQuantic)
2925
2954
 
2955
+
2926
2956
 
2927
2957
  if symm:
2928
- # print(evenement !=1, t1 > extDT*1e-6, t1 < measTime*60)
2958
+
2929
2959
  if evenement !=1 and t1 > extDT*1e-6 and t1 < measTime*60:
2930
2960
  # TDCR
2931
- p_nosingle = np.exp(-L*np.sum(np.asarray(e_quenching))/3) # probability to have 0 electrons in a PMT
2961
+ p_nosingle = np.exp(-L*mu*np.sum(np.asarray(e_quenching))/3) # probability to have 0 electrons in a PMT
2932
2962
  p_single = 1-p_nosingle # probability to have at least 1 electrons in a PMT
2933
- p_nosingle2 = np.exp(-L*np.sum(np.asarray(e_quenching2))/3) # probability to have 0 electrons in a PMT
2963
+ p_nosingle2 = np.exp(-L*mu*np.sum(np.asarray(e_quenching2))/3) # probability to have 0 electrons in a PMT
2934
2964
  p_single2 = 1-p_nosingle2
2935
2965
  efficiency0_S = 1-p_nosingle**3+1-p_nosingle2**3
2936
2966
  efficiency0_T = p_single**3+p_single2**3
@@ -2940,16 +2970,16 @@ def detectProbabilities(L, e_quenching, e_quenching2, t1, evenement, extDT, meas
2940
2970
  efficiency0_AC = efficiency0_AB
2941
2971
 
2942
2972
  # CN
2943
- p_nosingle = np.exp(-L*np.sum(np.asarray(e_quenching))/2) # probability to have 0 electrons in a PMT
2973
+ p_nosingle = np.exp(-L*mu*np.sum(np.asarray(e_quenching))/2) # probability to have 0 electrons in a PMT
2944
2974
  p_single = 1-p_nosingle # probability to have at least 1 electrons in a PMT
2945
- p_nosingle2 = np.exp(-L*np.sum(np.asarray(e_quenching2))/2) # probability to have 0 electrons in a PMT
2975
+ p_nosingle2 = np.exp(-L*mu*np.sum(np.asarray(e_quenching2))/2) # probability to have 0 electrons in a PMT
2946
2976
  p_single2 = 1-p_nosingle2
2947
2977
  efficiency0_A2 = p_single+p_single2
2948
2978
  efficiency0_B2 = efficiency0_A2
2949
2979
  efficiency0_D2 = p_single**2+p_single2**2
2950
2980
  else:
2951
2981
  # TDCR
2952
- p_nosingle = np.exp(-L*np.sum(np.asarray(e_quenching))/3) # probability to have 0 electrons in a PMT
2982
+ p_nosingle = np.exp(-L*mu*np.sum(np.asarray(e_quenching))/3) # probability to have 0 electrons in a PMT
2953
2983
  p_single = 1-p_nosingle # probability to have at least 1 electrons in a PMT
2954
2984
  efficiency0_S = 1-p_nosingle**3
2955
2985
  efficiency0_T = p_single**3
@@ -2959,7 +2989,7 @@ def detectProbabilities(L, e_quenching, e_quenching2, t1, evenement, extDT, meas
2959
2989
  efficiency0_AC = efficiency0_AB
2960
2990
 
2961
2991
  # CN
2962
- p_nosingle = np.exp(-L*np.sum(np.asarray(e_quenching))/2) # probability to have 0 electrons in a PMT
2992
+ p_nosingle = np.exp(-L*mu*np.sum(np.asarray(e_quenching))/2) # probability to have 0 electrons in a PMT
2963
2993
  p_single = 1-p_nosingle # probability to have at least 1 electrons in a PMT
2964
2994
  efficiency0_A2 = p_single
2965
2995
  efficiency0_B2 = efficiency0_A2
@@ -2967,18 +2997,18 @@ def detectProbabilities(L, e_quenching, e_quenching2, t1, evenement, extDT, meas
2967
2997
  else:
2968
2998
  if evenement !=1 and t1 > extDT*1e-6 and t1 < measTime*60:
2969
2999
  # TDCR
2970
- pA_nosingle = np.exp(-L[0]*np.sum(np.asarray(e_quenching))/3) # probability to have 0 electrons in a PMT
3000
+ pA_nosingle = np.exp(-L[0]*mu[0]*np.sum(np.asarray(e_quenching))/3) # probability to have 0 electrons in a PMT
2971
3001
  pA_single = 1-pA_nosingle # probability to have at least 1 electrons in a PMT
2972
- pB_nosingle = np.exp(-L[1]*np.sum(np.asarray(e_quenching))/3) # probability to have 0 electrons in a PMT
3002
+ pB_nosingle = np.exp(-L[1]*mu[1]*np.sum(np.asarray(e_quenching))/3) # probability to have 0 electrons in a PMT
2973
3003
  pB_single = 1-pB_nosingle # probability to have at least 1 electrons in a PMT
2974
- pC_nosingle = np.exp(-L[2]*np.sum(np.asarray(e_quenching))/3) # probability to have 0 electrons in a PMT
3004
+ pC_nosingle = np.exp(-L[2]*mu[2]*np.sum(np.asarray(e_quenching))/3) # probability to have 0 electrons in a PMT
2975
3005
  pC_single = 1-pC_nosingle # probability to have at least 1 electrons in a PMT
2976
3006
 
2977
- pA_nosingle2 = np.exp(-L[0]*np.sum(np.asarray(e_quenching2))/3) # probability to have 0 electrons in a PMT
3007
+ pA_nosingle2 = np.exp(-L[0]*mu[0]*np.sum(np.asarray(e_quenching2))/3) # probability to have 0 electrons in a PMT
2978
3008
  pA_single2 = 1-pA_nosingle2 # probability to have at least 1 electrons in a PMT
2979
- pB_nosingle2 = np.exp(-L[1]*np.sum(np.asarray(e_quenching2))/3) # probability to have 0 electrons in a PMT
3009
+ pB_nosingle2 = np.exp(-L[1]*mu[1]*np.sum(np.asarray(e_quenching2))/3) # probability to have 0 electrons in a PMT
2980
3010
  pB_single2 = 1-pB_nosingle2 # probability to have at least 1 electrons in a PMT
2981
- pC_nosingle2 = np.exp(-L[2]*np.sum(np.asarray(e_quenching2))/3) # probability to have 0 electrons in a PMT
3011
+ pC_nosingle2 = np.exp(-L[2]*mu[2]*np.sum(np.asarray(e_quenching2))/3) # probability to have 0 electrons in a PMT
2982
3012
  pC_single2 = 1-pC_nosingle2 # probability to have at least 1 electrons in a PMT
2983
3013
 
2984
3014
  efficiency0_A2 = pA_single+pA_single2
@@ -2993,24 +3023,24 @@ def detectProbabilities(L, e_quenching, e_quenching2, t1, evenement, extDT, meas
2993
3023
 
2994
3024
 
2995
3025
  # CN
2996
- pA_nosingle = np.exp(-L[0]*np.sum(np.asarray(e_quenching))/2) # probability to have 0 electrons in a PMT
3026
+ pA_nosingle = np.exp(-L[0]*mu[0]*np.sum(np.asarray(e_quenching))/2) # probability to have 0 electrons in a PMT
2997
3027
  pA_single = 1-pA_nosingle # probability to have at least 1 electrons in a PMT
2998
- pB_nosingle = np.exp(-L[1]*np.sum(np.asarray(e_quenching))/2) # probability to have 0 electrons in a PMT
3028
+ pB_nosingle = np.exp(-L[1]*mu[1]*np.sum(np.asarray(e_quenching))/2) # probability to have 0 electrons in a PMT
2999
3029
  pB_single = 1-pB_nosingle # probability to have at least 1 electrons in a PMT
3000
3030
 
3001
- pA_nosingle2 = np.exp(-L[0]*np.sum(np.asarray(e_quenching2))/2) # probability to have 0 electrons in a PMT
3031
+ pA_nosingle2 = np.exp(-L[0]*mu[0]*np.sum(np.asarray(e_quenching2))/2) # probability to have 0 electrons in a PMT
3002
3032
  pA_single2 = 1-pA_nosingle2 # probability to have at least 1 electrons in a PMT
3003
- pB_nosingle2 = np.exp(-L[1]*np.sum(np.asarray(e_quenching2))/2) # probability to have 0 electrons in a PMT
3033
+ pB_nosingle2 = np.exp(-L[1]*mu[1]*np.sum(np.asarray(e_quenching2))/2) # probability to have 0 electrons in a PMT
3004
3034
  pB_single2 = 1-pB_nosingle2 # probability to have at least 1 electrons in a PMT
3005
3035
 
3006
3036
  efficiency0_D2 = pA_single*pB_single+pA_single2*pB_single2
3007
3037
  else:
3008
3038
  # TDCR
3009
- pA_nosingle = np.exp(-L[0]*np.sum(np.asarray(e_quenching))/3) # probability to have 0 electrons in a PMT
3039
+ pA_nosingle = np.exp(-L[0]*mu[0]*np.sum(np.asarray(e_quenching))/3) # probability to have 0 electrons in a PMT
3010
3040
  pA_single = 1-pA_nosingle # probability to have at least 1 electrons in a PMT
3011
- pB_nosingle = np.exp(-L[1]*np.sum(np.asarray(e_quenching))/3) # probability to have 0 electrons in a PMT
3041
+ pB_nosingle = np.exp(-L[1]*mu[1]*np.sum(np.asarray(e_quenching))/3) # probability to have 0 electrons in a PMT
3012
3042
  pB_single = 1-pB_nosingle # probability to have at least 1 electrons in a PMT
3013
- pC_nosingle = np.exp(-L[2]*np.sum(np.asarray(e_quenching))/3) # probability to have 0 electrons in a PMT
3043
+ pC_nosingle = np.exp(-L[2]*mu[2]*np.sum(np.asarray(e_quenching))/3) # probability to have 0 electrons in a PMT
3014
3044
  pC_single = 1-pC_nosingle # probability to have at least 1 electrons in a PMT
3015
3045
 
3016
3046
  efficiency0_A2 = pA_single
@@ -3023,16 +3053,114 @@ def detectProbabilities(L, e_quenching, e_quenching2, t1, evenement, extDT, meas
3023
3053
  efficiency0_S = 1-pA_nosingle*pB_nosingle*pC_nosingle
3024
3054
 
3025
3055
  # CN
3026
- pA_nosingle = np.exp(-L[0]*np.sum(np.asarray(e_quenching))/2) # probability to have 0 electrons in a PMT
3056
+ pA_nosingle = np.exp(-L[0]*mu[0]*np.sum(np.asarray(e_quenching))/2) # probability to have 0 electrons in a PMT
3027
3057
  pA_single = 1-pA_nosingle # probability to have at least 1 electrons in a PMT
3028
- pB_nosingle = np.exp(-L[1]*np.sum(np.asarray(e_quenching))/2) # probability to have 0 electrons in a PMT
3058
+ pB_nosingle = np.exp(-L[1]*mu[1]*np.sum(np.asarray(e_quenching))/2) # probability to have 0 electrons in a PMT
3029
3059
  pB_single = 1-pB_nosingle # probability to have at least 1 electrons in a PMT
3030
3060
  efficiency0_D2 = pA_single*pB_single
3031
3061
 
3032
3062
  return efficiency0_S, efficiency0_D, efficiency0_T, efficiency0_AB, efficiency0_BC, efficiency0_AC, efficiency0_D2
3033
3063
 
3034
3064
 
3035
- def detectProbabilitiesMC(L, e_quenching, e_quenching2, t1, evenement, extDT, measTime, dir_param = alphaDir, effQuantic = effQuantic, optionModel=optionModel):
3065
+ def stochasticDepTD(diffP, PMTspace):
3066
+ """
3067
+ Generate the probability
3068
+
3069
+ Parameters
3070
+ ----------
3071
+ diffP : TYPE
3072
+ DESCRIPTION.
3073
+ PMTspace : TYPE
3074
+ DESCRIPTION.
3075
+
3076
+ Returns
3077
+ -------
3078
+ TYPE
3079
+ DESCRIPTION.
3080
+
3081
+ """
3082
+ detA = np.array([[2*(1+PMTspace), 0], [-(1+PMTspace), np.sqrt(3)*(1+PMTspace)]])
3083
+ detB = np.array([[-(1+PMTspace), np.sqrt(3)*(1+PMTspace)], [-(1+PMTspace), -np.sqrt(3)*(1+PMTspace)]])
3084
+ detC = np.array([[-(1+PMTspace), -np.sqrt(3)*(1+PMTspace)], [2*(1+PMTspace), 0]])
3085
+
3086
+ def simulate_photon_groups():
3087
+ rho = 1 * np.sqrt(np.random.uniform(0, 1, 1)) # Radial distance
3088
+ theta = np.random.uniform(0, 2 * np.pi, 1) # Angular position
3089
+ x = rho * np.cos(theta)
3090
+ y = rho * np.sin(theta)
3091
+ return x, y
3092
+
3093
+ def calculate_angle(O, det):
3094
+ A=det[0]
3095
+ B=det[1]
3096
+ OA = (A[0] - O[0], A[1] - O[1]) # Vecteurs OA et OB
3097
+ OB = (B[0] - O[0], B[1] - O[1])
3098
+ dot_product = OA[0] * OB[0] + OA[1] * OB[1] # Produit scalaire OA . OB
3099
+ norm_OA = math.sqrt((OA[0]**2 + OA[1]**2)[0]) # Normes des vecteurs OA et OB
3100
+ norm_OB = math.sqrt((OB[0]**2 + OB[1]**2)[0])
3101
+ cos_angle = dot_product / (norm_OA * norm_OB) # Cosinus de l'angle
3102
+ angle_rad = math.acos(cos_angle[0]) # Angle en radians
3103
+ angle_deg = math.degrees(angle_rad) # Convertir en degrés
3104
+ return angle_deg
3105
+
3106
+ x, y = simulate_photon_groups()
3107
+
3108
+ pa=(1-diffP)*calculate_angle([x, y], detA)/360+diffP/3
3109
+ pb=(1-diffP)*calculate_angle([x, y], detB)/360+diffP/3
3110
+ pc=(1-diffP)*calculate_angle([x, y], detC)/360+diffP/3
3111
+
3112
+ return pa, pb, pc
3113
+
3114
+ # Di = []; Ti = []
3115
+ # n=1000000
3116
+ # for i in range(n):
3117
+ # A = stochasticDepTD(1, 0)
3118
+ # B = np.random.poisson(2)
3119
+ # n_phPMT = np.random.multinomial(B, A) # sample the number of photons in each PMTs (TDCR configuration)
3120
+ # nA=np.random.binomial(n_phPMT[0],0.25) # sample the conversion to photoelectrons PMT A
3121
+ # nB=np.random.binomial(n_phPMT[1],0.25) # sample the conversion to photoelectrons PMT B
3122
+ # nC=np.random.binomial(n_phPMT[2],0.25) # sample the conversion to photoelectrons PMT C
3123
+ # Di.append(sum([nA>0, nB>0, nC>0])>1)
3124
+ # Ti.append(sum([nA>0, nB>0, nC>0])>2)
3125
+ # D = sum(Di)/n
3126
+ # uD = D/np.sqrt(sum(Di))#np.sqrt(n)
3127
+ # T = sum(Ti)/n
3128
+ # uT = T/np.sqrt(sum(Ti))#/np.sqrt(n)
3129
+ # print(D, uD)
3130
+ # print(T, uT)
3131
+
3132
+ def stochasticDepCN(diffP, PMTspace):
3133
+ def simulate_photon_groups():
3134
+ rho = 1 * np.sqrt(np.random.uniform(0, 1, 1)) # Radial distance
3135
+ theta = np.random.uniform(0, 2 * np.pi, 1) # Angular position
3136
+ x = rho * np.cos(theta)
3137
+ y = rho * np.sin(theta)
3138
+ return x, y
3139
+
3140
+ def calculate_angle(O):
3141
+ OA = (-1-PMTspace - O[0], 0 - O[1]) # Vecteurs OA et OB
3142
+ OB = (1+PMTspace - O[0], 0 - O[1])
3143
+ dot_product = OA[0] * OB[0] + OA[1] * OB[1] # Produit scalaire OA . OB
3144
+ norm_OA = math.sqrt((OA[0]**2 + OA[1]**2)[0]) # Normes des vecteurs OA et OB
3145
+ norm_OB = math.sqrt((OB[0]**2 + OB[1]**2)[0])
3146
+ cos_angle = dot_product / (norm_OA * norm_OB) # Cosinus de l'angle
3147
+ angle_rad = math.acos(cos_angle[0]) # Angle en radians
3148
+ angle_deg = math.degrees(angle_rad) # Convertir en degrés
3149
+ return angle_deg
3150
+
3151
+ x, y = simulate_photon_groups()
3152
+
3153
+ if np.random.randint(0, high=2)==0:
3154
+ pa=(1-diffP)*calculate_angle([x, y])/360+diffP/2
3155
+ pb=1-pa
3156
+ else:
3157
+ pb=(1-diffP)*calculate_angle([x, y])/360+diffP/2
3158
+ pa=1-pb
3159
+
3160
+ return pa, pb
3161
+
3162
+
3163
+ def detectProbabilitiesMC(L, e_quenching, e_quenching2, t1, evenement, extDT, measTime, effQuantic = effQuantic, optionModel=optionModel, diffP = diffP, PMTspace = PMTspace, dispParam=False):
3036
3164
  """
3037
3165
  Calculate detection probabilities for LS counting systems - see Broda, R., Cassette, P., Kossert, K., 2007. Radionuclide metrology using liquid scintillation counting. Metrologia 44. https://doi.org/10.1088/0026-1394/44/4/S06
3038
3166
 
@@ -3072,70 +3200,50 @@ def detectProbabilitiesMC(L, e_quenching, e_quenching2, t1, evenement, extDT, me
3072
3200
 
3073
3201
  """
3074
3202
  mu = effQuantic
3075
-
3076
- if dir_param < 1000:
3077
- dirichTD = np.random.dirichlet([dir_param, dir_param, dir_param])
3078
- dirichCN = np.random.dirichlet([dir_param, dir_param])
3079
- else:
3080
- dirichTD = [1/3, 1/3, 1/3]
3081
- dirichCN = [1/2, 1/2]
3082
-
3203
+
3083
3204
  if type(L) == float:
3084
3205
  L = [L, L, L]
3085
3206
 
3086
- def PMBmodel(e_q, dirichTD, dirichCN, L, mu):
3087
- n_e=np.zeros(3); n_eCN=np.zeros(2)
3088
- n_ph = np.random.poisson(sum(np.asarray(e_q))*np.mean(L)/np.mean(mu))
3089
- # TDCR
3090
- n_phPMT = np.random.multinomial(n_ph, dirichTD)
3091
- n_e[0]=np.random.binomial(n_phPMT[0],mu[0])
3092
- n_e[1]=np.random.binomial(n_phPMT[1],mu[0])
3093
- n_e[2]=np.random.binomial(n_phPMT[2],mu[0])
3094
- # C/N
3095
- n_phPMTCN = np.random.multinomial(n_ph, dirichCN)
3096
- n_eCN[0]=np.random.binomial(n_phPMTCN[0],mu[0])
3097
- n_eCN[1]=np.random.binomial(n_phPMTCN[1],mu[0])
3098
- return n_e, n_eCN
3207
+ if dispParam: print(f"EffQ = {mu} - model = {optionModel} - diffP = {diffP} - PMTspace = {PMTspace}")
3099
3208
 
3100
- def Pmodel(e_q, dirichTD, dirichCN, L, mu):
3101
- n_e=np.zeros(3); n_eCN=np.zeros(2)
3102
- n_e[0] = np.random.poisson(sum(np.asarray(e_q))*L[0]*mu[0]*dirichTD[0])
3103
- n_e[1] = np.random.poisson(sum(np.asarray(e_q))*L[1]*mu[1]*dirichTD[1])
3104
- n_e[2] = np.random.poisson(sum(np.asarray(e_q))*L[2]*mu[2]*dirichTD[2])
3105
- n_eCN[0] = np.random.poisson(sum(np.asarray(e_q))*L[0]*mu[0]*dirichCN[0])
3106
- n_eCN[1] = np.random.poisson(sum(np.asarray(e_q))*L[1]*mu[1]*dirichCN[1])
3107
- return n_e, n_eCN
3209
+ def stochasOpticModel(e_q, L, mu):
3210
+ n_e=np.zeros(3); n_eCN=np.zeros(2) # initilize the number of photoelectrons
3211
+
3212
+ n_ph = np.random.poisson(sum(np.asarray(e_q))*np.mean(L)) # sample the number of scintillation photons
3213
+
3214
+ pTD = stochasticDepTD(diffP, PMTspace) # probabilities for photons to move towards the different PMTs (TDCR configuration)
3215
+ n_phPMT = np.random.multinomial(n_ph, pTD) # sample the number of photons in each PMTs (TDCR configuration)
3216
+ n_e[0]=np.random.binomial(n_phPMT[0],mu[0]) # sample the conversion to photoelectrons PMT A
3217
+ n_e[1]=np.random.binomial(n_phPMT[1],mu[1]) # sample the conversion to photoelectrons PMT B
3218
+ n_e[2]=np.random.binomial(n_phPMT[2],mu[2]) # sample the conversion to photoelectrons PMT C
3219
+
3220
+ pCN = stochasticDepCN(diffP, PMTspace) # probabilities for photons to move towards the different PMTs (C/N configuration)
3221
+ n_phPMTCN = np.random.multinomial(n_ph, pCN) # sample the number of photons in each PMTs (C/N configuration)
3222
+ n_eCN[0]=np.random.binomial(n_phPMTCN[0],mu[0]) # sample the conversion to photoelectrons PMT A
3223
+ n_eCN[1]=np.random.binomial(n_phPMTCN[1],mu[1]) # sample the conversion to photoelectrons PMT B
3224
+
3225
+ return n_e, n_eCN
3108
3226
 
3109
- def EPmodel(e_q, dirichTD, dirichCN, L, mu):
3110
- n_e=np.zeros(3); n_eCN=np.zeros(2)
3111
- n_e[0] = sum(np.asarray(e_q))*L[0]*mu[0]*dirichTD[0]
3112
- n_e[1] = sum(np.asarray(e_q))*L[1]*mu[1]*dirichTD[1]
3113
- n_e[2] = sum(np.asarray(e_q))*L[2]*mu[2]*dirichTD[2]
3114
- n_eCN[0] = sum(np.asarray(e_q))*L[0]*mu[0]*dirichCN[0]
3115
- n_eCN[1] = sum(np.asarray(e_q))*L[1]*mu[1]*dirichCN[1]
3227
+ def Pmodel(e_q, pTD_ideal, pCN_ideal, L, mu):
3228
+ n_e=np.zeros(3); n_eCN=np.zeros(2) # initilize the number of photoelectrons
3229
+
3230
+ n_e[0] = np.random.poisson(sum(np.asarray(e_q))*L[0]*mu[0]*pTD_ideal[0]) # sample the conversion to photoelectrons PMT A
3231
+ n_e[1] = np.random.poisson(sum(np.asarray(e_q))*L[1]*mu[1]*pTD_ideal[1]) # sample the conversion to photoelectrons PMT B
3232
+ n_e[2] = np.random.poisson(sum(np.asarray(e_q))*L[2]*mu[2]*pTD_ideal[2]) # sample the conversion to photoelectrons PMT C
3233
+ n_eCN[0] = np.random.poisson(sum(np.asarray(e_q))*L[0]*mu[0]*pCN_ideal[0]) # sample the conversion to photoelectrons PMT A
3234
+ n_eCN[1] = np.random.poisson(sum(np.asarray(e_q))*L[1]*mu[1]*pCN_ideal[1]) # sample the conversion to photoelectrons PMT B
3235
+
3116
3236
  return n_e, n_eCN
3117
-
3118
- # def Amodel(e_q, dirichTD, dirichCN, L, mu):
3119
- # n_e=np.zeros(3); n_eCN=np.zeros(2)
3120
- # n_e[0] = 1-np.exp(-L[0]*np.sum(np.asarray(e_q))*dirichTD[0])
3121
- # n_e[1] = 1-np.exp(-L[0]*np.sum(np.asarray(e_q))*dirichTD[0])
3122
- # n_e[2] = 1-np.exp(-L[0]*np.sum(np.asarray(e_q))*dirichTD[0])
3123
- # n_eCN[0] = 1-np.exp(-L[0]*np.sum(np.asarray(e_q))*dirichTD[0])
3124
- # n_eCN[1] = 1-np.exp(-L[0]*np.sum(np.asarray(e_q))*dirichTD[0])
3125
- # return n_e, n_eCN
3126
-
3237
+
3127
3238
 
3128
3239
  efficiency0_S = 0; efficiency0_T = 0; efficiency0_D = 0
3129
3240
  efficiency0_AB = 0; efficiency0_BC = 0; efficiency0_AC = 0
3130
3241
  efficiency0_D2 = 0;
3131
3242
  # n_e = np.zeros(3); n_eCN = np.zeros(2); n_e2 = np.zeros(3); n_e2CN = np.zeros(2)
3132
-
3133
- if optionModel == "poisson-multinomial-binomial":
3134
- n_e, n_eCN = PMBmodel(e_quenching, dirichTD, dirichCN, L, mu)
3243
+ if optionModel == "stochastic-dependence":
3244
+ n_e, n_eCN = stochasOpticModel(e_quenching, L, mu)
3135
3245
  elif optionModel == "poisson":
3136
- n_e, n_eCN = Pmodel(e_quenching, dirichTD, dirichCN, L, mu)
3137
- elif optionModel == "expectation":
3138
- n_e, n_eCN = EPmodel(e_quenching, dirichTD, dirichCN, L, mu)
3246
+ n_e, n_eCN = Pmodel(e_quenching, [1/3, 1/3, 1/3], [1/2, 1/2], L, mu)
3139
3247
  else:
3140
3248
  print("unknown model")
3141
3249
 
@@ -3148,12 +3256,10 @@ def detectProbabilitiesMC(L, e_quenching, e_quenching2, t1, evenement, extDT, me
3148
3256
  if sum(n_eCN>1)>1: efficiency0_D2 =1
3149
3257
 
3150
3258
  if evenement !=1 and t1 > extDT*1e-6 and t1 < measTime*60:
3151
- if optionModel == "poisson-multinomial-binomial":
3152
- n_e2, n_e2CN = PMBmodel(e_quenching2, dirichTD, dirichCN, L, mu)
3259
+ if optionModel == "stochastic-dependence":
3260
+ n_e2, n_e2CN = stochasOpticModel(e_quenching2, L, mu)
3153
3261
  elif optionModel == "poisson":
3154
- n_e2, n_e2CN = Pmodel(e_quenching2, dirichTD, dirichCN, L, mu)
3155
- elif optionModel == "expectation":
3156
- n_e2, n_e2CN = EPmodel(e_quenching2, dirichTD, dirichCN, L, mu)
3262
+ n_e2, n_e2CN = Pmodel(e_quenching2, [1/3, 1/3, 1/3], [1/2, 1/2], L, mu)
3157
3263
  else:
3158
3264
  print("unknown model")
3159
3265
 
@@ -3168,9 +3274,6 @@ def detectProbabilitiesMC(L, e_quenching, e_quenching2, t1, evenement, extDT, me
3168
3274
  return efficiency0_S, efficiency0_D, efficiency0_T, efficiency0_AB, efficiency0_BC, efficiency0_AC, efficiency0_D2
3169
3275
 
3170
3276
 
3171
-
3172
-
3173
-
3174
3277
  def efficienciesEstimates(efficiency_S, efficiency_D, efficiency_T, efficiency_AB, efficiency_BC, efficiency_AC, efficiency_D2, N):
3175
3278
  """
3176
3279
  Calculate detection efficiencies from list of detection probabilities per decays.
@@ -3280,6 +3383,9 @@ def readRecQuenchedEnergies():
3280
3383
  e_quenching.append(energy)
3281
3384
  return Epromt, Edelayed
3282
3385
 
3386
+
3387
+
3388
+
3283
3389
  # N = 1e7
3284
3390
  # buildBetaSpectra('H-3', 16, N, prt=True); print('H-3 - done')
3285
3391
  # buildBetaSpectra('C-14', 16, N, prt=True); print('C-14 - done')
tdcrpy/config.toml CHANGED
@@ -32,12 +32,14 @@ diam_micelle = 2
32
32
  fAq = 0.1
33
33
 
34
34
  ## OPTICAL PROPERTIES
35
- # Dirichlet parameter
36
- alphaDir = 100000
37
35
  # Quantum efficiency
38
36
  effQuantum = 0.25, 0.25, 0.25
39
37
  # Optical MC model
40
- optionModel = poisson-multinomial-binomial
38
+ optionModel = stochastic-dependence
39
+ # fraction of diffused scintillation photons
40
+ diffP = 1.0
41
+ # relative distance from vials border to PMT entrance
42
+ PMTspace = 0.1
41
43
 
42
44
  ## PROPERTIES OF THE COUNTER
43
45
  # Coincidence resolving time (ns)
@@ -0,0 +1,62 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Wed May 14 16:17:16 2025
4
+
5
+ @author: romain.coulon
6
+ """
7
+ import numpy as np
8
+
9
+ # m = 0.1
10
+ # N = 1000000
11
+
12
+ # ps0 = 1-np.exp(-m)
13
+
14
+ # psi=np.random.poisson(m,N)
15
+ # ps1 = sum(psi>0)/N
16
+ # ups1 = np.sqrt(sum(psi>0))/N
17
+
18
+ # print(ps0,ps1,ups1)
19
+ # print(ps0-ps1,ups1)
20
+ # print(abs(ps0-ps1)<2*ups1)
21
+
22
+ import importlib
23
+ import tdcrpy
24
+ tdcrpy.TDCR_model_lib.modifyEffQ("0.1, 0.1, 0.1")
25
+ # tdcrpy.TDCR_model_lib.modifyOptModel("stochastic-dependence")
26
+ tdcrpy.TDCR_model_lib.modifyOptModel("poisson")
27
+ L = [1.0, 1.0, 1.0]
28
+ e_q = [100]
29
+ diffP = 1
30
+ importlib.reload(tdcrpy.TDCR_model_lib)
31
+
32
+ Q = tdcrpy.TDCR_model_lib.readEffQ0()
33
+ Q = Q.split(",")
34
+ Q = [float(i) for i in Q]
35
+ QL = [float(Qi)*L[i] for i, Qi in enumerate(Q)]
36
+
37
+ e_q2 = [0]; t1 = 0; evenement = 1; extDT = 50; measTime = 60000
38
+
39
+ S,D,T,_,_,_,_ = tdcrpy.TDCR_model_lib.detectProbabilities(QL, e_q, e_q2, t1, evenement, extDT, measTime)
40
+ SmcI=[];DmcI=[];TmcI=[]
41
+ nIter=100000
42
+ for i in range(nIter):
43
+ Smc,Dmc,Tmc,_,_,_,_ = tdcrpy.TDCR_model_lib.detectProbabilitiesMC(L, e_q, e_q2, t1, evenement, extDT, measTime, dispParam=True)
44
+ SmcI.append(Smc); DmcI.append(Dmc); TmcI.append(Tmc)
45
+
46
+ print('\n')
47
+ tdcrpy.TDCR_model_lib.readParameters(disp=True)
48
+
49
+ print("\nEffQ = ", Q, "\tEffQ*L = ", QL, "\n")
50
+
51
+ print("\nEFF, EFFmc, +/-")
52
+ print("single eff = ",round(S,4),round(np.mean(SmcI),4),round(np.std(SmcI)/np.sqrt(nIter),4))
53
+ print("double eff = ",round(D,4),round(np.mean(DmcI),4),round(np.std(DmcI)/np.sqrt(nIter),4))
54
+ print("triple eff = ",round(T,4),round(np.mean(TmcI),4),round(np.std(TmcI)/np.sqrt(nIter),4))
55
+ print('\nDEVIATION < 2 sigma')
56
+ print("single eff = ",abs(round(S,4)-round(np.mean(SmcI),4))<2*round(np.std(SmcI)/np.sqrt(nIter),4))
57
+ print("double eff = ",abs(round(D,4)-round(np.mean(DmcI),4))<2*round(np.std(DmcI)/np.sqrt(nIter),4))
58
+ print("triple eff = ",abs(round(T,4)-round(np.mean(TmcI),4))<2*round(np.std(TmcI)/np.sqrt(nIter),4))
59
+ print('\nPRECISION')
60
+ print("single eff = ",round(100*np.std(SmcI)/(np.sqrt(nIter)*round(np.mean(SmcI),4)),4)," %")
61
+ print("double eff = ",round(100*np.std(DmcI)/(np.sqrt(nIter)*round(np.mean(DmcI),4)),4)," %")
62
+ print("triple eff = ",round(100*np.std(TmcI)/(np.sqrt(nIter)*round(np.mean(TmcI),4)),4)," %")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: TDCRPy
3
- Version: 2.11.0
3
+ Version: 2.13.0
4
4
  Summary: TDCR model
5
5
  Home-page: https://pypi.org/project/TDCRPy/
6
6
  Author: RomainCoulon (Romain Coulon)
@@ -1,10 +1,11 @@
1
- tdcrpy/TDCRPy.py,sha256=VIwZJHtpjBt5e87KUqatCC6eTMr2sFVRl52LAc3PZq8,67850
1
+ tdcrpy/TDCRPy.py,sha256=vr3adVMfSRW6bNXZXJnvjwhhuql39nCAHEFFi_1S2Mo,67863
2
2
  tdcrpy/TDCRPy1.py,sha256=QTBZh5B5JWnGB0BQfD-cFmwA9W080OD4sG-aj50-ejo,38106
3
- tdcrpy/TDCR_model_lib.py,sha256=SIC_NRJuZHPW_kXsKksicXyooHula3y-Yd2LKlBDpig,133717
3
+ tdcrpy/TDCR_model_lib.py,sha256=F2n2yB20WQ2ivBgjnmUOZ0sVmilMSYxmYSjnUxR_UDU,138156
4
4
  tdcrpy/TDCRoptimize.py,sha256=c2XIGveeLdVYYek4Rg6dygMvVA2xIrIkMb3L-_jUucM,6496
5
5
  tdcrpy/__init__.py,sha256=9Djir8dPNchcJVQvhl-oRHEOsoDkiZlkOhWT-eHR7wQ,95
6
- tdcrpy/config.toml,sha256=zwVBgi2xtHri9WVcipksO7jHaeuJXGXks4tYhzoRHFY,1646
6
+ tdcrpy/config.toml,sha256=UvIV6oUFjkk96c0Z053l14vekVc0eZ2-C0xy8MTs2zQ,1725
7
7
  tdcrpy/test2.py,sha256=poLLXJyIaCeqh1VSkwgbi-udvY7lQjxz_YStKjJXGhU,501
8
+ tdcrpy/test_randomGen.py,sha256=CWN8jWJ3VAu6WhkRIR9BHv-e4V4hGx5jSfWBqGE0TT4,2195
8
9
  tdcrpy/MCNP-MATRIX/Spectra_for_analytical_model/dep_spectrum_C-14.txt,sha256=Eh3KaNbfYHakk_uStLu8K1aFciO6_i_rS2yKxGGppDE,8416
9
10
  tdcrpy/MCNP-MATRIX/Spectra_for_analytical_model/dep_spectrum_Ca-45.txt,sha256=TymodcK4ttoO1duZuW3RGOwHFwPPzw2ESPc_H_QQN8k,8830
10
11
  tdcrpy/MCNP-MATRIX/Spectra_for_analytical_model/dep_spectrum_Co-60.txt,sha256=kxD5E7tk_Gc1Ylg8qCG1r3oB21m7wUT4zBWsmbseiMU,40203
@@ -74,8 +75,8 @@ tdcrpy/docs/_build/html/source/modules.html,sha256=Jf-qxVBId0UgpwyvYuyjtMNG-ezPO
74
75
  tdcrpy/docs/_build/html/source/tdcrpy.html,sha256=-38lHMNFB22p1tWJEeN3yDqfDiCYE304vxDamO1-iRc,3779
75
76
  tdcrpy/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
77
  tdcrpy/test/test_tdcrpy.py,sha256=JINqSEMFoNpptE4f3h6ZzTYW1rBx90KkaoQzltSg-No,4692
77
- tdcrpy-2.11.0.dist-info/licenses/LICENCE.md,sha256=ZTpWyGU3qv_iwEpgvCijoCuCYpOPpyzJCgOk46WpUKU,1066
78
- tdcrpy-2.11.0.dist-info/METADATA,sha256=PqaOFwuqYUdhziM8kGpVOMSRxCUvF5zY-U3R1QWGjic,45299
79
- tdcrpy-2.11.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
80
- tdcrpy-2.11.0.dist-info/top_level.txt,sha256=f4vzFFcKSEnonAACs0ZXuRczmroLLqtPTqXFymU_VU0,14
81
- tdcrpy-2.11.0.dist-info/RECORD,,
78
+ tdcrpy-2.13.0.dist-info/licenses/LICENCE.md,sha256=ZTpWyGU3qv_iwEpgvCijoCuCYpOPpyzJCgOk46WpUKU,1066
79
+ tdcrpy-2.13.0.dist-info/METADATA,sha256=LT4cj01QHlDQIohIcSeAM5wIcF_e1hK1cZK6i9bZwaY,45299
80
+ tdcrpy-2.13.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
81
+ tdcrpy-2.13.0.dist-info/top_level.txt,sha256=f4vzFFcKSEnonAACs0ZXuRczmroLLqtPTqXFymU_VU0,14
82
+ tdcrpy-2.13.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5