15#include <fairmq/TransportFactory.h>
16#include <fairmq/Channel.h>
17#include <fairmq/Message.h>
32#include "TStopwatch.h"
33#include <fairlogger/Logger.h>
42#include <unordered_map>
56 std::stringstream
str;
57 str << conf.getOutPrefix() <<
"_serverlog";
64 std::stringstream
str;
65 str << conf.getOutPrefix() <<
"_workerlog";
72 std::stringstream
str;
73 str << conf.getOutPrefix() <<
"_mergerlog";
81 std::stringstream searchstr;
82 searchstr <<
"o2sim-.*-" << getpid() <<
"$";
85 for (
auto& fn : filenames) {
87 std::filesystem::remove(std::filesystem::path(fn));
89 LOG(warn) <<
"Couldn't remove tmp file " << fn;
97 if (conf.forwardKine()) {
98 auto factory = fair::mq::TransportFactory::CreateTransportFactory(
"zeromq");
99 auto forwardchannel = fair::mq::Channel{
"kineforward",
"pair", factory};
101 forwardchannel.Bind(
address.c_str());
102 forwardchannel.Validate();
103 fair::mq::Parts parts;
104 fair::mq::MessagePtr payload(forwardchannel.NewMessage());
107 auto channelAlloc = o2::pmr::getTransportAllocator(forwardchannel.Transport());
109 parts.AddPart(std::move(
header));
110 parts.AddPart(std::move(payload));
111 int timeoutinMS = 1000;
112 if (forwardchannel.Send(parts, timeoutinMS) > 0) {
113 LOGP(info,
"SENDING END-OF-STREAM TO PROXY AT {}",
address.c_str());
115 LOGP(warn,
"SENDING END-OF-STREAM TIMED OUT; PEER PROBABLY NO LONGER CONNECTED");
123 if (getenv(
"ALICE_O2SIM_DUMPLOG")) {
124 std::cerr <<
"------------- START OF EVENTSERVER LOG ----------" << std::endl;
125 std::stringstream catcommand1;
127 if (system(catcommand1.str().c_str()) != 0) {
128 LOG(warn) <<
"error executing system call";
131 std::cerr <<
"------------- START OF SIM WORKER(S) LOG --------" << std::endl;
132 std::stringstream catcommand2;
134 if (system(catcommand2.str().c_str()) != 0) {
135 LOG(warn) <<
"error executing system call";
138 std::cerr <<
"------------- START OF MERGER LOG ---------------" << std::endl;
139 std::stringstream catcommand3;
141 if (system(catcommand3.str().c_str()) != 0) {
142 LOG(warn) <<
"error executing system call";
154 if (!conf.writeToDisc()) {
161 LOG(warn) <<
"Kinematics file corrupted or does not exist";
164 auto tr =
static_cast<TTree*
>(
f.Get(
"o2sim"));
168 if (!conf.isFilterOutNoHitEvents()) {
169 if (tr->GetEntries() != conf.getNEvents()) {
170 LOG(warn) <<
"There are fewer events in the output than asked";
190 std::stringstream controlsocketname;
191 controlsocketname <<
"ipc:///tmp/o2sim-control-" << getpid();
192 return controlsocketname.str();
198 std::hash<std::string> hasher;
199 std::stringstream
str;
200 str <<
"o2sim-internal_" << getpid();
202 std::stringstream controlsocketname;
203 controlsocketname <<
"ipc:///tmp/" << tmp.substr(0, 10) <<
"_" << getpid();
204 return controlsocketname.str();
219 static std::vector<std::thread> threads;
222 LOG(info) <<
"Control address is: " << controladdress;
223 setenv(
"ALICE_O2SIMCONTROL", internalcontroladdress.c_str(), 1);
225 auto lambda = [controladdress, internalcontroladdress]() {
226 auto factory = fair::mq::TransportFactory::CreateTransportFactory(
"zeromq");
229 auto internalchannel = fair::mq::Channel{
"o2sim-internal",
"pub", factory};
230 internalchannel.Bind(internalcontroladdress);
231 internalchannel.Validate();
232 std::unique_ptr<fair::mq::Message>
message(internalchannel.NewMessage());
235 auto outsidechannel = fair::mq::Channel{
"o2sim-control",
"rep", factory};
236 outsidechannel.Bind(controladdress);
237 outsidechannel.Validate();
238 std::unique_ptr<fair::mq::Message> request(outsidechannel.NewMessage());
240 bool keepgoing =
true;
242 outsidechannel.Init();
243 outsidechannel.Bind(controladdress);
244 outsidechannel.Validate();
245 if (outsidechannel.Receive(request) > 0) {
246 std::string command(
reinterpret_cast<char const*
>(request->GetData()), request->GetSize());
247 LOG(info) <<
"Control message: " << command <<
" received ";
251 std::unique_ptr<fair::mq::Message> reply(outsidechannel.NewSimpleMessage(code));
252 outsidechannel.Send(reply);
259 LOG(warn) <<
"CONTROL REQUEST COULD NOT BE PARSED";
262 std::unique_ptr<fair::mq::Message> reply(outsidechannel.NewSimpleMessage(code));
263 outsidechannel.Send(reply);
270 internalchannel.Send(request);
271 keepgoing = !reconfig.
stop;
277 threads.push_back(std::thread(lambda));
278 threads.back().detach();
285 static std::vector<std::thread> threads;
287 auto factory = fair::mq::TransportFactory::CreateTransportFactory(
"zeromq");
289 auto listenchannel = fair::mq::Channel{
"channel0",
"sub", factory};
290 listenchannel.Init();
292 address <<
"ipc:///tmp/o2sim-worker-notifications-" << getpid();
293 listenchannel.Connect(
address.str());
294 listenchannel.Validate();
295 std::unique_ptr<fair::mq::Message>
message(listenchannel.NewMessage());
298 if (listenchannel.Receive(
message) > 0) {
299 std::string
msg(
reinterpret_cast<char const*
>(
message->GetData()),
message->GetSize());
300 LOG(info) <<
"Worker message: " <<
msg;
304 threads.push_back(std::thread(lambda));
305 threads.back().detach();
311 int pipefd, std::string text, std::vector<int>& eventcontainer,
312 std::function<
bool(std::vector<int>
const&)> callback = [](std::vector<int>
const&) {
return true; })
314 static std::vector<std::thread> threads;
315 auto lambda = [pipefd, text, callback, &eventcontainer]() {
318 ssize_t
count =
read(pipefd, &eventcounter,
sizeof(eventcounter));
320 LOG(info) <<
"ERROR READING";
321 if (errno == EINTR) {
326 }
else if (
count == 0) {
329 eventcontainer.push_back(eventcounter);
330 if (callback(eventcontainer)) {
331 LOG(info) << text.c_str() << eventcounter;
336 threads.push_back(std::thread(lambda));
337 threads.back().detach();
342 static std::vector<std::thread> threads;
349 struct timespec initial, remaining;
352 while (nanosleep(&initial, &remaining) == -1 && remaining.tv_sec > 0) {
355 LOG(info) <<
"Shutdown timer expired ... force killing remaining children";
357 if (p != 0 && killpg(p, 0) == 0) {
362 threads.push_back(std::thread(lambda));
363 threads.back().detach();
371 if (sig == SIGINT || sig == SIGTERM) {
373 LOG(info) <<
"o2-sim driver: Signal caught ... clean up and exit (please be patient)";
383 while ((cpid = wait(&status))) {
399 std::vector<std::string> modifiedArgs;
403 if (conf.resetFromArguments(argc, argv)) {
404 for (
int i = 0;
i < argc; ++
i) {
405 modifiedArgs.push_back(argv[
i]);
410 if (conf.getRunNumber() != -1) {
415 auto soreor = ccdbmgr.getRunDuration(conf.getRunNumber());
416 auto timestamp = conf.getTimestamp();
418 timestamp = soreor.first;
419 LOG(info) <<
"Fixing timestamp to " << timestamp <<
" based on run number";
420 modifiedArgs.push_back(
"--timestamp");
423 LOG(fatal) <<
"The given timestamp " << timestamp <<
" is incompatible with the given run number " << conf.getRunNumber() <<
" starting at " << soreor.first <<
" and ending at " << soreor.second;
427 std::vector<char*>
final(modifiedArgs.size(),
nullptr);
428 for (
int i = 0;
i < modifiedArgs.size(); ++
i) {
429 final[
i] =
new char[modifiedArgs[
i].size() + 1];
430 strcpy(
final[
i], modifiedArgs[
i].c_str());
437int main(
int argc,
char* argv[])
447 setenv(
"ALICE_SIMFORKINTERNAL",
"ON", 1);
450 if (setpgid(0, 0) == -1) {
457 auto o2env = getenv(
"O2_ROOT");
459 LOG(fatal) <<
"O2_ROOT environment not defined";
461 std::string rootpath(o2env);
462 std::string installpath = rootpath +
"/bin";
465 std::stringstream configss;
466 configss << rootpath <<
"/share/config/o2simtopology_template.json";
467 auto localconfig = std::string(
"o2simtopology_") +
std::to_string(getpid()) + std::string(
".json");
471 std::ifstream in(configss.str());
472 std::ofstream out(localconfig);
473 std::string wordToReplace(
"#PID#");
476 size_t len = wordToReplace.length();
477 while (std::getline(in, line)) {
478 size_t pos = line.find(wordToReplace);
479 if (
pos != std::string::npos) {
480 line.replace(
pos,
len, wordToReplaceWith);
493 if (finalArgs.size() == 0) {
501 if (!conf.resetFromArguments(finalArgs.size(), &finalArgs[0])) {
506 if (conf.getNEvents() <= 0 && !conf.asService()) {
507 LOG(info) <<
"No events to be simulated; Switching to non-distributed mode";
508 const int Nargs = finalArgs.size() + 1;
510 std::string
name(
"o2-sim-serial-run5");
512 std::string
name(
"o2-sim-serial");
516 for (
int i = 1;
i < finalArgs.size(); ++
i) {
520 std::string
path = installpath +
"/" +
name;
529 if (conf.asService()) {
536 int nworkers = conf.getNSimWorkers();
538 LOG(info) <<
"Running with " << nworkers <<
" sim workers ";
539 if (conf.getIsMT()) {
540 if (conf.getMCEngine() !=
"TGeant4") {
541 LOG(fatal) <<
"Multithreaded mode (--isMT) is only available with TGeant4";
544 setenv(
"ALICE_O2SIM_MT",
"ON", 1);
545 LOG(info) <<
"Multithreaded Geant4 with " << nworkers <<
" transport threads";
551 if (getenv(
"ALICE_NOSIMSHM")) {
555 int pipe_serverdriver_fd[2];
556 if (pipe(pipe_serverdriver_fd) != 0) {
557 perror(
"problem in creating pipe");
563 int fd = open(
getServerLogName().c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
564 setenv(
"ALICE_O2SIMSERVERTODRIVER_PIPE",
std::to_string(pipe_serverdriver_fd[1]).c_str(), 1);
569 close(pipe_serverdriver_fd[0]);
572 const std::string
name(
"o2-sim-primary-server-device-runner");
573 const std::string
path = installpath +
"/" +
name;
574 const std::string config = localconfig;
578 const int addNArgs = 12;
580 const int addNArgs = 11;
582 const int Nargs = finalArgs.size() + addNArgs;
598 for (
int i = 1;
i < finalArgs.size(); ++
i) {
602 for (
int i = 0;
i < Nargs; ++
i) {
607 std::cerr <<
"$$$$\n";
609 LOG(info) <<
"Starting the server"
618 close(pipe_serverdriver_fd[1]);
619 std::cout <<
"Spawning particle server on PID " <<
pid <<
"; Redirect output to " <<
getServerLogName() <<
"\n";
622 auto distributionCallback = [&conf, &externalpublishchannel](std::vector<int>
const&
v) {
623 std::stringstream
str;
624 if (
v.back() == -111) {
629 str <<
"EVENT " <<
v.back() <<
" DISTRIBUTED";
650 int pipe_mergerdriver_fd[2];
651 if (pipe(pipe_mergerdriver_fd) != 0) {
652 perror(
"problem in creating pipe");
657 std::atomic<bool> shutdown_initiated =
false;
658 std::function<
bool(std::vector<int>
const&)> finishCallback;
660 int fd = open(
getMergerLogName().c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
665 close(pipe_mergerdriver_fd[0]);
666 setenv(
"ALICE_O2SIMMERGERTODRIVER_PIPE",
std::to_string(pipe_mergerdriver_fd[1]).c_str(), 1);
667 const std::string
name(
"o2-sim-hit-merger-runner");
668 const std::string
path = installpath +
"/" +
name;
669 execl(
path.c_str(),
name.c_str(),
"--control",
"static",
"--catch-signals",
"0",
"--id",
"hitmerger",
"--mq-config", localconfig.c_str(),
"--color",
"false",
673 std::cout <<
"Spawning hit merger on PID " <<
pid <<
"; Redirect output to " <<
getMergerLogName() <<
"\n";
677 close(pipe_mergerdriver_fd[1]);
682 finishCallback = [&shutdown_initiated, &conf, &externalpublishchannel](std::vector<int>
const&
v) {
683 std::stringstream
str;
688 if (!conf.asService()) {
689 LOG(info) <<
"SIMULATION IS DONE. INITIATING SHUTDOWN.";
690 if (!shutdown_initiated) {
691 shutdown_initiated =
true;
693 if (killpg(p, 0) == 0) {
699 LOG(info) <<
"SIMULATION DONE. STAYING AS DAEMON.";
706 auto internalfork = getenv(
"ALICE_SIMFORKINTERNAL");
711 for (
int id = 0;
id < nworkers; ++
id) {
713 std::stringstream workerlogss;
717 std::stringstream workerss;
718 workerss <<
"worker" <<
id;
722 int fd = open(workerlogss.str().c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
728 const std::string
name(
"o2-sim-device-runner");
729 const std::string
path = installpath +
"/" +
name;
731 execl(
path.c_str(),
name.c_str(),
"--control",
"static",
"--id", workerss.str().c_str(),
"--config-key",
732 "worker",
"--mq-config", localconfig.c_str(),
"--severity",
"info", (
char*)
nullptr);
737 std::cout <<
"Spawning sim worker " <<
id <<
" on PID " <<
pid
738 <<
"; Redirect output to " << workerlogss.str() <<
"\n";
748 bool errored =
false;
750 while ((cpid = wait(&status)) != -1) {
751 if (cpid == mergerpid) {
755 if (WEXITSTATUS(status) || WIFSIGNALED(status)) {
756 if (!shutdown_initiated) {
757 LOG(info) <<
"Process " << cpid <<
" EXITED WITH CODE " << WEXITSTATUS(status) <<
" SIGNALED "
758 << WIFSIGNALED(status) <<
" SIGNAL " << WTERMSIG(status);
762 LOG(info) <<
"Problem detected (or child received termination signal) ... shutting down whole system ";
764 LOG(info) <<
"TERMINATING " << p;
765 if (killpg(p, 0) == 0) {
769 LOG(
error) <<
"SHUTTING DOWN DUE TO SIGNALED EXIT IN COMPONENT " << cpid;
777 if (cpid == mergerpid) {
778 if (WIFEXITED(status)) {
779 if (WEXITSTATUS(status) != 0 && WEXITSTATUS(status) != 128) {
780 LOG(
error) <<
"Merger process exited with abnormal exit status " << WEXITSTATUS(status);
783 }
else if (WIFSIGNALED(status)) {
784 auto sig = WTERMSIG(status);
785 if (sig == SIGKILL || sig == SIGBUS || sig == SIGSEGV || sig == SIGABRT) {
786 LOG(
error) <<
"Merger process terminated through abnormal signal " << WTERMSIG(status);
790 LOG(warning) <<
"Merger process exited with unexpected status.";
795 LOG(info) <<
"Merger process " << mergerpid <<
" returned";
796 LOG(info) <<
"Simulation process took " << timer.RealTime() <<
" s";
798 if (!errored && !shutdown_initiated) {
799 shutdown_initiated =
true;
802 if (p != mergerpid) {
803 LOG(info) <<
"SHUTTING DOWN CHILD PROCESS (normal thread)" << p;
804 if (killpg(p, 0) == 0) {
815 while ((cpid = wait(&status))) {
825 while (p != 0 && killpg(p, 0) == 0) {
838 if (returncode == 0) {
839 LOG(info) <<
"SIMULATION RETURNED SUCCESFULLY";
std::vector< std::string > header
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 bool
char error[CURL_ERROR_SIZE]
Definition of the Names Generator class.
static std::string getMCKinematicsFileName(const std::string_view prefix=STANDARDSIMPREFIX)
static BasicCCDBManager & instance()
static SimConfig & Instance()
static ShmManager & Instance()
bool isOperational() const
bool createGlobalSegment(int nsubsegments=1)
GLuint GLuint64EXT address
GLuint const GLchar * name
GLsizei const GLchar *const * path
GLuint GLsizei const GLchar * message
GLenum GLenum GLsizei len
bpo::variables_map arguments
bool parseSimReconfigFromString(std::string const &argumentstring, SimReconfigData &config)
int mergeG4ScoringDumps(const std::string &directory, int expectedWorkers=0)
DeliveryType read(const std::string &str)
@ Completed
The channel was signaled it will not receive any data.
fair::mq::MessagePtr getMessage(ContainerT &&container, FairMQMemoryResource *targetResource=nullptr)
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")
std::vector< std::string > listFiles(std::string const &dir, std::string const &searchpattern)
std::string getBuildInfo()
get information about build platform (for example OS and alidist release when used)
std::string gitRevision()
get O2 git commit used to build this
bool setStaticTLSReserve(unsigned long reserve=Geant4StaticTLSReserve)
Sets the static TLS reserve in the environment, for the programs started from here....
std::string fullVersion()
get full version information (official O2 release and git commit)
std::string to_string(gsl::span< T, Size > span)
std::string getServerLogName()
std::vector< int > gFinishedEvents
std::vector< int > gChildProcesses
void launchWorkerListenerThread()
std::atomic< bool > gPrimServerIsInitialized
std::string getInternalControlAddress()
std::vector< int > gDistributedEvents
void launchShutdownThread()
void launchThreadMonitoringEvents(int pipefd, std::string text, std::vector< int > &eventcontainer, std::function< bool(std::vector< int > const &)> callback=[](std::vector< int > const &) { return true;})
std::string getMergerLogName()
std::string getWorkerLogName()
std::vector< char * > checkArgs(int argc, char *argv[])
void launchControlThread()
std::string getControlAddress()
TODO: Make this a base class of SimConfigData?
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
uint64_t const void const *restrict const msg