108 std::vector<std::string> tokens = o2::RangeTokenizer::tokenize<std::string>(specifier);
111 std::pair<int, float> synconto(-1, 1);
114 std::string
name = tokens[0];
117 int collisionsasked = -1;
118 int collisionsavail = -1;
119 bool randomizeorder =
false;
120 if (tokens.size() > 2) {
121 auto mctoken = tokens[2];
122 std::regex re(
"([0-9]*):(r?)([0-9]*)$", std::regex_constants::extended);
125 if (std::regex_match(mctoken.c_str(),
m, re)) {
126 collisionsasked = std::atoi(
m[1].
str().c_str());
128 randomizeorder =
true;
130 collisionsavail = std::atoi(
m[3].
str().c_str());
132 LOG(error) <<
"Could not parse " << mctoken <<
" as MCNUMBERSTRING";
137 if (adjustEventCount) {
141 if (collisionsavail > 0) {
142 collisionsavail = std::min((
size_t)collisionsavail, (
size_t)mcreader.
getNEvents(0));
147 LOG(info) <<
"Collisions avail for " <<
name <<
" " << collisionsavail;
150 auto& interactionToken = tokens[1];
151 if (interactionToken[0] ==
'@' || interactionToken[0] ==
'r') {
154 std::regex re(
"[@r]([0-9]*):([ed])([0-9]*[.]?[0-9]?)$", std::regex_constants::extended);
157 if (std::regex_match(interactionToken.c_str(),
m, re)) {
158 auto crossindex = std::atoi(
m[1].
str().c_str());
159 auto mode =
m[2].str();
160 auto modevalue = std::atof(
m[3].
str().c_str());
162 if (crossindex > existingPatterns.size()) {
163 LOG(error) <<
"Reference to non-existent interaction spec";
166 synconto = std::pair<int, float>(crossindex, modevalue);
169 if (
mode.compare(
"e") == 0) {
172 if (
mode.compare(
"d") == 0) {
175 return InteractionSpec{
name,
rate, synconto, lockMode, interactionToken[0], collisionsasked, collisionsavail, randomizeorder};
177 LOG(error) <<
"Could not parse " << interactionToken <<
" as INTERACTIONSTRING";
180 }
catch (std::regex_error e) {
181 LOG(error) <<
"Exception during regular expression match " << e.what();
185 rate = std::atof(interactionToken.c_str());
193 bpo::options_description options(
194 "A utility to create and manipulate digitization contexts (MC collision structure within a timeframe).\n\n"
197 options.add_options()(
198 "interactions,i", bpo::value<std::vector<std::string>>(&optvalues.
interactionRates)->multitoken(),
"name,IRate|LockSpecifier")(
199 "QEDinteraction", bpo::value<std::string>(&optvalues.
qedInteraction)->default_value(
""),
"Interaction specifier for QED contribution (name,IRATE,maxeventnumber)")(
200 "outfile,o", bpo::value<std::string>(&optvalues.
outfilename)->default_value(
"collisioncontext.root"),
"Outfile of collision context")(
201 "orbits", bpo::value<int>(&optvalues.
orbits)->default_value(-1),
202 "Number of orbits to generate maximally (if given, can be used to determine the number of timeframes). "
203 "Otherwise, the context will be generated by using collision numbers from the interaction specification.")(
204 "seed", bpo::value<long>(&optvalues.
seed)->default_value(0L),
"Seed for random number generator (for time sampling etc). Default 0: Random")(
205 "show-context",
"Print generated collision context to terminal.")(
206 "bcPatternFile", bpo::value<std::string>(&optvalues.
bcpatternfile)->default_value(
""),
"Interacting BC pattern file (e.g. from CreateBCPattern.C); Use \"ccdb\" when fetching from CCDB.")(
207 "orbitsPerTF", bpo::value<int>(&optvalues.
orbitsPerTF)->default_value(256),
"Orbits per timeframes")(
208 "orbitsEarly", bpo::value<double>(&optvalues.
orbitsEarly)->default_value(0.),
"Number of orbits with extra collisions prefixed to each timeframe")(
209 "use-existing-kine",
"Read existing kinematics to adjust event counts")(
210 "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")(
211 "first-orbit", bpo::value<double>(&optvalues.
firstFractionalOrbit)->default_value(0),
"First (fractional) orbit in the run (HBFUtils.firstOrbit + BC from decimal)")(
212 "maxCollsPerTF", bpo::value<int>(&optvalues.
maxCollsPerTF)->default_value(-1),
"Maximal number of MC collisions to put into one timeframe. By default no constraint.")(
213 "noEmptyTF", bpo::bool_switch(&optvalues.
noEmptyTF),
"Enforce to have at least one collision")(
214 "configKeyValues", bpo::value<std::string>(&optvalues.
configKeyValues)->default_value(
""),
"Semicolon separated key=value strings (e.g.: 'TPC.gasDensity=1;...')")(
215 "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")(
216 "timestamp", bpo::value<long>(&optvalues.
timestamp)->default_value(-1L),
"Timestamp for CCDB queries / anchoring")(
218 "Extract individual timeframe contexts. Format required: time_frame_prefix[:comma_separated_list_of_signals_to_offset]")(
219 "import-external", bpo::value<std::string>(&optvalues.
external_path)->default_value(
""),
220 "Take collision contexts (per timeframe) from external files for instance for data-anchoring use-case. Needs timeframeID and number of orbits to be given as well.");
222 options.add_options()(
"help,h",
"Produce help message.");
224 bpo::variables_map vm;
226 bpo::store(bpo::command_line_parser(argc, argv).options(options).run(), vm);
230 if (vm.count(
"help")) {
231 std::cout << options << std::endl;
234 if (vm.count(
"show-context")) {
237 if (vm.count(
"use-existing-kine")) {
248 LOG(info) <<
"First BC " << optvalues.
firstBC;
250 }
catch (
const bpo::error& e) {
251 std::cerr << e.what() <<
"\n\n";
252 std::cerr <<
"Error parsing options; Available options:\n";
253 std::cerr << options << std::endl;
261 namespace fs = std::filesystem;
264 fs::path
filename = fs::path(external_path) / (
"collission_context_" +
std::to_string(this_tf_id) +
".root");
266 LOG(info) <<
"Checking existence of file: " <<
filename;
270 std::string path_prefix =
"tf";
271 std::stringstream destination_path_stream;
272 destination_path_stream << path_prefix << (target_tf_id) <<
"/collisioncontext.root";
273 fs::path destination_path = destination_path_stream.str();
276 fs::path destination_dir = destination_path.parent_path();
277 if (!fs::exists(destination_dir)) {
278 fs::create_directories(destination_dir);
279 LOG(info) <<
"Created directory: " << destination_dir;
283 fs::copy_file(
filename, destination_path, fs::copy_options::overwrite_existing);
284 LOG(info) <<
"Copied file to: " << destination_path;
287 LOG(warning) <<
"Source file does not exist: " <<
filename;
290 }
catch (
const fs::filesystem_error& e) {
291 LOG(error) <<
"Filesystem error: " << e.what();
293 }
catch (
const std::exception& e) {
294 LOG(error) <<
"Unexpected error: " << e.what();
300int main(
int argc,
char* argv[])
321 LOG(error) <<
"External mode; orbits need to be given";
326 LOG(error) <<
"External mode; need to have orbitsPerTF";
331 LOG(error) <<
"External mode: This requires --extract-per-timeframe";
337 LOG(info) <<
"External mode for " << num_timeframes <<
" consecutive timeframes; starting from " << options.
tfid;
340 for (
int i = 0;
i < num_timeframes; ++
i) {
341 auto this_tf_id = options.
tfid +
i;
350 gRandom->SetSeed(options.
seed);
352 std::vector<InteractionSpec> ispecs;
359 std::vector<std::pair<o2::InteractionTimeRecord, std::vector<o2::steer::EventPart>>> collisions;
360 std::vector<o2::BunchFilling> bunchFillings;
363 bool usetimeframelength = options.
orbits > 0;
365 auto setBCFillingHelper = [&options](
auto&
sampler,
auto& bcPatternString) {
366 if (bcPatternString ==
"ccdb") {
367 LOG(info) <<
"Fetch bcPattern information from CCDB";
370 ccdb.setCaching(
false);
371 ccdb.setLocalObjectValidityChecking(
true);
374 sampler.setBunchFilling(grpLHC->getBunchFilling());
376 sampler.setBunchFilling(bcPatternString);
383 auto orbits_total = options.
orbits;
389 for (
int id = 0;
id < ispecs.size(); ++
id) {
390 auto mode = ispecs[
id].syncmode;
392 auto sampler = std::make_unique<o2::steer::InteractionSampler>();
395 if (
const char* env = getenv(
"ALICEO2_ENFORCE_TRIVIAL_BC_SAMPLER")) {
396 std::string spec(env);
397 std::regex re(R
"((\d+):(\d+))");
399 int every_n = 1, mult = 1;
400 if (std::regex_match(spec,
match, re)) {
401 every_n = std::stoi(
match[1]);
402 mult = std::stoi(
match[2]);
404 LOG(error) <<
"ALICEO2_ENFORCE_TRIVIAL_BC_SAMPLER format invalid, expected NUMBER_1:NUMBER_2";
410 sampler->setInteractionRate(ispecs[
id].interactionRate);
419 record =
sampler->generateCollisionTime();
420 }
while (options.
noEmptyTF && usetimeframelength && record.
orbit >= orbitstart + orbits_total);
423 if (usetimeframelength && record.
orbit >= orbitstart + orbits_total) {
426 std::vector<o2::steer::EventPart> parts;
427 parts.emplace_back(
id,
count);
429 std::pair<o2::InteractionTimeRecord, std::vector<o2::steer::EventPart>> insertvalue(record, parts);
430 auto iter = std::lower_bound(collisions.begin(), collisions.end(), insertvalue, [](std::pair<
o2::InteractionTimeRecord, std::vector<o2::steer::EventPart>>
const&
a, std::pair<
o2::InteractionTimeRecord, std::vector<o2::steer::EventPart>>
const&
b) { return a.first < b.first; });
431 collisions.insert(iter, insertvalue);
432 record =
sampler->generateCollisionTime();
434 }
while ((ispecs[
id].mcnumberasked > 0 &&
count < ispecs[
id].mcnumberasked));
438 auto random_shuffle = [](
auto first,
auto last) {
440 for (
auto i =
n - 1;
i > 0; --
i) {
445 std::vector<int> eventindices(
count);
446 std::iota(eventindices.begin(), eventindices.end(), 0);
448 if (ispecs[
id].randomizeorder) {
449 random_shuffle(eventindices.begin(), eventindices.end());
451 if (ispecs[
id].mcnumberavail > 0) {
453 for (
auto& e : eventindices) {
454 e = e % ispecs[
id].mcnumberavail;
458 for (
auto&
col : collisions) {
459 for (
auto& part :
col.second) {
460 if (part.sourceID ==
id) {
461 part.entryID = eventindices[part.entryID];
467 bunchFillings.push_back(
sampler->getBunchFilling());
472 double lastcoltime = -1.;
473 auto distanceval = ispecs[
id].synconto.second;
474 auto lockonto = ispecs[
id].synconto.first;
477 for (
int colid = 0; colid < collisions.size(); ++colid) {
478 auto&
col = collisions[colid];
479 auto coltime =
col.first.getTimeNS();
481 bool rightinteraction =
false;
484 for (
auto& eventPart :
col.second) {
485 if (eventPart.sourceID == lockonto) {
486 rightinteraction =
true;
490 if (!rightinteraction) {
507 if (ispecs[
id].syncmodeop ==
'r') {
508 LOG(
debug) <<
"Replacing/overwriting another event ";
513 auto iter = std::find_if(
col.second.begin(),
col.second.end(), [lockonto](
auto val) { return lockonto == val.sourceID; });
514 if (iter !=
col.second.end()) {
515 col.second.erase(iter);
517 LOG(error) <<
"Expected to replace another event part but did not find one for source " << lockonto <<
" and collision " << colid;
521 if (ispecs[
id].mcnumberavail >= 0) {
522 col.second.emplace_back(
id,
eventcount % ispecs[
id].mcnumberavail);
528 lastcoltime = coltime;
542 for (
auto& p : collisions) {
543 records.push_back(p.first);
544 parts.push_back(p.second);
545 maxParts = std::max(p.second.size(), maxParts);
550 for (
int i = 1;
i < bunchFillings.size(); ++
i) {
551 bunchFillings[0].mergeWith(bunchFillings[
i]);
554 std::vector<std::string> prefixes;
555 for (
auto& p : ispecs) {
556 prefixes.push_back(p.name);
561 LOG(info) <<
"<<------ DENSE CONTEXT ---------";
565 LOG(info) <<
"-------- DENSE CONTEXT ------->>";
568 LOG(info) <<
"Fixed " << timeframeindices.size() <<
" timeframes ";
569 for (
auto p : timeframeindices) {
570 LOG(info) << std::get<0>(p) <<
" " << std::get<1>(p) <<
" " << std::get<2>(p);
576 LOG(info) <<
"Timeframe indices after collision filter";
577 LOG(info) <<
"Fixed " << timeframeindices.size() <<
" timeframes ";
578 for (
auto p : timeframeindices) {
579 LOG(info) << std::get<0>(p) <<
" " << std::get<1>(p) <<
" " << std::get<2>(p);
583 LOG(info) <<
"<<------ FILTERED CONTEXT ---------";
587 LOG(info) <<
"-------- FILTERED CONTEXT ------->>";
589 auto numTimeFrames = timeframeindices.size();
597 LOG(info) <<
"Applying vertexing using CCDB mean vertex " << *meanv;
600 LOG(fatal) <<
"No vertex available";
608 o2::dataformats::MeanVertexObject meanv(dparam.position[0], dparam.position[1], dparam.position[2], dparam.width[0], dparam.width[1], dparam.width[2], dparam.slopeX, dparam.slopeY);
609 LOG(info) <<
"Applying vertexing using DiamondParam mean vertex " << meanv;
614 LOG(error) <<
"Unknown vertex mode ... Not generating vertices";
623 std::cout <<
"### IRATE " << qedSpec.interactionRate <<
"\n";
624 digicontext.
fillQED(qedSpec.name, qedSpec.mcnumberasked, qedSpec.interactionRate);
636 LOG(info) <<
"Extracting individual timeframe collision contexts";
639 auto check_and_extract_tokens = [](
const std::string& input, std::vector<std::string>& tokens) {
641 const std::regex
pattern(R
"(^([a-zA-Z0-9]+)(:([a-zA-Z0-9]+(,[a-zA-Z0-9]+)*))?$)");
645 if (std::regex_match(input, matches,
pattern)) {
650 tokens.push_back(matches[1].
str());
652 std::string
b = matches[2].str();
653 std::regex token_pattern(R
"([a-zA-Z0-9]+)");
654 auto tokens_begin = std::sregex_iterator(
b.begin(),
b.end(), token_pattern);
655 auto tokens_end = std::sregex_iterator();
658 for (std::sregex_iterator
i = tokens_begin;
i != tokens_end; ++
i) {
659 tokens.push_back((*i).str());
663 LOG(error) <<
"Argument for --extract-per-timeframe does not match specification";
667 std::vector<std::string> tokens;
669 auto path_prefix = tokens[0];
670 std::vector<int> sources_to_offset{};
672 LOG(info) <<
"PREFIX is " << path_prefix;
674 for (
int i = 1;
i < tokens.size(); ++
i) {
675 LOG(info) <<
"Offsetting " << tokens[
i];
676 sources_to_offset.push_back(digicontext.
findSimPrefix(tokens[
i]));
679 auto first_timeframe = options.
orbitsEarly > 0. ? 1 : 0;
681 int tf_output_counter = 1;
682 for (
int tf_id = first_timeframe; tf_id < numTimeFrames; ++tf_id) {
689 copy.fillQED(qedSpec.name, qedSpec.mcnumberasked, qedSpec.interactionRate);
692 std::stringstream
str;
693 str << path_prefix << tf_output_counter++ <<
"/collisioncontext.root";
694 copy.saveToFile(
str.str());
695 LOG(info) <<
"---- CollisionContext for timeframe " << tf_id <<
" -----";
696 copy.printCollisionSummary();
DigitizationContext extractSingleTimeframe(int timeframeid, std::vector< std::tuple< int, int, int > > const &timeframeindices, std::vector< int > const &sources_to_offset)
void fillQED(std::string_view QEDprefix, int max_events, double qedrate)
add QED contributions to context, giving prefix; maximal event number and qed interaction rate
void applyMaxCollisionFilter(std::vector< std::tuple< int, int, int > > &timeframeindices, long startOrbit, long orbitsPerTF, int maxColl, double orbitsEarly=0.)
std::vector< o2::InteractionTimeRecord > & getEventRecords(bool withQED=false)
std::vector< std::vector< o2::steer::EventPart > > & getEventParts(bool withQED=false)
std::vector< std::tuple< int, int, int > > calcTimeframeIndices(long startOrbit, long orbitsPerTF, double orbitsEarly=0.) const
get timeframe structure --> index markers where timeframe starts/ends/is_influenced_by