Project
Loading...
Searching...
No Matches
Generator.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
13
15#include "Generators/Trigger.h"
20#include <SimConfig/SimConfig.h>
21#include "FairPrimaryGenerator.h"
22#include <fairlogger/Logger.h>
23#include <cmath>
24#include "TClonesArray.h"
25#include "TParticle.h"
26#include "TString.h"
27#include "TSystem.h"
28#include "TGrid.h"
30#include <filesystem>
31#include <cstdlib>
32#ifdef GENERATORS_WITH_TPCLOOPERS
35#endif
36#ifdef GENERATORS_WITH_PYTHIA8
39#endif
40
41namespace o2
42{
43namespace eventgen
44{
45
46std::atomic<int> Generator::InstanceCounter{0};
47unsigned int Generator::gTotalNEvents = 0;
48
50{
51 static const bool isHY = std::getenv("IS_HYPERLOOP") && std::atoi(std::getenv("IS_HYPERLOOP"));
52 return isHY;
53}
54/*****************************************************************/
55/*****************************************************************/
56
57Generator::Generator() : FairGenerator("ALICEo2", "ALICEo2 Generator"),
58 mBoost(0.)
59{
63#ifdef GENERATORS_WITH_TPCLOOPERS
64 const auto& simConfig = o2::conf::SimConfig::Instance();
65 const auto& loopersParam = o2::eventgen::GenTPCLoopersParam::Instance();
66 if (!loopersParam.loopersVeto) {
67 bool transport = (simConfig.getMCEngine() != "O2TrivialMCEngine");
68 if (transport) {
69 bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end());
70 if (tpcActive) {
71 if (initTPCLoopersGen()) {
72 mAddTPCLoopers = kTRUE;
73 }
74 } else {
75 LOG(info) << "TPC not active in readout detectors: loopers fast generator disabled.";
76 }
77 }
78 } else {
79 LOG(info) << "Loopers fast generator turned OFF with veto flag.";
80 }
81#endif
82}
83
84/*****************************************************************/
85
86Generator::Generator(const Char_t* name, const Char_t* title) : FairGenerator(name, title),
87 mBoost(0.)
88{
92#ifdef GENERATORS_WITH_TPCLOOPERS
93 const auto& simConfig = o2::conf::SimConfig::Instance();
94 const auto& loopersParam = o2::eventgen::GenTPCLoopersParam::Instance();
95 if (!loopersParam.loopersVeto) {
96 bool transport = (simConfig.getMCEngine() != "O2TrivialMCEngine");
97 if (transport) {
98 bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end());
99 if (tpcActive) {
100 if (initTPCLoopersGen()) {
101 mAddTPCLoopers = kTRUE;
102 }
103 } else {
104 LOG(info) << "TPC not active in readout detectors: loopers fast generator disabled.";
105 }
106 }
107 } else {
108 LOG(info) << "Loopers fast generator turned OFF with veto flag.";
109 }
110#endif
111}
112
113/*****************************************************************/
114
116{
118#ifdef GENERATORS_WITH_TPCLOOPERS
119 if (mTPCLoopersGen) {
120 delete mTPCLoopersGen;
121 mTPCLoopersGen = nullptr;
122 }
123#endif
124#ifdef GENERATORS_WITH_PYTHIA8
125 if (mEventDecayerReport) {
126 LOG(info) << "EventDecayer summary of generator " << GetName();
127 mEventDecayerReport->print();
128 }
129 delete mEventDecayer;
130 delete mEventDecayerReport;
131#endif
132}
133
134/*****************************************************************/
135#ifdef GENERATORS_WITH_TPCLOOPERS
136bool Generator::initTPCLoopersGen()
137{
138 // Expand all environment paths
139 const auto& loopersParam = o2::eventgen::GenTPCLoopersParam::Instance();
140 auto expandPathName = [](const std::string& path) {
141 TString expandedPath = path;
142 gSystem->ExpandPathName(expandedPath);
143 return std::string(expandedPath.Data());
144 };
145 std::string model_pairs = expandPathName(loopersParam.model_pairs);
146 std::string model_compton = expandPathName(loopersParam.model_compton);
147 std::string nclxrate = expandPathName(loopersParam.nclxrate);
148 const std::string scaler_pair = expandPathName(loopersParam.scaler_pair);
149 const std::string scaler_compton = expandPathName(loopersParam.scaler_compton);
150 const std::string poisson = expandPathName(loopersParam.poisson);
151 const std::string gauss = expandPathName(loopersParam.gauss);
152 const auto& flat_gas = loopersParam.flat_gas;
153 const auto& colsys = loopersParam.colsys;
154 if (flat_gas) {
155 if (colsys != "PbPb" && colsys != "pp") {
156 LOG(warning) << "Automatic background loopers configuration supports only 'pp' and 'PbPb' systems.";
157 LOG(warning) << "Fast loopers generator will remain OFF.";
158 return kFALSE;
159 }
160 bool isContext = std::filesystem::exists("collisioncontext.root");
161 if (!isContext) {
162 LOG(warning) << "Warning: No collisioncontext.root file found!";
163 LOG(warning) << "Loopers will be kept OFF.";
164 return kFALSE;
165 }
166 }
167 std::array<float, 2> multiplier = {loopersParam.multiplier[0], loopersParam.multiplier[1]};
168 unsigned int nLoopersPairs = loopersParam.fixedNLoopers[0];
169 unsigned int nLoopersCompton = loopersParam.fixedNLoopers[1];
170 const std::array<std::string, 3> models = {model_pairs, model_compton, nclxrate};
171 const std::array<std::string, 3> local_names = {"WGANpair.onnx", "WGANcompton.onnx", "nclxrate.root"};
172 const std::array<bool, 3> isAlien = {models[0].starts_with("alien://"), models[1].starts_with("alien://"), models[2].starts_with("alien://")};
173 const std::array<bool, 3> isCCDB = {models[0].starts_with("ccdb://"), models[1].starts_with("ccdb://"), models[2].starts_with("ccdb://")};
174 if (std::any_of(isAlien.begin(), isAlien.end(), [](bool v) { return v; })) {
175 if (!gGrid) {
176 TGrid::Connect("alien://");
177 if (!gGrid) {
178 LOG(fatal) << "AliEn connection failed, check token.";
179 exit(1);
180 }
181 }
182 for (size_t i = 0; i < models.size(); ++i) {
183 if (isAlien[i] && !TFile::Cp(models[i].c_str(), local_names[i].c_str())) {
184 LOG(fatal) << "Error: Model file " << models[i] << " does not exist!";
185 exit(1);
186 }
187 }
188 }
189 if (std::any_of(isCCDB.begin(), isCCDB.end(), [](bool v) { return v; })) {
191 ccdb.setURL("http://alice-ccdb.cern.ch");
192 // Get underlying CCDB API from BasicCCDBManager
193 auto& ccdb_api = ccdb.getCCDBAccessor();
194 for (size_t i = 0; i < models.size(); ++i) {
195 if (isCCDB[i]) {
196 auto model_path = models[i].substr(7); // Remove "ccdb://"
197 // Treat filename if provided in the CCDB path
198 auto extension = model_path.find(".onnx");
199 if (extension != std::string::npos) {
200 auto last_slash = model_path.find_last_of('/');
201 model_path = model_path.substr(0, last_slash);
202 }
203 std::map<std::string, std::string> filter;
204 if (!ccdb_api.retrieveBlob(model_path, "./", filter, o2::ccdb::getCurrentTimestamp(), false, local_names[i].c_str())) {
205 LOG(fatal) << "Error: issues in retrieving " << model_path << " from CCDB!";
206 exit(1);
207 }
208 }
209 }
210 }
211 model_pairs = isAlien[0] || isCCDB[0] ? local_names[0] : model_pairs;
212 model_compton = isAlien[1] || isCCDB[1] ? local_names[1] : model_compton;
213 nclxrate = isAlien[2] || isCCDB[2] ? local_names[2] : nclxrate;
214 try {
215 // Create the TPC loopers generator with the provided parameters
216 mTPCLoopersGen = new o2::eventgen::GenTPCLoopers(model_pairs, model_compton, poisson, gauss, scaler_pair, scaler_compton);
217 mTPCLoopersGen->setGeomProtection(loopersParam.geomProtection);
218 const auto& intrate = loopersParam.intrate;
219 // Configure the generator with flat gas loopers defined per orbit with clusters/track info
220 // If intrate is negative (default), automatic IR from collisioncontext.root will be used
221 if (flat_gas) {
222 mTPCLoopersGen->SetRate(nclxrate, (colsys == "PbPb") ? true : false, intrate);
223 mTPCLoopersGen->SetAdjust(loopersParam.adjust_flatgas);
224 } else {
225 // Otherwise, Poisson+Gauss sampling or fixed number of loopers per event will be used
226 // Multiplier is applied only with distribution sampling
227 // This configuration can be used for testing purposes, in all other cases flat gas is recommended
228 mTPCLoopersGen->SetNLoopers(nLoopersPairs, nLoopersCompton);
229 mTPCLoopersGen->SetMultiplier(multiplier);
230 }
231 LOG(info) << "TPC Loopers generator initialized successfully";
232 } catch (const std::exception& e) {
233 LOG(error) << "Failed to initialize TPC Loopers generator: " << e.what();
234 delete mTPCLoopersGen;
235 mTPCLoopersGen = nullptr;
236 return kFALSE;
237 }
238 return kTRUE;
239}
240#endif
241
242/*****************************************************************/
243
244Bool_t
246{
250 return kTRUE;
251}
252
253/*****************************************************************/
254
255Bool_t
257{
258#ifdef GENERATORS_WITH_TPCLOOPERS
259 if (mAddTPCLoopers) {
260 if (!mTPCLoopersGen) {
261 LOG(error) << "Loopers generator not initialized";
262 return kFALSE;
263 }
264
265 // Generate loopers using the initialized TPC loopers generator
266 if (!mTPCLoopersGen->generateEvent()) {
267 LOG(error) << "Failed to generate loopers event";
268 return kFALSE;
269 }
270 const auto nCandidates = mTPCLoopersGen->getNLoopers();
271 if (nCandidates == 0) {
272 LOG(warning) << "No loopers generated for this event";
273 return kTRUE;
274 }
275 const auto& looperParticles = mTPCLoopersGen->importParticles();
276 const auto skippedLoopers = mTPCLoopersGen->getNSkipped();
277 if (looperParticles.empty()) {
278 if (skippedLoopers == nCandidates) {
279 // all candidate loopers were dropped by the geometrical protection
280 LOG(debug) << "All " << skippedLoopers << " candidate loopers were outside the TPC active volume; none added for this event";
281 return kTRUE;
282 }
283 LOG(error) << "Failed to import loopers particles";
284 return kFALSE;
285 }
286 // Append the generated looper particles to the main particle list
287 mParticles.insert(mParticles.end(), looperParticles.begin(), looperParticles.end());
288
289 LOG(debug) << "Added " << looperParticles.size() << " looper particles";
290 if (skippedLoopers > 0) {
291 LOG(debug) << "Geometrical protection skipped " << skippedLoopers << " loopers outside the TPC active volume";
292 }
293 }
294#endif
295 return kTRUE;
296}
297
298/*****************************************************************/
299
300Bool_t
302{
306 while (true) {
308
310 mParticles.clear();
311
313 mSubGeneratorId = -1;
314
316 if (!generateEvent()) {
317 LOG(error) << "ReadEvent failed in generateEvent";
318 return kFALSE;
319 }
320
322 if (!importParticles()) {
323 LOG(error) << "ReadEvent failed in importParticles";
324 return kFALSE;
325 }
326
328 if (!finalizeEvent()) {
329 LOG(error) << "ReadEvent failed in finalizeEvent";
330 return kFALSE;
331 }
332
333 if (mSubGeneratorsIdToDesc.empty() && mSubGeneratorId > -1) {
334 LOG(fatal) << "ReadEvent failed because no SubGenerator description given";
335 }
336
337 if (!mSubGeneratorsIdToDesc.empty() && mSubGeneratorId < 0) {
338 LOG(fatal) << "ReadEvent failed because SubGenerator description given but sub-generator not set";
339 }
340
342 if (triggerEvent()) {
344 break;
345 } else {
347 }
348 }
349
350 auto header = primGen->GetEvent();
351 auto o2header = dynamic_cast<o2::dataformats::MCEventHeader*>(header);
352 if (!header) {
353 LOG(fatal) << "MC event header is not a 'o2::dataformats::MCEventHeader' object";
354 return kFALSE;
355 }
356
358 if (!decayParticles(o2header)) {
359 LOG(error) << "ReadEvent failed in decayParticles";
360 return kFALSE;
361 }
362
364 if (!addTracks(primGen)) {
365 LOG(error) << "ReadEvent failed in addTracks";
366 return kFALSE;
367 }
368
370 updateHeader(o2header);
371 updateSubGeneratorInformation(o2header);
372
374 return kTRUE;
375}
376
377/*****************************************************************/
378
379Bool_t
380 Generator::decayParticles(o2::dataformats::MCEventHeader* header)
381{
382#ifdef GENERATORS_WITH_PYTHIA8
384 return kTRUE;
385 }
386 if (!mEventDecayer) {
387 mEventDecayer = new EventDecayer();
388 if (!mEventDecayer->init()) {
389 LOG(fatal) << "Could not initialise the EventDecayer of generator " << GetName();
390 return kFALSE;
391 }
392 mEventDecayerReport = new EventDecayerReport();
393 }
395 mEventDecayerReport->merge(mEventDecayer->decay(mParticles));
396 if (header) {
397 header->putInfo<std::string>("eventDecayer", mEventDecayer->configSummary());
398 header->putInfo<int>("eventDecayerSeed", mEventDecayer->seed());
399 }
400#endif
401 return kTRUE;
402}
403
404/*****************************************************************/
405
406Bool_t
408{
411 auto o2primGen = dynamic_cast<PrimaryGenerator*>(primGen);
412 if (!o2primGen) {
413 LOG(fatal) << "PrimaryGenerator is not a o2::eventgen::PrimaryGenerator";
414 return kFALSE;
415 }
416
418 for (const auto& particle : mParticles) {
419 o2primGen->AddTrack(particle.GetPdgCode(),
420 particle.Px() * mMomentumUnit,
421 particle.Py() * mMomentumUnit,
422 particle.Pz() * mMomentumUnit,
423 particle.Vx() * mPositionUnit,
424 particle.Vy() * mPositionUnit,
425 particle.Vz() * mPositionUnit,
426 particle.GetMother(0),
427 particle.GetMother(1),
428 particle.GetDaughter(0),
429 particle.GetDaughter(1),
430 particle.TestBit(ParticleStatus::kToBeDone),
431 particle.Energy() * mEnergyUnit,
432 particle.T() * mTimeUnit,
433 particle.GetWeight(),
434 (TMCProcess)particle.GetUniqueID(),
435 particle.GetStatusCode()); // generator status information passed as status code field
436 }
437
439 return kTRUE;
440}
441
442/*****************************************************************/
443
444Bool_t
446{
450 return kTRUE;
451}
452
453/*****************************************************************/
454
455Bool_t
457{
461 if (mTriggers.size() == 0 && mDeepTriggers.size() == 0) {
462 return kTRUE;
463 }
464
466 Bool_t triggered;
467 if (mTriggerMode == kTriggerOFF) {
468 return kTRUE;
469 } else if (mTriggerMode == kTriggerOR) {
470 triggered = kFALSE;
471 } else if (mTriggerMode == kTriggerAND) {
472 triggered = kTRUE;
473 } else {
474 return kTRUE;
475 }
476
478 for (const auto& trigger : mTriggers) {
479 auto retval = trigger(mParticles);
480 if (mTriggerMode == kTriggerOR) {
481 triggered |= retval;
482 }
483 if (mTriggerMode == kTriggerAND) {
484 triggered &= retval;
485 }
486 }
487
489 for (const auto& trigger : mDeepTriggers) {
490 auto retval = trigger(mInterface, mInterfaceName);
491 if (mTriggerMode == kTriggerOR) {
492 triggered |= retval;
493 }
494 if (mTriggerMode == kTriggerAND) {
495 triggered &= retval;
496 }
497 }
498
500 return triggered;
501}
502
503/*****************************************************************/
504
505void Generator::addSubGenerator(int subGeneratorId, std::string const& subGeneratorDescription)
506{
507 if (subGeneratorId < 0) {
508 LOG(fatal) << "Sub-generator IDs must be >= 0, instead, passed value is " << subGeneratorId;
509 }
510 mSubGeneratorsIdToDesc.insert({subGeneratorId, subGeneratorDescription});
511}
512
513/*****************************************************************/
514
515void Generator::updateSubGeneratorInformation(o2::dataformats::MCEventHeader* header) const
516{
517 if (mSubGeneratorId < 0) {
518 return;
519 }
520 header->putInfo<int>(o2::mcgenid::GeneratorProperty::SUBGENERATORID, mSubGeneratorId);
521 header->putInfo<std::unordered_map<int, std::string>>(o2::mcgenid::GeneratorProperty::SUBGENERATORDESCRIPTIONMAP, mSubGeneratorsIdToDesc);
522}
523
524/*****************************************************************/
525/*****************************************************************/
526
527} /* namespace eventgen */
528} /* namespace o2 */
529
std::ostringstream debug
std::vector< std::string > header
int32_t i
ClassImp(o2::eventgen::Generator)
@ kToBeDone
static BasicCCDBManager & instance()
static SimConfig & Instance()
Definition SimConfig.h:112
void setUnits(double positionUnit, double timeUnit, double momentumUnit=1., double energyUnit=1.)
Sets the caller's units in cm, s, GeV/c and GeV, as the unit setters of Generator (default cm,...
EventDecayerReport decay(std::vector< TParticle > &particles)
std::string configSummary() const
virtual void updateHeader(o2::dataformats::MCEventHeader *eventHeader)
Definition Generator.h:81
std::string mInterfaceName
Definition Generator.h:141
Bool_t ReadEvent(FairPrimaryGenerator *primGen) final
static std::atomic< int > InstanceCounter
Definition Generator.h:168
std::vector< Trigger > mTriggers
Definition Generator.h:145
std::function< void(std::vector< TParticle > const &p, int eventCount)> mTriggerOkHook
Definition Generator.h:150
virtual Bool_t generateEvent()=0
std::vector< DeepTrigger > mDeepTriggers
Definition Generator.h:146
std::function< void(std::vector< TParticle > const &p, int eventCount)> mTriggerFalseHook
Definition Generator.h:151
void addSubGenerator(int subGeneratorId, std::string const &subGeneratorDescription)
ETriggerMode_t mTriggerMode
Definition Generator.h:144
static bool isHyperloop()
Definition Generator.cxx:49
Bool_t addTracks(FairPrimaryGenerator *primGen)
std::vector< TParticle > mParticles
Definition Generator.h:161
virtual Bool_t importParticles()=0
Bool_t Init() override
void AddTrack(Int_t pdgid, Double_t px, Double_t py, Double_t pz, Double_t vx, Double_t vy, Double_t vz, Int_t mother1=-1, Int_t mother2=-1, Int_t daughter1=-1, Int_t daughter2=-1, Bool_t wanttracking=true, Double_t e=-9e9, Double_t tof=0., Double_t weight=0., TMCProcess proc=kPPrimary, Int_t generatorStatus=0)
static constexpr Property SUBGENERATORID
static constexpr Property SUBGENERATORDESCRIPTIONMAP
const GLdouble * v
Definition glcorearb.h:832
GLuint const GLchar * name
Definition glcorearb.h:781
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition glcorearb.h:1308
GLboolean enable
Definition glcorearb.h:3991
GLsizei const GLchar *const * path
Definition glcorearb.h:3591
long getCurrentTimestamp()
returns the timestamp in long corresponding to "now"
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
void merge(EventDecayerReport const &other)
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"