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();
450 mu_hist.reset((TH1F*)local_hist->Clone(
"h2"));
453 mu_hist = events_per_bc->toTH1F();
455 LOG(warn) <<
"No mu(bc) distribution found on CCDB. Using uniform one";
460 auto mudistr_file = TFile::Open(mu_distr_source.c_str(),
"OPEN");
461 if (mudistr_file && !mudistr_file->IsZombie()) {
462 auto local_hist = mudistr_file->Get<TH1F>(
"hBcTVX");
463 mu_hist.reset((TH1F*)local_hist->Clone(
"h2"));
464 mudistr_file->Close();
468 LOG(info) <<
"Found an external mu distribution with mean BC value " << mu_hist->GetMean();
478 if (
const char* env = getenv(
"ALICEO2_ENFORCE_TRIVIAL_BC_SAMPLER")) {
479 std::string spec(env);
480 std::regex re(R
"((\d+):(\d+))");
482 int every_n = 1, mult = 1;
483 if (std::regex_match(spec,
match, re)) {
484 every_n = std::stoi(
match[1]);
485 mult = std::stoi(
match[2]);
487 LOG(error) <<
"ALICEO2_ENFORCE_TRIVIAL_BC_SAMPLER format invalid, expected NUMBER_1:NUMBER_2";
493 sampler->setInteractionRate(ispecs[
id].interactionRate);
500 sampler_cast->setBCIntensityScales(*mu_hist);
509 record =
sampler->generateCollisionTime();
510 }
while (options.
noEmptyTF && usetimeframelength && record.
orbit >= orbitstart + orbits_total);
513 if (usetimeframelength && record.
orbit >= orbitstart + orbits_total) {
516 std::vector<o2::steer::EventPart> parts;
517 parts.emplace_back(
id,
count);
519 std::pair<o2::InteractionTimeRecord, std::vector<o2::steer::EventPart>> insertvalue(record, parts);
520 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; });
521 collisions.insert(iter, insertvalue);
522 record =
sampler->generateCollisionTime();
524 }
while ((ispecs[
id].mcnumberasked > 0 &&
count < ispecs[
id].mcnumberasked));
528 auto random_shuffle = [](
auto first,
auto last) {
530 for (
auto i =
n - 1;
i > 0; --
i) {
535 std::vector<int> eventindices(
count);
536 std::iota(eventindices.begin(), eventindices.end(), 0);
538 if (ispecs[
id].randomizeorder) {
539 random_shuffle(eventindices.begin(), eventindices.end());
541 if (ispecs[
id].mcnumberavail > 0) {
543 for (
auto& e : eventindices) {
544 e = e % ispecs[
id].mcnumberavail;
548 for (
auto&
col : collisions) {
549 for (
auto& part :
col.second) {
550 if (part.sourceID ==
id) {
551 part.entryID = eventindices[part.entryID];
557 bunchFillings.push_back(
sampler->getBunchFilling());
562 double lastcoltime = -1.;
563 auto distanceval = ispecs[
id].synconto.second;
564 auto lockonto = ispecs[
id].synconto.first;
567 for (
int colid = 0; colid < collisions.size(); ++colid) {
568 auto&
col = collisions[colid];
569 auto coltime =
col.first.getTimeNS();
571 bool rightinteraction =
false;
574 for (
auto& eventPart :
col.second) {
575 if (eventPart.sourceID == lockonto) {
576 rightinteraction =
true;
580 if (!rightinteraction) {
597 if (ispecs[
id].syncmodeop ==
'r') {
598 LOG(
debug) <<
"Replacing/overwriting another event ";
603 auto iter = std::find_if(
col.second.begin(),
col.second.end(), [lockonto](
auto val) { return lockonto == val.sourceID; });
604 if (iter !=
col.second.end()) {
605 col.second.erase(iter);
607 LOG(error) <<
"Expected to replace another event part but did not find one for source " << lockonto <<
" and collision " << colid;
611 if (ispecs[
id].mcnumberavail >= 0) {
612 col.second.emplace_back(
id,
eventcount % ispecs[
id].mcnumberavail);
618 lastcoltime = coltime;
632 for (
auto& p : collisions) {
634 parts.push_back(p.second);
635 maxParts = std::max(p.second.size(), maxParts);
640 for (
int i = 1;
i < bunchFillings.size(); ++
i) {
641 bunchFillings[0].mergeWith(bunchFillings[
i]);
644 std::vector<std::string> prefixes;
646 float sgnIRate = -1.;
647 for (
auto& p : ispecs) {
648 prefixes.push_back(p.name);
652 if (sgnIRate < 0 && p.interactionRate > 0) {
653 LOG(
debug) <<
"Setting signal interaction rate to " << p.interactionRate <<
" Hz in the digitization context.";
654 sgnIRate = p.interactionRate;
661 LOG(info) <<
"<<------ DENSE CONTEXT ---------";
665 LOG(info) <<
"-------- DENSE CONTEXT ------->>";
668 LOG(info) <<
"Fixed " << timeframeindices.size() <<
" timeframes ";
669 for (
auto p : timeframeindices) {
670 LOG(info) << std::get<0>(p) <<
" " << std::get<1>(p) <<
" " << std::get<2>(p);
676 LOG(info) <<
"Timeframe indices after collision filter";
677 LOG(info) <<
"Fixed " << timeframeindices.size() <<
" timeframes ";
678 for (
auto p : timeframeindices) {
679 LOG(info) << std::get<0>(p) <<
" " << std::get<1>(p) <<
" " << std::get<2>(p);
683 LOG(info) <<
"<<------ FILTERED CONTEXT ---------";
687 LOG(info) <<
"-------- FILTERED CONTEXT ------->>";
689 auto numTimeFrames = timeframeindices.size();
697 LOG(info) <<
"Applying vertexing using CCDB mean vertex " << *meanv;
700 LOG(fatal) <<
"No vertex available";
708 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);
709 LOG(info) <<
"Applying vertexing using DiamondParam mean vertex " << meanv;
714 LOG(error) <<
"Unknown vertex mode ... Not generating vertices";
723 std::cout <<
"### IRATE " << qedSpec.interactionRate <<
"\n";
724 digicontext.
fillQED(qedSpec.name, qedSpec.mcnumberasked, qedSpec.interactionRate);
736 LOG(info) <<
"Extracting individual timeframe collision contexts";
739 auto check_and_extract_tokens = [](
const std::string& input, std::vector<std::string>& tokens) {
741 const std::regex
pattern(R
"(^([a-zA-Z0-9]+)(:([a-zA-Z0-9]+(,[a-zA-Z0-9]+)*))?$)");
745 if (std::regex_match(input, matches,
pattern)) {
750 tokens.push_back(matches[1].
str());
752 std::string
b = matches[2].str();
753 std::regex token_pattern(R
"([a-zA-Z0-9]+)");
754 auto tokens_begin = std::sregex_iterator(
b.begin(),
b.end(), token_pattern);
755 auto tokens_end = std::sregex_iterator();
758 for (std::sregex_iterator
i = tokens_begin;
i != tokens_end; ++
i) {
759 tokens.push_back((*i).str());
763 LOG(error) <<
"Argument for --extract-per-timeframe does not match specification";
767 std::vector<std::string> tokens;
769 auto path_prefix = tokens[0];
770 std::vector<int> sources_to_offset{};
772 LOG(info) <<
"PREFIX is " << path_prefix;
774 for (
int i = 1;
i < tokens.size(); ++
i) {
775 LOG(info) <<
"Offsetting " << tokens[
i];
776 sources_to_offset.push_back(digicontext.
findSimPrefix(tokens[
i]));
779 auto first_timeframe = options.
orbitsEarly > 0. ? 1 : 0;
781 int tf_output_counter = 1;
782 for (
int tf_id = first_timeframe; tf_id < numTimeFrames; ++tf_id) {
789 copy.fillQED(qedSpec.name, qedSpec.mcnumberasked, qedSpec.interactionRate);
792 std::stringstream
str;
793 str << path_prefix << tf_output_counter++ <<
"/collisioncontext.root";
794 copy.saveToFile(
str.str());
795 LOG(info) <<
"---- CollisionContext for timeframe " << tf_id <<
" -----";
796 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