Project
Loading...
Searching...
No Matches
DigitizationContext.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
16#include <TChain.h>
17#include <TFile.h>
18#include <iostream>
19#include <numeric> // for iota
20#include <MathUtils/Cartesian.h>
22#include <filesystem>
23
24using namespace o2::steer;
25
26void DigitizationContext::printCollisionSummary(bool withQED, int truncateOutputTo) const
27{
28 std::cout << "Summary of DigitizationContext --\n";
29 std::cout << "Maximal parts per collision " << mMaxPartNumber << "\n";
30 std::cout << "Collision parts taken from simulations specified by prefix:\n";
31 for (int p = 0; p < mSimPrefixes.size(); ++p) {
32 std::cout << "Part " << p << " : " << mSimPrefixes[p] << "\n";
33 }
34 std::cout << "QED information included " << isQEDProvided() << "\n";
35 if (withQED) {
36 std::cout << "Number of Collisions " << mEventRecords.size() << "\n";
37 std::cout << "Number of QED events " << mEventRecordsWithQED.size() - mEventRecords.size() << "\n";
38 // loop over combined stuff
39 for (int i = 0; i < mEventRecordsWithQED.size(); ++i) {
40 if (truncateOutputTo >= 0 && i > truncateOutputTo) {
41 std::cout << "--- Output truncated to " << truncateOutputTo << " ---\n";
42 break;
43 }
44 std::cout << "Record " << i << " TIME " << mEventRecordsWithQED[i];
45 for (auto& e : mEventPartsWithQED[i]) {
46 std::cout << " (" << e.sourceID << " , " << e.entryID << ")";
47 }
48 std::cout << "\n";
49 }
50 } else {
51 std::cout << "Number of Collisions " << mEventRecords.size() << "\n";
52 if (mEventPartsWithQED.size() > 0) {
53 auto num_qed_events = mEventPartsWithQED.size() - mEventRecords.size();
54 if (num_qed_events > 0) {
55 std::cout << "Number of QED events (but not shown) " << num_qed_events << "\n";
56 // find first and last QED collision so that we can give the range in orbits where these
57 // things are included
58 auto firstQEDcoll_iter = std::find_if(mEventPartsWithQED.begin(), mEventPartsWithQED.end(),
59 [](const std::vector<EventPart>& vec) {
60 return std::find_if(vec.begin(), vec.end(), [](EventPart const& p) { return p.sourceID == 99; }) != vec.end();
61 });
62
63 auto lastColl_iter = std::find_if(mEventPartsWithQED.rbegin(), mEventPartsWithQED.rend(),
64 [](const std::vector<EventPart>& vec) {
65 return std::find_if(vec.begin(), vec.end(), [](EventPart const& p) { return p.sourceID == 99; }) != vec.end();
66 });
67
68 auto firstindex = std::distance(mEventPartsWithQED.begin(), firstQEDcoll_iter);
69 auto lastindex = std::distance(mEventPartsWithQED.begin(), lastColl_iter.base()) - 1;
70 std::cout << "QED from: " << mEventRecordsWithQED[firstindex] << " ---> " << mEventRecordsWithQED[lastindex] << "\n";
71 }
72 }
73 for (int i = 0; i < mEventRecords.size(); ++i) {
74 if (truncateOutputTo >= 0 && i > truncateOutputTo) {
75 std::cout << "--- Output truncated to " << truncateOutputTo << " ---\n";
76 break;
77 }
78 std::cout << "Collision " << i << " TIME " << mEventRecords[i];
79 for (auto& e : mEventParts[i]) {
80 std::cout << " (" << e.sourceID << " , " << e.entryID << ")";
81 }
82 if (i < mInteractionVertices.size()) {
83 std::cout << " sampled vertex : " << mInteractionVertices[i];
84 }
85 std::cout << "\n";
86 }
87 }
88}
89
90void DigitizationContext::setSimPrefixes(std::vector<std::string> const& prefixes)
91{
92 mSimPrefixes = prefixes;
93}
94
95bool DigitizationContext::initSimChains(o2::detectors::DetID detid, std::vector<TChain*>& simchains) const
96{
97 if (!(simchains.size() == 0)) {
98 // nothing to do ... already setup
99 return false;
100 }
101
102 // check that all files are present, otherwise quit
103 for (int source = 0; source < mSimPrefixes.size(); ++source) {
104 if (!std::filesystem::exists(o2::base::DetectorNameConf::getHitsFileName(detid, mSimPrefixes[source].data()))) {
105 LOG(info) << "Not hit file present for " << detid.getName() << " (exiting SimChain setup)";
106 return false;
107 }
108 }
109
110 simchains.emplace_back(new TChain("o2sim"));
111 // add the main (background) file
112 simchains.back()->AddFile(o2::base::DetectorNameConf::getHitsFileName(detid, mSimPrefixes[0].data()).c_str());
113
114 for (int source = 1; source < mSimPrefixes.size(); ++source) {
115 simchains.emplace_back(new TChain("o2sim"));
116 // add signal files
117 simchains.back()->AddFile(o2::base::DetectorNameConf::getHitsFileName(detid, mSimPrefixes[source].data()).c_str());
118 }
119
120 // QED part
121 if (mEventRecordsWithQED.size() > 0) {
122 if (mSimPrefixes.size() >= QEDSOURCEID) {
123 LOG(fatal) << "Too many signal chains; crashes with QED source ID";
124 }
125
126 // it might be better to use an unordered_map for the simchains but this requires interface changes
127 simchains.resize(QEDSOURCEID + 1, nullptr);
128 simchains[QEDSOURCEID] = new TChain("o2sim");
129 simchains[QEDSOURCEID]->AddFile(o2::base::DetectorNameConf::getHitsFileName(detid, mQEDSimPrefix).c_str());
130 }
131
132 return true;
133}
134
138bool DigitizationContext::initSimKinematicsChains(std::vector<TChain*>& simkinematicschains) const
139{
140 if (!(simkinematicschains.size() == 0)) {
141 // nothing to do ... already setup
142 return false;
143 }
144
145 simkinematicschains.emplace_back(new TChain("o2sim"));
146 // add the main (background) file
147 simkinematicschains.back()->AddFile(o2::base::NameConf::getMCKinematicsFileName(mSimPrefixes[0].data()).c_str());
148
149 for (int source = 1; source < mSimPrefixes.size(); ++source) {
150 simkinematicschains.emplace_back(new TChain("o2sim"));
151 // add signal files
152 simkinematicschains.back()->AddFile(o2::base::NameConf::getMCKinematicsFileName(mSimPrefixes[source].data()).c_str());
153 }
154 return true;
155}
156
158{
159 if (mMaxPartNumber == 1) {
160 return true;
161 }
162
163 auto checkVertexPair = [](math_utils::Point3D<double> const& p1, math_utils::Point3D<double> const& p2) -> bool {
164 return (p2 - p1).Mag2() < 1E-6;
165 };
166
167 std::vector<TChain*> kinematicschain;
168 std::vector<TBranch*> headerbranches;
169 std::vector<o2::dataformats::MCEventHeader*> headers;
170 std::vector<math_utils::Point3D<double>> vertices;
171 initSimKinematicsChains(kinematicschain);
172 bool consistent = true;
173 if (kinematicschain.size() > 0) {
174 headerbranches.resize(kinematicschain.size(), nullptr);
175 headers.resize(kinematicschain.size(), nullptr);
176 // loop over all collisions in this context
177 int collisionID = 0;
178 for (auto& collision : getEventParts()) {
179 collisionID++;
180 vertices.clear();
181 for (auto& part : collision) {
182 const auto source = part.sourceID;
183 const auto entry = part.entryID;
184 auto chain = kinematicschain[source];
185 if (!headerbranches[source]) {
186 headerbranches[source] = chain->GetBranch("MCEventHeader.");
187 headerbranches[source]->SetAddress(&headers[source]);
188 }
189 // get the MCEventHeader to read out the vertex
190 headerbranches[source]->GetEntry(entry);
191 auto header = headers[source];
192 vertices.emplace_back(header->GetX(), header->GetY(), header->GetZ());
193 }
194 // analyse vertex matching
195 bool thiscollision = true;
196 const auto& p1 = vertices[0];
197 for (int j = 1; j < vertices.size(); ++j) {
198 const auto& p2 = vertices[j];
199 bool thischeck = checkVertexPair(p1, p2);
200 thiscollision &= thischeck;
201 }
202 if (verbose && !thiscollision) {
203 std::stringstream text;
204 text << "Found inconsistent vertices for digit collision ";
205 text << collisionID << " : ";
206 for (auto& p : vertices) {
207 text << p << " ";
208 }
209 LOG(error) << text.str();
210 }
211 consistent &= thiscollision;
212 }
213 }
214 return consistent;
215}
216
218{
219 if (!mGRP) {
220 // we take the GRP from the background file
221 // maybe we should add a check that all GRPs are consistent ..
222 mGRP = o2::parameters::GRPObject::loadFrom(mSimPrefixes[0]);
223 }
224 return *mGRP;
225}
226
227void DigitizationContext::saveToFile(std::string_view filename) const
228{
229 // checks if the path content of filename exists ... otherwise it is created before creating the ROOT file
230 auto ensure_path_exists = [](std::string_view filename) {
231 try {
232 // Extract the directory path from the filename
233 std::filesystem::path file_path(filename);
234 std::filesystem::path dir_path = file_path.parent_path();
235
236 // Check if the directory path is empty (which means filename was just a name without path)
237 if (dir_path.empty()) {
238 // nothing to do
239 return true;
240 }
241
242 // Create directories if they do not exist
243 if (!std::filesystem::exists(dir_path)) {
244 if (std::filesystem::create_directories(dir_path)) {
245 // std::cout << "Directories created successfully: " << dir_path.string() << std::endl;
246 return true;
247 } else {
248 std::cerr << "Failed to create directories: " << dir_path.string() << std::endl;
249 return false;
250 }
251 }
252 return true;
253 } catch (const std::filesystem::filesystem_error& ex) {
254 std::cerr << "Filesystem error: " << ex.what() << std::endl;
255 return false;
256 } catch (const std::exception& ex) {
257 std::cerr << "General error: " << ex.what() << std::endl;
258 return false;
259 }
260 };
261
262 if (!ensure_path_exists(filename)) {
263 LOG(error) << "Filename contains path component which could not be created";
264 return;
265 }
266
267 TFile file(filename.data(), "RECREATE");
268 if (file.IsOpen()) {
269 auto cl = TClass::GetClass(typeid(*this));
270 file.WriteObjectAny(this, cl, "DigitizationContext");
271 file.Close();
272 } else {
273 LOG(error) << "Could not write to file " << filename.data();
274 }
275}
276
278{
279 std::string tmpFile;
280 if (filename == "") {
282 filename = tmpFile;
283 }
284 DigitizationContext* incontext = nullptr;
285 TFile file(filename.data(), "OPEN");
286 file.GetObject("DigitizationContext", incontext);
287 return incontext;
288}
289
290void DigitizationContext::fillQED(std::string_view QEDprefix, int max_events, double qedrate)
291{
292 if (mEventRecords.size() <= 1) {
293 // nothing to do
294 return;
295 }
296
297 o2::steer::InteractionSampler qedInteractionSampler;
298 qedInteractionSampler.setBunchFilling(mBCFilling);
299
300 // get first and last "hadronic" interaction records and let
301 // QED events range from the first bunch crossing to the last bunch crossing
302 // in this range
303 auto first = mEventRecords.front();
304 auto last = mEventRecords.back();
305 first.bc = 0;
307 LOG(info) << "QED RATE " << qedrate;
308 qedInteractionSampler.setInteractionRate(qedrate);
309 qedInteractionSampler.setFirstIR(first);
310 qedInteractionSampler.init();
311 qedInteractionSampler.print();
312 std::vector<o2::InteractionTimeRecord> qedinteractionrecords;
314 LOG(info) << "GENERATING COL TIMES";
315 t = qedInteractionSampler.generateCollisionTime();
316 while ((t = qedInteractionSampler.generateCollisionTime()) < last) {
317 qedinteractionrecords.push_back(t);
318 }
319 LOG(info) << "DONE GENERATING COL TIMES";
320 fillQED(QEDprefix, qedinteractionrecords, max_events, false);
321}
322
323void DigitizationContext::fillQED(std::string_view QEDprefix, std::vector<o2::InteractionTimeRecord> const& irecord, int max_events, bool fromKinematics)
324{
325 mQEDSimPrefix = QEDprefix;
326
327 std::vector<std::vector<o2::steer::EventPart>> qedEventParts;
328
329 int numberQEDevents = max_events; // if this is -1 there will be no limitation
330 if (fromKinematics) {
331 // we need to fill the QED parts (using a simple round robin scheme)
332 auto qedKinematicsName = o2::base::NameConf::getMCKinematicsFileName(mQEDSimPrefix);
333 // find out how many events are stored
334 TFile f(qedKinematicsName.c_str(), "OPEN");
335 auto t = (TTree*)f.Get("o2sim");
336 if (!t) {
337 LOG(error) << "No QED kinematics found";
338 throw std::runtime_error("No QED kinematics found");
339 }
340 numberQEDevents = t->GetEntries();
341 }
342
343 int eventID = 0;
344 for (auto& tmp : irecord) {
345 std::vector<EventPart> qedpart;
346 qedpart.emplace_back(QEDSOURCEID, eventID++);
347 qedEventParts.push_back(qedpart);
348 if (eventID == numberQEDevents) {
349 eventID = 0;
350 }
351 }
352
353 // we need to do the interleaved event records for detectors consuming both
354 // normal and QED events
355 // --> merge both; sort first according to times and sort second one according to same order
356 auto combinedrecords = mEventRecords;
357 combinedrecords.insert(combinedrecords.end(), irecord.begin(), irecord.end());
358 auto combinedparts = mEventParts;
359 combinedparts.insert(combinedparts.end(), qedEventParts.begin(), qedEventParts.end());
360
361 // get sorted index vector based on event records
362 std::vector<size_t> idx(combinedrecords.size());
363 std::iota(idx.begin(), idx.end(), 0);
364
365 std::stable_sort(idx.begin(), idx.end(),
366 [&combinedrecords](size_t i1, size_t i2) { return combinedrecords[i1] < combinedrecords[i2]; });
367
368 mEventRecordsWithQED.clear();
369 mEventPartsWithQED.clear();
370 for (int i = 0; i < idx.size(); ++i) {
371 mEventRecordsWithQED.push_back(combinedrecords[idx[i]]);
372 mEventPartsWithQED.push_back(combinedparts[idx[i]]);
373 }
374}
375
376namespace
377{
378// a common helper for timeframe structure
379std::vector<std::pair<int, int>> getTimeFrameBoundaries(std::vector<o2::InteractionTimeRecord> const& irecords, long startOrbit, long orbitsPerTF)
380{
381 std::vector<std::pair<int, int>> result;
382
383 // the goal is to determine timeframe boundaries inside the interaction record vectors
384 // determine if we can do anything
385 if (irecords.size() == 0) {
386 // nothing to do
387 return result;
388 }
389
390 if (irecords.back().orbit < startOrbit) {
391 LOG(error) << "start orbit larger than last collision entry";
392 return result;
393 }
394
395 // skip to the first index falling within our constrained
396 int left = 0;
397 while (left < irecords.size() && irecords[left].orbit < startOrbit) {
398 left++;
399 }
400
401 // now we can start (2 pointer approach)
402 auto right = left;
403 int timeframe_count = 1;
404 while (right < irecords.size()) {
405 if (irecords[right].orbit >= startOrbit + timeframe_count * orbitsPerTF) {
406 // we finished one timeframe
407 result.emplace_back(std::pair<int, int>(left, right - 1));
408 timeframe_count++;
409 left = right;
410 }
411 right++;
412 }
413 // finished last timeframe
414 result.emplace_back(std::pair<int, int>(left, right - 1));
415 return result;
416}
417
418// a common helper for timeframe structure - includes indices for orbits-early (orbits from last timeframe still affecting current one)
419std::vector<std::tuple<int, int, int>> getTimeFrameBoundaries(std::vector<o2::InteractionTimeRecord> const& irecords,
420 long startOrbit,
421 long orbitsPerTF,
422 float orbitsEarly)
423{
424 // we could actually use the other method first ... then do another pass to fix the early-index ... or impact index
425 auto true_indices = getTimeFrameBoundaries(irecords, startOrbit, orbitsPerTF);
426
427 std::vector<std::tuple<int, int, int>> indices_with_early{};
428 for (int ti = 0; ti < true_indices.size(); ++ti) {
429 // for each timeframe we copy the true indices
430 auto& tf_range = true_indices[ti];
431
432 // init new index without fixing the early index yet
433 indices_with_early.push_back(std::make_tuple(tf_range.first, tf_range.second, -1));
434
435 // from the second timeframe on we can determine the index in the previous timeframe
436 // which matches our criterion
437 if (orbitsEarly > 0. && ti > 0) {
438 auto& prev_tf_range = true_indices[ti - 1];
439 // in this range search the smallest index which precedes
440 // timeframe ti by not more than "orbitsEarly" orbits
441 // (could probably use binary search, in case optimization becomes necessary)
442 int earlyOrbitIndex = prev_tf_range.second;
443
444 // this is the orbit of the ti-th timeframe start
445 auto orbit_timeframe_start = startOrbit + ti * orbitsPerTF;
446
447 auto orbit_timeframe_early_fractional = orbit_timeframe_start - orbitsEarly;
448 auto orbit_timeframe_early_integral = (uint32_t)(orbit_timeframe_early_fractional);
449
450 auto bc_early = (uint32_t)((orbit_timeframe_early_fractional - orbit_timeframe_early_integral) * o2::constants::lhc::LHCMaxBunches);
451
452 // this is the interaction record of the ti-th timeframe start
453 o2::InteractionRecord timeframe_start_record(0, orbit_timeframe_early_integral);
454 // this is the interaction record in some previous timeframe after which interactions could still
455 // influence the ti-th timeframe according to orbitsEarly
456 o2::InteractionRecord timeframe_early_record(bc_early, orbit_timeframe_early_integral);
457
458 auto differenceInBCNS_max = timeframe_start_record.differenceInBCNS(timeframe_early_record);
459
460 for (int j = prev_tf_range.second; j >= prev_tf_range.first; --j) {
461 // determine difference in timing in NS; compare that with the limit given by orbitsEarly
462 auto timediff_NS = timeframe_start_record.differenceInBCNS(irecords[j]);
463 if (timediff_NS < differenceInBCNS_max) {
464 earlyOrbitIndex = j;
465 } else {
466 break;
467 }
468 }
469 std::get<2>(indices_with_early.back()) = earlyOrbitIndex;
470 }
471 }
472 return indices_with_early;
473}
474
475} // namespace
476
477void DigitizationContext::applyMaxCollisionFilter(std::vector<std::tuple<int, int, int>>& timeframeindices, long startOrbit, long orbitsPerTF, int maxColl, double orbitsEarly)
478{
479 // the idea is to go through each timeframe and throw away collisions beyond a certain count
480 // then the indices should be condensed
481
482 std::vector<std::vector<o2::steer::EventPart>> newparts;
483 std::vector<o2::InteractionTimeRecord> newrecords;
484
485 std::unordered_map<int, int> currMaxId; // the max id encountered for a source
486 std::unordered_map<int, std::unordered_map<int, int>> reIndexMap; // for each source, a map of old to new index for the event parts
487
488 if (maxColl == -1) {
489 maxColl = mEventRecords.size();
490 }
491
492 // the actual first actual timeframe
493 int first_timeframe = orbitsEarly > 0. ? 1 : 0;
494
495 // mapping of old to new indices
496 std::unordered_map<size_t, size_t> indices_old_to_new;
497
498 // now we can go through the structure timeframe by timeframe
499 for (int tf_id = first_timeframe; tf_id < timeframeindices.size(); ++tf_id) {
500 auto& tf_indices = timeframeindices[tf_id];
501
502 auto firstindex = std::get<0>(tf_indices); // .first;
503 auto lastindex = std::get<1>(tf_indices); // .second;
504 auto previndex = std::get<2>(tf_indices);
505
506 LOG(info) << "timeframe indices " << previndex << " : " << firstindex << " : " << lastindex;
507
508 int collCount = 0; // counting collisions within timeframe
509 // copy to new structure
510 for (int index = previndex >= 0 ? previndex : firstindex; index <= lastindex; ++index) {
511 if (collCount >= maxColl) {
512 continue;
513 }
514
515 // look if this index was already done?
516 // avoid duplicate entries in transformed records
517 if (indices_old_to_new.find(index) != indices_old_to_new.end()) {
518 continue;
519 }
520
521 // we put these events under a certain condition
522 bool keep = index < firstindex || collCount < maxColl;
523
524 if (!keep) {
525 continue;
526 }
527
528 if (index >= firstindex) {
529 collCount++;
530 }
531
532 // we must also make sure that we don't duplicate the records
533 // moreover some records are merely put as precoll of tf2 ---> so they shouldn't be part of tf1 in the final
534 // extraction, ouch !
535 // maybe we should combine the filter and individual tf extraction in one step !!
536 indices_old_to_new[index] = newrecords.size();
537 newrecords.push_back(mEventRecords[index]);
538 newparts.push_back(mEventParts[index]);
539
540 // reindex the event parts to achieve compactification (and initial linear increase)
541 for (auto& part : newparts.back()) {
542 auto source = part.sourceID;
543 auto entry = part.entryID;
544 // have we remapped this entry before?
545 if (reIndexMap.find(source) != reIndexMap.end() && reIndexMap[source].find(entry) != reIndexMap[source].end()) {
546 part.entryID = reIndexMap[source][entry];
547 } else {
548 // assign the next free index
549 if (currMaxId.find(source) == currMaxId.end()) {
550 currMaxId[source] = 0;
551 }
552 part.entryID = currMaxId[source];
553 // cache this assignment
554 reIndexMap[source][entry] = currMaxId[source];
555 currMaxId[source] += 1;
556 }
557 }
558 } // ends one timeframe
559
560 // correct the timeframe indices
561 if (indices_old_to_new.find(firstindex) != indices_old_to_new.end()) {
562 std::get<0>(tf_indices) = indices_old_to_new[firstindex]; // start
563 }
564 if (indices_old_to_new.find(lastindex) != indices_old_to_new.end()) {
565 std::get<1>(tf_indices) = indices_old_to_new[lastindex]; // end;
566 } else {
567 std::get<1>(tf_indices) = newrecords.size(); // end;
568 }
569 if (indices_old_to_new.find(previndex) != indices_old_to_new.end()) {
570 std::get<2>(tf_indices) = indices_old_to_new[previndex]; // previous or "early" index
571 }
572 }
573 // reassignment
574 mEventRecords = newrecords;
575 mEventParts = newparts;
576}
577
578std::vector<std::tuple<int, int, int>> DigitizationContext::calcTimeframeIndices(long startOrbit, long orbitsPerTF, double orbitsEarly) const
579{
580 auto timeframeindices = getTimeFrameBoundaries(mEventRecords, startOrbit, orbitsPerTF, orbitsEarly);
581 LOG(info) << "Fixed " << timeframeindices.size() << " timeframes ";
582 for (auto p : timeframeindices) {
583 LOG(info) << std::get<0>(p) << " " << std::get<1>(p) << " " << std::get<2>(p);
584 }
585
586 return timeframeindices;
587}
588
589std::unordered_map<int, int> DigitizationContext::getCollisionIndicesForSource(int source) const
590{
591 // go through all collisions and pick those that have the give source
592 // then keep only the first collision index
593 std::unordered_map<int, int> result;
594 const auto& parts = getEventParts(false);
595 for (int collindex = 0; collindex < parts.size(); ++collindex) {
596 for (auto& eventpart : parts[collindex]) {
597 if (eventpart.sourceID == source) {
598 result[eventpart.entryID] = collindex;
599 }
600 }
601 }
602 return result;
603}
604
605int DigitizationContext::findSimPrefix(std::string const& prefix) const
606{
607 auto iter = std::find(mSimPrefixes.begin(), mSimPrefixes.end(), prefix);
608 if (iter != mSimPrefixes.end()) {
609 return std::distance(mSimPrefixes.begin(), iter);
610 }
611 return -1;
612}
613
614namespace
615{
616struct pair_hash {
617 template <class T1, class T2>
618 std::size_t operator()(const std::pair<T1, T2>& pair) const
619 {
620 return std::hash<T1>()(pair.first) ^ std::hash<T2>()(pair.second);
621 }
622};
623} // namespace
624
626{
627 // mapping of source x event --> index into mInteractionVertices
628 std::unordered_map<std::pair<int, int>, int, pair_hash> vertex_cache;
629
630 mInteractionVertices.clear();
631 int collcount = 0;
632
633 std::unordered_set<int> vset; // used to detect vertex incompatibilities
634 for (auto& coll : mEventParts) {
635 collcount++;
636 vset.clear();
637
638 // first detect if any of these entries already has an associated vertex
639 for (auto& part : coll) {
640 auto source = part.sourceID;
641 auto event = part.entryID;
642 auto cached_iter = vertex_cache.find(std::pair<int, int>(source, event));
643
644 if (cached_iter != vertex_cache.end()) {
645 vset.emplace(cached_iter->second);
646 }
647 }
648
649 // make sure that there is no conflict
650 if (vset.size() > 1) {
651 LOG(fatal) << "Impossible conflict during interaction vertex sampling";
652 }
653
654 int cacheindex = -1;
655 if (vset.size() == 1) {
656 // all of the parts need to be assigned the same existing vertex
657 cacheindex = *(vset.begin());
658 mInteractionVertices.push_back(mInteractionVertices[cacheindex]);
659 } else {
660 // we need to sample a new point
661 mInteractionVertices.emplace_back(meanv.sample());
662 cacheindex = mInteractionVertices.size() - 1;
663 }
664 // update the cache
665 for (auto& part : coll) {
666 auto source = part.sourceID;
667 auto event = part.entryID;
668 vertex_cache[std::pair<int, int>(source, event)] = cacheindex;
669 }
670 }
671}
672
673DigitizationContext DigitizationContext::extractSingleTimeframe(int timeframeid, std::vector<std::tuple<int, int, int>> const& timeframeindices, std::vector<int> const& sources_to_offset)
674{
675 DigitizationContext r; // make a return object
676 if (timeframeindices.size() == 0) {
677 LOG(error) << "Timeframe index structure empty; Returning empty object.";
678 return r;
679 }
680 r.mSimPrefixes = mSimPrefixes;
681 r.mMuBC = mMuBC;
682 try {
683 auto tf_ranges = timeframeindices.at(timeframeid);
684
685 auto startindex = std::get<0>(tf_ranges);
686 auto endindex = std::get<1>(tf_ranges);
687 auto earlyindex = std::get<2>(tf_ranges);
688
689 if (earlyindex >= 0) {
690 startindex = earlyindex;
691 }
692 std::copy(mEventRecords.begin() + startindex, mEventRecords.begin() + endindex, std::back_inserter(r.mEventRecords));
693 std::copy(mEventParts.begin() + startindex, mEventParts.begin() + endindex, std::back_inserter(r.mEventParts));
694 if (mInteractionVertices.size() > endindex) {
695 std::copy(mInteractionVertices.begin() + startindex, mInteractionVertices.begin() + endindex, std::back_inserter(r.mInteractionVertices));
696 }
697
698 // let's assume we want to fix the ids for source = source_id
699 // Then we find the first index that has this source_id and take the corresponding number
700 // as offset. Thereafter we subtract this offset from all known event parts.
701 auto perform_offsetting = [&r](int source_id) {
702 auto indices_for_source = r.getCollisionIndicesForSource(source_id);
703 int minvalue = std::numeric_limits<int>::max();
704 for (auto& p : indices_for_source) {
705 if (p.first < minvalue) {
706 minvalue = p.first;
707 }
708 }
709 // now fix them
710 for (auto& p : indices_for_source) {
711 auto index_into_mEventParts = p.second;
712 for (auto& part : r.mEventParts[index_into_mEventParts]) {
713 if (part.sourceID == source_id) {
714 part.entryID -= minvalue;
715 }
716 }
717 }
718 };
719 for (auto source_id : sources_to_offset) {
720 perform_offsetting(source_id);
721 }
722
723 } catch (std::exception) {
724 LOG(warn) << "No such timeframe id in collision context. Returing empty object";
725 }
726 // fix number of collisions
727 r.setNCollisions(r.mEventRecords.size());
728 return r;
729}
Definition of the Names Generator class.
uint64_t orbit
Definition RawEventData.h:6
int32_t i
GPUChain * chain
constexpr int p2()
constexpr int p1()
constexpr to accelerate the coordinates changing
uint32_t j
Definition RawData.h:0
static std::string getHitsFileName(DId d, const std::string_view prefix=STANDARDSIMPREFIX)
static std::string getMCKinematicsFileName(const std::string_view prefix=STANDARDSIMPREFIX)
Definition NameConf.h:46
static std::string getCollisionContextFileName(const std::string_view prefix="")
Definition NameConf.cxx:52
math_utils::Point3D< float > sample() const
sample a vertex from the MeanVertex parameters
Static class with identifiers, bitmasks and names for ALICE detectors.
Definition DetID.h:58
static constexpr const char * getName(ID id)
names of defined detectors
Definition DetID.h:145
static GRPObject * loadFrom(const std::string &grpFileName="")
DigitizationContext extractSingleTimeframe(int timeframeid, std::vector< std::tuple< int, int, int > > const &timeframeindices, std::vector< int > const &sources_to_offset)
bool checkVertexCompatibility(bool verbose=false) const
Check collision parts for vertex consistency.
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
bool initSimChains(o2::detectors::DetID detid, std::vector< TChain * > &simchains) const
std::unordered_map< int, int > getCollisionIndicesForSource(int source) const
void printCollisionSummary(bool withQED=false, int truncateOutputTo=-1) const
int findSimPrefix(std::string const &prefix) const
void applyMaxCollisionFilter(std::vector< std::tuple< int, int, int > > &timeframeindices, long startOrbit, long orbitsPerTF, int maxColl, double orbitsEarly=0.)
void setSimPrefixes(std::vector< std::string > const &p)
bool initSimKinematicsChains(std::vector< TChain * > &simkinematicschains) const
o2::parameters::GRPObject const & getGRP() const
returns the GRP object associated to this context
void sampleInteractionVertices(o2::dataformats::MeanVertexObject const &v)
std::vector< std::vector< o2::steer::EventPart > > & getEventParts(bool withQED=false)
void saveToFile(std::string_view filename) const
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
static DigitizationContext * loadFromFile(std::string_view filename="")
void setFirstIR(const o2::InteractionRecord &ir)
const o2::InteractionTimeRecord & generateCollisionTime()
void setBunchFilling(const BunchFilling &bc)
void setInteractionRate(float rateHz)
struct _cl_event * event
Definition glcorearb.h:2982
GLuint64EXT * result
Definition glcorearb.h:5662
GLuint entry
Definition glcorearb.h:5735
GLuint index
Definition glcorearb.h:781
GLdouble GLdouble right
Definition glcorearb.h:4077
GLdouble f
Definition glcorearb.h:310
GLsizei GLsizei GLchar * source
Definition glcorearb.h:798
GLint left
Definition glcorearb.h:1979
GLboolean * data
Definition glcorearb.h:298
GLboolean r
Definition glcorearb.h:1233
constexpr int LHCMaxBunches
std::string filename()
std::vector< o2::ctf::BufferType > vec
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"