This example macro takes all the known nuclei from the nuclear chart, and generates randomly a initial distribution of nuclei that are represented on a tkn::tknuclear_chart. The user can then simulate a time evolution that will make the nuclei decay corresponding to their main decay mode and lifetime.
This is not a perfect representation of the decay process, but just a simple view. For example, for a time evolution of 1 second, the program actually process 1000 successive decays of 1ms.
a fonction is already prepared to simulate the earth age time evolution using adapted time scales to have a global overview of the different decay steps.
#include "Riostream.h"
#include "TH2F.h"
#include "TROOT.h"
#include "TMath.h"
#include "TRandom3.h"
#include "TCanvas.h"
#include "TSystem.h"
#include "TLatex.h"
#include "TStyle.h"
#include <cctype>
#include <random>
#include "tkmanager.h"
#include "tknuclear_chart.h"
};
};
vector<nucleus> nucleus_list;
map<int, map<int, pair<int, nucleus>>> nuc_chart_mapping;
double felapsed_time = 0;
bool read_emitted_particle_count(
const tkstring &mode,
const tkstring &particle,
int &count)
{
if (mode == particle) {
count = 1;
return true;
}
return count > 0;
}
bool parse_cluster_emission(
const tkstring &mode,
int &z_offset,
int &n_offset)
{
if (mode.empty() || !std::isdigit(static_cast<unsigned char>(mode.front()))) return false;
int cluster_z = 0;
if (!gmanager->known_element(symbol, cluster_z)) return false;
const int cluster_a = mass_str.
atoi();
if (cluster_a <= cluster_z) return false;
z_offset -= cluster_z;
n_offset -= cluster_a - cluster_z;
return true;
}
{
if (mode.empty()) return true;
int count = 0;
if (read_emitted_particle_count(mode, "N", count)) {
return true;
}
if (read_emitted_particle_count(mode, "P", count)) {
return true;
}
if (mode == "A") {
return true;
}
if (mode == "F" || mode == "SF") {
return true;
}
int cluster_z = 0;
if (gmanager->known_element(symbol, cluster_z) && cluster_z > 2) {
return true;
}
return false;
}
{
if (mode.empty()) return step;
if (mode == "B-") {
return step;
}
if (mode == "2B-") {
return step;
}
if (apply_particle_emission(mode.
substr(2), step))
return step;
}
if (mode.
begins_with(
"B") && mode.length() > 1 && mode !=
"B+") {
legacy_step.
known =
true;
if (apply_particle_emission(mode.
substr(1), legacy_step))
return legacy_step;
}
if (mode == "B+" || mode == "EC" || mode == "EC+B+") {
return step;
}
if (mode == "2EC") {
return step;
}
if (apply_particle_emission(mode.
substr(2), step))
return step;
}
legacy_step.
known =
true;
if (apply_particle_emission(mode.
substr(1), legacy_step))
return legacy_step;
}
if (apply_particle_emission(mode, step)) {
return step;
}
return step;
}
void make_db()
{
if (tknuc_chart) return;
for (auto &nuc : gmanager->get_nuclei()) {
a_nuc.
name = nuc->get_symbol();
if (nuc->is_stable()) {
} else {
if (!nuc->get_lifetime_measure())
else
tkstring decay = nuc->get_property(
"decay_modes");
if (token.size() == 0)
decay = "";
else
decay = token.front();
auto decay_tokens = decay.
tokenize(
";");
tkstring decay_mode = decay_tokens.size() ? decay_tokens.front() :
"";
a_nuc.
decay = decode_decay_mode(decay_mode);
if (!a_nuc.
decay.
known && decay.length()) cout <<
"unknown decay: " << decay <<
" " << a_nuc.
name << endl;
}
nucleus_list.push_back(a_nuc);
nuc_chart_mapping[a_nuc.
z][a_nuc.
n] = make_pair(0, a_nuc);
}
}
void init(Int_t NEvents = 1e6)
{
if (nucleus_list.empty()) make_db();
for (auto &z : nuc_chart_mapping) {
for (auto &n : z.second) {
n.second.first = 0;
}
}
tknuc_chart->reset();
std::default_random_engine generator;
uniform_int_distribution<Int_t> dist(0, nucleus_list.size() - 1);
for (Int_t inuc = 0; inuc < NEvents; inuc++) {
nucleus a_nuc = nucleus_list.at(dist(generator));
nuc_chart_mapping[a_nuc.
z][a_nuc.
n].first += 1;
tknuc_chart->fill(a_nuc.
z, a_nuc.
n);
}
tknuc_chart->set_title("T = 0");
tknuc_chart->draw();
felapsed_time = 0.;
gSystem->Unlink("animation.gif");
tknuc_chart->save_as("animation.gif+");
}
void update_chart(int z, int n, int N)
{
if (!(nuc_chart_mapping.count(z) && nuc_chart_mapping[z].count(n))) {
return;
}
nuc_chart_mapping[z][n].first += N;
tknuc_chart->set_value(z, n, nuc_chart_mapping[z][n].first);
}
void process_decay(double dt, bool save = false)
{
if (tknuc_chart == nullptr) {
cout << "process first the init step to generate the T0 distribution" << endl;
return;
}
map<int, map<int, int>> decay_to_process;
for (auto &z : nuc_chart_mapping) {
for (auto &n : z.second) {
auto &pair = n.second;
if (pair.first == 0) continue;
auto &a_nuc = pair.second;
double Ndecay_double = pair.first - pair.first * exp(-TMath::Log(2) * dt / a_nuc.
lifetime_in_sec);
int Ndecay = TMath::Nint(Ndecay_double);
if (Ndecay_double < 1) {
if (gRandom->Uniform() < Ndecay_double) Ndecay = 1;
}
decay_to_process[z.first][n.first] = Ndecay;
}
}
for (auto &z : decay_to_process) {
for (auto &n : z.second) {
auto &a_nuc = nuc_chart_mapping[z.first][n.first].second;
int Ndecay = n.second;
update_chart(a_nuc.
z, a_nuc.
n, -Ndecay);
cout << "oups, a stable nucleus should not pass in this loop..." << endl;
}
for (int i = 0; i < Ndecay; i++) {
while (true) {
int newz1 = TMath::Nint(gRandom->Uniform(fission_z / 4, fission_z / 2));
int newn1 = TMath::Nint(gRandom->Uniform(fission_n / 4, fission_n / 2));
int emitted_neutrons = TMath::Nint(gRandom->Uniform(2, 7));
int newz2 = fission_z - newz1;
int newn2 = fission_n - newn1 - emitted_neutrons;
if (!(nuc_chart_mapping.count(newz1) && nuc_chart_mapping[newz1].count(newn1)) || !(nuc_chart_mapping.count(newz2) && nuc_chart_mapping[newz2].count(newn2))) continue;
update_chart(newz1, newn1, 1);
update_chart(newz2, newn2, 1);
break;
}
}
} else {
}
}
}
felapsed_time += dt;
if (save) {
if (felapsed_time < 60)
else if (felapsed_time < 60 * 60)
else if (felapsed_time < 60 * 60 * 24)
else if (felapsed_time < 60 * 60 * 24 * 365)
else
title +=
tkstring::form(
"%.1g years", felapsed_time / 60 / 60 / 24 / 365);
tknuc_chart->set_title(title);
tknuc_chart->update();
tknuc_chart->save_as("animation.gif+");
gSystem->ProcessEvents();
}
}
void elapse_time_in_seconds(double _time, double dt = 0., int nsave = 5)
{
if (tknuc_chart == nullptr) {
cout << "process first the init step to generate the T0 distribution" << endl;
return;
}
if (dt == 0.) dt = _time / 1000.;
int ndt = 1;
int save = (_time / dt) / nsave;
for (int i = 1; i * dt < _time; i++) {
if (ndt % save == 0)
process_decay(dt, true);
else
process_decay(dt);
ndt++;
}
process_decay(dt, true);
}
void elapse_time_in_hours(double time, double dt = 0.)
{
time = time * 3600.;
if (dt != 0.) dt = dt * 3600.;
int nsave = 4;
elapse_time_in_seconds(time, dt, nsave);
}
void elapse_time_in_days(double time, double dt = 0.)
{
time = time * 3600. * 24;
if (dt != 0.) dt = dt * 3600. * 24;
int nsave = 2;
elapse_time_in_seconds(time, dt, nsave);
}
void elapse_time_in_years(double time, double dt = 0.)
{
time = time * 3600. * 24 * 365;
if (dt != 0.) dt = dt * 3600. * 24 * 365;
int nsave = 1;
elapse_time_in_seconds(time, dt, nsave);
}
void decay_generator_animation()
{
init(1e9);
elapse_time_in_seconds(1);
elapse_time_in_seconds(9);
elapse_time_in_seconds(20);
elapse_time_in_seconds(30);
elapse_time_in_seconds(60);
elapse_time_in_seconds(60 * 8);
elapse_time_in_seconds(60 * 20);
elapse_time_in_seconds(60 * 30);
elapse_time_in_hours(1);
elapse_time_in_hours(8);
elapse_time_in_hours(14);
elapse_time_in_days(1);
elapse_time_in_days(8);
elapse_time_in_days(40);
elapse_time_in_days(50);
elapse_time_in_days(100);
elapse_time_in_days(165);
elapse_time_in_years(1);
elapse_time_in_years(4);
elapse_time_in_years(5);
elapse_time_in_years(40);
elapse_time_in_years(50);
elapse_time_in_years(400);
elapse_time_in_years(500);
elapse_time_in_years(9000);
elapse_time_in_years(40000);
elapse_time_in_years(50000);
elapse_time_in_years(400000);
elapse_time_in_years(500000);
elapse_time_in_years(4e6);
elapse_time_in_years(5e6);
elapse_time_in_years(4e7);
elapse_time_in_years(5e7);
elapse_time_in_years(4e8);
elapse_time_in_years(5e8);
elapse_time_in_years(4e9);
tknuc_chart->save_as("animation.gif++");
}
nuclear chart plot with ROOT
std::string with usefull tricks from TString (ROOT) and KVString (KaliVeda) and more....
tkstring extract_alpha()
Returns a tkstring composed only of the alphabetic letters of the original tkstring.
tkstring copy() const
Returns a copy of this string.
tkstring & to_lower()
Change all letters to lower case.
static const char * form(const char *_format,...)
std::vector< tkstring > tokenize(const tkstring &_delim=" ") const
Create a vector of string separated by at least one delimiter.
tkstring & remove_all(const tkstring &_s1)
tkstring substr(size_type __pos=0, size_type __n=npos) const
Inlines.
int atoi() const
Converts a string to integer value.
bool ends_with(const char *_s, ECaseCompare _cmp=kExact) const
tkstring remove_alpha()
Returns a tkstring composed only of the non alphabetic letters of the original tkstring.
bool begins_with(const char *_s, ECaseCompare _cmp=kExact) const
tkstring & capitalize()
Change first letter of string from lower to upper case.
tkstring & replace_all(const tkstring &_s1, const tkstring &_s2)
bool is_digit() const
Checks if all characters in string are digits (0-9) or whitespaces.
tkstring & to_upper()
Change all letters to upper case.