TDCRPy 1.3.0__py3-none-any.whl → 1.5.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/TDCRPy1.py ADDED
@@ -0,0 +1,672 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Mon Jan 23 16:01:49 2023
4
+
5
+ A Monte-Carlo code to calculate detection efficiency in TDCR measurements
6
+
7
+ @author: Romain Coulon, Jialin Hu
8
+ Bureau International des Poids et Mesures
9
+ """
10
+
11
+ ## IMPORT PYTHON MODULES
12
+ # import tdcrpy.TDCR_model_lib as tl
13
+ import TDCR_model_lib as tl
14
+ import importlib.resources
15
+ from importlib.resources import files
16
+ import configparser
17
+ import numpy as np
18
+ from tqdm import tqdm
19
+
20
+
21
+ def TDCR_relaxation(L, TD, TAB, TBC, TAC, daughter,Rad, kB, V,particle_vec, energy_vec,mode2,e_sum,level_before_trans,nE_alpha,nE_electron,uncData=False,Display=False,delay=False):
22
+ efficiency_S = []
23
+ efficiency_D = []
24
+ efficiency_T = []
25
+ efficiency_AB = []
26
+ efficiency_BC = []
27
+ efficiency_AC = []
28
+ for i_part in range(len(particle_vec)):
29
+ relaxation = False
30
+ if "Atom_K" in particle_vec[i_part] or "Atom_L" in particle_vec[i_part] or "Atom_M" in particle_vec[i_part]:
31
+ relaxation = True
32
+ while relaxation:
33
+ tf,ef = tl.relaxation_atom(daughter,Rad,particle_vec[i_part])
34
+ if tf == "XKA":
35
+ particle_vec[i_part] = "Atom_L"
36
+ particle_vec.append(tf)
37
+ energy_vec.append(ef)
38
+ relaxation = True
39
+ elif tf == "XKB":
40
+ particle_vec[i_part] = "Atom_M"
41
+ particle_vec.append(tf)
42
+ energy_vec.append(ef)
43
+ relaxation = False
44
+ elif tf == "XL":
45
+ particle_vec[i_part] = "Atom_M"
46
+ particle_vec.append(tf)
47
+ energy_vec.append(ef)
48
+ relaxation = False
49
+ elif tf == "Auger K":
50
+ particle_vec[i_part] = "Atom_L"
51
+ particle_vec.append(tf)
52
+ energy_vec.append(ef)
53
+ relaxation = True
54
+ elif tf == "Auger L":
55
+ particle_vec[i_part] = "Atom_M"
56
+ particle_vec.append(tf)
57
+ energy_vec.append(ef)
58
+ relaxation = False
59
+ else:
60
+ if Display: print("untermined x or Auger")
61
+ relaxation = False
62
+ e_sum += ef
63
+ if Display:
64
+ if delay:
65
+ print("\t Summary of the atomic relaxation(delayed emission)")
66
+ else:
67
+ print("\t Summary of the atomic relaxation(promt emission)")
68
+ for i, p in enumerate(particle_vec):
69
+ if p[:4] != "Atom":
70
+ print(f"\t\t energy of {p} = ", round(energy_vec[i],3), "keV")
71
+
72
+ for i, p in enumerate(particle_vec):
73
+ if p == "beta":
74
+ e_b,p_b = tl.readBetaShape(Rad,"beta-",level_before_trans) # read the data of BetaShape
75
+ index_beta_energy = tl.sampling(p_b) # sampling energy of beta
76
+ particle_vec[i] = "electron"
77
+ energy_vec[i] = e_b[index_beta_energy]
78
+
79
+ if p == "beta+":
80
+ e_b,p_b = tl.readBetaShape(Rad,"beta+",level_before_trans)
81
+ index_beta_energy = tl.sampling(p_b)
82
+ particle_vec[i] = "positron"
83
+ energy_vec[i] = e_b[index_beta_energy]
84
+ particle_vec.append("gamma")
85
+ particle_vec.append("gamma")
86
+ energy_vec.append(511)
87
+ energy_vec.append(511)
88
+ energy_vec_initial = energy_vec
89
+ if Display:
90
+ if delay:
91
+ print("\t Summary of emitted particles (delayed emission)")
92
+ else:
93
+ print("\t Summary of emitted particles (promt emission)")
94
+ for i, p in enumerate(particle_vec):
95
+ if p[:4] != "Atom": print(f"\t\t energy of {p} = ", round(energy_vec[i],3), "keV")
96
+
97
+ for i, p in enumerate(particle_vec):
98
+ if p == "electron":
99
+ energy_vec[i] = tl.energie_dep_beta(energy_vec[i])
100
+
101
+ if p == "beta+":
102
+ energy_vec[i] = tl.energie_dep_beta(energy_vec[i])
103
+
104
+ if p == "gamma" or p == "XKA" or p == "XKB" or p == "XL":
105
+ energy_vec[i] = tl.energie_dep_gamma(energy_vec[i],v=V) # sampling energy free from photon
106
+ particle_vec[i] = "electron"
107
+
108
+ if p == "Auger K" or p == "Auger L":
109
+ particle_vec[i] = "electron"
110
+ energy_vec[i] = tl.energie_dep_beta(energy_vec[i])
111
+
112
+ if Display:
113
+ if delay:
114
+ print("\t Summary of the energy deposited by charged particles (delayed emission)")
115
+ else:print("\t Summary of the energy deposited by charged particles (promt emission)")
116
+ for i, p in enumerate(particle_vec):
117
+ if p[:4] != "Atom": print(f"\t\t energy of {p} = ", round(energy_vec[i],3), "keV")
118
+
119
+ if Display:
120
+ if delay:
121
+ print("\t Summary of the estimation of quenched energies (delayed emission)")
122
+ else:
123
+ print("\t Summary of the estimation of quenched energies (promt emission)")
124
+ e_quenching=[]
125
+ for i, p in enumerate(particle_vec):
126
+ if p == "alpha":
127
+ energy_vec[i] = tl.Em_a(energy_vec[i],kB,nE_alpha)
128
+ e_quenching.append(energy_vec[i])
129
+ elif p == "electron" or p == "positron":
130
+ energy_vec[i] = tl.Em_e(energy_vec_initial[i]*1e3,energy_vec[i]*1e3,kB*1e3,nE_electron)*1e-3
131
+ e_quenching.append(energy_vec[i])
132
+ else:
133
+ e_quenching.append(0)
134
+ if Display: print("\t\t Birks constant = ", kB, ' cm/keV')
135
+ if Display:
136
+ for i, p in enumerate(particle_vec):
137
+ if p[:4] != "Atom": print(f"\t\t quenched energy of {p} = ", round(e_quenching[i],3), "keV")
138
+
139
+ if mode2=="sym":
140
+ p_nosingle = np.exp(-L*np.sum(np.asarray(e_quenching))/3) # probability to have 0 electrons in a PMT
141
+ p_single = 1-p_nosingle # probability to have at least 1 electrons in a PMT
142
+ efficiency_S.append(p_single)
143
+ efficiency_T.append(p_single**3)
144
+ efficiency_D.append(3*(p_single)**2-2*efficiency_T[-1])
145
+ if Display:
146
+ if delay:
147
+ print("\t Summary of TDCR measurement (delayed emission)")
148
+ else:
149
+ print("\t Summary of TDCR measurement (promt emission)")
150
+ if Display: print("\t\t Free parameter = ", L, "keV-1")
151
+ if Display: print("\t\t Efficiency of single events = ", round(efficiency_S[-1],5))
152
+ if Display: print("\t\t Efficiency of double events = ", round(efficiency_D[-1],5))
153
+ if Display: print("\t\t Efficiency of triple events = ", round(efficiency_T[-1],5))
154
+
155
+ elif mode2=="asym":
156
+ pA_nosingle = np.exp(-L[0]*np.sum(np.asarray(e_quenching))/3) # probability to have 0 electrons in a PMT
157
+ pA_single = 1-pA_nosingle # probability to have at least 1 electrons in a PMT
158
+ pB_nosingle = np.exp(-L[1]*np.sum(np.asarray(e_quenching))/3) # probability to have 0 electrons in a PMT
159
+ pB_single = 1-pB_nosingle # probability to have at least 1 electrons in a PMT
160
+ pC_nosingle = np.exp(-L[2]*np.sum(np.asarray(e_quenching))/3) # probability to have 0 electrons in a PMT
161
+ pC_single = 1-pC_nosingle # probability to have at least 1 electrons in a PMT
162
+
163
+ efficiency_AB.append(pA_single*pB_single)
164
+ efficiency_BC.append(pB_single*pC_single)
165
+ efficiency_AC.append(pA_single*pC_single)
166
+ efficiency_T.append(pA_single*pB_single*pC_single)
167
+ efficiency_D.append(efficiency_AB[-1]+efficiency_BC[-1]+efficiency_AC[-1]-2*efficiency_T[-1])
168
+ efficiency_S.append(pA_single+pB_single+pC_single-efficiency_D[-1]-efficiency_T[-1])
169
+ if Display:
170
+ if delay:
171
+ print("\t Summary of TDCR measurement (delayed emission)")
172
+ else:
173
+ print("\t Summary of TDCR measurement (promt emission)")
174
+ if Display: print("\t\t Free parameter PMT A: ", L[0], "keV-1")
175
+ if Display: print("\t\t Free parameter PMT B: ", L[1], "keV-1")
176
+ if Display: print("\t\t Free parameter PMT C: ", L[2], "keV-1")
177
+ if Display: print("\t\t Efficiency of single events: ", round(efficiency_S[-1],5))
178
+ if Display: print("\t\t Efficiency of double events: ", round(efficiency_D[-1],5))
179
+ if Display: print("\t\t Efficiency of triple events: ", round(efficiency_T[-1],5))
180
+
181
+
182
+ return efficiency_T,efficiency_D,efficiency_S,efficiency_AB,efficiency_AC,efficiency_BC
183
+
184
+
185
+
186
+
187
+ def TDCRPy(L, TD, TAB, TBC, TAC, Rad, pmf_1, N, kB, V, mode, mode2, Display=False, barp=False,uncData=False):
188
+ """
189
+ This is the main function of the TDCRPy package running the Monte-Carlo Triple-to-Double Coincidence Ratio model.
190
+ The computation is made for a given solution containing a radionuclide (or a mixture of radionuclides), a given volume of scintillator V and a given Birks constant kB.
191
+
192
+ It can operates in two modes:
193
+
194
+ --> In mode="eff", it calculates the efficiency of the TDCR system as a function of a value (triplet) of free parameter(s) L, the measurement data is not used;
195
+
196
+ --> In mode="res", it calculates the residual of the TDCR model parametrized by a value (or triplet) of free parameter(s) L and the measurement data TD, TAB, TBC, TAC.
197
+
198
+ also, two configuration can be set:
199
+
200
+ --> mode2="sym", where symmetry is considered between the 3 photomultiplier tubes - here L is a scalar and only the global TDCR value TD is used as measurement data.
201
+
202
+ --> mode2="asym", where an asymmetry between the 3 photomultiplier tubes is possible - here L is a triplet and only the specific TDCR values TAB, TBC, TAC are used as measurement data.
203
+
204
+ The parmeter N sets the number of Monte-Carlo trails used for the estimation. Each MC trial corresponds to a simulated radiactive decay.
205
+ TDCRPY() used a set of fonctions from the tdcrpy.TDCR_model_lib module.
206
+
207
+ Advanced settings can be configured in the config.toml file.
208
+
209
+ --> By default Y = True so that the analytical model is applied for solution containing only pure beta emitting radionuclides. If you would like to apply the MC calculation also for these nuclides, set Y = False.
210
+
211
+ --> If you would like to change the number of bins nE to discretize the linear energy space for quenching calculation, you can change nE_electron and nE_alpha parameters for respectively electrons and alpha particles.
212
+
213
+ --> By default the calculation is set for Ultima-Gold cocktail mixed with a small amount of aqueous solution. You can adapt for a specific scintillator by changing the density, the mean charge number Z and the mean mass number A of the scintillator.
214
+
215
+
216
+ Parameters
217
+ ----------
218
+ L : Float (if mode2="sym") or a tuple (if mode2="asym")
219
+ Free parameter in keV-1.
220
+ TD : float
221
+ triple-to-double coincidence ratio. Not consider if mode2="asym". Not consider if mode2="asym".
222
+ TAB : float
223
+ triple-to-double coincidence ratio (coincidences between channel A and B). Not consider if mode2="sym".
224
+ TBC : float
225
+ triple-to-double coincidence ratio (coincidences between channel B and C). Not consider if mode2="sym".
226
+ TAC : float
227
+ triple-to-double coincidence ratio (coincidences between channel A and C). Not consider if mode2="sym".
228
+ Rad : string
229
+ List of radionuclides (eg. "H-3, Co-60").
230
+ pmf_1 : string
231
+ list of probability of each radionuclide (eg. "0.8, 0.2").
232
+ N : integer
233
+ Number of Monte-Carlo trials. recommanded N>10000 (see JCGM 101). Not applied in the case of pure beta emitting radionuclides.
234
+ kB : float
235
+ Birks constant in cm/keV.
236
+ V : float
237
+ volume of the scintillator in ml.
238
+ mode : string
239
+ "res" to return the residual, "eff" to return efficiencies.
240
+ mode2 : string
241
+ "sym" for symetrical model, "asym" for symetrical model.
242
+ Display : Boolean, optional
243
+ "True" to display details on the decay sampling. The default is False.
244
+ barp : Boolean, optional
245
+ "True" to display the calculation progress. The default is True.
246
+
247
+ Returns
248
+ -------
249
+ res : float
250
+ Residuals of the model compared the measurement data for (a) given free parmeters L. (only in mode="res")
251
+ mean_efficiency_S : float
252
+ Estimation of the efficiency of single counting events. (only in mode="eff")
253
+ std_efficiency_S : float
254
+ Standard uncertainty from calculation associated with the estimation of the efficiency of single counting events. (only in mode="eff")
255
+ mean_efficiency_D : float
256
+ Estimation of the efficiency of logic sum of double coincidences. (only in mode="eff")
257
+ std_efficiency_D : float
258
+ Standard uncertainty from calculation associated with the estimation of the efficiency of logic sum of double coincidences. (only in mode="eff")
259
+ mean_efficiency_T : float
260
+ Estimation of the efficiency of triple coincidences. (only in mode="eff")
261
+ std_efficiency_T : float
262
+ Standard uncertainty from calculation associated with the estimation of the efficiency of triple coincidences. (only in mode="eff")
263
+ """
264
+ if barp: tl.display_header()
265
+ config = configparser.ConfigParser()
266
+ with importlib.resources.as_file(files('tdcrpy').joinpath('config.toml')) as data_path:
267
+ file_conf = data_path
268
+ config.read(file_conf)
269
+ tau=config["Inputs"].getfloat("tau")
270
+ Y=config["Inputs"].getboolean("Y")
271
+ radListPureBeta=config["Inputs"].get("radListPureBeta")
272
+ radListPureBeta=radListPureBeta.replace(" ","")
273
+ radListPureBeta=radListPureBeta.split(',')
274
+ X = Rad in radListPureBeta
275
+ if X:
276
+ nElist=config["Inputs"].get("nE")
277
+ nElist=nElist.split(',')
278
+ nElist = [int(i) for i in nElist]
279
+ if X and Y:
280
+ inE = radListPureBeta.index(Rad)
281
+ nE = nElist[inE]
282
+ out=tl.modelAnalytical(L,TD,TAB,TBC,TAC,Rad,kB,V,mode,mode2,nE)
283
+ if mode == "res":
284
+ return out
285
+ if mode == "eff":
286
+ return out[0], 0, out[1], 0, out[2], 0
287
+ else:
288
+ nE_electron = config["Inputs"].getint("nE_electron")
289
+ nE_alpha = config["Inputs"].getint("nE_alpha")
290
+ Rad=Rad.replace(" ","")
291
+ Rad=Rad.split(",")
292
+ pmf_1=pmf_1.split(",")
293
+ pmf_1 = [float(x) for x in pmf_1]
294
+
295
+ if np.size(pmf_1) > 1:
296
+ if sum(pmf_1) !=1: print("warning p not equal to 1")
297
+ elif pmf_1[0] != 1: print("warning")
298
+
299
+ """
300
+ Read PenNuc File
301
+ """
302
+ out_PenNuc = []
303
+ particle = [] # Particle(s) from the Mother -- indice 3
304
+ p_branch = [] # Probablity of the different decay of branch -- indice 5
305
+ e_branch = [] # Energy of the different decay of branch -- indice 4
306
+ LevelDaughter = [] # Level of the Daughter nucleus just after the particle emission -- indice 6
307
+ levelNumber = [] # The vector of level of the daughter to get information of all possible isomeric transitions -- indice 11
308
+ prob_trans = [] # Probability for each transition -- indice 10
309
+ prob_branch = [] # Probability for each branch -- indice 7
310
+ levelEnergy = [] # Energy of each level -- indice 13
311
+ transitionType = [] # type of each possible transitions (internal transitions or gamma emission) -- indice 8
312
+ e_trans = [] # Energy of the transition -- indice 9
313
+ next_level = [] # Next level on the daughter nucleus -- indice 12
314
+ Q_value = [] # Energy of the reaction -- indice 2
315
+ DaughterVec = [] # Daughters -- indice 0
316
+ Pdaughter = [] # Probabiblity related to daughters -- indice 1
317
+ Transition_prob_sum = []
318
+ u_prob_trans = [] # uncertainty of the probabilities for each transition -- indice 10
319
+ trans_halfLife = [] # half life of the transition
320
+
321
+ for rad_i in Rad:
322
+ out_PenNuc = tl.readPenNuc2(rad_i)
323
+ particle.append(out_PenNuc[3]) # Particle(s) from the Mother -- indice 3
324
+ p_branch.append(out_PenNuc[5]) # Probablity of the different decay of branch -- indice 5
325
+ e_branch.append(out_PenNuc[4]) # Energy of the different decay of branch -- indice 4
326
+ LevelDaughter.append(out_PenNuc[6]) # Level of the Daughter nucleus just after the particle emission -- indice 6
327
+ levelNumber.append(out_PenNuc[11]) # The vector of level of the daughter to get information of all possible isomeric transitions -- indice 11
328
+ prob_trans.append(out_PenNuc[10]) # Probability for each transition -- indice 10
329
+ prob_branch.append(out_PenNuc[7]) # Probability for each branch -- indice 7
330
+ levelEnergy.append(out_PenNuc[13]) # Energy of each level -- indice 13
331
+ transitionType.append(out_PenNuc[8]) # type of each possible transitions (internal transitions or gamma emission) -- indice 8
332
+ e_trans.append(out_PenNuc[9]) # Energy of the transition -- indice 9
333
+ next_level.append(out_PenNuc[12]) # Next level on the daughter nucleus -- indice 12
334
+ Q_value.append(out_PenNuc[2]) # Energy of the reaction -- indice 2
335
+ DaughterVec.append(out_PenNuc[0]) # Daughters -- indice 0
336
+ Pdaughter.append(out_PenNuc[1]) # Probabiblity related to daughters -- indice 1
337
+ Transition_prob_sum.append(out_PenNuc[14])
338
+ u_prob_trans.append(out_PenNuc[16])
339
+ trans_halfLife.append(out_PenNuc[15])
340
+ # print("\n",trans_halfLife)
341
+
342
+ efficiency_S = []
343
+ efficiency_D = []
344
+ efficiency_T = []
345
+ efficiency_S2 = []
346
+ efficiency_D2 = []
347
+ efficiency_T2 = []
348
+ efficiency_AB = []
349
+ efficiency_BC = []
350
+ efficiency_AC = []
351
+ efficiency_AB2 = []
352
+ efficiency_BC2 = []
353
+ efficiency_AC2 = []
354
+
355
+ if barp and not Display: NN = tqdm(range(N), desc="Processing", unit=" decays")
356
+ else: NN = range(N)
357
+ for i in NN: # Main Loop - Monte Carlo trials
358
+ particle_vec=[]
359
+ energy_vec=[]
360
+ '''
361
+ ===============================
362
+ 0. SAMPLING OF THE RADIONUCLIDE
363
+ ===============================
364
+ '''
365
+ index_rad = tl.sampling(pmf_1)
366
+ rad_i = Rad[index_rad]
367
+ if Display: print("\n Trial ",str(i+1),"- Sampled radionuclide: ", rad_i)
368
+
369
+
370
+ '''
371
+ ===========================
372
+ I. DESINTEGRATION NUCLEAIRE
373
+ ===========================
374
+ '''
375
+ #=========================
376
+ # Sampling of the daughter
377
+ #=========================
378
+ iDaughter=tl.sampling(np.asarray(Pdaughter[index_rad])/sum(np.asarray(Pdaughter[index_rad])))
379
+ Daughter = DaughterVec[index_rad][iDaughter]
380
+ if Display:
381
+ print("\t Sampled daughter:")
382
+ print("\t\t Daughter = ", Daughter)
383
+ #=============================
384
+ # Sampling of the decay branch
385
+ #=============================
386
+ branch_i = tl.normalise(prob_branch[index_rad][iDaughter]) # normalise la proba de branch
387
+ i_branch=tl.sampling(branch_i) # indice de la branche globale
388
+ if p_branch[index_rad][iDaughter][i_branch] != []:
389
+ branch_proba = tl.normalise(p_branch[index_rad][iDaughter][i_branch])
390
+ index_subBranch = tl.sampling(branch_proba) # indice de la branch precise
391
+ particle_branch = particle[index_rad][iDaughter][i_branch][index_subBranch] # sampled particle emitted by the mother
392
+ energy_branch = e_branch[index_rad][iDaughter][i_branch][index_subBranch] # energy of the particle emitted by the mother
393
+ # probability_branch = p_branch[index_rad][iDaughter][i_branch][index_subBranch] # probability of the sampled branch
394
+ levelOftheDaughter = LevelDaughter[index_rad][iDaughter][i_branch][index_subBranch] # Level of the daughter just after the particle emission from the mother
395
+ level_before_trans = LevelDaughter[index_rad][iDaughter][i_branch][index_subBranch]
396
+ if Display:
397
+ print("\t Sampled decay branch:")
398
+ if particle_branch[:4]=="Atom":
399
+ if particle_branch=="Atom_K": print("\t\t Electron capture on K shell")
400
+ if particle_branch=="Atom_L" or particle_branch=="Atom_L1" or particle_branch=="Atom_L2" or particle_branch=="Atom_L3": print("\t\t Electron capture on L shell")
401
+ if particle_branch=="Atom_M": print("\t\t Electron capture on M shell")
402
+ if particle_branch=="Atom_O": print("\t\t Electron capture on O shell")
403
+ else:
404
+ print("\t\t Particle: ", particle_branch)
405
+ print("\t\t Energy of the particle = ", energy_branch, " keV")
406
+ print("\t\t Level of the daughter nucleus: ", levelOftheDaughter)
407
+ #========
408
+ # Scoring
409
+ #========
410
+ e_sum = energy_branch # Update the Energy summary
411
+ particle_vec.append(particle_branch) # Update of the particle vector
412
+ energy_vec.append(energy_branch) # Update of the energy of the particle
413
+ else:
414
+ transition_prob = tl.normalise(Transition_prob_sum[index_rad][iDaughter])
415
+ index_transition_level = tl.sampling(transition_prob)
416
+ levelOftheDaughter = levelNumber[index_rad][iDaughter][index_transition_level][0]
417
+ if Display:
418
+ print("\t Sampled decay branch:")
419
+ print("\t\t Particle = isomeric transition, no particle")
420
+ print("\t\t Level of the nucleus : ",levelOftheDaughter)
421
+ e_sum = 0
422
+
423
+ '''
424
+ ==============
425
+ I-1 Transition
426
+ ==============
427
+ '''
428
+ if Display: print("\t Subsequent isomeric transition(s)") # finish with the mother / now with the daughter
429
+ evenement = 1
430
+ e_sum2 = 0
431
+ particle_vec2 = []
432
+ energy_vec2 = []
433
+ while levelOftheDaughter > 0: # Go on the loop while the daughter nucleus is a its fundamental level (energy 0)
434
+ i_level = levelNumber[index_rad][iDaughter].index([levelOftheDaughter]) # Find the position in the daughter level vector
435
+
436
+ t1 = np.random.exponential(trans_halfLife[index_rad][iDaughter][i_level][0], size=1)[0]
437
+
438
+ # test whether the decay occurs within the coincidence resolving time or not
439
+ if t1 > tau*1e-9:
440
+ #splitEvent = True
441
+ evenement = evenement + 1
442
+
443
+ if transitionType[index_rad][iDaughter][i_level] != []:
444
+ #====================================================================
445
+ # Sampling of the transition in energy levels of the daughter nucleus
446
+ #====================================================================
447
+
448
+ if uncData:
449
+ prob_trans_s=[]
450
+ for ipt, xpt in enumerate(prob_trans[index_rad][iDaughter][i_level]):
451
+ prob_trans_s.append(np.random.normal(xpt, u_prob_trans[index_rad][iDaughter][i_level][ipt], 1)[0])
452
+
453
+ probability_tran = tl.normalise(prob_trans_s) # normaliser la proba de transition
454
+ else:
455
+ probability_tran = tl.normalise(prob_trans[index_rad][iDaughter][i_level]) # normaliser la proba de transition
456
+ #probability_tran = tl.normalise(prob_trans[index_rad][iDaughter][i_level]) # normaliser la proba de transition
457
+ index_t = tl.sampling(probability_tran) # indice de la transition
458
+ if Display:
459
+ print("\t\t Energy of the level = ", levelEnergy[index_rad][iDaughter][i_level][0], " keV")
460
+ trans = transitionType[index_rad][iDaughter][i_level][index_t]
461
+ if trans == "GA":
462
+ print("\t\t Energy of the gamma ray = ", e_trans[index_rad][iDaughter][i_level][index_t], "keV")
463
+ elif trans == "EK":
464
+ print("\t\t Energy of the conversion electron from K shell = ", e_trans[index_rad][iDaughter][i_level][index_t], "keV")
465
+ elif trans == "EL":
466
+ print("\t\t Energy of the conversion electron from L shell = ", e_trans[index_rad][iDaughter][i_level][index_t], "keV")
467
+ elif trans == "EM":
468
+ print("\t\t Energy of the conversion electron from M shell = ", e_trans[index_rad][iDaughter][i_level][index_t], "keV")
469
+ print("\t\t next level = ", next_level[index_rad][iDaughter][i_level][index_t])
470
+ #========
471
+ # Scoring
472
+ #========
473
+
474
+ ## evenement retardé
475
+ if evenement != 1:
476
+ if transitionType[index_rad][iDaughter][i_level][index_t] == "GA": # if it is a gamma that has been emitted
477
+ particle_vec2.append("gamma") # Update of the particle vector
478
+ energy_vec2.append(e_trans[index_rad][iDaughter][i_level][index_t]) # Update the energy vector
479
+ else: # if not, it is a internal conversion, so an electron
480
+ particle_vec2.append("electron") # !!!!!!!!! it is OK for our model? Does the electron leave with the kinetic enegy of the transition
481
+ energy_vec2.append(e_trans[index_rad][iDaughter][i_level][index_t]) # Update the energy vector
482
+ if transitionType[index_rad][iDaughter][i_level][index_t] == "EK": # record that an electron is missing on the K shell of the dughter nucleus
483
+ particle_vec2.append("Atom_K")
484
+ energy_vec2.append(0)
485
+
486
+ if transitionType[index_rad][iDaughter][i_level][index_t] == "EL": # record that an electron is missing on the L1 shell of the dughter nucleus
487
+ particle_vec2.append("Atom_L")
488
+ energy_vec2.append(0)
489
+
490
+ if transitionType[index_rad][iDaughter][i_level][index_t] == "EL1": # record that an electron is missing on the L1 shell of the dughter nucleus
491
+ particle_vec2.append("Atom_L1")
492
+ energy_vec2.append(0)
493
+
494
+ if transitionType[index_rad][iDaughter][i_level][index_t] == "EL2": # record that an electron is missing on the L2 shell of the dughter nucleus
495
+ particle_vec2.append("Atom_L2")
496
+ energy_vec2.append(0)
497
+
498
+ if transitionType[index_rad][iDaughter][i_level][index_t] == "EL3": # record that an electron is missing on the L3 shell of the dughter nucleus
499
+ particle_vec2.append("Atom_L3")
500
+ energy_vec2.append(0)
501
+
502
+ if transitionType[index_rad][iDaughter][i_level][index_t] == "EM": # record that an electron is missing on the M shell of the dughter nucleus
503
+ particle_vec2.append("Atom_M")
504
+ energy_vec2.append(0)
505
+
506
+ if transitionType[index_rad][iDaughter][i_level][index_t] == "EN": # record that an electron is missing on the N shell of the dughter nucleus
507
+ particle_vec2.append("Atom_N")
508
+ energy_vec2.append(0)
509
+ e_sum2 += e_trans[index_rad][iDaughter][i_level][index_t] # Energy summary
510
+
511
+
512
+ ## evenement normal
513
+ else:
514
+ if transitionType[index_rad][iDaughter][i_level][index_t] == "GA": # if it is a gamma that has been emitted
515
+ particle_vec.append("gamma") # Update of the particle vector
516
+ energy_vec.append(e_trans[index_rad][iDaughter][i_level][index_t]) # Update the energy vector
517
+ else: # if not, it is a internal conversion, so an electron
518
+ particle_vec.append("electron") # !!!!!!!!! it is OK for our model? Does the electron leave with the kinetic enegy of the transition
519
+ energy_vec.append(e_trans[index_rad][iDaughter][i_level][index_t]) # Update the energy vector
520
+ if transitionType[index_rad][iDaughter][i_level][index_t] == "EK": # record that an electron is missing on the K shell of the dughter nucleus
521
+ particle_vec.append("Atom_K")
522
+ energy_vec.append(0)
523
+
524
+ if transitionType[index_rad][iDaughter][i_level][index_t] == "EL": # record that an electron is missing on the L1 shell of the dughter nucleus
525
+ particle_vec.append("Atom_L")
526
+ energy_vec.append(0)
527
+
528
+ if transitionType[index_rad][iDaughter][i_level][index_t] == "EL1": # record that an electron is missing on the L1 shell of the dughter nucleus
529
+ particle_vec.append("Atom_L1")
530
+ energy_vec.append(0)
531
+
532
+ if transitionType[index_rad][iDaughter][i_level][index_t] == "EL2": # record that an electron is missing on the L2 shell of the dughter nucleus
533
+ particle_vec.append("Atom_L2")
534
+ energy_vec.append(0)
535
+
536
+ if transitionType[index_rad][iDaughter][i_level][index_t] == "EL3": # record that an electron is missing on the L3 shell of the dughter nucleus
537
+ particle_vec.append("Atom_L3")
538
+ energy_vec.append(0)
539
+
540
+ if transitionType[index_rad][iDaughter][i_level][index_t] == "EM": # record that an electron is missing on the M shell of the dughter nucleus
541
+ particle_vec.append("Atom_M")
542
+ energy_vec.append(0)
543
+
544
+ if transitionType[index_rad][iDaughter][i_level][index_t] == "EN": # record that an electron is missing on the N shell of the dughter nucleus
545
+ particle_vec.append("Atom_N")
546
+ energy_vec.append(0)
547
+ e_sum += e_trans[index_rad][iDaughter][i_level][index_t] # Energy summary
548
+
549
+
550
+
551
+ levelOftheDaughter = next_level[index_rad][iDaughter][i_level][index_t] # set the next level
552
+
553
+ else:
554
+ i_level = levelNumber[index_rad][iDaughter].index([levelOftheDaughter])
555
+ print("warning:pas de données de transition:daughter,niveau,niveau d'énergie",DaughterVec[index_rad][iDaughter],levelOftheDaughter,levelEnergy[index_rad][iDaughter][i_level] )
556
+ levelOftheDaughter = 0 # set the next level
557
+
558
+ if Display:
559
+ print("\t Summary of the nuclear promt decay")
560
+ for i, p in enumerate(particle_vec):
561
+ if p[:4] != "Atom":
562
+ print('particle :',p)
563
+ print(f"\t\t energy of {p} = ", energy_vec[i]," keV")
564
+ if evenement != 0:
565
+ print("\t Summary of the nuclear delayed decay")
566
+ for i, p in enumerate(particle_vec2):
567
+ if p[:4] != "Atom":
568
+ print(f"\t\t energy of {p} = ", energy_vec2[i]," keV")
569
+
570
+ '''
571
+ ==========================
572
+ II. LA RELAXATION ATOMIQUE
573
+ ==========================
574
+ '''
575
+
576
+ daughter_relax = DaughterVec[index_rad][iDaughter]
577
+ efficiency_T,efficiency_D,efficiency_S,efficiency_AB,efficiency_AC,efficiency_BC = TDCR_relaxation(L,TD,TAB,TBC,TAC,daughter_relax,Rad[index_rad],kB,V,particle_vec,energy_vec,mode2,e_sum,level_before_trans,nE_alpha,nE_electron,uncData=uncData,Display=Display,delay=False)
578
+
579
+
580
+ ## evenement retarde
581
+
582
+ if evenement != 1:
583
+ delay = True
584
+ if Display:print(" taitement de delayed emission")
585
+ efficiency_T2,efficiency_D2,efficiency_S2,efficiency_AB2,efficiency_AC2,efficiency_BC2 = TDCR_relaxation(L,TD,TAB,TBC,TAC,daughter_relax,Rad[index_rad],kB,V,particle_vec2,energy_vec2,mode2,e_sum2,level_before_trans,nE_alpha,nE_electron,uncData=uncData,Display=Display,delay=delay)
586
+
587
+
588
+
589
+
590
+
591
+ '''
592
+ ====================
593
+ VI. CALCULATION OF THE FINAL ESTIMATORS
594
+ ====================
595
+ '''
596
+ mean_efficiency_T = np.mean(efficiency_T) # average
597
+ std_efficiency_T = np.std(efficiency_T)/np.sqrt(N) # standard deviation
598
+ std_efficiency_T = np.sqrt(std_efficiency_T**2+1e-8) # combined with uncertainty due to quenching calculation
599
+ mean_efficiency_D = np.mean(efficiency_D)
600
+ std_efficiency_D = np.std(efficiency_D)/np.sqrt(N)
601
+ std_efficiency_D = np.sqrt(std_efficiency_D**2+1e-8)
602
+ mean_efficiency_S = np.mean(efficiency_S)
603
+ std_efficiency_S = np.std(efficiency_S)/np.sqrt(N)
604
+ std_efficiency_S = np.sqrt(std_efficiency_S**2+1e-8)
605
+ if mode2=="sym":
606
+ TDCR_calcul = mean_efficiency_T/mean_efficiency_D
607
+ elif mode2=="asym":
608
+ mean_efficiency_AB = np.mean(efficiency_AB)
609
+ std_efficiency_AB = np.std(efficiency_AB)/np.sqrt(N)
610
+ mean_efficiency_BC = np.mean(efficiency_BC)
611
+ std_efficiency_BC = np.std(efficiency_BC)/np.sqrt(N)
612
+ mean_efficiency_AC = np.mean(efficiency_AC)
613
+ std_efficiency_AC = np.std(efficiency_AC)/np.sqrt(N)
614
+ TDCR_calcul = mean_efficiency_T/mean_efficiency_D
615
+ TABmodel = mean_efficiency_T/mean_efficiency_AB
616
+ TBCmodel = mean_efficiency_T/mean_efficiency_BC
617
+ TACmodel = mean_efficiency_T/mean_efficiency_AC
618
+
619
+
620
+ # x = np.arange(np.mean(efficiency_T),1.001,0.001)
621
+ # plt.figure("efficiency distribution")
622
+ # plt.clf()
623
+ # plt.hist(np.asarray(efficiency_D),bins=x,label="Efficiency of double coincidences")[0]
624
+ # plt.hist(np.asarray(efficiency_T),bins=x,label="Efficiency of triple coincidences")[0]
625
+ # plt.xlabel("Efficiency", fontsize = 14)
626
+ # plt.ylabel(r"Number of counts", fontsize = 14)
627
+ # plt.legend(fontsize = 12)
628
+ # plt.savefig('Effdistribution.png')
629
+
630
+ # x = np.arange(np.mean(tdcr),1.001,0.001)
631
+ # plt.figure("TDCR distribution")
632
+ # plt.clf()
633
+ # plt.hist(np.asarray(tdcr),bins=x,label="Calculated TDCR")[0]
634
+ # plt.plot(x,st.norm.pdf(x, TDCR_measure, u_TDCR_measure),label="measured TDCR")[0]
635
+ # plt.xlabel("Efficiency", fontsize = 14)
636
+ # plt.ylabel(r"Number of counts", fontsize = 14)
637
+ # plt.legend(fontsize = 12)
638
+ # plt.savefig('TDCRdistribution.png')
639
+
640
+ if mode2=="sym":
641
+ res=(TDCR_calcul-TD)**2
642
+ elif mode2=="asym":
643
+ res=(TAB-TABmodel)**2+(TBC-TBCmodel)**2+(TAC-TACmodel)**2
644
+
645
+ if mode == "res":
646
+ return res
647
+ if mode == "eff":
648
+ if N<200:
649
+ print("Warning. too low number of MC trials - inaccurate estimation")
650
+ return mean_efficiency_S, 1, mean_efficiency_D, 1, mean_efficiency_T, 1
651
+ else:
652
+ return mean_efficiency_S, std_efficiency_S, mean_efficiency_D, std_efficiency_D, mean_efficiency_T, std_efficiency_T
653
+ if mode =="dis":
654
+ return efficiency_S, efficiency_D, efficiency_T
655
+
656
+ L = (1, 1, 1)
657
+ #L = 1
658
+ TD = 0.977667386529166
659
+ TAB = 0.992232838598821
660
+ TBC = 0.992343419459002
661
+ TAC = 0.99275350064608
662
+ Rad="Fe-55"
663
+ pmf_1="1"
664
+ N = 10
665
+ kB =1.0e-5
666
+ V = 10
667
+ mode = "eff"
668
+ mode2 = "asym"
669
+
670
+
671
+ s,t,d,q,w,e = TDCRPy(L, TD, TAB, TBC, TAC, Rad, pmf_1, N, kB, V, mode, mode2, Display=True, barp=False,uncData=False)
672
+ #print(s,t,d,q,w,e)