TkN 2.6
Toolkit for Nuclei
Loading...
Searching...
No Matches
tkensdf_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 "tkensdf_builder.h"
15
16#include <iostream>
17#include <unistd.h>
18#include <cmath>
19
20#include "tklog.h"
21#include "tkensdf_level_rec.h"
22#include "tkensdf_gamma_rec.h"
23
24namespace tkn {
31}
32
33using namespace tkn;
34using namespace std;
35
36tkensdf_builder::tkensdf_builder(tkdatabase *_database, const tkstring &_input_folder) : fDataBase(_database),
37 fensdf_reader(_input_folder)
38{
39}
40
42
44{
45 //--------------------------------------------------------
46 // read files and fill database
47 //--------------------------------------------------------
48
49 fDataBase->begin("charge,mass,symbol,isotope_id", "isotope INNER JOIN element ON isotope.element_id=element.element_id", "charge>0");
50
51 tkstring message;
52
53 if (flevel_builder) {
54 message = "Building '" + flevel_builder->get_table().get_name() + "' table";
55 }
56
57 int oldZ = 0;
58 while (fDataBase->next()) {
59 tkstring nuc = tkstring::form("%s%s", (*fDataBase)["ISOTOPE"]["mass"].get_value().data(), (*fDataBase)["ELEMENT"]["symbol"].get_value().data());
60 tkstring zz = (*fDataBase)["ELEMENT"]["charge"].get_value();
61 tkstring aa = (*fDataBase)["ISOTOPE"]["mass"].get_value();
62 fIsotopeIndex = ((tkstring)(*fDataBase)["ISOTOPE"]["isotope_id"].get_value()).atoi();
63
64 int curZ = zz.atoi();
65 if (curZ < 1) continue;
66
67 // open the ensdf file
68 bool has_levels = fensdf_reader.open_nuc(nuc, kensdf);
69 // fensdf_reader.print_datasets();
70
71 // loop over the datasets
72 for (auto &dataset : *fensdf_reader.get_datasets()) {
73 if (dataset.fdsid.begins_with("COMMENTS")) continue;
74 tkstring date = dataset.fdate.copy().remove_all(" ");
75 if (date.length() == 6) date = date.substr(0, 4) + "-" + date.substr(4, 2);
76 int datasetIdx = add_dataset(dataset, nuc.data(), get_dataset_comment(&dataset), date, "ENSDF");
77 read_dataset(&dataset, datasetIdx);
78 }
79
80 // open the xundl file
81 bool has_levelsxundl = fensdf_reader.open_nuc(nuc, kxundl);
82 // loop over the datasets
83 for (auto &dataset : *fensdf_reader.get_datasets()) {
84 if (dataset.fdsid.begins_with("COMMENTS")) continue;
85 tkstring date = dataset.fdate.copy().remove_all(" ");
86 if (date.length() == 6) date = date.substr(0, 4) + "-" + date.substr(4, 2);
87 int datasetIdx = add_dataset(dataset, nuc.data(), get_dataset_comment(&dataset), date, "XUNDL");
88 read_dataset(&dataset, datasetIdx);
89 }
90
91 if (curZ != oldZ) {
92 glog.progress_bar(117, curZ, message.data());
93 oldZ = curZ;
94 }
95
96 if (has_levels || has_levelsxundl) {
97 fDataBase->exec_sql(tkstring::form("CREATE VIEW lvl%s as select * from level INNER JOIN dataset on level.dataset_id=dataset.dataset_id INNER JOIN isotope on level.isotope_id=isotope.isotope_id INNER JOIN element on isotope.element_id=element.element_id where element.charge=%s AND isotope.mass=%s", nuc.data(), zz.data(), aa.data()));
98 if (fdecay_builder) fDataBase->exec_sql(tkstring::form("CREATE VIEW dec%s as select * from decay INNER JOIN level on decay.level_from_id=level.level_id INNER JOIN dataset on level.dataset_id=dataset.dataset_id INNER JOIN isotope on level.isotope_id=isotope.isotope_id INNER JOIN element on isotope.element_id=element.element_id where element.charge=%s AND isotope.mass=%s", nuc.data(), zz.data(), aa.data()));
99 }
100 }
101
102 fDataBase->exec_sql("CREATE INDEX isotope_index ON level(isotope_id)");
103 fDataBase->exec_sql("CREATE INDEX dataset_index ON level(dataset_id)");
104 fDataBase->exec_sql("CREATE INDEX dataset_isotope_index ON dataset(isotope_id)");
105
106 fDataBase->exec_sql("CREATE INDEX levelfrom_index ON decay(level_from_id)");
107 fDataBase->exec_sql("CREATE INDEX levelto_index ON decay(level_to_id)");
108
109 fensdf_reader.print_record_counters();
110
111 return 0;
112}
113
114int tkensdf_builder::add_dataset(tkensdf_ident_rec &_dataset, const tkstring &_nucleus, const tkstring &_dataset_comment, const tkstring &_dataset_date, const tkstring &_dataset_source)
115{
116 tkstring newname = _dataset.fdsid.copy().remove_all_extra_white_space();
117 newname.replace_all("'", "`");
118
119 // use begins_with to handle at the same time:
120 // - ADOPTED LEVELS (no known gammas)
121 // - ADOPTED LEVELS, GAMMAS (known gammas)
122 if (_dataset.fdsid.begins_with("ADOPTED LEVELS") && !_dataset.fdsid.contains("TENTATIVE")) {
123 newname = tkstring::form("%s : ADOPTED LEVELS, GAMMAS", _nucleus.data()); //.prepend(tkstring::form("%s : ",_nucleus.data()));
124 _dataset.fis_adopted = true;
125 } else
126 _dataset.fis_adopted = false;
127
128 (*fDataBase)["DATASET"]["dataset_name"].set_value(newname);
129 (*fDataBase)["DATASET"]["isotope_id"].set_value(fIsotopeIndex);
130 if (!_dataset_comment.is_empty()) (*fDataBase)["DATASET"]["dataset_comment"].set_value(_dataset_comment);
131 if (!_dataset_date.is_empty()) (*fDataBase)["DATASET"]["dataset_date"].set_value(_dataset_date);
132 if (!_dataset_source.is_empty()) (*fDataBase)["DATASET"]["dataset_source"].set_value(_dataset_source);
133 (*fDataBase)["DATASET"]["dataset_id"].set_value(fDatasetIndex++);
134 (*fDataBase)["DATASET"].push_row();
135
136 return (fDatasetIndex - 1);
137}
138
139tkstring tkensdf_builder::get_dataset_comment(tkensdf_ident_rec *_data_set)
140{
141 tkstring record;
142 bool is_record = fensdf_reader.first_record(_data_set, record);
143
144 tkensdf_record the_record;
145 tkensdf_record dataset_comment;
146
147 while (is_record) {
148 if (!the_record.set_record(record)) {
149 is_record = fensdf_reader.next_record(_data_set, record);
150 continue;
151 }
152
153 if (the_record.get_record_type() == tkensdf_record::klevel) break;
154 if (the_record.is_comment()) dataset_comment.add_comment_record(record, the_record.is_continuation_record());
155
156 is_record = fensdf_reader.next_record(_data_set, record);
157 }
158
159 return dataset_comment.get_comment_record();
160}
161
162bool tkensdf_builder::read_dataset(tkensdf_ident_rec *_data_set, int _dataset_idx)
163{
164 glog.set_class("db_ensdf_builder");
165 glog.set_method(tkstring::form("read_levels(%s,%d)", _data_set->fdsid.data(), _dataset_idx));
166
167 tkensdf_record the_record;
168 tkensdf_level_rec the_level_record;
169 tkensdf_gamma_rec the_gamma_record;
170
171 tkstring record;
172 bool is_record = fensdf_reader.first_record(_data_set, record);
173
174 std::vector<tkensdf_level_rec> flist_of_levels;
175
176 char *sErrMsg = nullptr;
177 sqlite3_exec(fDataBase->get_sql_db(), "BEGIN TRANSACTION", nullptr, nullptr, &sErrMsg);
178
179 // loop over the dataset and fill temporary collections.
180 while (is_record) {
181
182 if (!the_record.set_record(record)) {
183 is_record = fensdf_reader.next_record(_data_set, record);
184 glog << error_v << "error in : " << _data_set->fnuclide.data() << ": " << _data_set->fdsid.data() << " line: " << _data_set->fcurrentPosition << do_endl;
185 continue;
186 }
187
188 // get the record type
189 tkensdf_record::record_type the_type = the_record.get_record_type();
190
191 // check and analyse the global comments records before the first level
192 if (flist_of_levels.empty() && the_record.is_comment()) {
194 // skip cases where time info is replaced by cross section data
196 the_level_record.fglobal_time_unit = "skip";
197 else if (record.contains("d|s/d|W", tkstring::ECaseCompare::kIgnoreCase))
198 the_level_record.fglobal_time_unit = "skip";
199 else if (record.contains("DS/DW", tkstring::ECaseCompare::kIgnoreCase))
200 the_level_record.fglobal_time_unit = "skip";
201 else if (record.contains("d(SIGMA)/d(OMEGA)", tkstring::ECaseCompare::kIgnoreCase))
202 the_level_record.fglobal_time_unit = "skip";
203 // skip cases where time info is replaced by Pygmy Dipole Resonance (PDR) strength (SIS)
204 else if (record.contains("S{-IS}", tkstring::ECaseCompare::kIgnoreCase))
205 the_level_record.fglobal_time_unit = "skip";
206 // no real idea of what it is..
207 else if (record.contains("G{-|g0", tkstring::ECaseCompare::kIgnoreCase))
208 the_level_record.fglobal_time_unit = "skip";
209 // here define the global witdth units
210 else if (record.contains("mev", tkstring::ECaseCompare::kIgnoreCase))
211 the_level_record.fglobal_time_unit = "MEV";
212 else if (record.contains("kev", tkstring::ECaseCompare::kIgnoreCase))
213 the_level_record.fglobal_time_unit = "KEV";
214 else if (record.contains("ev", tkstring::ECaseCompare::kIgnoreCase))
215 the_level_record.fglobal_time_unit = "EV";
216 }
217 }
218
219 if (fdecay_builder && the_type == tkensdf_record::kgamma && the_level_record.get_energy().value > 0.) {
220
221 the_gamma_record.clear();
222 the_gamma_record.set_record(record);
223
224 tkensdf_level_rec *level_to = nullptr;
225
226 // check if the following records are comments or continuation records on the level
227 is_record = fensdf_reader.next_record(_data_set, record);
228 the_record.set_record(record);
229 while (is_record && (the_record.is_continuation_record() || the_record.is_comment())) {
230 if (the_record.is_comment())
231 the_gamma_record.add_comment_record(record, the_record.is_continuation_record());
232 else if (the_record.is_continuation_record())
233 the_gamma_record.add_continuation_record(record);
234 is_record = fensdf_reader.next_record(_data_set, record);
235 the_record.set_record(record);
236 }
237
238 the_gamma_record.analyse_record();
239
240 // in specific cases that are not treated, the gamma energy is set to 0
241 if (the_gamma_record.get_energy().value == 0.) continue;
242
243 // if the level-to is manually specified:
244 if (the_gamma_record.is_final_level_set()) {
245
246 // if this is an unplaces gamma-ray, we skip it
247 if (the_gamma_record.get_final_level_energy() < 0)
248 continue;
249
250 // else we get the exact energy in the list
251 for (auto it = flist_of_levels.rbegin() + 1; it != flist_of_levels.rend(); ++it) {
252 auto level = &(*it);
253 if (the_gamma_record.get_final_level_energy() == level->get_energy().value) {
254 level_to = level;
255 }
256 }
257 if (level_to == nullptr) the_gamma_record.set_final_level_set(false);
258 }
259 if (level_to == nullptr) {
260 // if the level-to is not specified, or has not been found, we take the closest one
261 double lev_to_approx_energy = the_level_record.get_energy().value - the_gamma_record.get_energy().value;
262
263 // looking for the id of the level to
264 double last_diff = numeric_limits<double>::max();
265 for (auto it = flist_of_levels.rbegin() + 1; it != flist_of_levels.rend(); ++it) {
266 auto level = &(*it);
267 if (the_level_record.get_energy_offset() != level->get_energy_offset()) continue;
268 double diff = abs(lev_to_approx_energy - level->get_energy().value);
269 if (diff > last_diff) break;
270 level_to = level;
271 last_diff = diff;
272 }
273
274 if (level_to == nullptr) {
275 gdebug << "Level_to not found !" << do_endl;
276#ifdef HAS_DEBUG
277 cout << _data_set->fnuclide.data() << ": " << _data_set->fdsid.data() << " line: " << _data_set->fcurrentPosition << endl;
278 cout << "Level from record: ";
279 if (the_level_record.is_energy_offset()) cout << the_level_record.get_energy_offset() << " + ";
280 cout << the_level_record.get_energy().value << " " << the_level_record.get_energy().err << endl;
281 cout << " record -> " << the_level_record.get_record() << endl;
282 cout << "gamma record : " << the_gamma_record.get_energy().value << " " << the_gamma_record.get_energy().err << endl;
283 cout << " record -> " << the_gamma_record.get_record() << endl;
284 if (the_gamma_record.is_final_level_set()) cout << "gamma levels manually set !" << endl;
285 cout << " Looking for a level at: ";
286 if (the_level_record.is_energy_offset()) cout << the_level_record.get_energy_offset() << " + ";
287 cout << lev_to_approx_energy << " keV in:" << endl;
288 for (auto it = flist_of_levels.rbegin() + 1; it != flist_of_levels.rend(); ++it) {
289 if (it->get_energy_offset() != the_level_record.get_energy_offset()) continue;
290 if (it->is_energy_offset()) cout << it->get_energy_offset() << " + ";
291 cout << it->get_energy().value << " keV" << endl;
292 }
293#endif
294 continue;
295 }
296 // if no level-to found closest to 100keV above error bars, the gamma-ray is skipped
297 bool too_high_diff = ((last_diff - level_to->get_energy().err - the_level_record.get_energy().err - the_gamma_record.get_energy().err) > 100);
298 if (too_high_diff) {
299 gdebug << "High energy difference !" << do_endl;
300#ifdef HAS_DEBUG
301 cout << _data_set->fnuclide.data() << ": " << _data_set->fdsid.data() << " line: " << _data_set->fcurrentPosition << endl;
302 cout << "Level from record: ";
303 if (the_level_record.is_energy_offset()) cout << the_level_record.get_energy_offset() << " + ";
304 cout << the_level_record.get_energy().value << " " << the_level_record.get_energy().err << endl;
305 cout << " record -> " << the_level_record.get_record() << endl;
306 cout << "gamma record : " << the_gamma_record.get_energy().value << " " << the_gamma_record.get_energy().err << endl;
307 cout << " record -> " << the_gamma_record.get_record() << endl;
308 if (the_gamma_record.is_final_level_set()) cout << "gamma levels manually set !" << endl;
309 cout << " Looking for a level at: ";
310 if (the_level_record.is_energy_offset()) cout << the_level_record.get_energy_offset() << " + ";
311 cout << lev_to_approx_energy << " keV in:" << endl;
312 for (auto it = flist_of_levels.rbegin() + 1; it != flist_of_levels.rend(); ++it) {
313 if (it->get_energy_offset() != the_level_record.get_energy_offset()) continue;
314 if (it->is_energy_offset()) cout << it->get_energy_offset() << " + ";
315 cout << it->get_energy().value << " keV" << endl;
316 }
317 cout << " Found a level at: " << level_to->get_energy().value << " keV ==> Diff= " << last_diff << " keV" << endl;
318#endif
319 continue;
320 }
321 }
322
323 // fill level info in db
324 fdecay_builder->fill_gamma(the_level_record.flevelid, level_to->flevelid, the_gamma_record);
325 } else if (the_type == tkensdf_record::klevel) {
326 the_level_record.clear();
327 the_level_record.set_record(record);
328
329 // check if the following records are comments or continuation records on the level
330 is_record = fensdf_reader.next_record(_data_set, record);
331 the_record.set_record(record);
332 while (is_record && (the_record.is_continuation_record() || the_record.is_comment())) {
333 if (the_record.is_comment())
334 the_level_record.add_comment_record(record, the_record.is_continuation_record());
335 else if (the_record.is_continuation_record())
336 the_level_record.add_continuation_record(record);
337 is_record = fensdf_reader.next_record(_data_set, record);
338 the_record.set_record(record);
339 }
340
341 the_level_record.analyse_record();
342
343 // fill level info in db
344 if (flevel_builder && (the_level_record.get_energy().value >= 0.)) {
345
346 // if no ground state, add a dummy level a 0 energy
347 if (flist_of_levels.size() == 0 && (the_level_record.get_energy().value > 0. || the_level_record.is_energy_offset())) {
348 tkensdf_level_rec dummy_rec;
349 dummy_rec.set_record("Dummy L 0.0 ");
350 dummy_rec.analyse_record();
351 flevel_builder->fill_level(_dataset_idx, fIsotopeIndex, dummy_rec, _data_set->fis_adopted);
352 dummy_rec.flevelid = flevel_builder->get_level_id();
353 flist_of_levels.emplace_back(dummy_rec);
354 }
355
356 flevel_builder->fill_level(_dataset_idx, fIsotopeIndex, the_level_record, _data_set->fis_adopted);
357 the_level_record.flevelid = flevel_builder->get_level_id();
358 flist_of_levels.emplace_back(the_level_record);
359
360 // check that the energy is not higher thant the previous one
361 if (the_level_record.is_energy_offset()) {
362 std::vector<pair<int, tkensdf_level_rec *>> vec;
363 int idx = 0;
364 for (auto it = flist_of_levels.begin(); it != flist_of_levels.end(); it++, idx++) {
365 if (it->get_energy_offset() == the_level_record.get_energy_offset()) {
366 vec.push_back({idx, (&(*it))});
367 }
368 }
369 if (vec.size() > 1 && (vec.at(vec.size() - 1).second->get_energy().value < vec.at(vec.size() - 2).second->get_energy().value)) {
370 gdebug << "Inverted levels in: " << _data_set->fnuclide.data() << ": " << _data_set->fdsid.data() << " [ " << flist_of_levels.at(vec.at(vec.size() - 1).first).get_energy_offset() << "+" << flist_of_levels.at(vec.at(vec.size() - 1).first).get_energy().value << " ; " << flist_of_levels.at(vec.at(vec.size() - 2).first).get_energy_offset() << "+" << flist_of_levels.at(vec.at(vec.size() - 2).first).get_energy().value << "]" << do_endl;
371 swap(flist_of_levels[vec.at(vec.size() - 1).first], flist_of_levels[vec.at(vec.size() - 2).first]);
372 cin.get();
373 }
374 } else if (flist_of_levels.size() > 1 && (flist_of_levels.at(flist_of_levels.size() - 1).get_energy().value < flist_of_levels.at(flist_of_levels.size() - 2).get_energy().value)) {
375 gdebug << "Inverted levels in: " << _data_set->fnuclide.data() << ": " << _data_set->fdsid.data() << " [" << flist_of_levels.at(flist_of_levels.size() - 1).get_energy().value << " ; " << flist_of_levels.at(flist_of_levels.size() - 2).get_energy().value << "]" << do_endl;
376 swap(flist_of_levels[flist_of_levels.size() - 1], flist_of_levels[flist_of_levels.size() - 2]);
377 }
378 }
379 } else {
380 is_record = fensdf_reader.next_record(_data_set, record);
381 }
382 } // ok
383 sqlite3_exec(fDataBase->get_sql_db(), "END TRANSACTION", nullptr, nullptr, &sErrMsg);
384
385 glog.clear();
386
387 return true;
388}
389
390#ifdef HAS_ROOT
391ClassImp(tkensdf_builder);
392#endif
Interface to the sqlite database.
Definition tkdatabase.h:34
Main class dedicated to the ENSDF records decoding.
virtual ~tkensdf_builder()
int add_dataset(tkensdf_ident_rec &_dataset, const tkstring &_nucleus, const tkstring &_dataset_comment="", const tkstring &_dataset_date="", const tkstring &_dataset_source="")
tkensdf_builder(tkdatabase *_database, const tkstring &_input_folder)
virtual void analyse_record() override
analyse the record content
virtual bool set_record(const tkstring &_record) override
define the record from a string
bool is_final_level_set()
return true if the information on the final level is manually defined
double get_final_level_energy()
return true if the information on the final level is manually defined
void set_final_level_set(bool _status)
return true if the information on the final level is manually defined
Decodding of the ENSDF identification record properties.
virtual void analyse_record() override
analyse the record content
virtual bool set_record(const tkstring &_record) override
define the record from a string
bool first_record(tkensdf_ident_rec *_dataset, tkstring &record)
move in the current file to the first record of the selected data set
bool next_record(tkensdf_ident_rec *_dataset, tkstring &record)
get the next record of the selected data set
Decodding of the ENSDF records.
const tkstring & get_record()
get record
virtual void add_comment_record(const tkstring &_comment_record, bool _is_continuation=false)
add a continuation record from a string
bool is_energy_offset()
to now if the energy is given with an offset
virtual bool set_record(const tkstring &_record)
define the record from a string. Option false only checks if the record is an identification record
bool is_continuation_record()
to now if the record is a continuation record or not
virtual const tkstring & get_comment_record() const
get the continuation record
const tkdb_table::measure_data_struct & get_energy() const
tkstring get_energy_offset()
get_energy_offset
virtual void add_continuation_record(const tkstring &_continuation_record)
add a continuation record from a string
record_type get_record_type()
get record type
bool is_comment()
to now if the record is a comment
std::string with usefull tricks from TString (ROOT) and KVString (KaliVeda) and more....
Definition tkstring.h:32
tkstring copy() const
Returns a copy of this string.
Definition tkstring.cpp:391
static const char * form(const char *_format,...)
Definition tkstring.cpp:452
bool is_empty() const
Definition tkstring.h:141
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
tkstring & remove_all_extra_white_space()
Definition tkstring.cpp:547
bool contains(const char *_pat, ECaseCompare _cmp=kExact) const
Definition tkstring.h:175
bool begins_with(const char *_s, ECaseCompare _cmp=kExact) const
Definition tkstring.h:163
tkstring & replace_all(const tkstring &_s1, const tkstring &_s2)
Definition tkstring.h:181
Definition tklog.cpp:16
tklog & error_v(tklog &log)
Definition tklog.h:407
tklog & do_endl(tklog &log)
Definition tklog.h:212