135 std::vector<std::string> tokens = o2::RangeTokenizer::tokenize<std::string>(specifier);
138 std::pair<int, float> synconto(-1, 1);
141 std::string
name = tokens[0];
144 int collisionsasked = -1;
145 int collisionsavail = -1;
146 bool randomizeorder =
false;
147 if (tokens.size() > 2) {
148 auto mctoken = tokens[2];
149 std::regex re(
"([0-9]*):(r?)([0-9]*)$", std::regex_constants::extended);
152 if (std::regex_match(mctoken.c_str(),
m, re)) {
153 collisionsasked = std::atoi(
m[1].
str().c_str());
155 randomizeorder =
true;
157 collisionsavail = std::atoi(
m[3].
str().c_str());
159 LOG(error) <<
"Could not parse " << mctoken <<
" as MCNUMBERSTRING";
164 if (adjustEventCount) {
168 if (collisionsavail > 0) {
169 collisionsavail = std::min((
size_t)collisionsavail, (
size_t)mcreader.
getNEvents(0));
174 LOG(info) <<
"Collisions avail for " <<
name <<
" " << collisionsavail;
177 auto& interactionToken = tokens[1];
178 if (interactionToken[0] ==
'@' || interactionToken[0] ==
'r') {
181 std::regex re(
"[@r]([0-9]*):([ed])([0-9]*[.]?[0-9]?)$", std::regex_constants::extended);
184 if (std::regex_match(interactionToken.c_str(),
m, re)) {
185 auto crossindex = std::atoi(
m[1].
str().c_str());
186 auto mode =
m[2].str();
187 auto modevalue = std::atof(
m[3].
str().c_str());
189 if (crossindex > existingPatterns.size()) {
190 LOG(error) <<
"Reference to non-existent interaction spec";
193 synconto = std::pair<int, float>(crossindex, modevalue);
196 if (
mode.compare(
"e") == 0) {
199 if (
mode.compare(
"d") == 0) {
202 return InteractionSpec{
name,
rate, synconto, lockMode, interactionToken[0], collisionsasked, collisionsavail, randomizeorder};
204 LOG(error) <<
"Could not parse " << interactionToken <<
" as INTERACTIONSTRING";
207 }
catch (std::regex_error e) {
208 LOG(error) <<
"Exception during regular expression match " << e.what();
212 rate = std::atof(interactionToken.c_str());
220 bpo::options_description options(
221 "A utility to create and manipulate digitization contexts (MC collision structure within a timeframe).\n\n"
224 options.add_options()(
225 "interactions,i", bpo::value<std::vector<std::string>>(&optvalues.
interactionRates)->multitoken(),
"name,IRate|LockSpecifier")(
226 "QEDinteraction", bpo::value<std::string>(&optvalues.
qedInteraction)->default_value(
""),
"Interaction specifier for QED contribution (name,IRATE,maxeventnumber)")(
227 "outfile,o", bpo::value<std::string>(&optvalues.
outfilename)->default_value(
"collisioncontext.root"),
"Outfile of collision context")(
228 "orbits", bpo::value<int>(&optvalues.
orbits)->default_value(-1),
229 "Number of orbits to generate maximally (if given, can be used to determine the number of timeframes). "
230 "Otherwise, the context will be generated by using collision numbers from the interaction specification.")(
231 "seed", bpo::value<long>(&optvalues.
seed)->default_value(0L),
"Seed for random number generator (for time sampling etc). Default 0: Random")(
232 "show-context",
"Print generated collision context to terminal.")(
233 "bcPatternFile", bpo::value<std::string>(&optvalues.
bcpatternfile)->default_value(
""),
"Interacting BC pattern file (e.g. from CreateBCPattern.C); Use \"ccdb\" when fetching from CCDB.")(
234 "orbitsPerTF", bpo::value<int>(&optvalues.
orbitsPerTF)->default_value(256),
"Orbits per timeframes")(
235 "orbitsEarly", bpo::value<double>(&optvalues.
orbitsEarly)->default_value(0.),
"Number of orbits with extra collisions prefixed to each timeframe")(
236 "use-existing-kine",
"Read existing kinematics to adjust event counts")(
237 "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")(
238 "first-orbit", bpo::value<double>(&optvalues.
firstFractionalOrbit)->default_value(0),
"First (fractional) orbit in the run (HBFUtils.firstOrbit + BC from decimal)")(
239 "maxCollsPerTF", bpo::value<int>(&optvalues.
maxCollsPerTF)->default_value(-1),
"Maximal number of MC collisions to put into one timeframe. By default no constraint.")(
240 "noEmptyTF", bpo::bool_switch(&optvalues.
noEmptyTF),
"Enforce to have at least one collision")(
241 "configKeyValues", bpo::value<std::string>(&optvalues.
configKeyValues)->default_value(
""),
"Semicolon separated key=value strings (e.g.: 'TPC.gasDensity=1;...')")(
242 "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")(
243 "timestamp", bpo::value<long>(&optvalues.
timestamp)->default_value(-1L),
"Timestamp for CCDB queries / anchoring")(
245 "Extract individual timeframe contexts. Format required: time_frame_prefix[:comma_separated_list_of_signals_to_offset]")(
246 "import-external", bpo::value<std::string>(&optvalues.
external_path)->default_value(
""),
"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.")(
247 "nontrivial-mu-distribution", bpo::value<std::string>(&optvalues.
nontrivial_mu_distribution)->default_value(
""),
"Distribution for MU(BC)");
249 options.add_options()(
"help,h",
"Produce help message.");
251 bpo::variables_map vm;
253 bpo::store(bpo::command_line_parser(argc, argv).options(options).run(), vm);
257 if (vm.count(
"help")) {
258 std::cout << options << std::endl;
261 if (vm.count(
"show-context")) {
264 if (vm.count(
"use-existing-kine")) {
275 LOG(info) <<
"First BC " << optvalues.
firstBC;
277 }
catch (
const bpo::error& e) {
278 std::cerr << e.what() <<
"\n\n";
279 std::cerr <<
"Error parsing options; Available options:\n";
280 std::cerr << options << std::endl;
334int main(
int argc,
char* argv[])
355 LOG(error) <<
"External mode; orbits need to be given";
360 LOG(error) <<
"External mode; need to have orbitsPerTF";
365 LOG(error) <<
"External mode: This requires --extract-per-timeframe";
371 LOG(info) <<
"External mode for " << num_timeframes <<
" consecutive timeframes; starting from " << options.
tfid;
374 for (
int i = 0;
i < num_timeframes; ++
i) {
375 auto this_tf_id = options.
tfid +
i;
384 gRandom->SetSeed(options.
seed);
386 std::vector<InteractionSpec> ispecs;
393 std::vector<std::pair<o2::InteractionTimeRecord, std::vector<o2::steer::EventPart>>> collisions;
394 std::vector<o2::BunchFilling> bunchFillings;
397 bool usetimeframelength = options.
orbits > 0;
399 auto setBCFillingHelper = [&options](
auto&
sampler,
auto& bcPatternString) {
400 if (bcPatternString ==
"ccdb") {
401 LOG(info) <<
"Fetch bcPattern information from CCDB";
404 ccdb.setCaching(
false);
405 ccdb.setLocalObjectValidityChecking(
true);
408 sampler.setBunchFilling(grpLHC->getBunchFilling());
410 sampler.setBunchFilling(bcPatternString);
417 auto orbits_total = options.
orbits;
423 for (
int id = 0;
id < ispecs.size(); ++
id) {
424 auto mode = ispecs[
id].syncmode;
426 auto sampler = std::make_unique<o2::steer::InteractionSampler>();
427 TH1F* mu_hist =
nullptr;
431 if (mu_distr_source.size() > 0) {
432 if (mu_distr_source.find(
"ccdb") == 0) {
434 if (!ccdb_info_wrapper.has_value()) {
435 LOG(error) <<
"Could not parse CCDB path for mu(bc) distribution";
437 auto& ccdb_info = ccdb_info_wrapper.value();
444 mu_hist = (TH1F*)(local_hist->Clone(
"h2"));
446 LOG(warn) <<
"No mu(bc) distribution found on CCDB. Using uniform one";
451 auto mudistr_file = TFile::Open(mu_distr_source.c_str(),
"OPEN");
452 if (mudistr_file && !mudistr_file->IsZombie()) {
453 auto local_hist = mudistr_file->Get<TH1F>(
"hBcTVX");
454 mu_hist = (TH1F*)(local_hist->Clone(
"h2"));
455 mudistr_file->Close();
459 LOG(info) <<
"Found an external mu distribution with mean BC value " << mu_hist->GetMean();
469 if (
const char* env = getenv(
"ALICEO2_ENFORCE_TRIVIAL_BC_SAMPLER")) {
470 std::string spec(env);
471 std::regex re(R
"((\d+):(\d+))");
473 int every_n = 1, mult = 1;
474 if (std::regex_match(spec,
match, re)) {
475 every_n = std::stoi(
match[1]);
476 mult = std::stoi(
match[2]);
478 LOG(error) <<
"ALICEO2_ENFORCE_TRIVIAL_BC_SAMPLER format invalid, expected NUMBER_1:NUMBER_2";
484 sampler->setInteractionRate(ispecs[
id].interactionRate);
491 sampler_cast->setBCIntensityScales(*mu_hist);
499 record =
sampler->generateCollisionTime();
500 }
while (options.
noEmptyTF && usetimeframelength && record.
orbit >= orbitstart + orbits_total);
503 if (usetimeframelength && record.
orbit >= orbitstart + orbits_total) {
506 std::vector<o2::steer::EventPart> parts;
507 parts.emplace_back(
id,
count);
509 std::pair<o2::InteractionTimeRecord, std::vector<o2::steer::EventPart>> insertvalue(record, parts);
510 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; });
511 collisions.insert(iter, insertvalue);
512 record =
sampler->generateCollisionTime();
514 }
while ((ispecs[
id].mcnumberasked > 0 &&
count < ispecs[
id].mcnumberasked));
518 auto random_shuffle = [](
auto first,
auto last) {
520 for (
auto i =
n - 1;
i > 0; --
i) {
525 std::vector<int> eventindices(
count);
526 std::iota(eventindices.begin(), eventindices.end(), 0);
528 if (ispecs[
id].randomizeorder) {
529 random_shuffle(eventindices.begin(), eventindices.end());
531 if (ispecs[
id].mcnumberavail > 0) {
533 for (
auto& e : eventindices) {
534 e = e % ispecs[
id].mcnumberavail;
538 for (
auto&
col : collisions) {
539 for (
auto& part :
col.second) {
540 if (part.sourceID ==
id) {
541 part.entryID = eventindices[part.entryID];
547 bunchFillings.push_back(
sampler->getBunchFilling());
552 double lastcoltime = -1.;
553 auto distanceval = ispecs[
id].synconto.second;
554 auto lockonto = ispecs[
id].synconto.first;
557 for (
int colid = 0; colid < collisions.size(); ++colid) {
558 auto&
col = collisions[colid];
559 auto coltime =
col.first.getTimeNS();
561 bool rightinteraction =
false;
564 for (
auto& eventPart :
col.second) {
565 if (eventPart.sourceID == lockonto) {
566 rightinteraction =
true;
570 if (!rightinteraction) {
587 if (ispecs[
id].syncmodeop ==
'r') {
588 LOG(
debug) <<
"Replacing/overwriting another event ";
593 auto iter = std::find_if(
col.second.begin(),
col.second.end(), [lockonto](
auto val) { return lockonto == val.sourceID; });
594 if (iter !=
col.second.end()) {
595 col.second.erase(iter);
597 LOG(error) <<
"Expected to replace another event part but did not find one for source " << lockonto <<
" and collision " << colid;
601 if (ispecs[
id].mcnumberavail >= 0) {
602 col.second.emplace_back(
id,
eventcount % ispecs[
id].mcnumberavail);
608 lastcoltime = coltime;
622 for (
auto& p : collisions) {
624 parts.push_back(p.second);
625 maxParts = std::max(p.second.size(), maxParts);
630 for (
int i = 1;
i < bunchFillings.size(); ++
i) {
631 bunchFillings[0].mergeWith(bunchFillings[
i]);
634 std::vector<std::string> prefixes;
636 float sgnIRate = -1.;
637 for (
auto& p : ispecs) {
638 prefixes.push_back(p.name);
642 if (sgnIRate < 0 && p.interactionRate > 0) {
643 LOG(
debug) <<
"Setting signal interaction rate to " << p.interactionRate <<
" Hz in the digitization context.";
644 sgnIRate = p.interactionRate;
651 LOG(info) <<
"<<------ DENSE CONTEXT ---------";
655 LOG(info) <<
"-------- DENSE CONTEXT ------->>";
658 LOG(info) <<
"Fixed " << timeframeindices.size() <<
" timeframes ";
659 for (
auto p : timeframeindices) {
660 LOG(info) << std::get<0>(p) <<
" " << std::get<1>(p) <<
" " << std::get<2>(p);
666 LOG(info) <<
"Timeframe indices after collision filter";
667 LOG(info) <<
"Fixed " << timeframeindices.size() <<
" timeframes ";
668 for (
auto p : timeframeindices) {
669 LOG(info) << std::get<0>(p) <<
" " << std::get<1>(p) <<
" " << std::get<2>(p);
673 LOG(info) <<
"<<------ FILTERED CONTEXT ---------";
677 LOG(info) <<
"-------- FILTERED CONTEXT ------->>";
679 auto numTimeFrames = timeframeindices.size();
687 LOG(info) <<
"Applying vertexing using CCDB mean vertex " << *meanv;
690 LOG(fatal) <<
"No vertex available";
698 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);
699 LOG(info) <<
"Applying vertexing using DiamondParam mean vertex " << meanv;
704 LOG(error) <<
"Unknown vertex mode ... Not generating vertices";
713 std::cout <<
"### IRATE " << qedSpec.interactionRate <<
"\n";
714 digicontext.
fillQED(qedSpec.name, qedSpec.mcnumberasked, qedSpec.interactionRate);
726 LOG(info) <<
"Extracting individual timeframe collision contexts";
729 auto check_and_extract_tokens = [](
const std::string& input, std::vector<std::string>& tokens) {
731 const std::regex
pattern(R
"(^([a-zA-Z0-9]+)(:([a-zA-Z0-9]+(,[a-zA-Z0-9]+)*))?$)");
735 if (std::regex_match(input, matches,
pattern)) {
740 tokens.push_back(matches[1].
str());
742 std::string
b = matches[2].str();
743 std::regex token_pattern(R
"([a-zA-Z0-9]+)");
744 auto tokens_begin = std::sregex_iterator(
b.begin(),
b.end(), token_pattern);
745 auto tokens_end = std::sregex_iterator();
748 for (std::sregex_iterator
i = tokens_begin;
i != tokens_end; ++
i) {
749 tokens.push_back((*i).str());
753 LOG(error) <<
"Argument for --extract-per-timeframe does not match specification";
757 std::vector<std::string> tokens;
759 auto path_prefix = tokens[0];
760 std::vector<int> sources_to_offset{};
762 LOG(info) <<
"PREFIX is " << path_prefix;
764 for (
int i = 1;
i < tokens.size(); ++
i) {
765 LOG(info) <<
"Offsetting " << tokens[
i];
766 sources_to_offset.push_back(digicontext.
findSimPrefix(tokens[
i]));
769 auto first_timeframe = options.
orbitsEarly > 0. ? 1 : 0;
771 int tf_output_counter = 1;
772 for (
int tf_id = first_timeframe; tf_id < numTimeFrames; ++tf_id) {
779 copy.fillQED(qedSpec.name, qedSpec.mcnumberasked, qedSpec.interactionRate);
782 std::stringstream
str;
783 str << path_prefix << tf_output_counter++ <<
"/collisioncontext.root";
784 copy.saveToFile(
str.str());
785 LOG(info) <<
"---- CollisionContext for timeframe " << tf_id <<
" -----";
786 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< 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