#ifndef HEADUTIL_INCLUCED #define HEADUTIL_INCLUCED #include "Det.hh" #include "Head.hh" #include #include using stringutil::split; using stringutil::join; using stringutil::to; using stringutil::print; using stringutil::contains; using stringutil::is_a; /*! Return the vector of items that may differ when combining two Head's. */ inline vector& Head_items_that_may_differ() { static vector r = { "DAQ:livetime", "livetime:numberOfSeconds", "livetime:errorOfSeconds", "seed:program", "seed:level", "seed:iseed", "seed:field0", "seed:field1", "seed:field2", "generator:date", "generator:time", "simul:date", "simul:time", "physics:version", "physics:date", "physics:time", "propag:time", "propag:date", "flux:file_1", "start_run:run_id", "norma:numberOfPrimaries", "genvol:numberOfEvents" }; return r; } /*! Return the items that must be added together when merging two Head objects */ inline vector& Head_items_to_be_added() { static vector r = { "genvol:numberOfEvents", "DAQ:livetime", "livetime:numberOfSeconds", "norma:numberOfPrimaries" }; return r; } /*! remove training _n, with n a number */ inline string basekey( string key ) { auto v = split( key, "_" ); if ( v.size() < 2 ) return key; if ( is_a ( v.back() ) ) { v.pop_back(); return join("_", v); } return key; } /*! Return a list of keys, for which 'a' contains other values that 'b'. Keys that are missing in either Head are also included. */ inline vector diff( Head& a, Head& b , bool use_basekeys = false ) { vector r; std::set allkeys; for ( auto& p : a ) { allkeys.insert( p.first );} for ( auto& p : b ) { allkeys.insert( p.first );} for ( auto key : allkeys ) { bool key_in_both = a.have_line( key ) && b.have_line(key); if ( !key_in_both ) { r.push_back( key ); continue; } // white space, may differ, so split the line and check each item... vector line_a = split( a[key] ); // empty string if key is not there vector line_b = split( b[key] ); remove_element_if( line_a, [](string s){ return stringutil::trim(s)=="";} ); remove_element_if( line_b, [](string s){ return stringutil::trim(s)=="";} ); // if the lines have different number of entries, they are not compatible if ( line_a.size() != line_b.size() ) { r.push_back( key ); continue; } for (unsigned i = 0; i < line_a.size() ; i++) { if ( stringutil::trim(line_a[i]) != stringutil::trim(line_b[i]) ) { auto fieldnames = Head::_hdr_dict()[basekey(key)]; string fieldname = i < fieldnames.size() ? fieldnames[i] : "field" + str(i); string k = use_basekeys ? basekey( key ) : key ; r.push_back( k + ":" + fieldname); } } } return r; } /*! Return true iff the two headers are equivalent / can be added together */ inline bool headers_are_equivalent( Head& a, Head& b , bool run_by_run_mode = false ) { vector items_that_may_differ = Head_items_that_may_differ(); // copy as we may add things if ( run_by_run_mode ) { items_that_may_differ.push_back( "detector:field0"); items_that_may_differ.push_back( "detector:field1"); items_that_may_differ.push_back( "aashowerfit_detector:field0"); items_that_may_differ.push_back( "time_interval:field0"); items_that_may_differ.push_back( "time_interval:field1"); } auto D = diff( a, b, true ); for (string item : D ) { if ( !contains( items_that_may_differ, item ) ) { print(a); print(b); print ("headers can not be added because the following is different", item ); auto v = split(item, ":"); print ( item, " : ", a.get_field( v[0], v[1] )); print ( item, " : ", b.get_field( v[1], v[1] )); return false; } } return true; } /*! Add b to a, after stringent tests. If run_by_run_mode is true, the detector tags may differ */ inline bool add( Head& a, Head& b , bool run_by_run_mode = false ) { if (!headers_are_equivalent(a, b, run_by_run_mode ) ) { return false; } for ( string s : Head_items_to_be_added() ) { auto v = split(s, ":"); if (! a.have_line(v[0]) && !b.have_line( v[0] )) continue; double aa = stod ( a.get_field( v[0], v[1] ) ); double bb = stod ( b.get_field( v[0], v[1] ) ); // do a 'set-field', overwriting the existing value int idx = a.get_index_of_field( v[0], v[1] ); vector ss = split(a.get_line( v[0] )); remove_element_if( ss, [](string s){ return stringutil::trim(s)=="";} ); ss[idx] = str( aa + bb ); a.set_line( v[0], join(" ", ss ), false ); print("adding header item : ", v[0], v[1], aa, bb , "==>", a.get_line(v[0])); } return true; } /*! heuristic function that tries to determine the kind of file we are dealing with. */ enum filetype_t { unknown = -1, data = 0, mupage = 1, corsika = 2, genhen = 3, gseagen = 4 }; inline filetype_t filetype(Head& h) { string physics = h.have_line("physics") ? h.get_line("physics") : ""; if ( physics == "" ) physics = h.have_line("simul") ? h.get_line("simul") : ""; if ( physics == "" ) return filetype_t::data; if ( contains ( physics, "HEMAS" ) ) return filetype_t::mupage; if ( contains ( physics, "CORSIKA" )) return filetype_t::corsika; if ( contains ( physics, "gSeaGen" )) return filetype_t::gseagen; if ( contains ( physics, "genhen" )) return filetype_t::genhen; if ( contains ( physics, "GENHEN" )) return filetype_t::genhen; // todo: all the others return unknown; } inline double get_livetime(Head& h) { switch ( filetype(h) ) { case filetype_t::data : return h.daq_livetime(); case filetype_t::mupage : return h.mc_livetime(); case filetype_t::gseagen : return to ( h.get_field("tgen", "numberOfSeconds") ); case filetype_t::corsika : return 3.1536e+07; //https://git.km3net.de/simulation/corant/blob/master/cosmic_ray_flux.cpp (line 135) case filetype_t::genhen : return 3.156e7; //https://git.km3net.de/simulation/genhen/blob/master/inc/params.inc (line 9) default : return 0.; } } inline double get_ngen(Head& h) { switch ( filetype(h) ) { case filetype_t::genhen : case filetype_t::gseagen : return h.ngen(); case filetype_t::corsika : return to ( h.get_field("norma", "numberOfPrimaries") ); default : return 0.; } } /*! attemp to find a valid detector file in the header. various keys can be tried */ inline string get_detector_file_from_head( Head& h , string keys = "detector,detector_1,aashowerfit_detector" , bool verbose = true ) { vector ks = split(keys,","); print ( "ks =", ks ); for( auto& k : ks ) { if ( ! h.have_line( k ) ) continue; string s = stringutil::trim(h.get_line(k)); if ( s== "" ) continue; if ( file_exists( s ) ) { if (verbose) { print ("found an existing detector file for header key ", k ); print ("namely:", s); } return s; } } return ""; } #endif