Skip to content

Commit 07b679a

Browse files
committed
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
1 parent 9cda446 commit 07b679a

3 files changed

Lines changed: 82 additions & 13 deletions

File tree

Steer/include/Steer/MCKinematicsReader.h

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ class MCKinematicsReader
121121
}
122122

123123
private:
124+
/// slow path of the track accessors: loads what is missing, or reports an event that is not there
125+
void ensureTracksForSourceAndEvent(int source, int event) const;
126+
[[noreturn]] static void reportMissingSource(int source, size_t available);
127+
[[noreturn]] static void reportMissingEvent(const char* what, int source, int event, size_t available);
128+
124129
void initTracksForSource(int source) const;
125130
void loadTracksForSourceAndEvent(int source, int eventID) const;
126131
void loadHeadersForSource(int source) const;
@@ -151,7 +156,9 @@ inline MCTrack const* MCKinematicsReader::getTrack(o2::MCCompLabel const& label)
151156

152157
inline MCTrack const* MCKinematicsReader::getTrack(int source, int event, int track) const
153158
{
154-
return &getTracks(source, event)[track];
159+
auto const& tracks = getTracks(source, event);
160+
// one comparison, and it covers the negative track ID of a hit whose track was not kept
161+
return static_cast<size_t>(track) < tracks.size() ? &tracks[track] : nullptr;
155162
}
156163

157164
inline MCTrack const* MCKinematicsReader::getTrack(int event, int track) const
@@ -161,13 +168,18 @@ inline MCTrack const* MCKinematicsReader::getTrack(int event, int track) const
161168

