Project
Loading...
Searching...
No Matches
GeneratorFactory.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
16#include "FairGenerator.h"
18#include <fairlogger/Logger.h>
19#include <SimConfig/SimConfig.h>
20#include <SimConfig/G4Params.h>
24#ifdef GENERATORS_WITH_PYTHIA8
27#endif
31#ifdef GENERATORS_WITH_HEPMC3
34#endif
35#if defined(GENERATORS_WITH_PYTHIA8) && defined(GENERATORS_WITH_HEPMC3)
38#include <rapidjson/document.h>
39#include <rapidjson/writer.h>
40#include <rapidjson/ostreamwrapper.h>
41#include <fstream>
42#include <unistd.h>
43#endif
50
51#include "TRandom.h"
52
53namespace o2
54{
55namespace eventgen
56{
57
58#if defined(GENERATORS_WITH_PYTHIA8) && defined(GENERATORS_WITH_HEPMC3)
59// Builds a parallel GeneratorHybrid JSON configuration out of
60// 8 (Generator::NHyperloopParallelGenerators) identical "external" sub-generator entries,
61// using the configured GeneratorExternalParam. This makes the simulation parallelisation automatic
62// on Hyperloop.
63// To-do: Define the behaviour with ini configuration which are already definining a hybrid gen
64//
65// Note on GeneratorHybrid.num_workers: expanding to 8 sub-generators does not by itself raise
66// the number of TBB workers that actually run them in parallel. This is controlled by
67// the parameter GeneratorHybrid.num_workers. On Hyperloop this will be part
68// of the standard train configuration
69std::string buildHyperloopExternalHybridConfig(GeneratorExternalParam const& extparams)
70{
71 rapidjson::Document doc;
72 doc.SetObject();
73 auto& alloc = doc.GetAllocator();
74 doc.AddMember("mode", "parallel", alloc);
75
76 rapidjson::Value generators(rapidjson::kArrayType);
77 rapidjson::Value fractions(rapidjson::kArrayType);
78 for (int i = 0; i < Generator::NHyperloopParallelGenerators; ++i) {
79 rapidjson::Value config(rapidjson::kObjectType);
80 config.AddMember("fileName", rapidjson::Value(extparams.fileName.c_str(), alloc), alloc);
81 config.AddMember("funcName", rapidjson::Value(extparams.funcName.c_str(), alloc), alloc);
82 config.AddMember("iniFile", "", alloc);
83
84 rapidjson::Value generator(rapidjson::kObjectType);
85 generator.AddMember("name", "external", alloc);
86 generator.AddMember("config", config, alloc);
87 generators.PushBack(generator, alloc);
88 fractions.PushBack(1, alloc);
89 }
90 doc.AddMember("generators", generators, alloc);
91 doc.AddMember("fractions", fractions, alloc);
92
93 // The generated config file is deliberatelly left on disk for possible inspection after the process ran.
94 std::string path = "hyperloop_exttohybrid_" + std::to_string(getpid()) + ".json";
95 std::ofstream ofs(path);
96 if (!ofs.is_open()) {
97 LOG(fatal) << "Failed to open " << path << " for writing the Hyperloop hybrid generator configuration";
98 exit(1);
99 }
100 rapidjson::OStreamWrapper osw(ofs);
101 rapidjson::Writer<rapidjson::OStreamWrapper> writer(osw);
102 doc.Accept(writer);
103 return path;
104}
105#endif
106
107// reusable helper class
108// main purpose is to init a FairPrimGen given some (Sim)Config
110{
111 if (!primGen) {
112 LOG(warning) << "No primary generator instance; Cannot setup";
113 return;
114 }
115
116 auto primGenO2 = dynamic_cast<PrimaryGenerator*>(primGen);
117
118 auto makeBoxGen = [](int pdgid, int mult, double etamin, double etamax, double pmin, double pmax, double phimin, double phimax) {
119 return new o2::eventgen::BoxGenerator(pdgid, mult, etamin, etamax, pmin, pmax, phimin, phimax);
120 };
121
122#ifdef GENERATORS_WITH_PYTHIA8
123 auto makePythia8Gen = [](std::string& config) {
124 auto& singleton = GeneratorPythia8Param::Instance();
126 .config = config.size() > 0 ? config : singleton.config,
127 .hooksFileName = singleton.hooksFileName,
128 .hooksFuncName = singleton.hooksFuncName,
129 .includePartonEvent = singleton.includePartonEvent,
130 .particleFilter = singleton.particleFilter,
131 .verbose = singleton.verbose,
132 };
133 auto gen = new o2::eventgen::GeneratorPythia8(pars);
134 if (!config.empty()) {
135 LOG(info) << "Setting \'Pythia8\' base configuration: " << config << std::endl;
136 gen->setConfig(config); // assign config; will be executed in Init function
137 }
138 return gen;
139 };
140#endif
141
144 // Monopole configurable mass, set before the particles get registered
145 const double monopoleMass = o2::conf::G4Params::Instance().monopoleMass;
147 auto* pdgDB = o2::O2DatabasePDG::Instance(); //--> ALICE particles are added once in this call
148 if (!o2::O2DatabasePDG::hasMonopoleMass(pdgDB, monopoleMass)) {
149 LOG(fatal) << "Monopoles were registered in TDatabasePDG with a mass other than G4.monopoleMass = " << monopoleMass << " GeV";
150 }
151 auto genconfig = conf.getGenerator();
152#if defined(GENERATORS_WITH_PYTHIA8) && defined(GENERATORS_WITH_HEPMC3)
153 std::string hyperloopExtHybridConfigFile; // set when IS_HYPERLOOP is defined
154 if (genconfig.compare("external") == 0 || genconfig.compare("extgen") == 0) {
155 if (GeneratorHybridParam::Instance().switchExtToHybrid) {
156 LOG(info) << "Switching external generator to hybrid mode";
157 genconfig = "hybrid";
158 } else if (Generator::isHyperloop()) {
159 // Running under Hyperloop: transparently expand the single external generator
160 // configuration into a parallel hybrid of Generator::NHyperloopParallelGenerators
161 // clones, to increase on-the-fly MC-generation throughput.
162 LOG(info) << "IS_HYPERLOOP detected: expanding external generator into "
163 << Generator::NHyperloopParallelGenerators << " parallel hybrid sub-generators";
164 hyperloopExtHybridConfigFile = buildHyperloopExternalHybridConfig(GeneratorExternalParam::Instance());
165 genconfig = "hybrid";
166 }
167 }
168#endif
169 LOG(info) << "** Generator to use: '" << genconfig << "'";
170 if (genconfig.compare("boxgen") == 0) {
171 // a simple "box" generator configurable via BoxGunparam
172 auto& boxparam = BoxGunParam::Instance();
173 LOG(info) << "Init generic box generator with following parameters";
174 LOG(info) << boxparam;
175 auto boxGen = makeBoxGen(boxparam.pdg, boxparam.number, boxparam.eta[0], boxparam.eta[1], boxparam.prange[0], boxparam.prange[1], boxparam.phirange[0], boxparam.phirange[1]);
176 primGen->AddGenerator(boxGen);
177 } else if (genconfig.compare("fwmugen") == 0) {
178 // a simple "box" generator for forward muons
179 LOG(info) << "Init box forward muons generator";
180 auto boxGen = makeBoxGen(13, 1, -4, -2.5, 50., 50., 0., 360);
181 primGen->AddGenerator(boxGen);
182 } else if (genconfig.compare("hmpidgun") == 0) {
183 // a simple "box" generator for forward muons
184 LOG(info) << "Init hmpid gun generator";
185 auto boxGen = makeBoxGen(-211, 100, -0.5, -0.5, 2, 5, -5, 60);
186 primGen->AddGenerator(boxGen);
187 } else if (genconfig.compare("fwpigen") == 0) {
188 // a simple "box" generator for forward pions
189 LOG(info) << "Init box forward pions generator";
190 auto boxGen = makeBoxGen(-211, 10, -4, -2.5, 7, 7, 0, 360);
191 primGen->AddGenerator(boxGen);
192 } else if (genconfig.compare("fwrootino") == 0) {
193 // a simple "box" generator for forward rootinos
194 LOG(info) << "Init box forward rootinos generator";
195 auto boxGen = makeBoxGen(0, 1, -4, -2.5, 1, 5, 0, 360);
196 primGen->AddGenerator(boxGen);
197 } else if (genconfig.compare("zdcgen") == 0) {
198 // a simple "box" generator for forward neutrons
199 LOG(info) << "Init box forward/backward zdc generator";
200 auto boxGenC = makeBoxGen(2112 /*neutrons*/, 1, -8, -9999, 500, 1000, 0., 360.);
201 auto boxGenA = makeBoxGen(2112 /*neutrons*/, 1, 8, 9999, 500, 1000, 0., 360.);
202 primGen->AddGenerator(boxGenC);
203 primGen->AddGenerator(boxGenA);
204 } else if (genconfig.compare("emcgenele") == 0) {
205 // box generator with one electron per event
206 LOG(info) << "Init box generator for electrons in EMCAL";
207 // using phi range of emcal
208 auto elecgen = makeBoxGen(11, 1, -0.67, 0.67, 15, 15, 80, 187);
209 primGen->AddGenerator(elecgen);
210 } else if (genconfig.compare("emcgenphoton") == 0) {
211 LOG(info) << "Init box generator for photons in EMCAL";
212 auto photongen = makeBoxGen(22, 1, -0.67, 0.67, 15, 15, 80, 187);
213 primGen->AddGenerator(photongen);
214 } else if (genconfig.compare("fddgen") == 0) {
215 LOG(info) << "Init box FDD generator";
216 auto boxGenFDC = makeBoxGen(13, 1000, -7, -4.8, 10, 500, 0, 360.);
217 auto boxGenFDA = makeBoxGen(13, 1000, 4.9, 6.3, 10, 500, 0., 360);
218 primGen->AddGenerator(boxGenFDA);
219 primGen->AddGenerator(boxGenFDC);
220 } else if (genconfig.compare("extkin") == 0) {
221 // external kinematics
222 // needs precense of a kinematics file "Kinematics.root"
223 // TODO: make this configurable and check for presence
224 auto extGen = new o2::eventgen::GeneratorFromFile(conf.getExtKinematicsFileName().c_str());
225 extGen->SetStartEvent(conf.getStartEvent());
226 primGen->AddGenerator(extGen);
227 LOG(info) << "using external kinematics";
228 } else if (genconfig.compare("extkinO2") == 0) {
229 // external kinematics from previous O2 output
230 auto& singleton = GeneratorFromO2KineParam::Instance();
231 auto name1 = singleton.fileName;
232 auto name2 = conf.getExtKinematicsFileName();
233 auto pars = O2KineGenConfig{
234 .skipNonTrackable = singleton.skipNonTrackable,
235 .continueMode = singleton.continueMode,
236 .roundRobin = singleton.roundRobin,
237 .randomize = singleton.randomize,
238 .rngseed = singleton.rngseed,
239 .randomphi = singleton.randomphi,
240 .fileName = name1.size() > 0 ? name1.c_str() : name2.c_str()};
241 auto extGen = new o2::eventgen::GeneratorFromO2Kine(pars);
242 extGen->SetStartEvent(conf.getStartEvent());
243 primGen->AddGenerator(extGen);
244 if (pars.continueMode) {
245 auto o2PrimGen = dynamic_cast<o2::eventgen::PrimaryGenerator*>(primGen);
246 if (o2PrimGen) {
247 o2PrimGen->setApplyVertex(false);
248 }
249 }
250 LOG(info) << "using external O2 kinematics";
251 } else if (genconfig.compare("evtpool") == 0) {
252 // case of an "event-pool" which is a specialization of extkinO2
253 // with some additional logic in file management and less configurability
254 // and not features such as "continue transport"
256 primGen->AddGenerator(extGen);
257 LOG(info) << "using the eventpool generator";
258 } else if (genconfig.compare("tparticle") == 0) {
259 // External ROOT file(s) with tree of TParticle in clones array,
260 // or external program generating such a file
261 auto& param0 = GeneratorFileOrCmdParam::Instance();
263 LOG(info) << "Init 'GeneratorTParticle' with the following parameters";
264 LOG(info) << param0;
265 LOG(info) << param;
266 auto tgen = new o2::eventgen::GeneratorTParticle();
267 tgen->setup(param0, param, conf);
268 primGen->AddGenerator(tgen);
269#ifdef GENERATORS_WITH_HEPMC3
270 } else if (genconfig.compare("hepmc") == 0) {
271 // external HepMC file, or external program writing HepMC event
272 // records to standard output.
273 auto& param0 = GeneratorFileOrCmdParam::Instance();
275 LOG(info) << "Init \'GeneratorHepMC\' with following parameters";
276 LOG(info) << param0;
277 LOG(info) << param;
278 auto hepmcGen = new o2::eventgen::GeneratorHepMC();
279 hepmcGen->setup(param0, param, conf);
280 primGen->AddGenerator(hepmcGen);
281#endif
282#ifdef GENERATORS_WITH_PYTHIA8
283 } else if (genconfig.compare("alldets") == 0) {
284 // a simple generator for test purposes - making sure to generate hits
285 // in all detectors
286 // I compose it of:
287 // 1) pythia8
288 auto py8config = std::string(std::getenv("O2_ROOT")) + "/share/Generators/egconfig/pythia8_inel.cfg";
289 auto py8 = makePythia8Gen(py8config);
290 primGen->AddGenerator(py8);
291 // 2) forward muons
292 auto muon = makeBoxGen(13, 100, -2.5, -4.0, 100, 100, 0., 360);
293 primGen->AddGenerator(muon);
294 } else if (genconfig.compare("pythia8") == 0) {
295 auto py8config = std::string();
296 auto py8 = makePythia8Gen(py8config);
297 primGen->AddGenerator(py8);
298 } else if (genconfig.compare("pythia8pp") == 0) {
299 auto py8config = std::string(std::getenv("O2_ROOT")) + "/share/Generators/egconfig/pythia8_inel.cfg";
300 auto py8 = makePythia8Gen(py8config);
301 primGen->AddGenerator(py8);
302 } else if (genconfig.compare("pythia8hf") == 0) {
303 // pythia8 pp (HF production)
304 // configures pythia for HF production in pp collisions at 14 TeV
305 auto py8config = std::string(std::getenv("O2_ROOT")) + "/share/Generators/egconfig/pythia8_hf.cfg";
306 auto py8 = makePythia8Gen(py8config);
307 primGen->AddGenerator(py8);
308 } else if (genconfig.compare("pythia8hi") == 0) {
309 // pythia8 heavy-ion
310 // exploits pythia8 heavy-ion machinery (available from v8.230)
311 // configures pythia for min.bias Pb-Pb collisions at 5.52 TeV
312 auto py8config = std::string(std::getenv("O2_ROOT")) + "/share/Generators/egconfig/pythia8_hi.cfg";
313 auto py8 = makePythia8Gen(py8config);
314 primGen->AddGenerator(py8);
315 } else if (genconfig.compare("pythia8powheg") == 0) {
316 // pythia8 with powheg
317 auto py8config = std::string(std::getenv("O2_ROOT")) + "/share/Generators/egconfig/pythia8_powheg.cfg";
318 auto py8 = makePythia8Gen(py8config);
319 primGen->AddGenerator(py8);
320#endif
321 } else if (genconfig.compare("external") == 0 || genconfig.compare("extgen") == 0) {
322 // external generator via configuration macro
324 LOG(info) << "Setting up external generator with following parameters";
325 LOG(info) << params;
326 auto extgen_filename = params.fileName;
327 auto extgen_func = params.funcName;
328 auto extgen = o2::conf::GetFromMacro<FairGenerator*>(extgen_filename, extgen_func, "FairGenerator*", "extgen");
329 if (!extgen) {
330 LOG(fatal) << "Failed to retrieve \'extgen\': problem with configuration ";
331 }
332 primGen->AddGenerator(extgen);
333 } else if (genconfig.compare("toftest") == 0) { // 1 muon per sector and per module
334 LOG(info) << "Init tof test generator -> 1 muon per sector and per module";
335 for (int i = 0; i < 18; i++) {
336 for (int j = 0; j < 5; j++) {
337 auto boxGen = makeBoxGen(13 /*muons*/, 1, -0.8 + 0.32 * j + 0.15, -0.8 + 0.32 * j + 0.17, 9, 10, 10 + 20. * i - 1, 10 + 20. * i + 1);
338 primGen->AddGenerator(boxGen);
339 }
340 }
341#if defined(GENERATORS_WITH_PYTHIA8) && defined(GENERATORS_WITH_HEPMC3)
342 } else if (genconfig.compare("hybrid") == 0) { // hybrid using multiple generators
343 LOG(info) << "Init hybrid generator";
344 std::string config = !hyperloopExtHybridConfigFile.empty() ? hyperloopExtHybridConfigFile : GeneratorHybridParam::Instance().configFile;
345 // check if config string points to an existing and not empty file
346 if (config.empty()) {
347 LOG(fatal) << "No configuration file provided for hybrid generator";
348 return;
349 }
350 auto& hybrid = o2::eventgen::GeneratorHybrid::Instance(config);
351 primGen->AddGenerator(&hybrid);
352#endif
353 } else {
354 LOG(fatal) << "Invalid generator";
355 }
356
358 // to be set via GeneratorFactory only if generator is not hybrid
359 // external settings via JSON are supported in the latter
360
361 Trigger trigger = nullptr;
362 DeepTrigger deeptrigger = nullptr;
363 if (!(genconfig.compare("hybrid") == 0)) {
364 auto trgconfig = conf.getTrigger();
365 if (trgconfig.empty()) {
366 return;
367 } else if (trgconfig.compare("particle") == 0) {
369 } else if (trgconfig.compare("external") == 0) {
370 // external trigger via configuration macro
372 LOG(info) << "Setting up external trigger with following parameters";
373 LOG(info) << params;
374 auto external_trigger_filename = params.fileName;
375 auto external_trigger_func = params.funcName;
376 trigger = o2::conf::GetFromMacro<o2::eventgen::Trigger>(external_trigger_filename, external_trigger_func, "o2::eventgen::Trigger", "trigger");
377 if (!trigger) {
378 LOG(info) << "Trying to retrieve a \'o2::eventgen::DeepTrigger\' type" << std::endl;
379 deeptrigger = o2::conf::GetFromMacro<o2::eventgen::DeepTrigger>(external_trigger_filename, external_trigger_func, "o2::eventgen::DeepTrigger", "deeptrigger");
380 }
381 if (!trigger && !deeptrigger) {
382 LOG(fatal) << "Failed to retrieve \'external trigger\': problem with configuration ";
383 }
384 } else {
385 LOG(fatal) << "Invalid trigger";
386 }
387
389 auto generators = primGen->GetListOfGenerators();
390 for (int igen = 0; igen < generators->GetEntries(); ++igen) {
391 auto generator = dynamic_cast<o2::eventgen::Generator*>(generators->At(igen));
392 if (!generator) {
393 LOG(fatal) << "request to add a trigger to an unsupported generator";
394 return;
395 }
397 if (trigger) {
398 generator->addTrigger(trigger);
399 }
400 if (deeptrigger) {
401 generator->addDeepTrigger(deeptrigger);
402 }
403 }
404 }
405}
406
407} // end namespace eventgen
408} // end namespace o2
default_random_engine gen(dev())
int32_t i
uint32_t j
Definition RawData.h:0
void AddGenerator(FairGenerator *generator)
static TDatabasePDG * Instance()
static bool hasMonopoleMass(TDatabasePDG *db, double monopoleMass)
static void setMonopoleMass(double monopoleMass)
std::string getExtKinematicsFileName() const
Definition SimConfig.h:159
unsigned int getStartEvent() const
Definition SimConfig.h:161
std::string getTrigger() const
Definition SimConfig.h:156
std::string getGenerator() const
Definition SimConfig.h:155
static GeneratorHybrid & Instance(const std::string &inputgens="")
void setTriggerMode(ETriggerMode_t val)
Definition Generator.h:94
static constexpr int NHyperloopParallelGenerators
Definition Generator.h:110
static bool isHyperloop()
Definition Generator.cxx:49
GLenum const GLfloat * params
Definition glcorearb.h:272
GLsizei const GLchar *const * path
Definition glcorearb.h:3591
GLenum GLfloat param
Definition glcorearb.h:271
std::function< bool(void *, std::string)> DeepTrigger
Definition Trigger.h:26
std::function< bool(const std::vector< TParticle > &)> Trigger
Definition Trigger.h:25
Trigger TriggerParticle(const TriggerParticleParam &param)
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
static void setPrimaryGenerator(o2::conf::SimConfig const &, FairPrimaryGenerator *)
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"