// // ******************************************************************** // * License and Disclaimer * // * * // * The Geant4 software is copyright of the Copyright Holders of * // * the Geant4 Collaboration. It is provided under the terms and * // * conditions of the Geant4 Software License, included in the file * // * LICENSE and available at http://cern.ch/geant4/license . These * // * include a list of copyright holders. * // * * // * Neither the authors of this software system, nor their employing * // * institutes,nor the agencies providing financial support for this * // * work make any representation or warranty, express or implied, * // * regarding this software system or assume any liability for its * // * use. Please see the license in the file LICENSE and URL above * // * for the full disclaimer and the limitation of liability. * // * * // * This code implementation is the result of the scientific and * // * technical work of the GEANT4 collaboration. * // * By using, copying, modifying or distributing the software (or * // * any work based on the software) you agree to acknowledge its * // * use in resulting scientific publications, and indicate your * // * acceptance of all terms of the Geant4 Software license. * // ******************************************************************** // // #include "G4WorkerTaskRunManager.hh" #include "G4AutoLock.hh" #include "G4MTRunManager.hh" #include "G4ParallelWorldProcess.hh" #include "G4ParallelWorldProcessStore.hh" #include "G4RNGHelper.hh" #include "G4Run.hh" #include "G4SDManager.hh" #include "G4ScoringManager.hh" #include "G4TaskRunManager.hh" #include "G4Timer.hh" #include "G4TransportationManager.hh" #include "G4UImanager.hh" #include "G4UserRunAction.hh" #include "G4UserWorkerInitialization.hh" #include "G4UserWorkerThreadInitialization.hh" #include "G4VScoreNtupleWriter.hh" #include "G4VScoringMesh.hh" #include "G4VUserActionInitialization.hh" #include "G4VUserDetectorConstruction.hh" #include "G4VUserPhysicsList.hh" #include "G4VUserPrimaryGeneratorAction.hh" #include "G4VVisManager.hh" #include "G4WorkerTaskRunManagerKernel.hh" #include "G4WorkerThread.hh" #include #include //============================================================================// G4WorkerTaskRunManager* G4WorkerTaskRunManager::GetWorkerRunManager() { return static_cast(G4RunManager::GetRunManager()); } //============================================================================// G4WorkerTaskRunManagerKernel* G4WorkerTaskRunManager::GetWorkerRunManagerKernel() { return static_cast(GetWorkerRunManager()->kernel); } //============================================================================// void G4WorkerTaskRunManager::RunInitialization() { #ifdef G4MULTITHREADED if (!visIsSetUp) { G4VVisManager* pVVis = G4VVisManager::GetConcreteInstance(); if (pVVis != nullptr) { pVVis->SetUpForAThread(); visIsSetUp = true; } } #endif runIsSeeded = false; if (!(kernel->RunInitialization(fakeRun))) return; // Signal this thread can start event loop. // Note this will return only when all threads reach this point G4MTRunManager::GetMasterRunManager()->ThisWorkerReady(); if (fakeRun) return; const G4UserWorkerInitialization* uwi = G4MTRunManager::GetMasterRunManager()->GetUserWorkerInitialization(); CleanUpPreviousEvents(); delete currentRun; currentRun = nullptr; if (IfGeometryHasBeenDestroyed()) G4ParallelWorldProcessStore::GetInstance()->UpdateWorlds(); // Call a user hook: this is guaranteed all threads are "synchronized" if (uwi != nullptr) uwi->WorkerRunStart(); if (userRunAction != nullptr) currentRun = userRunAction->GenerateRun(); if (currentRun == nullptr) currentRun = new G4Run(); currentRun->SetRunID(runIDCounter); G4TaskRunManager* mrm = G4TaskRunManager::GetMasterRunManager(); numberOfEventToBeProcessed = mrm->GetNumberOfEventsToBeProcessed(); currentRun->SetNumberOfEventToBeProcessed(numberOfEventToBeProcessed); currentRun->SetDCtable(DCtable); G4SDManager* fSDM = G4SDManager::GetSDMpointerIfExist(); if (fSDM != nullptr) { currentRun->SetHCtable(fSDM->GetHCtable()); } if (G4VScoreNtupleWriter::Instance() != nullptr) { auto hce = (fSDM != nullptr) ? fSDM->PrepareNewEvent() : nullptr; isScoreNtupleWriter = G4VScoreNtupleWriter::Instance()->Book(hce); delete hce; } std::ostringstream oss; G4Random::saveFullState(oss); randomNumberStatusForThisRun = oss.str(); currentRun->SetRandomNumberStatus(randomNumberStatusForThisRun); for (G4int i_prev = 0; i_prev < n_perviousEventsToBeStored; ++i_prev) previousEvents->push_back(nullptr); if (printModulo > 0 || verboseLevel > 0) { G4cout << "### Run " << currentRun->GetRunID() << " starts on worker thread " << G4Threading::G4GetThreadId() << "." << G4endl; } if (userRunAction != nullptr) userRunAction->BeginOfRunAction(currentRun); if (isScoreNtupleWriter) { G4VScoreNtupleWriter::Instance()->OpenFile(); } if (storeRandomNumberStatus) { G4String fileN = "currentRun"; if (rngStatusEventsFlag) { std::ostringstream os; os << "run" << currentRun->GetRunID(); fileN = os.str(); } StoreRNGStatus(fileN); } runAborted = false; numberOfEventProcessed = 0; } //============================================================================// void G4WorkerTaskRunManager::DoEventLoop(G4int n_event, const char* macroFile, G4int n_select) { if (userPrimaryGeneratorAction == nullptr) { G4Exception("G4RunManager::GenerateEvent()", "Run0032", FatalException, "G4VUserPrimaryGeneratorAction is not defined!"); } // This is the same as in the sequential case, just the for-loop indexes are // different InitializeEventLoop(n_event, macroFile, n_select); // Reset random number seeds queue while (!seedsQueue.empty()) seedsQueue.pop(); // for each run, worker should receive at least one set of random number // seeds. // runIsSeeded = false; // Event loop eventLoopOnGoing = true; G4int i_event = -1; nevModulo = -1; currEvID = -1; for (G4int evt = 0; evt < n_event; ++evt) { ProcessOneEvent(i_event); if (eventLoopOnGoing) { TerminateOneEvent(); if (runAborted) eventLoopOnGoing = false; } if (!eventLoopOnGoing) break; } } //============================================================================// void G4WorkerTaskRunManager::ProcessOneEvent(G4int i_event) { currentEvent = GenerateEvent(i_event); if (eventLoopOnGoing) { eventManager->ProcessOneEvent(currentEvent); AnalyzeEvent(currentEvent); UpdateScoring(); if (currentEvent->GetEventID() < n_select_msg) { G4cout << "Applying command \"" << msgText << "\" @ " << __FUNCTION__ << ":" << __LINE__ << G4endl; G4UImanager::GetUIpointer()->ApplyCommand(msgText); } } } //============================================================================// G4Event* G4WorkerTaskRunManager::GenerateEvent(G4int i_event) { auto anEvent = new G4Event(i_event); G4long s1 = 0; G4long s2 = 0; G4long s3 = 0; G4bool eventHasToBeSeeded = true; if (G4MTRunManager::SeedOncePerCommunication() == 1 && runIsSeeded) eventHasToBeSeeded = false; if (i_event < 0) { G4int nevM = G4MTRunManager::GetMasterRunManager()->GetEventModulo(); if (nevM == 1) { eventLoopOnGoing = G4MTRunManager::GetMasterRunManager()->SetUpAnEvent(anEvent, s1, s2, s3, eventHasToBeSeeded); runIsSeeded = true; } else { if (nevModulo <= 0) { G4int nevToDo = G4MTRunManager::GetMasterRunManager()->SetUpNEvents(anEvent, &seedsQueue, eventHasToBeSeeded); if (nevToDo == 0) eventLoopOnGoing = false; else { currEvID = anEvent->GetEventID(); nevModulo = nevToDo - 1; } } else { if (G4MTRunManager::SeedOncePerCommunication() > 0) eventHasToBeSeeded = false; anEvent->SetEventID(++currEvID); nevModulo--; } if (eventLoopOnGoing && eventHasToBeSeeded) { s1 = seedsQueue.front(); seedsQueue.pop(); s2 = seedsQueue.front(); seedsQueue.pop(); } } if (!eventLoopOnGoing) { anEvent->ScoresRecorded(); delete anEvent; return nullptr; } } else if (eventHasToBeSeeded) { // Need to reseed random number generator G4RNGHelper* helper = G4RNGHelper::GetInstance(); s1 = helper->GetSeed(i_event * 2); s2 = helper->GetSeed(i_event * 2 + 1); } if (eventHasToBeSeeded) { G4long seeds[3] = {s1, s2, 0}; G4Random::setTheSeeds(seeds, -1); runIsSeeded = true; } // Read from file seed. // Andrea Dotti 4 November 2015 // This is required for strong-reproducibility, in MT mode we have that each // thread produces, for each event a status file, we want to do that. // Search a random file with the format run{%d}evt{%d}.rndm // This is the filename base constructed from run and event const auto filename = [&] { std::ostringstream os; os << "run" << currentRun->GetRunID() << "evt" << anEvent->GetEventID(); return os.str(); }; G4bool RNGstatusReadFromFile = false; if (readStatusFromFile) { // Build full path of RNG status file for this event std::ostringstream os; os << filename() << ".rndm"; const G4String& randomStatusFile = os.str(); std::ifstream ifile(randomStatusFile.c_str()); if (ifile) { // File valid and readable RNGstatusReadFromFile = true; G4Random::restoreEngineStatus(randomStatusFile.c_str()); } } if (storeRandomNumberStatusToG4Event == 1 || storeRandomNumberStatusToG4Event == 3) { std::ostringstream oss; G4Random::saveFullState(oss); randomNumberStatusForThisEvent = oss.str(); anEvent->SetRandomNumberStatus(randomNumberStatusForThisEvent); } if (storeRandomNumberStatus && !RNGstatusReadFromFile) { // If reading from file, avoid to rewrite the same G4String fileN = "currentEvent"; if (rngStatusEventsFlag) fileN = filename(); StoreRNGStatus(fileN); } if (printModulo > 0 && anEvent->GetEventID() % printModulo == 0) { G4cout << "--> Event " << anEvent->GetEventID() << " starts"; if (eventHasToBeSeeded) G4cout << " with initial seeds (" << s1 << "," << s2 << ")"; G4cout << "." << G4endl; } userPrimaryGeneratorAction->GeneratePrimaries(anEvent); return anEvent; } //============================================================================// void G4WorkerTaskRunManager::RunTermination() { if (!fakeRun && (currentRun != nullptr)) { MergePartialResults(); // Call a user hook: note this is before the next barrier // so threads execute this method asyncrhonouzly //(TerminateRun allows for synch via G4RunAction::EndOfRun) const G4UserWorkerInitialization* uwi = G4MTRunManager::GetMasterRunManager()->GetUserWorkerInitialization(); if (uwi != nullptr) uwi->WorkerRunEnd(); } if (currentRun != nullptr) { G4RunManager::RunTermination(); } // Signal this thread has finished envent-loop. // Note this will return only whan all threads reach this point G4MTRunManager::GetMasterRunManager()->ThisWorkerEndEventLoop(); } //============================================================================// void G4WorkerTaskRunManager::TerminateEventLoop() { if (verboseLevel > 0 && !fakeRun) { timer->Stop(); // prefix with thread # info due to how TBB calls this function G4String prefix = "[thread " + std::to_string(workerContext->GetThreadId()) + "] "; G4cout << prefix << "Thread-local run terminated." << G4endl; G4cout << prefix << "Run Summary" << G4endl; if (runAborted) G4cout << prefix << " Run Aborted after " << numberOfEventProcessed << " events processed." << G4endl; else G4cout << prefix << " Number of events processed : " << numberOfEventProcessed << G4endl; G4cout << prefix << " " << *timer << G4endl; } } //============================================================================// void G4WorkerTaskRunManager::SetupDefaultRNGEngine() { const CLHEP::HepRandomEngine* mrnge = G4MTRunManager::GetMasterRunManager()->getMasterRandomEngine(); assert(mrnge); // Master has created RNG const G4UserWorkerThreadInitialization* uwti = G4MTRunManager::GetMasterRunManager()->GetUserWorkerThreadInitialization(); uwti->SetupRNGEngine(mrnge); } //============================================================================// void G4WorkerTaskRunManager::StoreRNGStatus(const G4String& fn) { std::ostringstream os; os << randomNumberStatusDir << "G4Worker" << workerContext->GetThreadId() << "_" << fn << ".rndm"; G4Random::saveEngineStatus(os.str().c_str()); } //============================================================================// void G4WorkerTaskRunManager::ProcessUI() { G4TaskRunManager* mrm = G4TaskRunManager::GetMasterRunManager(); if (mrm == nullptr) return; //------------------------------------------------------------------------// // Check UI commands not already processed auto command_stack = mrm->GetCommandStack(); bool matching = (command_stack.size() == processedCommandStack.size()); if (matching) { for (uintmax_t i = 0; i < command_stack.size(); ++i) if (processedCommandStack.at(i) != command_stack.at(i)) { matching = false; break; } } //------------------------------------------------------------------------// // Execute UI commands stored in the master UI manager if (!matching) { for (const auto& itr : command_stack) G4UImanager::GetUIpointer()->ApplyCommand(itr); processedCommandStack = std::move(command_stack); } } //============================================================================// void G4WorkerTaskRunManager::DoCleanup() { CleanUpPreviousEvents(); delete currentRun; currentRun = nullptr; } //============================================================================// void G4WorkerTaskRunManager::DoWork() { G4TaskRunManager* mrm = G4TaskRunManager::GetMasterRunManager(); G4bool newRun = false; const G4Run* run = mrm->GetCurrentRun(); G4ThreadLocalStatic G4int runId = -1; if ((run != nullptr) && run->GetRunID() != runId) { runId = run->GetRunID(); newRun = true; if (runId > 0) { ProcessUI(); assert(workerContext != nullptr); } workerContext->UpdateGeometryAndPhysicsVectorFromMaster(); } // Start this run G4int nevts = mrm->GetNumberOfEventsPerTask(); G4int numSelect = mrm->GetNumberOfSelectEvents(); G4String macroFile = mrm->GetSelectMacro(); G4bool empty_macro = (macroFile.empty() || macroFile == " "); const char* macro = (empty_macro) ? nullptr : macroFile.c_str(); numSelect = (empty_macro) ? -1 : numSelect; if (newRun) { G4bool cond = ConfirmBeamOnCondition(); if (cond) { ConstructScoringWorlds(); RunInitialization(); } } DoEventLoop(nevts, macro, numSelect); } //============================================================================//