162169
inline std::vector<MCTrack> const& MCKinematicsReader::getTracks(int source, int event) const
163170
{
164-
if (mTracks[source].size() == 0) {
171+
if (static_cast<size_t>(source) >= mTracks.size()) {
172+
reportMissingSource(source, mTracks.size());
173+
}
174+
auto& perEvent = mTracks[source];
175+
if (perEvent.size() == 0) {
165176
initTracksForSource(source);
166177
}
167-
if (mTracks[source][event] == nullptr) {
168-
loadTracksForSourceAndEvent(source, event);
178+
// the event range shares the branch of the lazy load, so the fast path grows by one comparison
179+
if (static_cast<size_t>(event) >= perEvent.size() || perEvent[event] == nullptr) {
180+
ensureTracksForSourceAndEvent(source, event);
169181
}
170-
return *mTracks[source][event];
182+
return *perEvent[event];
171183
}
172184

173185
inline std::vector<MCTrack> const& MCKinematicsReader::getTracks(int event) const
@@ -177,26 +189,41 @@ inline std::vector<MCTrack> const& MCKinematicsReader::getTracks(int event) cons
177189

178190
inline o2::dataformats::MCEventHeader const& MCKinematicsReader::getMCEventHeader(int source, int event) const
179191
{
180-
if (mHeaders.at(source).size() == 0) {
192+
auto const& headers = mHeaders.at(source);
193+
if (headers.size() == 0) {
181194
loadHeadersForSource(source);
182195
}
183-
return mHeaders.at(source)[event];
196+
if (static_cast<size_t>(event) >= headers.size()) {
197+
reportMissingEvent("event headers", source, event, headers.size());
198+
}
199+
return headers[event];
184200
}
185201

186202
inline gsl::span<o2::TrackReference> MCKinematicsReader::getTrackRefs(int source, int event, int track) const
187203
{
188-
if (mIndexedTrackRefs[source].size() == 0) {
204+
if (static_cast<size_t>(source) >= mIndexedTrackRefs.size()) {
205+
return {};
206+
}
207+
auto& perEvent = mIndexedTrackRefs[source];
208+
if (perEvent.size() == 0) {
189209
loadTrackRefsForSource(source);
190210
}
191-
return mIndexedTrackRefs[source][event].getLabels(track);
211+
if (static_cast<size_t>(event) >= perEvent.size()) {
212+
return {};
213+
}
214+
return perEvent[event].getLabels(track);
192215
}
193216

194217
inline const std::vector<o2::TrackReference>& MCKinematicsReader::getTrackRefsByEvent(int source, int event) const
195218
{
196-
if (mIndexedTrackRefs[source].size() == 0) {
219+
auto const& perEvent = mIndexedTrackRefs.at(source);
220+
if (perEvent.size() == 0) {
197221
loadTrackRefsForSource(source);
198222
}
199-
return mIndexedTrackRefs[source][event].getTruthArray();
223+
if (static_cast<size_t>(event) >= perEvent.size()) {
224+
reportMissingEvent("events of track references", source, event, perEvent.size());
225+
}
226+
return perEvent[event].getTruthArray();
200227
}
201228

202229
inline gsl::span<o2::TrackReference> MCKinematicsReader::getTrackRefs(int event, int track) const

Steer/src/CollisionContextTool.cxx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "CommonDataFormat/InteractionRecord.h"
1919
#include "DataFormatsCalibration/MeanVertexObject.h"
2020
#include "SimulationDataFormat/DigitizationContext.h"
21+
#include "SimulationDataFormat/MCEventLabel.h"
2122
#include "SimConfig/InteractionDiamondParam.h"
2223
#include "DataFormatsFT0/EventsPerBc.h"
2324
#include <cmath>
@@ -215,6 +216,25 @@ InteractionSpec parseInteractionSpec(std::string const& specifier, std::vector<I
215216
}
216217
}
217218

219+
// The number of QED events which the collision context may cycle through.
220+
// The QED events are reused in a round robin over the sampled QED interactions, so the wrap has to
221+
// happen at the number of events that exist in the QED kinematics. Wrapping later hands out event
222+
// IDs which were never simulated: the hits are read modulo the file size while the MC labels keep
223+
// the unwrapped ID. See https://its.cern.ch/jira/browse/O2-7132
224+
int getQEDRoundRobinSize(InteractionSpec const& qedSpec)
225+
{
226+
if (qedSpec.mcnumberavail <= 0) {
227+
LOG(warn) << "No number of available QED events given (the MCNUMBERSTRING of --QEDinteraction); "
228+
<< "QED event IDs may name events which are not in the QED kinematics";
229+
return qedSpec.mcnumberasked;
230+
}
231+
if (qedSpec.mcnumberavail > (int)o2::MCEventLabel::MaxEventID()) {
232+
LOG(warn) << "The QED production has " << qedSpec.mcnumberavail << " events, more than the "
233+
<< o2::MCEventLabel::MaxEventID() << " an MCEventLabel can encode; QED event IDs will be truncated";
234+
}
235+
return qedSpec.mcnumberavail;
236+
}
237+
218238
bool parseOptions(int argc, char* argv[], Options& optvalues)
219239
{
220240
namespace bpo = boost::program_options;
@@ -721,7 +741,7 @@ int main(int argc, char* argv[])
721741
// TODO: use bcFilling information
722742
auto qedSpec = parseInteractionSpec(options.qedInteraction, ispecs, options.useexistingkinematics);
723743
std::cout << "### IRATE " << qedSpec.interactionRate << "\n";
724-
digicontext.fillQED(qedSpec.name, qedSpec.mcnumberasked, qedSpec.interactionRate);
744+
digicontext.fillQED(qedSpec.name, getQEDRoundRobinSize(qedSpec), qedSpec.interactionRate);
725745
}
726746

727747
if (options.printContext) {
@@ -786,7 +806,7 @@ int main(int argc, char* argv[])
786806
// This should probably be done inside the extraction itself
787807
if (digicontext.isQEDProvided()) {
788808
auto qedSpec = parseInteractionSpec(options.qedInteraction, ispecs, options.useexistingkinematics);
789-
copy.fillQED(qedSpec.name, qedSpec.mcnumberasked, qedSpec.interactionRate);
809+
copy.fillQED(qedSpec.name, getQEDRoundRobinSize(qedSpec), qedSpec.interactionRate);
790810
}
791811

792812
std::stringstream str;

Steer/src/MCKinematicsReader.cxx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,33 @@
1414
#include "SimulationDataFormat/MCEventHeader.h"
1515
#include "SimulationDataFormat/TrackReference.h"
1616
#include <TChain.h>
17+
#include <stdexcept>
18+
#include <string>
1719
#include <vector>
1820
#include <fairlogger/Logger.h>
1921

2022
using namespace o2::steer;
2123

24+
void MCKinematicsReader::reportMissingSource(int source, size_t available)
25+
{
26+
throw std::out_of_range("MCKinematicsReader: there are " + std::to_string(available) + " sources; source " +
27+
std::to_string(source) + " is not one of them");
28+
}
29+
30+
void MCKinematicsReader::reportMissingEvent(const char* what, int source, int event, size_t available)
31+
{
32+
throw std::out_of_range("MCKinematicsReader: source " + std::to_string(source) + " has " +
33+
std::to_string(available) + " " + what + "; there is no event " + std::to_string(event));
34+
}
35+
36+
void MCKinematicsReader::ensureTracksForSourceAndEvent(int source, int event) const
37+
{
38+
if (static_cast<size_t>(event) >= mTracks[source].size()) {
39+
reportMissingEvent("events", source, event, mTracks[source].size());
40+
}
41+
loadTracksForSourceAndEvent(source, event);
42+
}
43+
2244
MCKinematicsReader::~MCKinematicsReader()
2345
{
2446
for (auto chain : mInputChains) {

0 commit comments

Comments
 (0)