Project
Loading...
Searching...
No Matches
GeneratorFromFile.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 <fairlogger/Logger.h>
17#include <FairPrimaryGenerator.h>
18#include <TBranch.h>
19#include <TClonesArray.h>
20#include <TFile.h>
21#include <TMCProcess.h>
22#include <TParticle.h>
23#include <TTree.h>
24#include <sstream>
25#include <filesystem>
26#include <TGrid.h>
27#include <TSystem.h>
28
29namespace o2
30{
31namespace eventgen
32{
34{
35 mEventFile = TFile::Open(name);
36 if (mEventFile == nullptr) {
37 LOG(fatal) << "EventFile " << name << " not found \n";
38 return;
39 }
40 // the kinematics will be stored inside a Tree "TreeK" with branch "Particles"
41 // different events are stored inside TDirectories
42
43 // we need to probe for the number of events
44 TObject* object = nullptr;
45 do {
46 std::stringstream eventstringstr;
47 eventstringstr << "Event" << mEventsAvailable;
48 // std::cout << "probing for " << eventstring << "\n";
49 object = mEventFile->Get(eventstringstr.str().c_str());
50 // std::cout << "got " << object << "\n";
51 if (object != nullptr) {
52 mEventsAvailable++;
53 }
54 } while (object != nullptr);
55 LOG(info) << "Found " << mEventsAvailable << " events in this file \n";
56}
57
59{
60 if (start < mEventsAvailable) {
61 mEventCounter = start;
62 } else {
63 LOG(error) << "start event bigger than available events\n";
64 }
65}
66
68{
69 // avoid compute if the particle is not known in the PDG database
70 if (!p.GetPDG()) {
71 LOG(warn) << "Particle with pdg " << p.GetPdgCode() << " not known in DB (not fixing mass)";
72 // still returning true here ... primary will be flagged as non-trackable by primary event generator
73 return true;
74 }
75
76 const auto nominalmass = p.GetMass();
77 auto mom2 = p.Px() * p.Px() + p.Py() * p.Py() + p.Pz() * p.Pz();
78 auto calculatedmass = p.Energy() * p.Energy() - mom2;
79 calculatedmass = (calculatedmass >= 0.) ? std::sqrt(calculatedmass) : -std::sqrt(-calculatedmass);
80 const double tol = 1.E-4;
81 auto difference = std::abs(nominalmass - calculatedmass);
82 if (std::abs(nominalmass - calculatedmass) > tol) {
83 const auto asgmass = p.GetCalcMass();
84 bool fix = mFixOffShell && std::abs(nominalmass - asgmass) < tol;
85 LOG(warn) << "Particle " << p.GetPdgCode() << " has off-shell mass: M_PDG= " << nominalmass << " (assigned= " << asgmass
86 << ") calculated= " << calculatedmass << " -> diff= " << difference << " | " << (fix ? "fixing" : "skipping");
87 if (fix) {
88 double e = std::sqrt(nominalmass * nominalmass + mom2);
89 p.SetMomentum(p.Px(), p.Py(), p.Pz(), e);
90 p.SetCalcMass(nominalmass);
91 } else {
92 return false;
93 }
94 }
95 return true;
96}
97
99{
100 if (mEventCounter < mEventsAvailable) {
101 int particlecounter = 0;
102
103 // get the tree and the branch
104 std::stringstream treestringstr;
105 treestringstr << "Event" << mEventCounter << "/TreeK";
106 TTree* tree = (TTree*)mEventFile->Get(treestringstr.str().c_str());
107 if (tree == nullptr) {
108 return kFALSE;
109 }
110
111 auto branch = tree->GetBranch("Particles");
112 TParticle* particle = nullptr;
113 branch->SetAddress(&particle);
114 LOG(info) << "Reading " << branch->GetEntries() << " particles from Kinematics file";
115
116 // read the whole kinematics initially
117 std::vector<TParticle> particles;
118 for (int i = 0; i < branch->GetEntries(); ++i) {
119 branch->GetEntry(i);
120 particles.push_back(*particle);
121 }
122
123 // filter the particles from Kinematics.root originally put by a generator
124 // and which are trackable
125 auto isFirstTrackableDescendant = [](TParticle const& p) {
126 const int kTransportBit = BIT(14);
127 // The particle should have not set kDone bit and its status should not exceed 1
128 if ((p.GetUniqueID() > 0 && p.GetUniqueID() != kPNoProcess) || !p.TestBit(kTransportBit)) {
129 return false;
130 }
131 return true;
132 };
133
134 for (int i = 0; i < branch->GetEntries(); ++i) {
135 auto& p = particles[i];
136 if (!isFirstTrackableDescendant(p)) {
137 continue;
138 }
139
140 bool wanttracking = true; // RS as far as I understand, if it reached this point, it is trackable
141 if (wanttracking || !mSkipNonTrackable) {
142 if (!rejectOrFixKinematics(p)) {
143 continue;
144 }
145 auto pdgid = p.GetPdgCode();
146 auto px = p.Px();
147 auto py = p.Py();
148 auto pz = p.Pz();
149 auto vx = p.Vx();
150 auto vy = p.Vy();
151 auto vz = p.Vz();
152 auto parent = -1;
153 auto e = p.Energy();
154 auto tof = p.T();
155 auto weight = p.GetWeight();
156 LOG(debug) << "Putting primary " << pdgid << " " << p.GetStatusCode() << " " << p.GetUniqueID();
157 primGen->AddTrack(pdgid, px, py, pz, vx, vy, vz, parent, wanttracking, e, tof, weight);
158 particlecounter++;
159 }
160 }
161 mEventCounter++;
162
163 LOG(info) << "Event generator put " << particlecounter << " on stack";
164 return kTRUE;
165 } else {
166 LOG(error) << "GeneratorFromFile: Ran out of events\n";
167 }
168 return kFALSE;
169}
170
171// based on O2 kinematics
172
174{
175 // this generator should leave all dimensions the same as in the incoming kinematics file
176 setMomentumUnit(1.);
177 setEnergyUnit(1.);
178 setPositionUnit(1.);
179 setTimeUnit(1.);
180
181 if (strncmp(name, "alien:/", 7) == 0 && !gGrid) {
182 TGrid::Connect("alien:");
183 if (!gGrid) {
184 LOG(fatal) << "Could not connect to alien, did you check the alien token?";
185 return;
186 }
187 }
188 mEventFile = TFile::Open(name);
189 if (mEventFile == nullptr) {
190 LOG(fatal) << "EventFile " << name << " not found";
191 return;
192 }
193 // the kinematics will be stored inside a branch MCTrack
194 // different events are stored inside different entries
195 auto tree = (TTree*)mEventFile->Get("o2sim");
196 if (tree) {
197 mEventBranch = tree->GetBranch("MCTrack");
198 if (mEventBranch) {
199 mEventsAvailable = mEventBranch->GetEntries();
200 LOG(info) << "Found " << mEventsAvailable << " events in this file";
201 }
202 mMCHeaderBranch = tree->GetBranch("MCEventHeader.");
203 if (mMCHeaderBranch) {
204 LOG(info) << "Found " << mMCHeaderBranch->GetEntries() << " event-headers";
205 } else {
206 LOG(warn) << "No MCEventHeader branch found in kinematics input file";
207 }
208 return;
209 }
210 LOG(error) << "Problem reading events from file " << name;
211}
212
214{
215 mConfig = std::make_unique<O2KineGenConfig>(pars);
216}
217
219{
220
221 // read and set params
222
223 LOG(info) << "Init \'FromO2Kine\' generator";
224 mSkipNonTrackable = mConfig->skipNonTrackable;
225 mContinueMode = mConfig->continueMode;
226 mRoundRobin = mConfig->roundRobin;
227 mRandomize = mConfig->randomize;
228 mRngSeed = mConfig->rngseed;
229 mRandomPhi = mConfig->randomphi;
230 if (mRandomize) {
231 gRandom->SetSeed(mRngSeed);
232 }
233
234 return true;
235}
236
238{
239 if (start < mEventsAvailable) {
240 mEventCounter = start;
241 } else {
242 LOG(error) << "start event bigger than available events\n";
243 }
244}
245
247{
248 // NOTE: This should be usable with kinematics files without secondaries
249 // It might need some adjustment to make it work with secondaries or to continue
250 // from a kinematics snapshot
251
252 // Randomize the order of events in the input file
253 if (mRandomize) {
254 mEventCounter = gRandom->Integer(mEventsAvailable);
255 LOG(info) << "GeneratorFromO2Kine - Picking event " << mEventCounter;
256 }
257
258 double dPhi = 0.;
259 // Phi rotation
260 if (mRandomPhi) {
261 dPhi = gRandom->Uniform(2 * TMath::Pi());
262 LOG(info) << "Rotating phi by " << dPhi;
263 }
264
265 if (mEventCounter < mEventsAvailable) {
266 int particlecounter = 0;
267
268 std::vector<o2::MCTrack>* tracks = nullptr;
269 mEventBranch->SetAddress(&tracks);
270 mEventBranch->GetEntry(mEventCounter);
271
272 if (mMCHeaderBranch) {
273 o2::dataformats::MCEventHeader* mcheader = nullptr;
274 mMCHeaderBranch->SetAddress(&mcheader);
275 mMCHeaderBranch->GetEntry(mEventCounter);
276 mOrigMCEventHeader.reset(mcheader);
277 }
278
279 for (auto& t : *tracks) {
280
281 // in case we do not want to continue, take only primaries
282 if (!mContinueMode && !t.isPrimary()) {
283 continue;
284 }
285
286 auto pdg = t.GetPdgCode();
287 auto px = t.Px();
288 auto py = t.Py();
289 if (mRandomPhi) {
290 // transformation applied through rotation matrix
291 auto cos = TMath::Cos(dPhi);
292 auto sin = TMath::Sin(dPhi);
293 auto newPx = px * cos - py * sin;
294 auto newPy = px * sin + py * cos;
295 px = newPx;
296 py = newPy;
297 }
298 auto pz = t.Pz();
299 auto vx = t.Vx();
300 auto vy = t.Vy();
301 auto vz = t.Vz();
302 auto m1 = t.getMotherTrackId();
303 auto m2 = t.getSecondMotherTrackId();
304 auto d1 = t.getFirstDaughterTrackId();
305 auto d2 = t.getLastDaughterTrackId();
306 auto e = t.GetEnergy();
307 auto vt = t.T() * 1e-9; // MCTrack stores in ns ... generators and engines use seconds
308 auto weight = t.getWeight();
309 auto wanttracking = t.getToBeDone();
310
311 if (mContinueMode) { // in case we want to continue, do only inhibited tracks
312 wanttracking &= t.getInhibited();
313 }
314
315 LOG(debug) << "Putting primary " << pdg;
316
317 mParticles.push_back(TParticle(pdg, t.getStatusCode().fullEncoding, m1, m2, d1, d2, px, py, pz, e, vx, vy, vz, vt));
318 mParticles.back().SetUniqueID((unsigned int)t.getProcess()); // we should propagate the process ID
319 mParticles.back().SetBit(ParticleStatus::kToBeDone, wanttracking);
320 mParticles.back().SetWeight(weight);
321
322 particlecounter++;
323 }
324 mEventCounter++;
325 if (mRoundRobin) {
326 LOG(info) << "Resetting event counter to 0; Reusing events from file";
327 mEventCounter = mEventCounter % mEventsAvailable;
328 }
329
330 if (tracks) {
331 delete tracks;
332 }
333
334 LOG(info) << "Event generator put " << particlecounter << " on stack";
335 return true;
336 } else {
337 LOG(error) << "GeneratorFromO2Kine: Ran out of events\n";
338 }
339 return false;
340}
341
343{
346 // we forward the original header information if any
347 if (mOrigMCEventHeader.get()) {
348 eventHeader->copyInfoFrom(*mOrigMCEventHeader.get());
349 }
350 // we forward also the original basic vertex information contained in FairMCEventHeader
351 static_cast<FairMCEventHeader&>(*eventHeader) = static_cast<FairMCEventHeader&>(*mOrigMCEventHeader.get());
352
353 // put additional information about input file and event number of the current event
354 eventHeader->putInfo<std::string>("forwarding-generator", "generatorFromO2Kine");
355 eventHeader->putInfo<std::string>("forwarding-generator_inputFile", mEventFile->GetName());
356 eventHeader->putInfo<int>("forwarding-generator_inputEventNumber", mEventCounter - 1);
357}
358
359namespace
360{
361// some helper to execute a command and capture it's output in a vector
362std::vector<std::string> executeCommand(const std::string& command)
363{
364 std::vector<std::string> result;
365 std::unique_ptr<FILE, int (*)(FILE*)> pipe(popen(command.c_str(), "r"), pclose);
366 if (!pipe) {
367 throw std::runtime_error("Failed to open pipe");
368 }
369
370 char buffer[1024];
371 while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr) {
372 std::string line(buffer);
373 // Remove trailing newline character, if any
374 if (!line.empty() && line.back() == '\n') {
375 line.pop_back();
376 }
377 result.push_back(line);
378 }
379 return result;
380}
381} // namespace
382
386
388{
389 // this simply passes tracks trough. Leave units intact.
390 setTimeUnit(1.);
391 setPositionUnit(1.);
392 setEnergyUnit(1.);
393
394 // initialize the event pool
395 if (mConfig.rngseed > 0) {
396 mRandomEngine.seed(mConfig.rngseed);
397 } else {
398 std::random_device rd;
399 mRandomEngine.seed(rd());
400 }
401 TString expPath(mConfig.eventPoolPath);
402 gSystem->ExpandPathName(expPath);
403 mPoolFilesAvailable = setupFileUniverse(expPath.Data());
404
405 if (mPoolFilesAvailable.size() == 0) {
406 LOG(error) << "No file found that can be used with EventPool generator";
407 return false;
408 }
409 LOG(info) << "Found " << mPoolFilesAvailable.size() << " available event pool files";
410
411 // now choose the actual file
412 std::uniform_int_distribution<int> distribution(0, mPoolFilesAvailable.size() - 1);
413 auto chosenIndex = distribution(mRandomEngine);
414 mFileChosen = mPoolFilesAvailable[chosenIndex];
415 LOG(info) << "EventPool is using file " << mFileChosen;
416
417 // we bring up the internal mO2KineGenerator
418 auto kine_config = O2KineGenConfig{
420 .continueMode = false,
421 .roundRobin = false,
422 .randomize = mConfig.randomize,
423 .rngseed = mConfig.rngseed,
424 .randomphi = mConfig.randomphi,
425 .fileName = mFileChosen};
426 mO2KineGenerator.reset(new GeneratorFromO2Kine(kine_config));
427 return mO2KineGenerator->Init();
428}
429
430namespace
431{
432namespace fs = std::filesystem;
433// checks a single file name
434bool checkFileName(std::string const& pathStr)
435{
436 // LOG(info) << "Checking filename " << pathStr;
437 try {
438 // Remove optional protocol prefix "alien://"
439 const std::string protocol = "alien://";
440 std::string finalPathStr(pathStr);
441 if (pathStr.starts_with(protocol)) {
442 finalPathStr = pathStr.substr(protocol.size());
443 }
444 fs::path path(finalPathStr);
445
446 // Check if the filename is "evtpool.root"
448 } catch (const fs::filesystem_error& e) {
449 // Invalid path syntax will throw an exception
450 std::cerr << "Filesystem error: " << e.what() << '\n';
451 return false;
452 } catch (...) {
453 // Catch-all for other potential exceptions
454 std::cerr << "An unknown error occurred while checking the path.\n";
455 return false;
456 }
457}
458
459// checks a whole universe of file names
460bool checkFileUniverse(std::vector<std::string> const& universe)
461{
462 if (universe.size() == 0) {
463 return false;
464 }
465 for (auto& fn : universe) {
466 if (!checkFileName(fn)) {
467 return false;
468 }
469 }
470 // TODO: also check for a common path structure with maximally 00X as only difference
471
472 return true;
473}
474
475std::vector<std::string> readLines(const std::string& filePath)
476{
477 std::vector<std::string> lines;
478
479 // Check if the file is a valid text file
480 fs::path path(filePath);
481
482 // Open the file
483 std::ifstream file(filePath);
484 if (!file.is_open()) {
485 throw std::ios_base::failure("Failed to open the file.");
486 }
487
488 // Read up to n lines
489 std::string line;
490 while (std::getline(file, line)) {
491 lines.push_back(line);
492 }
493 return lines;
494}
495
496// Function to find all files named eventpool_filename under a given path
497std::vector<std::string> getLocalFileList(const fs::path& rootPath)
498{
499 std::vector<std::string> result;
500
501 // Ensure the root path exists and is a directory
502 if (!fs::exists(rootPath) || !fs::is_directory(rootPath)) {
503 throw std::invalid_argument("The provided path is not a valid directory.");
504 }
505
506 // Iterate over the directory and subdirectories
507 for (const auto& entry : fs::recursive_directory_iterator(rootPath)) {
508 if (entry.is_regular_file() && entry.path().filename() == GeneratorFromEventPool::eventpool_filename) {
509 result.push_back(entry.path().string());
510 }
511 }
512 return result;
513}
514
515} // end anonymous namespace
516
519std::vector<std::string> GeneratorFromEventPool::setupFileUniverse(std::string const& path) const
520{
521 // the path could refer to a local or alien filesystem; find out first
522 bool onAliEn = strncmp(path.c_str(), std::string(alien_protocol_prefix).c_str(), alien_protocol_prefix.size()) == 0;
523 std::vector<std::string> result;
524
525 if (onAliEn) {
526 // AliEn case
527 // we support: (a) an actual evtgen file and (b) a path containing multiple eventfiles
528
529 auto alienStatTypeCommand = std::string("alien.py stat ") + mConfig.eventPoolPath + std::string(" 2>/dev/null | grep Type ");
530 auto typeString = executeCommand(alienStatTypeCommand);
531 if (typeString.size() == 0) {
532 return result;
533 } else if (typeString.size() == 1 && typeString.front() == std::string("Type: f")) {
534 // this is a file ... simply use it
535 result.push_back(mConfig.eventPoolPath);
536 return result;
537 } else if (typeString.size() == 1 && typeString.front() == std::string("Type: d")) {
538 // this is a directory
539 // construct command to find actual event files
540 std::string alienSearchCommand = std::string("alien.py find ") +
541 mConfig.eventPoolPath + "/ " + std::string(eventpool_filename);
542
543 auto universe_vector = executeCommand(alienSearchCommand);
544 // check vector
545 if (!checkFileUniverse(universe_vector)) {
546 return result;
547 }
548 for (auto& f : universe_vector) {
549 f = std::string(alien_protocol_prefix) + f;
550 }
551
552 return universe_vector;
553 } else {
554 LOG(error) << "Unsupported file type";
555 return result;
556 }
557 } else {
558 // local file case
559 // check if the path is a regular file
560 auto is_actual_file = std::filesystem::is_regular_file(path);
561 if (is_actual_file) {
562 // The files must match a criteria of being canonical paths ending with evtpool.root
563 if (checkFileName(path)) {
564 TFile rootfile(path.c_str(), "OPEN");
565 if (!rootfile.IsZombie()) {
566 result.push_back(path);
567 return result;
568 }
569 } else {
570 // otherwise assume it is a text file containing a list of files themselves
571 auto files = readLines(path);
572 if (checkFileUniverse(files)) {
573 result = files;
574 return result;
575 }
576 }
577 } else {
578 // check if the path is just a path
579 // In this case we need to search something and check
580 auto is_dir = std::filesystem::is_directory(path);
581 if (!is_dir) {
582 return result;
583 }
584 auto files = getLocalFileList(path);
585 if (checkFileUniverse(files)) {
586 result = files;
587 return result;
588 }
589 }
590 }
591 return result;
592}
593
594} // namespace eventgen
595} // end namespace o2
596
std::ostringstream debug
int32_t i
ClassImp(o2::eventgen::GeneratorFromEventPool)
Definition of the MCTrack class.
@ kToBeDone
void copyInfoFrom(MCEventHeader const &other)
inits info fields from another Event header
void putInfo(std::string const &key, T const &value)
static constexpr std::string_view eventpool_filename
static constexpr std::string_view alien_protocol_prefix
std::vector< std::string > setupFileUniverse(std::string const &path) const
bool ReadEvent(FairPrimaryGenerator *primGen) override
void updateHeader(o2::dataformats::MCEventHeader *eventHeader) override
void setPositionUnit(double val)
Definition Generator.h:87
void setEnergyUnit(double val)
Definition Generator.h:85
void setTimeUnit(double val)
Definition Generator.h:89
std::vector< TParticle > mParticles
Definition Generator.h:147
void setMomentumUnit(double val)
Definition Generator.h:83
GLuint64EXT * result
Definition glcorearb.h:5662
GLuint buffer
Definition glcorearb.h:655
GLuint entry
Definition glcorearb.h:5735
GLuint const GLchar * name
Definition glcorearb.h:781
GLdouble f
Definition glcorearb.h:310
GLuint GLuint GLfloat weight
Definition glcorearb.h:5477
GLsizei const GLchar *const * path
Definition glcorearb.h:3591
GLuint start
Definition glcorearb.h:469
int32_t const char * file
int32_t const char int32_t line
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::unique_ptr< TTree > tree((TTree *) flIn.Get(std::string(o2::base::NameConf::CTFTREENAME).c_str()))
std::random_device rd