Project
Loading...
Searching...
No Matches
GeneratorHybrid.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#include <fairlogger/Logger.h>
14#include <algorithm>
15#include <tbb/concurrent_queue.h>
16#include <tbb/task_arena.h>
17#include <tbb/parallel_for.h>
19#include <filesystem>
20
21namespace o2
22{
23namespace eventgen
24{
25
26GeneratorHybrid& GeneratorHybrid::Instance(const std::string& inputgens)
27{
28 static GeneratorHybrid instance(inputgens);
29 return instance;
30}
31
32GeneratorHybrid::GeneratorHybrid(const std::string& inputgens)
33{
34 // This generator has trivial unit conversions
35 setTimeUnit(1.);
38 setEnergyUnit(1.);
39
40 if (!parseJSON(inputgens)) {
41 LOG(fatal) << "Failed to parse JSON configuration from input generators";
42 exit(1);
43 }
44 mRandomize = GeneratorHybridParam::Instance().randomize;
45 if (mConfigs.size() != mInputGens.size()) {
46 LOG(fatal) << "Number of configurations does not match the number of generators";
47 exit(1);
48 }
49 if (mConfigs.size() == 0) {
50 for (auto gen : mInputGens) {
51 mConfigs.push_back("");
52 }
53 }
54 int index = 0;
55 if (!(mRandomize || mGenerationMode == GenMode::kParallel)) {
56 if (mCocktailMode) {
57 if (mGroups.size() != mFractions.size()) {
58 LOG(fatal) << "Number of groups does not match the number of fractions";
59 return;
60 }
61 } else {
62 if (mFractions.size() != mInputGens.size()) {
63 LOG(fatal) << "Number of fractions does not match the number of generators";
64 return;
65 }
66 }
67 // Check if all elements of mFractions are 0
68 if (std::all_of(mFractions.begin(), mFractions.end(), [](int i) { return i == 0; })) {
69 LOG(fatal) << "All fractions provided are 0, no simulation will be performed";
70 return;
71 }
72 }
73 for (auto gen : mInputGens) {
74 // Search if the generator name is inside generatorNames (which is a vector of strings)
75 LOG(info) << "Checking if generator " << gen << " is in the list of available generators \n";
76 if (std::find(generatorNames.begin(), generatorNames.end(), gen) != generatorNames.end()) {
77 LOG(info) << "Found generator " << gen << " in the list of available generators \n";
78 if (gen.compare("boxgen") == 0) {
79 if (mConfigs[index].compare("") == 0) {
80 gens.push_back(std::make_shared<o2::eventgen::BoxGenerator>());
81 } else {
82 // Get the index of boxgen configuration
83 int confBoxIndex = std::stoi(mConfigs[index].substr(7));
84 gens.push_back(std::make_shared<o2::eventgen::BoxGenerator>(*mBoxGenConfigs[confBoxIndex]));
85 }
86 mGens.push_back(gen);
87 } else if (gen.compare(0, 7, "pythia8") == 0) {
88 // Check if mConfigs[index] contains pythia8_ and a number
89 if (mConfigs[index].compare("") == 0) {
90 auto pars = Pythia8GenConfig();
91 gens.push_back(std::make_shared<o2::eventgen::GeneratorPythia8>(pars));
92 } else {
93 // Get the index of pythia8 configuration
94 int confPythia8Index = std::stoi(mConfigs[index].substr(8));
95 gens.push_back(std::make_shared<o2::eventgen::GeneratorPythia8>(*mPythia8GenConfigs[confPythia8Index]));
96 }
97 mConfsPythia8.push_back(mConfigs[index]);
98 mGens.push_back(gen);
99 } else if (gen.compare("evtpool") == 0) {
100 int confEvtPoolIndex = std::stoi(mConfigs[index].substr(8));
101 gens.push_back(std::make_shared<o2::eventgen::GeneratorFromEventPool>(mEventPoolConfigs[confEvtPoolIndex]));
102 mGens.push_back(gen);
103 } else if (gen.compare("external") == 0) {
104 int confextIndex = std::stoi(mConfigs[index].substr(9));
105 // we need analyse the ini file to update the config key param
106 if (mExternalGenConfigs[confextIndex]->iniFile.size() > 0) {
107 LOG(info) << "Setting up external gen using the given INI file";
108
109 // this means that we go via the ConfigurableParam system ---> in order not to interfere with other
110 // generators we use an approach with backup and restore of the system
111
112 // we write the current state to a file
113 // create a tmp file name
114 std::string tmp_config_file = "configkey_tmp_backup_" + std::to_string(getpid()) + std::string(".ini");
116
117 auto expandedFileName = o2::utils::expandShellVarsInFileName(mExternalGenConfigs[confextIndex]->iniFile);
119 // toDo: check that this INI file makes sense
120
122 LOG(info) << "Setting up external generator with following parameters";
123 LOG(info) << params;
124 auto extgen_filename = params.fileName;
125 auto extgen_func = params.funcName;
126 auto extgen = std::shared_ptr<o2::eventgen::Generator>(o2::conf::GetFromMacro<o2::eventgen::Generator*>(extgen_filename, extgen_func, "FairGenerator*", "extgen"));
127 if (!extgen) {
128 LOG(fatal) << "Failed to retrieve \'extgen\': problem with configuration ";
129 }
130 // restore old state
132 // delete tmp file
133 std::filesystem::remove(tmp_config_file);
134
135 gens.push_back(std::move(extgen));
136 mGens.push_back(gen);
137 } else {
138 LOG(info) << "Setting up external gen using the given fileName and funcName";
139 // we need to restore the config key param system to what is was before
140 auto& extgen_filename = mExternalGenConfigs[confextIndex]->fileName;
141 auto& extgen_func = mExternalGenConfigs[confextIndex]->funcName;
142 auto extGen = std::shared_ptr<o2::eventgen::Generator>(o2::conf::GetFromMacro<o2::eventgen::Generator*>(extgen_filename, extgen_func, "FairGenerator*", "extgen"));
143 if (!extGen) {
144 LOG(fatal) << "Failed to load external generator from " << extgen_filename << " with function " << extgen_func;
145 }
146 gens.push_back(std::move(extGen));
147 mGens.push_back(gen);
148 }
149 } else if (gen.compare("hepmc") == 0) {
150 int confHepMCIndex = std::stoi(mConfigs[index].substr(6));
151 gens.push_back(std::make_shared<o2::eventgen::GeneratorHepMC>());
152 auto& globalConfig = o2::conf::SimConfig::Instance();
153 dynamic_cast<o2::eventgen::GeneratorHepMC*>(gens.back().get())->setup(*mFileOrCmdGenConfigs[confHepMCIndex], *mHepMCGenConfigs[confHepMCIndex], globalConfig);
154 mGens.push_back(gen);
155 }
156 } else {
157 LOG(fatal) << "Generator " << gen << " not found in the list of available generators \n";
158 exit(1);
159 }
160 index++;
161 }
162}
163
164GeneratorHybrid::~GeneratorHybrid()
165{
166 LOG(info) << "Destructor of generator hybrid called";
167 mStopFlag = true;
168}
169
171{
172 // init all sub-gens
173 int count = 0;
174 for (auto& gen : mGens) {
175 if (gen == "pythia8pp") {
176 auto config = std::string(std::getenv("O2_ROOT")) + "/share/Generators/egconfig/pythia8_inel.cfg";
177 LOG(info) << "Setting \'Pythia8\' base configuration: " << config << std::endl;
178 dynamic_cast<o2::eventgen::GeneratorPythia8*>(gens[count].get())->setConfig(config);
179 } else if (gen == "pythia8hf") {
180 auto config = std::string(std::getenv("O2_ROOT")) + "/share/Generators/egconfig/pythia8_hf.cfg";
181 LOG(info) << "Setting \'Pythia8\' base configuration: " << config << std::endl;
182 dynamic_cast<o2::eventgen::GeneratorPythia8*>(gens[count].get())->setConfig(config);
183 } else if (gen == "pythia8hi") {
184 auto config = std::string(std::getenv("O2_ROOT")) + "/share/Generators/egconfig/pythia8_hi.cfg";
185 LOG(info) << "Setting \'Pythia8\' base configuration: " << config << std::endl;
186 dynamic_cast<o2::eventgen::GeneratorPythia8*>(gens[count].get())->setConfig(config);
187 } else if (gen == "pythia8powheg") {
188 auto config = std::string(std::getenv("O2_ROOT")) + "/share/Generators/egconfig/pythia8_powheg.cfg";
189 LOG(info) << "Setting \'Pythia8\' base configuration: " << config << std::endl;
190 dynamic_cast<o2::eventgen::GeneratorPythia8*>(gens[count].get())->setConfig(config);
191 }
192 gens[count]->Init(); // TODO: move this to multi-threaded
194 if (mTriggerModes[count] != o2::eventgen::Generator::kTriggerOFF) {
195 gens[count]->setTriggerMode(mTriggerModes[count]);
196 LOG(info) << "Setting Trigger mode of generator " << gen << " to: " << mTriggerModes[count];
197 o2::eventgen::Trigger trigger = nullptr;
198 o2::eventgen::DeepTrigger deeptrigger = nullptr;
199 for (int trg = 0; trg < mTriggerMacros[count].size(); trg++) {
200 if (mTriggerMacros[count][trg].empty() || mTriggerFuncs[count][trg].empty()) {
201 continue;
202 }
203 std::string expandedMacro = o2::utils::expandShellVarsInFileName(mTriggerMacros[count][trg]);
204 LOG(info) << "Setting trigger " << trg << " of generator " << gen << " with following parameters";
205 LOG(info) << "Macro filename: " << expandedMacro;
206 LOG(info) << "Function name: " << mTriggerFuncs[count][trg];
207 trigger = o2::conf::GetFromMacro<o2::eventgen::Trigger>(expandedMacro, mTriggerFuncs[count][trg], "o2::eventgen::Trigger", "trigger");
208 if (!trigger) {
209 LOG(info) << "Trying to retrieve a \'o2::eventgen::DeepTrigger\' type";
210 deeptrigger = o2::conf::GetFromMacro<o2::eventgen::DeepTrigger>(expandedMacro, mTriggerFuncs[count][trg], "o2::eventgen::DeepTrigger", "deeptrigger");
211 }
212 if (!trigger && !deeptrigger) {
213 LOG(warn) << "Failed to retrieve \'external trigger\': problem with configuration";
214 LOG(warn) << "Trigger " << trg << " of generator " << gen << " will not be included";
215 continue;
216 } else {
217 LOG(info) << "Trigger " << trg << " of generator " << gen << " successfully set";
218 }
219 if (trigger) {
220 gens[count]->addTrigger(trigger);
221 } else {
222 gens[count]->addDeepTrigger(deeptrigger);
223 }
224 }
225 }
226 count++;
227 }
228 if (mRandomize) {
229 if (std::all_of(mFractions.begin(), mFractions.end(), [](int i) { return i == 1; })) {
230 LOG(info) << "Full randomisation of generators order";
231 } else {
232 LOG(info) << "Randomisation based on fractions";
233 int allfracs = 0;
234 for (auto& f : mFractions) {
235 allfracs += f;
236 }
237 // Assign new rng fractions
238 float sum = 0;
239 float chance = 0;
240 for (int k = 0; k < mFractions.size(); k++) {
241 if (mFractions[k] == 0) {
242 // Generator will not be used if fraction is 0
243 mRngFractions.push_back(-1);
244 LOG(info) << "Generator " << mGens[k] << " will not be used";
245 } else {
246 chance = static_cast<float>(mFractions[k]) / allfracs;
247 sum += chance;
248 mRngFractions.push_back(sum);
249 LOG(info) << "Generator " << (mConfigs[k] == "" ? mGens[k] : mConfigs[k]) << " has a " << chance * 100 << "% chance of being used";
250 }
251 }
252 }
253 } else {
254 LOG(info) << "Generators will be used in sequence, following provided fractions";
255 }
256
257 mGenIsInitialized.resize(gens.size(), false);
258 if (mGenerationMode == GenMode::kParallel) {
259 // in parallel mode we just use one queue --> collaboration
260 mResultQueue.resize(1);
261 } else {
262 // in sequential mode we have one queue per generator
263 mResultQueue.resize(gens.size());
264 }
265 // Create a task arena with a specified number of threads
266 mTaskArena.initialize(GeneratorHybridParam::Instance().num_workers);
267
268 // the process task function actually calls event generation
269 // when it is done it notifies the outside world by pushing it's index into an appropriate queue
270 // This should be a lambda, which can be given at TaskPool creation time
271 auto process_generator_task = [this](std::vector<std::shared_ptr<o2::eventgen::Generator>> const& generatorvec, int task) {
272 LOG(debug) << "Starting eventgen for task " << task;
273 auto& generator = generatorvec[task];
274 if (!mStopFlag) {
275 // TODO: activate this once we are use Init is threadsafe
276 // if (!mGenIsInitialized[task]) {
277 // if(!generator->Init()) {
278 // LOG(error) << "failed to init generator " << task;
279 // }
280 // mGenIsInitialized[task] = true;
281 // }
282 }
283 bool isTriggered = false;
284 while (!isTriggered) {
285 generator->clearParticles();
286 generator->generateEvent();
287 generator->importParticles();
288 isTriggered = generator->triggerEvent();
289 }
290 LOG(debug) << "eventgen finished for task " << task;
291 if (!mStopFlag) {
292 if (mGenerationMode == GenMode::kParallel) {
293 mResultQueue[0].push(task);
294 } else {
295 mResultQueue[task].push(task);
296 }
297 }
298 };
299
300 // fundamental tbb thread-worker function
301 auto worker_function = [this, process_generator_task]() {
302 // we increase the reference count in the generator pointers
303 // by making a copy of the vector. In this way we ensure that the lifetime
304 // of the generators is no shorter than the lifetime of the thread for this worker function
305 auto generators_copy = gens;
306
307 while (!mStopFlag) {
308 int task;
309 if (mInputTaskQueue.try_pop(task)) {
310 process_generator_task(generators_copy, task); // Process the task
311 } else {
312 std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Wait if no task
313 }
314 }
315 };
316
317 // let the TBB task system run in it's own thread
318 mTBBTaskPoolRunner = std::thread([this, worker_function]() { mTaskArena.execute([&]() { tbb::parallel_for(0, mTaskArena.max_concurrency(), [&](int) { worker_function(); }); }); });
319 mTBBTaskPoolRunner.detach(); // detaching because we don't like to wait on the thread to finish
320 // some of the generators might still be generating when we are done
321
322 // let's also push initial generation tasks for each event generator
323 for (size_t genindex = 0; genindex < gens.size(); ++genindex) {
324 mInputTaskQueue.push(genindex);
325 mTasksStarted++;
326 }
327 mIsInitialized = true;
328 return Generator::Init();
329}
330
332{
333 if (!mIsInitialized) {
334 Init();
335 }
336 if (mGenerationMode == GenMode::kParallel) {
337 mIndex = -1; // this means any index is welcome
338 notifySubGenerator(0); // we shouldn't distinguish the sub-gen ids
339 } else {
340 // Order randomisation or sequence of generators
341 // following provided fractions, if not generators are used in proper sequence
342 // Order randomisation or sequence of generators
343 // following provided fractions. If not available generators will be used sequentially
344 if (mRandomize) {
345 if (mRngFractions.size() != 0) {
346 // Generate number between 0 and 1
347 float rnum = gRandom->Rndm();
348 // Find generator index
349 for (int k = 0; k < mRngFractions.size(); k++) {
350 if (rnum <= mRngFractions[k]) {
351 mIndex = k;
352 break;
353 }
354 }
355 } else {
356 mIndex = gRandom->Integer(mFractions.size());
357 }
358 } else {
359 while (mFractions[mCurrentFraction] == 0 || mseqCounter == mFractions[mCurrentFraction]) {
360 if (mFractions[mCurrentFraction] != 0) {
361 mseqCounter = 0;
362 }
363 mCurrentFraction = (mCurrentFraction + 1) % mFractions.size();
364 }
365 mIndex = mCurrentFraction;
366 }
367 notifySubGenerator(mIndex);
368 }
369 return true;
370}
371
373{
374 int genIndex = -1;
375 std::vector<int> subGenIndex = {};
376 if (mIndex == -1) {
377 // this means parallel mode ---> we have a common queue
378 mResultQueue[0].pop(genIndex);
379 } else {
380 // need to pop from a particular queue
381 if (!mCocktailMode) {
382 mResultQueue[mIndex].pop(genIndex);
383 } else {
384 // in cocktail mode we need to pop from the group queue
385 subGenIndex.resize(mGroups[mIndex].size());
386 for (size_t pos = 0; pos < mGroups[mIndex].size(); ++pos) {
387 int subIndex = mGroups[mIndex][pos];
388 LOG(info) << "Getting generator " << mGens[subIndex] << " from cocktail group " << mIndex;
389 mResultQueue[subIndex].pop(subGenIndex[pos]);
390 }
391 }
392 }
393
394 auto unit_transformer = [](auto& p, auto pos_unit, auto time_unit, auto en_unit, auto mom_unit) {
395 p.SetMomentum(p.Px() * mom_unit, p.Py() * mom_unit, p.Pz() * mom_unit, p.Energy() * en_unit);
396 p.SetProductionVertex(p.Vx() * pos_unit, p.Vy() * pos_unit, p.Vz() * pos_unit, p.T() * time_unit);
397 };
398
399 auto index_transformer = [](auto& p, int offset) {
400 for (int i = 0; i < 2; ++i) {
401 if (p.GetMother(i) != -1) {
402 const auto newindex = p.GetMother(i) + offset;
403 p.SetMother(i, newindex);
404 }
405 }
406 if (p.GetNDaughters() > 0) {
407 for (int i = 0; i < 2; ++i) {
408 const auto newindex = p.GetDaughter(i) + offset;
409 p.SetDaughter(i, newindex);
410 }
411 }
412 };
413
414 // Clear particles and event header
415 mParticles.clear();
416 // event header of underlying generator must be fully reset
417 // this is important when using event pools where the full header information is forwarded from the generator
418 // otherwise some events might have mixed header information from different generators
419 mMCEventHeader.Reset();
420 if (mCocktailMode) {
421 // in cocktail mode we need to merge the particles from the different generators
422 for (auto subIndex : subGenIndex) {
423 LOG(info) << "Importing particles for task " << subIndex;
424 auto subParticles = gens[subIndex]->getParticles();
425
426 auto time_unit = gens[subIndex]->getTimeUnit();
427 auto pos_unit = gens[subIndex]->getPositionUnit();
428 auto mom_unit = gens[subIndex]->getMomentumUnit();
429 auto energy_unit = gens[subIndex]->getEnergyUnit();
430
431 // The particles carry mother and daughter indices, which are relative
432 // to the sub-generator. We need to adjust these indices to reflect that particles
433 // are now embedded into a cocktail.
434 auto offset = mParticles.size();
435 for (auto& p : subParticles) {
436 // apply the mother-daugher index transformation
437 index_transformer(p, offset);
438 // apply unit transformation of sub-generator
439 unit_transformer(p, pos_unit, time_unit, energy_unit, mom_unit);
440 }
441
442 mParticles.insert(mParticles.end(), subParticles.begin(), subParticles.end());
443 // first generator of the cocktail is used as reference to update the event header information
444 if (mHeaderGeneratorIndex == -1) {
445 gens[subIndex]->updateHeader(&mMCEventHeader);
446 mHeaderGeneratorIndex = subIndex; // store index of generator updating the header
447 }
448 mInputTaskQueue.push(subIndex);
449 mTasksStarted++;
450 }
451 } else {
452 LOG(info) << "Importing particles for task " << genIndex;
453 // at this moment the mIndex-th generator is ready to be used
454 mParticles = gens[genIndex]->getParticles();
455
456 auto time_unit = gens[genIndex]->getTimeUnit();
457 auto pos_unit = gens[genIndex]->getPositionUnit();
458 auto mom_unit = gens[genIndex]->getMomentumUnit();
459 auto energy_unit = gens[genIndex]->getEnergyUnit();
460
461 // transform units to units of the hybrid generator
462 for (auto& p : mParticles) {
463 // apply unit transformation
464 unit_transformer(p, pos_unit, time_unit, energy_unit, mom_unit);
465 }
466
467 // fetch the event Header information from the underlying generator
468 gens[genIndex]->updateHeader(&mMCEventHeader);
469 mHeaderGeneratorIndex = genIndex; // store index of generator updating the header
470 mInputTaskQueue.push(genIndex);
471 mTasksStarted++;
472 }
473
474 mseqCounter++;
475 mEventCounter++;
476 if (mEventCounter == getTotalNEvents()) {
477 LOG(info) << "HybridGen: Stopping TBB task pool";
478 mStopFlag = true;
479 }
480
481 return true;
482}
483
485{
486 if (eventHeader) {
487 // Overwrite current vertex information to the underlying generator header,
488 // otherwise the info will be dropped when copying the FairMCEventHeader part of the header
489 mMCEventHeader.SetVertex(eventHeader->GetX(), eventHeader->GetY(), eventHeader->GetZ());
490 mHeaderGeneratorIndex = -1; // reset header generator index for next event
491 // Forward the base class fields from FairMCEventHeader
492 static_cast<FairMCEventHeader&>(*eventHeader) = static_cast<FairMCEventHeader&>(mMCEventHeader);
493 // Copy the key-value store info
494 eventHeader->copyInfoFrom(mMCEventHeader);
495
496 // put additional information about
497 eventHeader->putInfo<std::string>("forwarding-generator", "HybridGen");
498 }
499}
500
501template <typename T>
503{
504 rapidjson::StringBuffer buffer;
505 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
506 value.Accept(writer);
507 return buffer.GetString();
508}
509
511{
512 std::string name = gen["name"].GetString();
513 mInputGens.push_back(name);
514 if (gen.HasMember("config")) {
515 if (name == "boxgen") {
516 const auto& boxconf = gen["config"];
517 auto boxConfig = TBufferJSON::FromJSON<o2::eventgen::BoxGenConfig>(jsonValueToString(boxconf).c_str());
518 mBoxGenConfigs.push_back(std::move(boxConfig));
519 mConfigs.push_back("boxgen_" + std::to_string(mBoxGenConfigs.size() - 1));
520 } else if (name == "pythia8") {
521 const auto& pythia8conf = gen["config"];
522 auto pythia8Config = TBufferJSON::FromJSON<o2::eventgen::Pythia8GenConfig>(jsonValueToString(pythia8conf).c_str());
523 mPythia8GenConfigs.push_back(std::move(pythia8Config));
524 mConfigs.push_back("pythia8_" + std::to_string(mPythia8GenConfigs.size() - 1));
525 } else if (name == "evtpool") {
526 const auto& o2kineconf = gen["config"];
527 auto poolConfig = TBufferJSON::FromJSON<o2::eventgen::EventPoolGenConfig>(jsonValueToString(o2kineconf).c_str());
528 mEventPoolConfigs.push_back(*poolConfig);
529 mConfigs.push_back("evtpool_" + std::to_string(mEventPoolConfigs.size() - 1));
530 } else if (name == "external") {
531 const auto& extconf = gen["config"];
532 auto extConfig = TBufferJSON::FromJSON<o2::eventgen::ExternalGenConfig>(jsonValueToString(extconf).c_str());
533 mExternalGenConfigs.push_back(std::move(extConfig));
534 mConfigs.push_back("external_" + std::to_string(mExternalGenConfigs.size() - 1));
535 } else if (name == "hepmc") {
536 const auto& genconf = gen["config"];
537 const auto& cmdconf = genconf["configcmd"];
538 const auto& hepmcconf = genconf["confighepmc"];
539 auto cmdConfig = TBufferJSON::FromJSON<o2::eventgen::FileOrCmdGenConfig>(jsonValueToString(cmdconf).c_str());
540 auto hepmcConfig = TBufferJSON::FromJSON<o2::eventgen::HepMCGenConfig>(jsonValueToString(hepmcconf).c_str());
541 mFileOrCmdGenConfigs.push_back(std::move(cmdConfig));
542 mHepMCGenConfigs.push_back(std::move(hepmcConfig));
543 mConfigs.push_back("hepmc_" + std::to_string(mFileOrCmdGenConfigs.size() - 1));
544 } else {
545 mConfigs.push_back("");
546 }
547 } else {
548 if (name == "boxgen" || name == "pythia8" || name == "external" || name == "hepmc") {
549 LOG(fatal) << "No configuration provided for generator " << name;
550 return false;
551 } else {
552 mConfigs.push_back("");
553 }
554 }
555 if (gen.HasMember("triggers")) {
556 const auto& trigger = gen["triggers"];
557 auto trigger_specs = [this, &trigger]() {
558 mTriggerMacros.push_back({});
559 mTriggerFuncs.push_back({});
560 if (trigger.HasMember("specs")) {
561 for (auto& spec : trigger["specs"].GetArray()) {
562 if (spec.HasMember("macro")) {
563 const auto& macro = spec["macro"].GetString();
564 if (!(strcmp(macro, "") == 0)) {
565 mTriggerMacros.back().push_back(macro);
566 } else {
567 mTriggerMacros.back().push_back("");
568 }
569 } else {
570 mTriggerMacros.back().push_back("");
571 }
572 if (spec.HasMember("function")) {
573 const auto& function = spec["function"].GetString();
574 if (!(strcmp(function, "") == 0)) {
575 mTriggerFuncs.back().push_back(function);
576 } else {
577 mTriggerFuncs.back().push_back("");
578 }
579 } else {
580 mTriggerFuncs.back().push_back("");
581 }
582 }
583 } else {
584 mTriggerMacros.back().push_back("");
585 mTriggerFuncs.back().push_back("");
586 }
587 };
588 if (trigger.HasMember("mode")) {
589 const auto& trmode = trigger["mode"].GetString();
590 if (strcmp(trmode, "or") == 0) {
591 mTriggerModes.push_back(o2::eventgen::Generator::kTriggerOR);
592 trigger_specs();
593 } else if (strcmp(trmode, "and") == 0) {
594 mTriggerModes.push_back(o2::eventgen::Generator::kTriggerAND);
595 trigger_specs();
596 } else if (strcmp(trmode, "off") == 0) {
597 mTriggerModes.push_back(o2::eventgen::Generator::kTriggerOFF);
598 mTriggerMacros.push_back({""});
599 mTriggerFuncs.push_back({""});
600 } else {
601 LOG(warn) << "Wrong trigger mode provided for generator " << name << ", keeping trigger OFF";
602 mTriggerModes.push_back(o2::eventgen::Generator::kTriggerOFF);
603 mTriggerMacros.push_back({""});
604 mTriggerFuncs.push_back({""});
605 }
606 } else {
607 LOG(warn) << "No trigger mode provided for generator " << name << ", turning trigger OFF";
608 mTriggerModes.push_back(o2::eventgen::Generator::kTriggerOFF);
609 mTriggerMacros.push_back({""});
610 mTriggerFuncs.push_back({""});
611 }
612 } else {
613 mTriggerModes.push_back(o2::eventgen::Generator::kTriggerOFF);
614 mTriggerMacros.push_back({""});
615 mTriggerFuncs.push_back({""});
616 }
617 return true;
618}
619
620Bool_t GeneratorHybrid::parseJSON(const std::string& path)
621{
622 auto expandedPath = o2::utils::expandShellVarsInFileName(path);
623 // Check if configuration file exists
624 if (gSystem->AccessPathName(expandedPath.c_str())) {
625 LOG(fatal) << "Configuration file " << expandedPath << " for hybrid generator does not exist";
626 return false;
627 }
628 // Parse JSON file to build map
629 std::ifstream fileStream(expandedPath, std::ios::in);
630 if (!fileStream.is_open()) {
631 LOG(error) << "Cannot open " << expandedPath;
632 return false;
633 }
634 rapidjson::IStreamWrapper isw(fileStream);
635 rapidjson::Document doc;
636 doc.ParseStream(isw);
637 if (doc.HasParseError()) {
638 LOG(error) << "Error parsing provided json file " << expandedPath;
639 LOG(error) << " - Error -> " << rapidjson::GetParseError_En(doc.GetParseError());
640 return false;
641 }
642
643 // check if there is a mode field
644 if (doc.HasMember("mode")) {
645 const auto& mode = doc["mode"].GetString();
646 if (strcmp(mode, "sequential") == 0) {
647 // events are generated in the order given by fractions or random weight
648 mGenerationMode = GenMode::kSeq;
649 }
650 if (strcmp(mode, "parallel") == 0) {
651 // events are generated fully in parallel and the order will be random
652 // this is mainly for event pool generation or mono-type generators
653 mGenerationMode = GenMode::kParallel;
654 LOG(info) << "Setting mode to parallel";
655 }
656 }
657
658 // Put the generator names in mInputGens
659 if (doc.HasMember("generators")) {
660 const auto& gens = doc["generators"];
661 for (const auto& gen : gens.GetArray()) {
662 mGroups.push_back({});
663 // Check if gen is an array (cocktail mode)
664 if (gen.HasMember("cocktail")) {
665 mCocktailMode = true;
666 for (const auto& subgen : gen["cocktail"].GetArray()) {
667 if (confSetter(subgen)) {
668 mGroups.back().push_back(mInputGens.size() - 1);
669 } else {
670 return false;
671 }
672 }
673 } else {
674 if (!confSetter(gen)) {
675 return false;
676 }
677 // Groups are created in case cocktail mode is activated, this way
678 // cocktails can be declared anywhere in the JSON file, without the need
679 // of grouping single generators. If no cocktail is defined
680 // groups will be ignored nonetheless.
681 mGroups.back().push_back(mInputGens.size() - 1);
682 }
683 }
684 }
685
686 // Get fractions and put them in mFractions
687 if (doc.HasMember("fractions")) {
688 const auto& fractions = doc["fractions"];
689 for (const auto& frac : fractions.GetArray()) {
690 mFractions.push_back(frac.GetInt());
691 }
692 } else {
693 // Set fractions to unity for all generators in case they are not provided
694 const auto& gens = doc["generators"];
695 for (const auto& gen : gens.GetArray()) {
696 mFractions.push_back(1);
697 }
698 }
699 return true;
700}
701
702} // namespace eventgen
703} // namespace o2
704
default_random_engine gen(dev())
std::ostringstream debug
int32_t i
ClassImp(o2::eventgen::GeneratorHybrid)
uint16_t pos
Definition RawData.h:3
static void writeINI(std::string const &filename, std::string const &keyOnly="")
static void updateFromFile(std::string const &, std::string const &paramsList="", bool unchangedOnly=false)
static SimConfig & Instance()
Definition SimConfig.h:111
void copyInfoFrom(MCEventHeader const &other)
inits info fields from another Event header
void putInfo(std::string const &key, T const &value)
GeneratorHybrid(const GeneratorHybrid &)=delete
Bool_t parseJSON(const std::string &path)
std::string jsonValueToString(const T &value)
void updateHeader(o2::dataformats::MCEventHeader *eventHeader) override
static GeneratorHybrid & Instance(const std::string &inputgens="")
Bool_t confSetter(const auto &gen)
void setPositionUnit(double val)
Definition Generator.h:87
void setEnergyUnit(double val)
Definition Generator.h:85
void notifySubGenerator(int subGeneratorId)
Definition Generator.h:123
static unsigned int getTotalNEvents()
Definition Generator.h:100
void setTimeUnit(double val)
Definition Generator.h:89
void addSubGenerator(int subGeneratorId, std::string const &subGeneratorDescription)
std::vector< TParticle > mParticles
Definition Generator.h:147
void setMomentumUnit(double val)
Definition Generator.h:83
Bool_t Init() override
float sum(float s, o2::dcs::DataPointValue v)
Definition dcs-ccdb.cxx:39
GLenum mode
Definition glcorearb.h:266
GLint GLsizei count
Definition glcorearb.h:399
GLuint buffer
Definition glcorearb.h:655
GLsizeiptr size
Definition glcorearb.h:659
GLuint index
Definition glcorearb.h:781
GLuint const GLchar * name
Definition glcorearb.h:781
GLdouble f
Definition glcorearb.h:310
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLenum const GLfloat * params
Definition glcorearb.h:272
GLintptr offset
Definition glcorearb.h:660
GLsizei const GLchar *const * path
Definition glcorearb.h:3591
std::function< bool(void *, std::string)> DeepTrigger
Definition Trigger.h:26
std::function< bool(const std::vector< TParticle > &)> Trigger
Definition Trigger.h:25
std::string expandShellVarsInFileName(std::string const &input)
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
void empty(int)
void compare(std::string_view s1, std::string_view s2)
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"