Back to home page

EIC code displayed by LXR

 
 

    


Warning, file /EICrecon/src/services/io/podio/JEventSourcePODIO.cc was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 // Copyright 2022, David Lawrence
0002 // Subject to the terms in the LICENSE file found in the top-level directory.
0003 //
0004 //
0005 // This is a JANA event source that uses PODIO to read from a ROOT
0006 // file created using the EDM4hep Data Model.
0007 
0008 #include "JEventSourcePODIO.h"
0009 
0010 #include <JANA/JApplication.h>
0011 #include <JANA/JEvent.h>
0012 #include <JANA/JException.h>
0013 #include <JANA/JLogger.h>
0014 #include <JANA/Utils/JTypeInfo.h>
0015 #include <TFile.h>
0016 #include <TObject.h>
0017 #include <fmt/color.h>
0018 #include <fmt/core.h>
0019 #include <fmt/format.h>
0020 #include <podio/CollectionBase.h>
0021 #include <podio/Frame.h>
0022 #include <podio/podioVersion.h>
0023 #include <algorithm>
0024 #include <cstdlib>
0025 #include <exception>
0026 #include <filesystem>
0027 #include <iostream>
0028 #include <map>
0029 #include <utility>
0030 #include <vector>
0031 
0032 // These files are generated automatically by make_datamodel_glue.py
0033 #include "services/io/podio/datamodel_glue.h"
0034 #include "services/io/podio/datamodel_includes.h" // IWYU pragma: keep
0035 
0036 
0037 //------------------------------------------------------------------------------
0038 // InsertingVisitor
0039 //
0040 /// This datamodel visitor will insert a PODIO collection into a JEvent.
0041 /// This allows us to access the PODIO data through JEvent::Get and JEvent::GetCollection.
0042 /// This makes it transparent to downstream factories whether the data was loaded from file, or calculated.
0043 /// InsertingVisitor is called in GetEvent()
0044 ///
0045 /// \param event             JANA JEvent to copy the data objects into
0046 /// \param collection_name   name of the collection which will be used as the factory tag for these objects
0047 //------------------------------------------------------------------------------
0048 struct InsertingVisitor {
0049     JEvent& m_event;
0050     const std::string& m_collection_name;
0051 
0052     InsertingVisitor(JEvent& event, const std::string& collection_name) : m_event(event), m_collection_name(collection_name){};
0053 
0054     template <typename T>
0055     void operator() (const T& collection) {
0056 
0057         using ContentsT = decltype(collection[0]);
0058         m_event.InsertCollectionAlreadyInFrame<ContentsT>(&collection, m_collection_name);
0059     }
0060 };
0061 
0062 
0063 //------------------------------------------------------------------------------
0064 // Constructor
0065 //
0066 ///
0067 /// \param resource_name  Name of root file to open (n.b. file is not opened until Open() is called)
0068 /// \param app            JApplication
0069 //------------------------------------------------------------------------------
0070 JEventSourcePODIO::JEventSourcePODIO(std::string resource_name, JApplication* app) : JEventSource(resource_name, app) {
0071     SetTypeName(NAME_OF_THIS); // Provide JANA with class name
0072 
0073     // Tell JANA that we want it to call the FinishEvent() method.
0074     // EnableFinishEvent();
0075 
0076     // Allow user to specify to recycle events forever
0077     GetApplication()->SetDefaultParameter(
0078             "podio:run_forever",
0079             m_run_forever,
0080             "set to true to recycle through events continuously"
0081             );
0082 
0083     bool print_type_table = false;
0084     GetApplication()->SetDefaultParameter(
0085             "podio:print_type_table",
0086             print_type_table,
0087             "Print list of collection names and their types"
0088             );
0089 
0090     // Hopefully we won't need to reimplement background event merging. Using podio frames, it looks like we would
0091     // have to do a deep copy of all data in order to insert it into the same frame, which would probably be
0092     // quite inefficient.
0093     /*
0094     std::string background_filename;
0095     GetApplication()->SetDefaultParameter(
0096             "podio:background_filename",
0097             background_filename,
0098             "Name of file containing background events to merge in (default is not to merge any background)"
0099             );
0100 
0101     int num_background_events=1;
0102     GetApplication()->SetDefaultParameter(
0103             "podio:num_background_events",
0104             num_background_events,
0105             "Number of background events to add to every primary event."
0106     );
0107     */
0108 }
0109 
0110 //------------------------------------------------------------------------------
0111 // Destructor
0112 //------------------------------------------------------------------------------
0113 JEventSourcePODIO::~JEventSourcePODIO() {
0114     LOG << "Closing Event Source for " << GetResourceName() << LOG_END;
0115 }
0116 
0117 //------------------------------------------------------------------------------
0118 // Open
0119 //
0120 /// Open the root file and read in metadata.
0121 //------------------------------------------------------------------------------
0122 void JEventSourcePODIO::Open() {
0123 
0124     bool print_type_table = GetApplication()->GetParameterValue<bool>("podio:print_type_table");
0125     // std::string background_filename = GetApplication()->GetParameterValue<std::string>("podio:background_filename");;
0126     // int num_background_events = GetApplication()->GetParameterValue<int>("podio:num_background_events");;
0127 
0128     // Open primary events file
0129     try {
0130 
0131         // Verify file exists
0132         if( ! std::filesystem::exists(GetResourceName()) ){
0133             // Here we go against the standard practice of throwing an error and print
0134             // the message and exit immediately. This is because we want the last message
0135             // on the screen to be that the file doesn't exist.
0136             auto mess = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),"ERROR: ");
0137             mess += fmt::format(fmt::emphasis::bold, "file: {} does not exist!",  GetResourceName());
0138             std::cerr << std::endl << std::endl << mess << std::endl << std::endl;
0139             std::_Exit(EXIT_FAILURE);
0140         }
0141 
0142         m_reader.openFile( GetResourceName() );
0143 
0144         auto version = m_reader.currentFileVersion();
0145         bool version_mismatch = version.major > podio::version::build_version.major;
0146         version_mismatch |= (version.major == podio::version::build_version.major) && (version.minor>podio::version::build_version.minor);
0147         if( version_mismatch ) {
0148             std::stringstream ss;
0149             ss << "Mismatch in PODIO versions! " << version << " > " << podio::version::build_version;
0150             // FIXME: The podio ROOTReader is somehow failing to read in the correct version numbers from the file
0151 //            throw JException(ss.str());
0152         }
0153 
0154         LOG << "PODIO version: file=" << version << " (executable=" << podio::version::build_version << ")" << LOG_END;
0155 
0156         Nevents_in_file = m_reader.getEntries("events");
0157         LOG << "Opened PODIO Frame file \"" << GetResourceName() << "\" with " << Nevents_in_file << " events" << LOG_END;
0158 
0159         if( print_type_table ) PrintCollectionTypeTable();
0160 
0161     }catch (std::exception &e ){
0162         LOG_ERROR(default_cerr_logger) << e.what() << LOG_END;
0163         throw JException( fmt::format( "Problem opening file \"{}\"", GetResourceName() ) );
0164     }
0165 
0166 }
0167 
0168 //------------------------------------------------------------------------------
0169 // Close
0170 //
0171 /// Cleanly close the resource when JANA is terminated via Ctrl-C or jana:nevents
0172 ///
0173 /// \param event
0174 //------------------------------------------------------------------------------
0175 void JEventSourcePODIO::Close() {
0176     // m_reader.close();
0177     // TODO: ROOTFrameReader does not appear to have a close() method.
0178 }
0179 
0180 
0181 //------------------------------------------------------------------------------
0182 // GetEvent
0183 //
0184 /// Read next event from file and copy its objects into the given JEvent.
0185 ///
0186 /// \param event
0187 //------------------------------------------------------------------------------
0188 void JEventSourcePODIO::GetEvent(std::shared_ptr<JEvent> event) {
0189 
0190     /// Calls to GetEvent are synchronized with each other, which means they can
0191     /// read and write state on the JEventSource without causing race conditions.
0192 
0193     // Check if we have exhausted events from file
0194     if( Nevents_read >= Nevents_in_file ) {
0195         if( m_run_forever ){
0196             Nevents_read = 0;
0197         }else{
0198             // m_reader.close();
0199             // TODO:: ROOTFrameReader does not appear to have a close() method.
0200             throw RETURN_STATUS::kNO_MORE_EVENTS;
0201         }
0202     }
0203 
0204     auto frame_data = m_reader.readEntry("events", Nevents_read);
0205     auto frame = std::make_unique<podio::Frame>(std::move(frame_data));
0206 
0207     const auto& event_headers = frame->get<edm4hep::EventHeaderCollection>("EventHeader"); // TODO: What is the collection name?
0208     if (event_headers.size() != 1) {
0209         throw JException("Bad event headers: Entry %d contains %d items, but 1 expected.", Nevents_read, event_headers.size());
0210     }
0211     event->SetEventNumber(event_headers[0].getEventNumber());
0212     event->SetRunNumber(event_headers[0].getRunNumber());
0213 
0214     // Insert contents odf frame into JFactories
0215     VisitPodioCollection<InsertingVisitor> visit;
0216     for (const std::string& coll_name : frame->getAvailableCollections()) {
0217         const podio::CollectionBase* collection = frame->get(coll_name);
0218         InsertingVisitor visitor(*event, coll_name);
0219         visit(visitor, *collection);
0220     }
0221 
0222     event->Insert(frame.release()); // Transfer ownership from unique_ptr to JFactoryT<podio::Frame>
0223     Nevents_read += 1;
0224 }
0225 
0226 //------------------------------------------------------------------------------
0227 // GetDescription
0228 //------------------------------------------------------------------------------
0229 std::string JEventSourcePODIO::GetDescription() {
0230 
0231     /// GetDescription() helps JANA explain to the user what is going on
0232     return "PODIO root file (Frames, podio >= v0.16.3)";
0233 }
0234 
0235 //------------------------------------------------------------------------------
0236 // CheckOpenable
0237 //
0238 /// Return a value from 0-1 indicating probability that this source will be
0239 /// able to read this root file. Currently, it simply checks that the file
0240 /// name contains the string ".root" and if does, returns a small number (0.02).
0241 /// This will need to be made more sophisticated if the alternative root file
0242 /// formats need to be supported by other event sources.
0243 ///
0244 /// \param resource_name name of root file to evaluate.
0245 /// \return              value from 0-1 indicating confidence that this source can open the given file
0246 //------------------------------------------------------------------------------
0247 template <>
0248 double JEventSourceGeneratorT<JEventSourcePODIO>::CheckOpenable(std::string resource_name) {
0249 
0250     // PODIO Frame reader gets slightly higher precedence than PODIO Legacy reader, but only if the file
0251     // contains a 'podio_metadata' TTree. If the file doesn't exist, this will return 0. The "file not found"
0252     // error will hopefully be generated by the PODIO legacy reader instead.
0253     if (resource_name.find(".root") == std::string::npos ) return 0.0;
0254 
0255     // PODIO FrameReader segfaults on legacy input files, so we use ROOT to validate beforehand. Of course,
0256     // we can't validate if ROOT can't read the file.
0257     std::unique_ptr<TFile> file = std::make_unique<TFile>(resource_name.c_str());
0258     if (!file || file->IsZombie()) return 0.0;
0259 
0260     // We test the format the same way that PODIO's python API does. See python/podio/reading.py
0261     TObject* tree = file->Get("podio_metadata");
0262     if (tree == nullptr) return 0.0;
0263     return 0.03;
0264 }
0265 
0266 //------------------------------------------------------------------------------
0267 // PrintCollectionTypeTable
0268 //
0269 /// Print the list of collection names from the currently open file along
0270 /// with their types. This will be called automatically when the file is
0271 /// open if the PODIO:PRINT_TYPE_TABLE variable is set to a non-zero value
0272 //------------------------------------------------------------------------------
0273 void JEventSourcePODIO::PrintCollectionTypeTable(void) {
0274 
0275     // Read the zeroth entry. This assumes that m_reader has already been initialized with a valid filename
0276     auto frame_data = m_reader.readEntry("events", 0);
0277     auto frame = std::make_unique<podio::Frame>(std::move(frame_data));
0278 
0279     std::map<std::string, std::string> collectionNames;
0280     size_t max_name_len = 0;
0281     size_t max_type_len = 0;
0282 
0283     // Record all (collection name, value type name) pairs
0284     // Record the maximum length of both strings so that we can print nicely aligned columns.
0285     for (const std::string& name : frame->getAvailableCollections()) {
0286         const podio::CollectionBase* coll = frame->get(name);
0287         const auto type = coll->getTypeName();
0288         max_name_len = std::max(max_name_len, name.length());
0289         max_type_len = std::max(max_type_len, type.length());
0290         collectionNames[name] = std::string(type);
0291     }
0292 
0293     // Print table
0294     std::cout << std::endl;
0295     std::cout << "Available Collections" << std::endl;
0296     std::cout << std::endl;
0297     std::cout << "Collection Name" << std::string(max_name_len + 2 - std::string("Collection Name").length(), ' ')
0298               << "Data Type" << std::endl;
0299     std::cout << std::string(max_name_len, '-') << "  " << std::string(max_name_len, '-') << std::endl;
0300     for (auto &[name, type] : collectionNames) {
0301         std::cout << name + std::string(max_name_len + 2 - name.length(), ' ');
0302         std::cout << type << std::endl;
0303     }
0304     std::cout << std::endl;
0305 
0306 }