TkN 2.1
Toolkit for Nuclei
decay_generator.C
1#include "Riostream.h"
2#include "TH2F.h"
3#include "TROOT.h"
4#include "TMath.h"
5#include "TRandom3.h"
6#include "TCanvas.h"
7#include "TSystem.h"
8#include "TLatex.h"
9#include "TStyle.h"
10
11#include <random>
12
13#include "tkmanager.h"
14#include "tknuclear_chart.h"
15
16using namespace tkn;
17
18// enumeration of the available decay modes
19enum decay_types{kEF,kEP,kBN,kB2N,kB3N,kstable,kproton, kdiproton, kneutron, kdineutron, kalpha, kbeta_plus, kdouble_beta_plus, kdouble_beta_minus ,kbeta_minus, kfission, kunknown};
20
21// nucleus structure containing lifetime and first decay mode
22struct nucleus {
23 int z,n;
25 bool is_stable = false;
27 decay_types decay_type;
28};
29
30// list containing all the known nuclei
31vector<nucleus> nucleus_list;
32// mapping of the histogram containing the generated nuclei and associated decays
33map<int, map<int, pair<int, nucleus>>> nuc_chart_mapping;
34// tknuclear chart object
35tknuclear_chart *tknuc_chart = nullptr;
36// total elapsed time
37double felapsed_time=0;
38
39// nuclei list initialisation
40void make_db()
41{
42 if(tknuc_chart) return;
43 tknuc_chart = new tknuclear_chart("Decay generator",tknuclear_chart::kAll,true);
44
45 // loop on all the nuclei known in tkn and fill the nucleus list
46 for(auto &nuc: gmanager->get_nuclei()) {
47 nucleus a_nuc;
48 a_nuc.name = nuc->get_symbol();
49 a_nuc.z = nuc->get_z();
50 a_nuc.n = nuc->get_n();
51 if(nuc->is_stable()) {
52 a_nuc.is_stable = true;
53 a_nuc.decay_type = kstable;
54 }
55 else {
56 if(!nuc->get_lifetime_measure()) a_nuc.lifetime_in_sec = 0.;
57 else a_nuc.lifetime_in_sec = nuc->get_lifetime();
58
59 tkstring decay = nuc->get_property("decay_modes");
60 auto token = decay.replace_all("]","[").tokenize("[");
61 if(token.size() == 0) decay = "";
62 else decay = token.front();
63 if(decay.begins_with("B-;")) a_nuc.decay_type = kbeta_minus;
64 else if(decay.begins_with("2B-")) a_nuc.decay_type = kdouble_beta_minus;
65 else if(decay.begins_with("B+")) a_nuc.decay_type = kbeta_plus;
66 else if(decay.begins_with("EC")) a_nuc.decay_type = kbeta_plus;
67 else if(decay.begins_with("2EC")) a_nuc.decay_type = kdouble_beta_plus;
68 else if(decay.begins_with("P")) a_nuc.decay_type = kproton;
69 else if(decay.begins_with("2P")) a_nuc.decay_type = kdiproton;
70 else if(decay.begins_with("N")) a_nuc.decay_type = kneutron;
71 else if(decay.begins_with("2N")) a_nuc.decay_type = kdineutron;
72 else if(decay.begins_with("A")) a_nuc.decay_type = kalpha;
73 else if(decay.begins_with("SF")) a_nuc.decay_type = kfission;
74 else if(decay.begins_with("EF")) a_nuc.decay_type = kEF;
75 else if(decay.begins_with("EP")) a_nuc.decay_type = kEP;
76 else if(decay.begins_with("BN")) a_nuc.decay_type = kBN;
77 else if(decay.begins_with("B2N")) a_nuc.decay_type = kB2N;
78 else if(decay.begins_with("B3N")) a_nuc.decay_type = kB3N;
79 else {
80 a_nuc.decay_type = kunknown;
81 if(decay.length()) cout<<"unknown decay: " << decay << " " << a_nuc.name<<endl;
82 }
83 }
84 nucleus_list.push_back(a_nuc);
85 nuc_chart_mapping[a_nuc.z][a_nuc.n] = make_pair(0,a_nuc);
86 }
87}
88
89// initialisation of the nuclear chart using NEvents nuclei, taken randomly
90void init(Int_t NEvents=1e6)
91{
92 if(nucleus_list.empty()) make_db();
93
94 // reset map
95 for(auto &z: nuc_chart_mapping) {
96 for(auto &n: z.second) {
97 n.second.first=0;
98 }
99 }
100 tknuc_chart->reset();
101
102 std::default_random_engine generator;
103 uniform_int_distribution<Int_t> dist(0,nucleus_list.size()-1);
104
105 for(Int_t inuc=0 ; inuc<NEvents ; inuc++) {
106 nucleus a_nuc = nucleus_list.at(dist(generator));
107 nuc_chart_mapping[a_nuc.z][a_nuc.n].first += 1;
108 tknuc_chart->fill(a_nuc.z,a_nuc.n);
109 }
110
111 tknuc_chart->set_title("T = 0");
112 tknuc_chart->draw();
113 felapsed_time=0.;
114
115 gSystem->Unlink("animation.gif");
116 tknuc_chart->save_as("animation.gif+");
117}
118
119// update the map and histogram after a decay
120void update_chart(int z, int n, int N) {
121 if(!(nuc_chart_mapping.count(z) && nuc_chart_mapping[z].count(n)) ) {
122 return;
123 }
124 nuc_chart_mapping[z][n].first += N;
125 tknuc_chart->set_value(z,n,nuc_chart_mapping[z][n].first);
126}
127
128// process a decay of all the nuclei of the nuclear chart considering a time dt
129void process_decay(double dt, bool save=false) {
130
131 if(tknuc_chart==nullptr) {
132 cout << "process first the init step to generate the T0 distribution" << endl;
133 return;
134 }
135
136 // first, determine how many decay per nucleus needs to be done
137 map<int, map<int, int>> decay_to_process;
138 for(auto &z: nuc_chart_mapping) {
139 for(auto &n: z.second) {
140 auto &pair = n.second;
141 if(pair.first==0) continue;
142 auto &a_nuc = pair.second;
143 if(a_nuc.is_stable) continue;
144 double Ndecay_double = pair.first - pair.first * exp(-TMath::Log(2)*dt/a_nuc.lifetime_in_sec);
145 int Ndecay = TMath::Nint(Ndecay_double);
146 // random decay when the number of decay to process is lower than 1
147 if(Ndecay_double<1) {
148 if(gRandom->Uniform()<Ndecay_double) Ndecay=1;
149 }
150 decay_to_process[z.first][n.first] = Ndecay;
151 }
152 }
153 for(auto &z: decay_to_process) {
154 for(auto &n: z.second) {
155 auto &a_nuc = nuc_chart_mapping[z.first][n.first].second;
156 int Ndecay = n.second;
157 update_chart(a_nuc.z,a_nuc.n,-Ndecay);
158 if(a_nuc.decay_type==kstable) {
159 cout<<"oups, a stable nucleus should not pass in this loop..."<<endl;
160 }
161 if(a_nuc.decay_type==kproton) {
162 update_chart(a_nuc.z-1,a_nuc.n,Ndecay);
163 }
164 else if(a_nuc.decay_type==kdiproton) {
165 update_chart(a_nuc.z-2,a_nuc.n,Ndecay);
166 }
167 else if(a_nuc.decay_type==kneutron) {
168 update_chart(a_nuc.z,a_nuc.n-1,Ndecay);
169 }
170 else if(a_nuc.decay_type==kdineutron) {
171 update_chart(a_nuc.z,a_nuc.n-2,Ndecay);
172 }
173 else if(a_nuc.decay_type==kalpha) {
174 update_chart(a_nuc.z-2,a_nuc.n-2,Ndecay);
175 }
176 else if(a_nuc.decay_type==kbeta_plus) {
177 update_chart(a_nuc.z-1,a_nuc.n+1,Ndecay);
178 }
179 else if(a_nuc.decay_type==kEP) {
180 update_chart(a_nuc.z-2,a_nuc.n+1,Ndecay);
181 }
182 else if(a_nuc.decay_type==kdouble_beta_plus) {
183 update_chart(a_nuc.z-1,a_nuc.n+1,Ndecay);
184 }
185 else if(a_nuc.decay_type==kbeta_minus) {
186 update_chart(a_nuc.z+1,a_nuc.n-1,Ndecay);
187 }
188 else if(a_nuc.decay_type==kdouble_beta_minus) {
189 update_chart(a_nuc.z+2,a_nuc.n-2,Ndecay);
190 }
191 else if(a_nuc.decay_type==kBN) {
192 update_chart(a_nuc.z+1,a_nuc.n-2,Ndecay);
193 }
194 else if(a_nuc.decay_type==kB2N) {
195 update_chart(a_nuc.z+1,a_nuc.n-3,Ndecay);
196 }
197 else if(a_nuc.decay_type==kB3N) {
198 update_chart(a_nuc.z+1,a_nuc.n-4,Ndecay);
199 }
200 // very approximative representation of the fission...
201 else if(a_nuc.decay_type==kfission || a_nuc.decay_type==kEF) {
202 for(int i=0 ; i<Ndecay ; i++) {
203 while(true) {
204 int newz1 = TMath::Nint(gRandom->Uniform(a_nuc.z/4,a_nuc.z/2));
205 int newn1 = TMath::Nint(gRandom->Uniform(a_nuc.n/4,a_nuc.n/2));
206 int emitted_neutrons = TMath::Nint(gRandom->Uniform(2,7));
207 int newz2 = a_nuc.z-newz1;
208 if(a_nuc.decay_type==kEF) newz2 -= 1;
209 int newn2 = a_nuc.n-newn1-emitted_neutrons;
210 if(!(nuc_chart_mapping.count(newz1) && nuc_chart_mapping[newz1].count(newn1)) || !(nuc_chart_mapping.count(newz2) && nuc_chart_mapping[newz2].count(newn2))) continue;
211 update_chart(newz1,newn1,1);
212 update_chart(newz2,newn2,1);
213 break;
214 }
215 }
216 }
217 else if(a_nuc.decay_type==kunknown) {
218 }
219 else {
220 cout<<"unknown decay for nucleus " << a_nuc.name <<endl;
221 }
222 }
223 }
224 felapsed_time += dt;
225
226 // if save, build a gif
227 if(save) {
228 tkstring title = "T = ";
229 if(felapsed_time<60) title += tkstring::form("%.1f sec",felapsed_time);
230 else if(felapsed_time<60*60) title += tkstring::form("%.1f min",felapsed_time/60);
231 else if(felapsed_time<60*60*24) title += tkstring::form("%.1f h",felapsed_time/60/60);
232 else if(felapsed_time<60*60*24*365) title += tkstring::form("%.1f days",felapsed_time/60/60/24);
233 else title += tkstring::form("%.1g years",felapsed_time/60/60/24/365);
234
235 tknuc_chart->set_title(title);
236 tknuc_chart->update();
237 tknuc_chart->save_as("animation.gif+");
238
239 gSystem->ProcessEvents();
240 }
241}
242
243// calculate the decay for _time seconds, with a delta t dt. Process nsave pictures in the gif
244void elapse_time_in_seconds(double _time, double dt=0., int nsave=5) {
245
246 if(tknuc_chart==nullptr) {
247 cout << "process first the init step to generate the T0 distribution" << endl;
248 return;
249 }
250
251 if(dt==0.) dt = _time/1000.;
252 int ndt=1;int save=(_time/dt)/nsave;
253 for(double i=dt ; i<_time ; i+=dt) {
254 if(ndt%save==0) process_decay(dt,true);
255 else process_decay(dt);
256 ndt++;
257 }
258 process_decay(dt,true);
259}
260
261void elapse_time_in_hours(double time, double dt=0.) {
262
263 time = time*3600.;
264 if(dt!=0.) dt = dt*3600.;
265 int nsave=4;
266 elapse_time_in_seconds(time,dt,nsave);
267}
268
269void elapse_time_in_days(double time, double dt=0.) {
270
271 time = time*3600.*24;
272 if(dt!=0.) dt = dt*3600.*24;
273 int nsave=2;
274 elapse_time_in_seconds(time,dt,nsave);
275}
276
277void elapse_time_in_years(double time, double dt=0.) {
278
279 time = time*3600.*24*365;
280 if(dt!=0.) dt = dt*3600.*24*365;
281 int nsave=1;
282 elapse_time_in_seconds(time,dt,nsave);
283}
284
285// decay generation during 5e9 years
286void decay_generator_animation() {
287 init(1e9);
288 elapse_time_in_seconds(1); // 1sec
289 elapse_time_in_seconds(9); // 10sec
290 elapse_time_in_seconds(20); // 30sec
291 elapse_time_in_seconds(30); // 1min
292 elapse_time_in_seconds(60); // 2min
293 elapse_time_in_seconds(60*8); // 10min
294 elapse_time_in_seconds(60*20); // 30min
295 elapse_time_in_seconds(60*30); // 1h
296 elapse_time_in_hours(1); // 2h
297 elapse_time_in_hours(8); // 10h
298 elapse_time_in_hours(14); // 1d
299 elapse_time_in_days(1); // 2d
300 elapse_time_in_days(8); // 10d
301 elapse_time_in_days(40); // 50d
302 elapse_time_in_days(50); // 100d
303 elapse_time_in_days(100); // 200d
304 elapse_time_in_days(165); // 1y
305 elapse_time_in_years(1); // 2y
306 elapse_time_in_years(4); // 5y
307 elapse_time_in_years(5); // 10y
308 elapse_time_in_years(40); // 50y
309 elapse_time_in_years(50); // 100y
310 elapse_time_in_years(400); // 500y
311 elapse_time_in_years(500); // 1000y
312 elapse_time_in_years(9000); // 10000y
313 elapse_time_in_years(40000); // 50000y
314 elapse_time_in_years(50000); // 100000y
315 elapse_time_in_years(400000); // 500000y
316 elapse_time_in_years(500000); // 1e6y
317 elapse_time_in_years(4e6); // 5e6y
318 elapse_time_in_years(5e6); // 1e7y
319 elapse_time_in_years(4e7); // 5e7y
320 elapse_time_in_years(5e7); // 1e8y
321 elapse_time_in_years(4e8); // 5e8y
322 elapse_time_in_years(5e8); // 1e9y
323 elapse_time_in_years(4e9); // 1e9y
324 tknuc_chart->save_as("animation.gif++");
325}
nuclear chart plot with ROOT
void set_title(TString _title)
define the nuclear chart title
void fill(int _zz, int _nn, double _val)
fill the bin for a given nucleus (increment by _val)
void draw(bool _update_only=false)
draw the tknucleus chart
void save_as(const char *file_name)
save the nuclear chart in the file file_name
void set_value(int _zz, int _nn, double _val)
set the bin value for a given nucleus
void update()
update the tknucleus chart
void reset()
reset the tknuclear chart
std::string with usefull tricks from TString (ROOT) and KVString (KaliVeda) and more....
Definition: tkstring.h:54
std::vector< tkstring > tokenize(const tkstring &_delim=" ") const
Create a vector of string separated by at least one delimiter.
Definition: tkstring.cpp:254
bool begins_with(const char *_s, ECaseCompare _cmp=kExact) const
Definition: tkstring.h:185
tkstring & replace_all(const tkstring &_s1, const tkstring &_s2)
Definition: tkstring.h:203
Definition: tklog.cpp:39
decay_types decay_type
tkstring name
bool is_stable
double lifetime_in_sec