Project
Loading...
Searching...
No Matches
O2SimDevice.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"
16#include "../macro/o2sim.C"
17#include "TVirtualMC.h"
18#include <fairmq/Message.h>
19#include <fairmq/Parts.h>
20#include <fairlogger/Logger.h>
21#include <DetectorsBase/Stack.h>
23#include <TRandom.h>
24#include <SimConfig/SimConfig.h>
25#include <cstring>
26#include <atomic>
28
29void doLogInfo(int workerID, std::string const& message)
30{
31 LOG(info) << "[W" << workerID << "] " << message;
32}
33
34namespace o2
35{
36namespace devices
37{
38
40{
41 FairSystemInfo sysinfo;
43 LOG(info) << "Shutting down O2SimDevice";
44 LOG(info) << "TIME-STAMP " << mTimer.RealTime() << "\t";
45 LOG(info) << "MEM-STAMP " << sysinfo.GetCurrentMemory() / (1024. * 1024) << " " << sysinfo.GetMaxMemory() << " MB\n";
46}
47
49{
50 // in the initialization phase we will init the simulation
51 // NOTE: In a fair::mq::Device this is better done here (instead of outside) since
52 // we have to setup simulation + worker in the same thread (due to many threadlocal variables
53 // in the simulation) ... at least as long fair::mq::Device is not spawning workers on the master thread
54 initSim(GetChannels().at("o2sim-primserv-info").at(0), mSimRun);
55
56 // set the vmc and app pointers
57 mVMC = TVirtualMC::GetMC();
58 mVMCApp = static_cast<o2::steer::O2MCApplication*>(TVirtualMCApplication::Instance());
59 lateInit();
60}
61
63{
64 // late init
65 mVMCApp->initLate();
66}
67
68bool O2SimDevice::initSim(fair::mq::Channel& channel, std::unique_ptr<FairRunSim>& simptr)
69{
70 if (!o2::querySimConfig(channel)) {
71 return false;
72 }
73
74 LOG(info) << "Setting up the simulation ...";
75 simptr = std::move(std::unique_ptr<FairRunSim>(o2sim_init(true)));
76 FairSystemInfo sysinfo;
77
78 // to finish initialization (trigger further cross section table building etc) -- which especially
79 // G4 is doing at the first ProcessRun
80 // The goal is to have everything setup before we fork
81 TVirtualMC::GetMC()->ProcessRun(0);
82
83 LOG(info) << "MEM-STAMP END OF SIM INIT" << sysinfo.GetCurrentMemory() / (1024. * 1024) << " "
84 << sysinfo.GetMaxMemory() << " MB\n";
85
86 return true;
87}
88
89bool O2SimDevice::isWorkAvailable(fair::mq::Channel& statuschannel, int workerID)
90{
91 std::stringstream str;
92 str << "[W" << workerID << "]";
93 auto workerStr = str.str();
94
95 int timeoutinMS = 2000; // wait for 2s max
96 bool reprobe = true;
97 while (reprobe) {
98 reprobe = false;
99 int i = -1;
100 fair::mq::MessagePtr request(statuschannel.NewSimpleMessage((int)O2PrimaryServerInfoRequest::Status));
101 fair::mq::MessagePtr reply(statuschannel.NewSimpleMessage(i));
102 auto sendcode = statuschannel.Send(request, timeoutinMS);
103 if (sendcode > 0) {
104 LOG(info) << workerStr << " Waiting for status answer ";
105 auto code = statuschannel.Receive(reply, timeoutinMS);
106 if (code > 0) {
107 int state(*((int*)(reply->GetData())));
109 LOG(info) << workerStr << " SERVER IS SERVING";
110 return true;
112 LOG(info) << workerStr << " SERVER IS STILL INITIALIZING";
113 reprobe = true;
114 sleep(1);
116 LOG(info) << workerStr << " SERVER IS WAITING FOR EVENT";
117 reprobe = true;
118 sleep(1);
119 } else if (state == (int)o2::O2PrimaryServerState::Idle) {
120 LOG(info) << workerStr << " SERVER IS IDLE";
121 return false;
122 } else {
123 LOG(info) << workerStr << " SERVER STATE UNKNOWN OR STOPPED";
124 }
125 } else {
126 LOG(error) << workerStr << " STATUS REQUEST UNSUCCESSFUL";
127 }
128 }
129 }
130 return false;
131}
132
133bool O2SimDevice::Kernel(int workerID, fair::mq::Channel& requestchannel, fair::mq::Channel& dataoutchannel, fair::mq::Channel* statuschannel)
134{
135 bool reproducibleSim = true;
136 if (getenv("O2_DISABLE_REPRODUCIBLE_SIM")) {
137 reproducibleSim = false;
138 }
139
140 mVMCApp->setSimDataChannel(&dataoutchannel);
141
142 // we log info with workerID prepended
143 auto workerStr = [workerID]() {
144 std::stringstream str;
145 str << "[W" << workerID << "]";
146 return str.str();
147 };
148
149 auto fetched = fetchChunk(workerID, requestchannel);
150 if (fetched.status == ChunkStatus::Stop) {
151 return false;
152 }
153 if (fetched.status != ChunkStatus::Work) {
154 // EndOfWork deliberately keeps asking: the server answers the next request as idle, which is the Stop
155 return true;
156 }
157 auto& chunk = fetched.chunk;
158
159 auto info = chunk->mSubEventInfo;
160 LOG(info) << workerStr() << " Processing " << chunk->mParticles.size() << " primary particles "
161 << "for event " << info.eventID << "/" << info.maxEvents << " "
162 << "part " << info.part << "/" << info.nparts;
163
164 // for debugging, O2SIM_RESTRICT_EVENTPART="event:part" transports only that sub-event
165 if (o2::steer::O2MCApplication::isSelectedSubEvent(info.eventID, info.part)) {
166 mVMCApp->setPrimaries(chunk->mParticles);
167 } else {
168 // nothing to transport here
169 mVMCApp->setPrimaries(std::vector<TParticle>{});
170 LOG(info) << workerStr() << " This chunk will be skipped";
171 }
172
173 mVMCApp->setSubEventInfo(&info);
174
175 if (reproducibleSim) {
176 LOG(info) << workerStr() << " Setting seed for this sub-event to " << chunk->mSubEventInfo.seed;
177 o2::base::simRandom()->SetSeed(chunk->mSubEventInfo.seed);
179 }
180
181 // Process one event
182 auto& conf = o2::conf::SimConfig::Instance();
183 if (strcmp(conf.getMCEngine().c_str(), "TGeant4") == 0 || strcmp(conf.getMCEngine().c_str(), "O2TrivialMCEngine") == 0) {
184 // this is preferred and necessary for Geant4
185 // since repeated "ProcessRun" might have significant overheads
186 mVMC->ProcessEvent();
187 } else {
188 // for Geant3 calling ProcessEvent is not enough
189 // as some hooks are not called
190 mVMC->ProcessRun(1);
191 }
192
193 FairSystemInfo sysinfo;
194 LOG(info) << workerStr() << " TIME-STAMP " << mTimer.RealTime() << "\t";
195 mTimer.Continue();
196 LOG(info) << workerStr() << " MEM-STAMP " << sysinfo.GetCurrentMemory() / (1024. * 1024) << " "
197 << sysinfo.GetMaxMemory() << " MB\n";
198 return true;
199}
200
201O2SimDevice::FetchedChunk O2SimDevice::fetchChunk(int workerID, fair::mq::Channel& requestchannel)
202{
203 static std::atomic<int> counter{0}; // shared by the transport threads in multithreaded mode
204 fair::mq::MessagePtr request(requestchannel.NewSimpleMessage(PrimaryChunkRequest{workerID, -1, counter++})); // <-- don't need content; channel means -> give primaries
205 fair::mq::Parts reply;
206
207 auto workerStr = [workerID]() {
208 std::stringstream str;
209 str << "[W" << workerID << "]";
210 return str.str();
211 };
212
213 doLogInfo(workerID, "Requesting work chunk");
214 int timeoutinMS = 2000;
215 auto sendcode = requestchannel.Send(request, timeoutinMS);
216 if (sendcode <= 0) {
217 LOG(info) << workerStr() << " Requesting work from server not possible. Return code " << sendcode;
218 return {ChunkStatus::Stop, nullptr};
219 }
220 doLogInfo(workerID, "Waiting for answer");
221 // asking for primary generation
222 auto code = requestchannel.Receive(reply);
223 if (code <= 0) {
224 LOG(info) << workerStr() << " No primary answer received from server (within timeout). Return code " << code;
225 return {ChunkStatus::Retry, nullptr};
226 }
227 doLogInfo(workerID, "Primary chunk received");
228 auto rawmessage = std::move(reply.At(0));
229 auto header = *(o2::PrimaryChunkAnswer*)(rawmessage->GetData());
230 if (!header.payload_attached) {
231 doLogInfo(workerID, "No payload; Server in stage " + std::string(PrimStateToString[(int)header.serverstate]));
232 // if no payload attached we inspect the server state, to see what to do
234 sleep(1); // back-off and retry
235 return {ChunkStatus::Retry, nullptr};
236 }
237 // we need to decide what to do when the server is idle ---> if this happens immediately after a new batch request it means that the server might just lag a bit behind
238 return {ChunkStatus::Stop, nullptr};
239 }
240 auto payload = std::move(reply.At(1));
241 // wrap incoming bytes as a TMessageWrapper which offers "adoption" of a buffer
242 TMessageWrapper message(payload->GetData(), payload->GetSize());
243 std::unique_ptr<o2::data::PrimaryChunk> chunk(static_cast<o2::data::PrimaryChunk*>(message.ReadObjectAny(message.GetClass())));
244
245 // no particles and eventID == -1 --> indication for no more work
246 if (chunk->mParticles.size() == 0 && chunk->mSubEventInfo.eventID == -1) {
247 doLogInfo(workerID, "No particles in reply : quitting kernel");
248 return {ChunkStatus::EndOfWork, nullptr};
249 }
250 return {ChunkStatus::Work, std::move(chunk)};
251}
252
254{
255 return Kernel(-1, GetChannels().at("primary-get").at(0), GetChannels().at("simdata").at(0));
256}
257
258void O2SimDevice::PostRun() { LOG(info) << "Shutting down "; }
259
260} // namespace devices
261} // namespace o2
Definition of the Stack class.
std::vector< std::string > header
int32_t i
GPUTPCCFCheckPadBaseline Kernel
char error[CURL_ERROR_SIZE]
SurfaceTrackState state
void doLogInfo(int workerID, std::string const &message)
size_t GetCurrentMemory()
Float_t GetMaxMemory()
A TMessage reading from a buffer it does not own.
static VMCSeederService const & instance()
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)
~O2SimDevice() final
Default destructor.
static FetchedChunk fetchChunk(int workerID, fair::mq::Channel &requestchannel)
requests one chunk of primaries from the server on the given channel
bool isWorkAvailable(fair::mq::Channel &statuschannel, int workerID=-1)
void InitTask() final
Overloads the InitTask() method of fair::mq::Device.
@ EndOfWork
the server sent the end-of-work marker
@ Stop
the request could not be sent, or the server has nothing more to give; stop asking
@ Retry
nothing yet (server busy or no answer); ask again
bool ConditionalRun() final
Overloads the ConditionalRun() method of fair::mq::Device.
static bool isSelectedSubEvent(int eventID, int part)
whether a sub-event passes the O2SIM_RESTRICT_EVENTPART ("event:part") selection, a debugging aid
void setSimDataChannel(fair::mq::Channel *channel)
void setPrimaries(std::vector< TParticle > const &p)
void setSubEventInfo(o2::data::SubEventInfo *i)
static ShmManager & Instance()
Definition ShmManager.h:62
GLuint GLsizei const GLchar * message
Definition glcorearb.h:2517
TRandom * simRandom()
Definition SimRandom.cxx:22
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
bool querySimConfig(fair::mq::Channel &channel)
constexpr const char * PrimStateToString[5]
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
const std::string str