// // ******************************************************************** // * 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 "G4SubEvtRunManager.hh" #include "G4AutoLock.hh" #include "G4EnvironmentUtils.hh" #include "G4ProductionCutsTable.hh" #include "G4Run.hh" #include "G4ScoringManager.hh" #include "G4StateManager.hh" #include "G4Task.hh" #include "G4TaskGroup.hh" #include "G4TaskManager.hh" #include "G4TaskRunManagerKernel.hh" #include "G4ThreadLocalSingleton.hh" #include "G4ThreadPool.hh" #include "G4Threading.hh" #include "G4Timer.hh" #include "G4TransportationManager.hh" #include "G4UImanager.hh" #include "G4UserRunAction.hh" #include "G4UserTaskInitialization.hh" #include "G4UserTaskQueue.hh" #include "G4UserSubEvtThreadInitialization.hh" #include "G4VUserActionInitialization.hh" #include "G4VUserPhysicsList.hh" #include "G4UserEventAction.hh" #include "G4VScoreNtupleWriter.hh" #include "G4WorkerSubEvtRunManager.hh" #include "G4WorkerThread.hh" #include "G4VVisManager.hh" #include #include #include #include //============================================================================// namespace { G4Mutex scorerMergerMutex; G4Mutex accessSubEventMutex; } // namespace //============================================================================// G4SubEvtRunManager::G4SubEvtRunManager(G4VUserTaskQueue* task_queue, G4bool useTBB, G4int grainsize) : G4TaskRunManager(task_queue, useTBB, grainsize) { runManagerType = subEventMasterRM; } //============================================================================// G4SubEvtRunManager::G4SubEvtRunManager(G4bool useTBB) : G4SubEvtRunManager(nullptr, useTBB, 0) { runManagerType = subEventMasterRM; } //============================================================================// G4SubEvtRunManager::~G4SubEvtRunManager() { // relying all the necessary deletion upon the base class // G4TaskRunManager::~G4TaskRunManager() } //============================================================================// void G4SubEvtRunManager::Initialize() { G4bool firstTime = (threadPool == nullptr); if (firstTime) G4TaskRunManager::InitializeThreadPool(); G4RunManager::Initialize(); // make sure all worker threads are set up. G4RunManager::BeamOn(0); if (firstTime) G4RunManager::SetRunIDCounter(0); // G4UImanager::GetUIpointer()->SetIgnoreCmdNotFound(true); } //============================================================================// void G4SubEvtRunManager::RunInitialization() { G4RunManager::RunInitialization(); if(!fakeRun) runInProgress = true; } //============================================================================// void G4SubEvtRunManager::ProcessOneEvent(G4int i_event) { currentEvent = GenerateEvent(i_event); eventManager->ProcessOneEventForSERM(currentEvent); // Following two lines should not be executed here, as spawned sub-events may // be still being processed. These methods are invoked when all sub-events belongings // to this event are processed and scores of these sub-events are marged to the // corresponding master event. //AnalyzeEvent(currentEvent); //UpdateScoring(); if (i_event < n_select_msg) G4UImanager::GetUIpointer()->ApplyCommand(msgText); } //============================================================================// void G4SubEvtRunManager::TerminateOneEvent() { // We must serialize access here because workers may access Run/Event vectors // and we can't override StackPreviousEvent G4AutoLock l(&accessSubEventMutex); StackPreviousEvent(currentEvent); currentEvent = nullptr; ++numberOfEventProcessed; } //============================================================================// void G4SubEvtRunManager::StackPreviousEvent(G4Event* anEvent) { if(n_perviousEventsToBeStored>0) { G4ExceptionDescription ed; ed << "G4RunManager::SetNumberOfEventsToBeStored() is not supported in sub-event parallel mode.\n" << "User may still keep events bu G4EventManager::KeepTheCurrentEvent()"; G4Exception("G4SubEvtRunManager::StackPreviousEvent","SubEvtRM1200",FatalException,ed); return; } if(anEvent->GetNumberOfRemainingSubEvents()>0) // sub-events are still under processing. Event is not yet fully completed. { currentRun->StoreEvent(anEvent); } else // Event is already completed. { // making sure this is the first path if(!(anEvent->IsEventCompleted())) { anEvent->EventCompleted(); if(userEventAction!=nullptr) userEventAction->EndOfEventAction(anEvent); auto pVisManager = G4VVisManager::GetConcreteInstance(); if (pVisManager) pVisManager->EventReadyForVis(anEvent); UpdateScoring(anEvent); if(!(anEvent->ToBeKept())) { ReportEventDeletion(anEvent); delete anEvent; } else { // we keep this event for post-processing (i.e. for vis) currentRun->StoreEvent(anEvent); } } else { G4Exception("G4SubEvtRunManager::StackPreviousEvent","SubEvtRM1209",FatalException,"We should not be here!!"); } } CleanUpUnnecessaryEvents(0); } //============================================================================// void G4SubEvtRunManager::CleanUpUnnecessaryEvents(G4int keepNEvents) { // Delete events that are no longer necessary for post // processing such as visualization. // N.B. If ToBeKept() is true, the pointer of this event is // kept in G4Run, and deleted along with the deletion of G4Run. if(keepNEvents>0) { G4ExceptionDescription ed; ed << "G4RunManager::SetNumberOfEventsToBeStored() is not supported in sub-event parallel mode.\n" << "User may still keep events bu G4EventManager::KeepTheCurrentEvent()"; G4Exception("G4SubEvtRunManager::CleanUpUnnecessaryEvents","SubEvtRM1201",FatalException,ed); return; } assert(currentRun!=nullptr); auto eventVector = currentRun->GetEventVector(); if(eventVector==nullptr || eventVector->empty()) return; auto eItr = eventVector->cbegin(); while(eItr != eventVector->cend()) { const G4Event* ev = *eItr; if(ev!=nullptr) { if(!(ev->IsEventCompleted()) && ev->GetNumberOfRemainingSubEvents()==0) { // This event has been completed since last time we were here ev->EventCompleted(); if(userEventAction!=nullptr) userEventAction->EndOfEventAction(ev); auto pVisManager = G4VVisManager::GetConcreteInstance(); if (pVisManager) pVisManager->EventReadyForVis(ev); UpdateScoring(ev); if(!(ev->ToBeKept())) { ReportEventDeletion(ev); delete ev; eItr = eventVector->erase(eItr); } else { // we keep this event for post-processing (i.e. for vis) eItr++; } } else if(!(ev->ToBeKept())) { // post-processing done. we no longer need this event ReportEventDeletion(ev); delete ev; eItr = eventVector->erase(eItr); } else { // we still need this event eItr++; } } else { // ev is a null pointer eItr = eventVector->erase(eItr); } } } //============================================================================// void G4SubEvtRunManager::CreateAndStartWorkers() { // Now loop on requested number of workers // This will also start the workers // Currently we do not allow to change the // number of threads: threads area created once // Instead of pthread based workers, create tbbTask static G4bool initializeStarted = false; ComputeNumberOfTasks(); if (fakeRun) { if (initializeStarted) { auto initCmdStack = GetCommandStack(); if (!initCmdStack.empty()) { threadPool->execute_on_all_threads([initCmdStack]() { for (auto& itr : initCmdStack) G4UImanager::GetUIpointer()->ApplyCommand(itr); G4WorkerTaskRunManager::GetWorkerRunManager()->DoWork(); }); } } else { std::stringstream msg; msg << "--> G4SubEvtRunManager::CreateAndStartWorkers() --> " << "Initializing workers..."; std::stringstream ss; ss.fill('='); ss << std::setw((G4int)msg.str().length()) << ""; G4cout << "\n" << ss.str() << "\n" << msg.str() << "\n" << ss.str() << "\n" << G4endl; G4TaskRunManagerKernel::InitCommandStack() = GetCommandStack(); threadPool->execute_on_all_threads([]() { G4TaskRunManagerKernel::InitializeWorker(); }); } initializeStarted = true; } else { auto initCmdStack = GetCommandStack(); if (!initCmdStack.empty()) { threadPool->execute_on_all_threads([initCmdStack]() { for (auto& itr : initCmdStack) G4UImanager::GetUIpointer()->ApplyCommand(itr); }); } // cleans up a previous run and events in case a thread // does not execute any tasks threadPool->execute_on_all_threads([]() { G4TaskRunManagerKernel::ExecuteWorkerInit(); }); { std::stringstream msg; msg << "--> G4SubEvtRunManager::CreateAndStartWorkers() --> " << "Creating " << numberOfTasks << " tasks with " << numberOfEventsPerTask << " events/task..."; std::stringstream ss; ss.fill('='); ss << std::setw((G4int)msg.str().length()) << ""; G4cout << "\n" << ss.str() << "\n" << msg.str() << "\n" << ss.str() << "\n" << G4endl; } /* TODO (PHASE-II): Better calculation of task/event/subevents Currently, number of tasks is equal to number of threads and each task has a loop that endlessly asks for next sub-event until no additional sub-event is available in the master. This is not ideal. We should make each task work only for some limited number of sub-events, and create as many number of tasks as needed on the fly during the event loop of the master thread., e.g. G4int remaining = numberOfEventToBeProcessed; for (G4int nt = 0; nt < numberOfTasks + 1; ++nt) { if (remaining > 0) AddEventTask(nt); remaining -= numberOfEventsPerTask; } */ for(G4int nt = 0; nt < numberOfTasks; ++nt) { AddEventTask(nt); } } } //============================================================================// void G4SubEvtRunManager::AddEventTask(G4int nt) { if (verboseLevel > 1) G4cout << "Adding task " << nt << " to task-group..." << G4endl; workTaskGroup->exec([]() { G4TaskRunManagerKernel::ExecuteWorkerTask(); }); } //============================================================================// void G4SubEvtRunManager::RefillSeeds() { G4RNGHelper* helper = G4RNGHelper::GetInstance(); G4int nFill = 0; switch (SeedOncePerCommunication()) { case 0: nFill = numberOfEventToBeProcessed - nSeedsFilled; break; case 1: nFill = numberOfTasks - nSeedsFilled; break; case 2: default: nFill = (numberOfEventToBeProcessed - nSeedsFilled * eventModulo) / eventModulo + 1; } // Generates up to nSeedsMax seed pairs only. if (nFill > nSeedsMax) nFill = nSeedsMax; masterRNGEngine->flatArray(nSeedsPerEvent * nFill, randDbl); helper->Refill(randDbl, nFill); nSeedsFilled += nFill; } //============================================================================// void G4SubEvtRunManager::InitializeEventLoop(G4int n_event, const char* macroFile, G4int n_select) { MTkernel->SetUpDecayChannels(); numberOfEventToBeProcessed = n_event; numberOfEventProcessed = 0; if (!fakeRun) { nSeedsUsed = 0; nSeedsFilled = 0; if (verboseLevel > 0) timer->Start(); n_select_msg = n_select; if (macroFile != nullptr) { if (n_select_msg < 0) n_select_msg = n_event; msgText = "/control/execute "; msgText += macroFile; selectMacro = macroFile; } else { n_select_msg = -1; selectMacro = ""; } ComputeNumberOfTasks(); // initialize seeds // If user did not implement InitializeSeeds, // use default: nSeedsPerEvent seeds per event if (n_event > 0) { G4bool _overload = InitializeSeeds(n_event); G4bool _functor = false; if (!_overload) _functor = initSeedsCallback(n_event, nSeedsPerEvent, nSeedsFilled); if (!_overload && !_functor) { G4RNGHelper* helper = G4RNGHelper::GetInstance(); switch (SeedOncePerCommunication()) { case 0: nSeedsFilled = n_event; break; case 1: nSeedsFilled = numberOfTasks; break; case 2: nSeedsFilled = n_event / eventModulo + 1; break; default: G4ExceptionDescription msgd; msgd << "Parameter value <" << SeedOncePerCommunication() << "> of seedOncePerCommunication is invalid. It is reset " "to 0."; G4Exception("G4SubEvtRunManager::InitializeEventLoop()", "Run10036", JustWarning, msgd); SetSeedOncePerCommunication(0); nSeedsFilled = n_event; } // Generates up to nSeedsMax seed pairs only. if (nSeedsFilled > nSeedsMax) nSeedsFilled = nSeedsMax; masterRNGEngine->flatArray(nSeedsPerEvent * nSeedsFilled, randDbl); helper->Fill(randDbl, nSeedsFilled, n_event, nSeedsPerEvent); } } } // Now initialize workers. Check if user defined a WorkerThreadInitialization if (userWorkerThreadInitialization == nullptr) userWorkerThreadInitialization = new G4UserSubEvtThreadInitialization(); // Prepare UI commands for threads PrepareCommandsStack(); // Start worker threads CreateAndStartWorkers(); } //============================================================================// void G4SubEvtRunManager::RunTermination() { // Wait for all worker threads to have finished the run // i.e. wait for them to return from RunTermination() // This guarantee that userrunaction for workers has been called runInProgress = false; //TODO (PHASE-II): do we need this??? workTaskGroup->wait(); // Wait now for all threads to finish event-loop WaitForEndEventLoopWorkers(); if(currentRun!=nullptr) CleanUpUnnecessaryEvents(0); // Now call base-class method G4RunManager::TerminateEventLoop(); G4RunManager::RunTermination(); } //============================================================================// void G4SubEvtRunManager::ConstructScoringWorlds() { masterScM = G4ScoringManager::GetScoringManagerIfExist(); // Call base class stuff... G4RunManager::ConstructScoringWorlds(); masterWorlds.clear(); auto nWorlds = (G4int)G4TransportationManager::GetTransportationManager()->GetNoWorlds(); auto itrW = G4TransportationManager::GetTransportationManager()->GetWorldsIterator(); for (G4int iWorld = 0; iWorld < nWorlds; ++iWorld) { addWorld(iWorld, *itrW); ++itrW; } } //============================================================================// void G4SubEvtRunManager::MergeScores(const G4ScoringManager* localScoringManager) { G4AutoLock l(&scorerMergerMutex); if (masterScM != nullptr) masterScM->Merge(localScoringManager); } const G4SubEvent* G4SubEvtRunManager::GetSubEvent(G4int ty, G4bool& notReady, G4long& s1, G4long& s2, G4long& s3, G4bool reseedRequired) { G4AutoLock l(&accessSubEventMutex); // This method is invoked from the worker, the ownership of G4SubEvent object // remains to the master, i.e. will be deleted by the master thread through // TerminateSubEvent() method. if(currentRun==nullptr) { // Run has not yet started. notReady = true; return nullptr; } auto eventVector = currentRun->GetEventVector(); // RACE HERE: against: // 1 G4Run::StoreEvent(G4Event*) G4Run.cc:80 // 2 G4RunManager::StackPreviousEvent(G4Event*) G4RunManager.cc:572 for(auto& ev : *eventVector) { // looping over stored events // RACE HERE: against: // 1 G4Run::StoreEvent(G4Event*) G4Run.cc:80 // 2 G4RunManager::StackPreviousEvent(G4Event*) G4RunManager.cc:572 auto se = const_cast(ev)->PopSubEvent(ty); if(se!=nullptr) { // Sub-event is found in an event that is already finished its event-loop notReady = false; if(reseedRequired) SetUpSeedsForSubEvent(s1,s2,s3); return se; } } auto sep = eventManager->PopSubEvent(ty); if(sep!=nullptr) { // Sub-event is found in an event that is still in the event loop notReady = false; if(reseedRequired) SetUpSeedsForSubEvent(s1,s2,s3); return sep; } else { // No sub-event available // RACE HERE vs line 345 if(runInProgress) { // Run is still in progress. Worker should wait until a sub-event is ready notReady = true; } else { // Run is over. No more sub-event to come unless new run starts. notReady = false; } return nullptr; } } void G4SubEvtRunManager::SetUpSeedsForSubEvent(G4long& s1, G4long& s2, G4long& s3) { //TODO (PHASE-II): Seeding scheme for sub-event has to be revisited G4RNGHelper* helper = G4RNGHelper::GetInstance(); G4int idx_rndm = nSeedsPerEvent * nSeedsUsed; s1 = helper->GetSeed(idx_rndm); s2 = helper->GetSeed(idx_rndm + 1); if (nSeedsPerEvent == 3) s3 = helper->GetSeed(idx_rndm + 2); ++nSeedsUsed; if (nSeedsUsed == nSeedsFilled) RefillSeeds(); } //============================================================================// // //G4bool G4SubEvtRunManager::SetUpAnEvent(G4Event* evt, G4long& s1, G4long& s2, G4long& s3, // G4bool reseedRequired) //{ // G4AutoLock l(&setUpEventMutex); // if (numberOfEventProcessed < numberOfEventToBeProcessed) { // evt->SetEventID(numberOfEventProcessed); // if (reseedRequired) { // G4RNGHelper* helper = G4RNGHelper::GetInstance(); // G4int idx_rndm = nSeedsPerEvent * nSeedsUsed; // s1 = helper->GetSeed(idx_rndm); // s2 = helper->GetSeed(idx_rndm + 1); // if (nSeedsPerEvent == 3) s3 = helper->GetSeed(idx_rndm + 2); // ++nSeedsUsed; // if (nSeedsUsed == nSeedsFilled) RefillSeeds(); // } // numberOfEventProcessed++; // return true; // } // return false; //} //============================================================================// // //G4int G4SubEvtRunManager::SetUpNEvents(G4Event* evt, G4SeedsQueue* seedsQueue, G4bool reseedRequired) //{ // G4AutoLock l(&setUpEventMutex); // if (numberOfEventProcessed < numberOfEventToBeProcessed && !runAborted) { // G4int nevt = numberOfEventsPerTask; // G4int nmod = eventModulo; // if (numberOfEventProcessed + nevt > numberOfEventToBeProcessed) { // nevt = numberOfEventToBeProcessed - numberOfEventProcessed; // nmod = numberOfEventToBeProcessed - numberOfEventProcessed; // } // evt->SetEventID(numberOfEventProcessed); // // if (reseedRequired) { // G4RNGHelper* helper = G4RNGHelper::GetInstance(); // G4int nevRnd = nmod; // if (SeedOncePerCommunication() > 0) nevRnd = 1; // for (G4int i = 0; i < nevRnd; ++i) { // seedsQueue->push(helper->GetSeed(nSeedsPerEvent * nSeedsUsed)); // seedsQueue->push(helper->GetSeed(nSeedsPerEvent * nSeedsUsed + 1)); // if (nSeedsPerEvent == 3) seedsQueue->push(helper->GetSeed(nSeedsPerEvent * nSeedsUsed + 2)); // nSeedsUsed++; // if (nSeedsUsed == nSeedsFilled) RefillSeeds(); // } // } // numberOfEventProcessed += nevt; // return nevt; // } // return 0; //} //============================================================================// void G4SubEvtRunManager::SubEventFinished(const G4SubEvent* se,const G4Event* evt) { G4AutoLock l(&accessSubEventMutex); UpdateScoringForSubEvent(se,evt); eventManager->TerminateSubEvent(se,evt); } //============================================================================// void G4SubEvtRunManager::UpdateScoringForSubEvent(const G4SubEvent* se,const G4Event* evt) { auto masterEvt = se->GetEvent(); if(masterEvt==nullptr) { G4Exception("G4SubEvtRunManager::UpdateScoringForSubEvent()","SERM0001", FatalException,"Pointer of master event is null. PANIC!"); } if(userEventAction) { userEventAction->MergeSubEvent(masterEvt,evt); } //if (isScoreNtupleWriter) { // G4VScoreNtupleWriter::Instance()->Fill(evt->GetHCofThisEvent(), // se->GetEvent()->GetEventID()); //} evt->ScoresRecorded(); G4ScoringManager* ScM = G4ScoringManager::GetScoringManagerIfExist(); if (ScM == nullptr) return; auto nPar = (G4int)ScM->GetNumberOfMesh(); if (nPar < 1) return; if(verboseLevel>2) { G4cout << "merging scores of sub-event belonging to event id #" << masterEvt->GetEventID() << " --- sub-event has " << evt->GetHCofThisEvent()->GetCapacity() << " hits collections" << G4endl; } G4HCofThisEvent* HCE = evt->GetHCofThisEvent(); if (HCE == nullptr) return; auto nColl = (G4int)HCE->GetCapacity(); for (G4int i = 0; i < nColl; ++i) { G4VHitsCollection* HC = HCE->GetHC(i); if (HC != nullptr) ScM->Accumulate(HC); } } //============================================================================// void G4SubEvtRunManager::CleanUpPreviousEvents() { // Delete all events carried over from previous run. // This method is invoked at the beginning of the next run // or from the destructor of G4RunManager at the very end of // the program. auto evItr = previousEvents->cbegin(); while (evItr != previousEvents->cend()) { G4Event* evt = *evItr; if (evt != nullptr) { ReportEventDeletion(evt); // remove evt from the event vector of G4Run as well if(currentRun!=nullptr) { auto eventVector = currentRun->GetEventVector(); auto eItr = std::find(eventVector->cbegin(),eventVector->cend(),evt); if(eItr != eventVector->cend()) eventVector->erase(eItr); } delete evt; } evItr = previousEvents->erase(evItr); } if(currentRun!=nullptr) { auto eventVector = currentRun->GetEventVector(); if(eventVector==nullptr || eventVector->empty()) return; auto eItr = eventVector->cbegin(); while(eItr != eventVector->cend()) { const G4Event* ev = *eItr; if(ev!=nullptr) { ReportEventDeletion(ev); delete ev; } eItr = eventVector->erase(eItr); } } } //============================================================================// void G4SubEvtRunManager::TerminateWorkers() { // Force workers to execute (if any) all UI commands left in the stack RequestWorkersProcessCommandsStack(); if (workTaskGroup != nullptr) { workTaskGroup->join(); if (!fakeRun) threadPool->execute_on_all_threads([]() { G4TaskRunManagerKernel::TerminateWorker(); }); } } //============================================================================// void G4SubEvtRunManager::AbortRun(G4bool softAbort) { // This method is valid only for GeomClosed or EventProc state G4ApplicationState currentState = G4StateManager::GetStateManager()->GetCurrentState(); if (currentState == G4State_GeomClosed || currentState == G4State_EventProc) { runAborted = true; MTkernel->BroadcastAbortRun(softAbort); } else { G4cerr << "Run is not in progress. AbortRun() ignored." << G4endl; } } //============================================================================// void G4SubEvtRunManager::AbortEvent() { // nothing to do in the master thread } //============================================================================// void G4SubEvtRunManager::WaitForEndEventLoopWorkers() { if (workTaskGroup != nullptr) { workTaskGroup->join(); if (!fakeRun) threadPool->execute_on_all_threads( []() { G4TaskRunManagerKernel::TerminateWorkerRunEventLoop(); }); } } //============================================================================// void G4SubEvtRunManager::RequestWorkersProcessCommandsStack() { PrepareCommandsStack(); auto process_commands_stack = []() { G4MTRunManager* mrm = G4MTRunManager::GetMasterRunManager(); if (mrm != nullptr) { auto cmds = mrm->GetCommandStack(); for (const auto& itr : cmds) G4UImanager::GetUIpointer()->ApplyCommand(itr); // TLS instance mrm->ThisWorkerProcessCommandsStackDone(); } }; if (threadPool != nullptr) threadPool->execute_on_all_threads(process_commands_stack); } //============================================================================// void G4SubEvtRunManager::ThisWorkerProcessCommandsStackDone() {} //============================================================================// void G4SubEvtRunManager::SetUserInitialization(G4UserWorkerInitialization* userInit) { userWorkerInitialization = userInit; } // -------------------------------------------------------------------- void G4SubEvtRunManager::SetUserInitialization(G4UserWorkerThreadInitialization* userInit) { userWorkerThreadInitialization = userInit; } // -------------------------------------------------------------------- void G4SubEvtRunManager::SetUserInitialization(G4VUserActionInitialization* userInit) { userActionInitialization = userInit; userActionInitialization->BuildForMaster(); } // -------------------------------------------------------------------- void G4SubEvtRunManager::SetUserInitialization(G4VUserDetectorConstruction* userDC) { G4RunManager::SetUserInitialization(userDC); } // -------------------------------------------------------------------- void G4SubEvtRunManager::SetUserInitialization(G4VUserPhysicsList* pl) { pl->InitializeWorker(); G4RunManager::SetUserInitialization(pl); } // -------------------------------------------------------------------- void G4SubEvtRunManager::SetUserAction(G4UserRunAction* userAction) { G4RunManager::SetUserAction(userAction); if (userAction != nullptr) userAction->SetMaster(true); } // -------------------------------------------------------------------- void G4SubEvtRunManager::SetUserAction(G4UserEventAction* ua) { G4RunManager::SetUserAction(ua); } // -------------------------------------------------------------------- void G4SubEvtRunManager::SetUserAction(G4VUserPrimaryGeneratorAction* ua) { G4RunManager::SetUserAction(ua); } // -------------------------------------------------------------------- void G4SubEvtRunManager::SetUserAction(G4UserStackingAction* ua) { G4RunManager::SetUserAction(ua); } // -------------------------------------------------------------------- void G4SubEvtRunManager::SetUserAction(G4UserTrackingAction* ua) { G4RunManager::SetUserAction(ua); } // -------------------------------------------------------------------- void G4SubEvtRunManager::SetUserAction(G4UserSteppingAction* ua) { G4RunManager::SetUserAction(ua); }