TkN 2.1
Toolkit for Nuclei
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 * This software is governed by the CeCILL-B license under French law and *
11 * abiding by the rules of distribution of free software. You can use, *
12 * modify and/ or redistribute the software under the terms of the *
13 * CeCILL-B license as circulated by CEA, CNRS and INRIA at the following *
14 * URL \"http://www.cecill.info\". *
15 * *
16 * As a counterpart to the access to the source code and rights to copy, *
17 * modify and redistribute granted by the license, users are provided *
18 * only with a limited warranty and the software's author, the holder of *
19 * the economic rights, and the successive licensors have only limited *
20 * liability. *
21 * *
22 * In this respect, the user's attention is drawn to the risks associated *
23 * with loading, using, modifying and/or developing or reproducing the *
24 * software by the user in light of its specific status of free software, *
25 * that may mean that it is complicated to manipulate, and that also *
26 * therefore means that it is reserved for developers and experienced *
27 * professionals having in-depth computer knowledge. Users are therefore *
28 * encouraged to load and test the software's suitability as regards *
29 * their requirements in conditions enabling the security of their *
30 * systems and/or data to be ensured and, more generally, to use and *
31 * operate it in the same conditions as regards security. *
32 * *
33 * The fact that you are presently reading this means that you have had *
34 * knowledge of the CeCILL-B license and that you accept its terms. *
35 ********************************************************************************/
36
37#include "tkensdf_builder.h"
38
39#include <iostream>
40#include <unistd.h>
41#include <cmath>
42
43#include "tklog.h"
44#include "tkensdf_level_rec.h"
45#include "tkensdf_gamma_rec.h"
46
47namespace tkn {
54}
55
56using namespace tkn;
57using namespace std;
58
59tkensdf_builder::tkensdf_builder(tkdatabase *_database, const tkstring &_input_folder):
60 fDataBase(_database),
61 fensdf_reader(_input_folder)
62{
63}
64
66
68{
69 //--------------------------------------------------------
70 // read files and fill database
71 //--------------------------------------------------------
72
73 fDataBase->begin("charge,mass,symbol,isotope_id","isotope INNER JOIN element ON isotope.element_id=element.element_id","charge>0");
74
75 tkstring message;
76
77 if(flevel_builder) {
78 message = "Building '" + flevel_builder->get_table().get_name() + "' table";
79 }
80
81 int oldZ=0;
82 while(fDataBase->next())
83 {
84 tkstring nuc = tkstring::form("%s%s",(*fDataBase)["ISOTOPE"]["mass"].get_value().data(),(*fDataBase)["ELEMENT"]["symbol"].get_value().data());
85 tkstring zz = (*fDataBase)["ELEMENT"]["charge"].get_value();
86 tkstring aa = (*fDataBase)["ISOTOPE"]["mass"].get_value();
87 fIsotopeIndex = ((tkstring)(*fDataBase)["ISOTOPE"]["isotope_id"].get_value()).atoi();
88
89 int curZ = zz.atoi();
90 if(curZ<1) continue;
91
92 // open the ensdf file
93 bool has_levels = fensdf_reader.open_nuc(nuc,kensdf);
94 // fensdf_reader.print_datasets();
95
96 // loop over the datasets
97 for(auto &dataset: *fensdf_reader.get_datasets()) {
98 if(dataset.fdsid.begins_with("COMMENTS")) continue;
99 int datasetIdx = add_dataset(dataset,nuc.data());
100 read_dataset(&dataset, datasetIdx);
101 }
102
103 // open the xundl file
104 bool has_levelsxundl = fensdf_reader.open_nuc(nuc,kxundl);
105 // loop over the datasets
106 for(auto &dataset: *fensdf_reader.get_datasets()) {
107 if(dataset.fdsid.begins_with("COMMENTS")) continue;
108 int datasetIdx = add_dataset(dataset,nuc.data());
109 read_dataset(&dataset, datasetIdx);
110 }
111
112 if(curZ!=oldZ) {
113 glog.progress_bar(117,curZ,message.data()); oldZ=curZ;
114 }
115
116 if(has_levels||has_levelsxundl){
117 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()));
118 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()));
119 }
120 }
121
122 fDataBase->exec_sql("CREATE INDEX isotope_index ON level(isotope_id)");
123 fDataBase->exec_sql("CREATE INDEX dataset_index ON level(dataset_id)");
124
125 fDataBase->exec_sql("CREATE INDEX levelfrom_index ON decay(level_from_id)");
126 fDataBase->exec_sql("CREATE INDEX levelto_index ON decay(level_to_id)");
127
128 fensdf_reader.print_record_counters();
129
130 return 0;
131}
132
134{
135 tkstring newname = _dataset.fdsid.copy().remove_all_extra_white_space();
136 newname.replace_all("'","`");
137
138 // use begins_with to handle at the same time:
139 // - ADOPTED LEVELS (no known gammas)
140 // - ADOPTED LEVELS, GAMMAS (known gammas)
141 if(_dataset.fdsid.begins_with("ADOPTED LEVELS") && !_dataset.fdsid.contains("TENTATIVE")) {
142 newname = tkstring::form("%s : ADOPTED LEVELS, GAMMAS",_comment.data());//.prepend(tkstring::form("%s : ",_comment.data()));
143 _dataset.fis_adopted = true;
144 }
145 else _dataset.fis_adopted = false;
146
147 (*fDataBase)["DATASET"]["dataset_name"].set_value(newname);
148 (*fDataBase)["DATASET"]["dataset_comment"].set_value(_comment);
149 (*fDataBase)["DATASET"]["dataset_id"].set_value(fDatasetIndex++);
150 (*fDataBase)["DATASET"].push_row();
151
152 return (fDatasetIndex-1);
153}
154
155bool tkensdf_builder::read_dataset(tkensdf_ident_rec *_data_set, int _dataset_idx)
156{
157 glog.set_class("db_ensdf_builder");
158 glog.set_method(tkstring::form("read_levels(%s,%d)", _data_set->fdsid.data(),_dataset_idx));
159
160 tkensdf_record the_record;
161 tkensdf_level_rec the_level_record;
162 tkensdf_gamma_rec the_gamma_record;
163
164 tkstring record;
165 bool is_record = fensdf_reader.first_record(_data_set,record);
166
167 std::vector<tkensdf_level_rec> flist_of_levels;
168
169 char * sErrMsg = nullptr;
170 sqlite3_exec(fDataBase->get_sql_db(), "BEGIN TRANSACTION", nullptr, nullptr, &sErrMsg);
171
172 // loop over the dataset and fill temporary collections.
173 while( is_record ) {
174
175 if(!the_record.set_record(record)) {
176 is_record = fensdf_reader.next_record(_data_set,record);
177 glog << error_v << "error in : " << _data_set->fnuclide.data()<<": "<<_data_set->fdsid.data()<<" line: "<<_data_set->fcurrentPosition<< do_endl;
178 continue;
179 }
180
181 // get the record type
182 tkensdf_record::record_type the_type = the_record.get_record_type();
183
184 // check and analyse the global comments records before the first level
185 if(flist_of_levels.empty() && the_record.is_comment()) {
186 if(record.contains("T$LABEL",tkstring::ECaseCompare::kIgnoreCase) || record.contains("T LABEL",tkstring::ECaseCompare::kIgnoreCase)) {
187 // skip cases where time info is replaced by cross section data
188 if(record.contains("mb/sr",tkstring::ECaseCompare::kIgnoreCase)) the_level_record.fglobal_time_unit = "skip";
189 else if(record.contains("d|s/d|W",tkstring::ECaseCompare::kIgnoreCase)) the_level_record.fglobal_time_unit = "skip";
190 else if(record.contains("DS/DW",tkstring::ECaseCompare::kIgnoreCase)) the_level_record.fglobal_time_unit = "skip";
191 else if(record.contains("d(SIGMA)/d(OMEGA)",tkstring::ECaseCompare::kIgnoreCase)) the_level_record.fglobal_time_unit = "skip";
192 // skip cases where time info is replaced by Pygmy Dipole Resonance (PDR) strength (SIS)
193 else if(record.contains("S{-IS}",tkstring::ECaseCompare::kIgnoreCase)) the_level_record.fglobal_time_unit = "skip";
194 // no real idea of what it is..
195 else if(record.contains("G{-|g0",tkstring::ECaseCompare::kIgnoreCase)) the_level_record.fglobal_time_unit = "skip";
196 // here define the global witdth units
197 else if(record.contains("mev",tkstring::ECaseCompare::kIgnoreCase)) the_level_record.fglobal_time_unit = "MEV";
198 else if(record.contains("kev",tkstring::ECaseCompare::kIgnoreCase)) the_level_record.fglobal_time_unit = "KEV";
199 else if(record.contains("ev",tkstring::ECaseCompare::kIgnoreCase)) the_level_record.fglobal_time_unit = "EV";
200 }
201 }
202
203 if(fdecay_builder && the_type == tkensdf_record::kgamma && the_level_record.get_energy().value>0.) {
204
205 the_gamma_record.clear();
206 the_gamma_record.set_record(record);
207
208 tkensdf_level_rec *level_to = nullptr;
209
210 // check if the following records are comments or continuation records on the level
211 is_record = fensdf_reader.next_record(_data_set,record);
212 the_record.set_record(record);
213 while( is_record && (the_record.is_continuation_record() || the_record.is_comment()) ) {
214 if(the_record.is_comment()) the_gamma_record.add_comment_record(record,the_record.is_continuation_record());
215 else if(the_record.is_continuation_record()) the_gamma_record.add_continuation_record(record);
216 is_record = fensdf_reader.next_record(_data_set,record);
217 the_record.set_record(record);
218 }
219
220 the_gamma_record.analyse_record();
221
222 // in specific cases that are not treated, the gamma energy is set to 0
223 if(the_gamma_record.get_energy().value==0.) continue;
224
225 // if the level-to is manually specified:
226 if(the_gamma_record.is_final_level_set()) {
227
228 // if this is an unplaces gamma-ray, we skip it
229 if(the_gamma_record.get_final_level_energy()<0)
230 continue;
231
232 // else we get the exact energy in the list
233 for(auto it = flist_of_levels.rbegin()+1 ; it != flist_of_levels.rend() ; ++it) {
234 auto level = &(*it);
235 if(the_gamma_record.get_final_level_energy() == level->get_energy().value) {
236 level_to = level;
237 }
238 }
239 if(level_to == nullptr) the_gamma_record.set_final_level_set(false);
240 }
241 if(level_to == nullptr) {
242 // if the level-to is not specified, or has not been found, we take the closest one
243 double lev_to_approx_energy = the_level_record.get_energy().value - the_gamma_record.get_energy().value;
244
245 // looking for the id of the level to
246 double last_diff = numeric_limits<double>::max();
247 for(auto it = flist_of_levels.rbegin()+1 ; it != flist_of_levels.rend() ; ++it) {
248 auto level = &(*it);
249 if(the_level_record.get_energy_offset() != level->get_energy_offset()) continue;
250 double diff = abs(lev_to_approx_energy-level->get_energy().value);
251 if(diff>last_diff) break;
252 level_to = level;
253 last_diff = diff;
254 }
255
256 if(level_to == nullptr) {
257 gdebug <<"Level_to not found !"<< do_endl;
258#ifdef HAS_DEBUG
259 cout<<_data_set->fnuclide.data()<<": "<<_data_set->fdsid.data()<<" line: "<<_data_set->fcurrentPosition<<endl;
260 cout<< "Level from record: ";
261 if(the_level_record.is_energy_offset()) cout << the_level_record.get_energy_offset() << " + ";
262 cout << the_level_record.get_energy().value << " " << the_level_record.get_energy().err <<endl;
263 cout<<" record -> " << the_level_record.get_record() << endl;
264 cout<< "gamma record : " << the_gamma_record.get_energy().value << " " << the_gamma_record.get_energy().err <<endl;
265 cout<<" record -> " << the_gamma_record.get_record() << endl;
266 if(the_gamma_record.is_final_level_set()) cout<<"gamma levels manually set !" << endl;
267 cout<< " Looking for a level at: ";
268 if(the_level_record.is_energy_offset()) cout << the_level_record.get_energy_offset() << " + ";
269 cout << lev_to_approx_energy<< " keV in:"<<endl; for(auto it = flist_of_levels.rbegin()+1 ; it != flist_of_levels.rend() ; ++it) {
270 if(it->get_energy_offset()!=the_level_record.get_energy_offset()) continue;
271 if(it->is_energy_offset()) cout << it->get_energy_offset() << " + ";
272 cout << it->get_energy().value << " keV" << endl;
273 }
274#endif
275 }
276 if(!level_to) continue;
277 // if no level-to found closest to 100keV above error bars, the gamma-ray is skipped
278 bool too_high_diff = ((last_diff-level_to->get_energy().err-the_level_record.get_energy().err-the_gamma_record.get_energy().err) > 100);
279 if(too_high_diff) {
280 gdebug <<"High energy difference !"<< do_endl;
281#ifdef HAS_DEBUG
282 cout<<_data_set->fnuclide.data()<<": "<<_data_set->fdsid.data()<<" line: "<<_data_set->fcurrentPosition<<endl;
283 cout<< "Level from record: ";
284 if(the_level_record.is_energy_offset()) cout << the_level_record.get_energy_offset() << " + ";
285 cout << the_level_record.get_energy().value << " " << the_level_record.get_energy().err <<endl;
286 cout<<" record -> " << the_level_record.get_record() << endl;
287 cout<< "gamma record : " << the_gamma_record.get_energy().value << " " << the_gamma_record.get_energy().err <<endl;
288 cout<<" record -> " << the_gamma_record.get_record() << endl;
289 if(the_gamma_record.is_final_level_set()) cout<<"gamma levels manually set !" << endl;
290 cout<< " Looking for a level at: ";
291 if(the_level_record.is_energy_offset()) cout << the_level_record.get_energy_offset() << " + ";
292 cout << lev_to_approx_energy<< " keV in:"<<endl;
293 for(auto it = flist_of_levels.rbegin()+1 ; it != flist_of_levels.rend() ; ++it) {
294 if(it->get_energy_offset()!=the_level_record.get_energy_offset()) continue;
295 if(it->is_energy_offset()) cout << it->get_energy_offset() << " + ";
296 cout << it->get_energy().value << " keV" << endl;
297 }
298 cout<< " Found a level at: " << level_to->get_energy().value<< " keV ==> Diff= "<<last_diff<< " keV" << endl;
299#endif
300 continue;
301 }
302 }
303
304 // fill level info in db
305 fdecay_builder->fill_gamma(the_level_record.flevelid, level_to->flevelid, the_gamma_record);
306 }
307 else if(the_type == tkensdf_record::klevel) {
308 the_level_record.clear();
309 the_level_record.set_record(record);
310
311 // check if the following records are comments or continuation records on the level
312 is_record = fensdf_reader.next_record(_data_set,record);
313 the_record.set_record(record);
314 while( is_record && (the_record.is_continuation_record() || the_record.is_comment()) ) {
315 if(the_record.is_comment()) the_level_record.add_comment_record(record,the_record.is_continuation_record());
316 else if(the_record.is_continuation_record()) the_level_record.add_continuation_record(record);
317 is_record = fensdf_reader.next_record(_data_set,record);
318 the_record.set_record(record);
319 }
320
321 the_level_record.analyse_record();
322
323 // fill level info in db
324 if(flevel_builder && (the_level_record.get_energy().value>=0.)) {
325
326 // if no ground state, add a dummy level a 0 energy
327 if(flist_of_levels.size()==0 && (the_level_record.get_energy().value>0. || the_level_record.is_energy_offset())) {
328 tkensdf_level_rec dummy_rec;
329 dummy_rec.set_record("Dummy L 0.0 ");
330 dummy_rec.analyse_record();
331 flevel_builder->fill_level(_dataset_idx, fIsotopeIndex, dummy_rec, _data_set->fis_adopted);
332 dummy_rec.flevelid = flevel_builder->get_level_id();
333 flist_of_levels.emplace_back(dummy_rec);
334 }
335
336 flevel_builder->fill_level(_dataset_idx, fIsotopeIndex, the_level_record, _data_set->fis_adopted);
337 the_level_record.flevelid = flevel_builder->get_level_id();
338 flist_of_levels.emplace_back(the_level_record);
339
340 // check that the energy is not higher thant the previous one
341 if(the_level_record.is_energy_offset()) {
342 std::vector<pair<int,tkensdf_level_rec*>> vec;
343 int idx=0;
344 for(auto it = flist_of_levels.begin() ; it != flist_of_levels.end() ; it++, idx++) {
345 if(it->get_energy_offset() == the_level_record.get_energy_offset()) {
346 vec.push_back({idx,(&(*it))});
347 }
348 }
349 if(vec.size()>1 && (vec.at(vec.size()-1).second->get_energy().value < vec.at(vec.size()-2).second->get_energy().value)) {
350 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;
351 swap(flist_of_levels[vec.at(vec.size()-1).first],flist_of_levels[vec.at(vec.size()-2).first]);
352 cin.get();
353 }
354 }
355 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)) {
356 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;
357 swap(flist_of_levels[flist_of_levels.size()-1],flist_of_levels[flist_of_levels.size()-2]);
358 }
359 }
360 }
361 else {
362 is_record = fensdf_reader.next_record(_data_set,record);
363 }
364 } // ok
365 sqlite3_exec(fDataBase->get_sql_db(), "END TRANSACTION", nullptr, nullptr, &sErrMsg);
366
367
368 glog.clear();
369
370 return true;
371}
372
373#ifdef HAS_ROOT
374ClassImp(tkensdf_builder);
375#endif
Interface to the sqlite database.
Definition: tkdatabase.h:57
void begin(tkstring _selection, tkstring _from, tkstring _condition="", tkstring _loop_name="default", tkstring _extra_cmd="")
Definition: tkdatabase.cpp:219
int exec_sql(const char *_cmd)
returns the first value for selection
Definition: tkdatabase.cpp:327
sqlite3 * get_sql_db()
Definition: tkdatabase.h:84
bool next(const tkstring &_loop_name="default")
executes the select statement
Definition: tkdatabase.cpp:245
tkdb_table & get_table()
Definition: tkdb_builder.h:69
tkstring get_name()
Definition: tkdb_table.h:116
void fill_gamma(const int &_level_idx_from, const int &_level_idx_to, const tkensdf_gamma_rec &_gamma_record)
Main class dedicated to the ENSDF records decoding.
virtual ~tkensdf_builder()
int add_dataset(tkensdf_ident_rec &_dataset, const tkstring &_comment)
Decodding of the ENSDF gamma 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 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.
Decodding of the ENSDF level 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 open_nuc(const tkstring &_nuc_name, const ensdf_data_type &_ftype=kensdf)
open the file for the slected nucleus and data type, and extract the available data sets
bool first_record(tkensdf_ident_rec *_dataset, tkstring &record)
move in the current file to the first record of the selected data set
void print_record_counters()
print record counters
bool next_record(tkensdf_ident_rec *_dataset, tkstring &record)
get the next record of the selected data set
std::vector< tkensdf_ident_rec > * get_datasets()
get access to the identification records
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
tkstring fglobal_time_unit
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
void fill_level(int _dset_idx, int _isotope_idx, const tkensdf_level_rec &_lev_record, bool _adopted)
std::string with usefull tricks from TString (ROOT) and KVString (KaliVeda) and more....
Definition: tkstring.h:54
tkstring copy() const
Returns a copy of this string.
Definition: tkstring.cpp:370
static const char * form(const char *_format,...)
Definition: tkstring.cpp:431
int atoi() const
Converts a string to integer value.
Definition: tkstring.cpp:175
tkstring & remove_all_extra_white_space()
Definition: tkstring.cpp:526
bool contains(const char *_pat, ECaseCompare _cmp=kExact) const
Definition: tkstring.h:197
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
tklog & error_v(tklog &log)
Definition: tklog.h:430
@ kensdf
@ kxundl
tklog & do_endl(tklog &log)
Definition: tklog.h:235