Project
Loading...
Searching...
No Matches
O2SimDeviceRunner.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
14#include "O2SimDevice.h"
15#include "SimSetup/SimSetup.h"
16#include <SimConfig/SimConfig.h>
18#include <TVirtualMC.h>
19#include <thread>
20#include <fairmq/DeviceRunner.h>
21#include <boost/program_options.hpp>
22#include <memory>
23#include <string>
24#include <fairmq/Channel.h>
25#include <mutex>
26#include <vector>
27#include <fairlogger/Logger.h>
28#include <fairmq/Parts.h>
29#include <fairmq/TransportFactory.h>
30#include <TStopwatch.h>
31#include <sys/wait.h>
32#include <pthread.h> // to set cpu affinity
33#include <cmath>
34#include <csignal>
35#include <unistd.h>
36#ifdef __linux__
37#include <sys/prctl.h>
38#endif
42#include <TROOT.h>
43#include <atomic>
44
45#include "rapidjson/document.h"
46#include "rapidjson/stringbuffer.h"
47#include "rapidjson/filereadstream.h"
49namespace bpo = boost::program_options;
50
51std::vector<int> gChildProcesses; // global vector of child pids
54
55// a handler for error/termination signals
56void sigaction_handler(int signal, siginfo_t* signal_info, void*)
57{
58 auto pid = getpid();
59 LOG(info) << pid << " caught signal " << signal << " from source " << signal_info->si_pid;
60 auto groupid = getpgrp();
61 if (pid == gMasterProcess && (signal_info->si_pid != gDriverProcess || signal == SIGTERM)) {
62 // master worker forwards signal to whole worker process group
63 // (always for SIGTERM, since a parent-death signal also carries the driver pid)
64 killpg(pid, signal);
65 } else {
66 if (signal_info->si_pid != gDriverProcess) {
67 // forward to master worker if coming internally
68 kill(groupid, signal);
69 }
70 }
71
72 if (signal_info->si_pid == gDriverProcess || signal == SIGTERM) {
73 // signal was sent from driver process --> not error
74 // or it was a standard SIGTERM
75
76 // shut down before waiting, so that the master worker finalises (e.g. writes its scoring dumps) before the driver's kill timer
78 // need to wait for potential children before exiting itself
79 // ... in order to have correct resource accounting
80 int status, cpid;
81 while ((cpid = wait(&status))) {
82 if (cpid == -1) {
83 break;
84 }
85 }
86 _exit(0);
87 }
88
89 // we treat internal signal interruption as an error
90 // because only ordinary termination is good in the context of the distributed system
91 _exit(128 + signal);
92}
93
94void addCustomOptions(bpo::options_description& options)
95{
96}
97
98void CustomCleanup(void* data, void* hint) { delete static_cast<std::string*>(hint); }
99
100// this will initialize the simulation setup
101// once before initializing the actual FairMQ device
102bool initializeSim(std::string transport, std::string address, std::unique_ptr<FairRunSim>& simptr)
103{
104 // This needs an already running PrimaryServer
105 auto factory = fair::mq::TransportFactory::CreateTransportFactory(transport);
106 auto channel = fair::mq::Channel{"o2sim-primserv-info", "req", factory};
107 channel.Connect(address);
108 channel.Validate();
109
110 return o2::devices::O2SimDevice::initSim(channel, simptr);
111}
112
114{
115 auto app = static_cast<o2::steer::O2MCApplication*>(TVirtualMCApplication::Instance());
116 auto vmc = TVirtualMC::GetMC();
117
118 if (app == nullptr) {
119 LOG(warning) << "no vmc application found at this stage";
120 }
121 return new o2::devices::O2SimDevice(app, vmc);
122}
123
124fair::mq::Device* getDevice(const fair::mq::ProgOptions& config)
125{
126 return getDevice();
127}
128
129int initAndRunDevice(int argc, char* argv[])
130{
131 using namespace fair::mq;
132 using namespace fair::mq::hooks;
133
134 try {
135 fair::mq::DeviceRunner runner{argc, argv};
136
137 runner.AddHook<SetCustomCmdLineOptions>([](DeviceRunner& r) {
138 boost::program_options::options_description customOptions("Custom options");
139 addCustomOptions(customOptions);
140 r.fConfig.AddToCmdLineOptions(customOptions);
141 });
142
143 runner.AddHook<InstantiateDevice>([](DeviceRunner& r) {
144 r.fDevice = std::unique_ptr<fair::mq::Device>{getDevice(r.fConfig)};
145 });
146
147 return runner.Run();
148 } catch (std::exception& e) {
149 LOG(error) << "Unhandled exception reached the top of main: " << e.what()
150 << ", application will now exit";
151 return 1;
152 } catch (...) {
153 LOG(error) << "Non-exception instance being thrown. Please make sure you use std::runtime_exception() instead. "
154 << "Application will now exit.";
155 return 1;
156 }
157}
158
161 fair::mq::Channel* primchannel = nullptr;
162 fair::mq::Channel* datachannel = nullptr;
163 fair::mq::Channel* primstatuschannel = nullptr;
164 int workerID = -1;
165};
166
167KernelSetup initSim(std::string transport, std::string primaddress, std::string primstatusaddress, std::string mergeraddress, int workerID)
168{
169 auto factory = fair::mq::TransportFactory::CreateTransportFactory(transport);
170 auto primchannel = new fair::mq::Channel{"primary-get", "req", factory};
171 primchannel->Connect(primaddress);
172 primchannel->Validate();
173
174 auto prim_status_channel = new fair::mq::Channel{"o2sim-primserv-info", "req", factory};
175 prim_status_channel->Connect(primstatusaddress);
176 prim_status_channel->Validate();
177
178 auto datachannel = new fair::mq::Channel{"simdata", "push", factory};
179 datachannel->Connect(mergeraddress);
180 datachannel->Validate();
181 datachannel->GetSocket().SetLinger(-1); // keep queued hits until the merger has taken them
182 // the channels are setup
183
184 // init the sim object
185 auto sim = getDevice();
186 sim->lateInit();
187
188 return KernelSetup{sim, primchannel, datachannel, prim_status_channel, workerID};
189}
190
192{
193 // the simplified runloop
194 while (setup.sim->Kernel(setup.workerID, *setup.primchannel, *setup.datachannel, setup.primstatuschannel)) {
195 }
196 doLogInfo(setup.workerID, "simulation is done");
198 return 0;
199}
200
201void pinToCPU(unsigned int cpuid)
202{
203 auto affinity = getenv("ALICE_CPUAFFINITY");
204 if (affinity) {
205 // MacOS does not support this API so we add a protection
206#ifndef __APPLE__
207
208 pthread_t thread;
209
210 thread = pthread_self();
211
212 cpu_set_t cpuset;
213 CPU_ZERO(&cpuset);
214 CPU_SET(cpuid, &cpuset);
215
216 auto s = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
217 if (s != 0) {
218 LOG(warning) << "FAILED TO SET PTHREAD AFFINITY";
219 }
220
221 /* Check the actual affinity mask assigned to the thread */
222 s = pthread_getaffinity_np(thread, sizeof(cpu_set_t), &cpuset);
223 if (s != 0) {
224 LOG(warning) << "FAILED TO GET PTHREAD AFFINITY";
225 }
226
227 for (int j = 0; j < CPU_SETSIZE; j++) {
228 if (CPU_ISSET(j, &cpuset)) {
229 LOG(info) << "ENABLED CPU " << j;
230 }
231 }
232#else
233 LOG(warn) << "CPU AFFINITY NOT IMPLEMENTED ON APPLE";
234#endif
235 }
236}
237
238bool waitForControlInput(int workerID)
239{
240 static bool initialized = false;
241 static fair::mq::Channel channel;
242 if (!initialized) {
243 // we do the channel connect and initialization only once
244 // (reducing the chances that we might miss a control message from the master)
245 static auto factory = fair::mq::TransportFactory::CreateTransportFactory("zeromq");
246 channel = fair::mq::Channel{"o2sim-control", "sub", factory};
247 auto controlsocketname = getenv("ALICE_O2SIMCONTROL");
248 channel.Connect(std::string(controlsocketname));
249 channel.Validate();
250 initialized = true;
251 }
252 std::unique_ptr<fair::mq::Message> reply(channel.NewMessage());
253
254 doLogInfo(workerID, "Listening for master control input");
255 if (channel.Receive(reply) > 0) {
256 auto data = reply->GetData();
257 auto size = reply->GetSize();
258
259 std::string command(reinterpret_cast<char const*>(data), size);
260 doLogInfo(workerID, "Received control message: " + command);
261
263 o2::conf::parseSimReconfigFromString(command, reconfig);
264 if (reconfig.stop) {
265 doLogInfo(workerID, "Stop asked, shutting down");
266 return false;
267 }
268 doLogInfo(workerID, "Asked to process " + std::to_string(reconfig.nEvents) + std::string(" new events"));
269 } else {
270 doLogInfo(workerID, "No control input received ");
271 }
272 return true;
273}
274
275// Multithreaded Geant4: one process whose Geant4 worker threads each ask the primary server for sub-events
276// and send their hits to the merger, as the forked worker processes do otherwise.
277std::atomic<bool> gServerDone{false};
278std::atomic<int> gNextWorkerID{0};
279std::atomic<int> gChunkRequests{0}; // requests for work in the current run
280
281// The channels of the transport threads, kept here so that they can be closed before the process exits
283 std::shared_ptr<fair::mq::TransportFactory> factory;
284 std::mutex setupMutex; // serialises the channel creation in the shared factory
285 std::mutex requestMutex; // one request channel for all threads (with one per thread, only one got work)
286 std::unique_ptr<fair::mq::Channel> request;
287 std::vector<std::unique_ptr<fair::mq::Channel>> data; // one per thread
288};
290
291fair::mq::Channel* makeWorkerChannel(std::unique_ptr<fair::mq::Channel>& owner, char const* name, char const* type, std::string const& address)
292{
293 std::lock_guard<std::mutex> lock(gMTChannels.setupMutex);
294 owner = std::make_unique<fair::mq::Channel>(name, type, gMTChannels.factory);
295 if (!owner->Connect(address) || !owner->Validate()) {
296 LOG(fatal) << "Could not connect channel " << name << " of a transport thread to " << address;
297 }
298 return owner.get();
299}
300
301// Closes the channels of the transport threads; with linger -1 this waits until the merger has taken all queued hits
303{
304 auto& hooks = o2::base::MTWorkerHooks::instance();
305 hooks.fetchChunk = nullptr;
306 hooks.dataChannel = nullptr;
307 gMTChannels.request.reset();
308 gMTChannels.data.clear();
309 gMTChannels.factory.reset();
310}
311
312// The Geant4 worker threads already exist when this is called; they use these hooks from the first real ProcessRun.
313void installMTWorkerHooks(std::string const& serveraddress, std::string const& mergeraddress)
314{
315 gMTChannels.factory = fair::mq::TransportFactory::CreateTransportFactory("zeromq");
316 auto& hooks = o2::base::MTWorkerHooks::instance();
317 hooks.fetchChunk = [serveraddress]() -> std::unique_ptr<o2::data::PrimaryChunk> {
318 thread_local int workerID = gNextWorkerID++;
320 std::lock_guard<std::mutex> lock(gMTChannels.requestMutex);
321 if (!gMTChannels.request) {
322 makeWorkerChannel(gMTChannels.request, "primary-get", "req", serveraddress);
323 }
325 while (true) {
327 if (fetched.status == Status::Work) {
328 return std::move(fetched.chunk);
329 }
330 if (fetched.status == Status::Stop) {
331 gServerDone = true;
332 return nullptr;
333 }
334 // Retry and EndOfWork: ask again, as a worker process does; the server answers as idle once done
335 }
336 };
337 hooks.dataChannel = [mergeraddress]() -> fair::mq::Channel* {
338 thread_local fair::mq::Channel* channel = nullptr;
339 if (!channel) {
340 std::unique_ptr<fair::mq::Channel> owner;
341 channel = makeWorkerChannel(owner, "simdata", "push", mergeraddress);
342 channel->GetSocket().SetLinger(-1); // keep queued hits until the merger has taken them
343 std::lock_guard<std::mutex> lock(gMTChannels.setupMutex);
344 gMTChannels.data.push_back(std::move(owner));
345 }
346 return channel;
347 };
348}
349
350[[noreturn]] void runMT(std::string const& serveraddress, std::string const& mergeraddress, fair::mq::Channel& pubchannel)
351{
352 installMTWorkerHooks(serveraddress, mergeraddress);
353 auto sim = getDevice();
354 sim->lateInit();
355 auto pushchannel = o2::simpubsub::createPUBChannel(o2::simpubsub::getPublishAddress("o2sim-workerinternal", gDriverProcess), "push");
356 std::thread([&pubchannel]() {
357 auto collectorchannel = o2::simpubsub::createPUBChannel(o2::simpubsub::getPublishAddress("o2sim-workerinternal", gDriverProcess), "pull");
358 std::unique_ptr<fair::mq::Message> msg(collectorchannel.NewMessage());
359 while (true) {
360 if (collectorchannel.Receive(msg) > 0) {
361 std::string text(reinterpret_cast<char const*>(msg->GetData()), msg->GetSize());
362 o2::simpubsub::publishMessage(pubchannel, text);
363 }
364 }
365 }).detach();
366 o2::simpubsub::publishMessage(pushchannel, o2::simpubsub::simStatusString("WORKER0", "STATUS", "SETUP COMPLETED"));
367
368 // a run ends once every thread was told there is no more work; it is repeated if the events ran out first
369 constexpr int kEventBlock = 100000;
370 auto vmc = TVirtualMC::GetMC();
371 if (auto& beforeTransport = o2::base::MTWorkerHooks::instance().beforeTransport) {
372 beforeTransport();
373 }
374 auto& conf = o2::conf::SimConfig::Instance();
375 bool more = true;
376 while (more) {
377 gServerDone = false;
378 while (!gServerDone) {
379 gChunkRequests = 0;
380 vmc->ProcessRun(kEventBlock);
381 if (gChunkRequests == 0) {
382 LOG(fatal) << "No transport thread asked for work in this run; Geant4 did not start it";
383 }
384 }
385 doLogInfo(0, "simulation is done");
386 if (conf.asService()) {
387 LOG(info) << "IN SERVICE MODE WAITING";
388 o2::simpubsub::publishMessage(pushchannel, o2::simpubsub::simStatusString("WORKER0", "STATUS", "AWAITING INPUT"));
389 more = waitForControlInput(0);
390 usleep(100);
391 } else {
392 o2::simpubsub::publishMessage(pushchannel, o2::simpubsub::simStatusString("WORKER0", "STATUS", "TERMINATING"));
393 LOG(info) << "FINISHING";
394 more = false;
395 }
396 }
399 _exit(0);
400}
401
402int main(int argc, char* argv[])
403{
404 // Geant4 is loaded by dlopen and may need a static TLS reserve, which glibc only takes at program start
406 struct sigaction act;
407 memset(&act, 0, sizeof act);
408 sigemptyset(&act.sa_mask);
409 act.sa_sigaction = &sigaction_handler;
410 act.sa_flags = SA_SIGINFO; // <--- enable sigaction
411
412 std::vector<int> handledsignals = {SIGTERM, SIGINT, SIGQUIT, SIGSEGV, SIGBUS, SIGFPE, SIGABRT}; // <--- may need to be completed
413 // remember that SIGKILL can't be handled
414 for (auto s : handledsignals) {
415 if (sigaction(s, &act, nullptr)) {
416 LOG(error) << "Could not install signal handler for " << s;
417 exit(EXIT_FAILURE);
418 }
419 }
420
421 // set the fatal callback for the logger to not do a core dump (since this might interfere with process shutdown sequence
422 // since it calls ROOT::TSystem and further child processes)
423 fair::Logger::OnFatal([] { throw fair::FatalException("Fatal error occured. Exiting without core dump..."); });
424 // initialy set logger verbosity to medium
426
427 // extract the path to FairMQ config
428 bpo::options_description desc{"Options"};
429 // clang-format off
430 desc.add_options()
431 ("control","control type")
432 ("id","ID")
433 ("config-key","config key")
434 ("mq-config",bpo::value<std::string>(),"path to FairMQ config")
435 ("severity","log severity");
436 // clang-format on
437 bpo::variables_map vm;
438 bpo::store(parse_command_line(argc, argv, desc), vm);
439 bpo::notify(vm);
440
441 std::string FMQconfig;
442 if (vm.count("mq-config")) {
443 FMQconfig = vm["mq-config"].as<std::string>();
444 }
445
446 auto internalfork = getenv("ALICE_SIMFORKINTERNAL");
447 if (internalfork) {
448 int driverPID = getppid();
449 auto pubchannel = o2::simpubsub::createPUBChannel(o2::simpubsub::getPublishAddress("o2sim-worker-notifications", driverPID));
450
451 if (FMQconfig.empty()) {
452 throw std::runtime_error("This should never be called without FairMQ config.");
453 }
454 // read the JSON config
455 FILE* fp = fopen(FMQconfig.c_str(), "r");
456 constexpr unsigned short usmax = std::numeric_limits<unsigned short>::max() - 1;
457 char readBuffer[usmax];
458 rapidjson::FileReadStream is(fp, readBuffer, sizeof(readBuffer));
459 rapidjson::Document d;
460 d.ParseStream(is);
461 fclose(fp);
462
463 // retrieve correct server and merger URLs
464 std::string serveraddress;
465 std::string mergeraddress;
466 std::string serverstatus_address;
467 std::string s;
468
469 auto& options = d["fairMQOptions"];
470 assert(options.IsObject());
471 for (auto option = options.MemberBegin(); option != options.MemberEnd(); ++option) {
472 s = option->name.GetString();
473 if (s == "devices") {
474 assert(option->value.IsArray());
475 auto devices = option->value.GetArray();
476 for (auto& device : devices) {
477 s = device["id"].GetString();
478 if (s == "primary-server") {
479 auto channels = device["channels"].GetArray();
480 auto sockets = (channels[0])["sockets"].GetArray();
481 auto address = (sockets[0])["address"].GetString();
482 serveraddress = address;
483 sockets = (channels[1])["sockets"].GetArray();
484 address = (sockets[0])["address"].GetString();
485 serverstatus_address = address;
486 }
487 if (s == "hitmerger") {
488 auto channels = device["channels"].GetArray();
489 for (auto& channel : channels) {
490 s = channel["name"].GetString();
491 if (s == "simdata") {
492 auto sockets = channel["sockets"].GetArray();
493 auto address = (sockets[0])["address"].GetString();
494 mergeraddress = address;
495 }
496 }
497 }
498 }
499 }
500 }
501
502 LOG(info) << "Parsed primary server address " << serveraddress;
503 LOG(info) << "Parsed primary server status address " << serverstatus_address;
504 LOG(info) << "Parsed merger address " << mergeraddress;
505 if (serveraddress.empty() || mergeraddress.empty()) {
506 throw std::runtime_error("Could not determine server or merger URLs.");
507 }
508 // This is a solution based on initializing the simulation once
509 // and then fork the process to share the simulation memory across
510 // many processes. Here we are not using fair::mq::Devices and just setup
511 // some channels manually and do our own runloop.
512
513 // in multithreaded mode, Geant4 starts its worker threads while the simulation is initialised
514 const bool mtMode = getenv("ALICE_O2SIM_MT") != nullptr;
515 if (mtMode) {
516 ROOT::EnableThreadSafety();
517 }
518
519 // we init the simulation first
520 std::unique_ptr<FairRunSim> simrun;
521 // TODO: take the addresses from somewhere else
522 if (!initializeSim("zeromq", serverstatus_address, simrun)) {
523 LOG(error) << "Could not initialize simulation";
524 return 1;
525 }
526
527 if (mtMode) {
528 gMasterProcess = getpid();
529 gDriverProcess = getppid();
530 runMT(serveraddress, mergeraddress, pubchannel);
531 }
532
533 // should be factored out?
534 unsigned int nworkers = std::max(1u, std::thread::hardware_concurrency() / 2);
535 auto f = getenv("ALICE_NSIMWORKERS");
536 if (f) {
537 nworkers = static_cast<unsigned int>(std::stoi(f));
538 }
539 LOG(info) << "Running with " << nworkers << " sim workers ";
540
541 gMasterProcess = getpid();
542 gDriverProcess = getppid();
543 // then we fork and create a device in each fork
544 for (auto i = 0u; i < nworkers; ++i) {
545 // we use the current process as one of the workers as it has nothing else to do
546 auto pid = (i == nworkers - 1) ? 0 : fork();
547 if (pid == 0) {
548#ifdef __linux__
549 // terminate if our parent dies, so a worker never outlives the o2-sim process tree
550 prctl(PR_SET_PDEATHSIG, SIGTERM);
551 if (getppid() != ((i == nworkers - 1) ? driverPID : gMasterProcess)) {
552 _exit(1);
553 }
554#endif
555 // Each worker can publish its progress/state on a ZMQ channel.
556 // We actually use a push/pull mechanism to collect all messages in the
557 // master worker which can then publish using PUB/SUB.
558 // auto factory = fair::mq::TransportFactory::CreateTransportFactory("zeromq");
559 auto collectAndPubThreadFunction = [driverPID, &pubchannel]() {
560 auto collectorchannel = o2::simpubsub::createPUBChannel(o2::simpubsub::getPublishAddress("o2sim-workerinternal", driverPID), "pull");
561 std::unique_ptr<fair::mq::Message> msg(collectorchannel.NewMessage());
562
563 while (true) {
564 if (collectorchannel.Receive(msg) > 0) {
565 auto data = msg->GetData();
566 auto size = msg->GetSize();
567 std::string text(reinterpret_cast<char const*>(data), size);
568 // LOG(info) << "Collector message: " << text;
569 o2::simpubsub::publishMessage(pubchannel, text);
570 }
571 }
572 };
573 if (i == nworkers - 1) { // <---- extremely important to take non-forked version since ZMQ sockets do not behave well on fork
574 std::vector<std::thread> threads;
575 threads.push_back(std::thread(collectAndPubThreadFunction));
576 threads.back().detach();
577 }
578
579 // everyone else is getting a push socket for notifications
580 auto pushchannel = std::make_unique<fair::mq::Channel>(o2::simpubsub::createPUBChannel(o2::simpubsub::getPublishAddress("o2sim-workerinternal", driverPID), "push"));
581
582 // we will try to pin each worker to a particular CPU
583 // this can be made configurable via environment variables??
584 pinToCPU(i);
585
586 auto kernelSetup = initSim("zeromq", serveraddress, serverstatus_address, mergeraddress, i);
587
588 std::stringstream worker;
589 worker << "WORKER" << i;
590 o2::simpubsub::publishMessage(*pushchannel, o2::simpubsub::simStatusString(worker.str(), "STATUS", "SETUP COMPLETED"));
591
592 auto& conf = o2::conf::SimConfig::Instance();
593
594 bool more = true;
595 while (more) {
596 runSim(kernelSetup);
597
598 if (conf.asService()) {
599 LOG(info) << "IN SERVICE MODE WAITING";
600 o2::simpubsub::publishMessage(*pushchannel, o2::simpubsub::simStatusString(worker.str(), "STATUS", "AWAITING INPUT"));
601 more = waitForControlInput(kernelSetup.workerID);
602 usleep(100); // --> why? (probably to give the server some chance to come to a "serving" state)
603 } else {
604 o2::simpubsub::publishMessage(*pushchannel, o2::simpubsub::simStatusString(worker.str(), "STATUS", "TERMINATING"));
605
606 LOG(info) << "FINISHING";
607 more = false;
608 }
609 }
610 // Close the worker transport; this blocks until the merger has taken all queued hits
611 pushchannel.reset();
612 delete kernelSetup.primchannel;
613 delete kernelSetup.primstatuschannel;
614 delete kernelSetup.datachannel;
615
616 if (i == nworkers - 1) {
617 // the master reaps its forked workers
618 int status, cpid;
619 while ((cpid = wait(&status)) != -1) {
620 }
621 }
622
623 // destruct setup (using _exit due to problems in ROOT shutdown (segmentation violations)
624 // Clearly at some moment, a more robust solution would be appreciated
625 _exit(0);
626 } else {
627 gChildProcesses.push_back(pid);
628 }
629 }
630 } else {
631 // This the solution where we setup an ordinary fair::mq::Device
632 // (each if which will setup its own simulation). Parallelism
633 // is achieved outside by instantiating multiple device processes.
634 _exit(initAndRunDevice(argc, argv));
635 }
636}
int32_t i
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error s
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char e
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char Directory with events to Events directory is and not inside events d
configStandalone Reconstruction rec_tpc multiplier for the combined cluster track error during track following area in cm for the search of for z only used if decrease removal by when above this local occupancy rowx Shift of TPC revert cluster flag edge bit distance to edge exceeds this ZOffset for secondary multiple of radius max P of electron K BB bands crossing max P of electron p BB bands crossing max P of electron d BB bands crossing max P of electron t BB bands crossing min accepted dEdxMaxTPC of the track Additive extra cluster error for Y2 if edge flag set Additive extra cluster error for Y2 if single set Additive extra cluster error for Y2 if or single set Additive extra cluster error for Z2 if or single set Extra errors to add to track when crossing depending on addErrorsCECrossing Inner portion of y range in sector that is not used in searching for extrapolated track candidates factor2YS for track merging factor2K for track merging Margin in cm of Y position when rejecting edge clusters based on uncorrected track Y Max fraction of shared clusters for tracklet Except pad from cluster finding if total number of charges in a fragment is above this Except pad from cluster finding if number of consecutive charges in a fragment is above this Threshold where a timebin is considered disabling the noisy pad check for that pad Treat signal above this minimum as saturated Smoothing factor for the exponential Highly Ionising Particle tail filter Number of timebins to use for the averaging Add additional custom track errors when crossing relative as fraction of Do N fit passes in final fit of< 0:reject at max -n hits") AddOptionRTC(rejectIFCLowRadiusCluster, uint8_t, 1, "", 0, "Reject clusters that get the IFC mask error during refit") AddOptionRTC(dEdxTruncLow, uint8_t, 2, "", 0, "Low truncation threshold, fraction of 128") AddOptionRTC(dEdxTruncHigh, uint8_t, 77, "", 0, "High truncation threshold, fraction of 128") AddOptionRTC(extrapolationTracking, int8_t, 1, "", 0, "Enable Extrapolation Tracking(prolong tracks to adjacent sectors to find short segments)") AddOptionRTC(disableRefitAttachment, uint8_t, 0, "", 0, "Bitmask to disable certain attachment steps during refit(1:attachment, 2:propagation, 4:loop following, 8:mirroring)") AddOptionRTC(rejectionStrategy, uint8_t, o2::gpu::GPUSettings::RejectionStrategyA, "", 0, "Enable rejection of TPC clusters for compression(0=no, 1=strategy A, 2=strategy B)") AddOptionRTC(mergeLoopersAfterburner, uint8_t, 1, "", 0, "Run afterburner for additional looper merging") AddOptionRTC(compressionTypeMask, uint8_t, o2::gpu::GPUSettings::CompressionFull, "", 0, "TPC Compression mode bits(1=truncate charge/width LSB, 2=differences, 4=track-model)") AddOptionRTC(compressionSortOrder, uint8_t, o2::gpu::GPUSettings::SortTime, "", 0, "Sort order of TPC compression(0=time, 1=pad, 2=Z-time-pad, 3=Z-pad-time, 4=no sorting(use incoming order))") AddOptionRTC(sigBitsCharge, uint8_t, 4, "", 0, "Number of significant bits for TPC cluster charge in compression mode 1") AddOptionRTC(sigBitsWidth, uint8_t, 3, "", 0, "Number of significant bits for TPC cluster width in compression mode 1") AddOptionRTC(dropLoopers, uint8_t, 0, "", 0, "Drop looping tracks starting from second loop") AddOptionRTC(mergerInterpolateErrors, uint8_t, 1, "", 0, "Use interpolation instead of extrapolation for chi2 based cluster rejection") AddOptionRTC(mergerInterpolateRejectAlsoOnCurrentPosition, uint8_t, 1, "", 0, "When using mergerInterpolateErrors, reject based on chi2 twice computed with interpolated and current track position starting from NDF > mergerNonInterpolateRejectMinNDF Merge tracks accross the central electrode Enable PID response Add sub threshold clusters in TPC dEdx computation OR mask of TPC flags that will reject the cluster in alternative dEdx Reject edge clusters based on uncorrected track Y during track fit Increase decrease removal by when on this number of pads from the edge h configStandalone Reconstruction rec_trd Max chi2 for TRD tracklets to be matched to a track Chi2 cut for strict matching mode Number of sigmas for ITS TPC track time error estimate Addition to search road around track prolongation along Y in cm Optimal tracklet rphi resolution in maximum chi2 per attached tracklet for TRD tracks take into account the full likelihood of the slope including exponential tails Tracks with less attached tracklets are discarded after the tracking Post trigger Pile up integration time in BCs fits all iterations but the last one in projections Use material lookup table for TPC refit Apply workarounds to avoid cyclic merge should not be needed tpc dyn configStandalone Processing proc_rtc Replace constant variables static by constexpr expressions Compile RTC in deterministic with NO_FAST_MATH flags and GPUCA_DETERMINISTIC_MODE define Use RTC to optimize GPU code h configStandalone Processing proc_rtctech Use a file lock to serialize access to the cache folder Print launch bounds used for RTC code as debugging for exit after Allow RTC with slave GPUReconstruction instances with optConstexpr and optSpecialcode Maximum number of RTC cache files per backend in the cache the oldest are Prepend RTC compilation commands by this string Load a parameter object containing the launch bounds from a file h configStandalone Processing proc_param TPC Cluster Z Error Parameterization h configStandalone Processing settings for neural network proc_nn std::string Specify inference device(cpu(default), rocm, cuda)") AddOption(nnInferenceDeviceId
configStandalone Reconstruction rec_tpc multiplier for the combined cluster track error during track following area in cm for the search of for z only used if decrease removal by when above this local occupancy rowx Shift of TPC revert cluster flag edge bit distance to edge exceeds this ZOffset for secondary multiple of radius max P of electron K BB bands crossing max P of electron p BB bands crossing max P of electron d BB bands crossing max P of electron t BB bands crossing min accepted dEdxMaxTPC of the track Additive extra cluster error for Y2 if edge flag set Additive extra cluster error for Y2 if single set Additive extra cluster error for Y2 if or single set Additive extra cluster error for Z2 if or single set Extra errors to add to track when crossing depending on addErrorsCECrossing Inner portion of y range in sector that is not used in searching for extrapolated track candidates factor2YS for track merging factor2K for track merging Margin in cm of Y position when rejecting edge clusters based on uncorrected track Y Max fraction of shared clusters for tracklet Except pad from cluster finding if total number of charges in a fragment is above this Except pad from cluster finding if number of consecutive charges in a fragment is above this Threshold where a timebin is considered disabling the noisy pad check for that pad Treat signal above this minimum as saturated Smoothing factor for the exponential Highly Ionising Particle tail filter Number of timebins to use for the averaging Add additional custom track errors when crossing relative as fraction of Do N fit passes in final fit of< 0:reject at max -n hits") AddOptionRTC(rejectIFCLowRadiusCluster, uint8_t, 1, "", 0, "Reject clusters that get the IFC mask error during refit") AddOptionRTC(dEdxTruncLow, uint8_t, 2, "", 0, "Low truncation threshold, fraction of 128") AddOptionRTC(dEdxTruncHigh, uint8_t, 77, "", 0, "High truncation threshold, fraction of 128") AddOptionRTC(extrapolationTracking, int8_t, 1, "", 0, "Enable Extrapolation Tracking(prolong tracks to adjacent sectors to find short segments)") AddOptionRTC(disableRefitAttachment, uint8_t, 0, "", 0, "Bitmask to disable certain attachment steps during refit(1:attachment, 2:propagation, 4:loop following, 8:mirroring)") AddOptionRTC(rejectionStrategy, uint8_t, o2::gpu::GPUSettings::RejectionStrategyA, "", 0, "Enable rejection of TPC clusters for compression(0=no, 1=strategy A, 2=strategy B)") AddOptionRTC(mergeLoopersAfterburner, uint8_t, 1, "", 0, "Run afterburner for additional looper merging") AddOptionRTC(compressionTypeMask, uint8_t, o2::gpu::GPUSettings::CompressionFull, "", 0, "TPC Compression mode bits(1=truncate charge/width LSB, 2=differences, 4=track-model)") AddOptionRTC(compressionSortOrder, uint8_t, o2::gpu::GPUSettings::SortTime, "", 0, "Sort order of TPC compression(0=time, 1=pad, 2=Z-time-pad, 3=Z-pad-time, 4=no sorting(use incoming order))") AddOptionRTC(sigBitsCharge, uint8_t, 4, "", 0, "Number of significant bits for TPC cluster charge in compression mode 1") AddOptionRTC(sigBitsWidth, uint8_t, 3, "", 0, "Number of significant bits for TPC cluster width in compression mode 1") AddOptionRTC(dropLoopers, uint8_t, 0, "", 0, "Drop looping tracks starting from second loop") AddOptionRTC(mergerInterpolateErrors, uint8_t, 1, "", 0, "Use interpolation instead of extrapolation for chi2 based cluster rejection") AddOptionRTC(mergerInterpolateRejectAlsoOnCurrentPosition, uint8_t, 1, "", 0, "When using mergerInterpolateErrors, reject based on chi2 twice computed with interpolated and current track position starting from NDF > mergerNonInterpolateRejectMinNDF Merge tracks accross the central electrode Enable PID response Add sub threshold clusters in TPC dEdx computation OR mask of TPC flags that will reject the cluster in alternative dEdx Reject edge clusters based on uncorrected track Y during track fit Increase decrease removal by when on this number of pads from the edge h configStandalone Reconstruction rec_trd Max chi2 for TRD tracklets to be matched to a track Chi2 cut for strict matching mode Number of sigmas for ITS TPC track time error estimate Addition to search road around track prolongation along Y in cm Optimal tracklet rphi resolution in maximum chi2 per attached tracklet for TRD tracks take into account the full likelihood of the slope including exponential tails Tracks with less attached tracklets are discarded after the tracking Post trigger Pile up integration time in BCs fits all iterations but the last one in projections Use material lookup table for TPC refit Apply workarounds to avoid cyclic merge should not be needed tpc dyn configStandalone Processing proc_rtc Replace constant variables static by constexpr expressions Compile RTC in deterministic with NO_FAST_MATH flags and GPUCA_DETERMINISTIC_MODE define Use RTC to optimize GPU code h configStandalone Processing proc_rtctech Use a file lock to serialize access to the cache folder Print launch bounds used for RTC code as debugging option
char error[CURL_ERROR_SIZE]
std::vector< int > gChildProcesses
void sigaction_handler(int signal, siginfo_t *signal_info, void *)
KernelSetup initSim(std::string transport, std::string primaddress, std::string primstatusaddress, std::string mergeraddress, int workerID)
int gMasterProcess
void pinToCPU(unsigned int cpuid)
void CustomCleanup(void *data, void *hint)
fair::mq::Channel * makeWorkerChannel(std::unique_ptr< fair::mq::Channel > &owner, char const *name, char const *type, std::string const &address)
bool initializeSim(std::string transport, std::string address, std::unique_ptr< FairRunSim > &simptr)
bool waitForControlInput(int workerID)
MTChannels gMTChannels
void installMTWorkerHooks(std::string const &serveraddress, std::string const &mergeraddress)
std::atomic< int > gChunkRequests
int initAndRunDevice(int argc, char *argv[])
int runSim(KernelSetup setup)
o2::devices::O2SimDevice * getDevice()
void runMT(std::string const &serveraddress, std::string const &mergeraddress, fair::mq::Channel &pubchannel)
std::atomic< bool > gServerDone
void addCustomOptions(bpo::options_description &options)
int gDriverProcess
std::atomic< int > gNextWorkerID
void closeMTChannels()
void doLogInfo(int workerID, std::string const &message)
uint32_t j
Definition RawData.h:0
uint16_t pid
Definition RawData.h:2
void SetLogVerbosityLevel(const char *verbosity)
Definition FairLogger.h:87
static FairLogger * GetLogger()
static SimConfig & Instance()
Definition SimConfig.h:112
static bool initSim(fair::mq::Channel &channel, std::unique_ptr< FairRunSim > &simptr)
bool Kernel(int workerID, fair::mq::Channel &requestchannel, fair::mq::Channel &dataoutchannel, fair::mq::Channel *statuschannel=nullptr)
static FetchedChunk fetchChunk(int workerID, fair::mq::Channel &requestchannel)
requests one chunk of primaries from the server on the given channel
ChunkStatus
outcome of asking the primary server for work
Definition O2SimDevice.h:65
GLuint GLuint64EXT address
Definition glcorearb.h:5846
GLsizeiptr size
Definition glcorearb.h:659
GLuint const GLchar * name
Definition glcorearb.h:781
GLdouble f
Definition glcorearb.h:310
GLint GLint GLsizei GLint GLenum GLenum type
Definition glcorearb.h:275
GLboolean r
Definition glcorearb.h:1233
bool parseSimReconfigFromString(std::string const &argumentstring, SimReconfigData &config)
std::string simStatusString(std::string const &origin, std::string const &topic, std::string const &message)
std::string getPublishAddress(std::string const &base, int pid=getpid())
bool publishMessage(fair::mq::Channel &channel, std::string const &message)
fair::mq::Channel createPUBChannel(std::string const &address, std::string const &type="pub")
void ensureStaticTLSReserve(char *argv[], unsigned long reserve=Geant4StaticTLSReserve)
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
fair::mq::Channel * datachannel
o2::devices::O2SimDevice * sim
fair::mq::Channel * primstatuschannel
fair::mq::Channel * primchannel
std::mutex requestMutex
std::unique_ptr< fair::mq::Channel > request
std::shared_ptr< fair::mq::TransportFactory > factory
std::vector< std::unique_ptr< fair::mq::Channel > > data
std::mutex setupMutex
static void shutdown()
Definition SimSetup.cxx:72
static MTWorkerHooks & instance()
TODO: Make this a base class of SimConfigData?
Definition SimConfig.h:205
#define main
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::vector< ChannelData > channels
uint64_t const void const *restrict const msg
Definition x9.h:153