Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TMCManager.cxx
Go to the documentation of this file.
1 // @(#)root/vmc:$Id$
2 // Authors: Benedikt Volkel 07/03/2019
3 
4 /*************************************************************************
5  * Copyright (C) 2019, Rene Brun and Fons Rademakers. *
6  * Copyright (C) 2019, ALICE Experiment at CERN. *
7  * All rights reserved. *
8  * *
9  * For the licensing terms see $ROOTSYS/LICENSE. *
10  * For the list of contributors see $ROOTSYS/README/CREDITS. *
11  *************************************************************************/
12 
13 #include "TError.h"
14 #include "TVector3.h"
15 #include "TLorentzVector.h"
16 #include "TParticle.h"
17 #include "TGeoBranchArray.h"
18 #include "TGeoNavigator.h"
19 
20 #include "TVirtualMCApplication.h"
21 #include "TVirtualMCStack.h"
22 #include "TMCManagerStack.h"
23 #include "TMCParticleStatus.h"
24 
25 #include "TMCManager.h"
26 
27 /** \class TMCManager
28  \ingroup vmc
29 
30 Singleton manager class for handling and steering a run with multiple TVirtualMC
31 engines sharing events.
32 It provides interfaces to transfer tracks between engines and exposes the correct
33 stacks to each running engine. A registered user stack is kept up-to-date
34 automatically seeing a consistent history.
35 Track objects (aka TParticle) are still owned by the user who must forward these to
36 the manager after creation. Everything else is done automatically.
37 */
38 
39 TMCThreadLocal TMCManager *TMCManager::fgInstance = nullptr;
40 
41 ////////////////////////////////////////////////////////////////////////////////
42 ///
43 /// Default and standard constructor
44 ///
45 
46 TMCManager::TMCManager()
47  : fApplication(nullptr), fCurrentEngine(nullptr), fTotalNPrimaries(0), fTotalNTracks(0), fUserStack(nullptr),
48  fBranchArrayContainer(), fIsInitialized(kFALSE), fIsInitializedUser(kFALSE)
49 {
50  if (fgInstance) {
51  ::Fatal("TMCManager::TMCManager", "Attempt to create two instances of singleton.");
52  }
53  fgInstance = this;
54 }
55 
56 ////////////////////////////////////////////////////////////////////////////////
57 ///
58 /// Destructor
59 ///
60 
61 TMCManager::~TMCManager()
62 {
63  for (auto &mc : fEngines) {
64  delete mc;
65  }
66  fgInstance = nullptr;
67 }
68 
69 ////////////////////////////////////////////////////////////////////////////////
70 ///
71 /// Static access method
72 ///
73 
74 TMCManager *TMCManager::Instance()
75 {
76  return fgInstance;
77 }
78 
79 ////////////////////////////////////////////////////////////////////////////////
80 ///
81 /// A TVirtualMC will register itself via this method during construction
82 /// if a TMCManager was instanciated before.
83 /// The TMCManager will assign an ID to the engines.
84 ///
85 
86 void TMCManager::Register(TVirtualMC *mc)
87 {
88  // Do not register an engine twice.
89  for (auto &currMC : fEngines) {
90  if (currMC == mc) {
91  ::Fatal("TMCManager::RegisterMC", "This engine is already registered.");
92  }
93  }
94  // Set id and register.
95  mc->SetId(fEngines.size());
96  fEngines.push_back(mc);
97  fStacks.emplace_back(new TMCManagerStack());
98  mc->SetStack(fStacks.back().get());
99  mc->SetManagerStack(fStacks.back().get());
100  // Must update engine pointers here since during construction of the concrete TVirtualMC
101  // implementation the static TVirtualMC::GetMC() or defined gMC might be used.
102  UpdateEnginePointers(mc);
103 }
104 
105 ////////////////////////////////////////////////////////////////////////////////
106 ///
107 /// The user application will register itself via this method when the
108 /// manager was requested.
109 ///
110 
111 void TMCManager::Register(TVirtualMCApplication *application)
112 {
113  if (fApplication) {
114  ::Fatal("TMCManager::Register", "The application is already registered.");
115  }
116  ::Info("TMCManager::Register", "Register user application and construct geometry");
117  fApplication = application;
118  // TODO Can these 3 functions can be called directly here? Or could any of these depend on an implemented VMC?
119  fApplication->ConstructGeometry();
120  fApplication->MisalignGeometry();
121  fApplication->ConstructOpGeometry();
122  if (!gGeoManager->IsClosed()) {
123  // Setting the top volume is the duty of the user as well as closing it.
124  // Failing here is just an additional cross check. If not closed the user
125  // might have forgotten something.
126  ::Fatal("TMCManager::Register", "The TGeo geometry is not closed. Please check whether you just have to close "
127  "it or whether something was forgotten.");
128  }
129 }
130 
131 ////////////////////////////////////////////////////////////////////////////////
132 ///
133 /// Return the number of registered engines.
134 ///
135 
136 Int_t TMCManager::NEngines() const
137 {
138  return fEngines.size();
139 }
140 
141 ////////////////////////////////////////////////////////////////////////////////
142 ///
143 /// Get registered engine pointers
144 ///
145 
146 void TMCManager::GetEngines(std::vector<TVirtualMC *> &engines) const
147 {
148  engines.clear();
149  engines.resize(fEngines.size(), nullptr);
150  for (UInt_t i = 0; i < fEngines.size(); i++) {
151  engines[i] = fEngines[i];
152  }
153 }
154 
155 ////////////////////////////////////////////////////////////////////////////////
156 ///
157 /// Return the number of registered engines.
158 ///
159 
160 TVirtualMC *TMCManager::GetEngine(Int_t id) const
161 {
162  if (id < 0 || id >= static_cast<Int_t>(fEngines.size())) {
163  ::Fatal("TMCManager::GetEngine", "Unknown engine ID.");
164  }
165  return fEngines[id];
166 }
167 
168 ////////////////////////////////////////////////////////////////////////////////
169 ///
170 /// Get engine ID by its name
171 ///
172 
173 Int_t TMCManager::GetEngineId(const char *engineName) const
174 {
175  for (UInt_t i = 0; i < fEngines.size(); i++) {
176  if (strcmp(engineName, fEngines[i]->GetName()) == 0) {
177  return i;
178  }
179  }
180  ::Warning("TMCManager::GetEngineId", "Unknown engine %s.", engineName);
181  return -1;
182 }
183 
184 ////////////////////////////////////////////////////////////////////////////////
185 ///
186 /// Get the current engine pointer
187 ///
188 
189 TVirtualMC *TMCManager::GetCurrentEngine() const
190 {
191  return fCurrentEngine;
192 }
193 
194 ////////////////////////////////////////////////////////////////////////////////
195 ///
196 /// Connect a pointer which is updated whenever the engine is changed
197 ///
198 
199 void TMCManager::ConnectEnginePointer(TVirtualMC **mc)
200 {
201  fConnectedEnginePointers.push_back(mc);
202  if (fCurrentEngine) {
203  *mc = fCurrentEngine;
204  }
205 }
206 
207 ////////////////////////////////////////////////////////////////////////////////
208 ///
209 /// Connect a pointer which is updated whenever the engine is changed
210 ///
211 
212 void TMCManager::ConnectEnginePointer(TVirtualMC *&mc)
213 {
214  ConnectEnginePointer(&mc);
215 }
216 
217 ////////////////////////////////////////////////////////////////////////////////
218 ///
219 /// Set user stack
220 ///
221 
222 void TMCManager::SetUserStack(TVirtualMCStack *stack)
223 {
224  fUserStack = stack;
225 }
226 
227 ////////////////////////////////////////////////////////////////////////////////
228 ///
229 /// User interface to forward particle to specifiic engine.
230 /// It is assumed that the TParticle is owned by the user. It will not be
231 /// modified by the TMCManager.
232 ///
233 
234 void TMCManager::ForwardTrack(Int_t toBeDone, Int_t trackId, Int_t parentId, TParticle *particle, Int_t engineId)
235 {
236  if (engineId < 0 || engineId >= static_cast<Int_t>(fEngines.size())) {
237  ::Fatal("TMCManager::ForwardTrack", "Engine ID %i out of bounds. Have %zu engines.", engineId, fEngines.size());
238  }
239  if (trackId >= static_cast<Int_t>(fParticles.size())) {
240  fParticles.resize(trackId + 1, nullptr);
241  fParticlesStatus.resize(trackId + 1);
242  }
243  fParticles[trackId] = particle;
244  fParticlesStatus[trackId].reset(new TMCParticleStatus());
245  fParticlesStatus[trackId]->fId = trackId;
246  fParticlesStatus[trackId]->fParentId = parentId;
247  fParticlesStatus[trackId]->InitFromParticle(particle);
248  fTotalNTracks++;
249  if (particle->IsPrimary()) {
250  fTotalNPrimaries++;
251  }
252 
253  if (toBeDone > 0) {
254  if (particle->IsPrimary()) {
255  fStacks[engineId]->PushPrimaryTrackId(trackId);
256  } else {
257  fStacks[engineId]->PushSecondaryTrackId(trackId);
258  }
259  }
260 }
261 
262 ////////////////////////////////////////////////////////////////////////////////
263 ///
264 /// User interface to forward particle to specifiic engine.
265 /// It is assumed that the TParticle is owned by the user. It will not be
266 /// modified by the TMCManager.
267 /// Assume current engine Id
268 ///
269 
270 void TMCManager::ForwardTrack(Int_t toBeDone, Int_t trackId, Int_t parentId, TParticle *particle)
271 {
272  ForwardTrack(toBeDone, trackId, parentId, particle, fCurrentEngine->GetId());
273 }
274 
275 ////////////////////////////////////////////////////////////////////////////////
276 ///
277 /// Transfer track from current engine to engine with engineTargetId
278 ///
279 
280 void TMCManager::TransferTrack(Int_t engineTargetId)
281 {
282  if (engineTargetId < 0 || engineTargetId >= static_cast<Int_t>(fEngines.size())) {
283  ::Fatal("TMCManager::TransferTrack",
284  "Target engine ID out of bounds. Have %zu engines. Requested target ID was %i", fEngines.size(),
285  engineTargetId);
286  }
287  TransferTrack(fEngines[engineTargetId]);
288 }
289 
290 ////////////////////////////////////////////////////////////////////////////////
291 ///
292 /// Transfer track from current engine to target engine mc
293 ///
294 
295 void TMCManager::TransferTrack(TVirtualMC *mc)
296 {
297  // Do nothing if target and current engines are the same
298  if (mc == fCurrentEngine) {
299  return;
300  }
301 
302  // Get information on current track and extract status from transporting engine
303  Int_t trackId = fStacks[fCurrentEngine->GetId()]->GetCurrentTrackNumber();
304 
305  fCurrentEngine->TrackPosition(fParticlesStatus[trackId]->fPosition);
306  fCurrentEngine->TrackMomentum(fParticlesStatus[trackId]->fMomentum);
307  fCurrentEngine->TrackPolarization(fParticlesStatus[trackId]->fPolarization);
308  fParticlesStatus[trackId]->fStepNumber = fCurrentEngine->StepNumber();
309  fParticlesStatus[trackId]->fTrackLength = fCurrentEngine->TrackLength();
310  fParticlesStatus[trackId]->fWeight = fCurrentEngine->TrackWeight();
311 
312  // Store TGeoNavidator's fIsOutside state
313  fParticlesStatus[trackId]->fIsOutside = gGeoManager->IsOutside();
314 
315  TGeoBranchArray *geoState = fBranchArrayContainer.GetNewGeoState(fParticlesStatus[trackId]->fGeoStateIndex);
316  geoState->InitFromNavigator(gGeoManager->GetCurrentNavigator());
317 
318  // Push only the particle ID
319  if (fParticles[trackId]->IsPrimary()) {
320  fStacks[mc->GetId()]->PushPrimaryTrackId(trackId);
321  } else {
322  fStacks[mc->GetId()]->PushSecondaryTrackId(trackId);
323  }
324  fCurrentEngine->InterruptTrack();
325 }
326 
327 ////////////////////////////////////////////////////////////////////////////////
328 ///
329 /// Try to restore geometry for a given track
330 ///
331 
332 Bool_t TMCManager::RestoreGeometryState(Int_t trackId, Bool_t checkTrackIdRange)
333 {
334  if (checkTrackIdRange && (trackId < 0 || trackId >= static_cast<Int_t>(fParticles.size()) || !fParticles[trackId])) {
335  return kFALSE;
336  }
337  UInt_t& geoStateId = fParticlesStatus[trackId]->fGeoStateIndex;
338  if(geoStateId == 0) {
339  return kFALSE;
340  }
341  const TGeoBranchArray* branchArray = fBranchArrayContainer.GetGeoState(geoStateId);
342  branchArray->UpdateNavigator(gGeoManager->GetCurrentNavigator());
343  fBranchArrayContainer.FreeGeoState(geoStateId);
344  gGeoManager->SetOutside(fParticlesStatus[trackId]->fIsOutside);
345  geoStateId = 0;
346  return kTRUE;
347 }
348 
349 ////////////////////////////////////////////////////////////////////////////////
350 ///
351 /// Try to restore geometry for the track currently set
352 ///
353 
354 Bool_t TMCManager::RestoreGeometryState()
355 {
356  return RestoreGeometryState(fStacks[fCurrentEngine->GetId()]->GetCurrentTrackNumber(), kFALSE);
357 }
358 
359 ////////////////////////////////////////////////////////////////////////////////
360 ///
361 /// Initialize engines
362 ///
363 
364 void TMCManager::Init()
365 {
366  if (fIsInitialized) {
367  return;
368  }
369 
370  if (!fUserStack) {
371  ::Fatal("TMCManager::Run", "Missing user stack pointer.");
372  }
373  if (fEngines.empty()) {
374  ::Fatal("TMCManager::Run", "No engines registered");
375  }
376 
377  for (auto &mc : fEngines) {
378  // Must have geometry handling via TGeo
379  if (!mc->IsRootGeometrySupported()) {
380  ::Fatal("TMCManager::Run", "Engine %s does not support geometry built via ROOT's TGeoManager", mc->GetName());
381  }
382  Int_t currentEngineId = mc->GetId();
383  // To be able to forward info to the user stack
384  fStacks[currentEngineId]->SetUserStack(fUserStack);
385  // Connect the engine's stack to the centrally managed vectors
386  fStacks[currentEngineId]->ConnectTrackContainers(&fParticles, &fParticlesStatus, &fBranchArrayContainer,
387  &fTotalNPrimaries, &fTotalNTracks);
388  }
389 
390  // Initialize the fBranchArrayContainer to manage and cache TGeoBranchArrays
391  fBranchArrayContainer.InitializeFromGeoManager(gGeoManager);
392 
393  fIsInitialized = kTRUE;
394 
395  // Send warning if only one engine ==> overhead
396  if (fEngines.size() == 1) {
397  ::Warning("TMCManager::Run", "Only one engine registered. That will lead to overhead in "
398  "the simulation run due to additional hooks and dispatches "
399  "to the TMCManager.");
400  }
401 }
402 
403 ////////////////////////////////////////////////////////////////////////////////
404 ///
405 /// Run the event loop
406 ///
407 
408 void TMCManager::Run(Int_t nEvents)
409 {
410  if (!fIsInitialized) {
411  ::Fatal("TMCManager::Run", "Engines have not yet been initialized.");
412  }
413 
414  // Set user initialize true in any case now so that this cannot happen by accident during a run
415  fIsInitializedUser = kTRUE;
416 
417  if (nEvents < 1) {
418  ::Fatal("TMCManager::Run", "Need at least one event to process but %i events specified.", nEvents);
419  }
420 
421  // Run 1 event nEvents times
422  for (Int_t i = 0; i < nEvents; i++) {
423  ::Info("TMCManager::Run", "Start event %i", i + 1);
424  PrepareNewEvent();
425  fApplication->BeginEvent();
426  // Loop as long as there are tracks in any engine stack
427  while (GetNextEngine()) {
428  fCurrentEngine->ProcessEvent(i, kTRUE);
429  }
430  fApplication->FinishEvent();
431  }
432  TerminateRun();
433 }
434 
435 ////////////////////////////////////////////////////////////////////////////////
436 ///
437 /// Choose next engines to be run in the loop
438 ///
439 
440 void TMCManager::PrepareNewEvent()
441 {
442  fBranchArrayContainer.FreeGeoStates();
443  // Reset in event flag for all engines and clear stacks
444  for (auto &stack : fStacks) {
445  stack->ResetInternals();
446  }
447  for (UInt_t i = 0; i < fParticles.size(); i++) {
448  fParticlesStatus.clear();
449  fParticlesStatus.resize(fParticles.size());
450  fParticles[i] = nullptr;
451  }
452 
453  // GeneratePrimaries centrally
454  fApplication->GeneratePrimaries();
455 }
456 
457 ////////////////////////////////////////////////////////////////////////////////
458 ///
459 /// Choose next engines to be run in the loop
460 ///
461 
462 Bool_t TMCManager::GetNextEngine()
463 {
464  // Select next engine based on finite number of particles on the stack
465  for (UInt_t i = 0; i < fStacks.size(); i++) {
466  if (fStacks[i]->GetStackedNtrack() > 0) {
467  UpdateEnginePointers(fEngines[i]);
468  return kTRUE;
469  }
470  }
471  // No tracks to be processed.
472  return kFALSE;
473 }
474 
475 ////////////////////////////////////////////////////////////////////////////////
476 ///
477 /// Update all engine pointers connected to the TMCManager
478 ///
479 
480 void TMCManager::UpdateEnginePointers(TVirtualMC *mc)
481 {
482  fCurrentEngine = mc;
483  for (TVirtualMC **&mcPtr : fConnectedEnginePointers) {
484  *mcPtr = mc;
485  }
486  // Make sure TVirtualMC::GetMC() returns the current engine.
487  TVirtualMC::fgMC = mc;
488 }
489 
490 ////////////////////////////////////////////////////////////////////////////////
491 ///
492 /// Terminate the run for all engines
493 ///
494 
495 void TMCManager::TerminateRun()
496 {
497  for (auto &mc : fEngines) {
498  mc->TerminateRun();
499  }
500 }