src/tools/geant4/WorkerRunManager.cpp

Implementation of WorkerRunManager. More…

Detailed Description

Implementation of WorkerRunManager.

Copyright: Copyright (c) 2019-2024 CERN and the Allpix Squared authors. This software is distributed under the terms of the MIT License, copied verbatim in the file “LICENSE.md”. In applying this license, CERN does not waive the privileges and immunities granted to it by virtue of its status as an Intergovernmental Organization or submit itself to any jurisdiction. SPDX-License-Identifier: MIT

Source code


#include "WorkerRunManager.hpp"
#include "G4LoggingDestination.hpp"
#include "MTRunManager.hpp"
#include "SensitiveDetectorAndFieldConstruction.hpp"
#include "core/module/exceptions.h"

#include <magic_enum/magic_enum.hpp>

#include <atomic>

#include <G4MTRunManager.hh>
#include <G4StateManager.hh>
#include <G4Threading.hh>
#include <G4TransportationManager.hh>
#include <G4UImanager.hh>
#include <G4UserRunAction.hh>
#include <G4UserWorkerInitialization.hh>
#include <G4UserWorkerThreadInitialization.hh>
#include <G4VUserActionInitialization.hh>
#include <G4VUserPrimaryGeneratorAction.hh>
#include <G4WorkerRunManager.hh>
#include <G4WorkerThread.hh>

#include "tools/geant4/G4ExceptionHandler.hpp"

using namespace allpix;

namespace {
    // counter to give each worker a unique id
    std::atomic<int> counter;
} // namespace

WorkerRunManager::WorkerRunManager() {
    G4UImanager* ui_g4 = G4UImanager::GetUIpointer();
    ui_g4->SetCoutDestination(G4LoggingDestination::getInstance());
    // Set exception handler for Geant4 exceptions:
    G4StateManager::GetStateManager()->SetExceptionHandler(new G4ExceptionHandler());
}

WorkerRunManager::~WorkerRunManager() {
    G4MTRunManager* master_run_manager = G4MTRunManager::GetMasterRunManager();

    // Step-6: Terminate worker thread
    if(master_run_manager->GetUserWorkerInitialization() != nullptr) {
        master_run_manager->GetUserWorkerInitialization()->WorkerStop();
    }
}

void WorkerRunManager::BeamOn(G4int n_event, const char* macroFile, G4int n_select) { // NOLINT
    G4MTRunManager* mrm = G4MTRunManager::GetMasterRunManager();

    // G4Comment: Execute UI commands stored in the master UI manager
    std::vector<G4String> cmds = mrm->GetCommandStack();
    G4UImanager* uimgr = G4UImanager::GetUIpointer();        // TLS instance
    std::vector<G4String>::const_iterator it = cmds.begin(); // NOLINT
    for(; it != cmds.end(); it++) {
        uimgr->ApplyCommand(*it);
    }

    G4RunManager::BeamOn(n_event, macroFile, n_select);
}

void WorkerRunManager::InitializeGeometry() {
    if(userDetector == nullptr) {
        throw ModuleError("G4VUserDetectorConstruction is not defined!");
    }
    if(fGeometryHasBeenDestroyed) {
        G4TransportationManager::GetTransportationManager()->ClearParallelWorlds();
    }

    // Step1: Get pointer to the physiWorld (note: needs to get the "super pointer, i.e. the one shared by all threads"
    G4RunManagerKernel* masterKernel = G4MTRunManager::GetMasterRunManagerKernel();
    G4VPhysicalVolume* worldVol = masterKernel->GetCurrentWorld();
    // Step2:, Call a new "WorkerDefineWorldVolume( pointer from 2-, false);
    kernel->WorkerDefineWorldVolume(worldVol, false);
    kernel->SetNumberOfParallelWorld(masterKernel->GetNumberOfParallelWorld());
    // Step3: Call user's ConstructSDandField()
    auto* master_run_manager = static_cast<MTRunManager*>(G4MTRunManager::GetMasterRunManager());
    SensitiveDetectorAndFieldConstruction* detector_construction = master_run_manager->GetSDAndFieldConstruction();
    if(detector_construction == nullptr) {
        throw ModuleError("DetectorConstruction is not defined!");
    }
    detector_construction->ConstructSDandField();
    // userDetector->ConstructParallelSD();
    geometryInitialized = true;
}

void WorkerRunManager::DoEventLoop(G4int n_event, const char* macroFile, G4int n_select) { // NOLINT
    if(userPrimaryGeneratorAction == nullptr) {
        throw ModuleError("G4VUserPrimaryGeneratorAction is not defined!");
    }

    InitializeEventLoop(n_event, macroFile, n_select);

    // For each run, worker should receive exactly one set of random number seeds.
    runIsSeeded = false;

    // Event loop
    eventLoopOnGoing = true;
    nevModulo = -1;
    currEvID = -1;
    while(eventLoopOnGoing) {
        ProcessOneEvent(-1);
        if(eventLoopOnGoing) {
            TerminateOneEvent();
            if(runAborted) {
                eventLoopOnGoing = false;
            }
        }
    }

    TerminateEventLoop();
}

