From fdd28aefd331dcad393847eea53a54b7f178deea Mon Sep 17 00:00:00 2001 From: swenzel Date: Fri, 13 Jun 2025 12:21:47 +0200 Subject: [PATCH] Optional InteractionSampler sampling overwrite Allow to overwrite InteractionSampler by means of virtualizing sampling function. Used in CollisionContextTool do allow putting collisions at fixed N intervals (export mode) for the purpose of systematic studies / debugging. - To use this feature say `export ALICEO2_ENFORCE_TRIVIAL_BC_SAMPLER="2:5" to put 5 collisions into every 2nd bunch-crossing (within the bunch filling scheme) --- .../SimulationDataFormat/InteractionSampler.h | 21 ++++++++++-- .../simulation/src/InteractionSampler.cxx | 18 ++++++++++ .../simulation/src/SimulationDataLinkDef.h | 1 + Steer/src/CollisionContextTool.cxx | 33 ++++++++++++++----- 4 files changed, 63 insertions(+), 10 deletions(-) diff --git a/DataFormats/simulation/include/SimulationDataFormat/InteractionSampler.h b/DataFormats/simulation/include/SimulationDataFormat/InteractionSampler.h index 13fe099aa344a..d2ccec147cc4f 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/InteractionSampler.h +++ b/DataFormats/simulation/include/SimulationDataFormat/InteractionSampler.h @@ -67,7 +67,7 @@ class InteractionSampler void print() const; protected: - int simulateInteractingBC(); + virtual int simulateInteractingBC(); void nextCollidingBC(int n); o2::math_utils::RandomRing<10000> mBCJumpGenerator; // generator of random jumps in BC @@ -89,7 +89,7 @@ class InteractionSampler static constexpr float DefIntRate = 50e3; ///< default interaction rate - ClassDefNV(InteractionSampler, 1); + ClassDef(InteractionSampler, 1); }; //_________________________________________________ @@ -113,6 +113,23 @@ inline void InteractionSampler::nextCollidingBC(int n) mIR.bc = mInteractingBCs[mCurrBCIdx]; } +// Special case of InteractionSampler without actual sampling. +// Engineers interaction sequence by putting one in each N-th BC with multiplicity mult. +class FixedSkipBC_InteractionSampler : public InteractionSampler +{ + + public: + FixedSkipBC_InteractionSampler(int every_n, int mult) : mEveryN{every_n}, mMultiplicity{mult}, InteractionSampler() {} + + protected: + int simulateInteractingBC() override; + + private: + int mEveryN; // the skip number ---> fills every N-th BC in the bunch filling scheme + int mMultiplicity; // how many events to put if bc is filled + ClassDef(FixedSkipBC_InteractionSampler, 1); +}; + } // namespace steer } // namespace o2 diff --git a/DataFormats/simulation/src/InteractionSampler.cxx b/DataFormats/simulation/src/InteractionSampler.cxx index 1936bf7dc06a9..5e14e22e5f8db 100644 --- a/DataFormats/simulation/src/InteractionSampler.cxx +++ b/DataFormats/simulation/src/InteractionSampler.cxx @@ -130,6 +130,24 @@ int InteractionSampler::simulateInteractingBC() return ncoll; } +//_________________________________________________ +int FixedSkipBC_InteractionSampler::simulateInteractingBC() +{ + // Returns number of collisions assigned to selected BC + + nextCollidingBC(mEveryN); // we jump regular intervals + int ncoll = mMultiplicity; // well defined pileup + + // assign random time withing a bunch + for (int i = ncoll; i--;) { + mTimeInBC.push_back(mCollTimeGenerator.getNextValue()); + } + if (ncoll > 1) { // sort in DECREASING time order (we are reading vector from the end) + std::sort(mTimeInBC.begin(), mTimeInBC.end(), [](const float a, const float b) { return a > b; }); + } + return ncoll; +} + //_________________________________________________ void InteractionSampler::setBunchFilling(const std::string& bcFillingFile) { diff --git a/DataFormats/simulation/src/SimulationDataLinkDef.h b/DataFormats/simulation/src/SimulationDataLinkDef.h index 8a1e0c536c089..15abe9d50390f 100644 --- a/DataFormats/simulation/src/SimulationDataLinkDef.h +++ b/DataFormats/simulation/src/SimulationDataLinkDef.h @@ -24,6 +24,7 @@ #pragma link off all functions; #pragma link C++ class o2::steer::InteractionSampler + ; +#pragma link C++ class o2::steer::FixedSkipBC_InteractionSampler + ; #pragma link C++ class o2::sim::StackParam + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::sim::StackParam> + ; #pragma link C++ class o2::MCTrackT < double> + ; diff --git a/Steer/src/CollisionContextTool.cxx b/Steer/src/CollisionContextTool.cxx index 6dffdc921d651..e2f7c0fce3d67 100644 --- a/Steer/src/CollisionContextTool.cxx +++ b/Steer/src/CollisionContextTool.cxx @@ -302,17 +302,34 @@ int main(int argc, char* argv[]) for (int id = 0; id < ispecs.size(); ++id) { auto mode = ispecs[id].syncmode; if (mode == InteractionLockMode::NOLOCK) { - o2::steer::InteractionSampler sampler; - sampler.setInteractionRate(ispecs[id].interactionRate); + auto sampler = std::make_unique(); + + // for debug purposes: allows to instantiate trivial sampler + if (const char* env = getenv("ALICEO2_ENFORCE_TRIVIAL_BC_SAMPLER")) { + std::string spec(env); + std::regex re(R"((\d+):(\d+))"); + std::smatch match; + int every_n = 1, mult = 1; + if (std::regex_match(spec, match, re)) { + every_n = std::stoi(match[1]); + mult = std::stoi(match[2]); + } else { + LOG(error) << "ALICEO2_ENFORCE_TRIVIAL_BC_SAMPLER format invalid, expected NUMBER_1:NUMBER_2"; + exit(1); + } + sampler.reset(new o2::steer::FixedSkipBC_InteractionSampler(every_n, mult)); + } + + sampler->setInteractionRate(ispecs[id].interactionRate); if (!options.bcpatternfile.empty()) { - setBCFillingHelper(sampler, options.bcpatternfile); + setBCFillingHelper(*sampler, options.bcpatternfile); } o2::InteractionTimeRecord record; // this loop makes sure that the first collision is within the range of orbits asked (if noEmptyTF is enabled) do { - sampler.setFirstIR(o2::InteractionRecord(options.firstBC, orbitstart)); - sampler.init(); - record = sampler.generateCollisionTime(); + sampler->setFirstIR(o2::InteractionRecord(options.firstBC, orbitstart)); + sampler->init(); + record = sampler->generateCollisionTime(); } while (options.noEmptyTF && usetimeframelength && record.orbit >= orbitstart + orbits_total); int count = 0; do { @@ -325,7 +342,7 @@ int main(int argc, char* argv[]) std::pair> insertvalue(record, parts); auto iter = std::lower_bound(collisions.begin(), collisions.end(), insertvalue, [](std::pair> const& a, std::pair> const& b) { return a.first < b.first; }); collisions.insert(iter, insertvalue); - record = sampler.generateCollisionTime(); + record = sampler->generateCollisionTime(); count++; } while ((ispecs[id].mcnumberasked > 0 && count < ispecs[id].mcnumberasked)); // TODO: this loop should probably be replaced by a condition with usetimeframelength and number of orbits @@ -360,7 +377,7 @@ int main(int argc, char* argv[]) } // keep bunch filling information produced by these samplers - bunchFillings.push_back(sampler.getBunchFilling()); + bunchFillings.push_back(sampler->getBunchFilling()); } else { // we are in some lock/sync mode and modify existing collisions