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