TkN 2.4
Toolkit for Nuclei
Loading...
Searching...
No Matches
tkisotope_builder.cpp
1/********************************************************************************
2 * Copyright (c) : Université de Lyon 1, CNRS/IN2P3, UMR5822, *
3 * IP2I, F-69622 Villeurbanne Cedex, France *
4 * Normandie Université, ENSICAEN, UNICAEN, CNRS/IN2P3, *
5 * LPC Caen, F-14000 Caen, France *
6 * Contibutor(s) : *
7 * Jérémie Dudouet jeremie.dudouet@cnrs.fr [2020] *
8 * Diego Gruyer diego.gruyer@cnrs.fr [2020] *
9 * *
10 * Licensed under the MIT License <http://opensource.org/licenses/MIT>. *
11 * SPDX-License-Identifier: MIT *
12 ********************************************************************************/
13
14#include "tkisotope_builder.h"
15
16#include <algorithm>
17#include <iostream>
18#include <fstream>
19
20#include "tklog.h"
21
22namespace tkn {
29}
30
31using namespace tkn;
32using namespace std;
33
34// Decay Mode : decay modes.
35// Mass excess : Mass excess
36// β2 : quadrupole deformation parameter obtained from the B(E2) value, as explained in the 2001 work of Raman et al. Basically,
37// β2=(4π/3ZR0^2)[B(E2)]^(1/2), where R0=1.2A^(1/3), Z and A are the number of protons and the number of nucleons respectively,
38// and the B(E2) values are obtained from ENSDF. B(E2) values can have large uncertainties, or be defined as upper/lower limits.
39// The cell will not be colored for the latter cases and furthermore, it will not be colored if the uncertainty of the derived β2 exceeds 50%.
40// FY235U : thermal neutron-induced fission yields for 235-Uranium. The fission yields are normalized so that their sum is equal to 2.
41// FY238U : neutron-induced fission yields for 238-Uranium. The fission yields are normalized so that their sum is equal to 2.
42// FY239Pu : thermal neutron-induced fission yields for 239-Plutonium. The fission yields are normalized so that their sum is equal to 2.
43// FY241Pu : neutron-induced fission yields for 241-Plutonium. The fission yields are normalized so that their sum is equal to 2.
44// FY252Cf : spontaneous fission yields for 252-Californium. The fission yields are normalized so that their sum is equal to 2.
45// cFY235U : cumulative fission yields
46// cFY238U : cumulative fission yields
47// cFY239Pu : cumulative fission yields
48// cFY241Pu : cumulative fission yields
49// cFY252Cf : cumulative fission yields
50
51
52tkisotope_builder::tkisotope_builder(tkdatabase *_database, const char *_table_name) : tkdb_builder(_database, _table_name)
53{
54 fTable = &fDataBase->new_table(fTableName);
55
56 fTable->add_column("isotope_id", "INT PRIMARY KEY NOT NULL");
57 fTable->add_column("element_id", "INT NOT NULL");
58 fTable->add_column("mass", "INT");
59
60 // data taken from IAEA API
61
62 fTable->add_column("isotope_year_discovered", "INT");
63
64 add_measure("mass_excess",false);
65 add_measure("electric_quadrupole",false);
66 add_measure("magnetic_dipole",false);
67 add_measure("radius",false);
68
69 // data taken from NUDAT3 json file
70
71 add_measure("abundance",false);
72 add_measure("quadrupoleDeformation",false);
73
74 add_measure("FY235U",false);
75 add_measure("FY238U",false);
76 add_measure("FY239Pu",false);
77 add_measure("FY241Pu",false);
78 add_measure("FY252Cf",false);
79
80 add_measure("cFY235U",false);
81 add_measure("cFY238U",false);
82 add_measure("cFY239Pu",false);
83 add_measure("cFY241Pu",false);
84 add_measure("cFY252Cf",false);
85
86 fTable->add_column("decay_modes", "TEXT");
87
88 // columns created here but filled later by ensdf values
89
90 fTable->add_column("spin","REAL");
91 fTable->add_column("parity","INT");
92 fTable->add_column("spin_parity","TEXT");
93
94 add_measure("lifetime",false);
95
96 fTable->add_constraint("FOREIGN KEY (element_id) REFERENCES ELEMENT (element_id)"); // hold this link with a constraints
97 fTable->write_to_database();
98
99 glog << info << "Creating '" << _table_name << "' table" << do_endl;
100}
101
103
104void tkisotope_builder::fill_isotope(const json_nucleus &_json_nuc)
105{
106 fIsotopeIdx++;
107
108// glog << debug << "filling db with : " << _json_nuc.name.data() << " " << fIsotopeIdx<< do_endl;
109
110 (*fTable)["isotope_id"].set_value(fIsotopeIdx);
111 (*fTable)["element_id"].set_value(_json_nuc.Z+1);
112 (*fTable)["mass"].set_value(_json_nuc.A);
113 (*fTable)["isotope_year_discovered"].set_value(_json_nuc.year);
114
115 fill_measure("mass_excess",_json_nuc.mass_excess,false);
116 fill_measure("abundance",_json_nuc.abundance,false);
117 fill_measure("quadrupoleDeformation",_json_nuc.quadrupoleDeformation,false);
118
119 fill_measure("FY235U",_json_nuc.FY235U,false);
120 fill_measure("FY238U",_json_nuc.FY238U,false);
121 fill_measure("FY239Pu",_json_nuc.FY239Pu,false);
122 fill_measure("FY241Pu",_json_nuc.FY241Pu,false);
123 fill_measure("FY252Cf",_json_nuc.FY252Cf,false);
124
125 fill_measure("cFY235U",_json_nuc.cFY235U,false);
126 fill_measure("cFY238U",_json_nuc.cFY238U,false);
127 fill_measure("cFY239Pu",_json_nuc.cFY239Pu,false);
128 fill_measure("cFY241Pu",_json_nuc.cFY241Pu,false);
129 fill_measure("cFY252Cf",_json_nuc.cFY252Cf,false);
130
131 fill_measure("electric_quadrupole",_json_nuc.electric_quadrupole,false);
132 fill_measure("magnetic_dipole",_json_nuc.magnetic_dipole,false);
133 fill_measure("radius",_json_nuc.radius,false);
134
135 // fill the neutron lifetime
136 if(_json_nuc.Z==0 && _json_nuc.N ==1) {
138 lifetime.value = 613.9;
139 lifetime.err = 0.6;
140 lifetime.unit = "S";
141 lifetime.filled = true;
142 fill_measure("lifetime",lifetime,false);
143 }
144
145 tkstring decays;
146 for(auto &dec: _json_nuc.decay_modes) decays.append(tkstring::form("[%s;%g]",dec.first.data(),dec.second));
147 (*fTable)["decay_modes"].set_value(decays.data());
148
149 fTable->push_row();
150}
151
152int tkisotope_builder::fill_database(const char *_json_filename,const char *_gs_filename, int _only_charge, int _only_mass)
153{
154 glog.set_class("db_isotope_builder");
155 glog.set_method(tkstring::form("fill_database('%s','%s')",_json_filename,_gs_filename));
156
157 tkdb_table &fTable = (*fDataBase)[fTableName.data()];
158
159 tkstring message = "Building '" + fTable.get_name() + "' table";
160
161 std::ifstream ifs(_json_filename);
162 if(!ifs.good()) {
163 glog<< error_v << "data file cannot be opened" << do_endl;
164 glog.clear();
165 return 1;
166 }
167
168 json jf = json::parse(ifs);
169
170 auto &nuclides = jf.at("nuclides");
171 int NNucs = nuclides.size();
172 int inuc=0;
173
174 map<int, map<int, json_nucleus>> nuclei;
175 tkstring nread;
176
177 for(auto &nuc: nuclides) {
178 json_nucleus json_nuc = read_nucleus(nuc);
179
180 bool do_push = ((_only_charge==0) && (_only_mass==0));
181 if((_only_charge == json_nuc.Z) && (_only_mass == 0)) do_push = true;
182 if((_only_charge == json_nuc.Z) && (_only_mass == json_nuc.A)) do_push = true;
183 if((_only_charge == 0) && (_only_mass == json_nuc.A)) do_push = true;
184
185 if(do_push) {
186 nuclei[json_nuc.Z][json_nuc.A] = json_nuc;
187 nread += tkstring::form("(%d,%d)", json_nuc.Z, json_nuc.A);
188 }
189 glog.progress_bar(NNucs,inuc,message.data());
190 inuc++;
191 }
192
193 tkn::tkstring line="";
194 fstream file (_gs_filename, ios::in);
195 if(file.is_open())
196 {
197 // read the first line to fill the <number,name> column map
198 if(!getline(file,line)) return 1;
199 int ic=0;
200
201 std::map<int,tkn::tkstring> gs_columns;
202 for(const auto &cname : line.tokenize(",")) gs_columns[ic++] = cname;
203
204 // now read all lines and fill the <name, value> map
205 while(getline(file, line))
206 {
207 std::map<tkn::tkstring, tkn::tkstring> values;
208 line.replace_all(",,",", ,");
209 line.replace_all(",,",", ,");
210 ic=0;
211 for(auto val : line.tokenize(",")) {val.remove_all(" "); values[gs_columns[ic]] = val; ic++;}
212 tkstring dum = tkstring::form("(%d,%d)",values["z"].atoi(),values["n"].atoi()+values["z"].atoi());
213
214 if(!nread.contains(tkstring::form("(%d,%d)",values["z"].atoi(),values["n"].atoi()+values["z"].atoi()))) continue;
215 auto& nuc = nuclei[values["z"].atoi()][values["n"].atoi()+values["z"].atoi()];
216 if(!values["massexcess"].is_empty()) nuc.mass_excess = {values["massexcess"].atof(),values["unc_me"].atof(),-1.,-1.,"keV",values["me_systematics"].equal_to("Y")?";ERR=SY":"",true};
217 if(!values["radius"].is_empty())nuc.radius = {values["radius"].atof(),values["unc_r"].atof(),-1.,-1.,"fm","",true};
218 if(!values["electric_quadrupole"].is_empty()) nuc.electric_quadrupole = {values["electric_quadrupole"].atof(),values["unc_eq"].atof(),-1.,-1.,"barn","",true};
219 if(!values["magnetic_dipole"].is_empty()) nuc.magnetic_dipole = {values["magnetic_dipole"].atof(),values["unc_md"].atof(),-1.,-1.,"mun","",true};
220 if(!values["discovery"].is_empty()) nuc.year = values["discovery"].atoi();
221 }
222 }
223
224 fDataBase->exec_sql("BEGIN TRANSACTION");
225
226 inuc=0;
227 for(auto &z: nuclei) {
228 for(auto &a: z.second) {
229// glog << debug << "filling db with : " << a.second.name.data() << do_endl;
230
231 fill_isotope(a.second);
232 // json_nuc.print();
233 fDataBase->exec_sql(tkstring::form("CREATE VIEW nuc%s as select * from isotope INNER JOIN element on isotope.element_id=element.element_id where element.charge=%d AND isotope.mass=%d",a.second.name.data(),a.second.Z,a.second.A));
234 glog.progress_bar(NNucs,inuc,message.data());
235 inuc++;
236 }
237 }
238
239 fDataBase->exec_sql("END TRANSACTION");
240
241 fDataBase->exec_sql("CREATE INDEX element_index ON isotope(element_id)");
242 fDataBase->exec_sql("CREATE INDEX mass_index ON isotope(mass)");
243
244
245 glog.clear();
246
247 return 0;
248}
249
250void tkisotope_builder::read_measure(const tkstring &key, const json &entry, tkdb_table::measure_data_struct &_data, bool _fromstd)
251{
252 auto to_number = [](const json &value) {
253 if(value.is_string()) return value.get<tkstring>().atof();
254 if(value.is_number()) return value.get<double>();
255 return 0.0;
256 };
257 bool value_set = false;
258 for(auto &_item: entry.items()) {
259 const auto &_key = _item.key();
260 const auto &_val = _item.value();
261 if(_key == "formats") continue;
262 if(_key == "uncertainty") {
263 if(_val.is_object()) {
264 const auto type = _val.value("type", "");
265 if(type == "symmetric") _data.err = to_number(_val.at("value"));
266 else if(type == "range") {
267 if(_val.contains("upperLimit") && _val.contains("lowerLimit")) {
268 const double upper = to_number(_val.at("upperLimit"));
269 const double lower = to_number(_val.at("lowerLimit"));
270 if(!value_set) _data.value = 0.5 * (upper + lower);
271 _data.err = 0.0;
272 _data.err_low = _data.value - lower;
273 _data.err_high = upper - _data.value;
274 }
275 }
276 } else {
277 _data.err = to_number(_val);
278 }
279 }
280 else if(_key == "value") {
281 _data.value = to_number(_val);
282 value_set = true;
283 }
284 else if(_key == "unit") _data.unit = _val.get<tkstring>();
285 else if(_key == "evaluatorInput") continue;
286 else if(_key == "isSystematics" && (_val == "true" || (_val.is_boolean() && _val.get<bool>()))) _data.info.append(";ERR=SY");
287 else {
288 glog << error << key << ": unkown key: " << _key << " type: "<< _item.value().type_name() << do_endl;
289 cout << _val << endl;
290 }
291 }
292 if(_fromstd) {
293 // extract value and err from nudat formated values
294 if(entry.contains("formats") && entry.at("formats").contains("STD")) {
295 tkstring format = entry.at("formats").at("STD").get<tkstring>();
296 auto tokens = format.tokenize();
297 _data.value = tokens.front().atof();
298 if(tokens.size()==2) {
299 _data.err = tokens.back().atof();
300 }
301 }
302 }
303 _data.filled = true;
304}
305
306tkisotope_builder::json_nucleus tkisotope_builder::read_nucleus(json &entry) {
307 json_nucleus nuc;
308 bool pause = false;
309 for(auto &item: entry.items())
310 {
311 auto &key = item.key();
312 auto &val = item.value();
313 if(key == "name") nuc.name = val;
314 else if(key == "z") nuc.Z = val;
315 else if(key == "n") nuc.N = val;
316 else if(key == "a") nuc.A = val;
317 else if(key == "bindingEnergy") continue;
318 else if(key == "bindingEnergyLDMFit") continue;
319 else if(key == "levels") {
320 // pick the ground state level when isomers are present
321 auto is_ground_state = [](const json &level) {
322 if(!level.contains("energy")) return false;
323 const auto &energy = level.at("energy");
324 if(!energy.is_object() || !energy.contains("value")) return false;
325 const auto &energy_val = energy.at("value");
326 if(energy_val.is_string()) return energy_val.get<tkstring>().atof() == 0.0;
327 if(energy_val.is_number()) return energy_val.get<double>() == 0.0;
328 return false;
329 };
330 const json *level_ptr = nullptr;
331 const auto it = std::find_if(val.begin(), val.end(), is_ground_state);
332 bool picked_ground = (it != val.end());
333 if(picked_ground) level_ptr = &(*it);
334 else if(!val.empty()) level_ptr = &val.front();
335 if(!level_ptr) continue;
336 for(auto &_item: level_ptr->items()) {
337 const auto &_key = _item.key();
338 const auto &_val = _item.value();
339 if(_key == "decayModes") {
340 if(_val.is_object() && (_val.contains("observed") || _val.contains("predicted"))) {
341 if(_val.contains("observed")) {
342 for(const auto &mode : _val.at("observed")) {
343 tkstring type = mode.value("mode", "");
344 tkstring ratio = "-1";
345 if(mode.contains("value")) {
346 if(mode.at("value").is_string()) ratio = mode.at("value").get<tkstring>();
347 else if(mode.at("value").is_number()) ratio = tkstring::form("%g", mode.at("value").get<double>());
348 }
349 nuc.decay_modes.emplace_back(type, ratio.atof());
350 }
351 }
352 if(_val.contains("predicted")) {
353 for(const auto &mode : _val.at("predicted")) {
354 tkstring type = mode.value("mode", "");
355 tkstring ratio = "-1";
356 nuc.decay_modes.emplace_back(type, ratio.atof());
357 }
358 }
359 } else {
360 for(auto &mode: _val.items()) {
361 if(mode.value().size()==3) {
362 tkstring type = mode.value().at("name");
363 tkstring symbol = mode.value().at("symbol");
364 tkstring ratio;
365 // unkown ratios
366 if(symbol == "?") {
367 symbol = "=";
368 ratio = "-1";
369 }
370 else ratio = mode.value().at("value");
371 nuc.decay_modes.emplace_back(type,ratio.atof());
372 }
373 else {
374 tkstring type = mode.value().at("name");
375 type.remove_all("?");
376 tkstring symbol = "=";
377 tkstring ratio = "-1";
378 nuc.decay_modes.emplace_back(type,ratio.atof());
379 }
380 }
381 }
382 }
383 else if(_key == "energy") {
384 if(_val.empty()) continue;
385 if(_val.size()<2) continue;
386 double ener = 0.0;
387 const auto &energy_val = _val.at("value");
388 if(energy_val.is_string()) ener = energy_val.get<tkstring>().atof();
389 else if(energy_val.is_number()) ener = energy_val.get<double>();
390 if(ener != 0. && picked_ground) {
391 glog << error << nuc.name << ": level is not ground state ! " << _val << do_endl;
392 }
393 }
394 else if(_key == "massExcess") read_measure(_key, _val, nuc.mass_excess);
395 else if(_key == "abundance") {
396 read_measure(_key, _val, nuc.abundance,true);
397 nuc.abundance.unit = "%";
398 }
399 // include in the ensdf parsing
400 else if(_key == "halflife" || _key == "spinAndParity" || _key == "spinParity") continue;
401 else {
402 glog << error << "levels: unkown key: " << _key << " type: "<< _item.value().type_name() << do_endl;
403 cout << _val << endl;
404 }
405 }
406 }
407 else if(key == "pairingGap") continue;
408 else if(key == "qValues") continue;
409 else if(key == "separationEnergies") continue;
410 else if(key == "webLinks") continue;
411 else if(key == "BE4DBE2") continue;
412 else if(key == "excitedStateEnergies") {
413 for(auto &_item: val.items()) {
414 const auto &_key = _item.key();
415 const auto &_val = _item.value();
416 if(_key == "firstExcitedStateEnergy") continue;
417 if(_key == "firstTwoPlusEnergy") continue;
418 if(_key == "firstFourPlusEnergy") continue;
419 if(_key == "firstFourPlusOverFirstTwoPlusEnergy") continue;
420 if(_key == "firstThreeMinusEnergy") continue;
421 glog << error << key << ": unkown key: " << _key << " type: "<< _item.value().type_name() << do_endl;
422 cout << _val << endl;
423 }
424 }
425 else if(key == "fissionYields") {
426 for(auto &_item: val.items()) {
427 const auto &_key = _item.key();
428 const auto &_val = _item.value();
429 if(_key == "FY235U") read_measure(_key, _val, nuc.FY235U);
430 else if(_key == "FY238U") read_measure(_key, _val, nuc.FY238U);
431 else if(_key == "FY239Pu") read_measure(_key, _val, nuc.FY239Pu);
432 else if(_key == "FY241Pu") read_measure(_key, _val, nuc.FY241Pu);
433 else if(_key == "FY252Cf") read_measure(_key, _val, nuc.FY252Cf);
434 else if(_key == "cFY235U") read_measure(_key, _val, nuc.cFY235U);
435 else if(_key == "cFY238U") read_measure(_key, _val, nuc.cFY238U);
436 else if(_key == "cFY239Pu") read_measure(_key, _val, nuc.cFY239Pu);
437 else if(_key == "cFY241Pu") read_measure(_key, _val, nuc.cFY241Pu);
438 else if(_key == "cFY252Cf") read_measure(_key, _val, nuc.cFY252Cf);
439 else {
440 glog << error << key << ": unkown key: " << _key << " type: "<< _item.value().type_name() << do_endl;
441 cout << _val << endl;
442 }
443 }
444 }
445 else if(key == "quadrupoleDeformation") read_measure(key, val, nuc.quadrupoleDeformation);
446 else if(key == "thermalNeutronCapture") continue;
447 else if(key == "thermalNeutronFission") continue;
448 else if(key == "discovery") continue;
449 else {
450 glog << warning << "unknown key: " << key << do_endl;
451 cout<< "value: " << endl;
452 cout<<val<<endl;
453 pause = true;
454 }
455 }
456 if(pause) {
457 nuc.print();
458 cin.get();
459 }
460 return nuc;
461}
462
463#ifdef HAS_ROOT
464ClassImp(tkisotope_builder);
465#endif
Interface to the sqlite database.
Definition tkdatabase.h:34
tkdb_builder(tkdatabase *_database, const char *_table_name)
Representaiton of a sqlite data table.
Definition tkdb_table.h:29
Decoding of the NUDAT isotope properties.
int fill_database(const char *_json_filename, const char *_gs_filename, int _only_charge=0, int _only_mass=0)
virtual ~tkisotope_builder() override
tkisotope_builder(tkdatabase *_database, const char *_table_name="ISOTOPE")
std::string with usefull tricks from TString (ROOT) and KVString (KaliVeda) and more....
Definition tkstring.h:31
static const char * form(const char *_format,...)
Definition tkstring.cpp:408
std::vector< tkstring > tokenize(const tkstring &_delim=" ") const
Create a vector of string separated by at least one delimiter.
Definition tkstring.cpp:231
tkstring & remove_all(const tkstring &_s1)
Definition tkstring.h:192
bool contains(const char *_pat, ECaseCompare _cmp=kExact) const
Definition tkstring.h:174
tkstring & append(const tkstring &_st)
Definition tkstring.h:204
tkstring & replace_all(const tkstring &_s1, const tkstring &_s2)
Definition tkstring.h:180
double atof() const
Converts a string to double value.
Definition tkstring.cpp:168
Definition tklog.cpp:16
tklog & error_v(tklog &log)
Definition tklog.h:407
tklog & info(tklog &log)
Definition tklog.h:313
tklog & error(tklog &log)
Definition tklog.h:344
tklog & do_endl(tklog &log)
Definition tklog.h:212
tklog & warning(tklog &log)
Definition tklog.h:331
data structure used to fill a tkmeasure object from the sqlite database
Definition tkdb_table.h:49