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-1.3.0.dist-info → TDCRPy-1.5.0.dist-info}/METADATA +1 -1
- {TDCRPy-1.3.0.dist-info → TDCRPy-1.5.0.dist-info}/RECORD +9 -7
- tdcrpy/MCNP-MATRIX/matrice/fichier/matrice_16beta-_2000_10000k.txt +1003 -0
- tdcrpy/TDCRPy.py +351 -218
- tdcrpy/TDCRPy1.py +672 -0
- tdcrpy/TDCR_model_lib.py +35 -34
- {TDCRPy-1.3.0.dist-info → TDCRPy-1.5.0.dist-info}/LICENCE.md +0 -0
- {TDCRPy-1.3.0.dist-info → TDCRPy-1.5.0.dist-info}/WHEEL +0 -0
- {TDCRPy-1.3.0.dist-info → TDCRPy-1.5.0.dist-info}/top_level.txt +0 -0
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)
|