You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Give every timeframe its own slot in the collision context
This fixes a problem in the timeframe index structure of the collision
context and adds a unit test.
- getTimeFrameBoundaries closed only one timeframe per collision, so a
timeframe without collisions was left out of the index structure
entirely and the collisions after it were assigned to the wrong
timeframe.
- The number of extracted per-timeframe contexts was therefore the number
of non-empty timeframes, not the number of timeframes asked for, and
the last tf<N>/collisioncontext.root could be missing.
- The scan now closes every timeframe a collision skips over and pads the
result to the number of timeframes the caller asks for, so entry i always
describes orbits [start + i*orbitsPerTF, start + (i+1)*orbitsPerTF).
- applyMaxCollisionFilter keeps an empty timeframe empty when it re-indexes,
and extractSingleTimeframe returns a valid empty context for it.
- o2-steer-colcontexttool passes the number of timeframes it asked for,
reports timeframes that came out empty together with the mean number of
collisions per timeframe implied by the interaction rate, and refuses to
continue when --noEmptyTF was requested.
https://its.cern.ch/jira/browse/O2-7132
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h
+5-1Lines changed: 5 additions & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -135,7 +135,11 @@ class DigitizationContext
135
135
voidapplyMaxCollisionFilter(std::vector<std::tuple<int, int, int>>& timeframeindices, long startOrbit, long orbitsPerTF, int maxColl, double orbitsEarly = 0.);
136
136
137
137
/// get timeframe structure --> index markers where timeframe starts/ends/is_influenced_by
// nTimeframes, when positive, is the number of timeframes the caller asked for; the result is
396
+
// padded with empty timeframes (or truncated) to exactly that length.
397
+
std::vector<std::pair<int, int>> getTimeFrameBoundaries(std::vector<o2::InteractionTimeRecord> const& irecords, long startOrbit, long orbitsPerTF, long nTimeframes = -1)
393
398
{
394
399
std::vector<std::pair<int, int>> result;
395
400
401
+
auto pad_and_return = [&result, nTimeframes](int index) {
402
+
if (nTimeframes > 0) {
403
+
while ((long)result.size() < nTimeframes) {
404
+
result.emplace_back(std::pair<int, int>(index, index - 1)); // an empty timeframe
405
+
}
406
+
result.resize(nTimeframes);
407
+
}
408
+
return result;
409
+
};
410
+
396
411
// the goal is to determine timeframe boundaries inside the interaction record vectors
397
-
// determine if we can do anything
398
412
if (irecords.size() == 0) {
399
-
// nothing to do
400
-
return result;
413
+
returnpad_and_return(0);
401
414
}
402
415
403
416
if (irecords.back().orbit < startOrbit) {
404
417
LOG(error) << "start orbit larger than last collision entry";
405
-
returnresult;
418
+
returnpad_and_return((int)irecords.size());
406
419
}
407
420
408
421
// skip to the first index falling within our constrained
uint32_t firstBC = 0; // first bunch crossing (relative to firstOrbit) of the first interaction;
57
58
int orbitsPerTF = 256; // number of orbits per timeframe --> used to calculate start orbit for collisions
58
59
bool useexistingkinematics = false;
59
-
bool noEmptyTF = false; // prevent empty timeframes; the first interaction will be shifted backwards to fall within the range given by Options.orbits
60
+
bool noEmptyTF = false; // prevent empty timeframes; the first interaction will be shifted backwards to fall within the range given by Options.orbits
61
+
bool failOnEmptyTF = false; // stop rather than continue when a timeframe holds no collision
60
62
int maxCollsPerTF = -1; // the maximal number of hadronic collisions per TF (can be used to constrain number of collisions per timeframe to some maximal value)
"timeframeID", bpo::value<int>(&optvalues.tfid)->default_value(0), "Timeframe id of the first timeframe int this context. Allows to generate contexts for different start orbits")(
239
241
"first-orbit", bpo::value<double>(&optvalues.firstFractionalOrbit)->default_value(0), "First (fractional) orbit in the run (HBFUtils.firstOrbit + BC from decimal)")(
240
242
"maxCollsPerTF", bpo::value<int>(&optvalues.maxCollsPerTF)->default_value(-1), "Maximal number of MC collisions to put into one timeframe. By default no constraint.")(
241
-
"noEmptyTF", bpo::bool_switch(&optvalues.noEmptyTF), "Enforce to have at least one collision")(
243
+
"noEmptyTF", bpo::bool_switch(&optvalues.noEmptyTF), "Shift the first collision backwards so that it falls within the sampled orbit range")(
244
+
"failOnEmptyTF", bpo::bool_switch(&optvalues.failOnEmptyTF), "Stop instead of continuing when one of the timeframes asked for ends up without a collision")(
"with-vertices", bpo::value<std::string>(&optvalues.vertexModeString)->default_value("kNoVertex"), "Assign vertices to collisions. Argument is the vertex mode. Defaults to no vertexing applied")(
244
247
"timestamp", bpo::value<long>(&optvalues.timestamp)->default_value(-1L), "Timestamp for CCDB queries / anchoring")(
@@ -664,7 +667,10 @@ int main(int argc, char* argv[])
664
667
}
665
668
LOG(info) << "-------- DENSE CONTEXT ------->>";
666
669
667
-
auto timeframeindices = digicontext.calcTimeframeIndices(orbitstart, options.orbitsPerTF, options.orbitsEarly);
670
+
// the number of timeframes we were asked for; passing it makes sure that a timeframe without
671
+
// collisions keeps its own slot instead of shifting every later timeframe down by one
0 commit comments