ATLAS Offline Software
Loading...
Searching...
No Matches
G4RunTool.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
5#include "G4RunTool.h"
7
8// Gaudi includes
9#include "GaudiKernel/ServiceHandle.h"
10
11// header files from Geant4
12#include "G4MTRunManager.hh"
13#include "G4StateManager.hh"
14#include "G4UImanager.hh"
15#include "G4GeometryManager.hh"
16#include "G4PhysicalVolumeStore.hh"
17#include "G4EventManager.hh"
18
20
21// Standard library
22#include <memory>
23
26
27 // Gaudi will automatically initialize child tools once this tool is initialized.
28 // We want these child tools to be initialized in the Geant4 main thread,
29 // disable() will tell Gaudi not to initialize them now.
30 // Ideally we'd move the initialization that needs to happen in the Geant4 main thread out of initialize
31 // and into a separate method called from Geant4main.
32 m_detConstruction.disable();
33 for(auto& tool : m_actionTools) {
34 tool.disable();
35 }
36
37 m_thread = std::make_unique<std::thread>(&G4RunTool::Geant4main, this);
38 ATH_MSG_DEBUG("Geant4main thread created, id=" << m_thread->get_id() << ", now waiting for G4 run manager");
39 return StatusCode::SUCCESS;
40}
41
43 // Signal finalization to G4 threads
45
46 // G4 worker threads should be waiting for the next event to simulate at this state
47 // Pushing empty event, will cause one worker thread to realize we are done and call AbortRun for G4
48 for(int i = 0; i < m_nG4threads; ++i) {
49 PushEvent(nullptr);
50 }
51
52 if (m_thread) {
53 try {
54 if(m_thread->joinable()) m_thread->join();
55 }
56 catch(const std::exception& e) {
57 ATH_MSG_ERROR("Failure in G4RunTool::finalize, joining Geant4 main thread:" << e.what());
58 }
59 }
60
61 return StatusCode::SUCCESS;
62}
63
64//---------------------------------------------------------------------------
65// Status management
66//---------------------------------------------------------------------------
67
68// Notify Athena that Geant4 is ready to start a run (called by Geant4 BeginOfRunAction).
72
73// Wait until Geant4 is ready to start a run (called by Athena initialize).
77
79 {
80 std::scoped_lock lk(m_mutex);
81 m_status = status;
82 }
83 m_cv.notify_all();
84}
85
87 std::unique_lock lk(m_mutex);
88 m_cv.wait(lk, [this, status]{ return m_status == status; });
89}
90
91//---------------------------------------------------------------------------
92// Queue management
93//---------------------------------------------------------------------------
94
95// Check the size of the event queue
96size_t G4RunTool::Size() const {
97 std::scoped_lock lk(m_eventQueueSync.m_mutex);
98 return m_eventQueueSync.m_events.size();
99}
100
101// Push an event to the queue (called from Athena threads)
103 {
104 std::scoped_lock lk(m_eventQueueSync.m_mutex);
105 m_eventQueueSync.m_events.push(std::move(ev));
106 }
107 m_eventQueueSync.m_cv.notify_one();
108}
109
111auto G4RunTool::GetEvent() -> UPEvent {
112
113 std::unique_lock lk(m_eventQueueSync.m_mutex);
114 m_eventQueueSync.m_cv.wait(lk, [this]{ return m_eventQueueSync.m_events.size() > 0; });
115 UPEvent ev = std::move(m_eventQueueSync.m_events.front());
116 m_eventQueueSync.m_events.pop();
117 return ev;
118}
119
120//---------------------------------------------------------------------------
121// Geant4 thread function
122//---------------------------------------------------------------------------
123
124// G4 main thread management
126
127 ATH_MSG_INFO("Geant4 main thread starts with id " << std::this_thread::get_id());
128
129 // Construct the default run manager
130 auto runManager = std::make_unique<G4MTRunManager>();
131
132 runManager->SetNumberOfThreads(m_nG4threads);
133 constexpr int seedFirstEventOnly = 1; // name the magic number
134 // we will take care of reseeding each event, turn off Geant4 reseeding
135 runManager->SetSeedOncePerCommunication(seedFirstEventOnly);
136
137 if(m_detConstruction.retrieve().isFailure()) {
138 ATH_MSG_ERROR("Failed to retrieve DetectorGeometryService");
139 return;
140 }
141
142 if(m_physicsListSvc.retrieve().isFailure()) {
143 ATH_MSG_ERROR("Failed to retrieve PhysicsListService");
144 return;
145 }
146
147 if(m_userActionSvc.retrieve().isFailure()) {
148 ATH_MSG_ERROR("Failed to retrieve UserActionService");
149 return;
150 }
151
152 if(m_actionTools.retrieve().isFailure()) {
153 ATH_MSG_ERROR("Failed to retrieve ActionTools");
154 return;
155 }
156
157 // Initialize action tools for G4RunTool
158 for (const auto& action_tool : m_actionTools) {
159 ATH_MSG_INFO("retrieving action tool " + action_tool.name());
160 if (m_userActionSvc->addActionTool(action_tool).isFailure()) {
161 throw std::runtime_error("Failed to add action tool " + action_tool.name());
162 }
163 }
164 // Set the user action service for the G4UA service
165 // Having a ServiceHandle<IG4RunTool> would be a circular dependency which is not supported...
166 m_userActionSvc->G4RunTool(this);
167
168 // Many of the objects created here must be created in the same thread as the run manager
169 runManager->SetUserInitialization(m_detConstruction->GetDetectorConstruction().release());
170
171 // The actual physics list object must be created in the same thread as the run manager
172 runManager->SetUserInitialization(m_physicsListSvc->GetPhysicsList());
173
174 // Set global physics-list options as soon as the list has been created and
175 // before any pre-initialization UI commands are applied.
176 m_physicsListSvc->SetPhysicsListOptions();
177
178 runManager->SetUserInitialization(
179 std::make_unique<G4RunToolWorkerThreadInitialization>().release());
180 runManager->SetUserInitialization(
181 std::make_unique<G4AtlasActionInitialization>(m_userActionSvc.get()).release());
182
183 // G4 user interface commands
184 G4UImanager *ui = G4UImanager::GetUIpointer();
185
186 // Load custom libraries
187 if (!m_libList.empty()) {
188 ATH_MSG_INFO("G4AtlasAlg specific libraries requested ");
189 std::string temp="/load "+m_libList;
190 ui->ApplyCommand(temp);
191 }
192 // Load custom physics
193 if (!m_physList.empty()) {
194 ATH_MSG_INFO("requesting a specific physics list "<< m_physList);
195 std::string temp="/Physics/GetPhysicsList "+m_physList;
196 ui->ApplyCommand(temp);
197 }
198 // Load custom magnetic field
199 if (!m_fieldMap.empty()) {
200 ATH_MSG_INFO("requesting a specific field map "<< m_fieldMap);
201 ATH_MSG_INFO("the field is initialized straight away");
202 std::string temp="/MagneticField/Select "+m_fieldMap;
203 ui->ApplyCommand(temp);
204 ui->ApplyCommand("/MagneticField/Initialize");
205 }
206
207 // Send UI commands
208 ATH_MSG_DEBUG("G4 Command: Trying at the end of initializeOnce()");
209 for (const auto& g4command : m_g4commands) {
210 int returnCode = ui->ApplyCommand( g4command );
211 commandLog(returnCode, g4command);
212 }
213
214 // Initialize run
215 runManager->Initialize();
216
217 // Process-specific UI commands require the processes to exist first. They
218 // are forwarded to the workers with the command stack at the next BeamOn.
219 m_physicsListSvc->SetPhysicsProcessOptions();
220
221 ATH_MSG_INFO("Initializing " << m_physicsInitializationTools.size() << " physics initialization tools");
222 for(auto& physicsTool : m_physicsInitializationTools) {
223 if (physicsTool->initializePhysics().isFailure()) {
224 throw std::runtime_error("Failed to initialize physics with tool " + physicsTool.name());
225 }
226 }
227 // Retrieve core services needed for G4 main thread
228 if(m_userLimitsSvc.retrieve().isFailure()) {
229 throw std::runtime_error("Could not initialize ATLAS UserLimitsSvc!");
230 }
231
232 ATH_MSG_INFO("Geant4 initialization done, BeamOn...");
233
234 // Repeat BeamOn as long as athena event loop is not finished
236 runManager->BeamOn(m_nG4eventsPerRun);
237 }
238
239 // Clean up geometry
240 G4GeometryManager::GetInstance()->OpenGeometry();
241 G4PhysicalVolumeStore::GetInstance()->Clean();
242 ATH_MSG_INFO("Geant4 main thread ended");
243}
244
245void G4RunTool::commandLog(int returnCode, const std::string& commandString) const
246{
247 switch(returnCode) {
248 case 0: { ATH_MSG_DEBUG("G4 Command: " << commandString << " - Command Succeeded"); } break;
249 case 100: { ATH_MSG_ERROR("G4 Command: " << commandString << " - Command Not Found!"); } break;
250 case 200: {
251 auto* stateManager = G4StateManager::GetStateManager();
252 ATH_MSG_DEBUG("G4 Command: " << commandString << " - Illegal Application State (" <<
253 stateManager->GetStateString(stateManager->GetCurrentState()) << ")!");
254 } break;
255 case 300: { ATH_MSG_ERROR("G4 Command: " << commandString << " - Parameter Out of Range!"); } break;
256 case 400: { ATH_MSG_ERROR("G4 Command: " << commandString << " - Parameter Unreadable!"); } break;
257 case 500: { ATH_MSG_ERROR("G4 Command: " << commandString << " - Parameter Out of Candidates!"); } break;
258 case 600: { ATH_MSG_ERROR("G4 Command: " << commandString << " - Alias Not Found!"); } break;
259 default: { ATH_MSG_ERROR("G4 Command: " << commandString << " - Unknown Status!"); } break;
260 }
261
262}
#define ATH_MSG_ERROR(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_DEBUG(x)
ServiceHandle< IPhysicsListSvc > m_physicsListSvc
Definition G4RunTool.h:74
virtual StatusCode finalize() override final
Definition G4RunTool.cxx:42
virtual void WaitBeginRun() override
Definition G4RunTool.cxx:74
EventQueueSynchronization m_eventQueueSync
Definition G4RunTool.h:121
std::unique_ptr< std::thread > m_thread
Definition G4RunTool.h:115
void Geant4main()
Geant4 main thread function, this is executed in a separate thread and blocks on BeamOn.
Gaudi::Property< std::vector< std::string > > m_g4commands
Definition G4RunTool.h:87
Gaudi::Property< int > m_nG4threads
Definition G4RunTool.h:89
Gaudi::Property< int > m_nG4eventsPerRun
Definition G4RunTool.h:90
virtual void NotifyBeginRun() override
Notify Athena that Geant4 is ready to start a run.
Definition G4RunTool.cxx:69
ServiceHandle< IUserLimitsSvc > m_userLimitsSvc
Definition G4RunTool.h:75
virtual UPEvent GetEvent() override
Get an event from the queue (called from Geant4 threads).
Gaudi::Property< std::string > m_libList
Definition G4RunTool.h:84
ToolHandleArray< G4UA::IUserActionTool > m_actionTools
Definition G4RunTool.h:79
virtual StatusCode initialize() override final
Initialize and start the Geant4 main thread, then wait until Geant4 is ready to start the run.
Definition G4RunTool.cxx:25
virtual size_t Size() const override
Definition G4RunTool.cxx:96
virtual void PushEvent(UPEvent ev) override
PublicToolHandleArray< IPhysicsInitializationTool > m_physicsInitializationTools
Definition G4RunTool.h:80
Gaudi::Property< std::string > m_fieldMap
Definition G4RunTool.h:86
ServiceHandle< G4UA::IUserActionSvc > m_userActionSvc
Definition G4RunTool.h:76
StateSynchronization m_statusSync
Definition G4RunTool.h:118
ToolHandle< IDetectorConstructionTool > m_detConstruction
Definition G4RunTool.h:78
void commandLog(int returnCode, const std::string &commandString) const
This command prints a message about a G4Command depending on its returnCode.
Gaudi::Property< std::string > m_physList
Definition G4RunTool.h:85
static std::string release
Definition computils.h:50
int ev
Definition globals.cxx:25
void SetStatus(const Status &)
Definition G4RunTool.cxx:78
std::condition_variable m_cv
Definition G4RunTool.h:104
void WaitStatus(const Status &)
Definition G4RunTool.cxx:86