From 07b679a56e22f8fa019c7af9865e3310f9672bf0 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Mon, 24 Aug 2026 13:30:32 +0200 Subject: [PATCH] Wrap the QED event IDs at the number of QED events available This fixes a problem in the QED round robin of the collision context and adds a guard in the MCKinematicsReader. - o2-steer-colcontexttool passed the number of MC events asked to fillQED, so the round robin never wrapped and the QED event parts named events which are not in the QED kinematics. - The number of events available is used now, with a warning when it is missing or larger than an MCEventLabel can encode. - MCKinematicsReader read past the end of its header, track and track-reference vectors for such an event ID. It throws std::out_of_range now, and the getters returning a pointer or a span report the miss instead. - The range test shares the branch of the lazy load and the message is built out of line, so the accessors keep their fast path. https://its.cern.ch/jira/browse/O2-7132 --- Steer/include/Steer/MCKinematicsReader.h | 49 ++++++++++++++++++------ Steer/src/CollisionContextTool.cxx | 24 +++++++++++- Steer/src/MCKinematicsReader.cxx | 22 +++++++++++ 3 files changed, 82 insertions(+), 13 deletions(-) diff --git a/Steer/include/Steer/MCKinematicsReader.h b/Steer/include/Steer/MCKinematicsReader.h index 6f12e9570528c..ae5ccf6615c56 100644 --- a/Steer/include/Steer/MCKinematicsReader.h +++ b/Steer/include/Steer/MCKinematicsReader.h @@ -121,6 +121,11 @@ class MCKinematicsReader } private: + /// slow path of the track accessors: loads what is missing, or reports an event that is not there + void ensureTracksForSourceAndEvent(int source, int event) const; + [[noreturn]] static void reportMissingSource(int source, size_t available); + [[noreturn]] static void reportMissingEvent(const char* what, int source, int event, size_t available); + void initTracksForSource(int source) const; void loadTracksForSourceAndEvent(int source, int eventID) const; void loadHeadersForSource(int source) const; @@ -151,7 +156,9 @@ inline MCTrack const* MCKinematicsReader::getTrack(o2::MCCompLabel const& label) inline MCTrack const* MCKinematicsReader::getTrack(int source, int event, int track) const { - return &getTracks(source, event)[track]; + auto const& tracks = getTracks(source, event); + // one comparison, and it covers the negative track ID of a hit whose track was not kept + return static_cast(track) < tracks.size() ? &tracks[track] : nullptr; } inline MCTrack const* MCKinematicsReader::getTrack(int event, int track) const @@ -161,13 +168,18 @@ inline MCTrack const* MCKinematicsReader::getTrack(int event, int track) const inline std::vector const& MCKinematicsReader::getTracks(int source, int event) const { - if (mTracks[source].size() == 0) { + if (static_cast(source) >= mTracks.size()) { + reportMissingSource(source, mTracks.size()); + } + auto& perEvent = mTracks[source]; + if (perEvent.size() == 0) { initTracksForSource(source); } - if (mTracks[source][event] == nullptr) { - loadTracksForSourceAndEvent(source, event); + // the event range shares the branch of the lazy load, so the fast path grows by one comparison + if (static_cast(event) >= perEvent.size() || perEvent[event] == nullptr) { + ensureTracksForSourceAndEvent(source, event); } - return *mTracks[source][event]; + return *perEvent[event]; } inline std::vector const& MCKinematicsReader::getTracks(int event) const @@ -177,26 +189,41 @@ inline std::vector const& MCKinematicsReader::getTracks(int event) cons inline o2::dataformats::MCEventHeader const& MCKinematicsReader::getMCEventHeader(int source, int event) const { - if (mHeaders.at(source).size() == 0) { + auto const& headers = mHeaders.at(source); + if (headers.size() == 0) { loadHeadersForSource(source); } - return mHeaders.at(source)[event]; + if (static_cast(event) >= headers.size()) { + reportMissingEvent("event headers", source, event, headers.size()); + } + return headers[event]; } inline gsl::span MCKinematicsReader::getTrackRefs(int source, int event, int track) const { - if (mIndexedTrackRefs[source].size() == 0) { + if (static_cast(source) >= mIndexedTrackRefs.size()) { + return {}; + } + auto& perEvent = mIndexedTrackRefs[source]; + if (perEvent.size() == 0) { loadTrackRefsForSource(source); } - return mIndexedTrackRefs[source][event].getLabels(track); + if (static_cast(event) >= perEvent.size()) { + return {}; + } + return perEvent[event].getLabels(track); } inline const std::vector& MCKinematicsReader::getTrackRefsByEvent(int source, int event) const { - if (mIndexedTrackRefs[source].size() == 0) { + auto const& perEvent = mIndexedTrackRefs.at(source); + if (perEvent.size() == 0) { loadTrackRefsForSource(source); } - return mIndexedTrackRefs[source][event].getTruthArray(); + if (static_cast(event) >= perEvent.size()) { + reportMissingEvent("events of track references", source, event, perEvent.size()); + } + return perEvent[event].getTruthArray(); } inline gsl::span MCKinematicsReader::getTrackRefs(int event, int track) const diff --git a/Steer/src/CollisionContextTool.cxx b/Steer/src/CollisionContextTool.cxx index f0abe6b7c5d03..41e22ab876268 100644 --- a/Steer/src/CollisionContextTool.cxx +++ b/Steer/src/CollisionContextTool.cxx @@ -18,6 +18,7 @@ #include "CommonDataFormat/InteractionRecord.h" #include "DataFormatsCalibration/MeanVertexObject.h" #include "SimulationDataFormat/DigitizationContext.h" +#include "SimulationDataFormat/MCEventLabel.h" #include "SimConfig/InteractionDiamondParam.h" #include "DataFormatsFT0/EventsPerBc.h" #include @@ -215,6 +216,25 @@ InteractionSpec parseInteractionSpec(std::string const& specifier, std::vector (int)o2::MCEventLabel::MaxEventID()) { + LOG(warn) << "The QED production has " << qedSpec.mcnumberavail << " events, more than the " + << o2::MCEventLabel::MaxEventID() << " an MCEventLabel can encode; QED event IDs will be truncated"; + } + return qedSpec.mcnumberavail; +} + bool parseOptions(int argc, char* argv[], Options& optvalues) { namespace bpo = boost::program_options; @@ -721,7 +741,7 @@ int main(int argc, char* argv[]) // TODO: use bcFilling information auto qedSpec = parseInteractionSpec(options.qedInteraction, ispecs, options.useexistingkinematics); std::cout << "### IRATE " << qedSpec.interactionRate << "\n"; - digicontext.fillQED(qedSpec.name, qedSpec.mcnumberasked, qedSpec.interactionRate); + digicontext.fillQED(qedSpec.name, getQEDRoundRobinSize(qedSpec), qedSpec.interactionRate); } if (options.printContext) { @@ -786,7 +806,7 @@ int main(int argc, char* argv[]) // This should probably be done inside the extraction itself if (digicontext.isQEDProvided()) { auto qedSpec = parseInteractionSpec(options.qedInteraction, ispecs, options.useexistingkinematics); - copy.fillQED(qedSpec.name, qedSpec.mcnumberasked, qedSpec.interactionRate); + copy.fillQED(qedSpec.name, getQEDRoundRobinSize(qedSpec), qedSpec.interactionRate); } std::stringstream str; diff --git a/Steer/src/MCKinematicsReader.cxx b/Steer/src/MCKinematicsReader.cxx index 116693f2063ee..21024dba78368 100644 --- a/Steer/src/MCKinematicsReader.cxx +++ b/Steer/src/MCKinematicsReader.cxx @@ -14,11 +14,33 @@ #include "SimulationDataFormat/MCEventHeader.h" #include "SimulationDataFormat/TrackReference.h" #include +#include +#include #include #include using namespace o2::steer; +void MCKinematicsReader::reportMissingSource(int source, size_t available) +{ + throw std::out_of_range("MCKinematicsReader: there are " + std::to_string(available) + " sources; source " + + std::to_string(source) + " is not one of them"); +} + +void MCKinematicsReader::reportMissingEvent(const char* what, int source, int event, size_t available) +{ + throw std::out_of_range("MCKinematicsReader: source " + std::to_string(source) + " has " + + std::to_string(available) + " " + what + "; there is no event " + std::to_string(event)); +} + +void MCKinematicsReader::ensureTracksForSourceAndEvent(int source, int event) const +{ + if (static_cast(event) >= mTracks[source].size()) { + reportMissingEvent("events", source, event, mTracks[source].size()); + } + loadTracksForSourceAndEvent(source, event); +} + MCKinematicsReader::~MCKinematicsReader() { for (auto chain : mInputChains) {