Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-12 08:06:28

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