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