#ifndef EVENTFILE_HH_INCLUDED #define EVENTFILE_HH_INCLUDED #include #include "stringutil.hh" #include "io_util.hh" #include "JDAQTimeslice.hh" #include "Det.hh" #include "Head_util.hh" #include "km3net-dataformat/offline/Evt.hh" #include "km3net-dataformat/offline/io_ascii.hh" #include "km3net-dataformat/offline/io_online.hh" #include "km3net-dataformat/definitions/root.hh" #include "TFile.h" #include "TStopwatch.h" #include "TTree.h" #include "TH2D.h" #include "TDirectory.h" #include "TKey.h" #include "TSocket.h" using stringutil::nextline; using stringutil::startswith; using stringutil::env; using stringutil::unzip_file; using stringutil::stage_file; using stringutil::replace_all; using namespace std; //! Abstract base class for polymorphic eventfiles struct BaseEventFile { virtual bool can_open( string filename ) { return false; } virtual bool can_open( TFile* f ) { return false; } virtual bool open( TFile* f ) { return false; } virtual bool open( string filename) = 0; virtual bool get_event(int i, Evt& evt, bool verbose = true ) = 0; virtual bool have_event(int i) = 0; virtual bool read_header(Head& h) = 0; virtual BaseEventFile* clone() = 0; virtual string info() { return "BaseEventFile."; } BaseEventFile(): slave(0), read_hits(true), read_mc_hits(true) {} virtual ~BaseEventFile() {}; //! pointer to other file that should be read along with this one BaseEventFile* slave; //! flags to control branch/tag loading bool read_hits; bool read_mc_hits; }; #ifdef HAVEJPPX // foward declaration of JNET::JControlHostObjectIterator namespace JNET { template< typename T> class JControlHostObjectIterator; } namespace KM3NETDAQ { class JDAQEvent; } struct SocketEventReader : public BaseEventFile { // index will go from 0 to infinity for every read event int index; JNET::JControlHostObjectIterator< KM3NETDAQ::JDAQEvent>* J; SocketEventReader() : index(-1), J(nullptr) {} virtual bool can_open( string hostname ); virtual bool open( string hostname ); bool have_event (int i ); bool get_event( int i, Evt& evt, bool verbose = true ); virtual string info() { return "SocketEventReader"; } virtual bool read_header( Head& h ) { return false; } SocketEventReader* clone() { return new SocketEventReader(*this) }; }; #endif //! Cache for keeping track of ascii event files template struct Cache { map M; list L; int size; Cache(int size = 2 ) : size(size) {} bool have(int idx) { return std::find( L.begin(), L.end(), idx ) != L.end(); } bool remove(int idx) { if (!have(idx)) return false; M.erase(idx); L.remove(idx); return true; } bool get(int idx, T& t) { if (!have(idx)) return false; t = M[idx]; return true; } void resize(int s ) { while (L.size() > unsigned(s) ) { M.erase( L.front() ); L.pop_front(); } } T& create(int idx) { if (have(idx)) fatal("attempt to re-create", idx); if (size > 0 ) resize( size - 1 ); L.push_back( idx ); static T o; //so all copies are the same (this is good for root compression) return M[idx] = o; } void insert(int idx, T& t) { if (have(idx)) fatal("attempt to re-insert", idx); create(idx) = t; } }; //! General file for reading for istream - to be subclassed for .evt format struct AsciiEventFile : public BaseEventFile { // index counts the start_event:s seen in the file, starting // at index=0 for the first event. All events ever loaded are // in the streampos map. index will always increase ifstream* f; int index; virtual string _ext() = 0; virtual bool _read( Evt& evt, istream& in ) = 0; virtual bool read_header( Head& h ) = 0; virtual bool skip_header() = 0; // keep track of recent events and the positions of all past events Cache cache; map streampos_map;; bool can_open( string filename ) { bool r = Filedesc(filename).ext == _ext() ; return r; } AsciiEventFile(): f(0) {} AsciiEventFile( string filename ) : f(0), cache(2) { open(filename ); } virtual ~AsciiEventFile() { if (f) delete f; } // open file and skip header bool open ( string filename ) { f = new ifstream( filename.c_str() ); if (!*f) { fatal("error opening", filename); return true; } index = -1; bool ok = skip_header(); return ok; } bool load_next_event() { if (!*f) return false; index++; // This is the only place where index is changed. // Make a new event in the cache and read the file into it streampos_map[index] = f->tellg(); Evt& e = cache.create( index ); e.id = index; bool ok = _read( e , *f ); if (!ok) // if reading failed, remove the event { cache.remove( index ); streampos_map.erase(index); } return ok; } bool have_event(int i) { static Evt dummy; return get_event(i, dummy); } bool get_event(int i, Evt& evt, bool verbose = true) { // do we have the event in the cache? if ( cache.have(i) ) { return cache.get(i, evt ); } // have we read the event before? if ( streampos_map.find( i ) != streampos_map.end() ) { if (f->eof() ) f -> clear(); streampos p = f->tellg(); f->seekg( streampos_map[i] ); bool ok = read( cache.create( i ), *f ); f->seekg( p ); if (!ok) return false; return cache.get(i, evt ); } // is it in the part of the file that we have not seen yet? while ( (*f) && (i > index ) ) // read next event until we get there { load_next_event(); } return cache.get(i, evt ); } bool good() { if (f) return bool(*f); else return false; } virtual string info() { return "AsciiEventFile."; } }; /*! Class to implement reading of ascii .evt files */ struct EvtEventFile : public AsciiEventFile { int run_id_from_header; bool run_id_from_header_valid; EvtEventFile() {} EvtEventFile( string filename ) : AsciiEventFile( filename ), run_id_from_header_valid(false) {} virtual string _ext() { return "evt";} virtual bool _read(Evt& evt, istream& in) { bool r = read( evt, in ); if (run_id_from_header_valid) evt.mc_run_id = run_id_from_header; return r; } virtual bool skip_header() { while (*f) { string l = nextline(*f); if ( startswith( l, "end_event:")) return true; } stringutil::print("error while trying to skip header"); return false; } virtual bool read_header( Head& h ) { streampos p = f -> tellg(); f -> seekg(0); bool ok = read(h, *f ); if ( h.have_line("start_run") ) { run_id_from_header = to ( split( h.get_line("start_run") )[0] ); run_id_from_header_valid = true; } f -> seekg( p ); return ok; } virtual string info() { return "EvtEventFile."; } virtual EvtEventFile* clone() { return new EvtEventFile(*this); } }; /*! Base class for reading ROOT files */ struct RootEventFile : public BaseEventFile { TFile* rootfile; TTree* tree; bool own_rootfile; // if we open it, we own it virtual string treename() {fatal("");} virtual string branchname() {fatal("");} virtual void setbranchaddress(TBranch* b) {fatal("");} virtual void readevt(Evt& evt) {fatal("");} virtual bool read_header(Head& h ) {fatal("");} virtual void setbranchstatus( bool read_hits = true, bool read_md_hits = true ) {} bool can_read( TTree* tree ) { return tree->GetName() == treename(); } bool can_open( TFile* f ) { if (!f) return false; TTree* T = (TTree*) f -> Get( treename().c_str() ); if ( T == 0 || T->GetEntries() == 0 ) { if (T) cout << " Warning: TTree " << treename() << " has zero entries" << endl; return false; } return true; } bool can_open( string filename ) { if (Filedesc(filename).ext != "root") return false; TFile *f = TFile::Open( filename.c_str() ); bool r = can_open(f); delete f; return r; } bool have_event(int i) { return ( i >= 0 && i < tree->GetEntries() ); } bool good() { return rootfile && rootfile->IsOpen(); } bool set_tree( TTree* tree_ ) { tree = tree_; TBranch* b = 0; for (auto& bname : split(branchname(), ",")) { b = (TBranch*) tree->GetBranch( bname.c_str() ); if (b) break; } if (!b) fatal("failed to get branch", branchname(), " out of tree", tree->GetName() ); setbranchaddress( b ); return true; } bool open(TFile* f) { own_rootfile = false; rootfile = f; tree = (TTree*) rootfile->Get( treename().c_str() ); if (!tree) fatal("failed to get tree out of ROOT file"); // handle recovered ROOT files cf https://git.km3net.de/working_groups/comp_soft/-/issues/110 if ( f->TestBit(TFile::kRecovered) ) { stringutil::print("The ROOT file was Recovered."); if ( stringutil::trim(env()["ROOT_FILE_RECOVERY_DISABLE"]) == "1" ) { print ( "ROOT_FILE_RECOVERY_DISABLE is 1 -> do not continue with recovered ROOT file." ); fatal("ROOT file not properly closed"); } } if ( tree->GetEntries() == 0 ) { stringutil::print ("warning: The tree", treename(), "was succesfully opened, but contains zero entries."); } return set_tree( tree ); } bool open(string filename ) { bool ok = open( TFile::Open( filename.c_str() ) ); own_rootfile = true; bool ok2 = 1; if (slave) ok2 = slave->open(filename); return ok && ok2; } bool get_event(int i, Evt& evt , bool verbose = true ) { int err = tree->GetEntry( i ); if ( err == -1 && verbose ) stringutil::print("RootEventFile::get_event : root io error"); if ( err < -1 && verbose ) stringutil::print("TTree::GetEntry returned ", err ); if ( err == 0 && verbose ) stringutil::print("RootEventFile::get_event : have no event", i ); readevt( evt ); return err !=0 && err !=-1; } RootEventFile(): rootfile(0), tree(0) , own_rootfile(false) {} RootEventFile(string filename): rootfile(0), tree(0) { open(filename); } ~RootEventFile() { if ( rootfile && own_rootfile ) { delete rootfile; } } virtual bool read_meta_data( vector& vmeta ) { if (!rootfile) return false; // assume all TNamed objects in the root-directory of the root file // are the meta-data auto L = rootfile->GetListOfKeys(); if (!L) return false; for ( auto p : * ( L ) ) { TKey* key = (TKey*) p; if (!key) return false; if ( string( key -> GetClassName () ) == "TNamed" ) { TObject obj; key->Read( &obj ); vmeta.push_back( &obj ); } } // and add ALL objects in the META sub-directory TDirectory* dir = rootfile->GetDirectory("META"); if (dir) { dir->ReadAll(); for( auto p : * ( dir->GetListOfKeys()) ) { TKey* key = (TKey*) p; if (!key) return false; TObject *obj = key->ReadObj(); vmeta.push_back( obj ); } } return true; } virtual string info() { return "RootEventFile."; } virtual RootEventFile* clone() { return new RootEventFile(*this); } }; #ifdef HAVEJPP #include "JReconstruction/JEvt.hh" struct JFitEventFile : public RootEventFile { JFIT::JEvt *jevt; string treename() { return JROOT::getTreeParameters().getTreeName().Data(); } string branchname() { return JROOT::getTreeParameters().getBranchName().Data();} void setbranchaddress( TBranch* b ) { b->SetAddress( &jevt);} void readevt(Evt& evt ) { read( evt, *jevt); evt.id = tree->GetReadEntry(); } bool read_header(Head& h) {return true;} // no nothing JFitEventFile(): jevt(0) {} ~JFitEventFile(); virtual string info() { return "JFitEventFile."; } }; #endif /*! call this function before reading any JDAQTimeslices. It is needed for the costum streamer */ inline void prepare_summary_frame(TFile* f) { auto i = ((TStreamerInfo*) f -> GetStreamerInfoList()->FindObject( KM3NETDAQ::JDAQSummaryslice::Class_Name() )); if (!i) return; JDAQSummaryFrame::ROOT_IO_VERSION = i->GetClassVersion(); } struct JppEventFile : public RootEventFile { KM3NETDAQ::JDAQEvent *daqevent; string treename() { return TTREE_ONLINE_EVENT; } string branchname() { return TBRANCH_ONLINE_EVENT;} void setbranchaddress( TBranch* b ) { b->SetAddress( &daqevent); } bool read_header(Head& h) { return true;} void readevt(Evt& evt ) { read( evt, *daqevent); evt.id = tree->GetReadEntry(); } JppEventFile(): daqevent(0) {} ~JppEventFile(); virtual string info() { return "JppEventFile."; } virtual JppEventFile* clone() { return new JppEventFile(*this); } }; struct JppTimesliceFile : public RootEventFile { KM3NETDAQ::JDAQTimeslice *timeslice; std::string timeslice_treename; string treename() { return timeslice_treename;} string branchname() { if ( timeslice_treename == TTREE_ONLINE_TIMESLICE ) return TBRANCH_ONLINE_TIMESLICE; if ( timeslice_treename == TTREE_ONLINE_TIMESLICEL0 ) return TBRANCH_ONLINE_TIMESLICEL0; if ( timeslice_treename == TTREE_ONLINE_TIMESLICEL1 ) return TBRANCH_ONLINE_TIMESLICEL1; if ( timeslice_treename == TTREE_ONLINE_TIMESLICEL2 ) return TBRANCH_ONLINE_TIMESLICEL2; if ( timeslice_treename == TTREE_ONLINE_TIMESLICESN ) return TBRANCH_ONLINE_TIMESLICESN; return TTREE_ONLINE_TIMESLICE; } void setbranchaddress( TBranch* b ) { b->SetAddress( ×lice);} void readevt(Evt& evt ) { read( evt, *timeslice); evt.id = tree->GetReadEntry();} bool read_header(Head& h) {return true;} // do nothing JppTimesliceFile( std::string treename = TTREE_ONLINE_TIMESLICE ) : timeslice(0) , timeslice_treename( treename ) {} ~JppTimesliceFile(); virtual string info() { return "JppTimesliceFile."; } virtual JppTimesliceFile* clone() { return new JppTimesliceFile(*this); } }; struct AaEventFile : public RootEventFile { Evt* _evt; bool own_evt; bool mc_only; string treename() { return TTREE_OFFLINE_EVENT;} string branchname() { return TBRANCH_OFFLINE_EVENT;} void setbranchaddress( TBranch* b ) { b->SetAddress( &_evt ); if (mc_only) { tree->SetBranchStatus("*", 0 ); tree->SetBranchStatus("mc_*", 1 ); tree->SetBranchStatus("w", 1 ); } } void readevt(Evt& evt ) { // only copy _evt into evt if they are not the same object if ( _evt != &evt ) evt = *_evt; } bool read_header(Head& h ) { Head* hh = (Head*) rootfile->Get("Head"); if (!hh) { hh = (Head*) rootfile->Get("Header"); } if (hh) { h = *hh; return true; } print ("failed to get header from offline eventfile"); return false; } // special case: can take pointer to Evt to use AaEventFile(Evt* e = 0, bool mc_only_mode = false) : _evt(e) , own_evt(false), mc_only(mc_only_mode) { if (_evt == 0) own_evt = true; // Ttree will make it. We must delete } ~AaEventFile() { if (own_evt && _evt ) delete _evt; } virtual string info() { return "AaEventFile."; } virtual AaEventFile* clone() { return new AaEventFile(*this); } }; #define DBG( ... ) // do #define DBG dbg to turn on /*! The Eventfile class provides a uniform interface to all types of files that aanet can read. This includes, ascii evt files, KM3NeT dataformat online and offline files. It supports stl-like iteration to loop over the events. Iteration also works in python/pyroot. A basic event loop looks as follows in python: \code{.py} import aa, ROOT f = ROOT.EventFile("inputfile.root") for evt in f : print(evt) #do things with the evt here. \endcode and like this in C++ \code{.cpp} EventFile f("inputfile.root"); for (auto& evt : f ) { print( evt ) } \endcode Multiple input files can be specified and are handled as expected. */ struct EventFile : public TObject { // settings static string stagedir; //!< directory for staging files static bool read_timeslices; //!< read timeslices in stead of (triggered) events static string timeslice_treename; //!< if timeslices are read, this is the name of the root TTree bool use_tree_index_for_mc_reading; //!< workaround for buggy files bool autosave; //!< write event to ouput file, just before loading the next bool run_by_run_mode; //!< if true, header merging is less strict for e.g. different detector tags BaseEventFile* impl; //!< pointer to underlying polymorphic implementation vector filenames; //!< list of all input files vector events_per_file; //!< how many events in each input file int file_index; //!< current index into fileanames; -1 denotes end() int index; //!< event index within currently open file int total_events; //!< total number of events in all files (if known) Evt evt; //!< The Event that gets read from file Evt* pevt; //!< pointer to evt Head current_file_header; //!< The header containing MC information of the currently open file Head header; //!< Header containing 'added' MC informaton headers in 'filenames' file (if they are compatible) vector meta_data; //!< Meta data void (*new_file_callback)() ; //!< callback for when opening file TStopwatch wallwatch, iowatch; //----------------------------------------------------------------------- static vector& _registered_filetypes() { static vector r; return r; } /*! Return true if the currently open file has event i */ bool have_index(int i) { iowatch.Start(0); bool r = impl -> have_event( i ); iowatch.Stop(); return r; } /*! Set index of event in the currently open file to i */ bool set_index(int i, bool verbose = true ) { // todo: add check global index is in range. // do not write on the first call to set_index if ( autosave && i != 0 ) _fill_output_tree(); DBG(index, i ); if ( i == -1 ) return false; if ( i == index ) return true; iowatch.Start(0); bool ok = impl -> get_event( i, evt , verbose ) ; if ( ok ) index = i; if (!ok && verbose ) { stringutil::print ("failed to get event ", i ); } //----load 2ndary event if needed if (ok && impl->slave ) { // we should use trigger_counter, but use index as fallback int idx = use_tree_index_for_mc_reading ? index : evt.trigger_counter; bool ok2 = impl->slave -> get_event( idx , evt , verbose ) ; if (!ok2 && verbose ) { stringutil::print ("attempting to load event pointed to by triggercounter"); stringutil::print ("but failed to get event ", idx ); } } iowatch.Stop(); return ok; } /*! return an index that keeps counting over all input files */ int global_index() { int r = index; for (int i =0; i< file_index; i++ ) r+= events_per_file[i]; return r; } /*! Load an event accding to its index (in the root tree) and return it */ Evt& get_event(int index) { set_index( index ); return evt; } /*! Load the event in the given file and for the given event index */ bool set_index( int file_idx , int evt_idx ) { if (file_idx < 0 ) return false; if ( file_idx != file_index ) { if ( unsigned(file_idx) > filenames.size() ) return false; string fn = filenames[ file_idx ]; cout << "new file " << fn << endl; close_impl(); open_impl( fn ); if (new_file_callback) new_file_callback(); file_index = file_idx; } return set_index( evt_idx ); } string current_filename() const { return filenames[file_index]; } /*! iterator type for stl-like looping over the file */ struct iterator { // the (pyroot) pythonization of stl-iteration has a quirk. It does // a = it; // it++; // *a; // deref the old one. // // Hence, iterators must be stricktly lazy (hold an index, allow copying, // but don't load the event until deref). EventFile* eventfilelist; int it_file_index; int it_evt_index; iterator( EventFile* efl, int file_idx, int evt_idx ) : eventfilelist(efl), it_file_index( file_idx ) , it_evt_index( evt_idx ) {} bool operator==( const iterator& a ) { return it_file_index == a.it_file_index && it_evt_index == a.it_evt_index; } bool operator!=( const iterator& a ) { return !operator==(a); } iterator& operator++() // ++it { if ( it_file_index == -1 ) return *this; it_evt_index++; if (! eventfilelist-> have_index( it_evt_index ) ) { it_evt_index = 0; it_file_index++; if (it_file_index < 0 || unsigned (it_file_index) >= eventfilelist->filenames.size() ) // no more files { it_evt_index = it_file_index = -1; // we are now == end() } } return *this; } iterator operator++(int) // it++ { iterator me = *this; operator++(); return me; } Evt& operator*() { bool ok = eventfilelist->set_index ( it_file_index, it_evt_index ); if ( !ok ) fatal("tyring to deref invalid iterator."); return eventfilelist->evt; } Evt* operator->() { return &(operator*()); } ClassDefNV(iterator, 1); }; /*! Iterator to first event */ iterator begin() { return iterator( this, 0 , 0 ) ; } /*! Iterator to past-last event */ iterator end() { return iterator( this, -1, -1 ) ; } /*! print some information on the type of eventfile being read */ string info() { string s = "Eventfile \n"; s += "impl = " + impl->info() + "\n"; s += "impl->slave = " + ( impl->slave ? impl->slave->info() : "[null]" ) + "\n"; return s; } void _init() { autosave = true; use_tree_index_for_mc_reading = false; run_by_run_mode = false; total_events = 0; pevt = &evt; index = -1; file_index = -1; impl = 0; output_file = 0; output_tree = 0; new_file_callback = nullptr; } EventFile() { _init(); } EventFile( string filename ) { _init(); open( vector {filename} ); } EventFile( vector filenamelist ) { _init(); open( filenamelist ); } /* Constructor for a custom, specific BaseEventFile */ EventFile( string filename_, BaseEventFile* ff ) { _init(); string filename = stage_file( filename_, stagedir ); // returns "" if file does not exists filename = unzip_file( filename ); filenames = {filename}; impl = ff; impl -> open( filename ); impl -> read_header( current_file_header ); file_index = 0; } EventFile( TFile* f ) { _init(); impl = create_impl( f ); impl -> open( f ); impl -> read_header( current_file_header ); header = current_file_header; file_index = 0; total_events = roottree()->GetEntries(); } BaseEventFile* create_impl( string filename_); BaseEventFile* create_impl( TFile* rfile ); /*! Open the correct type of input file */ bool open_impl( string filename_ ) { index = -1; string filename = stage_file( filename_, stagedir ); // returns "" if file does not exists filename = unzip_file( filename ); impl = create_impl( filename ); // also sets impl->slave if (!impl) return false; impl -> open( filename ); // also opens slave, if it is there // open 2ndary tree in same file. TFile* ff = rootfile(); if ( ff && impl->slave ) { ((RootEventFile*) impl->slave) -> open(ff); // --- sanity check of mc indexing ---- // In the jpp files, the trigger_index should point to the index // in the mc-tree. But sometimes this goes wrong. int old_index = index; int pe_entries = ((RootEventFile*) impl )->tree->GetEntries(); int mc_entries = ((RootEventFile*) impl->slave)->tree->GetEntries(); // get the trigger_counter from the last event set_index( pe_entries - 1 , false ); int last_trigger_counter = evt.trigger_counter; set_index( old_index ); if ( last_trigger_counter > mc_entries - 1 ) { stringutil::print ("---------------------------------------------------------------------"); stringutil::print (" ** EventFile found a problem with the file." ); stringutil::print (" ** The last event does not point to a valid entry in the mc tree ** "); stringutil::print (" ** trig.counter of last event:", last_trigger_counter, "entries in mc tree:", mc_entries); if ( pe_entries == mc_entries ) { stringutil::print (" ** But trees have same #entries -> will use index (beware!)"); use_tree_index_for_mc_reading = true; } else { stringutil::print (" -> turning off mc reading "); delete impl->slave; impl->slave = 0; } stringutil::print ("---------------------------------------------------------------------"); } // sanity check done } if (impl) impl -> read_header( current_file_header ); if (impl->slave) impl->slave-> read_header( current_file_header ); if ( rootfile() ) ( dynamic_cast< RootEventFile* > ( impl ) ) ->read_meta_data( meta_data ); iowatch.Stop(); return true; } /*! open one or more input files. In case of multiple files, the headers are 'added' if they are compatile. In case of one file header and current_file_header will be the same. */ bool open( vector input_files ) { filenames = input_files; events_per_file.resize( input_files.size() ); if (input_files.size() == 0 ) return false; // make sure we can open each of the files for (unsigned i = 0; i < input_files.size() ; i++ ) { string fn = input_files[i]; bool ok = open_impl( fn ); if (!ok) { stringutil::print ("failed to open file", fn ); return false; } string filetype = impl->info(); string slavetype = (impl->slave) ? impl->slave->info() : "none"; int entries = current_file_size(); if (entries < 0 ) total_events = -1; else { total_events += entries; events_per_file[i] = entries; } // -- header -- if (i == 0) { header = current_file_header; } else { if (header.size() > 0 ) // if it's empty, thats fine -> don't add anything { bool ok = add( header, current_file_header, run_by_run_mode ); if (!ok) { dbg ("headers are not compatible. EventFile::header will be empty."); header.clear(); } } } close_impl(); } meta_data.clear(); // start with fresh meta data // open the first file; open_impl( input_files[0] ); if (new_file_callback) new_file_callback(); file_index = 0; return true; } /*! Return the underlying ROOT TFile object (if there is one) */ TFile* rootfile() { RootEventFile* rimpl = dynamic_cast< RootEventFile* > ( impl ); if (!rimpl) return 0; // not a root-file return rimpl->rootfile; } /*! Return the current underlying ROOT Tree (if there is one) */ TTree* roottree() { RootEventFile* rimpl = dynamic_cast< RootEventFile* > ( impl ); if (!rimpl) return 0; // not a root-file return rimpl->tree; } /*! Returns the number of events in the current file. Or -1 if the number of events is unknown (for ascii file) */ int current_file_size() { TTree* t = roottree(); if (!t) return -1; // size unknown return t->GetEntries(); } /*! Returns the total number of events in all files */ int size() { return total_events; } /*! human-readible string with information */ string __str__() const { if ( filenames.size() == 0 ) { return "Eventfile with no input files"; } return "EventFile for "+ filenames[0] +" and " + str(filenames.size()-1) +"other filess"; } void print(std::ostream& out = std::cout ) const { out << __str__(); } //------------------------------------------------------------------------------- TFile* output_file; TTree* output_tree; /*! Open an output file, with an aanet Evt TTree. When you Call EventFile::write(), the current EventFile::evt will be written to the output file. When autosave = true, this write will be called automatically when iterating through the input file */ bool set_output(string output_filename, bool change_dir = false ) { stringutil::print("Warning: EventFile::set_output is depricated. use OutputEventFile instead."); TDirectory* d = gDirectory; output_file = new TFile( output_filename.c_str() , "RECREATE"); output_file->WriteObject( &header , "Head"); output_tree = new TTree( TTREE_OFFLINE_EVENT, "Evt Tree"); output_tree->Branch(TBRANCH_OFFLINE_EVENT, &evt, BASKET_SIZE_OFFLINE_EVENT ,SPLIT_LEVEL_OFFLINE_EVENT); if (!change_dir) d->cd(); // cd to orinal directory return true; } /*! print the 'meta data' to the output file */ void print_meta_data(std::ostream& out = std::cout) { for ( auto i : meta_data ) { out << i -> GetName() << " " << i->GetTitle() << std::endl; } } /*! write the 'meta data' to the output file */ bool write_meta_data() { output_file -> mkdir("META"); TDirectory* d = (TDirectory*) output_file -> Get("META"); for ( auto i : meta_data ) { d -> WriteTObject( i , i ->GetName() ); } return true; } /*! Write the current evt to the output file. If you call this, you do it most likely from the event loop, and you most likely want autosave off. */ int write(bool turn_autosave_off = true ) { if ( turn_autosave_off ) autosave = false; if (!output_tree) { stringutil::print("there is no valid output file to write to"); return 0; } return _fill_output_tree(); } int _fill_output_tree() { iowatch.Start(0); DBG(output_tree); if (!output_tree) return 0; // this is ok - we call this func just in case we have one int r = output_tree->Fill(); iowatch.Stop(); return r; } //------------------------------------------------------------------------------- //#ifdef HAVEJPP KM3NETDAQ::JDAQSummaryslice* get_rates( Det& det ) { cout << " sumslice " << evt.frame_index << endl; KM3NETDAQ::JDAQSummaryslice* r = get_summary_slice( rootfile(), evt.frame_index ); if (!r) { cout << " failed to find timeslice for frame_index " << evt.frame_index << endl; det.zero_rates(); } else { read( det, *r ); } return r; } //#endif /*! print some performance numbers */ void report() { double walltime = wallwatch.RealTime(); wallwatch.Start(0); double iotime = iowatch.RealTime(); cout << "EventFile io / wall time = " << iotime << " / " << walltime; cout << " (" << 100 * iotime / walltime << " % spent on io.)" << endl; iowatch.Start(0); } /*! return estimated numnber of seconds left for prcessing all files */ double time_left() { double f = global_index() / total_events; double walltime = wallwatch.RealTime(); wallwatch.Start(0); return f * walltime; } void close_impl() { if (impl) { if (impl->slave) delete impl->slave; impl->slave = 0; delete impl; } impl = 0; } void close(bool show_timing_info = true ) { if (output_file) { // stringutil::print ("writing output file", output_file); output_file->cd(); output_file->Write("", TObject::kOverwrite); output_file->Close(); delete output_file; output_tree = 0; output_file = 0; } close_impl(); } ~EventFile() { report(); close(); } ClassDef(EventFile, 2) }; #endif