136 std::vector<std::string> tokens = o2::RangeTokenizer::tokenize<std::string>(specifier);
139 std::pair<int, float> synconto(-1, 1);
142 std::string
name = tokens[0];
145 int collisionsasked = -1;
146 int collisionsavail = -1;
147 bool randomizeorder =
false;
148 if (tokens.size() > 2) {
149 auto mctoken = tokens[2];
150 std::regex re(
"([0-9]*):(r?)([0-9]*)$", std::regex_constants::extended);
153 if (std::regex_match(mctoken.c_str(),
m, re)) {
154 collisionsasked = std::atoi(
m[1].
str().c_str());
156 randomizeorder =
true;
158 collisionsavail = std::atoi(
m[3].
str().c_str());
160 LOG(error) <<
"Could not parse " << mctoken <<
" as MCNUMBERSTRING";
165 if (adjustEventCount) {
169 if (collisionsavail > 0) {
170 collisionsavail = std::min((
size_t)collisionsavail, (
size_t)mcreader.
getNEvents(0));
175 LOG(info) <<
"Collisions avail for " <<
name <<
" " << collisionsavail;
178 auto& interactionToken = tokens[1];
179 if (interactionToken[0] ==
'@' || interactionToken[0] ==
'r') {
182 std::regex re(
"[@r]([0-9]*):([ed])([0-9]*[.]?[0-9]?)$", std::regex_constants::extended);
185 if (std::regex_match(interactionToken.c_str(),
m, re)) {
186 auto crossindex = std::atoi(
m[1].
str().c_str());
187 auto mode =
m[2].str();
188 auto modevalue = std::atof(
m[3].
str().c_str());
190 if (crossindex > existingPatterns.size()) {
191 LOG(error) <<
"Reference to non-existent interaction spec";
194 synconto = std::pair<int, float>(crossindex, modevalue);
197 if (
mode.compare(
"e") == 0) {
200 if (
mode.compare(
"d") == 0) {
203 return InteractionSpec{
name,
rate, synconto, lockMode, interactionToken[0], collisionsasked, collisionsavail, randomizeorder};
205 LOG(error) <<
"Could not parse " << interactionToken <<
" as INTERACTIONSTRING";
208 }
catch (std::regex_error e) {
209 LOG(error) <<
"Exception during regular expression match " << e.what();
213 rate = std::atof(interactionToken.c_str());
221 bpo::options_description options(
222 "A utility to create and manipulate digitization contexts (MC collision structure within a timeframe).\n\n"
225 options.add_options()(
226 "interactions,i", bpo::value<std::vector<std::string>>(&optvalues.
interactionRates)->multitoken(),
"name,IRate|LockSpecifier")(
227 "QEDinteraction", bpo::value<std::string>(&optvalues.
qedInteraction)->default_value(
""),
"Interaction specifier for QED contribution (name,IRATE,maxeventnumber)")(
228 "outfile,o", bpo::value<std::string>(&optvalues.
outfilename)->default_value(
"collisioncontext.root"),
"Outfile of collision context")(
229 "orbits", bpo::value<int>(&optvalues.
orbits)->default_value(-1),
230 "Number of orbits to generate maximally (if given, can be used to determine the number of timeframes). "
231 "Otherwise, the context will be generated by using collision numbers from the interaction specification.")(
232 "seed", bpo::value<long>(&optvalues.
seed)->default_value(0L),
"Seed for random number generator (for time sampling etc). Default 0: Random")(
233 "show-context",
"Print generated collision context to terminal.")(
234 "bcPatternFile", bpo::value<std::string>(&optvalues.
bcpatternfile)->default_value(
""),
"Interacting BC pattern file (e.g. from CreateBCPattern.C); Use \"ccdb\" when fetching from CCDB.")(
235 "orbitsPerTF", bpo::value<int>(&optvalues.
orbitsPerTF)->default_value(256),
"Orbits per timeframes")(
236 "orbitsEarly", bpo::value<double>(&optvalues.
orbitsEarly)->default_value(0.),
"Number of orbits with extra collisions prefixed to each timeframe")(
237 "use-existing-kine",
"Read existing kinematics to adjust event counts")(
238 "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 "first-orbit", bpo::value<double>(&optvalues.
firstFractionalOrbit)->default_value(0),
"First (fractional) orbit in the run (HBFUtils.firstOrbit + BC from decimal)")(
240 "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")(
242 "configKeyValues", bpo::value<std::string>(&optvalues.
configKeyValues)->default_value(
""),
"Semicolon separated key=value strings (e.g.: 'TPC.gasDensity=1;...')")(
243 "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 "timestamp", bpo::value<long>(&optvalues.
timestamp)->default_value(-1L),
"Timestamp for CCDB queries / anchoring")(
246 "Extract individual timeframe contexts. Format required: time_frame_prefix[:comma_separated_list_of_signals_to_offset]")(
247 "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.")(
248 "nontrivial-mu-distribution", bpo::value<std::string>(&optvalues.
nontrivial_mu_distribution)->default_value(
""),
"Distribution for MU(BC)");
250 options.add_options()(
"help,h",
"Produce help message.");
252 bpo::variables_map vm;
254 bpo::store(bpo::command_line_parser(argc, argv).options(options).run(), vm);
258 if (vm.count(
"help")) {
259 std::cout << options << std::endl;
262 if (vm.count(
"show-context")) {
265 if (vm.count(
"use-existing-kine")) {
276 LOG(info) <<
"First BC " << optvalues.
firstBC;
278 }
catch (
const bpo::error& e) {
279 std::cerr << e.what() <<
"\n\n";
280 std::cerr <<
"Error parsing options; Available options:\n";
281 std::cerr << options << std::endl;
335int main(
int argc,
char* argv[])
356 LOG(error) <<
"External mode; orbits need to be given";
361 LOG(error) <<
"External mode; need to have orbitsPerTF";
366 LOG(error) <<
"External mode: This requires --extract-per-timeframe";
372 LOG(info) <<
"External mode for " << num_timeframes <<
" consecutive timeframes; starting from " << options.
tfid;
375 for (
int i = 0;
i < num_timeframes; ++
i) {
376 auto this_tf_id = options.
tfid +
i;
385 gRandom->SetSeed(options.
seed);
387 std::vector<InteractionSpec> ispecs;
394 std::vector<std::pair<o2::InteractionTimeRecord, std::vector<o2::steer::EventPart>>> collisions;
395 std::vector<o2::BunchFilling> bunchFillings;
398 bool usetimeframelength = options.
orbits > 0;
400 auto setBCFillingHelper = [&options](
auto&
sampler,
auto& bcPatternString) {
401 if (bcPatternString ==
"ccdb") {
402 LOG(info) <<
"Fetch bcPattern information from CCDB";
405 ccdb.setCaching(
false);
406 ccdb.setLocalObjectValidityChecking(
true);
409 sampler.setBunchFilling(grpLHC->getBunchFilling());
411 sampler.setBunchFilling(bcPatternString);
418 auto orbits_total = options.
orbits;
424 for (
int id = 0;
id < ispecs.size(); ++
id) {
425 auto mode = ispecs[
id].syncmode;
427 auto sampler = std::make_unique<o2::steer::InteractionSampler>();
428 std::unique_ptr<TH1F> mu_hist;
432 if (mu_distr_source.size() > 0) {
433 if (mu_distr_source.find(
"ccdb") == 0) {
435 if (!ccdb_info_wrapper.has_value()) {
436 LOG(error) <<
"Could not parse CCDB path for mu(bc) distribution";
438 auto& ccdb_info = ccdb_info_wrapper.value();
446 mu_hist.reset((TH1F*)local_hist->Clone(
"h2"));
449 mu_hist = events_per_bc->toTH1F();
451 LOG(warn) <<
"No mu(bc) distribution found on CCDB. Using uniform one";
456 auto mudistr_file = TFile::Open(mu_distr_source.c_str(),
"OPEN");
457 if (mudistr_file && !mudistr_file->IsZombie()) {
458 auto local_hist = mudistr_file->Get<TH1F>(
"hBcTVX");
459 mu_hist.reset((TH1F*)local_hist->Clone(
"h2"));
460 mudistr_file->Close();
464 LOG(info) <<
"Found an external mu distribution with mean BC value " << mu_hist->GetMean();
474 if (
const char* env = getenv(
"ALICEO2_ENFORCE_TRIVIAL_BC_SAMPLER")) {
475 std::string spec(env);
476 std::regex re(R
"((\d+):(\d+))");
478 int every_n = 1, mult = 1;
479 if (std::regex_match(spec,
match, re)) {
480 every_n = std::stoi(
match[1]);
481 mult = std::stoi(
match[2]);
483 LOG(error) <<
"ALICEO2_ENFORCE_TRIVIAL_BC_SAMPLER format invalid, expected NUMBER_1:NUMBER_2";
489 sampler->setInteractionRate(ispecs[
id].interactionRate);
496 sampler_cast->setBCIntensityScales(*mu_hist);
505 record =
sampler->generateCollisionTime();
506 }
while (options.
noEmptyTF && usetimeframelength && record.
orbit >= orbitstart + orbits_total);
509 if (usetimeframelength && record.
orbit >= orbitstart + orbits_total) {
512 std::vector<o2::steer::EventPart> parts;
513 parts.emplace_back(
id,
count);
515 std::pair<o2::InteractionTimeRecord, std::vector<o2::steer::EventPart>> insertvalue(record, parts);
516 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; });
517 collisions.insert(iter, insertvalue);
518 record =
sampler->generateCollisionTime();
520 }
while ((ispecs[
id].mcnumberasked > 0 &&
count < ispecs[
id].mcnumberasked));
524 auto random_shuffle = [](
auto first,
auto last) {
526 for (
auto i =
n - 1;
i > 0; --
i) {
531 std::vector<int> eventindices(
count);
532 std::iota(eventindices.begin(), eventindices.end(), 0);
534 if (ispecs[
id].randomizeorder) {
535 random_shuffle(eventindices.begin(), eventindices.end());
537 if (ispecs[
id].mcnumberavail > 0) {
539 for (
auto& e : eventindices) {
540 e = e % ispecs[
id].mcnumberavail;
544 for (
auto&
col : collisions) {
545 for (
auto& part :
col.second) {
546 if (part.sourceID ==
id) {
547 part.entryID = eventindices[part.entryID];
553 bunchFillings.push_back(
sampler->getBunchFilling());
558 double lastcoltime = -1.;
559 auto distanceval = ispecs[
id].synconto.second;
560 auto lockonto = ispecs[
id].synconto.first;
563 for (
int colid = 0; colid < collisions.size(); ++colid) {
564 auto&
col = collisions[colid];
565 auto coltime =
col.first.getTimeNS();
567 bool rightinteraction =
false;
570 for (
auto& eventPart :
col.second) {
571 if (eventPart.sourceID == lockonto) {
572 rightinteraction =
true;
576 if (!rightinteraction) {
593 if (ispecs[
id].syncmodeop ==
'r') {
594 LOG(
debug) <<
"Replacing/overwriting another event ";
599 auto iter = std::find_if(
col.second.begin(),
col.second.end(), [lockonto](
auto val) { return lockonto == val.sourceID; });
600 if (iter !=
col.second.end()) {
601 col.second.erase(iter);
603 LOG(error) <<
"Expected to replace another event part but did not find one for source " << lockonto <<
" and collision " << colid;
607 if (ispecs[
id].mcnumberavail >= 0) {
608 col.second.emplace_back(
id,
eventcount % ispecs[
id].mcnumberavail);
614 lastcoltime = coltime;
628 for (
auto& p : collisions) {
630 parts.push_back(p.second);
631 maxParts = std::max(p.second.size(), maxParts);
636 for (
int i = 1;
i < bunchFillings.size(); ++
i) {
637 bunchFillings[0].mergeWith(bunchFillings[
i]);
640 std::vector<std::string> prefixes;
642 float sgnIRate = -1.;
643 for (
auto& p : ispecs) {
644 prefixes.push_back(p.name);
648 if (sgnIRate < 0 && p.interactionRate > 0) {
649 LOG(
debug) <<
"Setting signal interaction rate to " << p.interactionRate <<
" Hz in the digitization context.";
650 sgnIRate = p.interactionRate;
657 LOG(info) <<
"<<------ DENSE CONTEXT ---------";
661 LOG(info) <<
"-------- DENSE CONTEXT ------->>";
664 LOG(info) <<
"Fixed " << timeframeindices.size() <<
" timeframes ";
665 for (
auto p : timeframeindices) {
666 LOG(info) << std::get<0>(p) <<
" " << std::get<1>(p) <<
" " << std::get<2>(p);
672 LOG(info) <<
"Timeframe indices after collision filter";
673 LOG(info) <<
"Fixed " << timeframeindices.size() <<
" timeframes ";
674 for (
auto p : timeframeindices) {
675 LOG(info) << std::get<0>(p) <<
" " << std::get<1>(p) <<
" " << std::get<2>(p);
679 LOG(info) <<
"<<------ FILTERED CONTEXT ---------";
683 LOG(info) <<
"-------- FILTERED CONTEXT ------->>";
685 auto numTimeFrames = timeframeindices.size();
693 LOG(info) <<
"Applying vertexing using CCDB mean vertex " << *meanv;
696 LOG(fatal) <<
"No vertex available";
704 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);
705 LOG(info) <<
"Applying vertexing using DiamondParam mean vertex " << meanv;
710 LOG(error) <<
"Unknown vertex mode ... Not generating vertices";
719 std::cout <<
"### IRATE " << qedSpec.interactionRate <<
"\n";
720 digicontext.
fillQED(qedSpec.name, qedSpec.mcnumberasked, qedSpec.interactionRate);
732 LOG(info) <<
"Extracting individual timeframe collision contexts";
735 auto check_and_extract_tokens = [](
const std::string& input, std::vector<std::string>& tokens) {
737 const std::regex
pattern(R
"(^([a-zA-Z0-9]+)(:([a-zA-Z0-9]+(,[a-zA-Z0-9]+)*))?$)");
741 if (std::regex_match(input, matches,
pattern)) {
746 tokens.push_back(matches[1].
str());
748 std::string
b = matches[2].str();
749 std::regex token_pattern(R
"([a-zA-Z0-9]+)");
750 auto tokens_begin = std::sregex_iterator(
b.begin(),
b.end(), token_pattern);
751 auto tokens_end = std::sregex_iterator();
754 for (std::sregex_iterator
i = tokens_begin;
i != tokens_end; ++
i) {
755 tokens.push_back((*i).str());
759 LOG(error) <<
"Argument for --extract-per-timeframe does not match specification";
763 std::vector<std::string> tokens;
765 auto path_prefix = tokens[0];
766 std::vector<int> sources_to_offset{};
768 LOG(info) <<
"PREFIX is " << path_prefix;
770 for (
int i = 1;
i < tokens.size(); ++
i) {
771 LOG(info) <<
"Offsetting " << tokens[
i];
772 sources_to_offset.push_back(digicontext.
findSimPrefix(tokens[
i]));
775 auto first_timeframe = options.
orbitsEarly > 0. ? 1 : 0;
777 int tf_output_counter = 1;
778 for (
int tf_id = first_timeframe; tf_id < numTimeFrames; ++tf_id) {
785 copy.fillQED(qedSpec.name, qedSpec.mcnumberasked, qedSpec.interactionRate);
788 std::stringstream
str;
789 str << path_prefix << tf_output_counter++ <<
"/collisioncontext.root";
790 copy.saveToFile(
str.str());
791 LOG(info) <<
"---- CollisionContext for timeframe " << tf_id <<
" -----";
792 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