G4Event* WorkerRunManager::GenerateEvent(G4int) {
    if(userPrimaryGeneratorAction == nullptr) {
        throw ModuleError("G4VUserPrimaryGeneratorAction is not defined!");
    }

    G4Event* anEvent = nullptr;

    if(numberOfEventProcessed < numberOfEventToBeProcessed && !runAborted) {
        anEvent = new G4Event(numberOfEventProcessed);

        if(!runIsSeeded) {
            // Seeds are stored in this queue to ensure we can reproduce the results of events
            // each event will reseed the random number generator
            long s1 = seedsQueue.front();
            seedsQueue.pop();
            long s2 = seedsQueue.front();
            seedsQueue.pop();

            // Seed RNG for this run only once
            long seeds[3] = {s1, s2, 0}; // NOLINT
            G4Random::setTheSeeds(seeds, -1);
            runIsSeeded = true;
        }

        userPrimaryGeneratorAction->GeneratePrimaries(anEvent);
    } else {
        // This flag must be set so the event loop exits if no more events
        // to be processed
        eventLoopOnGoing = false;
    }

    return anEvent;
}

WorkerRunManager* WorkerRunManager::GetNewInstanceForThread() { // NOLINT
    WorkerRunManager* thread_run_manager = nullptr;
    G4MTRunManager* master_run_manager = G4MTRunManager::GetMasterRunManager();

    // Step-0: Thread ID
    // Initliazie per-thread stream-output
    // The following line is needed before we actually do I/O initialization
    // because the constructor of UI manager resets the I/O destination.
    G4int this_id = counter.fetch_add(1);
    G4Threading::G4SetThreadId(this_id);
    G4UImanager::GetUIpointer()->SetUpForAThread(this_id);

    // Step-1: Random number engine
    // RNG Engine needs to be initialized by "cloning" the master one.
    const CLHEP::HepRandomEngine* master_engine = master_run_manager->getMasterRandomEngine();
    master_run_manager->GetUserWorkerThreadInitialization()->SetupRNGEngine(master_engine);

    // Step-2: Initialize worker thread
    if(master_run_manager->GetUserWorkerInitialization() != nullptr) {
        master_run_manager->GetUserWorkerInitialization()->WorkerInitialize();
    }

    if(master_run_manager->GetUserActionInitialization() != nullptr) {
        G4VSteppingVerbose* stepping_verbose =
            master_run_manager->GetUserActionInitialization()->InitializeSteppingVerbose();
        if(stepping_verbose != nullptr) {
            G4VSteppingVerbose::SetInstance(stepping_verbose);
        }
    }

    // Now initialize worker part of shared objects (geometry/physics)
    G4WorkerThread::BuildGeometryAndPhysicsVector();

    // create the new instance
    thread_run_manager = new WorkerRunManager;

    // Step-3: Setup worker run manager
    // Set the detector and physics list to the worker thread. Share with master
    const G4VUserDetectorConstruction* detector = master_run_manager->GetUserDetectorConstruction();

    thread_run_manager->G4RunManager::SetUserInitialization(const_cast<G4VUserDetectorConstruction*>(detector)); // NOLINT

    const G4VUserPhysicsList* physicslist = master_run_manager->GetUserPhysicsList();
    thread_run_manager->SetUserInitialization(const_cast<G4VUserPhysicsList*>(physicslist)); // NOLINT

    // Step-4: Initialize worker run manager
    if(master_run_manager->GetUserActionInitialization() != nullptr) {
        master_run_manager->GetNonConstUserActionInitialization()->Build();
    }

    if(master_run_manager->GetUserWorkerInitialization() != nullptr) {
        master_run_manager->GetUserWorkerInitialization()->WorkerStart();
    }

    thread_run_manager->Initialize();

    // Execute UI commands stored in the master UI manager
    std::vector<G4String> cmds = master_run_manager->GetCommandStack();
    G4UImanager* uimgr = G4UImanager::GetUIpointer();        // TLS instance
    std::vector<G4String>::const_iterator it = cmds.begin(); // NOLINT
    for(; it != cmds.end(); it++) {
        uimgr->ApplyCommand(*it);
    }

    return thread_run_manager;
}

void WorkerRunManager::AbortRun(bool 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;
        if(currentState == G4State_EventProc && !softAbort) {
            currentEvent->SetEventAborted();
            eventManager->AbortCurrentEvent();
            LOG(TRACE) << "Aborted Geant4 event";
        }
        // Ready for new event, set the state back to G4State_Idle
        G4StateManager::GetStateManager()->SetNewState(G4State_Idle);
        LOG(DEBUG) << "Reset Geant4 state to "
                   << magic_enum::enum_name(G4StateManager::GetStateManager()->GetCurrentState());
    } else {

        LOG(WARNING) << "Run is not in progress. AbortRun() ignored." << G4endl;
    }
}

Updated on 2024-12-13 at 08:31:37 +0000