TkN 2.5
Toolkit for Nuclei
Loading...
Searching...
No Matches
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 <cctype>
12#include <random>
13
14#include "tkmanager.h"
15#include "tknuclear_chart.h"
16
17using namespace tkn;
18
19struct decay_step {
20 bool known = false;
21 bool is_fission = false;
22 int z_offset = 0;
23 int n_offset = 0;
24};
25
26// nucleus structure containing lifetime and first decay mode
27struct nucleus {
28 int z, n;
30 bool is_stable = false;
33};
34
35// list containing all the known nuclei
36vector<nucleus> nucleus_list;
37// mapping of the histogram containing the generated nuclei and associated decays
38map<int, map<int, pair<int, nucleus>>> nuc_chart_mapping;
39// tknuclear chart object
40tknuclear_chart *tknuc_chart = nullptr;
41// total elapsed time
42double felapsed_time = 0;
43
44bool read_emitted_particle_count(const tkstring &mode, const tkstring &particle, int &count)
45{
46 if (mode == particle) {
47 count = 1;
48 return true;
49 }
50
51 if (!mode.ends_with(particle)) return false;
52 tkstring number = mode.substr(0, mode.length() - particle.length());
53 if (!number.is_digit()) return false;
54
55 count = number.atoi();
56 return count > 0;
57}
58
59bool parse_cluster_emission(const tkstring &mode, int &z_offset, int &n_offset)
60{
61 if (mode.empty() || !std::isdigit(static_cast<unsigned char>(mode.front()))) return false;
62
63 tkstring mass_str = mode.copy().remove_alpha();
64 if (!mass_str.is_digit()) return false;
65
66 tkstring symbol = mode.copy().extract_alpha();
67 symbol.to_lower().capitalize();
68
69 int cluster_z = 0;
70 if (!gmanager->known_element(symbol, cluster_z)) return false;
71
72 const int cluster_a = mass_str.atoi();
73 if (cluster_a <= cluster_z) return false;
74
75 z_offset -= cluster_z;
76 n_offset -= cluster_a - cluster_z;
77 return true;
78}
79
80bool apply_particle_emission(const tkstring &mode, decay_step &step)
81{
82 if (mode.empty()) return true;
83
84 int count = 0;
85 if (read_emitted_particle_count(mode, "N", count)) {
86 step.n_offset -= count;
87 return true;
88 }
89 if (read_emitted_particle_count(mode, "P", count)) {
90 step.z_offset -= count;
91 return true;
92 }
93 if (mode == "A") {
94 step.z_offset -= 2;
95 step.n_offset -= 2;
96 return true;
97 }
98 if (mode == "F" || mode == "SF") {
99 step.is_fission = true;
100 return true;
101 }
102 if (parse_cluster_emission(mode, step.z_offset, step.n_offset)) return true;
103
104 // NuDat sometimes reports heavy-cluster emission without a mass number
105 // (for example "Si"). The animation only needs a qualitative branch.
106 tkstring symbol = mode;
107 symbol.to_lower().capitalize();
108 int cluster_z = 0;
109 if (gmanager->known_element(symbol, cluster_z) && cluster_z > 2) {
110 step.is_fission = true;
111 return true;
112 }
113
114 return false;
115}
116
117decay_step decode_decay_mode(tkstring mode)
118{
119 decay_step step;
120 mode.remove_all(" ");
121 mode.to_upper();
122
123 if (mode.empty()) return step;
124
125 if (mode == "B-") {
126 step.known = true;
127 step.z_offset = 1;
128 step.n_offset = -1;
129 return step;
130 }
131 if (mode == "2B-") {
132 step.known = true;
133 step.z_offset = 2;
134 step.n_offset = -2;
135 return step;
136 }
137 if (mode.begins_with("B-")) {
138 step.known = true;
139 step.z_offset = 1;
140 step.n_offset = -1;
141 if (apply_particle_emission(mode.substr(2), step)) return step;
142 }
143 if (mode.begins_with("B") && mode.length() > 1 && mode != "B+") {
144 decay_step legacy_step;
145 legacy_step.known = true;
146 legacy_step.z_offset = 1;
147 legacy_step.n_offset = -1;
148 if (apply_particle_emission(mode.substr(1), legacy_step)) return legacy_step;
149 }
150
151 if (mode == "B+" || mode == "EC" || mode == "EC+B+") {
152 step.known = true;
153 step.z_offset = -1;
154 step.n_offset = 1;
155 return step;
156 }
157 if (mode == "2EC") {
158 step.known = true;
159 step.z_offset = -2;
160 step.n_offset = 2;
161 return step;
162 }
163 if (mode.begins_with("EC")) {
164 step.known = true;
165 step.z_offset = -1;
166 step.n_offset = 1;
167 if (apply_particle_emission(mode.substr(2), step)) return step;
168 }
169 if (mode.begins_with("E") && mode.length() > 1) {
170 decay_step legacy_step;
171 legacy_step.known = true;
172 legacy_step.z_offset = -1;
173 legacy_step.n_offset = 1;
174 if (apply_particle_emission(mode.substr(1), legacy_step)) return legacy_step;
175 }
176
177 if (apply_particle_emission(mode, step)) {
178 step.known = true;
179 return step;
180 }
181
182 step.known = false;
183 step.is_fission = false;
184 step.z_offset = 0;
185 step.n_offset = 0;
186 return step;
187}
188
189// nuclei list initialisation
190void make_db()
191{
192 if (tknuc_chart) return;
193 tknuc_chart = new tknuclear_chart("Decay generator", tknuclear_chart::kAll, true);
194
195 // loop on all the nuclei known in tkn and fill the nucleus list
196 for (auto &nuc : gmanager->get_nuclei()) {
197 nucleus a_nuc;
198 a_nuc.name = nuc->get_symbol();
199 a_nuc.z = nuc->get_z();
200 a_nuc.n = nuc->get_n();
201 if (nuc->is_stable()) {
202 a_nuc.is_stable = true;
203 } else {
204 if (!nuc->get_lifetime_measure())
205 a_nuc.lifetime_in_sec = 0.;
206 else
207 a_nuc.lifetime_in_sec = nuc->get_lifetime();
208
209 tkstring decay = nuc->get_property("decay_modes");
210 auto token = decay.replace_all("]", "[").tokenize("[");
211 if (token.size() == 0)
212 decay = "";
213 else
214 decay = token.front();
215 auto decay_tokens = decay.tokenize(";");
216 tkstring decay_mode = decay_tokens.size() ? decay_tokens.front() : "";
217 a_nuc.decay = decode_decay_mode(decay_mode);
218 if (!a_nuc.decay.known && decay.length()) cout << "unknown decay: " << decay << " " << a_nuc.name << endl;
219 }
220 nucleus_list.push_back(a_nuc);
221 nuc_chart_mapping[a_nuc.z][a_nuc.n] = make_pair(0, a_nuc);
222 }
223}
224
225// initialisation of the nuclear chart using NEvents nuclei, taken randomly
226void init(Int_t NEvents = 1e6)
227{
228 if (nucleus_list.empty()) make_db();
229
230 // reset map
231 for (auto &z : nuc_chart_mapping) {
232 for (auto &n : z.second) {
233 n.second.first = 0;
234 }
235 }
236 tknuc_chart->reset();
237
238 std::default_random_engine generator;
239 uniform_int_distribution<Int_t> dist(0, nucleus_list.size() - 1);
240
241 for (Int_t inuc = 0; inuc < NEvents; inuc++) {
242 nucleus a_nuc = nucleus_list.at(dist(generator));
243 nuc_chart_mapping[a_nuc.z][a_nuc.n].first += 1;
244 tknuc_chart->fill(a_nuc.z, a_nuc.n);
245 }
246
247 tknuc_chart->set_title("T = 0");
248 tknuc_chart->draw();
249 felapsed_time = 0.;
250
251 gSystem->Unlink("animation.gif");
252 tknuc_chart->save_as("animation.gif+");
253}
254
255// update the map and histogram after a decay
256void update_chart(int z, int n, int N)
257{
258 if (!(nuc_chart_mapping.count(z) && nuc_chart_mapping[z].count(n))) {
259 return;
260 }
261 nuc_chart_mapping[z][n].first += N;
262 tknuc_chart->set_value(z, n, nuc_chart_mapping[z][n].first);
263}
264
265// process a decay of all the nuclei of the nuclear chart considering a time dt
266void process_decay(double dt, bool save = false)
267{
268
269 if (tknuc_chart == nullptr) {
270 cout << "process first the init step to generate the T0 distribution" << endl;
271 return;
272 }
273
274 // first, determine how many decay per nucleus needs to be done
275 map<int, map<int, int>> decay_to_process;
276 for (auto &z : nuc_chart_mapping) {
277 for (auto &n : z.second) {
278 auto &pair = n.second;
279 if (pair.first == 0) continue;
280 auto &a_nuc = pair.second;
281 if (a_nuc.is_stable) continue;
282 double Ndecay_double = pair.first - pair.first * exp(-TMath::Log(2) * dt / a_nuc.lifetime_in_sec);
283 int Ndecay = TMath::Nint(Ndecay_double);
284 // random decay when the number of decay to process is lower than 1
285 if (Ndecay_double < 1) {
286 if (gRandom->Uniform() < Ndecay_double) Ndecay = 1;
287 }
288 decay_to_process[z.first][n.first] = Ndecay;
289 }
290 }
291 for (auto &z : decay_to_process) {
292 for (auto &n : z.second) {
293 auto &a_nuc = nuc_chart_mapping[z.first][n.first].second;
294 int Ndecay = n.second;
295 update_chart(a_nuc.z, a_nuc.n, -Ndecay);
296 if (a_nuc.is_stable) {
297 cout << "oups, a stable nucleus should not pass in this loop..." << endl;
298 }
299 // very approximative representation of the fission...
300 if (a_nuc.decay.is_fission) {
301 const int fission_z = a_nuc.z + a_nuc.decay.z_offset;
302 const int fission_n = a_nuc.n + a_nuc.decay.n_offset;
303 for (int i = 0; i < Ndecay; i++) {
304 while (true) {
305 int newz1 = TMath::Nint(gRandom->Uniform(fission_z / 4, fission_z / 2));
306 int newn1 = TMath::Nint(gRandom->Uniform(fission_n / 4, fission_n / 2));
307 int emitted_neutrons = TMath::Nint(gRandom->Uniform(2, 7));
308 int newz2 = fission_z - newz1;
309 int newn2 = fission_n - newn1 - emitted_neutrons;
310 if (!(nuc_chart_mapping.count(newz1) && nuc_chart_mapping[newz1].count(newn1)) || !(nuc_chart_mapping.count(newz2) && nuc_chart_mapping[newz2].count(newn2))) continue;
311 update_chart(newz1, newn1, 1);
312 update_chart(newz2, newn2, 1);
313 break;
314 }
315 }
316 } else if (a_nuc.decay.known) {
317 update_chart(a_nuc.z + a_nuc.decay.z_offset, a_nuc.n + a_nuc.decay.n_offset, Ndecay);
318 } else {
319 // Keep the historical behaviour for nuclei without usable decay mode:
320 // remove the decayed parent and do not feed a daughter nucleus.
321 }
322 }
323 }
324 felapsed_time += dt;
325
326 // if save, build a gif
327 if (save) {
328 tkstring title = "T = ";
329 if (felapsed_time < 60)
330 title += tkstring::form("%.1f sec", felapsed_time);
331 else if (felapsed_time < 60 * 60)
332 title += tkstring::form("%.1f min", felapsed_time / 60);
333 else if (felapsed_time < 60 * 60 * 24)
334 title += tkstring::form("%.1f h", felapsed_time / 60 / 60);
335 else if (felapsed_time < 60 * 60 * 24 * 365)
336 title += tkstring::form("%.1f days", felapsed_time / 60 / 60 / 24);
337 else
338 title += tkstring::form("%.1g years", felapsed_time / 60 / 60 / 24 / 365);
339
340 tknuc_chart->set_title(title);
341 tknuc_chart->update();
342 tknuc_chart->save_as("animation.gif+");
343
344 gSystem->ProcessEvents();
345 }
346}
347
348// calculate the decay for _time seconds, with a delta t dt. Process nsave pictures in the gif
349void elapse_time_in_seconds(double _time, double dt = 0., int nsave = 5)
350{
351
352 if (tknuc_chart == nullptr) {
353 cout << "process first the init step to generate the T0 distribution" << endl;
354 return;
355 }
356
357 if (dt == 0.) dt = _time / 1000.;
358 int ndt = 1;
359 int save = (_time / dt) / nsave;
360 for (int i = 1; i * dt < _time; i++) {
361 if (ndt % save == 0)
362 process_decay(dt, true);
363 else
364 process_decay(dt);
365 ndt++;
366 }
367 process_decay(dt, true);
368}
369
370void elapse_time_in_hours(double time, double dt = 0.)
371{
372
373 time = time * 3600.;
374 if (dt != 0.) dt = dt * 3600.;
375 int nsave = 4;
376 elapse_time_in_seconds(time, dt, nsave);
377}
378
379void elapse_time_in_days(double time, double dt = 0.)
380{
381
382 time = time * 3600. * 24;
383 if (dt != 0.) dt = dt * 3600. * 24;
384 int nsave = 2;
385 elapse_time_in_seconds(time, dt, nsave);
386}
387
388void elapse_time_in_years(double time, double dt = 0.)
389{
390
391 time = time * 3600. * 24 * 365;
392 if (dt != 0.) dt = dt * 3600. * 24 * 365;
393 int nsave = 1;
394 elapse_time_in_seconds(time, dt, nsave);
395}
396
397// decay generation during 5e9 years
398void decay_generator_animation()
399{
400 init(1e9);
401 elapse_time_in_seconds(1); // 1sec
402 elapse_time_in_seconds(9); // 10sec
403 elapse_time_in_seconds(20); // 30sec
404 elapse_time_in_seconds(30); // 1min
405 elapse_time_in_seconds(60); // 2min
406 elapse_time_in_seconds(60 * 8); // 10min
407 elapse_time_in_seconds(60 * 20); // 30min
408 elapse_time_in_seconds(60 * 30); // 1h
409 elapse_time_in_hours(1); // 2h
410 elapse_time_in_hours(8); // 10h
411 elapse_time_in_hours(14); // 1d
412 elapse_time_in_days(1); // 2d
413 elapse_time_in_days(8); // 10d
414 elapse_time_in_days(40); // 50d
415 elapse_time_in_days(50); // 100d
416 elapse_time_in_days(100); // 200d
417 elapse_time_in_days(165); // 1y
418 elapse_time_in_years(1); // 2y
419 elapse_time_in_years(4); // 5y
420 elapse_time_in_years(5); // 10y
421 elapse_time_in_years(40); // 50y
422 elapse_time_in_years(50); // 100y
423 elapse_time_in_years(400); // 500y
424 elapse_time_in_years(500); // 1000y
425 elapse_time_in_years(9000); // 10000y
426 elapse_time_in_years(40000); // 50000y
427 elapse_time_in_years(50000); // 100000y
428 elapse_time_in_years(400000); // 500000y
429 elapse_time_in_years(500000); // 1e6y
430 elapse_time_in_years(4e6); // 5e6y
431 elapse_time_in_years(5e6); // 1e7y
432 elapse_time_in_years(4e7); // 5e7y
433 elapse_time_in_years(5e7); // 1e8y
434 elapse_time_in_years(4e8); // 5e8y
435 elapse_time_in_years(5e8); // 1e9y
436 elapse_time_in_years(4e9); // 1e9y
437 tknuc_chart->save_as("animation.gif++");
438}
nuclear chart plot with ROOT
std::string with usefull tricks from TString (ROOT) and KVString (KaliVeda) and more....
Definition tkstring.h:32
tkstring extract_alpha()
Returns a tkstring composed only of the alphabetic letters of the original tkstring.
Definition tkstring.cpp:408
tkstring copy() const
Returns a copy of this string.
Definition tkstring.cpp:391
tkstring & to_lower()
Change all letters to lower case.
Definition tkstring.cpp:80
static const char * form(const char *_format,...)
Definition tkstring.cpp:452
std::vector< tkstring > tokenize(const tkstring &_delim=" ") const
Create a vector of string separated by at least one delimiter.
Definition tkstring.cpp:275
tkstring & remove_all(const tkstring &_s1)
Definition tkstring.h:193
tkstring substr(size_type __pos=0, size_type __n=npos) const
Inlines.
Definition tkstring.h:157
int atoi() const
Converts a string to integer value.
Definition tkstring.cpp:196
bool ends_with(const char *_s, ECaseCompare _cmp=kExact) const
Definition tkstring.cpp:262
tkstring remove_alpha()
Returns a tkstring composed only of the non alphabetic letters of the original tkstring.
Definition tkstring.cpp:422
bool begins_with(const char *_s, ECaseCompare _cmp=kExact) const
Definition tkstring.h:163
tkstring & capitalize()
Change first letter of string from lower to upper case.
Definition tkstring.cpp:397
tkstring & replace_all(const tkstring &_s1, const tkstring &_s2)
Definition tkstring.h:181
bool is_digit() const
Checks if all characters in string are digits (0-9) or whitespaces.
Definition tkstring.cpp:102
tkstring & to_upper()
Change all letters to upper case.
Definition tkstring.cpp:86
Definition tklog.cpp:16
tkstring name
decay_step decay
double lifetime_in_sec