Geant4 Cross Reference |
1 // 2 // ******************************************************************** 3 // * License and Disclaimer * 4 // * * 5 // * The Geant4 software is copyright of the Copyright Holders of * 6 // * the Geant4 Collaboration. It is provided under the terms and * 7 // * conditions of the Geant4 Software License, included in the file * 8 // * LICENSE and available at http://cern.ch/geant4/license . These * 9 // * include a list of copyright holders. * 10 // * * 11 // * Neither the authors of this software system, nor their employing * 12 // * institutes,nor the agencies providing financial support for this * 13 // * work make any representation or warranty, express or implied, * 14 // * regarding this software system or assume any liability for its * 15 // * use. Please see the license in the file LICENSE and URL above * 16 // * for the full disclaimer and the limitation of liability. * 17 // * * 18 // * This code implementation is the result of the scientific and * 19 // * technical work of the GEANT4 collaboration. * 20 // * By using, copying, modifying or distributing the software (or * 21 // * any work based on the software) you agree to acknowledge its * 22 // * use in resulting scientific publications, and indicate your * 23 // * acceptance of all terms of the Geant4 Software license. * 24 // ******************************************************************** 25 // 26 // 27 28 #include "G4WorkerTaskRunManager.hh" 29 30 #include "G4AutoLock.hh" 31 #include "G4MTRunManager.hh" 32 #include "G4ParallelWorldProcess.hh" 33 #include "G4ParallelWorldProcessStore.hh" 34 #include "G4RNGHelper.hh" 35 #include "G4Run.hh" 36 #include "G4SDManager.hh" 37 #include "G4ScoringManager.hh" 38 #include "G4TaskRunManager.hh" 39 #include "G4Timer.hh" 40 #include "G4TransportationManager.hh" 41 #include "G4UImanager.hh" 42 #include "G4UserRunAction.hh" 43 #include "G4UserWorkerInitialization.hh" 44 #include "G4UserWorkerThreadInitialization.hh" 45 #include "G4VScoreNtupleWriter.hh" 46 #include "G4VScoringMesh.hh" 47 #include "G4VUserActionInitialization.hh" 48 #include "G4VUserDetectorConstruction.hh" 49 #include "G4VUserPhysicsList.hh" 50 #include "G4VUserPrimaryGeneratorAction.hh" 51 #include "G4VVisManager.hh" 52 #include "G4WorkerTaskRunManagerKernel.hh" 53 #include "G4WorkerThread.hh" 54 55 #include <fstream> 56 #include <sstream> 57 58 //============================================================================// 59 60 G4WorkerTaskRunManager* G4WorkerTaskRunManager::GetWorkerRunManager() 61 { 62 return static_cast<G4WorkerTaskRunManager*>(G4RunManager::GetRunManager()); 63 } 64 65 //============================================================================// 66 67 G4WorkerTaskRunManagerKernel* G4WorkerTaskRunManager::GetWorkerRunManagerKernel() 68 { 69 return static_cast<G4WorkerTaskRunManagerKernel*>(GetWorkerRunManager()->kernel); 70 } 71 72 //============================================================================// 73 74 void G4WorkerTaskRunManager::RunInitialization() 75 { 76 #ifdef G4MULTITHREADED 77 if (!visIsSetUp) { 78 G4VVisManager* pVVis = G4VVisManager::GetConcreteInstance(); 79 if (pVVis != nullptr) { 80 pVVis->SetUpForAThread(); 81 visIsSetUp = true; 82 } 83 } 84 #endif 85 runIsSeeded = false; 86 87 if (!(kernel->RunInitialization(fakeRun))) return; 88 89 // Signal this thread can start event loop. 90 // Note this will return only when all threads reach this point 91 G4MTRunManager::GetMasterRunManager()->ThisWorkerReady(); 92 if (fakeRun) return; 93 94 const G4UserWorkerInitialization* uwi = 95 G4MTRunManager::GetMasterRunManager()->GetUserWorkerInitialization(); 96 97 CleanUpPreviousEvents(); 98 99 delete currentRun; 100 101 currentRun = nullptr; 102 103 if (IfGeometryHasBeenDestroyed()) G4ParallelWorldProcessStore::GetInstance()->UpdateWorlds(); 104 105 // Call a user hook: this is guaranteed all threads are "synchronized" 106 if (uwi != nullptr) uwi->WorkerRunStart(); 107 108 if (userRunAction != nullptr) currentRun = userRunAction->GenerateRun(); 109 if (currentRun == nullptr) currentRun = new G4Run(); 110 111 currentRun->SetRunID(runIDCounter); 112 G4TaskRunManager* mrm = G4TaskRunManager::GetMasterRunManager(); 113 numberOfEventToBeProcessed = mrm->GetNumberOfEventsToBeProcessed(); 114 currentRun->SetNumberOfEventToBeProcessed(numberOfEventToBeProcessed); 115 116 currentRun->SetDCtable(DCtable); 117 G4SDManager* fSDM = G4SDManager::GetSDMpointerIfExist(); 118 if (fSDM != nullptr) { 119 currentRun->SetHCtable(fSDM->GetHCtable()); 120 } 121 122 if (G4VScoreNtupleWriter::Instance() != nullptr) { 123 auto hce = (fSDM != nullptr) ? fSDM->PrepareNewEvent() : nullptr; 124 isScoreNtupleWriter = G4VScoreNtupleWriter::Instance()->Book(hce); 125 delete hce; 126 } 127 128 std::ostringstream oss; 129 G4Random::saveFullState(oss); 130 randomNumberStatusForThisRun = oss.str(); 131 currentRun->SetRandomNumberStatus(randomNumberStatusForThisRun); 132 133 for (G4int i_prev = 0; i_prev < n_perviousEventsToBeStored; ++i_prev) 134 previousEvents->push_back(nullptr); 135 136 if (printModulo > 0 || verboseLevel > 0) { 137 G4cout << "### Run " << currentRun->GetRunID() << " starts on worker thread " 138 << G4Threading::G4GetThreadId() << "." << G4endl; 139 } 140 141 if (userRunAction != nullptr) userRunAction->BeginOfRunAction(currentRun); 142 143 if (isScoreNtupleWriter) { 144 G4VScoreNtupleWriter::Instance()->OpenFile(); 145 } 146 147 if (storeRandomNumberStatus) { 148 G4String fileN = "currentRun"; 149 if (rngStatusEventsFlag) { 150 std::ostringstream os; 151 os << "run" << currentRun->GetRunID(); 152 fileN = os.str(); 153 } 154 StoreRNGStatus(fileN); 155 } 156 157 runAborted = false; 158 numberOfEventProcessed = 0; 159 } 160 161 //============================================================================// 162 163 void G4WorkerTaskRunManager::DoEventLoop(G4int n_event, const char* macroFile, G4int n_select) 164 { 165 if (userPrimaryGeneratorAction == nullptr) { 166 G4Exception("G4RunManager::GenerateEvent()", "Run0032", FatalException, 167 "G4VUserPrimaryGeneratorAction is not defined!"); 168 } 169 170 // This is the same as in the sequential case, just the for-loop indexes are 171 // different 172 InitializeEventLoop(n_event, macroFile, n_select); 173 174 // Reset random number seeds queue 175 while (!seedsQueue.empty()) 176 seedsQueue.pop(); 177 // for each run, worker should receive at least one set of random number 178 // seeds. 179 // runIsSeeded = false; 180 181 // Event loop 182 eventLoopOnGoing = true; 183 G4int i_event = -1; 184 nevModulo = -1; 185 currEvID = -1; 186 187 for (G4int evt = 0; evt < n_event; ++evt) { 188 ProcessOneEvent(i_event); 189 if (eventLoopOnGoing) { 190 TerminateOneEvent(); 191 if (runAborted) eventLoopOnGoing = false; 192 } 193 if (!eventLoopOnGoing) break; 194 } 195 } 196 197 //============================================================================// 198 199 void G4WorkerTaskRunManager::ProcessOneEvent(G4int i_event) 200 { 201 currentEvent = GenerateEvent(i_event); 202 if (eventLoopOnGoing) { 203 eventManager->ProcessOneEvent(currentEvent); 204 AnalyzeEvent(currentEvent); 205 UpdateScoring(); 206 if (currentEvent->GetEventID() < n_select_msg) { 207 G4cout << "Applying command \"" << msgText << "\" @ " << __FUNCTION__ << ":" << __LINE__ 208 << G4endl; 209 G4UImanager::GetUIpointer()->ApplyCommand(msgText); 210 } 211 } 212 } 213 214 //============================================================================// 215 216 G4Event* G4WorkerTaskRunManager::GenerateEvent(G4int i_event) 217 { 218 auto anEvent = new G4Event(i_event); 219 G4long s1 = 0; 220 G4long s2 = 0; 221 G4long s3 = 0; 222 G4bool eventHasToBeSeeded = true; 223 if (G4MTRunManager::SeedOncePerCommunication() == 1 && runIsSeeded) eventHasToBeSeeded = false; 224 225 if (i_event < 0) { 226 G4int nevM = G4MTRunManager::GetMasterRunManager()->GetEventModulo(); 227 if (nevM == 1) { 228 eventLoopOnGoing = G4MTRunManager::GetMasterRunManager()->SetUpAnEvent(anEvent, s1, s2, s3, 229 eventHasToBeSeeded); 230 runIsSeeded = true; 231 } 232 else { 233 if (nevModulo <= 0) { 234 G4int nevToDo = G4MTRunManager::GetMasterRunManager()->SetUpNEvents(anEvent, &seedsQueue, 235 eventHasToBeSeeded); 236 if (nevToDo == 0) 237 eventLoopOnGoing = false; 238 else { 239 currEvID = anEvent->GetEventID(); 240 nevModulo = nevToDo - 1; 241 } 242 } 243 else { 244 if (G4MTRunManager::SeedOncePerCommunication() > 0) eventHasToBeSeeded = false; 245 anEvent->SetEventID(++currEvID); 246 nevModulo--; 247 } 248 if (eventLoopOnGoing && eventHasToBeSeeded) { 249 s1 = seedsQueue.front(); 250 seedsQueue.pop(); 251 s2 = seedsQueue.front(); 252 seedsQueue.pop(); 253 } 254 } 255 256 if (!eventLoopOnGoing) { 257 anEvent->ScoresRecorded(); 258 delete anEvent; 259 return nullptr; 260 } 261 } 262 else if (eventHasToBeSeeded) { 263 // Need to reseed random number generator 264 G4RNGHelper* helper = G4RNGHelper::GetInstance(); 265 s1 = helper->GetSeed(i_event * 2); 266 s2 = helper->GetSeed(i_event * 2 + 1); 267 } 268 269 if (eventHasToBeSeeded) { 270 G4long seeds[3] = {s1, s2, 0}; 271 G4Random::setTheSeeds(seeds, -1); 272 runIsSeeded = true; 273 } 274 275 // Read from file seed. 276 // Andrea Dotti 4 November 2015 277 // This is required for strong-reproducibility, in MT mode we have that each 278 // thread produces, for each event a status file, we want to do that. 279 // Search a random file with the format run{%d}evt{%d}.rndm 280 281 // This is the filename base constructed from run and event 282 const auto filename = [&] { 283 std::ostringstream os; 284 os << "run" << currentRun->GetRunID() << "evt" << anEvent->GetEventID(); 285 return os.str(); 286 }; 287 288 G4bool RNGstatusReadFromFile = false; 289 if (readStatusFromFile) { 290 // Build full path of RNG status file for this event 291 std::ostringstream os; 292 os << filename() << ".rndm"; 293 const G4String& randomStatusFile = os.str(); 294 std::ifstream ifile(randomStatusFile.c_str()); 295 if (ifile) { 296 // File valid and readable 297 RNGstatusReadFromFile = true; 298 G4Random::restoreEngineStatus(randomStatusFile.c_str()); 299 } 300 } 301 302 if (storeRandomNumberStatusToG4Event == 1 || storeRandomNumberStatusToG4Event == 3) { 303 std::ostringstream oss; 304 G4Random::saveFullState(oss); 305 randomNumberStatusForThisEvent = oss.str(); 306 anEvent->SetRandomNumberStatus(randomNumberStatusForThisEvent); 307 } 308 309 if (storeRandomNumberStatus && !RNGstatusReadFromFile) { 310 // If reading from file, avoid to rewrite the same 311 G4String fileN = "currentEvent"; 312 if (rngStatusEventsFlag) fileN = filename(); 313 StoreRNGStatus(fileN); 314 } 315 316 if (printModulo > 0 && anEvent->GetEventID() % printModulo == 0) { 317 G4cout << "--> Event " << anEvent->GetEventID() << " starts"; 318 if (eventHasToBeSeeded) G4cout << " with initial seeds (" << s1 << "," << s2 << ")"; 319 G4cout << "." << G4endl; 320 } 321 userPrimaryGeneratorAction->GeneratePrimaries(anEvent); 322 return anEvent; 323 } 324 325 //============================================================================// 326 327 void G4WorkerTaskRunManager::RunTermination() 328 { 329 if (!fakeRun && (currentRun != nullptr)) { 330 MergePartialResults(); 331 332 // Call a user hook: note this is before the next barrier 333 // so threads execute this method asyncrhonouzly 334 //(TerminateRun allows for synch via G4RunAction::EndOfRun) 335 const G4UserWorkerInitialization* uwi = 336 G4MTRunManager::GetMasterRunManager()->GetUserWorkerInitialization(); 337 if (uwi != nullptr) uwi->WorkerRunEnd(); 338 } 339 340 if (currentRun != nullptr) { 341 G4RunManager::RunTermination(); 342 } 343 // Signal this thread has finished envent-loop. 344 // Note this will return only whan all threads reach this point 345 G4MTRunManager::GetMasterRunManager()->ThisWorkerEndEventLoop(); 346 } 347 348 //============================================================================// 349 350 void G4WorkerTaskRunManager::TerminateEventLoop() 351 { 352 if (verboseLevel > 0 && !fakeRun) { 353 timer->Stop(); 354 // prefix with thread # info due to how TBB calls this function 355 G4String prefix = "[thread " + std::to_string(workerContext->GetThreadId()) + "] "; 356 G4cout << prefix << "Thread-local run terminated." << G4endl; 357 G4cout << prefix << "Run Summary" << G4endl; 358 if (runAborted) 359 G4cout << prefix << " Run Aborted after " << numberOfEventProcessed << " events processed." 360 << G4endl; 361 else 362 G4cout << prefix << " Number of events processed : " << numberOfEventProcessed << G4endl; 363 G4cout << prefix << " " << *timer << G4endl; 364 } 365 } 366 367 //============================================================================// 368 369 void G4WorkerTaskRunManager::SetupDefaultRNGEngine() 370 { 371 const CLHEP::HepRandomEngine* mrnge = 372 G4MTRunManager::GetMasterRunManager()->getMasterRandomEngine(); 373 assert(mrnge); // Master has created RNG 374 const G4UserWorkerThreadInitialization* uwti = 375 G4MTRunManager::GetMasterRunManager()->GetUserWorkerThreadInitialization(); 376 uwti->SetupRNGEngine(mrnge); 377 } 378 379 //============================================================================// 380 381 void G4WorkerTaskRunManager::StoreRNGStatus(const G4String& fn) 382 { 383 std::ostringstream os; 384 os << randomNumberStatusDir << "G4Worker" << workerContext->GetThreadId() << "_" << fn << ".rndm"; 385 G4Random::saveEngineStatus(os.str().c_str()); 386 } 387 388 //============================================================================// 389 390 void G4WorkerTaskRunManager::ProcessUI() 391 { 392 G4TaskRunManager* mrm = G4TaskRunManager::GetMasterRunManager(); 393 if (mrm == nullptr) return; 394 395 //------------------------------------------------------------------------// 396 // Check UI commands not already processed 397 auto command_stack = mrm->GetCommandStack(); 398 bool matching = (command_stack.size() == processedCommandStack.size()); 399 if (matching) { 400 for (uintmax_t i = 0; i < command_stack.size(); ++i) 401 if (processedCommandStack.at(i) != command_stack.at(i)) { 402 matching = false; 403 break; 404 } 405 } 406 407 //------------------------------------------------------------------------// 408 // Execute UI commands stored in the master UI manager 409 if (!matching) { 410 for (const auto& itr : command_stack) 411 G4UImanager::GetUIpointer()->ApplyCommand(itr); 412 processedCommandStack = std::move(command_stack); 413 } 414 } 415 416 //============================================================================// 417 418 void G4WorkerTaskRunManager::DoCleanup() 419 { 420 CleanUpPreviousEvents(); 421 422 delete currentRun; 423 currentRun = nullptr; 424 } 425 426 //============================================================================// 427 428 void G4WorkerTaskRunManager::DoWork() 429 { 430 G4TaskRunManager* mrm = G4TaskRunManager::GetMasterRunManager(); 431 G4bool newRun = false; 432 const G4Run* run = mrm->GetCurrentRun(); 433 G4ThreadLocalStatic G4int runId = -1; 434 if ((run != nullptr) && run->GetRunID() != runId) { 435 runId = run->GetRunID(); 436 newRun = true; 437 if (runId > 0) { 438 ProcessUI(); 439 assert(workerContext != nullptr); 440 } 441 workerContext->UpdateGeometryAndPhysicsVectorFromMaster(); 442 } 443 444 // Start this run 445 G4int nevts = mrm->GetNumberOfEventsPerTask(); 446 G4int numSelect = mrm->GetNumberOfSelectEvents(); 447 G4String macroFile = mrm->GetSelectMacro(); 448 G4bool empty_macro = (macroFile.empty() || macroFile == " "); 449 450 const char* macro = (empty_macro) ? nullptr : macroFile.c_str(); 451 numSelect = (empty_macro) ? -1 : numSelect; 452 453 if (newRun) { 454 G4bool cond = ConfirmBeamOnCondition(); 455 if (cond) { 456 ConstructScoringWorlds(); 457 RunInitialization(); 458 } 459 } 460 DoEventLoop(nevts, macro, numSelect); 461 } 462 463 //============================================================================// 464